Chromium Code Reviews| Index: content/browser/renderer_host/media/video_capture_controller.cc |
| diff --git a/content/browser/renderer_host/media/video_capture_controller.cc b/content/browser/renderer_host/media/video_capture_controller.cc |
| index dbafeba49223d38b2edec3f738977f6241e8898c..454864cc1b9ce5dcefe45875d4f47aae7c233e62 100644 |
| --- a/content/browser/renderer_host/media/video_capture_controller.cc |
| +++ b/content/browser/renderer_host/media/video_capture_controller.cc |
| @@ -93,14 +93,79 @@ struct VideoCaptureController::ControllerClient { |
| bool session_closed; |
| }; |
| +// Receives events from the VideoCaptureDevice and posts them to a |
| +// VideoCaptureController on the IO thread. An instance of this class may safely |
| +// outlive its target VideoCaptureController. |
| +// |
| +// Methods of this class may be called from any thread, and in practice will |
| +// often be called on some auxiliary thread depending on the platform and the |
| +// device type; including, for example, the DirectShow thread on Windows, the |
| +// v4l2_thread on Linux, and the UI thread for tab capture. |
| +class VideoCaptureController::VideoCaptureDeviceClient |
| + : public media::VideoCaptureDevice::EventHandler { |
| + public: |
| + explicit VideoCaptureDeviceClient( |
| + const base::WeakPtr<VideoCaptureController>& controller); |
| + virtual ~VideoCaptureDeviceClient(); |
| + |
| + // VideoCaptureDevice::EventHandler implementation. |
| + virtual scoped_refptr<media::VideoFrame> ReserveOutputBuffer() OVERRIDE; |
| + virtual void OnIncomingCapturedFrame(const uint8* data, |
| + int length, |
| + base::Time timestamp, |
| + int rotation, |
| + bool flip_vert, |
| + bool flip_horiz) OVERRIDE; |
| + virtual void OnIncomingCapturedVideoFrame( |
| + const scoped_refptr<media::VideoFrame>& frame, |
| + base::Time timestamp) OVERRIDE; |
| + virtual void OnError() OVERRIDE; |
| + virtual void OnFrameInfo( |
| + const media::VideoCaptureCapability& info) OVERRIDE; |
| + virtual void OnFrameInfoChanged( |
| + const media::VideoCaptureCapability& info) OVERRIDE; |
| + |
| + private: |
| + // The controller to which we post events. |
| + const base::WeakPtr<VideoCaptureController> controller_; |
| + |
| + // The pool of shared-memory buffers used for capturing. |
| + scoped_refptr<VideoCaptureBufferPool> buffer_pool_; |
| + |
| + // Chopped pixels in width/height in case video capture device has odd |
| + // numbers for width/height. |
| + int chopped_width_; |
| + int chopped_height_; |
| + |
| + // Tracks the current frame format. |
| + media::VideoCaptureCapability frame_info_; |
| +}; |
| + |
| VideoCaptureController::VideoCaptureController() |
| - : chopped_width_(0), |
| - chopped_height_(0), |
| - frame_info_available_(false), |
| - state_(VIDEO_CAPTURE_STATE_STARTED) { |
| + : state_(VIDEO_CAPTURE_STATE_STARTED), |
|
wjia(left Chromium)
2013/09/18 01:29:02
|state_| is not really needed any more since there
|
| + weak_ptr_factory_(this) { |
| memset(¤t_params_, 0, sizeof(current_params_)); |
| } |
| +VideoCaptureController::VideoCaptureDeviceClient::VideoCaptureDeviceClient( |
| + const base::WeakPtr<VideoCaptureController>& controller) |
| + : controller_(controller), |
| + chopped_width_(0), |
| + chopped_height_(0) {} |
| + |
| +VideoCaptureController::VideoCaptureDeviceClient::~VideoCaptureDeviceClient() {} |
| + |
| +base::WeakPtr<VideoCaptureController> VideoCaptureController::GetWeakPtr() { |
| + return weak_ptr_factory_.GetWeakPtr(); |
| +} |
| + |
| +scoped_ptr<media::VideoCaptureDevice::EventHandler> |
| +VideoCaptureController::NewDeviceClient() { |
| + scoped_ptr<media::VideoCaptureDevice::EventHandler> result( |
| + new VideoCaptureDeviceClient(this->GetWeakPtr())); |
| + return result.Pass(); |
| +} |
| + |
| void VideoCaptureController::AddClient( |
| const VideoCaptureControllerID& id, |
| VideoCaptureControllerEventHandler* event_handler, |
| @@ -129,7 +194,7 @@ void VideoCaptureController::AddClient( |
| // If we already have gotten frame_info from the device, repeat it to the new |
| // client. |
| if (state_ == VIDEO_CAPTURE_STATE_STARTED) { |
| - if (frame_info_available_) { |
| + if (frame_info_.IsValid()) { |
| SendFrameInfoAndBuffers(client); |
| } |
| controller_clients_.push_back(client); |
| @@ -165,8 +230,7 @@ int VideoCaptureController::RemoveClient( |
| return session_id; |
| } |
| -void VideoCaptureController::StopSession( |
| - int session_id) { |
| +void VideoCaptureController::StopSession(int session_id) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| DVLOG(1) << "VideoCaptureController::StopSession, id " << session_id; |
| @@ -198,20 +262,15 @@ void VideoCaptureController::ReturnBuffer( |
| buffer_pool_->RelinquishConsumerHold(buffer_id, 1); |
| } |
| -scoped_refptr<media::VideoFrame> VideoCaptureController::ReserveOutputBuffer() { |
| - base::AutoLock lock(buffer_pool_lock_); |
| - if (!buffer_pool_.get()) |
| - return NULL; |
| +scoped_refptr<media::VideoFrame> |
| +VideoCaptureController::VideoCaptureDeviceClient::ReserveOutputBuffer() { |
| return buffer_pool_->ReserveI420VideoFrame(gfx::Size(frame_info_.width, |
| frame_info_.height), |
| 0); |
| } |
| -// Implements VideoCaptureDevice::EventHandler. |
| -// OnIncomingCapturedFrame is called the thread running the capture device. |
| -// I.e.- DirectShow thread on windows and v4l2_thread on Linux. |
| #if !defined(OS_IOS) && !defined(OS_ANDROID) |
| -void VideoCaptureController::OnIncomingCapturedFrame( |
| +void VideoCaptureController::VideoCaptureDeviceClient::OnIncomingCapturedFrame( |
| const uint8* data, |
| int length, |
| base::Time timestamp, |
| @@ -220,14 +279,10 @@ void VideoCaptureController::OnIncomingCapturedFrame( |
| bool flip_horiz) { |
| TRACE_EVENT0("video", "VideoCaptureController::OnIncomingCapturedFrame"); |
| - scoped_refptr<media::VideoFrame> dst; |
| - { |
| - base::AutoLock lock(buffer_pool_lock_); |
| - if (!buffer_pool_.get()) |
| - return; |
| - dst = buffer_pool_->ReserveI420VideoFrame( |
| - gfx::Size(frame_info_.width, frame_info_.height), rotation); |
| - } |
| + if (!buffer_pool_.get()) |
| + return; |
| + scoped_refptr<media::VideoFrame> dst = buffer_pool_->ReserveI420VideoFrame( |
| + gfx::Size(frame_info_.width, frame_info_.height), rotation); |
| if (!dst.get()) |
| return; |
| @@ -336,12 +391,12 @@ void VideoCaptureController::OnIncomingCapturedFrame( |
| BrowserThread::IO, |
| FROM_HERE, |
| base::Bind(&VideoCaptureController::DoIncomingCapturedFrameOnIOThread, |
| - this, |
| + controller_, |
| dst, |
| timestamp)); |
| } |
| #else |
| -void VideoCaptureController::OnIncomingCapturedFrame( |
| +void VideoCaptureController::VideoCaptureDeviceClient::OnIncomingCapturedFrame( |
| const uint8* data, |
| int length, |
| base::Time timestamp, |
| @@ -354,15 +409,12 @@ void VideoCaptureController::OnIncomingCapturedFrame( |
| TRACE_EVENT0("video", "VideoCaptureController::OnIncomingCapturedFrame"); |
| - scoped_refptr<media::VideoFrame> dst; |
| - { |
| - base::AutoLock lock(buffer_pool_lock_); |
| - if (!buffer_pool_.get()) |
| - return; |
| - dst = buffer_pool_->ReserveI420VideoFrame(gfx::Size(frame_info_.width, |
| - frame_info_.height), |
| - rotation); |
| - } |
| + if (!buffer_pool_) |
| + return; |
| + scoped_refptr<media::VideoFrame> dst = |
| + buffer_pool_->ReserveI420VideoFrame(gfx::Size(frame_info_.width, |
| + frame_info_.height), |
| + rotation); |
| if (!dst.get()) |
| return; |
| @@ -426,39 +478,36 @@ void VideoCaptureController::OnIncomingCapturedFrame( |
| BrowserThread::PostTask(BrowserThread::IO, |
| FROM_HERE, |
| base::Bind(&VideoCaptureController::DoIncomingCapturedFrameOnIOThread, |
| - this, dst, timestamp)); |
| + controller_, dst, timestamp)); |
| } |
| #endif // #if !defined(OS_IOS) && !defined(OS_ANDROID) |
| -// OnIncomingCapturedVideoFrame is called the thread running the capture device. |
| -void VideoCaptureController::OnIncomingCapturedVideoFrame( |
| +void |
| +VideoCaptureController::VideoCaptureDeviceClient::OnIncomingCapturedVideoFrame( |
| const scoped_refptr<media::VideoFrame>& frame, |
| base::Time timestamp) { |
| - scoped_refptr<media::VideoFrame> target; |
| - { |
| - base::AutoLock lock(buffer_pool_lock_); |
| - |
| - if (!buffer_pool_.get()) |
| - return; |
| - |
| - // If this is a frame that belongs to the buffer pool, we can forward it |
| - // directly to the IO thread and be done. |
| - if (buffer_pool_->RecognizeReservedBuffer( |
| - frame->shared_memory_handle()) >= 0) { |
| - BrowserThread::PostTask(BrowserThread::IO, |
| - FROM_HERE, |
| - base::Bind(&VideoCaptureController::DoIncomingCapturedFrameOnIOThread, |
| - this, frame, timestamp)); |
| - return; |
| - } |
| - // Otherwise, this is a frame that belongs to the caller, and we must copy |
| - // it to a frame from the buffer pool. |
| - target = buffer_pool_->ReserveI420VideoFrame(gfx::Size(frame_info_.width, |
| - frame_info_.height), |
| - 0); |
| + if (!buffer_pool_) |
| + return; |
| + |
| + // If this is a frame that belongs to the buffer pool, we can forward it |
| + // directly to the IO thread and be done. |
| + if (buffer_pool_->RecognizeReservedBuffer( |
| + frame->shared_memory_handle()) >= 0) { |
| + BrowserThread::PostTask(BrowserThread::IO, |
| + FROM_HERE, |
| + base::Bind(&VideoCaptureController::DoIncomingCapturedFrameOnIOThread, |
| + controller_, frame, timestamp)); |
| + return; |
| } |
| + // Otherwise, this is a frame that belongs to the caller, and we must copy |
| + // it to a frame from the buffer pool. |
| + scoped_refptr<media::VideoFrame> target = |
| + buffer_pool_->ReserveI420VideoFrame(gfx::Size(frame_info_.width, |
| + frame_info_.height), |
| + 0); |
| + |
| if (!target.get()) |
| return; |
| @@ -545,18 +594,18 @@ void VideoCaptureController::OnIncomingCapturedVideoFrame( |
| BrowserThread::PostTask(BrowserThread::IO, |
| FROM_HERE, |
| base::Bind(&VideoCaptureController::DoIncomingCapturedFrameOnIOThread, |
| - this, target, timestamp)); |
| + controller_, target, timestamp)); |
| } |
| -void VideoCaptureController::OnError() { |
| +void VideoCaptureController::VideoCaptureDeviceClient::OnError() { |
| BrowserThread::PostTask(BrowserThread::IO, |
| FROM_HERE, |
| - base::Bind(&VideoCaptureController::DoErrorOnIOThread, this)); |
| + base::Bind(&VideoCaptureController::DoErrorOnIOThread, controller_)); |
| } |
| -void VideoCaptureController::OnFrameInfo( |
| +void VideoCaptureController::VideoCaptureDeviceClient::OnFrameInfo( |
| const media::VideoCaptureCapability& info) { |
| - frame_info_= info; |
| + frame_info_ = info; |
| // Handle cases when |info| has odd numbers for width/height. |
| if (info.width & 1) { |
| --frame_info_.width; |
| @@ -570,17 +619,38 @@ void VideoCaptureController::OnFrameInfo( |
| } else { |
| chopped_height_ = 0; |
| } |
| + |
| + DCHECK(!buffer_pool_.get()); |
| + |
| + // TODO(nick): Give BufferPool the same lifetime as the controller, have it |
| + // support frame size changes, and stop checking it for NULL everywhere. |
| + // http://crbug.com/266082 |
| + buffer_pool_ = new VideoCaptureBufferPool( |
| + media::VideoFrame::AllocationSize( |
| + media::VideoFrame::I420, |
| + gfx::Size(frame_info_.width, frame_info_.height)), |
| + kNoOfBuffers); |
| + |
| + // Check whether all buffers were created successfully. |
| + if (!buffer_pool_->Allocate()) { |
| + // Transition to the error state. |
| + buffer_pool_ = NULL; |
| + OnError(); |
| + return; |
| + } |
| + |
| BrowserThread::PostTask(BrowserThread::IO, |
| FROM_HERE, |
| - base::Bind(&VideoCaptureController::DoFrameInfoOnIOThread, this)); |
| + base::Bind(&VideoCaptureController::DoFrameInfoOnIOThread, controller_, |
| + frame_info_, buffer_pool_)); |
| } |
| -void VideoCaptureController::OnFrameInfoChanged( |
| +void VideoCaptureController::VideoCaptureDeviceClient::OnFrameInfoChanged( |
| const media::VideoCaptureCapability& info) { |
| BrowserThread::PostTask(BrowserThread::IO, |
| FROM_HERE, |
| base::Bind(&VideoCaptureController::DoFrameInfoChangedOnIOThread, |
| - this, info)); |
| + controller_, info)); |
| } |
| VideoCaptureController::~VideoCaptureController() { |
| @@ -621,7 +691,9 @@ void VideoCaptureController::DoIncomingCapturedFrameOnIOThread( |
| buffer_pool_->HoldForConsumers(buffer_id, count); |
| } |
| -void VideoCaptureController::DoFrameInfoOnIOThread() { |
| +void VideoCaptureController::DoFrameInfoOnIOThread( |
| + const media::VideoCaptureCapability& frame_info, |
| + const scoped_refptr<VideoCaptureBufferPool>& buffer_pool) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| DCHECK(!buffer_pool_.get()) << "Frame info should happen only once."; |
| @@ -629,24 +701,8 @@ void VideoCaptureController::DoFrameInfoOnIOThread() { |
| if (state_ != VIDEO_CAPTURE_STATE_STARTED) |
| return; |
| - scoped_refptr<VideoCaptureBufferPool> buffer_pool = |
| - new VideoCaptureBufferPool( |
| - media::VideoFrame::AllocationSize( |
| - media::VideoFrame::I420, |
| - gfx::Size(frame_info_.width, frame_info_.height)), |
| - kNoOfBuffers); |
| - |
| - // Check whether all buffers were created successfully. |
| - if (!buffer_pool->Allocate()) { |
| - DoErrorOnIOThread(); |
| - return; |
| - } |
| - |
| - { |
| - base::AutoLock lock(buffer_pool_lock_); |
| - buffer_pool_ = buffer_pool; |
| - } |
| - frame_info_available_ = true; |
| + frame_info_ = frame_info; |
| + buffer_pool_ = buffer_pool; |
| for (ControllerClients::iterator client_it = controller_clients_.begin(); |
| client_it != controller_clients_.end(); ++client_it) { |
| @@ -690,7 +746,7 @@ void VideoCaptureController::DoErrorOnIOThread() { |
| void VideoCaptureController::SendFrameInfoAndBuffers(ControllerClient* client) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| - DCHECK(frame_info_available_); |
| + DCHECK(frame_info_.IsValid()); |
| client->event_handler->OnFrameInfo(client->controller_id, |
| frame_info_); |
| for (int buffer_id = 0; buffer_id < buffer_pool_->count(); ++buffer_id) { |