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