Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(593)

Side by Side Diff: content/renderer/pepper/pepper_video_capture_host.cc

Issue 11274036: Refactor video capture to new design (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: export Created 8 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 "content/renderer/pepper/pepper_video_capture_host.h"
6
7 #include "ppapi/host/dispatch_host_message.h"
8 #include "ppapi/host/ppapi_host.h"
9 #include "ppapi/proxy/host_dispatcher.h"
10 #include "ppapi/proxy/ppapi_messages.h"
11 #include "ppapi/shared_impl/host_resource.h"
12 #include "ppapi/thunk/enter.h"
13 #include "ppapi/thunk/ppb_buffer_api.h"
14 #include "webkit/plugins/ppapi/host_globals.h"
15 #include "webkit/plugins/ppapi/ppapi_plugin_instance.h"
16 #include "webkit/plugins/ppapi/resource_helper.h"
17
18 using ppapi::DeviceRefData;
19 using ppapi::HostResource;
20 using ppapi::TrackedCallback;
21 using ppapi::thunk::EnterResourceNoLock;
22 using ppapi::thunk::PPB_Buffer_API;
23 using ppapi::thunk::PPB_BufferTrusted_API;
24 using webkit::ppapi::HostGlobals;
25 using webkit::ppapi::ResourceHelper;
26 using webkit::ppapi::PPB_Buffer_Impl;
27 using webkit::ppapi::PluginInstance;
28
29 namespace {
30
31 // Maximum number of buffers to actually allocate.
32 const uint32_t kMaxBuffers = 20;
33
34 } // namespace
35
36 namespace content {
37
38 PepperVideoCaptureHost::PepperVideoCaptureHost(RendererPpapiHost* 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 }
46
47 PepperVideoCaptureHost::~PepperVideoCaptureHost() {
48 Close();
49 }
50
51 bool PepperVideoCaptureHost::Init() {
52 PluginInstance* instance = GetPluginInstance();
53 return !!instance;
54 }
55
56 int32_t PepperVideoCaptureHost::OnResourceMessageReceived(
57 const IPC::Message& msg,
58 ppapi::host::HostMessageContext* context) {
59 IPC_BEGIN_MESSAGE_MAP(PepperVideoCaptureHost, msg)
60 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
61 PpapiHostMsg_VideoCapture_EnumerateDevices,
62 OnEnumerateDevices)
63 PPAPI_DISPATCH_HOST_RESOURCE_CALL(
64 PpapiHostMsg_VideoCapture_Open,
65 OnOpen)
66 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
67 PpapiHostMsg_VideoCapture_StartCapture,
68 OnStartCapture)
69 PPAPI_DISPATCH_HOST_RESOURCE_CALL(
70 PpapiHostMsg_VideoCapture_ReuseBuffer,
71 OnReuseBuffer)
72 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
73 PpapiHostMsg_VideoCapture_StopCapture,
74 OnStopCapture)
75 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
76 PpapiHostMsg_VideoCapture_Close,
77 OnClose)
78 IPC_END_MESSAGE_MAP()
79 return PP_ERROR_FAILED;
80 }
81
82 void PepperVideoCaptureHost::OnInitialized(media::VideoCapture* capture,
83 bool succeeded) {
84 DCHECK(capture == platform_video_capture_.get());
85
86 if (succeeded) {
87 open_reply_context_.params.set_result(PP_OK);
88 } else {
89 DetachPlatformVideoCapture();
90 open_reply_context_.params.set_result(PP_ERROR_FAILED);
91 }
92
93 host()->SendReply(open_reply_context_,
94 PpapiPluginMsg_VideoCapture_OpenReply());
95 }
96
97 void PepperVideoCaptureHost::OnStarted(media::VideoCapture* capture) {
98 if (SetStatus(PP_VIDEO_CAPTURE_STATUS_STARTED, false))
99 SendStatus();
100 }
101
102 void PepperVideoCaptureHost::OnStopped(media::VideoCapture* capture) {
103 if (SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPED, false))
104 SendStatus();
105 }
106
107 void PepperVideoCaptureHost::OnPaused(media::VideoCapture* capture) {
108 if (SetStatus(PP_VIDEO_CAPTURE_STATUS_PAUSED, false))
109 SendStatus();
110 }
111
112 void PepperVideoCaptureHost::OnError(media::VideoCapture* capture,
113 int error_code) {
114 // Today, the media layer only sends "1" as an error.
115 DCHECK(error_code == 1);
116 // It either comes because some error was detected while starting (e.g. 2
117 // conflicting "master" resolution), or because the browser failed to start
118 // the capture.
119 SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPED, true);
120 host()->SendUnsolicitedReply(pp_resource(),
121 PpapiPluginMsg_VideoCapture_OnError(PP_ERROR_FAILED));
122 }
123
124 void PepperVideoCaptureHost::OnRemoved(media::VideoCapture* capture) {
125 }
126
127 void PepperVideoCaptureHost::OnBufferReady(
128 media::VideoCapture* capture,
129 scoped_refptr<media::VideoCapture::VideoFrameBuffer> buffer) {
130 DCHECK(buffer.get());
131 for (uint32_t i = 0; i < buffers_.size(); ++i) {
132 if (!buffers_[i].in_use) {
133 // TODO(ihf): Switch to a size calculation based on stride.
134 // Stride is filled out now but not more meaningful than size
135 // until wjia unifies VideoFrameBuffer and media::VideoFrame.
136 size_t size = std::min(static_cast<size_t>(buffers_[i].buffer->size()),
137 buffer->buffer_size);
138 memcpy(buffers_[i].data, buffer->memory_pointer, size);
139 buffers_[i].in_use = true;
140 platform_video_capture_->FeedBuffer(buffer);
141 host()->SendUnsolicitedReply(pp_resource(),
142 PpapiPluginMsg_VideoCapture_OnBufferReady(i));
143 return;
144 }
145 }
146
147 // No free slot, just discard the frame and tell the media layer it can
148 // re-use the buffer.
149 platform_video_capture_->FeedBuffer(buffer);
150 }
151
152 void PepperVideoCaptureHost::OnDeviceInfoReceived(
153 media::VideoCapture* capture,
154 const media::VideoCaptureParams& device_info) {
155 PP_VideoCaptureDeviceInfo_Dev info = {
156 static_cast<uint32_t>(device_info.width),
157 static_cast<uint32_t>(device_info.height),
158 static_cast<uint32_t>(device_info.frame_per_second)
159 };
160 ReleaseBuffers();
161
162 // YUV 4:2:0
163 int uv_width = info.width / 2;
164 int uv_height = info.height / 2;
165 size_t size = info.width * info.height + 2 * uv_width * uv_height;
166
167 ppapi::proxy::ResourceMessageReplyParams params(pp_resource(), 0);
168
169 // Allocate buffers. We keep a reference to them, that is released in
170 // ReleaseBuffers. In the mean time, we prepare the resource and handle here
171 // for sending below.
172 std::vector<HostResource> buffer_host_resources;
173 buffers_.reserve(buffer_count_hint_);
174 ::ppapi::ResourceTracker* tracker =
175 HostGlobals::Get()->GetResourceTracker();
176 ppapi::proxy::HostDispatcher* dispatcher =
177 ppapi::proxy::HostDispatcher::GetForInstance(pp_instance());
178 for (size_t i = 0; i < buffer_count_hint_; ++i) {
179 PP_Resource res = PPB_Buffer_Impl::Create(pp_instance(), size);
180 if (!res)
181 break;
182
183 EnterResourceNoLock<PPB_Buffer_API> enter(res, true);
184 DCHECK(enter.succeeded());
185
186 BufferInfo buf;
187 buf.buffer = static_cast<PPB_Buffer_Impl*>(enter.object());
188 buf.data = buf.buffer->Map();
189 if (!buf.data) {
190 tracker->ReleaseResource(res);
191 break;
192 }
193 buffers_.push_back(buf);
194
195 // Add to HostResource array to be sent.
196 {
197 HostResource host_resource;
198 host_resource.SetHostResource(pp_instance(), res);
199 buffer_host_resources.push_back(host_resource);
200
201 // Add a reference for the plugin, which is resposible for releasing it.
202 tracker->AddRefResource(res);
203 }
204
205 // Add the serialized shared memory handle to params. FileDescriptor is
206 // treated in special case.
207 {
208 EnterResourceNoLock<PPB_BufferTrusted_API> enter(res, true);
209 DCHECK(enter.succeeded());
210 int handle;
211 int32_t result = enter.object()->GetSharedMemory(&handle);
212 DCHECK(result == PP_OK);
213 // TODO(piman/brettw): Change trusted interface to return a PP_FileHandle,
214 // those casts are ugly.
215 base::PlatformFile platform_file =
216 #if defined(OS_WIN)
217 reinterpret_cast<HANDLE>(static_cast<intptr_t>(handle));
218 #elif defined(OS_POSIX)
219 handle;
220 #else
221 #error Not implemented.
222 #endif
223 params.AppendHandle(
224 ppapi::proxy::SerializedHandle(
225 dispatcher->ShareHandleWithRemote(platform_file, false),
226 size));
227 }
228 }
229
230 if (buffers_.empty()) {
231 // We couldn't allocate/map buffers at all. Send an error and stop the
232 // capture.
233 SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPING, true);
234 platform_video_capture_->StopCapture(this);
235 OnError(capture, PP_ERROR_NOMEMORY);
236 return;
237 }
238
239 host()->Send(new PpapiPluginMsg_ResourceReply(
240 params, PpapiPluginMsg_VideoCapture_OnDeviceInfo(
241 info, buffer_host_resources, size)));
242 }
243
244 PluginInstance* PepperVideoCaptureHost::GetPluginInstance() const {
245 return renderer_ppapi_host_->GetPluginInstance(pp_instance());
246 }
247
248 int32_t PepperVideoCaptureHost::OnEnumerateDevices(
249 ppapi::host::HostMessageContext* context) {
250 PluginInstance* instance = GetPluginInstance();
251 if (!instance)
252 return PP_ERROR_FAILED;
253
254 enum_reply_context_ = context->MakeReplyMessageContext();
255 instance->delegate()->EnumerateDevices(
256 PP_DEVICETYPE_DEV_VIDEOCAPTURE,
257 base::Bind(&PepperVideoCaptureHost::EnumerateDevicesCallbackFunc,
258 AsWeakPtr()));
259 return PP_OK_COMPLETIONPENDING;
260 }
261
262 int32_t PepperVideoCaptureHost::OnOpen(
263 ppapi::host::HostMessageContext* context,
264 const std::string& device_id,
265 const PP_VideoCaptureDeviceInfo_Dev& requested_info,
266 uint32_t buffer_count) {
267 if (platform_video_capture_.get())
268 return PP_ERROR_FAILED;
269
270 PluginInstance* instance = GetPluginInstance();
271 if (!instance)
272 return PP_ERROR_FAILED;
273
274 SetRequestedInfo(requested_info, buffer_count);
275
276 platform_video_capture_ =
277 instance->delegate()->CreateVideoCapture(device_id, this);
278
279 open_reply_context_ = context->MakeReplyMessageContext();
280
281 // It is able to complete synchronously if the default device is used.
282 bool sync_completion = device_id.empty();
283 if (sync_completion) {
284 // Send OpenACK directly, but still need to return PP_OK_COMPLETIONPENDING
285 // to make PluginResource happy.
286 OnInitialized(platform_video_capture_.get(), true);
287 }
288
289 return PP_OK_COMPLETIONPENDING;
290 }
291
292 int32_t PepperVideoCaptureHost::OnStartCapture(
293 ppapi::host::HostMessageContext* context) {
294 if (!SetStatus(PP_VIDEO_CAPTURE_STATUS_STARTING, false) ||
295 !platform_video_capture_.get())
296 return PP_ERROR_FAILED;
297
298 DCHECK(buffers_.empty());
299
300 // It's safe to call this regardless it's capturing or not, because
301 // PepperPlatformVideoCaptureImpl maintains the state.
302 platform_video_capture_->StartCapture(this, capability_);
303 return PP_OK;
304 }
305
306 int32_t PepperVideoCaptureHost::OnReuseBuffer(
307 ppapi::host::HostMessageContext* context,
308 uint32_t buffer) {
309 if (buffer >= buffers_.size() || !buffers_[buffer].in_use)
310 return PP_ERROR_BADARGUMENT;
311 buffers_[buffer].in_use = false;
312 return PP_OK;
313 }
314
315 int32_t PepperVideoCaptureHost::OnStopCapture(
316 ppapi::host::HostMessageContext* context) {
317 return StopCapture();
318 }
319
320 int32_t PepperVideoCaptureHost::OnClose(
321 ppapi::host::HostMessageContext* context) {
322 return Close();
323 }
324
325 int32_t PepperVideoCaptureHost::StopCapture() {
326 if (!SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPING, false))
327 return PP_ERROR_FAILED;
328
329 DCHECK(platform_video_capture_.get());
330
331 ReleaseBuffers();
332 // It's safe to call this regardless it's capturing or not, because
333 // PepperPlatformVideoCaptureImpl maintains the state.
334 platform_video_capture_->StopCapture(this);
335 return PP_OK;
336 }
337
338 int32_t PepperVideoCaptureHost::Close() {
339 if (!platform_video_capture_.get())
340 return PP_OK;
341
342 StopCapture();
343 DCHECK(buffers_.empty());
344 DetachPlatformVideoCapture();
345 return PP_OK;
346 }
347
348 void PepperVideoCaptureHost::ReleaseBuffers() {
349 ::ppapi::ResourceTracker* tracker = HostGlobals::Get()->GetResourceTracker();
350 for (size_t i = 0; i < buffers_.size(); ++i) {
351 buffers_[i].buffer->Unmap();
352 tracker->ReleaseResource(buffers_[i].buffer->pp_resource());
353 }
354 buffers_.clear();
355 }
356
357 void PepperVideoCaptureHost::SendStatus() {
358 host()->SendUnsolicitedReply(pp_resource(),
359 PpapiPluginMsg_VideoCapture_OnStatus(status_));
360 }
361
362 void PepperVideoCaptureHost::SetRequestedInfo(
363 const PP_VideoCaptureDeviceInfo_Dev& device_info,
364 uint32_t buffer_count) {
365 // Clamp the buffer count to between 1 and |kMaxBuffers|.
366 buffer_count_hint_ = std::min(std::max(buffer_count, 1U), kMaxBuffers);
367
368 capability_.width = device_info.width;
369 capability_.height = device_info.height;
370 capability_.frame_rate = device_info.frames_per_second;
371 capability_.expected_capture_delay = 0; // Ignored.
372 capability_.color = media::VideoCaptureCapability::kI420;
373 capability_.interlaced = false; // Ignored.
374 }
375
376 void PepperVideoCaptureHost::DetachPlatformVideoCapture() {
377 if (platform_video_capture_.get()) {
378 platform_video_capture_->DetachEventHandler();
379 platform_video_capture_ = NULL;
380 }
381 }
382
383 void PepperVideoCaptureHost::EnumerateDevicesCallbackFunc(
384 int request_id,
385 bool succeeded,
386 const std::vector<ppapi::DeviceRefData>& devices) {
387 PluginInstance* instance = GetPluginInstance();
388 if (instance)
389 instance->delegate()->StopEnumerateDevices(request_id);
390
391 if (succeeded) {
392 enum_reply_context_.params.set_result(PP_OK);
393 host()->SendReply(enum_reply_context_,
394 PpapiPluginMsg_VideoCapture_EnumerateDevicesReply(
395 devices));
396 } else {
397 enum_reply_context_.params.set_result(PP_ERROR_FAILED);
398 host()->SendReply(enum_reply_context_,
399 PpapiPluginMsg_VideoCapture_EnumerateDevicesReply(
400 std::vector<DeviceRefData>()));
401 }
402 }
403
404 bool PepperVideoCaptureHost::SetStatus(PP_VideoCaptureStatus_Dev status,
405 bool forced) {
406 if (!forced) {
407 switch (status) {
408 case PP_VIDEO_CAPTURE_STATUS_STOPPED:
409 if (status_ != PP_VIDEO_CAPTURE_STATUS_STOPPING)
410 return false;
411 break;
412 case PP_VIDEO_CAPTURE_STATUS_STARTING:
413 if (status_ != PP_VIDEO_CAPTURE_STATUS_STOPPED)
414 return false;
415 break;
416 case PP_VIDEO_CAPTURE_STATUS_STARTED:
417 switch (status_) {
418 case PP_VIDEO_CAPTURE_STATUS_STARTING:
419 case PP_VIDEO_CAPTURE_STATUS_PAUSED:
420 break;
421 default:
422 return false;
423 }
424 break;
425 case PP_VIDEO_CAPTURE_STATUS_PAUSED:
426 switch (status_) {
427 case PP_VIDEO_CAPTURE_STATUS_STARTING:
428 case PP_VIDEO_CAPTURE_STATUS_STARTED:
429 break;
430 default:
431 return false;
432 }
433 break;
434 case PP_VIDEO_CAPTURE_STATUS_STOPPING:
435 switch (status_) {
436 case PP_VIDEO_CAPTURE_STATUS_STARTING:
437 case PP_VIDEO_CAPTURE_STATUS_STARTED:
438 case PP_VIDEO_CAPTURE_STATUS_PAUSED:
439 break;
440 default:
441 return false;
442 }
443 break;
444 }
445 }
446
447 status_ = status;
448 return true;
449 }
450
451 PepperVideoCaptureHost::BufferInfo::BufferInfo()
452 : in_use(false),
453 data(NULL),
454 buffer() {
455 }
456
457 PepperVideoCaptureHost::BufferInfo::~BufferInfo() {
458 }
459
460 } // namespace content
OLDNEW
« no previous file with comments | « content/renderer/pepper/pepper_video_capture_host.h ('k') | ppapi/api/dev/ppb_video_capture_dev.idl » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698