diff options
-rw-r--r-- | chrome/chrome_tests.gypi | 1 | ||||
-rw-r--r-- | chrome/common/gpu_messages_internal.h | 20 | ||||
-rw-r--r-- | chrome/common/gpu_video_common.cc | 28 | ||||
-rw-r--r-- | chrome/common/gpu_video_common.h | 25 | ||||
-rw-r--r-- | chrome/gpu/gpu_channel.cc | 20 | ||||
-rw-r--r-- | chrome/gpu/gpu_channel.h | 3 | ||||
-rw-r--r-- | chrome/gpu/gpu_video_decoder.cc | 23 | ||||
-rw-r--r-- | chrome/gpu/gpu_video_decoder.h | 7 | ||||
-rw-r--r-- | chrome/gpu/gpu_video_decoder_unittest.cc | 8 | ||||
-rw-r--r-- | chrome/gpu/gpu_video_service.cc | 31 | ||||
-rw-r--r-- | chrome/gpu/gpu_video_service.h | 12 | ||||
-rw-r--r-- | chrome/renderer/gpu_video_decoder_host.cc | 298 | ||||
-rw-r--r-- | chrome/renderer/gpu_video_decoder_host.h | 77 | ||||
-rw-r--r-- | chrome/renderer/gpu_video_decoder_host_unittest.cc | 259 | ||||
-rw-r--r-- | chrome/renderer/gpu_video_service_host.cc | 18 | ||||
-rw-r--r-- | chrome/renderer/gpu_video_service_host.h | 10 | ||||
-rw-r--r-- | media/base/video_frame.h | 2 | ||||
-rw-r--r-- | media/video/mock_objects.h | 20 |
18 files changed, 631 insertions, 231 deletions
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index 80653b1..1af8b15 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -1555,6 +1555,7 @@ 'renderer/extensions/extension_api_json_validity_unittest.cc', 'renderer/extensions/extension_renderer_info_unittest.cc', 'renderer/extensions/json_schema_unittest.cc', + 'renderer/gpu_video_decoder_host_unittest.cc', 'renderer/media/audio_renderer_impl_unittest.cc', 'renderer/net/predictor_queue_unittest.cc', 'renderer/net/renderer_predictor_unittest.cc', diff --git a/chrome/common/gpu_messages_internal.h b/chrome/common/gpu_messages_internal.h index 9a6896b..daaaa24 100644 --- a/chrome/common/gpu_messages_internal.h +++ b/chrome/common/gpu_messages_internal.h @@ -188,11 +188,13 @@ IPC_BEGIN_MESSAGES(GpuChannel) // Create hardware video decoder && associate it with the output |decoder_id|; // We need this to be control message because we had to map the GpuChannel and // |decoder_id|. - IPC_SYNC_MESSAGE_CONTROL0_1(GpuChannelMsg_CreateVideoDecoder, - GpuVideoDecoderInfoParam) + IPC_MESSAGE_CONTROL2(GpuChannelMsg_CreateVideoDecoder, + int32, /* context_route_id */ + int32) /* decoder_id */ // Release all resource of the hardware video decoder which was assocaited // with the input |decoder_id|. + // TODO(hclam): This message needs to be asynchronous. IPC_SYNC_MESSAGE_CONTROL1_0(GpuChannelMsg_DestroyVideoDecoder, int32 /* decoder_id */) @@ -284,8 +286,8 @@ IPC_BEGIN_MESSAGES(GpuCommandBuffer) IPC_END_MESSAGES(GpuCommandBuffer) //------------------------------------------------------------------------------ - -// GpuVideoDecoderMsgs : send from renderer process to gpu process. +// GPU Video Decoder Messages +// These messages are sent from Renderer process to GPU process. IPC_BEGIN_MESSAGES(GpuVideoDecoder) // Initialize and configure GpuVideoDecoder asynchronously. IPC_MESSAGE_ROUTED1(GpuVideoDecoderMsg_Initialize, @@ -314,9 +316,13 @@ IPC_BEGIN_MESSAGES(GpuVideoDecoder) IPC_END_MESSAGES(GpuVideoDecoder) //------------------------------------------------------------------------------ - -// GpuVideoDecoderMsgs : send from gpu process to renderer process. +// GPU Video Decoder Host Messages +// These messages are sent from GPU process to Renderer process. IPC_BEGIN_MESSAGES(GpuVideoDecoderHost) + // Inform GpuVideoDecoderHost that a GpuVideoDecoder is created. + IPC_MESSAGE_ROUTED1(GpuVideoDecoderHostMsg_CreateVideoDecoderDone, + int32) /* decoder_id */ + // Confirm GpuVideoDecoder had been initialized or failed to initialize. IPC_MESSAGE_ROUTED1(GpuVideoDecoderHostMsg_InitializeACK, GpuVideoDecoderInitDoneParam) @@ -342,7 +348,7 @@ IPC_BEGIN_MESSAGES(GpuVideoDecoderHost) // Allocate video frames for output of the hardware video decoder. IPC_MESSAGE_ROUTED4(GpuVideoDecoderHostMsg_AllocateVideoFrames, - int32, /* Numer of video frames to generate */ + int32, /* Number of video frames to generate */ uint32, /* Width of the video frame */ uint32, /* Height of the video frame */ int32 /* Format of the video frame */) diff --git a/chrome/common/gpu_video_common.cc b/chrome/common/gpu_video_common.cc index 556e385..f6339e3 100644 --- a/chrome/common/gpu_video_common.cc +++ b/chrome/common/gpu_video_common.cc @@ -32,34 +32,6 @@ void ParamTraits<GpuVideoServiceInfoParam>::Log( /////////////////////////////////////////////////////////////////////////////// -void ParamTraits<GpuVideoDecoderInfoParam>::Write( - Message* m, const GpuVideoDecoderInfoParam& p) { - WriteParam(m, p.context_id); - WriteParam(m, p.decoder_id); - WriteParam(m, p.decoder_route_id); - WriteParam(m, p.decoder_host_route_id); -} - -bool ParamTraits<GpuVideoDecoderInfoParam>::Read( - const Message* m, void** iter, GpuVideoDecoderInfoParam* r) { - if (!ReadParam(m, iter, &r->context_id) || - !ReadParam(m, iter, &r->decoder_id) || - !ReadParam(m, iter, &r->decoder_route_id) || - !ReadParam(m, iter, &r->decoder_host_route_id)) - return false; - return true; -} - -void ParamTraits<GpuVideoDecoderInfoParam>::Log( - const GpuVideoDecoderInfoParam& p, std::string* l) { - l->append(StringPrintf("(%d, %d, %d)", - p.decoder_id, - p.decoder_route_id, - p.decoder_host_route_id)); -} - -/////////////////////////////////////////////////////////////////////////////// - void ParamTraits<GpuVideoDecoderInitParam>::Write( Message* m, const GpuVideoDecoderInitParam& p) { WriteParam(m, p.codec_id); diff --git a/chrome/common/gpu_video_common.h b/chrome/common/gpu_video_common.h index 30a94be..55a44bc 100644 --- a/chrome/common/gpu_video_common.h +++ b/chrome/common/gpu_video_common.h @@ -19,27 +19,14 @@ enum GpuVideoBufferFlag { struct GpuVideoServiceInfoParam { // route id for GpuVideoService on GPU process side for this channel. int32 video_service_route_id; + // route id for GpuVideoServiceHost on Render process side for this channel. int32 video_service_host_route_id; + // TODO(jiesun): define capabilities of video service. int32 service_available; }; -struct GpuVideoDecoderInfoParam { - // Context ID of the GLES2 context what this decoder should assicate with. - int context_id; - - // Global decoder id. - int32 decoder_id; - - // Route id for GpuVideoDecoder on GPU process side for this channel. - int32 decoder_route_id; - - // TODO(hclam): Merge this ID with |decoder_route_id_|. - // Route id for GpuVideoServiceHost on Render process side for this channel. - int32 decoder_host_route_id; -}; - struct GpuVideoDecoderInitParam { int32 codec_id; int32 width; @@ -86,14 +73,6 @@ struct ParamTraits<GpuVideoServiceInfoParam> { }; template <> -struct ParamTraits<GpuVideoDecoderInfoParam> { - typedef GpuVideoDecoderInfoParam param_type; - static void Write(Message* m, const param_type& p); - static bool Read(const Message* m, void** iter, param_type* r); - static void Log(const param_type& p, std::string* l); -}; - -template <> struct ParamTraits<GpuVideoDecoderInitParam> { typedef GpuVideoDecoderInitParam param_type; static void Write(Message* m, const param_type& p); diff --git a/chrome/gpu/gpu_channel.cc b/chrome/gpu/gpu_channel.cc index c5c5025..9b8095f 100644 --- a/chrome/gpu/gpu_channel.cc +++ b/chrome/gpu/gpu_channel.cc @@ -206,24 +206,26 @@ void GpuChannel::OnGetVideoService(GpuVideoServiceInfoParam* info) { #endif } -void GpuChannel::OnCreateVideoDecoder(GpuVideoDecoderInfoParam* info) { +void GpuChannel::OnCreateVideoDecoder(int32 context_route_id, + int32 decoder_host_id) { #if defined(ENABLE_GPU) LOG(INFO) << "GpuChannel::OnCreateVideoDecoder"; - info->decoder_id = -1; GpuVideoService* service = GpuVideoService::get(); - if (service == NULL) + if (service == NULL) { + // TODO(hclam): Need to send a failure message. return; + } // The context ID corresponds to the command buffer used. - GpuCommandBufferStub* stub = stubs_.Lookup(info->context_id); - - info->decoder_host_route_id = GenerateRouteID(); - info->decoder_route_id = GenerateRouteID(); + GpuCommandBufferStub* stub = stubs_.Lookup(context_route_id); + int32 decoder_id = GenerateRouteID(); // TODO(hclam): Need to be careful about the lifetime of the command buffer // decoder. - service->CreateVideoDecoder(this, &router_, info, - stub->processor()->decoder()); + bool ret = service->CreateVideoDecoder( + this, &router_, decoder_host_id, decoder_id, + stub->processor()->decoder()); + DCHECK(ret) << "Failed to create a GpuVideoDecoder"; #endif } diff --git a/chrome/gpu/gpu_channel.h b/chrome/gpu/gpu_channel.h index 13994c3..73fce43 100644 --- a/chrome/gpu/gpu_channel.h +++ b/chrome/gpu/gpu_channel.h @@ -76,7 +76,8 @@ class GpuChannel : public IPC::Channel::Listener, void OnDestroyCommandBuffer(int32 route_id); void OnGetVideoService(GpuVideoServiceInfoParam* info); - void OnCreateVideoDecoder(GpuVideoDecoderInfoParam* info); + void OnCreateVideoDecoder(int32 context_route_id, + int32 decoder_host_id); void OnDestroyVideoDecoder(int32 decoder_id); scoped_ptr<IPC::SyncChannel> channel_; diff --git a/chrome/gpu/gpu_video_decoder.cc b/chrome/gpu/gpu_video_decoder.cc index a7877a0..b316253 100644 --- a/chrome/gpu/gpu_video_decoder.cc +++ b/chrome/gpu/gpu_video_decoder.cc @@ -209,12 +209,12 @@ void GpuVideoDecoder::SetGpuVideoDevice(GpuVideoDevice* device) { GpuVideoDecoder::GpuVideoDecoder( MessageLoop* message_loop, - const GpuVideoDecoderInfoParam* param, + int32 decoder_host_id, IPC::Message::Sender* sender, base::ProcessHandle handle, gpu::gles2::GLES2Decoder* decoder) : message_loop_(message_loop), - decoder_host_route_id_(param->decoder_host_route_id), + decoder_host_id_(decoder_host_id), sender_(sender), renderer_handle_(handle), gles2_decoder_(decoder) { @@ -310,33 +310,34 @@ void GpuVideoDecoder::OnVideoFrameAllocated(int32 frame_id, void GpuVideoDecoder::SendInitializeDone( const GpuVideoDecoderInitDoneParam& param) { if (!sender_->Send( - new GpuVideoDecoderHostMsg_InitializeACK(route_id(), param))) { + new GpuVideoDecoderHostMsg_InitializeACK(decoder_host_id(), param))) { LOG(ERROR) << "GpuVideoDecoderMsg_InitializeACK failed"; } } void GpuVideoDecoder::SendUninitializeDone() { - if (!sender_->Send(new GpuVideoDecoderHostMsg_DestroyACK(route_id()))) { + if (!sender_->Send( + new GpuVideoDecoderHostMsg_DestroyACK(decoder_host_id()))) { LOG(ERROR) << "GpuVideoDecoderMsg_DestroyACK failed"; } } void GpuVideoDecoder::SendFlushDone() { - if (!sender_->Send(new GpuVideoDecoderHostMsg_FlushACK(route_id()))) { + if (!sender_->Send(new GpuVideoDecoderHostMsg_FlushACK(decoder_host_id()))) { LOG(ERROR) << "GpuVideoDecoderMsg_FlushACK failed"; } } void GpuVideoDecoder::SendEmptyBufferDone() { if (!sender_->Send( - new GpuVideoDecoderHostMsg_EmptyThisBufferDone(route_id()))) { + new GpuVideoDecoderHostMsg_EmptyThisBufferDone(decoder_host_id()))) { LOG(ERROR) << "GpuVideoDecoderMsg_EmptyThisBufferDone failed"; } } void GpuVideoDecoder::SendEmptyBufferACK() { if (!sender_->Send( - new GpuVideoDecoderHostMsg_EmptyThisBufferACK(route_id()))) { + new GpuVideoDecoderHostMsg_EmptyThisBufferACK(decoder_host_id()))) { LOG(ERROR) << "GpuVideoDecoderMsg_EmptyThisBufferACK failed"; } } @@ -345,7 +346,7 @@ void GpuVideoDecoder::SendConsumeVideoFrame( int32 frame_id, int64 timestamp, int64 duration, int32 flags) { if (!sender_->Send( new GpuVideoDecoderHostMsg_ConsumeVideoFrame( - route_id(), frame_id, timestamp, duration, flags))) { + decoder_host_id(), frame_id, timestamp, duration, flags))) { LOG(ERROR) << "GpuVideoDecodeHostMsg_ConsumeVideoFrame failed."; } } @@ -354,14 +355,16 @@ void GpuVideoDecoder::SendAllocateVideoFrames( int n, size_t width, size_t height, media::VideoFrame::Format format) { if (!sender_->Send( new GpuVideoDecoderHostMsg_AllocateVideoFrames( - route_id(), n, width, height, static_cast<int32>(format)))) { + decoder_host_id(), n, width, height, + static_cast<int32>(format)))) { LOG(ERROR) << "GpuVideoDecoderMsg_AllocateVideoFrames failed"; } } void GpuVideoDecoder::SendReleaseAllVideoFrames() { if (!sender_->Send( - new GpuVideoDecoderHostMsg_ReleaseAllVideoFrames(route_id()))) { + new GpuVideoDecoderHostMsg_ReleaseAllVideoFrames( + decoder_host_id()))) { LOG(ERROR) << "GpuVideoDecoderMsg_ReleaseAllVideoFrames failed"; } } diff --git a/chrome/gpu/gpu_video_decoder.h b/chrome/gpu/gpu_video_decoder.h index f975eb8..786e0de 100644 --- a/chrome/gpu/gpu_video_decoder.h +++ b/chrome/gpu/gpu_video_decoder.h @@ -93,7 +93,7 @@ class GpuVideoDecoder public: // Constructor and destructor. GpuVideoDecoder(MessageLoop* message_loop, - const GpuVideoDecoderInfoParam* param, + int32 decoder_host_id, IPC::Message::Sender* sender, base::ProcessHandle handle, gpu::gles2::GLES2Decoder* decoder); @@ -139,7 +139,7 @@ class GpuVideoDecoder Task* task; }; - int32 route_id() { return decoder_host_route_id_; } + int32 decoder_host_id() { return decoder_host_id_; } bool CreateInputTransferBuffer(uint32 size, base::SharedMemoryHandle* handle); @@ -168,7 +168,8 @@ class GpuVideoDecoder // The message loop that this object should run on. MessageLoop* message_loop_; - int32 decoder_host_route_id_; + // ID of GpuVideoDecoderHost in the Renderer Process. + int32 decoder_host_id_; // Used only in system memory path. i.e. Remove this later. scoped_refptr<VideoFrame> frame_; diff --git a/chrome/gpu/gpu_video_decoder_unittest.cc b/chrome/gpu/gpu_video_decoder_unittest.cc index 4548807..948e30b 100644 --- a/chrome/gpu/gpu_video_decoder_unittest.cc +++ b/chrome/gpu/gpu_video_decoder_unittest.cc @@ -18,6 +18,7 @@ using testing::Return; using testing::SetArgumentPointee; static const int32 kFrameId = 10; +static const int32 kDecoderHostId = 50; static const media::VideoFrame::GlTexture kClientTexture = 101; static const media::VideoFrame::GlTexture kServiceTexture = 102; static const size_t kWidth = 320; @@ -71,11 +72,8 @@ class GpuVideoDecoderTest : public testing::Test, // Create the mock objects. gles2_decoder_.reset(new gpu::gles2::MockGLES2Decoder(&group_)); - // Initialize GpuVideoDecoder with the default params. - GpuVideoDecoderInfoParam param; - memset(¶m, 0, sizeof(param)); gpu_video_decoder_ = new GpuVideoDecoder( - &message_loop_, ¶m, this, base::kNullProcessHandle, + &message_loop_, kDecoderHostId, this, base::kNullProcessHandle, gles2_decoder_.get()); // Create the mock objects. @@ -95,7 +93,7 @@ class GpuVideoDecoderTest : public testing::Test, &device_frame_); } - ~GpuVideoDecoderTest() { + virtual ~GpuVideoDecoderTest() { gpu_video_decoder_->SetVideoDecodeEngine(NULL); gpu_video_decoder_->SetGpuVideoDevice(NULL); } diff --git a/chrome/gpu/gpu_video_service.cc b/chrome/gpu/gpu_video_service.cc index 6ff2e81..2ef32e8 100644 --- a/chrome/gpu/gpu_video_service.cc +++ b/chrome/gpu/gpu_video_service.cc @@ -7,7 +7,7 @@ #include "chrome/gpu/gpu_video_decoder.h" #include "chrome/gpu/gpu_video_service.h" -GpuVideoService::GpuVideoService() : next_available_decoder_id_(0) { +GpuVideoService::GpuVideoService() { // TODO(jiesun): move this time consuming stuff out of here. IntializeGpuVideoService(); } @@ -43,30 +43,27 @@ bool GpuVideoService::UnintializeGpuVideoService() { bool GpuVideoService::CreateVideoDecoder( GpuChannel* channel, MessageRouter* router, - GpuVideoDecoderInfoParam* param, + int32 decoder_host_id, + int32 decoder_id, gpu::gles2::GLES2Decoder* gles2_decoder) { GpuVideoDecoderInfo decoder_info; - int32 decoder_id = GetNextAvailableDecoderID(); - param->decoder_id = decoder_id; - base::ProcessHandle handle = channel->renderer_handle(); - decoder_info.decoder_ = new GpuVideoDecoder(MessageLoop::current(), - param, channel, handle, - gles2_decoder); - decoder_info.channel_ = channel; - decoder_info.param = *param; + decoder_info.decoder = new GpuVideoDecoder(MessageLoop::current(), + decoder_host_id, + channel, + channel->renderer_handle(), + gles2_decoder); + decoder_info.channel = channel; decoder_map_[decoder_id] = decoder_info; - router->AddRoute(param->decoder_route_id, decoder_info.decoder_); + router->AddRoute(decoder_id, decoder_info.decoder); + + channel->Send(new GpuVideoDecoderHostMsg_CreateVideoDecoderDone( + decoder_host_id, decoder_id)); return true; } void GpuVideoService::DestroyVideoDecoder( MessageRouter* router, int32 decoder_id) { - int32 route_id = decoder_map_[decoder_id].param.decoder_route_id; - router->RemoveRoute(route_id); + router->RemoveRoute(decoder_id); decoder_map_.erase(decoder_id); } - -int32 GpuVideoService::GetNextAvailableDecoderID() { - return ++next_available_decoder_id_; -} diff --git a/chrome/gpu/gpu_video_service.h b/chrome/gpu/gpu_video_service.h index 0b446bb..a2c090e 100644 --- a/chrome/gpu/gpu_video_service.h +++ b/chrome/gpu/gpu_video_service.h @@ -22,32 +22,30 @@ class GpuVideoService : public IPC::Channel::Listener, virtual void OnChannelError(); virtual void OnMessageReceived(const IPC::Message& message); + // TODO(hclam): Remove return value. bool CreateVideoDecoder(GpuChannel* channel, MessageRouter* router, - GpuVideoDecoderInfoParam* param, + int32 decoder_host_id, + int32 decoder_id, gpu::gles2::GLES2Decoder* gles2_decoder); void DestroyVideoDecoder(MessageRouter* router, int32 decoder_id); private: struct GpuVideoDecoderInfo { - scoped_refptr<GpuVideoDecoder> decoder_; - GpuChannel* channel_; - GpuVideoDecoderInfoParam param; + scoped_refptr<GpuVideoDecoder> decoder; + GpuChannel* channel; }; GpuVideoService(); virtual ~GpuVideoService(); std::map<int32, GpuVideoDecoderInfo> decoder_map_; - int32 next_available_decoder_id_; // Specialize video service on different platform will override. virtual bool IntializeGpuVideoService(); virtual bool UnintializeGpuVideoService(); - int32 GetNextAvailableDecoderID(); - friend struct DefaultSingletonTraits<GpuVideoService>; DISALLOW_COPY_AND_ASSIGN(GpuVideoService); }; diff --git a/chrome/renderer/gpu_video_decoder_host.cc b/chrome/renderer/gpu_video_decoder_host.cc index 08e7d5d..4a97702 100644 --- a/chrome/renderer/gpu_video_decoder_host.cc +++ b/chrome/renderer/gpu_video_decoder_host.cc @@ -5,22 +5,25 @@ #include "chrome/renderer/gpu_video_decoder_host.h" #include "chrome/common/gpu_messages.h" -#include "chrome/renderer/gpu_video_service_host.h" -#include "chrome/renderer/render_thread.h" +#include "chrome/common/message_router.h" +#include "media/video/video_decode_context.h" -GpuVideoDecoderHost::GpuVideoDecoderHost(GpuVideoServiceHost* service_host, +GpuVideoDecoderHost::GpuVideoDecoderHost(MessageRouter* router, IPC::Message::Sender* ipc_sender, - int context_route_id) - : gpu_video_service_host_(service_host), + int context_route_id, + int32 decoder_host_id) + : router_(router), ipc_sender_(ipc_sender), context_route_id_(context_route_id), + message_loop_(NULL), event_handler_(NULL), - buffer_id_serial_(0), + context_(NULL), state_(kStateUninitialized), - input_buffer_busy_(false) { + decoder_host_id_(decoder_host_id), + decoder_id_(0), + input_buffer_busy_(false), + current_frame_id_(0) { memset(&config_, 0, sizeof(config_)); - memset(&done_param_, 0, sizeof(done_param_)); - memset(&decoder_info_, 0, sizeof(decoder_info_)); } void GpuVideoDecoderHost::OnChannelError() { @@ -29,6 +32,8 @@ void GpuVideoDecoderHost::OnChannelError() { void GpuVideoDecoderHost::OnMessageReceived(const IPC::Message& msg) { IPC_BEGIN_MESSAGE_MAP(GpuVideoDecoderHost, msg) + IPC_MESSAGE_HANDLER(GpuVideoDecoderHostMsg_CreateVideoDecoderDone, + OnCreateVideoDecoderDone) IPC_MESSAGE_HANDLER(GpuVideoDecoderHostMsg_InitializeACK, OnInitializeDone) IPC_MESSAGE_HANDLER(GpuVideoDecoderHostMsg_DestroyACK, @@ -38,7 +43,13 @@ void GpuVideoDecoderHost::OnMessageReceived(const IPC::Message& msg) { IPC_MESSAGE_HANDLER(GpuVideoDecoderHostMsg_EmptyThisBufferACK, OnEmptyThisBufferACK) IPC_MESSAGE_HANDLER(GpuVideoDecoderHostMsg_EmptyThisBufferDone, - OnEmptyThisBufferDone) + OnProduceVideoSample) + IPC_MESSAGE_HANDLER(GpuVideoDecoderHostMsg_ConsumeVideoFrame, + OnConsumeVideoFrame) + IPC_MESSAGE_HANDLER(GpuVideoDecoderHostMsg_AllocateVideoFrames, + OnAllocateVideoFrames) + IPC_MESSAGE_HANDLER(GpuVideoDecoderHostMsg_ReleaseAllVideoFrames, + OnReleaseAllVideoFrames) IPC_MESSAGE_UNHANDLED_ERROR() IPC_END_MESSAGE_MAP() } @@ -46,41 +57,44 @@ void GpuVideoDecoderHost::OnMessageReceived(const IPC::Message& msg) { void GpuVideoDecoderHost::Initialize( MessageLoop* message_loop, VideoDecodeEngine::EventHandler* event_handler, media::VideoDecodeContext* context, const media::VideoCodecConfig& config) { - // TODO(hclam): Call |event_handler| here. - DCHECK_EQ(state_, kStateUninitialized); - - // Save the event handler before we perform initialization operations so - // that we can report initialization events. - event_handler_ = event_handler; - - // TODO(hclam): This create video decoder operation is synchronous, need to - // make it asynchronous. - decoder_info_.context_id = context_route_id_; - if (!ipc_sender_->Send( - new GpuChannelMsg_CreateVideoDecoder(&decoder_info_))) { - LOG(ERROR) << "GpuChannelMsg_CreateVideoDecoder failed"; + if (MessageLoop::current() != message_loop) { + message_loop->PostTask( + FROM_HERE, + NewRunnableMethod(this, &GpuVideoDecoderHost::Initialize, message_loop, + event_handler, context, config)); return; } - // Add the route so we'll receive messages. - gpu_video_service_host_->AddRoute(my_route_id(), this); - - // Save the configuration parameters. + // Initialization operations should be performed on the message loop assigned. + // Save the parameters and post a task to complete initialization. + DCHECK_EQ(kStateUninitialized, state_); + DCHECK(!message_loop_); + message_loop_ = message_loop; + event_handler_ = event_handler; + context_ = context; config_ = config; - // TODO(hclam): Initialize |param| with the right values. - GpuVideoDecoderInitParam param; - param.width = config.width; - param.height = config.height; + // Add the route so we'll receive messages. + router_->AddRoute(decoder_host_id_, this); - if (!ipc_sender_ || !ipc_sender_->Send( - new GpuVideoDecoderMsg_Initialize(route_id(), param))) { - LOG(ERROR) << "GpuVideoDecoderMsg_Initialize failed"; + if (!ipc_sender_->Send( + new GpuChannelMsg_CreateVideoDecoder(context_route_id_, + decoder_host_id_))) { + LOG(ERROR) << "GpuChannelMsg_CreateVideoDecoder failed"; + event_handler_->OnError(); return; } } void GpuVideoDecoderHost::ConsumeVideoSample(scoped_refptr<Buffer> buffer) { + if (MessageLoop::current() != message_loop_) { + message_loop_->PostTask( + FROM_HERE, + NewRunnableMethod( + this, &GpuVideoDecoderHost::ConsumeVideoSample, buffer)); + return; + } + DCHECK_NE(state_, kStateUninitialized); DCHECK_NE(state_, kStateFlushing); @@ -90,124 +104,226 @@ void GpuVideoDecoderHost::ConsumeVideoSample(scoped_refptr<Buffer> buffer) { return; input_buffer_queue_.push_back(buffer); - SendInputBufferToGpu(); + SendConsumeVideoSample(); } void GpuVideoDecoderHost::ProduceVideoFrame(scoped_refptr<VideoFrame> frame) { + if (MessageLoop::current() != message_loop_) { + message_loop_->PostTask( + FROM_HERE, + NewRunnableMethod( + this, &GpuVideoDecoderHost::ProduceVideoFrame, frame)); + return; + } + DCHECK_NE(state_, kStateUninitialized); - // Depends on who provides buffer. client could return buffer to - // us while flushing. + // During flush client of this object will call this method to return all + // video frames. We should only ignore such method calls if we are in error + // state. if (state_ == kStateError) return; - // TODO(hclam): We should keep an IDMap to convert between a frame a buffer - // ID so that we can signal GpuVideoDecoder in GPU process to use the buffer. - // This eliminates one conversion step. + // Check that video frame is valid. + if (!frame || frame->format() == media::VideoFrame::EMPTY || + frame->IsEndOfStream()) { + return; + } + + SendProduceVideoFrame(frame); } void GpuVideoDecoderHost::Uninitialize() { - if (!ipc_sender_ || !ipc_sender_->Send( - new GpuVideoDecoderMsg_Destroy(route_id()))) { - LOG(ERROR) << "GpuVideoDecoderMsg_Destroy failed"; + if (MessageLoop::current() != message_loop_) { + message_loop_->PostTask( + FROM_HERE, + NewRunnableMethod(this, &GpuVideoDecoderHost::Uninitialize)); return; } - gpu_video_service_host_->RemoveRoute(my_route_id()); - return; + if (!ipc_sender_->Send(new GpuVideoDecoderMsg_Destroy(decoder_id_))) { + LOG(ERROR) << "GpuVideoDecoderMsg_Destroy failed"; + event_handler_->OnError(); + } } void GpuVideoDecoderHost::Flush() { + if (MessageLoop::current() != message_loop_) { + message_loop_->PostTask( + FROM_HERE, NewRunnableMethod(this, &GpuVideoDecoderHost::Flush)); + return; + } + state_ = kStateFlushing; - if (!ipc_sender_ || !ipc_sender_->Send( - new GpuVideoDecoderMsg_Flush(route_id()))) { + if (!ipc_sender_->Send(new GpuVideoDecoderMsg_Flush(decoder_id_))) { LOG(ERROR) << "GpuVideoDecoderMsg_Flush failed"; + event_handler_->OnError(); return; } + input_buffer_queue_.clear(); // TODO(jiesun): because GpuVideoDeocder/GpuVideoDecoder are asynchronously. // We need a way to make flush logic more clear. but I think ring buffer // should make the busy flag obsolete, therefore I will leave it for now. input_buffer_busy_ = false; - return; } void GpuVideoDecoderHost::Seek() { // TODO(hclam): Implement. } +void GpuVideoDecoderHost::OnCreateVideoDecoderDone(int32 decoder_id) { + DCHECK_EQ(message_loop_, MessageLoop::current()); + decoder_id_ = decoder_id; + + // TODO(hclam): Initialize |param| with the right values. + GpuVideoDecoderInitParam param; + param.width = config_.width; + param.height = config_.height; + + if (!ipc_sender_->Send( + new GpuVideoDecoderMsg_Initialize(decoder_id, param))) { + LOG(ERROR) << "GpuVideoDecoderMsg_Initialize failed"; + event_handler_->OnError(); + } +} + void GpuVideoDecoderHost::OnInitializeDone( const GpuVideoDecoderInitDoneParam& param) { - done_param_ = param; - bool success = false; + DCHECK_EQ(message_loop_, MessageLoop::current()); - do { - if (!param.success) - break; + bool success = param.success && + base::SharedMemory::IsHandleValid(param.input_buffer_handle); - if (!base::SharedMemory::IsHandleValid(param.input_buffer_handle)) - break; + if (success) { input_transfer_buffer_.reset( new base::SharedMemory(param.input_buffer_handle, false)); - if (!input_transfer_buffer_->Map(param.input_buffer_size)) - break; - - success = true; - } while (0); - + success = input_transfer_buffer_->Map(param.input_buffer_size); + } state_ = success ? kStateNormal : kStateError; - media::VideoCodecInfo info; - info.success = success; // TODO(hclam): There's too many unnecessary copies for width and height! // Need to clean it up. // TODO(hclam): Need to fill in more information. + media::VideoCodecInfo info; + info.success = success; info.stream_info.surface_width = config_.width; info.stream_info.surface_height = config_.height; event_handler_->OnInitializeComplete(info); } void GpuVideoDecoderHost::OnUninitializeDone() { + DCHECK_EQ(message_loop_, MessageLoop::current()); + input_transfer_buffer_.reset(); + router_->RemoveRoute(decoder_host_id_); + context_->ReleaseAllVideoFrames(); event_handler_->OnUninitializeComplete(); } void GpuVideoDecoderHost::OnFlushDone() { + DCHECK_EQ(message_loop_, MessageLoop::current()); + state_ = kStateNormal; event_handler_->OnFlushComplete(); } -void GpuVideoDecoderHost::OnEmptyThisBufferDone() { +void GpuVideoDecoderHost::OnEmptyThisBufferACK() { + DCHECK_EQ(message_loop_, MessageLoop::current()); + + input_buffer_busy_ = false; + SendConsumeVideoSample(); +} + +void GpuVideoDecoderHost::OnProduceVideoSample() { + DCHECK_EQ(message_loop_, MessageLoop::current()); + DCHECK_EQ(kStateNormal, state_); + event_handler_->ProduceVideoSample(NULL); } void GpuVideoDecoderHost::OnConsumeVideoFrame(int32 frame_id, int64 timestamp, int64 duration, int32 flags) { - scoped_refptr<VideoFrame> frame; + DCHECK_EQ(message_loop_, MessageLoop::current()); + scoped_refptr<VideoFrame> frame; if (flags & kGpuVideoEndOfStream) { VideoFrame::CreateEmptyFrame(&frame); } else { - // TODO(hclam): Use |frame_id| to find the VideoFrame. + frame = video_frame_map_[frame_id]; + DCHECK(frame) << "Invalid frame ID received"; + + frame->SetDuration(base::TimeDelta::FromMilliseconds(duration)); + frame->SetTimestamp(base::TimeDelta::FromMilliseconds(timestamp)); } - // TODO(hclam): Call the event handler. event_handler_->ConsumeVideoFrame(frame); } -void GpuVideoDecoderHost::OnEmptyThisBufferACK() { - input_buffer_busy_ = false; - SendInputBufferToGpu(); +void GpuVideoDecoderHost::OnAllocateVideoFrames( + int32 n, uint32 width, uint32 height, int32 format) { + DCHECK_EQ(message_loop_, MessageLoop::current()); + DCHECK_EQ(0u, video_frames_.size()); + + context_->AllocateVideoFrames( + n, width, height, static_cast<media::VideoFrame::Format>(format), + &video_frames_, + NewRunnableMethod(this, + &GpuVideoDecoderHost::OnAllocateVideoFramesDone)); +} + +void GpuVideoDecoderHost::OnReleaseAllVideoFrames() { + DCHECK_EQ(message_loop_, MessageLoop::current()); + + context_->ReleaseAllVideoFrames(); + video_frame_map_.clear(); + video_frames_.clear(); +} + +void GpuVideoDecoderHost::OnAllocateVideoFramesDone() { + if (MessageLoop::current() != message_loop_) { + message_loop_->PostTask( + FROM_HERE, + NewRunnableMethod( + this, &GpuVideoDecoderHost::OnAllocateVideoFramesDone)); + return; + } + + // After video frame allocation is done we add these frames to a map and + // send them to the GPU process. + DCHECK(video_frames_.size()) << "No video frames allocated"; + for (size_t i = 0; i < video_frames_.size(); ++i) { + DCHECK(video_frames_[i]); + video_frame_map_.insert( + std::make_pair(current_frame_id_, video_frames_[i])); + SendVideoFrameAllocated(current_frame_id_, video_frames_[i]); + ++current_frame_id_; + } +} + +void GpuVideoDecoderHost::SendVideoFrameAllocated( + int32 frame_id, scoped_refptr<media::VideoFrame> frame) { + DCHECK_EQ(message_loop_, MessageLoop::current()); + + std::vector<uint32> textures; + for (size_t i = 0; i < frame->planes(); ++i) { + textures.push_back(frame->gl_texture(i)); + } + + if (!ipc_sender_->Send(new GpuVideoDecoderMsg_VideoFrameAllocated( + decoder_id_, frame_id, textures))) { + LOG(ERROR) << "GpuVideoDecoderMsg_EmptyThisBuffer failed"; + } } -void GpuVideoDecoderHost::SendInputBufferToGpu() { - if (input_buffer_busy_) return; - if (input_buffer_queue_.empty()) return; +void GpuVideoDecoderHost::SendConsumeVideoSample() { + DCHECK_EQ(message_loop_, MessageLoop::current()); + if (input_buffer_busy_ || input_buffer_queue_.empty()) + return; input_buffer_busy_ = true; - scoped_refptr<Buffer> buffer; - buffer = input_buffer_queue_.front(); + scoped_refptr<Buffer> buffer = input_buffer_queue_.front(); input_buffer_queue_.pop_front(); // Send input data to GPU process. @@ -216,8 +332,36 @@ void GpuVideoDecoderHost::SendInputBufferToGpu() { param.size = buffer->GetDataSize(); param.timestamp = buffer->GetTimestamp().InMicroseconds(); memcpy(input_transfer_buffer_->memory(), buffer->GetData(), param.size); - if (!ipc_sender_ || !ipc_sender_->Send( - new GpuVideoDecoderMsg_EmptyThisBuffer(route_id(), param))) { + + if (!ipc_sender_->Send( + new GpuVideoDecoderMsg_EmptyThisBuffer(decoder_id_, param))) { LOG(ERROR) << "GpuVideoDecoderMsg_EmptyThisBuffer failed"; } } + +void GpuVideoDecoderHost::SendProduceVideoFrame( + scoped_refptr<media::VideoFrame> frame) { + DCHECK_EQ(message_loop_, MessageLoop::current()); + + // TODO(hclam): I should mark a frame being used to DCHECK and make sure + // user doesn't use it the second time. + // TODO(hclam): Derive a faster way to lookup the frame ID. + bool found = false; + int32 frame_id = 0; + for (VideoFrameMap::iterator i = video_frame_map_.begin(); + i != video_frame_map_.end(); ++i) { + if (frame == i->second) { + frame_id = i->first; + found = true; + break; + } + } + + DCHECK(found) << "Invalid video frame received"; + if (found && !ipc_sender_->Send( + new GpuVideoDecoderMsg_ProduceVideoFrame(decoder_id_, frame_id))) { + LOG(ERROR) << "GpuVideoDecoderMsg_ProduceVideoFrame failed"; + } +} + +DISABLE_RUNNABLE_METHOD_REFCOUNT(GpuVideoDecoderHost); diff --git a/chrome/renderer/gpu_video_decoder_host.h b/chrome/renderer/gpu_video_decoder_host.h index 68953f6..e5a26fd 100644 --- a/chrome/renderer/gpu_video_decoder_host.h +++ b/chrome/renderer/gpu_video_decoder_host.h @@ -6,6 +6,7 @@ #define CHROME_RENDERER_GPU_VIDEO_DECODER_HOST_H_ #include <deque> +#include <map> #include "base/singleton.h" #include "chrome/common/gpu_video_common.h" @@ -18,7 +19,7 @@ using media::VideoFrame; using media::Buffer; -class GpuVideoServiceHost; +class MessageRouter; // This class is used to talk to GpuVideoDecoder in the GPU process through // IPC messages. It implements the interface of VideoDecodeEngine so users @@ -36,6 +37,10 @@ class GpuVideoServiceHost; class GpuVideoDecoderHost : public media::VideoDecodeEngine, public IPC::Channel::Listener { public: + GpuVideoDecoderHost(MessageRouter* router, + IPC::Message::Sender* ipc_sender, + int context_route_id, + int32 decoder_host_id); virtual ~GpuVideoDecoderHost() {} // IPC::Channel::Listener. @@ -55,7 +60,7 @@ class GpuVideoDecoderHost : public media::VideoDecodeEngine, virtual void Seek(); private: - friend class GpuVideoServiceHost; + typedef std::map<int32, scoped_refptr<media::VideoFrame> > VideoFrameMap; // Internal states. enum GpuVideoDecoderHostState { @@ -65,30 +70,38 @@ class GpuVideoDecoderHost : public media::VideoDecodeEngine, kStateFlushing, }; - // Private constructor. - GpuVideoDecoderHost(GpuVideoServiceHost* service_host, - IPC::Message::Sender* ipc_sender, - int context_route_id); - - // Input message handler. + // Handlers for messages received from the GPU process. + void OnCreateVideoDecoderDone(int32 decoder_id); void OnInitializeDone(const GpuVideoDecoderInitDoneParam& param); void OnUninitializeDone(); void OnFlushDone(); - void OnEmptyThisBufferDone(); + void OnEmptyThisBufferACK(); + void OnProduceVideoSample(); void OnConsumeVideoFrame(int32 frame_id, int64 timestamp, int64 duration, int32 flags); - void OnEmptyThisBufferACK(); + void OnAllocateVideoFrames(int32 n, uint32 width, + uint32 height, int32 format); + void OnReleaseAllVideoFrames(); + + // Handler for VideoDecodeContext. This method is called when video frames + // allocation is done. + void OnAllocateVideoFramesDone(); - // Helper function. - void SendInputBufferToGpu(); + // Send a message to the GPU process to inform that a video frame is + // allocated. + void SendVideoFrameAllocated(int32 frame_id, + scoped_refptr<media::VideoFrame> frame); - // Getter methods for IDs. - int32 decoder_id() { return decoder_info_.decoder_id; } - int32 route_id() { return decoder_info_.decoder_route_id; } - int32 my_route_id() { return decoder_info_.decoder_host_route_id; } + // Send a video sample to the GPU process and tell it to use the buffer for + // video decoding. + void SendConsumeVideoSample(); - // We expect that GpuVideoServiceHost's always available during our life span. - GpuVideoServiceHost* gpu_video_service_host_; + // Look up the frame_id for |frame| and send a message to the GPU process + // to use that video frame to produce an output. + void SendProduceVideoFrame(scoped_refptr<media::VideoFrame> frame); + + // A router used to send us IPC messages. + MessageRouter* router_; // Sends IPC messages to the GPU process. IPC::Message::Sender* ipc_sender_; @@ -96,24 +109,27 @@ class GpuVideoDecoderHost : public media::VideoDecodeEngine, // Route ID of the GLES2 context in the GPU process. int context_route_id_; + // Message loop that this object runs on. + MessageLoop* message_loop_; + // We expect that the client of us will always available during our life span. EventHandler* event_handler_; - // Globally identify this decoder in the GPU process. - GpuVideoDecoderInfoParam decoder_info_; - - // Input buffer id serial number generator. - int32 buffer_id_serial_; + // A Context for allocating video frame textures. + media::VideoDecodeContext* context_; // Hold information about GpuVideoDecoder configuration. media::VideoCodecConfig config_; - // Hold information about output surface format, etc. - GpuVideoDecoderInitDoneParam done_param_; - // Current state of video decoder. GpuVideoDecoderHostState state_; + // ID of this GpuVideoDecoderHost. + int32 decoder_host_id_; + + // ID of GpuVideoDecoder in the GPU process. + int32 decoder_id_; + // We are not able to push all received buffer to gpu process at once. std::deque<scoped_refptr<Buffer> > input_buffer_queue_; @@ -125,6 +141,15 @@ class GpuVideoDecoderHost : public media::VideoDecodeEngine, // TODO(jiesun): remove output buffer when hardware composition is ready. scoped_ptr<base::SharedMemory> input_transfer_buffer_; + // Frame ID for the newly generated video frame. + int32 current_frame_id_; + + // The list of video frames allocated by VideoDecodeContext. + std::vector<scoped_refptr<media::VideoFrame> > video_frames_; + + // The mapping between video frame ID and a video frame. + VideoFrameMap video_frame_map_; + DISALLOW_COPY_AND_ASSIGN(GpuVideoDecoderHost); }; diff --git a/chrome/renderer/gpu_video_decoder_host_unittest.cc b/chrome/renderer/gpu_video_decoder_host_unittest.cc new file mode 100644 index 0000000..c0a10be --- /dev/null +++ b/chrome/renderer/gpu_video_decoder_host_unittest.cc @@ -0,0 +1,259 @@ +// 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<media::VideoFrame> frame; + media::VideoFrame::CreateFrameGlTexture(media::VideoFrame::YV12, + kWidth, kHeight, textures, + base::TimeDelta(), + base::TimeDelta(), + &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_METHOD1(ConsumeVideoFrame, + void(scoped_refptr<media::VideoFrame> 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_->Create("", false, false, 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<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(); +} diff --git a/chrome/renderer/gpu_video_service_host.cc b/chrome/renderer/gpu_video_service_host.cc index 94803a4..d47e309 100644 --- a/chrome/renderer/gpu_video_service_host.cc +++ b/chrome/renderer/gpu_video_service_host.cc @@ -11,7 +11,8 @@ GpuVideoServiceHost::GpuVideoServiceHost() : channel_host_(NULL), router_(NULL), - message_loop_(NULL) { + message_loop_(NULL), + next_decoder_host_id_(0) { memset(&service_info_, 0, sizeof(service_info_)); } @@ -55,14 +56,9 @@ GpuVideoDecoderHost* GpuVideoServiceHost::CreateVideoDecoder( int context_route_id) { DCHECK(RenderThread::current()); - return new GpuVideoDecoderHost(this, channel_host_, context_route_id); -} - -void GpuVideoServiceHost::AddRoute(int route_id, - GpuVideoDecoderHost* decoder_host) { - router_->AddRoute(route_id, decoder_host); -} - -void GpuVideoServiceHost::RemoveRoute(int route_id) { - router_->RemoveRoute(route_id); + GpuVideoDecoderHost* host = new GpuVideoDecoderHost(router_, channel_host_, + context_route_id, + next_decoder_host_id_); + ++next_decoder_host_id_; + return host; } diff --git a/chrome/renderer/gpu_video_service_host.h b/chrome/renderer/gpu_video_service_host.h index e5417a4..ff54cd2 100644 --- a/chrome/renderer/gpu_video_service_host.h +++ b/chrome/renderer/gpu_video_service_host.h @@ -45,13 +45,6 @@ class GpuVideoServiceHost : public IPC::Channel::Listener, // Returns a GpuVideoDecoderHost as a handle to control the video decoder. GpuVideoDecoderHost* CreateVideoDecoder(int context_route_id); - // TODO(hclam): Provide a method to destroy the decoder. We also need to - // listen to context lost event. - - // Methods called by GpuVideoDecoderHost. - void AddRoute(int route_id, GpuVideoDecoderHost* decoder_host); - void RemoveRoute(int route_id); - private: GpuVideoServiceHost(); @@ -60,6 +53,9 @@ class GpuVideoServiceHost : public IPC::Channel::Listener, GpuVideoServiceInfoParam service_info_; MessageLoop* message_loop_; // Message loop of render thread. + // ID for the next GpuVideoDecoderHost. + int32 next_decoder_host_id_; + friend struct DefaultSingletonTraits<GpuVideoServiceHost>; DISALLOW_COPY_AND_ASSIGN(GpuVideoServiceHost); }; diff --git a/media/base/video_frame.h b/media/base/video_frame.h index 88c11dd..33d3779 100644 --- a/media/base/video_frame.h +++ b/media/base/video_frame.h @@ -83,6 +83,7 @@ class VideoFrame : public StreamSample { scoped_refptr<VideoFrame>* frame_out); // Creates a new frame with GL textures. + // TODO(hclam): Remove timestamp and duration. static void CreateFrameGlTexture(Format format, size_t width, size_t height, @@ -92,6 +93,7 @@ class VideoFrame : public StreamSample { scoped_refptr<VideoFrame>* frame_out); // Creates a new frame with D3d textures. + // TODO(hclam): Remove timestamp and duration. static void CreateFrameD3dTexture(Format format, size_t width, size_t height, diff --git a/media/video/mock_objects.h b/media/video/mock_objects.h index a975ea8..24cd4d3 100644 --- a/media/video/mock_objects.h +++ b/media/video/mock_objects.h @@ -5,6 +5,7 @@ #ifndef MEDIA_VIDEO_MOCK_OBJECTS_H_ #define MEDIA_VIDEO_MOCK_OBJECTS_H_ +#include "media/video/video_decode_context.h" #include "media/video/video_decode_engine.h" #include "testing/gmock/include/gmock/gmock.h" @@ -29,6 +30,25 @@ class MockVideoDecodeEngine : public VideoDecodeEngine { DISALLOW_COPY_AND_ASSIGN(MockVideoDecodeEngine); }; +class MockVideoDecodeContext : public VideoDecodeContext { + public: + MockVideoDecodeContext() {} + virtual ~MockVideoDecodeContext() {} + + MOCK_METHOD0(GetDevice, void*()); + MOCK_METHOD6(AllocateVideoFrames, void( + int n, size_t width, size_t height, VideoFrame::Format format, + std::vector<scoped_refptr<VideoFrame> >* frames, + Task* task)); + MOCK_METHOD0(ReleaseAllVideoFrames, void()); + MOCK_METHOD3(UploadToVideoFrame, void( + void* buffer, scoped_refptr<VideoFrame> frame, Task* task)); + MOCK_METHOD1(Destroy, void(Task* task)); + + private: + DISALLOW_COPY_AND_ASSIGN(MockVideoDecodeContext); +}; + } // namespace media #endif // MEDIA_VIDEO_MOCK_OBJECTS_H_ |