diff options
author | jam@chromium.org <jam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-03-17 19:15:35 +0000 |
---|---|---|
committer | jam@chromium.org <jam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-03-17 19:15:35 +0000 |
commit | 6f516083864319feeb02ae4dc32609216455366b (patch) | |
tree | 285a57fee02c581e22d272951633d4a56a7846e6 /content/renderer/gpu_video_decoder_host.cc | |
parent | 6421fe84fee932545c65ab77b7dcdc60be28accc (diff) | |
download | chromium_src-6f516083864319feeb02ae4dc32609216455366b.zip chromium_src-6f516083864319feeb02ae4dc32609216455366b.tar.gz chromium_src-6f516083864319feeb02ae4dc32609216455366b.tar.bz2 |
Move a bunch of gpu/worker/plugin renderer code to content. I temporarily disabled the sad plugin code while I add a renderer chrome interface in a follow up.
TBR=avi
Review URL: http://codereview.chromium.org/6713005
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@78579 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content/renderer/gpu_video_decoder_host.cc')
-rw-r--r-- | content/renderer/gpu_video_decoder_host.cc | 399 |
1 files changed, 399 insertions, 0 deletions
diff --git a/content/renderer/gpu_video_decoder_host.cc b/content/renderer/gpu_video_decoder_host.cc new file mode 100644 index 0000000..315cd32 --- /dev/null +++ b/content/renderer/gpu_video_decoder_host.cc @@ -0,0 +1,399 @@ +// 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 "content/renderer/gpu_video_decoder_host.h" + +#include "content/common/gpu_messages.h" +#include "content/common/message_router.h" +#include "media/base/pipeline.h" +#include "media/video/video_decode_context.h" + +GpuVideoDecoderHost::GpuVideoDecoderHost(MessageRouter* router, + IPC::Message::Sender* ipc_sender, + 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), + context_(NULL), + width_(0), + height_(0), + state_(kStateUninitialized), + decoder_host_id_(decoder_host_id), + decoder_id_(0), + input_buffer_busy_(false), + current_frame_id_(0) { +} + +GpuVideoDecoderHost::~GpuVideoDecoderHost() {} + +void GpuVideoDecoderHost::OnChannelError() { + ipc_sender_ = NULL; +} + +bool GpuVideoDecoderHost::OnMessageReceived(const IPC::Message& msg) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(GpuVideoDecoderHost, msg) + IPC_MESSAGE_HANDLER(GpuVideoDecoderHostMsg_CreateVideoDecoderDone, + OnCreateVideoDecoderDone) + IPC_MESSAGE_HANDLER(GpuVideoDecoderHostMsg_InitializeACK, + OnInitializeDone) + IPC_MESSAGE_HANDLER(GpuVideoDecoderHostMsg_DestroyACK, + OnUninitializeDone) + IPC_MESSAGE_HANDLER(GpuVideoDecoderHostMsg_FlushACK, + OnFlushDone) + IPC_MESSAGE_HANDLER(GpuVideoDecoderHostMsg_PrerollDone, + OnPrerollDone) + IPC_MESSAGE_HANDLER(GpuVideoDecoderHostMsg_EmptyThisBufferACK, + OnEmptyThisBufferACK) + IPC_MESSAGE_HANDLER(GpuVideoDecoderHostMsg_EmptyThisBufferDone, + OnProduceVideoSample) + IPC_MESSAGE_HANDLER(GpuVideoDecoderHostMsg_ConsumeVideoFrame, + OnConsumeVideoFrame) + IPC_MESSAGE_HANDLER(GpuVideoDecoderHostMsg_AllocateVideoFrames, + OnAllocateVideoFrames) + IPC_MESSAGE_HANDLER(GpuVideoDecoderHostMsg_ReleaseAllVideoFrames, + OnReleaseAllVideoFrames) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + DCHECK(handled); + return handled; +} + +void GpuVideoDecoderHost::Initialize( + MessageLoop* message_loop, VideoDecodeEngine::EventHandler* event_handler, + media::VideoDecodeContext* context, const media::VideoCodecConfig& config) { + DCHECK_EQ(kStateUninitialized, state_); + DCHECK(!message_loop_); + message_loop_ = message_loop; + event_handler_ = event_handler; + context_ = context; + width_ = config.width(); + height_ = config.height(); + + if (MessageLoop::current() != message_loop) { + message_loop->PostTask( + FROM_HERE, + NewRunnableMethod(this, &GpuVideoDecoderHost::CreateVideoDecoder)); + return; + } + CreateVideoDecoder(); +} + +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); + + // We never own input buffers, therefore when client in flush state, it + // never call us with EmptyThisBuffer. + if (state_ != kStateNormal) + return; + + input_buffer_queue_.push_back(buffer); + 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); + + // 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; + + // Check that video frame is valid. + if (!frame || frame->format() == media::VideoFrame::EMPTY || + frame->IsEndOfStream()) { + return; + } + + SendProduceVideoFrame(frame); +} + +void GpuVideoDecoderHost::Uninitialize() { + if (MessageLoop::current() != message_loop_) { + message_loop_->PostTask( + FROM_HERE, + NewRunnableMethod(this, &GpuVideoDecoderHost::Uninitialize)); + 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_->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; +} + +void GpuVideoDecoderHost::Seek() { + if (MessageLoop::current() != message_loop_) { + message_loop_->PostTask( + FROM_HERE, NewRunnableMethod(this, &GpuVideoDecoderHost::Seek)); + return; + } + + if (!ipc_sender_->Send(new GpuVideoDecoderMsg_Preroll(decoder_id_))) { + LOG(ERROR) << "GpuVideoDecoderMsg_Preroll failed"; + event_handler_->OnError(); + return; + } +} + +void GpuVideoDecoderHost::CreateVideoDecoder() { + DCHECK_EQ(message_loop_, MessageLoop::current()); + + // Add the route so we'll receive messages. + router_->AddRoute(decoder_host_id_, this); + + if (!ipc_sender_->Send( + new GpuChannelMsg_CreateVideoDecoder(context_route_id_, + decoder_host_id_))) { + LOG(ERROR) << "GpuChannelMsg_CreateVideoDecoder failed"; + event_handler_->OnError(); + return; + } +} + +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 = width_; + param.height = 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) { + DCHECK_EQ(message_loop_, MessageLoop::current()); + + bool success = param.success && + base::SharedMemory::IsHandleValid(param.input_buffer_handle); + + if (success) { + input_transfer_buffer_.reset( + new base::SharedMemory(param.input_buffer_handle, false)); + success = input_transfer_buffer_->Map(param.input_buffer_size); + } + state_ = success ? kStateNormal : kStateError; + + // 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 = width_; + info.stream_info.surface_height = 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::OnPrerollDone() { + DCHECK_EQ(message_loop_, MessageLoop::current()); + + state_ = kStateNormal; + event_handler_->OnSeekComplete(); +} + +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) { + DCHECK_EQ(message_loop_, MessageLoop::current()); + + scoped_refptr<VideoFrame> frame; + if (flags & kGpuVideoEndOfStream) { + VideoFrame::CreateEmptyFrame(&frame); + } else { + frame = video_frame_map_[frame_id]; + DCHECK(frame) << "Invalid frame ID received"; + + frame->SetDuration(base::TimeDelta::FromMicroseconds(duration)); + frame->SetTimestamp(base::TimeDelta::FromMicroseconds(timestamp)); + } + + media::PipelineStatistics statistics; + // TODO(sjl): Fill in statistics. + + event_handler_->ConsumeVideoFrame(frame, statistics); +} + +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::SendConsumeVideoSample() { + DCHECK_EQ(message_loop_, MessageLoop::current()); + + if (input_buffer_busy_ || input_buffer_queue_.empty()) + return; + input_buffer_busy_ = true; + + scoped_refptr<Buffer> buffer = input_buffer_queue_.front(); + input_buffer_queue_.pop_front(); + + // Send input data to GPU process. + GpuVideoDecoderInputBufferParam param; + param.offset = 0; + param.size = buffer->GetDataSize(); + param.timestamp = buffer->GetTimestamp().InMicroseconds(); + memcpy(input_transfer_buffer_->memory(), buffer->GetData(), param.size); + + 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); |