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