- add sources.
[platform/framework/web/crosswalk.git] / src / content / renderer / pepper / pepper_video_capture_host.cc
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 #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"
23
24 using ppapi::HostResource;
25 using ppapi::TrackedCallback;
26 using ppapi::thunk::EnterResourceNoLock;
27 using ppapi::thunk::PPB_Buffer_API;
28
29 namespace {
30
31 // Maximum number of buffers to actually allocate.
32 const uint32_t kMaxBuffers = 20;
33
34 }  // namespace
35
36 namespace content {
37
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_(
46           this,
47           PepperMediaDeviceManager::GetForRenderView(
48               host->GetRenderViewForInstance(pp_instance())),
49           PP_DEVICETYPE_DEV_VIDEOCAPTURE) {
50 }
51
52 PepperVideoCaptureHost::~PepperVideoCaptureHost() {
53   Close();
54 }
55
56 bool PepperVideoCaptureHost::Init() {
57   return !!renderer_ppapi_host_->GetPluginInstance(pp_instance());
58 }
59
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;
66
67   IPC_BEGIN_MESSAGE_MAP(PepperVideoCaptureHost, msg)
68     PPAPI_DISPATCH_HOST_RESOURCE_CALL(
69         PpapiHostMsg_VideoCapture_Open,
70         OnOpen)
71     PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
72         PpapiHostMsg_VideoCapture_StartCapture,
73         OnStartCapture)
74     PPAPI_DISPATCH_HOST_RESOURCE_CALL(
75         PpapiHostMsg_VideoCapture_ReuseBuffer,
76         OnReuseBuffer)
77     PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
78         PpapiHostMsg_VideoCapture_StopCapture,
79         OnStopCapture)
80     PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
81         PpapiHostMsg_VideoCapture_Close,
82         OnClose)
83   IPC_END_MESSAGE_MAP()
84   return PP_ERROR_FAILED;
85 }
86
87 void PepperVideoCaptureHost::OnInitialized(media::VideoCapture* capture,
88                                            bool succeeded) {
89   DCHECK(capture == platform_video_capture_.get());
90
91   if (succeeded) {
92     open_reply_context_.params.set_result(PP_OK);
93   } else {
94     DetachPlatformVideoCapture();
95     open_reply_context_.params.set_result(PP_ERROR_FAILED);
96   }
97
98   host()->SendReply(open_reply_context_,
99                     PpapiPluginMsg_VideoCapture_OpenReply());
100 }
101
102 void PepperVideoCaptureHost::OnStarted(media::VideoCapture* capture) {
103   if (SetStatus(PP_VIDEO_CAPTURE_STATUS_STARTED, false))
104     SendStatus();
105 }
106
107 void PepperVideoCaptureHost::OnStopped(media::VideoCapture* capture) {
108   if (SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPED, false))
109     SendStatus();
110 }
111
112 void PepperVideoCaptureHost::OnPaused(media::VideoCapture* capture) {
113   if (SetStatus(PP_VIDEO_CAPTURE_STATUS_PAUSED, false))
114     SendStatus();
115 }
116
117 void PepperVideoCaptureHost::OnError(media::VideoCapture* capture,
118                                      int error_code) {
119   // Today, the media layer only sends "1" as an error.
120   DCHECK(error_code == 1);
121   PostErrorReply();
122 }
123
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
127   // the capture.
128   SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPED, true);
129   host()->SendUnsolicitedReply(pp_resource(),
130       PpapiPluginMsg_VideoCapture_OnError(PP_ERROR_FAILED));
131 }
132
133 void PepperVideoCaptureHost::OnRemoved(media::VideoCapture* capture) {
134 }
135
136 void PepperVideoCaptureHost::OnFrameReady(
137     media::VideoCapture* capture,
138     const scoped_refptr<media::VideoFrame>& frame) {
139   DCHECK(frame.get());
140
141   if (alloc_size_ != frame->coded_size()) {
142     AllocBuffers(frame->coded_size(), capture->CaptureFrameRate());
143     alloc_size_ = frame->coded_size();
144   }
145
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.
153         return;
154       }
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());
160            ++j) {
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);
166           dst += row_bytes;
167           src += src_stride;
168         }
169       }
170       buffers_[i].in_use = true;
171       host()->SendUnsolicitedReply(pp_resource(),
172           PpapiPluginMsg_VideoCapture_OnBufferReady(i));
173       return;
174     }
175   }
176 }
177
178 void PepperVideoCaptureHost::AllocBuffers(
179     const gfx::Size& resolution,
180     int frame_rate) {
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)
185   };
186   ReleaseBuffers();
187
188   const size_t size = media::VideoFrame::AllocationSize(
189       media::VideoFrame::I420, gfx::Size(info.width, info.height));
190
191   ppapi::proxy::ResourceMessageReplyParams params(pp_resource(), 0);
192
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);
204     if (!res)
205       break;
206
207     EnterResourceNoLock<PPB_Buffer_API> enter(res, true);
208     DCHECK(enter.succeeded());
209
210     BufferInfo buf;
211     buf.buffer = static_cast<PPB_Buffer_Impl*>(enter.object());
212     buf.data = buf.buffer->Map();
213     if (!buf.data) {
214       tracker->ReleaseResource(res);
215       break;
216     }
217     buffers_.push_back(buf);
218
219     // Add to HostResource array to be sent.
220     {
221       HostResource host_resource;
222       host_resource.SetHostResource(pp_instance(), res);
223       buffer_host_resources.push_back(host_resource);
224
225       // Add a reference for the plugin, which is resposible for releasing it.
226       tracker->AddRefResource(res);
227     }
228
229     // Add the serialized shared memory handle to params. FileDescriptor is
230     // treated in special case.
231     {
232       EnterResourceNoLock<PPB_Buffer_API> enter(res, true);
233       DCHECK(enter.succeeded());
234       int handle;
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 =
240 #if defined(OS_WIN)
241           reinterpret_cast<HANDLE>(static_cast<intptr_t>(handle));
242 #elif defined(OS_POSIX)
243           handle;
244 #else
245 #error Not implemented.
246 #endif
247       params.AppendHandle(
248           ppapi::proxy::SerializedHandle(
249               dispatcher->ShareHandleWithRemote(platform_file, false),
250               size));
251     }
252   }
253
254   if (buffers_.empty()) {
255     // We couldn't allocate/map buffers at all. Send an error and stop the
256     // capture.
257     SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPING, true);
258     platform_video_capture_->StopCapture(this);
259     PostErrorReply();
260     return;
261   }
262
263   host()->Send(new PpapiPluginMsg_ResourceReply(
264       params, PpapiPluginMsg_VideoCapture_OnDeviceInfo(
265           info, buffer_host_resources, size)));
266 }
267
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;
275
276   SetRequestedInfo(requested_info, buffer_count);
277
278   PepperPluginInstance* instance =
279       renderer_ppapi_host_->GetPluginInstance(pp_instance());
280   if (!instance)
281     return PP_ERROR_FAILED;
282
283   RenderViewImpl* render_view = static_cast<RenderViewImpl*>(
284       renderer_ppapi_host_->GetRenderViewForInstance(pp_instance()));
285
286   platform_video_capture_ = new PepperPlatformVideoCapture(
287       render_view->AsWeakPtr(), device_id,
288       instance->GetContainer()->element().document().url(), this);
289
290   open_reply_context_ = context->MakeReplyMessageContext();
291
292   return PP_OK_COMPLETIONPENDING;
293 }
294
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;
300
301   DCHECK(buffers_.empty());
302
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_);
306   return PP_OK;
307 }
308
309 int32_t PepperVideoCaptureHost::OnReuseBuffer(
310     ppapi::host::HostMessageContext* context,
311     uint32_t buffer) {
312   if (buffer >= buffers_.size() || !buffers_[buffer].in_use)
313     return PP_ERROR_BADARGUMENT;
314   buffers_[buffer].in_use = false;
315   return PP_OK;
316 }
317
318 int32_t PepperVideoCaptureHost::OnStopCapture(
319     ppapi::host::HostMessageContext* context) {
320   return StopCapture();
321 }
322
323 int32_t PepperVideoCaptureHost::OnClose(
324     ppapi::host::HostMessageContext* context) {
325   return Close();
326 }
327
328 int32_t PepperVideoCaptureHost::StopCapture() {
329   if (!SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPING, false))
330     return PP_ERROR_FAILED;
331
332   DCHECK(platform_video_capture_.get());
333
334   ReleaseBuffers();
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);
338   return PP_OK;
339 }
340
341 int32_t PepperVideoCaptureHost::Close() {
342   if (!platform_video_capture_.get())
343     return PP_OK;
344
345   StopCapture();
346   DCHECK(buffers_.empty());
347   DetachPlatformVideoCapture();
348   return PP_OK;
349 }
350
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());
356   }
357   buffers_.clear();
358 }
359
360 void PepperVideoCaptureHost::SendStatus() {
361   host()->SendUnsolicitedReply(pp_resource(),
362       PpapiPluginMsg_VideoCapture_OnStatus(status_));
363 }
364
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);
370
371   video_capture_params_.requested_format =
372       media::VideoCaptureFormat(device_info.width,
373                                 device_info.height,
374                                 device_info.frames_per_second,
375                                 media::ConstantResolutionVideoCaptureDevice);
376 }
377
378 void PepperVideoCaptureHost::DetachPlatformVideoCapture() {
379   if (platform_video_capture_.get()) {
380     platform_video_capture_->DetachEventHandler();
381     platform_video_capture_ = NULL;
382   }
383 }
384
385 bool PepperVideoCaptureHost::SetStatus(PP_VideoCaptureStatus_Dev status,
386                                        bool forced) {
387   if (!forced) {
388     switch (status) {
389       case PP_VIDEO_CAPTURE_STATUS_STOPPED:
390         if (status_ != PP_VIDEO_CAPTURE_STATUS_STOPPING)
391           return false;
392         break;
393       case PP_VIDEO_CAPTURE_STATUS_STARTING:
394         if (status_ != PP_VIDEO_CAPTURE_STATUS_STOPPED)
395           return false;
396         break;
397       case PP_VIDEO_CAPTURE_STATUS_STARTED:
398         switch (status_) {
399           case PP_VIDEO_CAPTURE_STATUS_STARTING:
400           case PP_VIDEO_CAPTURE_STATUS_PAUSED:
401             break;
402           default:
403             return false;
404         }
405         break;
406       case PP_VIDEO_CAPTURE_STATUS_PAUSED:
407         switch (status_) {
408           case PP_VIDEO_CAPTURE_STATUS_STARTING:
409           case PP_VIDEO_CAPTURE_STATUS_STARTED:
410             break;
411           default:
412             return false;
413         }
414         break;
415       case PP_VIDEO_CAPTURE_STATUS_STOPPING:
416         switch (status_) {
417           case PP_VIDEO_CAPTURE_STATUS_STARTING:
418           case PP_VIDEO_CAPTURE_STATUS_STARTED:
419           case PP_VIDEO_CAPTURE_STATUS_PAUSED:
420             break;
421           default:
422             return false;
423         }
424         break;
425     }
426   }
427
428   status_ = status;
429   return true;
430 }
431
432 PepperVideoCaptureHost::BufferInfo::BufferInfo()
433     : in_use(false),
434       data(NULL),
435       buffer() {
436 }
437
438 PepperVideoCaptureHost::BufferInfo::~BufferInfo() {
439 }
440
441 }  // namespace content