OLD | NEW |
| (Empty) |
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 "webkit/plugins/ppapi/ppb_video_capture_impl.h" | |
6 | |
7 #include <algorithm> | |
8 #include <string> | |
9 | |
10 #include "base/bind.h" | |
11 #include "base/logging.h" | |
12 #include "base/memory/scoped_ptr.h" | |
13 #include "ppapi/c/dev/pp_video_capture_dev.h" | |
14 #include "ppapi/c/dev/ppb_video_capture_dev.h" | |
15 #include "ppapi/c/pp_completion_callback.h" | |
16 #include "ppapi/c/pp_errors.h" | |
17 #include "ppapi/shared_impl/ppapi_globals.h" | |
18 #include "ppapi/shared_impl/ppb_device_ref_shared.h" | |
19 #include "ppapi/shared_impl/resource_tracker.h" | |
20 #include "ppapi/shared_impl/tracked_callback.h" | |
21 #include "ppapi/thunk/enter.h" | |
22 #include "webkit/plugins/ppapi/common.h" | |
23 #include "webkit/plugins/ppapi/plugin_module.h" | |
24 #include "webkit/plugins/ppapi/ppapi_plugin_instance.h" | |
25 #include "webkit/plugins/ppapi/ppb_buffer_impl.h" | |
26 #include "webkit/plugins/ppapi/resource_helper.h" | |
27 | |
28 using ppapi::DeviceRefData; | |
29 using ppapi::PpapiGlobals; | |
30 using ppapi::thunk::EnterResourceNoLock; | |
31 using ppapi::thunk::PPB_Buffer_API; | |
32 using ppapi::thunk::PPB_VideoCapture_API; | |
33 using ppapi::TrackedCallback; | |
34 | |
35 namespace { | |
36 | |
37 // Maximum number of buffers to actually allocate. | |
38 const uint32_t kMaxBuffers = 20; | |
39 | |
40 } // namespace | |
41 | |
42 namespace webkit { | |
43 namespace ppapi { | |
44 | |
45 PPB_VideoCapture_Impl::PPB_VideoCapture_Impl(PP_Instance instance) | |
46 : PPB_VideoCapture_Shared(instance), | |
47 buffer_count_hint_(0), | |
48 ppp_videocapture_(NULL), | |
49 capability_() { | |
50 } | |
51 | |
52 PPB_VideoCapture_Impl::~PPB_VideoCapture_Impl() { | |
53 Close(); | |
54 } | |
55 | |
56 bool PPB_VideoCapture_Impl::Init() { | |
57 PluginInstance* instance = ResourceHelper::GetPluginInstance(this); | |
58 if (!instance) | |
59 return false; | |
60 ppp_videocapture_ = static_cast<const PPP_VideoCapture_Dev*>( | |
61 instance->module()->GetPluginInterface(PPP_VIDEO_CAPTURE_DEV_INTERFACE)); | |
62 if (!ppp_videocapture_) | |
63 return false; | |
64 | |
65 return true; | |
66 } | |
67 | |
68 void PPB_VideoCapture_Impl::OnStarted(media::VideoCapture* capture) { | |
69 if (SetStatus(PP_VIDEO_CAPTURE_STATUS_STARTED, false)) | |
70 SendStatus(); | |
71 } | |
72 | |
73 void PPB_VideoCapture_Impl::OnStopped(media::VideoCapture* capture) { | |
74 if (SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPED, false)) | |
75 SendStatus(); | |
76 } | |
77 | |
78 void PPB_VideoCapture_Impl::OnPaused(media::VideoCapture* capture) { | |
79 if (SetStatus(PP_VIDEO_CAPTURE_STATUS_PAUSED, false)) | |
80 SendStatus(); | |
81 } | |
82 | |
83 void PPB_VideoCapture_Impl::OnError(media::VideoCapture* capture, | |
84 int error_code) { | |
85 // Today, the media layer only sends "1" as an error. | |
86 DCHECK(error_code == 1); | |
87 // It either comes because some error was detected while starting (e.g. 2 | |
88 // conflicting "master" resolution), or because the browser failed to start | |
89 // the capture. | |
90 SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPED, true); | |
91 ppp_videocapture_->OnError(pp_instance(), pp_resource(), PP_ERROR_FAILED); | |
92 } | |
93 | |
94 void PPB_VideoCapture_Impl::OnRemoved(media::VideoCapture* capture) { | |
95 } | |
96 | |
97 void PPB_VideoCapture_Impl::OnBufferReady( | |
98 media::VideoCapture* capture, | |
99 scoped_refptr<media::VideoCapture::VideoFrameBuffer> buffer) { | |
100 DCHECK(buffer.get()); | |
101 for (uint32_t i = 0; i < buffers_.size(); ++i) { | |
102 if (!buffers_[i].in_use) { | |
103 // TODO(ihf): Switch to a size calculation based on stride. | |
104 // Stride is filled out now but not more meaningful than size | |
105 // until wjia unifies VideoFrameBuffer and media::VideoFrame. | |
106 size_t size = std::min(static_cast<size_t>(buffers_[i].buffer->size()), | |
107 buffer->buffer_size); | |
108 memcpy(buffers_[i].data, buffer->memory_pointer, size); | |
109 buffers_[i].in_use = true; | |
110 platform_video_capture_->FeedBuffer(buffer); | |
111 ppp_videocapture_->OnBufferReady(pp_instance(), pp_resource(), i); | |
112 return; | |
113 } | |
114 } | |
115 | |
116 // No free slot, just discard the frame and tell the media layer it can | |
117 // re-use the buffer. | |
118 platform_video_capture_->FeedBuffer(buffer); | |
119 } | |
120 | |
121 void PPB_VideoCapture_Impl::OnDeviceInfoReceived( | |
122 media::VideoCapture* capture, | |
123 const media::VideoCaptureParams& device_info) { | |
124 PP_VideoCaptureDeviceInfo_Dev info = { | |
125 static_cast<uint32_t>(device_info.width), | |
126 static_cast<uint32_t>(device_info.height), | |
127 static_cast<uint32_t>(device_info.frame_per_second) | |
128 }; | |
129 ReleaseBuffers(); | |
130 | |
131 // Allocate buffers. We keep a reference to them, that is released in | |
132 // ReleaseBuffers. | |
133 // YUV 4:2:0 | |
134 int uv_width = info.width / 2; | |
135 int uv_height = info.height / 2; | |
136 size_t size = info.width * info.height + 2 * uv_width * uv_height; | |
137 scoped_array<PP_Resource> resources(new PP_Resource[buffer_count_hint_]); | |
138 | |
139 buffers_.reserve(buffer_count_hint_); | |
140 for (size_t i = 0; i < buffer_count_hint_; ++i) { | |
141 resources[i] = PPB_Buffer_Impl::Create(pp_instance(), size); | |
142 if (!resources[i]) | |
143 break; | |
144 | |
145 EnterResourceNoLock<PPB_Buffer_API> enter(resources[i], true); | |
146 DCHECK(enter.succeeded()); | |
147 | |
148 BufferInfo info; | |
149 info.buffer = static_cast<PPB_Buffer_Impl*>(enter.object()); | |
150 info.data = info.buffer->Map(); | |
151 if (!info.data) { | |
152 PpapiGlobals::Get()->GetResourceTracker()->ReleaseResource(resources[i]); | |
153 break; | |
154 } | |
155 buffers_.push_back(info); | |
156 } | |
157 | |
158 if (buffers_.empty()) { | |
159 // We couldn't allocate/map buffers at all. Send an error and stop the | |
160 // capture. | |
161 ppp_videocapture_->OnError(pp_instance(), pp_resource(), PP_ERROR_NOMEMORY); | |
162 SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPING, true); | |
163 platform_video_capture_->StopCapture(this); | |
164 return; | |
165 } | |
166 | |
167 ppp_videocapture_->OnDeviceInfo(pp_instance(), pp_resource(), &info, | |
168 buffers_.size(), resources.get()); | |
169 } | |
170 | |
171 void PPB_VideoCapture_Impl::OnInitialized(media::VideoCapture* capture, | |
172 bool succeeded) { | |
173 DCHECK(capture == platform_video_capture_.get()); | |
174 | |
175 OnOpenComplete(succeeded ? PP_OK : PP_ERROR_FAILED); | |
176 } | |
177 | |
178 int32_t PPB_VideoCapture_Impl::InternalEnumerateDevices( | |
179 PP_Resource* devices, | |
180 scoped_refptr<TrackedCallback> callback) { | |
181 PluginInstance* instance = ResourceHelper::GetPluginInstance(this); | |
182 if (!instance) | |
183 return PP_ERROR_FAILED; | |
184 | |
185 devices_ = devices; | |
186 enumerate_devices_callback_ = callback; | |
187 instance->delegate()->EnumerateDevices( | |
188 PP_DEVICETYPE_DEV_VIDEOCAPTURE, | |
189 base::Bind(&PPB_VideoCapture_Impl::EnumerateDevicesCallbackFunc, | |
190 AsWeakPtr())); | |
191 return PP_OK_COMPLETIONPENDING; | |
192 } | |
193 | |
194 int32_t PPB_VideoCapture_Impl::InternalOpen( | |
195 const std::string& device_id, | |
196 const PP_VideoCaptureDeviceInfo_Dev& requested_info, | |
197 uint32_t buffer_count, | |
198 scoped_refptr<TrackedCallback> callback) { | |
199 // It is able to complete synchronously if the default device is used. | |
200 bool sync_completion = device_id.empty(); | |
201 | |
202 PluginInstance* instance = ResourceHelper::GetPluginInstance(this); | |
203 if (!instance) | |
204 return PP_ERROR_FAILED; | |
205 | |
206 SetRequestedInfo(requested_info, buffer_count); | |
207 | |
208 DCHECK(!platform_video_capture_.get()); | |
209 platform_video_capture_ = | |
210 instance->delegate()->CreateVideoCapture(device_id, this); | |
211 | |
212 if (sync_completion) { | |
213 OnInitialized(platform_video_capture_.get(), true); | |
214 return PP_OK; | |
215 } else { | |
216 open_callback_ = callback; | |
217 return PP_OK_COMPLETIONPENDING; | |
218 } | |
219 } | |
220 | |
221 int32_t PPB_VideoCapture_Impl::InternalStartCapture() { | |
222 DCHECK(buffers_.empty()); | |
223 platform_video_capture_->StartCapture(this, capability_); | |
224 return PP_OK; | |
225 } | |
226 | |
227 int32_t PPB_VideoCapture_Impl::InternalReuseBuffer(uint32_t buffer) { | |
228 if (buffer >= buffers_.size() || !buffers_[buffer].in_use) | |
229 return PP_ERROR_BADARGUMENT; | |
230 buffers_[buffer].in_use = false; | |
231 return PP_OK; | |
232 } | |
233 | |
234 int32_t PPB_VideoCapture_Impl::InternalStopCapture() { | |
235 ReleaseBuffers(); | |
236 platform_video_capture_->StopCapture(this); | |
237 return PP_OK; | |
238 } | |
239 | |
240 void PPB_VideoCapture_Impl::InternalClose() { | |
241 StopCapture(); | |
242 DCHECK(buffers_.empty()); | |
243 | |
244 DetachPlatformVideoCapture(); | |
245 } | |
246 | |
247 int32_t PPB_VideoCapture_Impl::InternalStartCapture0_1( | |
248 const PP_VideoCaptureDeviceInfo_Dev& requested_info, | |
249 uint32_t buffer_count) { | |
250 PluginInstance* instance = ResourceHelper::GetPluginInstance(this); | |
251 if (!instance) { | |
252 SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPED, true); | |
253 return PP_ERROR_FAILED; | |
254 } | |
255 | |
256 DCHECK(buffers_.empty()); | |
257 | |
258 SetRequestedInfo(requested_info, buffer_count); | |
259 | |
260 DetachPlatformVideoCapture(); | |
261 platform_video_capture_ = | |
262 instance->delegate()->CreateVideoCapture("", this); | |
263 platform_video_capture_->StartCapture(this, capability_); | |
264 | |
265 return PP_OK; | |
266 } | |
267 | |
268 const PPB_VideoCapture_Impl::DeviceRefDataVector& | |
269 PPB_VideoCapture_Impl::InternalGetDeviceRefData() const { | |
270 return devices_data_; | |
271 } | |
272 | |
273 void PPB_VideoCapture_Impl::ReleaseBuffers() { | |
274 ::ppapi::ResourceTracker* tracker = PpapiGlobals::Get()->GetResourceTracker(); | |
275 for (size_t i = 0; i < buffers_.size(); ++i) { | |
276 buffers_[i].buffer->Unmap(); | |
277 tracker->ReleaseResource(buffers_[i].buffer->pp_resource()); | |
278 } | |
279 buffers_.clear(); | |
280 } | |
281 | |
282 void PPB_VideoCapture_Impl::SendStatus() { | |
283 ppp_videocapture_->OnStatus(pp_instance(), pp_resource(), status_); | |
284 } | |
285 | |
286 void PPB_VideoCapture_Impl::SetRequestedInfo( | |
287 const PP_VideoCaptureDeviceInfo_Dev& device_info, | |
288 uint32_t buffer_count) { | |
289 // Clamp the buffer count to between 1 and |kMaxBuffers|. | |
290 buffer_count_hint_ = std::min(std::max(buffer_count, 1U), kMaxBuffers); | |
291 | |
292 capability_.width = device_info.width; | |
293 capability_.height = device_info.height; | |
294 capability_.frame_rate = device_info.frames_per_second; | |
295 capability_.expected_capture_delay = 0; // Ignored. | |
296 capability_.color = media::VideoCaptureCapability::kI420; | |
297 capability_.interlaced = false; // Ignored. | |
298 } | |
299 | |
300 void PPB_VideoCapture_Impl::DetachPlatformVideoCapture() { | |
301 if (platform_video_capture_.get()) { | |
302 platform_video_capture_->DetachEventHandler(); | |
303 platform_video_capture_ = NULL; | |
304 } | |
305 } | |
306 | |
307 void PPB_VideoCapture_Impl::EnumerateDevicesCallbackFunc( | |
308 int request_id, | |
309 bool succeeded, | |
310 const DeviceRefDataVector& devices) { | |
311 devices_data_.clear(); | |
312 if (succeeded) | |
313 devices_data_ = devices; | |
314 | |
315 PluginInstance* instance = ResourceHelper::GetPluginInstance(this); | |
316 if (instance) | |
317 instance->delegate()->StopEnumerateDevices(request_id); | |
318 | |
319 OnEnumerateDevicesComplete(succeeded ? PP_OK : PP_ERROR_FAILED, devices); | |
320 } | |
321 | |
322 PPB_VideoCapture_Impl::BufferInfo::BufferInfo() | |
323 : in_use(false), | |
324 data(NULL), | |
325 buffer() { | |
326 } | |
327 | |
328 PPB_VideoCapture_Impl::BufferInfo::~BufferInfo() { | |
329 } | |
330 | |
331 } // namespace ppapi | |
332 } // namespace webkit | |
OLD | NEW |