blob: 704840b180d28b8a755408fe74025ef85350d7e7 [file] [log] [blame]
jlmiller@webrtc.org5f93d0a2015-01-20 21:36:13 +00001/*
kjellander1afca732016-02-07 20:46:45 -08002 * Copyright (c) 2010 The WebRTC project authors. All Rights Reserved.
jlmiller@webrtc.org5f93d0a2015-01-20 21:36:13 +00003 *
kjellander1afca732016-02-07 20:46:45 -08004 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
jlmiller@webrtc.org5f93d0a2015-01-20 21:36:13 +00009 */
10
henrike@webrtc.org28e20752013-07-10 00:45:36 +000011// Implementation file of class VideoCapturer.
12
kjellandera96e2d72016-02-04 23:52:28 -080013#include "webrtc/media/base/videocapturer.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000014
15#include <algorithm>
16
nisseaf916892017-01-10 07:44:26 -080017#include "webrtc/api/video/i420_buffer.h"
18#include "webrtc/api/video/video_frame.h"
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000019#include "webrtc/base/logging.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000020
henrike@webrtc.org28e20752013-07-10 00:45:36 +000021namespace cricket {
22
23namespace {
24
Peter Boström0c4e06b2015-10-07 12:23:21 +020025static const int64_t kMaxDistance = ~(static_cast<int64_t>(1) << 63);
kjellanderfcfc8042016-01-14 11:01:09 -080026#ifdef WEBRTC_LINUX
henrike@webrtc.org28e20752013-07-10 00:45:36 +000027static const int kYU12Penalty = 16; // Needs to be higher than MJPG index.
henrike@webrtc.orgf5bebd42014-04-04 18:39:07 +000028#endif
henrike@webrtc.org28e20752013-07-10 00:45:36 +000029
30} // namespace
31
32/////////////////////////////////////////////////////////////////////
henrike@webrtc.org28e20752013-07-10 00:45:36 +000033// Implementation of class VideoCapturer
34/////////////////////////////////////////////////////////////////////
deadbeeff5629ad2016-03-18 11:38:26 -070035VideoCapturer::VideoCapturer() : apply_rotation_(false) {
Perfb45d172016-02-29 12:07:35 +010036 thread_checker_.DetachFromThread();
henrike@webrtc.org28e20752013-07-10 00:45:36 +000037 Construct();
38}
39
40void VideoCapturer::Construct() {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000041 enable_camera_list_ = false;
42 capture_state_ = CS_STOPPED;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000043 scaled_width_ = 0;
44 scaled_height_ = 0;
henrike@webrtc.orga7b98182014-02-21 15:51:43 +000045 enable_video_adapter_ = true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000046}
47
48const std::vector<VideoFormat>* VideoCapturer::GetSupportedFormats() const {
49 return &filtered_supported_formats_;
50}
51
52bool VideoCapturer::StartCapturing(const VideoFormat& capture_format) {
Perfb45d172016-02-29 12:07:35 +010053 RTC_DCHECK(thread_checker_.CalledOnValidThread());
henrike@webrtc.org28e20752013-07-10 00:45:36 +000054 CaptureState result = Start(capture_format);
55 const bool success = (result == CS_RUNNING) || (result == CS_STARTING);
56 if (!success) {
57 return false;
58 }
59 if (result == CS_RUNNING) {
60 SetCaptureState(result);
61 }
62 return true;
63}
64
henrike@webrtc.org28e20752013-07-10 00:45:36 +000065void VideoCapturer::SetSupportedFormats(
66 const std::vector<VideoFormat>& formats) {
Perfb45d172016-02-29 12:07:35 +010067 // This method is OK to call during initialization on a separate thread.
68 RTC_DCHECK(capture_state_ == CS_STOPPED ||
69 thread_checker_.CalledOnValidThread());
henrike@webrtc.org28e20752013-07-10 00:45:36 +000070 supported_formats_ = formats;
71 UpdateFilteredSupportedFormats();
72}
73
74bool VideoCapturer::GetBestCaptureFormat(const VideoFormat& format,
75 VideoFormat* best_format) {
Perfb45d172016-02-29 12:07:35 +010076 RTC_DCHECK(thread_checker_.CalledOnValidThread());
henrike@webrtc.org28e20752013-07-10 00:45:36 +000077 // TODO(fbarchard): Directly support max_format.
78 UpdateFilteredSupportedFormats();
79 const std::vector<VideoFormat>* supported_formats = GetSupportedFormats();
80
81 if (supported_formats->empty()) {
82 return false;
83 }
84 LOG(LS_INFO) << " Capture Requested " << format.ToString();
Peter Boström0c4e06b2015-10-07 12:23:21 +020085 int64_t best_distance = kMaxDistance;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000086 std::vector<VideoFormat>::const_iterator best = supported_formats->end();
87 std::vector<VideoFormat>::const_iterator i;
88 for (i = supported_formats->begin(); i != supported_formats->end(); ++i) {
Peter Boström0c4e06b2015-10-07 12:23:21 +020089 int64_t distance = GetFormatDistance(format, *i);
henrike@webrtc.org28e20752013-07-10 00:45:36 +000090 // TODO(fbarchard): Reduce to LS_VERBOSE if/when camera capture is
91 // relatively bug free.
92 LOG(LS_INFO) << " Supported " << i->ToString() << " distance " << distance;
93 if (distance < best_distance) {
94 best_distance = distance;
95 best = i;
96 }
97 }
98 if (supported_formats->end() == best) {
99 LOG(LS_ERROR) << " No acceptable camera format found";
100 return false;
101 }
102
103 if (best_format) {
104 best_format->width = best->width;
105 best_format->height = best->height;
106 best_format->fourcc = best->fourcc;
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000107 best_format->interval = best->interval;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000108 LOG(LS_INFO) << " Best " << best_format->ToString() << " Interval "
109 << best_format->interval << " distance " << best_distance;
110 }
111 return true;
112}
113
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000114void VideoCapturer::ConstrainSupportedFormats(const VideoFormat& max_format) {
Perfb45d172016-02-29 12:07:35 +0100115 RTC_DCHECK(thread_checker_.CalledOnValidThread());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000116 max_format_.reset(new VideoFormat(max_format));
117 LOG(LS_VERBOSE) << " ConstrainSupportedFormats " << max_format.ToString();
118 UpdateFilteredSupportedFormats();
119}
120
nissefcc640f2016-04-01 01:10:42 -0700121bool VideoCapturer::GetInputSize(int* width, int* height) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000122 rtc::CritScope cs(&frame_stats_crit_);
nissefcc640f2016-04-01 01:10:42 -0700123 if (!input_size_valid_) {
124 return false;
125 }
126 *width = input_width_;
127 *height = input_height_;
128
129 return true;
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000130}
131
Pera5092412016-02-12 13:30:57 +0100132void VideoCapturer::RemoveSink(
nisseacd935b2016-11-11 03:55:13 -0800133 rtc::VideoSinkInterface<webrtc::VideoFrame>* sink) {
Perfb45d172016-02-29 12:07:35 +0100134 RTC_DCHECK(thread_checker_.CalledOnValidThread());
Pera5092412016-02-12 13:30:57 +0100135 broadcaster_.RemoveSink(sink);
perkj2d5f0912016-02-29 00:04:41 -0800136 OnSinkWantsChanged(broadcaster_.wants());
Pera5092412016-02-12 13:30:57 +0100137}
138
139void VideoCapturer::AddOrUpdateSink(
nisseacd935b2016-11-11 03:55:13 -0800140 rtc::VideoSinkInterface<webrtc::VideoFrame>* sink,
Pera5092412016-02-12 13:30:57 +0100141 const rtc::VideoSinkWants& wants) {
Perfb45d172016-02-29 12:07:35 +0100142 RTC_DCHECK(thread_checker_.CalledOnValidThread());
Pera5092412016-02-12 13:30:57 +0100143 broadcaster_.AddOrUpdateSink(sink, wants);
144 OnSinkWantsChanged(broadcaster_.wants());
145}
146
147void VideoCapturer::OnSinkWantsChanged(const rtc::VideoSinkWants& wants) {
Perfb45d172016-02-29 12:07:35 +0100148 RTC_DCHECK(thread_checker_.CalledOnValidThread());
Pera5092412016-02-12 13:30:57 +0100149 apply_rotation_ = wants.rotation_applied;
perkj2d5f0912016-02-29 00:04:41 -0800150
151 if (video_adapter()) {
sprangc5d62e22017-04-02 23:53:04 -0700152 video_adapter()->OnResolutionFramerateRequest(wants.target_pixel_count,
153 wants.max_pixel_count,
154 wants.max_framerate_fps);
perkj2d5f0912016-02-29 00:04:41 -0800155 }
Pera5092412016-02-12 13:30:57 +0100156}
157
nisse47ac4622016-05-25 08:47:01 -0700158bool VideoCapturer::AdaptFrame(int width,
159 int height,
nisse191b3592016-06-22 08:36:53 -0700160 int64_t camera_time_us,
161 int64_t system_time_us,
nisse47ac4622016-05-25 08:47:01 -0700162 int* out_width,
163 int* out_height,
164 int* crop_width,
165 int* crop_height,
166 int* crop_x,
nisse191b3592016-06-22 08:36:53 -0700167 int* crop_y,
168 int64_t* translated_camera_time_us) {
nissea0758482016-09-14 00:37:00 -0700169 if (translated_camera_time_us) {
170 *translated_camera_time_us =
171 timestamp_aligner_.TranslateTimestamp(camera_time_us, system_time_us);
172 }
Pera5092412016-02-12 13:30:57 +0100173 if (!broadcaster_.frame_wanted()) {
nisse47ac4622016-05-25 08:47:01 -0700174 return false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000175 }
braveyao@webrtc.org086c8d52014-12-22 05:46:42 +0000176
magjed@webrtc.orgf58b4552014-11-19 18:09:14 +0000177 if (enable_video_adapter_ && !IsScreencast()) {
nisse47ac4622016-05-25 08:47:01 -0700178 if (!video_adapter_.AdaptFrameResolution(
nisse191b3592016-06-22 08:36:53 -0700179 width, height, camera_time_us * rtc::kNumNanosecsPerMicrosec,
nisse47ac4622016-05-25 08:47:01 -0700180 crop_width, crop_height, out_width, out_height)) {
magjed@webrtc.orgf58b4552014-11-19 18:09:14 +0000181 // VideoAdapter dropped the frame.
nisse47ac4622016-05-25 08:47:01 -0700182 return false;
magjed@webrtc.orgf58b4552014-11-19 18:09:14 +0000183 }
nisse47ac4622016-05-25 08:47:01 -0700184 *crop_x = (width - *crop_width) / 2;
185 *crop_y = (height - *crop_height) / 2;
186 } else {
187 *out_width = width;
188 *out_height = height;
189 *crop_width = width;
190 *crop_height = height;
191 *crop_x = 0;
192 *crop_y = 0;
193 }
nisse191b3592016-06-22 08:36:53 -0700194
nisse47ac4622016-05-25 08:47:01 -0700195 return true;
196}
197
nisseacd935b2016-11-11 03:55:13 -0800198void VideoCapturer::OnFrame(const webrtc::VideoFrame& frame,
nisse47ac4622016-05-25 08:47:01 -0700199 int orig_width,
200 int orig_height) {
nissef5297a02016-09-30 01:34:27 -0700201 // For a child class which implements rotation itself, we should
202 // always have apply_rotation_ == false or frame.rotation() == 0.
203 // Except possibly during races where apply_rotation_ is changed
204 // mid-stream.
205 if (apply_rotation_ && frame.rotation() != webrtc::kVideoRotation_0) {
206 rtc::scoped_refptr<webrtc::VideoFrameBuffer> buffer(
207 frame.video_frame_buffer());
208 if (buffer->native_handle()) {
209 // Sources producing native frames must handle apply_rotation
210 // themselves. But even if they do, we may occasionally end up
211 // in this case, for frames in flight at the time
212 // applied_rotation is set to true. In that case, we just drop
213 // the frame.
214 LOG(LS_WARNING) << "Native frame requiring rotation. Discarding.";
215 return;
216 }
nisseacd935b2016-11-11 03:55:13 -0800217 broadcaster_.OnFrame(webrtc::VideoFrame(
nisseaf916892017-01-10 07:44:26 -0800218 webrtc::I420Buffer::Rotate(*buffer, frame.rotation()),
nissef5297a02016-09-30 01:34:27 -0700219 webrtc::kVideoRotation_0, frame.timestamp_us()));
220 } else {
221 broadcaster_.OnFrame(frame);
222 }
nisse47ac4622016-05-25 08:47:01 -0700223 UpdateInputSize(orig_width, orig_height);
Pera5092412016-02-12 13:30:57 +0100224}
225
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000226void VideoCapturer::SetCaptureState(CaptureState state) {
Perfb45d172016-02-29 12:07:35 +0100227 RTC_DCHECK(thread_checker_.CalledOnValidThread());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000228 if (state == capture_state_) {
229 // Don't trigger a state changed callback if the state hasn't changed.
230 return;
231 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000232 capture_state_ = state;
Perfb45d172016-02-29 12:07:35 +0100233 SignalStateChange(this, capture_state_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000234}
235
236// Get the distance between the supported and desired formats.
237// Prioritization is done according to this algorithm:
238// 1) Width closeness. If not same, we prefer wider.
239// 2) Height closeness. If not same, we prefer higher.
240// 3) Framerate closeness. If not same, we prefer faster.
241// 4) Compression. If desired format has a specific fourcc, we need exact match;
242// otherwise, we use preference.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200243int64_t VideoCapturer::GetFormatDistance(const VideoFormat& desired,
244 const VideoFormat& supported) {
Perfb45d172016-02-29 12:07:35 +0100245 RTC_DCHECK(thread_checker_.CalledOnValidThread());
Peter Boström0c4e06b2015-10-07 12:23:21 +0200246 int64_t distance = kMaxDistance;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000247
248 // Check fourcc.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200249 uint32_t supported_fourcc = CanonicalFourCC(supported.fourcc);
250 int64_t delta_fourcc = kMaxDistance;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000251 if (FOURCC_ANY == desired.fourcc) {
252 // Any fourcc is OK for the desired. Use preference to find best fourcc.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200253 std::vector<uint32_t> preferred_fourccs;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000254 if (!GetPreferredFourccs(&preferred_fourccs)) {
255 return distance;
256 }
257
258 for (size_t i = 0; i < preferred_fourccs.size(); ++i) {
259 if (supported_fourcc == CanonicalFourCC(preferred_fourccs[i])) {
260 delta_fourcc = i;
kjellanderfcfc8042016-01-14 11:01:09 -0800261#ifdef WEBRTC_LINUX
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000262 // For HD avoid YU12 which is a software conversion and has 2 bugs
263 // b/7326348 b/6960899. Reenable when fixed.
264 if (supported.height >= 720 && (supported_fourcc == FOURCC_YU12 ||
265 supported_fourcc == FOURCC_YV12)) {
266 delta_fourcc += kYU12Penalty;
267 }
268#endif
269 break;
270 }
271 }
272 } else if (supported_fourcc == CanonicalFourCC(desired.fourcc)) {
273 delta_fourcc = 0; // Need exact match.
274 }
275
276 if (kMaxDistance == delta_fourcc) {
277 // Failed to match fourcc.
278 return distance;
279 }
280
281 // Check resolution and fps.
282 int desired_width = desired.width;
283 int desired_height = desired.height;
Peter Boström0c4e06b2015-10-07 12:23:21 +0200284 int64_t delta_w = supported.width - desired_width;
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000285 float supported_fps = VideoFormat::IntervalToFpsFloat(supported.interval);
286 float delta_fps =
287 supported_fps - VideoFormat::IntervalToFpsFloat(desired.interval);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000288 // Check height of supported height compared to height we would like it to be.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200289 int64_t aspect_h = desired_width
290 ? supported.width * desired_height / desired_width
291 : desired_height;
292 int64_t delta_h = supported.height - aspect_h;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000293
294 distance = 0;
295 // Set high penalty if the supported format is lower than the desired format.
296 // 3x means we would prefer down to down to 3/4, than up to double.
297 // But we'd prefer up to double than down to 1/2. This is conservative,
298 // strongly avoiding going down in resolution, similar to
299 // the old method, but not completely ruling it out in extreme situations.
300 // It also ignores framerate, which is often very low at high resolutions.
301 // TODO(fbarchard): Improve logic to use weighted factors.
302 static const int kDownPenalty = -3;
303 if (delta_w < 0) {
304 delta_w = delta_w * kDownPenalty;
305 }
306 if (delta_h < 0) {
307 delta_h = delta_h * kDownPenalty;
308 }
309 // Require camera fps to be at least 80% of what is requested if resolution
310 // matches.
311 // Require camera fps to be at least 96% of what is requested, or higher,
312 // if resolution differs. 96% allows for slight variations in fps. e.g. 29.97
313 if (delta_fps < 0) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000314 float min_desirable_fps = delta_w ?
315 VideoFormat::IntervalToFpsFloat(desired.interval) * 28.f / 30.f :
316 VideoFormat::IntervalToFpsFloat(desired.interval) * 23.f / 30.f;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000317 delta_fps = -delta_fps;
318 if (supported_fps < min_desirable_fps) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200319 distance |= static_cast<int64_t>(1) << 62;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000320 } else {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200321 distance |= static_cast<int64_t>(1) << 15;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000322 }
323 }
Peter Boström0c4e06b2015-10-07 12:23:21 +0200324 int64_t idelta_fps = static_cast<int>(delta_fps);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000325
326 // 12 bits for width and height and 8 bits for fps and fourcc.
327 distance |=
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000328 (delta_w << 28) | (delta_h << 16) | (idelta_fps << 8) | delta_fourcc;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000329
330 return distance;
331}
332
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000333void VideoCapturer::UpdateFilteredSupportedFormats() {
334 filtered_supported_formats_.clear();
335 filtered_supported_formats_ = supported_formats_;
336 if (!max_format_) {
337 return;
338 }
339 std::vector<VideoFormat>::iterator iter = filtered_supported_formats_.begin();
340 while (iter != filtered_supported_formats_.end()) {
341 if (ShouldFilterFormat(*iter)) {
342 iter = filtered_supported_formats_.erase(iter);
343 } else {
344 ++iter;
345 }
346 }
347 if (filtered_supported_formats_.empty()) {
348 // The device only captures at resolutions higher than |max_format_| this
349 // indicates that |max_format_| should be ignored as it is better to capture
350 // at too high a resolution than to not capture at all.
351 filtered_supported_formats_ = supported_formats_;
352 }
353}
354
355bool VideoCapturer::ShouldFilterFormat(const VideoFormat& format) const {
Perfb45d172016-02-29 12:07:35 +0100356 RTC_DCHECK(thread_checker_.CalledOnValidThread());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000357 if (!enable_camera_list_) {
358 return false;
359 }
360 return format.width > max_format_->width ||
361 format.height > max_format_->height;
362}
363
nisse47ac4622016-05-25 08:47:01 -0700364void VideoCapturer::UpdateInputSize(int width, int height) {
buildbot@webrtc.org0b53bd22014-05-06 17:12:36 +0000365 // Update stats protected from fetches from different thread.
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000366 rtc::CritScope cs(&frame_stats_crit_);
buildbot@webrtc.org0b53bd22014-05-06 17:12:36 +0000367
nissefcc640f2016-04-01 01:10:42 -0700368 input_size_valid_ = true;
nisse47ac4622016-05-25 08:47:01 -0700369 input_width_ = width;
370 input_height_ = height;
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000371}
372
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000373} // namespace cricket