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_VP8MAIN:
     57       return media::VP8PROFILE_MAIN;
     58     case PP_VIDEOPROFILE_VP9MAIN:
     59       return media::VP9PROFILE_MAIN;
     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     bool allow_software_fallback) {
    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   // This is not synchronous, but subsequent IPC messages will be buffered, so
    132   // it is okay to immediately send IPC messages through the returned channel.
    133   GpuChannelHost* channel = graphics3d->channel();
    134   DCHECK(channel);
    135   decoder_ = channel->CreateVideoDecoder(command_buffer_route_id);
    136   if (decoder_ && decoder_->Initialize(media_profile, this)) {
    137     initialized_ = true;
    138     return PP_OK;
    139   }
    140   decoder_.reset();
    141 
    142   if (!allow_software_fallback)
    143     return PP_ERROR_NOTSUPPORTED;
    144 
    145   decoder_.reset(new VideoDecoderShim(this));
    146   initialize_reply_context_ = context->MakeReplyMessageContext();
    147   decoder_->Initialize(media_profile, this);
    148 
    149   return PP_OK_COMPLETIONPENDING;
    150 }
    151 
    152 int32_t PepperVideoDecoderHost::OnHostMsgGetShm(
    153     ppapi::host::HostMessageContext* context,
    154     uint32_t shm_id,
    155     uint32_t shm_size) {
    156   if (!initialized_)
    157     return PP_ERROR_FAILED;
    158 
    159   // Make the buffers larger since we hope to reuse them.
    160   shm_size = std::max(
    161       shm_size,
    162       static_cast<uint32_t>(ppapi::proxy::kMinimumBitstreamBufferSize));
    163   if (shm_size > ppapi::proxy::kMaximumBitstreamBufferSize)
    164     return PP_ERROR_FAILED;
    165 
    166   if (shm_id >= ppapi::proxy::kMaximumPendingDecodes)
    167     return PP_ERROR_FAILED;
    168   // The shm_id must be inside or at the end of shm_buffers_.
    169   if (shm_id > shm_buffers_.size())
    170     return PP_ERROR_FAILED;
    171   // Reject an attempt to reallocate a busy shm buffer.
    172   if (shm_id < shm_buffers_.size() && shm_buffer_busy_[shm_id])
    173     return PP_ERROR_FAILED;
    174 
    175   content::RenderThread* render_thread = content::RenderThread::Get();
    176   scoped_ptr<base::SharedMemory> shm(
    177       render_thread->HostAllocateSharedMemoryBuffer(shm_size).Pass());
    178   if (!shm || !shm->Map(shm_size))
    179     return PP_ERROR_FAILED;
    180 
    181   base::SharedMemoryHandle shm_handle = shm->handle();
    182   if (shm_id == shm_buffers_.size()) {
    183     shm_buffers_.push_back(shm.release());
    184     shm_buffer_busy_.push_back(false);
    185   } else {
    186     // Remove the old buffer. Delete manually since ScopedVector won't delete
    187     // the existing element if we just assign over it.
    188     delete shm_buffers_[shm_id];
    189     shm_buffers_[shm_id] = shm.release();
    190   }
    191 
    192 #if defined(OS_WIN)
    193   base::PlatformFile platform_file = shm_handle;
    194 #elif defined(OS_POSIX)
    195   base::PlatformFile platform_file = shm_handle.fd;
    196 #else
    197 #error Not implemented.
    198 #endif
    199   SerializedHandle handle(
    200       renderer_ppapi_host_->ShareHandleWithRemote(platform_file, false),
    201       shm_size);
    202   ppapi::host::ReplyMessageContext reply_context =
    203       context->MakeReplyMessageContext();
    204   reply_context.params.AppendHandle(handle);
    205   host()->SendReply(reply_context,
    206                     PpapiPluginMsg_VideoDecoder_GetShmReply(shm_size));
    207 
    208   return PP_OK_COMPLETIONPENDING;
    209 }
    210 
    211 int32_t PepperVideoDecoderHost::OnHostMsgDecode(
    212     ppapi::host::HostMessageContext* context,
    213     uint32_t shm_id,
    214     uint32_t size,
    215     int32_t decode_id) {
    216   if (!initialized_)
    217     return PP_ERROR_FAILED;
    218   DCHECK(decoder_);
    219   // |shm_id| is just an index into shm_buffers_. Make sure it's in range.
    220   if (static_cast<size_t>(shm_id) >= shm_buffers_.size())
    221     return PP_ERROR_FAILED;
    222   // Reject an attempt to pass a busy buffer to the decoder again.
    223   if (shm_buffer_busy_[shm_id])
    224     return PP_ERROR_FAILED;
    225   // Reject non-unique decode_id values.
    226   if (pending_decodes_.find(decode_id) != pending_decodes_.end())
    227     return PP_ERROR_FAILED;
    228 
    229   if (flush_reply_context_.is_valid() || reset_reply_context_.is_valid())
    230     return PP_ERROR_FAILED;
    231 
    232   pending_decodes_.insert(std::make_pair(
    233       decode_id, PendingDecode(shm_id, context->MakeReplyMessageContext())));
    234 
    235   shm_buffer_busy_[shm_id] = true;
    236   decoder_->Decode(
    237       media::BitstreamBuffer(decode_id, shm_buffers_[shm_id]->handle(), size));
    238 
    239   return PP_OK_COMPLETIONPENDING;
    240 }
    241 
    242 int32_t PepperVideoDecoderHost::OnHostMsgAssignTextures(
    243     ppapi::host::HostMessageContext* context,
    244     const PP_Size& size,
    245     const std::vector<uint32_t>& texture_ids) {
    246   if (!initialized_)
    247     return PP_ERROR_FAILED;
    248   DCHECK(decoder_);
    249 
    250   std::vector<media::PictureBuffer> picture_buffers;
    251   for (uint32 i = 0; i < texture_ids.size(); i++) {
    252     media::PictureBuffer buffer(
    253         texture_ids[i],  // Use the texture_id to identify the buffer.
    254         gfx::Size(size.width, size.height),
    255         texture_ids[i]);
    256     picture_buffers.push_back(buffer);
    257   }
    258   decoder_->AssignPictureBuffers(picture_buffers);
    259   return PP_OK;
    260 }
    261 
    262 int32_t PepperVideoDecoderHost::OnHostMsgRecyclePicture(
    263     ppapi::host::HostMessageContext* context,
    264     uint32_t texture_id) {
    265   if (!initialized_)
    266     return PP_ERROR_FAILED;
    267   DCHECK(decoder_);
    268 
    269   decoder_->ReusePictureBuffer(texture_id);
    270   return PP_OK;
    271 }
    272 
    273 int32_t PepperVideoDecoderHost::OnHostMsgFlush(
    274     ppapi::host::HostMessageContext* context) {
    275   if (!initialized_)
    276     return PP_ERROR_FAILED;
    277   DCHECK(decoder_);
    278   if (flush_reply_context_.is_valid() || reset_reply_context_.is_valid())
    279     return PP_ERROR_FAILED;
    280 
    281   flush_reply_context_ = context->MakeReplyMessageContext();
    282   decoder_->Flush();
    283 
    284   return PP_OK_COMPLETIONPENDING;
    285 }
    286 
    287 int32_t PepperVideoDecoderHost::OnHostMsgReset(
    288     ppapi::host::HostMessageContext* context) {
    289   if (!initialized_)
    290     return PP_ERROR_FAILED;
    291   DCHECK(decoder_);
    292   if (flush_reply_context_.is_valid() || reset_reply_context_.is_valid())
    293     return PP_ERROR_FAILED;
    294 
    295   reset_reply_context_ = context->MakeReplyMessageContext();
    296   decoder_->Reset();
    297 
    298   return PP_OK_COMPLETIONPENDING;
    299 }
    300 
    301 void PepperVideoDecoderHost::ProvidePictureBuffers(
    302     uint32 requested_num_of_buffers,
    303     const gfx::Size& dimensions,
    304     uint32 texture_target) {
    305   RequestTextures(requested_num_of_buffers,
    306                   dimensions,
    307                   texture_target,
    308                   std::vector<gpu::Mailbox>());
    309 }
    310 
    311 void PepperVideoDecoderHost::PictureReady(const media::Picture& picture) {
    312   host()->SendUnsolicitedReply(
    313       pp_resource(),
    314       PpapiPluginMsg_VideoDecoder_PictureReady(picture.bitstream_buffer_id(),
    315                                                picture.picture_buffer_id()));
    316 }
    317 
    318 void PepperVideoDecoderHost::DismissPictureBuffer(int32 picture_buffer_id) {
    319   host()->SendUnsolicitedReply(
    320       pp_resource(),
    321       PpapiPluginMsg_VideoDecoder_DismissPicture(picture_buffer_id));
    322 }
    323 
    324 void PepperVideoDecoderHost::NotifyEndOfBitstreamBuffer(
    325     int32 bitstream_buffer_id) {
    326   PendingDecodeMap::iterator it = pending_decodes_.find(bitstream_buffer_id);
    327   if (it == pending_decodes_.end()) {
    328     NOTREACHED();
    329     return;
    330   }
    331   const PendingDecode& pending_decode = it->second;
    332   host()->SendReply(
    333       pending_decode.reply_context,
    334       PpapiPluginMsg_VideoDecoder_DecodeReply(pending_decode.shm_id));
    335   shm_buffer_busy_[pending_decode.shm_id] = false;
    336   pending_decodes_.erase(it);
    337 }
    338 
    339 void PepperVideoDecoderHost::NotifyFlushDone() {
    340   DCHECK(pending_decodes_.empty());
    341   host()->SendReply(flush_reply_context_,
    342                     PpapiPluginMsg_VideoDecoder_FlushReply());
    343   flush_reply_context_ = ppapi::host::ReplyMessageContext();
    344 }
    345 
    346 void PepperVideoDecoderHost::NotifyResetDone() {
    347   DCHECK(pending_decodes_.empty());
    348   host()->SendReply(reset_reply_context_,
    349                     PpapiPluginMsg_VideoDecoder_ResetReply());
    350   reset_reply_context_ = ppapi::host::ReplyMessageContext();
    351 }
    352 
    353 void PepperVideoDecoderHost::NotifyError(
    354     media::VideoDecodeAccelerator::Error error) {
    355   int32_t pp_error = PP_ERROR_FAILED;
    356   switch (error) {
    357     case media::VideoDecodeAccelerator::UNREADABLE_INPUT:
    358       pp_error = PP_ERROR_MALFORMED_INPUT;
    359       break;
    360     case media::VideoDecodeAccelerator::ILLEGAL_STATE:
    361     case media::VideoDecodeAccelerator::INVALID_ARGUMENT:
    362     case media::VideoDecodeAccelerator::PLATFORM_FAILURE:
    363     case media::VideoDecodeAccelerator::LARGEST_ERROR_ENUM:
    364       pp_error = PP_ERROR_RESOURCE_FAILED;
    365       break;
    366     // No default case, to catch unhandled enum values.
    367   }
    368   host()->SendUnsolicitedReply(
    369       pp_resource(), PpapiPluginMsg_VideoDecoder_NotifyError(pp_error));
    370 }
    371 
    372 void PepperVideoDecoderHost::OnInitializeComplete(int32_t result) {
    373   if (!initialized_) {
    374     if (result == PP_OK)
    375       initialized_ = true;
    376     initialize_reply_context_.params.set_result(result);
    377     host()->SendReply(initialize_reply_context_,
    378                       PpapiPluginMsg_VideoDecoder_InitializeReply());
    379   }
    380 }
    381 
    382 const uint8_t* PepperVideoDecoderHost::DecodeIdToAddress(uint32_t decode_id) {
    383   PendingDecodeMap::const_iterator it = pending_decodes_.find(decode_id);
    384   DCHECK(it != pending_decodes_.end());
    385   uint32_t shm_id = it->second.shm_id;
    386   return static_cast<uint8_t*>(shm_buffers_[shm_id]->memory());
    387 }
    388 
    389 void PepperVideoDecoderHost::RequestTextures(
    390     uint32 requested_num_of_buffers,
    391     const gfx::Size& dimensions,
    392     uint32 texture_target,
    393     const std::vector<gpu::Mailbox>& mailboxes) {
    394   host()->SendUnsolicitedReply(
    395       pp_resource(),
    396       PpapiPluginMsg_VideoDecoder_RequestTextures(
    397           requested_num_of_buffers,
    398           PP_MakeSize(dimensions.width(), dimensions.height()),
    399           texture_target,
    400           mailboxes));
    401 }
    402 
    403 }  // namespace content
    404