video_capture_controller.cc revision d0247b1b59f9c528cb6df88b4f2b9afaf80d181e
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/debug/trace_event.h"
11#include "base/memory/scoped_ptr.h"
12#include "base/stl_util.h"
13#include "content/browser/renderer_host/media/media_stream_manager.h"
14#include "content/browser/renderer_host/media/video_capture_manager.h"
15#include "content/public/browser/browser_thread.h"
16#include "media/base/video_frame.h"
17#include "media/base/video_util.h"
18#include "media/base/yuv_convert.h"
19
20#if !defined(OS_IOS) && !defined(OS_ANDROID)
21#include "third_party/libyuv/include/libyuv.h"
22#endif
23
24namespace {
25
26#if defined(OS_IOS) || defined(OS_ANDROID)
27// TODO(wjia): Support stride.
28void RotatePackedYV12Frame(
29    const uint8* src,
30    uint8* dest_yplane,
31    uint8* dest_uplane,
32    uint8* dest_vplane,
33    int width,
34    int height,
35    int rotation,
36    bool flip_vert,
37    bool flip_horiz) {
38  media::RotatePlaneByPixels(
39      src, dest_yplane, width, height, rotation, flip_vert, flip_horiz);
40  int y_size = width * height;
41  src += y_size;
42  media::RotatePlaneByPixels(
43      src, dest_uplane, width/2, height/2, rotation, flip_vert, flip_horiz);
44  src += y_size/4;
45  media::RotatePlaneByPixels(
46      src, dest_vplane, width/2, height/2, rotation, flip_vert, flip_horiz);
47}
48#endif  // #if defined(OS_IOS) || defined(OS_ANDROID)
49
50}  // namespace
51
52namespace content {
53
54// The number of buffers that VideoCaptureBufferPool should allocate.
55static const int kNoOfBuffers = 3;
56
57struct VideoCaptureController::ControllerClient {
58  ControllerClient(
59      const VideoCaptureControllerID& id,
60      VideoCaptureControllerEventHandler* handler,
61      base::ProcessHandle render_process,
62      const media::VideoCaptureParams& params)
63      : controller_id(id),
64        event_handler(handler),
65        render_process_handle(render_process),
66        parameters(params),
67        session_closed(false) {
68  }
69
70  ~ControllerClient() {}
71
72  // ID used for identifying this object.
73  VideoCaptureControllerID controller_id;
74  VideoCaptureControllerEventHandler* event_handler;
75
76  // Handle to the render process that will receive the capture buffers.
77  base::ProcessHandle render_process_handle;
78  media::VideoCaptureParams parameters;
79
80  // Buffers used by this client.
81  std::set<int> buffers;
82
83  // State of capture session, controlled by VideoCaptureManager directly. This
84  // transitions to true as soon as StopSession() occurs, at which point the
85  // client is sent an OnEnded() event. However, because the client retains a
86  // VideoCaptureController* pointer, its ControllerClient entry lives on until
87  // it unregisters itself via RemoveClient(), which may happen asynchronously.
88  //
89  // TODO(nick): If we changed the semantics of VideoCaptureHost so that
90  // OnEnded() events were processed synchronously (with the RemoveClient() done
91  // implicitly), we could avoid tracking this state here in the Controller, and
92  // simplify the code in both places.
93  bool session_closed;
94};
95
96// Receives events from the VideoCaptureDevice and posts them to a
97// VideoCaptureController on the IO thread. An instance of this class may safely
98// outlive its target VideoCaptureController.
99//
100// Methods of this class may be called from any thread, and in practice will
101// often be called on some auxiliary thread depending on the platform and the
102// device type; including, for example, the DirectShow thread on Windows, the
103// v4l2_thread on Linux, and the UI thread for tab capture.
104class VideoCaptureController::VideoCaptureDeviceClient
105    : public media::VideoCaptureDevice::EventHandler {
106 public:
107  explicit VideoCaptureDeviceClient(
108      const base::WeakPtr<VideoCaptureController>& controller);
109  virtual ~VideoCaptureDeviceClient();
110
111  // VideoCaptureDevice::EventHandler implementation.
112  virtual scoped_refptr<media::VideoFrame> ReserveOutputBuffer() OVERRIDE;
113  virtual void OnIncomingCapturedFrame(const uint8* data,
114                                       int length,
115                                       base::Time timestamp,
116                                       int rotation,
117                                       bool flip_vert,
118                                       bool flip_horiz) OVERRIDE;
119  virtual void OnIncomingCapturedVideoFrame(
120      const scoped_refptr<media::VideoFrame>& frame,
121      base::Time timestamp) OVERRIDE;
122  virtual void OnError() OVERRIDE;
123  virtual void OnFrameInfo(
124      const media::VideoCaptureCapability& info) OVERRIDE;
125  virtual void OnFrameInfoChanged(
126      const media::VideoCaptureCapability& info) OVERRIDE;
127
128 private:
129  // The controller to which we post events.
130  const base::WeakPtr<VideoCaptureController> controller_;
131
132  // The pool of shared-memory buffers used for capturing.
133  scoped_refptr<VideoCaptureBufferPool> buffer_pool_;
134
135  // Chopped pixels in width/height in case video capture device has odd
136  // numbers for width/height.
137  int chopped_width_;
138  int chopped_height_;
139
140  // Tracks the current frame format.
141  media::VideoCaptureCapability frame_info_;
142
143  // For NV21 we have to do color conversion into the intermediate buffer and
144  // from there the rotations. This variable won't be needed after
145  // http://crbug.com/292400
146#if defined(OS_IOS) || defined(OS_ANDROID)
147  scoped_ptr<uint8[]> i420_intermediate_buffer_;
148#endif  // #if defined(OS_IOS) || defined(OS_ANDROID)
149};
150
151VideoCaptureController::VideoCaptureController()
152    : state_(VIDEO_CAPTURE_STATE_STARTED),
153      weak_ptr_factory_(this) {
154  memset(&current_params_, 0, sizeof(current_params_));
155}
156
157VideoCaptureController::VideoCaptureDeviceClient::VideoCaptureDeviceClient(
158    const base::WeakPtr<VideoCaptureController>& controller)
159    : controller_(controller),
160      chopped_width_(0),
161      chopped_height_(0) {}
162
163VideoCaptureController::VideoCaptureDeviceClient::~VideoCaptureDeviceClient() {}
164
165base::WeakPtr<VideoCaptureController> VideoCaptureController::GetWeakPtr() {
166  return weak_ptr_factory_.GetWeakPtr();
167}
168
169scoped_ptr<media::VideoCaptureDevice::EventHandler>
170VideoCaptureController::NewDeviceClient() {
171  scoped_ptr<media::VideoCaptureDevice::EventHandler> result(
172      new VideoCaptureDeviceClient(this->GetWeakPtr()));
173  return result.Pass();
174}
175
176void VideoCaptureController::AddClient(
177    const VideoCaptureControllerID& id,
178    VideoCaptureControllerEventHandler* event_handler,
179    base::ProcessHandle render_process,
180    const media::VideoCaptureParams& params) {
181  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
182  DVLOG(1) << "VideoCaptureController::AddClient, id " << id.device_id
183           << ", (" << params.width
184           << ", " << params.height
185           << ", " << params.frame_rate
186           << ", " << params.session_id
187           << ")";
188
189  // Signal error in case device is already in error state.
190  if (state_ == VIDEO_CAPTURE_STATE_ERROR) {
191    event_handler->OnError(id);
192    return;
193  }
194
195  // Do nothing if this client has called AddClient before.
196  if (FindClient(id, event_handler, controller_clients_))
197    return;
198
199  ControllerClient* client = new ControllerClient(id, event_handler,
200                                                  render_process, params);
201  // If we already have gotten frame_info from the device, repeat it to the new
202  // client.
203  if (state_ == VIDEO_CAPTURE_STATE_STARTED) {
204    if (frame_info_.IsValid()) {
205      SendFrameInfoAndBuffers(client);
206    }
207    controller_clients_.push_back(client);
208    return;
209  }
210}
211
212int VideoCaptureController::RemoveClient(
213    const VideoCaptureControllerID& id,
214    VideoCaptureControllerEventHandler* event_handler) {
215  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
216  DVLOG(1) << "VideoCaptureController::RemoveClient, id " << id.device_id;
217
218  ControllerClient* client = FindClient(id, event_handler, controller_clients_);
219  if (!client)
220    return kInvalidMediaCaptureSessionId;
221
222  // Take back all buffers held by the |client|.
223  if (buffer_pool_.get()) {
224    for (std::set<int>::iterator buffer_it = client->buffers.begin();
225         buffer_it != client->buffers.end();
226         ++buffer_it) {
227      int buffer_id = *buffer_it;
228      buffer_pool_->RelinquishConsumerHold(buffer_id, 1);
229    }
230  }
231  client->buffers.clear();
232
233  int session_id = client->parameters.session_id;
234  controller_clients_.remove(client);
235  delete client;
236
237  return session_id;
238}
239
240void VideoCaptureController::StopSession(int session_id) {
241  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
242  DVLOG(1) << "VideoCaptureController::StopSession, id " << session_id;
243
244  ControllerClient* client = FindClient(session_id, controller_clients_);
245
246  if (client) {
247    client->session_closed = true;
248    client->event_handler->OnEnded(client->controller_id);
249  }
250}
251
252void VideoCaptureController::ReturnBuffer(
253    const VideoCaptureControllerID& id,
254    VideoCaptureControllerEventHandler* event_handler,
255    int buffer_id) {
256  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
257
258  ControllerClient* client = FindClient(id, event_handler, controller_clients_);
259
260  // If this buffer is not held by this client, or this client doesn't exist
261  // in controller, do nothing.
262  if (!client ||
263      client->buffers.find(buffer_id) == client->buffers.end()) {
264    NOTREACHED();
265    return;
266  }
267
268  client->buffers.erase(buffer_id);
269  buffer_pool_->RelinquishConsumerHold(buffer_id, 1);
270}
271
272scoped_refptr<media::VideoFrame>
273VideoCaptureController::VideoCaptureDeviceClient::ReserveOutputBuffer() {
274  return buffer_pool_->ReserveI420VideoFrame(gfx::Size(frame_info_.width,
275                                                       frame_info_.height),
276                                             0);
277}
278
279#if !defined(OS_IOS) && !defined(OS_ANDROID)
280void VideoCaptureController::VideoCaptureDeviceClient::OnIncomingCapturedFrame(
281    const uint8* data,
282    int length,
283    base::Time timestamp,
284    int rotation,
285    bool flip_vert,
286    bool flip_horiz) {
287  TRACE_EVENT0("video", "VideoCaptureController::OnIncomingCapturedFrame");
288
289  if (!buffer_pool_.get())
290    return;
291  scoped_refptr<media::VideoFrame> dst = buffer_pool_->ReserveI420VideoFrame(
292      gfx::Size(frame_info_.width, frame_info_.height), rotation);
293
294  if (!dst.get())
295    return;
296
297  uint8* yplane = dst->data(media::VideoFrame::kYPlane);
298  uint8* uplane = dst->data(media::VideoFrame::kUPlane);
299  uint8* vplane = dst->data(media::VideoFrame::kVPlane);
300  int yplane_stride = frame_info_.width;
301  int uv_plane_stride = (frame_info_.width + 1) / 2;
302  int crop_x = 0;
303  int crop_y = 0;
304  libyuv::FourCC origin_colorspace = libyuv::FOURCC_ANY;
305  // Assuming rotation happens first and flips next, we can consolidate both
306  // vertical and horizontal flips together with rotation into two variables:
307  // new_rotation = (rotation + 180 * vertical_flip) modulo 360
308  // new_vertical_flip = horizontal_flip XOR vertical_flip
309  int new_rotation_angle = (rotation + 180 * flip_vert) % 360;
310  libyuv::RotationMode rotation_mode = libyuv::kRotate0;
311  if (new_rotation_angle == 90)
312    rotation_mode = libyuv::kRotate90;
313  else if (new_rotation_angle == 180)
314    rotation_mode = libyuv::kRotate180;
315  else if (new_rotation_angle == 270)
316    rotation_mode = libyuv::kRotate270;
317
318  switch (frame_info_.color) {
319    case media::PIXEL_FORMAT_UNKNOWN:  // Color format not set.
320      break;
321    case media::PIXEL_FORMAT_I420:
322      DCHECK(!chopped_width_ && !chopped_height_);
323      origin_colorspace = libyuv::FOURCC_I420;
324      break;
325    case media::PIXEL_FORMAT_YV12:
326      DCHECK(!chopped_width_ && !chopped_height_);
327      origin_colorspace = libyuv::FOURCC_YV12;
328      break;
329    case media::PIXEL_FORMAT_NV21:
330      DCHECK(!chopped_width_ && !chopped_height_);
331      origin_colorspace = libyuv::FOURCC_NV12;
332      break;
333    case media::PIXEL_FORMAT_YUY2:
334      DCHECK(!chopped_width_ && !chopped_height_);
335      origin_colorspace = libyuv::FOURCC_YUY2;
336      break;
337    case media::PIXEL_FORMAT_UYVY:
338      DCHECK(!chopped_width_ && !chopped_height_);
339      origin_colorspace = libyuv::FOURCC_UYVY;
340      break;
341    case media::PIXEL_FORMAT_RGB24:
342      origin_colorspace = libyuv::FOURCC_RAW;
343      break;
344    case media::PIXEL_FORMAT_ARGB:
345      origin_colorspace = libyuv::FOURCC_ARGB;
346      break;
347    case media::PIXEL_FORMAT_MJPEG:
348      origin_colorspace = libyuv::FOURCC_MJPG;
349      break;
350    default:
351      NOTREACHED();
352  }
353
354  int need_convert_rgb24_on_win = false;
355#if defined(OS_WIN)
356  // kRGB24 on Windows start at the bottom line and has a negative stride. This
357  // is not supported by libyuv, so the media API is used instead.
358  if (frame_info_.color == media::PIXEL_FORMAT_RGB24) {
359    // Rotation and flipping is not supported in kRGB24 and OS_WIN case.
360    DCHECK(!rotation && !flip_vert && !flip_horiz);
361    need_convert_rgb24_on_win = true;
362  }
363#endif
364  if (need_convert_rgb24_on_win) {
365    int rgb_stride = -3 * (frame_info_.width + chopped_width_);
366    const uint8* rgb_src =
367        data + 3 * (frame_info_.width + chopped_width_) *
368                   (frame_info_.height - 1 + chopped_height_);
369    media::ConvertRGB24ToYUV(rgb_src,
370                             yplane,
371                             uplane,
372                             vplane,
373                             frame_info_.width,
374                             frame_info_.height,
375                             rgb_stride,
376                             yplane_stride,
377                             uv_plane_stride);
378  } else {
379    libyuv::ConvertToI420(
380        data,
381        length,
382        yplane,
383        yplane_stride,
384        uplane,
385        uv_plane_stride,
386        vplane,
387        uv_plane_stride,
388        crop_x,
389        crop_y,
390        frame_info_.width + chopped_width_,
391        frame_info_.height * (flip_vert ^ flip_horiz ? -1 : 1),
392        frame_info_.width,
393        frame_info_.height,
394        rotation_mode,
395        origin_colorspace);
396  }
397  BrowserThread::PostTask(
398      BrowserThread::IO,
399      FROM_HERE,
400      base::Bind(&VideoCaptureController::DoIncomingCapturedFrameOnIOThread,
401                 controller_,
402                 dst,
403                 timestamp));
404}
405#else
406void VideoCaptureController::VideoCaptureDeviceClient::OnIncomingCapturedFrame(
407    const uint8* data,
408    int length,
409    base::Time timestamp,
410    int rotation,
411    bool flip_vert,
412    bool flip_horiz) {
413  DCHECK(frame_info_.color == media::PIXEL_FORMAT_I420 ||
414         frame_info_.color == media::PIXEL_FORMAT_YV12 ||
415         frame_info_.color == media::PIXEL_FORMAT_NV21 ||
416         (rotation == 0 && !flip_vert && !flip_horiz));
417
418  TRACE_EVENT0("video", "VideoCaptureController::OnIncomingCapturedFrame");
419
420  if (!buffer_pool_)
421    return;
422  scoped_refptr<media::VideoFrame> dst =
423      buffer_pool_->ReserveI420VideoFrame(gfx::Size(frame_info_.width,
424                                                    frame_info_.height),
425                                          rotation);
426
427  if (!dst.get())
428    return;
429
430  uint8* yplane = dst->data(media::VideoFrame::kYPlane);
431  uint8* uplane = dst->data(media::VideoFrame::kUPlane);
432  uint8* vplane = dst->data(media::VideoFrame::kVPlane);
433
434  // Do color conversion from the camera format to I420.
435  switch (frame_info_.color) {
436    case media::PIXEL_FORMAT_UNKNOWN:  // Color format not set.
437      break;
438    case media::PIXEL_FORMAT_I420:
439      DCHECK(!chopped_width_ && !chopped_height_);
440      RotatePackedYV12Frame(
441          data, yplane, uplane, vplane, frame_info_.width, frame_info_.height,
442          rotation, flip_vert, flip_horiz);
443      break;
444    case media::PIXEL_FORMAT_YV12:
445      DCHECK(!chopped_width_ && !chopped_height_);
446      RotatePackedYV12Frame(
447          data, yplane, vplane, uplane, frame_info_.width, frame_info_.height,
448          rotation, flip_vert, flip_horiz);
449      break;
450    case media::PIXEL_FORMAT_NV21: {
451      DCHECK(!chopped_width_ && !chopped_height_);
452      int num_pixels = frame_info_.width * frame_info_.height;
453      media::ConvertNV21ToYUV(data,
454                              &i420_intermediate_buffer_[0],
455                              &i420_intermediate_buffer_[num_pixels],
456                              &i420_intermediate_buffer_[num_pixels * 5 / 4],
457                              frame_info_.width,
458                              frame_info_.height);
459      RotatePackedYV12Frame(
460          i420_intermediate_buffer_.get(), yplane, uplane, vplane,
461          frame_info_.width, frame_info_.height,
462          rotation, flip_vert, flip_horiz);
463       break;
464    }
465    case media::PIXEL_FORMAT_YUY2:
466      DCHECK(!chopped_width_ && !chopped_height_);
467      if (frame_info_.width * frame_info_.height * 2 != length) {
468        // If |length| of |data| does not match the expected width and height
469        // we can't convert the frame to I420. YUY2 is 2 bytes per pixel.
470        break;
471      }
472
473      media::ConvertYUY2ToYUV(data, yplane, uplane, vplane, frame_info_.width,
474                              frame_info_.height);
475      break;
476    case media::PIXEL_FORMAT_RGB24: {
477      int ystride = frame_info_.width;
478      int uvstride = frame_info_.width / 2;
479      int rgb_stride = 3 * (frame_info_.width + chopped_width_);
480      const uint8* rgb_src = data;
481      media::ConvertRGB24ToYUV(rgb_src, yplane, uplane, vplane,
482                               frame_info_.width, frame_info_.height,
483                               rgb_stride, ystride, uvstride);
484      break;
485    }
486    case media::PIXEL_FORMAT_ARGB:
487      media::ConvertRGB32ToYUV(data, yplane, uplane, vplane, frame_info_.width,
488                               frame_info_.height,
489                               (frame_info_.width + chopped_width_) * 4,
490                               frame_info_.width, frame_info_.width / 2);
491      break;
492    default:
493      NOTREACHED();
494  }
495
496  BrowserThread::PostTask(BrowserThread::IO,
497      FROM_HERE,
498      base::Bind(&VideoCaptureController::DoIncomingCapturedFrameOnIOThread,
499                 controller_, dst, timestamp));
500}
501#endif  // #if !defined(OS_IOS) && !defined(OS_ANDROID)
502
503void
504VideoCaptureController::VideoCaptureDeviceClient::OnIncomingCapturedVideoFrame(
505    const scoped_refptr<media::VideoFrame>& frame,
506    base::Time timestamp) {
507  if (!buffer_pool_)
508    return;
509
510  // If this is a frame that belongs to the buffer pool, we can forward it
511  // directly to the IO thread and be done.
512  if (buffer_pool_->RecognizeReservedBuffer(
513      frame->shared_memory_handle()) >= 0) {
514    BrowserThread::PostTask(BrowserThread::IO,
515        FROM_HERE,
516        base::Bind(&VideoCaptureController::DoIncomingCapturedFrameOnIOThread,
517                   controller_, frame, timestamp));
518    return;
519  }
520
521  // Otherwise, this is a frame that belongs to the caller, and we must copy
522  // it to a frame from the buffer pool.
523  scoped_refptr<media::VideoFrame> target =
524      buffer_pool_->ReserveI420VideoFrame(gfx::Size(frame_info_.width,
525                                                    frame_info_.height),
526                                          0);
527
528  if (!target.get())
529    return;
530
531  // Validate the inputs.
532  if (frame->coded_size() != target->coded_size())
533    return;  // Only exact copies are supported.
534  if (!(frame->format() == media::VideoFrame::I420 ||
535        frame->format() == media::VideoFrame::YV12 ||
536        frame->format() == media::VideoFrame::RGB32)) {
537    NOTREACHED() << "Unsupported format passed to OnIncomingCapturedVideoFrame";
538    return;
539  }
540
541  const int kYPlane = media::VideoFrame::kYPlane;
542  const int kUPlane = media::VideoFrame::kUPlane;
543  const int kVPlane = media::VideoFrame::kVPlane;
544  const int kAPlane = media::VideoFrame::kAPlane;
545  const int kRGBPlane = media::VideoFrame::kRGBPlane;
546
547  // Do color conversion from the camera format to I420.
548  switch (frame->format()) {
549#if defined(GOOGLE_TV)
550    case media::VideoFrame::HOLE:
551      // Fall-through to NOTREACHED() block.
552#endif
553    case media::VideoFrame::INVALID:
554    case media::VideoFrame::YV16:
555    case media::VideoFrame::EMPTY:
556    case media::VideoFrame::NATIVE_TEXTURE: {
557      NOTREACHED();
558      break;
559    }
560    case media::VideoFrame::I420:
561    case media::VideoFrame::YV12: {
562      DCHECK(!chopped_width_ && !chopped_height_);
563      media::CopyYPlane(frame->data(kYPlane),
564                        frame->stride(kYPlane),
565                        frame->rows(kYPlane),
566                        target.get());
567      media::CopyUPlane(frame->data(kUPlane),
568                        frame->stride(kUPlane),
569                        frame->rows(kUPlane),
570                        target.get());
571      media::CopyVPlane(frame->data(kVPlane),
572                        frame->stride(kVPlane),
573                        frame->rows(kVPlane),
574                        target.get());
575      break;
576    }
577    case media::VideoFrame::YV12A: {
578      DCHECK(!chopped_width_ && !chopped_height_);
579      media::CopyYPlane(frame->data(kYPlane),
580                        frame->stride(kYPlane),
581                        frame->rows(kYPlane),
582                        target.get());
583      media::CopyUPlane(frame->data(kUPlane),
584                        frame->stride(kUPlane),
585                        frame->rows(kUPlane),
586                        target.get());
587      media::CopyVPlane(frame->data(kVPlane),
588                        frame->stride(kVPlane),
589                        frame->rows(kVPlane),
590                        target.get());
591      media::CopyAPlane(frame->data(kAPlane),
592                        frame->stride(kAPlane),
593                        frame->rows(kAPlane),
594                        target.get());
595      break;
596    }
597    case media::VideoFrame::RGB32: {
598      media::ConvertRGB32ToYUV(frame->data(kRGBPlane),
599                               target->data(kYPlane),
600                               target->data(kUPlane),
601                               target->data(kVPlane),
602                               target->coded_size().width(),
603                               target->coded_size().height(),
604                               frame->stride(kRGBPlane),
605                               target->stride(kYPlane),
606                               target->stride(kUPlane));
607      break;
608    }
609  }
610
611  BrowserThread::PostTask(BrowserThread::IO,
612      FROM_HERE,
613      base::Bind(&VideoCaptureController::DoIncomingCapturedFrameOnIOThread,
614                 controller_, target, timestamp));
615}
616
617void VideoCaptureController::VideoCaptureDeviceClient::OnError() {
618  BrowserThread::PostTask(BrowserThread::IO,
619      FROM_HERE,
620      base::Bind(&VideoCaptureController::DoErrorOnIOThread, controller_));
621}
622
623void VideoCaptureController::VideoCaptureDeviceClient::OnFrameInfo(
624    const media::VideoCaptureCapability& info) {
625  frame_info_ = info;
626  // Handle cases when |info| has odd numbers for width/height.
627  if (info.width & 1) {
628    --frame_info_.width;
629    chopped_width_ = 1;
630  } else {
631    chopped_width_ = 0;
632  }
633  if (info.height & 1) {
634    --frame_info_.height;
635    chopped_height_ = 1;
636  } else {
637    chopped_height_ = 0;
638  }
639#if defined(OS_IOS) || defined(OS_ANDROID)
640  if (frame_info_.color == media::PIXEL_FORMAT_NV21 &&
641      !i420_intermediate_buffer_) {
642    i420_intermediate_buffer_.reset(
643        new uint8[frame_info_.width * frame_info_.height * 12 / 8]);
644  }
645#endif  // #if defined(OS_IOS) || defined(OS_ANDROID)
646
647  DCHECK(!buffer_pool_.get());
648
649  // TODO(nick): Give BufferPool the same lifetime as the controller, have it
650  // support frame size changes, and stop checking it for NULL everywhere.
651  // http://crbug.com/266082
652  buffer_pool_ = new VideoCaptureBufferPool(
653      media::VideoFrame::AllocationSize(
654            media::VideoFrame::I420,
655            gfx::Size(frame_info_.width, frame_info_.height)),
656      kNoOfBuffers);
657
658  // Check whether all buffers were created successfully.
659  if (!buffer_pool_->Allocate()) {
660    // Transition to the error state.
661    buffer_pool_ = NULL;
662    OnError();
663    return;
664  }
665
666  BrowserThread::PostTask(BrowserThread::IO,
667      FROM_HERE,
668      base::Bind(&VideoCaptureController::DoFrameInfoOnIOThread, controller_,
669                 frame_info_, buffer_pool_));
670}
671
672void VideoCaptureController::VideoCaptureDeviceClient::OnFrameInfoChanged(
673    const media::VideoCaptureCapability& info) {
674  BrowserThread::PostTask(BrowserThread::IO,
675      FROM_HERE,
676      base::Bind(&VideoCaptureController::DoFrameInfoChangedOnIOThread,
677                 controller_, info));
678}
679
680VideoCaptureController::~VideoCaptureController() {
681  buffer_pool_ = NULL;  // Release all buffers.
682  STLDeleteContainerPointers(controller_clients_.begin(),
683                             controller_clients_.end());
684}
685
686void VideoCaptureController::DoIncomingCapturedFrameOnIOThread(
687    const scoped_refptr<media::VideoFrame>& reserved_frame,
688    base::Time timestamp) {
689  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
690
691  if (!buffer_pool_.get())
692    return;
693
694  int buffer_id = buffer_pool_->RecognizeReservedBuffer(
695      reserved_frame->shared_memory_handle());
696  if (buffer_id < 0) {
697    NOTREACHED();
698    return;
699  }
700
701  int count = 0;
702  if (state_ == VIDEO_CAPTURE_STATE_STARTED) {
703    for (ControllerClients::iterator client_it = controller_clients_.begin();
704         client_it != controller_clients_.end(); ++client_it) {
705      if ((*client_it)->session_closed)
706        continue;
707
708      (*client_it)->event_handler->OnBufferReady((*client_it)->controller_id,
709                                                 buffer_id, timestamp);
710      (*client_it)->buffers.insert(buffer_id);
711      count++;
712    }
713  }
714
715  buffer_pool_->HoldForConsumers(buffer_id, count);
716}
717
718void VideoCaptureController::DoFrameInfoOnIOThread(
719    const media::VideoCaptureCapability& frame_info,
720    const scoped_refptr<VideoCaptureBufferPool>& buffer_pool) {
721  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
722  DCHECK(!buffer_pool_.get()) << "Frame info should happen only once.";
723
724  // Allocate memory only when device has been started.
725  if (state_ != VIDEO_CAPTURE_STATE_STARTED)
726    return;
727
728  frame_info_ = frame_info;
729  buffer_pool_ = buffer_pool;
730
731  for (ControllerClients::iterator client_it = controller_clients_.begin();
732       client_it != controller_clients_.end(); ++client_it) {
733    if ((*client_it)->session_closed)
734        continue;
735
736    SendFrameInfoAndBuffers(*client_it);
737  }
738}
739
740void VideoCaptureController::DoFrameInfoChangedOnIOThread(
741    const media::VideoCaptureCapability& info) {
742  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
743  // TODO(mcasas): Here we should reallocate the VideoCaptureBufferPool, if
744  // needed, to support the new video capture format. See crbug.com/266082.
745  for (ControllerClients::iterator client_it = controller_clients_.begin();
746       client_it != controller_clients_.end(); ++client_it) {
747    if ((*client_it)->session_closed)
748        continue;
749
750    (*client_it)->event_handler->OnFrameInfoChanged(
751        (*client_it)->controller_id,
752        info.width,
753        info.height,
754        info.frame_rate);
755  }
756}
757
758void VideoCaptureController::DoErrorOnIOThread() {
759  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
760  state_ = VIDEO_CAPTURE_STATE_ERROR;
761  ControllerClients::iterator client_it;
762  for (client_it = controller_clients_.begin();
763       client_it != controller_clients_.end(); ++client_it) {
764     if ((*client_it)->session_closed)
765        continue;
766
767    (*client_it)->event_handler->OnError((*client_it)->controller_id);
768  }
769}
770
771void VideoCaptureController::SendFrameInfoAndBuffers(ControllerClient* client) {
772  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
773  DCHECK(frame_info_.IsValid());
774  client->event_handler->OnFrameInfo(client->controller_id,
775                                     frame_info_);
776  for (int buffer_id = 0; buffer_id < buffer_pool_->count(); ++buffer_id) {
777    base::SharedMemoryHandle remote_handle =
778        buffer_pool_->ShareToProcess(buffer_id, client->render_process_handle);
779
780    client->event_handler->OnBufferCreated(client->controller_id,
781                                           remote_handle,
782                                           buffer_pool_->GetMemorySize(),
783                                           buffer_id);
784  }
785}
786
787VideoCaptureController::ControllerClient*
788VideoCaptureController::FindClient(
789    const VideoCaptureControllerID& id,
790    VideoCaptureControllerEventHandler* handler,
791    const ControllerClients& clients) {
792  for (ControllerClients::const_iterator client_it = clients.begin();
793       client_it != clients.end(); ++client_it) {
794    if ((*client_it)->controller_id == id &&
795        (*client_it)->event_handler == handler) {
796      return *client_it;
797    }
798  }
799  return NULL;
800}
801
802VideoCaptureController::ControllerClient*
803VideoCaptureController::FindClient(
804    int session_id,
805    const ControllerClients& clients) {
806  for (ControllerClients::const_iterator client_it = clients.begin();
807       client_it != clients.end(); ++client_it) {
808    if ((*client_it)->parameters.session_id == session_id) {
809      return *client_it;
810    }
811  }
812  return NULL;
813}
814
815int VideoCaptureController::GetClientCount() {
816  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
817  return controller_clients_.size();
818}
819
820}  // namespace content
821