Roll src/third_party/WebKit d10c917:a1123a1 (svn 198729:198730)
[chromium-blink-merge.git] / content / renderer / media / media_stream_video_capturer_source.cc
blob4d13871d83d545c1a1c006cfadef78b40edb74f2
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 "content/public/common/media_stream_request.h"
11 #include "content/renderer/media/media_stream_constraints_util.h"
12 #include "content/renderer/media/video_capture_impl_manager.h"
13 #include "content/renderer/render_thread_impl.h"
14 #include "media/base/bind_to_current_loop.h"
15 #include "media/base/limits.h"
16 #include "media/base/video_frame.h"
18 namespace content {
20 namespace {
22 // Resolutions used if the source doesn't support capability enumeration.
23 struct {
24 int width;
25 int height;
26 } const kVideoResolutions[] = {{1920, 1080},
27 {1280, 720},
28 {960, 720},
29 {640, 480},
30 {640, 360},
31 {320, 240},
32 {320, 180}};
34 // Frame rates for sources with no support for capability enumeration.
35 const int kVideoFrameRates[] = {30, 60};
37 // Hard upper-bound frame rate for tab/desktop capture.
38 const double kMaxScreenCastFrameRate = 120.0;
40 // Returns true if the value for width or height is reasonable.
41 bool DimensionValueIsValid(int x) {
42 return x > 0 && x <= media::limits::kMaxDimension;
45 // Returns true if the value for frame rate is reasonable.
46 bool FrameRateValueIsValid(double frame_rate) {
47 return (frame_rate > (1.0 / 60.0)) && // Lower-bound: One frame per minute.
48 (frame_rate <= media::limits::kMaxFramesPerSecond);
51 // Returns true if the aspect ratio of |a| and |b| are equivalent to two
52 // significant digits.
53 bool AreNearlyEquivalentInAspectRatio(const gfx::Size& a, const gfx::Size& b) {
54 DCHECK(!a.IsEmpty());
55 DCHECK(!b.IsEmpty());
56 const int aspect_ratio_a = (100 * a.width()) / a.height();
57 const int aspect_ratio_b = (100 * b.width()) / b.height();
58 return aspect_ratio_a == aspect_ratio_b;
61 // Interprets the properties in |constraints| to override values in |params| and
62 // determine the resolution change policy.
63 void SetScreenCastParamsFromConstraints(
64 const blink::WebMediaConstraints& constraints,
65 MediaStreamType type,
66 media::VideoCaptureParams* params) {
67 // The default resolution change policies for tab versus desktop capture are
68 // the way they are for legacy reasons.
69 if (type == MEDIA_TAB_VIDEO_CAPTURE) {
70 params->resolution_change_policy =
71 media::RESOLUTION_POLICY_FIXED_RESOLUTION;
72 } else if (type == MEDIA_DESKTOP_VIDEO_CAPTURE) {
73 params->resolution_change_policy =
74 media::RESOLUTION_POLICY_ANY_WITHIN_LIMIT;
75 } else {
76 NOTREACHED();
79 // If the maximum frame resolution was provided in the constraints, use it if
80 // either: 1) none has been set yet; or 2) the maximum specificed is smaller
81 // than the current setting.
82 int width = 0;
83 int height = 0;
84 gfx::Size desired_max_frame_size;
85 if (GetConstraintValueAsInteger(constraints,
86 MediaStreamVideoSource::kMaxWidth,
87 &width) &&
88 GetConstraintValueAsInteger(constraints,
89 MediaStreamVideoSource::kMaxHeight,
90 &height) &&
91 DimensionValueIsValid(width) &&
92 DimensionValueIsValid(height)) {
93 desired_max_frame_size.SetSize(width, height);
94 if (params->requested_format.frame_size.IsEmpty() ||
95 desired_max_frame_size.width() <
96 params->requested_format.frame_size.width() ||
97 desired_max_frame_size.height() <
98 params->requested_format.frame_size.height()) {
99 params->requested_format.frame_size = desired_max_frame_size;
103 // Set the default frame resolution if none was provided.
104 if (params->requested_format.frame_size.IsEmpty()) {
105 params->requested_format.frame_size.SetSize(
106 MediaStreamVideoSource::kDefaultWidth,
107 MediaStreamVideoSource::kDefaultHeight);
110 // If the maximum frame rate was provided, use it if either: 1) none has been
111 // set yet; or 2) the maximum specificed is smaller than the current setting.
112 double frame_rate = 0.0;
113 if (GetConstraintValueAsDouble(constraints,
114 MediaStreamVideoSource::kMaxFrameRate,
115 &frame_rate) &&
116 FrameRateValueIsValid(frame_rate)) {
117 if (params->requested_format.frame_rate <= 0.0f ||
118 frame_rate < params->requested_format.frame_rate) {
119 params->requested_format.frame_rate = frame_rate;
123 // Set the default frame rate if none was provided.
124 if (params->requested_format.frame_rate <= 0.0f) {
125 params->requested_format.frame_rate =
126 MediaStreamVideoSource::kDefaultFrameRate;
129 // If the minimum frame resolution was provided, compare it to the maximum
130 // frame resolution to determine the intended resolution change policy.
131 if (!desired_max_frame_size.IsEmpty() &&
132 GetConstraintValueAsInteger(constraints,
133 MediaStreamVideoSource::kMinWidth,
134 &width) &&
135 GetConstraintValueAsInteger(constraints,
136 MediaStreamVideoSource::kMinHeight,
137 &height) &&
138 width <= desired_max_frame_size.width() &&
139 height <= desired_max_frame_size.height()) {
140 if (width == desired_max_frame_size.width() &&
141 height == desired_max_frame_size.height()) {
142 // Constraints explicitly require a single frame resolution.
143 params->resolution_change_policy =
144 media::RESOLUTION_POLICY_FIXED_RESOLUTION;
145 } else if (DimensionValueIsValid(width) &&
146 DimensionValueIsValid(height) &&
147 AreNearlyEquivalentInAspectRatio(gfx::Size(width, height),
148 desired_max_frame_size)) {
149 // Constraints only mention a single aspect ratio.
150 params->resolution_change_policy =
151 media::RESOLUTION_POLICY_FIXED_ASPECT_RATIO;
152 } else {
153 // Constraints specify a minimum resolution that is smaller than the
154 // maximum resolution and has a different aspect ratio (possibly even
155 // 0x0). This indicates any frame resolution and aspect ratio is
156 // acceptable.
157 params->resolution_change_policy =
158 media::RESOLUTION_POLICY_ANY_WITHIN_LIMIT;
162 DVLOG(1) << "SetScreenCastParamsFromConstraints: "
163 << media::VideoCaptureFormat::ToString(params->requested_format)
164 << " with resolution change policy "
165 << params->resolution_change_policy;
168 } // namespace
170 VideoCapturerDelegate::VideoCapturerDelegate(
171 const StreamDeviceInfo& device_info)
172 : session_id_(device_info.session_id),
173 is_screen_cast_(device_info.device.type == MEDIA_TAB_VIDEO_CAPTURE ||
174 device_info.device.type == MEDIA_DESKTOP_VIDEO_CAPTURE),
175 weak_factory_(this) {
176 DVLOG(3) << "VideoCapturerDelegate::ctor";
178 // NULL in unit test.
179 if (RenderThreadImpl::current()) {
180 VideoCaptureImplManager* manager =
181 RenderThreadImpl::current()->video_capture_impl_manager();
182 if (manager)
183 release_device_cb_ = manager->UseDevice(session_id_);
187 VideoCapturerDelegate::~VideoCapturerDelegate() {
188 DCHECK(thread_checker_.CalledOnValidThread());
189 DVLOG(3) << "VideoCapturerDelegate::dtor";
190 if (!release_device_cb_.is_null())
191 release_device_cb_.Run();
194 void VideoCapturerDelegate::GetCurrentSupportedFormats(
195 int max_requested_width,
196 int max_requested_height,
197 double max_requested_frame_rate,
198 const VideoCaptureDeviceFormatsCB& callback) {
199 DVLOG(3)
200 << "GetCurrentSupportedFormats("
201 << " { max_requested_height = " << max_requested_height << "})"
202 << " { max_requested_width = " << max_requested_width << "})"
203 << " { max_requested_frame_rate = " << max_requested_frame_rate << "})";
205 if (is_screen_cast_) {
206 const int width = max_requested_width ?
207 max_requested_width : MediaStreamVideoSource::kDefaultWidth;
208 const int height = max_requested_height ?
209 max_requested_height : MediaStreamVideoSource::kDefaultHeight;
210 callback.Run(media::VideoCaptureFormats(1, media::VideoCaptureFormat(
211 gfx::Size(width, height),
212 static_cast<float>(std::min(kMaxScreenCastFrameRate,
213 max_requested_frame_rate)),
214 media::PIXEL_FORMAT_I420)));
215 return;
218 // NULL in unit test.
219 if (!RenderThreadImpl::current())
220 return;
221 VideoCaptureImplManager* const manager =
222 RenderThreadImpl::current()->video_capture_impl_manager();
223 if (!manager)
224 return;
225 DCHECK(source_formats_callback_.is_null());
226 source_formats_callback_ = callback;
227 manager->GetDeviceFormatsInUse(
228 session_id_,
229 media::BindToCurrentLoop(
230 base::Bind(
231 &VideoCapturerDelegate::OnDeviceFormatsInUseReceived,
232 weak_factory_.GetWeakPtr())));
235 void VideoCapturerDelegate::StartCapture(
236 const media::VideoCaptureParams& params,
237 const VideoCaptureDeliverFrameCB& new_frame_callback,
238 scoped_refptr<base::SingleThreadTaskRunner> frame_callback_task_runner,
239 const RunningCallback& running_callback) {
240 DCHECK(params.requested_format.IsValid());
241 DCHECK(thread_checker_.CalledOnValidThread());
242 running_callback_ = running_callback;
244 // NULL in unit test.
245 if (!RenderThreadImpl::current())
246 return;
247 VideoCaptureImplManager* const manager =
248 RenderThreadImpl::current()->video_capture_impl_manager();
249 if (!manager)
250 return;
251 if (frame_callback_task_runner !=
252 RenderThreadImpl::current()->GetIOMessageLoopProxy()) {
253 DCHECK(false) << "Only IO thread supported right now.";
254 running_callback.Run(false);
255 return;
258 stop_capture_cb_ =
259 manager->StartCapture(
260 session_id_,
261 params,
262 media::BindToCurrentLoop(base::Bind(
263 &VideoCapturerDelegate::OnStateUpdate,
264 weak_factory_.GetWeakPtr())),
265 new_frame_callback);
268 void VideoCapturerDelegate::StopCapture() {
269 // Immediately make sure we don't provide more frames.
270 DVLOG(3) << "VideoCapturerDelegate::StopCapture()";
271 DCHECK(thread_checker_.CalledOnValidThread());
272 if (!stop_capture_cb_.is_null()) {
273 base::ResetAndReturn(&stop_capture_cb_).Run();
275 running_callback_.Reset();
276 source_formats_callback_.Reset();
279 void VideoCapturerDelegate::OnStateUpdate(
280 VideoCaptureState state) {
281 DCHECK(thread_checker_.CalledOnValidThread());
282 DVLOG(3) << "OnStateUpdate state = " << state;
283 if (state == VIDEO_CAPTURE_STATE_STARTED && !running_callback_.is_null()) {
284 running_callback_.Run(true);
285 return;
287 if (state > VIDEO_CAPTURE_STATE_STARTED && !running_callback_.is_null()) {
288 base::ResetAndReturn(&running_callback_).Run(false);
292 void VideoCapturerDelegate::OnDeviceFormatsInUseReceived(
293 const media::VideoCaptureFormats& formats_in_use) {
294 DVLOG(3) << "OnDeviceFormatsInUseReceived: " << formats_in_use.size();
295 DCHECK(thread_checker_.CalledOnValidThread());
296 // StopCapture() might have destroyed |source_formats_callback_| before
297 // arriving here.
298 if (source_formats_callback_.is_null())
299 return;
300 // If there are no formats in use, try to retrieve the whole list of
301 // supported form.
302 if (!formats_in_use.empty()) {
303 source_formats_callback_.Run(formats_in_use);
304 source_formats_callback_.Reset();
305 return;
308 // NULL in unit test.
309 if (!RenderThreadImpl::current())
310 return;
311 VideoCaptureImplManager* const manager =
312 RenderThreadImpl::current()->video_capture_impl_manager();
313 if (!manager)
314 return;
316 manager->GetDeviceSupportedFormats(
317 session_id_,
318 media::BindToCurrentLoop(
319 base::Bind(
320 &VideoCapturerDelegate::OnDeviceSupportedFormatsEnumerated,
321 weak_factory_.GetWeakPtr())));
324 void VideoCapturerDelegate::OnDeviceSupportedFormatsEnumerated(
325 const media::VideoCaptureFormats& formats) {
326 DVLOG(3) << "OnDeviceSupportedFormatsEnumerated: " << formats.size()
327 << " received";
328 DCHECK(thread_checker_.CalledOnValidThread());
329 // StopCapture() might have destroyed |source_formats_callback_| before
330 // arriving here.
331 if (source_formats_callback_.is_null())
332 return;
333 if (formats.size()) {
334 base::ResetAndReturn(&source_formats_callback_).Run(formats);
335 } else {
336 // The capture device doesn't seem to support capability enumeration,
337 // compose a fallback list of capabilities.
338 media::VideoCaptureFormats default_formats;
339 for (const auto& resolution : kVideoResolutions) {
340 for (const auto frame_rate : kVideoFrameRates) {
341 default_formats.push_back(media::VideoCaptureFormat(
342 gfx::Size(resolution.width, resolution.height),
343 frame_rate,
344 media::PIXEL_FORMAT_I420));
347 base::ResetAndReturn(&source_formats_callback_).Run(default_formats);
351 MediaStreamVideoCapturerSource::MediaStreamVideoCapturerSource(
352 const SourceStoppedCallback& stop_callback,
353 scoped_ptr<media::VideoCapturerSource> delegate)
354 : delegate_(delegate.Pass()) {
355 SetStopCallback(stop_callback);
358 MediaStreamVideoCapturerSource::~MediaStreamVideoCapturerSource() {
361 void MediaStreamVideoCapturerSource::SetDeviceInfo(
362 const StreamDeviceInfo& device_info) {
363 MediaStreamVideoSource::SetDeviceInfo(device_info);
366 void MediaStreamVideoCapturerSource::GetCurrentSupportedFormats(
367 int max_requested_width,
368 int max_requested_height,
369 double max_requested_frame_rate,
370 const VideoCaptureDeviceFormatsCB& callback) {
371 delegate_->GetCurrentSupportedFormats(
372 max_requested_width,
373 max_requested_height,
374 max_requested_frame_rate,
375 callback);
378 void MediaStreamVideoCapturerSource::StartSourceImpl(
379 const media::VideoCaptureFormat& format,
380 const blink::WebMediaConstraints& constraints,
381 const VideoCaptureDeliverFrameCB& frame_callback) {
382 media::VideoCaptureParams new_params;
383 new_params.requested_format = format;
384 if (device_info().device.type == MEDIA_TAB_VIDEO_CAPTURE ||
385 device_info().device.type == MEDIA_DESKTOP_VIDEO_CAPTURE) {
386 SetScreenCastParamsFromConstraints(
387 constraints, device_info().device.type, &new_params);
389 delegate_->StartCapture(
390 new_params,
391 frame_callback,
392 RenderThreadImpl::current() ?
393 RenderThreadImpl::current()->GetIOMessageLoopProxy() :
394 nullptr,
395 base::Bind(&MediaStreamVideoCapturerSource::OnStarted,
396 base::Unretained(this)));
399 void MediaStreamVideoCapturerSource::StopSourceImpl() {
400 delegate_->StopCapture();
403 void MediaStreamVideoCapturerSource::OnStarted(bool result) {
404 OnStartDone(result ? MEDIA_DEVICE_OK : MEDIA_DEVICE_TRACK_START_FAILURE);
407 } // namespace content