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 "ppapi/host/dispatch_host_message.h"
     14 #include "ppapi/host/ppapi_host.h"
     15 #include "ppapi/proxy/host_dispatcher.h"
     16 #include "ppapi/proxy/ppapi_messages.h"
     17 #include "ppapi/shared_impl/host_resource.h"
     18 #include "ppapi/thunk/enter.h"
     19 #include "ppapi/thunk/ppb_buffer_api.h"
     20 #include "third_party/WebKit/public/web/WebDocument.h"
     21 #include "third_party/WebKit/public/web/WebElement.h"
     22 #include "third_party/WebKit/public/web/WebPluginContainer.h"
     23 
     24 using ppapi::HostResource;
     25 using ppapi::TrackedCallback;
     26 using ppapi::thunk::EnterResourceNoLock;
     27 using ppapi::thunk::PPB_Buffer_API;
     28 
     29 namespace {
     30 
     31 // Maximum number of buffers to actually allocate.
     32 const uint32_t kMaxBuffers = 20;
     33 
     34 }  // namespace
     35 
     36 namespace content {
     37 
     38 PepperVideoCaptureHost::PepperVideoCaptureHost(RendererPpapiHostImpl* host,
     39                                                PP_Instance instance,
     40                                                PP_Resource resource)
     41     : ResourceHost(host->GetPpapiHost(), instance, resource),
     42       renderer_ppapi_host_(host),
     43       buffer_count_hint_(0),
     44       status_(PP_VIDEO_CAPTURE_STATUS_STOPPED),
     45       enumeration_helper_(
     46           this,
     47           PepperMediaDeviceManager::GetForRenderView(
     48               host->GetRenderViewForInstance(pp_instance())),
     49           PP_DEVICETYPE_DEV_VIDEOCAPTURE) {
     50 }
     51 
     52 PepperVideoCaptureHost::~PepperVideoCaptureHost() {
     53   Close();
     54 }
     55 
     56 bool PepperVideoCaptureHost::Init() {
     57   return !!renderer_ppapi_host_->GetPluginInstance(pp_instance());
     58 }
     59 
     60 int32_t PepperVideoCaptureHost::OnResourceMessageReceived(
     61     const IPC::Message& msg,
     62     ppapi::host::HostMessageContext* context) {
     63   int32_t result = PP_ERROR_FAILED;
     64   if (enumeration_helper_.HandleResourceMessage(msg, context, &result))
     65     return result;
     66 
     67   IPC_BEGIN_MESSAGE_MAP(PepperVideoCaptureHost, msg)
     68     PPAPI_DISPATCH_HOST_RESOURCE_CALL(
     69         PpapiHostMsg_VideoCapture_Open,
     70         OnOpen)
     71     PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
     72         PpapiHostMsg_VideoCapture_StartCapture,
     73         OnStartCapture)
     74     PPAPI_DISPATCH_HOST_RESOURCE_CALL(
     75         PpapiHostMsg_VideoCapture_ReuseBuffer,
     76         OnReuseBuffer)
     77     PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
     78         PpapiHostMsg_VideoCapture_StopCapture,
     79         OnStopCapture)
     80     PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
     81         PpapiHostMsg_VideoCapture_Close,
     82         OnClose)
     83   IPC_END_MESSAGE_MAP()
     84   return PP_ERROR_FAILED;
     85 }
     86 
     87 void PepperVideoCaptureHost::OnInitialized(media::VideoCapture* capture,
     88                                            bool succeeded) {
     89   DCHECK(capture == platform_video_capture_.get());
     90 
     91   if (succeeded) {
     92     open_reply_context_.params.set_result(PP_OK);
     93   } else {
     94     DetachPlatformVideoCapture();
     95     open_reply_context_.params.set_result(PP_ERROR_FAILED);
     96   }
     97 
     98   host()->SendReply(open_reply_context_,
     99                     PpapiPluginMsg_VideoCapture_OpenReply());
    100 }
    101 
    102 void PepperVideoCaptureHost::OnStarted(media::VideoCapture* capture) {
    103   if (SetStatus(PP_VIDEO_CAPTURE_STATUS_STARTED, false))
    104     SendStatus();
    105 }
    106 
    107 void PepperVideoCaptureHost::OnStopped(media::VideoCapture* capture) {
    108   if (SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPED, false))
    109     SendStatus();
    110 }
    111 
    112 void PepperVideoCaptureHost::OnPaused(media::VideoCapture* capture) {
    113   if (SetStatus(PP_VIDEO_CAPTURE_STATUS_PAUSED, false))
    114     SendStatus();
    115 }
    116 
    117 void PepperVideoCaptureHost::OnError(media::VideoCapture* capture,
    118                                      int error_code) {
    119   // Today, the media layer only sends "1" as an error.
    120   DCHECK(error_code == 1);
    121   // It either comes because some error was detected while starting (e.g. 2
    122   // conflicting "master" resolution), or because the browser failed to start
    123   // the capture.
    124   SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPED, true);
    125   host()->SendUnsolicitedReply(pp_resource(),
    126       PpapiPluginMsg_VideoCapture_OnError(PP_ERROR_FAILED));
    127 }
    128 
    129 void PepperVideoCaptureHost::OnRemoved(media::VideoCapture* capture) {
    130 }
    131 
    132 void PepperVideoCaptureHost::OnBufferReady(
    133     media::VideoCapture* capture,
    134     scoped_refptr<media::VideoCapture::VideoFrameBuffer> buffer) {
    135   DCHECK(buffer.get());
    136   for (uint32_t i = 0; i < buffers_.size(); ++i) {
    137     if (!buffers_[i].in_use) {
    138       // TODO(ihf): Switch to a size calculation based on stride.
    139       // Stride is filled out now but not more meaningful than size
    140       // until wjia unifies VideoFrameBuffer and media::VideoFrame.
    141       size_t size = std::min(static_cast<size_t>(buffers_[i].buffer->size()),
    142           buffer->buffer_size);
    143       memcpy(buffers_[i].data, buffer->memory_pointer, size);
    144       buffers_[i].in_use = true;
    145       platform_video_capture_->FeedBuffer(buffer);
    146       host()->SendUnsolicitedReply(pp_resource(),
    147           PpapiPluginMsg_VideoCapture_OnBufferReady(i));
    148       return;
    149     }
    150   }
    151 
    152   // No free slot, just discard the frame and tell the media layer it can
    153   // re-use the buffer.
    154   platform_video_capture_->FeedBuffer(buffer);
    155 }
    156 
    157 void PepperVideoCaptureHost::OnDeviceInfoReceived(
    158     media::VideoCapture* capture,
    159     const media::VideoCaptureParams& device_info) {
    160   PP_VideoCaptureDeviceInfo_Dev info = {
    161     static_cast<uint32_t>(device_info.width),
    162     static_cast<uint32_t>(device_info.height),
    163     static_cast<uint32_t>(device_info.frame_per_second)
    164   };
    165   ReleaseBuffers();
    166 
    167   // YUV 4:2:0
    168   int uv_width = info.width / 2;
    169   int uv_height = info.height / 2;
    170   size_t size = info.width * info.height + 2 * uv_width * uv_height;
    171 
    172   ppapi::proxy::ResourceMessageReplyParams params(pp_resource(), 0);
    173 
    174   // Allocate buffers. We keep a reference to them, that is released in
    175   // ReleaseBuffers. In the mean time, we prepare the resource and handle here
    176   // for sending below.
    177   std::vector<HostResource> buffer_host_resources;
    178   buffers_.reserve(buffer_count_hint_);
    179   ppapi::ResourceTracker* tracker =
    180       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(
    229           ppapi::proxy::SerializedHandle(
    230               dispatcher->ShareHandleWithRemote(platform_file, false),
    231               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(this);
    240     OnError(capture, PP_ERROR_NOMEMORY);
    241     return;
    242   }
    243 
    244   host()->Send(new PpapiPluginMsg_ResourceReply(
    245       params, PpapiPluginMsg_VideoCapture_OnDeviceInfo(
    246           info, buffer_host_resources, size)));
    247 }
    248 
    249 int32_t PepperVideoCaptureHost::OnOpen(
    250     ppapi::host::HostMessageContext* context,
    251     const std::string& device_id,
    252     const PP_VideoCaptureDeviceInfo_Dev& requested_info,
    253     uint32_t buffer_count) {
    254   if (platform_video_capture_.get())
    255     return PP_ERROR_FAILED;
    256 
    257   SetRequestedInfo(requested_info, buffer_count);
    258 
    259   PepperPluginInstance* instance =
    260       renderer_ppapi_host_->GetPluginInstance(pp_instance());
    261   if (!instance)
    262     return PP_ERROR_FAILED;
    263 
    264   RenderViewImpl* render_view = static_cast<RenderViewImpl*>(
    265       renderer_ppapi_host_->GetRenderViewForInstance(pp_instance()));
    266 
    267   platform_video_capture_ = new PepperPlatformVideoCapture(
    268       render_view->AsWeakPtr(), device_id,
    269       instance->GetContainer()->element().document().url(), 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(this, capability_);
    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(this);
    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 
    352   capability_.width = device_info.width;
    353   capability_.height = device_info.height;
    354   capability_.frame_rate = device_info.frames_per_second;
    355   capability_.expected_capture_delay = 0;  // Ignored.
    356   capability_.color = media::VideoCaptureCapability::kI420;
    357   capability_.interlaced = false;  // Ignored.
    358 }
    359 
    360 void PepperVideoCaptureHost::DetachPlatformVideoCapture() {
    361   if (platform_video_capture_.get()) {
    362     platform_video_capture_->DetachEventHandler();
    363     platform_video_capture_ = NULL;
    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),
    416       data(NULL),
    417       buffer() {
    418 }
    419 
    420 PepperVideoCaptureHost::BufferInfo::~BufferInfo() {
    421 }
    422 
    423 }  // namespace content
    424