pepper_video_capture_host.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
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  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
80void 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
92void PepperVideoCaptureHost::OnStarted() {
93  if (SetStatus(PP_VIDEO_CAPTURE_STATUS_STARTED, false))
94    SendStatus();
95}
96
97void PepperVideoCaptureHost::OnStopped() {
98  if (SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPED, false))
99    SendStatus();
100}
101
102void PepperVideoCaptureHost::OnPaused() {
103  if (SetStatus(PP_VIDEO_CAPTURE_STATUS_PAUSED, false))
104    SendStatus();
105}
106
107void PepperVideoCaptureHost::OnError() {
108  PostErrorReply();
109}
110
111void 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
120void 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
162void 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
248int32_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
273int32_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
287int32_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
296int32_t PepperVideoCaptureHost::OnStopCapture(
297    ppapi::host::HostMessageContext* context) {
298  return StopCapture();
299}
300
301int32_t PepperVideoCaptureHost::OnClose(
302    ppapi::host::HostMessageContext* context) {
303  return Close();
304}
305
306int32_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
319int32_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
329void 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
338void PepperVideoCaptureHost::SendStatus() {
339  host()->SendUnsolicitedReply(pp_resource(),
340                               PpapiPluginMsg_VideoCapture_OnStatus(status_));
341}
342
343void 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
360void PepperVideoCaptureHost::DetachPlatformVideoCapture() {
361  if (platform_video_capture_) {
362    platform_video_capture_->DetachEventHandler();
363    platform_video_capture_.reset();
364  }
365}
366
367bool 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
414PepperVideoCaptureHost::BufferInfo::BufferInfo()
415    : in_use(false), data(NULL), buffer() {
416}
417
418PepperVideoCaptureHost::BufferInfo::~BufferInfo() {
419}
420
421}  // namespace content
422