Index: content/renderer/pepper/pepper_video_capture_host.cc |
diff --git a/content/renderer/pepper/pepper_video_capture_host.cc b/content/renderer/pepper/pepper_video_capture_host.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..dcefa04fe421c1ee84c321078b2369906cefa7b5 |
--- /dev/null |
+++ b/content/renderer/pepper/pepper_video_capture_host.cc |
@@ -0,0 +1,458 @@ |
+// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "content/renderer/pepper/pepper_video_capture_host.h" |
+ |
+#include "content/public/renderer/renderer_ppapi_host.h" |
yzshen1
2012/11/06 21:56:23
you have included it in .h.
victorhsieh
2012/11/08 09:20:18
IWYU says we should include them.
http://code.goog
yzshen1
2012/11/10 01:14:40
I think that is not true:
--------------------
"In
victorhsieh
2012/11/13 03:10:53
I see. Done.
|
+#include "ppapi/host/dispatch_host_message.h" |
+#include "ppapi/host/host_message_context.h" |
yzshen1
2012/11/06 21:56:23
ditto.
victorhsieh
2012/11/13 03:10:53
Done.
|
+#include "ppapi/host/ppapi_host.h" |
+#include "ppapi/proxy/host_dispatcher.h" |
+#include "ppapi/proxy/ppapi_messages.h" |
+#include "ppapi/shared_impl/host_resource.h" |
+#include "ppapi/shared_impl/ppapi_globals.h" |
+#include "ppapi/shared_impl/ppb_device_ref_shared.h" |
yzshen1
2012/11/06 21:56:23
ditto.
victorhsieh
2012/11/13 03:10:53
Done.
|
+#include "ppapi/thunk/enter.h" |
+#include "ppapi/thunk/ppb_buffer_api.h" |
+#include "webkit/plugins/ppapi/ppapi_plugin_instance.h" |
+#include "webkit/plugins/ppapi/ppb_buffer_impl.h" |
yzshen1
2012/11/06 21:56:23
ditto.
victorhsieh
2012/11/13 03:10:53
Done.
|
+#include "webkit/plugins/ppapi/resource_helper.h" |
+ |
+using ppapi::DeviceRefData; |
+using ppapi::HostResource; |
+using ppapi::PpapiGlobals; |
+using ppapi::TrackedCallback; |
+using ppapi::thunk::EnterResourceNoLock; |
+using ppapi::thunk::PPB_Buffer_API; |
+using ppapi::thunk::PPB_BufferTrusted_API; |
+using webkit::ppapi::ResourceHelper; |
+using webkit::ppapi::PPB_Buffer_Impl; |
+using webkit::ppapi::PluginInstance; |
+ |
+namespace { |
+ |
+// Maximum number of buffers to actually allocate. |
+const uint32_t kMaxBuffers = 20; |
+ |
+} // namespace |
+ |
+namespace content { |
+ |
+PepperVideoCaptureHost::PepperVideoCaptureHost(RendererPpapiHost* host, |
+ PP_Instance instance, |
yzshen1
2012/11/06 21:56:23
wrong indent.
victorhsieh
2012/11/08 09:20:18
Done.
|
+ PP_Resource resource) |
+ : ResourceHost(host->GetPpapiHost(), instance, resource), |
yzshen1
2012/11/06 21:56:23
wrong indent: four spaces.
victorhsieh
2012/11/08 09:20:18
Done.
|
+ status_(PP_VIDEO_CAPTURE_STATUS_STOPPED) { |
yzshen1
2012/11/06 21:56:23
please init buffer_count_hint_.
victorhsieh
2012/11/08 09:20:18
Done.
|
+} |
+ |
+PepperVideoCaptureHost::~PepperVideoCaptureHost() { |
+ Close(); |
+} |
+ |
+bool PepperVideoCaptureHost::Init() { |
+ PluginInstance* instance = GetPluginInstance(); |
+ return !!instance; |
+} |
+ |
+int32_t PepperVideoCaptureHost::OnResourceMessageReceived( |
+ const IPC::Message& msg, |
yzshen1
2012/11/06 21:56:23
wrong indent.
victorhsieh
2012/11/08 09:20:18
Done.
|
+ ppapi::host::HostMessageContext* context) { |
+ IPC_BEGIN_MESSAGE_MAP(PepperVideoCaptureHost, msg) |
+ PPAPI_DISPATCH_HOST_RESOURCE_CALL_0( |
+ PpapiHostMsg_VideoCapture_EnumerateDevices, |
+ OnEnumerateDevices) |
+ PPAPI_DISPATCH_HOST_RESOURCE_CALL( |
+ PpapiHostMsg_VideoCapture_Open, |
+ OnOpen) |
+ PPAPI_DISPATCH_HOST_RESOURCE_CALL_0( |
+ PpapiHostMsg_VideoCapture_StartCapture, |
+ OnStartCapture) |
+ PPAPI_DISPATCH_HOST_RESOURCE_CALL( |
+ PpapiHostMsg_VideoCapture_ReuseBuffer, |
+ OnReuseBuffer) |
+ PPAPI_DISPATCH_HOST_RESOURCE_CALL_0( |
+ PpapiHostMsg_VideoCapture_StopCapture, |
+ OnStopCapture) |
+ PPAPI_DISPATCH_HOST_RESOURCE_CALL_0( |
+ PpapiHostMsg_VideoCapture_Close, |
+ OnClose) |
+ IPC_END_MESSAGE_MAP() |
+ return PP_ERROR_FAILED; |
+} |
+ |
+void PepperVideoCaptureHost::OnInitialized(media::VideoCapture* capture, |
+ bool succeeded) { |
+ DCHECK(capture == platform_video_capture_.get()); |
+ |
+ if (succeeded) { |
+ open_reply_context_.params.set_result(PP_OK); |
+ } else { |
+ DetachPlatformVideoCapture(); |
+ open_reply_context_.params.set_result(PP_ERROR_FAILED); |
+ } |
+ |
+ host()->SendReply(open_reply_context_, |
+ PpapiPluginMsg_VideoCapture_OpenReply()); |
+} |
+ |
+void PepperVideoCaptureHost::OnStarted(media::VideoCapture* capture) { |
+ if (SetStatus(PP_VIDEO_CAPTURE_STATUS_STARTED, false)) |
+ SendStatus(); |
+} |
+ |
+void PepperVideoCaptureHost::OnStopped(media::VideoCapture* capture) { |
+ if (SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPED, false)) |
+ SendStatus(); |
+} |
+ |
+void PepperVideoCaptureHost::OnPaused(media::VideoCapture* capture) { |
+ if (SetStatus(PP_VIDEO_CAPTURE_STATUS_PAUSED, false)) |
+ SendStatus(); |
+} |
+ |
+void PepperVideoCaptureHost::OnError(media::VideoCapture* capture, |
+ int error_code) { |
+ // Today, the media layer only sends "1" as an error. |
+ DCHECK(error_code == 1); |
+ // It either comes because some error was detected while starting (e.g. 2 |
+ // conflicting "master" resolution), or because the browser failed to start |
+ // the capture. |
+ SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPED, true); |
+ host()->SendUnsolicitedReply(pp_resource(), |
+ PpapiPluginMsg_VideoCapture_OnError(PP_ERROR_FAILED)); |
+} |
+ |
+void PepperVideoCaptureHost::OnRemoved(media::VideoCapture* capture) { |
+} |
+ |
+void PepperVideoCaptureHost::OnBufferReady( |
+ media::VideoCapture* capture, |
+ scoped_refptr<media::VideoCapture::VideoFrameBuffer> buffer) { |
+ DCHECK(buffer.get()); |
+ for (uint32_t i = 0; i < buffers_.size(); ++i) { |
+ if (!buffers_[i].in_use) { |
+ // TODO(ihf): Switch to a size calculation based on stride. |
+ // Stride is filled out now but not more meaningful than size |
+ // until wjia unifies VideoFrameBuffer and media::VideoFrame. |
+ size_t size = std::min(static_cast<size_t>(buffers_[i].buffer->size()), |
+ buffer->buffer_size); |
+ memcpy(buffers_[i].data, buffer->memory_pointer, size); |
+ buffers_[i].in_use = true; |
+ platform_video_capture_->FeedBuffer(buffer); |
+ host()->SendUnsolicitedReply(pp_resource(), |
+ PpapiPluginMsg_VideoCapture_OnBufferReady(i)); |
+ return; |
+ } |
+ } |
+ |
+ // No free slot, just discard the frame and tell the media layer it can |
+ // re-use the buffer. |
+ platform_video_capture_->FeedBuffer(buffer); |
+} |
+ |
+void PepperVideoCaptureHost::OnDeviceInfoReceived( |
+ media::VideoCapture* capture, |
+ const media::VideoCaptureParams& device_info) { |
+ PP_VideoCaptureDeviceInfo_Dev info = { |
+ static_cast<uint32_t>(device_info.width), |
+ static_cast<uint32_t>(device_info.height), |
+ static_cast<uint32_t>(device_info.frame_per_second) |
+ }; |
+ ReleaseBuffers(); |
+ |
+ // YUV 4:2:0 |
+ int uv_width = info.width / 2; |
+ int uv_height = info.height / 2; |
+ size_t size = info.width * info.height + 2 * uv_width * uv_height; |
+ |
+ ppapi::proxy::ResourceMessageReplyParams params(pp_resource(), 0); |
+ |
+ // Allocate buffers. We keep a reference to them, that is released in |
+ // ReleaseBuffers. In the mean time, we prepare the resource and handle here |
+ // for sending below. |
+ std::vector<HostResource> buffer_host_resources; |
+ buffers_.reserve(buffer_count_hint_); |
+ ppapi::proxy::HostDispatcher* dispatcher = |
+ ppapi::proxy::HostDispatcher::GetForInstance(pp_instance()); |
+ for (size_t i = 0; i < buffer_count_hint_; ++i) { |
+ PP_Resource res = PPB_Buffer_Impl::Create(pp_instance(), size); |
+ if (!res) |
+ break; |
+ |
+ EnterResourceNoLock<PPB_Buffer_API> enter(res, true); |
+ DCHECK(enter.succeeded()); |
+ |
+ BufferInfo buf; |
+ buf.buffer = static_cast<PPB_Buffer_Impl*>(enter.object()); |
+ buf.data = buf.buffer->Map(); |
+ if (!buf.data) { |
+ PpapiGlobals::Get()->GetResourceTracker()->ReleaseResource(res); |
+ break; |
+ } |
+ buffers_.push_back(buf); |
+ |
+ // Add to HostResource array to be sent. |
+ { |
+ HostResource host_resource; |
yzshen1
2012/11/06 21:56:23
You have to add one more ref for the plugin side.
victorhsieh
2012/11/08 09:20:18
Done. But how does that work when running out of
yzshen1
2012/11/10 01:14:40
What I said above is for out-of-process. And pleas
victorhsieh
2012/11/13 03:10:53
Done. I was thinking in different way and got con
yzshen1
2012/11/14 06:54:16
HostGlobals SGTM.
|
+ host_resource.SetHostResource(pp_instance(), res); |
+ buffer_host_resources.push_back(host_resource); |
+ } |
+ |
+ // Add the serialized shared memory handle to params. FileDescriptor is |
+ // treated in special case. |
+ { |
+ EnterResourceNoLock<PPB_BufferTrusted_API> enter(res, true); |
+ DCHECK(enter.succeeded()); |
+ int handle; |
+ int32_t result = enter.object()->GetSharedMemory(&handle); |
+ DCHECK(result == PP_OK); |
+ // TODO(piman/brettw): Change trusted interface to return a PP_FileHandle, |
+ // those casts are ugly. |
+ base::PlatformFile platform_file = |
+#if defined(OS_WIN) |
+ reinterpret_cast<HANDLE>(static_cast<intptr_t>(handle)); |
+#elif defined(OS_POSIX) |
+ handle; |
+#else |
+#error Not implemented. |
+#endif |
+ params.AppendHandle( |
+ ppapi::proxy::SerializedHandle( |
+ dispatcher->ShareHandleWithRemote(platform_file, false), |
+ size)); |
+ } |
+ } |
+ |
+ if (buffers_.empty()) { |
+ // We couldn't allocate/map buffers at all. Send an error and stop the |
+ // capture. |
+ SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPING, true); |
+ platform_video_capture_->StopCapture(this); |
+ OnError(capture, PP_ERROR_NOMEMORY); |
+ return; |
+ } |
+ |
+ host()->Send(new PpapiPluginMsg_ResourceReply( |
+ params, PpapiPluginMsg_VideoCapture_OnDeviceInfo( |
+ info, buffer_host_resources, size))); |
+} |
+ |
+PluginInstance* PepperVideoCaptureHost::GetPluginInstance() const { |
+ return ResourceHelper::PPInstanceToPluginInstance(pp_instance()); |
yzshen1
2012/11/06 21:56:23
I thought this wouldn't link. Maybe you are using
victorhsieh
2012/11/08 09:20:18
Actually I just learned about component build this
yzshen1
2012/11/10 01:14:40
It has been checked in.
victorhsieh
2012/11/13 03:10:53
Done.
|
+} |
+ |
+int32_t PepperVideoCaptureHost::OnEnumerateDevices( |
+ ppapi::host::HostMessageContext* context) { |
+ PluginInstance* instance = GetPluginInstance(); |
+ if (!instance) |
+ return PP_ERROR_FAILED; |
+ |
+ enum_reply_context_ = context->MakeReplyMessageContext(); |
yzshen1
2012/11/06 21:56:23
If we receive a second enumerate devices request b
victorhsieh
2012/11/08 09:20:18
I don't see any risk here if we don't check? enum
yzshen1
2012/11/10 01:14:40
Okay.
On 2012/11/08 09:20:18, Victor Hsieh wrote:
|
+ instance->delegate()->EnumerateDevices( |
+ PP_DEVICETYPE_DEV_VIDEOCAPTURE, |
+ base::Bind(&PepperVideoCaptureHost::EnumerateDevicesCallbackFunc, |
+ AsWeakPtr())); |
+ return PP_OK_COMPLETIONPENDING; |
+} |
+ |
+int32_t PepperVideoCaptureHost::OnOpen( |
+ ppapi::host::HostMessageContext* context, |
+ const std::string& device_id, |
+ const PP_VideoCaptureDeviceInfo_Dev& requested_info, |
+ uint32_t buffer_count) { |
+ if (platform_video_capture_.get()) |
+ return PP_ERROR_FAILED; |
+ |
+ PluginInstance* instance = GetPluginInstance(); |
+ if (!instance) |
+ return PP_ERROR_FAILED; |
+ |
+ SetRequestedInfo(requested_info, buffer_count); |
+ |
+ platform_video_capture_ = |
+ instance->delegate()->CreateVideoCapture(device_id, this); |
+ |
+ open_reply_context_ = context->MakeReplyMessageContext(); |
+ |
+ // It is able to complete synchronously if the default device is used. |
+ bool sync_completion = device_id.empty(); |
+ if (sync_completion) { |
+ // Send OpenACK directly, but still need to return PP_OK_COMPLETIONPENDING |
+ // to make PluginResource happy. |
+ OnInitialized(platform_video_capture_.get(), true); |
+ return PP_OK_COMPLETIONPENDING; |
yzshen1
2012/11/06 21:56:23
You don't need this line.
victorhsieh
2012/11/08 09:20:18
Done.
|
+ } |
+ |
+ return PP_OK_COMPLETIONPENDING; |
+} |
+ |
+int32_t PepperVideoCaptureHost::OnStartCapture( |
+ ppapi::host::HostMessageContext* context) { |
+ if (!SetStatus(PP_VIDEO_CAPTURE_STATUS_STARTING, false)) |
+ return PP_ERROR_FAILED; |
+ |
+ DCHECK(platform_video_capture_.get()); |
yzshen1
2012/11/06 21:56:23
If we want this code to handle unexpected requests
victorhsieh
2012/11/08 09:20:18
Done.
|
+ DCHECK(buffers_.empty()); |
+ |
+ // It's safe to call this regardless it's capturing or not, because |
+ // PepperPlatformVideoCaptureImpl maintains the state. |
+ platform_video_capture_->StartCapture(this, capability_); |
+ return PP_OK; |
+} |
+ |
+int32_t PepperVideoCaptureHost::OnReuseBuffer( |
+ ppapi::host::HostMessageContext* context, |
+ uint32_t buffer) { |
+ if (buffer >= buffers_.size() || !buffers_[buffer].in_use) |
+ return PP_ERROR_BADARGUMENT; |
+ buffers_[buffer].in_use = false; |
+ return PP_OK; |
+} |
+ |
+int32_t PepperVideoCaptureHost::OnStopCapture( |
+ ppapi::host::HostMessageContext* context) { |
+ return StopCapture(); |
+} |
+ |
+int32_t PepperVideoCaptureHost::OnClose( |
+ ppapi::host::HostMessageContext* context) { |
+ return Close(); |
+} |
+ |
+int32_t PepperVideoCaptureHost::StopCapture() { |
+ if (!SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPING, false)) |
+ return PP_ERROR_FAILED; |
+ |
+ DCHECK(platform_video_capture_.get()); |
+ |
+ ReleaseBuffers(); |
+ // It's safe to call this regardless it's capturing or not, because |
+ // PepperPlatformVideoCaptureImpl maintains the state. |
+ platform_video_capture_->StopCapture(this); |
+ return PP_OK; |
+} |
+ |
+int32_t PepperVideoCaptureHost::Close() { |
+ if (!platform_video_capture_.get()) |
+ return PP_OK; |
+ |
+ StopCapture(); |
+ DCHECK(buffers_.empty()); |
+ DetachPlatformVideoCapture(); |
+ return PP_OK; |
+} |
+ |
+void PepperVideoCaptureHost::ReleaseBuffers() { |
+ ::ppapi::ResourceTracker* tracker = PpapiGlobals::Get()->GetResourceTracker(); |
+ for (size_t i = 0; i < buffers_.size(); ++i) { |
+ buffers_[i].buffer->Unmap(); |
+ tracker->ReleaseResource(buffers_[i].buffer->pp_resource()); |
+ } |
+ buffers_.clear(); |
+} |
+ |
+void PepperVideoCaptureHost::SendStatus() { |
+ host()->SendUnsolicitedReply(pp_resource(), |
+ PpapiPluginMsg_VideoCapture_OnStatus(status_)); |
+} |
+ |
+void PepperVideoCaptureHost::SetRequestedInfo( |
+ const PP_VideoCaptureDeviceInfo_Dev& device_info, |
+ uint32_t buffer_count) { |
+ // Clamp the buffer count to between 1 and |kMaxBuffers|. |
+ buffer_count_hint_ = std::min(std::max(buffer_count, 1U), kMaxBuffers); |
+ |
+ capability_.width = device_info.width; |
+ capability_.height = device_info.height; |
+ capability_.frame_rate = device_info.frames_per_second; |
+ capability_.expected_capture_delay = 0; // Ignored. |
+ capability_.color = media::VideoCaptureCapability::kI420; |
+ capability_.interlaced = false; // Ignored. |
+} |
+ |
+void PepperVideoCaptureHost::DetachPlatformVideoCapture() { |
+ if (platform_video_capture_.get()) { |
+ platform_video_capture_->DetachEventHandler(); |
+ platform_video_capture_ = NULL; |
+ } |
+} |
+ |
+void PepperVideoCaptureHost::EnumerateDevicesCallbackFunc( |
+ int request_id, |
+ bool succeeded, |
+ const std::vector<ppapi::DeviceRefData>& devices) { |
+ PluginInstance* instance = GetPluginInstance(); |
+ if (instance) |
+ instance->delegate()->StopEnumerateDevices(request_id); |
+ |
+ if (succeeded) { |
+ enum_reply_context_.params.set_result(PP_OK); |
+ host()->SendReply(enum_reply_context_, |
+ PpapiPluginMsg_VideoCapture_EnumerateDevicesReply( |
+ devices)); |
+ } else { |
+ enum_reply_context_.params.set_result(PP_ERROR_FAILED); |
+ host()->SendReply(enum_reply_context_, |
+ PpapiPluginMsg_VideoCapture_EnumerateDevicesReply( |
+ std::vector<DeviceRefData>())); |
+ } |
+} |
+ |
+bool PepperVideoCaptureHost::SetStatus(PP_VideoCaptureStatus_Dev status, |
+ bool forced) { |
+ if (!forced) { |
+ switch (status) { |
+ case PP_VIDEO_CAPTURE_STATUS_STOPPED: |
+ if (status_ != PP_VIDEO_CAPTURE_STATUS_STOPPING) |
+ return false; |
+ break; |
+ case PP_VIDEO_CAPTURE_STATUS_STARTING: |
+ if (status_ != PP_VIDEO_CAPTURE_STATUS_STOPPED) |
+ return false; |
+ break; |
+ case PP_VIDEO_CAPTURE_STATUS_STARTED: |
+ switch (status_) { |
+ case PP_VIDEO_CAPTURE_STATUS_STARTING: |
+ case PP_VIDEO_CAPTURE_STATUS_PAUSED: |
+ break; |
+ default: |
+ return false; |
+ } |
+ break; |
+ case PP_VIDEO_CAPTURE_STATUS_PAUSED: |
+ switch (status_) { |
+ case PP_VIDEO_CAPTURE_STATUS_STARTING: |
+ case PP_VIDEO_CAPTURE_STATUS_STARTED: |
+ break; |
+ default: |
+ return false; |
+ } |
+ break; |
+ case PP_VIDEO_CAPTURE_STATUS_STOPPING: |
+ switch (status_) { |
+ case PP_VIDEO_CAPTURE_STATUS_STARTING: |
+ case PP_VIDEO_CAPTURE_STATUS_STARTED: |
+ case PP_VIDEO_CAPTURE_STATUS_PAUSED: |
+ break; |
+ default: |
+ return false; |
+ } |
+ break; |
+ } |
+ } |
+ |
+ status_ = status; |
+ return true; |
+} |
+ |
+PepperVideoCaptureHost::BufferInfo::BufferInfo() |
+ : in_use(false), |
+ data(NULL), |
+ buffer() { |
+} |
+ |
+PepperVideoCaptureHost::BufferInfo::~BufferInfo() { |
+} |
+ |
+} // namespace content |