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/common/child_process.h"
10 #include "content/common/media/video_capture_messages.h"
14 struct VideoCaptureImpl::DIBBuffer
{
17 base::SharedMemory
* d
,
18 media::VideoCapture::VideoFrameBuffer
* ptr
)
25 scoped_ptr
<base::SharedMemory
> dib
;
26 scoped_refptr
<media::VideoCapture::VideoFrameBuffer
> mapped_memory
;
28 // Number of clients which hold this DIB.
32 bool VideoCaptureImpl::CaptureStarted() {
33 return state_
== VIDEO_CAPTURE_STATE_STARTED
;
36 int VideoCaptureImpl::CaptureWidth() {
37 return current_params_
.width
;
40 int VideoCaptureImpl::CaptureHeight() {
41 return current_params_
.height
;
44 int VideoCaptureImpl::CaptureFrameRate() {
45 return current_params_
.frame_per_second
;
48 VideoCaptureImpl::VideoCaptureImpl(
49 const media::VideoCaptureSessionId id
,
50 base::MessageLoopProxy
* capture_message_loop_proxy
,
51 VideoCaptureMessageFilter
* filter
)
53 message_filter_(filter
),
54 capture_message_loop_proxy_(capture_message_loop_proxy
),
55 io_message_loop_proxy_(ChildProcess::current()->io_message_loop_proxy()),
57 video_type_(media::VideoCaptureCapability::kI420
),
58 device_info_available_(false),
60 state_(VIDEO_CAPTURE_STATE_STOPPED
) {
62 memset(¤t_params_
, 0, sizeof(current_params_
));
63 memset(&device_info_
, 0, sizeof(device_info_
));
64 current_params_
.session_id
= id
;
67 VideoCaptureImpl::~VideoCaptureImpl() {
68 STLDeleteValues(&cached_dibs_
);
71 void VideoCaptureImpl::Init() {
72 if (!io_message_loop_proxy_
->BelongsToCurrentThread()) {
73 io_message_loop_proxy_
->PostTask(FROM_HERE
,
74 base::Bind(&VideoCaptureImpl::AddDelegateOnIOThread
,
75 base::Unretained(this)));
77 AddDelegateOnIOThread();
81 void VideoCaptureImpl::DeInit(base::Closure task
) {
82 capture_message_loop_proxy_
->PostTask(FROM_HERE
,
83 base::Bind(&VideoCaptureImpl::DoDeInitOnCaptureThread
,
84 base::Unretained(this), task
));
87 void VideoCaptureImpl::StartCapture(
88 media::VideoCapture::EventHandler
* handler
,
89 const media::VideoCaptureCapability
& capability
) {
90 DCHECK_EQ(capability
.color
, media::VideoCaptureCapability::kI420
);
92 capture_message_loop_proxy_
->PostTask(FROM_HERE
,
93 base::Bind(&VideoCaptureImpl::DoStartCaptureOnCaptureThread
,
94 base::Unretained(this), handler
, capability
));
97 void VideoCaptureImpl::StopCapture(media::VideoCapture::EventHandler
* handler
) {
98 capture_message_loop_proxy_
->PostTask(FROM_HERE
,
99 base::Bind(&VideoCaptureImpl::DoStopCaptureOnCaptureThread
,
100 base::Unretained(this), handler
));
103 void VideoCaptureImpl::FeedBuffer(scoped_refptr
<VideoFrameBuffer
> buffer
) {
104 capture_message_loop_proxy_
->PostTask(FROM_HERE
,
105 base::Bind(&VideoCaptureImpl::DoFeedBufferOnCaptureThread
,
106 base::Unretained(this), buffer
));
109 void VideoCaptureImpl::OnBufferCreated(
110 base::SharedMemoryHandle handle
,
111 int length
, int buffer_id
) {
112 capture_message_loop_proxy_
->PostTask(FROM_HERE
,
113 base::Bind(&VideoCaptureImpl::DoBufferCreatedOnCaptureThread
,
114 base::Unretained(this), handle
, length
, buffer_id
));
117 void VideoCaptureImpl::OnBufferReceived(int buffer_id
, base::Time timestamp
) {
118 capture_message_loop_proxy_
->PostTask(FROM_HERE
,
119 base::Bind(&VideoCaptureImpl::DoBufferReceivedOnCaptureThread
,
120 base::Unretained(this), buffer_id
, timestamp
));
123 void VideoCaptureImpl::OnStateChanged(VideoCaptureState state
) {
124 capture_message_loop_proxy_
->PostTask(FROM_HERE
,
125 base::Bind(&VideoCaptureImpl::DoStateChangedOnCaptureThread
,
126 base::Unretained(this), state
));
129 void VideoCaptureImpl::OnDeviceInfoReceived(
130 const media::VideoCaptureParams
& device_info
) {
131 capture_message_loop_proxy_
->PostTask(FROM_HERE
,
132 base::Bind(&VideoCaptureImpl::DoDeviceInfoReceivedOnCaptureThread
,
133 base::Unretained(this), device_info
));
136 void VideoCaptureImpl::OnDelegateAdded(int32 device_id
) {
137 capture_message_loop_proxy_
->PostTask(FROM_HERE
,
138 base::Bind(&VideoCaptureImpl::DoDelegateAddedOnCaptureThread
,
139 base::Unretained(this), device_id
));
142 void VideoCaptureImpl::SuspendCapture(bool suspend
) {
143 capture_message_loop_proxy_
->PostTask(FROM_HERE
,
144 base::Bind(&VideoCaptureImpl::DoSuspendCaptureOnCaptureThread
,
145 base::Unretained(this), suspend
));
148 void VideoCaptureImpl::DoDeInitOnCaptureThread(base::Closure task
) {
149 if (state_
== VIDEO_CAPTURE_STATE_STARTED
)
150 Send(new VideoCaptureHostMsg_Stop(device_id_
));
152 io_message_loop_proxy_
->PostTask(FROM_HERE
,
153 base::Bind(&VideoCaptureImpl::RemoveDelegateOnIOThread
,
154 base::Unretained(this), task
));
157 void VideoCaptureImpl::DoStartCaptureOnCaptureThread(
158 media::VideoCapture::EventHandler
* handler
,
159 const media::VideoCaptureCapability
& capability
) {
160 DCHECK(capture_message_loop_proxy_
->BelongsToCurrentThread());
162 if (state_
== VIDEO_CAPTURE_STATE_ERROR
) {
163 handler
->OnError(this, 1);
164 handler
->OnRemoved(this);
165 } else if ((clients_pending_on_filter_
.find(handler
) !=
166 clients_pending_on_filter_
.end()) ||
167 (clients_pending_on_restart_
.find(handler
) !=
168 clients_pending_on_restart_
.end()) ||
169 clients_
.find(handler
) != clients_
.end() ) {
170 // This client has started.
171 } else if (!device_id_
) {
172 clients_pending_on_filter_
[handler
] = capability
;
174 handler
->OnStarted(this);
175 if (state_
== VIDEO_CAPTURE_STATE_STARTED
) {
176 // TODO(wjia): Temporarily disable restarting till client supports
179 if (capability
.width
> current_params_
.width
||
180 capability
.height
> current_params_
.height
) {
182 DVLOG(1) << "StartCapture: Got client with higher resolution ("
183 << capability
.width
<< ", " << capability
.height
<< ") "
184 << "after started, try to restart.";
185 clients_pending_on_restart_
[handler
] = capability
;
189 if (device_info_available_
) {
190 handler
->OnDeviceInfoReceived(this, device_info_
);
193 clients_
[handler
] = capability
;
195 } else if (state_
== VIDEO_CAPTURE_STATE_STOPPING
) {
196 clients_pending_on_restart_
[handler
] = capability
;
197 DVLOG(1) << "StartCapture: Got new resolution ("
198 << capability
.width
<< ", " << capability
.height
<< ") "
199 << ", during stopping.";
201 clients_
[handler
] = capability
;
202 DCHECK_EQ(1ul, clients_
.size());
203 video_type_
= capability
.color
;
204 current_params_
.width
= capability
.width
;
205 current_params_
.height
= capability
.height
;
206 current_params_
.frame_per_second
= capability
.frame_rate
;
207 DVLOG(1) << "StartCapture: starting with first resolution ("
208 << current_params_
.width
<< "," << current_params_
.height
<< ")";
210 StartCaptureInternal();
215 void VideoCaptureImpl::DoStopCaptureOnCaptureThread(
216 media::VideoCapture::EventHandler
* handler
) {
217 DCHECK(capture_message_loop_proxy_
->BelongsToCurrentThread());
219 // A handler can be in only one client list.
220 // If this handler is in any client list, we can just remove it from
221 // that client list and don't have to run the other following RemoveClient().
222 RemoveClient(handler
, &clients_pending_on_filter_
) ||
223 RemoveClient(handler
, &clients_pending_on_restart_
) ||
224 RemoveClient(handler
, &clients_
);
226 if (clients_
.empty()) {
227 DVLOG(1) << "StopCapture: No more client, stopping ...";
232 void VideoCaptureImpl::DoFeedBufferOnCaptureThread(
233 scoped_refptr
<VideoFrameBuffer
> buffer
) {
234 DCHECK(capture_message_loop_proxy_
->BelongsToCurrentThread());
236 CachedDIB::iterator it
;
237 for (it
= cached_dibs_
.begin(); it
!= cached_dibs_
.end(); ++it
) {
238 if (buffer
== it
->second
->mapped_memory
)
242 if (it
!= cached_dibs_
.end() && it
->second
) {
243 DCHECK_GT(it
->second
->references
, 0);
244 --it
->second
->references
;
245 if (it
->second
->references
== 0) {
246 Send(new VideoCaptureHostMsg_BufferReady(device_id_
, it
->first
));
251 void VideoCaptureImpl::DoBufferCreatedOnCaptureThread(
252 base::SharedMemoryHandle handle
,
253 int length
, int buffer_id
) {
254 DCHECK(capture_message_loop_proxy_
->BelongsToCurrentThread());
256 // In case client calls StopCapture before the arrival of created buffer,
257 // just close this buffer and return.
258 if (state_
!= VIDEO_CAPTURE_STATE_STARTED
) {
259 base::SharedMemory::CloseHandle(handle
);
263 DCHECK(device_info_available_
);
265 media::VideoCapture::VideoFrameBuffer
* buffer
;
266 DCHECK(cached_dibs_
.find(buffer_id
) == cached_dibs_
.end());
268 base::SharedMemory
* dib
= new base::SharedMemory(handle
, false);
270 buffer
= new VideoFrameBuffer();
271 buffer
->memory_pointer
= static_cast<uint8
*>(dib
->memory());
272 buffer
->buffer_size
= length
;
273 buffer
->width
= device_info_
.width
;
274 buffer
->height
= device_info_
.height
;
275 buffer
->stride
= device_info_
.width
;
277 DIBBuffer
* dib_buffer
= new DIBBuffer(dib
, buffer
);
278 cached_dibs_
[buffer_id
] = dib_buffer
;
281 void VideoCaptureImpl::DoBufferReceivedOnCaptureThread(
282 int buffer_id
, base::Time timestamp
) {
283 DCHECK(capture_message_loop_proxy_
->BelongsToCurrentThread());
285 if (state_
!= VIDEO_CAPTURE_STATE_STARTED
|| suspended_
) {
286 Send(new VideoCaptureHostMsg_BufferReady(device_id_
, buffer_id
));
290 media::VideoCapture::VideoFrameBuffer
* buffer
;
291 DCHECK(cached_dibs_
.find(buffer_id
) != cached_dibs_
.end());
292 buffer
= cached_dibs_
[buffer_id
]->mapped_memory
;
293 buffer
->timestamp
= timestamp
;
295 for (ClientInfo::iterator it
= clients_
.begin(); it
!= clients_
.end(); ++it
) {
296 it
->first
->OnBufferReady(this, buffer
);
298 cached_dibs_
[buffer_id
]->references
= clients_
.size();
301 void VideoCaptureImpl::DoStateChangedOnCaptureThread(VideoCaptureState state
) {
302 DCHECK(capture_message_loop_proxy_
->BelongsToCurrentThread());
305 case VIDEO_CAPTURE_STATE_STARTED
:
307 case VIDEO_CAPTURE_STATE_STOPPED
:
308 state_
= VIDEO_CAPTURE_STATE_STOPPED
;
309 DVLOG(1) << "OnStateChanged: stopped!, device_id = " << device_id_
;
310 STLDeleteValues(&cached_dibs_
);
311 if (!clients_
.empty() || !clients_pending_on_restart_
.empty())
314 case VIDEO_CAPTURE_STATE_PAUSED
:
315 for (ClientInfo::iterator it
= clients_
.begin();
316 it
!= clients_
.end(); ++it
) {
317 it
->first
->OnPaused(this);
320 case VIDEO_CAPTURE_STATE_ERROR
:
321 DVLOG(1) << "OnStateChanged: error!, device_id = " << device_id_
;
322 for (ClientInfo::iterator it
= clients_
.begin();
323 it
!= clients_
.end(); ++it
) {
324 // TODO(wjia): browser process would send error code.
325 it
->first
->OnError(this, 1);
326 it
->first
->OnRemoved(this);
329 state_
= VIDEO_CAPTURE_STATE_ERROR
;
331 case VIDEO_CAPTURE_STATE_ENDED
:
332 DVLOG(1) << "OnStateChanged: ended!, device_id = " << device_id_
;
333 for (ClientInfo::iterator it
= clients_
.begin();
334 it
!= clients_
.end(); ++it
) {
335 it
->first
->OnRemoved(this);
338 state_
= VIDEO_CAPTURE_STATE_ENDED
;
345 void VideoCaptureImpl::DoDeviceInfoReceivedOnCaptureThread(
346 const media::VideoCaptureParams
& device_info
) {
347 DCHECK(capture_message_loop_proxy_
->BelongsToCurrentThread());
348 DCHECK(!ClientHasDIB());
350 STLDeleteValues(&cached_dibs_
);
352 device_info_
= device_info
;
353 device_info_available_
= true;
354 for (ClientInfo::iterator it
= clients_
.begin(); it
!= clients_
.end(); ++it
) {
355 it
->first
->OnDeviceInfoReceived(this, device_info
);
359 void VideoCaptureImpl::DoDelegateAddedOnCaptureThread(int32 device_id
) {
360 DVLOG(1) << "DoDelegateAdded: device_id " << device_id
;
361 DCHECK(capture_message_loop_proxy_
->BelongsToCurrentThread());
363 device_id_
= device_id
;
364 for (ClientInfo::iterator it
= clients_pending_on_filter_
.begin();
365 it
!= clients_pending_on_filter_
.end(); ) {
366 media::VideoCapture::EventHandler
* handler
= it
->first
;
367 const media::VideoCaptureCapability capability
= it
->second
;
368 clients_pending_on_filter_
.erase(it
++);
369 StartCapture(handler
, capability
);
373 void VideoCaptureImpl::DoSuspendCaptureOnCaptureThread(bool suspend
) {
374 DVLOG(1) << "DoSuspendCapture: suspend " << (suspend
? "yes" : "no");
375 DCHECK(capture_message_loop_proxy_
->BelongsToCurrentThread());
377 suspended_
= suspend
;
380 void VideoCaptureImpl::StopDevice() {
381 DCHECK(capture_message_loop_proxy_
->BelongsToCurrentThread());
383 device_info_available_
= false;
384 if (state_
== VIDEO_CAPTURE_STATE_STARTED
) {
385 state_
= VIDEO_CAPTURE_STATE_STOPPING
;
386 Send(new VideoCaptureHostMsg_Stop(device_id_
));
387 current_params_
.width
= current_params_
.height
= 0;
391 void VideoCaptureImpl::RestartCapture() {
392 DCHECK(capture_message_loop_proxy_
->BelongsToCurrentThread());
393 DCHECK_EQ(state_
, VIDEO_CAPTURE_STATE_STOPPED
);
397 for (ClientInfo::iterator it
= clients_
.begin();
398 it
!= clients_
.end(); ++it
) {
399 width
= std::max(width
, it
->second
.width
);
400 height
= std::max(height
, it
->second
.height
);
402 for (ClientInfo::iterator it
= clients_pending_on_restart_
.begin();
403 it
!= clients_pending_on_restart_
.end(); ) {
404 width
= std::max(width
, it
->second
.width
);
405 height
= std::max(height
, it
->second
.height
);
406 clients_
[it
->first
] = it
->second
;
407 clients_pending_on_restart_
.erase(it
++);
409 current_params_
.width
= width
;
410 current_params_
.height
= height
;
411 DVLOG(1) << "RestartCapture, " << current_params_
.width
<< ", "
412 << current_params_
.height
;
413 StartCaptureInternal();
416 void VideoCaptureImpl::StartCaptureInternal() {
417 DCHECK(capture_message_loop_proxy_
->BelongsToCurrentThread());
420 Send(new VideoCaptureHostMsg_Start(device_id_
, current_params_
));
421 state_
= VIDEO_CAPTURE_STATE_STARTED
;
424 void VideoCaptureImpl::AddDelegateOnIOThread() {
425 DCHECK(io_message_loop_proxy_
->BelongsToCurrentThread());
426 message_filter_
->AddDelegate(this);
429 void VideoCaptureImpl::RemoveDelegateOnIOThread(base::Closure task
) {
430 DCHECK(io_message_loop_proxy_
->BelongsToCurrentThread());
431 message_filter_
->RemoveDelegate(this);
432 capture_message_loop_proxy_
->PostTask(FROM_HERE
, task
);
435 void VideoCaptureImpl::Send(IPC::Message
* message
) {
436 io_message_loop_proxy_
->PostTask(FROM_HERE
,
437 base::Bind(base::IgnoreResult(&VideoCaptureMessageFilter::Send
),
438 message_filter_
.get(), message
));
441 bool VideoCaptureImpl::ClientHasDIB() const {
442 DCHECK(capture_message_loop_proxy_
->BelongsToCurrentThread());
443 for (CachedDIB::const_iterator it
= cached_dibs_
.begin();
444 it
!= cached_dibs_
.end(); ++it
) {
445 if (it
->second
->references
> 0)
451 bool VideoCaptureImpl::RemoveClient(
452 media::VideoCapture::EventHandler
* handler
,
453 ClientInfo
* clients
) {
454 DCHECK(capture_message_loop_proxy_
->BelongsToCurrentThread());
457 ClientInfo::iterator it
= clients
->find(handler
);
458 if (it
!= clients
->end()) {
459 handler
->OnStopped(this);
460 handler
->OnRemoved(this);
467 } // namespace content