Home | History | Annotate | Download | only in base
      1 /*
      2  * libjingle
      3  * Copyright 2010 Google Inc.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are met:
      7  *
      8  *  1. Redistributions of source code must retain the above copyright notice,
      9  *     this list of conditions and the following disclaimer.
     10  *  2. Redistributions in binary form must reproduce the above copyright notice,
     11  *     this list of conditions and the following disclaimer in the documentation
     12  *     and/or other materials provided with the distribution.
     13  *  3. The name of the author may not be used to endorse or promote products
     14  *     derived from this software without specific prior written permission.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
     17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
     19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
     25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26  */
     27 
     28 // Implementation file of class VideoCapturer.
     29 
     30 #include "talk/media/base/videocapturer.h"
     31 
     32 #include <algorithm>
     33 
     34 #include "libyuv/scale_argb.h"
     35 #include "talk/media/base/videoframefactory.h"
     36 #include "webrtc/base/common.h"
     37 #include "webrtc/base/logging.h"
     38 #include "webrtc/base/systeminfo.h"
     39 
     40 #if defined(HAVE_WEBRTC_VIDEO)
     41 #include "talk/media/webrtc/webrtcvideoframe.h"
     42 #include "talk/media/webrtc/webrtcvideoframefactory.h"
     43 #endif  // HAVE_WEBRTC_VIDEO
     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_t kMaxDistance = ~(static_cast<int64_t>(1) << 63);
     62 #ifdef WEBRTC_LINUX
     63 static const int kYU12Penalty = 16;  // Needs to be higher than MJPG index.
     64 #endif
     65 static const int kDefaultScreencastFps = 5;
     66 typedef rtc::TypedMessageData<CaptureState> StateChangeParams;
     67 
     68 // Limit stats data collections to ~20 seconds of 30fps data before dropping
     69 // old data in case stats aren't reset for long periods of time.
     70 static const size_t kMaxAccumulatorSize = 600;
     71 
     72 }  // namespace
     73 
     74 /////////////////////////////////////////////////////////////////////
     75 // Implementation of struct CapturedFrame
     76 /////////////////////////////////////////////////////////////////////
     77 CapturedFrame::CapturedFrame()
     78     : width(0),
     79       height(0),
     80       fourcc(0),
     81       pixel_width(0),
     82       pixel_height(0),
     83       time_stamp(0),
     84       data_size(0),
     85       rotation(webrtc::kVideoRotation_0),
     86       data(NULL) {}
     87 
     88 // TODO(fbarchard): Remove this function once lmimediaengine stops using it.
     89 bool CapturedFrame::GetDataSize(uint32_t* size) const {
     90   if (!size || data_size == CapturedFrame::kUnknownDataSize) {
     91     return false;
     92   }
     93   *size = data_size;
     94   return true;
     95 }
     96 
     97 /////////////////////////////////////////////////////////////////////
     98 // Implementation of class VideoCapturer
     99 /////////////////////////////////////////////////////////////////////
    100 VideoCapturer::VideoCapturer()
    101     : thread_(rtc::Thread::Current()),
    102       adapt_frame_drops_data_(kMaxAccumulatorSize),
    103       frame_time_data_(kMaxAccumulatorSize),
    104       apply_rotation_(true) {
    105   Construct();
    106 }
    107 
    108 VideoCapturer::VideoCapturer(rtc::Thread* thread)
    109     : thread_(thread),
    110       adapt_frame_drops_data_(kMaxAccumulatorSize),
    111       frame_time_data_(kMaxAccumulatorSize),
    112       apply_rotation_(true) {
    113   Construct();
    114 }
    115 
    116 void VideoCapturer::Construct() {
    117   ClearAspectRatio();
    118   enable_camera_list_ = false;
    119   square_pixel_aspect_ratio_ = false;
    120   capture_state_ = CS_STOPPED;
    121   SignalFrameCaptured.connect(this, &VideoCapturer::OnFrameCaptured);
    122   scaled_width_ = 0;
    123   scaled_height_ = 0;
    124   muted_ = false;
    125   black_frame_count_down_ = kNumBlackFramesOnMute;
    126   enable_video_adapter_ = true;
    127   adapt_frame_drops_ = 0;
    128   previous_frame_time_ = 0.0;
    129 #ifdef HAVE_WEBRTC_VIDEO
    130   // There are lots of video capturers out there that don't call
    131   // set_frame_factory.  We can either go change all of them, or we
    132   // can set this default.
    133   // TODO(pthatcher): Remove this hack and require the frame factory
    134   // to be passed in the constructor.
    135   set_frame_factory(new WebRtcVideoFrameFactory());
    136 #endif
    137 }
    138 
    139 const std::vector<VideoFormat>* VideoCapturer::GetSupportedFormats() const {
    140   return &filtered_supported_formats_;
    141 }
    142 
    143 bool VideoCapturer::StartCapturing(const VideoFormat& capture_format) {
    144   previous_frame_time_ = frame_length_time_reporter_.TimerNow();
    145   CaptureState result = Start(capture_format);
    146   const bool success = (result == CS_RUNNING) || (result == CS_STARTING);
    147   if (!success) {
    148     return false;
    149   }
    150   if (result == CS_RUNNING) {
    151     SetCaptureState(result);
    152   }
    153   return true;
    154 }
    155 
    156 void VideoCapturer::UpdateAspectRatio(int ratio_w, int ratio_h) {
    157   if (ratio_w == 0 || ratio_h == 0) {
    158     LOG(LS_WARNING) << "UpdateAspectRatio ignored invalid ratio: "
    159                     << ratio_w << "x" << ratio_h;
    160     return;
    161   }
    162   ratio_w_ = ratio_w;
    163   ratio_h_ = ratio_h;
    164 }
    165 
    166 void VideoCapturer::ClearAspectRatio() {
    167   ratio_w_ = 0;
    168   ratio_h_ = 0;
    169 }
    170 
    171 // Override this to have more control of how your device is started/stopped.
    172 bool VideoCapturer::Pause(bool pause) {
    173   if (pause) {
    174     if (capture_state() == CS_PAUSED) {
    175       return true;
    176     }
    177     bool is_running = capture_state() == CS_STARTING ||
    178         capture_state() == CS_RUNNING;
    179     if (!is_running) {
    180       LOG(LS_ERROR) << "Cannot pause a stopped camera.";
    181       return false;
    182     }
    183     LOG(LS_INFO) << "Pausing a camera.";
    184     rtc::scoped_ptr<VideoFormat> capture_format_when_paused(
    185         capture_format_ ? new VideoFormat(*capture_format_) : NULL);
    186     Stop();
    187     SetCaptureState(CS_PAUSED);
    188     // If you override this function be sure to restore the capture format
    189     // after calling Stop().
    190     SetCaptureFormat(capture_format_when_paused.get());
    191   } else {  // Unpause.
    192     if (capture_state() != CS_PAUSED) {
    193       LOG(LS_WARNING) << "Cannot unpause a camera that hasn't been paused.";
    194       return false;
    195     }
    196     if (!capture_format_) {
    197       LOG(LS_ERROR) << "Missing capture_format_, cannot unpause a camera.";
    198       return false;
    199     }
    200     if (muted_) {
    201       LOG(LS_WARNING) << "Camera cannot be unpaused while muted.";
    202       return false;
    203     }
    204     LOG(LS_INFO) << "Unpausing a camera.";
    205     if (!Start(*capture_format_)) {
    206       LOG(LS_ERROR) << "Camera failed to start when unpausing.";
    207       return false;
    208     }
    209   }
    210   return true;
    211 }
    212 
    213 bool VideoCapturer::Restart(const VideoFormat& capture_format) {
    214   if (!IsRunning()) {
    215     return StartCapturing(capture_format);
    216   }
    217 
    218   if (GetCaptureFormat() != NULL && *GetCaptureFormat() == capture_format) {
    219     // The reqested format is the same; nothing to do.
    220     return true;
    221   }
    222 
    223   Stop();
    224   return StartCapturing(capture_format);
    225 }
    226 
    227 bool VideoCapturer::MuteToBlackThenPause(bool muted) {
    228   if (muted == IsMuted()) {
    229     return true;
    230   }
    231 
    232   LOG(LS_INFO) << (muted ? "Muting" : "Unmuting") << " this video capturer.";
    233   muted_ = muted;  // Do this before calling Pause().
    234   if (muted) {
    235     // Reset black frame count down.
    236     black_frame_count_down_ = kNumBlackFramesOnMute;
    237     // Following frames will be overritten with black, then the camera will be
    238     // paused.
    239     return true;
    240   }
    241   // Start the camera.
    242   thread_->Clear(this, MSG_DO_PAUSE);
    243   return Pause(false);
    244 }
    245 
    246 // Note that the last caller decides whether rotation should be applied if there
    247 // are multiple send streams using the same camera.
    248 bool VideoCapturer::SetApplyRotation(bool enable) {
    249   apply_rotation_ = enable;
    250   if (frame_factory_) {
    251     frame_factory_->SetApplyRotation(apply_rotation_);
    252   }
    253   return true;
    254 }
    255 
    256 void VideoCapturer::SetSupportedFormats(
    257     const std::vector<VideoFormat>& formats) {
    258   supported_formats_ = formats;
    259   UpdateFilteredSupportedFormats();
    260 }
    261 
    262 bool VideoCapturer::GetBestCaptureFormat(const VideoFormat& format,
    263                                          VideoFormat* best_format) {
    264   // TODO(fbarchard): Directly support max_format.
    265   UpdateFilteredSupportedFormats();
    266   const std::vector<VideoFormat>* supported_formats = GetSupportedFormats();
    267 
    268   if (supported_formats->empty()) {
    269     return false;
    270   }
    271   LOG(LS_INFO) << " Capture Requested " << format.ToString();
    272   int64_t best_distance = kMaxDistance;
    273   std::vector<VideoFormat>::const_iterator best = supported_formats->end();
    274   std::vector<VideoFormat>::const_iterator i;
    275   for (i = supported_formats->begin(); i != supported_formats->end(); ++i) {
    276     int64_t distance = GetFormatDistance(format, *i);
    277     // TODO(fbarchard): Reduce to LS_VERBOSE if/when camera capture is
    278     // relatively bug free.
    279     LOG(LS_INFO) << " Supported " << i->ToString() << " distance " << distance;
    280     if (distance < best_distance) {
    281       best_distance = distance;
    282       best = i;
    283     }
    284   }
    285   if (supported_formats->end() == best) {
    286     LOG(LS_ERROR) << " No acceptable camera format found";
    287     return false;
    288   }
    289 
    290   if (best_format) {
    291     best_format->width = best->width;
    292     best_format->height = best->height;
    293     best_format->fourcc = best->fourcc;
    294     best_format->interval = best->interval;
    295     LOG(LS_INFO) << " Best " << best_format->ToString() << " Interval "
    296                  << best_format->interval << " distance " << best_distance;
    297   }
    298   return true;
    299 }
    300 
    301 void VideoCapturer::ConstrainSupportedFormats(const VideoFormat& max_format) {
    302   max_format_.reset(new VideoFormat(max_format));
    303   LOG(LS_VERBOSE) << " ConstrainSupportedFormats " << max_format.ToString();
    304   UpdateFilteredSupportedFormats();
    305 }
    306 
    307 std::string VideoCapturer::ToString(const CapturedFrame* captured_frame) const {
    308   std::string fourcc_name = GetFourccName(captured_frame->fourcc) + " ";
    309   for (std::string::const_iterator i = fourcc_name.begin();
    310        i < fourcc_name.end(); ++i) {
    311     // Test character is printable; Avoid isprint() which asserts on negatives.
    312     if (*i < 32 || *i >= 127) {
    313       fourcc_name = "";
    314       break;
    315     }
    316   }
    317 
    318   std::ostringstream ss;
    319   ss << fourcc_name << captured_frame->width << "x" << captured_frame->height;
    320   return ss.str();
    321 }
    322 
    323 void VideoCapturer::set_frame_factory(VideoFrameFactory* frame_factory) {
    324   frame_factory_.reset(frame_factory);
    325   if (frame_factory) {
    326     frame_factory->SetApplyRotation(apply_rotation_);
    327   }
    328 }
    329 
    330 void VideoCapturer::GetStats(VariableInfo<int>* adapt_drops_stats,
    331                              VariableInfo<int>* effect_drops_stats,
    332                              VariableInfo<double>* frame_time_stats,
    333                              VideoFormat* last_captured_frame_format) {
    334   rtc::CritScope cs(&frame_stats_crit_);
    335   GetVariableSnapshot(adapt_frame_drops_data_, adapt_drops_stats);
    336   GetVariableSnapshot(frame_time_data_, frame_time_stats);
    337   *last_captured_frame_format = last_captured_frame_format_;
    338 
    339   adapt_frame_drops_data_.Reset();
    340   frame_time_data_.Reset();
    341 }
    342 
    343 void VideoCapturer::OnFrameCaptured(VideoCapturer*,
    344                                     const CapturedFrame* captured_frame) {
    345   if (muted_) {
    346     if (black_frame_count_down_ == 0) {
    347       thread_->Post(this, MSG_DO_PAUSE, NULL);
    348     } else {
    349       --black_frame_count_down_;
    350     }
    351   }
    352 
    353   if (SignalVideoFrame.is_empty()) {
    354     return;
    355   }
    356 
    357   // Use a temporary buffer to scale
    358   rtc::scoped_ptr<uint8_t[]> scale_buffer;
    359 
    360   if (IsScreencast()) {
    361     int scaled_width, scaled_height;
    362     int desired_screencast_fps = capture_format_.get() ?
    363       VideoFormat::IntervalToFps(capture_format_->interval) :
    364       kDefaultScreencastFps;
    365     ComputeScale(captured_frame->width, captured_frame->height,
    366                  desired_screencast_fps, &scaled_width, &scaled_height);
    367 
    368     if (FOURCC_ARGB == captured_frame->fourcc &&
    369         (scaled_width != captured_frame->width ||
    370         scaled_height != captured_frame->height)) {
    371       if (scaled_width != scaled_width_ || scaled_height != scaled_height_) {
    372         LOG(LS_INFO) << "Scaling Screencast from "
    373                      << captured_frame->width << "x"
    374                      << captured_frame->height << " to "
    375                      << scaled_width << "x" << scaled_height;
    376         scaled_width_ = scaled_width;
    377         scaled_height_ = scaled_height;
    378       }
    379       CapturedFrame* modified_frame =
    380           const_cast<CapturedFrame*>(captured_frame);
    381       const int modified_frame_size = scaled_width * scaled_height * 4;
    382       scale_buffer.reset(new uint8_t[modified_frame_size]);
    383       // Compute new width such that width * height is less than maximum but
    384       // maintains original captured frame aspect ratio.
    385       // Round down width to multiple of 4 so odd width won't round up beyond
    386       // maximum, and so chroma channel is even width to simplify spatial
    387       // resampling.
    388       libyuv::ARGBScale(reinterpret_cast<const uint8_t*>(captured_frame->data),
    389                         captured_frame->width * 4, captured_frame->width,
    390                         captured_frame->height, scale_buffer.get(),
    391                         scaled_width * 4, scaled_width, scaled_height,
    392                         libyuv::kFilterBilinear);
    393       modified_frame->width = scaled_width;
    394       modified_frame->height = scaled_height;
    395       modified_frame->data_size = scaled_width * 4 * scaled_height;
    396       modified_frame->data = scale_buffer.get();
    397     }
    398   }
    399 
    400   const int kYuy2Bpp = 2;
    401   const int kArgbBpp = 4;
    402   // TODO(fbarchard): Make a helper function to adjust pixels to square.
    403   // TODO(fbarchard): Hook up experiment to scaling.
    404   // TODO(fbarchard): Avoid scale and convert if muted.
    405   // Temporary buffer is scoped here so it will persist until i420_frame.Init()
    406   // makes a copy of the frame, converting to I420.
    407   rtc::scoped_ptr<uint8_t[]> temp_buffer;
    408   // YUY2 can be scaled vertically using an ARGB scaler.  Aspect ratio is only
    409   // a problem on OSX.  OSX always converts webcams to YUY2 or UYVY.
    410   bool can_scale =
    411       FOURCC_YUY2 == CanonicalFourCC(captured_frame->fourcc) ||
    412       FOURCC_UYVY == CanonicalFourCC(captured_frame->fourcc);
    413 
    414   // If pixels are not square, optionally use vertical scaling to make them
    415   // square.  Square pixels simplify the rest of the pipeline, including
    416   // effects and rendering.
    417   if (can_scale && square_pixel_aspect_ratio_ &&
    418       captured_frame->pixel_width != captured_frame->pixel_height) {
    419     int scaled_width, scaled_height;
    420     // modified_frame points to the captured_frame but with const casted away
    421     // so it can be modified.
    422     CapturedFrame* modified_frame = const_cast<CapturedFrame*>(captured_frame);
    423     // Compute the frame size that makes pixels square pixel aspect ratio.
    424     ComputeScaleToSquarePixels(captured_frame->width, captured_frame->height,
    425                                captured_frame->pixel_width,
    426                                captured_frame->pixel_height,
    427                                &scaled_width, &scaled_height);
    428 
    429     if (scaled_width != scaled_width_ || scaled_height != scaled_height_) {
    430       LOG(LS_INFO) << "Scaling WebCam from "
    431                    << captured_frame->width << "x"
    432                    << captured_frame->height << " to "
    433                    << scaled_width << "x" << scaled_height
    434                    << " for PAR "
    435                    << captured_frame->pixel_width << "x"
    436                    << captured_frame->pixel_height;
    437       scaled_width_ = scaled_width;
    438       scaled_height_ = scaled_height;
    439     }
    440     const int modified_frame_size = scaled_width * scaled_height * kYuy2Bpp;
    441     uint8_t* temp_buffer_data;
    442     // Pixels are wide and short; Increasing height. Requires temporary buffer.
    443     if (scaled_height > captured_frame->height) {
    444       temp_buffer.reset(new uint8_t[modified_frame_size]);
    445       temp_buffer_data = temp_buffer.get();
    446     } else {
    447       // Pixels are narrow and tall; Decreasing height. Scale will be done
    448       // in place.
    449       temp_buffer_data = reinterpret_cast<uint8_t*>(captured_frame->data);
    450     }
    451 
    452     // Use ARGBScaler to vertically scale the YUY2 image, adjusting for 16 bpp.
    453     libyuv::ARGBScale(reinterpret_cast<const uint8_t*>(captured_frame->data),
    454                       captured_frame->width * kYuy2Bpp,  // Stride for YUY2.
    455                       captured_frame->width * kYuy2Bpp / kArgbBpp,  // Width.
    456                       abs(captured_frame->height),                  // Height.
    457                       temp_buffer_data,
    458                       scaled_width * kYuy2Bpp,             // Stride for YUY2.
    459                       scaled_width * kYuy2Bpp / kArgbBpp,  // Width.
    460                       abs(scaled_height),                  // New height.
    461                       libyuv::kFilterBilinear);
    462     modified_frame->width = scaled_width;
    463     modified_frame->height = scaled_height;
    464     modified_frame->pixel_width = 1;
    465     modified_frame->pixel_height = 1;
    466     modified_frame->data_size = modified_frame_size;
    467     modified_frame->data = temp_buffer_data;
    468   }
    469 
    470   // Size to crop captured frame to.  This adjusts the captured frames
    471   // aspect ratio to match the final view aspect ratio, considering pixel
    472   // aspect ratio and rotation.  The final size may be scaled down by video
    473   // adapter to better match ratio_w_ x ratio_h_.
    474   // Note that abs() of frame height is passed in, because source may be
    475   // inverted, but output will be positive.
    476   int cropped_width = captured_frame->width;
    477   int cropped_height = captured_frame->height;
    478 
    479   // TODO(fbarchard): Improve logic to pad or crop.
    480   // MJPG can crop vertically, but not horizontally.  This logic disables crop.
    481   // Alternatively we could pad the image with black, or implement a 2 step
    482   // crop.
    483   bool can_crop = true;
    484   if (captured_frame->fourcc == FOURCC_MJPG) {
    485     float cam_aspect = static_cast<float>(captured_frame->width) /
    486         static_cast<float>(captured_frame->height);
    487     float view_aspect = static_cast<float>(ratio_w_) /
    488         static_cast<float>(ratio_h_);
    489     can_crop = cam_aspect <= view_aspect;
    490   }
    491   if (can_crop && !IsScreencast()) {
    492     // TODO(ronghuawu): The capturer should always produce the native
    493     // resolution and the cropping should be done in downstream code.
    494     ComputeCrop(ratio_w_, ratio_h_, captured_frame->width,
    495                 abs(captured_frame->height), captured_frame->pixel_width,
    496                 captured_frame->pixel_height, captured_frame->rotation,
    497                 &cropped_width, &cropped_height);
    498   }
    499 
    500   int adapted_width = cropped_width;
    501   int adapted_height = cropped_height;
    502   if (enable_video_adapter_ && !IsScreencast()) {
    503     const VideoFormat adapted_format =
    504         video_adapter_.AdaptFrameResolution(cropped_width, cropped_height);
    505     if (adapted_format.IsSize0x0()) {
    506       // VideoAdapter dropped the frame.
    507       ++adapt_frame_drops_;
    508       return;
    509     }
    510     adapted_width = adapted_format.width;
    511     adapted_height = adapted_format.height;
    512   }
    513 
    514   if (!frame_factory_) {
    515     LOG(LS_ERROR) << "No video frame factory.";
    516     return;
    517   }
    518 
    519   rtc::scoped_ptr<VideoFrame> adapted_frame(
    520       frame_factory_->CreateAliasedFrame(captured_frame,
    521                                          cropped_width, cropped_height,
    522                                          adapted_width, adapted_height));
    523 
    524   if (!adapted_frame) {
    525     // TODO(fbarchard): LOG more information about captured frame attributes.
    526     LOG(LS_ERROR) << "Couldn't convert to I420! "
    527                   << "From " << ToString(captured_frame) << " To "
    528                   << cropped_width << " x " << cropped_height;
    529     return;
    530   }
    531 
    532   if (muted_) {
    533     // TODO(pthatcher): Use frame_factory_->CreateBlackFrame() instead.
    534     adapted_frame->SetToBlack();
    535   }
    536   SignalVideoFrame(this, adapted_frame.get());
    537 
    538   UpdateStats(captured_frame);
    539 }
    540 
    541 void VideoCapturer::SetCaptureState(CaptureState state) {
    542   if (state == capture_state_) {
    543     // Don't trigger a state changed callback if the state hasn't changed.
    544     return;
    545   }
    546   StateChangeParams* state_params = new StateChangeParams(state);
    547   capture_state_ = state;
    548   thread_->Post(this, MSG_STATE_CHANGE, state_params);
    549 }
    550 
    551 void VideoCapturer::OnMessage(rtc::Message* message) {
    552   switch (message->message_id) {
    553     case MSG_STATE_CHANGE: {
    554       rtc::scoped_ptr<StateChangeParams> p(
    555           static_cast<StateChangeParams*>(message->pdata));
    556       SignalStateChange(this, p->data());
    557       break;
    558     }
    559     case MSG_DO_PAUSE: {
    560       Pause(true);
    561       break;
    562     }
    563     case MSG_DO_UNPAUSE: {
    564       Pause(false);
    565       break;
    566     }
    567     default: {
    568       ASSERT(false);
    569     }
    570   }
    571 }
    572 
    573 // Get the distance between the supported and desired formats.
    574 // Prioritization is done according to this algorithm:
    575 // 1) Width closeness. If not same, we prefer wider.
    576 // 2) Height closeness. If not same, we prefer higher.
    577 // 3) Framerate closeness. If not same, we prefer faster.
    578 // 4) Compression. If desired format has a specific fourcc, we need exact match;
    579 //                otherwise, we use preference.
    580 int64_t VideoCapturer::GetFormatDistance(const VideoFormat& desired,
    581                                          const VideoFormat& supported) {
    582   int64_t distance = kMaxDistance;
    583 
    584   // Check fourcc.
    585   uint32_t supported_fourcc = CanonicalFourCC(supported.fourcc);
    586   int64_t delta_fourcc = kMaxDistance;
    587   if (FOURCC_ANY == desired.fourcc) {
    588     // Any fourcc is OK for the desired. Use preference to find best fourcc.
    589     std::vector<uint32_t> preferred_fourccs;
    590     if (!GetPreferredFourccs(&preferred_fourccs)) {
    591       return distance;
    592     }
    593 
    594     for (size_t i = 0; i < preferred_fourccs.size(); ++i) {
    595       if (supported_fourcc == CanonicalFourCC(preferred_fourccs[i])) {
    596         delta_fourcc = i;
    597 #ifdef WEBRTC_LINUX
    598         // For HD avoid YU12 which is a software conversion and has 2 bugs
    599         // b/7326348 b/6960899.  Reenable when fixed.
    600         if (supported.height >= 720 && (supported_fourcc == FOURCC_YU12 ||
    601                                         supported_fourcc == FOURCC_YV12)) {
    602           delta_fourcc += kYU12Penalty;
    603         }
    604 #endif
    605         break;
    606       }
    607     }
    608   } else if (supported_fourcc == CanonicalFourCC(desired.fourcc)) {
    609     delta_fourcc = 0;  // Need exact match.
    610   }
    611 
    612   if (kMaxDistance == delta_fourcc) {
    613     // Failed to match fourcc.
    614     return distance;
    615   }
    616 
    617   // Check resolution and fps.
    618   int desired_width = desired.width;
    619   int desired_height = desired.height;
    620   int64_t delta_w = supported.width - desired_width;
    621   float supported_fps = VideoFormat::IntervalToFpsFloat(supported.interval);
    622   float delta_fps =
    623       supported_fps - VideoFormat::IntervalToFpsFloat(desired.interval);
    624   // Check height of supported height compared to height we would like it to be.
    625   int64_t aspect_h = desired_width
    626                          ? supported.width * desired_height / desired_width
    627                          : desired_height;
    628   int64_t delta_h = supported.height - aspect_h;
    629 
    630   distance = 0;
    631   // Set high penalty if the supported format is lower than the desired format.
    632   // 3x means we would prefer down to down to 3/4, than up to double.
    633   // But we'd prefer up to double than down to 1/2.  This is conservative,
    634   // strongly avoiding going down in resolution, similar to
    635   // the old method, but not completely ruling it out in extreme situations.
    636   // It also ignores framerate, which is often very low at high resolutions.
    637   // TODO(fbarchard): Improve logic to use weighted factors.
    638   static const int kDownPenalty = -3;
    639   if (delta_w < 0) {
    640     delta_w = delta_w * kDownPenalty;
    641   }
    642   if (delta_h < 0) {
    643     delta_h = delta_h * kDownPenalty;
    644   }
    645   // Require camera fps to be at least 80% of what is requested if resolution
    646   // matches.
    647   // Require camera fps to be at least 96% of what is requested, or higher,
    648   // if resolution differs. 96% allows for slight variations in fps. e.g. 29.97
    649   if (delta_fps < 0) {
    650     float min_desirable_fps = delta_w ?
    651     VideoFormat::IntervalToFpsFloat(desired.interval) * 28.f / 30.f :
    652     VideoFormat::IntervalToFpsFloat(desired.interval) * 23.f / 30.f;
    653     delta_fps = -delta_fps;
    654     if (supported_fps < min_desirable_fps) {
    655       distance |= static_cast<int64_t>(1) << 62;
    656     } else {
    657       distance |= static_cast<int64_t>(1) << 15;
    658     }
    659   }
    660   int64_t idelta_fps = static_cast<int>(delta_fps);
    661 
    662   // 12 bits for width and height and 8 bits for fps and fourcc.
    663   distance |=
    664       (delta_w << 28) | (delta_h << 16) | (idelta_fps << 8) | delta_fourcc;
    665 
    666   return distance;
    667 }
    668 
    669 void VideoCapturer::UpdateFilteredSupportedFormats() {
    670   filtered_supported_formats_.clear();
    671   filtered_supported_formats_ = supported_formats_;
    672   if (!max_format_) {
    673     return;
    674   }
    675   std::vector<VideoFormat>::iterator iter = filtered_supported_formats_.begin();
    676   while (iter != filtered_supported_formats_.end()) {
    677     if (ShouldFilterFormat(*iter)) {
    678       iter = filtered_supported_formats_.erase(iter);
    679     } else {
    680       ++iter;
    681     }
    682   }
    683   if (filtered_supported_formats_.empty()) {
    684     // The device only captures at resolutions higher than |max_format_| this
    685     // indicates that |max_format_| should be ignored as it is better to capture
    686     // at too high a resolution than to not capture at all.
    687     filtered_supported_formats_ = supported_formats_;
    688   }
    689 }
    690 
    691 bool VideoCapturer::ShouldFilterFormat(const VideoFormat& format) const {
    692   if (!enable_camera_list_) {
    693     return false;
    694   }
    695   return format.width > max_format_->width ||
    696          format.height > max_format_->height;
    697 }
    698 
    699 void VideoCapturer::UpdateStats(const CapturedFrame* captured_frame) {
    700   // Update stats protected from fetches from different thread.
    701   rtc::CritScope cs(&frame_stats_crit_);
    702 
    703   last_captured_frame_format_.width = captured_frame->width;
    704   last_captured_frame_format_.height = captured_frame->height;
    705   // TODO(ronghuawu): Useful to report interval as well?
    706   last_captured_frame_format_.interval = 0;
    707   last_captured_frame_format_.fourcc = captured_frame->fourcc;
    708 
    709   double time_now = frame_length_time_reporter_.TimerNow();
    710   if (previous_frame_time_ != 0.0) {
    711     adapt_frame_drops_data_.AddSample(adapt_frame_drops_);
    712     frame_time_data_.AddSample(time_now - previous_frame_time_);
    713   }
    714   previous_frame_time_ = time_now;
    715   adapt_frame_drops_ = 0;
    716 }
    717 
    718 template<class T>
    719 void VideoCapturer::GetVariableSnapshot(
    720     const rtc::RollingAccumulator<T>& data,
    721     VariableInfo<T>* stats) {
    722   stats->max_val = data.ComputeMax();
    723   stats->mean = data.ComputeMean();
    724   stats->min_val = data.ComputeMin();
    725   stats->variance = data.ComputeVariance();
    726 }
    727 
    728 }  // namespace cricket
    729