| Index: webrtc/modules/audio_coding/codecs/opus/audio_encoder_opus.cc
|
| diff --git a/webrtc/modules/audio_coding/codecs/opus/audio_encoder_opus.cc b/webrtc/modules/audio_coding/codecs/opus/audio_encoder_opus.cc
|
| index be32aef152e80e05feed810d415f5718ca04a869..9ebb63d8497ceabd2907720b1ab95482a9b2c2e0 100644
|
| --- a/webrtc/modules/audio_coding/codecs/opus/audio_encoder_opus.cc
|
| +++ b/webrtc/modules/audio_coding/codecs/opus/audio_encoder_opus.cc
|
| @@ -19,6 +19,7 @@
|
| #include "webrtc/base/logging.h"
|
| #include "webrtc/base/numerics/exp_filter.h"
|
| #include "webrtc/base/protobuf_utils.h"
|
| +#include "webrtc/base/ptr_util.h"
|
| #include "webrtc/base/safe_conversions.h"
|
| #include "webrtc/base/safe_minmax.h"
|
| #include "webrtc/base/string_to_number.h"
|
| @@ -48,11 +49,6 @@ constexpr int kOpusBitrateNbBps = 12000;
|
| constexpr int kOpusBitrateWbBps = 20000;
|
| constexpr int kOpusBitrateFbBps = 32000;
|
|
|
| -// Opus API allows a min bitrate of 500bps, but Opus documentation suggests
|
| -// bitrate should be in the range of 6000 to 510000, inclusive.
|
| -constexpr int kOpusMinBitrateBps = 6000;
|
| -constexpr int kOpusMaxBitrateBps = 510000;
|
| -
|
| constexpr int kSampleRateHz = 48000;
|
| constexpr int kDefaultMaxPlaybackRate = 48000;
|
|
|
| @@ -133,8 +129,8 @@ int CalculateDefaultBitrate(int max_playback_rate, size_t num_channels) {
|
| return kOpusBitrateFbBps * rtc::dchecked_cast<int>(num_channels);
|
| }
|
| }();
|
| - RTC_DCHECK_GE(bitrate, kOpusMinBitrateBps);
|
| - RTC_DCHECK_LE(bitrate, kOpusMaxBitrateBps);
|
| + RTC_DCHECK_GE(bitrate, AudioEncoderOpusConfig::kMinBitrateBps);
|
| + RTC_DCHECK_LE(bitrate, AudioEncoderOpusConfig::kMaxBitrateBps);
|
| return bitrate;
|
| }
|
|
|
| @@ -150,7 +146,8 @@ int CalculateBitrate(int max_playback_rate_hz,
|
| const auto bitrate = rtc::StringToNumber<int>(*bitrate_param);
|
| if (bitrate) {
|
| const int chosen_bitrate =
|
| - std::max(kOpusMinBitrateBps, std::min(*bitrate, kOpusMaxBitrateBps));
|
| + std::max(AudioEncoderOpusConfig::kMinBitrateBps,
|
| + std::min(*bitrate, AudioEncoderOpusConfig::kMaxBitrateBps));
|
| if (bitrate != chosen_bitrate) {
|
| LOG(LS_WARNING) << "Invalid maxaveragebitrate " << *bitrate
|
| << " clamped to " << chosen_bitrate;
|
| @@ -195,7 +192,7 @@ int GetFrameSizeMs(const SdpAudioFormat& format) {
|
| return *(std::end(kOpusSupportedFrameLengths) - 1);
|
| }
|
|
|
| - return AudioEncoderOpus::Config::kDefaultFrameSizeMs;
|
| + return AudioEncoderOpusConfig::kDefaultFrameSizeMs;
|
| }
|
|
|
| void FindSupportedFrameLengths(int min_frame_length_ms,
|
| @@ -211,8 +208,39 @@ void FindSupportedFrameLengths(int min_frame_length_ms,
|
| RTC_DCHECK(std::is_sorted(out->begin(), out->end()));
|
| }
|
|
|
| +int GetBitrateBps(const AudioEncoderOpusConfig& config) {
|
| + RTC_DCHECK(config.IsOk());
|
| + return *config.bitrate_bps;
|
| +}
|
| +
|
| } // namespace
|
|
|
| +void AudioEncoderOpus::AppendSupportedEncoders(
|
| + std::vector<AudioCodecSpec>* specs) {
|
| + const SdpAudioFormat fmt = {
|
| + "opus", 48000, 2, {{"minptime", "10"}, {"useinbandfec", "1"}}};
|
| + const AudioCodecInfo info = QueryAudioEncoder(*SdpToConfig(fmt));
|
| + specs->push_back({fmt, info});
|
| +}
|
| +
|
| +AudioCodecInfo AudioEncoderOpus::QueryAudioEncoder(
|
| + const AudioEncoderOpusConfig& config) {
|
| + RTC_DCHECK(config.IsOk());
|
| + AudioCodecInfo info(48000, config.num_channels, *config.bitrate_bps,
|
| + AudioEncoderOpusConfig::kMinBitrateBps,
|
| + AudioEncoderOpusConfig::kMaxBitrateBps);
|
| + info.allow_comfort_noise = false;
|
| + info.supports_network_adaption = true;
|
| + return info;
|
| +}
|
| +
|
| +std::unique_ptr<AudioEncoder> AudioEncoderOpus::MakeAudioEncoder(
|
| + const AudioEncoderOpusConfig& config,
|
| + int payload_type) {
|
| + RTC_DCHECK(config.IsOk());
|
| + return rtc::MakeUnique<AudioEncoderOpus>(config, payload_type);
|
| +}
|
| +
|
| rtc::Optional<AudioCodecInfo> AudioEncoderOpus::QueryAudioEncoder(
|
| const SdpAudioFormat& format) {
|
| if (STR_CASE_CMP(format.name.c_str(), GetPayloadName()) == 0 &&
|
| @@ -221,8 +249,9 @@ rtc::Optional<AudioCodecInfo> AudioEncoderOpus::QueryAudioEncoder(
|
| const int bitrate =
|
| CalculateBitrate(GetMaxPlaybackRate(format), num_channels,
|
| GetFormatParameter(format, "maxaveragebitrate"));
|
| - AudioCodecInfo info(48000, num_channels, bitrate, kOpusMinBitrateBps,
|
| - kOpusMaxBitrateBps);
|
| + AudioCodecInfo info(48000, num_channels, bitrate,
|
| + AudioEncoderOpusConfig::kMinBitrateBps,
|
| + AudioEncoderOpusConfig::kMaxBitrateBps);
|
| info.allow_comfort_noise = false;
|
| info.supports_network_adaption = true;
|
|
|
| @@ -231,27 +260,36 @@ rtc::Optional<AudioCodecInfo> AudioEncoderOpus::QueryAudioEncoder(
|
| return rtc::Optional<AudioCodecInfo>();
|
| }
|
|
|
| -AudioEncoderOpus::Config AudioEncoderOpus::CreateConfig(
|
| +AudioEncoderOpusConfig AudioEncoderOpus::CreateConfig(
|
| + int payload_type,
|
| + const SdpAudioFormat& format) {
|
| + auto opt_config = SdpToConfig(format);
|
| + RTC_CHECK(opt_config);
|
| + opt_config->payload_type = payload_type;
|
| + return *opt_config;
|
| +}
|
| +
|
| +AudioEncoderOpusConfig AudioEncoderOpus::CreateConfig(
|
| const CodecInst& codec_inst) {
|
| - AudioEncoderOpus::Config config;
|
| + AudioEncoderOpusConfig config;
|
| config.frame_size_ms = rtc::CheckedDivExact(codec_inst.pacsize, 48);
|
| config.num_channels = codec_inst.channels;
|
| config.bitrate_bps = rtc::Optional<int>(codec_inst.rate);
|
| - config.payload_type = codec_inst.pltype;
|
| - config.application = config.num_channels == 1 ? AudioEncoderOpus::kVoip
|
| - : AudioEncoderOpus::kAudio;
|
| + config.application = config.num_channels == 1
|
| + ? AudioEncoderOpusConfig::ApplicationMode::kVoip
|
| + : AudioEncoderOpusConfig::ApplicationMode::kAudio;
|
| config.supported_frame_lengths_ms.push_back(config.frame_size_ms);
|
| -#if WEBRTC_OPUS_VARIABLE_COMPLEXITY
|
| - config.low_rate_complexity = 9;
|
| -#endif
|
| return config;
|
| }
|
|
|
| -AudioEncoderOpus::Config AudioEncoderOpus::CreateConfig(
|
| - int payload_type,
|
| +rtc::Optional<AudioEncoderOpusConfig> AudioEncoderOpus::SdpToConfig(
|
| const SdpAudioFormat& format) {
|
| - AudioEncoderOpus::Config config;
|
| + if (STR_CASE_CMP(format.name.c_str(), "opus") != 0 ||
|
| + format.clockrate_hz != 48000 || format.num_channels != 2) {
|
| + return rtc::Optional<AudioEncoderOpusConfig>();
|
| + }
|
|
|
| + AudioEncoderOpusConfig config;
|
| config.num_channels = GetChannelCount(format);
|
| config.frame_size_ms = GetFrameSizeMs(format);
|
| config.max_playback_rate_hz = GetMaxPlaybackRate(format);
|
| @@ -261,16 +299,14 @@ AudioEncoderOpus::Config AudioEncoderOpus::CreateConfig(
|
| config.bitrate_bps = rtc::Optional<int>(
|
| CalculateBitrate(config.max_playback_rate_hz, config.num_channels,
|
| GetFormatParameter(format, "maxaveragebitrate")));
|
| - config.payload_type = payload_type;
|
| - config.application = config.num_channels == 1 ? AudioEncoderOpus::kVoip
|
| - : AudioEncoderOpus::kAudio;
|
| -#if WEBRTC_OPUS_VARIABLE_COMPLEXITY
|
| - config.low_rate_complexity = 9;
|
| -#endif
|
| + config.application = config.num_channels == 1
|
| + ? AudioEncoderOpusConfig::ApplicationMode::kVoip
|
| + : AudioEncoderOpusConfig::ApplicationMode::kAudio;
|
|
|
| constexpr int kMinANAFrameLength = kANASupportedFrameLengths[0];
|
| constexpr int kMaxANAFrameLength =
|
| kANASupportedFrameLengths[arraysize(kANASupportedFrameLengths) - 1];
|
| +
|
| // For now, minptime and maxptime are only used with ANA. If ptime is outside
|
| // of this range, it will get adjusted once ANA takes hold. Ideally, we'd know
|
| // if ANA was to be used when setting up the config, and adjust accordingly.
|
| @@ -281,7 +317,25 @@ AudioEncoderOpus::Config AudioEncoderOpus::CreateConfig(
|
|
|
| FindSupportedFrameLengths(min_frame_length_ms, max_frame_length_ms,
|
| &config.supported_frame_lengths_ms);
|
| - return config;
|
| + RTC_DCHECK(config.IsOk());
|
| + return rtc::Optional<AudioEncoderOpusConfig>(config);
|
| +}
|
| +
|
| +rtc::Optional<int> AudioEncoderOpus::GetNewComplexity(
|
| + const AudioEncoderOpusConfig& config) {
|
| + RTC_DCHECK(config.IsOk());
|
| + const int bitrate_bps = GetBitrateBps(config);
|
| + if (bitrate_bps >= config.complexity_threshold_bps -
|
| + config.complexity_threshold_window_bps &&
|
| + bitrate_bps <= config.complexity_threshold_bps +
|
| + config.complexity_threshold_window_bps) {
|
| + // Within the hysteresis window; make no change.
|
| + return rtc::Optional<int>();
|
| + } else {
|
| + return rtc::Optional<int>(bitrate_bps <= config.complexity_threshold_bps
|
| + ? config.low_rate_complexity
|
| + : config.complexity);
|
| + }
|
| }
|
|
|
| class AudioEncoderOpus::PacketLossFractionSmoother {
|
| @@ -311,58 +365,16 @@ class AudioEncoderOpus::PacketLossFractionSmoother {
|
| rtc::ExpFilter smoother_;
|
| };
|
|
|
| -AudioEncoderOpus::Config::Config() {
|
| -#if WEBRTC_OPUS_VARIABLE_COMPLEXITY
|
| - low_rate_complexity = 9;
|
| -#endif
|
| -}
|
| -AudioEncoderOpus::Config::Config(const Config&) = default;
|
| -AudioEncoderOpus::Config::~Config() = default;
|
| -auto AudioEncoderOpus::Config::operator=(const Config&) -> Config& = default;
|
| -
|
| -bool AudioEncoderOpus::Config::IsOk() const {
|
| - if (frame_size_ms <= 0 || frame_size_ms % 10 != 0)
|
| - return false;
|
| - if (num_channels != 1 && num_channels != 2)
|
| - return false;
|
| - if (bitrate_bps &&
|
| - (*bitrate_bps < kOpusMinBitrateBps || *bitrate_bps > kOpusMaxBitrateBps))
|
| - return false;
|
| - if (complexity < 0 || complexity > 10)
|
| - return false;
|
| - if (low_rate_complexity < 0 || low_rate_complexity > 10)
|
| - return false;
|
| - return true;
|
| -}
|
| -
|
| -int AudioEncoderOpus::Config::GetBitrateBps() const {
|
| - RTC_DCHECK(IsOk());
|
| - if (bitrate_bps)
|
| - return *bitrate_bps; // Explicitly set value.
|
| - else
|
| - return num_channels == 1 ? 32000 : 64000; // Default value.
|
| -}
|
| -
|
| -rtc::Optional<int> AudioEncoderOpus::Config::GetNewComplexity() const {
|
| - RTC_DCHECK(IsOk());
|
| - const int bitrate_bps = GetBitrateBps();
|
| - if (bitrate_bps >=
|
| - complexity_threshold_bps - complexity_threshold_window_bps &&
|
| - bitrate_bps <=
|
| - complexity_threshold_bps + complexity_threshold_window_bps) {
|
| - // Within the hysteresis window; make no change.
|
| - return rtc::Optional<int>();
|
| - }
|
| - return bitrate_bps <= complexity_threshold_bps
|
| - ? rtc::Optional<int>(low_rate_complexity)
|
| - : rtc::Optional<int>(complexity);
|
| -}
|
| +AudioEncoderOpus::AudioEncoderOpus(const AudioEncoderOpusConfig& config)
|
| + : AudioEncoderOpus(config, config.payload_type) {}
|
|
|
| AudioEncoderOpus::AudioEncoderOpus(
|
| - const Config& config,
|
| + const AudioEncoderOpusConfig& config,
|
| + int payload_type,
|
| AudioNetworkAdaptorCreator&& audio_network_adaptor_creator,
|
| std::unique_ptr<SmoothingFilter> bitrate_smoother)
|
| - : send_side_bwe_with_overhead_(webrtc::field_trial::IsEnabled(
|
| + : payload_type_(payload_type),
|
| + send_side_bwe_with_overhead_(webrtc::field_trial::IsEnabled(
|
| "WebRTC-SendSideBwe-WithOverhead")),
|
| packet_loss_rate_(0.0),
|
| inst_(nullptr),
|
| @@ -379,15 +391,21 @@ AudioEncoderOpus::AudioEncoderOpus(
|
| ? std::move(bitrate_smoother) : std::unique_ptr<SmoothingFilter>(
|
| // We choose 5sec as initial time constant due to empirical data.
|
| new SmoothingFilterImpl(5000))) {
|
| + RTC_DCHECK(0 <= payload_type && payload_type <= 127);
|
| +
|
| + // Sanity check of the redundant payload type field that we want to get rid
|
| + // of. See https://bugs.chromium.org/p/webrtc/issues/detail?id=7847
|
| + RTC_CHECK(config.payload_type == -1 || config.payload_type == payload_type);
|
| +
|
| RTC_CHECK(RecreateEncoderInstance(config));
|
| }
|
|
|
| AudioEncoderOpus::AudioEncoderOpus(const CodecInst& codec_inst)
|
| - : AudioEncoderOpus(CreateConfig(codec_inst), nullptr) {}
|
| + : AudioEncoderOpus(CreateConfig(codec_inst), codec_inst.pltype) {}
|
|
|
| AudioEncoderOpus::AudioEncoderOpus(int payload_type,
|
| const SdpAudioFormat& format)
|
| - : AudioEncoderOpus(CreateConfig(payload_type, format), nullptr) {}
|
| + : AudioEncoderOpus(*SdpToConfig(format), payload_type) {}
|
|
|
| AudioEncoderOpus::~AudioEncoderOpus() {
|
| RTC_CHECK_EQ(0, WebRtcOpus_EncoderFree(inst_));
|
| @@ -410,7 +428,7 @@ size_t AudioEncoderOpus::Max10MsFramesInAPacket() const {
|
| }
|
|
|
| int AudioEncoderOpus::GetTargetBitrate() const {
|
| - return config_.GetBitrateBps();
|
| + return GetBitrateBps(config_);
|
| }
|
|
|
| void AudioEncoderOpus::Reset() {
|
| @@ -445,10 +463,10 @@ bool AudioEncoderOpus::SetApplication(Application application) {
|
| auto conf = config_;
|
| switch (application) {
|
| case Application::kSpeech:
|
| - conf.application = AudioEncoderOpus::kVoip;
|
| + conf.application = AudioEncoderOpusConfig::ApplicationMode::kVoip;
|
| break;
|
| case Application::kAudio:
|
| - conf.application = AudioEncoderOpus::kAudio;
|
| + conf.application = AudioEncoderOpusConfig::ApplicationMode::kAudio;
|
| break;
|
| }
|
| return RecreateEncoderInstance(conf);
|
| @@ -523,9 +541,10 @@ void AudioEncoderOpus::OnReceivedUplinkBandwidth(
|
| }
|
| const int overhead_bps = static_cast<int>(
|
| *overhead_bytes_per_packet_ * 8 * 100 / Num10MsFramesInNextPacket());
|
| - SetTargetBitrate(std::min(
|
| - kOpusMaxBitrateBps,
|
| - std::max(kOpusMinBitrateBps, target_audio_bitrate_bps - overhead_bps)));
|
| + SetTargetBitrate(
|
| + std::min(AudioEncoderOpusConfig::kMaxBitrateBps,
|
| + std::max(AudioEncoderOpusConfig::kMinBitrateBps,
|
| + target_audio_bitrate_bps - overhead_bps)));
|
| } else {
|
| SetTargetBitrate(target_audio_bitrate_bps);
|
| }
|
| @@ -597,7 +616,7 @@ AudioEncoder::EncodedInfo AudioEncoderOpus::EncodeImpl(
|
| config_.frame_size_ms = next_frame_length_ms_;
|
|
|
| info.encoded_timestamp = first_timestamp_in_buffer_;
|
| - info.payload_type = config_.payload_type;
|
| + info.payload_type = payload_type_;
|
| info.send_even_if_empty = true; // Allows Opus to send empty packets.
|
| info.speech = (info.encoded_bytes > 0);
|
| info.encoder_type = CodecType::kOpus;
|
| @@ -616,7 +635,7 @@ size_t AudioEncoderOpus::SufficientOutputBufferSize() const {
|
| // Calculate the number of bytes we expect the encoder to produce,
|
| // then multiply by two to give a wide margin for error.
|
| const size_t bytes_per_millisecond =
|
| - static_cast<size_t>(config_.GetBitrateBps() / (1000 * 8) + 1);
|
| + static_cast<size_t>(GetBitrateBps(config_) / (1000 * 8) + 1);
|
| const size_t approx_encoded_bytes =
|
| Num10msFramesPerPacket() * 10 * bytes_per_millisecond;
|
| return 2 * approx_encoded_bytes;
|
| @@ -625,7 +644,8 @@ size_t AudioEncoderOpus::SufficientOutputBufferSize() const {
|
| // If the given config is OK, recreate the Opus encoder instance with those
|
| // settings, save the config, and return true. Otherwise, do nothing and return
|
| // false.
|
| -bool AudioEncoderOpus::RecreateEncoderInstance(const Config& config) {
|
| +bool AudioEncoderOpus::RecreateEncoderInstance(
|
| + const AudioEncoderOpusConfig& config) {
|
| if (!config.IsOk())
|
| return false;
|
| config_ = config;
|
| @@ -633,9 +653,13 @@ bool AudioEncoderOpus::RecreateEncoderInstance(const Config& config) {
|
| RTC_CHECK_EQ(0, WebRtcOpus_EncoderFree(inst_));
|
| input_buffer_.clear();
|
| input_buffer_.reserve(Num10msFramesPerPacket() * SamplesPer10msFrame());
|
| - RTC_CHECK_EQ(0, WebRtcOpus_EncoderCreate(&inst_, config.num_channels,
|
| - config.application));
|
| - RTC_CHECK_EQ(0, WebRtcOpus_SetBitRate(inst_, config.GetBitrateBps()));
|
| + RTC_CHECK_EQ(0, WebRtcOpus_EncoderCreate(
|
| + &inst_, config.num_channels,
|
| + config.application ==
|
| + AudioEncoderOpusConfig::ApplicationMode::kVoip
|
| + ? 0
|
| + : 1));
|
| + RTC_CHECK_EQ(0, WebRtcOpus_SetBitRate(inst_, GetBitrateBps(config)));
|
| if (config.fec_enabled) {
|
| RTC_CHECK_EQ(0, WebRtcOpus_EnableFec(inst_));
|
| } else {
|
| @@ -645,7 +669,7 @@ bool AudioEncoderOpus::RecreateEncoderInstance(const Config& config) {
|
| 0, WebRtcOpus_SetMaxPlaybackRate(inst_, config.max_playback_rate_hz));
|
| // Use the default complexity if the start bitrate is within the hysteresis
|
| // window.
|
| - complexity_ = config.GetNewComplexity().value_or(config.complexity);
|
| + complexity_ = GetNewComplexity(config).value_or(config.complexity);
|
| RTC_CHECK_EQ(0, WebRtcOpus_SetComplexity(inst_, complexity_));
|
| if (config.dtx_enabled) {
|
| RTC_CHECK_EQ(0, WebRtcOpus_EnableDtx(inst_));
|
| @@ -692,10 +716,11 @@ void AudioEncoderOpus::SetProjectedPacketLossRate(float fraction) {
|
|
|
| void AudioEncoderOpus::SetTargetBitrate(int bits_per_second) {
|
| config_.bitrate_bps = rtc::Optional<int>(rtc::SafeClamp<int>(
|
| - bits_per_second, kOpusMinBitrateBps, kOpusMaxBitrateBps));
|
| + bits_per_second, AudioEncoderOpusConfig::kMinBitrateBps,
|
| + AudioEncoderOpusConfig::kMaxBitrateBps));
|
| RTC_DCHECK(config_.IsOk());
|
| - RTC_CHECK_EQ(0, WebRtcOpus_SetBitRate(inst_, config_.GetBitrateBps()));
|
| - const auto new_complexity = config_.GetNewComplexity();
|
| + RTC_CHECK_EQ(0, WebRtcOpus_SetBitRate(inst_, GetBitrateBps(config_)));
|
| + const auto new_complexity = GetNewComplexity(config_);
|
| if (new_complexity && complexity_ != *new_complexity) {
|
| complexity_ = *new_complexity;
|
| RTC_CHECK_EQ(0, WebRtcOpus_SetComplexity(inst_, complexity_));
|
| @@ -728,11 +753,11 @@ AudioEncoderOpus::DefaultAudioNetworkAdaptorCreator(
|
| AudioNetworkAdaptorImpl::Config config;
|
| config.event_log = event_log;
|
| return std::unique_ptr<AudioNetworkAdaptor>(new AudioNetworkAdaptorImpl(
|
| - config,
|
| - ControllerManagerImpl::Create(
|
| - config_string, NumChannels(), supported_frame_lengths_ms(),
|
| - kOpusMinBitrateBps, num_channels_to_encode_, next_frame_length_ms_,
|
| - GetTargetBitrate(), config_.fec_enabled, GetDtx())));
|
| + config, ControllerManagerImpl::Create(
|
| + config_string, NumChannels(), supported_frame_lengths_ms(),
|
| + AudioEncoderOpusConfig::kMinBitrateBps,
|
| + num_channels_to_encode_, next_frame_length_ms_,
|
| + GetTargetBitrate(), config_.fec_enabled, GetDtx())));
|
| }
|
|
|
| void AudioEncoderOpus::MaybeUpdateUplinkBandwidth() {
|
|
|