/* * Copyright 2017 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ package org.webrtc; /** * BitrateAdjuster that tracks the bandwidth produced by an encoder and dynamically adjusts the * bitrate. Used for hardware codecs that pay attention to framerate but still deviate from the * target bitrate by unacceptable margins. */ class DynamicBitrateAdjuster extends BaseBitrateAdjuster { // Change the bitrate at most once every three seconds. private static final double BITRATE_ADJUSTMENT_SEC = 3.0; // Maximum bitrate adjustment scale - no more than 4 times. private static final double BITRATE_ADJUSTMENT_MAX_SCALE = 4; // Amount of adjustment steps to reach maximum scale. private static final int BITRATE_ADJUSTMENT_STEPS = 20; private static final double BITS_PER_BYTE = 8.0; // How far the codec has deviated above (or below) the target bitrate (tracked in bytes). private double deviationBytes; private double timeSinceLastAdjustmentMs; private int bitrateAdjustmentScaleExp; @Override public void setTargets(int targetBitrateBps, double targetFramerateFps) { if (this.targetBitrateBps > 0 && targetBitrateBps < this.targetBitrateBps) { // Rescale the accumulator level if the accumulator max decreases deviationBytes = deviationBytes * targetBitrateBps / this.targetBitrateBps; } super.setTargets(targetBitrateBps, targetFramerateFps); } @Override public void reportEncodedFrame(int size) { if (targetFramerateFps == 0) { return; } // Accumulate the difference between actual and expected frame sizes. double expectedBytesPerFrame = (targetBitrateBps / BITS_PER_BYTE) / targetFramerateFps; deviationBytes += (size - expectedBytesPerFrame); timeSinceLastAdjustmentMs += 1000.0 / targetFramerateFps; // Adjust the bitrate when the encoder accumulates one second's worth of data in excess or // shortfall of the target. double deviationThresholdBytes = targetBitrateBps / BITS_PER_BYTE; // Cap the deviation, i.e., don't let it grow beyond some level to avoid using too old data for // bitrate adjustment. This also prevents taking more than 3 "steps" in a given 3-second cycle. double deviationCap = BITRATE_ADJUSTMENT_SEC * deviationThresholdBytes; deviationBytes = Math.min(deviationBytes, deviationCap); deviationBytes = Math.max(deviationBytes, -deviationCap); // Do bitrate adjustment every 3 seconds if actual encoder bitrate deviates too much // from the target value. if (timeSinceLastAdjustmentMs <= 1000 * BITRATE_ADJUSTMENT_SEC) { return; } if (deviationBytes > deviationThresholdBytes) { // Encoder generates too high bitrate - need to reduce the scale. int bitrateAdjustmentInc = (int) (deviationBytes / deviationThresholdBytes + 0.5); bitrateAdjustmentScaleExp -= bitrateAdjustmentInc; // Don't let the adjustment scale drop below -BITRATE_ADJUSTMENT_STEPS. // This sets a minimum exponent of -1 (bitrateAdjustmentScaleExp / BITRATE_ADJUSTMENT_STEPS). bitrateAdjustmentScaleExp = Math.max(bitrateAdjustmentScaleExp, -BITRATE_ADJUSTMENT_STEPS); deviationBytes = deviationThresholdBytes; } else if (deviationBytes < -deviationThresholdBytes) { // Encoder generates too low bitrate - need to increase the scale. int bitrateAdjustmentInc = (int) (-deviationBytes / deviationThresholdBytes + 0.5); bitrateAdjustmentScaleExp += bitrateAdjustmentInc; // Don't let the adjustment scale exceed BITRATE_ADJUSTMENT_STEPS. // This sets a maximum exponent of 1 (bitrateAdjustmentScaleExp / BITRATE_ADJUSTMENT_STEPS). bitrateAdjustmentScaleExp = Math.min(bitrateAdjustmentScaleExp, BITRATE_ADJUSTMENT_STEPS); deviationBytes = -deviationThresholdBytes; } timeSinceLastAdjustmentMs = 0; } private double getBitrateAdjustmentScale() { return Math.pow(BITRATE_ADJUSTMENT_MAX_SCALE, (double) bitrateAdjustmentScaleExp / BITRATE_ADJUSTMENT_STEPS); } @Override public int getAdjustedBitrateBps() { return (int) (targetBitrateBps * getBitrateAdjustmentScale()); } }