Home | History | Annotate | Download | only in base
      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 
     45 namespace cricket {
     46 
     47 namespace {
     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.
     52 const int kNumBlackFramesOnMute = 30;
     53 
     54 // MessageHandler constants.
     55 enum {
     56   MSG_DO_PAUSE = 0,
     57   MSG_DO_UNPAUSE,
     58   MSG_STATE_CHANGE
     59 };
     60 
     61 static const int64 kMaxDistance = ~(static_cast<int64>(1) << 63);
     62 static const int kYU12Penalty = 16;  // Needs to be higher than MJPG index.
     63 static const int kDefaultScreencastFps = 5;
     64 typedef talk_base::TypedMessageData<CaptureState> StateChangeParams;
     65 
     66 }  // namespace
     67 
     68 /////////////////////////////////////////////////////////////////////
     69 // Implementation of struct CapturedFrame
     70 /////////////////////////////////////////////////////////////////////
     71 CapturedFrame::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.
     84 bool 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 /////////////////////////////////////////////////////////////////////
     95 VideoCapturer::VideoCapturer() : thread_(talk_base::Thread::Current()) {
     96   Construct();
     97 }
     98 
     99 VideoCapturer::VideoCapturer(talk_base::Thread* thread) : thread_(thread) {
    100   Construct();
    101 }
    102 
    103 void 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 
    116 const std::vector<VideoFormat>* VideoCapturer::GetSupportedFormats() const {
    117   return &filtered_supported_formats_;
    118 }
    119 
    120 bool 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 
    132 void 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 
    142 void 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.
    148 bool 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 
    189 bool 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 
    203 bool 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 
    222 void VideoCapturer::SetSupportedFormats(
    223     const std::vector<VideoFormat>& formats) {
    224   supported_formats_ = formats;
    225   UpdateFilteredSupportedFormats();
    226 }
    227 
    228 bool 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 = talk_base::_max(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 
    267 void 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 
    274 bool 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 
    285 void 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 
    291 std::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::IntervalToFps(captured_frame->elapsed_time);
    305   return ss.str();
    306 }
    307 
    308 void 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 
    500 void 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 
    510 void 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.
    539 int64 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   int64 supported_fps = VideoFormat::IntervalToFps(supported.interval);
    581   int64 delta_fps =
    582       supported_fps - VideoFormat::IntervalToFps(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     int64 min_desirable_fps = delta_w ?
    610     VideoFormat::IntervalToFps(desired.interval) * 29 / 30 :
    611     VideoFormat::IntervalToFps(desired.interval) * 24 / 30;
    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 
    620   // 12 bits for width and height and 8 bits for fps and fourcc.
    621   distance |=
    622       (delta_w << 28) | (delta_h << 16) | (delta_fps << 8) | delta_fourcc;
    623 
    624   return distance;
    625 }
    626 
    627 bool VideoCapturer::ApplyProcessors(VideoFrame* video_frame) {
    628   bool drop_frame = false;
    629   talk_base::CritScope cs(&crit_);
    630   for (VideoProcessors::iterator iter = video_processors_.begin();
    631        iter != video_processors_.end(); ++iter) {
    632     (*iter)->OnFrame(kDummyVideoSsrc, video_frame, &drop_frame);
    633     if (drop_frame) {
    634       return false;
    635     }
    636   }
    637   return true;
    638 }
    639 
    640 void VideoCapturer::UpdateFilteredSupportedFormats() {
    641   filtered_supported_formats_.clear();
    642   filtered_supported_formats_ = supported_formats_;
    643   if (!max_format_) {
    644     return;
    645   }
    646   std::vector<VideoFormat>::iterator iter = filtered_supported_formats_.begin();
    647   while (iter != filtered_supported_formats_.end()) {
    648     if (ShouldFilterFormat(*iter)) {
    649       iter = filtered_supported_formats_.erase(iter);
    650     } else {
    651       ++iter;
    652     }
    653   }
    654   if (filtered_supported_formats_.empty()) {
    655     // The device only captures at resolutions higher than |max_format_| this
    656     // indicates that |max_format_| should be ignored as it is better to capture
    657     // at too high a resolution than to not capture at all.
    658     filtered_supported_formats_ = supported_formats_;
    659   }
    660 }
    661 
    662 bool VideoCapturer::ShouldFilterFormat(const VideoFormat& format) const {
    663   if (!enable_camera_list_) {
    664     return false;
    665   }
    666   return format.width > max_format_->width ||
    667          format.height > max_format_->height;
    668 }
    669 
    670 }  // namespace cricket
    671