Home | History | Annotate | Download | only in media
      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:
     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:
     51   virtual ~PoolBuffer() { pool_->RelinquishProducerReservation(id()); }
     52 
     53   const scoped_refptr<VideoCaptureBufferPool> pool_;
     54 };
     55 
     56 }  // anonymous namespace
     57 
     58 struct VideoCaptureController::ControllerClient {
     59   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 
     71   ~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 
    149 VideoCaptureController::VideoCaptureController()
    150     : buffer_pool_(new VideoCaptureBufferPool(kNoOfBuffers)),
    151       state_(VIDEO_CAPTURE_STATE_STARTED),
    152       weak_ptr_factory_(this) {
    153 }
    154 
    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 
    160 VideoCaptureController::VideoCaptureDeviceClient::~VideoCaptureDeviceClient() {}
    161 
    162 base::WeakPtr<VideoCaptureController> VideoCaptureController::GetWeakPtr() {
    163   return weak_ptr_factory_.GetWeakPtr();
    164 }
    165 
    166 scoped_ptr<media::VideoCaptureDevice::Client>
    167 VideoCaptureController::NewDeviceClient() {
    168   scoped_ptr<media::VideoCaptureDevice::Client> result(
    169       new VideoCaptureDeviceClient(this->GetWeakPtr(), buffer_pool_));
    170   return result.Pass();
    171 }
    172 
    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 
    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 
    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 
    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&
    277 VideoCaptureController::GetVideoCaptureFormat() const {
    278   DCHECK_CURRENTLY_ON(BrowserThread::IO);
    279   return video_capture_format_;
    280 }
    281 
    282 scoped_refptr<media::VideoCaptureDevice::Client::Buffer>
    283 VideoCaptureController::VideoCaptureDeviceClient::ReserveOutputBuffer(
    284     media::VideoFrame::Format format,
    285     const gfx::Size& size) {
    286   return DoReserveOutputBuffer(format, size);
    287 }
    288 
    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
    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 
    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 
    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>
    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 
    533 VideoCaptureController::~VideoCaptureController() {
    534   STLDeleteContainerPointers(controller_clients_.begin(),
    535                              controller_clients_.end());
    536 }
    537 
    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 
    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 
    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*
    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*
    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 
    645 int VideoCaptureController::GetClientCount() {
    646   DCHECK_CURRENTLY_ON(BrowserThread::IO);
    647   return controller_clients_.size();
    648 }
    649 
    650 }  // namespace content
    651