Home | History | Annotate | Download | only in pepper
      1 // Copyright (c) 2014 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 "content/renderer/pepper/pepper_video_decoder_host.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/memory/shared_memory.h"
      9 #include "content/common/gpu/client/gpu_channel_host.h"
     10 #include "content/public/renderer/render_thread.h"
     11 #include "content/public/renderer/renderer_ppapi_host.h"
     12 #include "content/renderer/pepper/ppb_graphics_3d_impl.h"
     13 #include "content/renderer/pepper/video_decoder_shim.h"
     14 #include "media/video/video_decode_accelerator.h"
     15 #include "ppapi/c/pp_completion_callback.h"
     16 #include "ppapi/c/pp_errors.h"
     17 #include "ppapi/host/dispatch_host_message.h"
     18 #include "ppapi/host/ppapi_host.h"
     19 #include "ppapi/proxy/ppapi_messages.h"
     20 #include "ppapi/proxy/video_decoder_constants.h"
     21 #include "ppapi/thunk/enter.h"
     22 #include "ppapi/thunk/ppb_graphics_3d_api.h"
     23 
     24 using ppapi::proxy::SerializedHandle;
     25 using ppapi::thunk::EnterResourceNoLock;
     26 using ppapi::thunk::PPB_Graphics3D_API;
     27 
     28 namespace content {
     29 
     30 namespace {
     31 
     32 media::VideoCodecProfile PepperToMediaVideoProfile(PP_VideoProfile profile) {
     33   switch (profile) {
     34     case PP_VIDEOPROFILE_H264BASELINE:
     35       return media::H264PROFILE_BASELINE;
     36     case PP_VIDEOPROFILE_H264MAIN:
     37       return media::H264PROFILE_MAIN;
     38     case PP_VIDEOPROFILE_H264EXTENDED:
     39       return media::H264PROFILE_EXTENDED;
     40     case PP_VIDEOPROFILE_H264HIGH:
     41       return media::H264PROFILE_HIGH;
     42     case PP_VIDEOPROFILE_H264HIGH10PROFILE:
     43       return media::H264PROFILE_HIGH10PROFILE;
     44     case PP_VIDEOPROFILE_H264HIGH422PROFILE:
     45       return media::H264PROFILE_HIGH422PROFILE;
     46     case PP_VIDEOPROFILE_H264HIGH444PREDICTIVEPROFILE:
     47       return media::H264PROFILE_HIGH444PREDICTIVEPROFILE;
     48     case PP_VIDEOPROFILE_H264SCALABLEBASELINE:
     49       return media::H264PROFILE_SCALABLEBASELINE;
     50     case PP_VIDEOPROFILE_H264SCALABLEHIGH:
     51       return media::H264PROFILE_SCALABLEHIGH;
     52     case PP_VIDEOPROFILE_H264STEREOHIGH:
     53       return media::H264PROFILE_STEREOHIGH;
     54     case PP_VIDEOPROFILE_H264MULTIVIEWHIGH:
     55       return media::H264PROFILE_MULTIVIEWHIGH;
     56     case PP_VIDEOPROFILE_VP8_ANY:
     57       return media::VP8PROFILE_ANY;
     58     case PP_VIDEOPROFILE_VP9_ANY:
     59       return media::VP9PROFILE_ANY;
     60     // No default case, to catch unhandled PP_VideoProfile values.
     61   }
     62 
     63   return media::VIDEO_CODEC_PROFILE_UNKNOWN;
     64 }
     65 
     66 }  // namespace
     67 
     68 PepperVideoDecoderHost::PendingDecode::PendingDecode(
     69     uint32_t shm_id,
     70     const ppapi::host::ReplyMessageContext& reply_context)
     71     : shm_id(shm_id), reply_context(reply_context) {
     72 }
     73 
     74 PepperVideoDecoderHost::PendingDecode::~PendingDecode() {
     75 }
     76 
     77 PepperVideoDecoderHost::PepperVideoDecoderHost(RendererPpapiHost* host,
     78                                                PP_Instance instance,
     79                                                PP_Resource resource)
     80     : ResourceHost(host->GetPpapiHost(), instance, resource),
     81       renderer_ppapi_host_(host),
     82       initialized_(false) {
     83 }
     84 
     85 PepperVideoDecoderHost::~PepperVideoDecoderHost() {
     86 }
     87 
     88 int32_t PepperVideoDecoderHost::OnResourceMessageReceived(
     89     const IPC::Message& msg,
     90     ppapi::host::HostMessageContext* context) {
     91   PPAPI_BEGIN_MESSAGE_MAP(PepperVideoDecoderHost, msg)
     92     PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoDecoder_Initialize,
     93                                       OnHostMsgInitialize)
     94     PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoDecoder_GetShm,
     95                                       OnHostMsgGetShm)
     96     PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoDecoder_Decode,
     97                                       OnHostMsgDecode)
     98     PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoDecoder_AssignTextures,
     99                                       OnHostMsgAssignTextures)
    100     PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoDecoder_RecyclePicture,
    101                                       OnHostMsgRecyclePicture)
    102     PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_VideoDecoder_Flush,
    103                                         OnHostMsgFlush)
    104     PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_VideoDecoder_Reset,
    105                                         OnHostMsgReset)
    106   PPAPI_END_MESSAGE_MAP()
    107   return PP_ERROR_FAILED;
    108 }
    109 
    110 int32_t PepperVideoDecoderHost::OnHostMsgInitialize(
    111     ppapi::host::HostMessageContext* context,
    112     const ppapi::HostResource& graphics_context,
    113     PP_VideoProfile profile,
    114     PP_HardwareAcceleration acceleration) {
    115   if (initialized_)
    116     return PP_ERROR_FAILED;
    117 
    118   EnterResourceNoLock<PPB_Graphics3D_API> enter_graphics(
    119       graphics_context.host_resource(), true);
    120   if (enter_graphics.failed())
    121     return PP_ERROR_FAILED;
    122   PPB_Graphics3D_Impl* graphics3d =
    123       static_cast<PPB_Graphics3D_Impl*>(enter_graphics.object());
    124 
    125   int command_buffer_route_id = graphics3d->GetCommandBufferRouteId();
    126   if (!command_buffer_route_id)
    127     return PP_ERROR_FAILED;
    128 
    129   media::VideoCodecProfile media_profile = PepperToMediaVideoProfile(profile);
    130 
    131   if (acceleration != PP_HARDWAREACCELERATION_NONE) {
    132     // This is not synchronous, but subsequent IPC messages will be buffered, so
    133     // it is okay to immediately send IPC messages through the returned channel.
    134     GpuChannelHost* channel = graphics3d->channel();
    135     DCHECK(channel);
    136     decoder_ = channel->CreateVideoDecoder(command_buffer_route_id);
    137     if (decoder_ && decoder_->Initialize(media_profile, this)) {
    138       initialized_ = true;
    139       return PP_OK;
    140     }
    141     decoder_.reset();
    142     if (acceleration == PP_HARDWAREACCELERATION_ONLY)
    143       return PP_ERROR_NOTSUPPORTED;
    144   }
    145 
    146 #if defined(OS_ANDROID)
    147   return PP_ERROR_NOTSUPPORTED;
    148 #else
    149   decoder_.reset(new VideoDecoderShim(this));
    150   initialize_reply_context_ = context->MakeReplyMessageContext();
    151   decoder_->Initialize(media_profile, this);
    152 
    153   return PP_OK_COMPLETIONPENDING;
    154 #endif
    155 }
    156 
    157 int32_t PepperVideoDecoderHost::OnHostMsgGetShm(
    158     ppapi::host::HostMessageContext* context,
    159     uint32_t shm_id,
    160     uint32_t shm_size) {
    161   if (!initialized_)
    162     return PP_ERROR_FAILED;
    163 
    164   // Make the buffers larger since we hope to reuse them.
    165   shm_size = std::max(
    166       shm_size,
    167       static_cast<uint32_t>(ppapi::proxy::kMinimumBitstreamBufferSize));
    168   if (shm_size > ppapi::proxy::kMaximumBitstreamBufferSize)
    169     return PP_ERROR_FAILED;
    170 
    171   if (shm_id >= ppapi::proxy::kMaximumPendingDecodes)
    172     return PP_ERROR_FAILED;
    173   // The shm_id must be inside or at the end of shm_buffers_.
    174   if (shm_id > shm_buffers_.size())
    175     return PP_ERROR_FAILED;
    176   // Reject an attempt to reallocate a busy shm buffer.
    177   if (shm_id < shm_buffers_.size() && shm_buffer_busy_[shm_id])
    178     return PP_ERROR_FAILED;
    179 
    180   content::RenderThread* render_thread = content::RenderThread::Get();
    181   scoped_ptr<base::SharedMemory> shm(
    182       render_thread->HostAllocateSharedMemoryBuffer(shm_size).Pass());
    183   if (!shm || !shm->Map(shm_size))
    184     return PP_ERROR_FAILED;
    185 
    186   base::SharedMemoryHandle shm_handle = shm->handle();
    187   if (shm_id == shm_buffers_.size()) {
    188     shm_buffers_.push_back(shm.release());
    189     shm_buffer_busy_.push_back(false);
    190   } else {
    191     // Remove the old buffer. Delete manually since ScopedVector won't delete
    192     // the existing element if we just assign over it.
    193     delete shm_buffers_[shm_id];
    194     shm_buffers_[shm_id] = shm.release();
    195   }
    196 
    197 #if defined(OS_WIN)
    198   base::PlatformFile platform_file = shm_handle;
    199 #elif defined(OS_POSIX)
    200   base::PlatformFile platform_file = shm_handle.fd;
    201 #else
    202 #error Not implemented.
    203 #endif
    204   SerializedHandle handle(
    205       renderer_ppapi_host_->ShareHandleWithRemote(platform_file, false),
    206       shm_size);
    207   ppapi::host::ReplyMessageContext reply_context =
    208       context->MakeReplyMessageContext();
    209   reply_context.params.AppendHandle(handle);
    210   host()->SendReply(reply_context,
    211                     PpapiPluginMsg_VideoDecoder_GetShmReply(shm_size));
    212 
    213   return PP_OK_COMPLETIONPENDING;
    214 }
    215 
    216 int32_t PepperVideoDecoderHost::OnHostMsgDecode(
    217     ppapi::host::HostMessageContext* context,
    218     uint32_t shm_id,
    219     uint32_t size,
    220     int32_t decode_id) {
    221   if (!initialized_)
    222     return PP_ERROR_FAILED;
    223   DCHECK(decoder_);
    224   // |shm_id| is just an index into shm_buffers_. Make sure it's in range.
    225   if (static_cast<size_t>(shm_id) >= shm_buffers_.size())
    226     return PP_ERROR_FAILED;
    227   // Reject an attempt to pass a busy buffer to the decoder again.
    228   if (shm_buffer_busy_[shm_id])
    229     return PP_ERROR_FAILED;
    230   // Reject non-unique decode_id values.
    231   if (pending_decodes_.find(decode_id) != pending_decodes_.end())
    232     return PP_ERROR_FAILED;
    233 
    234   if (flush_reply_context_.is_valid() || reset_reply_context_.is_valid())
    235     return PP_ERROR_FAILED;
    236 
    237   pending_decodes_.insert(std::make_pair(
    238       decode_id, PendingDecode(shm_id, context->MakeReplyMessageContext())));
    239 
    240   shm_buffer_busy_[shm_id] = true;
    241   decoder_->Decode(
    242       media::BitstreamBuffer(decode_id, shm_buffers_[shm_id]->handle(), size));
    243 
    244   return PP_OK_COMPLETIONPENDING;
    245 }
    246 
    247 int32_t PepperVideoDecoderHost::OnHostMsgAssignTextures(
    248     ppapi::host::HostMessageContext* context,
    249     const PP_Size& size,
    250     const std::vector<uint32_t>& texture_ids) {
    251   if (!initialized_)
    252     return PP_ERROR_FAILED;
    253   DCHECK(decoder_);
    254 
    255   std::vector<media::PictureBuffer> picture_buffers;
    256   for (uint32 i = 0; i < texture_ids.size(); i++) {
    257     media::PictureBuffer buffer(
    258         texture_ids[i],  // Use the texture_id to identify the buffer.
    259         gfx::Size(size.width, size.height),
    260         texture_ids[i]);
    261     picture_buffers.push_back(buffer);
    262   }
    263   decoder_->AssignPictureBuffers(picture_buffers);
    264   return PP_OK;
    265 }
    266 
    267 int32_t PepperVideoDecoderHost::OnHostMsgRecyclePicture(
    268     ppapi::host::HostMessageContext* context,
    269     uint32_t texture_id) {
    270   if (!initialized_)
    271     return PP_ERROR_FAILED;
    272   DCHECK(decoder_);
    273 
    274   decoder_->ReusePictureBuffer(texture_id);
    275   return PP_OK;
    276 }
    277 
    278 int32_t PepperVideoDecoderHost::OnHostMsgFlush(
    279     ppapi::host::HostMessageContext* context) {
    280   if (!initialized_)
    281     return PP_ERROR_FAILED;
    282   DCHECK(decoder_);
    283   if (flush_reply_context_.is_valid() || reset_reply_context_.is_valid())
    284     return PP_ERROR_FAILED;
    285 
    286   flush_reply_context_ = context->MakeReplyMessageContext();
    287   decoder_->Flush();
    288 
    289   return PP_OK_COMPLETIONPENDING;
    290 }
    291 
    292 int32_t PepperVideoDecoderHost::OnHostMsgReset(
    293     ppapi::host::HostMessageContext* context) {
    294   if (!initialized_)
    295     return PP_ERROR_FAILED;
    296   DCHECK(decoder_);
    297   if (flush_reply_context_.is_valid() || reset_reply_context_.is_valid())
    298     return PP_ERROR_FAILED;
    299 
    300   reset_reply_context_ = context->MakeReplyMessageContext();
    301   decoder_->Reset();
    302 
    303   return PP_OK_COMPLETIONPENDING;
    304 }
    305 
    306 void PepperVideoDecoderHost::ProvidePictureBuffers(
    307     uint32 requested_num_of_buffers,
    308     const gfx::Size& dimensions,
    309     uint32 texture_target) {
    310   RequestTextures(requested_num_of_buffers,
    311                   dimensions,
    312                   texture_target,
    313                   std::vector<gpu::Mailbox>());
    314 }
    315 
    316 void PepperVideoDecoderHost::PictureReady(const media::Picture& picture) {
    317   // So far picture.visible_rect is not used. If used, visible_rect should
    318   // be validated since it comes from GPU process and may not be trustworthy.
    319   host()->SendUnsolicitedReply(
    320       pp_resource(),
    321       PpapiPluginMsg_VideoDecoder_PictureReady(picture.bitstream_buffer_id(),
    322                                                picture.picture_buffer_id()));
    323 }
    324 
    325 void PepperVideoDecoderHost::DismissPictureBuffer(int32 picture_buffer_id) {
    326   host()->SendUnsolicitedReply(
    327       pp_resource(),
    328       PpapiPluginMsg_VideoDecoder_DismissPicture(picture_buffer_id));
    329 }
    330 
    331 void PepperVideoDecoderHost::NotifyEndOfBitstreamBuffer(
    332     int32 bitstream_buffer_id) {
    333   PendingDecodeMap::iterator it = pending_decodes_.find(bitstream_buffer_id);
    334   if (it == pending_decodes_.end()) {
    335     NOTREACHED();
    336     return;
    337   }
    338   const PendingDecode& pending_decode = it->second;
    339   host()->SendReply(
    340       pending_decode.reply_context,
    341       PpapiPluginMsg_VideoDecoder_DecodeReply(pending_decode.shm_id));
    342   shm_buffer_busy_[pending_decode.shm_id] = false;
    343   pending_decodes_.erase(it);
    344 }
    345 
    346 void PepperVideoDecoderHost::NotifyFlushDone() {
    347   DCHECK(pending_decodes_.empty());
    348   host()->SendReply(flush_reply_context_,
    349                     PpapiPluginMsg_VideoDecoder_FlushReply());
    350   flush_reply_context_ = ppapi::host::ReplyMessageContext();
    351 }
    352 
    353 void PepperVideoDecoderHost::NotifyResetDone() {
    354   DCHECK(pending_decodes_.empty());
    355   host()->SendReply(reset_reply_context_,
    356                     PpapiPluginMsg_VideoDecoder_ResetReply());
    357   reset_reply_context_ = ppapi::host::ReplyMessageContext();
    358 }
    359 
    360 void PepperVideoDecoderHost::NotifyError(
    361     media::VideoDecodeAccelerator::Error error) {
    362   int32_t pp_error = PP_ERROR_FAILED;
    363   switch (error) {
    364     case media::VideoDecodeAccelerator::UNREADABLE_INPUT:
    365       pp_error = PP_ERROR_MALFORMED_INPUT;
    366       break;
    367     case media::VideoDecodeAccelerator::ILLEGAL_STATE:
    368     case media::VideoDecodeAccelerator::INVALID_ARGUMENT:
    369     case media::VideoDecodeAccelerator::PLATFORM_FAILURE:
    370     case media::VideoDecodeAccelerator::LARGEST_ERROR_ENUM:
    371       pp_error = PP_ERROR_RESOURCE_FAILED;
    372       break;
    373     // No default case, to catch unhandled enum values.
    374   }
    375   host()->SendUnsolicitedReply(
    376       pp_resource(), PpapiPluginMsg_VideoDecoder_NotifyError(pp_error));
    377 }
    378 
    379 void PepperVideoDecoderHost::OnInitializeComplete(int32_t result) {
    380   if (!initialized_) {
    381     if (result == PP_OK)
    382       initialized_ = true;
    383     initialize_reply_context_.params.set_result(result);
    384     host()->SendReply(initialize_reply_context_,
    385                       PpapiPluginMsg_VideoDecoder_InitializeReply());
    386   }
    387 }
    388 
    389 const uint8_t* PepperVideoDecoderHost::DecodeIdToAddress(uint32_t decode_id) {
    390   PendingDecodeMap::const_iterator it = pending_decodes_.find(decode_id);
    391   DCHECK(it != pending_decodes_.end());
    392   uint32_t shm_id = it->second.shm_id;
    393   return static_cast<uint8_t*>(shm_buffers_[shm_id]->memory());
    394 }
    395 
    396 void PepperVideoDecoderHost::RequestTextures(
    397     uint32 requested_num_of_buffers,
    398     const gfx::Size& dimensions,
    399     uint32 texture_target,
    400     const std::vector<gpu::Mailbox>& mailboxes) {
    401   host()->SendUnsolicitedReply(
    402       pp_resource(),
    403       PpapiPluginMsg_VideoDecoder_RequestTextures(
    404           requested_num_of_buffers,
    405           PP_MakeSize(dimensions.width(), dimensions.height()),
    406           texture_target,
    407           mailboxes));
    408 }
    409 
    410 }  // namespace content
    411