// 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/media/video_capture_module_impl.h" #include "base/atomicops.h" #include "base/bind.h" #include "content/renderer/media/video_capture_impl_manager.h" VideoCaptureModuleImpl::VideoCaptureModuleImpl( const media::VideoCaptureSessionId id, VideoCaptureImplManager* vc_manager) : webrtc::videocapturemodule::VideoCaptureImpl(id), session_id_(id), thread_("VideoCaptureModuleImpl"), stopped_event_(false, false), vc_manager_(vc_manager), state_(video_capture::kStopped), width_(-1), height_(-1), frame_rate_(-1), video_type_(webrtc::kVideoI420), capture_engine_(NULL), ref_count_(0) { DCHECK(vc_manager_); Init(); } VideoCaptureModuleImpl::~VideoCaptureModuleImpl() { // Ensure to stop capture in case user doesn't do it. StopCapture(); vc_manager_->RemoveDevice(session_id_, this); // Ensure capture stopped by |capture_engine_| since all tasks posted on this // thread will be executed and VCMImpl is waiting for OnStopped signal from // |capture_engine_|. thread_.Stop(); } void VideoCaptureModuleImpl::Init() { thread_.Start(); message_loop_proxy_ = thread_.message_loop_proxy(); capture_engine_ = vc_manager_->AddDevice(session_id_, this); } int32_t VideoCaptureModuleImpl::AddRef() { DVLOG(1) << "VideoCaptureModuleImpl::AddRef()"; return base::subtle::Barrier_AtomicIncrement(&ref_count_, 1); } int32_t VideoCaptureModuleImpl::Release() { DVLOG(1) << "VideoCaptureModuleImpl::Release()"; int ret = base::subtle::Barrier_AtomicIncrement(&ref_count_, -1); if (ret == 0) { DVLOG(1) << "Reference count is zero, hence this object is now deleted."; delete this; } return ret; } WebRtc_Word32 VideoCaptureModuleImpl::StartCapture( const webrtc::VideoCaptureCapability& capability) { message_loop_proxy_->PostTask( FROM_HERE, base::Bind(&VideoCaptureModuleImpl::StartCaptureOnCaptureThread, base::Unretained(this), capability)); return 0; } WebRtc_Word32 VideoCaptureModuleImpl::StopCapture() { message_loop_proxy_->PostTask( FROM_HERE, base::Bind(&VideoCaptureModuleImpl::StopCaptureOnCaptureThread, base::Unretained(this))); return 0; } bool VideoCaptureModuleImpl::CaptureStarted() { return state_ == video_capture::kStarted; } WebRtc_Word32 VideoCaptureModuleImpl::CaptureSettings( webrtc::VideoCaptureCapability& settings) { settings.width = width_; settings.height = height_; settings.maxFPS = frame_rate_; settings.expectedCaptureDelay = 120; settings.rawType = webrtc::kVideoI420; return 0; } void VideoCaptureModuleImpl::OnStarted(media::VideoCapture* capture) { NOTIMPLEMENTED(); } void VideoCaptureModuleImpl::OnStopped(media::VideoCapture* capture) { stopped_event_.Signal(); } void VideoCaptureModuleImpl::OnPaused(media::VideoCapture* capture) { NOTIMPLEMENTED(); } void VideoCaptureModuleImpl::OnError(media::VideoCapture* capture, int error_code) { NOTIMPLEMENTED(); } void VideoCaptureModuleImpl::OnRemoved(media::VideoCapture* capture) { NOTIMPLEMENTED(); } void VideoCaptureModuleImpl::OnBufferReady( media::VideoCapture* capture, scoped_refptr buf) { message_loop_proxy_->PostTask( FROM_HERE, base::Bind(&VideoCaptureModuleImpl::OnBufferReadyOnCaptureThread, base::Unretained(this), capture, buf)); } void VideoCaptureModuleImpl::OnDeviceInfoReceived( media::VideoCapture* capture, const media::VideoCaptureParams& device_info) { NOTIMPLEMENTED(); } void VideoCaptureModuleImpl::StartCaptureOnCaptureThread( const webrtc::VideoCaptureCapability& capability) { DCHECK(message_loop_proxy_->BelongsToCurrentThread()); DCHECK_NE(state_, video_capture::kStarted); DVLOG(1) << "StartCaptureOnCaptureThread: " << capability.width << ", " << capability.height; StartCaptureInternal(capability); return; } void VideoCaptureModuleImpl::StartCaptureInternal( const webrtc::VideoCaptureCapability& capability) { DCHECK(message_loop_proxy_->BelongsToCurrentThread()); DCHECK_EQ(capability.rawType, webrtc::kVideoI420); video_type_ = capability.rawType; width_ = capability.width; height_ = capability.height; frame_rate_ = capability.maxFPS; state_ = video_capture::kStarted; media::VideoCaptureCapability cap; cap.width = capability.width; cap.height = capability.height; cap.frame_rate = capability.maxFPS; cap.color = media::VideoFrame::I420; capture_engine_->StartCapture(this, cap); } void VideoCaptureModuleImpl::StopCaptureOnCaptureThread() { DCHECK(message_loop_proxy_->BelongsToCurrentThread()); if (state_ != video_capture::kStarted) { DVLOG(1) << "Got a StopCapture while not started!!! "; return; } DVLOG(1) << "StopCaptureOnCaptureThread. "; capture_engine_->StopCapture(this); // Need to use synchronous mode here to make sure |capture_engine_| will // not call VCMImpl after this function returns. Otherwise, there could be // use-after-free, especially when StopCapture() is called from dtor. stopped_event_.Wait(); DVLOG(1) << "Capture Stopped!!! "; state_ = video_capture::kStopped; width_ = -1; height_ = -1; frame_rate_ = -1; } void VideoCaptureModuleImpl::OnBufferReadyOnCaptureThread( media::VideoCapture* capture, scoped_refptr buf) { DCHECK(message_loop_proxy_->BelongsToCurrentThread()); if (state_ != video_capture::kStarted) return; frameInfo_.width = buf->width; frameInfo_.height = buf->height; frameInfo_.rawType = video_type_; IncomingFrame( static_cast(buf->memory_pointer), static_cast(buf->buffer_size), frameInfo_, static_cast( (buf->timestamp - start_time_).InMilliseconds())); capture->FeedBuffer(buf); }