summaryrefslogtreecommitdiffstats
path: root/chrome/renderer
diff options
context:
space:
mode:
authorhclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-10-07 18:59:36 +0000
committerhclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-10-07 18:59:36 +0000
commit3b098bbef42fa93a2c17c96c888a65564191f767 (patch)
tree0509385a390e0ff9b348e96382c10074127319f6 /chrome/renderer
parentc7b7800afbf7aadb5c9f99c95209237cdf869678 (diff)
downloadchromium_src-3b098bbef42fa93a2c17c96c888a65564191f767.zip
chromium_src-3b098bbef42fa93a2c17c96c888a65564191f767.tar.gz
chromium_src-3b098bbef42fa93a2c17c96c888a65564191f767.tar.bz2
Implement GpuVideoDecoderHost and unit tests
Add the following feature to GpuVideoDecoderHost: 1. Video frame allocation / release. 2. ProduceVideoFrame / ConsumeVideoFrame using frames allocated. 3. Change GpuVideoDecoder creation to asynchronous. BUG=53714 TEST=unit_tests --gtest_filter=GpuVideoDecoder* Review URL: http://codereview.chromium.org/3397027 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@61824 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/renderer')
-rw-r--r--chrome/renderer/gpu_video_decoder_host.cc298
-rw-r--r--chrome/renderer/gpu_video_decoder_host.h77
-rw-r--r--chrome/renderer/gpu_video_decoder_host_unittest.cc259
-rw-r--r--chrome/renderer/gpu_video_service_host.cc18
-rw-r--r--chrome/renderer/gpu_video_service_host.h10
5 files changed, 541 insertions, 121 deletions
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);
};