1// Copyright 2014 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/renderer/media/media_stream_video_capturer_source.h"
6
7#include "base/bind.h"
8#include "base/callback_helpers.h"
9#include "base/location.h"
10#include "content/renderer/media/video_capture_impl_manager.h"
11#include "content/renderer/render_thread_impl.h"
12#include "media/base/bind_to_current_loop.h"
13#include "media/base/video_frame.h"
14
15namespace {
16
17struct SourceVideoResolution {
18  int width;
19  int height;
20};
21
22// Resolutions used if the source doesn't support capability enumeration.
23const SourceVideoResolution kVideoResolutions[] = {{1920, 1080},
24                                                   {1280, 720},
25                                                   {960, 720},
26                                                   {640, 480},
27                                                   {640, 360},
28                                                   {320, 240},
29                                                   {320, 180}};
30// Frame rates for sources with no support for capability enumeration.
31const int kVideoFrameRates[] = {30, 60};
32
33}  // namespace
34
35namespace content {
36
37VideoCapturerDelegate::VideoCapturerDelegate(
38    const StreamDeviceInfo& device_info)
39    : session_id_(device_info.session_id),
40      is_screen_cast_(device_info.device.type == MEDIA_TAB_VIDEO_CAPTURE ||
41                      device_info.device.type == MEDIA_DESKTOP_VIDEO_CAPTURE),
42      got_first_frame_(false) {
43  DVLOG(3) << "VideoCapturerDelegate::ctor";
44
45  // NULL in unit test.
46  if (RenderThreadImpl::current()) {
47    VideoCaptureImplManager* manager =
48        RenderThreadImpl::current()->video_capture_impl_manager();
49    if (manager)
50      release_device_cb_ = manager->UseDevice(session_id_);
51  }
52}
53
54VideoCapturerDelegate::~VideoCapturerDelegate() {
55  DVLOG(3) << "VideoCapturerDelegate::dtor";
56  if (!release_device_cb_.is_null())
57    release_device_cb_.Run();
58}
59
60void VideoCapturerDelegate::GetCurrentSupportedFormats(
61    int max_requested_width,
62    int max_requested_height,
63    const VideoCaptureDeviceFormatsCB& callback) {
64  DVLOG(3) << "GetCurrentSupportedFormats("
65           << " { max_requested_height = " << max_requested_height << "})"
66           << " { max_requested_width = " << max_requested_width << "})";
67
68  if (is_screen_cast_) {
69    media::VideoCaptureFormats formats;
70    const int width = max_requested_width ?
71        max_requested_width : MediaStreamVideoSource::kDefaultWidth;
72    const int height = max_requested_height ?
73        max_requested_height : MediaStreamVideoSource::kDefaultHeight;
74    formats.push_back(
75          media::VideoCaptureFormat(
76              gfx::Size(width, height),
77              MediaStreamVideoSource::kDefaultFrameRate,
78              media::PIXEL_FORMAT_I420));
79    callback.Run(formats);
80    return;
81  }
82
83  // NULL in unit test.
84  if (!RenderThreadImpl::current())
85    return;
86  VideoCaptureImplManager* manager =
87      RenderThreadImpl::current()->video_capture_impl_manager();
88  if (!manager)
89    return;
90  DCHECK(source_formats_callback_.is_null());
91  source_formats_callback_ = callback;
92  manager->GetDeviceFormatsInUse(
93      session_id_,
94      media::BindToCurrentLoop(
95          base::Bind(
96              &VideoCapturerDelegate::OnDeviceFormatsInUseReceived, this)));
97}
98
99void VideoCapturerDelegate::StartCapture(
100    const media::VideoCaptureParams& params,
101    const VideoCaptureDeliverFrameCB& new_frame_callback,
102    const RunningCallback& running_callback) {
103  DCHECK(params.requested_format.IsValid());
104  DCHECK(thread_checker_.CalledOnValidThread());
105  running_callback_ = running_callback;
106  got_first_frame_ = false;
107
108  // NULL in unit test.
109  if (!RenderThreadImpl::current())
110    return;
111  VideoCaptureImplManager* manager =
112      RenderThreadImpl::current()->video_capture_impl_manager();
113  if (!manager)
114    return;
115  stop_capture_cb_ =
116      manager->StartCapture(
117          session_id_,
118          params,
119          media::BindToCurrentLoop(base::Bind(
120              &VideoCapturerDelegate::OnStateUpdateOnRenderThread, this)),
121          new_frame_callback);
122}
123
124void VideoCapturerDelegate::StopCapture() {
125  // Immediately make sure we don't provide more frames.
126  DVLOG(3) << "VideoCapturerDelegate::StopCapture()";
127  DCHECK(thread_checker_.CalledOnValidThread());
128  if (!stop_capture_cb_.is_null()) {
129    base::ResetAndReturn(&stop_capture_cb_).Run();
130  }
131  running_callback_.Reset();
132  source_formats_callback_.Reset();
133}
134
135void VideoCapturerDelegate::OnStateUpdateOnRenderThread(
136    VideoCaptureState state) {
137  DCHECK(thread_checker_.CalledOnValidThread());
138  DVLOG(3) << "OnStateUpdateOnRenderThread state = " << state;
139  if (state == VIDEO_CAPTURE_STATE_STARTED && !running_callback_.is_null()) {
140    running_callback_.Run(true);
141    return;
142  }
143  if (state > VIDEO_CAPTURE_STATE_STARTED && !running_callback_.is_null()) {
144    base::ResetAndReturn(&running_callback_).Run(false);
145  }
146}
147
148void VideoCapturerDelegate::OnDeviceFormatsInUseReceived(
149    const media::VideoCaptureFormats& formats_in_use) {
150  DVLOG(3) << "OnDeviceFormatsInUseReceived: " << formats_in_use.size();
151  DCHECK(thread_checker_.CalledOnValidThread());
152  // StopCapture() might have destroyed |source_formats_callback_| before
153  // arriving here.
154  if (source_formats_callback_.is_null())
155    return;
156  // If there are no formats in use, try to retrieve the whole list of
157  // supported form.
158  if (!formats_in_use.empty()) {
159    source_formats_callback_.Run(formats_in_use);
160    source_formats_callback_.Reset();
161    return;
162  }
163
164  // NULL in unit test.
165  if (!RenderThreadImpl::current())
166    return;
167  VideoCaptureImplManager* manager =
168      RenderThreadImpl::current()->video_capture_impl_manager();
169  if (!manager)
170    return;
171  manager->GetDeviceSupportedFormats(
172      session_id_,
173      media::BindToCurrentLoop(
174          base::Bind(
175              &VideoCapturerDelegate::OnDeviceSupportedFormatsEnumerated,
176              this)));
177}
178
179void VideoCapturerDelegate::OnDeviceSupportedFormatsEnumerated(
180    const media::VideoCaptureFormats& formats) {
181  DVLOG(3) << "OnDeviceSupportedFormatsEnumerated: " << formats.size()
182           << " received";
183  DCHECK(thread_checker_.CalledOnValidThread());
184  // StopCapture() might have destroyed |source_formats_callback_| before
185  // arriving here.
186  if (source_formats_callback_.is_null())
187    return;
188  if (formats.size()) {
189    source_formats_callback_.Run(formats);
190  } else {
191    // The capture device doesn't seem to support capability enumeration,
192    // compose a fallback list of capabilities.
193    media::VideoCaptureFormats default_formats;
194    for (size_t i = 0; i < arraysize(kVideoResolutions); ++i) {
195      for (size_t j = 0; j < arraysize(kVideoFrameRates); ++j) {
196        default_formats.push_back(media::VideoCaptureFormat(
197            gfx::Size(kVideoResolutions[i].width, kVideoResolutions[i].height),
198            kVideoFrameRates[j], media::PIXEL_FORMAT_I420));
199      }
200    }
201    source_formats_callback_.Run(default_formats);
202  }
203  source_formats_callback_.Reset();
204}
205
206MediaStreamVideoCapturerSource::MediaStreamVideoCapturerSource(
207    const StreamDeviceInfo& device_info,
208    const SourceStoppedCallback& stop_callback,
209    const scoped_refptr<VideoCapturerDelegate>& delegate)
210    : delegate_(delegate) {
211  SetDeviceInfo(device_info);
212  SetStopCallback(stop_callback);
213}
214
215MediaStreamVideoCapturerSource::~MediaStreamVideoCapturerSource() {
216}
217
218void MediaStreamVideoCapturerSource::GetCurrentSupportedFormats(
219    int max_requested_width,
220    int max_requested_height,
221    const VideoCaptureDeviceFormatsCB& callback) {
222  delegate_->GetCurrentSupportedFormats(
223      max_requested_width,
224      max_requested_height,
225      callback);
226}
227
228void MediaStreamVideoCapturerSource::StartSourceImpl(
229    const media::VideoCaptureParams& params,
230    const VideoCaptureDeliverFrameCB& frame_callback) {
231  media::VideoCaptureParams new_params(params);
232  if (device_info().device.type == MEDIA_TAB_VIDEO_CAPTURE ||
233      device_info().device.type == MEDIA_DESKTOP_VIDEO_CAPTURE) {
234    new_params.allow_resolution_change = true;
235  }
236  delegate_->StartCapture(
237      new_params,
238      frame_callback,
239      base::Bind(&MediaStreamVideoCapturerSource::OnStartDone,
240                 base::Unretained(this)));
241}
242
243void MediaStreamVideoCapturerSource::StopSourceImpl() {
244  delegate_->StopCapture();
245}
246
247}  // namespace content
248