| 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"
|
| +#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),
|
| + status_(PP_VIDEO_CAPTURE_STATUS_STOPPED) {
|
| +}
|
| +
|
| +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)
|
| + 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;
|
| + 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(
|
| + 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;
|
| + }
|
| +
|
| + 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());
|
| + 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
|
|
|