blob: efd075d4d08f795faa29dd81726dec5736fbb272 [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()) {
lliuuf9ed2352017-03-30 10:44:38 -0700152 video_adapter()->OnResolutionRequest(wants.target_pixel_count,
153 wants.max_pixel_count);
perkj2d5f0912016-02-29 00:04:41 -0800154 }
Pera5092412016-02-12 13:30:57 +0100155}
156
nisse47ac4622016-05-25 08:47:01 -0700157bool VideoCapturer::AdaptFrame(int width,
158 int height,
nisse191b3592016-06-22 08:36:53 -0700159 int64_t camera_time_us,
160 int64_t system_time_us,
nisse47ac4622016-05-25 08:47:01 -0700161 int* out_width,
162 int* out_height,
163 int* crop_width,
164 int* crop_height,
165 int* crop_x,
nisse191b3592016-06-22 08:36:53 -0700166 int* crop_y,
167 int64_t* translated_camera_time_us) {
nissea0758482016-09-14 00:37:00 -0700168 if (translated_camera_time_us) {
169 *translated_camera_time_us =
170 timestamp_aligner_.TranslateTimestamp(camera_time_us, system_time_us);
171 }
Pera5092412016-02-12 13:30:57 +0100172 if (!broadcaster_.frame_wanted()) {
nisse47ac4622016-05-25 08:47:01 -0700173 return false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000174 }
braveyao@webrtc.org086c8d52014-12-22 05:46:42 +0000175
magjed@webrtc.orgf58b4552014-11-19 18:09:14 +0000176 if (enable_video_adapter_ && !IsScreencast()) {
nisse47ac4622016-05-25 08:47:01 -0700177 if (!video_adapter_.AdaptFrameResolution(
nisse191b3592016-06-22 08:36:53 -0700178 width, height, camera_time_us * rtc::kNumNanosecsPerMicrosec,
nisse47ac4622016-05-25 08:47:01 -0700179 crop_width, crop_height, out_width, out_height)) {
magjed@webrtc.orgf58b4552014-11-19 18:09:14 +0000180 // VideoAdapter dropped the frame.
nisse47ac4622016-05-25 08:47:01 -0700181 return false;
magjed@webrtc.orgf58b4552014-11-19 18:09:14 +0000182 }
nisse47ac4622016-05-25 08:47:01 -0700183 *crop_x = (width - *crop_width) / 2;
184 *crop_y = (height - *crop_height) / 2;
185 } else {
186 *out_width = width;
187 *out_height = height;
188 *crop_width = width;
189 *crop_height = height;
190 *crop_x = 0;
191 *crop_y = 0;
192 }
nisse191b3592016-06-22 08:36:53 -0700193
nisse47ac4622016-05-25 08:47:01 -0700194 return true;
195}
196
nisseacd935b2016-11-11 03:55:13 -0800197void VideoCapturer::OnFrame(const webrtc::VideoFrame& frame,
nisse47ac4622016-05-25 08:47:01 -0700198 int orig_width,
199 int orig_height) {
nissef5297a02016-09-30 01:34:27 -0700200 // For a child class which implements rotation itself, we should
201 // always have apply_rotation_ == false or frame.rotation() == 0.
202 // Except possibly during races where apply_rotation_ is changed
203 // mid-stream.
204 if (apply_rotation_ && frame.rotation() != webrtc::kVideoRotation_0) {
205 rtc::scoped_refptr<webrtc::VideoFrameBuffer> buffer(
206 frame.video_frame_buffer());
207 if (buffer->native_handle()) {
208 // Sources producing native frames must handle apply_rotation
209 // themselves. But even if they do, we may occasionally end up
210 // in this case, for frames in flight at the time
211 // applied_rotation is set to true. In that case, we just drop
212 // the frame.
213 LOG(LS_WARNING) << "Native frame requiring rotation. Discarding.";
214 return;
215 }
nisseacd935b2016-11-11 03:55:13 -0800216 broadcaster_.OnFrame(webrtc::VideoFrame(
nisseaf916892017-01-10 07:44:26 -0800217 webrtc::I420Buffer::Rotate(*buffer, frame.rotation()),
nissef5297a02016-09-30 01:34:27 -0700218 webrtc::kVideoRotation_0, frame.timestamp_us()));
219 } else {
220 broadcaster_.OnFrame(frame);
221 }
nisse47ac4622016-05-25 08:47:01 -0700222 UpdateInputSize(orig_width, orig_height);
Pera5092412016-02-12 13:30:57 +0100223}
224
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000225void VideoCapturer::SetCaptureState(CaptureState state) {
Perfb45d172016-02-29 12:07:35 +0100226 RTC_DCHECK(thread_checker_.CalledOnValidThread());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000227 if (state == capture_state_) {
228 // Don't trigger a state changed callback if the state hasn't changed.
229 return;
230 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000231 capture_state_ = state;
Perfb45d172016-02-29 12:07:35 +0100232 SignalStateChange(this, capture_state_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000233}
234
235// Get the distance between the supported and desired formats.
236// Prioritization is done according to this algorithm:
237// 1) Width closeness. If not same, we prefer wider.
238// 2) Height closeness. If not same, we prefer higher.
239// 3) Framerate closeness. If not same, we prefer faster.
240// 4) Compression. If desired format has a specific fourcc, we need exact match;
241// otherwise, we use preference.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200242int64_t VideoCapturer::GetFormatDistance(const VideoFormat& desired,
243 const VideoFormat& supported) {
Perfb45d172016-02-29 12:07:35 +0100244 RTC_DCHECK(thread_checker_.CalledOnValidThread());
Peter Boström0c4e06b2015-10-07 12:23:21 +0200245 int64_t distance = kMaxDistance;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000246
247 // Check fourcc.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200248 uint32_t supported_fourcc = CanonicalFourCC(supported.fourcc);
249 int64_t delta_fourcc = kMaxDistance;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000250 if (FOURCC_ANY == desired.fourcc) {
251 // Any fourcc is OK for the desired. Use preference to find best fourcc.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200252 std::vector<uint32_t> preferred_fourccs;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000253 if (!GetPreferredFourccs(&preferred_fourccs)) {
254 return distance;
255 }
256
257 for (size_t i = 0; i < preferred_fourccs.size(); ++i) {
258 if (supported_fourcc == CanonicalFourCC(preferred_fourccs[i])) {
259 delta_fourcc = i;
kjellanderfcfc8042016-01-14 11:01:09 -0800260#ifdef WEBRTC_LINUX
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000261 // For HD avoid YU12 which is a software conversion and has 2 bugs
262 // b/7326348 b/6960899. Reenable when fixed.
263 if (supported.height >= 720 && (supported_fourcc == FOURCC_YU12 ||
264 supported_fourcc == FOURCC_YV12)) {
265 delta_fourcc += kYU12Penalty;
266 }
267#endif
268 break;
269 }
270 }
271 } else if (supported_fourcc == CanonicalFourCC(desired.fourcc)) {
272 delta_fourcc = 0; // Need exact match.
273 }
274
275 if (kMaxDistance == delta_fourcc) {
276 // Failed to match fourcc.
277 return distance;
278 }
279
280 // Check resolution and fps.
281 int desired_width = desired.width;
282 int desired_height = desired.height;
Peter Boström0c4e06b2015-10-07 12:23:21 +0200283 int64_t delta_w = supported.width - desired_width;
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000284 float supported_fps = VideoFormat::IntervalToFpsFloat(supported.interval);
285 float delta_fps =
286 supported_fps - VideoFormat::IntervalToFpsFloat(desired.interval);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000287 // Check height of supported height compared to height we would like it to be.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200288 int64_t aspect_h = desired_width
289 ? supported.width * desired_height / desired_width
290 : desired_height;
291 int64_t delta_h = supported.height - aspect_h;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000292
293 distance = 0;
294 // Set high penalty if the supported format is lower than the desired format.
295 // 3x means we would prefer down to down to 3/4, than up to double.
296 // But we'd prefer up to double than down to 1/2. This is conservative,
297 // strongly avoiding going down in resolution, similar to
298 // the old method, but not completely ruling it out in extreme situations.
299 // It also ignores framerate, which is often very low at high resolutions.
300 // TODO(fbarchard): Improve logic to use weighted factors.
301 static const int kDownPenalty = -3;
302 if (delta_w < 0) {
303 delta_w = delta_w * kDownPenalty;
304 }
305 if (delta_h < 0) {
306 delta_h = delta_h * kDownPenalty;
307 }
308 // Require camera fps to be at least 80% of what is requested if resolution
309 // matches.
310 // Require camera fps to be at least 96% of what is requested, or higher,
311 // if resolution differs. 96% allows for slight variations in fps. e.g. 29.97
312 if (delta_fps < 0) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000313 float min_desirable_fps = delta_w ?
314 VideoFormat::IntervalToFpsFloat(desired.interval) * 28.f / 30.f :
315 VideoFormat::IntervalToFpsFloat(desired.interval) * 23.f / 30.f;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000316 delta_fps = -delta_fps;
317 if (supported_fps < min_desirable_fps) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200318 distance |= static_cast<int64_t>(1) << 62;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000319 } else {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200320 distance |= static_cast<int64_t>(1) << 15;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000321 }
322 }
Peter Boström0c4e06b2015-10-07 12:23:21 +0200323 int64_t idelta_fps = static_cast<int>(delta_fps);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000324
325 // 12 bits for width and height and 8 bits for fps and fourcc.
326 distance |=
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000327 (delta_w << 28) | (delta_h << 16) | (idelta_fps << 8) | delta_fourcc;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000328
329 return distance;
330}
331
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000332void VideoCapturer::UpdateFilteredSupportedFormats() {
333 filtered_supported_formats_.clear();
334 filtered_supported_formats_ = supported_formats_;
335 if (!max_format_) {
336 return;
337 }
338 std::vector<VideoFormat>::iterator iter = filtered_supported_formats_.begin();
339 while (iter != filtered_supported_formats_.end()) {
340 if (ShouldFilterFormat(*iter)) {
341 iter = filtered_supported_formats_.erase(iter);
342 } else {
343 ++iter;
344 }
345 }
346 if (filtered_supported_formats_.empty()) {
347 // The device only captures at resolutions higher than |max_format_| this
348 // indicates that |max_format_| should be ignored as it is better to capture
349 // at too high a resolution than to not capture at all.
350 filtered_supported_formats_ = supported_formats_;
351 }
352}
353
354bool VideoCapturer::ShouldFilterFormat(const VideoFormat& format) const {
Perfb45d172016-02-29 12:07:35 +0100355 RTC_DCHECK(thread_checker_.CalledOnValidThread());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000356 if (!enable_camera_list_) {
357 return false;
358 }
359 return format.width > max_format_->width ||
360 format.height > max_format_->height;
361}
362
nisse47ac4622016-05-25 08:47:01 -0700363void VideoCapturer::UpdateInputSize(int width, int height) {
buildbot@webrtc.org0b53bd22014-05-06 17:12:36 +0000364 // Update stats protected from fetches from different thread.
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000365 rtc::CritScope cs(&frame_stats_crit_);
buildbot@webrtc.org0b53bd22014-05-06 17:12:36 +0000366
nissefcc640f2016-04-01 01:10:42 -0700367 input_size_valid_ = true;
nisse47ac4622016-05-25 08:47:01 -0700368 input_width_ = width;
369 input_height_ = height;
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000370}
371
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000372} // namespace cricket