Chromium Code Reviews| 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..6a0389b0a1ab73480d17b03e3771a01579d747e1 |
| --- /dev/null |
| +++ b/content/renderer/pepper/pepper_video_capture_host.cc |
| @@ -0,0 +1,436 @@ |
| +// 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" |
| +#include "ppapi/host/dispatch_host_message.h" |
| +#include "ppapi/host/host_message_context.h" |
| +#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" |
| +#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" |
| +#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, |
| + PP_Resource resource) |
| + : ResourceHost(host->GetPpapiHost(), instance, resource), |
| + ppapi::PPB_VideoCapture_Shared() { |
| +} |
| + |
| +PepperVideoCaptureHost::~PepperVideoCaptureHost() { |
| + Close(); |
| +} |
| + |
| +bool PepperVideoCaptureHost::Init() { |
| + PluginInstance* instance = GetPluginInstance(); |
| + return !!instance; |
| +} |
| + |
| +int32_t PepperVideoCaptureHost::OnResourceMessageReceived( |
| + const IPC::Message& msg, |
| + 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) |
| + PPAPI_DISPATCH_HOST_RESOURCE_CALL( |
| + PpapiHostMsg_VideoCapture_StartCapture0_1, |
| + OnStartCapture0_1) |
| + IPC_END_MESSAGE_MAP() |
| + return PP_ERROR_FAILED; |
| +} |
| + |
| +void PepperVideoCaptureHost::OnInitialized(media::VideoCapture* capture, |
| + bool succeeded) { |
| + DCHECK(capture == platform_video_capture_.get()); |
| + |
| + if (open_state_ == BEFORE_OPEN && succeeded) |
| + open_state_ = OPENED; |
| + |
| + host()->SendReply(open_reply_context_, |
| + PpapiPluginMsg_VideoCapture_OpenACK(PP_OK)); |
| +} |
| + |
| +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; |
| + 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()); |
| +} |
| + |
| +int32_t PepperVideoCaptureHost::OnEnumerateDevices( |
| + ppapi::host::HostMessageContext* context) { |
| + PluginInstance* instance = GetPluginInstance(); |
| + if (!instance) |
| + return PP_ERROR_FAILED; |
| + |
| + enum_reply_context_ = context->MakeReplyMessageContext(); |
| + instance->delegate()->EnumerateDevices( |
|
yzshen1
2012/10/25 20:41:05
FYI: I don't think we want to use the plugin deleg
victorhsieh
2012/10/26 02:34:54
I see.
|
| + 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 (open_state_ != BEFORE_OPEN) |
| + return PP_ERROR_FAILED; |
| + |
| + PluginInstance* instance = GetPluginInstance(); |
| + if (!instance) |
| + return PP_ERROR_FAILED; |
| + |
| + SetRequestedInfo(requested_info, buffer_count); |
| + |
| + DCHECK(!platform_video_capture_.get()); |
| + 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; |
|
victorhsieh
2012/10/25 09:16:42
The orignal impl returns PP_OK, but that will fail
yzshen1
2012/10/25 20:41:05
Yes. The original proxy code uses EnterHostFromHos
|
| + } |
| + |
| + return PP_OK_COMPLETIONPENDING; |
| +} |
| + |
| +int32_t PepperVideoCaptureHost::OnStartCapture( |
| + ppapi::host::HostMessageContext* context) { |
| + return StartCapture(); |
| +} |
| + |
| +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::OnStartCapture0_1( |
| + ppapi::host::HostMessageContext* context, |
| + const PP_VideoCaptureDeviceInfo_Dev& requested_info, |
| + uint32_t buffer_count) { |
| + return StartCapture0_1(requested_info, buffer_count); |
| +} |
| + |
| +int32_t PepperVideoCaptureHost::Close() { |
| + if (open_state_ == CLOSED) |
| + return PP_OK; |
| + |
| + StopCapture(); |
| + DCHECK(buffers_.empty()); |
| + DetachPlatformVideoCapture(); |
| + |
| + open_state_ = CLOSED; |
| + |
| + return PP_OK; |
| +} |
| + |
| +int32_t PepperVideoCaptureHost::InternalStartCapture() { |
| + DCHECK(buffers_.empty()); |
| + platform_video_capture_->StartCapture(this, capability_); |
| + return PP_OK; |
| +} |
| + |
| +int32_t PepperVideoCaptureHost::InternalStopCapture() { |
| + ReleaseBuffers(); |
| + platform_video_capture_->StopCapture(this); |
| + return PP_OK; |
| +} |
| + |
| +int32_t PepperVideoCaptureHost::InternalStartCapture0_1( |
| + const PP_VideoCaptureDeviceInfo_Dev& requested_info, |
| + uint32_t buffer_count) { |
| + if (platform_video_capture_.get()) { |
| + return PP_ERROR_FAILED; |
| + } |
| + |
| + PluginInstance* instance = GetPluginInstance(); |
| + if (!instance) { |
| + SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPED, true); |
| + return PP_ERROR_FAILED; |
| + } |
| + |
| + DCHECK(buffers_.empty()); |
| + |
| + SetRequestedInfo(requested_info, buffer_count); |
| + |
| + DetachPlatformVideoCapture(); |
| + platform_video_capture_ = |
| + instance->delegate()->CreateVideoCapture("", this); |
| + platform_video_capture_->StartCapture(this, capability_); |
| + |
| + 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) { |
| + host()->SendReply(enum_reply_context_, |
| + PpapiPluginMsg_VideoCapture_EnumerateDevicesResponse( |
| + PP_OK, |
| + devices)); |
| + } else { |
| + host()->SendReply(enum_reply_context_, |
| + PpapiPluginMsg_VideoCapture_EnumerateDevicesResponse( |
| + PP_ERROR_FAILED, |
| + std::vector<DeviceRefData>())); |
| + } |
| +} |
| + |
| +PepperVideoCaptureHost::BufferInfo::BufferInfo() |
| + : in_use(false), |
| + data(NULL), |
| + buffer() { |
| +} |
| + |
| +PepperVideoCaptureHost::BufferInfo::~BufferInfo() { |
| +} |
| + |
| +} // namespace content |