Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / content / renderer / pepper / pepper_video_capture_host.cc
blob820dd12b67ca0b722b1bae03f032d0ce8fad5982
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;
29 namespace {
31 // Maximum number of buffers to actually allocate.
32 const uint32_t kMaxBuffers = 20;
34 } // namespace
36 namespace content {
38 PepperVideoCaptureHost::PepperVideoCaptureHost(RendererPpapiHostImpl* host,
39 PP_Instance instance,
40 PP_Resource resource)
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() {
53 Close();
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))
65 return 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,
70 OnStartCapture)
71 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoCapture_ReuseBuffer,
72 OnReuseBuffer)
73 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_VideoCapture_StopCapture,
74 OnStopCapture)
75 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_VideoCapture_Close,
76 OnClose)
77 PPAPI_END_MESSAGE_MAP()
78 return PP_ERROR_FAILED;
81 void PepperVideoCaptureHost::OnInitialized(bool succeeded) {
82 if (succeeded) {
83 open_reply_context_.params.set_result(PP_OK);
84 } else {
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))
95 SendStatus();
98 void PepperVideoCaptureHost::OnStopped() {
99 if (SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPED, false))
100 SendStatus();
103 void PepperVideoCaptureHost::OnPaused() {
104 if (SetStatus(PP_VIDEO_CAPTURE_STATUS_PAUSED, false))
105 SendStatus();
108 void PepperVideoCaptureHost::OnError() {
109 PostErrorReply();
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
115 // the capture.
116 SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPED, true);
117 host()->SendUnsolicitedReply(
118 pp_resource(),
119 PpapiPluginMsg_VideoCapture_OnError(
120 static_cast<uint32_t>(PP_ERROR_FAILED)));
123 void PepperVideoCaptureHost::OnFrameReady(
124 const scoped_refptr<media::VideoFrame>& frame) {
125 DCHECK(frame.get());
127 if (alloc_size_ != frame->visible_rect().size() || buffers_.empty()) {
128 alloc_size_ = frame->visible_rect().size();
129 double frame_rate;
130 int rounded_frame_rate;
131 if (frame->metadata()->GetDouble(media::VideoFrameMetadata::FRAME_RATE,
132 &frame_rate))
133 rounded_frame_rate = static_cast<int>(frame_rate + 0.5 /* round */);
134 else
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.
145 return;
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());
152 ++j) {
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);
158 dst += row_bytes;
159 src += src_stride;
162 buffers_[i].in_use = true;
163 host()->SendUnsolicitedReply(
164 pp_resource(), PpapiPluginMsg_VideoCapture_OnBufferReady(i));
165 return;
170 void PepperVideoCaptureHost::AllocBuffers(const gfx::Size& resolution,
171 int frame_rate) {
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)};
176 ReleaseBuffers();
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);
193 if (!res)
194 break;
196 EnterResourceNoLock<PPB_Buffer_API> enter(res, true);
197 DCHECK(enter.succeeded());
199 BufferInfo buf;
200 buf.buffer = static_cast<PPB_Buffer_Impl*>(enter.object());
201 buf.data = buf.buffer->Map();
202 if (!buf.data) {
203 tracker->ReleaseResource(res);
204 break;
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());
223 int handle;
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 =
229 #if defined(OS_WIN)
230 reinterpret_cast<HANDLE>(static_cast<intptr_t>(handle));
231 #elif defined(OS_POSIX)
232 handle;
233 #else
234 #error Not implemented.
235 #endif
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
243 // capture.
244 SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPING, true);
245 platform_video_capture_->StopCapture();
246 PostErrorReply();
247 return;
250 host()->Send(
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())->
272 GetRoutingID(),
273 device_id,
274 document_url,
275 this));
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_);
293 return PP_OK;
296 int32_t PepperVideoCaptureHost::OnReuseBuffer(
297 ppapi::host::HostMessageContext* context,
298 uint32_t buffer) {
299 if (buffer >= buffers_.size() || !buffers_[buffer].in_use)
300 return PP_ERROR_BADARGUMENT;
301 buffers_[buffer].in_use = false;
302 return PP_OK;
305 int32_t PepperVideoCaptureHost::OnStopCapture(
306 ppapi::host::HostMessageContext* context) {
307 return StopCapture();
310 int32_t PepperVideoCaptureHost::OnClose(
311 ppapi::host::HostMessageContext* context) {
312 return Close();
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());
321 ReleaseBuffers();
322 // It's safe to call this regardless it's capturing or not, because
323 // PepperPlatformVideoCapture maintains the state.
324 platform_video_capture_->StopCapture();
325 return PP_OK;
328 int32_t PepperVideoCaptureHost::Close() {
329 if (!platform_video_capture_.get())
330 return PP_OK;
332 StopCapture();
333 DCHECK(buffers_.empty());
334 DetachPlatformVideoCapture();
335 return PP_OK;
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());
344 buffers_.clear();
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),
364 frames_per_second,
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,
376 bool forced) {
377 if (!forced) {
378 switch (status) {
379 case PP_VIDEO_CAPTURE_STATUS_STOPPED:
380 if (status_ != PP_VIDEO_CAPTURE_STATUS_STOPPING)
381 return false;
382 break;
383 case PP_VIDEO_CAPTURE_STATUS_STARTING:
384 if (status_ != PP_VIDEO_CAPTURE_STATUS_STOPPED)
385 return false;
386 break;
387 case PP_VIDEO_CAPTURE_STATUS_STARTED:
388 switch (status_) {
389 case PP_VIDEO_CAPTURE_STATUS_STARTING:
390 case PP_VIDEO_CAPTURE_STATUS_PAUSED:
391 break;
392 default:
393 return false;
395 break;
396 case PP_VIDEO_CAPTURE_STATUS_PAUSED:
397 switch (status_) {
398 case PP_VIDEO_CAPTURE_STATUS_STARTING:
399 case PP_VIDEO_CAPTURE_STATUS_STARTED:
400 break;
401 default:
402 return false;
404 break;
405 case PP_VIDEO_CAPTURE_STATUS_STOPPING:
406 switch (status_) {
407 case PP_VIDEO_CAPTURE_STATUS_STARTING:
408 case PP_VIDEO_CAPTURE_STATUS_STARTED:
409 case PP_VIDEO_CAPTURE_STATUS_PAUSED:
410 break;
411 default:
412 return false;
414 break;
418 status_ = status;
419 return true;
422 PepperVideoCaptureHost::BufferInfo::BufferInfo()
423 : in_use(false), data(NULL), buffer() {
426 PepperVideoCaptureHost::BufferInfo::~BufferInfo() {
429 } // namespace content