Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(37)

Unified Diff: webrtc/modules/audio_coding/codecs/opus/audio_encoder_opus.cc

Issue 2948483002: Opus implementation of the AudioEncoderFactoryTemplate API (Closed)
Patch Set: rebase Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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() {

Powered by Google App Engine
This is Rietveld 408576698