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