video_capture_controller.cc revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
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 <set>
8
9#include "base/bind.h"
10#include "base/memory/scoped_ptr.h"
11#include "base/stl_util.h"
12#include "content/browser/renderer_host/media/media_stream_manager.h"
13#include "content/browser/renderer_host/media/video_capture_manager.h"
14#include "content/public/browser/browser_thread.h"
15#include "media/base/video_frame.h"
16#include "media/base/video_util.h"
17#include "media/base/yuv_convert.h"
18
19#if !defined(OS_IOS) && !defined(OS_ANDROID)
20#include "third_party/libyuv/include/libyuv.h"
21#endif
22
23namespace {
24
25// TODO(wjia): Support stride.
26void RotatePackedYV12Frame(
27    const uint8* src,
28    uint8* dest_yplane,
29    uint8* dest_uplane,
30    uint8* dest_vplane,
31    int width,
32    int height,
33    int rotation,
34    bool flip_vert,
35    bool flip_horiz) {
36  media::RotatePlaneByPixels(
37      src, dest_yplane, width, height, rotation, flip_vert, flip_horiz);
38  int y_size = width * height;
39  src += y_size;
40  media::RotatePlaneByPixels(
41      src, dest_uplane, width/2, height/2, rotation, flip_vert, flip_horiz);
42  src += y_size/4;
43  media::RotatePlaneByPixels(
44      src, dest_vplane, width/2, height/2, rotation, flip_vert, flip_horiz);
45}
46
47}  // namespace
48
49namespace content {
50
51// The number of buffers that VideoCaptureBufferPool should allocate.
52static const int kNoOfBuffers = 3;
53
54struct VideoCaptureController::ControllerClient {
55  ControllerClient(
56      const VideoCaptureControllerID& id,
57      VideoCaptureControllerEventHandler* handler,
58      base::ProcessHandle render_process,
59      const media::VideoCaptureParams& params)
60      : controller_id(id),
61        event_handler(handler),
62        render_process_handle(render_process),
63        parameters(params),
64        session_closed(false) {
65  }
66
67  ~ControllerClient() {}
68
69  // ID used for identifying this object.
70  VideoCaptureControllerID controller_id;
71  VideoCaptureControllerEventHandler* event_handler;
72
73  // Handle to the render process that will receive the capture buffers.
74  base::ProcessHandle render_process_handle;
75  media::VideoCaptureParams parameters;
76
77  // Buffers used by this client.
78  std::set<int> buffers;
79
80  // State of capture session, controlled by VideoCaptureManager directly.
81  bool session_closed;
82};
83
84VideoCaptureController::VideoCaptureController(
85    VideoCaptureManager* video_capture_manager)
86    : chopped_width_(0),
87      chopped_height_(0),
88      frame_info_available_(false),
89      video_capture_manager_(video_capture_manager),
90      device_in_use_(false),
91      state_(VIDEO_CAPTURE_STATE_STOPPED) {
92  memset(&current_params_, 0, sizeof(current_params_));
93}
94
95void VideoCaptureController::StartCapture(
96    const VideoCaptureControllerID& id,
97    VideoCaptureControllerEventHandler* event_handler,
98    base::ProcessHandle render_process,
99    const media::VideoCaptureParams& params) {
100  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
101  DVLOG(1) << "VideoCaptureController::StartCapture, id " << id.device_id
102           << ", (" << params.width
103           << ", " << params.height
104           << ", " << params.frame_per_second
105           << ", " << params.session_id
106           << ")";
107
108  // Signal error in case device is already in error state.
109  if (state_ == VIDEO_CAPTURE_STATE_ERROR) {
110    event_handler->OnError(id);
111    return;
112  }
113
114  // Do nothing if this client has called StartCapture before.
115  if (FindClient(id, event_handler, controller_clients_) ||
116      FindClient(id, event_handler, pending_clients_))
117    return;
118
119  ControllerClient* client = new ControllerClient(id, event_handler,
120                                                  render_process, params);
121  // In case capture has been started, need to check different conditions.
122  if (state_ == VIDEO_CAPTURE_STATE_STARTED) {
123  // TODO(wjia): Temporarily disable restarting till client supports resampling.
124#if 0
125    // This client has higher resolution than what is currently requested.
126    // Need restart capturing.
127    if (params.width > current_params_.width ||
128        params.height > current_params_.height) {
129      video_capture_manager_->Stop(current_params_.session_id,
130          base::Bind(&VideoCaptureController::OnDeviceStopped, this));
131      frame_info_available_ = false;
132      state_ = VIDEO_CAPTURE_STATE_STOPPING;
133      pending_clients_.push_back(client);
134      return;
135    }
136#endif
137
138    // This client's resolution is no larger than what's currently requested.
139    // When frame_info has been returned by device, send them to client.
140    if (frame_info_available_) {
141      SendFrameInfoAndBuffers(client);
142    }
143    controller_clients_.push_back(client);
144    return;
145  }
146
147  // In case the device is in the middle of stopping, put the client in
148  // pending queue.
149  if (state_ == VIDEO_CAPTURE_STATE_STOPPING) {
150    pending_clients_.push_back(client);
151    return;
152  }
153
154  // Fresh start.
155  controller_clients_.push_back(client);
156  current_params_ = params;
157  // Order the manager to start the actual capture.
158  video_capture_manager_->Start(params, this);
159  state_ = VIDEO_CAPTURE_STATE_STARTED;
160  device_in_use_ = true;
161}
162
163void VideoCaptureController::StopCapture(
164    const VideoCaptureControllerID& id,
165    VideoCaptureControllerEventHandler* event_handler) {
166  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
167  DVLOG(1) << "VideoCaptureController::StopCapture, id " << id.device_id;
168
169  ControllerClient* client = FindClient(id, event_handler, pending_clients_);
170  // If the client is still in pending queue, just remove it.
171  if (client) {
172    pending_clients_.remove(client);
173    return;
174  }
175
176  client = FindClient(id, event_handler, controller_clients_);
177  if (!client)
178    return;
179
180  // Take back all buffers held by the |client|.
181  if (buffer_pool_.get()) {
182    for (std::set<int>::iterator buffer_it = client->buffers.begin();
183         buffer_it != client->buffers.end();
184         ++buffer_it) {
185      int buffer_id = *buffer_it;
186      buffer_pool_->RelinquishConsumerHold(buffer_id, 1);
187    }
188  }
189  client->buffers.clear();
190
191  int session_id = client->parameters.session_id;
192  delete client;
193  controller_clients_.remove(client);
194
195  // No more clients. Stop device.
196  if (controller_clients_.empty() &&
197      (state_ == VIDEO_CAPTURE_STATE_STARTED ||
198       state_ == VIDEO_CAPTURE_STATE_ERROR)) {
199    video_capture_manager_->Stop(session_id,
200        base::Bind(&VideoCaptureController::OnDeviceStopped, this));
201    frame_info_available_ = false;
202    state_ = VIDEO_CAPTURE_STATE_STOPPING;
203  }
204}
205
206void VideoCaptureController::StopSession(
207    int session_id) {
208  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
209  DVLOG(1) << "VideoCaptureController::StopSession, id " << session_id;
210
211  ControllerClient* client = FindClient(session_id, pending_clients_);
212  if (!client)
213    client = FindClient(session_id, controller_clients_);
214
215  if (client) {
216    client->session_closed = true;
217    client->event_handler->OnEnded(client->controller_id);
218  }
219}
220
221void VideoCaptureController::ReturnBuffer(
222    const VideoCaptureControllerID& id,
223    VideoCaptureControllerEventHandler* event_handler,
224    int buffer_id) {
225  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
226
227  ControllerClient* client = FindClient(id, event_handler,
228                                        controller_clients_);
229
230  // If this buffer is not held by this client, or this client doesn't exist
231  // in controller, do nothing.
232  if (!client ||
233      client->buffers.find(buffer_id) == client->buffers.end())
234    return;
235
236  client->buffers.erase(buffer_id);
237  buffer_pool_->RelinquishConsumerHold(buffer_id, 1);
238
239  // When all buffers have been returned by clients and device has been
240  // called to stop, check if restart is needed. This could happen when
241  // capture needs to be restarted due to resolution change.
242  if (!buffer_pool_->IsAnyBufferHeldForConsumers() &&
243      state_ == VIDEO_CAPTURE_STATE_STOPPING) {
244    PostStopping();
245  }
246}
247
248scoped_refptr<media::VideoFrame> VideoCaptureController::ReserveOutputBuffer() {
249  base::AutoLock lock(buffer_pool_lock_);
250  if (!buffer_pool_.get())
251    return NULL;
252  return buffer_pool_->ReserveI420VideoFrame(gfx::Size(frame_info_.width,
253                                                       frame_info_.height),
254                                             0);
255}
256
257// Implements VideoCaptureDevice::EventHandler.
258// OnIncomingCapturedFrame is called the thread running the capture device.
259// I.e.- DirectShow thread on windows and v4l2_thread on Linux.
260void VideoCaptureController::OnIncomingCapturedFrame(
261    const uint8* data,
262    int length,
263    base::Time timestamp,
264    int rotation,
265    bool flip_vert,
266    bool flip_horiz) {
267  DCHECK(frame_info_.color == media::VideoCaptureCapability::kI420 ||
268         frame_info_.color == media::VideoCaptureCapability::kYV12 ||
269         (rotation == 0 && !flip_vert && !flip_horiz));
270
271  scoped_refptr<media::VideoFrame> dst;
272  {
273    base::AutoLock lock(buffer_pool_lock_);
274    if (!buffer_pool_.get())
275      return;
276    dst = buffer_pool_->ReserveI420VideoFrame(gfx::Size(frame_info_.width,
277                                                        frame_info_.height),
278                                              rotation);
279  }
280
281  if (!dst.get())
282    return;
283
284  uint8* yplane = dst->data(media::VideoFrame::kYPlane);
285  uint8* uplane = dst->data(media::VideoFrame::kUPlane);
286  uint8* vplane = dst->data(media::VideoFrame::kVPlane);
287
288  // Do color conversion from the camera format to I420.
289  switch (frame_info_.color) {
290    case media::VideoCaptureCapability::kColorUnknown:  // Color format not set.
291      break;
292    case media::VideoCaptureCapability::kI420:
293      DCHECK(!chopped_width_ && !chopped_height_);
294      RotatePackedYV12Frame(
295          data, yplane, uplane, vplane, frame_info_.width, frame_info_.height,
296          rotation, flip_vert, flip_horiz);
297      break;
298    case media::VideoCaptureCapability::kYV12:
299      DCHECK(!chopped_width_ && !chopped_height_);
300      RotatePackedYV12Frame(
301          data, yplane, vplane, uplane, frame_info_.width, frame_info_.height,
302          rotation, flip_vert, flip_horiz);
303      break;
304    case media::VideoCaptureCapability::kNV21:
305      DCHECK(!chopped_width_ && !chopped_height_);
306      media::ConvertNV21ToYUV(data, yplane, uplane, vplane, frame_info_.width,
307                              frame_info_.height);
308      break;
309    case media::VideoCaptureCapability::kYUY2:
310      DCHECK(!chopped_width_ && !chopped_height_);
311      if (frame_info_.width * frame_info_.height * 2 != length) {
312        // If |length| of |data| does not match the expected width and height
313        // we can't convert the frame to I420. YUY2 is 2 bytes per pixel.
314        break;
315      }
316
317      media::ConvertYUY2ToYUV(data, yplane, uplane, vplane, frame_info_.width,
318                              frame_info_.height);
319      break;
320    case media::VideoCaptureCapability::kRGB24: {
321      int ystride = frame_info_.width;
322      int uvstride = frame_info_.width / 2;
323#if defined(OS_WIN)  // RGB on Windows start at the bottom line.
324      int rgb_stride = -3 * (frame_info_.width + chopped_width_);
325      const uint8* rgb_src = data + 3 * (frame_info_.width + chopped_width_) *
326          (frame_info_.height -1 + chopped_height_);
327#else
328      int rgb_stride = 3 * (frame_info_.width + chopped_width_);
329      const uint8* rgb_src = data;
330#endif
331      media::ConvertRGB24ToYUV(rgb_src, yplane, uplane, vplane,
332                               frame_info_.width, frame_info_.height,
333                               rgb_stride, ystride, uvstride);
334      break;
335    }
336    case media::VideoCaptureCapability::kARGB:
337      media::ConvertRGB32ToYUV(data, yplane, uplane, vplane, frame_info_.width,
338                               frame_info_.height,
339                               (frame_info_.width + chopped_width_) * 4,
340                               frame_info_.width, frame_info_.width / 2);
341      break;
342#if !defined(OS_IOS) && !defined(OS_ANDROID)
343    case media::VideoCaptureCapability::kMJPEG: {
344      int yplane_stride = frame_info_.width;
345      int uv_plane_stride = (frame_info_.width + 1) / 2;
346      int crop_x = 0;
347      int crop_y = 0;
348      libyuv::ConvertToI420(data, length, yplane, yplane_stride, uplane,
349                            uv_plane_stride, vplane, uv_plane_stride, crop_x,
350                            crop_y, frame_info_.width, frame_info_.height,
351                            frame_info_.width, frame_info_.height,
352                            libyuv::kRotate0, libyuv::FOURCC_MJPG);
353      break;
354    }
355#endif
356    default:
357      NOTREACHED();
358  }
359
360  BrowserThread::PostTask(BrowserThread::IO,
361      FROM_HERE,
362      base::Bind(&VideoCaptureController::DoIncomingCapturedFrameOnIOThread,
363                 this, dst, timestamp));
364}
365
366// OnIncomingCapturedVideoFrame is called the thread running the capture device.
367void VideoCaptureController::OnIncomingCapturedVideoFrame(
368    const scoped_refptr<media::VideoFrame>& frame,
369    base::Time timestamp) {
370
371  scoped_refptr<media::VideoFrame> target;
372  {
373    base::AutoLock lock(buffer_pool_lock_);
374
375    if (!buffer_pool_.get())
376      return;
377
378    // If this is a frame that belongs to the buffer pool, we can forward it
379    // directly to the IO thread and be done.
380    if (buffer_pool_->RecognizeReservedBuffer(
381        frame->shared_memory_handle()) >= 0) {
382      BrowserThread::PostTask(BrowserThread::IO,
383          FROM_HERE,
384          base::Bind(&VideoCaptureController::DoIncomingCapturedFrameOnIOThread,
385                     this, frame, timestamp));
386      return;
387    }
388    // Otherwise, this is a frame that belongs to the caller, and we must copy
389    // it to a frame from the buffer pool.
390    target = buffer_pool_->ReserveI420VideoFrame(gfx::Size(frame_info_.width,
391                                                           frame_info_.height),
392                                                 0);
393  }
394
395  if (!target.get())
396    return;
397
398  // Validate the inputs.
399  if (frame->coded_size() != target->coded_size())
400    return;  // Only exact copies are supported.
401  if (!(frame->format() == media::VideoFrame::I420 ||
402        frame->format() == media::VideoFrame::YV12 ||
403        frame->format() == media::VideoFrame::RGB32)) {
404    NOTREACHED() << "Unsupported format passed to OnIncomingCapturedVideoFrame";
405    return;
406  }
407
408  const int kYPlane = media::VideoFrame::kYPlane;
409  const int kUPlane = media::VideoFrame::kUPlane;
410  const int kVPlane = media::VideoFrame::kVPlane;
411  const int kAPlane = media::VideoFrame::kAPlane;
412  const int kRGBPlane = media::VideoFrame::kRGBPlane;
413
414  // Do color conversion from the camera format to I420.
415  switch (frame->format()) {
416#if defined(GOOGLE_TV)
417    case media::VideoFrame::HOLE:
418      // Fall-through to NOTREACHED() block.
419#endif
420    case media::VideoFrame::INVALID:
421    case media::VideoFrame::YV16:
422    case media::VideoFrame::EMPTY:
423    case media::VideoFrame::NATIVE_TEXTURE: {
424      NOTREACHED();
425      break;
426    }
427    case media::VideoFrame::I420:
428    case media::VideoFrame::YV12: {
429      DCHECK(!chopped_width_ && !chopped_height_);
430      media::CopyYPlane(frame->data(kYPlane),
431                        frame->stride(kYPlane),
432                        frame->rows(kYPlane),
433                        target.get());
434      media::CopyUPlane(frame->data(kUPlane),
435                        frame->stride(kUPlane),
436                        frame->rows(kUPlane),
437                        target.get());
438      media::CopyVPlane(frame->data(kVPlane),
439                        frame->stride(kVPlane),
440                        frame->rows(kVPlane),
441                        target.get());
442      break;
443    }
444    case media::VideoFrame::YV12A: {
445      DCHECK(!chopped_width_ && !chopped_height_);
446      media::CopyYPlane(frame->data(kYPlane),
447                        frame->stride(kYPlane),
448                        frame->rows(kYPlane),
449                        target.get());
450      media::CopyUPlane(frame->data(kUPlane),
451                        frame->stride(kUPlane),
452                        frame->rows(kUPlane),
453                        target.get());
454      media::CopyVPlane(frame->data(kVPlane),
455                        frame->stride(kVPlane),
456                        frame->rows(kVPlane),
457                        target.get());
458      media::CopyAPlane(frame->data(kAPlane),
459                        frame->stride(kAPlane),
460                        frame->rows(kAPlane),
461                        target.get());
462      break;
463    }
464    case media::VideoFrame::RGB32: {
465      media::ConvertRGB32ToYUV(frame->data(kRGBPlane),
466                               target->data(kYPlane),
467                               target->data(kUPlane),
468                               target->data(kVPlane),
469                               target->coded_size().width(),
470                               target->coded_size().height(),
471                               frame->stride(kRGBPlane),
472                               target->stride(kYPlane),
473                               target->stride(kUPlane));
474      break;
475    }
476  }
477
478  BrowserThread::PostTask(BrowserThread::IO,
479      FROM_HERE,
480      base::Bind(&VideoCaptureController::DoIncomingCapturedFrameOnIOThread,
481                 this, target, timestamp));
482}
483
484void VideoCaptureController::OnError() {
485  BrowserThread::PostTask(BrowserThread::IO,
486      FROM_HERE,
487      base::Bind(&VideoCaptureController::DoErrorOnIOThread, this));
488}
489
490void VideoCaptureController::OnFrameInfo(
491    const media::VideoCaptureCapability& info) {
492  frame_info_= info;
493  // Handle cases when |info| has odd numbers for width/height.
494  if (info.width & 1) {
495    --frame_info_.width;
496    chopped_width_ = 1;
497  } else {
498    chopped_width_ = 0;
499  }
500  if (info.height & 1) {
501    --frame_info_.height;
502    chopped_height_ = 1;
503  } else {
504    chopped_height_ = 0;
505  }
506  BrowserThread::PostTask(BrowserThread::IO,
507      FROM_HERE,
508      base::Bind(&VideoCaptureController::DoFrameInfoOnIOThread, this));
509}
510
511VideoCaptureController::~VideoCaptureController() {
512  buffer_pool_ = NULL;  // Release all buffers.
513  STLDeleteContainerPointers(controller_clients_.begin(),
514                             controller_clients_.end());
515  STLDeleteContainerPointers(pending_clients_.begin(),
516                             pending_clients_.end());
517}
518
519// Called by VideoCaptureManager when a device have been stopped.
520void VideoCaptureController::OnDeviceStopped() {
521  BrowserThread::PostTask(BrowserThread::IO,
522      FROM_HERE,
523      base::Bind(&VideoCaptureController::DoDeviceStoppedOnIOThread, this));
524}
525
526void VideoCaptureController::DoIncomingCapturedFrameOnIOThread(
527    const scoped_refptr<media::VideoFrame>& reserved_frame,
528    base::Time timestamp) {
529  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
530
531  if (!buffer_pool_.get())
532    return;
533
534  int buffer_id = buffer_pool_->RecognizeReservedBuffer(
535      reserved_frame->shared_memory_handle());
536  if (buffer_id < 0) {
537    NOTREACHED();
538    return;
539  }
540
541  int count = 0;
542  if (state_ == VIDEO_CAPTURE_STATE_STARTED) {
543    for (ControllerClients::iterator client_it = controller_clients_.begin();
544         client_it != controller_clients_.end(); ++client_it) {
545      if ((*client_it)->session_closed)
546        continue;
547
548      (*client_it)->event_handler->OnBufferReady((*client_it)->controller_id,
549                                                 buffer_id, timestamp);
550      (*client_it)->buffers.insert(buffer_id);
551      count++;
552    }
553  }
554
555  buffer_pool_->HoldForConsumers(buffer_id, count);
556}
557
558void VideoCaptureController::DoFrameInfoOnIOThread() {
559  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
560  DCHECK(!buffer_pool_.get())
561      << "Device is restarted without releasing shared memory.";
562
563  // Allocate memory only when device has been started.
564  if (state_ != VIDEO_CAPTURE_STATE_STARTED)
565    return;
566
567  scoped_refptr<VideoCaptureBufferPool> buffer_pool =
568      new VideoCaptureBufferPool(frame_info_.width * frame_info_.height * 3 / 2,
569                                 kNoOfBuffers);
570
571  // Check whether all buffers were created successfully.
572  if (!buffer_pool->Allocate()) {
573    state_ = VIDEO_CAPTURE_STATE_ERROR;
574    for (ControllerClients::iterator client_it = controller_clients_.begin();
575         client_it != controller_clients_.end(); ++client_it) {
576      (*client_it)->event_handler->OnError((*client_it)->controller_id);
577    }
578    return;
579  }
580
581  {
582    base::AutoLock lock(buffer_pool_lock_);
583    buffer_pool_ = buffer_pool;
584  }
585  frame_info_available_ = true;
586
587  for (ControllerClients::iterator client_it = controller_clients_.begin();
588       client_it != controller_clients_.end(); ++client_it) {
589    SendFrameInfoAndBuffers(*client_it);
590  }
591}
592
593void VideoCaptureController::DoErrorOnIOThread() {
594  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
595  state_ = VIDEO_CAPTURE_STATE_ERROR;
596  ControllerClients::iterator client_it;
597  for (client_it = controller_clients_.begin();
598       client_it != controller_clients_.end(); ++client_it) {
599    (*client_it)->event_handler->OnError((*client_it)->controller_id);
600  }
601  for (client_it = pending_clients_.begin();
602       client_it != pending_clients_.end(); ++client_it) {
603    (*client_it)->event_handler->OnError((*client_it)->controller_id);
604  }
605}
606
607void VideoCaptureController::DoDeviceStoppedOnIOThread() {
608  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
609  device_in_use_ = false;
610  if (state_ == VIDEO_CAPTURE_STATE_STOPPING) {
611    PostStopping();
612  }
613}
614
615void VideoCaptureController::SendFrameInfoAndBuffers(ControllerClient* client) {
616  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
617  DCHECK(frame_info_available_);
618  client->event_handler->OnFrameInfo(client->controller_id,
619                                     frame_info_.width, frame_info_.height,
620                                     frame_info_.frame_rate);
621  if (!buffer_pool_.get())
622    return;
623
624  for (int buffer_id = 0; buffer_id < buffer_pool_->count(); ++buffer_id) {
625    base::SharedMemoryHandle remote_handle =
626        buffer_pool_->ShareToProcess(buffer_id, client->render_process_handle);
627
628    client->event_handler->OnBufferCreated(client->controller_id,
629                                           remote_handle,
630                                           buffer_pool_->GetMemorySize(),
631                                           buffer_id);
632  }
633}
634
635VideoCaptureController::ControllerClient*
636VideoCaptureController::FindClient(
637    const VideoCaptureControllerID& id,
638    VideoCaptureControllerEventHandler* handler,
639    const ControllerClients& clients) {
640  for (ControllerClients::const_iterator client_it = clients.begin();
641       client_it != clients.end(); ++client_it) {
642    if ((*client_it)->controller_id == id &&
643        (*client_it)->event_handler == handler) {
644      return *client_it;
645    }
646  }
647  return NULL;
648}
649
650VideoCaptureController::ControllerClient*
651VideoCaptureController::FindClient(
652    int session_id,
653    const ControllerClients& clients) {
654  for (ControllerClients::const_iterator client_it = clients.begin();
655       client_it != clients.end(); ++client_it) {
656    if ((*client_it)->parameters.session_id == session_id) {
657      return *client_it;
658    }
659  }
660  return NULL;
661}
662
663// This function is called when all buffers have been returned to controller,
664// or when device is stopped. It decides whether the device needs to be
665// restarted.
666void VideoCaptureController::PostStopping() {
667  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
668  DCHECK_EQ(state_, VIDEO_CAPTURE_STATE_STOPPING);
669
670  // When clients still have some buffers, or device has not been stopped yet,
671  // do nothing.
672  if ((buffer_pool_.get() && buffer_pool_->IsAnyBufferHeldForConsumers()) ||
673      device_in_use_)
674    return;
675
676  {
677    base::AutoLock lock(buffer_pool_lock_);
678    buffer_pool_ = NULL;
679  }
680
681  // No more client. Therefore the controller is stopped.
682  if (controller_clients_.empty() && pending_clients_.empty()) {
683    state_ = VIDEO_CAPTURE_STATE_STOPPED;
684    return;
685  }
686
687  // Restart the device.
688  current_params_.width = 0;
689  current_params_.height = 0;
690  ControllerClients::iterator client_it;
691  for (client_it = controller_clients_.begin();
692       client_it != controller_clients_.end(); ++client_it) {
693    if (current_params_.width < (*client_it)->parameters.width)
694      current_params_.width = (*client_it)->parameters.width;
695    if (current_params_.height < (*client_it)->parameters.height)
696      current_params_.height = (*client_it)->parameters.height;
697  }
698  for (client_it = pending_clients_.begin();
699       client_it != pending_clients_.end(); ) {
700    if (current_params_.width < (*client_it)->parameters.width)
701      current_params_.width = (*client_it)->parameters.width;
702    if (current_params_.height < (*client_it)->parameters.height)
703      current_params_.height = (*client_it)->parameters.height;
704    controller_clients_.push_back((*client_it));
705    pending_clients_.erase(client_it++);
706  }
707  // Request the manager to start the actual capture.
708  video_capture_manager_->Start(current_params_, this);
709  state_ = VIDEO_CAPTURE_STATE_STARTED;
710  device_in_use_ = true;
711}
712
713}  // namespace content
714