• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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