Home | History | Annotate | Download | only in pepper
      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 "content/renderer/pepper/pepper_video_capture_host.h"
      6 
      7 #include "content/renderer/pepper/host_globals.h"
      8 #include "content/renderer/pepper/pepper_media_device_manager.h"
      9 #include "content/renderer/pepper/pepper_platform_video_capture.h"
     10 #include "content/renderer/pepper/pepper_plugin_instance_impl.h"
     11 #include "content/renderer/pepper/renderer_ppapi_host_impl.h"
     12 #include "content/renderer/render_frame_impl.h"
     13 #include "media/base/limits.h"
     14 #include "media/base/video_frame.h"
     15 #include "ppapi/host/dispatch_host_message.h"
     16 #include "ppapi/host/ppapi_host.h"
     17 #include "ppapi/proxy/host_dispatcher.h"
     18 #include "ppapi/proxy/ppapi_messages.h"
     19 #include "ppapi/shared_impl/host_resource.h"
     20 #include "ppapi/thunk/enter.h"
     21 #include "ppapi/thunk/ppb_buffer_api.h"
     22 
     23 using ppapi::HostResource;
     24 using ppapi::TrackedCallback;
     25 using ppapi::thunk::EnterResourceNoLock;
     26 using ppapi::thunk::PPB_Buffer_API;
     27 
     28 namespace {
     29 
     30 // Maximum number of buffers to actually allocate.
     31 const uint32_t kMaxBuffers = 20;
     32 
     33 }  // namespace
     34 
     35 namespace content {
     36 
     37 PepperVideoCaptureHost::PepperVideoCaptureHost(RendererPpapiHostImpl* host,
     38                                                PP_Instance instance,
     39                                                PP_Resource resource)
     40     : ResourceHost(host->GetPpapiHost(), instance, resource),
     41       renderer_ppapi_host_(host),
     42       buffer_count_hint_(0),
     43       status_(PP_VIDEO_CAPTURE_STATUS_STOPPED),
     44       enumeration_helper_(this,
     45                           PepperMediaDeviceManager::GetForRenderFrame(
     46                               host->GetRenderFrameForInstance(pp_instance())),
     47                           PP_DEVICETYPE_DEV_VIDEOCAPTURE,
     48                           host->GetDocumentURL(instance)) {
     49 }
     50 
     51 PepperVideoCaptureHost::~PepperVideoCaptureHost() {
     52   Close();
     53 }
     54 
     55 bool PepperVideoCaptureHost::Init() {
     56   return !!renderer_ppapi_host_->GetPluginInstance(pp_instance());
     57 }
     58 
     59 int32_t PepperVideoCaptureHost::OnResourceMessageReceived(
     60     const IPC::Message& msg,
     61     ppapi::host::HostMessageContext* context) {
     62   int32_t result = PP_ERROR_FAILED;
     63   if (enumeration_helper_.HandleResourceMessage(msg, context, &result))
     64     return result;
     65 
     66   PPAPI_BEGIN_MESSAGE_MAP(PepperVideoCaptureHost, msg)
     67     PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoCapture_Open, OnOpen)
     68     PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_VideoCapture_StartCapture,
     69                                         OnStartCapture)
     70     PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoCapture_ReuseBuffer,
     71                                       OnReuseBuffer)
     72     PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_VideoCapture_StopCapture,
     73                                         OnStopCapture)
     74     PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_VideoCapture_Close,
     75                                         OnClose)
     76   PPAPI_END_MESSAGE_MAP()
     77   return PP_ERROR_FAILED;
     78 }
     79 
     80 void PepperVideoCaptureHost::OnInitialized(bool succeeded) {
     81   if (succeeded) {
     82     open_reply_context_.params.set_result(PP_OK);
     83   } else {
     84     DetachPlatformVideoCapture();
     85     open_reply_context_.params.set_result(PP_ERROR_FAILED);
     86   }
     87 
     88   host()->SendReply(open_reply_context_,
     89                     PpapiPluginMsg_VideoCapture_OpenReply());
     90 }
     91 
     92 void PepperVideoCaptureHost::OnStarted() {
     93   if (SetStatus(PP_VIDEO_CAPTURE_STATUS_STARTED, false))
     94     SendStatus();
     95 }
     96 
     97 void PepperVideoCaptureHost::OnStopped() {
     98   if (SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPED, false))
     99     SendStatus();
    100 }
    101 
    102 void PepperVideoCaptureHost::OnPaused() {
    103   if (SetStatus(PP_VIDEO_CAPTURE_STATUS_PAUSED, false))
    104     SendStatus();
    105 }
    106 
    107 void PepperVideoCaptureHost::OnError() {
    108   PostErrorReply();
    109 }
    110 
    111 void PepperVideoCaptureHost::PostErrorReply() {
    112   // It either comes because some error was detected while starting (e.g. 2
    113   // conflicting "master" resolution), or because the browser failed to start
    114   // the capture.
    115   SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPED, true);
    116   host()->SendUnsolicitedReply(
    117       pp_resource(),
    118       PpapiPluginMsg_VideoCapture_OnError(
    119           static_cast<uint32_t>(PP_ERROR_FAILED)));
    120 }
    121 
    122 void PepperVideoCaptureHost::OnFrameReady(
    123     const scoped_refptr<media::VideoFrame>& frame,
    124     media::VideoCaptureFormat format) {
    125   DCHECK(frame.get());
    126 
    127   if (alloc_size_ != frame->coded_size() || buffers_.empty()) {
    128     AllocBuffers(frame->coded_size(), format.frame_rate);
    129     alloc_size_ = frame->coded_size();
    130   }
    131 
    132   for (uint32_t i = 0; i < buffers_.size(); ++i) {
    133     if (!buffers_[i].in_use) {
    134       DCHECK_EQ(frame->format(), media::VideoFrame::I420);
    135       if (buffers_[i].buffer->size() <
    136           media::VideoFrame::AllocationSize(frame->format(),
    137                                             frame->coded_size())) {
    138         // TODO(ihf): handle size mismatches gracefully here.
    139         return;
    140       }
    141       uint8* dst = reinterpret_cast<uint8*>(buffers_[i].data);
    142       COMPILE_ASSERT(media::VideoFrame::kYPlane == 0, y_plane_should_be_0);
    143       COMPILE_ASSERT(media::VideoFrame::kUPlane == 1, u_plane_should_be_1);
    144       COMPILE_ASSERT(media::VideoFrame::kVPlane == 2, v_plane_should_be_2);
    145       for (size_t j = 0; j < media::VideoFrame::NumPlanes(frame->format());
    146            ++j) {
    147         const uint8* src = frame->data(j);
    148         const size_t row_bytes = frame->row_bytes(j);
    149         const size_t src_stride = frame->stride(j);
    150         for (int k = 0; k < frame->rows(j); ++k) {
    151           memcpy(dst, src, row_bytes);
    152           dst += row_bytes;
    153           src += src_stride;
    154         }
    155       }
    156       buffers_[i].in_use = true;
    157       host()->SendUnsolicitedReply(
    158           pp_resource(), PpapiPluginMsg_VideoCapture_OnBufferReady(i));
    159       return;
    160     }
    161   }
    162 }
    163 
    164 void PepperVideoCaptureHost::AllocBuffers(const gfx::Size& resolution,
    165                                           int frame_rate) {
    166   PP_VideoCaptureDeviceInfo_Dev info = {
    167       static_cast<uint32_t>(resolution.width()),
    168       static_cast<uint32_t>(resolution.height()),
    169       static_cast<uint32_t>(frame_rate)};
    170   ReleaseBuffers();
    171 
    172   const size_t size = media::VideoFrame::AllocationSize(
    173       media::VideoFrame::I420, gfx::Size(info.width, info.height));
    174 
    175   ppapi::proxy::ResourceMessageReplyParams params(pp_resource(), 0);
    176 
    177   // Allocate buffers. We keep a reference to them, that is released in
    178   // ReleaseBuffers. In the mean time, we prepare the resource and handle here
    179   // for sending below.
    180   std::vector<HostResource> buffer_host_resources;
    181   buffers_.reserve(buffer_count_hint_);
    182   ppapi::ResourceTracker* tracker = HostGlobals::Get()->GetResourceTracker();
    183   ppapi::proxy::HostDispatcher* dispatcher =
    184       ppapi::proxy::HostDispatcher::GetForInstance(pp_instance());
    185   for (size_t i = 0; i < buffer_count_hint_; ++i) {
    186     PP_Resource res = PPB_Buffer_Impl::Create(pp_instance(), size);
    187     if (!res)
    188       break;
    189 
    190     EnterResourceNoLock<PPB_Buffer_API> enter(res, true);
    191     DCHECK(enter.succeeded());
    192 
    193     BufferInfo buf;
    194     buf.buffer = static_cast<PPB_Buffer_Impl*>(enter.object());
    195     buf.data = buf.buffer->Map();
    196     if (!buf.data) {
    197       tracker->ReleaseResource(res);
    198       break;
    199     }
    200     buffers_.push_back(buf);
    201 
    202     // Add to HostResource array to be sent.
    203     {
    204       HostResource host_resource;
    205       host_resource.SetHostResource(pp_instance(), res);
    206       buffer_host_resources.push_back(host_resource);
    207 
    208       // Add a reference for the plugin, which is resposible for releasing it.
    209       tracker->AddRefResource(res);
    210     }
    211 
    212     // Add the serialized shared memory handle to params. FileDescriptor is
    213     // treated in special case.
    214     {
    215       EnterResourceNoLock<PPB_Buffer_API> enter(res, true);
    216       DCHECK(enter.succeeded());
    217       int handle;
    218       int32_t result = enter.object()->GetSharedMemory(&handle);
    219       DCHECK(result == PP_OK);
    220       // TODO(piman/brettw): Change trusted interface to return a PP_FileHandle,
    221       // those casts are ugly.
    222       base::PlatformFile platform_file =
    223 #if defined(OS_WIN)
    224           reinterpret_cast<HANDLE>(static_cast<intptr_t>(handle));
    225 #elif defined(OS_POSIX)
    226           handle;
    227 #else
    228 #error Not implemented.
    229 #endif
    230       params.AppendHandle(ppapi::proxy::SerializedHandle(
    231           dispatcher->ShareHandleWithRemote(platform_file, false), size));
    232     }
    233   }
    234 
    235   if (buffers_.empty()) {
    236     // We couldn't allocate/map buffers at all. Send an error and stop the
    237     // capture.
    238     SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPING, true);
    239     platform_video_capture_->StopCapture();
    240     PostErrorReply();
    241     return;
    242   }
    243 
    244   host()->Send(
    245       new PpapiPluginMsg_ResourceReply(params,
    246                                        PpapiPluginMsg_VideoCapture_OnDeviceInfo(
    247                                            info, buffer_host_resources, size)));
    248 }
    249 
    250 int32_t PepperVideoCaptureHost::OnOpen(
    251     ppapi::host::HostMessageContext* context,
    252     const std::string& device_id,
    253     const PP_VideoCaptureDeviceInfo_Dev& requested_info,
    254     uint32_t buffer_count) {
    255   if (platform_video_capture_.get())
    256     return PP_ERROR_FAILED;
    257 
    258   SetRequestedInfo(requested_info, buffer_count);
    259 
    260   GURL document_url = renderer_ppapi_host_->GetDocumentURL(pp_instance());
    261   if (!document_url.is_valid())
    262     return PP_ERROR_FAILED;
    263 
    264   platform_video_capture_.reset(new PepperPlatformVideoCapture(
    265       renderer_ppapi_host_->GetRenderFrameForInstance(pp_instance())->
    266           GetRoutingID(),
    267       device_id,
    268       document_url,
    269       this));
    270 
    271   open_reply_context_ = context->MakeReplyMessageContext();
    272 
    273   return PP_OK_COMPLETIONPENDING;
    274 }
    275 
    276 int32_t PepperVideoCaptureHost::OnStartCapture(
    277     ppapi::host::HostMessageContext* context) {
    278   if (!SetStatus(PP_VIDEO_CAPTURE_STATUS_STARTING, false) ||
    279       !platform_video_capture_.get())
    280     return PP_ERROR_FAILED;
    281 
    282   DCHECK(buffers_.empty());
    283 
    284   // It's safe to call this regardless it's capturing or not, because
    285   // PepperPlatformVideoCapture maintains the state.
    286   platform_video_capture_->StartCapture(video_capture_params_);
    287   return PP_OK;
    288 }
    289 
    290 int32_t PepperVideoCaptureHost::OnReuseBuffer(
    291     ppapi::host::HostMessageContext* context,
    292     uint32_t buffer) {
    293   if (buffer >= buffers_.size() || !buffers_[buffer].in_use)
    294     return PP_ERROR_BADARGUMENT;
    295   buffers_[buffer].in_use = false;
    296   return PP_OK;
    297 }
    298 
    299 int32_t PepperVideoCaptureHost::OnStopCapture(
    300     ppapi::host::HostMessageContext* context) {
    301   return StopCapture();
    302 }
    303 
    304 int32_t PepperVideoCaptureHost::OnClose(
    305     ppapi::host::HostMessageContext* context) {
    306   return Close();
    307 }
    308 
    309 int32_t PepperVideoCaptureHost::StopCapture() {
    310   if (!SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPING, false))
    311     return PP_ERROR_FAILED;
    312 
    313   DCHECK(platform_video_capture_.get());
    314 
    315   ReleaseBuffers();
    316   // It's safe to call this regardless it's capturing or not, because
    317   // PepperPlatformVideoCapture maintains the state.
    318   platform_video_capture_->StopCapture();
    319   return PP_OK;
    320 }
    321 
    322 int32_t PepperVideoCaptureHost::Close() {
    323   if (!platform_video_capture_.get())
    324     return PP_OK;
    325 
    326   StopCapture();
    327   DCHECK(buffers_.empty());
    328   DetachPlatformVideoCapture();
    329   return PP_OK;
    330 }
    331 
    332 void PepperVideoCaptureHost::ReleaseBuffers() {
    333   ppapi::ResourceTracker* tracker = HostGlobals::Get()->GetResourceTracker();
    334   for (size_t i = 0; i < buffers_.size(); ++i) {
    335     buffers_[i].buffer->Unmap();
    336     tracker->ReleaseResource(buffers_[i].buffer->pp_resource());
    337   }
    338   buffers_.clear();
    339 }
    340 
    341 void PepperVideoCaptureHost::SendStatus() {
    342   host()->SendUnsolicitedReply(pp_resource(),
    343                                PpapiPluginMsg_VideoCapture_OnStatus(status_));
    344 }
    345 
    346 void PepperVideoCaptureHost::SetRequestedInfo(
    347     const PP_VideoCaptureDeviceInfo_Dev& device_info,
    348     uint32_t buffer_count) {
    349   // Clamp the buffer count to between 1 and |kMaxBuffers|.
    350   buffer_count_hint_ = std::min(std::max(buffer_count, 1U), kMaxBuffers);
    351   // Clamp the frame rate to between 1 and |kMaxFramesPerSecond - 1|.
    352   int frames_per_second =
    353       std::min(std::max(device_info.frames_per_second, 1U),
    354                static_cast<uint32_t>(media::limits::kMaxFramesPerSecond - 1));
    355 
    356   video_capture_params_.requested_format = media::VideoCaptureFormat(
    357       gfx::Size(device_info.width, device_info.height),
    358       frames_per_second,
    359       media::PIXEL_FORMAT_I420);
    360 }
    361 
    362 void PepperVideoCaptureHost::DetachPlatformVideoCapture() {
    363   if (platform_video_capture_) {
    364     platform_video_capture_->DetachEventHandler();
    365     platform_video_capture_.reset();
    366   }
    367 }
    368 
    369 bool PepperVideoCaptureHost::SetStatus(PP_VideoCaptureStatus_Dev status,
    370                                        bool forced) {
    371   if (!forced) {
    372     switch (status) {
    373       case PP_VIDEO_CAPTURE_STATUS_STOPPED:
    374         if (status_ != PP_VIDEO_CAPTURE_STATUS_STOPPING)
    375           return false;
    376         break;
    377       case PP_VIDEO_CAPTURE_STATUS_STARTING:
    378         if (status_ != PP_VIDEO_CAPTURE_STATUS_STOPPED)
    379           return false;
    380         break;
    381       case PP_VIDEO_CAPTURE_STATUS_STARTED:
    382         switch (status_) {
    383           case PP_VIDEO_CAPTURE_STATUS_STARTING:
    384           case PP_VIDEO_CAPTURE_STATUS_PAUSED:
    385             break;
    386           default:
    387             return false;
    388         }
    389         break;
    390       case PP_VIDEO_CAPTURE_STATUS_PAUSED:
    391         switch (status_) {
    392           case PP_VIDEO_CAPTURE_STATUS_STARTING:
    393           case PP_VIDEO_CAPTURE_STATUS_STARTED:
    394             break;
    395           default:
    396             return false;
    397         }
    398         break;
    399       case PP_VIDEO_CAPTURE_STATUS_STOPPING:
    400         switch (status_) {
    401           case PP_VIDEO_CAPTURE_STATUS_STARTING:
    402           case PP_VIDEO_CAPTURE_STATUS_STARTED:
    403           case PP_VIDEO_CAPTURE_STATUS_PAUSED:
    404             break;
    405           default:
    406             return false;
    407         }
    408         break;
    409     }
    410   }
    411 
    412   status_ = status;
    413   return true;
    414 }
    415 
    416 PepperVideoCaptureHost::BufferInfo::BufferInfo()
    417     : in_use(false), data(NULL), buffer() {
    418 }
    419 
    420 PepperVideoCaptureHost::BufferInfo::~BufferInfo() {
    421 }
    422 
    423 }  // namespace content
    424