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 "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
21 using ppapi::HostResource;
22 using ppapi::TrackedCallback;
23 using ppapi::thunk::EnterResourceNoLock;
24 using ppapi::thunk::PPB_Buffer_API;
25
26 namespace {
27
28 // Maximum number of buffers to actually allocate.
29 const uint32_t kMaxBuffers = 20;
30
31 } // namespace
32
33 namespace content {
34
PepperVideoCaptureHost(RendererPpapiHostImpl * host,PP_Instance instance,PP_Resource resource)35 PepperVideoCaptureHost::PepperVideoCaptureHost(RendererPpapiHostImpl* host,
36 PP_Instance instance,
37 PP_Resource resource)
38 : ResourceHost(host->GetPpapiHost(), instance, resource),
39 renderer_ppapi_host_(host),
40 buffer_count_hint_(0),
41 status_(PP_VIDEO_CAPTURE_STATUS_STOPPED),
42 enumeration_helper_(
43 this,
44 PepperMediaDeviceManager::GetForRenderView(
45 host->GetRenderViewForInstance(pp_instance())),
46 PP_DEVICETYPE_DEV_VIDEOCAPTURE,
47 host->GetDocumentURL(instance)) {
48 }
49
~PepperVideoCaptureHost()50 PepperVideoCaptureHost::~PepperVideoCaptureHost() {
51 Close();
52 }
53
Init()54 bool PepperVideoCaptureHost::Init() {
55 return !!renderer_ppapi_host_->GetPluginInstance(pp_instance());
56 }
57
OnResourceMessageReceived(const IPC::Message & msg,ppapi::host::HostMessageContext * context)58 int32_t PepperVideoCaptureHost::OnResourceMessageReceived(
59 const IPC::Message& msg,
60 ppapi::host::HostMessageContext* context) {
61 int32_t result = PP_ERROR_FAILED;
62 if (enumeration_helper_.HandleResourceMessage(msg, context, &result))
63 return result;
64
65 IPC_BEGIN_MESSAGE_MAP(PepperVideoCaptureHost, msg)
66 PPAPI_DISPATCH_HOST_RESOURCE_CALL(
67 PpapiHostMsg_VideoCapture_Open,
68 OnOpen)
69 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
70 PpapiHostMsg_VideoCapture_StartCapture,
71 OnStartCapture)
72 PPAPI_DISPATCH_HOST_RESOURCE_CALL(
73 PpapiHostMsg_VideoCapture_ReuseBuffer,
74 OnReuseBuffer)
75 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
76 PpapiHostMsg_VideoCapture_StopCapture,
77 OnStopCapture)
78 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
79 PpapiHostMsg_VideoCapture_Close,
80 OnClose)
81 IPC_END_MESSAGE_MAP()
82 return PP_ERROR_FAILED;
83 }
84
OnInitialized(media::VideoCapture * capture,bool succeeded)85 void PepperVideoCaptureHost::OnInitialized(media::VideoCapture* capture,
86 bool succeeded) {
87 DCHECK(capture == platform_video_capture_.get());
88
89 if (succeeded) {
90 open_reply_context_.params.set_result(PP_OK);
91 } else {
92 DetachPlatformVideoCapture();
93 open_reply_context_.params.set_result(PP_ERROR_FAILED);
94 }
95
96 host()->SendReply(open_reply_context_,
97 PpapiPluginMsg_VideoCapture_OpenReply());
98 }
99
OnStarted(media::VideoCapture * capture)100 void PepperVideoCaptureHost::OnStarted(media::VideoCapture* capture) {
101 if (SetStatus(PP_VIDEO_CAPTURE_STATUS_STARTED, false))
102 SendStatus();
103 }
104
OnStopped(media::VideoCapture * capture)105 void PepperVideoCaptureHost::OnStopped(media::VideoCapture* capture) {
106 if (SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPED, false))
107 SendStatus();
108 }
109
OnPaused(media::VideoCapture * capture)110 void PepperVideoCaptureHost::OnPaused(media::VideoCapture* capture) {
111 if (SetStatus(PP_VIDEO_CAPTURE_STATUS_PAUSED, false))
112 SendStatus();
113 }
114
OnError(media::VideoCapture * capture,int error_code)115 void PepperVideoCaptureHost::OnError(media::VideoCapture* capture,
116 int error_code) {
117 // Today, the media layer only sends "1" as an error.
118 DCHECK(error_code == 1);
119 PostErrorReply();
120 }
121
PostErrorReply()122 void PepperVideoCaptureHost::PostErrorReply() {
123 // It either comes because some error was detected while starting (e.g. 2
124 // conflicting "master" resolution), or because the browser failed to start
125 // the capture.
126 SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPED, true);
127 host()->SendUnsolicitedReply(pp_resource(),
128 PpapiPluginMsg_VideoCapture_OnError(PP_ERROR_FAILED));
129 }
130
OnRemoved(media::VideoCapture * capture)131 void PepperVideoCaptureHost::OnRemoved(media::VideoCapture* capture) {
132 }
133
OnFrameReady(media::VideoCapture * capture,const scoped_refptr<media::VideoFrame> & frame)134 void PepperVideoCaptureHost::OnFrameReady(
135 media::VideoCapture* capture,
136 const scoped_refptr<media::VideoFrame>& frame) {
137 DCHECK(frame.get());
138
139 if (alloc_size_ != frame->coded_size()) {
140 AllocBuffers(frame->coded_size(), capture->CaptureFrameRate());
141 alloc_size_ = frame->coded_size();
142 }
143
144 for (uint32_t i = 0; i < buffers_.size(); ++i) {
145 if (!buffers_[i].in_use) {
146 DCHECK_EQ(frame->format(), media::VideoFrame::I420);
147 if (buffers_[i].buffer->size() <
148 media::VideoFrame::AllocationSize(frame->format(),
149 frame->coded_size())) {
150 // TODO(ihf): handle size mismatches gracefully here.
151 return;
152 }
153 uint8* dst = reinterpret_cast<uint8*>(buffers_[i].data);
154 COMPILE_ASSERT(media::VideoFrame::kYPlane == 0, y_plane_should_be_0);
155 COMPILE_ASSERT(media::VideoFrame::kUPlane == 1, u_plane_should_be_1);
156 COMPILE_ASSERT(media::VideoFrame::kVPlane == 2, v_plane_should_be_2);
157 for (size_t j = 0; j < media::VideoFrame::NumPlanes(frame->format());
158 ++j) {
159 const uint8* src = frame->data(j);
160 const size_t row_bytes = frame->row_bytes(j);
161 const size_t src_stride = frame->stride(j);
162 for (int k = 0; k < frame->rows(j); ++k) {
163 memcpy(dst, src, row_bytes);
164 dst += row_bytes;
165 src += src_stride;
166 }
167 }
168 buffers_[i].in_use = true;
169 host()->SendUnsolicitedReply(pp_resource(),
170 PpapiPluginMsg_VideoCapture_OnBufferReady(i));
171 return;
172 }
173 }
174 }
175
AllocBuffers(const gfx::Size & resolution,int frame_rate)176 void PepperVideoCaptureHost::AllocBuffers(
177 const gfx::Size& resolution,
178 int frame_rate) {
179 PP_VideoCaptureDeviceInfo_Dev info = {
180 static_cast<uint32_t>(resolution.width()),
181 static_cast<uint32_t>(resolution.height()),
182 static_cast<uint32_t>(frame_rate)
183 };
184 ReleaseBuffers();
185
186 const size_t size = media::VideoFrame::AllocationSize(
187 media::VideoFrame::I420, gfx::Size(info.width, info.height));
188
189 ppapi::proxy::ResourceMessageReplyParams params(pp_resource(), 0);
190
191 // Allocate buffers. We keep a reference to them, that is released in
192 // ReleaseBuffers. In the mean time, we prepare the resource and handle here
193 // for sending below.
194 std::vector<HostResource> buffer_host_resources;
195 buffers_.reserve(buffer_count_hint_);
196 ppapi::ResourceTracker* tracker =
197 HostGlobals::Get()->GetResourceTracker();
198 ppapi::proxy::HostDispatcher* dispatcher =
199 ppapi::proxy::HostDispatcher::GetForInstance(pp_instance());
200 for (size_t i = 0; i < buffer_count_hint_; ++i) {
201 PP_Resource res = PPB_Buffer_Impl::Create(pp_instance(), size);
202 if (!res)
203 break;
204
205 EnterResourceNoLock<PPB_Buffer_API> enter(res, true);
206 DCHECK(enter.succeeded());
207
208 BufferInfo buf;
209 buf.buffer = static_cast<PPB_Buffer_Impl*>(enter.object());
210 buf.data = buf.buffer->Map();
211 if (!buf.data) {
212 tracker->ReleaseResource(res);
213 break;
214 }
215 buffers_.push_back(buf);
216
217 // Add to HostResource array to be sent.
218 {
219 HostResource host_resource;
220 host_resource.SetHostResource(pp_instance(), res);
221 buffer_host_resources.push_back(host_resource);
222
223 // Add a reference for the plugin, which is resposible for releasing it.
224 tracker->AddRefResource(res);
225 }
226
227 // Add the serialized shared memory handle to params. FileDescriptor is
228 // treated in special case.
229 {
230 EnterResourceNoLock<PPB_Buffer_API> enter(res, true);
231 DCHECK(enter.succeeded());
232 int handle;
233 int32_t result = enter.object()->GetSharedMemory(&handle);
234 DCHECK(result == PP_OK);
235 // TODO(piman/brettw): Change trusted interface to return a PP_FileHandle,
236 // those casts are ugly.
237 base::PlatformFile platform_file =
238 #if defined(OS_WIN)
239 reinterpret_cast<HANDLE>(static_cast<intptr_t>(handle));
240 #elif defined(OS_POSIX)
241 handle;
242 #else
243 #error Not implemented.
244 #endif
245 params.AppendHandle(
246 ppapi::proxy::SerializedHandle(
247 dispatcher->ShareHandleWithRemote(platform_file, false),
248 size));
249 }
250 }
251
252 if (buffers_.empty()) {
253 // We couldn't allocate/map buffers at all. Send an error and stop the
254 // capture.
255 SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPING, true);
256 platform_video_capture_->StopCapture(this);
257 PostErrorReply();
258 return;
259 }
260
261 host()->Send(new PpapiPluginMsg_ResourceReply(
262 params, PpapiPluginMsg_VideoCapture_OnDeviceInfo(
263 info, buffer_host_resources, size)));
264 }
265
OnOpen(ppapi::host::HostMessageContext * context,const std::string & device_id,const PP_VideoCaptureDeviceInfo_Dev & requested_info,uint32_t buffer_count)266 int32_t PepperVideoCaptureHost::OnOpen(
267 ppapi::host::HostMessageContext* context,
268 const std::string& device_id,
269 const PP_VideoCaptureDeviceInfo_Dev& requested_info,
270 uint32_t buffer_count) {
271 if (platform_video_capture_.get())
272 return PP_ERROR_FAILED;
273
274 SetRequestedInfo(requested_info, buffer_count);
275
276 GURL document_url = renderer_ppapi_host_->GetDocumentURL(pp_instance());
277 if (!document_url.is_valid())
278 return PP_ERROR_FAILED;
279
280 RenderViewImpl* render_view = static_cast<RenderViewImpl*>(
281 renderer_ppapi_host_->GetRenderViewForInstance(pp_instance()));
282
283 platform_video_capture_ = new PepperPlatformVideoCapture(
284 render_view->AsWeakPtr(), device_id,
285 document_url, this);
286
287 open_reply_context_ = context->MakeReplyMessageContext();
288
289 return PP_OK_COMPLETIONPENDING;
290 }
291
OnStartCapture(ppapi::host::HostMessageContext * context)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 // PepperPlatformVideoCapture maintains the state.
302 platform_video_capture_->StartCapture(this, video_capture_params_);
303 return PP_OK;
304 }
305
OnReuseBuffer(ppapi::host::HostMessageContext * context,uint32_t buffer)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
OnStopCapture(ppapi::host::HostMessageContext * context)315 int32_t PepperVideoCaptureHost::OnStopCapture(
316 ppapi::host::HostMessageContext* context) {
317 return StopCapture();
318 }
319
OnClose(ppapi::host::HostMessageContext * context)320 int32_t PepperVideoCaptureHost::OnClose(
321 ppapi::host::HostMessageContext* context) {
322 return Close();
323 }
324
StopCapture()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 // PepperPlatformVideoCapture maintains the state.
334 platform_video_capture_->StopCapture(this);
335 return PP_OK;
336 }
337
Close()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
ReleaseBuffers()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
SendStatus()357 void PepperVideoCaptureHost::SendStatus() {
358 host()->SendUnsolicitedReply(pp_resource(),
359 PpapiPluginMsg_VideoCapture_OnStatus(status_));
360 }
361
SetRequestedInfo(const PP_VideoCaptureDeviceInfo_Dev & device_info,uint32_t buffer_count)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 video_capture_params_.requested_format = media::VideoCaptureFormat(
369 gfx::Size(device_info.width, device_info.height),
370 device_info.frames_per_second,
371 media::PIXEL_FORMAT_I420);
372 video_capture_params_.allow_resolution_change = false;
373 }
374
DetachPlatformVideoCapture()375 void PepperVideoCaptureHost::DetachPlatformVideoCapture() {
376 if (platform_video_capture_.get()) {
377 platform_video_capture_->DetachEventHandler();
378 platform_video_capture_ = NULL;
379 }
380 }
381
SetStatus(PP_VideoCaptureStatus_Dev status,bool forced)382 bool PepperVideoCaptureHost::SetStatus(PP_VideoCaptureStatus_Dev status,
383 bool forced) {
384 if (!forced) {
385 switch (status) {
386 case PP_VIDEO_CAPTURE_STATUS_STOPPED:
387 if (status_ != PP_VIDEO_CAPTURE_STATUS_STOPPING)
388 return false;
389 break;
390 case PP_VIDEO_CAPTURE_STATUS_STARTING:
391 if (status_ != PP_VIDEO_CAPTURE_STATUS_STOPPED)
392 return false;
393 break;
394 case PP_VIDEO_CAPTURE_STATUS_STARTED:
395 switch (status_) {
396 case PP_VIDEO_CAPTURE_STATUS_STARTING:
397 case PP_VIDEO_CAPTURE_STATUS_PAUSED:
398 break;
399 default:
400 return false;
401 }
402 break;
403 case PP_VIDEO_CAPTURE_STATUS_PAUSED:
404 switch (status_) {
405 case PP_VIDEO_CAPTURE_STATUS_STARTING:
406 case PP_VIDEO_CAPTURE_STATUS_STARTED:
407 break;
408 default:
409 return false;
410 }
411 break;
412 case PP_VIDEO_CAPTURE_STATUS_STOPPING:
413 switch (status_) {
414 case PP_VIDEO_CAPTURE_STATUS_STARTING:
415 case PP_VIDEO_CAPTURE_STATUS_STARTED:
416 case PP_VIDEO_CAPTURE_STATUS_PAUSED:
417 break;
418 default:
419 return false;
420 }
421 break;
422 }
423 }
424
425 status_ = status;
426 return true;
427 }
428
BufferInfo()429 PepperVideoCaptureHost::BufferInfo::BufferInfo()
430 : in_use(false),
431 data(NULL),
432 buffer() {
433 }
434
~BufferInfo()435 PepperVideoCaptureHost::BufferInfo::~BufferInfo() {
436 }
437
438 } // namespace content
439