blob: e9f69a6db74653b3d3c3b78cec1d58a15049d2de [file] [log] [blame]
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/message_loop.h"
#include "content/common/message_router.h"
#include "content/common/gpu_messages.h"
#include "content/renderer/gpu_video_decoder_host.h"
#include "media/base/pipeline.h"
#include "media/video/video_mock_objects.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::_;
using testing::DoAll;
using testing::NotNull;
using testing::Return;
using testing::SetArgumentPointee;
static const int kContextRouteId = 50;
static const int kDecoderHostId = 51;
static const int kDecoderId = 51;
static const int kVideoFrames = 3;
static const int kWidth = 320;
static const int kHeight = 240;
static const int kFrameRateNumerator = 25;
static const int kFrameRateDenominator = 1;
static const int kTransportBufferSize = 1024;
ACTION_P(SimulateAllocateVideoFrames, frames) {
// Fake some texture IDs here.
media::VideoFrame::GlTexture textures[] = {4, 5, 6};
for (int i = 0; i < kVideoFrames; ++i) {
scoped_refptr<media::VideoFrame> frame;
media::VideoFrame::CreateFrameGlTexture(media::VideoFrame::YV12,
kWidth, kHeight, textures,
&frame);
frames->push_back(frame);
arg4->push_back(frame);
}
// Execute the callback to complete the task.
arg5->Run();
delete arg5;
}
ACTION_P2(SendMessage, handler, msg) {
handler->OnMessageReceived(msg);
}
class GpuVideoDecoderHostTest : public testing::Test,
public IPC::Message::Sender,
public media::VideoDecodeEngine::EventHandler {
public:
// This method is used to dispatch IPC messages to mock methods.
virtual bool Send(IPC::Message* msg) {
EXPECT_TRUE(msg);
if (!msg)
return false;
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(GpuVideoDecoderHostTest, *msg)
IPC_MESSAGE_HANDLER(GpuChannelMsg_CreateVideoDecoder,
OnCreateVideoDecoder)
IPC_MESSAGE_HANDLER(GpuVideoDecoderMsg_Initialize,
OnInitialize)
IPC_MESSAGE_HANDLER(GpuVideoDecoderMsg_Destroy,
OnDestroy)
IPC_MESSAGE_HANDLER(GpuVideoDecoderMsg_Flush,
OnFlush)
IPC_MESSAGE_HANDLER(GpuVideoDecoderMsg_EmptyThisBuffer,
OnEmptyThisBuffer)
IPC_MESSAGE_HANDLER(GpuVideoDecoderMsg_VideoFrameAllocated,
OnVideoFrameAllocated)
IPC_MESSAGE_HANDLER(GpuVideoDecoderMsg_ProduceVideoFrame,
OnProduceVideoFrame)
IPC_MESSAGE_UNHANDLED_ERROR()
IPC_END_MESSAGE_MAP()
EXPECT_TRUE(handled);
delete msg;
return true;
}
// Mock methods for outgoing messages.
MOCK_METHOD1(OnInitialize, void(GpuVideoDecoderInitParam param));
MOCK_METHOD0(OnDestroy, void());
MOCK_METHOD0(OnFlush, void());
MOCK_METHOD1(OnEmptyThisBuffer,
void(GpuVideoDecoderInputBufferParam param));
MOCK_METHOD1(OnProduceVideoFrame, void(int32 frame_id));
MOCK_METHOD2(OnVideoFrameAllocated,
void(int32 frame_id, std::vector<uint32> textures));
MOCK_METHOD2(OnCreateVideoDecoder,
void(int32 context_route_id, int32 decoder_host_id));
// Mock methods for VideoDecodeEngine::EventHandler.
MOCK_METHOD1(ProduceVideoSample,
void(scoped_refptr<media::Buffer> buffer));
MOCK_METHOD2(ConsumeVideoFrame,
void(scoped_refptr<media::VideoFrame> frame,
const media::PipelineStatistics& statistics));
MOCK_METHOD1(OnInitializeComplete,
void(const media::VideoCodecInfo& info));
MOCK_METHOD0(OnUninitializeComplete, void());
MOCK_METHOD0(OnFlushComplete, void());
MOCK_METHOD0(OnSeekComplete, void());
MOCK_METHOD0(OnError, void());
MOCK_METHOD1(OnFormatChange,
void(media::VideoStreamInfo stream_info));
void Initialize() {
decoder_host_.reset(
new GpuVideoDecoderHost(&router_, this, kContextRouteId,
kDecoderHostId));
shared_memory_.reset(new base::SharedMemory());
shared_memory_->CreateAnonymous(kTransportBufferSize);
GpuVideoDecoderHostMsg_CreateVideoDecoderDone msg1(kDecoderHostId,
kDecoderId);
EXPECT_CALL(*this, OnCreateVideoDecoder(kContextRouteId, kDecoderHostId))
.WillOnce(SendMessage(decoder_host_.get(), msg1));
GpuVideoDecoderInitDoneParam param;
param.success = true;
param.input_buffer_size = kTransportBufferSize;
param.input_buffer_handle = shared_memory_->handle();
GpuVideoDecoderHostMsg_InitializeACK msg2(kDecoderHostId, param);
EXPECT_CALL(*this, OnInitialize(_))
.WillOnce(SendMessage(decoder_host_.get(), msg2));
EXPECT_CALL(*this, OnInitializeComplete(_));
media::VideoCodecConfig config(
media::kCodecH264,
kWidth,
kHeight,
kFrameRateNumerator,
kFrameRateDenominator,
NULL,
0);
decoder_host_->Initialize(&message_loop_, this, &context_, config);
message_loop_.RunAllPending();
}
void Uninitialize() {
// A message is sent to GPU process to destroy the decoder.
GpuVideoDecoderHostMsg_DestroyACK msg(kDecoderHostId);
EXPECT_CALL(*this, OnDestroy())
.WillOnce(SendMessage(decoder_host_.get(), msg));
EXPECT_CALL(context_, ReleaseAllVideoFrames());
EXPECT_CALL(*this, OnUninitializeComplete());
decoder_host_->Uninitialize();
}
void AllocateVideoFrames() {
// Expect context is called to allocate video frames.
EXPECT_CALL(context_,
AllocateVideoFrames(kVideoFrames, kWidth, kHeight,
media::VideoFrame::YV12,
NotNull(), NotNull()))
.WillOnce(SimulateAllocateVideoFrames(&frames_))
.RetiresOnSaturation();
// Expect that we send the video frames to the GPU process.
EXPECT_CALL(*this, OnVideoFrameAllocated(_, _))
.Times(kVideoFrames)
.RetiresOnSaturation();
// Pretend that a message is sent to GpuVideoDecoderHost to allocate
// video frames.
GpuVideoDecoderHostMsg_AllocateVideoFrames msg(
kDecoderHostId, kVideoFrames, kWidth, kHeight,
static_cast<int32>(media::VideoFrame::YV12));
decoder_host_->OnMessageReceived(msg);
}
void ReleaseVideoFrames() {
// Expect that context is called to release all video frames.
EXPECT_CALL(context_, ReleaseAllVideoFrames())
.RetiresOnSaturation();
// Pretend a message is sent to release all video frames.
GpuVideoDecoderHostMsg_ReleaseAllVideoFrames msg(kDecoderHostId);
decoder_host_->OnMessageReceived(msg);
// Clear the list of video frames allocated.
frames_.clear();
}
void ProduceVideoFrame(int first_frame_id) {
for (int i = 0; i < kVideoFrames; ++i) {
// Expect that a request is received to produce a video frame.
GpuVideoDecoderHostMsg_ConsumeVideoFrame msg(
kDecoderHostId, first_frame_id + i, 0, 0, 0);
EXPECT_CALL(*this, OnProduceVideoFrame(first_frame_id + i))
.WillOnce(SendMessage(decoder_host_.get(), msg))
.RetiresOnSaturation();
// Expect that a reply is made when a video frame is ready.
EXPECT_CALL(*this, ConsumeVideoFrame(frames_[i], _))
.RetiresOnSaturation();
// Use the allocated video frames to make a request.
decoder_host_->ProduceVideoFrame(frames_[i]);
}
}
private:
MessageLoop message_loop_;
MessageRouter router_;
media::MockVideoDecodeContext context_;
scoped_ptr<GpuVideoDecoderHost> decoder_host_;
scoped_ptr<base::SharedMemory> shared_memory_;
// Keeps the video frames allocated.
std::vector<scoped_refptr<media::VideoFrame> > frames_;
};
// Test that when we initialize GpuVideoDecoderHost the corresponding
// IPC messages are sent and at the end OnInitializeComplete() is
// called with the right parameters.
TEST_F(GpuVideoDecoderHostTest, Initialize) {
Initialize();
}
// Test that the sequence of method calls and IPC messages is correct.
// And at the end OnUninitializeComplete() is called.
TEST_F(GpuVideoDecoderHostTest, Uninitialize) {
Initialize();
Uninitialize();
}
// Test that IPC messages are sent to GpuVideoDecoderHost and it
// calls VideoDecodeContext to allocate textures and send these
// textures back to the GPU process by IPC messages.
TEST_F(GpuVideoDecoderHostTest, AllocateVideoFrames) {
Initialize();
AllocateVideoFrames();
Uninitialize();
}
// Test that IPC messages are sent to GpuVideoDecoderHost to
// release textures and VideoDecodeContext is called correctly.
TEST_F(GpuVideoDecoderHostTest, ReleaseVideoFrames) {
Initialize();
AllocateVideoFrames();
ReleaseVideoFrames();
AllocateVideoFrames();
ReleaseVideoFrames();
Uninitialize();
}
// Test the sequence of IPC messages and methods calls for a decode
// routine. This tests the output port only.
TEST_F(GpuVideoDecoderHostTest, ProduceVideoFrame) {
Initialize();
AllocateVideoFrames();
ProduceVideoFrame(0);
ReleaseVideoFrames();
AllocateVideoFrames();
ProduceVideoFrame(kVideoFrames);
ReleaseVideoFrames();
Uninitialize();
}