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_view_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::GetForRenderView(
     46                               host->GetRenderViewForInstance(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(), PpapiPluginMsg_VideoCapture_OnError(PP_ERROR_FAILED));
    118 }
    119 
    120 void PepperVideoCaptureHost::OnFrameReady(
    121     const scoped_refptr<media::VideoFrame>& frame,
    122     media::VideoCaptureFormat format) {
    123   DCHECK(frame.get());
    124 
    125   if (alloc_size_ != frame->coded_size() || buffers_.empty()) {
    126     AllocBuffers(frame->coded_size(), format.frame_rate);
    127     alloc_size_ = frame->coded_size();
    128   }
    129 
    130   for (uint32_t i = 0; i < buffers_.size(); ++i) {
    131     if (!buffers_[i].in_use) {
    132       DCHECK_EQ(frame->format(), media::VideoFrame::I420);
    133       if (buffers_[i].buffer->size() <
    134           media::VideoFrame::AllocationSize(frame->format(),
    135                                             frame->coded_size())) {
    136         // TODO(ihf): handle size mismatches gracefully here.
    137         return;
    138       }
    139       uint8* dst = reinterpret_cast<uint8*>(buffers_[i].data);
    140       COMPILE_ASSERT(media::VideoFrame::kYPlane == 0, y_plane_should_be_0);
    141       COMPILE_ASSERT(media::VideoFrame::kUPlane == 1, u_plane_should_be_1);
    142       COMPILE_ASSERT(media::VideoFrame::kVPlane == 2, v_plane_should_be_2);
    143       for (size_t j = 0; j < media::VideoFrame::NumPlanes(frame->format());
    144            ++j) {
    145         const uint8* src = frame->data(j);
    146         const size_t row_bytes = frame->row_bytes(j);
    147         const size_t src_stride = frame->stride(j);
    148         for (int k = 0; k < frame->rows(j); ++k) {
    149           memcpy(dst, src, row_bytes);
    150           dst += row_bytes;
    151           src += src_stride;
    152         }
    153       }
    154       buffers_[i].in_use = true;
    155       host()->SendUnsolicitedReply(
    156           pp_resource(), PpapiPluginMsg_VideoCapture_OnBufferReady(i));
    157       return;
    158     }
    159   }
    160 }
    161 
    162 void PepperVideoCaptureHost::AllocBuffers(const gfx::Size& resolution,
    163                                           int frame_rate) {
    164   PP_VideoCaptureDeviceInfo_Dev info = {
    165       static_cast<uint32_t>(resolution.width()),
    166       static_cast<uint32_t>(resolution.height()),
    167       static_cast<uint32_t>(frame_rate)};
    168   ReleaseBuffers();
    169 
    170   const size_t size = media::VideoFrame::AllocationSize(
    171       media::VideoFrame::I420, gfx::Size(info.width, info.height));
    172 
    173   ppapi::proxy::ResourceMessageReplyParams params(pp_resource(), 0);
    174 
    175   // Allocate buffers. We keep a reference to them, that is released in
    176   // ReleaseBuffers. In the mean time, we prepare the resource and handle here
    177   // for sending below.
    178   std::vector<HostResource> buffer_host_resources;
    179   buffers_.reserve(buffer_count_hint_);
    180   ppapi::ResourceTracker* tracker = HostGlobals::Get()->GetResourceTracker();
    181   ppapi::proxy::HostDispatcher* dispatcher =
    182       ppapi::proxy::HostDispatcher::GetForInstance(pp_instance());
    183   for (size_t i = 0; i < buffer_count_hint_; ++i) {
    184     PP_Resource res = PPB_Buffer_Impl::Create(pp_instance(), size);
    185     if (!res)
    186       break;
    187 
    188     EnterResourceNoLock<PPB_Buffer_API> enter(res, true);
    189     DCHECK(enter.succeeded());
    190 
    191     BufferInfo buf;
    192     buf.buffer = static_cast<PPB_Buffer_Impl*>(enter.object());
    193     buf.data = buf.buffer->Map();
    194     if (!buf.data) {
    195       tracker->ReleaseResource(res);
    196       break;
    197     }
    198     buffers_.push_back(buf);
    199 
    200     // Add to HostResource array to be sent.
    201     {
    202       HostResource host_resource;
    203       host_resource.SetHostResource(pp_instance(), res);
    204       buffer_host_resources.push_back(host_resource);
    205 
    206       // Add a reference for the plugin, which is resposible for releasing it.
    207       tracker->AddRefResource(res);
    208     }
    209 
    210     // Add the serialized shared memory handle to params. FileDescriptor is
    211     // treated in special case.
    212     {
    213       EnterResourceNoLock<PPB_Buffer_API> enter(res, true);
    214       DCHECK(enter.succeeded());
    215       int handle;
    216       int32_t result = enter.object()->GetSharedMemory(&handle);
    217       DCHECK(result == PP_OK);
    218       // TODO(piman/brettw): Change trusted interface to return a PP_FileHandle,
    219       // those casts are ugly.
    220       base::PlatformFile platform_file =
    221 #if defined(OS_WIN)
    222           reinterpret_cast<HANDLE>(static_cast<intptr_t>(handle));
    223 #elif defined(OS_POSIX)
    224           handle;
    225 #else
    226 #error Not implemented.
    227 #endif
    228       params.AppendHandle(ppapi::proxy::SerializedHandle(
    229           dispatcher->ShareHandleWithRemote(platform_file, false), size));
    230     }
    231   }
    232 
    233   if (buffers_.empty()) {
    234     // We couldn't allocate/map buffers at all. Send an error and stop the
    235     // capture.
    236     SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPING, true);
    237     platform_video_capture_->StopCapture();
    238     PostErrorReply();
    239     return;
    240   }
    241 
    242   host()->Send(
    243       new PpapiPluginMsg_ResourceReply(params,
    244                                        PpapiPluginMsg_VideoCapture_OnDeviceInfo(
    245                                            info, buffer_host_resources, size)));
    246 }
    247 
    248 int32_t PepperVideoCaptureHost::OnOpen(
    249     ppapi::host::HostMessageContext* context,
    250     const std::string& device_id,
    251     const PP_VideoCaptureDeviceInfo_Dev& requested_info,
    252     uint32_t buffer_count) {
    253   if (platform_video_capture_.get())
    254     return PP_ERROR_FAILED;
    255 
    256   SetRequestedInfo(requested_info, buffer_count);
    257 
    258   GURL document_url = renderer_ppapi_host_->GetDocumentURL(pp_instance());
    259   if (!document_url.is_valid())
    260     return PP_ERROR_FAILED;
    261 
    262   RenderViewImpl* render_view = static_cast<RenderViewImpl*>(
    263       renderer_ppapi_host_->GetRenderViewForInstance(pp_instance()));
    264 
    265   platform_video_capture_.reset(new PepperPlatformVideoCapture(
    266       render_view->AsWeakPtr(), device_id, document_url, this));
    267 
    268   open_reply_context_ = context->MakeReplyMessageContext();
    269 
    270   return PP_OK_COMPLETIONPENDING;
    271 }
    272 
    273 int32_t PepperVideoCaptureHost::OnStartCapture(
    274     ppapi::host::HostMessageContext* context) {
    275   if (!SetStatus(PP_VIDEO_CAPTURE_STATUS_STARTING, false) ||
    276       !platform_video_capture_.get())
    277     return PP_ERROR_FAILED;
    278 
    279   DCHECK(buffers_.empty());
    280 
    281   // It's safe to call this regardless it's capturing or not, because
    282   // PepperPlatformVideoCapture maintains the state.
    283   platform_video_capture_->StartCapture(video_capture_params_);
    284   return PP_OK;
    285 }
    286 
    287 int32_t PepperVideoCaptureHost::OnReuseBuffer(
    288     ppapi::host::HostMessageContext* context,
    289     uint32_t buffer) {
    290   if (buffer >= buffers_.size() || !buffers_[buffer].in_use)
    291     return PP_ERROR_BADARGUMENT;
    292   buffers_[buffer].in_use = false;
    293   return PP_OK;
    294 }
    295 
    296 int32_t PepperVideoCaptureHost::OnStopCapture(
    297     ppapi::host::HostMessageContext* context) {
    298   return StopCapture();
    299 }
    300 
    301 int32_t PepperVideoCaptureHost::OnClose(
    302     ppapi::host::HostMessageContext* context) {
    303   return Close();
    304 }
    305 
    306 int32_t PepperVideoCaptureHost::StopCapture() {
    307   if (!SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPING, false))
    308     return PP_ERROR_FAILED;
    309 
    310   DCHECK(platform_video_capture_.get());
    311 
    312   ReleaseBuffers();
    313   // It's safe to call this regardless it's capturing or not, because
    314   // PepperPlatformVideoCapture maintains the state.
    315   platform_video_capture_->StopCapture();
    316   return PP_OK;
    317 }
    318 
    319 int32_t PepperVideoCaptureHost::Close() {
    320   if (!platform_video_capture_.get())
    321     return PP_OK;
    322 
    323   StopCapture();
    324   DCHECK(buffers_.empty());
    325   DetachPlatformVideoCapture();
    326   return PP_OK;
    327 }
    328 
    329 void PepperVideoCaptureHost::ReleaseBuffers() {
    330   ppapi::ResourceTracker* tracker = HostGlobals::Get()->GetResourceTracker();
    331   for (size_t i = 0; i < buffers_.size(); ++i) {
    332     buffers_[i].buffer->Unmap();
    333     tracker->ReleaseResource(buffers_[i].buffer->pp_resource());
    334   }
    335   buffers_.clear();
    336 }
    337 
    338 void PepperVideoCaptureHost::SendStatus() {
    339   host()->SendUnsolicitedReply(pp_resource(),
    340                                PpapiPluginMsg_VideoCapture_OnStatus(status_));
    341 }
    342 
    343 void PepperVideoCaptureHost::SetRequestedInfo(
    344     const PP_VideoCaptureDeviceInfo_Dev& device_info,
    345     uint32_t buffer_count) {
    346   // Clamp the buffer count to between 1 and |kMaxBuffers|.
    347   buffer_count_hint_ = std::min(std::max(buffer_count, 1U), kMaxBuffers);
    348   // Clamp the frame rate to between 1 and |kMaxFramesPerSecond - 1|.
    349   int frames_per_second =
    350       std::min(std::max(device_info.frames_per_second, 1U),
    351                static_cast<uint32_t>(media::limits::kMaxFramesPerSecond - 1));
    352 
    353   video_capture_params_.requested_format = media::VideoCaptureFormat(
    354       gfx::Size(device_info.width, device_info.height),
    355       frames_per_second,
    356       media::PIXEL_FORMAT_I420);
    357   video_capture_params_.allow_resolution_change = false;
    358 }
    359 
    360 void PepperVideoCaptureHost::DetachPlatformVideoCapture() {
    361   if (platform_video_capture_) {
    362     platform_video_capture_->DetachEventHandler();
    363     platform_video_capture_.reset();
    364   }
    365 }
    366 
    367 bool PepperVideoCaptureHost::SetStatus(PP_VideoCaptureStatus_Dev status,
    368                                        bool forced) {
    369   if (!forced) {
    370     switch (status) {
    371       case PP_VIDEO_CAPTURE_STATUS_STOPPED:
    372         if (status_ != PP_VIDEO_CAPTURE_STATUS_STOPPING)
    373           return false;
    374         break;
    375       case PP_VIDEO_CAPTURE_STATUS_STARTING:
    376         if (status_ != PP_VIDEO_CAPTURE_STATUS_STOPPED)
    377           return false;
    378         break;
    379       case PP_VIDEO_CAPTURE_STATUS_STARTED:
    380         switch (status_) {
    381           case PP_VIDEO_CAPTURE_STATUS_STARTING:
    382           case PP_VIDEO_CAPTURE_STATUS_PAUSED:
    383             break;
    384           default:
    385             return false;
    386         }
    387         break;
    388       case PP_VIDEO_CAPTURE_STATUS_PAUSED:
    389         switch (status_) {
    390           case PP_VIDEO_CAPTURE_STATUS_STARTING:
    391           case PP_VIDEO_CAPTURE_STATUS_STARTED:
    392             break;
    393           default:
    394             return false;
    395         }
    396         break;
    397       case PP_VIDEO_CAPTURE_STATUS_STOPPING:
    398         switch (status_) {
    399           case PP_VIDEO_CAPTURE_STATUS_STARTING:
    400           case PP_VIDEO_CAPTURE_STATUS_STARTED:
    401           case PP_VIDEO_CAPTURE_STATUS_PAUSED:
    402             break;
    403           default:
    404             return false;
    405         }
    406         break;
    407     }
    408   }
    409 
    410   status_ = status;
    411   return true;
    412 }
    413 
    414 PepperVideoCaptureHost::BufferInfo::BufferInfo()
    415     : in_use(false), data(NULL), buffer() {
    416 }
    417 
    418 PepperVideoCaptureHost::BufferInfo::~BufferInfo() {
    419 }
    420 
    421 }  // namespace content
    422