 Chromium Code Reviews
 Chromium Code Reviews Issue 270213004:
  Implement Pepper PPB_VideoDecoder interface.  (Closed) 
  Base URL: svn://svn.chromium.org/chrome/trunk/src
    
  
    Issue 270213004:
  Implement Pepper PPB_VideoDecoder interface.  (Closed) 
  Base URL: svn://svn.chromium.org/chrome/trunk/src| Index: ppapi/proxy/video_decoder_resource.cc | 
| diff --git a/ppapi/proxy/video_decoder_resource.cc b/ppapi/proxy/video_decoder_resource.cc | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..cbf480ed478c6df4643b1277633172573bf4fac7 | 
| --- /dev/null | 
| +++ b/ppapi/proxy/video_decoder_resource.cc | 
| @@ -0,0 +1,478 @@ | 
| +// Copyright (c) 2012 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/proxy/video_decoder_resource.h" | 
| + | 
| +#include "base/bind.h" | 
| +#include "gpu/command_buffer/client/gles2_cmd_helper.h" | 
| +#include "gpu/command_buffer/client/gles2_implementation.h" | 
| +#include "ipc/ipc_message.h" | 
| +#include "ppapi/c/pp_errors.h" | 
| +#include "ppapi/c/ppb_opengles2.h" | 
| +#include "ppapi/proxy/plugin_dispatcher.h" | 
| +#include "ppapi/proxy/ppapi_messages.h" | 
| +#include "ppapi/proxy/ppb_graphics_3d_proxy.h" | 
| +#include "ppapi/shared_impl/ppapi_globals.h" | 
| +#include "ppapi/shared_impl/ppb_graphics_3d_shared.h" | 
| +#include "ppapi/shared_impl/proxy_lock.h" | 
| +#include "ppapi/shared_impl/resource_tracker.h" | 
| +#include "ppapi/shared_impl/scoped_pp_resource.h" | 
| +#include "ppapi/thunk/enter.h" | 
| + | 
| +using ppapi::thunk::EnterResourceNoLock; | 
| +using ppapi::thunk::PPB_Graphics3D_API; | 
| +using ppapi::thunk::PPB_VideoDecoder_API; | 
| + | 
| +namespace { | 
| + | 
| +// Maximum number of concurrent decodes which can be pending. | 
| +const uint32_t kMaximumPendingDecodes = 8; | 
| + | 
| +// Minimum size of shared-memory buffers we allocate. Make them large since | 
| +// we try to reuse them. | 
| +const uint32_t kMinimumBitstreamBufferSize = 100 << 10; | 
| + | 
| +} // namespace | 
| + | 
| +namespace ppapi { | 
| +namespace proxy { | 
| + | 
| +VideoDecoderResource::ShmBuffer::ShmBuffer(base::SharedMemory* shm, | 
| + uint32_t size, | 
| + uint32_t shm_id) | 
| + : shm_(shm), size_(size), addr_(NULL), shm_id_(shm_id) { | 
| + if (shm_->Map(size_)) | 
| + addr_ = shm_->memory(); | 
| + DCHECK(addr_); | 
| +} | 
| + | 
| +VideoDecoderResource::ShmBuffer::~ShmBuffer() { | 
| +} | 
| + | 
| +VideoDecoderResource::Texture::Texture(uint32_t texture_target, | 
| + const PP_Size& size) | 
| + : texture_target_(texture_target), size_(size) { | 
| +} | 
| + | 
| +VideoDecoderResource::Texture::~Texture() { | 
| +} | 
| + | 
| +VideoDecoderResource::Picture::Picture(int32_t decode_id, uint32_t texture_id) | 
| + : decode_id_(decode_id), texture_id_(texture_id) { | 
| +} | 
| + | 
| +VideoDecoderResource::Picture::~Picture() { | 
| +} | 
| + | 
| +VideoDecoderResource::VideoDecoderResource(Connection connection, | 
| + PP_Instance instance) | 
| + : PluginResource(connection, instance), | 
| + decode_id_(0), | 
| + decode_size_(0), | 
| + decode_buffer_(NULL), | 
| + pending_decode_count_(0), | 
| + get_shm_buffer_pending_(false), | 
| + get_picture_(NULL), | 
| + gles2_impl_(NULL), | 
| + initialized_(false), | 
| + testing_(false), | 
| + // Set |decoder_last_error_| to PP_OK after successful initialization. | 
| + // This makes error checking a little more concise, since we can check | 
| + // that the decoder has been initialized and hasn't returned an error by | 
| + // just testing |decoder_last_error_|. | 
| + decoder_last_error_(PP_ERROR_FAILED) { | 
| + SendCreate(RENDERER, PpapiHostMsg_VideoDecoder_Create()); | 
| +} | 
| + | 
| +VideoDecoderResource::~VideoDecoderResource() { | 
| + // Destroy any textures which haven't been dismissed. | 
| + TextureMap::iterator it = textures_.begin(); | 
| + for (; it != textures_.end(); ++it) | 
| + DeleteGLTexture(it->first); | 
| +} | 
| + | 
| +PPB_VideoDecoder_API* VideoDecoderResource::AsPPB_VideoDecoder_API() { | 
| + return this; | 
| +} | 
| + | 
| +int32_t VideoDecoderResource::Initialize( | 
| + PP_Resource graphics_context, | 
| + PP_VideoProfile profile, | 
| + PP_Bool allow_software_fallback, | 
| + scoped_refptr<TrackedCallback> callback) { | 
| + if (initialized_) | 
| + return PP_ERROR_FAILED; | 
| + if (profile < 0 || profile > PP_VIDEOPROFILE_MAX) | 
| + return PP_ERROR_BADARGUMENT; | 
| + if (initialize_callback_) | 
| + return PP_ERROR_INPROGRESS; | 
| + if (!graphics_context) | 
| + return PP_ERROR_BADRESOURCE; | 
| + | 
| + // Create a new Graphics3D resource that can create texture resources to share | 
| + // with the plugin. We can't use the plugin's Graphics3D, since we create | 
| + // textures on a proxy thread, which would interfere with the plugin. | 
| + thunk::EnterResourceCreationNoLock enter_create(pp_instance()); | 
| + if (enter_create.failed()) | 
| + return PP_ERROR_FAILED; | 
| + int32_t attrib_list[] = {PP_GRAPHICS3DATTRIB_NONE}; | 
| + HostResource host_resource; | 
| + if (!testing_) { | 
| + ScopedPPResource graphics3d( | 
| + ScopedPPResource::PassRef(), | 
| + enter_create.functions()->CreateGraphics3D( | 
| + pp_instance(), graphics_context, attrib_list)); | 
| + EnterResourceNoLock<PPB_Graphics3D_API> enter_graphics(graphics3d.get(), | 
| + true); | 
| + if (enter_graphics.failed()) | 
| + return PP_ERROR_BADRESOURCE; | 
| + | 
| + graphics3d_ = static_cast<PPB_Graphics3D_Shared*>(enter_graphics.object()); | 
| + gles2_impl_ = graphics3d_->gles2_impl(); | 
| + host_resource = graphics3d_->host_resource(); | 
| + graphics3d.Release(); | 
| 
yzshen1
2014/05/14 18:08:23
Is this leak intentional?
 
bbudge
2014/05/14 19:35:04
Won't graphics3d_ (scoped_refptr) hold onto it?
 
yzshen1
2014/05/14 22:58:16
- When assigning a value to scoped_refptr, it adds
 | 
| + } | 
| + | 
| + initialize_callback_ = callback; | 
| + | 
| + Call<PpapiPluginMsg_VideoDecoder_InitializeReply>( | 
| + RENDERER, | 
| + PpapiHostMsg_VideoDecoder_Initialize( | 
| + host_resource, profile, PP_ToBool(allow_software_fallback)), | 
| + base::Bind(&VideoDecoderResource::OnPluginMsgInitializeComplete, this)); | 
| + | 
| + return PP_OK_COMPLETIONPENDING; | 
| +} | 
| + | 
| +int32_t VideoDecoderResource::Decode(uint32_t decode_id, | 
| + uint32_t size, | 
| + const void* buffer, | 
| + scoped_refptr<TrackedCallback> callback) { | 
| + if (decoder_last_error_) | 
| + return decoder_last_error_; | 
| + if (flush_callback_ || reset_callback_) | 
| + return PP_ERROR_FAILED; | 
| + if (decode_callback_) | 
| + return PP_ERROR_INPROGRESS; | 
| + | 
| + return TryDecode(decode_id, size, buffer, callback); | 
| +} | 
| + | 
| +int32_t VideoDecoderResource::GetPicture( | 
| + PP_VideoPicture* picture, | 
| + scoped_refptr<TrackedCallback> callback) { | 
| + if (decoder_last_error_) | 
| + return decoder_last_error_; | 
| + if (reset_callback_) | 
| + return PP_ERROR_FAILED; | 
| + if (get_picture_callback_) | 
| + return PP_ERROR_INPROGRESS; | 
| + | 
| + // If the next picture is ready, return it synchronously. | 
| + if (!received_pictures_.empty()) { | 
| + WriteNextPicture(picture); | 
| + return PP_OK; | 
| + } | 
| + | 
| + get_picture_callback_ = callback; | 
| + get_picture_ = picture; | 
| + return PP_OK_COMPLETIONPENDING; | 
| +} | 
| + | 
| +void VideoDecoderResource::RecyclePicture(const PP_VideoPicture* picture) { | 
| + if (decoder_last_error_) | 
| + return; | 
| + if (reset_callback_) | 
| + return; | 
| + | 
| + Post(RENDERER, PpapiHostMsg_VideoDecoder_RecyclePicture(picture->texture_id)); | 
| +} | 
| + | 
| +int32_t VideoDecoderResource::Flush(scoped_refptr<TrackedCallback> callback) { | 
| + if (decoder_last_error_) | 
| + return decoder_last_error_; | 
| + if (reset_callback_) | 
| + return PP_ERROR_FAILED; | 
| + if (flush_callback_) | 
| + return PP_ERROR_INPROGRESS; | 
| + flush_callback_ = callback; | 
| + | 
| + Call<PpapiPluginMsg_VideoDecoder_FlushReply>( | 
| + RENDERER, | 
| + PpapiHostMsg_VideoDecoder_Flush(), | 
| + base::Bind(&VideoDecoderResource::OnPluginMsgFlushComplete, this)); | 
| + | 
| + return PP_OK_COMPLETIONPENDING; | 
| +} | 
| + | 
| +int32_t VideoDecoderResource::Reset(scoped_refptr<TrackedCallback> callback) { | 
| + if (decoder_last_error_) | 
| + return decoder_last_error_; | 
| + if (flush_callback_) | 
| + return PP_ERROR_FAILED; | 
| + if (reset_callback_) | 
| + return PP_ERROR_INPROGRESS; | 
| + reset_callback_ = callback; | 
| + | 
| + // Cause any pending Decode or GetPicture callbacks to abort immediately. | 
| + // Reentrancy isn't a problem, since all calls fail until Reset completes. | 
| + if (TrackedCallback::IsPending(decode_callback_)) | 
| + decode_callback_->Abort(); | 
| + decode_callback_ = NULL; | 
| + if (TrackedCallback::IsPending(get_picture_callback_)) | 
| + get_picture_callback_->Abort(); | 
| + get_picture_callback_ = NULL; | 
| + Call<PpapiPluginMsg_VideoDecoder_ResetReply>( | 
| + RENDERER, | 
| + PpapiHostMsg_VideoDecoder_Reset(), | 
| + base::Bind(&VideoDecoderResource::OnPluginMsgResetComplete, this)); | 
| + | 
| + return PP_OK_COMPLETIONPENDING; | 
| +} | 
| + | 
| +void VideoDecoderResource::OnReplyReceived( | 
| + const ResourceMessageReplyParams& params, | 
| + const IPC::Message& msg) { | 
| + PPAPI_BEGIN_MESSAGE_MAP(VideoDecoderResource, msg) | 
| + PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL( | 
| + PpapiPluginMsg_VideoDecoder_RequestTextures, OnPluginMsgRequestTextures) | 
| + PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL( | 
| + PpapiPluginMsg_VideoDecoder_PictureReady, OnPluginMsgPictureReady) | 
| + PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL( | 
| + PpapiPluginMsg_VideoDecoder_DismissPicture, OnPluginMsgDismissPicture) | 
| + PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL( | 
| + PpapiPluginMsg_VideoDecoder_NotifyError, OnPluginMsgNotifyError) | 
| + PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL_UNHANDLED( | 
| + PluginResource::OnReplyReceived(params, msg)) | 
| + PPAPI_END_MESSAGE_MAP() | 
| +} | 
| + | 
| +void VideoDecoderResource::SetForTest() { | 
| + testing_ = true; | 
| +} | 
| + | 
| +void VideoDecoderResource::OnPluginMsgRequestTextures( | 
| + const ResourceMessageReplyParams& params, | 
| + uint32_t num_textures, | 
| + PP_Size size, | 
| + uint32_t texture_target) { | 
| + DCHECK(num_textures); | 
| + std::vector<uint32_t> texture_ids(num_textures); | 
| + if (gles2_impl_) { | 
| + gles2_impl_->GenTextures(num_textures, &texture_ids.front()); | 
| + for (uint32_t i = 0; i < num_textures; ++i) { | 
| + gles2_impl_->ActiveTexture(GL_TEXTURE0); | 
| + gles2_impl_->BindTexture(texture_target, texture_ids[i]); | 
| + gles2_impl_->TexParameteri( | 
| + texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | 
| + gles2_impl_->TexParameteri( | 
| + texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | 
| + gles2_impl_->TexParameterf( | 
| + texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | 
| + gles2_impl_->TexParameterf( | 
| + texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | 
| + | 
| + if (texture_target == GL_TEXTURE_2D) { | 
| + gles2_impl_->TexImage2D(texture_target, | 
| + 0, | 
| + GL_RGBA, | 
| + size.width, | 
| + size.height, | 
| + 0, | 
| + GL_RGBA, | 
| + GL_UNSIGNED_BYTE, | 
| + NULL); | 
| + } | 
| + | 
| + textures_.insert( | 
| + std::make_pair(texture_ids[i], Texture(texture_target, size))); | 
| + } | 
| + gles2_impl_->Flush(); | 
| + } else if (testing_) { | 
| + // Create some fake texture ids so we can test picture handling. | 
| + for (uint32_t i = 0; i < num_textures; ++i) { | 
| + texture_ids[i] = i + 1; | 
| + textures_.insert( | 
| + std::make_pair(texture_ids[i], Texture(texture_target, size))); | 
| + } | 
| + } | 
| + | 
| + Post(RENDERER, PpapiHostMsg_VideoDecoder_AssignTextures(size, texture_ids)); | 
| +} | 
| + | 
| +void VideoDecoderResource::OnPluginMsgPictureReady( | 
| + const ResourceMessageReplyParams& params, | 
| + uint32_t decode_id, | 
| + uint32_t texture_id) { | 
| + received_pictures_.push(Picture(decode_id, texture_id)); | 
| + // Prepare to accept another call to GetPicture in the callback. | 
| + scoped_refptr<TrackedCallback> callback; | 
| + callback.swap(get_picture_callback_); | 
| + PP_VideoPicture* picture = get_picture_; | 
| + get_picture_ = NULL; | 
| + if (TrackedCallback::IsPending(callback)) { | 
| + WriteNextPicture(picture); | 
| + callback->Run(PP_OK); | 
| + } | 
| +} | 
| + | 
| +void VideoDecoderResource::OnPluginMsgDismissPicture( | 
| + const ResourceMessageReplyParams& params, | 
| + uint32_t texture_id) { | 
| + DeleteGLTexture(texture_id); | 
| + textures_.erase(texture_id); | 
| +} | 
| + | 
| +void VideoDecoderResource::OnPluginMsgNotifyError( | 
| + const ResourceMessageReplyParams& params, | 
| + int32_t error) { | 
| + decoder_last_error_ = error; | 
| + // Cause any pending Decode or GetPicture callbacks to run immediately. | 
| + // Reentrancy isn't a problem, since the resource is unusable now. | 
| + if (TrackedCallback::IsPending(decode_callback_)) | 
| + decode_callback_->Run(decoder_last_error_); | 
| + decode_callback_ = NULL; | 
| + if (TrackedCallback::IsPending(get_picture_callback_)) | 
| + get_picture_callback_->Run(decoder_last_error_); | 
| + get_picture_callback_ = NULL; | 
| +} | 
| + | 
| +void VideoDecoderResource::OnPluginMsgInitializeComplete( | 
| + const ResourceMessageReplyParams& params) { | 
| + decoder_last_error_ = params.result(); | 
| + if (decoder_last_error_ == PP_OK) | 
| + initialized_ = true; | 
| + | 
| + // Let the plugin call Initialize again from its callback in case of failure. | 
| + scoped_refptr<TrackedCallback> callback; | 
| + callback.swap(initialize_callback_); | 
| + callback->Run(decoder_last_error_); | 
| +} | 
| + | 
| +void VideoDecoderResource::OnPluginMsgGetShmComplete( | 
| + const ResourceMessageReplyParams& params, | 
| + uint32_t size) { | 
| + get_shm_buffer_pending_ = false; | 
| + int32_t result = params.result(); | 
| + if (result == PP_OK) { | 
| + base::SharedMemoryHandle shm_handle = base::SharedMemory::NULLHandle(); | 
| + if (!params.TakeSharedMemoryHandleAtIndex(0, &shm_handle)) { | 
| + RunDecodeCallback(PP_ERROR_FAILED); | 
| + return; | 
| + } | 
| + uint32_t shm_id = static_cast<uint32_t>(shm_buffers_.size()); | 
| + ShmBuffer* shm_buffer = | 
| + new ShmBuffer(new base::SharedMemory(shm_handle, false /* read_only */), | 
| + size, | 
| + shm_id); | 
| + shm_buffers_.push_back(shm_buffer); | 
| + SendDecodeMessage(shm_id); | 
| + RunDecodeCallback(PP_OK); | 
| + } | 
| +} | 
| + | 
| +void VideoDecoderResource::OnPluginMsgDecodeComplete( | 
| + const ResourceMessageReplyParams& params, | 
| + uint32_t shm_id) { | 
| + pending_decode_count_--; | 
| + available_shm_buffers_.push_back(shm_buffers_[shm_id]); | 
| + // If a Decode is pending, and we're not waiting for a shm buffer, attempt | 
| + // the Decode again. | 
| + if (decode_callback_ && !get_shm_buffer_pending_) { | 
| + int32_t result = | 
| + TryDecode(decode_id_, decode_size_, decode_buffer_, decode_callback_); | 
| + if (result == PP_OK) | 
| + RunDecodeCallback(PP_OK); | 
| + } | 
| +} | 
| + | 
| +void VideoDecoderResource::OnPluginMsgFlushComplete( | 
| + const ResourceMessageReplyParams& params) { | 
| + if (get_picture_callback_) { | 
| + scoped_refptr<TrackedCallback> callback; | 
| + callback.swap(get_picture_callback_); | 
| + callback->Abort(); | 
| + } | 
| + | 
| + scoped_refptr<TrackedCallback> callback; | 
| + callback.swap(flush_callback_); | 
| + callback->Run(params.result()); | 
| +} | 
| + | 
| +void VideoDecoderResource::OnPluginMsgResetComplete( | 
| + const ResourceMessageReplyParams& params) { | 
| + scoped_refptr<TrackedCallback> callback; | 
| + callback.swap(reset_callback_); | 
| + callback->Run(params.result()); | 
| +} | 
| + | 
| +int32_t VideoDecoderResource::TryDecode( | 
| + uint32_t decode_id, | 
| + uint32_t size, | 
| + const void* buffer, | 
| + scoped_refptr<TrackedCallback> callback) { | 
| + decode_id_ = decode_id; | 
| + decode_size_ = size; | 
| + decode_buffer_ = buffer; | 
| + if (available_shm_buffers_.empty() || | 
| + available_shm_buffers_.back()->size_ < size) { | 
| + decode_callback_ = callback; | 
| + | 
| + if (pending_decode_count_ < kMaximumPendingDecodes) { | 
| + get_shm_buffer_pending_ = true; | 
| + uint32_t alloc_size = std::max(size, kMinimumBitstreamBufferSize); | 
| + Call<PpapiPluginMsg_VideoDecoder_GetShmReply>( | 
| + RENDERER, | 
| + PpapiHostMsg_VideoDecoder_GetShm(alloc_size), | 
| + base::Bind(&VideoDecoderResource::OnPluginMsgGetShmComplete, this)); | 
| + } | 
| + | 
| + return PP_OK_COMPLETIONPENDING; | 
| + } | 
| + | 
| + ShmBuffer* shm_buffer = available_shm_buffers_.back(); | 
| + available_shm_buffers_.pop_back(); | 
| + SendDecodeMessage(shm_buffer->shm_id_); | 
| + return PP_OK; | 
| +} | 
| + | 
| +void VideoDecoderResource::SendDecodeMessage(uint32_t shm_id) { | 
| + ShmBuffer* shm_buffer = shm_buffers_[shm_id]; | 
| + memcpy(shm_buffer->addr_, decode_buffer_, decode_size_); | 
| + pending_decode_count_++; | 
| + | 
| + Call<PpapiPluginMsg_VideoDecoder_DecodeReply>( | 
| + RENDERER, | 
| + PpapiHostMsg_VideoDecoder_Decode(shm_id, decode_id_, decode_size_), | 
| + base::Bind(&VideoDecoderResource::OnPluginMsgDecodeComplete, this)); | 
| +} | 
| + | 
| +void VideoDecoderResource::RunDecodeCallback(int32_t result) { | 
| + scoped_refptr<TrackedCallback> callback; | 
| + callback.swap(decode_callback_); | 
| + callback->Run(result); | 
| +} | 
| + | 
| +void VideoDecoderResource::DeleteGLTexture(uint32_t id) { | 
| + if (gles2_impl_) { | 
| + gles2_impl_->DeleteTextures(1, &id); | 
| + gles2_impl_->Flush(); | 
| + } | 
| +} | 
| + | 
| +void VideoDecoderResource::WriteNextPicture(PP_VideoPicture* pp_picture) { | 
| + DCHECK(!received_pictures_.empty()); | 
| + Picture& picture = received_pictures_.front(); | 
| + uint32_t texture_id = picture.texture_id_; | 
| + TextureMap::iterator it = textures_.find(texture_id); | 
| + DCHECK(it != textures_.end()); | 
| + pp_picture->decode_id = picture.decode_id_; | 
| + pp_picture->texture_id = texture_id; | 
| + pp_picture->texture_target = it->second.texture_target_; | 
| + pp_picture->texture_size = it->second.size_; | 
| + received_pictures_.pop(); | 
| +} | 
| + | 
| +} // namespace proxy | 
| +} // namespace ppapi |