OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/renderer/media/video_capture_impl.h" | 5 #include "content/renderer/media/video_capture_impl.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/stl_util.h" | 8 #include "base/stl_util.h" |
9 #include "content/child/child_process.h" | 9 #include "content/child/child_process.h" |
10 #include "content/common/media/video_capture_messages.h" | 10 #include "content/common/media/video_capture_messages.h" |
| 11 #include "media/base/bind_to_loop.h" |
11 #include "media/base/limits.h" | 12 #include "media/base/limits.h" |
12 | 13 |
13 namespace content { | 14 namespace content { |
14 | 15 |
15 struct VideoCaptureImpl::DIBBuffer { | 16 class VideoCaptureImpl::ClientBuffer : public base::RefCounted<ClientBuffer> { |
16 public: | 17 public: |
17 DIBBuffer( | 18 ClientBuffer(scoped_ptr<base::SharedMemory> buffer, |
18 base::SharedMemory* d, | 19 size_t buffer_size, |
19 media::VideoCapture::VideoFrameBuffer* ptr) | 20 int frame_width, |
20 : dib(d), | 21 int frame_height, |
21 mapped_memory(ptr), | 22 int frame_stride) |
22 references(0) { | 23 : buffer(buffer.Pass()), |
23 } | 24 buffer_size(buffer_size), |
24 ~DIBBuffer() {} | 25 frame_width(frame_width), |
| 26 frame_height(frame_height), |
| 27 frame_stride(frame_stride) {} |
| 28 const scoped_ptr<base::SharedMemory> buffer; |
| 29 const size_t buffer_size; |
| 30 const int frame_width; // In pixels. |
| 31 const int frame_height; // In pixels. |
| 32 const int frame_stride; // In pixels. |
25 | 33 |
26 scoped_ptr<base::SharedMemory> dib; | 34 private: |
27 scoped_refptr<media::VideoCapture::VideoFrameBuffer> mapped_memory; | 35 friend class base::RefCounted<ClientBuffer>; |
28 | 36 |
29 // Number of clients which hold this DIB. | 37 virtual ~ClientBuffer() {} |
30 int references; | 38 |
| 39 DISALLOW_COPY_AND_ASSIGN(ClientBuffer); |
31 }; | 40 }; |
32 | 41 |
33 bool VideoCaptureImpl::CaptureStarted() { | 42 bool VideoCaptureImpl::CaptureStarted() { |
34 return state_ == VIDEO_CAPTURE_STATE_STARTED; | 43 return state_ == VIDEO_CAPTURE_STATE_STARTED; |
35 } | 44 } |
36 | 45 |
37 int VideoCaptureImpl::CaptureWidth() { | 46 int VideoCaptureImpl::CaptureWidth() { |
38 return capture_format_.width; | 47 return capture_format_.width; |
39 } | 48 } |
40 | 49 |
41 int VideoCaptureImpl::CaptureHeight() { | 50 int VideoCaptureImpl::CaptureHeight() { |
42 return capture_format_.height; | 51 return capture_format_.height; |
43 } | 52 } |
44 | 53 |
45 int VideoCaptureImpl::CaptureFrameRate() { | 54 int VideoCaptureImpl::CaptureFrameRate() { |
46 return capture_format_.frame_rate; | 55 return capture_format_.frame_rate; |
47 } | 56 } |
48 | 57 |
49 VideoCaptureImpl::VideoCaptureImpl( | 58 VideoCaptureImpl::VideoCaptureImpl( |
50 const media::VideoCaptureSessionId id, | 59 const media::VideoCaptureSessionId id, |
51 base::MessageLoopProxy* capture_message_loop_proxy, | 60 base::MessageLoopProxy* capture_message_loop_proxy, |
52 VideoCaptureMessageFilter* filter) | 61 VideoCaptureMessageFilter* filter) |
53 : VideoCapture(), | 62 : VideoCapture(), |
54 message_filter_(filter), | 63 message_filter_(filter), |
55 capture_message_loop_proxy_(capture_message_loop_proxy), | 64 capture_message_loop_proxy_(capture_message_loop_proxy), |
56 io_message_loop_proxy_(ChildProcess::current()->io_message_loop_proxy()), | 65 io_message_loop_proxy_(ChildProcess::current()->io_message_loop_proxy()), |
57 device_id_(0), | 66 device_id_(0), |
| 67 client_buffer_weak_this_factory_(this), |
58 video_type_(media::PIXEL_FORMAT_I420), | 68 video_type_(media::PIXEL_FORMAT_I420), |
59 device_info_available_(false), | 69 device_info_available_(false), |
60 suspended_(false), | 70 suspended_(false), |
61 state_(VIDEO_CAPTURE_STATE_STOPPED) { | 71 state_(VIDEO_CAPTURE_STATE_STOPPED) { |
62 DCHECK(filter); | 72 DCHECK(filter); |
63 capture_format_.session_id = id; | 73 capture_format_.session_id = id; |
64 } | 74 } |
65 | 75 |
66 VideoCaptureImpl::~VideoCaptureImpl() { | 76 VideoCaptureImpl::~VideoCaptureImpl() {} |
67 STLDeleteValues(&cached_dibs_); | |
68 } | |
69 | 77 |
70 void VideoCaptureImpl::Init() { | 78 void VideoCaptureImpl::Init() { |
71 if (!io_message_loop_proxy_->BelongsToCurrentThread()) { | 79 if (!io_message_loop_proxy_->BelongsToCurrentThread()) { |
72 io_message_loop_proxy_->PostTask(FROM_HERE, | 80 io_message_loop_proxy_->PostTask(FROM_HERE, |
73 base::Bind(&VideoCaptureImpl::AddDelegateOnIOThread, | 81 base::Bind(&VideoCaptureImpl::AddDelegateOnIOThread, |
74 base::Unretained(this))); | 82 base::Unretained(this))); |
75 } else { | 83 } else { |
76 AddDelegateOnIOThread(); | 84 AddDelegateOnIOThread(); |
77 } | 85 } |
78 } | 86 } |
(...skipping 13 matching lines...) Expand all Loading... |
92 base::Bind(&VideoCaptureImpl::DoStartCaptureOnCaptureThread, | 100 base::Bind(&VideoCaptureImpl::DoStartCaptureOnCaptureThread, |
93 base::Unretained(this), handler, capability)); | 101 base::Unretained(this), handler, capability)); |
94 } | 102 } |
95 | 103 |
96 void VideoCaptureImpl::StopCapture(media::VideoCapture::EventHandler* handler) { | 104 void VideoCaptureImpl::StopCapture(media::VideoCapture::EventHandler* handler) { |
97 capture_message_loop_proxy_->PostTask(FROM_HERE, | 105 capture_message_loop_proxy_->PostTask(FROM_HERE, |
98 base::Bind(&VideoCaptureImpl::DoStopCaptureOnCaptureThread, | 106 base::Bind(&VideoCaptureImpl::DoStopCaptureOnCaptureThread, |
99 base::Unretained(this), handler)); | 107 base::Unretained(this), handler)); |
100 } | 108 } |
101 | 109 |
102 void VideoCaptureImpl::FeedBuffer(scoped_refptr<VideoFrameBuffer> buffer) { | |
103 capture_message_loop_proxy_->PostTask(FROM_HERE, | |
104 base::Bind(&VideoCaptureImpl::DoFeedBufferOnCaptureThread, | |
105 base::Unretained(this), buffer)); | |
106 } | |
107 | |
108 void VideoCaptureImpl::OnBufferCreated( | 110 void VideoCaptureImpl::OnBufferCreated( |
109 base::SharedMemoryHandle handle, | 111 base::SharedMemoryHandle handle, |
110 int length, int buffer_id) { | 112 int length, int buffer_id) { |
111 capture_message_loop_proxy_->PostTask(FROM_HERE, | 113 capture_message_loop_proxy_->PostTask(FROM_HERE, |
112 base::Bind(&VideoCaptureImpl::DoBufferCreatedOnCaptureThread, | 114 base::Bind(&VideoCaptureImpl::DoBufferCreatedOnCaptureThread, |
113 base::Unretained(this), handle, length, buffer_id)); | 115 base::Unretained(this), handle, length, buffer_id)); |
114 } | 116 } |
115 | 117 |
116 void VideoCaptureImpl::OnBufferReceived(int buffer_id, base::Time timestamp) { | 118 void VideoCaptureImpl::OnBufferReceived(int buffer_id, base::Time timestamp) { |
117 capture_message_loop_proxy_->PostTask(FROM_HERE, | 119 capture_message_loop_proxy_->PostTask(FROM_HERE, |
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
228 // A handler can be in only one client list. | 230 // A handler can be in only one client list. |
229 // If this handler is in any client list, we can just remove it from | 231 // If this handler is in any client list, we can just remove it from |
230 // that client list and don't have to run the other following RemoveClient(). | 232 // that client list and don't have to run the other following RemoveClient(). |
231 RemoveClient(handler, &clients_pending_on_filter_) || | 233 RemoveClient(handler, &clients_pending_on_filter_) || |
232 RemoveClient(handler, &clients_pending_on_restart_) || | 234 RemoveClient(handler, &clients_pending_on_restart_) || |
233 RemoveClient(handler, &clients_); | 235 RemoveClient(handler, &clients_); |
234 | 236 |
235 if (clients_.empty()) { | 237 if (clients_.empty()) { |
236 DVLOG(1) << "StopCapture: No more client, stopping ..."; | 238 DVLOG(1) << "StopCapture: No more client, stopping ..."; |
237 StopDevice(); | 239 StopDevice(); |
| 240 client_buffers_.clear(); |
| 241 client_buffer_weak_this_factory_.InvalidateWeakPtrs(); |
238 } | 242 } |
239 } | 243 } |
240 | 244 |
241 void VideoCaptureImpl::DoFeedBufferOnCaptureThread( | |
242 scoped_refptr<VideoFrameBuffer> buffer) { | |
243 DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread()); | |
244 | |
245 CachedDIB::iterator it; | |
246 for (it = cached_dibs_.begin(); it != cached_dibs_.end(); ++it) { | |
247 if (buffer.get() == it->second->mapped_memory.get()) | |
248 break; | |
249 } | |
250 | |
251 if (it != cached_dibs_.end() && it->second) { | |
252 DCHECK_GT(it->second->references, 0); | |
253 --it->second->references; | |
254 if (it->second->references == 0) { | |
255 Send(new VideoCaptureHostMsg_BufferReady(device_id_, it->first)); | |
256 } | |
257 } | |
258 } | |
259 | |
260 void VideoCaptureImpl::DoBufferCreatedOnCaptureThread( | 245 void VideoCaptureImpl::DoBufferCreatedOnCaptureThread( |
261 base::SharedMemoryHandle handle, | 246 base::SharedMemoryHandle handle, |
262 int length, int buffer_id) { | 247 int length, int buffer_id) { |
263 DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread()); | 248 DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread()); |
264 | 249 |
265 // In case client calls StopCapture before the arrival of created buffer, | 250 // In case client calls StopCapture before the arrival of created buffer, |
266 // just close this buffer and return. | 251 // just close this buffer and return. |
267 if (state_ != VIDEO_CAPTURE_STATE_STARTED) { | 252 if (state_ != VIDEO_CAPTURE_STATE_STARTED) { |
268 base::SharedMemory::CloseHandle(handle); | 253 base::SharedMemory::CloseHandle(handle); |
269 return; | 254 return; |
270 } | 255 } |
271 | 256 |
272 DCHECK(device_info_available_); | 257 DCHECK(device_info_available_); |
273 | 258 |
274 media::VideoCapture::VideoFrameBuffer* buffer; | 259 scoped_ptr<base::SharedMemory> shm(new base::SharedMemory(handle, false)); |
275 DCHECK(cached_dibs_.find(buffer_id) == cached_dibs_.end()); | 260 if (!shm->Map(length)) { |
| 261 DLOG(ERROR) << "DoBufferCreatedOnCaptureThread: Map() failed."; |
| 262 return; |
| 263 } |
276 | 264 |
277 base::SharedMemory* dib = new base::SharedMemory(handle, false); | 265 bool inserted = |
278 dib->Map(length); | 266 client_buffers_.insert(std::make_pair( |
279 buffer = new VideoFrameBuffer(); | 267 buffer_id, |
280 buffer->memory_pointer = static_cast<uint8*>(dib->memory()); | 268 new ClientBuffer(shm.Pass(), |
281 buffer->buffer_size = length; | 269 length, |
282 buffer->width = device_info_.width; | 270 device_info_.width, |
283 buffer->height = device_info_.height; | 271 device_info_.height, |
284 buffer->stride = device_info_.width; | 272 device_info_.width))).second; |
285 | 273 DCHECK(inserted); |
286 DIBBuffer* dib_buffer = new DIBBuffer(dib, buffer); | |
287 cached_dibs_[buffer_id] = dib_buffer; | |
288 } | 274 } |
289 | 275 |
290 void VideoCaptureImpl::DoBufferReceivedOnCaptureThread( | 276 void VideoCaptureImpl::DoBufferReceivedOnCaptureThread( |
291 int buffer_id, base::Time timestamp) { | 277 int buffer_id, base::Time timestamp) { |
292 DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread()); | 278 DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread()); |
293 | 279 |
294 if (state_ != VIDEO_CAPTURE_STATE_STARTED || suspended_) { | 280 if (state_ != VIDEO_CAPTURE_STATE_STARTED || suspended_) { |
295 Send(new VideoCaptureHostMsg_BufferReady(device_id_, buffer_id)); | 281 Send(new VideoCaptureHostMsg_BufferReady(device_id_, buffer_id)); |
296 return; | 282 return; |
297 } | 283 } |
298 | 284 |
299 media::VideoCapture::VideoFrameBuffer* buffer; | 285 ClientBufferMap::iterator iter = client_buffers_.find(buffer_id); |
300 DCHECK(cached_dibs_.find(buffer_id) != cached_dibs_.end()); | 286 DCHECK(iter != client_buffers_.end()); |
301 buffer = cached_dibs_[buffer_id]->mapped_memory.get(); | 287 scoped_refptr<ClientBuffer> buffer = iter->second; |
302 buffer->timestamp = timestamp; | 288 scoped_refptr<media::VideoFrame> frame = |
| 289 media::VideoFrame::WrapExternalSharedMemory( |
| 290 media::VideoFrame::I420, |
| 291 gfx::Size(buffer->frame_stride, buffer->frame_height), |
| 292 gfx::Rect(0, 0, buffer->frame_width, buffer->frame_height), |
| 293 gfx::Size(buffer->frame_width, buffer->frame_height), |
| 294 reinterpret_cast<uint8*>(buffer->buffer->memory()), |
| 295 buffer->buffer_size, |
| 296 buffer->buffer->handle(), |
| 297 // TODO(sheu): convert VideoCaptureMessageFilter::Delegate to use |
| 298 // base::TimeTicks instead of base::Time. http://crbug.com/249215 |
| 299 timestamp - base::Time::UnixEpoch(), |
| 300 media::BindToLoop( |
| 301 capture_message_loop_proxy_, |
| 302 base::Bind( |
| 303 &VideoCaptureImpl::DoClientBufferFinishedOnCaptureThread, |
| 304 client_buffer_weak_this_factory_.GetWeakPtr(), |
| 305 buffer_id, |
| 306 buffer))); |
303 | 307 |
304 for (ClientInfo::iterator it = clients_.begin(); it != clients_.end(); ++it) { | 308 for (ClientInfo::iterator it = clients_.begin(); it != clients_.end(); ++it) |
305 it->first->OnBufferReady(this, buffer); | 309 it->first->OnFrameReady(this, frame); |
306 } | 310 } |
307 cached_dibs_[buffer_id]->references = clients_.size(); | 311 |
| 312 void VideoCaptureImpl::DoClientBufferFinishedOnCaptureThread( |
| 313 int buffer_id, |
| 314 const scoped_refptr<ClientBuffer>& buffer) { |
| 315 DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread()); |
| 316 Send(new VideoCaptureHostMsg_BufferReady(device_id_, buffer_id)); |
308 } | 317 } |
309 | 318 |
310 void VideoCaptureImpl::DoStateChangedOnCaptureThread(VideoCaptureState state) { | 319 void VideoCaptureImpl::DoStateChangedOnCaptureThread(VideoCaptureState state) { |
311 DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread()); | 320 DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread()); |
312 | 321 |
313 switch (state) { | 322 switch (state) { |
314 case VIDEO_CAPTURE_STATE_STARTED: | 323 case VIDEO_CAPTURE_STATE_STARTED: |
315 break; | 324 break; |
316 case VIDEO_CAPTURE_STATE_STOPPED: | 325 case VIDEO_CAPTURE_STATE_STOPPED: |
317 state_ = VIDEO_CAPTURE_STATE_STOPPED; | 326 state_ = VIDEO_CAPTURE_STATE_STOPPED; |
318 DVLOG(1) << "OnStateChanged: stopped!, device_id = " << device_id_; | 327 DVLOG(1) << "OnStateChanged: stopped!, device_id = " << device_id_; |
319 STLDeleteValues(&cached_dibs_); | 328 client_buffers_.clear(); |
| 329 client_buffer_weak_this_factory_.InvalidateWeakPtrs(); |
320 if (!clients_.empty() || !clients_pending_on_restart_.empty()) | 330 if (!clients_.empty() || !clients_pending_on_restart_.empty()) |
321 RestartCapture(); | 331 RestartCapture(); |
322 break; | 332 break; |
323 case VIDEO_CAPTURE_STATE_PAUSED: | 333 case VIDEO_CAPTURE_STATE_PAUSED: |
324 for (ClientInfo::iterator it = clients_.begin(); | 334 for (ClientInfo::iterator it = clients_.begin(); |
325 it != clients_.end(); ++it) { | 335 it != clients_.end(); ++it) { |
326 it->first->OnPaused(this); | 336 it->first->OnPaused(this); |
327 } | 337 } |
328 break; | 338 break; |
329 case VIDEO_CAPTURE_STATE_ERROR: | 339 case VIDEO_CAPTURE_STATE_ERROR: |
(...skipping 17 matching lines...) Expand all Loading... |
347 state_ = VIDEO_CAPTURE_STATE_ENDED; | 357 state_ = VIDEO_CAPTURE_STATE_ENDED; |
348 break; | 358 break; |
349 default: | 359 default: |
350 break; | 360 break; |
351 } | 361 } |
352 } | 362 } |
353 | 363 |
354 void VideoCaptureImpl::DoDeviceInfoReceivedOnCaptureThread( | 364 void VideoCaptureImpl::DoDeviceInfoReceivedOnCaptureThread( |
355 const media::VideoCaptureParams& device_info) { | 365 const media::VideoCaptureParams& device_info) { |
356 DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread()); | 366 DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread()); |
357 DCHECK(!ClientHasDIB()); | 367 DCHECK(client_buffers_.empty()); |
358 | |
359 STLDeleteValues(&cached_dibs_); | |
360 | 368 |
361 device_info_ = device_info; | 369 device_info_ = device_info; |
362 device_info_available_ = true; | 370 device_info_available_ = true; |
363 for (ClientInfo::iterator it = clients_.begin(); it != clients_.end(); ++it) { | 371 for (ClientInfo::iterator it = clients_.begin(); it != clients_.end(); ++it) { |
364 it->first->OnDeviceInfoReceived(this, device_info); | 372 it->first->OnDeviceInfoReceived(this, device_info); |
365 } | 373 } |
366 } | 374 } |
367 | 375 |
368 void VideoCaptureImpl::DoDeviceInfoChangedOnCaptureThread( | 376 void VideoCaptureImpl::DoDeviceInfoChangedOnCaptureThread( |
369 const media::VideoCaptureParams& device_info) { | 377 const media::VideoCaptureParams& device_info) { |
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
455 message_filter_->RemoveDelegate(this); | 463 message_filter_->RemoveDelegate(this); |
456 capture_message_loop_proxy_->PostTask(FROM_HERE, task); | 464 capture_message_loop_proxy_->PostTask(FROM_HERE, task); |
457 } | 465 } |
458 | 466 |
459 void VideoCaptureImpl::Send(IPC::Message* message) { | 467 void VideoCaptureImpl::Send(IPC::Message* message) { |
460 io_message_loop_proxy_->PostTask(FROM_HERE, | 468 io_message_loop_proxy_->PostTask(FROM_HERE, |
461 base::Bind(base::IgnoreResult(&VideoCaptureMessageFilter::Send), | 469 base::Bind(base::IgnoreResult(&VideoCaptureMessageFilter::Send), |
462 message_filter_.get(), message)); | 470 message_filter_.get(), message)); |
463 } | 471 } |
464 | 472 |
465 bool VideoCaptureImpl::ClientHasDIB() const { | |
466 DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread()); | |
467 for (CachedDIB::const_iterator it = cached_dibs_.begin(); | |
468 it != cached_dibs_.end(); ++it) { | |
469 if (it->second->references > 0) | |
470 return true; | |
471 } | |
472 return false; | |
473 } | |
474 | |
475 bool VideoCaptureImpl::RemoveClient( | 473 bool VideoCaptureImpl::RemoveClient( |
476 media::VideoCapture::EventHandler* handler, | 474 media::VideoCapture::EventHandler* handler, |
477 ClientInfo* clients) { | 475 ClientInfo* clients) { |
478 DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread()); | 476 DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread()); |
479 bool found = false; | 477 bool found = false; |
480 | 478 |
481 ClientInfo::iterator it = clients->find(handler); | 479 ClientInfo::iterator it = clients->find(handler); |
482 if (it != clients->end()) { | 480 if (it != clients->end()) { |
483 handler->OnStopped(this); | 481 handler->OnStopped(this); |
484 handler->OnRemoved(this); | 482 handler->OnRemoved(this); |
485 clients->erase(it); | 483 clients->erase(it); |
486 found = true; | 484 found = true; |
487 } | 485 } |
488 return found; | 486 return found; |
489 } | 487 } |
490 | 488 |
491 } // namespace content | 489 } // namespace content |
OLD | NEW |