Chromium Code Reviews| 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..571b4da25fb3f6313e5442a89021ea600ef92924 | 
| --- /dev/null | 
| +++ b/ppapi/proxy/video_decoder_resource.cc | 
| @@ -0,0 +1,566 @@ | 
| +// 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/thunk/enter.h" | 
| + | 
| +using ppapi::thunk::EnterResourceNoLock; | 
| +using ppapi::thunk::PPB_Graphics3D_API; | 
| +using ppapi::thunk::PPB_VideoDecoder_API; | 
| + | 
| +namespace ppapi { | 
| +namespace proxy { | 
| + | 
| +namespace { | 
| + | 
| +// These constants should be kept in sync with PepperVideoDecoderHost. | 
| +// Maximum number of concurrent decodes which can be pending. | 
| +const uint32_t kMaximumPendingDecodes = 8; | 
| +// Minimum size of shared-memory buffers we allocate (100 KB). Make them large | 
| +// since we try to reuse them. | 
| +const uint32_t kMinimumBitstreamBufferSize = 100 << 10; | 
| +// Maximum size of shared-memory buffers we allocate (4 MB). This should be | 
| +// enough for even 4K video at reasonable compression levels. | 
| +const uint32_t kMaximumBitstreamBufferSize = 4 << 20; | 
| + | 
| +} // namespace | 
| + | 
| +VideoDecoderResource::ShmBuffer::ShmBuffer( | 
| + scoped_ptr<base::SharedMemory> shm_ptr, | 
| + uint32_t size, | 
| + uint32_t shm_id) | 
| + : shm(shm_ptr.Pass()), size(size), addr(NULL), shm_id(shm_id) { | 
| + if (shm->Map(size)) | 
| 
 
dmichael (off chromium)
2014/05/20 22:40:38
Given this is what size means, do you need the siz
 
bbudge
2014/05/22 18:34:55
Done.
 
 | 
| + 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), | 
| + next_decode_id_(0), | 
| + decode_size_(0), | 
| + decode_buffer_(NULL), | 
| + get_shm_buffer_pending_(false), | 
| + pending_shm_id_(0), | 
| + 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) { | 
| + // Clear the decode_ids_ array. | 
| + memset(decode_ids_, 0, arraysize(decode_ids_)); | 
| + 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, and 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_) { | 
| + graphics3d_ = | 
| + ScopedPPResource(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; | 
| + | 
| + PPB_Graphics3D_Shared* ppb_graphics3d_shared = | 
| + static_cast<PPB_Graphics3D_Shared*>(enter_graphics.object()); | 
| + gles2_impl_ = ppb_graphics3d_shared->gles2_impl(); | 
| + host_resource = ppb_graphics3d_shared->host_resource(); | 
| + } | 
| + | 
| + 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; | 
| + if (size > kMaximumBitstreamBufferSize) | 
| + return PP_ERROR_NOMEMORY; | 
| + | 
| + // Save decode_id in a ring buffer. The ring buffer is large enough to store | 
| + // decode_id for the maximim picture delay. | 
| 
 
dmichael (off chromium)
2014/05/20 22:40:38
maximim->maximum
 
bbudge
2014/05/22 18:34:55
Done.
 
 | 
| + decode_ids_[next_decode_id_] = decode_id; | 
| 
 
dmichael (off chromium)
2014/05/20 22:40:38
So, decode_id is from the plugin and can be anythi
 
bbudge
2014/05/22 18:34:55
The resource and host both maintain a count of dec
 
 | 
| + next_decode_id_ = (next_decode_id_ + 1) % kMaximumPictureLatency; | 
| + | 
| + return TryDecode(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, | 
| + const 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; | 
| + uint32_t shm_id = pending_shm_id_; | 
| 
 
dmichael (off chromium)
2014/05/20 22:40:38
So you only allow one request for a shared memory
 
bbudge
2014/05/22 18:34:55
It does add latency, but as discussed offline, it
 
 | 
| + int32_t result = params.result(); | 
| + // Return the error to the plugin. It has the option of calling Reset and | 
| + // trying to seek around a problem frame. | 
| + if (result != PP_OK) { | 
| + RunDecodeCallback(PP_ERROR_NOMEMORY); | 
| + return; | 
| + } | 
| + | 
| + base::SharedMemoryHandle shm_handle = base::SharedMemory::NULLHandle(); | 
| + if (!params.TakeSharedMemoryHandleAtIndex(0, &shm_handle)) { | 
| + RunDecodeCallback(PP_ERROR_NOMEMORY); | 
| + return; | 
| + } | 
| + scoped_ptr<base::SharedMemory> shm( | 
| + new base::SharedMemory(shm_handle, false /* read_only */)); | 
| + scoped_ptr<ShmBuffer> shm_buffer(new ShmBuffer(shm.Pass(), size, shm_id)); | 
| + if (!shm_buffer->addr) { | 
| + RunDecodeCallback(PP_ERROR_NOMEMORY); | 
| + return; | 
| + } | 
| + | 
| + if (shm_id == shm_buffers_.size()) { | 
| + shm_buffers_.push_back(shm_buffer.release()); | 
| + } else { | 
| + // When we grow a shm buffer, we always pick available_shm_buffers_.back(). | 
| + // Remove it from the list now. | 
| + available_shm_buffers_.pop_back(); | 
| + // Delete manually, since ScopedVector doesn't do the right thing. | 
| + delete shm_buffers_[shm_id]; | 
| + shm_buffers_[shm_id] = shm_buffer.release(); | 
| + } | 
| + | 
| + SendDecodeMessage(shm_id); | 
| 
 
dmichael (off chromium)
2014/05/20 22:40:38
So this adds to latency at the start of decode als
 
bbudge
2014/05/22 18:34:55
Not always. In the steady state, Decode calls TryD
 
 | 
| + RunDecodeCallback(PP_OK); | 
| +} | 
| + | 
| +void VideoDecoderResource::OnPluginMsgDecodeComplete( | 
| + const ResourceMessageReplyParams& params, | 
| + uint32_t shm_id) { | 
| + if (shm_id >= shm_buffers_.size()) { | 
| + NOTREACHED(); | 
| + return; | 
| + } | 
| + // Return the shm buffer to the available list. | 
| + available_shm_buffers_.push_back(shm_buffers_[shm_id]); | 
| + | 
| + if (decode_callback_) { | 
| + // A buffer is free and there's no buffer awaiting decode. Allow the plugin | 
| + // to call Decode again. | 
| 
 
dmichael (off chromium)
2014/05/20 22:40:38
I think it would be clearer with this comment insi
 
bbudge
2014/05/22 18:34:55
Done.
 
 | 
| + if (!decode_buffer_) { | 
| + RunDecodeCallback(PP_OK); | 
| + return; | 
| + } | 
| + | 
| + // decode_buffer_ is waiting for a shm buffer allocated specifically for it. | 
| + if (get_shm_buffer_pending_) | 
| + return; | 
| + | 
| + // Re-try the waiting decode. | 
| + int32_t result = TryDecode(decode_size_, decode_buffer_, decode_callback_); | 
| + // Return success or failure, but not a second completion pending result. | 
| + if (result != PP_OK_COMPLETIONPENDING) | 
| + RunDecodeCallback(result); | 
| + } | 
| +} | 
| + | 
| +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 size, | 
| + const void* buffer, | 
| + scoped_refptr<TrackedCallback> callback) { | 
| + decode_size_ = size; | 
| + decode_buffer_ = buffer; | 
| + bool can_create_buffers = shm_buffers_.size() < kMaximumPendingDecodes; | 
| + // If the last available buffer is big enough, send the Decode. We don't try | 
| + // to do anything more sophisticated, since we expect in the steady state to | 
| + // have a pool of roughly equal sized buffers. | 
| + if (!available_shm_buffers_.empty() && | 
| + available_shm_buffers_.back()->size >= size) { | 
| + ShmBuffer* shm_buffer = available_shm_buffers_.back(); | 
| + available_shm_buffers_.pop_back(); | 
| + SendDecodeMessage(shm_buffer->shm_id); | 
| + // If we have another free buffer, or we can still create new buffers, let | 
| + // the plugin call Decode again. | 
| + if (!available_shm_buffers_.empty() || can_create_buffers) | 
| + return PP_OK; | 
| + | 
| + // All buffers are busy and we can't create more. Delay completion until a | 
| + // buffer is available. | 
| + decode_callback_ = callback; | 
| + return PP_OK_COMPLETIONPENDING; | 
| + } | 
| + | 
| + decode_callback_ = callback; | 
| + | 
| + // Copy the plugin's buffer if we haven't already. This only happens at | 
| + // startup when we're creating our pool of shm buffers, or when the plugin | 
| + // sends us a large buffer that won't fit in the last available shm buffer. | 
| + if (!temp_buffer_) { | 
| + temp_buffer_.reset(new char[size]); | 
| + memcpy(temp_buffer_.get(), decode_buffer_, decode_size_); | 
| + decode_buffer_ = temp_buffer_.get(); | 
| + } | 
| + | 
| + // If all buffers are busy and we can't create any more, then the plugin must | 
| + // wait for one to become free. | 
| + if (available_shm_buffers_.empty() && !can_create_buffers) | 
| + return PP_OK_COMPLETIONPENDING; | 
| + | 
| + // If we can create a new buffer, signal the host by sending an index one | 
| + // greater than the number of existing buffers. If we can't create more, | 
| + // signal the host to grow the last available free shm buffer by passing an | 
| + // index in the legal range. | 
| + if (can_create_buffers) | 
| + pending_shm_id_ = static_cast<uint32_t>(shm_buffers_.size()); | 
| + else | 
| + pending_shm_id_ = available_shm_buffers_.back()->shm_id; | 
| + | 
| + get_shm_buffer_pending_ = true; | 
| + uint32_t alloc_size = std::max(size, kMinimumBitstreamBufferSize); | 
| + Call<PpapiPluginMsg_VideoDecoder_GetShmReply>( | 
| + RENDERER, | 
| + PpapiHostMsg_VideoDecoder_GetShm(alloc_size, pending_shm_id_), | 
| + base::Bind(&VideoDecoderResource::OnPluginMsgGetShmComplete, this)); | 
| + | 
| + return PP_OK_COMPLETIONPENDING; | 
| +} | 
| + | 
| +void VideoDecoderResource::SendDecodeMessage(uint32_t shm_id) { | 
| + DCHECK(shm_id < shm_buffers_.size()); | 
| + ShmBuffer* shm_buffer = shm_buffers_[shm_id]; | 
| + memcpy(shm_buffer->addr, decode_buffer_, decode_size_); | 
| + if (temp_buffer_) | 
| + temp_buffer_.reset(); | 
| + | 
| + // We don't bother sending a decode_id value to the host. It uses its own | 
| + // counter to assign a unique id to the Decode in sync with this class, and | 
| + // returns the right value in the PictureReady message, which we use to look | 
| + // up the value we saved in decode_ids_. | 
| + Call<PpapiPluginMsg_VideoDecoder_DecodeReply>( | 
| + RENDERER, | 
| + PpapiHostMsg_VideoDecoder_Decode(shm_id, decode_size_), | 
| + base::Bind(&VideoDecoderResource::OnPluginMsgDecodeComplete, this)); | 
| + | 
| + decode_buffer_ = NULL; | 
| + decode_size_ = 0; | 
| +} | 
| + | 
| +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()); | 
| + // Use decode_id to get the plugin's id that we saved in the ring buffer. | 
| + uint32_t decode_id = picture.decode_id % kMaximumPictureLatency; | 
| + pp_picture->decode_id = decode_ids_[decode_id]; | 
| 
 
dmichael (off chromium)
2014/05/20 22:40:38
How do you know that the picture.decode_id will ma
 
bbudge
2014/05/22 18:34:55
The resource and host both count decodes to give e
 
 | 
| + 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 |