// Copyright (c) 2010 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 "chrome/common/gpu_messages.h" #include "chrome/common/message_router.h" #include "chrome/renderer/gpu_video_decoder_host.h" #include "media/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 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 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 textures)); MOCK_METHOD2(OnCreateVideoDecoder, void(int32 context_route_id, int32 decoder_host_id)); // Mock methods for VideoDecodeEngine::EventHandler. MOCK_METHOD1(ProduceVideoSample, void(scoped_refptr buffer)); MOCK_METHOD1(ConsumeVideoFrame, void(scoped_refptr frame)); 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; config.codec = media::kCodecH264; config.width = kWidth; config.height = kHeight; 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(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 decoder_host_; scoped_ptr shared_memory_; // Keeps the video frames allocated. std::vector > 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(); }