videocapturer.cc revision 67ee6b9a6260fa80b83326c4b4fec8857c0e578c
1// 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;
106  square_pixel_aspect_ratio_ = false;
107  capture_state_ = CS_STOPPED;
108  SignalFrameCaptured.connect(this, &VideoCapturer::OnFrameCaptured);
109  scaled_width_ = 0;
110  scaled_height_ = 0;
111  screencast_max_pixels_ = 0;
112  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;
260    best_format->interval = best->interval;
261    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
304     << "x" << VideoFormat::IntervalToFpsFloat(captured_frame->elapsed_time);
305  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;
328    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    }
338
339    if (FOURCC_ARGB == captured_frame->fourcc &&
340        (scaled_width != captured_frame->width ||
341        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);
352      // 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,
360                        reinterpret_cast<uint8*>(modified_frame->data),
361                        scaled_width * 4, scaled_width, scaled_height,
362                        libyuv::kFilterBilinear);
363      modified_frame->width = scaled_width;
364      modified_frame->height = scaled_height;
365      modified_frame->data_size = scaled_width * 4 * scaled_height;
366    }
367  }
368
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.
376  talk_base::scoped_ptr<uint8[]> temp_buffer;
377  // 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  }
438#endif  // !DISABLE_YUV
439
440  // Size to crop captured frame to.  This adjusts the captured frames
441  // aspect ratio to match the final view aspect ratio, considering pixel
442  // 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;
471  if (!i420_frame.Alias(captured_frame, desired_width, desired_height)) {
472    // 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  }
478
479  VideoFrame* adapted_frame = &i420_frame;
480  if (!SignalAdaptFrame.is_empty() && !IsScreencast()) {
481    VideoFrame* out_frame = NULL;
482    SignalAdaptFrame(this, adapted_frame, &out_frame);
483    if (!out_frame) {
484      return;  // VideoAdapter dropped the frame.
485    }
486    adapted_frame = out_frame;
487  }
488
489  if (!muted_ && !ApplyProcessors(adapted_frame)) {
490    // Processor dropped the frame.
491    return;
492  }
493  if (muted_) {
494    adapted_frame->SetToBlack();
495  }
496  SignalVideoFrame(this, adapted_frame);
497#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;
580  float supported_fps = VideoFormat::IntervalToFpsFloat(supported.interval);
581  float delta_fps =
582      supported_fps - VideoFormat::IntervalToFpsFloat(desired.interval);
583  // 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) {
609    float min_desirable_fps = delta_w ?
610    VideoFormat::IntervalToFpsFloat(desired.interval) * 28.f / 30.f :
611    VideoFormat::IntervalToFpsFloat(desired.interval) * 23.f / 30.f;
612    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  }
619  int64 idelta_fps = static_cast<int>(delta_fps);
620
621  // 12 bits for width and height and 8 bits for fps and fourcc.
622  distance |=
623      (delta_w << 28) | (delta_h << 16) | (idelta_fps << 8) | delta_fourcc;
624
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
672