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() { |