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 "ppapi/proxy/video_capture_resource.h"
6
7#include "ppapi/c/dev/ppp_video_capture_dev.h"
8#include "ppapi/proxy/dispatch_reply_message.h"
9#include "ppapi/proxy/plugin_dispatcher.h"
10#include "ppapi/proxy/plugin_globals.h"
11#include "ppapi/proxy/plugin_resource_tracker.h"
12#include "ppapi/proxy/ppapi_messages.h"
13#include "ppapi/proxy/ppb_buffer_proxy.h"
14#include "ppapi/proxy/resource_message_params.h"
15#include "ppapi/shared_impl/proxy_lock.h"
16#include "ppapi/shared_impl/tracked_callback.h"
17
18namespace ppapi {
19namespace proxy {
20
21VideoCaptureResource::VideoCaptureResource(
22    Connection connection,
23    PP_Instance instance,
24    PluginDispatcher* dispatcher)
25    : PluginResource(connection, instance),
26      open_state_(BEFORE_OPEN),
27      enumeration_helper_(this) {
28  SendCreate(RENDERER, PpapiHostMsg_VideoCapture_Create());
29
30  ppp_video_capture_impl_ = static_cast<const PPP_VideoCapture_Dev*>(
31      dispatcher->local_get_interface()(PPP_VIDEO_CAPTURE_DEV_INTERFACE));
32}
33
34VideoCaptureResource::~VideoCaptureResource() {
35}
36
37void VideoCaptureResource::OnReplyReceived(
38    const ResourceMessageReplyParams& params,
39    const IPC::Message& msg) {
40  if (enumeration_helper_.HandleReply(params, msg))
41    return;
42
43  if (params.sequence()) {
44    PluginResource::OnReplyReceived(params, msg);
45    return;
46  }
47
48  PPAPI_BEGIN_MESSAGE_MAP(VideoCaptureResource, msg)
49    PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(
50        PpapiPluginMsg_VideoCapture_OnDeviceInfo,
51        OnPluginMsgOnDeviceInfo)
52    PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(
53        PpapiPluginMsg_VideoCapture_OnStatus,
54        OnPluginMsgOnStatus)
55    PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(
56        PpapiPluginMsg_VideoCapture_OnError,
57        OnPluginMsgOnError)
58    PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(
59        PpapiPluginMsg_VideoCapture_OnBufferReady,
60        OnPluginMsgOnBufferReady)
61    PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL_UNHANDLED(NOTREACHED())
62  PPAPI_END_MESSAGE_MAP()
63}
64
65int32_t VideoCaptureResource::EnumerateDevices(
66    const PP_ArrayOutput& output,
67    scoped_refptr<TrackedCallback> callback) {
68  return enumeration_helper_.EnumerateDevices(output, callback);
69}
70
71int32_t VideoCaptureResource::MonitorDeviceChange(
72    PP_MonitorDeviceChangeCallback callback,
73    void* user_data) {
74  return enumeration_helper_.MonitorDeviceChange(callback, user_data);
75}
76
77int32_t VideoCaptureResource::Open(
78    const std::string& device_id,
79    const PP_VideoCaptureDeviceInfo_Dev& requested_info,
80    uint32_t buffer_count,
81    scoped_refptr<TrackedCallback> callback) {
82  if (open_state_ != BEFORE_OPEN)
83    return PP_ERROR_FAILED;
84
85  if (TrackedCallback::IsPending(open_callback_))
86    return PP_ERROR_INPROGRESS;
87
88  open_callback_ = callback;
89
90  Call<PpapiPluginMsg_VideoCapture_OpenReply>(
91      RENDERER,
92      PpapiHostMsg_VideoCapture_Open(device_id, requested_info, buffer_count),
93      base::Bind(&VideoCaptureResource::OnPluginMsgOpenReply, this));
94  return PP_OK_COMPLETIONPENDING;
95}
96
97int32_t VideoCaptureResource::StartCapture() {
98  if (open_state_ != OPENED)
99    return PP_ERROR_FAILED;
100
101  Post(RENDERER, PpapiHostMsg_VideoCapture_StartCapture());
102  return PP_OK;
103}
104
105int32_t VideoCaptureResource::ReuseBuffer(uint32_t buffer) {
106  if (buffer >= buffer_in_use_.size() || !buffer_in_use_[buffer])
107    return PP_ERROR_BADARGUMENT;
108  Post(RENDERER, PpapiHostMsg_VideoCapture_ReuseBuffer(buffer));
109  return PP_OK;
110}
111
112int32_t VideoCaptureResource::StopCapture() {
113  if (open_state_ != OPENED)
114    return PP_ERROR_FAILED;
115
116  Post(RENDERER, PpapiHostMsg_VideoCapture_StopCapture());
117  return PP_OK;
118}
119
120void VideoCaptureResource::Close() {
121  if (open_state_ == CLOSED)
122    return;
123
124  Post(RENDERER, PpapiHostMsg_VideoCapture_Close());
125
126  open_state_ = CLOSED;
127
128  if (TrackedCallback::IsPending(open_callback_))
129    open_callback_->PostAbort();
130}
131
132int32_t VideoCaptureResource::EnumerateDevicesSync(
133    const PP_ArrayOutput& devices) {
134  return enumeration_helper_.EnumerateDevicesSync(devices);
135}
136
137void VideoCaptureResource::LastPluginRefWasDeleted() {
138  enumeration_helper_.LastPluginRefWasDeleted();
139}
140
141void VideoCaptureResource::OnPluginMsgOnDeviceInfo(
142    const ResourceMessageReplyParams& params,
143    const struct PP_VideoCaptureDeviceInfo_Dev& info,
144    const std::vector<HostResource>& buffers,
145    uint32_t buffer_size) {
146  if (!ppp_video_capture_impl_)
147    return;
148
149  std::vector<base::SharedMemoryHandle> handles;
150  params.TakeAllSharedMemoryHandles(&handles);
151  CHECK(handles.size() == buffers.size());
152
153  PluginResourceTracker* tracker =
154      PluginGlobals::Get()->plugin_resource_tracker();
155  scoped_ptr<PP_Resource[]> resources(new PP_Resource[buffers.size()]);
156  for (size_t i = 0; i < buffers.size(); ++i) {
157    // We assume that the browser created a new set of resources.
158    DCHECK(!tracker->PluginResourceForHostResource(buffers[i]));
159    resources[i] = ppapi::proxy::PPB_Buffer_Proxy::AddProxyResource(
160        buffers[i], handles[i], buffer_size);
161  }
162
163  buffer_in_use_ = std::vector<bool>(buffers.size());
164
165  CallWhileUnlocked(ppp_video_capture_impl_->OnDeviceInfo,
166                    pp_instance(),
167                    pp_resource(),
168                    &info,
169                    buffers.size(),
170                    resources.get());
171
172  for (size_t i = 0; i < buffers.size(); ++i)
173    tracker->ReleaseResource(resources[i]);
174}
175
176void VideoCaptureResource::OnPluginMsgOnStatus(
177    const ResourceMessageReplyParams& params,
178    uint32_t status) {
179  switch (status) {
180    case PP_VIDEO_CAPTURE_STATUS_STARTING:
181    case PP_VIDEO_CAPTURE_STATUS_STOPPING:
182      // Those states are not sent by the browser.
183      NOTREACHED();
184      break;
185  }
186  if (ppp_video_capture_impl_) {
187    CallWhileUnlocked(ppp_video_capture_impl_->OnStatus,
188                      pp_instance(),
189                      pp_resource(),
190                      status);
191  }
192}
193
194void VideoCaptureResource::OnPluginMsgOnError(
195    const ResourceMessageReplyParams& params,
196    uint32_t error_code) {
197  open_state_ = CLOSED;
198  if (ppp_video_capture_impl_) {
199    CallWhileUnlocked(ppp_video_capture_impl_->OnError,
200                      pp_instance(),
201                      pp_resource(),
202                      error_code);
203  }
204}
205
206void VideoCaptureResource::OnPluginMsgOnBufferReady(
207    const ResourceMessageReplyParams& params,
208    uint32_t buffer) {
209  SetBufferInUse(buffer);
210  if (ppp_video_capture_impl_) {
211    CallWhileUnlocked(ppp_video_capture_impl_->OnBufferReady,
212                      pp_instance(),
213                      pp_resource(),
214                      buffer);
215  }
216}
217
218void VideoCaptureResource::OnPluginMsgOpenReply(
219    const ResourceMessageReplyParams& params) {
220  if (open_state_ == BEFORE_OPEN && params.result() == PP_OK)
221    open_state_ = OPENED;
222
223  // The callback may have been aborted by Close().
224  if (TrackedCallback::IsPending(open_callback_))
225    open_callback_->Run(params.result());
226}
227
228void VideoCaptureResource::SetBufferInUse(uint32_t buffer_index) {
229  CHECK(buffer_index < buffer_in_use_.size());
230  buffer_in_use_[buffer_index] = true;
231}
232
233}  // namespace proxy
234}  // namespace ppapi
235