| /* |
| * 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, int targetFps) { |
| if (this.targetBitrateBps > 0 && targetBitrateBps < this.targetBitrateBps) { |
| // Rescale the accumulator level if the accumulator max decreases |
| deviationBytes = deviationBytes * targetBitrateBps / this.targetBitrateBps; |
| } |
| super.setTargets(targetBitrateBps, targetFps); |
| } |
| |
| @Override |
| public void reportEncodedFrame(int size) { |
| if (targetFps == 0) { |
| return; |
| } |
| |
| // Accumulate the difference between actual and expected frame sizes. |
| double expectedBytesPerFrame = (targetBitrateBps / BITS_PER_BYTE) / targetFps; |
| deviationBytes += (size - expectedBytesPerFrame); |
| timeSinceLastAdjustmentMs += 1000.0 / targetFps; |
| |
| // 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()); |
| } |
| } |