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