Fix build break
[chromium-blink-merge.git] / content / renderer / media / video_capture_impl.cc
blob7ee2a2e8766ea5fed68658ec93470cc1fb0a057a
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"
7 #include "base/bind.h"
8 #include "base/stl_util.h"
9 #include "content/common/child_process.h"
10 #include "content/common/media/video_capture_messages.h"
12 namespace content {
14 struct VideoCaptureImpl::DIBBuffer {
15 public:
16 DIBBuffer(
17 base::SharedMemory* d,
18 media::VideoCapture::VideoFrameBuffer* ptr)
19 : dib(d),
20 mapped_memory(ptr),
21 references(0) {
23 ~DIBBuffer() {}
25 scoped_ptr<base::SharedMemory> dib;
26 scoped_refptr<media::VideoCapture::VideoFrameBuffer> mapped_memory;
28 // Number of clients which hold this DIB.
29 int references;
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)
52 : VideoCapture(),
53 message_filter_(filter),
54 capture_message_loop_proxy_(capture_message_loop_proxy),
55 io_message_loop_proxy_(ChildProcess::current()->io_message_loop_proxy()),
56 device_id_(0),
57 video_type_(media::VideoCaptureCapability::kI420),
58 device_info_available_(false),
59 suspended_(false),
60 state_(VIDEO_CAPTURE_STATE_STOPPED) {
61 DCHECK(filter);
62 memset(&current_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)));
76 } else {
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;
173 } else {
174 handler->OnStarted(this);
175 if (state_ == VIDEO_CAPTURE_STATE_STARTED) {
176 // TODO(wjia): Temporarily disable restarting till client supports
177 // resampling.
178 #if 0
179 if (capability.width > current_params_.width ||
180 capability.height > current_params_.height) {
181 StopDevice();
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;
186 } else {
187 #endif
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.";
200 } else {
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 ...";
228 StopDevice();
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)
239 break;
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);
260 return;
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);
269 dib->Map(length);
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));
287 return;
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());
304 switch (state) {
305 case VIDEO_CAPTURE_STATE_STARTED:
306 break;
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())
312 RestartCapture();
313 break;
314 case VIDEO_CAPTURE_STATE_PAUSED:
315 for (ClientInfo::iterator it = clients_.begin();
316 it != clients_.end(); ++it) {
317 it->first->OnPaused(this);
319 break;
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);
328 clients_.clear();
329 state_ = VIDEO_CAPTURE_STATE_ERROR;
330 break;
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);
337 clients_.clear();
338 state_ = VIDEO_CAPTURE_STATE_ENDED;
339 break;
340 default:
341 break;
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);
395 int width = 0;
396 int height = 0;
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());
418 DCHECK(device_id_);
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)
446 return true;
448 return false;
451 bool VideoCaptureImpl::RemoveClient(
452 media::VideoCapture::EventHandler* handler,
453 ClientInfo* clients) {
454 DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread());
455 bool found = false;
457 ClientInfo::iterator it = clients->find(handler);
458 if (it != clients->end()) {
459 handler->OnStopped(this);
460 handler->OnRemoved(this);
461 clients->erase(it);
462 found = true;
464 return found;
467 } // namespace content