Index: webkit/plugins/ppapi/ppb_video_capture_impl.cc |
diff --git a/webkit/plugins/ppapi/ppb_video_capture_impl.cc b/webkit/plugins/ppapi/ppb_video_capture_impl.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..c112fce5e8b3503c9908d5cc378f29d1c17dbabc |
--- /dev/null |
+++ b/webkit/plugins/ppapi/ppb_video_capture_impl.cc |
@@ -0,0 +1,271 @@ |
+// Copyright (c) 2011 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 "webkit/plugins/ppapi/ppb_video_capture_impl.h" |
+ |
+#include <algorithm> |
+#include <string> |
+ |
+#include "base/logging.h" |
+#include "ppapi/c/dev/pp_video_capture_dev.h" |
+#include "ppapi/c/dev/ppb_video_capture_dev.h" |
+#include "ppapi/c/pp_completion_callback.h" |
+#include "ppapi/c/pp_errors.h" |
+#include "ppapi/thunk/enter.h" |
+#include "webkit/plugins/ppapi/common.h" |
+#include "webkit/plugins/ppapi/plugin_module.h" |
+#include "webkit/plugins/ppapi/ppapi_plugin_instance.h" |
+#include "webkit/plugins/ppapi/ppb_buffer_impl.h" |
+#include "webkit/plugins/ppapi/resource_tracker.h" |
+ |
+using ppapi::thunk::EnterResourceNoLock; |
+using ppapi::thunk::PPB_Buffer_API; |
+using ppapi::thunk::PPB_VideoCapture_API; |
+ |
+namespace webkit { |
+namespace ppapi { |
+ |
+PPB_VideoCapture_Impl::PPB_VideoCapture_Impl(PluginInstance* instance) |
+ : Resource(instance), |
+ buffer_count_hint_(0), |
+ ppp_videocapture_(NULL), |
+ status_(PP_VIDEO_CAPTURE_STATUS_STOPPED) { |
+} |
+ |
+PPB_VideoCapture_Impl::~PPB_VideoCapture_Impl() { |
+ if (platform_video_capture_.get()) |
+ StopCapture(); |
+} |
+ |
+bool PPB_VideoCapture_Impl::Init() { |
+ ppp_videocapture_ = |
+ static_cast<const PPP_VideoCapture_Dev*>(instance()->module()-> |
+ GetPluginInterface(PPP_VIDEO_CAPTURE_DEV_INTERFACE)); |
+ if (!ppp_videocapture_) |
+ return false; |
+ |
+ platform_video_capture_.reset( |
+ instance()->delegate()->CreateVideoCapture(this)); |
+ return !!platform_video_capture_.get(); |
+} |
+ |
+PPB_VideoCapture_API* PPB_VideoCapture_Impl::AsPPB_VideoCapture_API() { |
+ return this; |
+} |
+ |
+int32_t PPB_VideoCapture_Impl::StartCapture( |
+ const PP_VideoCaptureDeviceInfo_Dev& requested_info, |
+ uint32_t buffer_count) { |
+ switch (status_) { |
+ case PP_VIDEO_CAPTURE_STATUS_STARTING: |
+ case PP_VIDEO_CAPTURE_STATUS_STARTED: |
+ case PP_VIDEO_CAPTURE_STATUS_PAUSED: |
+ default: |
+ return PP_ERROR_FAILED; |
+ case PP_VIDEO_CAPTURE_STATUS_STOPPED: |
+ case PP_VIDEO_CAPTURE_STATUS_STOPPING: |
+ break; |
+ } |
+ DCHECK(buffers_.empty()); |
+ |
+ buffer_count_hint_ = std::min(buffer_count, 1U); |
+ media::VideoCapture::VideoCaptureCapability capability = { |
+ requested_info.width, |
+ requested_info.height, |
+ requested_info.frames_per_second, |
+ 0, // ignored. |
+ media::VideoFrame::I420, |
+ false, // ignored |
+ false // resolution_fixed |
+ }; |
+ status_ = PP_VIDEO_CAPTURE_STATUS_STARTING; |
+ platform_video_capture_->StartCapture(this, capability); |
+ return PP_OK; |
+} |
+ |
+int32_t PPB_VideoCapture_Impl::ReuseBuffer(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 PPB_VideoCapture_Impl::StopCapture() { |
+ switch (status_) { |
+ case PP_VIDEO_CAPTURE_STATUS_STOPPED: |
+ case PP_VIDEO_CAPTURE_STATUS_STOPPING: |
+ default: |
+ return PP_ERROR_FAILED; |
+ case PP_VIDEO_CAPTURE_STATUS_STARTING: |
+ case PP_VIDEO_CAPTURE_STATUS_STARTED: |
+ case PP_VIDEO_CAPTURE_STATUS_PAUSED: |
+ break; |
+ } |
+ ReleaseBuffers(); |
+ status_ = PP_VIDEO_CAPTURE_STATUS_STOPPING; |
+ platform_video_capture_->StopCapture(this); |
+ return PP_OK; |
+} |
+ |
+void PPB_VideoCapture_Impl::OnStarted(media::VideoCapture* capture) { |
+ switch (status_) { |
+ case PP_VIDEO_CAPTURE_STATUS_STARTING: |
+ case PP_VIDEO_CAPTURE_STATUS_PAUSED: |
+ break; |
+ case PP_VIDEO_CAPTURE_STATUS_STOPPED: |
+ case PP_VIDEO_CAPTURE_STATUS_STOPPING: |
+ case PP_VIDEO_CAPTURE_STATUS_STARTED: |
+ default: |
+ return; |
+ } |
+ status_ = PP_VIDEO_CAPTURE_STATUS_STARTED; |
+ SendStatus(); |
+} |
+ |
+void PPB_VideoCapture_Impl::OnStopped(media::VideoCapture* capture) { |
+ switch (status_) { |
+ case PP_VIDEO_CAPTURE_STATUS_STOPPING: |
+ break; |
+ case PP_VIDEO_CAPTURE_STATUS_STARTING: |
+ case PP_VIDEO_CAPTURE_STATUS_PAUSED: |
+ case PP_VIDEO_CAPTURE_STATUS_STOPPED: |
+ case PP_VIDEO_CAPTURE_STATUS_STARTED: |
+ default: |
+ return; |
+ } |
+ status_ = PP_VIDEO_CAPTURE_STATUS_STOPPED; |
+ SendStatus(); |
+} |
+ |
+void PPB_VideoCapture_Impl::OnPaused(media::VideoCapture* capture) { |
+ switch (status_) { |
+ case PP_VIDEO_CAPTURE_STATUS_STARTING: |
+ case PP_VIDEO_CAPTURE_STATUS_STARTED: |
+ break; |
+ case PP_VIDEO_CAPTURE_STATUS_STOPPED: |
+ case PP_VIDEO_CAPTURE_STATUS_STOPPING: |
+ case PP_VIDEO_CAPTURE_STATUS_PAUSED: |
+ default: |
+ return; |
+ } |
+ status_ = PP_VIDEO_CAPTURE_STATUS_PAUSED; |
+ SendStatus(); |
+} |
+ |
+void PPB_VideoCapture_Impl::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. |
+ status_ = PP_VIDEO_CAPTURE_STATUS_STOPPED; |
+ ppp_videocapture_->OnError(instance()->pp_instance(), |
+ ScopedResourceId(this).id, |
+ PP_ERROR_FAILED); |
+} |
+ |
+void PPB_VideoCapture_Impl::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(piman): it looks like stride isn't actually used/filled. |
+ DCHECK(buffer->stride == 0); |
+ size_t size = std::min(static_cast<size_t>(buffers_[i].buffer->size()), |
+ buffer->buffer_size); |
+ memcpy(buffers_[i].data, buffer->memory_pointer, size); |
+ platform_video_capture_->FeedBuffer(buffer); |
+ ppp_videocapture_->OnBufferReady(instance()->pp_instance(), |
+ ScopedResourceId(this).id, |
+ i); |
+ return; |
+ } |
+ } |
+ // TODO(piman): signal dropped buffers ? |
+ platform_video_capture_->FeedBuffer(buffer); |
+} |
+ |
+void PPB_VideoCapture_Impl::OnDeviceInfoReceived( |
+ media::VideoCapture* capture, |
+ const media::VideoCaptureParams& device_info) { |
+ PP_VideoCaptureDeviceInfo_Dev info = { |
+ device_info.width, |
+ device_info.height, |
+ device_info.frame_per_second |
+ }; |
+ ReleaseBuffers(); |
+ |
+ // Allocate buffers. We keep a reference to them, that is released in |
+ // 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; |
+ scoped_array<PP_Resource> resources(new PP_Resource[buffer_count_hint_]); |
+ |
+ buffers_.reserve(buffer_count_hint_); |
+ for (size_t i = 0; i < buffer_count_hint_; ++i) { |
+ resources[i] = PPB_Buffer_Impl::Create(instance(), size); |
+ if (!resources[i]) |
+ break; |
+ |
+ EnterResourceNoLock<PPB_Buffer_API> enter(resources[i], true); |
+ DCHECK(enter.succeeded()); |
+ |
+ BufferInfo info; |
+ info.buffer = static_cast<PPB_Buffer_Impl*>(enter.object()); |
+ info.data = info.buffer->Map(); |
+ if (!info.data) { |
+ ResourceTracker::Get()->UnrefResource(resources[i]); |
+ break; |
+ } |
+ buffers_.push_back(info); |
+ } |
+ |
+ if (buffers_.empty()) { |
+ // We couldn't allocate/map buffers at all. Send an error and stop the |
+ // capture. |
+ ppp_videocapture_->OnError(instance()->pp_instance(), |
+ ScopedResourceId(this).id, |
+ PP_ERROR_NOMEMORY); |
+ status_ = PP_VIDEO_CAPTURE_STATUS_STOPPING; |
+ platform_video_capture_->StopCapture(this); |
+ return; |
+ } |
+ |
+ ppp_videocapture_->OnDeviceInfo(instance()->pp_instance(), |
+ ScopedResourceId(this).id, |
+ &info, |
+ buffers_.size(), |
+ resources.get()); |
+} |
+ |
+void PPB_VideoCapture_Impl::ReleaseBuffers() { |
+ ResourceTracker *tracker = ResourceTracker::Get(); |
+ for (size_t i = 0; i < buffers_.size(); ++i) { |
+ buffers_[i].buffer->Unmap(); |
+ tracker->UnrefResource(buffers_[i].buffer->GetReferenceNoAddRef()); |
+ } |
+ buffers_.clear(); |
+} |
+ |
+void PPB_VideoCapture_Impl::SendStatus() { |
+ ppp_videocapture_->OnStatus(instance()->pp_instance(), |
+ ScopedResourceId(this).id, |
+ status_); |
+} |
+ |
+PPB_VideoCapture_Impl::BufferInfo::BufferInfo() |
+ : in_use(false), |
+ data(NULL), |
+ buffer() { |
+} |
+ |
+PPB_VideoCapture_Impl::BufferInfo::~BufferInfo() { |
+} |
+ |
+} // namespace ppapi |
+} // namespace webkit |