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.
4
5 #include "content/browser/renderer_host/media/video_capture_controller.h"
6
7 #include <map>
8 #include <set>
9
10 #include "base/bind.h"
11 #include "base/debug/trace_event.h"
12 #include "base/metrics/histogram.h"
13 #include "base/metrics/sparse_histogram.h"
14 #include "base/stl_util.h"
15 #include "content/browser/renderer_host/media/media_stream_manager.h"
16 #include "content/browser/renderer_host/media/video_capture_manager.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "gpu/command_buffer/common/mailbox_holder.h"
19 #include "media/base/video_frame.h"
20 #include "media/base/video_util.h"
21 #include "media/base/yuv_convert.h"
22 #include "third_party/libyuv/include/libyuv.h"
23
24 using media::VideoCaptureFormat;
25
26 namespace content {
27
28 namespace {
29
30 static const int kInfiniteRatio = 99999;
31
32 #define UMA_HISTOGRAM_ASPECT_RATIO(name, width, height) \
33 UMA_HISTOGRAM_SPARSE_SLOWLY( \
34 name, \
35 (height) ? ((width) * 100) / (height) : kInfiniteRatio);
36
37 // The number of buffers that VideoCaptureBufferPool should allocate.
38 const int kNoOfBuffers = 3;
39
40 class PoolBuffer : public media::VideoCaptureDevice::Client::Buffer {
41 public:
PoolBuffer(const scoped_refptr<VideoCaptureBufferPool> & pool,int buffer_id,void * data,size_t size)42 PoolBuffer(const scoped_refptr<VideoCaptureBufferPool>& pool,
43 int buffer_id,
44 void* data,
45 size_t size)
46 : Buffer(buffer_id, data, size), pool_(pool) {
47 DCHECK(pool_);
48 }
49
50 private:
~PoolBuffer()51 virtual ~PoolBuffer() { pool_->RelinquishProducerReservation(id()); }
52
53 const scoped_refptr<VideoCaptureBufferPool> pool_;
54 };
55
56 } // anonymous namespace
57
58 struct VideoCaptureController::ControllerClient {
ControllerClientcontent::VideoCaptureController::ControllerClient59 ControllerClient(const VideoCaptureControllerID& id,
60 VideoCaptureControllerEventHandler* handler,
61 base::ProcessHandle render_process,
62 media::VideoCaptureSessionId session_id,
63 const media::VideoCaptureParams& params)
64 : controller_id(id),
65 event_handler(handler),
66 render_process_handle(render_process),
67 session_id(session_id),
68 parameters(params),
69 session_closed(false) {}
70
~ControllerClientcontent::VideoCaptureController::ControllerClient71 ~ControllerClient() {}
72
73 // ID used for identifying this object.
74 const VideoCaptureControllerID controller_id;
75 VideoCaptureControllerEventHandler* const event_handler;
76
77 // Handle to the render process that will receive the capture buffers.
78 const base::ProcessHandle render_process_handle;
79 const media::VideoCaptureSessionId session_id;
80 const media::VideoCaptureParams parameters;
81
82 // Buffers that are currently known to this client.
83 std::set<int> known_buffers;
84
85 // Buffers currently held by this client, and syncpoint callback to call when
86 // they are returned from the client.
87 typedef std::map<int, scoped_refptr<media::VideoFrame> > ActiveBufferMap;
88 ActiveBufferMap active_buffers;
89
90 // State of capture session, controlled by VideoCaptureManager directly. This
91 // transitions to true as soon as StopSession() occurs, at which point the
92 // client is sent an OnEnded() event. However, because the client retains a
93 // VideoCaptureController* pointer, its ControllerClient entry lives on until
94 // it unregisters itself via RemoveClient(), which may happen asynchronously.
95 //
96 // TODO(nick): If we changed the semantics of VideoCaptureHost so that
97 // OnEnded() events were processed synchronously (with the RemoveClient() done
98 // implicitly), we could avoid tracking this state here in the Controller, and
99 // simplify the code in both places.
100 bool session_closed;
101 };
102
103 // Receives events from the VideoCaptureDevice and posts them to a
104 // VideoCaptureController on the IO thread. An instance of this class may safely
105 // outlive its target VideoCaptureController.
106 //
107 // Methods of this class may be called from any thread, and in practice will
108 // often be called on some auxiliary thread depending on the platform and the
109 // device type; including, for example, the DirectShow thread on Windows, the
110 // v4l2_thread on Linux, and the UI thread for tab capture.
111 class VideoCaptureController::VideoCaptureDeviceClient
112 : public media::VideoCaptureDevice::Client {
113 public:
114 explicit VideoCaptureDeviceClient(
115 const base::WeakPtr<VideoCaptureController>& controller,
116 const scoped_refptr<VideoCaptureBufferPool>& buffer_pool);
117 virtual ~VideoCaptureDeviceClient();
118
119 // VideoCaptureDevice::Client implementation.
120 virtual scoped_refptr<Buffer> ReserveOutputBuffer(
121 media::VideoFrame::Format format,
122 const gfx::Size& size) OVERRIDE;
123 virtual void OnIncomingCapturedData(const uint8* data,
124 int length,
125 const VideoCaptureFormat& frame_format,
126 int rotation,
127 base::TimeTicks timestamp) OVERRIDE;
128 virtual void OnIncomingCapturedVideoFrame(
129 const scoped_refptr<Buffer>& buffer,
130 const VideoCaptureFormat& buffer_format,
131 const scoped_refptr<media::VideoFrame>& frame,
132 base::TimeTicks timestamp) OVERRIDE;
133 virtual void OnError(const std::string& reason) OVERRIDE;
134 virtual void OnLog(const std::string& message) OVERRIDE;
135
136 private:
137 scoped_refptr<Buffer> DoReserveOutputBuffer(media::VideoFrame::Format format,
138 const gfx::Size& dimensions);
139
140 // The controller to which we post events.
141 const base::WeakPtr<VideoCaptureController> controller_;
142
143 // The pool of shared-memory buffers used for capturing.
144 const scoped_refptr<VideoCaptureBufferPool> buffer_pool_;
145
146 bool first_frame_;
147 };
148
VideoCaptureController()149 VideoCaptureController::VideoCaptureController()
150 : buffer_pool_(new VideoCaptureBufferPool(kNoOfBuffers)),
151 state_(VIDEO_CAPTURE_STATE_STARTED),
152 weak_ptr_factory_(this) {
153 }
154
VideoCaptureDeviceClient(const base::WeakPtr<VideoCaptureController> & controller,const scoped_refptr<VideoCaptureBufferPool> & buffer_pool)155 VideoCaptureController::VideoCaptureDeviceClient::VideoCaptureDeviceClient(
156 const base::WeakPtr<VideoCaptureController>& controller,
157 const scoped_refptr<VideoCaptureBufferPool>& buffer_pool)
158 : controller_(controller), buffer_pool_(buffer_pool), first_frame_(true) {}
159
~VideoCaptureDeviceClient()160 VideoCaptureController::VideoCaptureDeviceClient::~VideoCaptureDeviceClient() {}
161
GetWeakPtr()162 base::WeakPtr<VideoCaptureController> VideoCaptureController::GetWeakPtr() {
163 return weak_ptr_factory_.GetWeakPtr();
164 }
165
166 scoped_ptr<media::VideoCaptureDevice::Client>
NewDeviceClient()167 VideoCaptureController::NewDeviceClient() {
168 scoped_ptr<media::VideoCaptureDevice::Client> result(
169 new VideoCaptureDeviceClient(this->GetWeakPtr(), buffer_pool_));
170 return result.Pass();
171 }
172
AddClient(const VideoCaptureControllerID & id,VideoCaptureControllerEventHandler * event_handler,base::ProcessHandle render_process,media::VideoCaptureSessionId session_id,const media::VideoCaptureParams & params)173 void VideoCaptureController::AddClient(
174 const VideoCaptureControllerID& id,
175 VideoCaptureControllerEventHandler* event_handler,
176 base::ProcessHandle render_process,
177 media::VideoCaptureSessionId session_id,
178 const media::VideoCaptureParams& params) {
179 DCHECK_CURRENTLY_ON(BrowserThread::IO);
180 DVLOG(1) << "VideoCaptureController::AddClient, id " << id.device_id
181 << ", " << params.requested_format.frame_size.ToString()
182 << ", " << params.requested_format.frame_rate
183 << ", " << session_id
184 << ")";
185
186 // If this is the first client added to the controller, cache the parameters.
187 if (!controller_clients_.size())
188 video_capture_format_ = params.requested_format;
189
190 // Signal error in case device is already in error state.
191 if (state_ == VIDEO_CAPTURE_STATE_ERROR) {
192 event_handler->OnError(id);
193 return;
194 }
195
196 // Do nothing if this client has called AddClient before.
197 if (FindClient(id, event_handler, controller_clients_))
198 return;
199
200 ControllerClient* client = new ControllerClient(
201 id, event_handler, render_process, session_id, params);
202 // If we already have gotten frame_info from the device, repeat it to the new
203 // client.
204 if (state_ == VIDEO_CAPTURE_STATE_STARTED) {
205 controller_clients_.push_back(client);
206 return;
207 }
208 }
209
RemoveClient(const VideoCaptureControllerID & id,VideoCaptureControllerEventHandler * event_handler)210 int VideoCaptureController::RemoveClient(
211 const VideoCaptureControllerID& id,
212 VideoCaptureControllerEventHandler* event_handler) {
213 DCHECK_CURRENTLY_ON(BrowserThread::IO);
214 DVLOG(1) << "VideoCaptureController::RemoveClient, id " << id.device_id;
215
216 ControllerClient* client = FindClient(id, event_handler, controller_clients_);
217 if (!client)
218 return kInvalidMediaCaptureSessionId;
219
220 // Take back all buffers held by the |client|.
221 for (ControllerClient::ActiveBufferMap::iterator buffer_it =
222 client->active_buffers.begin();
223 buffer_it != client->active_buffers.end();
224 ++buffer_it) {
225 buffer_pool_->RelinquishConsumerHold(buffer_it->first, 1);
226 }
227 client->active_buffers.clear();
228
229 int session_id = client->session_id;
230 controller_clients_.remove(client);
231 delete client;
232
233 return session_id;
234 }
235
StopSession(int session_id)236 void VideoCaptureController::StopSession(int session_id) {
237 DCHECK_CURRENTLY_ON(BrowserThread::IO);
238 DVLOG(1) << "VideoCaptureController::StopSession, id " << session_id;
239
240 ControllerClient* client = FindClient(session_id, controller_clients_);
241
242 if (client) {
243 client->session_closed = true;
244 client->event_handler->OnEnded(client->controller_id);
245 }
246 }
247
ReturnBuffer(const VideoCaptureControllerID & id,VideoCaptureControllerEventHandler * event_handler,int buffer_id,const std::vector<uint32> & sync_points)248 void VideoCaptureController::ReturnBuffer(
249 const VideoCaptureControllerID& id,
250 VideoCaptureControllerEventHandler* event_handler,
251 int buffer_id,
252 const std::vector<uint32>& sync_points) {
253 DCHECK_CURRENTLY_ON(BrowserThread::IO);
254
255 ControllerClient* client = FindClient(id, event_handler, controller_clients_);
256
257 // If this buffer is not held by this client, or this client doesn't exist
258 // in controller, do nothing.
259 ControllerClient::ActiveBufferMap::iterator iter;
260 if (!client || (iter = client->active_buffers.find(buffer_id)) ==
261 client->active_buffers.end()) {
262 NOTREACHED();
263 return;
264 }
265 scoped_refptr<media::VideoFrame> frame = iter->second;
266 client->active_buffers.erase(iter);
267
268 if (frame->format() == media::VideoFrame::NATIVE_TEXTURE) {
269 for (size_t i = 0; i < sync_points.size(); i++)
270 frame->AppendReleaseSyncPoint(sync_points[i]);
271 }
272
273 buffer_pool_->RelinquishConsumerHold(buffer_id, 1);
274 }
275
276 const media::VideoCaptureFormat&
GetVideoCaptureFormat() const277 VideoCaptureController::GetVideoCaptureFormat() const {
278 DCHECK_CURRENTLY_ON(BrowserThread::IO);
279 return video_capture_format_;
280 }
281
282 scoped_refptr<media::VideoCaptureDevice::Client::Buffer>
ReserveOutputBuffer(media::VideoFrame::Format format,const gfx::Size & size)283 VideoCaptureController::VideoCaptureDeviceClient::ReserveOutputBuffer(
284 media::VideoFrame::Format format,
285 const gfx::Size& size) {
286 return DoReserveOutputBuffer(format, size);
287 }
288
OnIncomingCapturedData(const uint8 * data,int length,const VideoCaptureFormat & frame_format,int rotation,base::TimeTicks timestamp)289 void VideoCaptureController::VideoCaptureDeviceClient::OnIncomingCapturedData(
290 const uint8* data,
291 int length,
292 const VideoCaptureFormat& frame_format,
293 int rotation,
294 base::TimeTicks timestamp) {
295 TRACE_EVENT0("video", "VideoCaptureController::OnIncomingCapturedData");
296
297 if (!frame_format.IsValid())
298 return;
299
300 // Chopped pixels in width/height in case video capture device has odd
301 // numbers for width/height.
302 int chopped_width = 0;
303 int chopped_height = 0;
304 int new_unrotated_width = frame_format.frame_size.width();
305 int new_unrotated_height = frame_format.frame_size.height();
306
307 if (new_unrotated_width & 1) {
308 --new_unrotated_width;
309 chopped_width = 1;
310 }
311 if (new_unrotated_height & 1) {
312 --new_unrotated_height;
313 chopped_height = 1;
314 }
315
316 int destination_width = new_unrotated_width;
317 int destination_height = new_unrotated_height;
318 if (rotation == 90 || rotation == 270) {
319 destination_width = new_unrotated_height;
320 destination_height = new_unrotated_width;
321 }
322 const gfx::Size dimensions(destination_width, destination_height);
323 if (!media::VideoFrame::IsValidConfig(media::VideoFrame::I420,
324 dimensions,
325 gfx::Rect(dimensions),
326 dimensions)) {
327 return;
328 }
329
330 scoped_refptr<Buffer> buffer =
331 DoReserveOutputBuffer(media::VideoFrame::I420, dimensions);
332
333 if (!buffer)
334 return;
335 uint8* yplane = NULL;
336 bool flip = false;
337 yplane = reinterpret_cast<uint8*>(buffer->data());
338 uint8* uplane =
339 yplane +
340 media::VideoFrame::PlaneAllocationSize(
341 media::VideoFrame::I420, media::VideoFrame::kYPlane, dimensions);
342 uint8* vplane =
343 uplane +
344 media::VideoFrame::PlaneAllocationSize(
345 media::VideoFrame::I420, media::VideoFrame::kUPlane, dimensions);
346 int yplane_stride = dimensions.width();
347 int uv_plane_stride = yplane_stride / 2;
348 int crop_x = 0;
349 int crop_y = 0;
350 libyuv::FourCC origin_colorspace = libyuv::FOURCC_ANY;
351
352 libyuv::RotationMode rotation_mode = libyuv::kRotate0;
353 if (rotation == 90)
354 rotation_mode = libyuv::kRotate90;
355 else if (rotation == 180)
356 rotation_mode = libyuv::kRotate180;
357 else if (rotation == 270)
358 rotation_mode = libyuv::kRotate270;
359
360 switch (frame_format.pixel_format) {
361 case media::PIXEL_FORMAT_UNKNOWN: // Color format not set.
362 break;
363 case media::PIXEL_FORMAT_I420:
364 DCHECK(!chopped_width && !chopped_height);
365 origin_colorspace = libyuv::FOURCC_I420;
366 break;
367 case media::PIXEL_FORMAT_YV12:
368 DCHECK(!chopped_width && !chopped_height);
369 origin_colorspace = libyuv::FOURCC_YV12;
370 break;
371 case media::PIXEL_FORMAT_NV21:
372 DCHECK(!chopped_width && !chopped_height);
373 origin_colorspace = libyuv::FOURCC_NV21;
374 break;
375 case media::PIXEL_FORMAT_YUY2:
376 DCHECK(!chopped_width && !chopped_height);
377 origin_colorspace = libyuv::FOURCC_YUY2;
378 break;
379 case media::PIXEL_FORMAT_UYVY:
380 DCHECK(!chopped_width && !chopped_height);
381 origin_colorspace = libyuv::FOURCC_UYVY;
382 break;
383 case media::PIXEL_FORMAT_RGB24:
384 origin_colorspace = libyuv::FOURCC_24BG;
385 #if defined(OS_WIN)
386 // TODO(wjia): Currently, for RGB24 on WIN, capture device always
387 // passes in positive src_width and src_height. Remove this hardcoded
388 // value when nagative src_height is supported. The negative src_height
389 // indicates that vertical flipping is needed.
390 flip = true;
391 #endif
392 break;
393 case media::PIXEL_FORMAT_ARGB:
394 origin_colorspace = libyuv::FOURCC_ARGB;
395 break;
396 case media::PIXEL_FORMAT_MJPEG:
397 origin_colorspace = libyuv::FOURCC_MJPG;
398 break;
399 default:
400 NOTREACHED();
401 }
402
403 libyuv::ConvertToI420(data,
404 length,
405 yplane,
406 yplane_stride,
407 uplane,
408 uv_plane_stride,
409 vplane,
410 uv_plane_stride,
411 crop_x,
412 crop_y,
413 frame_format.frame_size.width(),
414 (flip ? -frame_format.frame_size.height() :
415 frame_format.frame_size.height()),
416 new_unrotated_width,
417 new_unrotated_height,
418 rotation_mode,
419 origin_colorspace);
420 scoped_refptr<media::VideoFrame> frame =
421 media::VideoFrame::WrapExternalPackedMemory(
422 media::VideoFrame::I420,
423 dimensions,
424 gfx::Rect(dimensions),
425 dimensions,
426 yplane,
427 media::VideoFrame::AllocationSize(media::VideoFrame::I420,
428 dimensions),
429 base::SharedMemory::NULLHandle(),
430 base::TimeDelta(),
431 base::Closure());
432 DCHECK(frame);
433
434 VideoCaptureFormat format(
435 dimensions, frame_format.frame_rate, media::PIXEL_FORMAT_I420);
436 BrowserThread::PostTask(
437 BrowserThread::IO,
438 FROM_HERE,
439 base::Bind(
440 &VideoCaptureController::DoIncomingCapturedVideoFrameOnIOThread,
441 controller_,
442 buffer,
443 format,
444 frame,
445 timestamp));
446
447 if (first_frame_) {
448 UMA_HISTOGRAM_COUNTS("Media.VideoCapture.Width",
449 frame_format.frame_size.width());
450 UMA_HISTOGRAM_COUNTS("Media.VideoCapture.Height",
451 frame_format.frame_size.height());
452 UMA_HISTOGRAM_ASPECT_RATIO("Media.VideoCapture.AspectRatio",
453 frame_format.frame_size.width(),
454 frame_format.frame_size.height());
455 UMA_HISTOGRAM_COUNTS("Media.VideoCapture.FrameRate",
456 frame_format.frame_rate);
457 UMA_HISTOGRAM_ENUMERATION("Media.VideoCapture.PixelFormat",
458 frame_format.pixel_format,
459 media::PIXEL_FORMAT_MAX);
460 first_frame_ = false;
461 }
462 }
463
464 void
OnIncomingCapturedVideoFrame(const scoped_refptr<Buffer> & buffer,const VideoCaptureFormat & buffer_format,const scoped_refptr<media::VideoFrame> & frame,base::TimeTicks timestamp)465 VideoCaptureController::VideoCaptureDeviceClient::OnIncomingCapturedVideoFrame(
466 const scoped_refptr<Buffer>& buffer,
467 const VideoCaptureFormat& buffer_format,
468 const scoped_refptr<media::VideoFrame>& frame,
469 base::TimeTicks timestamp) {
470 BrowserThread::PostTask(
471 BrowserThread::IO,
472 FROM_HERE,
473 base::Bind(
474 &VideoCaptureController::DoIncomingCapturedVideoFrameOnIOThread,
475 controller_,
476 buffer,
477 buffer_format,
478 frame,
479 timestamp));
480 }
481
OnError(const std::string & reason)482 void VideoCaptureController::VideoCaptureDeviceClient::OnError(
483 const std::string& reason) {
484 MediaStreamManager::SendMessageToNativeLog(
485 "Error on video capture: " + reason);
486 BrowserThread::PostTask(BrowserThread::IO,
487 FROM_HERE,
488 base::Bind(&VideoCaptureController::DoErrorOnIOThread, controller_));
489 }
490
OnLog(const std::string & message)491 void VideoCaptureController::VideoCaptureDeviceClient::OnLog(
492 const std::string& message) {
493 MediaStreamManager::SendMessageToNativeLog("Video capture: " + message);
494 }
495
496 scoped_refptr<media::VideoCaptureDevice::Client::Buffer>
DoReserveOutputBuffer(media::VideoFrame::Format format,const gfx::Size & dimensions)497 VideoCaptureController::VideoCaptureDeviceClient::DoReserveOutputBuffer(
498 media::VideoFrame::Format format,
499 const gfx::Size& dimensions) {
500 size_t frame_bytes = 0;
501 if (format == media::VideoFrame::NATIVE_TEXTURE) {
502 DCHECK_EQ(dimensions.width(), 0);
503 DCHECK_EQ(dimensions.height(), 0);
504 } else {
505 // The capture pipeline expects I420 for now.
506 DCHECK_EQ(format, media::VideoFrame::I420)
507 << "Non-I420 output buffer format " << format << " requested";
508 frame_bytes = media::VideoFrame::AllocationSize(format, dimensions);
509 }
510
511 int buffer_id_to_drop = VideoCaptureBufferPool::kInvalidId;
512 int buffer_id =
513 buffer_pool_->ReserveForProducer(frame_bytes, &buffer_id_to_drop);
514 if (buffer_id == VideoCaptureBufferPool::kInvalidId)
515 return NULL;
516 void* data;
517 size_t size;
518 buffer_pool_->GetBufferInfo(buffer_id, &data, &size);
519
520 scoped_refptr<media::VideoCaptureDevice::Client::Buffer> output_buffer(
521 new PoolBuffer(buffer_pool_, buffer_id, data, size));
522
523 if (buffer_id_to_drop != VideoCaptureBufferPool::kInvalidId) {
524 BrowserThread::PostTask(BrowserThread::IO,
525 FROM_HERE,
526 base::Bind(&VideoCaptureController::DoBufferDestroyedOnIOThread,
527 controller_, buffer_id_to_drop));
528 }
529
530 return output_buffer;
531 }
532
~VideoCaptureController()533 VideoCaptureController::~VideoCaptureController() {
534 STLDeleteContainerPointers(controller_clients_.begin(),
535 controller_clients_.end());
536 }
537
DoIncomingCapturedVideoFrameOnIOThread(const scoped_refptr<media::VideoCaptureDevice::Client::Buffer> & buffer,const media::VideoCaptureFormat & buffer_format,const scoped_refptr<media::VideoFrame> & frame,base::TimeTicks timestamp)538 void VideoCaptureController::DoIncomingCapturedVideoFrameOnIOThread(
539 const scoped_refptr<media::VideoCaptureDevice::Client::Buffer>& buffer,
540 const media::VideoCaptureFormat& buffer_format,
541 const scoped_refptr<media::VideoFrame>& frame,
542 base::TimeTicks timestamp) {
543 DCHECK_CURRENTLY_ON(BrowserThread::IO);
544 DCHECK_NE(buffer->id(), VideoCaptureBufferPool::kInvalidId);
545
546 int count = 0;
547 if (state_ == VIDEO_CAPTURE_STATE_STARTED) {
548 for (ControllerClients::iterator client_it = controller_clients_.begin();
549 client_it != controller_clients_.end(); ++client_it) {
550 ControllerClient* client = *client_it;
551 if (client->session_closed)
552 continue;
553
554 if (frame->format() == media::VideoFrame::NATIVE_TEXTURE) {
555 client->event_handler->OnMailboxBufferReady(client->controller_id,
556 buffer->id(),
557 *frame->mailbox_holder(),
558 buffer_format,
559 timestamp);
560 } else {
561 bool is_new_buffer = client->known_buffers.insert(buffer->id()).second;
562 if (is_new_buffer) {
563 // On the first use of a buffer on a client, share the memory handle.
564 size_t memory_size = 0;
565 base::SharedMemoryHandle remote_handle = buffer_pool_->ShareToProcess(
566 buffer->id(), client->render_process_handle, &memory_size);
567 client->event_handler->OnBufferCreated(
568 client->controller_id, remote_handle, memory_size, buffer->id());
569 }
570
571 client->event_handler->OnBufferReady(
572 client->controller_id, buffer->id(), buffer_format, timestamp);
573 }
574
575 bool inserted =
576 client->active_buffers.insert(std::make_pair(buffer->id(), frame))
577 .second;
578 DCHECK(inserted) << "Unexpected duplicate buffer: " << buffer->id();
579 count++;
580 }
581 }
582
583 buffer_pool_->HoldForConsumers(buffer->id(), count);
584 }
585
DoErrorOnIOThread()586 void VideoCaptureController::DoErrorOnIOThread() {
587 DCHECK_CURRENTLY_ON(BrowserThread::IO);
588 state_ = VIDEO_CAPTURE_STATE_ERROR;
589
590 for (ControllerClients::iterator client_it = controller_clients_.begin();
591 client_it != controller_clients_.end(); ++client_it) {
592 ControllerClient* client = *client_it;
593 if (client->session_closed)
594 continue;
595
596 client->event_handler->OnError(client->controller_id);
597 }
598 }
599
DoBufferDestroyedOnIOThread(int buffer_id_to_drop)600 void VideoCaptureController::DoBufferDestroyedOnIOThread(
601 int buffer_id_to_drop) {
602 DCHECK_CURRENTLY_ON(BrowserThread::IO);
603
604 for (ControllerClients::iterator client_it = controller_clients_.begin();
605 client_it != controller_clients_.end(); ++client_it) {
606 ControllerClient* client = *client_it;
607 if (client->session_closed)
608 continue;
609
610 if (client->known_buffers.erase(buffer_id_to_drop)) {
611 client->event_handler->OnBufferDestroyed(client->controller_id,
612 buffer_id_to_drop);
613 }
614 }
615 }
616
617 VideoCaptureController::ControllerClient*
FindClient(const VideoCaptureControllerID & id,VideoCaptureControllerEventHandler * handler,const ControllerClients & clients)618 VideoCaptureController::FindClient(
619 const VideoCaptureControllerID& id,
620 VideoCaptureControllerEventHandler* handler,
621 const ControllerClients& clients) {
622 for (ControllerClients::const_iterator client_it = clients.begin();
623 client_it != clients.end(); ++client_it) {
624 if ((*client_it)->controller_id == id &&
625 (*client_it)->event_handler == handler) {
626 return *client_it;
627 }
628 }
629 return NULL;
630 }
631
632 VideoCaptureController::ControllerClient*
FindClient(int session_id,const ControllerClients & clients)633 VideoCaptureController::FindClient(
634 int session_id,
635 const ControllerClients& clients) {
636 for (ControllerClients::const_iterator client_it = clients.begin();
637 client_it != clients.end(); ++client_it) {
638 if ((*client_it)->session_id == session_id) {
639 return *client_it;
640 }
641 }
642 return NULL;
643 }
644
GetClientCount()645 int VideoCaptureController::GetClientCount() {
646 DCHECK_CURRENTLY_ON(BrowserThread::IO);
647 return controller_clients_.size();
648 }
649
650 } // namespace content
651