| /* | |
| * 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()); | |
| } | |
| } |