Upstream version 10.39.225.0
[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_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
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
51 PepperVideoCaptureHost::~PepperVideoCaptureHost() {
52   Close();
53 }
54
55 bool PepperVideoCaptureHost::Init() {
56   return !!renderer_ppapi_host_->GetPluginInstance(pp_instance());
57 }
58
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
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
92 void PepperVideoCaptureHost::OnStarted() {
93   if (SetStatus(PP_VIDEO_CAPTURE_STATUS_STARTED, false))
94     SendStatus();
95 }
96
97 void PepperVideoCaptureHost::OnStopped() {
98   if (SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPED, false))
99     SendStatus();
100 }
101
102 void PepperVideoCaptureHost::OnPaused() {
103   if (SetStatus(PP_VIDEO_CAPTURE_STATUS_PAUSED, false))
104     SendStatus();
105 }
106
107 void PepperVideoCaptureHost::OnError() {
108   PostErrorReply();
109 }
110
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
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
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
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
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
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
299 int32_t PepperVideoCaptureHost::OnStopCapture(
300     ppapi::host::HostMessageContext* context) {
301   return StopCapture();
302 }
303
304 int32_t PepperVideoCaptureHost::OnClose(
305     ppapi::host::HostMessageContext* context) {
306   return Close();
307 }
308
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
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
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
341 void PepperVideoCaptureHost::SendStatus() {
342   host()->SendUnsolicitedReply(pp_resource(),
343                                PpapiPluginMsg_VideoCapture_OnStatus(status_));
344 }
345
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
362 void PepperVideoCaptureHost::DetachPlatformVideoCapture() {
363   if (platform_video_capture_) {
364     platform_video_capture_->DetachEventHandler();
365     platform_video_capture_.reset();
366   }
367 }
368
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
416 PepperVideoCaptureHost::BufferInfo::BufferInfo()
417     : in_use(false), data(NULL), buffer() {
418 }
419
420 PepperVideoCaptureHost::BufferInfo::~BufferInfo() {
421 }
422
423 }  // namespace content