blob: b9d865ef46adff30826ce6b24b438889fa3b747d [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
henrike@webrtc.org28e20752013-07-10 00:45:36 +000017#include "libyuv/scale_argb.h"
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000018#include "webrtc/base/common.h"
19#include "webrtc/base/logging.h"
20#include "webrtc/base/systeminfo.h"
kjellandera96e2d72016-02-04 23:52:28 -080021#include "webrtc/media/base/videoframefactory.h"
perkj74622e02016-02-26 02:54:38 -080022
23#if defined(HAVE_WEBRTC_VIDEO)
kjellander@webrtc.org5ad12972016-02-12 06:39:40 +010024#include "webrtc/media/engine/webrtcvideoframe.h"
25#include "webrtc/media/engine/webrtcvideoframefactory.h"
perkj74622e02016-02-26 02:54:38 -080026#endif // HAVE_WEBRTC_VIDEO
henrike@webrtc.org28e20752013-07-10 00:45:36 +000027
henrike@webrtc.org28e20752013-07-10 00:45:36 +000028namespace cricket {
29
30namespace {
31
perkj74622e02016-02-26 02:54:38 -080032// TODO(thorcarpenter): This is a BIG hack to flush the system with black
33// frames. Frontends should coordinate to update the video state of a muted
34// user. When all frontends to this consider removing the black frame business.
35const int kNumBlackFramesOnMute = 30;
36
37// MessageHandler constants.
38enum {
39 MSG_DO_PAUSE = 0,
40 MSG_DO_UNPAUSE,
41 MSG_STATE_CHANGE
42};
43
Peter Boström0c4e06b2015-10-07 12:23:21 +020044static const int64_t kMaxDistance = ~(static_cast<int64_t>(1) << 63);
kjellanderfcfc8042016-01-14 11:01:09 -080045#ifdef WEBRTC_LINUX
henrike@webrtc.org28e20752013-07-10 00:45:36 +000046static const int kYU12Penalty = 16; // Needs to be higher than MJPG index.
henrike@webrtc.orgf5bebd42014-04-04 18:39:07 +000047#endif
henrike@webrtc.org28e20752013-07-10 00:45:36 +000048static const int kDefaultScreencastFps = 5;
perkj74622e02016-02-26 02:54:38 -080049typedef rtc::TypedMessageData<CaptureState> StateChangeParams;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000050
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +000051// Limit stats data collections to ~20 seconds of 30fps data before dropping
52// old data in case stats aren't reset for long periods of time.
53static const size_t kMaxAccumulatorSize = 600;
54
henrike@webrtc.org28e20752013-07-10 00:45:36 +000055} // namespace
56
57/////////////////////////////////////////////////////////////////////
58// Implementation of struct CapturedFrame
59/////////////////////////////////////////////////////////////////////
60CapturedFrame::CapturedFrame()
61 : width(0),
62 height(0),
63 fourcc(0),
64 pixel_width(0),
65 pixel_height(0),
henrike@webrtc.org28e20752013-07-10 00:45:36 +000066 time_stamp(0),
67 data_size(0),
perkj7755e202015-11-19 12:02:21 -080068 rotation(webrtc::kVideoRotation_0),
henrike@webrtc.org28e20752013-07-10 00:45:36 +000069 data(NULL) {}
70
71// TODO(fbarchard): Remove this function once lmimediaengine stops using it.
Peter Boström0c4e06b2015-10-07 12:23:21 +020072bool CapturedFrame::GetDataSize(uint32_t* size) const {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000073 if (!size || data_size == CapturedFrame::kUnknownDataSize) {
74 return false;
75 }
76 *size = data_size;
77 return true;
78}
79
80/////////////////////////////////////////////////////////////////////
81// Implementation of class VideoCapturer
82/////////////////////////////////////////////////////////////////////
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +000083VideoCapturer::VideoCapturer()
perkj74622e02016-02-26 02:54:38 -080084 : thread_(rtc::Thread::Current()),
85 adapt_frame_drops_data_(kMaxAccumulatorSize),
guoweis@webrtc.org1226e922015-02-11 18:37:54 +000086 frame_time_data_(kMaxAccumulatorSize),
87 apply_rotation_(true) {
perkj74622e02016-02-26 02:54:38 -080088 Construct();
89}
90
91VideoCapturer::VideoCapturer(rtc::Thread* thread)
92 : thread_(thread),
93 adapt_frame_drops_data_(kMaxAccumulatorSize),
94 frame_time_data_(kMaxAccumulatorSize),
95 apply_rotation_(true) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000096 Construct();
97}
98
99void VideoCapturer::Construct() {
perkj74622e02016-02-26 02:54:38 -0800100 ClearAspectRatio();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000101 enable_camera_list_ = false;
mallinath@webrtc.org1b15f422013-09-06 22:56:28 +0000102 square_pixel_aspect_ratio_ = false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000103 capture_state_ = CS_STOPPED;
104 SignalFrameCaptured.connect(this, &VideoCapturer::OnFrameCaptured);
Pera5092412016-02-12 13:30:57 +0100105 // TODO(perkj) SignalVideoFrame is used directly by Chrome remoting.
106 // Before that is refactored, SignalVideoFrame must forward frames to the
107 // |VideoBroadcaster|;
108 SignalVideoFrame.connect(this, &VideoCapturer::OnFrame);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000109 scaled_width_ = 0;
110 scaled_height_ = 0;
perkj74622e02016-02-26 02:54:38 -0800111 muted_ = false;
112 black_frame_count_down_ = kNumBlackFramesOnMute;
henrike@webrtc.orga7b98182014-02-21 15:51:43 +0000113 enable_video_adapter_ = true;
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000114 adapt_frame_drops_ = 0;
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000115 previous_frame_time_ = 0.0;
perkj74622e02016-02-26 02:54:38 -0800116#ifdef HAVE_WEBRTC_VIDEO
buildbot@webrtc.orgc3df61e2014-08-13 23:57:23 +0000117 // There are lots of video capturers out there that don't call
118 // set_frame_factory. We can either go change all of them, or we
119 // can set this default.
120 // TODO(pthatcher): Remove this hack and require the frame factory
121 // to be passed in the constructor.
122 set_frame_factory(new WebRtcVideoFrameFactory());
perkj74622e02016-02-26 02:54:38 -0800123#endif
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000124}
125
126const std::vector<VideoFormat>* VideoCapturer::GetSupportedFormats() const {
127 return &filtered_supported_formats_;
128}
129
130bool VideoCapturer::StartCapturing(const VideoFormat& capture_format) {
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000131 previous_frame_time_ = frame_length_time_reporter_.TimerNow();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000132 CaptureState result = Start(capture_format);
133 const bool success = (result == CS_RUNNING) || (result == CS_STARTING);
134 if (!success) {
135 return false;
136 }
137 if (result == CS_RUNNING) {
138 SetCaptureState(result);
139 }
140 return true;
141}
142
perkj74622e02016-02-26 02:54:38 -0800143void VideoCapturer::UpdateAspectRatio(int ratio_w, int ratio_h) {
144 if (ratio_w == 0 || ratio_h == 0) {
145 LOG(LS_WARNING) << "UpdateAspectRatio ignored invalid ratio: "
146 << ratio_w << "x" << ratio_h;
147 return;
148 }
149 ratio_w_ = ratio_w;
150 ratio_h_ = ratio_h;
151}
152
153void VideoCapturer::ClearAspectRatio() {
154 ratio_w_ = 0;
155 ratio_h_ = 0;
156}
157
158// Override this to have more control of how your device is started/stopped.
159bool VideoCapturer::Pause(bool pause) {
160 if (pause) {
161 if (capture_state() == CS_PAUSED) {
162 return true;
163 }
164 bool is_running = capture_state() == CS_STARTING ||
165 capture_state() == CS_RUNNING;
166 if (!is_running) {
167 LOG(LS_ERROR) << "Cannot pause a stopped camera.";
168 return false;
169 }
170 LOG(LS_INFO) << "Pausing a camera.";
kwiberg686a8ef2016-02-26 03:00:35 -0800171 std::unique_ptr<VideoFormat> capture_format_when_paused(
perkj74622e02016-02-26 02:54:38 -0800172 capture_format_ ? new VideoFormat(*capture_format_) : NULL);
173 Stop();
174 SetCaptureState(CS_PAUSED);
175 // If you override this function be sure to restore the capture format
176 // after calling Stop().
177 SetCaptureFormat(capture_format_when_paused.get());
178 } else { // Unpause.
179 if (capture_state() != CS_PAUSED) {
180 LOG(LS_WARNING) << "Cannot unpause a camera that hasn't been paused.";
181 return false;
182 }
183 if (!capture_format_) {
184 LOG(LS_ERROR) << "Missing capture_format_, cannot unpause a camera.";
185 return false;
186 }
187 if (muted_) {
188 LOG(LS_WARNING) << "Camera cannot be unpaused while muted.";
189 return false;
190 }
191 LOG(LS_INFO) << "Unpausing a camera.";
192 if (!Start(*capture_format_)) {
193 LOG(LS_ERROR) << "Camera failed to start when unpausing.";
194 return false;
195 }
196 }
197 return true;
198}
199
200bool VideoCapturer::Restart(const VideoFormat& capture_format) {
201 if (!IsRunning()) {
202 return StartCapturing(capture_format);
203 }
204
205 if (GetCaptureFormat() != NULL && *GetCaptureFormat() == capture_format) {
206 // The reqested format is the same; nothing to do.
207 return true;
208 }
209
210 Stop();
211 return StartCapturing(capture_format);
212}
213
214bool VideoCapturer::MuteToBlackThenPause(bool muted) {
215 if (muted == IsMuted()) {
216 return true;
217 }
218
219 LOG(LS_INFO) << (muted ? "Muting" : "Unmuting") << " this video capturer.";
220 muted_ = muted; // Do this before calling Pause().
221 if (muted) {
222 // Reset black frame count down.
223 black_frame_count_down_ = kNumBlackFramesOnMute;
224 // Following frames will be overritten with black, then the camera will be
225 // paused.
226 return true;
227 }
228 // Start the camera.
229 thread_->Clear(this, MSG_DO_PAUSE);
230 return Pause(false);
231}
232
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000233void VideoCapturer::SetSupportedFormats(
234 const std::vector<VideoFormat>& formats) {
235 supported_formats_ = formats;
236 UpdateFilteredSupportedFormats();
237}
238
239bool VideoCapturer::GetBestCaptureFormat(const VideoFormat& format,
240 VideoFormat* best_format) {
241 // TODO(fbarchard): Directly support max_format.
242 UpdateFilteredSupportedFormats();
243 const std::vector<VideoFormat>* supported_formats = GetSupportedFormats();
244
245 if (supported_formats->empty()) {
246 return false;
247 }
248 LOG(LS_INFO) << " Capture Requested " << format.ToString();
Peter Boström0c4e06b2015-10-07 12:23:21 +0200249 int64_t best_distance = kMaxDistance;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000250 std::vector<VideoFormat>::const_iterator best = supported_formats->end();
251 std::vector<VideoFormat>::const_iterator i;
252 for (i = supported_formats->begin(); i != supported_formats->end(); ++i) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200253 int64_t distance = GetFormatDistance(format, *i);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000254 // TODO(fbarchard): Reduce to LS_VERBOSE if/when camera capture is
255 // relatively bug free.
256 LOG(LS_INFO) << " Supported " << i->ToString() << " distance " << distance;
257 if (distance < best_distance) {
258 best_distance = distance;
259 best = i;
260 }
261 }
262 if (supported_formats->end() == best) {
263 LOG(LS_ERROR) << " No acceptable camera format found";
264 return false;
265 }
266
267 if (best_format) {
268 best_format->width = best->width;
269 best_format->height = best->height;
270 best_format->fourcc = best->fourcc;
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000271 best_format->interval = best->interval;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000272 LOG(LS_INFO) << " Best " << best_format->ToString() << " Interval "
273 << best_format->interval << " distance " << best_distance;
274 }
275 return true;
276}
277
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000278void VideoCapturer::ConstrainSupportedFormats(const VideoFormat& max_format) {
279 max_format_.reset(new VideoFormat(max_format));
280 LOG(LS_VERBOSE) << " ConstrainSupportedFormats " << max_format.ToString();
281 UpdateFilteredSupportedFormats();
282}
283
284std::string VideoCapturer::ToString(const CapturedFrame* captured_frame) const {
285 std::string fourcc_name = GetFourccName(captured_frame->fourcc) + " ";
286 for (std::string::const_iterator i = fourcc_name.begin();
287 i < fourcc_name.end(); ++i) {
288 // Test character is printable; Avoid isprint() which asserts on negatives.
289 if (*i < 32 || *i >= 127) {
290 fourcc_name = "";
291 break;
292 }
293 }
294
295 std::ostringstream ss;
magjedb09b6602015-10-01 03:02:44 -0700296 ss << fourcc_name << captured_frame->width << "x" << captured_frame->height;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000297 return ss.str();
298}
299
guoweis@webrtc.org1226e922015-02-11 18:37:54 +0000300void VideoCapturer::set_frame_factory(VideoFrameFactory* frame_factory) {
301 frame_factory_.reset(frame_factory);
302 if (frame_factory) {
303 frame_factory->SetApplyRotation(apply_rotation_);
304 }
305}
306
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000307void VideoCapturer::GetStats(VariableInfo<int>* adapt_drops_stats,
308 VariableInfo<int>* effect_drops_stats,
buildbot@webrtc.org0b53bd22014-05-06 17:12:36 +0000309 VariableInfo<double>* frame_time_stats,
310 VideoFormat* last_captured_frame_format) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000311 rtc::CritScope cs(&frame_stats_crit_);
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000312 GetVariableSnapshot(adapt_frame_drops_data_, adapt_drops_stats);
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000313 GetVariableSnapshot(frame_time_data_, frame_time_stats);
buildbot@webrtc.org0b53bd22014-05-06 17:12:36 +0000314 *last_captured_frame_format = last_captured_frame_format_;
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000315
316 adapt_frame_drops_data_.Reset();
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000317 frame_time_data_.Reset();
318}
319
Pera5092412016-02-12 13:30:57 +0100320void VideoCapturer::RemoveSink(
321 rtc::VideoSinkInterface<cricket::VideoFrame>* sink) {
322 broadcaster_.RemoveSink(sink);
perkj2d5f0912016-02-29 00:04:41 -0800323 OnSinkWantsChanged(broadcaster_.wants());
Pera5092412016-02-12 13:30:57 +0100324}
325
326void VideoCapturer::AddOrUpdateSink(
327 rtc::VideoSinkInterface<cricket::VideoFrame>* sink,
328 const rtc::VideoSinkWants& wants) {
329 broadcaster_.AddOrUpdateSink(sink, wants);
330 OnSinkWantsChanged(broadcaster_.wants());
331}
332
333void VideoCapturer::OnSinkWantsChanged(const rtc::VideoSinkWants& wants) {
334 apply_rotation_ = wants.rotation_applied;
335 if (frame_factory_) {
336 frame_factory_->SetApplyRotation(apply_rotation_);
337 }
perkj2d5f0912016-02-29 00:04:41 -0800338
339 if (video_adapter()) {
340 video_adapter()->OnCpuResolutionRequest(wants.max_pixel_count,
341 wants.max_pixel_count_step_up);
342 }
Pera5092412016-02-12 13:30:57 +0100343}
344
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000345void VideoCapturer::OnFrameCaptured(VideoCapturer*,
346 const CapturedFrame* captured_frame) {
perkj74622e02016-02-26 02:54:38 -0800347 if (muted_) {
348 if (black_frame_count_down_ == 0) {
349 thread_->Post(this, MSG_DO_PAUSE, NULL);
350 } else {
351 --black_frame_count_down_;
352 }
353 }
354
Pera5092412016-02-12 13:30:57 +0100355 if (!broadcaster_.frame_wanted()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000356 return;
357 }
braveyao@webrtc.org086c8d52014-12-22 05:46:42 +0000358
359 // Use a temporary buffer to scale
kwiberg686a8ef2016-02-26 03:00:35 -0800360 std::unique_ptr<uint8_t[]> scale_buffer;
perkj74622e02016-02-26 02:54:38 -0800361
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000362 if (IsScreencast()) {
363 int scaled_width, scaled_height;
perkj74622e02016-02-26 02:54:38 -0800364 int desired_screencast_fps = capture_format_.get() ?
365 VideoFormat::IntervalToFps(capture_format_->interval) :
366 kDefaultScreencastFps;
nissee6bf5872015-12-21 13:18:13 -0800367 ComputeScale(captured_frame->width, captured_frame->height,
368 desired_screencast_fps, &scaled_width, &scaled_height);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000369
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000370 if (FOURCC_ARGB == captured_frame->fourcc &&
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000371 (scaled_width != captured_frame->width ||
perkj74622e02016-02-26 02:54:38 -0800372 scaled_height != captured_frame->height)) {
mallinath@webrtc.org1b15f422013-09-06 22:56:28 +0000373 if (scaled_width != scaled_width_ || scaled_height != scaled_height_) {
perkj74622e02016-02-26 02:54:38 -0800374 LOG(LS_INFO) << "Scaling Screencast from "
375 << captured_frame->width << "x"
376 << captured_frame->height << " to "
377 << scaled_width << "x" << scaled_height;
mallinath@webrtc.org1b15f422013-09-06 22:56:28 +0000378 scaled_width_ = scaled_width;
379 scaled_height_ = scaled_height;
380 }
381 CapturedFrame* modified_frame =
382 const_cast<CapturedFrame*>(captured_frame);
braveyao@webrtc.org086c8d52014-12-22 05:46:42 +0000383 const int modified_frame_size = scaled_width * scaled_height * 4;
Peter Boström0c4e06b2015-10-07 12:23:21 +0200384 scale_buffer.reset(new uint8_t[modified_frame_size]);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000385 // Compute new width such that width * height is less than maximum but
386 // maintains original captured frame aspect ratio.
387 // Round down width to multiple of 4 so odd width won't round up beyond
388 // maximum, and so chroma channel is even width to simplify spatial
389 // resampling.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200390 libyuv::ARGBScale(reinterpret_cast<const uint8_t*>(captured_frame->data),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000391 captured_frame->width * 4, captured_frame->width,
Peter Boström0c4e06b2015-10-07 12:23:21 +0200392 captured_frame->height, scale_buffer.get(),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000393 scaled_width * 4, scaled_width, scaled_height,
394 libyuv::kFilterBilinear);
mallinath@webrtc.org1b15f422013-09-06 22:56:28 +0000395 modified_frame->width = scaled_width;
396 modified_frame->height = scaled_height;
397 modified_frame->data_size = scaled_width * 4 * scaled_height;
braveyao@webrtc.org086c8d52014-12-22 05:46:42 +0000398 modified_frame->data = scale_buffer.get();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000399 }
400 }
mallinath@webrtc.org1b15f422013-09-06 22:56:28 +0000401
402 const int kYuy2Bpp = 2;
403 const int kArgbBpp = 4;
404 // TODO(fbarchard): Make a helper function to adjust pixels to square.
405 // TODO(fbarchard): Hook up experiment to scaling.
perkj74622e02016-02-26 02:54:38 -0800406 // TODO(fbarchard): Avoid scale and convert if muted.
mallinath@webrtc.org1b15f422013-09-06 22:56:28 +0000407 // Temporary buffer is scoped here so it will persist until i420_frame.Init()
408 // makes a copy of the frame, converting to I420.
kwiberg686a8ef2016-02-26 03:00:35 -0800409 std::unique_ptr<uint8_t[]> temp_buffer;
mallinath@webrtc.org1b15f422013-09-06 22:56:28 +0000410 // YUY2 can be scaled vertically using an ARGB scaler. Aspect ratio is only
411 // a problem on OSX. OSX always converts webcams to YUY2 or UYVY.
412 bool can_scale =
413 FOURCC_YUY2 == CanonicalFourCC(captured_frame->fourcc) ||
414 FOURCC_UYVY == CanonicalFourCC(captured_frame->fourcc);
415
416 // If pixels are not square, optionally use vertical scaling to make them
417 // square. Square pixels simplify the rest of the pipeline, including
418 // effects and rendering.
419 if (can_scale && square_pixel_aspect_ratio_ &&
420 captured_frame->pixel_width != captured_frame->pixel_height) {
421 int scaled_width, scaled_height;
422 // modified_frame points to the captured_frame but with const casted away
423 // so it can be modified.
424 CapturedFrame* modified_frame = const_cast<CapturedFrame*>(captured_frame);
425 // Compute the frame size that makes pixels square pixel aspect ratio.
426 ComputeScaleToSquarePixels(captured_frame->width, captured_frame->height,
427 captured_frame->pixel_width,
428 captured_frame->pixel_height,
429 &scaled_width, &scaled_height);
430
431 if (scaled_width != scaled_width_ || scaled_height != scaled_height_) {
432 LOG(LS_INFO) << "Scaling WebCam from "
433 << captured_frame->width << "x"
434 << captured_frame->height << " to "
435 << scaled_width << "x" << scaled_height
436 << " for PAR "
437 << captured_frame->pixel_width << "x"
438 << captured_frame->pixel_height;
439 scaled_width_ = scaled_width;
440 scaled_height_ = scaled_height;
441 }
442 const int modified_frame_size = scaled_width * scaled_height * kYuy2Bpp;
Peter Boström0c4e06b2015-10-07 12:23:21 +0200443 uint8_t* temp_buffer_data;
mallinath@webrtc.org1b15f422013-09-06 22:56:28 +0000444 // Pixels are wide and short; Increasing height. Requires temporary buffer.
445 if (scaled_height > captured_frame->height) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200446 temp_buffer.reset(new uint8_t[modified_frame_size]);
mallinath@webrtc.org1b15f422013-09-06 22:56:28 +0000447 temp_buffer_data = temp_buffer.get();
448 } else {
449 // Pixels are narrow and tall; Decreasing height. Scale will be done
450 // in place.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200451 temp_buffer_data = reinterpret_cast<uint8_t*>(captured_frame->data);
mallinath@webrtc.org1b15f422013-09-06 22:56:28 +0000452 }
453
454 // Use ARGBScaler to vertically scale the YUY2 image, adjusting for 16 bpp.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200455 libyuv::ARGBScale(reinterpret_cast<const uint8_t*>(captured_frame->data),
mallinath@webrtc.org1b15f422013-09-06 22:56:28 +0000456 captured_frame->width * kYuy2Bpp, // Stride for YUY2.
457 captured_frame->width * kYuy2Bpp / kArgbBpp, // Width.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200458 abs(captured_frame->height), // Height.
mallinath@webrtc.org1b15f422013-09-06 22:56:28 +0000459 temp_buffer_data,
Peter Boström0c4e06b2015-10-07 12:23:21 +0200460 scaled_width * kYuy2Bpp, // Stride for YUY2.
mallinath@webrtc.org1b15f422013-09-06 22:56:28 +0000461 scaled_width * kYuy2Bpp / kArgbBpp, // Width.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200462 abs(scaled_height), // New height.
mallinath@webrtc.org1b15f422013-09-06 22:56:28 +0000463 libyuv::kFilterBilinear);
464 modified_frame->width = scaled_width;
465 modified_frame->height = scaled_height;
466 modified_frame->pixel_width = 1;
467 modified_frame->pixel_height = 1;
468 modified_frame->data_size = modified_frame_size;
469 modified_frame->data = temp_buffer_data;
470 }
mallinath@webrtc.org1b15f422013-09-06 22:56:28 +0000471
472 // Size to crop captured frame to. This adjusts the captured frames
473 // aspect ratio to match the final view aspect ratio, considering pixel
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000474 // aspect ratio and rotation. The final size may be scaled down by video
475 // adapter to better match ratio_w_ x ratio_h_.
476 // Note that abs() of frame height is passed in, because source may be
477 // inverted, but output will be positive.
magjed@webrtc.orgf58b4552014-11-19 18:09:14 +0000478 int cropped_width = captured_frame->width;
479 int cropped_height = captured_frame->height;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000480
481 // TODO(fbarchard): Improve logic to pad or crop.
482 // MJPG can crop vertically, but not horizontally. This logic disables crop.
483 // Alternatively we could pad the image with black, or implement a 2 step
484 // crop.
485 bool can_crop = true;
486 if (captured_frame->fourcc == FOURCC_MJPG) {
487 float cam_aspect = static_cast<float>(captured_frame->width) /
488 static_cast<float>(captured_frame->height);
489 float view_aspect = static_cast<float>(ratio_w_) /
490 static_cast<float>(ratio_h_);
491 can_crop = cam_aspect <= view_aspect;
492 }
493 if (can_crop && !IsScreencast()) {
494 // TODO(ronghuawu): The capturer should always produce the native
495 // resolution and the cropping should be done in downstream code.
496 ComputeCrop(ratio_w_, ratio_h_, captured_frame->width,
497 abs(captured_frame->height), captured_frame->pixel_width,
498 captured_frame->pixel_height, captured_frame->rotation,
magjed@webrtc.orgf58b4552014-11-19 18:09:14 +0000499 &cropped_width, &cropped_height);
500 }
501
502 int adapted_width = cropped_width;
503 int adapted_height = cropped_height;
504 if (enable_video_adapter_ && !IsScreencast()) {
505 const VideoFormat adapted_format =
506 video_adapter_.AdaptFrameResolution(cropped_width, cropped_height);
507 if (adapted_format.IsSize0x0()) {
508 // VideoAdapter dropped the frame.
509 ++adapt_frame_drops_;
510 return;
511 }
512 adapted_width = adapted_format.width;
513 adapted_height = adapted_format.height;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000514 }
515
buildbot@webrtc.org4f0d4012014-08-07 04:47:36 +0000516 if (!frame_factory_) {
517 LOG(LS_ERROR) << "No video frame factory.";
518 return;
519 }
520
kwiberg686a8ef2016-02-26 03:00:35 -0800521 std::unique_ptr<VideoFrame> adapted_frame(
magjed@webrtc.orgf58b4552014-11-19 18:09:14 +0000522 frame_factory_->CreateAliasedFrame(captured_frame,
523 cropped_width, cropped_height,
524 adapted_width, adapted_height));
525
526 if (!adapted_frame) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000527 // TODO(fbarchard): LOG more information about captured frame attributes.
528 LOG(LS_ERROR) << "Couldn't convert to I420! "
529 << "From " << ToString(captured_frame) << " To "
magjed@webrtc.orgf58b4552014-11-19 18:09:14 +0000530 << cropped_width << " x " << cropped_height;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000531 return;
532 }
mallinath@webrtc.org67ee6b92014-02-03 16:57:16 +0000533
perkj74622e02016-02-26 02:54:38 -0800534 if (muted_) {
535 // TODO(pthatcher): Use frame_factory_->CreateBlackFrame() instead.
536 adapted_frame->SetToBlack();
537 }
perkj162c3392016-02-11 02:56:35 -0800538 SignalVideoFrame(this, adapted_frame.get());
buildbot@webrtc.org0b53bd22014-05-06 17:12:36 +0000539 UpdateStats(captured_frame);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000540}
541
Pera5092412016-02-12 13:30:57 +0100542void VideoCapturer::OnFrame(VideoCapturer* capturer, const VideoFrame* frame) {
543 broadcaster_.OnFrame(*frame);
544}
545
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000546void VideoCapturer::SetCaptureState(CaptureState state) {
547 if (state == capture_state_) {
548 // Don't trigger a state changed callback if the state hasn't changed.
549 return;
550 }
perkj74622e02016-02-26 02:54:38 -0800551 StateChangeParams* state_params = new StateChangeParams(state);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000552 capture_state_ = state;
perkj74622e02016-02-26 02:54:38 -0800553 thread_->Post(this, MSG_STATE_CHANGE, state_params);
554}
555
556void VideoCapturer::OnMessage(rtc::Message* message) {
557 switch (message->message_id) {
558 case MSG_STATE_CHANGE: {
kwiberg686a8ef2016-02-26 03:00:35 -0800559 std::unique_ptr<StateChangeParams> p(
perkj74622e02016-02-26 02:54:38 -0800560 static_cast<StateChangeParams*>(message->pdata));
561 SignalStateChange(this, p->data());
562 break;
563 }
564 case MSG_DO_PAUSE: {
565 Pause(true);
566 break;
567 }
568 case MSG_DO_UNPAUSE: {
569 Pause(false);
570 break;
571 }
572 default: {
573 ASSERT(false);
574 }
575 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000576}
577
578// Get the distance between the supported and desired formats.
579// Prioritization is done according to this algorithm:
580// 1) Width closeness. If not same, we prefer wider.
581// 2) Height closeness. If not same, we prefer higher.
582// 3) Framerate closeness. If not same, we prefer faster.
583// 4) Compression. If desired format has a specific fourcc, we need exact match;
584// otherwise, we use preference.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200585int64_t VideoCapturer::GetFormatDistance(const VideoFormat& desired,
586 const VideoFormat& supported) {
587 int64_t distance = kMaxDistance;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000588
589 // Check fourcc.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200590 uint32_t supported_fourcc = CanonicalFourCC(supported.fourcc);
591 int64_t delta_fourcc = kMaxDistance;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000592 if (FOURCC_ANY == desired.fourcc) {
593 // Any fourcc is OK for the desired. Use preference to find best fourcc.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200594 std::vector<uint32_t> preferred_fourccs;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000595 if (!GetPreferredFourccs(&preferred_fourccs)) {
596 return distance;
597 }
598
599 for (size_t i = 0; i < preferred_fourccs.size(); ++i) {
600 if (supported_fourcc == CanonicalFourCC(preferred_fourccs[i])) {
601 delta_fourcc = i;
kjellanderfcfc8042016-01-14 11:01:09 -0800602#ifdef WEBRTC_LINUX
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000603 // For HD avoid YU12 which is a software conversion and has 2 bugs
604 // b/7326348 b/6960899. Reenable when fixed.
605 if (supported.height >= 720 && (supported_fourcc == FOURCC_YU12 ||
606 supported_fourcc == FOURCC_YV12)) {
607 delta_fourcc += kYU12Penalty;
608 }
609#endif
610 break;
611 }
612 }
613 } else if (supported_fourcc == CanonicalFourCC(desired.fourcc)) {
614 delta_fourcc = 0; // Need exact match.
615 }
616
617 if (kMaxDistance == delta_fourcc) {
618 // Failed to match fourcc.
619 return distance;
620 }
621
622 // Check resolution and fps.
623 int desired_width = desired.width;
624 int desired_height = desired.height;
Peter Boström0c4e06b2015-10-07 12:23:21 +0200625 int64_t delta_w = supported.width - desired_width;
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000626 float supported_fps = VideoFormat::IntervalToFpsFloat(supported.interval);
627 float delta_fps =
628 supported_fps - VideoFormat::IntervalToFpsFloat(desired.interval);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000629 // Check height of supported height compared to height we would like it to be.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200630 int64_t aspect_h = desired_width
631 ? supported.width * desired_height / desired_width
632 : desired_height;
633 int64_t delta_h = supported.height - aspect_h;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000634
635 distance = 0;
636 // Set high penalty if the supported format is lower than the desired format.
637 // 3x means we would prefer down to down to 3/4, than up to double.
638 // But we'd prefer up to double than down to 1/2. This is conservative,
639 // strongly avoiding going down in resolution, similar to
640 // the old method, but not completely ruling it out in extreme situations.
641 // It also ignores framerate, which is often very low at high resolutions.
642 // TODO(fbarchard): Improve logic to use weighted factors.
643 static const int kDownPenalty = -3;
644 if (delta_w < 0) {
645 delta_w = delta_w * kDownPenalty;
646 }
647 if (delta_h < 0) {
648 delta_h = delta_h * kDownPenalty;
649 }
650 // Require camera fps to be at least 80% of what is requested if resolution
651 // matches.
652 // Require camera fps to be at least 96% of what is requested, or higher,
653 // if resolution differs. 96% allows for slight variations in fps. e.g. 29.97
654 if (delta_fps < 0) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000655 float min_desirable_fps = delta_w ?
656 VideoFormat::IntervalToFpsFloat(desired.interval) * 28.f / 30.f :
657 VideoFormat::IntervalToFpsFloat(desired.interval) * 23.f / 30.f;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000658 delta_fps = -delta_fps;
659 if (supported_fps < min_desirable_fps) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200660 distance |= static_cast<int64_t>(1) << 62;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000661 } else {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200662 distance |= static_cast<int64_t>(1) << 15;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000663 }
664 }
Peter Boström0c4e06b2015-10-07 12:23:21 +0200665 int64_t idelta_fps = static_cast<int>(delta_fps);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000666
667 // 12 bits for width and height and 8 bits for fps and fourcc.
668 distance |=
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000669 (delta_w << 28) | (delta_h << 16) | (idelta_fps << 8) | delta_fourcc;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000670
671 return distance;
672}
673
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000674void VideoCapturer::UpdateFilteredSupportedFormats() {
675 filtered_supported_formats_.clear();
676 filtered_supported_formats_ = supported_formats_;
677 if (!max_format_) {
678 return;
679 }
680 std::vector<VideoFormat>::iterator iter = filtered_supported_formats_.begin();
681 while (iter != filtered_supported_formats_.end()) {
682 if (ShouldFilterFormat(*iter)) {
683 iter = filtered_supported_formats_.erase(iter);
684 } else {
685 ++iter;
686 }
687 }
688 if (filtered_supported_formats_.empty()) {
689 // The device only captures at resolutions higher than |max_format_| this
690 // indicates that |max_format_| should be ignored as it is better to capture
691 // at too high a resolution than to not capture at all.
692 filtered_supported_formats_ = supported_formats_;
693 }
694}
695
696bool VideoCapturer::ShouldFilterFormat(const VideoFormat& format) const {
697 if (!enable_camera_list_) {
698 return false;
699 }
700 return format.width > max_format_->width ||
701 format.height > max_format_->height;
702}
703
buildbot@webrtc.org0b53bd22014-05-06 17:12:36 +0000704void VideoCapturer::UpdateStats(const CapturedFrame* captured_frame) {
705 // Update stats protected from fetches from different thread.
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000706 rtc::CritScope cs(&frame_stats_crit_);
buildbot@webrtc.org0b53bd22014-05-06 17:12:36 +0000707
708 last_captured_frame_format_.width = captured_frame->width;
709 last_captured_frame_format_.height = captured_frame->height;
710 // TODO(ronghuawu): Useful to report interval as well?
711 last_captured_frame_format_.interval = 0;
712 last_captured_frame_format_.fourcc = captured_frame->fourcc;
713
714 double time_now = frame_length_time_reporter_.TimerNow();
715 if (previous_frame_time_ != 0.0) {
716 adapt_frame_drops_data_.AddSample(adapt_frame_drops_);
buildbot@webrtc.org0b53bd22014-05-06 17:12:36 +0000717 frame_time_data_.AddSample(time_now - previous_frame_time_);
718 }
719 previous_frame_time_ = time_now;
buildbot@webrtc.org0b53bd22014-05-06 17:12:36 +0000720 adapt_frame_drops_ = 0;
721}
722
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000723template<class T>
724void VideoCapturer::GetVariableSnapshot(
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000725 const rtc::RollingAccumulator<T>& data,
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000726 VariableInfo<T>* stats) {
727 stats->max_val = data.ComputeMax();
728 stats->mean = data.ComputeMean();
729 stats->min_val = data.ComputeMin();
730 stats->variance = data.ComputeVariance();
731}
732
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000733} // namespace cricket