Home | History | Annotate | Download | only in utility
      1 /*
      2  *  Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
      3  *
      4  *  Use of this source code is governed by a BSD-style license
      5  *  that can be found in the LICENSE file in the root of the source
      6  *  tree. An additional intellectual property rights grant can be found
      7  *  in the file PATENTS.  All contributing project authors may
      8  *  be found in the AUTHORS file in the root of the source tree.
      9  */
     10 #include "webrtc/modules/video_coding/utility/quality_scaler.h"
     11 
     12 namespace webrtc {
     13 
     14 static const int kMinFps = 10;
     15 static const int kMeasureSeconds = 5;
     16 static const int kFramedropPercentThreshold = 60;
     17 
     18 const int QualityScaler::kDefaultLowQpDenominator = 3;
     19 // Note that this is the same for width and height to permit 120x90 in both
     20 // portrait and landscape mode.
     21 const int QualityScaler::kDefaultMinDownscaleDimension = 90;
     22 
     23 QualityScaler::QualityScaler()
     24     : num_samples_(0),
     25       low_qp_threshold_(-1),
     26       downscale_shift_(0),
     27       framerate_down_(false),
     28       min_width_(kDefaultMinDownscaleDimension),
     29       min_height_(kDefaultMinDownscaleDimension) {}
     30 
     31 void QualityScaler::Init(int low_qp_threshold,
     32                          int high_qp_threshold,
     33                          bool use_framerate_reduction) {
     34   ClearSamples();
     35   low_qp_threshold_ = low_qp_threshold;
     36   high_qp_threshold_ = high_qp_threshold;
     37   use_framerate_reduction_ = use_framerate_reduction;
     38   target_framerate_ = -1;
     39 }
     40 
     41 void QualityScaler::SetMinResolution(int min_width, int min_height) {
     42   min_width_ = min_width;
     43   min_height_ = min_height;
     44 }
     45 
     46 // Report framerate(fps) to estimate # of samples.
     47 void QualityScaler::ReportFramerate(int framerate) {
     48   num_samples_ = static_cast<size_t>(
     49       kMeasureSeconds * (framerate < kMinFps ? kMinFps : framerate));
     50   framerate_ = framerate;
     51 }
     52 
     53 void QualityScaler::ReportQP(int qp) {
     54   framedrop_percent_.AddSample(0);
     55   average_qp_.AddSample(qp);
     56 }
     57 
     58 void QualityScaler::ReportDroppedFrame() {
     59   framedrop_percent_.AddSample(100);
     60 }
     61 
     62 void QualityScaler::OnEncodeFrame(const VideoFrame& frame) {
     63   // Should be set through InitEncode -> Should be set by now.
     64   assert(low_qp_threshold_ >= 0);
     65   assert(num_samples_ > 0);
     66   res_.width = frame.width();
     67   res_.height = frame.height();
     68 
     69   // Update scale factor.
     70   int avg_drop = 0;
     71   int avg_qp = 0;
     72 
     73   // When encoder consistently overshoots, framerate reduction and spatial
     74   // resizing will be triggered to get a smoother video.
     75   if ((framedrop_percent_.GetAverage(num_samples_, &avg_drop) &&
     76        avg_drop >= kFramedropPercentThreshold) ||
     77       (average_qp_.GetAverage(num_samples_, &avg_qp) &&
     78        avg_qp > high_qp_threshold_)) {
     79     // Reducing frame rate before spatial resolution change.
     80     // Reduce frame rate only when it is above a certain number.
     81     // Only one reduction is allowed for now.
     82     // TODO(jackychen): Allow more than one framerate reduction.
     83     if (use_framerate_reduction_ && !framerate_down_ && framerate_ >= 20) {
     84       target_framerate_ = framerate_ / 2;
     85       framerate_down_ = true;
     86       // If frame rate has been updated, clear the buffer. We don't want
     87       // spatial resolution to change right after frame rate change.
     88       ClearSamples();
     89     } else {
     90       AdjustScale(false);
     91     }
     92   } else if (average_qp_.GetAverage(num_samples_, &avg_qp) &&
     93              avg_qp <= low_qp_threshold_) {
     94     if (use_framerate_reduction_ && framerate_down_) {
     95       target_framerate_ = -1;
     96       framerate_down_ = false;
     97       ClearSamples();
     98     } else {
     99       AdjustScale(true);
    100     }
    101   }
    102 
    103   assert(downscale_shift_ >= 0);
    104   for (int shift = downscale_shift_;
    105        shift > 0 && (res_.width / 2 >= min_width_) &&
    106        (res_.height / 2 >= min_height_);
    107        --shift) {
    108     res_.width /= 2;
    109     res_.height /= 2;
    110   }
    111 }
    112 
    113 QualityScaler::Resolution QualityScaler::GetScaledResolution() const {
    114   return res_;
    115 }
    116 
    117 int QualityScaler::GetTargetFramerate() const {
    118   return target_framerate_;
    119 }
    120 
    121 const VideoFrame& QualityScaler::GetScaledFrame(const VideoFrame& frame) {
    122   Resolution res = GetScaledResolution();
    123   if (res.width == frame.width())
    124     return frame;
    125 
    126   scaler_.Set(frame.width(), frame.height(), res.width, res.height, kI420,
    127               kI420, kScaleBox);
    128   if (scaler_.Scale(frame, &scaled_frame_) != 0)
    129     return frame;
    130 
    131   scaled_frame_.set_ntp_time_ms(frame.ntp_time_ms());
    132   scaled_frame_.set_timestamp(frame.timestamp());
    133   scaled_frame_.set_render_time_ms(frame.render_time_ms());
    134 
    135   return scaled_frame_;
    136 }
    137 
    138 void QualityScaler::ClearSamples() {
    139   framedrop_percent_.Reset();
    140   average_qp_.Reset();
    141 }
    142 
    143 void QualityScaler::AdjustScale(bool up) {
    144   downscale_shift_ += up ? -1 : 1;
    145   if (downscale_shift_ < 0)
    146     downscale_shift_ = 0;
    147   ClearSamples();
    148 }
    149 
    150 }  // namespace webrtc
    151