Home | History | Annotate | Download | only in proxy
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "ppapi/proxy/video_decoder_resource.h"
      6 
      7 #include "base/bind.h"
      8 #include "gpu/command_buffer/client/gles2_cmd_helper.h"
      9 #include "gpu/command_buffer/client/gles2_implementation.h"
     10 #include "gpu/command_buffer/common/mailbox.h"
     11 #include "ipc/ipc_message.h"
     12 #include "ppapi/c/pp_errors.h"
     13 #include "ppapi/c/ppb_opengles2.h"
     14 #include "ppapi/proxy/plugin_dispatcher.h"
     15 #include "ppapi/proxy/ppapi_messages.h"
     16 #include "ppapi/proxy/ppb_graphics_3d_proxy.h"
     17 #include "ppapi/proxy/serialized_handle.h"
     18 #include "ppapi/proxy/video_decoder_constants.h"
     19 #include "ppapi/shared_impl/ppapi_globals.h"
     20 #include "ppapi/shared_impl/ppb_graphics_3d_shared.h"
     21 #include "ppapi/shared_impl/proxy_lock.h"
     22 #include "ppapi/shared_impl/resource_tracker.h"
     23 #include "ppapi/thunk/enter.h"
     24 
     25 using ppapi::thunk::EnterResourceNoLock;
     26 using ppapi::thunk::PPB_Graphics3D_API;
     27 using ppapi::thunk::PPB_VideoDecoder_API;
     28 
     29 namespace ppapi {
     30 namespace proxy {
     31 
     32 VideoDecoderResource::ShmBuffer::ShmBuffer(
     33     scoped_ptr<base::SharedMemory> shm_ptr,
     34     uint32_t size,
     35     uint32_t shm_id)
     36     : shm(shm_ptr.Pass()), addr(NULL), shm_id(shm_id) {
     37   if (shm->Map(size))
     38     addr = shm->memory();
     39 }
     40 
     41 VideoDecoderResource::ShmBuffer::~ShmBuffer() {
     42 }
     43 
     44 VideoDecoderResource::Texture::Texture(uint32_t texture_target,
     45                                        const PP_Size& size)
     46     : texture_target(texture_target), size(size) {
     47 }
     48 
     49 VideoDecoderResource::Texture::~Texture() {
     50 }
     51 
     52 VideoDecoderResource::Picture::Picture(int32_t decode_id, uint32_t texture_id)
     53     : decode_id(decode_id), texture_id(texture_id) {
     54 }
     55 
     56 VideoDecoderResource::Picture::~Picture() {
     57 }
     58 
     59 VideoDecoderResource::VideoDecoderResource(Connection connection,
     60                                            PP_Instance instance)
     61     : PluginResource(connection, instance),
     62       num_decodes_(0),
     63       get_picture_(NULL),
     64       gles2_impl_(NULL),
     65       initialized_(false),
     66       testing_(false),
     67       // Set |decoder_last_error_| to PP_OK after successful initialization.
     68       // This makes error checking a little more concise, since we can check
     69       // that the decoder has been initialized and hasn't returned an error by
     70       // just testing |decoder_last_error_|.
     71       decoder_last_error_(PP_ERROR_FAILED) {
     72   // Clear the decode_ids_ array.
     73   memset(decode_ids_, 0, arraysize(decode_ids_));
     74   SendCreate(RENDERER, PpapiHostMsg_VideoDecoder_Create());
     75 }
     76 
     77 VideoDecoderResource::~VideoDecoderResource() {
     78   // Destroy any textures which haven't been dismissed.
     79   TextureMap::iterator it = textures_.begin();
     80   for (; it != textures_.end(); ++it)
     81     DeleteGLTexture(it->first);
     82 }
     83 
     84 PPB_VideoDecoder_API* VideoDecoderResource::AsPPB_VideoDecoder_API() {
     85   return this;
     86 }
     87 
     88 int32_t VideoDecoderResource::Initialize(
     89     PP_Resource graphics_context,
     90     PP_VideoProfile profile,
     91     PP_Bool allow_software_fallback,
     92     scoped_refptr<TrackedCallback> callback) {
     93   if (initialized_)
     94     return PP_ERROR_FAILED;
     95   if (profile < 0 || profile > PP_VIDEOPROFILE_MAX)
     96     return PP_ERROR_BADARGUMENT;
     97   if (initialize_callback_)
     98     return PP_ERROR_INPROGRESS;
     99   if (!graphics_context)
    100     return PP_ERROR_BADRESOURCE;
    101 
    102   HostResource host_resource;
    103   if (!testing_) {
    104     // Create a new Graphics3D resource that can create texture resources to
    105     // share with the plugin. We can't use the plugin's Graphics3D, since we
    106     // create textures on a proxy thread, and would interfere with the plugin.
    107     thunk::EnterResourceCreationNoLock enter_create(pp_instance());
    108     if (enter_create.failed())
    109       return PP_ERROR_FAILED;
    110     int32_t attrib_list[] = {PP_GRAPHICS3DATTRIB_NONE};
    111     graphics3d_ =
    112         ScopedPPResource(ScopedPPResource::PassRef(),
    113                          enter_create.functions()->CreateGraphics3D(
    114                              pp_instance(), graphics_context, attrib_list));
    115     EnterResourceNoLock<PPB_Graphics3D_API> enter_graphics(graphics3d_.get(),
    116                                                            false);
    117     if (enter_graphics.failed())
    118       return PP_ERROR_BADRESOURCE;
    119 
    120     PPB_Graphics3D_Shared* ppb_graphics3d_shared =
    121         static_cast<PPB_Graphics3D_Shared*>(enter_graphics.object());
    122     gles2_impl_ = ppb_graphics3d_shared->gles2_impl();
    123     host_resource = ppb_graphics3d_shared->host_resource();
    124   }
    125 
    126   initialize_callback_ = callback;
    127 
    128   Call<PpapiPluginMsg_VideoDecoder_InitializeReply>(
    129       RENDERER,
    130       PpapiHostMsg_VideoDecoder_Initialize(
    131           host_resource, profile, PP_ToBool(allow_software_fallback)),
    132       base::Bind(&VideoDecoderResource::OnPluginMsgInitializeComplete, this));
    133 
    134   return PP_OK_COMPLETIONPENDING;
    135 }
    136 
    137 int32_t VideoDecoderResource::Decode(uint32_t decode_id,
    138                                      uint32_t size,
    139                                      const void* buffer,
    140                                      scoped_refptr<TrackedCallback> callback) {
    141   if (decoder_last_error_)
    142     return decoder_last_error_;
    143   if (flush_callback_ || reset_callback_)
    144     return PP_ERROR_FAILED;
    145   if (decode_callback_)
    146     return PP_ERROR_INPROGRESS;
    147   if (size > kMaximumBitstreamBufferSize)
    148     return PP_ERROR_NOMEMORY;
    149 
    150   // If we allow the plugin to call Decode again, we must have somewhere to
    151   // copy their buffer.
    152   DCHECK(!available_shm_buffers_.empty() ||
    153          shm_buffers_.size() < kMaximumPendingDecodes);
    154 
    155   // Count up, wrapping back to 0 before overflowing.
    156   int32_t uid = ++num_decodes_;
    157   if (uid == std::numeric_limits<int32_t>::max())
    158     num_decodes_ = 0;
    159 
    160   // Save decode_id in a ring buffer. The ring buffer is sized to store
    161   // decode_id for the maximum picture delay.
    162   decode_ids_[uid % kMaximumPictureDelay] = decode_id;
    163 
    164   if (available_shm_buffers_.empty() ||
    165       available_shm_buffers_.back()->shm->mapped_size() < size) {
    166     uint32_t shm_id;
    167     if (shm_buffers_.size() < kMaximumPendingDecodes) {
    168       // Signal the host to create a new shm buffer by passing an index outside
    169       // the legal range.
    170       shm_id = static_cast<uint32_t>(shm_buffers_.size());
    171     } else {
    172       // Signal the host to grow a buffer by passing a legal index. Choose the
    173       // last available shm buffer for simplicity.
    174       shm_id = available_shm_buffers_.back()->shm_id;
    175       available_shm_buffers_.pop_back();
    176     }
    177 
    178     // Synchronously get shared memory. Use GenericSyncCall so we can get the
    179     // reply params, which contain the handle.
    180     uint32_t shm_size = 0;
    181     IPC::Message reply;
    182     ResourceMessageReplyParams reply_params;
    183     int32_t result =
    184         GenericSyncCall(RENDERER,
    185                         PpapiHostMsg_VideoDecoder_GetShm(shm_id, size),
    186                         &reply,
    187                         &reply_params);
    188     if (result != PP_OK)
    189       return PP_ERROR_FAILED;
    190     if (!UnpackMessage<PpapiPluginMsg_VideoDecoder_GetShmReply>(reply,
    191                                                                 &shm_size))
    192       return PP_ERROR_FAILED;
    193     base::SharedMemoryHandle shm_handle = base::SharedMemory::NULLHandle();
    194     if (!reply_params.TakeSharedMemoryHandleAtIndex(0, &shm_handle))
    195       return PP_ERROR_NOMEMORY;
    196     scoped_ptr<base::SharedMemory> shm(
    197         new base::SharedMemory(shm_handle, false /* read_only */));
    198     scoped_ptr<ShmBuffer> shm_buffer(
    199         new ShmBuffer(shm.Pass(), shm_size, shm_id));
    200     if (!shm_buffer->addr)
    201       return PP_ERROR_NOMEMORY;
    202 
    203     available_shm_buffers_.push_back(shm_buffer.get());
    204     if (shm_buffers_.size() < kMaximumPendingDecodes) {
    205       shm_buffers_.push_back(shm_buffer.release());
    206     } else {
    207       // Delete manually since ScopedVector won't delete the existing element if
    208       // we just assign it.
    209       delete shm_buffers_[shm_id];
    210       shm_buffers_[shm_id] = shm_buffer.release();
    211     }
    212   }
    213 
    214   // At this point we should have shared memory to hold the plugin's buffer.
    215   DCHECK(!available_shm_buffers_.empty() &&
    216          available_shm_buffers_.back()->shm->mapped_size() >= size);
    217 
    218   ShmBuffer* shm_buffer = available_shm_buffers_.back();
    219   available_shm_buffers_.pop_back();
    220   memcpy(shm_buffer->addr, buffer, size);
    221 
    222   Call<PpapiPluginMsg_VideoDecoder_DecodeReply>(
    223       RENDERER,
    224       PpapiHostMsg_VideoDecoder_Decode(shm_buffer->shm_id, size, uid),
    225       base::Bind(&VideoDecoderResource::OnPluginMsgDecodeComplete, this));
    226 
    227   // If we have another free buffer, or we can still create new buffers, let
    228   // the plugin call Decode again.
    229   if (!available_shm_buffers_.empty() ||
    230       shm_buffers_.size() < kMaximumPendingDecodes)
    231     return PP_OK;
    232 
    233   // All buffers are busy and we can't create more. Delay completion until a
    234   // buffer is available.
    235   decode_callback_ = callback;
    236   return PP_OK_COMPLETIONPENDING;
    237 }
    238 
    239 int32_t VideoDecoderResource::GetPicture(
    240     PP_VideoPicture* picture,
    241     scoped_refptr<TrackedCallback> callback) {
    242   if (decoder_last_error_)
    243     return decoder_last_error_;
    244   if (reset_callback_)
    245     return PP_ERROR_FAILED;
    246   if (get_picture_callback_)
    247     return PP_ERROR_INPROGRESS;
    248 
    249   // If the next picture is ready, return it synchronously.
    250   if (!received_pictures_.empty()) {
    251     WriteNextPicture(picture);
    252     return PP_OK;
    253   }
    254 
    255   get_picture_callback_ = callback;
    256   get_picture_ = picture;
    257   return PP_OK_COMPLETIONPENDING;
    258 }
    259 
    260 void VideoDecoderResource::RecyclePicture(const PP_VideoPicture* picture) {
    261   if (decoder_last_error_)
    262     return;
    263 
    264   Post(RENDERER, PpapiHostMsg_VideoDecoder_RecyclePicture(picture->texture_id));
    265 }
    266 
    267 int32_t VideoDecoderResource::Flush(scoped_refptr<TrackedCallback> callback) {
    268   if (decoder_last_error_)
    269     return decoder_last_error_;
    270   if (reset_callback_)
    271     return PP_ERROR_FAILED;
    272   if (flush_callback_)
    273     return PP_ERROR_INPROGRESS;
    274   flush_callback_ = callback;
    275 
    276   Call<PpapiPluginMsg_VideoDecoder_FlushReply>(
    277       RENDERER,
    278       PpapiHostMsg_VideoDecoder_Flush(),
    279       base::Bind(&VideoDecoderResource::OnPluginMsgFlushComplete, this));
    280 
    281   return PP_OK_COMPLETIONPENDING;
    282 }
    283 
    284 int32_t VideoDecoderResource::Reset(scoped_refptr<TrackedCallback> callback) {
    285   if (decoder_last_error_)
    286     return decoder_last_error_;
    287   if (flush_callback_)
    288     return PP_ERROR_FAILED;
    289   if (reset_callback_)
    290     return PP_ERROR_INPROGRESS;
    291   reset_callback_ = callback;
    292 
    293   // Cause any pending Decode or GetPicture callbacks to abort after we return,
    294   // to avoid reentering the plugin.
    295   if (TrackedCallback::IsPending(decode_callback_))
    296     decode_callback_->PostAbort();
    297   decode_callback_ = NULL;
    298   if (TrackedCallback::IsPending(get_picture_callback_))
    299     get_picture_callback_->PostAbort();
    300   get_picture_callback_ = NULL;
    301   Call<PpapiPluginMsg_VideoDecoder_ResetReply>(
    302       RENDERER,
    303       PpapiHostMsg_VideoDecoder_Reset(),
    304       base::Bind(&VideoDecoderResource::OnPluginMsgResetComplete, this));
    305 
    306   return PP_OK_COMPLETIONPENDING;
    307 }
    308 
    309 void VideoDecoderResource::OnReplyReceived(
    310     const ResourceMessageReplyParams& params,
    311     const IPC::Message& msg) {
    312   PPAPI_BEGIN_MESSAGE_MAP(VideoDecoderResource, msg)
    313     PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(
    314         PpapiPluginMsg_VideoDecoder_RequestTextures, OnPluginMsgRequestTextures)
    315     PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(
    316         PpapiPluginMsg_VideoDecoder_PictureReady, OnPluginMsgPictureReady)
    317     PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(
    318         PpapiPluginMsg_VideoDecoder_DismissPicture, OnPluginMsgDismissPicture)
    319     PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(
    320         PpapiPluginMsg_VideoDecoder_NotifyError, OnPluginMsgNotifyError)
    321     PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL_UNHANDLED(
    322         PluginResource::OnReplyReceived(params, msg))
    323   PPAPI_END_MESSAGE_MAP()
    324 }
    325 
    326 void VideoDecoderResource::SetForTest() {
    327   testing_ = true;
    328 }
    329 
    330 void VideoDecoderResource::OnPluginMsgRequestTextures(
    331     const ResourceMessageReplyParams& params,
    332     uint32_t num_textures,
    333     const PP_Size& size,
    334     uint32_t texture_target,
    335     const std::vector<gpu::Mailbox>& mailboxes) {
    336   DCHECK(num_textures);
    337   DCHECK(mailboxes.empty() || mailboxes.size() == num_textures);
    338   std::vector<uint32_t> texture_ids(num_textures);
    339   if (gles2_impl_) {
    340     gles2_impl_->GenTextures(num_textures, &texture_ids.front());
    341     for (uint32_t i = 0; i < num_textures; ++i) {
    342       gles2_impl_->ActiveTexture(GL_TEXTURE0);
    343       gles2_impl_->BindTexture(texture_target, texture_ids[i]);
    344       gles2_impl_->TexParameteri(
    345           texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    346       gles2_impl_->TexParameteri(
    347           texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    348       gles2_impl_->TexParameterf(
    349           texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    350       gles2_impl_->TexParameterf(
    351           texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    352 
    353       if (texture_target == GL_TEXTURE_2D) {
    354         gles2_impl_->TexImage2D(texture_target,
    355                                 0,
    356                                 GL_RGBA,
    357                                 size.width,
    358                                 size.height,
    359                                 0,
    360                                 GL_RGBA,
    361                                 GL_UNSIGNED_BYTE,
    362                                 NULL);
    363       }
    364       if (!mailboxes.empty()) {
    365         gles2_impl_->ProduceTextureCHROMIUM(
    366             GL_TEXTURE_2D, reinterpret_cast<const GLbyte*>(mailboxes[i].name));
    367       }
    368 
    369       textures_.insert(
    370           std::make_pair(texture_ids[i], Texture(texture_target, size)));
    371     }
    372     gles2_impl_->Flush();
    373   } else {
    374     DCHECK(testing_);
    375     // Create some fake texture ids so we can test picture handling.
    376     for (uint32_t i = 0; i < num_textures; ++i) {
    377       texture_ids[i] = i + 1;
    378       textures_.insert(
    379           std::make_pair(texture_ids[i], Texture(texture_target, size)));
    380     }
    381   }
    382 
    383   Post(RENDERER, PpapiHostMsg_VideoDecoder_AssignTextures(size, texture_ids));
    384 }
    385 
    386 void VideoDecoderResource::OnPluginMsgPictureReady(
    387     const ResourceMessageReplyParams& params,
    388     int32_t decode_id,
    389     uint32_t texture_id) {
    390   received_pictures_.push(Picture(decode_id, texture_id));
    391 
    392   if (TrackedCallback::IsPending(get_picture_callback_)) {
    393     // The plugin may call GetPicture in its callback.
    394     scoped_refptr<TrackedCallback> callback;
    395     callback.swap(get_picture_callback_);
    396     PP_VideoPicture* picture = get_picture_;
    397     get_picture_ = NULL;
    398     WriteNextPicture(picture);
    399     callback->Run(PP_OK);
    400   }
    401 }
    402 
    403 void VideoDecoderResource::OnPluginMsgDismissPicture(
    404     const ResourceMessageReplyParams& params,
    405     uint32_t texture_id) {
    406   DeleteGLTexture(texture_id);
    407   textures_.erase(texture_id);
    408 }
    409 
    410 void VideoDecoderResource::OnPluginMsgNotifyError(
    411     const ResourceMessageReplyParams& params,
    412     int32_t error) {
    413   decoder_last_error_ = error;
    414   // Cause any pending callbacks to run immediately. Reentrancy isn't a problem,
    415   // since the plugin wasn't calling us.
    416   RunCallbackWithError(&initialize_callback_);
    417   RunCallbackWithError(&decode_callback_);
    418   RunCallbackWithError(&get_picture_callback_);
    419   RunCallbackWithError(&flush_callback_);
    420   RunCallbackWithError(&reset_callback_);
    421 }
    422 
    423 void VideoDecoderResource::OnPluginMsgInitializeComplete(
    424     const ResourceMessageReplyParams& params) {
    425   decoder_last_error_ = params.result();
    426   if (decoder_last_error_ == PP_OK)
    427     initialized_ = true;
    428 
    429   // Let the plugin call Initialize again from its callback in case of failure.
    430   scoped_refptr<TrackedCallback> callback;
    431   callback.swap(initialize_callback_);
    432   callback->Run(decoder_last_error_);
    433 }
    434 
    435 void VideoDecoderResource::OnPluginMsgDecodeComplete(
    436     const ResourceMessageReplyParams& params,
    437     uint32_t shm_id) {
    438   if (shm_id >= shm_buffers_.size()) {
    439     NOTREACHED();
    440     return;
    441   }
    442   // Make the shm buffer available.
    443   available_shm_buffers_.push_back(shm_buffers_[shm_id]);
    444   // If the plugin is waiting, let it call Decode again.
    445   if (decode_callback_) {
    446     scoped_refptr<TrackedCallback> callback;
    447     callback.swap(decode_callback_);
    448     callback->Run(PP_OK);
    449   }
    450 }
    451 
    452 void VideoDecoderResource::OnPluginMsgFlushComplete(
    453     const ResourceMessageReplyParams& params) {
    454   // All shm buffers should have been made available by now.
    455   DCHECK_EQ(shm_buffers_.size(), available_shm_buffers_.size());
    456 
    457   if (get_picture_callback_) {
    458     scoped_refptr<TrackedCallback> callback;
    459     callback.swap(get_picture_callback_);
    460     callback->Abort();
    461   }
    462 
    463   scoped_refptr<TrackedCallback> callback;
    464   callback.swap(flush_callback_);
    465   callback->Run(params.result());
    466 }
    467 
    468 void VideoDecoderResource::OnPluginMsgResetComplete(
    469     const ResourceMessageReplyParams& params) {
    470   // All shm buffers should have been made available by now.
    471   DCHECK_EQ(shm_buffers_.size(), available_shm_buffers_.size());
    472   // Recycle any pictures which haven't been passed to the plugin.
    473   while (!received_pictures_.empty()) {
    474     Post(RENDERER, PpapiHostMsg_VideoDecoder_RecyclePicture(
    475         received_pictures_.front().texture_id));
    476     received_pictures_.pop();
    477   }
    478 
    479   scoped_refptr<TrackedCallback> callback;
    480   callback.swap(reset_callback_);
    481   callback->Run(params.result());
    482 }
    483 
    484 void VideoDecoderResource::RunCallbackWithError(
    485     scoped_refptr<TrackedCallback>* callback) {
    486   if (TrackedCallback::IsPending(*callback)) {
    487     scoped_refptr<TrackedCallback> temp;
    488     callback->swap(temp);
    489     temp->Run(decoder_last_error_);
    490   }
    491 }
    492 
    493 void VideoDecoderResource::DeleteGLTexture(uint32_t id) {
    494   if (gles2_impl_) {
    495     gles2_impl_->DeleteTextures(1, &id);
    496     gles2_impl_->Flush();
    497   }
    498 }
    499 
    500 void VideoDecoderResource::WriteNextPicture(PP_VideoPicture* pp_picture) {
    501   DCHECK(!received_pictures_.empty());
    502   Picture& picture = received_pictures_.front();
    503   // Internally, we identify decodes by a unique id, which the host returns
    504   // to us in the picture. Use this to get the plugin's decode_id.
    505   pp_picture->decode_id = decode_ids_[picture.decode_id % kMaximumPictureDelay];
    506   pp_picture->texture_id = picture.texture_id;
    507   TextureMap::iterator it = textures_.find(picture.texture_id);
    508   if (it != textures_.end()) {
    509     pp_picture->texture_target = it->second.texture_target;
    510     pp_picture->texture_size = it->second.size;
    511   } else {
    512     NOTREACHED();
    513   }
    514   received_pictures_.pop();
    515 }
    516 
    517 }  // namespace proxy
    518 }  // namespace ppapi
    519