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 "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_frame_impl.h"
13 #include "media/base/limits.h"
14 #include "media/base/video_frame.h"
15 #include "ppapi/host/dispatch_host_message.h"
16 #include "ppapi/host/ppapi_host.h"
17 #include "ppapi/proxy/host_dispatcher.h"
18 #include "ppapi/proxy/ppapi_messages.h"
19 #include "ppapi/shared_impl/host_resource.h"
20 #include "ppapi/thunk/enter.h"
21 #include "ppapi/thunk/ppb_buffer_api.h"
23 using ppapi::HostResource
;
24 using ppapi::TrackedCallback
;
25 using ppapi::thunk::EnterResourceNoLock
;
26 using ppapi::thunk::PPB_Buffer_API
;
30 // Maximum number of buffers to actually allocate.
31 const uint32_t kMaxBuffers
= 20;
37 PepperVideoCaptureHost::PepperVideoCaptureHost(RendererPpapiHostImpl
* host
,
40 : ResourceHost(host
->GetPpapiHost(), instance
, resource
),
41 renderer_ppapi_host_(host
),
42 buffer_count_hint_(0),
43 status_(PP_VIDEO_CAPTURE_STATUS_STOPPED
),
44 enumeration_helper_(this,
45 PepperMediaDeviceManager::GetForRenderFrame(
46 host
->GetRenderFrameForInstance(pp_instance())),
47 PP_DEVICETYPE_DEV_VIDEOCAPTURE
,
48 host
->GetDocumentURL(instance
)) {
51 PepperVideoCaptureHost::~PepperVideoCaptureHost() {
55 bool PepperVideoCaptureHost::Init() {
56 return !!renderer_ppapi_host_
->GetPluginInstance(pp_instance());
59 int32_t PepperVideoCaptureHost::OnResourceMessageReceived(
60 const IPC::Message
& msg
,
61 ppapi::host::HostMessageContext
* context
) {
62 int32_t result
= PP_ERROR_FAILED
;
63 if (enumeration_helper_
.HandleResourceMessage(msg
, context
, &result
))
66 PPAPI_BEGIN_MESSAGE_MAP(PepperVideoCaptureHost
, msg
)
67 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoCapture_Open
, OnOpen
)
68 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_VideoCapture_StartCapture
,
70 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoCapture_ReuseBuffer
,
72 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_VideoCapture_StopCapture
,
74 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_VideoCapture_Close
,
76 PPAPI_END_MESSAGE_MAP()
77 return PP_ERROR_FAILED
;
80 void PepperVideoCaptureHost::OnInitialized(bool succeeded
) {
82 open_reply_context_
.params
.set_result(PP_OK
);
84 DetachPlatformVideoCapture();
85 open_reply_context_
.params
.set_result(PP_ERROR_FAILED
);
88 host()->SendReply(open_reply_context_
,
89 PpapiPluginMsg_VideoCapture_OpenReply());
92 void PepperVideoCaptureHost::OnStarted() {
93 if (SetStatus(PP_VIDEO_CAPTURE_STATUS_STARTED
, false))
97 void PepperVideoCaptureHost::OnStopped() {
98 if (SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPED
, false))
102 void PepperVideoCaptureHost::OnPaused() {
103 if (SetStatus(PP_VIDEO_CAPTURE_STATUS_PAUSED
, false))
107 void PepperVideoCaptureHost::OnError() {
111 void PepperVideoCaptureHost::PostErrorReply() {
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(
118 PpapiPluginMsg_VideoCapture_OnError(
119 static_cast<uint32_t>(PP_ERROR_FAILED
)));
122 void PepperVideoCaptureHost::OnFrameReady(
123 const scoped_refptr
<media::VideoFrame
>& frame
,
124 media::VideoCaptureFormat format
) {
127 if (alloc_size_
!= frame
->coded_size() || buffers_
.empty()) {
128 AllocBuffers(frame
->coded_size(), format
.frame_rate
);
129 alloc_size_
= frame
->coded_size();
132 for (uint32_t i
= 0; i
< buffers_
.size(); ++i
) {
133 if (!buffers_
[i
].in_use
) {
134 DCHECK_EQ(frame
->format(), media::VideoFrame::I420
);
135 if (buffers_
[i
].buffer
->size() <
136 media::VideoFrame::AllocationSize(frame
->format(),
137 frame
->coded_size())) {
138 // TODO(ihf): handle size mismatches gracefully here.
141 uint8
* dst
= reinterpret_cast<uint8
*>(buffers_
[i
].data
);
142 COMPILE_ASSERT(media::VideoFrame::kYPlane
== 0, y_plane_should_be_0
);
143 COMPILE_ASSERT(media::VideoFrame::kUPlane
== 1, u_plane_should_be_1
);
144 COMPILE_ASSERT(media::VideoFrame::kVPlane
== 2, v_plane_should_be_2
);
145 for (size_t j
= 0; j
< media::VideoFrame::NumPlanes(frame
->format());
147 const uint8
* src
= frame
->data(j
);
148 const size_t row_bytes
= frame
->row_bytes(j
);
149 const size_t src_stride
= frame
->stride(j
);
150 for (int k
= 0; k
< frame
->rows(j
); ++k
) {
151 memcpy(dst
, src
, row_bytes
);
156 buffers_
[i
].in_use
= true;
157 host()->SendUnsolicitedReply(
158 pp_resource(), PpapiPluginMsg_VideoCapture_OnBufferReady(i
));
164 void PepperVideoCaptureHost::AllocBuffers(const gfx::Size
& resolution
,
166 PP_VideoCaptureDeviceInfo_Dev info
= {
167 static_cast<uint32_t>(resolution
.width()),
168 static_cast<uint32_t>(resolution
.height()),
169 static_cast<uint32_t>(frame_rate
)};
172 const size_t size
= media::VideoFrame::AllocationSize(
173 media::VideoFrame::I420
, gfx::Size(info
.width
, info
.height
));
175 ppapi::proxy::ResourceMessageReplyParams
params(pp_resource(), 0);
177 // Allocate buffers. We keep a reference to them, that is released in
178 // ReleaseBuffers. In the mean time, we prepare the resource and handle here
179 // for sending below.
180 std::vector
<HostResource
> buffer_host_resources
;
181 buffers_
.reserve(buffer_count_hint_
);
182 ppapi::ResourceTracker
* tracker
= HostGlobals::Get()->GetResourceTracker();
183 ppapi::proxy::HostDispatcher
* dispatcher
=
184 ppapi::proxy::HostDispatcher::GetForInstance(pp_instance());
185 for (size_t i
= 0; i
< buffer_count_hint_
; ++i
) {
186 PP_Resource res
= PPB_Buffer_Impl::Create(pp_instance(), size
);
190 EnterResourceNoLock
<PPB_Buffer_API
> enter(res
, true);
191 DCHECK(enter
.succeeded());
194 buf
.buffer
= static_cast<PPB_Buffer_Impl
*>(enter
.object());
195 buf
.data
= buf
.buffer
->Map();
197 tracker
->ReleaseResource(res
);
200 buffers_
.push_back(buf
);
202 // Add to HostResource array to be sent.
204 HostResource host_resource
;
205 host_resource
.SetHostResource(pp_instance(), res
);
206 buffer_host_resources
.push_back(host_resource
);
208 // Add a reference for the plugin, which is resposible for releasing it.
209 tracker
->AddRefResource(res
);
212 // Add the serialized shared memory handle to params. FileDescriptor is
213 // treated in special case.
215 EnterResourceNoLock
<PPB_Buffer_API
> enter(res
, true);
216 DCHECK(enter
.succeeded());
218 int32_t result
= enter
.object()->GetSharedMemory(&handle
);
219 DCHECK(result
== PP_OK
);
220 // TODO(piman/brettw): Change trusted interface to return a PP_FileHandle,
221 // those casts are ugly.
222 base::PlatformFile platform_file
=
224 reinterpret_cast<HANDLE
>(static_cast<intptr_t>(handle
));
225 #elif defined(OS_POSIX)
228 #error Not implemented.
230 params
.AppendHandle(ppapi::proxy::SerializedHandle(
231 dispatcher
->ShareHandleWithRemote(platform_file
, false), size
));
235 if (buffers_
.empty()) {
236 // We couldn't allocate/map buffers at all. Send an error and stop the
238 SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPING
, true);
239 platform_video_capture_
->StopCapture();
245 new PpapiPluginMsg_ResourceReply(params
,
246 PpapiPluginMsg_VideoCapture_OnDeviceInfo(
247 info
, buffer_host_resources
, size
)));
250 int32_t PepperVideoCaptureHost::OnOpen(
251 ppapi::host::HostMessageContext
* context
,
252 const std::string
& device_id
,
253 const PP_VideoCaptureDeviceInfo_Dev
& requested_info
,
254 uint32_t buffer_count
) {
255 if (platform_video_capture_
.get())
256 return PP_ERROR_FAILED
;
258 SetRequestedInfo(requested_info
, buffer_count
);
260 GURL document_url
= renderer_ppapi_host_
->GetDocumentURL(pp_instance());
261 if (!document_url
.is_valid())
262 return PP_ERROR_FAILED
;
264 platform_video_capture_
.reset(new PepperPlatformVideoCapture(
265 renderer_ppapi_host_
->GetRenderFrameForInstance(pp_instance())->
271 open_reply_context_
= context
->MakeReplyMessageContext();
273 return PP_OK_COMPLETIONPENDING
;
276 int32_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
;
282 DCHECK(buffers_
.empty());
284 // It's safe to call this regardless it's capturing or not, because
285 // PepperPlatformVideoCapture maintains the state.
286 platform_video_capture_
->StartCapture(video_capture_params_
);
290 int32_t PepperVideoCaptureHost::OnReuseBuffer(
291 ppapi::host::HostMessageContext
* context
,
293 if (buffer
>= buffers_
.size() || !buffers_
[buffer
].in_use
)
294 return PP_ERROR_BADARGUMENT
;
295 buffers_
[buffer
].in_use
= false;
299 int32_t PepperVideoCaptureHost::OnStopCapture(
300 ppapi::host::HostMessageContext
* context
) {
301 return StopCapture();
304 int32_t PepperVideoCaptureHost::OnClose(
305 ppapi::host::HostMessageContext
* context
) {
309 int32_t PepperVideoCaptureHost::StopCapture() {
310 if (!SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPING
, false))
311 return PP_ERROR_FAILED
;
313 DCHECK(platform_video_capture_
.get());
316 // It's safe to call this regardless it's capturing or not, because
317 // PepperPlatformVideoCapture maintains the state.
318 platform_video_capture_
->StopCapture();
322 int32_t PepperVideoCaptureHost::Close() {
323 if (!platform_video_capture_
.get())
327 DCHECK(buffers_
.empty());
328 DetachPlatformVideoCapture();
332 void 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());
341 void PepperVideoCaptureHost::SendStatus() {
342 host()->SendUnsolicitedReply(pp_resource(),
343 PpapiPluginMsg_VideoCapture_OnStatus(status_
));
346 void 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 // Clamp the frame rate to between 1 and |kMaxFramesPerSecond - 1|.
352 int frames_per_second
=
353 std::min(std::max(device_info
.frames_per_second
, 1U),
354 static_cast<uint32_t>(media::limits::kMaxFramesPerSecond
- 1));
356 video_capture_params_
.requested_format
= media::VideoCaptureFormat(
357 gfx::Size(device_info
.width
, device_info
.height
),
359 media::PIXEL_FORMAT_I420
);
360 video_capture_params_
.allow_resolution_change
= false;
363 void PepperVideoCaptureHost::DetachPlatformVideoCapture() {
364 if (platform_video_capture_
) {
365 platform_video_capture_
->DetachEventHandler();
366 platform_video_capture_
.reset();
370 bool PepperVideoCaptureHost::SetStatus(PP_VideoCaptureStatus_Dev status
,
374 case PP_VIDEO_CAPTURE_STATUS_STOPPED
:
375 if (status_
!= PP_VIDEO_CAPTURE_STATUS_STOPPING
)
378 case PP_VIDEO_CAPTURE_STATUS_STARTING
:
379 if (status_
!= PP_VIDEO_CAPTURE_STATUS_STOPPED
)
382 case PP_VIDEO_CAPTURE_STATUS_STARTED
:
384 case PP_VIDEO_CAPTURE_STATUS_STARTING
:
385 case PP_VIDEO_CAPTURE_STATUS_PAUSED
:
391 case PP_VIDEO_CAPTURE_STATUS_PAUSED
:
393 case PP_VIDEO_CAPTURE_STATUS_STARTING
:
394 case PP_VIDEO_CAPTURE_STATUS_STARTED
:
400 case PP_VIDEO_CAPTURE_STATUS_STOPPING
:
402 case PP_VIDEO_CAPTURE_STATUS_STARTING
:
403 case PP_VIDEO_CAPTURE_STATUS_STARTED
:
404 case PP_VIDEO_CAPTURE_STATUS_PAUSED
:
417 PepperVideoCaptureHost::BufferInfo::BufferInfo()
418 : in_use(false), data(NULL
), buffer() {
421 PepperVideoCaptureHost::BufferInfo::~BufferInfo() {
424 } // namespace content