Index: content/renderer/media/video_capture_impl.cc |
=================================================================== |
--- content/renderer/media/video_capture_impl.cc (revision 0) |
+++ content/renderer/media/video_capture_impl.cc (revision 0) |
@@ -0,0 +1,394 @@ |
+// 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 "content/renderer/media/video_capture_impl.h" |
+ |
+#include "content/common/child_process.h" |
+#include "content/common/video_capture_messages.h" |
+ |
+namespace { |
+ |
+// Timeout setting for StartCapture. |
+enum { kTryTimes = 1000 }; |
jam
2011/05/25 17:21:52
enums are used when you have a list of related con
wjia(left Chromium)
2011/05/25 19:26:23
Done.
|
+enum { kDelayInMillisecond = 1 }; |
+ |
+} // namespace |
+ |
+VideoCaptureImpl::DIBBuffer::DIBBuffer( |
+ TransportDIB* d, media::VideoCapture::VideoFrameBuffer* ptr) |
+ : dib(d), |
+ mapped_memory(ptr) {} |
+ |
+VideoCaptureImpl::DIBBuffer::~DIBBuffer() { |
+ delete dib; |
+} |
+ |
+bool VideoCaptureImpl::CaptureStarted() { |
+ return state_ == kStarted; |
+} |
+ |
+int VideoCaptureImpl::CaptureWidth() { |
+ return width_; |
+} |
+ |
+int VideoCaptureImpl::CaptureHeight() { |
+ return height_; |
+} |
+ |
+int VideoCaptureImpl::CaptureFrameRate() { |
+ return frame_rate_; |
+} |
+ |
+VideoCaptureImpl::VideoCaptureImpl( |
+ const media::VideoCaptureSessionId id, |
+ scoped_refptr<base::MessageLoopProxy> ml_proxy, |
+ VideoCaptureMessageFilter* filter) |
+ : VideoCapture(), |
+ message_filter_(filter), |
+ session_id_(id), |
+ ml_proxy_(ml_proxy), |
+ device_id_(0), |
+ width_(0), |
+ height_(0), |
+ frame_rate_(0), |
+ video_type_(media::VideoFrame::I420), |
+ new_width_(0), |
+ new_height_(0), |
+ state_(kStopped) { |
+ DCHECK(filter); |
+} |
+ |
+VideoCaptureImpl::~VideoCaptureImpl() {} |
+ |
+void VideoCaptureImpl::Init() { |
+ base::MessageLoopProxy* message_loop_proxy = |
+ ChildProcess::current()->io_message_loop_proxy(); |
+ if (!message_loop_proxy) |
+ return; |
jam
2011/05/25 17:21:52
why add this? have you tracked a scenario where t
wjia(left Chromium)
2011/05/25 19:26:23
removed. I added it because I am not sure if io th
|
+ |
+ if (!message_loop_proxy->BelongsToCurrentThread()) { |
+ message_loop_proxy->PostTask(FROM_HERE, |
+ NewRunnableMethod(this, &VideoCaptureImpl::AddDelegateOnIOThread)); |
+ return; |
+ } |
+ |
+ AddDelegateOnIOThread(); |
+} |
+ |
+void VideoCaptureImpl::DeInit(Task* task) { |
+ if (state_ == kStarted) |
+ message_filter_->Send(new VideoCaptureHostMsg_Stop(0, device_id_)); |
+ |
+ base::MessageLoopProxy* message_loop_proxy = |
+ ChildProcess::current()->io_message_loop_proxy(); |
+ if (!message_loop_proxy) |
+ return; |
jam
2011/05/25 17:21:52
ditto
wjia(left Chromium)
2011/05/25 19:26:23
removed.
|
+ |
+ if (!message_loop_proxy->BelongsToCurrentThread()) { |
+ message_loop_proxy->PostTask(FROM_HERE, |
+ NewRunnableMethod(this, &VideoCaptureImpl::RemoveDelegateOnIOThread, |
+ task)); |
+ return; |
+ } |
+ |
+ RemoveDelegateOnIOThread(task); |
+} |
+ |
+void VideoCaptureImpl::StartCapture( |
+ media::VideoCapture::EventHandler* handler, |
+ const VideoCaptureCapability& capability) { |
+ DCHECK_EQ(capability.raw_type, media::VideoFrame::I420); |
+ |
+ if (!ml_proxy_->BelongsToCurrentThread()) { |
+ ml_proxy_->PostTask(FROM_HERE, |
+ NewRunnableMethod(this, &VideoCaptureImpl::StartCapture, handler, |
+ capability)); |
+ return; |
+ } |
+ |
+ PendingClient::iterator it = pending_clients_.find(handler); |
+ if (!device_id_ || !message_filter_->ReadyToSend()) { |
+ if (it != pending_clients_.end() && it->second <= 0) { |
+ if (it->second == 0) { |
+ handler->OnError(this, 2); |
+ } |
+ pending_clients_.erase(it); |
+ } else { |
+ if (it == pending_clients_.end()) |
+ pending_clients_[handler] = kTryTimes; |
+ else |
+ it->second--; |
+ |
+ ml_proxy_->PostDelayedTask(FROM_HERE, |
+ NewRunnableMethod(this, &VideoCaptureImpl::StartCapture, handler, |
+ capability), kDelayInMillisecond); |
+ } |
+ return; |
+ } |
+ |
+ if (it != pending_clients_.end()) |
+ pending_clients_.erase(it); |
+ |
+ if (capability.resolution_fixed && master_clients_.size() && |
+ (capability.width != width_ || capability.height != height_)) { |
+ // Can't have 2 master clients with different resolutions. |
+ handler->OnError(this, 1); |
+ return; |
+ } |
+ |
+ clients_[handler] = capability; |
+ if (capability.resolution_fixed) { |
+ master_clients_.push_back(handler); |
+ if (master_clients_.size() > 1) |
+ return; |
+ } |
+ |
+ if (state_ == kStarted) { |
+ // Take the resolution of master client. |
+ if (capability.resolution_fixed && |
+ (capability.width != width_ || capability.height != height_)) { |
+ new_width_ = capability.width; |
+ new_height_ = capability.height; |
+ DLOG(INFO) << "StartCapture: Got master client with new resolution " |
+ "during started, try to restart."; |
+ StopDevice(); |
+ } |
+ handler->OnStarted(this); |
+ return; |
+ } |
+ |
+ if (state_ == kStopping) { |
+ if (capability.resolution_fixed || !pending_start()) { |
+ new_width_ = capability.width; |
+ new_height_ = capability.height; |
+ DLOG(INFO) << "StartCapture: Got new resolution, already in stopping."; |
+ } |
+ handler->OnStarted(this); |
+ return; |
+ } |
+ |
+ DCHECK_EQ(clients_.size(), 1ul); |
+ video_type_ = capability.raw_type; |
+ new_width_ = 0; |
+ new_height_ = 0; |
+ width_ = capability.width; |
+ height_ = capability.height; |
+ |
+ StartCaptureInternal(); |
+} |
+ |
+void VideoCaptureImpl::StopCapture(media::VideoCapture::EventHandler* handler) { |
+ if (!ml_proxy_->BelongsToCurrentThread()) { |
+ ml_proxy_->PostTask(FROM_HERE, |
+ NewRunnableMethod(this, &VideoCaptureImpl::StopCapture, handler)); |
+ return; |
+ } |
+ |
+ PendingClient::iterator it = pending_clients_.find(handler); |
+ if (it != pending_clients_.end()) { |
+ handler->OnStopped(this); |
+ it->second = -1; |
+ return; |
+ } |
+ |
+ if (clients_.find(handler) == clients_.end()) |
+ return; |
+ |
+ handler->OnStopped(this); |
+ clients_.erase(handler); |
+ master_clients_.remove(handler); |
+ |
+ // Still have at least one master client. |
+ if (master_clients_.size() > 0) |
+ return; |
+ |
+ // TODO(wjia): Is it really needed to handle resolution change for non-master |
+ // clients, except no client case? |
+ if (clients_.size() > 0) { |
+ DLOG(INFO) << "StopCapture: No master client."; |
+ int maxw = 0; |
+ int maxh = 0; |
+ for (ClientInfo::iterator it = clients_.begin(); |
+ it != clients_.end(); it++) { |
+ if (it->second.width > maxw && it->second.height > maxh) { |
+ maxw = it->second.width; |
+ maxh = it->second.height; |
+ } |
+ } |
+ |
+ if (state_ == kStarted) { |
+ // Only handle resolution reduction. |
+ if (maxw < width_ && maxh < height_) { |
+ new_width_ = maxw; |
+ new_height_ = maxh; |
+ DLOG(INFO) << "StopCapture: New smaller resolution, stopping ..."; |
+ StopDevice(); |
+ } |
+ return; |
+ } |
+ |
+ if (state_ == kStopping) { |
+ new_width_ = maxw; |
+ new_height_ = maxh; |
+ DLOG(INFO) << "StopCapture: New resolution, during stopping."; |
+ return; |
+ } |
+ } else { |
+ new_width_ = width_ = 0; |
+ new_height_ = height_ = 0; |
+ DLOG(INFO) << "StopCapture: No more client, stopping ..."; |
+ StopDevice(); |
+ } |
+} |
+ |
+void VideoCaptureImpl::OnBufferReceived(TransportDIB::Handle handle, |
+ base::Time timestamp) { |
+ if (!ml_proxy_->BelongsToCurrentThread()) { |
+ ml_proxy_->PostTask(FROM_HERE, |
+ NewRunnableMethod(this, &VideoCaptureImpl::OnBufferReceived, |
+ handle, timestamp)); |
+ return; |
+ } |
+ |
+ if (state_ != kStarted) { |
+ message_filter_->Send( |
+ new VideoCaptureHostMsg_BufferReady(0, device_id_, handle)); |
+ return; |
+ } |
+ |
+ media::VideoCapture::VideoFrameBuffer* buffer; |
+ CachedDIB::iterator it; |
+ for (it = cached_dibs_.begin(); it != cached_dibs_.end(); it++) { |
+ if ((*it)->dib->handle() == handle) |
+ break; |
+ } |
+ if (it == cached_dibs_.end()) { |
+ TransportDIB* dib = TransportDIB::Map(handle); |
+ buffer = new VideoFrameBuffer(); |
+ buffer->memory_pointer = dib->memory(); |
+ buffer->buffer_size = dib->size(); |
+ buffer->width = width_; |
+ buffer->height = height_; |
+ |
+ DIBBuffer* dib_buffer = new DIBBuffer(dib, buffer); |
+ cached_dibs_.push_back(dib_buffer); |
+ } else { |
+ buffer = (*it)->mapped_memory; |
+ } |
+ |
+ // TODO(wjia): handle buffer sharing with downstream modules. |
+ for (ClientInfo::iterator it = clients_.begin(); it != clients_.end(); it++) { |
+ it->first->OnBufferReady(this, buffer); |
+ } |
+ |
+ message_filter_->Send( |
+ new VideoCaptureHostMsg_BufferReady(0, device_id_, handle)); |
+} |
+ |
+void VideoCaptureImpl::OnStateChanged( |
+ const media::VideoCapture::State& state) { |
+ if (!ml_proxy_->BelongsToCurrentThread()) { |
+ ml_proxy_->PostTask(FROM_HERE, |
+ NewRunnableMethod(this, &VideoCaptureImpl::OnStateChanged, state)); |
+ return; |
+ } |
+ |
+ switch (state) { |
+ case media::VideoCapture::kStarted: |
+ for (ClientInfo::iterator it = clients_.begin(); |
+ it != clients_.end(); it++) { |
+ it->first->OnStarted(this); |
+ } |
+ break; |
+ case media::VideoCapture::kStopped: |
+ state_ = kStopped; |
+ DLOG(INFO) << "OnStateChanged: stopped!, device_id = " << device_id_; |
+ if (pending_start()) |
+ RestartCapture(); |
+ break; |
+ case media::VideoCapture::kPaused: |
+ for (ClientInfo::iterator it = clients_.begin(); |
+ it != clients_.end(); it++) { |
+ it->first->OnPaused(this); |
+ } |
+ break; |
+ case media::VideoCapture::kError: |
+ for (ClientInfo::iterator it = clients_.begin(); |
+ it != clients_.end(); it++) { |
+ // TODO(wjia): browser process would send error code. |
+ it->first->OnError(this, 1); |
+ } |
+ break; |
+ default: |
+ break; |
+ } |
+} |
+ |
+void VideoCaptureImpl::OnDeviceInfoReceived( |
+ const media::VideoCaptureParams& device_info) { |
+ if (!ml_proxy_->BelongsToCurrentThread()) { |
+ ml_proxy_->PostTask(FROM_HERE, |
+ NewRunnableMethod(this, &VideoCaptureImpl::OnDeviceInfoReceived, |
+ device_info)); |
+ return; |
+ } |
+ |
+ for (ClientInfo::iterator it = clients_.begin(); it != clients_.end(); it++) { |
+ it->first->OnDeviceInfoReceived(this, device_info); |
+ } |
+} |
+ |
+void VideoCaptureImpl::StopDevice() { |
+ if (!ml_proxy_->BelongsToCurrentThread()) { |
+ ml_proxy_->PostTask(FROM_HERE, |
+ NewRunnableMethod(this, &VideoCaptureImpl::StopDevice)); |
+ return; |
+ } |
+ |
+ if (state_ == kStarted) { |
+ state_ = kStopping; |
+ message_filter_->Send(new VideoCaptureHostMsg_Stop(0, device_id_)); |
+ width_ = height_ = 0; |
+ } |
+} |
+ |
+void VideoCaptureImpl::RestartCapture() { |
+ DCHECK(ml_proxy_->BelongsToCurrentThread()); |
+ DCHECK_EQ(state_, kStopped); |
+ |
+ width_ = new_width_; |
+ height_ = new_height_; |
+ new_width_ = 0; |
+ new_height_ = 0; |
+ |
+ DLOG(INFO) << "RestartCapture, " << width_ << ", " << height_; |
+ StartCaptureInternal(); |
+} |
+ |
+void VideoCaptureImpl::StartCaptureInternal() { |
+ DCHECK(ml_proxy_->BelongsToCurrentThread()); |
+ DCHECK(device_id_); |
+ DCHECK(message_filter_->ReadyToSend()); |
+ |
+ media::VideoCaptureParams params; |
+ params.width = width_; |
+ params.height = height_; |
+ params.session_id = session_id_; |
+ |
+ message_filter_->Send(new VideoCaptureHostMsg_Start(0, device_id_, params)); |
+ state_ = kStarted; |
+ for (ClientInfo::iterator it = clients_.begin(); it != clients_.end(); it++) { |
+ it->first->OnStarted(this); |
+ } |
+} |
+ |
+void VideoCaptureImpl::AddDelegateOnIOThread() { |
+ // |device_id_| is modified only on io thread. |
+ device_id_ = message_filter_->AddDelegate(this); |
+} |
+ |
+void VideoCaptureImpl::RemoveDelegateOnIOThread(Task* task) { |
+ message_filter_->RemoveDelegate(this); |
+ media::AutoTaskRunner auto_runner(task); |
+} |
Property changes on: content/renderer/media/video_capture_impl.cc |
___________________________________________________________________ |
Added: svn:eol-style |
+ LF |