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/media/media_stream_video_source.h"
8 #include "content/renderer/pepper/host_globals.h"
9 #include "content/renderer/pepper/pepper_media_device_manager.h"
10 #include "content/renderer/pepper/pepper_platform_video_capture.h"
11 #include "content/renderer/pepper/pepper_plugin_instance_impl.h"
12 #include "content/renderer/pepper/renderer_ppapi_host_impl.h"
13 #include "content/renderer/render_frame_impl.h"
14 #include "media/base/limits.h"
15 #include "media/base/video_frame.h"
16 #include "ppapi/host/dispatch_host_message.h"
17 #include "ppapi/host/ppapi_host.h"
18 #include "ppapi/proxy/host_dispatcher.h"
19 #include "ppapi/proxy/ppapi_messages.h"
20 #include "ppapi/shared_impl/host_resource.h"
21 #include "ppapi/thunk/enter.h"
22 #include "ppapi/thunk/ppb_buffer_api.h"
24 using ppapi::HostResource
;
25 using ppapi::TrackedCallback
;
26 using ppapi::thunk::EnterResourceNoLock
;
27 using ppapi::thunk::PPB_Buffer_API
;
31 // Maximum number of buffers to actually allocate.
32 const uint32_t kMaxBuffers
= 20;
38 PepperVideoCaptureHost::PepperVideoCaptureHost(RendererPpapiHostImpl
* host
,
41 : ResourceHost(host
->GetPpapiHost(), instance
, resource
),
42 renderer_ppapi_host_(host
),
43 buffer_count_hint_(0),
44 status_(PP_VIDEO_CAPTURE_STATUS_STOPPED
),
45 enumeration_helper_(this,
46 PepperMediaDeviceManager::GetForRenderFrame(
47 host
->GetRenderFrameForInstance(pp_instance())),
48 PP_DEVICETYPE_DEV_VIDEOCAPTURE
,
49 host
->GetDocumentURL(instance
)) {
52 PepperVideoCaptureHost::~PepperVideoCaptureHost() {
56 bool PepperVideoCaptureHost::Init() {
57 return !!renderer_ppapi_host_
->GetPluginInstance(pp_instance());
60 int32_t PepperVideoCaptureHost::OnResourceMessageReceived(
61 const IPC::Message
& msg
,
62 ppapi::host::HostMessageContext
* context
) {
63 int32_t result
= PP_ERROR_FAILED
;
64 if (enumeration_helper_
.HandleResourceMessage(msg
, context
, &result
))
67 PPAPI_BEGIN_MESSAGE_MAP(PepperVideoCaptureHost
, msg
)
68 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoCapture_Open
, OnOpen
)
69 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_VideoCapture_StartCapture
,
71 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoCapture_ReuseBuffer
,
73 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_VideoCapture_StopCapture
,
75 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_VideoCapture_Close
,
77 PPAPI_END_MESSAGE_MAP()
78 return PP_ERROR_FAILED
;
81 void PepperVideoCaptureHost::OnInitialized(bool succeeded
) {
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() {
94 if (SetStatus(PP_VIDEO_CAPTURE_STATUS_STARTED
, false))
98 void PepperVideoCaptureHost::OnStopped() {
99 if (SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPED
, false))
103 void PepperVideoCaptureHost::OnPaused() {
104 if (SetStatus(PP_VIDEO_CAPTURE_STATUS_PAUSED
, false))
108 void PepperVideoCaptureHost::OnError() {
112 void PepperVideoCaptureHost::PostErrorReply() {
113 // It either comes because some error was detected while starting (e.g. 2
114 // conflicting "master" resolution), or because the browser failed to start
116 SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPED
, true);
117 host()->SendUnsolicitedReply(
119 PpapiPluginMsg_VideoCapture_OnError(
120 static_cast<uint32_t>(PP_ERROR_FAILED
)));
123 void PepperVideoCaptureHost::OnFrameReady(
124 const scoped_refptr
<media::VideoFrame
>& frame
) {
127 if (alloc_size_
!= frame
->visible_rect().size() || buffers_
.empty()) {
128 alloc_size_
= frame
->visible_rect().size();
130 int rounded_frame_rate
;
131 if (frame
->metadata()->GetDouble(media::VideoFrameMetadata::FRAME_RATE
,
133 rounded_frame_rate
= static_cast<int>(frame_rate
+ 0.5 /* round */);
135 rounded_frame_rate
= MediaStreamVideoSource::kUnknownFrameRate
;
136 AllocBuffers(alloc_size_
, rounded_frame_rate
);
139 for (uint32_t i
= 0; i
< buffers_
.size(); ++i
) {
140 if (!buffers_
[i
].in_use
) {
141 DCHECK_EQ(frame
->format(), media::VideoFrame::I420
);
142 if (buffers_
[i
].buffer
->size() <
143 media::VideoFrame::AllocationSize(frame
->format(), alloc_size_
)) {
144 // TODO(ihf): handle size mismatches gracefully here.
147 uint8
* dst
= reinterpret_cast<uint8
*>(buffers_
[i
].data
);
148 static_assert(media::VideoFrame::kYPlane
== 0, "y plane should be 0");
149 static_assert(media::VideoFrame::kUPlane
== 1, "u plane should be 1");
150 static_assert(media::VideoFrame::kVPlane
== 2, "v plane should be 2");
151 for (size_t j
= 0; j
< media::VideoFrame::NumPlanes(frame
->format());
153 const uint8
* src
= frame
->visible_data(j
);
154 const size_t row_bytes
= frame
->row_bytes(j
);
155 const size_t src_stride
= frame
->stride(j
);
156 for (int k
= 0; k
< frame
->rows(j
); ++k
) {
157 memcpy(dst
, src
, row_bytes
);
162 buffers_
[i
].in_use
= true;
163 host()->SendUnsolicitedReply(
164 pp_resource(), PpapiPluginMsg_VideoCapture_OnBufferReady(i
));
170 void PepperVideoCaptureHost::AllocBuffers(const gfx::Size
& resolution
,
172 PP_VideoCaptureDeviceInfo_Dev info
= {
173 static_cast<uint32_t>(resolution
.width()),
174 static_cast<uint32_t>(resolution
.height()),
175 static_cast<uint32_t>(frame_rate
)};
178 const size_t size
= media::VideoFrame::AllocationSize(
179 media::VideoFrame::I420
, gfx::Size(info
.width
, info
.height
));
181 ppapi::proxy::ResourceMessageReplyParams
params(pp_resource(), 0);
183 // Allocate buffers. We keep a reference to them, that is released in
184 // ReleaseBuffers. In the mean time, we prepare the resource and handle here
185 // for sending below.
186 std::vector
<HostResource
> buffer_host_resources
;
187 buffers_
.reserve(buffer_count_hint_
);
188 ppapi::ResourceTracker
* tracker
= HostGlobals::Get()->GetResourceTracker();
189 ppapi::proxy::HostDispatcher
* dispatcher
=
190 ppapi::proxy::HostDispatcher::GetForInstance(pp_instance());
191 for (size_t i
= 0; i
< buffer_count_hint_
; ++i
) {
192 PP_Resource res
= PPB_Buffer_Impl::Create(pp_instance(), size
);
196 EnterResourceNoLock
<PPB_Buffer_API
> enter(res
, true);
197 DCHECK(enter
.succeeded());
200 buf
.buffer
= static_cast<PPB_Buffer_Impl
*>(enter
.object());
201 buf
.data
= buf
.buffer
->Map();
203 tracker
->ReleaseResource(res
);
206 buffers_
.push_back(buf
);
208 // Add to HostResource array to be sent.
210 HostResource host_resource
;
211 host_resource
.SetHostResource(pp_instance(), res
);
212 buffer_host_resources
.push_back(host_resource
);
214 // Add a reference for the plugin, which is resposible for releasing it.
215 tracker
->AddRefResource(res
);
218 // Add the serialized shared memory handle to params. FileDescriptor is
219 // treated in special case.
221 EnterResourceNoLock
<PPB_Buffer_API
> enter(res
, true);
222 DCHECK(enter
.succeeded());
224 int32_t result
= enter
.object()->GetSharedMemory(&handle
);
225 DCHECK(result
== PP_OK
);
226 // TODO(piman/brettw): Change trusted interface to return a PP_FileHandle,
227 // those casts are ugly.
228 base::PlatformFile platform_file
=
230 reinterpret_cast<HANDLE
>(static_cast<intptr_t>(handle
));
231 #elif defined(OS_POSIX)
234 #error Not implemented.
236 params
.AppendHandle(ppapi::proxy::SerializedHandle(
237 dispatcher
->ShareHandleWithRemote(platform_file
, false), size
));
241 if (buffers_
.empty()) {
242 // We couldn't allocate/map buffers at all. Send an error and stop the
244 SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPING
, true);
245 platform_video_capture_
->StopCapture();
251 new PpapiPluginMsg_ResourceReply(params
,
252 PpapiPluginMsg_VideoCapture_OnDeviceInfo(
253 info
, buffer_host_resources
, size
)));
256 int32_t PepperVideoCaptureHost::OnOpen(
257 ppapi::host::HostMessageContext
* context
,
258 const std::string
& device_id
,
259 const PP_VideoCaptureDeviceInfo_Dev
& requested_info
,
260 uint32_t buffer_count
) {
261 if (platform_video_capture_
.get())
262 return PP_ERROR_FAILED
;
264 SetRequestedInfo(requested_info
, buffer_count
);
266 GURL document_url
= renderer_ppapi_host_
->GetDocumentURL(pp_instance());
267 if (!document_url
.is_valid())
268 return PP_ERROR_FAILED
;
270 platform_video_capture_
.reset(new PepperPlatformVideoCapture(
271 renderer_ppapi_host_
->GetRenderFrameForInstance(pp_instance())->
277 open_reply_context_
= context
->MakeReplyMessageContext();
279 return PP_OK_COMPLETIONPENDING
;
282 int32_t PepperVideoCaptureHost::OnStartCapture(
283 ppapi::host::HostMessageContext
* context
) {
284 if (!SetStatus(PP_VIDEO_CAPTURE_STATUS_STARTING
, false) ||
285 !platform_video_capture_
.get())
286 return PP_ERROR_FAILED
;
288 DCHECK(buffers_
.empty());
290 // It's safe to call this regardless it's capturing or not, because
291 // PepperPlatformVideoCapture maintains the state.
292 platform_video_capture_
->StartCapture(video_capture_params_
);
296 int32_t PepperVideoCaptureHost::OnReuseBuffer(
297 ppapi::host::HostMessageContext
* context
,
299 if (buffer
>= buffers_
.size() || !buffers_
[buffer
].in_use
)
300 return PP_ERROR_BADARGUMENT
;
301 buffers_
[buffer
].in_use
= false;
305 int32_t PepperVideoCaptureHost::OnStopCapture(
306 ppapi::host::HostMessageContext
* context
) {
307 return StopCapture();
310 int32_t PepperVideoCaptureHost::OnClose(
311 ppapi::host::HostMessageContext
* context
) {
315 int32_t PepperVideoCaptureHost::StopCapture() {
316 if (!SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPING
, false))
317 return PP_ERROR_FAILED
;
319 DCHECK(platform_video_capture_
.get());
322 // It's safe to call this regardless it's capturing or not, because
323 // PepperPlatformVideoCapture maintains the state.
324 platform_video_capture_
->StopCapture();
328 int32_t PepperVideoCaptureHost::Close() {
329 if (!platform_video_capture_
.get())
333 DCHECK(buffers_
.empty());
334 DetachPlatformVideoCapture();
338 void PepperVideoCaptureHost::ReleaseBuffers() {
339 ppapi::ResourceTracker
* tracker
= HostGlobals::Get()->GetResourceTracker();
340 for (size_t i
= 0; i
< buffers_
.size(); ++i
) {
341 buffers_
[i
].buffer
->Unmap();
342 tracker
->ReleaseResource(buffers_
[i
].buffer
->pp_resource());
347 void PepperVideoCaptureHost::SendStatus() {
348 host()->SendUnsolicitedReply(pp_resource(),
349 PpapiPluginMsg_VideoCapture_OnStatus(status_
));
352 void PepperVideoCaptureHost::SetRequestedInfo(
353 const PP_VideoCaptureDeviceInfo_Dev
& device_info
,
354 uint32_t buffer_count
) {
355 // Clamp the buffer count to between 1 and |kMaxBuffers|.
356 buffer_count_hint_
= std::min(std::max(buffer_count
, 1U), kMaxBuffers
);
357 // Clamp the frame rate to between 1 and |kMaxFramesPerSecond - 1|.
358 int frames_per_second
=
359 std::min(std::max(device_info
.frames_per_second
, 1U),
360 static_cast<uint32_t>(media::limits::kMaxFramesPerSecond
- 1));
362 video_capture_params_
.requested_format
= media::VideoCaptureFormat(
363 gfx::Size(device_info
.width
, device_info
.height
),
365 media::PIXEL_FORMAT_I420
);
368 void PepperVideoCaptureHost::DetachPlatformVideoCapture() {
369 if (platform_video_capture_
) {
370 platform_video_capture_
->DetachEventHandler();
371 platform_video_capture_
.reset();
375 bool PepperVideoCaptureHost::SetStatus(PP_VideoCaptureStatus_Dev status
,
379 case PP_VIDEO_CAPTURE_STATUS_STOPPED
:
380 if (status_
!= PP_VIDEO_CAPTURE_STATUS_STOPPING
)
383 case PP_VIDEO_CAPTURE_STATUS_STARTING
:
384 if (status_
!= PP_VIDEO_CAPTURE_STATUS_STOPPED
)
387 case PP_VIDEO_CAPTURE_STATUS_STARTED
:
389 case PP_VIDEO_CAPTURE_STATUS_STARTING
:
390 case PP_VIDEO_CAPTURE_STATUS_PAUSED
:
396 case PP_VIDEO_CAPTURE_STATUS_PAUSED
:
398 case PP_VIDEO_CAPTURE_STATUS_STARTING
:
399 case PP_VIDEO_CAPTURE_STATUS_STARTED
:
405 case PP_VIDEO_CAPTURE_STATUS_STOPPING
:
407 case PP_VIDEO_CAPTURE_STATUS_STARTING
:
408 case PP_VIDEO_CAPTURE_STATUS_STARTED
:
409 case PP_VIDEO_CAPTURE_STATUS_PAUSED
:
422 PepperVideoCaptureHost::BufferInfo::BufferInfo()
423 : in_use(false), data(NULL
), buffer() {
426 PepperVideoCaptureHost::BufferInfo::~BufferInfo() {
429 } // namespace content