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_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"
22
23 using ppapi::HostResource;
24 using ppapi::TrackedCallback;
25 using ppapi::thunk::EnterResourceNoLock;
26 using ppapi::thunk::PPB_Buffer_API;
27
28 namespace {
29
30 // Maximum number of buffers to actually allocate.
31 const uint32_t kMaxBuffers = 20;
32
33 } // namespace
34
35 namespace content {
36
PepperVideoCaptureHost(RendererPpapiHostImpl * host,PP_Instance instance,PP_Resource resource)37 PepperVideoCaptureHost::PepperVideoCaptureHost(RendererPpapiHostImpl* host,
38 PP_Instance instance,
39 PP_Resource resource)
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)) {
49 }
50
~PepperVideoCaptureHost()51 PepperVideoCaptureHost::~PepperVideoCaptureHost() {
52 Close();
53 }
54
Init()55 bool PepperVideoCaptureHost::Init() {
56 return !!renderer_ppapi_host_->GetPluginInstance(pp_instance());
57 }
58
OnResourceMessageReceived(const IPC::Message & msg,ppapi::host::HostMessageContext * context)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))
64 return result;
65
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,
69 OnStartCapture)
70 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoCapture_ReuseBuffer,
71 OnReuseBuffer)
72 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_VideoCapture_StopCapture,
73 OnStopCapture)
74 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_VideoCapture_Close,
75 OnClose)
76 PPAPI_END_MESSAGE_MAP()
77 return PP_ERROR_FAILED;
78 }
79
OnInitialized(bool succeeded)80 void PepperVideoCaptureHost::OnInitialized(bool succeeded) {
81 if (succeeded) {
82 open_reply_context_.params.set_result(PP_OK);
83 } else {
84 DetachPlatformVideoCapture();
85 open_reply_context_.params.set_result(PP_ERROR_FAILED);
86 }
87
88 host()->SendReply(open_reply_context_,
89 PpapiPluginMsg_VideoCapture_OpenReply());
90 }
91
OnStarted()92 void PepperVideoCaptureHost::OnStarted() {
93 if (SetStatus(PP_VIDEO_CAPTURE_STATUS_STARTED, false))
94 SendStatus();
95 }
96
OnStopped()97 void PepperVideoCaptureHost::OnStopped() {
98 if (SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPED, false))
99 SendStatus();
100 }
101
OnPaused()102 void PepperVideoCaptureHost::OnPaused() {
103 if (SetStatus(PP_VIDEO_CAPTURE_STATUS_PAUSED, false))
104 SendStatus();
105 }
106
OnError()107 void PepperVideoCaptureHost::OnError() {
108 PostErrorReply();
109 }
110
PostErrorReply()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
114 // the capture.
115 SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPED, true);
116 host()->SendUnsolicitedReply(
117 pp_resource(),
118 PpapiPluginMsg_VideoCapture_OnError(
119 static_cast<uint32_t>(PP_ERROR_FAILED)));
120 }
121
OnFrameReady(const scoped_refptr<media::VideoFrame> & frame,media::VideoCaptureFormat format)122 void PepperVideoCaptureHost::OnFrameReady(
123 const scoped_refptr<media::VideoFrame>& frame,
124 media::VideoCaptureFormat format) {
125 DCHECK(frame.get());
126
127 if (alloc_size_ != frame->coded_size() || buffers_.empty()) {
128 AllocBuffers(frame->coded_size(), format.frame_rate);
129 alloc_size_ = frame->coded_size();
130 }
131
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.
139 return;
140 }
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());
146 ++j) {
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);
152 dst += row_bytes;
153 src += src_stride;
154 }
155 }
156 buffers_[i].in_use = true;
157 host()->SendUnsolicitedReply(
158 pp_resource(), PpapiPluginMsg_VideoCapture_OnBufferReady(i));
159 return;
160 }
161 }
162 }
163
AllocBuffers(const gfx::Size & resolution,int frame_rate)164 void PepperVideoCaptureHost::AllocBuffers(const gfx::Size& resolution,
165 int frame_rate) {
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)};
170 ReleaseBuffers();
171
172 const size_t size = media::VideoFrame::AllocationSize(
173 media::VideoFrame::I420, gfx::Size(info.width, info.height));
174
175 ppapi::proxy::ResourceMessageReplyParams params(pp_resource(), 0);
176
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);
187 if (!res)
188 break;
189
190 EnterResourceNoLock<PPB_Buffer_API> enter(res, true);
191 DCHECK(enter.succeeded());
192
193 BufferInfo buf;
194 buf.buffer = static_cast<PPB_Buffer_Impl*>(enter.object());
195 buf.data = buf.buffer->Map();
196 if (!buf.data) {
197 tracker->ReleaseResource(res);
198 break;
199 }
200 buffers_.push_back(buf);
201
202 // Add to HostResource array to be sent.
203 {
204 HostResource host_resource;
205 host_resource.SetHostResource(pp_instance(), res);
206 buffer_host_resources.push_back(host_resource);
207
208 // Add a reference for the plugin, which is resposible for releasing it.
209 tracker->AddRefResource(res);
210 }
211
212 // Add the serialized shared memory handle to params. FileDescriptor is
213 // treated in special case.
214 {
215 EnterResourceNoLock<PPB_Buffer_API> enter(res, true);
216 DCHECK(enter.succeeded());
217 int handle;
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 =
223 #if defined(OS_WIN)
224 reinterpret_cast<HANDLE>(static_cast<intptr_t>(handle));
225 #elif defined(OS_POSIX)
226 handle;
227 #else
228 #error Not implemented.
229 #endif
230 params.AppendHandle(ppapi::proxy::SerializedHandle(
231 dispatcher->ShareHandleWithRemote(platform_file, false), size));
232 }
233 }
234
235 if (buffers_.empty()) {
236 // We couldn't allocate/map buffers at all. Send an error and stop the
237 // capture.
238 SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPING, true);
239 platform_video_capture_->StopCapture();
240 PostErrorReply();
241 return;
242 }
243
244 host()->Send(
245 new PpapiPluginMsg_ResourceReply(params,
246 PpapiPluginMsg_VideoCapture_OnDeviceInfo(
247 info, buffer_host_resources, size)));
248 }
249
OnOpen(ppapi::host::HostMessageContext * context,const std::string & device_id,const PP_VideoCaptureDeviceInfo_Dev & requested_info,uint32_t buffer_count)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;
257
258 SetRequestedInfo(requested_info, buffer_count);
259
260 GURL document_url = renderer_ppapi_host_->GetDocumentURL(pp_instance());
261 if (!document_url.is_valid())
262 return PP_ERROR_FAILED;
263
264 platform_video_capture_.reset(new PepperPlatformVideoCapture(
265 renderer_ppapi_host_->GetRenderFrameForInstance(pp_instance())->
266 GetRoutingID(),
267 device_id,
268 document_url,
269 this));
270
271 open_reply_context_ = context->MakeReplyMessageContext();
272
273 return PP_OK_COMPLETIONPENDING;
274 }
275
OnStartCapture(ppapi::host::HostMessageContext * context)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;
281
282 DCHECK(buffers_.empty());
283
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_);
287 return PP_OK;
288 }
289
OnReuseBuffer(ppapi::host::HostMessageContext * context,uint32_t buffer)290 int32_t PepperVideoCaptureHost::OnReuseBuffer(
291 ppapi::host::HostMessageContext* context,
292 uint32_t buffer) {
293 if (buffer >= buffers_.size() || !buffers_[buffer].in_use)
294 return PP_ERROR_BADARGUMENT;
295 buffers_[buffer].in_use = false;
296 return PP_OK;
297 }
298
OnStopCapture(ppapi::host::HostMessageContext * context)299 int32_t PepperVideoCaptureHost::OnStopCapture(
300 ppapi::host::HostMessageContext* context) {
301 return StopCapture();
302 }
303
OnClose(ppapi::host::HostMessageContext * context)304 int32_t PepperVideoCaptureHost::OnClose(
305 ppapi::host::HostMessageContext* context) {
306 return Close();
307 }
308
StopCapture()309 int32_t PepperVideoCaptureHost::StopCapture() {
310 if (!SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPING, false))
311 return PP_ERROR_FAILED;
312
313 DCHECK(platform_video_capture_.get());
314
315 ReleaseBuffers();
316 // It's safe to call this regardless it's capturing or not, because
317 // PepperPlatformVideoCapture maintains the state.
318 platform_video_capture_->StopCapture();
319 return PP_OK;
320 }
321
Close()322 int32_t PepperVideoCaptureHost::Close() {
323 if (!platform_video_capture_.get())
324 return PP_OK;
325
326 StopCapture();
327 DCHECK(buffers_.empty());
328 DetachPlatformVideoCapture();
329 return PP_OK;
330 }
331
ReleaseBuffers()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());
337 }
338 buffers_.clear();
339 }
340
SendStatus()341 void PepperVideoCaptureHost::SendStatus() {
342 host()->SendUnsolicitedReply(pp_resource(),
343 PpapiPluginMsg_VideoCapture_OnStatus(status_));
344 }
345
SetRequestedInfo(const PP_VideoCaptureDeviceInfo_Dev & device_info,uint32_t buffer_count)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));
355
356 video_capture_params_.requested_format = media::VideoCaptureFormat(
357 gfx::Size(device_info.width, device_info.height),
358 frames_per_second,
359 media::PIXEL_FORMAT_I420);
360 }
361
DetachPlatformVideoCapture()362 void PepperVideoCaptureHost::DetachPlatformVideoCapture() {
363 if (platform_video_capture_) {
364 platform_video_capture_->DetachEventHandler();
365 platform_video_capture_.reset();
366 }
367 }
368
SetStatus(PP_VideoCaptureStatus_Dev status,bool forced)369 bool PepperVideoCaptureHost::SetStatus(PP_VideoCaptureStatus_Dev status,
370 bool forced) {
371 if (!forced) {
372 switch (status) {
373 case PP_VIDEO_CAPTURE_STATUS_STOPPED:
374 if (status_ != PP_VIDEO_CAPTURE_STATUS_STOPPING)
375 return false;
376 break;
377 case PP_VIDEO_CAPTURE_STATUS_STARTING:
378 if (status_ != PP_VIDEO_CAPTURE_STATUS_STOPPED)
379 return false;
380 break;
381 case PP_VIDEO_CAPTURE_STATUS_STARTED:
382 switch (status_) {
383 case PP_VIDEO_CAPTURE_STATUS_STARTING:
384 case PP_VIDEO_CAPTURE_STATUS_PAUSED:
385 break;
386 default:
387 return false;
388 }
389 break;
390 case PP_VIDEO_CAPTURE_STATUS_PAUSED:
391 switch (status_) {
392 case PP_VIDEO_CAPTURE_STATUS_STARTING:
393 case PP_VIDEO_CAPTURE_STATUS_STARTED:
394 break;
395 default:
396 return false;
397 }
398 break;
399 case PP_VIDEO_CAPTURE_STATUS_STOPPING:
400 switch (status_) {
401 case PP_VIDEO_CAPTURE_STATUS_STARTING:
402 case PP_VIDEO_CAPTURE_STATUS_STARTED:
403 case PP_VIDEO_CAPTURE_STATUS_PAUSED:
404 break;
405 default:
406 return false;
407 }
408 break;
409 }
410 }
411
412 status_ = status;
413 return true;
414 }
415
BufferInfo()416 PepperVideoCaptureHost::BufferInfo::BufferInfo()
417 : in_use(false), data(NULL), buffer() {
418 }
419
~BufferInfo()420 PepperVideoCaptureHost::BufferInfo::~BufferInfo() {
421 }
422
423 } // namespace content
424