blob: b2f41dcfb451d38462a13d7126b653ef51671a51 [file] [log] [blame]
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001// libjingle
2// Copyright 2010 Google Inc.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are met:
6//
7// 1. Redistributions of source code must retain the above copyright notice,
8// this list of conditions and the following disclaimer.
9// 2. Redistributions in binary form must reproduce the above copyright notice,
10// this list of conditions and the following disclaimer in the documentation
11// and/or other materials provided with the distribution.
12// 3. The name of the author may not be used to endorse or promote products
13// derived from this software without specific prior written permission.
14//
15// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
16// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
17// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
18// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25//
26// Implementation file of class VideoCapturer.
27
28#include "talk/media/base/videocapturer.h"
29
30#include <algorithm>
31
32#if !defined(DISABLE_YUV)
33#include "libyuv/scale_argb.h"
34#endif
35#include "talk/base/common.h"
36#include "talk/base/logging.h"
37#include "talk/base/systeminfo.h"
38#include "talk/media/base/videoprocessor.h"
39
40#if defined(HAVE_WEBRTC_VIDEO)
41#include "talk/media/webrtc/webrtcvideoframe.h"
42#endif // HAVE_WEBRTC_VIDEO
43
44
45namespace cricket {
46
47namespace {
48
49// TODO(thorcarpenter): This is a BIG hack to flush the system with black
50// frames. Frontends should coordinate to update the video state of a muted
51// user. When all frontends to this consider removing the black frame business.
52const int kNumBlackFramesOnMute = 30;
53
54// MessageHandler constants.
55enum {
56 MSG_DO_PAUSE = 0,
57 MSG_DO_UNPAUSE,
58 MSG_STATE_CHANGE
59};
60
61static const int64 kMaxDistance = ~(static_cast<int64>(1) << 63);
62static const int kYU12Penalty = 16; // Needs to be higher than MJPG index.
63static const int kDefaultScreencastFps = 5;
64typedef talk_base::TypedMessageData<CaptureState> StateChangeParams;
65
66} // namespace
67
68/////////////////////////////////////////////////////////////////////
69// Implementation of struct CapturedFrame
70/////////////////////////////////////////////////////////////////////
71CapturedFrame::CapturedFrame()
72 : width(0),
73 height(0),
74 fourcc(0),
75 pixel_width(0),
76 pixel_height(0),
77 elapsed_time(0),
78 time_stamp(0),
79 data_size(0),
80 rotation(0),
81 data(NULL) {}
82
83// TODO(fbarchard): Remove this function once lmimediaengine stops using it.
84bool CapturedFrame::GetDataSize(uint32* size) const {
85 if (!size || data_size == CapturedFrame::kUnknownDataSize) {
86 return false;
87 }
88 *size = data_size;
89 return true;
90}
91
92/////////////////////////////////////////////////////////////////////
93// Implementation of class VideoCapturer
94/////////////////////////////////////////////////////////////////////
95VideoCapturer::VideoCapturer() : thread_(talk_base::Thread::Current()) {
96 Construct();
97}
98
99VideoCapturer::VideoCapturer(talk_base::Thread* thread) : thread_(thread) {
100 Construct();
101}
102
103void VideoCapturer::Construct() {
104 ClearAspectRatio();
105 enable_camera_list_ = false;
mallinath@webrtc.org1b15f422013-09-06 22:56:28 +0000106 square_pixel_aspect_ratio_ = false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000107 capture_state_ = CS_STOPPED;
108 SignalFrameCaptured.connect(this, &VideoCapturer::OnFrameCaptured);
109 scaled_width_ = 0;
110 scaled_height_ = 0;
wu@webrtc.orgcadf9042013-08-30 21:24:16 +0000111 screencast_max_pixels_ = 0;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000112 muted_ = false;
113 black_frame_count_down_ = kNumBlackFramesOnMute;
114}
115
116const std::vector<VideoFormat>* VideoCapturer::GetSupportedFormats() const {
117 return &filtered_supported_formats_;
118}
119
120bool VideoCapturer::StartCapturing(const VideoFormat& capture_format) {
121 CaptureState result = Start(capture_format);
122 const bool success = (result == CS_RUNNING) || (result == CS_STARTING);
123 if (!success) {
124 return false;
125 }
126 if (result == CS_RUNNING) {
127 SetCaptureState(result);
128 }
129 return true;
130}
131
132void VideoCapturer::UpdateAspectRatio(int ratio_w, int ratio_h) {
133 if (ratio_w == 0 || ratio_h == 0) {
134 LOG(LS_WARNING) << "UpdateAspectRatio ignored invalid ratio: "
135 << ratio_w << "x" << ratio_h;
136 return;
137 }
138 ratio_w_ = ratio_w;
139 ratio_h_ = ratio_h;
140}
141
142void VideoCapturer::ClearAspectRatio() {
143 ratio_w_ = 0;
144 ratio_h_ = 0;
145}
146
147// Override this to have more control of how your device is started/stopped.
148bool VideoCapturer::Pause(bool pause) {
149 if (pause) {
150 if (capture_state() == CS_PAUSED) {
151 return true;
152 }
153 bool is_running = capture_state() == CS_STARTING ||
154 capture_state() == CS_RUNNING;
155 if (!is_running) {
156 LOG(LS_ERROR) << "Cannot pause a stopped camera.";
157 return false;
158 }
159 LOG(LS_INFO) << "Pausing a camera.";
160 talk_base::scoped_ptr<VideoFormat> capture_format_when_paused(
161 capture_format_ ? new VideoFormat(*capture_format_) : NULL);
162 Stop();
163 SetCaptureState(CS_PAUSED);
164 // If you override this function be sure to restore the capture format
165 // after calling Stop().
166 SetCaptureFormat(capture_format_when_paused.get());
167 } else { // Unpause.
168 if (capture_state() != CS_PAUSED) {
169 LOG(LS_WARNING) << "Cannot unpause a camera that hasn't been paused.";
170 return false;
171 }
172 if (!capture_format_) {
173 LOG(LS_ERROR) << "Missing capture_format_, cannot unpause a camera.";
174 return false;
175 }
176 if (muted_) {
177 LOG(LS_WARNING) << "Camera cannot be unpaused while muted.";
178 return false;
179 }
180 LOG(LS_INFO) << "Unpausing a camera.";
181 if (!Start(*capture_format_)) {
182 LOG(LS_ERROR) << "Camera failed to start when unpausing.";
183 return false;
184 }
185 }
186 return true;
187}
188
189bool VideoCapturer::Restart(const VideoFormat& capture_format) {
190 if (!IsRunning()) {
191 return StartCapturing(capture_format);
192 }
193
194 if (GetCaptureFormat() != NULL && *GetCaptureFormat() == capture_format) {
195 // The reqested format is the same; nothing to do.
196 return true;
197 }
198
199 Stop();
200 return StartCapturing(capture_format);
201}
202
203bool VideoCapturer::MuteToBlackThenPause(bool muted) {
204 if (muted == IsMuted()) {
205 return true;
206 }
207
208 LOG(LS_INFO) << (muted ? "Muting" : "Unmuting") << " this video capturer.";
209 muted_ = muted; // Do this before calling Pause().
210 if (muted) {
211 // Reset black frame count down.
212 black_frame_count_down_ = kNumBlackFramesOnMute;
213 // Following frames will be overritten with black, then the camera will be
214 // paused.
215 return true;
216 }
217 // Start the camera.
218 thread_->Clear(this, MSG_DO_PAUSE);
219 return Pause(false);
220}
221
222void VideoCapturer::SetSupportedFormats(
223 const std::vector<VideoFormat>& formats) {
224 supported_formats_ = formats;
225 UpdateFilteredSupportedFormats();
226}
227
228bool VideoCapturer::GetBestCaptureFormat(const VideoFormat& format,
229 VideoFormat* best_format) {
230 // TODO(fbarchard): Directly support max_format.
231 UpdateFilteredSupportedFormats();
232 const std::vector<VideoFormat>* supported_formats = GetSupportedFormats();
233
234 if (supported_formats->empty()) {
235 return false;
236 }
237 LOG(LS_INFO) << " Capture Requested " << format.ToString();
238 int64 best_distance = kMaxDistance;
239 std::vector<VideoFormat>::const_iterator best = supported_formats->end();
240 std::vector<VideoFormat>::const_iterator i;
241 for (i = supported_formats->begin(); i != supported_formats->end(); ++i) {
242 int64 distance = GetFormatDistance(format, *i);
243 // TODO(fbarchard): Reduce to LS_VERBOSE if/when camera capture is
244 // relatively bug free.
245 LOG(LS_INFO) << " Supported " << i->ToString() << " distance " << distance;
246 if (distance < best_distance) {
247 best_distance = distance;
248 best = i;
249 }
250 }
251 if (supported_formats->end() == best) {
252 LOG(LS_ERROR) << " No acceptable camera format found";
253 return false;
254 }
255
256 if (best_format) {
257 best_format->width = best->width;
258 best_format->height = best->height;
259 best_format->fourcc = best->fourcc;
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000260 best_format->interval = best->interval;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000261 LOG(LS_INFO) << " Best " << best_format->ToString() << " Interval "
262 << best_format->interval << " distance " << best_distance;
263 }
264 return true;
265}
266
267void VideoCapturer::AddVideoProcessor(VideoProcessor* video_processor) {
268 talk_base::CritScope cs(&crit_);
269 ASSERT(std::find(video_processors_.begin(), video_processors_.end(),
270 video_processor) == video_processors_.end());
271 video_processors_.push_back(video_processor);
272}
273
274bool VideoCapturer::RemoveVideoProcessor(VideoProcessor* video_processor) {
275 talk_base::CritScope cs(&crit_);
276 VideoProcessors::iterator found = std::find(
277 video_processors_.begin(), video_processors_.end(), video_processor);
278 if (found == video_processors_.end()) {
279 return false;
280 }
281 video_processors_.erase(found);
282 return true;
283}
284
285void VideoCapturer::ConstrainSupportedFormats(const VideoFormat& max_format) {
286 max_format_.reset(new VideoFormat(max_format));
287 LOG(LS_VERBOSE) << " ConstrainSupportedFormats " << max_format.ToString();
288 UpdateFilteredSupportedFormats();
289}
290
291std::string VideoCapturer::ToString(const CapturedFrame* captured_frame) const {
292 std::string fourcc_name = GetFourccName(captured_frame->fourcc) + " ";
293 for (std::string::const_iterator i = fourcc_name.begin();
294 i < fourcc_name.end(); ++i) {
295 // Test character is printable; Avoid isprint() which asserts on negatives.
296 if (*i < 32 || *i >= 127) {
297 fourcc_name = "";
298 break;
299 }
300 }
301
302 std::ostringstream ss;
303 ss << fourcc_name << captured_frame->width << "x" << captured_frame->height
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000304 << "x" << VideoFormat::IntervalToFpsFloat(captured_frame->elapsed_time);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000305 return ss.str();
306}
307
308void VideoCapturer::OnFrameCaptured(VideoCapturer*,
309 const CapturedFrame* captured_frame) {
310 if (muted_) {
311 if (black_frame_count_down_ == 0) {
312 thread_->Post(this, MSG_DO_PAUSE, NULL);
313 } else {
314 --black_frame_count_down_;
315 }
316 }
317
318 if (SignalVideoFrame.is_empty()) {
319 return;
320 }
321#if defined(HAVE_WEBRTC_VIDEO)
322#define VIDEO_FRAME_NAME WebRtcVideoFrame
323#endif
324#if defined(VIDEO_FRAME_NAME)
325#if !defined(DISABLE_YUV)
326 if (IsScreencast()) {
327 int scaled_width, scaled_height;
wu@webrtc.orgcadf9042013-08-30 21:24:16 +0000328 if (screencast_max_pixels_ > 0) {
329 ComputeScaleMaxPixels(captured_frame->width, captured_frame->height,
330 screencast_max_pixels_, &scaled_width, &scaled_height);
331 } else {
332 int desired_screencast_fps = capture_format_.get() ?
333 VideoFormat::IntervalToFps(capture_format_->interval) :
334 kDefaultScreencastFps;
335 ComputeScale(captured_frame->width, captured_frame->height,
336 desired_screencast_fps, &scaled_width, &scaled_height);
337 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000338
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000339 if (FOURCC_ARGB == captured_frame->fourcc &&
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000340 (scaled_width != captured_frame->width ||
mallinath@webrtc.org1b15f422013-09-06 22:56:28 +0000341 scaled_height != captured_frame->height)) {
342 if (scaled_width != scaled_width_ || scaled_height != scaled_height_) {
343 LOG(LS_INFO) << "Scaling Screencast from "
344 << captured_frame->width << "x"
345 << captured_frame->height << " to "
346 << scaled_width << "x" << scaled_height;
347 scaled_width_ = scaled_width;
348 scaled_height_ = scaled_height;
349 }
350 CapturedFrame* modified_frame =
351 const_cast<CapturedFrame*>(captured_frame);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000352 // Compute new width such that width * height is less than maximum but
353 // maintains original captured frame aspect ratio.
354 // Round down width to multiple of 4 so odd width won't round up beyond
355 // maximum, and so chroma channel is even width to simplify spatial
356 // resampling.
357 libyuv::ARGBScale(reinterpret_cast<const uint8*>(captured_frame->data),
358 captured_frame->width * 4, captured_frame->width,
359 captured_frame->height,
mallinath@webrtc.org1b15f422013-09-06 22:56:28 +0000360 reinterpret_cast<uint8*>(modified_frame->data),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000361 scaled_width * 4, scaled_width, scaled_height,
362 libyuv::kFilterBilinear);
mallinath@webrtc.org1b15f422013-09-06 22:56:28 +0000363 modified_frame->width = scaled_width;
364 modified_frame->height = scaled_height;
365 modified_frame->data_size = scaled_width * 4 * scaled_height;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000366 }
367 }
mallinath@webrtc.org1b15f422013-09-06 22:56:28 +0000368
369 const int kYuy2Bpp = 2;
370 const int kArgbBpp = 4;
371 // TODO(fbarchard): Make a helper function to adjust pixels to square.
372 // TODO(fbarchard): Hook up experiment to scaling.
373 // TODO(fbarchard): Avoid scale and convert if muted.
374 // Temporary buffer is scoped here so it will persist until i420_frame.Init()
375 // makes a copy of the frame, converting to I420.
wu@webrtc.org97077a32013-10-25 21:18:33 +0000376 talk_base::scoped_ptr<uint8[]> temp_buffer;
mallinath@webrtc.org1b15f422013-09-06 22:56:28 +0000377 // YUY2 can be scaled vertically using an ARGB scaler. Aspect ratio is only
378 // a problem on OSX. OSX always converts webcams to YUY2 or UYVY.
379 bool can_scale =
380 FOURCC_YUY2 == CanonicalFourCC(captured_frame->fourcc) ||
381 FOURCC_UYVY == CanonicalFourCC(captured_frame->fourcc);
382
383 // If pixels are not square, optionally use vertical scaling to make them
384 // square. Square pixels simplify the rest of the pipeline, including
385 // effects and rendering.
386 if (can_scale && square_pixel_aspect_ratio_ &&
387 captured_frame->pixel_width != captured_frame->pixel_height) {
388 int scaled_width, scaled_height;
389 // modified_frame points to the captured_frame but with const casted away
390 // so it can be modified.
391 CapturedFrame* modified_frame = const_cast<CapturedFrame*>(captured_frame);
392 // Compute the frame size that makes pixels square pixel aspect ratio.
393 ComputeScaleToSquarePixels(captured_frame->width, captured_frame->height,
394 captured_frame->pixel_width,
395 captured_frame->pixel_height,
396 &scaled_width, &scaled_height);
397
398 if (scaled_width != scaled_width_ || scaled_height != scaled_height_) {
399 LOG(LS_INFO) << "Scaling WebCam from "
400 << captured_frame->width << "x"
401 << captured_frame->height << " to "
402 << scaled_width << "x" << scaled_height
403 << " for PAR "
404 << captured_frame->pixel_width << "x"
405 << captured_frame->pixel_height;
406 scaled_width_ = scaled_width;
407 scaled_height_ = scaled_height;
408 }
409 const int modified_frame_size = scaled_width * scaled_height * kYuy2Bpp;
410 uint8* temp_buffer_data;
411 // Pixels are wide and short; Increasing height. Requires temporary buffer.
412 if (scaled_height > captured_frame->height) {
413 temp_buffer.reset(new uint8[modified_frame_size]);
414 temp_buffer_data = temp_buffer.get();
415 } else {
416 // Pixels are narrow and tall; Decreasing height. Scale will be done
417 // in place.
418 temp_buffer_data = reinterpret_cast<uint8*>(captured_frame->data);
419 }
420
421 // Use ARGBScaler to vertically scale the YUY2 image, adjusting for 16 bpp.
422 libyuv::ARGBScale(reinterpret_cast<const uint8*>(captured_frame->data),
423 captured_frame->width * kYuy2Bpp, // Stride for YUY2.
424 captured_frame->width * kYuy2Bpp / kArgbBpp, // Width.
425 abs(captured_frame->height), // Height.
426 temp_buffer_data,
427 scaled_width * kYuy2Bpp, // Stride for YUY2.
428 scaled_width * kYuy2Bpp / kArgbBpp, // Width.
429 abs(scaled_height), // New height.
430 libyuv::kFilterBilinear);
431 modified_frame->width = scaled_width;
432 modified_frame->height = scaled_height;
433 modified_frame->pixel_width = 1;
434 modified_frame->pixel_height = 1;
435 modified_frame->data_size = modified_frame_size;
436 modified_frame->data = temp_buffer_data;
437 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000438#endif // !DISABLE_YUV
mallinath@webrtc.org1b15f422013-09-06 22:56:28 +0000439
440 // Size to crop captured frame to. This adjusts the captured frames
441 // aspect ratio to match the final view aspect ratio, considering pixel
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000442 // aspect ratio and rotation. The final size may be scaled down by video
443 // adapter to better match ratio_w_ x ratio_h_.
444 // Note that abs() of frame height is passed in, because source may be
445 // inverted, but output will be positive.
446 int desired_width = captured_frame->width;
447 int desired_height = captured_frame->height;
448
449 // TODO(fbarchard): Improve logic to pad or crop.
450 // MJPG can crop vertically, but not horizontally. This logic disables crop.
451 // Alternatively we could pad the image with black, or implement a 2 step
452 // crop.
453 bool can_crop = true;
454 if (captured_frame->fourcc == FOURCC_MJPG) {
455 float cam_aspect = static_cast<float>(captured_frame->width) /
456 static_cast<float>(captured_frame->height);
457 float view_aspect = static_cast<float>(ratio_w_) /
458 static_cast<float>(ratio_h_);
459 can_crop = cam_aspect <= view_aspect;
460 }
461 if (can_crop && !IsScreencast()) {
462 // TODO(ronghuawu): The capturer should always produce the native
463 // resolution and the cropping should be done in downstream code.
464 ComputeCrop(ratio_w_, ratio_h_, captured_frame->width,
465 abs(captured_frame->height), captured_frame->pixel_width,
466 captured_frame->pixel_height, captured_frame->rotation,
467 &desired_width, &desired_height);
468 }
469
470 VIDEO_FRAME_NAME i420_frame;
wu@webrtc.org16d62542013-11-05 23:45:14 +0000471 if (!i420_frame.Alias(captured_frame, desired_width, desired_height)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000472 // TODO(fbarchard): LOG more information about captured frame attributes.
473 LOG(LS_ERROR) << "Couldn't convert to I420! "
474 << "From " << ToString(captured_frame) << " To "
475 << desired_width << " x " << desired_height;
476 return;
477 }
mallinath@webrtc.org67ee6b92014-02-03 16:57:16 +0000478
479 VideoFrame* adapted_frame = &i420_frame;
xians@webrtc.orgef221512014-02-21 10:31:29 +0000480 if (!SignalAdaptFrame.is_empty() && !IsScreencast()) {
mallinath@webrtc.org67ee6b92014-02-03 16:57:16 +0000481 VideoFrame* out_frame = NULL;
xians@webrtc.orgef221512014-02-21 10:31:29 +0000482 SignalAdaptFrame(this, adapted_frame, &out_frame);
mallinath@webrtc.org67ee6b92014-02-03 16:57:16 +0000483 if (!out_frame) {
484 return; // VideoAdapter dropped the frame.
485 }
486 adapted_frame = out_frame;
487 }
488
489 if (!muted_ && !ApplyProcessors(adapted_frame)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000490 // Processor dropped the frame.
491 return;
492 }
493 if (muted_) {
mallinath@webrtc.org67ee6b92014-02-03 16:57:16 +0000494 adapted_frame->SetToBlack();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000495 }
mallinath@webrtc.org67ee6b92014-02-03 16:57:16 +0000496 SignalVideoFrame(this, adapted_frame);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000497#endif // VIDEO_FRAME_NAME
498}
499
500void VideoCapturer::SetCaptureState(CaptureState state) {
501 if (state == capture_state_) {
502 // Don't trigger a state changed callback if the state hasn't changed.
503 return;
504 }
505 StateChangeParams* state_params = new StateChangeParams(state);
506 capture_state_ = state;
507 thread_->Post(this, MSG_STATE_CHANGE, state_params);
508}
509
510void VideoCapturer::OnMessage(talk_base::Message* message) {
511 switch (message->message_id) {
512 case MSG_STATE_CHANGE: {
513 talk_base::scoped_ptr<StateChangeParams> p(
514 static_cast<StateChangeParams*>(message->pdata));
515 SignalStateChange(this, p->data());
516 break;
517 }
518 case MSG_DO_PAUSE: {
519 Pause(true);
520 break;
521 }
522 case MSG_DO_UNPAUSE: {
523 Pause(false);
524 break;
525 }
526 default: {
527 ASSERT(false);
528 }
529 }
530}
531
532// Get the distance between the supported and desired formats.
533// Prioritization is done according to this algorithm:
534// 1) Width closeness. If not same, we prefer wider.
535// 2) Height closeness. If not same, we prefer higher.
536// 3) Framerate closeness. If not same, we prefer faster.
537// 4) Compression. If desired format has a specific fourcc, we need exact match;
538// otherwise, we use preference.
539int64 VideoCapturer::GetFormatDistance(const VideoFormat& desired,
540 const VideoFormat& supported) {
541 int64 distance = kMaxDistance;
542
543 // Check fourcc.
544 uint32 supported_fourcc = CanonicalFourCC(supported.fourcc);
545 int64 delta_fourcc = kMaxDistance;
546 if (FOURCC_ANY == desired.fourcc) {
547 // Any fourcc is OK for the desired. Use preference to find best fourcc.
548 std::vector<uint32> preferred_fourccs;
549 if (!GetPreferredFourccs(&preferred_fourccs)) {
550 return distance;
551 }
552
553 for (size_t i = 0; i < preferred_fourccs.size(); ++i) {
554 if (supported_fourcc == CanonicalFourCC(preferred_fourccs[i])) {
555 delta_fourcc = i;
556#ifdef LINUX
557 // For HD avoid YU12 which is a software conversion and has 2 bugs
558 // b/7326348 b/6960899. Reenable when fixed.
559 if (supported.height >= 720 && (supported_fourcc == FOURCC_YU12 ||
560 supported_fourcc == FOURCC_YV12)) {
561 delta_fourcc += kYU12Penalty;
562 }
563#endif
564 break;
565 }
566 }
567 } else if (supported_fourcc == CanonicalFourCC(desired.fourcc)) {
568 delta_fourcc = 0; // Need exact match.
569 }
570
571 if (kMaxDistance == delta_fourcc) {
572 // Failed to match fourcc.
573 return distance;
574 }
575
576 // Check resolution and fps.
577 int desired_width = desired.width;
578 int desired_height = desired.height;
579 int64 delta_w = supported.width - desired_width;
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000580 float supported_fps = VideoFormat::IntervalToFpsFloat(supported.interval);
581 float delta_fps =
582 supported_fps - VideoFormat::IntervalToFpsFloat(desired.interval);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000583 // Check height of supported height compared to height we would like it to be.
584 int64 aspect_h =
585 desired_width ? supported.width * desired_height / desired_width
586 : desired_height;
587 int64 delta_h = supported.height - aspect_h;
588
589 distance = 0;
590 // Set high penalty if the supported format is lower than the desired format.
591 // 3x means we would prefer down to down to 3/4, than up to double.
592 // But we'd prefer up to double than down to 1/2. This is conservative,
593 // strongly avoiding going down in resolution, similar to
594 // the old method, but not completely ruling it out in extreme situations.
595 // It also ignores framerate, which is often very low at high resolutions.
596 // TODO(fbarchard): Improve logic to use weighted factors.
597 static const int kDownPenalty = -3;
598 if (delta_w < 0) {
599 delta_w = delta_w * kDownPenalty;
600 }
601 if (delta_h < 0) {
602 delta_h = delta_h * kDownPenalty;
603 }
604 // Require camera fps to be at least 80% of what is requested if resolution
605 // matches.
606 // Require camera fps to be at least 96% of what is requested, or higher,
607 // if resolution differs. 96% allows for slight variations in fps. e.g. 29.97
608 if (delta_fps < 0) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000609 float min_desirable_fps = delta_w ?
610 VideoFormat::IntervalToFpsFloat(desired.interval) * 28.f / 30.f :
611 VideoFormat::IntervalToFpsFloat(desired.interval) * 23.f / 30.f;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000612 delta_fps = -delta_fps;
613 if (supported_fps < min_desirable_fps) {
614 distance |= static_cast<int64>(1) << 62;
615 } else {
616 distance |= static_cast<int64>(1) << 15;
617 }
618 }
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000619 int64 idelta_fps = static_cast<int>(delta_fps);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000620
621 // 12 bits for width and height and 8 bits for fps and fourcc.
622 distance |=
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000623 (delta_w << 28) | (delta_h << 16) | (idelta_fps << 8) | delta_fourcc;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000624
625 return distance;
626}
627
628bool VideoCapturer::ApplyProcessors(VideoFrame* video_frame) {
629 bool drop_frame = false;
630 talk_base::CritScope cs(&crit_);
631 for (VideoProcessors::iterator iter = video_processors_.begin();
632 iter != video_processors_.end(); ++iter) {
633 (*iter)->OnFrame(kDummyVideoSsrc, video_frame, &drop_frame);
634 if (drop_frame) {
635 return false;
636 }
637 }
638 return true;
639}
640
641void VideoCapturer::UpdateFilteredSupportedFormats() {
642 filtered_supported_formats_.clear();
643 filtered_supported_formats_ = supported_formats_;
644 if (!max_format_) {
645 return;
646 }
647 std::vector<VideoFormat>::iterator iter = filtered_supported_formats_.begin();
648 while (iter != filtered_supported_formats_.end()) {
649 if (ShouldFilterFormat(*iter)) {
650 iter = filtered_supported_formats_.erase(iter);
651 } else {
652 ++iter;
653 }
654 }
655 if (filtered_supported_formats_.empty()) {
656 // The device only captures at resolutions higher than |max_format_| this
657 // indicates that |max_format_| should be ignored as it is better to capture
658 // at too high a resolution than to not capture at all.
659 filtered_supported_formats_ = supported_formats_;
660 }
661}
662
663bool VideoCapturer::ShouldFilterFormat(const VideoFormat& format) const {
664 if (!enable_camera_list_) {
665 return false;
666 }
667 return format.width > max_format_->width ||
668 format.height > max_format_->height;
669}
670
671} // namespace cricket