Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1216)

Unified Diff: content/renderer/pepper/pepper_video_capture_host.cc

Issue 11274036: Refactor video capture to new design (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: export Created 8 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « content/renderer/pepper/pepper_video_capture_host.h ('k') | ppapi/api/dev/ppb_video_capture_dev.idl » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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..19866d677476cd0e674d213c6d9bfa8e8587f649
--- /dev/null
+++ b/content/renderer/pepper/pepper_video_capture_host.cc
@@ -0,0 +1,460 @@
+// 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 "ppapi/host/dispatch_host_message.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/thunk/enter.h"
+#include "ppapi/thunk/ppb_buffer_api.h"
+#include "webkit/plugins/ppapi/host_globals.h"
+#include "webkit/plugins/ppapi/ppapi_plugin_instance.h"
+#include "webkit/plugins/ppapi/resource_helper.h"
+
+using ppapi::DeviceRefData;
+using ppapi::HostResource;
+using ppapi::TrackedCallback;
+using ppapi::thunk::EnterResourceNoLock;
+using ppapi::thunk::PPB_Buffer_API;
+using ppapi::thunk::PPB_BufferTrusted_API;
+using webkit::ppapi::HostGlobals;
+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),
+ renderer_ppapi_host_(host),
+ buffer_count_hint_(0),
+ 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::ResourceTracker* tracker =
+ HostGlobals::Get()->GetResourceTracker();
+ 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) {
+ tracker->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 a reference for the plugin, which is resposible for releasing it.
+ tracker->AddRefResource(res);
+ }
+
+ // 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 renderer_ppapi_host_->GetPluginInstance(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;
+}
+
+int32_t PepperVideoCaptureHost::OnStartCapture(
+ ppapi::host::HostMessageContext* context) {
+ if (!SetStatus(PP_VIDEO_CAPTURE_STATUS_STARTING, false) ||
+ !platform_video_capture_.get())
+ return PP_ERROR_FAILED;
+
+ 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 = HostGlobals::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
« no previous file with comments | « content/renderer/pepper/pepper_video_capture_host.h ('k') | ppapi/api/dev/ppb_video_capture_dev.idl » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698