Chromium Code Reviews| 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..22fdfc9422cdd23e5812a14c21a0464ccf29c2ee |
| --- /dev/null |
| +++ b/webkit/plugins/ppapi/ppb_video_capture_impl.cc |
| @@ -0,0 +1,258 @@ |
| +// 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), |
| + status_(PP_VIDEO_CAPTURE_STATUS_STOPPED) { |
| + ppp_videocapture_ = |
|
brettw
2011/08/02 17:17:44
I'd initialize this to null in the initializer lis
piman
2011/08/03 00:44:41
Done.
|
| + static_cast<const PPP_VideoCapture_Dev*>(instance->module()-> |
| + GetPluginInterface(PPP_VIDEO_CAPTURE_DEV_INTERFACE)); |
| +} |
| + |
| +PPB_VideoCapture_Impl::~PPB_VideoCapture_Impl() { |
| + if (platform_video_capture_.get()) |
| + StopCapture(); |
| +} |
| + |
| +bool PPB_VideoCapture_Impl::Init() { |
| + platform_video_capture_.reset( |
| + instance()->delegate()->CreateVideoCapture(this)); |
| + return ppp_videocapture_ && 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; |
|
wjia(left Chromium)
2011/08/02 16:22:28
Is this a fatal error, or just warning? It might b
piman
2011/08/03 00:44:41
It's essentially silent, except if the plugin look
|
| + case PP_VIDEO_CAPTURE_STATUS_STARTING: |
| + case PP_VIDEO_CAPTURE_STATUS_STARTED: |
| + case PP_VIDEO_CAPTURE_STATUS_PAUSED: |
| + break; |
| + } |
| + FreeBuffers(); |
| + 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, |
|
wjia(left Chromium)
2011/08/02 16:22:28
This |capture| looks like an alien since PPB_Video
piman
2011/08/03 00:44:41
So, I was kinda wondering about that, but decided
|
| + scoped_refptr<media::VideoCapture::VideoFrameBuffer> buffer) { |
|
wjia(left Chromium)
2011/08/02 16:22:28
it would be good to check buffer (not a NULL point
piman
2011/08/03 00:44:41
Is there a reason the VideoCaptureImpl would send
wjia(left Chromium)
2011/08/03 02:40:00
I meant DCHECK which has been added.
|
| + if (!buffers_.empty()) { |
| + 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(buffers_[i].buffer->size(), buffer->buffer_size); |
| + memcpy(buffers_[i].data, buffer->memory_pointer, size); |
|
wjia(left Chromium)
2011/08/02 16:22:28
would it be good to call "platform_video_capture_-
piman
2011/08/03 00:44:41
Done.
|
| + ppp_videocapture_->OnBufferReady(instance()->pp_instance(), |
| + ScopedResourceId(this).id, |
| + i); |
| + break; |
| + } |
| + } |
| + } |
| + // 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 |
| + }; |
| + FreeBuffers(); |
|
wjia(left Chromium)
2011/08/02 16:22:28
is it guaranteed that no buffer is in use by plugi
piman
2011/08/03 00:44:41
The plugin is responsible for keeping its own refe
|
| + |
| + // Allocate buffers. We keep a reference to them, that is released in |
| + // FreeBuffers. |
| + // 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::FreeBuffers() { |
| + 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_); |
| +} |
| + |
| +} // namespace ppapi |
| +} // namespace webkit |