1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/renderer/media/video_capture_impl.h"
8 #include "base/stl_util.h"
9 #include "content/child/child_process.h"
10 #include "content/common/media/video_capture_messages.h"
11 #include "media/base/bind_to_loop.h"
12 #include "media/base/limits.h"
16 class VideoCaptureImpl::ClientBuffer
17 : public base::RefCountedThreadSafe<ClientBuffer> {
19 ClientBuffer(scoped_ptr<base::SharedMemory> buffer,
21 : buffer(buffer.Pass()),
22 buffer_size(buffer_size) {}
23 const scoped_ptr<base::SharedMemory> buffer;
24 const size_t buffer_size;
27 friend class base::RefCountedThreadSafe<ClientBuffer>;
29 virtual ~ClientBuffer() {}
31 DISALLOW_COPY_AND_ASSIGN(ClientBuffer);
34 bool VideoCaptureImpl::CaptureStarted() {
35 return state_ == VIDEO_CAPTURE_STATE_STARTED;
38 int VideoCaptureImpl::CaptureFrameRate() {
39 return last_frame_format_.frame_rate;
42 VideoCaptureImpl::VideoCaptureImpl(
43 const media::VideoCaptureSessionId session_id,
44 base::MessageLoopProxy* capture_message_loop_proxy,
45 VideoCaptureMessageFilter* filter)
47 message_filter_(filter),
48 capture_message_loop_proxy_(capture_message_loop_proxy),
49 io_message_loop_proxy_(ChildProcess::current()->io_message_loop_proxy()),
51 session_id_(session_id),
52 client_buffer_weak_this_factory_(this),
54 state_(VIDEO_CAPTURE_STATE_STOPPED) {
58 VideoCaptureImpl::~VideoCaptureImpl() {}
60 void VideoCaptureImpl::Init() {
61 if (!io_message_loop_proxy_->BelongsToCurrentThread()) {
62 io_message_loop_proxy_->PostTask(FROM_HERE,
63 base::Bind(&VideoCaptureImpl::AddDelegateOnIOThread,
64 base::Unretained(this)));
66 AddDelegateOnIOThread();
70 void VideoCaptureImpl::DeInit(base::Closure task) {
71 capture_message_loop_proxy_->PostTask(FROM_HERE,
72 base::Bind(&VideoCaptureImpl::DoDeInitOnCaptureThread,
73 base::Unretained(this), task));
76 void VideoCaptureImpl::StartCapture(
77 media::VideoCapture::EventHandler* handler,
78 const media::VideoCaptureParams& params) {
79 capture_message_loop_proxy_->PostTask(FROM_HERE,
80 base::Bind(&VideoCaptureImpl::DoStartCaptureOnCaptureThread,
81 base::Unretained(this), handler, params));
84 void VideoCaptureImpl::StopCapture(media::VideoCapture::EventHandler* handler) {
85 capture_message_loop_proxy_->PostTask(FROM_HERE,
86 base::Bind(&VideoCaptureImpl::DoStopCaptureOnCaptureThread,
87 base::Unretained(this), handler));
90 void VideoCaptureImpl::OnBufferCreated(
91 base::SharedMemoryHandle handle,
92 int length, int buffer_id) {
93 capture_message_loop_proxy_->PostTask(FROM_HERE,
94 base::Bind(&VideoCaptureImpl::DoBufferCreatedOnCaptureThread,
95 base::Unretained(this), handle, length, buffer_id));
98 void VideoCaptureImpl::OnBufferDestroyed(int buffer_id) {
99 capture_message_loop_proxy_->PostTask(FROM_HERE,
100 base::Bind(&VideoCaptureImpl::DoBufferDestroyedOnCaptureThread,
101 base::Unretained(this), buffer_id));
104 void VideoCaptureImpl::OnBufferReceived(
106 base::Time timestamp,
107 const media::VideoCaptureFormat& format) {
108 capture_message_loop_proxy_->PostTask(FROM_HERE,
109 base::Bind(&VideoCaptureImpl::DoBufferReceivedOnCaptureThread,
110 base::Unretained(this), buffer_id, timestamp, format));
113 void VideoCaptureImpl::OnStateChanged(VideoCaptureState state) {
114 capture_message_loop_proxy_->PostTask(FROM_HERE,
115 base::Bind(&VideoCaptureImpl::DoStateChangedOnCaptureThread,
116 base::Unretained(this), state));
119 void VideoCaptureImpl::OnDelegateAdded(int32 device_id) {
120 capture_message_loop_proxy_->PostTask(FROM_HERE,
121 base::Bind(&VideoCaptureImpl::DoDelegateAddedOnCaptureThread,
122 base::Unretained(this), device_id));
125 void VideoCaptureImpl::SuspendCapture(bool suspend) {
126 capture_message_loop_proxy_->PostTask(FROM_HERE,
127 base::Bind(&VideoCaptureImpl::DoSuspendCaptureOnCaptureThread,
128 base::Unretained(this), suspend));
131 void VideoCaptureImpl::DoDeInitOnCaptureThread(base::Closure task) {
132 if (state_ == VIDEO_CAPTURE_STATE_STARTED)
133 Send(new VideoCaptureHostMsg_Stop(device_id_));
135 io_message_loop_proxy_->PostTask(FROM_HERE,
136 base::Bind(&VideoCaptureImpl::RemoveDelegateOnIOThread,
137 base::Unretained(this), task));
140 void VideoCaptureImpl::DoStartCaptureOnCaptureThread(
141 media::VideoCapture::EventHandler* handler,
142 const media::VideoCaptureParams& params) {
143 DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread());
145 if (state_ == VIDEO_CAPTURE_STATE_ERROR) {
146 handler->OnError(this, 1);
147 handler->OnRemoved(this);
148 } else if ((clients_pending_on_filter_.find(handler) !=
149 clients_pending_on_filter_.end()) ||
150 (clients_pending_on_restart_.find(handler) !=
151 clients_pending_on_restart_.end()) ||
152 clients_.find(handler) != clients_.end() ) {
153 // This client has started.
154 } else if (!device_id_) {
155 clients_pending_on_filter_[handler] = params;
157 handler->OnStarted(this);
158 if (state_ == VIDEO_CAPTURE_STATE_STARTED) {
159 clients_[handler] = params;
160 } else if (state_ == VIDEO_CAPTURE_STATE_STOPPING) {
161 clients_pending_on_restart_[handler] = params;
162 DVLOG(1) << "StartCapture: Got new resolution ("
163 << params.requested_format.width << ", "
164 << params.requested_format.height << ") "
165 << ", during stopping.";
167 DCHECK_EQ(params.session_id, 0);
168 clients_[handler] = params;
169 DCHECK_EQ(1ul, clients_.size());
171 params_.session_id = session_id_;
172 if (params_.requested_format.frame_rate >
173 media::limits::kMaxFramesPerSecond) {
174 params_.requested_format.frame_rate =
175 media::limits::kMaxFramesPerSecond;
177 DVLOG(1) << "StartCapture: starting with first resolution ("
178 << params_.requested_format.width << ","
179 << params_.requested_format.height << ")";
181 StartCaptureInternal();
186 void VideoCaptureImpl::DoStopCaptureOnCaptureThread(
187 media::VideoCapture::EventHandler* handler) {
188 DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread());
190 // A handler can be in only one client list.
191 // If this handler is in any client list, we can just remove it from
192 // that client list and don't have to run the other following RemoveClient().
193 RemoveClient(handler, &clients_pending_on_filter_) ||
194 RemoveClient(handler, &clients_pending_on_restart_) ||
195 RemoveClient(handler, &clients_);
197 if (clients_.empty()) {
198 DVLOG(1) << "StopCapture: No more client, stopping ...";
200 client_buffers_.clear();
201 client_buffer_weak_this_factory_.InvalidateWeakPtrs();
205 void VideoCaptureImpl::DoBufferCreatedOnCaptureThread(
206 base::SharedMemoryHandle handle,
207 int length, int buffer_id) {
208 DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread());
210 // In case client calls StopCapture before the arrival of created buffer,
211 // just close this buffer and return.
212 if (state_ != VIDEO_CAPTURE_STATE_STARTED) {
213 base::SharedMemory::CloseHandle(handle);
217 scoped_ptr<base::SharedMemory> shm(new base::SharedMemory(handle, false));
218 if (!shm->Map(length)) {
219 DLOG(ERROR) << "DoBufferCreatedOnCaptureThread: Map() failed.";
224 client_buffers_.insert(std::make_pair(
226 new ClientBuffer(shm.Pass(),
231 void VideoCaptureImpl::DoBufferDestroyedOnCaptureThread(int buffer_id) {
232 DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread());
234 ClientBufferMap::iterator iter = client_buffers_.find(buffer_id);
235 if (iter == client_buffers_.end())
238 DCHECK(!iter->second || iter->second->HasOneRef())
239 << "Instructed to delete buffer we are still using.";
240 client_buffers_.erase(iter);
243 void VideoCaptureImpl::DoBufferReceivedOnCaptureThread(
245 base::Time timestamp,
246 const media::VideoCaptureFormat& format) {
247 DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread());
249 if (state_ != VIDEO_CAPTURE_STATE_STARTED || suspended_) {
250 Send(new VideoCaptureHostMsg_BufferReady(device_id_, buffer_id));
254 last_frame_format_ = format;
255 gfx::Size size(format.width, format.height);
257 ClientBufferMap::iterator iter = client_buffers_.find(buffer_id);
258 DCHECK(iter != client_buffers_.end());
259 scoped_refptr<ClientBuffer> buffer = iter->second;
260 scoped_refptr<media::VideoFrame> frame =
261 media::VideoFrame::WrapExternalSharedMemory(
262 media::VideoFrame::I420,
263 size, gfx::Rect(size), size,
264 reinterpret_cast<uint8*>(buffer->buffer->memory()),
266 buffer->buffer->handle(),
267 // TODO(sheu): convert VideoCaptureMessageFilter::Delegate to use
268 // base::TimeTicks instead of base::Time. http://crbug.com/249215
269 timestamp - base::Time::UnixEpoch(),
271 capture_message_loop_proxy_,
273 &VideoCaptureImpl::DoClientBufferFinishedOnCaptureThread,
274 client_buffer_weak_this_factory_.GetWeakPtr(),
278 for (ClientInfo::iterator it = clients_.begin(); it != clients_.end(); ++it)
279 it->first->OnFrameReady(this, frame);
282 void VideoCaptureImpl::DoClientBufferFinishedOnCaptureThread(
284 const scoped_refptr<ClientBuffer>& buffer) {
285 DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread());
286 Send(new VideoCaptureHostMsg_BufferReady(device_id_, buffer_id));
289 void VideoCaptureImpl::DoStateChangedOnCaptureThread(VideoCaptureState state) {
290 DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread());
293 case VIDEO_CAPTURE_STATE_STARTED:
295 case VIDEO_CAPTURE_STATE_STOPPED:
296 state_ = VIDEO_CAPTURE_STATE_STOPPED;
297 DVLOG(1) << "OnStateChanged: stopped!, device_id = " << device_id_;
298 client_buffers_.clear();
299 client_buffer_weak_this_factory_.InvalidateWeakPtrs();
300 if (!clients_.empty() || !clients_pending_on_restart_.empty())
303 case VIDEO_CAPTURE_STATE_PAUSED:
304 for (ClientInfo::iterator it = clients_.begin();
305 it != clients_.end(); ++it) {
306 it->first->OnPaused(this);
309 case VIDEO_CAPTURE_STATE_ERROR:
310 DVLOG(1) << "OnStateChanged: error!, device_id = " << device_id_;
311 for (ClientInfo::iterator it = clients_.begin();
312 it != clients_.end(); ++it) {
313 // TODO(wjia): browser process would send error code.
314 it->first->OnError(this, 1);
315 it->first->OnRemoved(this);
318 state_ = VIDEO_CAPTURE_STATE_ERROR;
320 case VIDEO_CAPTURE_STATE_ENDED:
321 DVLOG(1) << "OnStateChanged: ended!, device_id = " << device_id_;
322 for (ClientInfo::iterator it = clients_.begin();
323 it != clients_.end(); ++it) {
324 it->first->OnRemoved(this);
327 state_ = VIDEO_CAPTURE_STATE_ENDED;
334 void VideoCaptureImpl::DoDelegateAddedOnCaptureThread(int32 device_id) {
335 DVLOG(1) << "DoDelegateAdded: device_id " << device_id;
336 DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread());
338 device_id_ = device_id;
339 for (ClientInfo::iterator it = clients_pending_on_filter_.begin();
340 it != clients_pending_on_filter_.end(); ) {
341 media::VideoCapture::EventHandler* handler = it->first;
342 const media::VideoCaptureParams params = it->second;
343 clients_pending_on_filter_.erase(it++);
344 StartCapture(handler, params);
348 void VideoCaptureImpl::DoSuspendCaptureOnCaptureThread(bool suspend) {
349 DVLOG(1) << "DoSuspendCapture: suspend " << (suspend ? "yes" : "no");
350 DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread());
352 suspended_ = suspend;
355 void VideoCaptureImpl::StopDevice() {
356 DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread());
358 if (state_ == VIDEO_CAPTURE_STATE_STARTED) {
359 state_ = VIDEO_CAPTURE_STATE_STOPPING;
360 Send(new VideoCaptureHostMsg_Stop(device_id_));
361 params_.requested_format.width = params_.requested_format.height = 0;
365 void VideoCaptureImpl::RestartCapture() {
366 DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread());
367 DCHECK_EQ(state_, VIDEO_CAPTURE_STATE_STOPPED);
371 for (ClientInfo::iterator it = clients_.begin();
372 it != clients_.end(); ++it) {
373 width = std::max(width, it->second.requested_format.width);
374 height = std::max(height, it->second.requested_format.height);
376 for (ClientInfo::iterator it = clients_pending_on_restart_.begin();
377 it != clients_pending_on_restart_.end(); ) {
378 width = std::max(width, it->second.requested_format.width);
379 height = std::max(height, it->second.requested_format.height);
380 clients_[it->first] = it->second;
381 clients_pending_on_restart_.erase(it++);
383 params_.requested_format.width = width;
384 params_.requested_format.height = height;
385 DVLOG(1) << "RestartCapture, " << params_.requested_format.width << ", "
386 << params_.requested_format.height;
387 StartCaptureInternal();
390 void VideoCaptureImpl::StartCaptureInternal() {
391 DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread());
394 Send(new VideoCaptureHostMsg_Start(device_id_, params_));
395 state_ = VIDEO_CAPTURE_STATE_STARTED;
398 void VideoCaptureImpl::AddDelegateOnIOThread() {
399 DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
400 message_filter_->AddDelegate(this);
403 void VideoCaptureImpl::RemoveDelegateOnIOThread(base::Closure task) {
404 DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
405 message_filter_->RemoveDelegate(this);
406 capture_message_loop_proxy_->PostTask(FROM_HERE, task);
409 void VideoCaptureImpl::Send(IPC::Message* message) {
410 io_message_loop_proxy_->PostTask(FROM_HERE,
411 base::Bind(base::IgnoreResult(&VideoCaptureMessageFilter::Send),
412 message_filter_.get(), message));
415 bool VideoCaptureImpl::RemoveClient(
416 media::VideoCapture::EventHandler* handler,
417 ClientInfo* clients) {
418 DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread());
421 ClientInfo::iterator it = clients->find(handler);
422 if (it != clients->end()) {
423 handler->OnStopped(this);
424 handler->OnRemoved(this);
431 } // namespace content