Opus implementation of the AudioEncoderFactoryTemplate API

This was previously reverted, because external projects were using the
internal webrtc::AudioEncoderOpus class and broke when it was renamed.
This re-land avoids renaming it immediately, to give those projects
time to adapt. It also has to revert some of the changes I had made to the
Config struct, since that was also used by the same external projects.

BUG=webrtc:7831

Review-Url: https://codereview.webrtc.org/2948483002
Cr-Commit-Position: refs/heads/master@{#18852}
This commit is contained in:
kwiberg
2017-06-30 04:23:22 -07:00
committed by Commit Bot
parent 41bafb2f2d
commit 96da0115d7
13 changed files with 512 additions and 245 deletions

View File

@ -21,6 +21,13 @@ specific_include_rules = {
"+webrtc/modules/audio_coding/codecs/builtin_audio_encoder_factory.h", "+webrtc/modules/audio_coding/codecs/builtin_audio_encoder_factory.h",
], ],
# Needed because AudioEncoderOpus is in the wrong place for
# backwards compatibilty reasons. See
# https://bugs.chromium.org/p/webrtc/issues/detail?id=7847
"audio_encoder_opus\.h": [
"+webrtc/modules/audio_coding/codecs/opus/audio_encoder_opus.h",
],
# We allow .cc files in webrtc/api/ to #include a bunch of stuff # We allow .cc files in webrtc/api/ to #include a bunch of stuff
# that's off-limits for the .h files. That's because .h files leak # that's off-limits for the .h files. That's because .h files leak
# their #includes to whoever's #including them, but .cc files do not # their #includes to whoever's #including them, but .cc files do not

View File

@ -0,0 +1,42 @@
# Copyright (c) 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.
import("../../../webrtc.gni")
if (is_android) {
import("//build/config/android/config.gni")
import("//build/config/android/rules.gni")
}
rtc_static_library("audio_encoder_opus_config") {
sources = [
"audio_encoder_opus_config.cc",
"audio_encoder_opus_config.h",
]
deps = [
"../../../base:rtc_base_approved",
]
defines = []
if (rtc_opus_variable_complexity) {
defines += [ "WEBRTC_OPUS_VARIABLE_COMPLEXITY=1" ]
} else {
defines += [ "WEBRTC_OPUS_VARIABLE_COMPLEXITY=0" ]
}
}
rtc_source_set("audio_encoder_opus") {
sources = [
"audio_encoder_opus.h",
]
deps = [
":audio_encoder_opus_config",
"..:audio_codecs_api",
"../../../base:protobuf_utils", # TODO(kwiberg): Why is this needed?
"../../../base:rtc_base_approved",
"../../../modules/audio_coding:webrtc_opus",
]
}

View File

@ -0,0 +1,40 @@
/*
* Copyright (c) 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.
*/
#ifndef WEBRTC_API_AUDIO_CODECS_OPUS_AUDIO_ENCODER_OPUS_H_
#define WEBRTC_API_AUDIO_CODECS_OPUS_AUDIO_ENCODER_OPUS_H_
#include "webrtc/modules/audio_coding/codecs/opus/audio_encoder_opus.h"
namespace webrtc {
// Opus encoder API for use as a template parameter to
// CreateAudioEncoderFactory<...>().
//
// NOTE: At the moment, this struct actually resides in another file. This is a
// temporary backwards compatibility hack; see
// https://bugs.chromium.org/p/webrtc/issues/detail?id=7847
//
// NOTE: This struct is still under development and may change without notice.
/*
struct AudioEncoderOpus {
static rtc::Optional<AudioEncoderOpusConfig> SdpToConfig(
const SdpAudioFormat& audio_format);
static void AppendSupportedEncoders(std::vector<AudioCodecSpec>* specs);
static AudioCodecInfo QueryAudioEncoder(const AudioEncoderOpusConfig& config);
static std::unique_ptr<AudioEncoder> MakeAudioEncoder(
const AudioEncoderOpusConfig&,
int payload_type);
};
*/
} // namespace webrtc
#endif // WEBRTC_API_AUDIO_CODECS_OPUS_AUDIO_ENCODER_OPUS_H_

View File

@ -0,0 +1,70 @@
/*
* Copyright (c) 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.
*/
#include "webrtc/api/audio_codecs/opus/audio_encoder_opus_config.h"
namespace webrtc {
namespace {
#if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS) || defined(WEBRTC_ARCH_ARM)
// If we are on Android, iOS and/or ARM, use a lower complexity setting by
// default, to save encoder complexity.
constexpr int kDefaultComplexity = 5;
#else
constexpr int kDefaultComplexity = 9;
#endif
constexpr int kDefaultLowRateComplexity =
WEBRTC_OPUS_VARIABLE_COMPLEXITY ? 9 : kDefaultComplexity;
} // namespace
constexpr int AudioEncoderOpusConfig::kDefaultFrameSizeMs;
constexpr int AudioEncoderOpusConfig::kMinBitrateBps;
constexpr int AudioEncoderOpusConfig::kMaxBitrateBps;
AudioEncoderOpusConfig::AudioEncoderOpusConfig()
: frame_size_ms(kDefaultFrameSizeMs),
num_channels(1),
application(ApplicationMode::kVoip),
bitrate_bps(32000),
fec_enabled(false),
cbr_enabled(false),
max_playback_rate_hz(48000),
complexity(kDefaultComplexity),
low_rate_complexity(kDefaultLowRateComplexity),
complexity_threshold_bps(12500),
complexity_threshold_window_bps(1500),
dtx_enabled(false),
uplink_bandwidth_update_interval_ms(200),
payload_type(-1) {}
AudioEncoderOpusConfig::AudioEncoderOpusConfig(const AudioEncoderOpusConfig&) =
default;
AudioEncoderOpusConfig::~AudioEncoderOpusConfig() = default;
AudioEncoderOpusConfig& AudioEncoderOpusConfig::operator=(
const AudioEncoderOpusConfig&) = default;
bool AudioEncoderOpusConfig::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)
return false;
if (*bitrate_bps < kMinBitrateBps || *bitrate_bps > kMaxBitrateBps)
return false;
if (complexity < 0 || complexity > 10)
return false;
if (low_rate_complexity < 0 || low_rate_complexity > 10)
return false;
return true;
}
} // namespace webrtc

View File

@ -0,0 +1,73 @@
/*
* Copyright (c) 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.
*/
#ifndef WEBRTC_API_AUDIO_CODECS_OPUS_AUDIO_ENCODER_OPUS_CONFIG_H_
#define WEBRTC_API_AUDIO_CODECS_OPUS_AUDIO_ENCODER_OPUS_CONFIG_H_
#include <stddef.h>
#include <vector>
#include "webrtc/base/optional.h"
namespace webrtc {
// NOTE: This struct is still under development and may change without notice.
struct AudioEncoderOpusConfig {
static constexpr int kDefaultFrameSizeMs = 20;
// Opus API allows a min bitrate of 500bps, but Opus documentation suggests
// bitrate should be in the range of 6000 to 510000, inclusive.
static constexpr int kMinBitrateBps = 6000;
static constexpr int kMaxBitrateBps = 510000;
AudioEncoderOpusConfig();
AudioEncoderOpusConfig(const AudioEncoderOpusConfig&);
~AudioEncoderOpusConfig();
AudioEncoderOpusConfig& operator=(const AudioEncoderOpusConfig&);
bool IsOk() const; // Checks if the values are currently OK.
int frame_size_ms;
size_t num_channels;
enum class ApplicationMode { kVoip, kAudio };
ApplicationMode application;
// NOTE: This member must always be set.
// TODO(kwiberg): Turn it into just an int.
rtc::Optional<int> bitrate_bps;
bool fec_enabled;
bool cbr_enabled;
int max_playback_rate_hz;
// |complexity| is used when the bitrate goes above
// |complexity_threshold_bps| + |complexity_threshold_window_bps|;
// |low_rate_complexity| is used when the bitrate falls below
// |complexity_threshold_bps| - |complexity_threshold_window_bps|. In the
// interval in the middle, we keep using the most recent of the two
// complexity settings.
int complexity;
int low_rate_complexity;
int complexity_threshold_bps;
int complexity_threshold_window_bps;
bool dtx_enabled;
std::vector<int> supported_frame_lengths_ms;
int uplink_bandwidth_update_interval_ms;
// NOTE: This member isn't necessary, and will soon go away. See
// https://bugs.chromium.org/p/webrtc/issues/detail?id=7847
int payload_type;
};
} // namespace webrtc
#endif // WEBRTC_API_AUDIO_CODECS_OPUS_AUDIO_ENCODER_OPUS_CONFIG_H_

View File

@ -21,6 +21,7 @@ if (rtc_include_tests) {
] ]
deps = [ deps = [
"..:audio_codecs_api", "..:audio_codecs_api",
"../../../base:protobuf_utils", # TODO(kwiberg): Why is this needed?
"../../../base:rtc_base_approved", "../../../base:rtc_base_approved",
"../../../test:audio_codec_mocks", "../../../test:audio_codec_mocks",
"../../../test:test_support", "../../../test:test_support",
@ -28,6 +29,7 @@ if (rtc_include_tests) {
"../g722:audio_encoder_g722", "../g722:audio_encoder_g722",
"../ilbc:audio_decoder_ilbc", "../ilbc:audio_decoder_ilbc",
"../ilbc:audio_encoder_ilbc", "../ilbc:audio_encoder_ilbc",
"../opus:audio_encoder_opus",
"//testing/gmock", "//testing/gmock",
] ]
} }

View File

@ -11,6 +11,7 @@
#include "webrtc/api/audio_codecs/audio_encoder_factory_template.h" #include "webrtc/api/audio_codecs/audio_encoder_factory_template.h"
#include "webrtc/api/audio_codecs/g722/audio_encoder_g722.h" #include "webrtc/api/audio_codecs/g722/audio_encoder_g722.h"
#include "webrtc/api/audio_codecs/ilbc/audio_encoder_ilbc.h" #include "webrtc/api/audio_codecs/ilbc/audio_encoder_ilbc.h"
#include "webrtc/api/audio_codecs/opus/audio_encoder_opus.h"
#include "webrtc/base/ptr_util.h" #include "webrtc/base/ptr_util.h"
#include "webrtc/test/gmock.h" #include "webrtc/test/gmock.h"
#include "webrtc/test/gtest.h" #include "webrtc/test/gtest.h"
@ -149,4 +150,26 @@ TEST(AudioEncoderFactoryTemplateTest, Ilbc) {
EXPECT_EQ(8000, enc->SampleRateHz()); EXPECT_EQ(8000, enc->SampleRateHz());
} }
TEST(AudioEncoderFactoryTemplateTest, Opus) {
auto factory = CreateAudioEncoderFactory<AudioEncoderOpus>();
AudioCodecInfo info = {48000, 1, 32000, 6000, 510000};
info.allow_comfort_noise = false;
info.supports_network_adaption = true;
EXPECT_THAT(
factory->GetSupportedEncoders(),
testing::ElementsAre(AudioCodecSpec{
{"opus", 48000, 2, {{"minptime", "10"}, {"useinbandfec", "1"}}},
info}));
EXPECT_EQ(rtc::Optional<AudioCodecInfo>(),
factory->QueryAudioEncoder({"foo", 8000, 1}));
EXPECT_EQ(
rtc::Optional<AudioCodecInfo>(info),
factory->QueryAudioEncoder(
{"opus", 48000, 2, {{"minptime", "10"}, {"useinbandfec", "1"}}}));
EXPECT_EQ(nullptr, factory->MakeAudioEncoder(17, {"bar", 16000, 1}));
auto enc = factory->MakeAudioEncoder(17, {"opus", 48000, 2});
ASSERT_NE(nullptr, enc);
EXPECT_EQ(48000, enc->SampleRateHz());
}
} // namespace webrtc } // namespace webrtc

View File

@ -829,6 +829,7 @@ rtc_static_library("webrtc_opus") {
":audio_network_adaptor", ":audio_network_adaptor",
"../..:webrtc_common", "../..:webrtc_common",
"../../api/audio_codecs:audio_codecs_api", "../../api/audio_codecs:audio_codecs_api",
"../../api/audio_codecs/opus:audio_encoder_opus_config",
"../../base:protobuf_utils", "../../base:protobuf_utils",
"../../base:rtc_base_approved", "../../base:rtc_base_approved",
"../../base:rtc_numerics", "../../base:rtc_numerics",
@ -840,11 +841,6 @@ rtc_static_library("webrtc_opus") {
] ]
defines = audio_codec_defines defines = audio_codec_defines
if (rtc_opus_variable_complexity) {
defines += [ "WEBRTC_OPUS_VARIABLE_COMPLEXITY=1" ]
} else {
defines += [ "WEBRTC_OPUS_VARIABLE_COMPLEXITY=0" ]
}
if (rtc_build_opus) { if (rtc_build_opus) {
public_deps += [ rtc_opus_dir ] public_deps += [ rtc_opus_dir ]
@ -1485,6 +1481,7 @@ if (rtc_include_tests) {
":neteq", ":neteq",
":neteq_tools", ":neteq_tools",
"../../api/audio_codecs:audio_codecs_api", "../../api/audio_codecs:audio_codecs_api",
"../../api/audio_codecs/opus:audio_encoder_opus",
"../../base:protobuf_utils", "../../base:protobuf_utils",
"../../common_audio", "../../common_audio",
"../../test:test_main", "../../test:test_main",

View File

@ -19,6 +19,7 @@
#include "webrtc/base/logging.h" #include "webrtc/base/logging.h"
#include "webrtc/base/numerics/exp_filter.h" #include "webrtc/base/numerics/exp_filter.h"
#include "webrtc/base/protobuf_utils.h" #include "webrtc/base/protobuf_utils.h"
#include "webrtc/base/ptr_util.h"
#include "webrtc/base/safe_conversions.h" #include "webrtc/base/safe_conversions.h"
#include "webrtc/base/safe_minmax.h" #include "webrtc/base/safe_minmax.h"
#include "webrtc/base/string_to_number.h" #include "webrtc/base/string_to_number.h"
@ -48,11 +49,6 @@ constexpr int kOpusBitrateNbBps = 12000;
constexpr int kOpusBitrateWbBps = 20000; constexpr int kOpusBitrateWbBps = 20000;
constexpr int kOpusBitrateFbBps = 32000; 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 kSampleRateHz = 48000;
constexpr int kDefaultMaxPlaybackRate = 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); return kOpusBitrateFbBps * rtc::dchecked_cast<int>(num_channels);
} }
}(); }();
RTC_DCHECK_GE(bitrate, kOpusMinBitrateBps); RTC_DCHECK_GE(bitrate, AudioEncoderOpusConfig::kMinBitrateBps);
RTC_DCHECK_LE(bitrate, kOpusMaxBitrateBps); RTC_DCHECK_LE(bitrate, AudioEncoderOpusConfig::kMaxBitrateBps);
return bitrate; return bitrate;
} }
@ -150,7 +146,8 @@ int CalculateBitrate(int max_playback_rate_hz,
const auto bitrate = rtc::StringToNumber<int>(*bitrate_param); const auto bitrate = rtc::StringToNumber<int>(*bitrate_param);
if (bitrate) { if (bitrate) {
const int chosen_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) { if (bitrate != chosen_bitrate) {
LOG(LS_WARNING) << "Invalid maxaveragebitrate " << *bitrate LOG(LS_WARNING) << "Invalid maxaveragebitrate " << *bitrate
<< " clamped to " << chosen_bitrate; << " clamped to " << chosen_bitrate;
@ -195,7 +192,7 @@ int GetFrameSizeMs(const SdpAudioFormat& format) {
return *(std::end(kOpusSupportedFrameLengths) - 1); return *(std::end(kOpusSupportedFrameLengths) - 1);
} }
return AudioEncoderOpus::Config::kDefaultFrameSizeMs; return AudioEncoderOpusConfig::kDefaultFrameSizeMs;
} }
void FindSupportedFrameLengths(int min_frame_length_ms, 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())); RTC_DCHECK(std::is_sorted(out->begin(), out->end()));
} }
int GetBitrateBps(const AudioEncoderOpusConfig& config) {
RTC_DCHECK(config.IsOk());
return *config.bitrate_bps;
}
} // namespace } // 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( rtc::Optional<AudioCodecInfo> AudioEncoderOpus::QueryAudioEncoder(
const SdpAudioFormat& format) { const SdpAudioFormat& format) {
if (STR_CASE_CMP(format.name.c_str(), GetPayloadName()) == 0 && if (STR_CASE_CMP(format.name.c_str(), GetPayloadName()) == 0 &&
@ -221,8 +249,9 @@ rtc::Optional<AudioCodecInfo> AudioEncoderOpus::QueryAudioEncoder(
const int bitrate = const int bitrate =
CalculateBitrate(GetMaxPlaybackRate(format), num_channels, CalculateBitrate(GetMaxPlaybackRate(format), num_channels,
GetFormatParameter(format, "maxaveragebitrate")); GetFormatParameter(format, "maxaveragebitrate"));
AudioCodecInfo info(48000, num_channels, bitrate, kOpusMinBitrateBps, AudioCodecInfo info(48000, num_channels, bitrate,
kOpusMaxBitrateBps); AudioEncoderOpusConfig::kMinBitrateBps,
AudioEncoderOpusConfig::kMaxBitrateBps);
info.allow_comfort_noise = false; info.allow_comfort_noise = false;
info.supports_network_adaption = true; info.supports_network_adaption = true;
@ -231,27 +260,36 @@ rtc::Optional<AudioCodecInfo> AudioEncoderOpus::QueryAudioEncoder(
return rtc::Optional<AudioCodecInfo>(); 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) { const CodecInst& codec_inst) {
AudioEncoderOpus::Config config; AudioEncoderOpusConfig config;
config.frame_size_ms = rtc::CheckedDivExact(codec_inst.pacsize, 48); config.frame_size_ms = rtc::CheckedDivExact(codec_inst.pacsize, 48);
config.num_channels = codec_inst.channels; config.num_channels = codec_inst.channels;
config.bitrate_bps = rtc::Optional<int>(codec_inst.rate); config.bitrate_bps = rtc::Optional<int>(codec_inst.rate);
config.payload_type = codec_inst.pltype; config.application = config.num_channels == 1
config.application = config.num_channels == 1 ? AudioEncoderOpus::kVoip ? AudioEncoderOpusConfig::ApplicationMode::kVoip
: AudioEncoderOpus::kAudio; : AudioEncoderOpusConfig::ApplicationMode::kAudio;
config.supported_frame_lengths_ms.push_back(config.frame_size_ms); config.supported_frame_lengths_ms.push_back(config.frame_size_ms);
#if WEBRTC_OPUS_VARIABLE_COMPLEXITY
config.low_rate_complexity = 9;
#endif
return config; return config;
} }
AudioEncoderOpus::Config AudioEncoderOpus::CreateConfig( rtc::Optional<AudioEncoderOpusConfig> AudioEncoderOpus::SdpToConfig(
int payload_type,
const SdpAudioFormat& format) { 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.num_channels = GetChannelCount(format);
config.frame_size_ms = GetFrameSizeMs(format); config.frame_size_ms = GetFrameSizeMs(format);
config.max_playback_rate_hz = GetMaxPlaybackRate(format); config.max_playback_rate_hz = GetMaxPlaybackRate(format);
@ -261,16 +299,14 @@ AudioEncoderOpus::Config AudioEncoderOpus::CreateConfig(
config.bitrate_bps = rtc::Optional<int>( config.bitrate_bps = rtc::Optional<int>(
CalculateBitrate(config.max_playback_rate_hz, config.num_channels, CalculateBitrate(config.max_playback_rate_hz, config.num_channels,
GetFormatParameter(format, "maxaveragebitrate"))); GetFormatParameter(format, "maxaveragebitrate")));
config.payload_type = payload_type; config.application = config.num_channels == 1
config.application = config.num_channels == 1 ? AudioEncoderOpus::kVoip ? AudioEncoderOpusConfig::ApplicationMode::kVoip
: AudioEncoderOpus::kAudio; : AudioEncoderOpusConfig::ApplicationMode::kAudio;
#if WEBRTC_OPUS_VARIABLE_COMPLEXITY
config.low_rate_complexity = 9;
#endif
constexpr int kMinANAFrameLength = kANASupportedFrameLengths[0]; constexpr int kMinANAFrameLength = kANASupportedFrameLengths[0];
constexpr int kMaxANAFrameLength = constexpr int kMaxANAFrameLength =
kANASupportedFrameLengths[arraysize(kANASupportedFrameLengths) - 1]; kANASupportedFrameLengths[arraysize(kANASupportedFrameLengths) - 1];
// For now, minptime and maxptime are only used with ANA. If ptime is outside // 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 // 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. // 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, FindSupportedFrameLengths(min_frame_length_ms, max_frame_length_ms,
&config.supported_frame_lengths_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 { class AudioEncoderOpus::PacketLossFractionSmoother {
@ -311,58 +365,16 @@ class AudioEncoderOpus::PacketLossFractionSmoother {
rtc::ExpFilter smoother_; rtc::ExpFilter smoother_;
}; };
AudioEncoderOpus::Config::Config() { AudioEncoderOpus::AudioEncoderOpus(const AudioEncoderOpusConfig& config)
#if WEBRTC_OPUS_VARIABLE_COMPLEXITY : AudioEncoderOpus(config, config.payload_type) {}
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( AudioEncoderOpus::AudioEncoderOpus(
const Config& config, const AudioEncoderOpusConfig& config,
int payload_type,
AudioNetworkAdaptorCreator&& audio_network_adaptor_creator, AudioNetworkAdaptorCreator&& audio_network_adaptor_creator,
std::unique_ptr<SmoothingFilter> bitrate_smoother) 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")), "WebRTC-SendSideBwe-WithOverhead")),
packet_loss_rate_(0.0), packet_loss_rate_(0.0),
inst_(nullptr), inst_(nullptr),
@ -379,15 +391,21 @@ AudioEncoderOpus::AudioEncoderOpus(
? std::move(bitrate_smoother) : std::unique_ptr<SmoothingFilter>( ? std::move(bitrate_smoother) : std::unique_ptr<SmoothingFilter>(
// We choose 5sec as initial time constant due to empirical data. // We choose 5sec as initial time constant due to empirical data.
new SmoothingFilterImpl(5000))) { 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)); RTC_CHECK(RecreateEncoderInstance(config));
} }
AudioEncoderOpus::AudioEncoderOpus(const CodecInst& codec_inst) AudioEncoderOpus::AudioEncoderOpus(const CodecInst& codec_inst)
: AudioEncoderOpus(CreateConfig(codec_inst), nullptr) {} : AudioEncoderOpus(CreateConfig(codec_inst), codec_inst.pltype) {}
AudioEncoderOpus::AudioEncoderOpus(int payload_type, AudioEncoderOpus::AudioEncoderOpus(int payload_type,
const SdpAudioFormat& format) const SdpAudioFormat& format)
: AudioEncoderOpus(CreateConfig(payload_type, format), nullptr) {} : AudioEncoderOpus(*SdpToConfig(format), payload_type) {}
AudioEncoderOpus::~AudioEncoderOpus() { AudioEncoderOpus::~AudioEncoderOpus() {
RTC_CHECK_EQ(0, WebRtcOpus_EncoderFree(inst_)); RTC_CHECK_EQ(0, WebRtcOpus_EncoderFree(inst_));
@ -410,7 +428,7 @@ size_t AudioEncoderOpus::Max10MsFramesInAPacket() const {
} }
int AudioEncoderOpus::GetTargetBitrate() const { int AudioEncoderOpus::GetTargetBitrate() const {
return config_.GetBitrateBps(); return GetBitrateBps(config_);
} }
void AudioEncoderOpus::Reset() { void AudioEncoderOpus::Reset() {
@ -445,10 +463,10 @@ bool AudioEncoderOpus::SetApplication(Application application) {
auto conf = config_; auto conf = config_;
switch (application) { switch (application) {
case Application::kSpeech: case Application::kSpeech:
conf.application = AudioEncoderOpus::kVoip; conf.application = AudioEncoderOpusConfig::ApplicationMode::kVoip;
break; break;
case Application::kAudio: case Application::kAudio:
conf.application = AudioEncoderOpus::kAudio; conf.application = AudioEncoderOpusConfig::ApplicationMode::kAudio;
break; break;
} }
return RecreateEncoderInstance(conf); return RecreateEncoderInstance(conf);
@ -523,9 +541,10 @@ void AudioEncoderOpus::OnReceivedUplinkBandwidth(
} }
const int overhead_bps = static_cast<int>( const int overhead_bps = static_cast<int>(
*overhead_bytes_per_packet_ * 8 * 100 / Num10MsFramesInNextPacket()); *overhead_bytes_per_packet_ * 8 * 100 / Num10MsFramesInNextPacket());
SetTargetBitrate(std::min( SetTargetBitrate(
kOpusMaxBitrateBps, std::min(AudioEncoderOpusConfig::kMaxBitrateBps,
std::max(kOpusMinBitrateBps, target_audio_bitrate_bps - overhead_bps))); std::max(AudioEncoderOpusConfig::kMinBitrateBps,
target_audio_bitrate_bps - overhead_bps)));
} else { } else {
SetTargetBitrate(target_audio_bitrate_bps); SetTargetBitrate(target_audio_bitrate_bps);
} }
@ -597,7 +616,7 @@ AudioEncoder::EncodedInfo AudioEncoderOpus::EncodeImpl(
config_.frame_size_ms = next_frame_length_ms_; config_.frame_size_ms = next_frame_length_ms_;
info.encoded_timestamp = first_timestamp_in_buffer_; 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.send_even_if_empty = true; // Allows Opus to send empty packets.
info.speech = (info.encoded_bytes > 0); info.speech = (info.encoded_bytes > 0);
info.encoder_type = CodecType::kOpus; 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, // Calculate the number of bytes we expect the encoder to produce,
// then multiply by two to give a wide margin for error. // then multiply by two to give a wide margin for error.
const size_t bytes_per_millisecond = 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 = const size_t approx_encoded_bytes =
Num10msFramesPerPacket() * 10 * bytes_per_millisecond; Num10msFramesPerPacket() * 10 * bytes_per_millisecond;
return 2 * approx_encoded_bytes; 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 // 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 // settings, save the config, and return true. Otherwise, do nothing and return
// false. // false.
bool AudioEncoderOpus::RecreateEncoderInstance(const Config& config) { bool AudioEncoderOpus::RecreateEncoderInstance(
const AudioEncoderOpusConfig& config) {
if (!config.IsOk()) if (!config.IsOk())
return false; return false;
config_ = config; config_ = config;
@ -633,9 +653,13 @@ bool AudioEncoderOpus::RecreateEncoderInstance(const Config& config) {
RTC_CHECK_EQ(0, WebRtcOpus_EncoderFree(inst_)); RTC_CHECK_EQ(0, WebRtcOpus_EncoderFree(inst_));
input_buffer_.clear(); input_buffer_.clear();
input_buffer_.reserve(Num10msFramesPerPacket() * SamplesPer10msFrame()); input_buffer_.reserve(Num10msFramesPerPacket() * SamplesPer10msFrame());
RTC_CHECK_EQ(0, WebRtcOpus_EncoderCreate(&inst_, config.num_channels, RTC_CHECK_EQ(0, WebRtcOpus_EncoderCreate(
config.application)); &inst_, config.num_channels,
RTC_CHECK_EQ(0, WebRtcOpus_SetBitRate(inst_, config.GetBitrateBps())); config.application ==
AudioEncoderOpusConfig::ApplicationMode::kVoip
? 0
: 1));
RTC_CHECK_EQ(0, WebRtcOpus_SetBitRate(inst_, GetBitrateBps(config)));
if (config.fec_enabled) { if (config.fec_enabled) {
RTC_CHECK_EQ(0, WebRtcOpus_EnableFec(inst_)); RTC_CHECK_EQ(0, WebRtcOpus_EnableFec(inst_));
} else { } else {
@ -645,7 +669,7 @@ bool AudioEncoderOpus::RecreateEncoderInstance(const Config& config) {
0, WebRtcOpus_SetMaxPlaybackRate(inst_, config.max_playback_rate_hz)); 0, WebRtcOpus_SetMaxPlaybackRate(inst_, config.max_playback_rate_hz));
// Use the default complexity if the start bitrate is within the hysteresis // Use the default complexity if the start bitrate is within the hysteresis
// window. // window.
complexity_ = config.GetNewComplexity().value_or(config.complexity); complexity_ = GetNewComplexity(config).value_or(config.complexity);
RTC_CHECK_EQ(0, WebRtcOpus_SetComplexity(inst_, complexity_)); RTC_CHECK_EQ(0, WebRtcOpus_SetComplexity(inst_, complexity_));
if (config.dtx_enabled) { if (config.dtx_enabled) {
RTC_CHECK_EQ(0, WebRtcOpus_EnableDtx(inst_)); RTC_CHECK_EQ(0, WebRtcOpus_EnableDtx(inst_));
@ -692,10 +716,11 @@ void AudioEncoderOpus::SetProjectedPacketLossRate(float fraction) {
void AudioEncoderOpus::SetTargetBitrate(int bits_per_second) { void AudioEncoderOpus::SetTargetBitrate(int bits_per_second) {
config_.bitrate_bps = rtc::Optional<int>(rtc::SafeClamp<int>( 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_DCHECK(config_.IsOk());
RTC_CHECK_EQ(0, WebRtcOpus_SetBitRate(inst_, config_.GetBitrateBps())); RTC_CHECK_EQ(0, WebRtcOpus_SetBitRate(inst_, GetBitrateBps(config_)));
const auto new_complexity = config_.GetNewComplexity(); const auto new_complexity = GetNewComplexity(config_);
if (new_complexity && complexity_ != *new_complexity) { if (new_complexity && complexity_ != *new_complexity) {
complexity_ = *new_complexity; complexity_ = *new_complexity;
RTC_CHECK_EQ(0, WebRtcOpus_SetComplexity(inst_, complexity_)); RTC_CHECK_EQ(0, WebRtcOpus_SetComplexity(inst_, complexity_));
@ -728,11 +753,11 @@ AudioEncoderOpus::DefaultAudioNetworkAdaptorCreator(
AudioNetworkAdaptorImpl::Config config; AudioNetworkAdaptorImpl::Config config;
config.event_log = event_log; config.event_log = event_log;
return std::unique_ptr<AudioNetworkAdaptor>(new AudioNetworkAdaptorImpl( return std::unique_ptr<AudioNetworkAdaptor>(new AudioNetworkAdaptorImpl(
config, config, ControllerManagerImpl::Create(
ControllerManagerImpl::Create( config_string, NumChannels(), supported_frame_lengths_ms(),
config_string, NumChannels(), supported_frame_lengths_ms(), AudioEncoderOpusConfig::kMinBitrateBps,
kOpusMinBitrateBps, num_channels_to_encode_, next_frame_length_ms_, num_channels_to_encode_, next_frame_length_ms_,
GetTargetBitrate(), config_.fec_enabled, GetDtx()))); GetTargetBitrate(), config_.fec_enabled, GetDtx())));
} }
void AudioEncoderOpus::MaybeUpdateUplinkBandwidth() { void AudioEncoderOpus::MaybeUpdateUplinkBandwidth() {

View File

@ -18,6 +18,7 @@
#include "webrtc/api/audio_codecs/audio_encoder.h" #include "webrtc/api/audio_codecs/audio_encoder.h"
#include "webrtc/api/audio_codecs/audio_format.h" #include "webrtc/api/audio_codecs/audio_format.h"
#include "webrtc/api/audio_codecs/opus/audio_encoder_opus_config.h"
#include "webrtc/base/constructormagic.h" #include "webrtc/base/constructormagic.h"
#include "webrtc/base/optional.h" #include "webrtc/base/optional.h"
#include "webrtc/base/protobuf_utils.h" #include "webrtc/base/protobuf_utils.h"
@ -33,62 +34,42 @@ struct CodecInst;
class AudioEncoderOpus final : public AudioEncoder { class AudioEncoderOpus final : public AudioEncoder {
public: public:
enum ApplicationMode { static void AppendSupportedEncoders(std::vector<AudioCodecSpec>* specs);
kVoip = 0, static AudioCodecInfo QueryAudioEncoder(const AudioEncoderOpusConfig& config);
kAudio = 1, static std::unique_ptr<AudioEncoder> MakeAudioEncoder(
}; const AudioEncoderOpusConfig&,
int payload_type);
struct Config { // NOTE: This alias will soon go away. See
Config(); // https://bugs.chromium.org/p/webrtc/issues/detail?id=7847
Config(const Config&); using Config = AudioEncoderOpusConfig;
~Config();
Config& operator=(const Config&);
bool IsOk() const;
int GetBitrateBps() const;
// Returns empty if the current bitrate falls within the hysteresis window,
// defined by complexity_threshold_bps +/- complexity_threshold_window_bps.
// Otherwise, returns the current complexity depending on whether the
// current bitrate is above or below complexity_threshold_bps.
rtc::Optional<int> GetNewComplexity() const;
static constexpr int kDefaultFrameSizeMs = 20;
int frame_size_ms = kDefaultFrameSizeMs;
size_t num_channels = 1;
int payload_type = 120;
ApplicationMode application = kVoip;
rtc::Optional<int> bitrate_bps; // Unset means to use default value.
bool fec_enabled = false;
bool cbr_enabled = false;
int max_playback_rate_hz = 48000;
int complexity = kDefaultComplexity;
// This value may change in the struct's constructor.
int low_rate_complexity = kDefaultComplexity;
// low_rate_complexity is used when the bitrate is below this threshold.
int complexity_threshold_bps = 12500;
int complexity_threshold_window_bps = 1500;
bool dtx_enabled = false;
std::vector<int> supported_frame_lengths_ms;
int uplink_bandwidth_update_interval_ms = 200;
private:
#if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS) || defined(WEBRTC_ARCH_ARM)
// If we are on Android, iOS and/or ARM, use a lower complexity setting as
// default, to save encoder complexity.
static const int kDefaultComplexity = 5;
#else
static const int kDefaultComplexity = 9;
#endif
};
// NOTE: This function will soon go away. See
// https://bugs.chromium.org/p/webrtc/issues/detail?id=7847
static Config CreateConfig(int payload_type, const SdpAudioFormat& format); static Config CreateConfig(int payload_type, const SdpAudioFormat& format);
static Config CreateConfig(const CodecInst& codec_inst);
static AudioEncoderOpusConfig CreateConfig(const CodecInst& codec_inst);
static rtc::Optional<AudioEncoderOpusConfig> SdpToConfig(
const SdpAudioFormat& format);
// Returns empty if the current bitrate falls within the hysteresis window,
// defined by complexity_threshold_bps +/- complexity_threshold_window_bps.
// Otherwise, returns the current complexity depending on whether the
// current bitrate is above or below complexity_threshold_bps.
static rtc::Optional<int> GetNewComplexity(
const AudioEncoderOpusConfig& config);
using AudioNetworkAdaptorCreator = using AudioNetworkAdaptorCreator =
std::function<std::unique_ptr<AudioNetworkAdaptor>(const std::string&, std::function<std::unique_ptr<AudioNetworkAdaptor>(const std::string&,
RtcEventLog*)>; RtcEventLog*)>;
// NOTE: This constructor will soon go away. See
// https://bugs.chromium.org/p/webrtc/issues/detail?id=7847
AudioEncoderOpus(const AudioEncoderOpusConfig& config);
AudioEncoderOpus( AudioEncoderOpus(
const Config& config, const AudioEncoderOpusConfig& config,
int payload_type,
AudioNetworkAdaptorCreator&& audio_network_adaptor_creator = nullptr, AudioNetworkAdaptorCreator&& audio_network_adaptor_creator = nullptr,
std::unique_ptr<SmoothingFilter> bitrate_smoother = nullptr); std::unique_ptr<SmoothingFilter> bitrate_smoother = nullptr);
@ -110,9 +91,9 @@ class AudioEncoderOpus final : public AudioEncoder {
void Reset() override; void Reset() override;
bool SetFec(bool enable) override; bool SetFec(bool enable) override;
// Set Opus DTX. Once enabled, Opus stops transmission, when it detects voice // Set Opus DTX. Once enabled, Opus stops transmission, when it detects
// being inactive. During that, it still sends 2 packets (one for content, one // voice being inactive. During that, it still sends 2 packets (one for
// for signaling) about every 400 ms. // content, one for signaling) about every 400 ms.
bool SetDtx(bool enable) override; bool SetDtx(bool enable) override;
bool GetDtx() const override; bool GetDtx() const override;
@ -138,7 +119,9 @@ class AudioEncoderOpus final : public AudioEncoder {
// Getters for testing. // Getters for testing.
float packet_loss_rate() const { return packet_loss_rate_; } float packet_loss_rate() const { return packet_loss_rate_; }
ApplicationMode application() const { return config_.application; } AudioEncoderOpusConfig::ApplicationMode application() const {
return config_.application;
}
bool fec_enabled() const { return config_.fec_enabled; } bool fec_enabled() const { return config_.fec_enabled; }
size_t num_channels_to_encode() const { return num_channels_to_encode_; } size_t num_channels_to_encode() const { return num_channels_to_encode_; }
int next_frame_length_ms() const { return next_frame_length_ms_; } int next_frame_length_ms() const { return next_frame_length_ms_; }
@ -154,7 +137,7 @@ class AudioEncoderOpus final : public AudioEncoder {
size_t Num10msFramesPerPacket() const; size_t Num10msFramesPerPacket() const;
size_t SamplesPer10msFrame() const; size_t SamplesPer10msFrame() const;
size_t SufficientOutputBufferSize() const; size_t SufficientOutputBufferSize() const;
bool RecreateEncoderInstance(const Config& config); bool RecreateEncoderInstance(const AudioEncoderOpusConfig& config);
void SetFrameLength(int frame_length_ms); void SetFrameLength(int frame_length_ms);
void SetNumChannelsToEncode(size_t num_channels_to_encode); void SetNumChannelsToEncode(size_t num_channels_to_encode);
void SetProjectedPacketLossRate(float fraction); void SetProjectedPacketLossRate(float fraction);
@ -170,7 +153,8 @@ class AudioEncoderOpus final : public AudioEncoder {
void MaybeUpdateUplinkBandwidth(); void MaybeUpdateUplinkBandwidth();
Config config_; AudioEncoderOpusConfig config_;
const int payload_type_;
const bool send_side_bwe_with_overhead_; const bool send_side_bwe_with_overhead_;
float packet_loss_rate_; float packet_loss_rate_;
std::vector<int16_t> input_buffer_; std::vector<int16_t> input_buffer_;

View File

@ -33,22 +33,22 @@ namespace {
const CodecInst kDefaultOpusSettings = {105, "opus", 48000, 960, 1, 32000}; const CodecInst kDefaultOpusSettings = {105, "opus", 48000, 960, 1, 32000};
constexpr int64_t kInitialTimeUs = 12345678; constexpr int64_t kInitialTimeUs = 12345678;
AudioEncoderOpus::Config CreateConfig(const CodecInst& codec_inst) { AudioEncoderOpusConfig CreateConfig(const CodecInst& codec_inst) {
AudioEncoderOpus::Config config; AudioEncoderOpusConfig config;
config.frame_size_ms = rtc::CheckedDivExact(codec_inst.pacsize, 48); config.frame_size_ms = rtc::CheckedDivExact(codec_inst.pacsize, 48);
config.num_channels = codec_inst.channels; config.num_channels = codec_inst.channels;
config.bitrate_bps = rtc::Optional<int>(codec_inst.rate); config.bitrate_bps = rtc::Optional<int>(codec_inst.rate);
config.payload_type = codec_inst.pltype; config.application = config.num_channels == 1
config.application = config.num_channels == 1 ? AudioEncoderOpus::kVoip ? AudioEncoderOpusConfig::ApplicationMode::kVoip
: AudioEncoderOpus::kAudio; : AudioEncoderOpusConfig::ApplicationMode::kAudio;
config.supported_frame_lengths_ms.push_back(config.frame_size_ms); config.supported_frame_lengths_ms.push_back(config.frame_size_ms);
return config; return config;
} }
AudioEncoderOpus::Config CreateConfigWithParameters( AudioEncoderOpusConfig CreateConfigWithParameters(
const SdpAudioFormat::Parameters& params) { const SdpAudioFormat::Parameters& params) {
SdpAudioFormat format("opus", 48000, 2, params); const SdpAudioFormat format("opus", 48000, 2, params);
return AudioEncoderOpus::CreateConfig(0, format); return *AudioEncoderOpus::SdpToConfig(format);
} }
struct AudioEncoderOpusStates { struct AudioEncoderOpusStates {
@ -56,7 +56,7 @@ struct AudioEncoderOpusStates {
MockSmoothingFilter* mock_bitrate_smoother; MockSmoothingFilter* mock_bitrate_smoother;
std::unique_ptr<AudioEncoderOpus> encoder; std::unique_ptr<AudioEncoderOpus> encoder;
std::unique_ptr<rtc::ScopedFakeClock> fake_clock; std::unique_ptr<rtc::ScopedFakeClock> fake_clock;
AudioEncoderOpus::Config config; AudioEncoderOpusConfig config;
}; };
AudioEncoderOpusStates CreateCodec(size_t num_channels) { AudioEncoderOpusStates CreateCodec(size_t num_channels) {
@ -87,7 +87,8 @@ AudioEncoderOpusStates CreateCodec(size_t num_channels) {
new MockSmoothingFilter()); new MockSmoothingFilter());
states.mock_bitrate_smoother = bitrate_smoother.get(); states.mock_bitrate_smoother = bitrate_smoother.get();
states.encoder.reset(new AudioEncoderOpus(states.config, std::move(creator), states.encoder.reset(new AudioEncoderOpus(states.config, codec_inst.pltype,
std::move(creator),
std::move(bitrate_smoother))); std::move(bitrate_smoother)));
return states; return states;
} }
@ -142,19 +143,22 @@ std::unique_ptr<test::AudioLoop> Create10msAudioBlocks(
TEST(AudioEncoderOpusTest, DefaultApplicationModeMono) { TEST(AudioEncoderOpusTest, DefaultApplicationModeMono) {
auto states = CreateCodec(1); auto states = CreateCodec(1);
EXPECT_EQ(AudioEncoderOpus::kVoip, states.encoder->application()); EXPECT_EQ(AudioEncoderOpusConfig::ApplicationMode::kVoip,
states.encoder->application());
} }
TEST(AudioEncoderOpusTest, DefaultApplicationModeStereo) { TEST(AudioEncoderOpusTest, DefaultApplicationModeStereo) {
auto states = CreateCodec(2); auto states = CreateCodec(2);
EXPECT_EQ(AudioEncoderOpus::kAudio, states.encoder->application()); EXPECT_EQ(AudioEncoderOpusConfig::ApplicationMode::kAudio,
states.encoder->application());
} }
TEST(AudioEncoderOpusTest, ChangeApplicationMode) { TEST(AudioEncoderOpusTest, ChangeApplicationMode) {
auto states = CreateCodec(2); auto states = CreateCodec(2);
EXPECT_TRUE( EXPECT_TRUE(
states.encoder->SetApplication(AudioEncoder::Application::kSpeech)); states.encoder->SetApplication(AudioEncoder::Application::kSpeech));
EXPECT_EQ(AudioEncoderOpus::kVoip, states.encoder->application()); EXPECT_EQ(AudioEncoderOpusConfig::ApplicationMode::kVoip,
states.encoder->application());
} }
TEST(AudioEncoderOpusTest, ResetWontChangeApplicationMode) { TEST(AudioEncoderOpusTest, ResetWontChangeApplicationMode) {
@ -163,17 +167,20 @@ TEST(AudioEncoderOpusTest, ResetWontChangeApplicationMode) {
// Trigger a reset. // Trigger a reset.
states.encoder->Reset(); states.encoder->Reset();
// Verify that the mode is still kAudio. // Verify that the mode is still kAudio.
EXPECT_EQ(AudioEncoderOpus::kAudio, states.encoder->application()); EXPECT_EQ(AudioEncoderOpusConfig::ApplicationMode::kAudio,
states.encoder->application());
// Now change to kVoip. // Now change to kVoip.
EXPECT_TRUE( EXPECT_TRUE(
states.encoder->SetApplication(AudioEncoder::Application::kSpeech)); states.encoder->SetApplication(AudioEncoder::Application::kSpeech));
EXPECT_EQ(AudioEncoderOpus::kVoip, states.encoder->application()); EXPECT_EQ(AudioEncoderOpusConfig::ApplicationMode::kVoip,
states.encoder->application());
// Trigger a reset again. // Trigger a reset again.
states.encoder->Reset(); states.encoder->Reset();
// Verify that the mode is still kVoip. // Verify that the mode is still kVoip.
EXPECT_EQ(AudioEncoderOpus::kVoip, states.encoder->application()); EXPECT_EQ(AudioEncoderOpusConfig::ApplicationMode::kVoip,
states.encoder->application());
} }
TEST(AudioEncoderOpusTest, ToggleDtx) { TEST(AudioEncoderOpusTest, ToggleDtx) {
@ -452,25 +459,25 @@ TEST(AudioEncoderOpusTest, BitrateBounded) {
// Verifies that the complexity adaptation in the config works as intended. // Verifies that the complexity adaptation in the config works as intended.
TEST(AudioEncoderOpusTest, ConfigComplexityAdaptation) { TEST(AudioEncoderOpusTest, ConfigComplexityAdaptation) {
AudioEncoderOpus::Config config; AudioEncoderOpusConfig config;
config.low_rate_complexity = 8; config.low_rate_complexity = 8;
config.complexity = 6; config.complexity = 6;
// Bitrate within hysteresis window. Expect empty output. // Bitrate within hysteresis window. Expect empty output.
config.bitrate_bps = rtc::Optional<int>(12500); config.bitrate_bps = rtc::Optional<int>(12500);
EXPECT_EQ(rtc::Optional<int>(), config.GetNewComplexity()); EXPECT_EQ(rtc::Optional<int>(), AudioEncoderOpus::GetNewComplexity(config));
// Bitrate below hysteresis window. Expect higher complexity. // Bitrate below hysteresis window. Expect higher complexity.
config.bitrate_bps = rtc::Optional<int>(10999); config.bitrate_bps = rtc::Optional<int>(10999);
EXPECT_EQ(rtc::Optional<int>(8), config.GetNewComplexity()); EXPECT_EQ(rtc::Optional<int>(8), AudioEncoderOpus::GetNewComplexity(config));
// Bitrate within hysteresis window. Expect empty output. // Bitrate within hysteresis window. Expect empty output.
config.bitrate_bps = rtc::Optional<int>(12500); config.bitrate_bps = rtc::Optional<int>(12500);
EXPECT_EQ(rtc::Optional<int>(), config.GetNewComplexity()); EXPECT_EQ(rtc::Optional<int>(), AudioEncoderOpus::GetNewComplexity(config));
// Bitrate above hysteresis window. Expect lower complexity. // Bitrate above hysteresis window. Expect lower complexity.
config.bitrate_bps = rtc::Optional<int>(14001); config.bitrate_bps = rtc::Optional<int>(14001);
EXPECT_EQ(rtc::Optional<int>(6), config.GetNewComplexity()); EXPECT_EQ(rtc::Optional<int>(6), AudioEncoderOpus::GetNewComplexity(config));
} }
TEST(AudioEncoderOpusTest, EmptyConfigDoesNotAffectEncoderSettings) { TEST(AudioEncoderOpusTest, EmptyConfigDoesNotAffectEncoderSettings) {
@ -552,84 +559,82 @@ TEST(AudioEncoderOpusTest, EncodeAtMinBitrate) {
} }
TEST(AudioEncoderOpusTest, TestConfigDefaults) { TEST(AudioEncoderOpusTest, TestConfigDefaults) {
const AudioEncoderOpus::Config config = const auto config_opt = AudioEncoderOpus::SdpToConfig({"opus", 48000, 2});
AudioEncoderOpus::CreateConfig(0, {"opus", 48000, 2}); ASSERT_TRUE(config_opt);
EXPECT_EQ(48000, config_opt->max_playback_rate_hz);
EXPECT_EQ(48000, config.max_playback_rate_hz); EXPECT_EQ(1u, config_opt->num_channels);
EXPECT_EQ(1u, config.num_channels); EXPECT_FALSE(config_opt->fec_enabled);
EXPECT_FALSE(config.fec_enabled); EXPECT_FALSE(config_opt->dtx_enabled);
EXPECT_FALSE(config.dtx_enabled); EXPECT_EQ(20, config_opt->frame_size_ms);
EXPECT_EQ(20, config.frame_size_ms);
} }
TEST(AudioEncoderOpusTest, TestConfigFromParams) { TEST(AudioEncoderOpusTest, TestConfigFromParams) {
AudioEncoderOpus::Config config; const auto config1 = CreateConfigWithParameters({{"stereo", "0"}});
EXPECT_EQ(1U, config1.num_channels);
config = CreateConfigWithParameters({{"stereo", "0"}}); const auto config2 = CreateConfigWithParameters({{"stereo", "1"}});
EXPECT_EQ(1U, config.num_channels); EXPECT_EQ(2U, config2.num_channels);
config = CreateConfigWithParameters({{"stereo", "1"}}); const auto config3 = CreateConfigWithParameters({{"useinbandfec", "0"}});
EXPECT_EQ(2U, config.num_channels); EXPECT_FALSE(config3.fec_enabled);
config = CreateConfigWithParameters({{"useinbandfec", "0"}}); const auto config4 = CreateConfigWithParameters({{"useinbandfec", "1"}});
EXPECT_FALSE(config.fec_enabled); EXPECT_TRUE(config4.fec_enabled);
config = CreateConfigWithParameters({{"useinbandfec", "1"}}); const auto config5 = CreateConfigWithParameters({{"usedtx", "0"}});
EXPECT_TRUE(config.fec_enabled); EXPECT_FALSE(config5.dtx_enabled);
config = CreateConfigWithParameters({{"usedtx", "0"}}); const auto config6 = CreateConfigWithParameters({{"usedtx", "1"}});
EXPECT_FALSE(config.dtx_enabled); EXPECT_TRUE(config6.dtx_enabled);
config = CreateConfigWithParameters({{"usedtx", "1"}}); const auto config7 = CreateConfigWithParameters({{"cbr", "0"}});
EXPECT_TRUE(config.dtx_enabled); EXPECT_FALSE(config7.cbr_enabled);
config = CreateConfigWithParameters({{"cbr", "0"}}); const auto config8 = CreateConfigWithParameters({{"cbr", "1"}});
EXPECT_FALSE(config.cbr_enabled); EXPECT_TRUE(config8.cbr_enabled);
config = CreateConfigWithParameters({{"cbr", "1"}}); const auto config9 =
EXPECT_TRUE(config.cbr_enabled); CreateConfigWithParameters({{"maxplaybackrate", "12345"}});
EXPECT_EQ(12345, config9.max_playback_rate_hz);
config = CreateConfigWithParameters({{"maxplaybackrate", "12345"}}); const auto config10 =
EXPECT_EQ(12345, config.max_playback_rate_hz); CreateConfigWithParameters({{"maxaveragebitrate", "96000"}});
EXPECT_EQ(96000, config10.bitrate_bps);
config = CreateConfigWithParameters({{"maxaveragebitrate", "96000"}}); const auto config11 = CreateConfigWithParameters({{"maxptime", "40"}});
EXPECT_EQ(96000, config.bitrate_bps); for (int frame_length : config11.supported_frame_lengths_ms) {
config = CreateConfigWithParameters({{"maxptime", "40"}});
for (int frame_length : config.supported_frame_lengths_ms) {
EXPECT_LE(frame_length, 40); EXPECT_LE(frame_length, 40);
} }
config = CreateConfigWithParameters({{"minptime", "40"}}); const auto config12 = CreateConfigWithParameters({{"minptime", "40"}});
for (int frame_length : config.supported_frame_lengths_ms) { for (int frame_length : config12.supported_frame_lengths_ms) {
EXPECT_GE(frame_length, 40); EXPECT_GE(frame_length, 40);
} }
config = CreateConfigWithParameters({{"ptime", "40"}}); const auto config13 = CreateConfigWithParameters({{"ptime", "40"}});
EXPECT_EQ(40, config.frame_size_ms); EXPECT_EQ(40, config13.frame_size_ms);
constexpr int kMinSupportedFrameLength = 10; constexpr int kMinSupportedFrameLength = 10;
constexpr int kMaxSupportedFrameLength = constexpr int kMaxSupportedFrameLength =
WEBRTC_OPUS_SUPPORT_120MS_PTIME ? 120 : 60; WEBRTC_OPUS_SUPPORT_120MS_PTIME ? 120 : 60;
config = CreateConfigWithParameters({{"ptime", "1"}}); const auto config14 = CreateConfigWithParameters({{"ptime", "1"}});
EXPECT_EQ(kMinSupportedFrameLength, config.frame_size_ms); EXPECT_EQ(kMinSupportedFrameLength, config14.frame_size_ms);
config = CreateConfigWithParameters({{"ptime", "2000"}}); const auto config15 = CreateConfigWithParameters({{"ptime", "2000"}});
EXPECT_EQ(kMaxSupportedFrameLength, config.frame_size_ms); EXPECT_EQ(kMaxSupportedFrameLength, config15.frame_size_ms);
} }
TEST(AudioEncoderOpusTest, TestConfigFromInvalidParams) { TEST(AudioEncoderOpusTest, TestConfigFromInvalidParams) {
const webrtc::SdpAudioFormat format("opus", 48000, 2); const webrtc::SdpAudioFormat format("opus", 48000, 2);
const AudioEncoderOpus::Config default_config = const auto default_config = *AudioEncoderOpus::SdpToConfig(format);
AudioEncoderOpus::CreateConfig(0, format);
#if WEBRTC_OPUS_SUPPORT_120MS_PTIME #if WEBRTC_OPUS_SUPPORT_120MS_PTIME
const std::vector<int> default_supported_frame_lengths_ms({20, 60, 120}); const std::vector<int> default_supported_frame_lengths_ms({20, 60, 120});
#else #else
const std::vector<int> default_supported_frame_lengths_ms({20, 60}); const std::vector<int> default_supported_frame_lengths_ms({20, 60});
#endif #endif
AudioEncoderOpus::Config config; AudioEncoderOpusConfig config;
config = CreateConfigWithParameters({{"stereo", "invalid"}}); config = CreateConfigWithParameters({{"stereo", "invalid"}});
EXPECT_EQ(default_config.num_channels, config.num_channels); EXPECT_EQ(default_config.num_channels, config.num_channels);
@ -681,18 +686,18 @@ TEST(AudioEncoderOpusTest, TestConfigFromInvalidParams) {
// range of 6000 and 510000 // range of 6000 and 510000
TEST(AudioEncoderOpusTest, SetSendCodecOpusMaxAverageBitrate) { TEST(AudioEncoderOpusTest, SetSendCodecOpusMaxAverageBitrate) {
// Ignore if less than 6000. // Ignore if less than 6000.
const AudioEncoderOpus::Config config1 = AudioEncoderOpus::CreateConfig( const auto config1 = AudioEncoderOpus::SdpToConfig(
0, {"opus", 48000, 2, {{"maxaveragebitrate", "5999"}}}); {"opus", 48000, 2, {{"maxaveragebitrate", "5999"}}});
EXPECT_EQ(6000, config1.bitrate_bps); EXPECT_EQ(6000, config1->bitrate_bps);
// Ignore if larger than 510000. // Ignore if larger than 510000.
const AudioEncoderOpus::Config config2 = AudioEncoderOpus::CreateConfig( const auto config2 = AudioEncoderOpus::SdpToConfig(
0, {"opus", 48000, 2, {{"maxaveragebitrate", "510001"}}}); {"opus", 48000, 2, {{"maxaveragebitrate", "510001"}}});
EXPECT_EQ(510000, config2.bitrate_bps); EXPECT_EQ(510000, config2->bitrate_bps);
const AudioEncoderOpus::Config config3 = AudioEncoderOpus::CreateConfig( const auto config3 = AudioEncoderOpus::SdpToConfig(
0, {"opus", 48000, 2, {{"maxaveragebitrate", "200000"}}}); {"opus", 48000, 2, {{"maxaveragebitrate", "200000"}}});
EXPECT_EQ(200000, config3.bitrate_bps); EXPECT_EQ(200000, config3->bitrate_bps);
} }
// Test maxplaybackrate <= 8000 triggers Opus narrow band mode. // Test maxplaybackrate <= 8000 triggers Opus narrow band mode.

View File

@ -19,9 +19,10 @@
namespace webrtc { namespace webrtc {
namespace { namespace {
int64_t RunComplexityTest(const AudioEncoderOpus::Config& config) { int64_t RunComplexityTest(const AudioEncoderOpusConfig& config) {
// Create encoder. // Create encoder.
AudioEncoderOpus encoder(config); constexpr int payload_type = 17;
AudioEncoderOpus encoder(config, payload_type);
// Open speech file. // Open speech file.
const std::string kInputFileName = const std::string kInputFileName =
webrtc::test::ResourcePath("audio_coding/speech_mono_32_48kHz", "pcm"); webrtc::test::ResourcePath("audio_coding/speech_mono_32_48kHz", "pcm");
@ -60,7 +61,7 @@ int64_t RunComplexityTest(const AudioEncoderOpus::Config& config) {
// the lower rate. // the lower rate.
TEST(AudioEncoderOpusComplexityAdaptationTest, AdaptationOn) { TEST(AudioEncoderOpusComplexityAdaptationTest, AdaptationOn) {
// Create config. // Create config.
AudioEncoderOpus::Config config; AudioEncoderOpusConfig config;
// The limit -- including the hysteresis window -- at which the complexity // The limit -- including the hysteresis window -- at which the complexity
// shuold be increased. // shuold be increased.
config.bitrate_bps = rtc::Optional<int>(11000 - 1); config.bitrate_bps = rtc::Optional<int>(11000 - 1);
@ -80,7 +81,7 @@ TEST(AudioEncoderOpusComplexityAdaptationTest, AdaptationOn) {
// that the resulting ratio is less than 100% at all times. // that the resulting ratio is less than 100% at all times.
TEST(AudioEncoderOpusComplexityAdaptationTest, AdaptationOff) { TEST(AudioEncoderOpusComplexityAdaptationTest, AdaptationOff) {
// Create config. // Create config.
AudioEncoderOpus::Config config; AudioEncoderOpusConfig config;
// The limit -- including the hysteresis window -- at which the complexity // The limit -- including the hysteresis window -- at which the complexity
// shuold be increased (but not in this test since complexity adaptation is // shuold be increased (but not in this test since complexity adaptation is
// disabled). // disabled).

View File

@ -17,6 +17,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "webrtc/api/audio_codecs/opus/audio_encoder_opus.h"
#include "webrtc/modules/audio_coding/codecs/g711/audio_decoder_pcm.h" #include "webrtc/modules/audio_coding/codecs/g711/audio_decoder_pcm.h"
#include "webrtc/modules/audio_coding/codecs/g711/audio_encoder_pcm.h" #include "webrtc/modules/audio_coding/codecs/g711/audio_encoder_pcm.h"
#include "webrtc/modules/audio_coding/codecs/g722/audio_decoder_g722.h" #include "webrtc/modules/audio_coding/codecs/g722/audio_decoder_g722.h"
@ -28,7 +29,6 @@
#include "webrtc/modules/audio_coding/codecs/isac/main/include/audio_decoder_isac.h" #include "webrtc/modules/audio_coding/codecs/isac/main/include/audio_decoder_isac.h"
#include "webrtc/modules/audio_coding/codecs/isac/main/include/audio_encoder_isac.h" #include "webrtc/modules/audio_coding/codecs/isac/main/include/audio_encoder_isac.h"
#include "webrtc/modules/audio_coding/codecs/opus/audio_decoder_opus.h" #include "webrtc/modules/audio_coding/codecs/opus/audio_decoder_opus.h"
#include "webrtc/modules/audio_coding/codecs/opus/audio_encoder_opus.h"
#include "webrtc/modules/audio_coding/codecs/pcm16b/audio_decoder_pcm16b.h" #include "webrtc/modules/audio_coding/codecs/pcm16b/audio_decoder_pcm16b.h"
#include "webrtc/modules/audio_coding/codecs/pcm16b/audio_encoder_pcm16b.h" #include "webrtc/modules/audio_coding/codecs/pcm16b/audio_encoder_pcm16b.h"
#include "webrtc/modules/audio_coding/neteq/tools/resample_input_audio_file.h" #include "webrtc/modules/audio_coding/neteq/tools/resample_input_audio_file.h"
@ -433,11 +433,10 @@ class AudioDecoderOpusTest : public AudioDecoderTest {
frame_size_ = 480; frame_size_ = 480;
data_length_ = 10 * frame_size_; data_length_ = 10 * frame_size_;
decoder_ = new AudioDecoderOpus(1); decoder_ = new AudioDecoderOpus(1);
AudioEncoderOpus::Config config; AudioEncoderOpusConfig config;
config.frame_size_ms = static_cast<int>(frame_size_) / 48; config.frame_size_ms = static_cast<int>(frame_size_) / 48;
config.payload_type = payload_type_; config.application = AudioEncoderOpusConfig::ApplicationMode::kVoip;
config.application = AudioEncoderOpus::kVoip; audio_encoder_ = AudioEncoderOpus::MakeAudioEncoder(config, payload_type_);
audio_encoder_.reset(new AudioEncoderOpus(config));
} }
}; };
@ -447,12 +446,11 @@ class AudioDecoderOpusStereoTest : public AudioDecoderOpusTest {
channels_ = 2; channels_ = 2;
delete decoder_; delete decoder_;
decoder_ = new AudioDecoderOpus(2); decoder_ = new AudioDecoderOpus(2);
AudioEncoderOpus::Config config; AudioEncoderOpusConfig config;
config.frame_size_ms = static_cast<int>(frame_size_) / 48; config.frame_size_ms = static_cast<int>(frame_size_) / 48;
config.num_channels = 2; config.num_channels = 2;
config.payload_type = payload_type_; config.application = AudioEncoderOpusConfig::ApplicationMode::kAudio;
config.application = AudioEncoderOpus::kAudio; audio_encoder_ = AudioEncoderOpus::MakeAudioEncoder(config, payload_type_);
audio_encoder_.reset(new AudioEncoderOpus(config));
} }
}; };