Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / renderer / media / media_stream_video_capturer_source.cc
blobd5e7f40efbec311b20e5021d0c8d8ec45f9226a7
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.
5 #include "content/renderer/media/media_stream_video_capturer_source.h"
7 #include "base/bind.h"
8 #include "base/callback_helpers.h"
9 #include "base/location.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "content/public/common/media_stream_request.h"
12 #include "content/renderer/media/media_stream_constraints_util.h"
13 #include "content/renderer/media/video_capture_impl_manager.h"
14 #include "content/renderer/render_thread_impl.h"
15 #include "media/base/bind_to_current_loop.h"
16 #include "media/base/limits.h"
17 #include "media/base/video_capturer_source.h"
18 #include "media/base/video_frame.h"
20 namespace content {
22 namespace {
24 // Resolutions used if the source doesn't support capability enumeration.
25 struct {
26 int width;
27 int height;
28 } const kVideoResolutions[] = {{1920, 1080},
29 {1280, 720},
30 {960, 720},
31 {640, 480},
32 {640, 360},
33 {320, 240},
34 {320, 180}};
36 // Frame rates for sources with no support for capability enumeration.
37 const int kVideoFrameRates[] = {30, 60};
39 // Hard upper-bound frame rate for tab/desktop capture.
40 const double kMaxScreenCastFrameRate = 120.0;
42 // Allows the user to Override default power line frequency.
43 const char kPowerLineFrequency[] = "googPowerLineFrequency";
45 // Returns true if the value for width or height is reasonable.
46 bool DimensionValueIsValid(int x) {
47 return x > 0 && x <= media::limits::kMaxDimension;
50 // Returns true if the value for frame rate is reasonable.
51 bool FrameRateValueIsValid(double frame_rate) {
52 return (frame_rate > (1.0 / 60.0)) && // Lower-bound: One frame per minute.
53 (frame_rate <= media::limits::kMaxFramesPerSecond);
56 // Returns true if the aspect ratio of |a| and |b| are equivalent to two
57 // significant digits.
58 bool AreNearlyEquivalentInAspectRatio(const gfx::Size& a, const gfx::Size& b) {
59 DCHECK(!a.IsEmpty());
60 DCHECK(!b.IsEmpty());
61 const int aspect_ratio_a = (100 * a.width()) / a.height();
62 const int aspect_ratio_b = (100 * b.width()) / b.height();
63 return aspect_ratio_a == aspect_ratio_b;
66 // Checks if |device_info|s type is a generated content, e.g. Tab or Desktop.
67 bool IsContentVideoCaptureDevice(const StreamDeviceInfo& device_info) {
68 return device_info.device.type == MEDIA_TAB_VIDEO_CAPTURE ||
69 device_info.device.type == MEDIA_DESKTOP_VIDEO_CAPTURE;
72 // Interprets the properties in |constraints| to override values in |params| and
73 // determine the resolution change policy.
74 void SetContentCaptureParamsFromConstraints(
75 const blink::WebMediaConstraints& constraints,
76 MediaStreamType type,
77 media::VideoCaptureParams* params) {
78 // The default resolution change policies for tab versus desktop capture are
79 // the way they are for legacy reasons.
80 if (type == MEDIA_TAB_VIDEO_CAPTURE) {
81 params->resolution_change_policy =
82 media::RESOLUTION_POLICY_FIXED_RESOLUTION;
83 } else if (type == MEDIA_DESKTOP_VIDEO_CAPTURE) {
84 params->resolution_change_policy =
85 media::RESOLUTION_POLICY_ANY_WITHIN_LIMIT;
86 } else {
87 NOTREACHED();
90 // If the maximum frame resolution was provided in the constraints, use it if
91 // either: 1) none has been set yet; or 2) the maximum specificed is smaller
92 // than the current setting.
93 int width = 0;
94 int height = 0;
95 gfx::Size desired_max_frame_size;
96 if (GetConstraintValueAsInteger(constraints,
97 MediaStreamVideoSource::kMaxWidth,
98 &width) &&
99 GetConstraintValueAsInteger(constraints,
100 MediaStreamVideoSource::kMaxHeight,
101 &height) &&
102 DimensionValueIsValid(width) &&
103 DimensionValueIsValid(height)) {
104 desired_max_frame_size.SetSize(width, height);
105 if (params->requested_format.frame_size.IsEmpty() ||
106 desired_max_frame_size.width() <
107 params->requested_format.frame_size.width() ||
108 desired_max_frame_size.height() <
109 params->requested_format.frame_size.height()) {
110 params->requested_format.frame_size = desired_max_frame_size;
114 // Set the default frame resolution if none was provided.
115 if (params->requested_format.frame_size.IsEmpty()) {
116 params->requested_format.frame_size.SetSize(
117 MediaStreamVideoSource::kDefaultWidth,
118 MediaStreamVideoSource::kDefaultHeight);
121 // If the maximum frame rate was provided, use it if either: 1) none has been
122 // set yet; or 2) the maximum specificed is smaller than the current setting.
123 double frame_rate = 0.0;
124 if (GetConstraintValueAsDouble(constraints,
125 MediaStreamVideoSource::kMaxFrameRate,
126 &frame_rate) &&
127 FrameRateValueIsValid(frame_rate)) {
128 if (params->requested_format.frame_rate <= 0.0f ||
129 frame_rate < params->requested_format.frame_rate) {
130 params->requested_format.frame_rate = frame_rate;
134 // Set the default frame rate if none was provided.
135 if (params->requested_format.frame_rate <= 0.0f) {
136 params->requested_format.frame_rate =
137 MediaStreamVideoSource::kDefaultFrameRate;
140 // If the minimum frame resolution was provided, compare it to the maximum
141 // frame resolution to determine the intended resolution change policy.
142 if (!desired_max_frame_size.IsEmpty() &&
143 GetConstraintValueAsInteger(constraints,
144 MediaStreamVideoSource::kMinWidth,
145 &width) &&
146 GetConstraintValueAsInteger(constraints,
147 MediaStreamVideoSource::kMinHeight,
148 &height) &&
149 width <= desired_max_frame_size.width() &&
150 height <= desired_max_frame_size.height()) {
151 if (width == desired_max_frame_size.width() &&
152 height == desired_max_frame_size.height()) {
153 // Constraints explicitly require a single frame resolution.
154 params->resolution_change_policy =
155 media::RESOLUTION_POLICY_FIXED_RESOLUTION;
156 } else if (DimensionValueIsValid(width) &&
157 DimensionValueIsValid(height) &&
158 AreNearlyEquivalentInAspectRatio(gfx::Size(width, height),
159 desired_max_frame_size)) {
160 // Constraints only mention a single aspect ratio.
161 params->resolution_change_policy =
162 media::RESOLUTION_POLICY_FIXED_ASPECT_RATIO;
163 } else {
164 // Constraints specify a minimum resolution that is smaller than the
165 // maximum resolution and has a different aspect ratio (possibly even
166 // 0x0). This indicates any frame resolution and aspect ratio is
167 // acceptable.
168 params->resolution_change_policy =
169 media::RESOLUTION_POLICY_ANY_WITHIN_LIMIT;
173 DVLOG(1) << __FUNCTION__ << " "
174 << media::VideoCaptureFormat::ToString(params->requested_format)
175 << " with resolution change policy "
176 << params->resolution_change_policy;
179 // Interprets the properties in |constraints| to override values in |params| and
180 // determine the power line frequency.
181 void SetPowerLineFrequencyParamFromConstraints(
182 const blink::WebMediaConstraints& constraints,
183 media::VideoCaptureParams* params) {
184 int freq;
185 params->power_line_frequency = media::PowerLineFrequency::FREQUENCY_DEFAULT;
186 if (!GetOptionalConstraintValueAsInteger(constraints, kPowerLineFrequency,
187 &freq)) {
188 return;
190 if (freq == static_cast<int>(media::PowerLineFrequency::FREQUENCY_50HZ))
191 params->power_line_frequency = media::PowerLineFrequency::FREQUENCY_50HZ;
192 else if (freq == static_cast<int>(media::PowerLineFrequency::FREQUENCY_60HZ))
193 params->power_line_frequency = media::PowerLineFrequency::FREQUENCY_60HZ;
196 // LocalVideoCapturerSource is a delegate used by MediaStreamVideoCapturerSource
197 // for local video capture. It uses the Render singleton VideoCaptureImplManager
198 // to start / stop and receive I420 frames from Chrome's video capture
199 // implementation. This is a main Render thread only object.
200 class LocalVideoCapturerSource final : public media::VideoCapturerSource {
201 public:
202 explicit LocalVideoCapturerSource(const StreamDeviceInfo& device_info);
203 ~LocalVideoCapturerSource() override;
205 // VideoCaptureDelegate Implementation.
206 void GetCurrentSupportedFormats(
207 int max_requested_width,
208 int max_requested_height,
209 double max_requested_frame_rate,
210 const VideoCaptureDeviceFormatsCB& callback) override;
211 void StartCapture(const media::VideoCaptureParams& params,
212 const VideoCaptureDeliverFrameCB& new_frame_callback,
213 const RunningCallback& running_callback) override;
214 void StopCapture() override;
216 private:
217 void OnStateUpdate(VideoCaptureState state);
218 void OnDeviceFormatsInUseReceived(const media::VideoCaptureFormats& formats);
219 void OnDeviceSupportedFormatsEnumerated(
220 const media::VideoCaptureFormats& formats);
222 // |session_id_| identifies the capture device used for this capture session.
223 const media::VideoCaptureSessionId session_id_;
225 VideoCaptureImplManager* const manager_;
227 const base::Closure release_device_cb_;
229 // Indicates if we are capturing generated content, e.g. Tab or Desktop.
230 const bool is_content_capture_;
232 // These two are valid between StartCapture() and StopCapture().
233 base::Closure stop_capture_cb_;
234 RunningCallback running_callback_;
236 // Placeholder keeping the callback between asynchronous device enumeration
237 // calls.
238 VideoCaptureDeviceFormatsCB formats_enumerated_callback_;
240 // Bound to the main render thread.
241 base::ThreadChecker thread_checker_;
243 base::WeakPtrFactory<LocalVideoCapturerSource> weak_factory_;
245 DISALLOW_COPY_AND_ASSIGN(LocalVideoCapturerSource);
248 } // namespace
250 LocalVideoCapturerSource::LocalVideoCapturerSource(
251 const StreamDeviceInfo& device_info)
252 : session_id_(device_info.session_id),
253 manager_(RenderThreadImpl::current()->video_capture_impl_manager()),
254 release_device_cb_(manager_->UseDevice(session_id_)),
255 is_content_capture_(IsContentVideoCaptureDevice(device_info)),
256 weak_factory_(this) {
257 DCHECK(RenderThreadImpl::current());
260 LocalVideoCapturerSource::~LocalVideoCapturerSource() {
261 DCHECK(thread_checker_.CalledOnValidThread());
262 release_device_cb_.Run();
265 void LocalVideoCapturerSource::GetCurrentSupportedFormats(
266 int max_requested_width,
267 int max_requested_height,
268 double max_requested_frame_rate,
269 const VideoCaptureDeviceFormatsCB& callback) {
270 DVLOG(3) << "GetCurrentSupportedFormats({ max_requested_height = "
271 << max_requested_height << "}) { max_requested_width = "
272 << max_requested_width << "}) { max_requested_frame_rate = "
273 << max_requested_frame_rate << "})";
274 DCHECK(thread_checker_.CalledOnValidThread());
276 if (is_content_capture_) {
277 const int width = max_requested_width ?
278 max_requested_width : MediaStreamVideoSource::kDefaultWidth;
279 const int height = max_requested_height ?
280 max_requested_height : MediaStreamVideoSource::kDefaultHeight;
281 callback.Run(media::VideoCaptureFormats(
282 1, media::VideoCaptureFormat(
283 gfx::Size(width, height),
284 static_cast<float>(
285 std::min(kMaxScreenCastFrameRate, max_requested_frame_rate)),
286 media::PIXEL_FORMAT_I420)));
287 return;
290 DCHECK(formats_enumerated_callback_.is_null());
291 formats_enumerated_callback_ = callback;
292 manager_->GetDeviceFormatsInUse(
293 session_id_, media::BindToCurrentLoop(base::Bind(
294 &LocalVideoCapturerSource::OnDeviceFormatsInUseReceived,
295 weak_factory_.GetWeakPtr())));
298 void LocalVideoCapturerSource::StartCapture(
299 const media::VideoCaptureParams& params,
300 const VideoCaptureDeliverFrameCB& new_frame_callback,
301 const RunningCallback& running_callback) {
302 DCHECK(params.requested_format.IsValid());
303 DCHECK(thread_checker_.CalledOnValidThread());
304 running_callback_ = running_callback;
306 stop_capture_cb_ = manager_->StartCapture(
307 session_id_, params, media::BindToCurrentLoop(base::Bind(
308 &LocalVideoCapturerSource::OnStateUpdate,
309 weak_factory_.GetWeakPtr())),
310 new_frame_callback);
313 void LocalVideoCapturerSource::StopCapture() {
314 DVLOG(3) << __FUNCTION__;
315 DCHECK(thread_checker_.CalledOnValidThread());
316 // Immediately make sure we don't provide more frames.
317 if (!stop_capture_cb_.is_null())
318 base::ResetAndReturn(&stop_capture_cb_).Run();
319 running_callback_.Reset();
320 // Invalidate any potential format enumerations going on.
321 formats_enumerated_callback_.Reset();
324 void LocalVideoCapturerSource::OnStateUpdate(VideoCaptureState state) {
325 DVLOG(3) << __FUNCTION__ << " state = " << state;
326 DCHECK(thread_checker_.CalledOnValidThread());
327 if (running_callback_.is_null())
328 return;
329 const bool is_started_ok = state == VIDEO_CAPTURE_STATE_STARTED;
330 running_callback_.Run(is_started_ok);
331 if (!is_started_ok)
332 running_callback_.Reset();
335 void LocalVideoCapturerSource::OnDeviceFormatsInUseReceived(
336 const media::VideoCaptureFormats& formats_in_use) {
337 DVLOG(3) << __FUNCTION__ << ", #formats received: " << formats_in_use.size();
338 DCHECK(thread_checker_.CalledOnValidThread());
339 // StopCapture() might have destroyed |formats_enumerated_callback_| before
340 // arriving here.
341 if (formats_enumerated_callback_.is_null())
342 return;
343 if (formats_in_use.size()) {
344 base::ResetAndReturn(&formats_enumerated_callback_).Run(formats_in_use);
345 return;
348 // The device doesn't seem to have formats in use so try and retrieve the
349 // whole list of supported ones.
350 manager_->GetDeviceSupportedFormats(
351 session_id_,
352 media::BindToCurrentLoop(
353 base::Bind(
354 &LocalVideoCapturerSource::OnDeviceSupportedFormatsEnumerated,
355 weak_factory_.GetWeakPtr())));
358 void LocalVideoCapturerSource::OnDeviceSupportedFormatsEnumerated(
359 const media::VideoCaptureFormats& formats) {
360 DVLOG(3) << __FUNCTION__ << ", #formats received: " << formats.size();
361 DCHECK(thread_checker_.CalledOnValidThread());
362 // StopCapture() might have destroyed |formats_enumerated_callback_| before
363 // arriving here.
364 if (formats_enumerated_callback_.is_null())
365 return;
366 if (formats.size()) {
367 base::ResetAndReturn(&formats_enumerated_callback_).Run(formats);
368 return;
371 // The capture device doesn't seem to support capability enumeration, compose
372 // a fallback list of capabilities.
373 media::VideoCaptureFormats default_formats;
374 for (const auto& resolution : kVideoResolutions) {
375 for (const auto frame_rate : kVideoFrameRates) {
376 default_formats.push_back(media::VideoCaptureFormat(
377 gfx::Size(resolution.width, resolution.height), frame_rate,
378 media::PIXEL_FORMAT_I420));
381 base::ResetAndReturn(&formats_enumerated_callback_).Run(default_formats);
384 MediaStreamVideoCapturerSource::MediaStreamVideoCapturerSource(
385 const SourceStoppedCallback& stop_callback,
386 scoped_ptr<media::VideoCapturerSource> source)
387 : source_(source.Pass()) {
388 SetStopCallback(stop_callback);
391 MediaStreamVideoCapturerSource::MediaStreamVideoCapturerSource(
392 const SourceStoppedCallback& stop_callback,
393 const StreamDeviceInfo& device_info)
394 : source_(new LocalVideoCapturerSource(device_info)) {
395 SetStopCallback(stop_callback);
396 SetDeviceInfo(device_info);
399 MediaStreamVideoCapturerSource::~MediaStreamVideoCapturerSource() {
402 void MediaStreamVideoCapturerSource::GetCurrentSupportedFormats(
403 int max_requested_width,
404 int max_requested_height,
405 double max_requested_frame_rate,
406 const VideoCaptureDeviceFormatsCB& callback) {
407 source_->GetCurrentSupportedFormats(
408 max_requested_width,
409 max_requested_height,
410 max_requested_frame_rate,
411 callback);
414 void MediaStreamVideoCapturerSource::StartSourceImpl(
415 const media::VideoCaptureFormat& format,
416 const blink::WebMediaConstraints& constraints,
417 const VideoCaptureDeliverFrameCB& frame_callback) {
418 media::VideoCaptureParams new_params;
419 new_params.requested_format = format;
420 if (IsContentVideoCaptureDevice(device_info())) {
421 SetContentCaptureParamsFromConstraints(
422 constraints, device_info().device.type, &new_params);
423 } else if (device_info().device.type == MEDIA_DEVICE_VIDEO_CAPTURE) {
424 SetPowerLineFrequencyParamFromConstraints(constraints, &new_params);
427 source_->StartCapture(new_params,
428 frame_callback,
429 base::Bind(&MediaStreamVideoCapturerSource::OnStarted,
430 base::Unretained(this)));
433 void MediaStreamVideoCapturerSource::StopSourceImpl() {
434 source_->StopCapture();
437 void MediaStreamVideoCapturerSource::OnStarted(bool result) {
438 OnStartDone(result ? MEDIA_DEVICE_OK : MEDIA_DEVICE_TRACK_START_FAILURE);
441 const char*
442 MediaStreamVideoCapturerSource::GetPowerLineFrequencyForTesting() const {
443 return kPowerLineFrequency;
446 } // namespace content