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_view_impl.h"
13 #include "ppapi/host/dispatch_host_message.h"
14 #include "ppapi/host/ppapi_host.h"
15 #include "ppapi/proxy/host_dispatcher.h"
16 #include "ppapi/proxy/ppapi_messages.h"
17 #include "ppapi/shared_impl/host_resource.h"
18 #include "ppapi/thunk/enter.h"
19 #include "ppapi/thunk/ppb_buffer_api.h"
20 #include "third_party/WebKit/public/web/WebDocument.h"
21 #include "third_party/WebKit/public/web/WebElement.h"
22 #include "third_party/WebKit/public/web/WebPluginContainer.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),
47 PepperMediaDeviceManager::GetForRenderView(
48 host->GetRenderViewForInstance(pp_instance())),
49 PP_DEVICETYPE_DEV_VIDEOCAPTURE) {
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 IPC_BEGIN_MESSAGE_MAP(PepperVideoCaptureHost, msg)
68 PPAPI_DISPATCH_HOST_RESOURCE_CALL(
69 PpapiHostMsg_VideoCapture_Open,
71 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
72 PpapiHostMsg_VideoCapture_StartCapture,
74 PPAPI_DISPATCH_HOST_RESOURCE_CALL(
75 PpapiHostMsg_VideoCapture_ReuseBuffer,
77 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
78 PpapiHostMsg_VideoCapture_StopCapture,
80 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
81 PpapiHostMsg_VideoCapture_Close,
84 return PP_ERROR_FAILED;
87 void PepperVideoCaptureHost::OnInitialized(media::VideoCapture* capture,
89 DCHECK(capture == platform_video_capture_.get());
92 open_reply_context_.params.set_result(PP_OK);
94 DetachPlatformVideoCapture();
95 open_reply_context_.params.set_result(PP_ERROR_FAILED);
98 host()->SendReply(open_reply_context_,
99 PpapiPluginMsg_VideoCapture_OpenReply());
102 void PepperVideoCaptureHost::OnStarted(media::VideoCapture* capture) {
103 if (SetStatus(PP_VIDEO_CAPTURE_STATUS_STARTED, false))
107 void PepperVideoCaptureHost::OnStopped(media::VideoCapture* capture) {
108 if (SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPED, false))
112 void PepperVideoCaptureHost::OnPaused(media::VideoCapture* capture) {
113 if (SetStatus(PP_VIDEO_CAPTURE_STATUS_PAUSED, false))
117 void PepperVideoCaptureHost::OnError(media::VideoCapture* capture,
119 // Today, the media layer only sends "1" as an error.
120 DCHECK(error_code == 1);
124 void PepperVideoCaptureHost::PostErrorReply() {
125 // It either comes because some error was detected while starting (e.g. 2
126 // conflicting "master" resolution), or because the browser failed to start
128 SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPED, true);
129 host()->SendUnsolicitedReply(pp_resource(),
130 PpapiPluginMsg_VideoCapture_OnError(PP_ERROR_FAILED));
133 void PepperVideoCaptureHost::OnRemoved(media::VideoCapture* capture) {
136 void PepperVideoCaptureHost::OnFrameReady(
137 media::VideoCapture* capture,
138 const scoped_refptr<media::VideoFrame>& frame) {
141 if (alloc_size_ != frame->coded_size()) {
142 AllocBuffers(frame->coded_size(), capture->CaptureFrameRate());
143 alloc_size_ = frame->coded_size();
146 for (uint32_t i = 0; i < buffers_.size(); ++i) {
147 if (!buffers_[i].in_use) {
148 DCHECK_EQ(frame->format(), media::VideoFrame::I420);
149 if (buffers_[i].buffer->size() <
150 media::VideoFrame::AllocationSize(frame->format(),
151 frame->coded_size())) {
152 // TODO(ihf): handle size mismatches gracefully here.
155 uint8* dst = reinterpret_cast<uint8*>(buffers_[i].data);
156 COMPILE_ASSERT(media::VideoFrame::kYPlane == 0, y_plane_should_be_0);
157 COMPILE_ASSERT(media::VideoFrame::kUPlane == 1, u_plane_should_be_1);
158 COMPILE_ASSERT(media::VideoFrame::kVPlane == 2, v_plane_should_be_2);
159 for (size_t j = 0; j < media::VideoFrame::NumPlanes(frame->format());
161 const uint8* src = frame->data(j);
162 const size_t row_bytes = frame->row_bytes(j);
163 const size_t src_stride = frame->stride(j);
164 for (int k = 0; k < frame->rows(j); ++k) {
165 memcpy(dst, src, row_bytes);
170 buffers_[i].in_use = true;
171 host()->SendUnsolicitedReply(pp_resource(),
172 PpapiPluginMsg_VideoCapture_OnBufferReady(i));
178 void PepperVideoCaptureHost::AllocBuffers(
179 const gfx::Size& resolution,
181 PP_VideoCaptureDeviceInfo_Dev info = {
182 static_cast<uint32_t>(resolution.width()),
183 static_cast<uint32_t>(resolution.height()),
184 static_cast<uint32_t>(frame_rate)
188 const size_t size = media::VideoFrame::AllocationSize(
189 media::VideoFrame::I420, gfx::Size(info.width, info.height));
191 ppapi::proxy::ResourceMessageReplyParams params(pp_resource(), 0);
193 // Allocate buffers. We keep a reference to them, that is released in
194 // ReleaseBuffers. In the mean time, we prepare the resource and handle here
195 // for sending below.
196 std::vector<HostResource> buffer_host_resources;
197 buffers_.reserve(buffer_count_hint_);
198 ppapi::ResourceTracker* tracker =
199 HostGlobals::Get()->GetResourceTracker();
200 ppapi::proxy::HostDispatcher* dispatcher =
201 ppapi::proxy::HostDispatcher::GetForInstance(pp_instance());
202 for (size_t i = 0; i < buffer_count_hint_; ++i) {
203 PP_Resource res = PPB_Buffer_Impl::Create(pp_instance(), size);
207 EnterResourceNoLock<PPB_Buffer_API> enter(res, true);
208 DCHECK(enter.succeeded());
211 buf.buffer = static_cast<PPB_Buffer_Impl*>(enter.object());
212 buf.data = buf.buffer->Map();
214 tracker->ReleaseResource(res);
217 buffers_.push_back(buf);
219 // Add to HostResource array to be sent.
221 HostResource host_resource;
222 host_resource.SetHostResource(pp_instance(), res);
223 buffer_host_resources.push_back(host_resource);
225 // Add a reference for the plugin, which is resposible for releasing it.
226 tracker->AddRefResource(res);
229 // Add the serialized shared memory handle to params. FileDescriptor is
230 // treated in special case.
232 EnterResourceNoLock<PPB_Buffer_API> enter(res, true);
233 DCHECK(enter.succeeded());
235 int32_t result = enter.object()->GetSharedMemory(&handle);
236 DCHECK(result == PP_OK);
237 // TODO(piman/brettw): Change trusted interface to return a PP_FileHandle,
238 // those casts are ugly.
239 base::PlatformFile platform_file =
241 reinterpret_cast<HANDLE>(static_cast<intptr_t>(handle));
242 #elif defined(OS_POSIX)
245 #error Not implemented.
248 ppapi::proxy::SerializedHandle(
249 dispatcher->ShareHandleWithRemote(platform_file, false),
254 if (buffers_.empty()) {
255 // We couldn't allocate/map buffers at all. Send an error and stop the
257 SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPING, true);
258 platform_video_capture_->StopCapture(this);
263 host()->Send(new PpapiPluginMsg_ResourceReply(
264 params, PpapiPluginMsg_VideoCapture_OnDeviceInfo(
265 info, buffer_host_resources, size)));
268 int32_t PepperVideoCaptureHost::OnOpen(
269 ppapi::host::HostMessageContext* context,
270 const std::string& device_id,
271 const PP_VideoCaptureDeviceInfo_Dev& requested_info,
272 uint32_t buffer_count) {
273 if (platform_video_capture_.get())
274 return PP_ERROR_FAILED;
276 SetRequestedInfo(requested_info, buffer_count);
278 PepperPluginInstance* instance =
279 renderer_ppapi_host_->GetPluginInstance(pp_instance());
281 return PP_ERROR_FAILED;
283 RenderViewImpl* render_view = static_cast<RenderViewImpl*>(
284 renderer_ppapi_host_->GetRenderViewForInstance(pp_instance()));
286 platform_video_capture_ = new PepperPlatformVideoCapture(
287 render_view->AsWeakPtr(), device_id,
288 instance->GetContainer()->element().document().url(), this);
290 open_reply_context_ = context->MakeReplyMessageContext();
292 return PP_OK_COMPLETIONPENDING;
295 int32_t PepperVideoCaptureHost::OnStartCapture(
296 ppapi::host::HostMessageContext* context) {
297 if (!SetStatus(PP_VIDEO_CAPTURE_STATUS_STARTING, false) ||
298 !platform_video_capture_.get())
299 return PP_ERROR_FAILED;
301 DCHECK(buffers_.empty());
303 // It's safe to call this regardless it's capturing or not, because
304 // PepperPlatformVideoCapture maintains the state.
305 platform_video_capture_->StartCapture(this, video_capture_params_);
309 int32_t PepperVideoCaptureHost::OnReuseBuffer(
310 ppapi::host::HostMessageContext* context,
312 if (buffer >= buffers_.size() || !buffers_[buffer].in_use)
313 return PP_ERROR_BADARGUMENT;
314 buffers_[buffer].in_use = false;
318 int32_t PepperVideoCaptureHost::OnStopCapture(
319 ppapi::host::HostMessageContext* context) {
320 return StopCapture();
323 int32_t PepperVideoCaptureHost::OnClose(
324 ppapi::host::HostMessageContext* context) {
328 int32_t PepperVideoCaptureHost::StopCapture() {
329 if (!SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPING, false))
330 return PP_ERROR_FAILED;
332 DCHECK(platform_video_capture_.get());
335 // It's safe to call this regardless it's capturing or not, because
336 // PepperPlatformVideoCapture maintains the state.
337 platform_video_capture_->StopCapture(this);
341 int32_t PepperVideoCaptureHost::Close() {
342 if (!platform_video_capture_.get())
346 DCHECK(buffers_.empty());
347 DetachPlatformVideoCapture();
351 void PepperVideoCaptureHost::ReleaseBuffers() {
352 ppapi::ResourceTracker* tracker = HostGlobals::Get()->GetResourceTracker();
353 for (size_t i = 0; i < buffers_.size(); ++i) {
354 buffers_[i].buffer->Unmap();
355 tracker->ReleaseResource(buffers_[i].buffer->pp_resource());
360 void PepperVideoCaptureHost::SendStatus() {
361 host()->SendUnsolicitedReply(pp_resource(),
362 PpapiPluginMsg_VideoCapture_OnStatus(status_));
365 void PepperVideoCaptureHost::SetRequestedInfo(
366 const PP_VideoCaptureDeviceInfo_Dev& device_info,
367 uint32_t buffer_count) {
368 // Clamp the buffer count to between 1 and |kMaxBuffers|.
369 buffer_count_hint_ = std::min(std::max(buffer_count, 1U), kMaxBuffers);
371 video_capture_params_.requested_format =
372 media::VideoCaptureFormat(device_info.width,
374 device_info.frames_per_second,
375 media::ConstantResolutionVideoCaptureDevice);
378 void PepperVideoCaptureHost::DetachPlatformVideoCapture() {
379 if (platform_video_capture_.get()) {
380 platform_video_capture_->DetachEventHandler();
381 platform_video_capture_ = NULL;
385 bool PepperVideoCaptureHost::SetStatus(PP_VideoCaptureStatus_Dev status,
389 case PP_VIDEO_CAPTURE_STATUS_STOPPED:
390 if (status_ != PP_VIDEO_CAPTURE_STATUS_STOPPING)
393 case PP_VIDEO_CAPTURE_STATUS_STARTING:
394 if (status_ != PP_VIDEO_CAPTURE_STATUS_STOPPED)
397 case PP_VIDEO_CAPTURE_STATUS_STARTED:
399 case PP_VIDEO_CAPTURE_STATUS_STARTING:
400 case PP_VIDEO_CAPTURE_STATUS_PAUSED:
406 case PP_VIDEO_CAPTURE_STATUS_PAUSED:
408 case PP_VIDEO_CAPTURE_STATUS_STARTING:
409 case PP_VIDEO_CAPTURE_STATUS_STARTED:
415 case PP_VIDEO_CAPTURE_STATUS_STOPPING:
417 case PP_VIDEO_CAPTURE_STATUS_STARTING:
418 case PP_VIDEO_CAPTURE_STATUS_STARTED:
419 case PP_VIDEO_CAPTURE_STATUS_PAUSED:
432 PepperVideoCaptureHost::BufferInfo::BufferInfo()
438 PepperVideoCaptureHost::BufferInfo::~BufferInfo() {
441 } // namespace content