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.
5 #include "content/renderer/pepper/pepper_video_capture_host.h"
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"
17 using ppapi::HostResource
;
18 using ppapi::TrackedCallback
;
19 using ppapi::thunk::EnterResourceNoLock
;
20 using ppapi::thunk::PPB_Buffer_API
;
21 using webkit::ppapi::HostGlobals
;
22 using webkit::ppapi::PPB_Buffer_Impl
;
26 // Maximum number of buffers to actually allocate.
27 const uint32_t kMaxBuffers
= 20;
33 PepperVideoCaptureHost::PepperVideoCaptureHost(RendererPpapiHost
* host
,
36 : ResourceHost(host
->GetPpapiHost(), instance
, resource
),
37 renderer_ppapi_host_(host
),
38 buffer_count_hint_(0),
39 status_(PP_VIDEO_CAPTURE_STATUS_STOPPED
),
40 enumeration_helper_(this, this, PP_DEVICETYPE_DEV_VIDEOCAPTURE
) {
43 PepperVideoCaptureHost::~PepperVideoCaptureHost() {
47 bool PepperVideoCaptureHost::Init() {
48 return !!GetPluginDelegate();
51 int32_t PepperVideoCaptureHost::OnResourceMessageReceived(
52 const IPC::Message
& msg
,
53 ppapi::host::HostMessageContext
* context
) {
54 int32_t result
= PP_ERROR_FAILED
;
55 if (enumeration_helper_
.HandleResourceMessage(msg
, context
, &result
))
58 IPC_BEGIN_MESSAGE_MAP(PepperVideoCaptureHost
, msg
)
59 PPAPI_DISPATCH_HOST_RESOURCE_CALL(
60 PpapiHostMsg_VideoCapture_Open
,
62 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
63 PpapiHostMsg_VideoCapture_StartCapture
,
65 PPAPI_DISPATCH_HOST_RESOURCE_CALL(
66 PpapiHostMsg_VideoCapture_ReuseBuffer
,
68 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
69 PpapiHostMsg_VideoCapture_StopCapture
,
71 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
72 PpapiHostMsg_VideoCapture_Close
,
75 return PP_ERROR_FAILED
;
78 void PepperVideoCaptureHost::OnInitialized(media::VideoCapture
* capture
,
80 DCHECK(capture
== platform_video_capture_
.get());
83 open_reply_context_
.params
.set_result(PP_OK
);
85 DetachPlatformVideoCapture();
86 open_reply_context_
.params
.set_result(PP_ERROR_FAILED
);
89 host()->SendReply(open_reply_context_
,
90 PpapiPluginMsg_VideoCapture_OpenReply());
93 void PepperVideoCaptureHost::OnStarted(media::VideoCapture
* capture
) {
94 if (SetStatus(PP_VIDEO_CAPTURE_STATUS_STARTED
, false))
98 void PepperVideoCaptureHost::OnStopped(media::VideoCapture
* capture
) {
99 if (SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPED
, false))
103 void PepperVideoCaptureHost::OnPaused(media::VideoCapture
* capture
) {
104 if (SetStatus(PP_VIDEO_CAPTURE_STATUS_PAUSED
, false))
108 void PepperVideoCaptureHost::OnError(media::VideoCapture
* capture
,
110 // Today, the media layer only sends "1" as an error.
111 DCHECK(error_code
== 1);
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
115 SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPED
, true);
116 host()->SendUnsolicitedReply(pp_resource(),
117 PpapiPluginMsg_VideoCapture_OnError(PP_ERROR_FAILED
));
120 void PepperVideoCaptureHost::OnRemoved(media::VideoCapture
* capture
) {
123 void PepperVideoCaptureHost::OnBufferReady(
124 media::VideoCapture
* capture
,
125 scoped_refptr
<media::VideoCapture::VideoFrameBuffer
> buffer
) {
126 DCHECK(buffer
.get());
127 for (uint32_t i
= 0; i
< buffers_
.size(); ++i
) {
128 if (!buffers_
[i
].in_use
) {
129 // TODO(ihf): Switch to a size calculation based on stride.
130 // Stride is filled out now but not more meaningful than size
131 // until wjia unifies VideoFrameBuffer and media::VideoFrame.
132 size_t size
= std::min(static_cast<size_t>(buffers_
[i
].buffer
->size()),
133 buffer
->buffer_size
);
134 memcpy(buffers_
[i
].data
, buffer
->memory_pointer
, size
);
135 buffers_
[i
].in_use
= true;
136 platform_video_capture_
->FeedBuffer(buffer
);
137 host()->SendUnsolicitedReply(pp_resource(),
138 PpapiPluginMsg_VideoCapture_OnBufferReady(i
));
143 // No free slot, just discard the frame and tell the media layer it can
144 // re-use the buffer.
145 platform_video_capture_
->FeedBuffer(buffer
);
148 void PepperVideoCaptureHost::OnDeviceInfoReceived(
149 media::VideoCapture
* capture
,
150 const media::VideoCaptureParams
& device_info
) {
151 PP_VideoCaptureDeviceInfo_Dev info
= {
152 static_cast<uint32_t>(device_info
.width
),
153 static_cast<uint32_t>(device_info
.height
),
154 static_cast<uint32_t>(device_info
.frame_per_second
)
159 int uv_width
= info
.width
/ 2;
160 int uv_height
= info
.height
/ 2;
161 size_t size
= info
.width
* info
.height
+ 2 * uv_width
* uv_height
;
163 ppapi::proxy::ResourceMessageReplyParams
params(pp_resource(), 0);
165 // Allocate buffers. We keep a reference to them, that is released in
166 // ReleaseBuffers. In the mean time, we prepare the resource and handle here
167 // for sending below.
168 std::vector
<HostResource
> buffer_host_resources
;
169 buffers_
.reserve(buffer_count_hint_
);
170 ::ppapi::ResourceTracker
* tracker
=
171 HostGlobals::Get()->GetResourceTracker();
172 ppapi::proxy::HostDispatcher
* dispatcher
=
173 ppapi::proxy::HostDispatcher::GetForInstance(pp_instance());
174 for (size_t i
= 0; i
< buffer_count_hint_
; ++i
) {
175 PP_Resource res
= PPB_Buffer_Impl::Create(pp_instance(), size
);
179 EnterResourceNoLock
<PPB_Buffer_API
> enter(res
, true);
180 DCHECK(enter
.succeeded());
183 buf
.buffer
= static_cast<PPB_Buffer_Impl
*>(enter
.object());
184 buf
.data
= buf
.buffer
->Map();
186 tracker
->ReleaseResource(res
);
189 buffers_
.push_back(buf
);
191 // Add to HostResource array to be sent.
193 HostResource host_resource
;
194 host_resource
.SetHostResource(pp_instance(), res
);
195 buffer_host_resources
.push_back(host_resource
);
197 // Add a reference for the plugin, which is resposible for releasing it.
198 tracker
->AddRefResource(res
);
201 // Add the serialized shared memory handle to params. FileDescriptor is
202 // treated in special case.
204 EnterResourceNoLock
<PPB_Buffer_API
> enter(res
, true);
205 DCHECK(enter
.succeeded());
207 int32_t result
= enter
.object()->GetSharedMemory(&handle
);
208 DCHECK(result
== PP_OK
);
209 // TODO(piman/brettw): Change trusted interface to return a PP_FileHandle,
210 // those casts are ugly.
211 base::PlatformFile platform_file
=
213 reinterpret_cast<HANDLE
>(static_cast<intptr_t>(handle
));
214 #elif defined(OS_POSIX)
217 #error Not implemented.
220 ppapi::proxy::SerializedHandle(
221 dispatcher
->ShareHandleWithRemote(platform_file
, false),
226 if (buffers_
.empty()) {
227 // We couldn't allocate/map buffers at all. Send an error and stop the
229 SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPING
, true);
230 platform_video_capture_
->StopCapture(this);
231 OnError(capture
, PP_ERROR_NOMEMORY
);
235 host()->Send(new PpapiPluginMsg_ResourceReply(
236 params
, PpapiPluginMsg_VideoCapture_OnDeviceInfo(
237 info
, buffer_host_resources
, size
)));
240 webkit::ppapi::PluginDelegate
* PepperVideoCaptureHost::GetPluginDelegate() {
241 webkit::ppapi::PluginInstance
* instance
=
242 renderer_ppapi_host_
->GetPluginInstance(pp_instance());
244 return instance
->delegate();
248 int32_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_
)
254 return PP_ERROR_FAILED
;
256 webkit::ppapi::PluginDelegate
* plugin_delegate
= GetPluginDelegate();
257 if (!plugin_delegate
)
258 return PP_ERROR_FAILED
;
260 SetRequestedInfo(requested_info
, buffer_count
);
262 platform_video_capture_
=
263 plugin_delegate
->CreateVideoCapture(device_id
, this);
265 open_reply_context_
= context
->MakeReplyMessageContext();
267 // It is able to complete synchronously if the default device is used.
268 bool sync_completion
= device_id
.empty();
269 if (sync_completion
) {
270 // Send OpenACK directly, but still need to return PP_OK_COMPLETIONPENDING
271 // to make PluginResource happy.
272 OnInitialized(platform_video_capture_
.get(), true);
275 return PP_OK_COMPLETIONPENDING
;
278 int32_t PepperVideoCaptureHost::OnStartCapture(
279 ppapi::host::HostMessageContext
* context
) {
280 if (!SetStatus(PP_VIDEO_CAPTURE_STATUS_STARTING
, false) ||
281 !platform_video_capture_
.get())
282 return PP_ERROR_FAILED
;
284 DCHECK(buffers_
.empty());
286 // It's safe to call this regardless it's capturing or not, because
287 // PepperPlatformVideoCaptureImpl maintains the state.
288 platform_video_capture_
->StartCapture(this, capability_
);
292 int32_t PepperVideoCaptureHost::OnReuseBuffer(
293 ppapi::host::HostMessageContext
* context
,
295 if (buffer
>= buffers_
.size() || !buffers_
[buffer
].in_use
)
296 return PP_ERROR_BADARGUMENT
;
297 buffers_
[buffer
].in_use
= false;
301 int32_t PepperVideoCaptureHost::OnStopCapture(
302 ppapi::host::HostMessageContext
* context
) {
303 return StopCapture();
306 int32_t PepperVideoCaptureHost::OnClose(
307 ppapi::host::HostMessageContext
* context
) {
311 int32_t PepperVideoCaptureHost::StopCapture() {
312 if (!SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPING
, false))
313 return PP_ERROR_FAILED
;
315 DCHECK(platform_video_capture_
.get());
318 // It's safe to call this regardless it's capturing or not, because
319 // PepperPlatformVideoCaptureImpl maintains the state.
320 platform_video_capture_
->StopCapture(this);
324 int32_t PepperVideoCaptureHost::Close() {
325 if (!platform_video_capture_
)
329 DCHECK(buffers_
.empty());
330 DetachPlatformVideoCapture();
334 void PepperVideoCaptureHost::ReleaseBuffers() {
335 ::ppapi::ResourceTracker
* tracker
= HostGlobals::Get()->GetResourceTracker();
336 for (size_t i
= 0; i
< buffers_
.size(); ++i
) {
337 buffers_
[i
].buffer
->Unmap();
338 tracker
->ReleaseResource(buffers_
[i
].buffer
->pp_resource());
343 void PepperVideoCaptureHost::SendStatus() {
344 host()->SendUnsolicitedReply(pp_resource(),
345 PpapiPluginMsg_VideoCapture_OnStatus(status_
));
348 void PepperVideoCaptureHost::SetRequestedInfo(
349 const PP_VideoCaptureDeviceInfo_Dev
& device_info
,
350 uint32_t buffer_count
) {
351 // Clamp the buffer count to between 1 and |kMaxBuffers|.
352 buffer_count_hint_
= std::min(std::max(buffer_count
, 1U), kMaxBuffers
);
354 capability_
.width
= device_info
.width
;
355 capability_
.height
= device_info
.height
;
356 capability_
.frame_rate
= device_info
.frames_per_second
;
357 capability_
.expected_capture_delay
= 0; // Ignored.
358 capability_
.color
= media::VideoCaptureCapability::kI420
;
359 capability_
.interlaced
= false; // Ignored.
362 void PepperVideoCaptureHost::DetachPlatformVideoCapture() {
363 if (platform_video_capture_
) {
364 platform_video_capture_
->DetachEventHandler();
365 platform_video_capture_
= NULL
;
369 bool PepperVideoCaptureHost::SetStatus(PP_VideoCaptureStatus_Dev status
,
373 case PP_VIDEO_CAPTURE_STATUS_STOPPED
:
374 if (status_
!= PP_VIDEO_CAPTURE_STATUS_STOPPING
)
377 case PP_VIDEO_CAPTURE_STATUS_STARTING
:
378 if (status_
!= PP_VIDEO_CAPTURE_STATUS_STOPPED
)
381 case PP_VIDEO_CAPTURE_STATUS_STARTED
:
383 case PP_VIDEO_CAPTURE_STATUS_STARTING
:
384 case PP_VIDEO_CAPTURE_STATUS_PAUSED
:
390 case PP_VIDEO_CAPTURE_STATUS_PAUSED
:
392 case PP_VIDEO_CAPTURE_STATUS_STARTING
:
393 case PP_VIDEO_CAPTURE_STATUS_STARTED
:
399 case PP_VIDEO_CAPTURE_STATUS_STOPPING
:
401 case PP_VIDEO_CAPTURE_STATUS_STARTING
:
402 case PP_VIDEO_CAPTURE_STATUS_STARTED
:
403 case PP_VIDEO_CAPTURE_STATUS_PAUSED
:
416 PepperVideoCaptureHost::BufferInfo::BufferInfo()
422 PepperVideoCaptureHost::BufferInfo::~BufferInfo() {
425 } // namespace content