diff options
-rw-r--r-- | chrome/chrome_tests.gypi | 31 | ||||
-rw-r--r-- | content/common/gpu_messages.h | 38 | ||||
-rw-r--r-- | content/gpu/gpu_video_decode_accelerator.cc | 211 | ||||
-rw-r--r-- | content/gpu/gpu_video_decode_accelerator.h | 83 | ||||
-rw-r--r-- | content/gpu/gpu_video_decode_accelerator_tester.cc | 897 | ||||
-rw-r--r-- | media/media.gyp | 1 | ||||
-rw-r--r-- | media/video/picture.cc | 72 | ||||
-rw-r--r-- | media/video/picture.h | 65 | ||||
-rw-r--r-- | media/video/video_decode_accelerator.cc | 15 | ||||
-rw-r--r-- | media/video/video_decode_accelerator.h | 237 | ||||
-rw-r--r-- | ppapi/c/dev/pp_video_dev.h | 13 | ||||
-rw-r--r-- | ppapi/ppapi_tests.gypi | 2 | ||||
-rw-r--r-- | ppapi/tests/test_video_decoder.cc | 46 | ||||
-rw-r--r-- | ppapi/tests/test_video_decoder.h | 34 | ||||
-rw-r--r-- | webkit/plugins/ppapi/ppb_video_decoder_impl.cc | 8 |
15 files changed, 1721 insertions, 32 deletions
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index 284b916..034ae4d 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -3294,6 +3294,37 @@ ], }, { + # Executable that contains tests that specifically test gpu video decoding + # features. Excludes for example command buffer implementation. + 'target_name': 'gpu_video_tests', + 'type': 'executable', + 'dependencies': [ + 'test_support_common', + '../base/base.gyp:base', + '../base/base.gyp:base_i18n', + '../base/base.gyp:test_support_base', + '../ipc/ipc.gyp:test_support_ipc', + '../skia/skia.gyp:skia', + '../testing/gtest.gyp:gtest', + '../testing/gmock.gyp:gmock', + ], + 'include_dirs': [ + '..', + ], + 'sources': [ + '<(DEPTH)/content/common/gpu_messages.h', + '<(DEPTH)/content/gpu/gpu_video_decode_accelerator.cc', + '<(DEPTH)/content/gpu/gpu_video_decode_accelerator.h', + '<(DEPTH)/content/gpu/gpu_video_decode_accelerator_tester.cc', + '<(DEPTH)/media/video/picture.cc', + '<(DEPTH)/media/video/picture.h', + '<(DEPTH)/media/video/video_decode_accelerator.cc', + '<(DEPTH)/media/video/video_decode_accelerator.h', + '<(DEPTH)/ui/gfx/size.cc', + '<(DEPTH)/ui/gfx/size.h', + ], + }, + { 'target_name': 'plugin_tests', 'type': 'executable', 'msvs_guid': 'A1CAA831-C507-4B2E-87F3-AEC63C9907F9', diff --git a/content/common/gpu_messages.h b/content/common/gpu_messages.h index 4e4d5c7..9ea9beb 100644 --- a/content/common/gpu_messages.h +++ b/content/common/gpu_messages.h @@ -65,13 +65,6 @@ IPC_STRUCT_BEGIN(GpuVideoDecoderFormatChangeParam) IPC_STRUCT_MEMBER(base::SharedMemoryHandle, input_buffer_handle) IPC_STRUCT_END() -IPC_STRUCT_BEGIN(AcceleratedVideoDecoderDecodeParam) - IPC_STRUCT_MEMBER(base::SharedMemoryHandle, input_buffer_handle) - IPC_STRUCT_MEMBER(int32, offset) - IPC_STRUCT_MEMBER(int32, size) - IPC_STRUCT_MEMBER(int32, flags) // Miscellaneous flag bit mask. -IPC_STRUCT_END() - #if defined(OS_MACOSX) IPC_STRUCT_BEGIN(GpuHostMsg_AcceleratedSurfaceSetIOSurface_Params) IPC_STRUCT_MEMBER(int32, renderer_id) @@ -535,27 +528,31 @@ IPC_SYNC_MESSAGE_CONTROL1_1(AcceleratedVideoDecoderMsg_GetConfigs, // Message to create the accelerated video decoder. IPC_SYNC_MESSAGE_CONTROL1_1(AcceleratedVideoDecoderMsg_Create, std::vector<uint32>, /* Config */ - int32) /* Decoder ID, 0 equals failure */ + int32) /* Decoder ID, -1 equals failure */ // Send input buffer for decoding. -IPC_MESSAGE_ROUTED4(AcceleratedVideoDecoderMsg_Decode, +IPC_MESSAGE_ROUTED3(AcceleratedVideoDecoderMsg_Decode, base::SharedMemoryHandle, /* input_buffer_handle */ int32, /* offset */ - int32, /* size */ - int32) /* flags */ + int32) /* size */ // Sent from Renderer process to the GPU process to give the texture IDs for // generated GL textures. -IPC_MESSAGE_ROUTED2(AcceleratedVideoDecoderMsg_AssignPictureBuffer, +IPC_MESSAGE_ROUTED3(AcceleratedVideoDecoderMsg_AssignPictureBuffer, int32, /* Picture buffer ID */ + base::SharedMemoryHandle, /* Pointer to sysmem output */ std::vector<uint32>) /* TextureIDs for pictures */ +// Send from Renderer process to the GPU process to recycle the given picture +// buffer for further decoding. IPC_MESSAGE_ROUTED1(AcceleratedVideoDecoderMsg_ReusePictureBuffer, int32) /* Picture buffer ID */ // Send flush request to the decoder. -IPC_MESSAGE_ROUTED1(AcceleratedVideoDecoderMsg_Flush, - int32) /* 0 for normal flush, 1 for abort flush */ +IPC_MESSAGE_ROUTED0(AcceleratedVideoDecoderMsg_Flush) + +// Send abort request to the decoder. +IPC_MESSAGE_ROUTED0(AcceleratedVideoDecoderMsg_Abort) // Destroy and release decoder asynchronously. IPC_SYNC_MESSAGE_CONTROL0_0(AcceleratedVideoDecoderMsg_Destroy) @@ -563,14 +560,16 @@ IPC_SYNC_MESSAGE_CONTROL0_0(AcceleratedVideoDecoderMsg_Destroy) //------------------------------------------------------------------------------ // Accelerated Video Decoder Host Messages // These messages are sent from GPU process to Renderer process. +// Inform AcceleratedVideoDecoderHost that AcceleratedVideoDecoder has been +// created. + // Accelerated video decoder has consumed input buffer from transfer buffer. -IPC_MESSAGE_ROUTED0(AcceleratedVideoDecoderHostMsg_BitstreamBufferProcessed) +IPC_MESSAGE_ROUTED1(AcceleratedVideoDecoderHostMsg_BitstreamBufferProcessed, + base::SharedMemoryHandle) /* Processed buffer handle */ // Allocate video frames for output of the hardware video decoder. -IPC_MESSAGE_ROUTED4(AcceleratedVideoDecoderHostMsg_ProvidePictureBuffers, +IPC_MESSAGE_ROUTED2(AcceleratedVideoDecoderHostMsg_ProvidePictureBuffers, int32, /* Number of video frames to generate */ - uint32, /* Width of the video frame */ - uint32, /* Height of the video frame */ std::vector<uint32>) /* Vector containing the dictionary for buffer config */ @@ -586,6 +585,9 @@ IPC_MESSAGE_ROUTED1(AcceleratedVideoDecoderHostMsg_PictureReady, // Confirm decoder has been flushed. IPC_MESSAGE_ROUTED0(AcceleratedVideoDecoderHostMsg_FlushDone) +// Confirm decoder has been aborted. +IPC_MESSAGE_ROUTED0(AcceleratedVideoDecoderHostMsg_AbortDone) + // Decoder has faced end of stream marker in the stream. IPC_MESSAGE_ROUTED0(AcceleratedVideoDecoderHostMsg_EndOfStream) diff --git a/content/gpu/gpu_video_decode_accelerator.cc b/content/gpu/gpu_video_decode_accelerator.cc new file mode 100644 index 0000000..cc1a53e --- /dev/null +++ b/content/gpu/gpu_video_decode_accelerator.cc @@ -0,0 +1,211 @@ +// 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/gpu/gpu_video_decode_accelerator.h" + +#include <vector> + +#include "base/shared_memory.h" +#include "content/common/gpu_messages.h" +#include "ipc/ipc_message_macros.h" +#include "ipc/ipc_message_utils.h" +#include "media/video/picture.h" +#include "ui/gfx/size.h" + +GpuVideoDecodeAccelerator::GpuVideoDecodeAccelerator( + IPC::Message::Sender* sender, + int32 host_route_id) + : sender_(sender), + route_id_(host_route_id), + cb_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { +} + +GpuVideoDecodeAccelerator::~GpuVideoDecodeAccelerator() {} + +bool GpuVideoDecodeAccelerator::OnMessageReceived(const IPC::Message& msg) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(GpuVideoDecodeAccelerator, msg) + IPC_MESSAGE_HANDLER(AcceleratedVideoDecoderMsg_GetConfigs, + OnGetConfigs) + IPC_MESSAGE_HANDLER(AcceleratedVideoDecoderMsg_Decode, + OnDecode) + IPC_MESSAGE_HANDLER(AcceleratedVideoDecoderMsg_AssignPictureBuffer, + OnAssignPictureBuffer) + IPC_MESSAGE_HANDLER(AcceleratedVideoDecoderMsg_ReusePictureBuffer, + OnReusePictureBuffer) + IPC_MESSAGE_HANDLER(AcceleratedVideoDecoderMsg_Flush, + OnFlush) + IPC_MESSAGE_HANDLER(AcceleratedVideoDecoderMsg_Abort, + OnAbort) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +void GpuVideoDecodeAccelerator::OnChannelConnected(int32 peer_pid) { + // TODO(vmr): Do we have to react on channel connections? +} + +void GpuVideoDecodeAccelerator::OnChannelError() { + // TODO(vmr): Do we have to react on channel errors? +} + +void GpuVideoDecodeAccelerator::ProvidePictureBuffers( + uint32 requested_num_of_buffers, + const std::vector<uint32>& buffer_properties) { + Send(new AcceleratedVideoDecoderHostMsg_ProvidePictureBuffers( + route_id_, + requested_num_of_buffers, + buffer_properties)); +} + +void GpuVideoDecodeAccelerator::DismissPictureBuffer( + media::VideoDecodeAccelerator::PictureBuffer* picture_buffer) { + DCHECK(picture_buffer); + // TODO(vmr): Unmap system memory here. + Send(new AcceleratedVideoDecoderHostMsg_DismissPictureBuffer( + route_id_, + picture_buffer->GetId())); +} + +void GpuVideoDecodeAccelerator::PictureReady( + media::VideoDecodeAccelerator::Picture* picture) { + Send(new AcceleratedVideoDecoderHostMsg_PictureReady( + route_id_, + picture->picture_buffer()->GetId())); +} + +void GpuVideoDecodeAccelerator::NotifyEndOfStream() { + Send(new AcceleratedVideoDecoderHostMsg_EndOfStream(route_id_)); +} + +void GpuVideoDecodeAccelerator::NotifyError( + media::VideoDecodeAccelerator::Error error) { + Send(new AcceleratedVideoDecoderHostMsg_ErrorNotification(route_id_, + error)); +} + +void GpuVideoDecodeAccelerator::OnGetConfigs(std::vector<uint32> config, + std::vector<uint32>* configs) { + DCHECK(configs); + *configs = video_decode_accelerator_->GetConfig(config); +} + +void GpuVideoDecodeAccelerator::OnCreate(std::vector<uint32> config, + int32* decoder_id) { +} + +void GpuVideoDecodeAccelerator::OnDecode(base::SharedMemoryHandle handle, + int32 offset, + int32 size) { + if (offset < 0 || size <= 0) { + NotifyError(media::VideoDecodeAccelerator::VIDEODECODERERROR_INVALIDINPUT); + return; + } + if (!base::SharedMemory::IsHandleValid(handle)) { + NotifyError(media::VideoDecodeAccelerator::VIDEODECODERERROR_MEMFAILURE); + return; + } + base::SharedMemory* shm = new base::SharedMemory(handle, true); + if (!shm || !shm->Map(offset + size)) { + NotifyError(media::VideoDecodeAccelerator::VIDEODECODERERROR_MEMFAILURE); + return; + } + // Set the freshly mapped memory address to the bitstream buffer. + uint8* mem_ptr = static_cast<uint8*>(shm->memory()); + media::BitstreamBuffer bitstream(mem_ptr + offset, size, NULL); + // Store the SHM in our FIFO queue. We need to do this before Decode because + // it is legal to call BitstreamBufferProcessed callback from the Decode + // context. + shm_in_.push_back(shm); + video_decode_accelerator_->Decode( + &bitstream, + cb_factory_.NewCallback( + &GpuVideoDecodeAccelerator::OnBitstreamBufferProcessed)); +} + +void GpuVideoDecodeAccelerator::OnAssignPictureBuffer( + int32 picture_buffer_id, + base::SharedMemoryHandle handle, + std::vector<uint32> textures) { + // TODO(vmr): Get the right size for picture buffers from config. + gfx::Size size(320, 240); + uint32 bits_per_pixel = 32; + media::VideoDecodeAccelerator::PictureBuffer::MemoryType memory_type; + std::vector<media::VideoDecodeAccelerator::PictureBuffer::DataPlaneHandle> + planes; + if (handle == base::SharedMemory::NULLHandle()) { + // TODO(vmr): Handle GLES textures here. + memory_type = media::VideoDecodeAccelerator::PictureBuffer:: + PICTUREBUFFER_MEMORYTYPE_GL_TEXTURE; + } else { + // Input buffer provided is in system memory in one plane. + memory_type = media::VideoDecodeAccelerator::PictureBuffer:: + PICTUREBUFFER_MEMORYTYPE_SYSTEM; + if (!base::SharedMemory::IsHandleValid(handle)) { + NotifyError(media::VideoDecodeAccelerator::VIDEODECODERERROR_MEMFAILURE); + return; + } + base::SharedMemory* shm = new base::SharedMemory(handle, true); + if (!shm || !shm->Map(size.width() * size.height() * bits_per_pixel)) { + NotifyError(media::VideoDecodeAccelerator::VIDEODECODERERROR_MEMFAILURE); + return; + } + media::VideoDecodeAccelerator::PictureBuffer::DataPlaneHandle sysmem_plane; + sysmem_plane.sysmem = shm; + planes.push_back(sysmem_plane); + } + std::vector<media::VideoDecodeAccelerator::PictureBuffer*> bufs; + std::vector<uint32> color_format; + media::VideoDecodeAccelerator::PictureBuffer* picture_buffer = + new media::PictureBuffer(picture_buffer_id, + size, + color_format, + memory_type, + planes); + bufs.push_back(picture_buffer); + video_decode_accelerator_->AssignPictureBuffer(bufs); +} + +void GpuVideoDecodeAccelerator::OnReusePictureBuffer( + int32 picture_buffer_id) { + // TODO(vmr): Get the picture buffer with the id. + media::VideoDecodeAccelerator::PictureBuffer* picture_buffer = NULL; + video_decode_accelerator_->ReusePictureBuffer(picture_buffer); +} + +void GpuVideoDecodeAccelerator::OnFlush() { + if (!video_decode_accelerator_->Flush(cb_factory_.NewCallback( + &GpuVideoDecodeAccelerator::OnFlushDone))) { + NotifyError( + media::VideoDecodeAccelerator::VIDEODECODERERROR_UNEXPECTED_FLUSH); + } +} + +void GpuVideoDecodeAccelerator::OnAbort() { + video_decode_accelerator_->Abort(cb_factory_.NewCallback( + &GpuVideoDecodeAccelerator::OnAbortDone)); +} + +void GpuVideoDecodeAccelerator::OnBitstreamBufferProcessed() { + base::SharedMemory* shm = shm_in_.front(); + DCHECK(shm); // Shared memory should always be non-NULL. + Send(new AcceleratedVideoDecoderHostMsg_BitstreamBufferProcessed( + 0, shm->handle())); + shm_in_.pop_front(); +} + +void GpuVideoDecodeAccelerator::OnFlushDone() { + Send(new AcceleratedVideoDecoderHostMsg_FlushDone(0)); +} + +void GpuVideoDecodeAccelerator::OnAbortDone() { + Send(new AcceleratedVideoDecoderHostMsg_AbortDone(0)); +} + +bool GpuVideoDecodeAccelerator::Send(IPC::Message* message) { + DCHECK(sender_); + return sender_->Send(message); +} + diff --git a/content/gpu/gpu_video_decode_accelerator.h b/content/gpu/gpu_video_decode_accelerator.h new file mode 100644 index 0000000..d06f6f1 --- /dev/null +++ b/content/gpu/gpu_video_decode_accelerator.h @@ -0,0 +1,83 @@ +// 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. + +#ifndef CONTENT_GPU_GPU_VIDEO_DECODE_ACCELERATOR_H_ +#define CONTENT_GPU_GPU_VIDEO_DECODE_ACCELERATOR_H_ + +#include <deque> +#include <vector> + +#include "base/memory/scoped_ptr.h" +#include "base/memory/scoped_callback_factory.h" +#include "base/shared_memory.h" +#include "ipc/ipc_channel.h" +#include "ipc/ipc_message.h" +#include "media/video/video_decode_accelerator.h" + +class GpuVideoDecodeAccelerator + : public base::RefCountedThreadSafe<GpuVideoDecodeAccelerator>, + public IPC::Channel::Listener, + public IPC::Message::Sender, + public media::VideoDecodeAccelerator::Client { + public: + GpuVideoDecodeAccelerator(IPC::Message::Sender* sender, int32 host_route_id); + virtual ~GpuVideoDecodeAccelerator(); + + // IPC::Channel::Listener implementation. + virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; + virtual void OnChannelConnected(int32 peer_pid) OVERRIDE; + virtual void OnChannelError() OVERRIDE; + + // media::VideoDecodeAccelerator::Client implementation. + virtual void ProvidePictureBuffers( + uint32 requested_num_of_buffers, + const std::vector<uint32>& buffer_properties) OVERRIDE; + virtual void DismissPictureBuffer( + media::VideoDecodeAccelerator::PictureBuffer* picture_buffer) OVERRIDE; + virtual void PictureReady( + media::VideoDecodeAccelerator::Picture* picture) OVERRIDE; + virtual void NotifyEndOfStream(); + virtual void NotifyError(media::VideoDecodeAccelerator::Error error); + + // Function to delegate sending to actual sender. + virtual bool Send(IPC::Message* message); + + void set_video_decode_accelerator( + media::VideoDecodeAccelerator* accelerator) { + video_decode_accelerator_ = accelerator; + } + + private: + // Handlers for IPC messages. + void OnGetConfigs(std::vector<uint32> config, std::vector<uint32>* configs); + void OnCreate(std::vector<uint32> config, int32* decoder_id); + void OnDecode(base::SharedMemoryHandle handle, int32 offset, int32 size); + void OnAssignPictureBuffer(int32 picture_buffer_id, + base::SharedMemoryHandle handle, + std::vector<uint32> texture_ids); + void OnReusePictureBuffer(int32 picture_buffer_id); + void OnFlush(); + void OnAbort(); + + // One-time callbacks from the accelerator. + void OnBitstreamBufferProcessed(); + void OnFlushDone(); + void OnAbortDone(); + + // Pointer to the IPC message sender. + IPC::Message::Sender* sender_; + // Route ID to communicate with the host. + int32 route_id_; + // Pointer to the underlying VideoDecodeAccelerator. + media::VideoDecodeAccelerator* video_decode_accelerator_; + // Callback factory to generate one-time callbacks. + base::ScopedCallbackFactory<GpuVideoDecodeAccelerator> cb_factory_; + // Container to hold shared memory blocks so that we can return the + // information about their consumption to renderer. + std::deque<base::SharedMemory*> shm_in_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(GpuVideoDecodeAccelerator); +}; + +#endif // CONTENT_GPU_GPU_VIDEO_DECODE_ACCELERATOR_H_ diff --git a/content/gpu/gpu_video_decode_accelerator_tester.cc b/content/gpu/gpu_video_decode_accelerator_tester.cc new file mode 100644 index 0000000..87a679d --- /dev/null +++ b/content/gpu/gpu_video_decode_accelerator_tester.cc @@ -0,0 +1,897 @@ +// 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. + +// TODO(vmr): Once done with the implementation, add conclusive documentation +// what you can do with this tester, how you can configure it and how +// you can extend it. + +#include <fstream> +#include <ios> +#include <iostream> +#include <new> +#include <vector> + +#include "base/at_exit.h" +#include "base/message_loop.h" +#include "base/process.h" +#include "base/memory/ref_counted.h" +#include "base/shared_memory.h" +#include "base/synchronization/waitable_event.h" +#include "base/threading/platform_thread.h" +#include "base/threading/thread.h" +#include "content/common/common_param_traits.h" +#include "content/common/gpu_messages.h" +#include "content/gpu/gpu_video_decode_accelerator.h" +#include "ipc/ipc_channel.h" +#include "ipc/ipc_message.h" +#include "ipc/ipc_message_utils.h" +#include "media/base/bitstream_buffer.h" +#include "media/video/picture.h" +#include "media/video/video_decode_accelerator.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::_; +using testing::AnyNumber; +using testing::Invoke; +using testing::InvokeArgument; +using testing::Matcher; +using testing::NiceMock; +using testing::Return; +using testing::ReturnRef; + +// Route ID. +static const int32 kRouteId = 99; + +class MockGpuChannel : public IPC::Message::Sender { + public: + MockGpuChannel() {} + virtual ~MockGpuChannel() {} + + // IPC::Message::Sender implementation. + MOCK_METHOD1(Send, bool(IPC::Message* msg)); + + private: + DISALLOW_COPY_AND_ASSIGN(MockGpuChannel); +}; + +class MockVideoDecodeAccelerator : public media::VideoDecodeAccelerator { + public: + MockVideoDecodeAccelerator() {} + virtual ~MockVideoDecodeAccelerator() {} + + MOCK_METHOD1(GetConfig, + const std::vector<uint32>& + (const std::vector<uint32>& prototype_config)); + MOCK_METHOD1(Initialize, bool(const std::vector<uint32>& config)); + MOCK_METHOD2(Decode, bool(media::BitstreamBuffer* bitstream_buffer, + media::VideoDecodeAcceleratorCallback* callback)); + MOCK_METHOD1(AssignPictureBuffer, + void(std::vector<media::VideoDecodeAccelerator::PictureBuffer*> + picture_buffers)); + MOCK_METHOD1(ReusePictureBuffer, + void(media::VideoDecodeAccelerator::PictureBuffer* + picture_buffer)); + MOCK_METHOD1(Flush, bool(media::VideoDecodeAcceleratorCallback* callback)); + MOCK_METHOD1(Abort, bool(media::VideoDecodeAcceleratorCallback* callback)); + + private: + DISALLOW_COPY_AND_ASSIGN(MockVideoDecodeAccelerator); +}; + +// Pull-based video source to read video data from a file. +// TODO(vmr): Make this less of a memory hog, Now reads whole file into mem in +// the beginning. Do this when it actually becomes a problem. +class TestVideoSource { + public: + TestVideoSource() + : file_length_(0), + offset_(0) {} + + ~TestVideoSource() {} + + bool Open(const std::string& url) { + // TODO(vmr): Use file_util::ReadFileToString or equivalent to read the file + // if one-shot reading is used. + scoped_ptr<std::ifstream> file; + file.reset( + new std::ifstream(url.c_str(), + std::ios::in | std::ios::binary | std::ios::ate)); + if (!file->good()) { + DLOG(ERROR) << "Failed to open file \"" << url << "\"."; + return false; + } + file->seekg(0, std::ios::end); + uint32 length = file->tellg(); + file->seekg(0, std::ios::beg); + mem_.reset(new uint8[length]); + DCHECK(mem_.get()); // Simply assumed to work on tester. + file->read(reinterpret_cast<char*>(mem_.get()), length); + file_length_ = length; + file->close(); + return true; + } + + // Reads next packet from the input stream. + // Returns number of read bytes on success, 0 on when there was no valid data + // to be read and -1 if user gave NULL or too small buffer. + // TODO(vmr): Modify to differentiate between errors and EOF. + int32 Read(uint8* target_mem, uint32 size) { + if (!target_mem) + return -1; + uint8* unit_begin = NULL; + uint8* unit_end = NULL; + uint8* ptr = mem_.get() + offset_; + while (offset_ + 4 < file_length_) { + if (ptr[0] == 0 && ptr[1] == 0 && ptr[2] == 0 && ptr[3] == 1) { + // start code found + if (!unit_begin) { + unit_begin = ptr; + } else { + // back-up 1 byte. + unit_end = ptr; + break; + } + } + ptr++; + offset_++; + } + if (unit_begin && offset_ + 4 == file_length_) { + // Last unit. Set the unit_end to point to the last byte. + unit_end = ptr + 4; + offset_ += 4; + } else if (!unit_begin || !unit_end) { + // No unit start codes found in buffer. + return 0; + } + if (static_cast<int32>(size) >= unit_end - unit_begin) { + memcpy(target_mem, unit_begin, unit_end - unit_begin); + return unit_end - unit_begin; + } + // Rewind to the beginning start code if there is one as it should be + // returned with next Read(). + offset_ = unit_begin - mem_.get(); + return -1; + } + + private: + uint32 file_length_; + uint32 offset_; + scoped_array<uint8> mem_; +}; + +// Class for posting QuitTask to other message loop when observed message loop +// is quitting. Observer must be added to the message loop by calling +// AddDestructionObserver on the message loop to be tracked. +class QuitObserver + : public MessageLoop::DestructionObserver { + public: + explicit QuitObserver(MessageLoop* loop_to_quit) + : loop_to_quit_(loop_to_quit) { + } + ~QuitObserver() {} + + void WillDestroyCurrentMessageLoop() { + loop_to_quit_->PostTask(FROM_HERE, new MessageLoop::QuitTask()); + } + + protected: + MessageLoop* loop_to_quit_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(QuitObserver); +}; + +// FakeClient is a class that mimics normal operation from the client +// process perspective. Underlying code will be receiving IPC commands from the +// FakeClient and sending IPC commands back to the FakeClient just as the +// underlying code would do with actual process running on top of it. +class FakeClient + : public base::RefCountedThreadSafe<FakeClient>, + public IPC::Message::Sender, + public base::PlatformThread::Delegate { + public: + FakeClient(MessageLoop* message_loop_for_quit, + IPC::Channel::Listener* gpu_video_decode_accelerator_, + std::string video_source_filename) + : fake_gpu_process_(gpu_video_decode_accelerator_), + message_loop_for_quit_(message_loop_for_quit), + test_video_source_(), + assigned_buffer_count_(0), + end_of_stream_(false), + error_(false), + thread_initialized_event_(true, false), // Manual reset & unsignalled. + video_source_filename_(video_source_filename) { + // Start-up the thread for processing incoming IPC from GpuVideoDecoder and + // wait until it has finished setting up the message loop. + base::PlatformThread::Create(0, this, &thread_handle_); + while (!thread_initialized_event_.Wait()) { + // Do nothing but wait. + }; + + // Create 1Mb of shared memory. + shm_.reset(new base::SharedMemory()); + CHECK(shm_->CreateAndMapAnonymous(1024 * 1024)); + } + + virtual ~FakeClient() { + DCHECK_EQ(assigned_buffer_count_, 0); + } + + bool DispatchFirstDecode() { + if (!test_video_source_.Open(video_source_filename_)) { + return false; + } + message_loop_->PostTask( + FROM_HERE, + NewRunnableMethod(this, &FakeClient::OnBitstreamBufferProcessed, + base::SharedMemory::NULLHandle())); + return true; + } + + // PlatformThread::Delegate implementation, i.e. the thread where the + // GpuVideoDecoder IPC handling executes. + virtual void ThreadMain() { + message_loop_.reset(new MessageLoop()); + // Set the test done observer to observe when client is done. + test_done_observer_.reset(new QuitObserver(message_loop_for_quit_)); + message_loop_->AddDestructionObserver(test_done_observer_.get()); + thread_initialized_event_.Signal(); + message_loop_->Run(); + message_loop_.reset(); // Destroy the message_loop_. + } + + // IPC::Message::Sender implementation. + virtual bool Send(IPC::Message* msg) { + // Dispatch the message loops to the single message loop which we want to + // execute all the simulated IPC. + if (MessageLoop::current() != message_loop_) { + message_loop_->PostTask( + FROM_HERE, + NewRunnableMethod(this, + &FakeClient::Send, + msg)); + return true; + } + // This execution should happen in the context of our fake GPU thread. + CHECK(msg); + LogMessage(msg); + IPC_BEGIN_MESSAGE_MAP(FakeClient, *msg) + IPC_MESSAGE_HANDLER( + AcceleratedVideoDecoderHostMsg_BitstreamBufferProcessed, + OnBitstreamBufferProcessed) + IPC_MESSAGE_HANDLER(AcceleratedVideoDecoderHostMsg_ProvidePictureBuffers, + OnProvidePictureBuffers) + IPC_MESSAGE_HANDLER(AcceleratedVideoDecoderHostMsg_DismissPictureBuffer, + OnDismissPictureBuffer) + IPC_MESSAGE_HANDLER(AcceleratedVideoDecoderHostMsg_PictureReady, + OnPictureReady) + IPC_MESSAGE_HANDLER(AcceleratedVideoDecoderHostMsg_FlushDone, + OnFlushDone) + IPC_MESSAGE_HANDLER(AcceleratedVideoDecoderHostMsg_AbortDone, + OnAbortDone) + IPC_MESSAGE_HANDLER(AcceleratedVideoDecoderHostMsg_EndOfStream, + OnEndOfStream) + IPC_MESSAGE_HANDLER(AcceleratedVideoDecoderHostMsg_ErrorNotification, + OnError) + IPC_MESSAGE_UNHANDLED_ERROR() + IPC_END_MESSAGE_MAP() + delete msg; + return true; + } + + virtual void OnBitstreamBufferProcessed(base::SharedMemoryHandle handle) { + // No action on end of stream. + if (end_of_stream_) + return; + uint32 read = test_video_source_.Read( + reinterpret_cast<uint8*>(shm_->memory()), + shm_->created_size()); + if (read > 0) { + AcceleratedVideoDecoderMsg_Decode msg(kRouteId, + shm_->handle(), + 0, + read); + fake_gpu_process_->OnMessageReceived(msg); + } else if (read == 0) { + // If no more data, flush to get the rest out. + end_of_stream_ = true; + AcceleratedVideoDecoderMsg_Flush msg(kRouteId); + fake_gpu_process_->OnMessageReceived(msg); + } else { + // Error. Let's flush and abort. + error_ = true; + AcceleratedVideoDecoderMsg_Flush msg(kRouteId); + fake_gpu_process_->OnMessageReceived(msg); + } + } + + virtual void OnProvidePictureBuffers(int32 num_frames, + std::vector<uint32> config) { + // Allocate and assign the picture buffers. + std::vector<uint32> textures; + // TODO(vmr): Get from config. + uint32 width = 320; + uint32 height = 240; + uint32 bits_per_pixel = 32; + for (int32 i = 0; i < num_frames; i++) { + // Create the shared memory, send it and store it into our local map. + base::SharedMemory* shm = new base::SharedMemory(); + CHECK(shm->CreateAnonymous(width * height * bits_per_pixel / 8)); + shm_map_[next_picture_buffer_id_] = shm; + AcceleratedVideoDecoderMsg_AssignPictureBuffer msg( + kRouteId, + next_picture_buffer_id_, + shm->handle(), + textures); + fake_gpu_process_->OnMessageReceived(msg); + next_picture_buffer_id_++; + assigned_buffer_count_++; + } + } + + virtual void OnDismissPictureBuffer(int32 picture_buffer_id) { + // Free previously allocated buffers. + base::SharedMemory* shm = shm_map_[picture_buffer_id]; + shm_map_.erase(picture_buffer_id); + delete shm; // Will also close shared memory. + assigned_buffer_count_--; + } + + virtual void OnPictureReady(int32 picture_buffer_id) { + // Process & recycle picture buffer. + AcceleratedVideoDecoderMsg_ReusePictureBuffer msg(kRouteId, + picture_buffer_id); + fake_gpu_process_->OnMessageReceived(msg); + } + + virtual void OnFlushDone() { + // TODO(vmr): Check that we had been actually flushing. + if (end_of_stream_ || error_) { + // Send the final Abort request. + AcceleratedVideoDecoderMsg_Abort msg(kRouteId); + fake_gpu_process_->OnMessageReceived(msg); + } + } + + virtual void OnAbortDone() { + // Done aborting... case over. + message_loop_->QuitNow(); + } + + virtual void OnEndOfStream() { + end_of_stream_ = true; + AcceleratedVideoDecoderMsg_Flush msg(kRouteId); + fake_gpu_process_->OnMessageReceived(msg); + } + + virtual void OnError(uint32 error_id) { + // Send the final Abort request. + AcceleratedVideoDecoderMsg_Abort msg(kRouteId); + fake_gpu_process_->OnMessageReceived(msg); + } + + private: + // TODO(vmr): Remove redundant logging for IPC calls with proper Chromium + // logging facilities. + void LogMessage(IPC::Message* msg) { + switch (msg->type()) { + case AcceleratedVideoDecoderHostMsg_BitstreamBufferProcessed::ID: + DLOG(INFO) << "CLIENT << " + "AcceleratedVideoDecoderHostMsg_BitstreamBufferProcessed"; + break; + case AcceleratedVideoDecoderHostMsg_ProvidePictureBuffers::ID: + DLOG(INFO) << "CLIENT << " + "AcceleratedVideoDecoderHostMsg_ProvidePictureBuffers"; + break; + case AcceleratedVideoDecoderHostMsg_DismissPictureBuffer::ID: + DLOG(INFO) << "CLIENT << " + "AcceleratedVideoDecoderHostMsg_DismissPictureBuffer"; + break; + case AcceleratedVideoDecoderHostMsg_PictureReady::ID: + DLOG(INFO) << "CLIENT << AcceleratedVideoDecoderHostMsg_PictureReady"; + break; + case AcceleratedVideoDecoderHostMsg_FlushDone::ID: + DLOG(INFO) << "CLIENT << AcceleratedVideoDecoderHostMsg_FlushDone"; + break; + case AcceleratedVideoDecoderHostMsg_AbortDone::ID: + DLOG(INFO) << "CLIENT << AcceleratedVideoDecoderHostMsg_AbortDone"; + break; + case AcceleratedVideoDecoderHostMsg_EndOfStream::ID: + DLOG(INFO) << "CLIENT << AcceleratedVideoDecoderHostMsg_EndOfStream"; + break; + case AcceleratedVideoDecoderHostMsg_ErrorNotification::ID: + DLOG(INFO) << "CLIENT << " + "AcceleratedVideoDecoderHostMsg_ErrorNotification"; + break; + default: + DLOG(INFO) << "CLIENT << UNKNOWN MESSAGE"; + break; + } + } + + // Listener which should receive the messages we decide to generate. + IPC::Channel::Listener* fake_gpu_process_; + // Message loop into which we want to post any send messages. + scoped_ptr<MessageLoop> message_loop_; + // Message loop into which we want to post quit when we're done. + MessageLoop* message_loop_for_quit_; + // Test video source to read our data from. + TestVideoSource test_video_source_; + // SharedMemory used for input buffers. + scoped_ptr<base::SharedMemory> shm_; + // Incremental picture buffer id. + uint32 next_picture_buffer_id_; + // Counter to count assigned buffers. + int32 assigned_buffer_count_; + // Counter to count locked buffers. + uint32 locked_buffer_count_; + // Flag to determine whether we have received end of stream from decoder. + bool end_of_stream_; + // Flag to determine whether we have faced an error. + bool error_; + // Event to determine the initialization state of the fake client thread. + base::WaitableEvent thread_initialized_event_; + // We own the observer used to track messageloop destruction. + scoped_ptr<QuitObserver> test_done_observer_; + // Handle to the thread. + base::PlatformThreadHandle thread_handle_; + // File name for video. + std::string video_source_filename_; + // Map to hold the picture buffer objects we create. + std::map<int32, base::SharedMemory*> shm_map_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(FakeClient); +}; + +// Class to fake the regular behaviour of video decode accelerator. +class FakeVideoDecodeAccelerator : public media::VideoDecodeAccelerator { + public: + explicit FakeVideoDecodeAccelerator( + media::VideoDecodeAccelerator::Client* client) + : state_(IDLE), + end_of_stream_(false), + client_(client), + output_counter_(0) { + } + virtual ~FakeVideoDecodeAccelerator() {} + + virtual const std::vector<uint32>& GetConfig( + const std::vector<uint32>& prototype_config) { + return config_; + } + + virtual bool Initialize(const std::vector<uint32>& config) { + return config_ == config; + } + + // |bitstream_buffer| is owned by client and client guarantees it will not + // release it before |callback| is called. + virtual bool Decode(media::BitstreamBuffer* bitstream_buffer, + media::VideoDecodeAcceleratorCallback* callback) { + if (assigned_picture_buffers_.empty()) { + std::vector<uint32> cfg; + bitstream_buffer_cb_.reset(callback); + client_->ProvidePictureBuffers(2, cfg); + return true; + } + // Simulate the bitstream processed callback. + if (!end_of_stream_) { + callback->Run(); + delete callback; + } + // Then call the picture ready. + // TODO(vmr): Add container for locked picture buffers. + if (output_counter_ > 300) { + if (!end_of_stream_) { + client_->NotifyEndOfStream(); + } + end_of_stream_ = true; + return true; + } + media::PictureBuffer* picture_buffer = + reinterpret_cast<media::PictureBuffer*>(assigned_picture_buffers_.at( + output_counter_ % assigned_picture_buffers_.size())); + // TODO(vmr): Get the real values. + gfx::Size size(320, 240); + std::vector<uint32> color_format; + // TODO(vmr): Add the correct mechanism to recycle buffers from assigned to + // locked and back. + client_->PictureReady(new media::Picture(picture_buffer, size, size, NULL)); + output_counter_++; + return true; + } + + virtual void AssignPictureBuffer( + std::vector<media::VideoDecodeAccelerator::PictureBuffer*> + picture_buffers) { + assigned_picture_buffers_.insert( + assigned_picture_buffers_.begin(), + picture_buffers.begin(), + picture_buffers.end()); + + if (EnoughPictureBuffers()) { + ChangeState(OPERATIONAL); + bitstream_buffer_cb_->Run(); + bitstream_buffer_cb_.reset(); + } + } + + virtual void ReusePictureBuffer( + media::VideoDecodeAccelerator::PictureBuffer* picture_buffer) { + // TODO(vmr): Move the picture buffer from locked picture buffer container + // to the assigned picture buffer container. + } + + bool Flush(media::VideoDecodeAcceleratorCallback* callback) { + scoped_ptr<media::VideoDecodeAcceleratorCallback> flush_cb(callback); + if (state_ != OPERATIONAL) { + // Flush request is accepted only in OPERATIONAL state. + return false; + } + ChangeState(FLUSHING); + ChangeState(OPERATIONAL); + flush_cb->Run(); + return true; + } + + bool Abort(media::VideoDecodeAcceleratorCallback* callback) { + scoped_ptr<media::VideoDecodeAcceleratorCallback> abort_cb(callback); + if (state_ == UNINITIALIZED || state_ == ABORTING) { + // Flush requested accepted in all other states. + return false; + } + ChangeState(ABORTING); + // Stop the component here. + // As buffers are released callback for each buffer DismissBuffer. + while (!assigned_picture_buffers_.empty()) { + client_->DismissPictureBuffer(assigned_picture_buffers_.back()); + assigned_picture_buffers_.pop_back(); + } + ChangeState(IDLE); + abort_cb->Run(); + return true; + } + + private: + enum DecodingState { + UNINITIALIZED, // Component has not been configured. + IDLE, // Component has been initialized but does not have needed resources. + OPERATIONAL, // Component is operational with all resources assigned. + FLUSHING, // Component is flushing. + ABORTING, // Component is aborting. + } state_; + + static const char* kStateString[5]; + + void ChangeState(DecodingState new_state) { + DLOG(INFO) << "VideoDecodeAccelerator state change: " + << kStateString[state_] << " => " << kStateString[new_state]; + state_ = new_state; + } + + bool EnoughPictureBuffers() { + return assigned_picture_buffers_.size() >= 2; + } + + bool end_of_stream_; + std::vector<uint32> config_; + std::vector<media::VideoDecodeAccelerator::PictureBuffer*> + assigned_picture_buffers_; + std::vector<media::VideoDecodeAccelerator::PictureBuffer*> + locked_picture_buffers; + media::VideoDecodeAccelerator::Client* client_; + scoped_ptr<media::VideoDecodeAcceleratorCallback> bitstream_buffer_cb_; + uint32 output_counter_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(FakeVideoDecodeAccelerator); +}; + +const char* FakeVideoDecodeAccelerator::kStateString[5] = { + "UNINITIALIZED", + "IDLE", + "OPERATIONAL", + "FLUSHING", + "ABORTING", +}; + +// Class to simulate the GpuThread. It will simply simulate the listener +// interface to GPU video decoder by making sure they get ran in our simulated +// GpuThread's context. The thread where loop is created will be used to +// simulate the GpuThread in the actual gpu process environment. User is +// responsible for calling the Run() when it is ready to start processing the +// input messages. Calling Run() will block until QuitTask has been posted to +// the message loop. +class FakeGpuThread + : public base::RefCountedThreadSafe<FakeGpuThread>, + public IPC::Channel::Listener { + public: + explicit FakeGpuThread(IPC::Channel::Listener* listener) + : listener_(listener) { + message_loop_.reset(new MessageLoop()); + } + + // Run will run the message loop. + void Run() { + message_loop_->Run(); + } + // Returns pointer to the message loop this class has initialized. + MessageLoop* message_loop() { + return message_loop_.get(); + } + + // IPC::Channel::Listener implementation. + bool OnMessageReceived(const IPC::Message& message) { + // Dispatch the message loops to the single message loop which we want to + // execute all the simulated IPC. + if (MessageLoop::current() != message_loop_.get()) { + message_loop_->PostTask( + FROM_HERE, + NewRunnableMethod( + this, + &FakeGpuThread::OnMessageReceived, + message)); + return true; + } + LogMessage(message); + return listener_->OnMessageReceived(message); + } + void OnChannelConnected(int32 peer_pid) { + // Dispatch the message loops to the single message loop which we want to + // execute all the simulated IPC. + if (MessageLoop::current() != message_loop_.get()) { + message_loop_->PostTask( + FROM_HERE, + NewRunnableMethod( + this, + &FakeGpuThread::OnChannelConnected, + peer_pid)); + return; + } + listener_->OnChannelConnected(peer_pid); + } + void OnChannelError() { + // Dispatch the message loops to the single message loop which we want to + // execute all the simulated IPC. + if (MessageLoop::current() != message_loop_.get()) { + message_loop_->PostTask( + FROM_HERE, + NewRunnableMethod( + this, + &FakeGpuThread::OnChannelError)); + return; + } + listener_->OnChannelError(); + } + + private: + // TODO(vmr): Use proper Chrome IPC logging instead. + void LogMessage(const IPC::Message& msg) { + switch (msg.type()) { + case AcceleratedVideoDecoderMsg_GetConfigs::ID: + DLOG(INFO) << "GPU << AcceleratedVideoDecoderMsg_GetConfigs"; + break; + case AcceleratedVideoDecoderMsg_Create::ID: + DLOG(INFO) << "GPU << AcceleratedVideoDecoderMsg_Create"; + break; + case AcceleratedVideoDecoderMsg_Decode::ID: + DLOG(INFO) << "GPU << AcceleratedVideoDecoderMsg_Decode"; + break; + case AcceleratedVideoDecoderMsg_AssignPictureBuffer::ID: + DLOG(INFO) << "GPU << AcceleratedVideoDecoderMsg_AssignPictureBuffer"; + break; + case AcceleratedVideoDecoderMsg_ReusePictureBuffer::ID: + DLOG(INFO) << "GPU << AcceleratedVideoDecoderMsg_ReusePictureBuffer"; + break; + case AcceleratedVideoDecoderMsg_Flush::ID: + DLOG(INFO) << "GPU << AcceleratedVideoDecoderMsg_Flush"; + break; + case AcceleratedVideoDecoderMsg_Abort::ID: + DLOG(INFO) << "GPU << AcceleratedVideoDecoderMsg_Abort"; + break; + case AcceleratedVideoDecoderMsg_Destroy::ID: + DLOG(INFO) << "GPU << AcceleratedVideoDecoderMsg_Destroy"; + break; + default: + DLOG(INFO) << "GPU << UNKNOWN MESSAGE"; + break; + } + } + + // Pointer to the listener where the messages are to be dispatched. + IPC::Channel::Listener* listener_; + // MessageLoop where the GpuVideoDecodeAccelerator execution is supposed to + // run in. + scoped_ptr<MessageLoop> message_loop_; + + // To make sure that only scoped_refptr can release us. + friend class base::RefCountedThreadSafe<FakeGpuThread>; + virtual ~FakeGpuThread() {} +}; + +// GpuVideoDecodeAcceleratorTest is the main test class that owns all the other +// objects and does global setup and teardown. +class GpuVideoDecodeAcceleratorTest + : public ::testing::TestWithParam<const char*>, + public IPC::Channel::Listener { + public: + GpuVideoDecodeAcceleratorTest() {} + virtual ~GpuVideoDecodeAcceleratorTest() {} + + // Functions for GUnit test fixture setups. + void SetUp() { + // Sets up the message loop pretending the fake client. + SetUpFakeClientAndGpuThread(); + + // Initialize the GPU video decode accelerator with the mock underlying + // accelerator. + gpu_video_decode_accelerator_ = + new GpuVideoDecodeAccelerator(fake_client_.get(), kRouteId); + gpu_video_decode_accelerator_->set_video_decode_accelerator( + &mock_video_decode_accelerator_); + // Set-up the underlying video decode accelerator. + video_decode_accelerator_.reset( + new FakeVideoDecodeAccelerator(gpu_video_decode_accelerator_.get())); + std::vector<uint32> config; + video_decode_accelerator_->Initialize(config); + + // Set up the default mock behaviour. + SetUpDefaultMockGpuChannelDelegation(); + SetUpMockVideoDecodeAcceleratorDelegation(); + } + + void Teardown() {} + + void SetUpFakeClientAndGpuThread() { + // Create the message loop for the IO thread (current thread). + // Also implements passing channel messages automatically to this thread. + fake_gpu_thread_ = new FakeGpuThread(this); + + // Initialize the fake client to inform our io message loop and use the + // fresh FakeGpuThread object to send the simulated IPC to the decoder. + fake_client_ = new FakeClient( + fake_gpu_thread_->message_loop(), fake_gpu_thread_.get(), + GetParam()); + } + + void SetUpDefaultMockGpuChannelDelegation() { + // Set the MockGpuChannel to call by default always the FakeClient when + // anything is called from the GpuChannel. This will simulate default flow + // of the video decoding. + ON_CALL(mock_gpu_channel_, Send(_)) + .WillByDefault(Invoke(fake_client_.get(), &FakeClient::Send)); + } + + void SetUpMockVideoDecodeAcceleratorDelegation() { + // Set the MockVideoDecodeAccelerator to call by the used decode + // accelerator. + // Default builds against fake. + ON_CALL(mock_video_decode_accelerator_, GetConfig(_)) + .WillByDefault(Invoke(video_decode_accelerator_.get(), + &FakeVideoDecodeAccelerator::GetConfig)); + ON_CALL(mock_video_decode_accelerator_, Initialize(_)) + .WillByDefault(Invoke(video_decode_accelerator_.get(), + &FakeVideoDecodeAccelerator::Initialize)); + ON_CALL(mock_video_decode_accelerator_, Decode(_, _)) + .WillByDefault(Invoke(video_decode_accelerator_.get(), + &FakeVideoDecodeAccelerator::Decode)); + ON_CALL(mock_video_decode_accelerator_, AssignPictureBuffer(_)) + .WillByDefault(Invoke(video_decode_accelerator_.get(), + &FakeVideoDecodeAccelerator::AssignPictureBuffer)); + ON_CALL(mock_video_decode_accelerator_, ReusePictureBuffer(_)) + .WillByDefault(Invoke(video_decode_accelerator_.get(), + &FakeVideoDecodeAccelerator::ReusePictureBuffer)); + ON_CALL(mock_video_decode_accelerator_, Flush(_)) + .WillByDefault(Invoke(video_decode_accelerator_.get(), + &FakeVideoDecodeAccelerator::Flush)); + ON_CALL(mock_video_decode_accelerator_, Abort(_)) + .WillByDefault(Invoke(video_decode_accelerator_.get(), + &FakeVideoDecodeAccelerator::Abort)); + } + + // IPC::Channel::Listener implementation. + bool OnMessageReceived(const IPC::Message& message) { + DCHECK(gpu_video_decode_accelerator_.get()); + return gpu_video_decode_accelerator_->OnMessageReceived(message); + } + void OnChannelConnected(int32 peer_pid) { + DCHECK(gpu_video_decode_accelerator_.get()); + gpu_video_decode_accelerator_->OnChannelConnected(peer_pid); + } + void OnChannelError() { + DCHECK(gpu_video_decode_accelerator_.get()); + gpu_video_decode_accelerator_->OnChannelError(); + } + + // Threading related functions. + void RunMessageLoop() { + fake_gpu_thread_->Run(); + } + + protected: + // We need exit manager to please the message loops we're creating. + base::AtExitManager exit_manager_; + // Mock and fake delegate for the IPC channel. + NiceMock<MockGpuChannel> mock_gpu_channel_; + // Reference counted pointer to the gpu channel. + scoped_refptr<FakeClient> fake_client_; + // Reference counted pointer to the fake gpu channel. + scoped_refptr<FakeGpuThread> fake_gpu_thread_; + // Handle to the initialized GpuVideoDecodeAccelerator. Tester owns the + // GpuVideoDecodeAccelerator. + scoped_refptr<GpuVideoDecodeAccelerator> gpu_video_decode_accelerator_; + // Mock and default fake delegate for the underlying video decode accelerator. + NiceMock<MockVideoDecodeAccelerator> mock_video_decode_accelerator_; + scoped_ptr<FakeVideoDecodeAccelerator> video_decode_accelerator_; +}; + +static uint32 kTestH264BitstreamConfig[] = { + // Intentionally breaking formatting rules to make things more readable. + media::VIDEOATTRIBUTEKEY_BITSTREAMFORMAT_FOURCC, + media::VIDEOCODECFOURCC_H264, + media::VIDEOATTRIBUTEKEY_BITSTREAMFORMAT_H264_PROFILE, + media::H264PROFILE_BASELINE, + media::VIDEOATTRIBUTEKEY_BITSTREAMFORMAT_H264_LEVEL, + media::H264LEVEL_30, + media::VIDEOATTRIBUTEKEY_BITSTREAMFORMAT_H264_PAYLOADFORMAT, + media::H264PAYLOADFORMAT_BYTESTREAM, + media::VIDEOATTRIBUTEKEY_TERMINATOR +}; + +static uint32 kTestH264BitstreamConfigCount = + sizeof(kTestH264BitstreamConfig) / sizeof(kTestH264BitstreamConfig[0]); + +static std::vector<uint32> kTestH264BitstreamConfigVector( + kTestH264BitstreamConfig, + kTestH264BitstreamConfig+kTestH264BitstreamConfigCount); + +static uint32 kTestColorConfig[] = { + media::VIDEOATTRIBUTEKEY_COLORFORMAT_PLANE_PIXEL_SIZE, 16, + media::VIDEOATTRIBUTEKEY_COLORFORMAT_RED_SIZE, 5, + media::VIDEOATTRIBUTEKEY_COLORFORMAT_GREEN_SIZE, 6, + media::VIDEOATTRIBUTEKEY_COLORFORMAT_BLUE_SIZE, 5, + media::VIDEOATTRIBUTEKEY_TERMINATOR +}; + +static uint32 kTestColorConfigCount = + sizeof(kTestColorConfig) / sizeof(kTestColorConfig[0]); + +static std::vector<uint32> kTestColorConfigVector( + kTestColorConfig, kTestColorConfig+kTestColorConfigCount); + +MATCHER(ErrorMatcher, std::string("Decoder reported unexpected error")) { + return arg->type() == AcceleratedVideoDecoderHostMsg_ErrorNotification::ID; +} + +TEST_P(GpuVideoDecodeAcceleratorTest, RegularDecodingFlow) { + // We will carry out the creation of video decoder manually with specified + // configuration which we know will work. The rest of the functionality is + // asynchronous and is designed to be tested with this test bench. + + // TODO(vmr): Remove the first decode from here after proper init. + if (!fake_client_->DispatchFirstDecode()) { + FAIL() << "Failed to open input file"; + return; + } + + // TODO(vmr): Verify how we are going to not allow any sending of data. + // EXPECT_CALL(mock_gpu_channel_, Send(ErrorMatcher())); + // .Times(0); + + RunMessageLoop(); + // Once message loop has finished our case is over. +} + +const char* kFileNames[] = { + "media/test/data/test-25fps.h264", +}; + +INSTANTIATE_TEST_CASE_P(RegularDecodingFlowWithFile, + GpuVideoDecodeAcceleratorTest, + ::testing::ValuesIn(kFileNames)); + +int main(int argc, char **argv) { + // TODO(vmr): Integrate with existing unit test targets. + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/media/media.gyp b/media/media.gyp index 23e1956..60cffe2 100644 --- a/media/media.gyp +++ b/media/media.gyp @@ -172,6 +172,7 @@ 'video/ffmpeg_video_allocator.h', 'video/ffmpeg_video_decode_engine.cc', 'video/ffmpeg_video_decode_engine.h', + 'video/video_decode_accelerator.cc', 'video/video_decode_accelerator.h', 'video/video_decode_engine.cc', 'video/video_decode_engine.h', diff --git a/media/video/picture.cc b/media/video/picture.cc new file mode 100644 index 0000000..dd57e4a --- /dev/null +++ b/media/video/picture.cc @@ -0,0 +1,72 @@ +// 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 "media/video/picture.h" + +namespace media { + +// PictureBuffer implementation. +PictureBuffer::PictureBuffer(int32 id, + gfx::Size pixel_size, + std::vector<uint32> color_format, + MemoryType memory_type, + std::vector<DataPlaneHandle> data_plane_handles) + : id_(id), + pixel_size_(pixel_size), + color_format_(color_format), + memory_type_(memory_type), + data_plane_handles_(data_plane_handles) { +} + +PictureBuffer::~PictureBuffer() {} + +int32 PictureBuffer::GetId() { + return id_; +} + +gfx::Size PictureBuffer::GetSize() { + return pixel_size_; +} + +const std::vector<uint32>& PictureBuffer::GetColorFormat() { + return color_format_; +} + +PictureBuffer::MemoryType PictureBuffer::GetMemoryType() { + return memory_type_; +} + +std::vector<PictureBuffer::DataPlaneHandle>& PictureBuffer::GetPlaneHandles() { + return data_plane_handles_; +} + +// Picture implementation. +Picture::Picture(PictureBuffer* picture_buffer, gfx::Size decoded_pixel_size, + gfx::Size visible_pixel_size, void* user_handle) + : picture_buffer_(picture_buffer), + decoded_pixel_size_(decoded_pixel_size), + visible_pixel_size_(visible_pixel_size), + user_handle_(user_handle) { +} + +Picture::~Picture() {} + +PictureBuffer* Picture::picture_buffer() { + return picture_buffer_; +} + +gfx::Size Picture::GetDecodedSize() const { + return decoded_pixel_size_; +} + +gfx::Size Picture::GetVisibleSize() const { + return visible_pixel_size_; +} + +void* Picture::GetUserHandle() { + return user_handle_; +} + +} // namespace media + diff --git a/media/video/picture.h b/media/video/picture.h new file mode 100644 index 0000000..ec84453 --- /dev/null +++ b/media/video/picture.h @@ -0,0 +1,65 @@ +// 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. + +#ifndef MEDIA_VIDEO_PICTURE_H_ +#define MEDIA_VIDEO_PICTURE_H_ + +#include <vector> + +#include "base/compiler_specific.h" +#include "media/video/video_decode_accelerator.h" + +namespace media { + +// TODO(vmr): Evaluate the generalization potential of these interfaces & +// classes and refactor as needed with the rest of media stack. +class PictureBuffer : public VideoDecodeAccelerator::PictureBuffer { + public: + PictureBuffer(int32 id, gfx::Size pixel_size, + std::vector<uint32> color_format, MemoryType memory_type, + std::vector<DataPlaneHandle> data_plane_handles); + virtual ~PictureBuffer(); + + // VideoDecodeAccelerator::PictureBuffer implementation. + virtual int32 GetId() OVERRIDE; + virtual gfx::Size GetSize() OVERRIDE; + virtual const std::vector<uint32>& GetColorFormat() OVERRIDE; + virtual MemoryType GetMemoryType() OVERRIDE; + virtual std::vector<DataPlaneHandle>& GetPlaneHandles() OVERRIDE; + + private: + int32 id_; + gfx::Size pixel_size_; + std::vector<uint32> color_format_; + MemoryType memory_type_; + std::vector<DataPlaneHandle>& data_plane_handles_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(PictureBuffer); +}; + +class Picture : public VideoDecodeAccelerator::Picture { + public: + Picture(PictureBuffer* picture_buffer, gfx::Size decoded_pixel_size, + gfx::Size visible_pixel_size, void* user_handle); + virtual ~Picture(); + + // VideoDecodeAccelerator::Picture implementation. + virtual PictureBuffer* picture_buffer() OVERRIDE; + virtual gfx::Size GetDecodedSize() const OVERRIDE; + virtual gfx::Size GetVisibleSize() const OVERRIDE; + virtual void* GetUserHandle() OVERRIDE; + + private: + // Pointer to the picture buffer which contains this picture. + PictureBuffer* picture_buffer_; + gfx::Size decoded_pixel_size_; + gfx::Size visible_pixel_size_; + void* user_handle_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(Picture); +}; + +} // namespace media + +#endif // MEDIA_VIDEO_PICTURE_H_ diff --git a/media/video/video_decode_accelerator.cc b/media/video/video_decode_accelerator.cc new file mode 100644 index 0000000..c88d48a --- /dev/null +++ b/media/video/video_decode_accelerator.cc @@ -0,0 +1,15 @@ +// 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 "media/video/video_decode_accelerator.h" + +namespace media { + +VideoDecodeAccelerator::~VideoDecodeAccelerator() {} + +VideoDecodeAccelerator::PictureBuffer::~PictureBuffer() {} + +VideoDecodeAccelerator::Picture::~Picture() {} + +} diff --git a/media/video/video_decode_accelerator.h b/media/video/video_decode_accelerator.h index 0c4405f..be799c11 100644 --- a/media/video/video_decode_accelerator.h +++ b/media/video/video_decode_accelerator.h @@ -16,34 +16,260 @@ namespace media { typedef Callback0::Type VideoDecodeAcceleratorCallback; +// Enumeration defining global dictionary ranges for various purposes that are +// used to handle the configurations of the video decoder. +enum VideoAttributeKey { + VIDEOATTRIBUTEKEY_TERMINATOR = 0, + + VIDEOATTRIBUTEKEY_BITSTREAM_FORMAT_BASE = 0x100, + // Array of key/value pairs describing video configuration. + // It could include any keys from PP_VideoKey. Its last element shall be + // VIDEOATTRIBUTEKEY_BITSTREAMFORMAT_NONE with no corresponding value. + // An example: + // { + // VIDEOATTRIBUTEKEY_BITSTREAMFORMAT_FOURCC, PP_VIDEODECODECID_VP8, + // VIDEOATTRIBUTEKEY_BITSTREAMFORMAT_VP8_PROFILE, (VP8PROFILE_1 | + // VP8PROFILE_2 | + // VP8PROFILE_3), + // VIDEOATTRIBUTEKEY_TERMINATOR + // }; + // Keys for defining video bitstream format. + // Value is type of PP_VideoCodecFourcc. Commonly known attributes values are + // defined in PP_VideoCodecFourcc enumeration. + VIDEOATTRIBUTEKEY_BITSTREAMFORMAT_FOURCC, + // Bitrate in bits/s. Attribute value is 32-bit unsigned integer. + VIDEOATTRIBUTEKEY_BITSTREAMFORMAT_BITRATE, + // Width and height of the input video bitstream, if known by the application. + // Decoder will expect the bitstream to match these values and does memory + // considerations accordingly. + VIDEOATTRIBUTEKEY_BITSTREAMFORMAT_WIDTH, + VIDEOATTRIBUTEKEY_BITSTREAMFORMAT_HEIGHT, + // Following attributes are applicable only in case of VP8. + // Key for VP8 profile attribute. Attribute value is bitmask of flags defined + // in PP_VP8Profile_Dev enumeration. + VIDEOATTRIBUTEKEY_BITSTREAMFORMAT_VP8_PROFILE, + // Number of partitions per picture. Attribute value is unsigned 32-bit + // integer. + VIDEOATTRIBUTEKEY_BITSTREAMFORMAT_VP8_NUM_OF_PARTITIONS, + // Following attributes are applicable only in case of H.264. + // Value is bitmask collection from the flags defined in PP_H264Profile. + VIDEOATTRIBUTEKEY_BITSTREAMFORMAT_H264_PROFILE, + // Value is type of PP_H264Level. + VIDEOATTRIBUTEKEY_BITSTREAMFORMAT_H264_LEVEL, + // Value is type of PP_H264PayloadFormat_Dev. + VIDEOATTRIBUTEKEY_BITSTREAMFORMAT_H264_PAYLOADFORMAT, + // Subset for H.264 features, attribute value 0 signifies unsupported. + // This is needed in case decoder has partial support for certain profile. + // Default for features are enabled if they're part of supported profile. + // H264 tool called Flexible Macroblock Ordering. + VIDEOATTRIBUTEKEY_BITSTREAMFORMAT_H264_FEATURE_FMO, + // H264 tool called Arbitrary Slice Ordering. + VIDEOATTRIBUTEKEY_BITSTREAMFORMAT_H264_FEATURE_ASO, + // H264 tool called Interlacing. + VIDEOATTRIBUTEKEY_BITSTREAMFORMAT_H264_FEATURE_INTERLACE, + // H264 tool called Context-Adaptive Binary Arithmetic Coding. + VIDEOATTRIBUTEKEY_BITSTREAMFORMAT_H264_FEATURE_CABAC, + // H264 tool called Weighted Prediction. + VIDEOATTRIBUTEKEY_BITSTREAMFORMAT_H264_FEATURE_WEIGHTEDPREDICTION, + + VIDEOATTRIBUTEKEY_COLORFORMAT_BASE = 0x1000, + // Keys for definining attributes of a color buffer. Using these attributes + // users can define color spaces in terms of red, green, blue and alpha + // components as well as with combination of luma and chroma values with + // different subsampling schemes. Also planar, semiplanar and interleaved + // formats can be described by using the provided keys as instructed. + // + // Rules for describing the color planes (1 or more) that constitute the whole + // picture are: + // 1. Each plane starts with VIDEOATTRIBUTEKEY_COLORFORMAT_PLANE_PIXEL_SIZE + // attribute telling how many bits per pixel the plane contains. + // 2. VIDEOATTRIBUTEKEY_COLORFORMAT_PLANE_PIXEL_SIZE attribute must be + // followed either by + // a. Red, green and blue components followed optionally by alpha size + // attribute. + // OR + // b. Luma, blue difference chroma and red difference chroma components as + // well as three sampling reference factors that tell how the chroma may + // have been subsampled with respect to luma. + // 3. Description must be terminated with VIDEOATTRIBUTEKEY_COLORFORMAT_NONE + // key with no value for attribute. + // + // For example, semiplanar YCbCr 4:2:2 (2 planes, one containing 8-bit luma, + // the other containing two interleaved chroma data components) may be + // described with the following attributes: + // { + // VIDEOATTRIBUTEKEY_COLORFORMAT_PLANE_PIXEL_SIZE, 8, + // VIDEOATTRIBUTEKEY_COLORFORMAT_LUMA_SIZE, 8, + // VIDEOATTRIBUTEKEY_COLORFORMAT_PLANE_PIXEL_SIZE, 16, + // VIDEOATTRIBUTEKEY_COLORFORMAT_CHROMA_BLUE_SIZE, 8, + // VIDEOATTRIBUTEKEY_COLORFORMAT_CHROMA_RED_SIZE, 8, + // VIDEOATTRIBUTEKEY_COLORFORMAT_HORIZONTAL_SAMPLING_FACTOR_REFERENCE, 4, + // VIDEOATTRIBUTEKEY_COLORFORMAT_CHROMA_HORIZONTAL_SUBSAMPLING_FACTOR, 2, + // VIDEOATTRIBUTEKEY_COLORFORMAT_CHROMA_VERTICAL_SUBSAMPLING_FACTOR, 2 + // VIDEOATTRIBUTEKEY_TERMINATOR + // } + // + // Another example, commonly known 16-bit RGB 565 color format may be + // specified as follows: + // { + // VIDEOATTRIBUTEKEY_COLORFORMAT_PLANE_PIXEL_SIZE, 16, + // VIDEOATTRIBUTEKEY_COLORFORMAT_RED_SIZE, 5, + // VIDEOATTRIBUTEKEY_COLORFORMAT_GREEN_SIZE, 6, + // VIDEOATTRIBUTEKEY_COLORFORMAT_BLUE_SIZE, 5, + // VIDEOATTRIBUTEKEY_TERMINATOR + // } + // Total color component bits per pixel in the picture buffer. + VIDEOATTRIBUTEKEY_COLORFORMAT_PLANE_PIXEL_SIZE, + // Bits of red per pixel in picture buffer. + VIDEOATTRIBUTEKEY_COLORFORMAT_RED_SIZE, + // Bits of green per pixel in picture buffer. + VIDEOATTRIBUTEKEY_COLORFORMAT_GREEN_SIZE, + // Bits of blue per pixel in picture buffer. + VIDEOATTRIBUTEKEY_COLORFORMAT_BLUE_SIZE, + // Bits of alpha in color buffer. + VIDEOATTRIBUTEKEY_COLORFORMAT_ALPHA_SIZE, + // Bits of luma per pixel in color buffer. + VIDEOATTRIBUTEKEY_COLORFORMAT_LUMA_SIZE, + // Bits of blue difference chroma (Cb) data in color buffer. + VIDEOATTRIBUTEKEY_COLORFORMAT_CHROMA_BLUE_SIZE, + // Bits of blue difference chroma (Cr) data in color buffer. + VIDEOATTRIBUTEKEY_COLORFORMAT_CHROMA_RED_SIZE, + // Three keys to describe the subsampling of YCbCr sampled digital video + // signal. For example, 4:2:2 sampling could be defined by setting: + // VIDEOATTRIBUTEKEY_COLORFORMAT_HORIZONTAL_SAMPLING_FACTOR_REFERENCE = 4 + // VIDEOATTRIBUTEKEY_COLORFORMAT_CHROMINANCE_HORIZONTAL_SUBSAMPLING_FACTOR = 2 + // VIDEOATTRIBUTEKEY_COLORFORMAT_CHROMINANCE_VERTICAL_SUBSAMPLING_FACTOR = 2 + VIDEOATTRIBUTEKEY_COLORFORMAT_HORIZONTAL_SAMPLING_FACTOR_REFERENCE, + VIDEOATTRIBUTEKEY_COLORFORMAT_CHROMA_HORIZONTAL_SUBSAMPLING_FACTOR, + VIDEOATTRIBUTEKEY_COLORFORMAT_CHROMA_VERTICAL_SUBSAMPLING_FACTOR, + // Base for telling implementation specific information about the optimal + // number of picture buffers to be provided to the implementation. + VIDEOATTRIBUTEKEY_PICTUREBUFFER_REQUIREMENTS_BASE = 0x10000, + // Following two keys are used to signal how many buffers are needed by the + // implementation as a function of the maximum number of reference frames set + // by the stream. Number of required buffers is + // MAX_REF_FRAMES * REFERENCE_PIC_MULTIPLIER + ADDITIONAL_BUFFERS + VIDEOATTRIBUTEKEY_PICTUREBUFFER_REQUIREMENTS_ADDITIONAL_BUFFERS, + VIDEOATTRIBUTEKEY_PICTUREBUFFER_REQUIREMENTS_REFERENCE_PIC_MULTIPLIER, + // If decoder does not support pixel accurate strides for picture buffer, this + // parameter tells the stride multiple that is needed by the decoder. Plugin + // must obey the given stride in its picture buffer allocations. + VIDEOATTRIBUTEKEY_PICTUREBUFFER_REQUIREMENTS_STRIDE_MULTIPLE, +}; + +enum VideoCodecFourcc { + VIDEOCODECFOURCC_NONE = 0, + VIDEOCODECFOURCC_VP8 = 0x00385056, // a.k.a. Fourcc 'VP8\0'. + VIDEOCODECFOURCC_H264 = 0x31637661, // a.k.a. Fourcc 'avc1'. +}; + +// VP8 specific information to be carried over the APIs. +// Enumeration for flags defining supported VP8 profiles. +enum VP8Profile { + VP8PROFILE_NONE = 0, + VP8PROFILE_0 = 1, + VP8PROFILE_1 = 1 << 1, + VP8PROFILE_2 = 1 << 2, + VP8PROFILE_3 = 1 << 3, +}; + +// H.264 specific information to be carried over the APIs. +// Enumeration for flags defining supported H.264 profiles. +enum H264Profile { + H264PROFILE_NONE = 0, + H264PROFILE_BASELINE = 1, + H264PROFILE_MAIN = 1 << 2, + H264PROFILE_EXTENDED = 1 << 3, + H264PROFILE_HIGH = 1 << 4, + H264PROFILE_HIGH10PROFILE = 1 << 5, + H264PROFILE_HIGH422PROFILE = 1 << 6, + H264PROFILE_HIGH444PREDICTIVEPROFILE = 1 << 7, + H264PROFILE_SCALABLEBASELINE = 1 << 8, + H264PROFILE_SCALABLEHIGH = 1 << 9, + H264PROFILE_STEREOHIGH = 1 << 10, + H264PROFILE_MULTIVIEWHIGH = 1 << 11, +}; + +// Enumeration for defining H.264 level of decoder implementation. +enum H264Level { + H264LEVEL_NONE = 0, + H264LEVEL_10 = 1, + H264LEVEL_1B = H264LEVEL_10 | 1 << 1, + H264LEVEL_11 = H264LEVEL_1B | 1 << 2, + H264LEVEL_12 = H264LEVEL_11 | 1 << 3, + H264LEVEL_13 = H264LEVEL_12 | 1 << 4, + H264LEVEL_20 = H264LEVEL_13 | 1 << 5, + H264LEVEL_21 = H264LEVEL_20 | 1 << 6, + H264LEVEL_22 = H264LEVEL_21 | 1 << 7, + H264LEVEL_30 = H264LEVEL_22 | 1 << 8, + H264LEVEL_31 = H264LEVEL_30 | 1 << 9, + H264LEVEL_32 = H264LEVEL_31 | 1 << 10, + H264LEVEL_40 = H264LEVEL_32 | 1 << 11, + H264LEVEL_41 = H264LEVEL_40 | 1 << 12, + H264LEVEL_42 = H264LEVEL_41 | 1 << 13, + H264LEVEL_50 = H264LEVEL_42 | 1 << 14, + H264LEVEL_51 = H264LEVEL_50 | 1 << 15, +}; + +// Enumeration to describe which payload format is used within the exchanged +// bitstream buffers. +enum H264PayloadFormat { + H264PAYLOADFORMAT_NONE = 0, + // NALUs separated by Start Code. + H264PAYLOADFORMAT_BYTESTREAM = 1, + // Exactly one raw NALU per buffer. + H264PAYLOADFORMAT_ONE_NALU_PER_BUFFER = 1 << 1, + // NALU separated by 1-byte interleaved length field. + H264PAYLOADFORMAT_ONE_BYTE_INTERLEAVED_LENGTH = 1 << 2, + // NALU separated by 2-byte interleaved length field. + H264PAYLOADFORMAT_TWO_BYTE_INTERLEAVED_LENGTH = 1 << 3, + // NALU separated by 4-byte interleaved length field. + H264PAYLOADFORMAT_FOUR_BYTE_INTERLEAVED_LENGTH = 1 << 4, +}; + // Video decoder interface. +// TODO(vmr): Move much of the inner classes to media namespace to simplify code +// that's using it. class VideoDecodeAccelerator { public: - virtual ~VideoDecodeAccelerator() {} + virtual ~VideoDecodeAccelerator(); // Enumeration of potential errors generated by the API. enum Error { VIDEODECODERERROR_NONE = 0, VIDEODECODERERROR_UNINITIALIZED, VIDEODECODERERROR_UNSUPPORTED, + VIDEODECODERERROR_INVALIDINPUT, + VIDEODECODERERROR_MEMFAILURE, VIDEODECODERERROR_INSUFFICIENT_BUFFERS, VIDEODECODERERROR_INSUFFICIENT_RESOURCES, VIDEODECODERERROR_HARDWARE, + VIDEODECODERERROR_UNEXPECTED_FLUSH, }; // Interface expected from PictureBuffers where pictures are stored. class PictureBuffer { public: enum MemoryType { - PICTUREBUFFER_MEMORYTYPE_SYSTEM = 0, + PICTUREBUFFER_MEMORYTYPE_NONE = 0, + PICTUREBUFFER_MEMORYTYPE_SYSTEM, PICTUREBUFFER_MEMORYTYPE_GL_TEXTURE, }; + // Union to represent one data plane in picture buffer. + union DataPlaneHandle { + struct { + uint32 context_id; // GLES context id. + uint32 texture_id; // GLES texture id. + }; + void* sysmem; // Simply a pointer to system memory. + }; virtual ~PictureBuffer(); - virtual uint32 GetId() = 0; + virtual int32 GetId() = 0; virtual gfx::Size GetSize() = 0; virtual const std::vector<uint32>& GetColorFormat() = 0; virtual MemoryType GetMemoryType() = 0; + virtual std::vector<DataPlaneHandle>& GetPlaneHandles() = 0; }; class Picture { @@ -76,6 +302,9 @@ class VideoDecodeAccelerator { // the picture buffer size it has provided to the decoder. Thus, there is // no function to query the buffer size from this class. + // Returns the picture buffer where this picture is contained. + virtual PictureBuffer* picture_buffer() = 0; + // Returns the decoded size of the decoded picture in pixels. virtual gfx::Size GetDecodedSize() const = 0; @@ -181,7 +410,7 @@ class VideoDecodeAccelerator { // |callback| contains the callback function pointer. // // Returns true when command successfully accepted. Otherwise false. - virtual bool Abort(VideoDecodeAcceleratorCallback* callback) = 0; + virtual bool Abort(VideoDecodeAcceleratorCallback* callback) = 0; }; } // namespace media diff --git a/ppapi/c/dev/pp_video_dev.h b/ppapi/c/dev/pp_video_dev.h index f568105..50ad226 100644 --- a/ppapi/c/dev/pp_video_dev.h +++ b/ppapi/c/dev/pp_video_dev.h @@ -31,8 +31,6 @@ enum PP_VideoAttributeDictionary { // PP_VIDEOATTR_DICTIONARY_TERMINATOR // }; // Keys for defining video bitstream format. - // Terminating entry for bitstream format descriptions. - PP_VIDEOATTR_BITSTREAMFORMATKEY_NONE, // Value is type of PP_VideoCodecFourcc. Commonly known attributes values are // defined in PP_VideoCodecFourcc enumeration. PP_VIDEOATTR_BITSTREAMFORMATKEY_FOURCC, @@ -287,11 +285,10 @@ union PP_PictureData_Dev { PP_Resource sysmem; // Structure to define explicitly a GLES2 context. struct { - // Context allocated using. Use PPB_Context3D_Dev interface to handle this - // resource. + // Context allocated using PPB_Context3D_Dev. PP_Resource context; // Texture ID in the given context where picture is stored. - GLuint textureId; + GLuint texture_id; } gles2_texture; // Client-specified id for the picture buffer. By using this value client can // keep track of the buffers it has assigned to the video decoder and how they @@ -312,7 +309,7 @@ struct PP_Picture_Dev { // information carried over metadata includes timestamps. If there is // multiple NAL units each with their own respective metadata, only the // metadata from the latest call to Decode will be carried over. - void* metadata; + void* user_handle; }; // Enumeration for error events that may be reported through @@ -325,6 +322,10 @@ enum PP_VideoDecodeError_Dev { PP_VIDEODECODEERROR_UNINITIALIZED, // Decoder does not support feature of configuration or bitstream. PP_VIDEODECODEERROR_UNSUPPORTED, + // Decoder did not get valid input. + PP_VIDEODECODERERROR_INVALIDINPUT, + // Failure in memory allocation or mapping. + PP_VIDEODECODERERROR_MEMFAILURE, // Decoder was given bitstream that would result in output pictures but it // has not been provided buffers to do all this. PP_VIDEODECODEERROR_INSUFFICIENT_BUFFERS, diff --git a/ppapi/ppapi_tests.gypi b/ppapi/ppapi_tests.gypi index c2fc7eb..4c4271c 100644 --- a/ppapi/ppapi_tests.gypi +++ b/ppapi/ppapi_tests.gypi @@ -232,6 +232,8 @@ 'tests/test_url_util.h', 'tests/test_utils.cc', 'tests/test_utils.h', + 'tests/test_video_decoder.cc', + 'tests/test_video_decoder.h', # Deprecated test cases. 'tests/test_instance_deprecated.cc', diff --git a/ppapi/tests/test_video_decoder.cc b/ppapi/tests/test_video_decoder.cc new file mode 100644 index 0000000..f669d47 --- /dev/null +++ b/ppapi/tests/test_video_decoder.cc @@ -0,0 +1,46 @@ +// 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 "ppapi/tests/test_video_decoder.h" + +#include "ppapi/c/dev/ppb_video_decoder_dev.h" +#include "ppapi/c/dev/ppb_testing_dev.h" +#include "ppapi/c/ppb_var.h" +#include "ppapi/tests/testing_instance.h" + +REGISTER_TEST_CASE(VideoDecoder); + +bool TestVideoDecoder::Init() { + video_decoder_interface_ = reinterpret_cast<PPB_VideoDecoder_Dev const*>( + pp::Module::Get()->GetBrowserInterface(PPB_VIDEODECODER_DEV_INTERFACE)); + var_interface_ = reinterpret_cast<PPB_Var const*>( + pp::Module::Get()->GetBrowserInterface(PPB_VAR_INTERFACE)); + testing_interface_ = reinterpret_cast<PPB_Testing_Dev const*>( + pp::Module::Get()->GetBrowserInterface(PPB_TESTING_DEV_INTERFACE)); + if (!testing_interface_) { + // Give a more helpful error message for the testing interface being gone + // since that needs special enabling in Chrome. + instance_->AppendError("This test needs the testing interface, which is " + "not currently available. In Chrome, use --enable-pepper-testing when " + "launching."); + } + return video_decoder_interface_ && var_interface_ && testing_interface_; +} + +void TestVideoDecoder::RunTest() { + instance_->LogTest("Create", TestCreate()); +} + +void TestVideoDecoder::QuitMessageLoop() { + testing_interface_->QuitMessageLoop(instance_->pp_instance()); +} + +std::string TestVideoDecoder::TestCreate() { + PP_Resource decoder = video_decoder_interface_->Create( + instance_->pp_instance(), NULL); + if (decoder == 0) { + return "Error creating the decoder"; + } + PASS(); +} diff --git a/ppapi/tests/test_video_decoder.h b/ppapi/tests/test_video_decoder.h new file mode 100644 index 0000000..7c852b7 --- /dev/null +++ b/ppapi/tests/test_video_decoder.h @@ -0,0 +1,34 @@ +// 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. + +#ifndef PPAPI_TESTS_TEST_VIDEO_DECODER_H_ +#define PPAPI_TESTS_TEST_VIDEO_DECODER_H_ + +#include "ppapi/c/pp_stdint.h" +#include "ppapi/tests/test_case.h" + +struct PPB_Testing_Dev; +struct PPB_Var; +struct PPB_VideoDecoder_Dev; + +class TestVideoDecoder : public TestCase { + public: + TestVideoDecoder(TestingInstance* instance) : TestCase(instance) {} + + // TestCase implementation. + virtual bool Init(); + virtual void RunTest(); + + void QuitMessageLoop(); + + private: + std::string TestCreate(); + + // Used by the tests that access the C API directly. + const PPB_VideoDecoder_Dev* video_decoder_interface_; + const PPB_Var* var_interface_; + const PPB_Testing_Dev* testing_interface_; +}; + +#endif // PPAPI_TESTS_TEST_VIDEO_DECODER_H_ diff --git a/webkit/plugins/ppapi/ppb_video_decoder_impl.cc b/webkit/plugins/ppapi/ppb_video_decoder_impl.cc index 60bed3d..ec2d3f1 100644 --- a/webkit/plugins/ppapi/ppb_video_decoder_impl.cc +++ b/webkit/plugins/ppapi/ppb_video_decoder_impl.cc @@ -91,20 +91,20 @@ void ReusePictureBuffer(PP_Resource video_decoder, decoder->ReusePictureBuffer(picture_buffer); } -PP_Bool Flush(PP_Resource decoder_id, +PP_Bool Flush(PP_Resource video_decoder, PP_CompletionCallback callback) { scoped_refptr<PPB_VideoDecoder_Impl> decoder( - Resource::GetAs<PPB_VideoDecoder_Impl>(decoder_id)); + Resource::GetAs<PPB_VideoDecoder_Impl>(video_decoder)); if (!decoder) return PP_FALSE; return BoolToPPBool(decoder->Flush(callback)); } -PP_Bool Abort(PP_Resource decoder_id, +PP_Bool Abort(PP_Resource video_decoder, PP_CompletionCallback callback) { scoped_refptr<PPB_VideoDecoder_Impl> decoder( - Resource::GetAs<PPB_VideoDecoder_Impl>(decoder_id)); + Resource::GetAs<PPB_VideoDecoder_Impl>(video_decoder)); if (!decoder) return PP_FALSE; |