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
24using ppapi::HostResource;
25using ppapi::TrackedCallback;
26using ppapi::thunk::EnterResourceNoLock;
27using ppapi::thunk::PPB_Buffer_API;
28
29namespace {
30
31// Maximum number of buffers to actually allocate.
32const uint32_t kMaxBuffers = 20;
33
34}  // namespace
35
36namespace content {
37
38PepperVideoCaptureHost::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
52PepperVideoCaptureHost::~PepperVideoCaptureHost() {
53  Close();
54}
55
56bool PepperVideoCaptureHost::Init() {
57  return !!renderer_ppapi_host_->GetPluginInstance(pp_instance());
58}
59
60int32_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
87void 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
102void PepperVideoCaptureHost::OnStarted(media::VideoCapture* capture) {
103  if (SetStatus(PP_VIDEO_CAPTURE_STATUS_STARTED, false))
104    SendStatus();
105}
106
107void PepperVideoCaptureHost::OnStopped(media::VideoCapture* capture) {
108  if (SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPED, false))
109    SendStatus();
110}
111
112void PepperVideoCaptureHost::OnPaused(media::VideoCapture* capture) {
113  if (SetStatus(PP_VIDEO_CAPTURE_STATUS_PAUSED, false))
114    SendStatus();
115}
116
117void 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
129void PepperVideoCaptureHost::OnRemoved(media::VideoCapture* capture) {
130}
131
132void 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
157void 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
249int32_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
276int32_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
290int32_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
299int32_t PepperVideoCaptureHost::OnStopCapture(
300    ppapi::host::HostMessageContext* context) {
301  return StopCapture();
302}
303
304int32_t PepperVideoCaptureHost::OnClose(
305    ppapi::host::HostMessageContext* context) {
306  return Close();
307}
308
309int32_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
322int32_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
332void 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
341void PepperVideoCaptureHost::SendStatus() {
342  host()->SendUnsolicitedReply(pp_resource(),
343      PpapiPluginMsg_VideoCapture_OnStatus(status_));
344}
345
346void 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
360void PepperVideoCaptureHost::DetachPlatformVideoCapture() {
361  if (platform_video_capture_.get()) {
362    platform_video_capture_->DetachEventHandler();
363    platform_video_capture_ = NULL;
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),
416      data(NULL),
417      buffer() {
418}
419
420PepperVideoCaptureHost::BufferInfo::~BufferInfo() {
421}
422
423}  // namespace content
424