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