AudioCodingModule: Remove support for creating encoders

After this CL, all audio encoders have to be injected by the caller.
This means that there is no special "built-in" set of codecs, and
users won't have to pay the binary size and security costs of codecs
they aren't using.

Bug: webrtc:8396
Change-Id: Idb0959ce395940c8bb3bbb49256cdcd84fc87bb6
Reviewed-on: https://webrtc-review.googlesource.com/c/103821
Commit-Queue: Karl Wiberg <kwiberg@webrtc.org>
Reviewed-by: Ivo Creusen <ivoc@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#25600}
This commit is contained in:
Karl Wiberg
2018-11-12 14:21:58 +01:00
committed by Commit Bot
parent 80c6762a37
commit 49c33ce528
9 changed files with 75 additions and 1402 deletions

View File

@ -14,31 +14,6 @@ if (!build_with_mozilla) {
visibility = [ ":*" ]
audio_codec_deps = [
":g711",
":pcm16b",
]
if (rtc_include_ilbc) {
audio_codec_deps += [ ":ilbc" ]
}
if (rtc_include_opus) {
audio_codec_deps += [ ":webrtc_opus" ]
}
if (current_cpu == "arm") {
audio_codec_deps += [ ":isac_fix" ]
} else {
audio_codec_deps += [ ":isac" ]
}
audio_codec_deps += [ ":g722" ]
if (!build_with_mozilla && !build_with_chromium) {
audio_codec_deps += [ ":red" ]
}
audio_coding_deps = audio_codec_deps + [
"../..:webrtc_common",
"../../common_audio",
"../../system_wrappers",
]
rtc_static_library("audio_format_conversion") {
visibility += webrtc_default_visibility
sources = [
@ -63,7 +38,6 @@ rtc_static_library("rent_a_codec") {
# TODO(bugs.webrtc.org/9808): Move to private visibility as soon as that
# client code gets updated.
visibility += [ "*" ]
allow_poison = [ "audio_codecs" ]
sources = [
"acm2/acm_codec_database.cc",
@ -72,22 +46,18 @@ rtc_static_library("rent_a_codec") {
"acm2/rent_a_codec.h",
]
deps = [
"../../rtc_base:checks",
"../../api:array_view",
"//third_party/abseil-cpp/absl/strings",
"//third_party/abseil-cpp/absl/types:optional",
"../../api/audio_codecs:audio_codecs_api",
":audio_coding_module_typedefs",
":neteq_decoder_enum",
"../..:webrtc_common",
"../../api:array_view",
"../../api/audio_codecs:audio_codecs_api",
"../../rtc_base:checks",
"../../rtc_base:protobuf_utils",
"../../rtc_base:rtc_base_approved",
"../../system_wrappers",
":audio_coding_module_typedefs",
":isac_common",
":isac_fix_c",
":audio_encoder_cng",
":neteq_decoder_enum",
] + audio_codec_deps
"//third_party/abseil-cpp/absl/strings",
"//third_party/abseil-cpp/absl/types:optional",
]
defines = audio_codec_defines
}
@ -102,7 +72,6 @@ rtc_source_set("audio_coding_module_typedefs") {
rtc_static_library("audio_coding") {
visibility += [ "*" ]
allow_poison = [ "audio_codecs" ] # TODO(bugs.webrtc.org/8396): Remove.
sources = [
"acm2/acm_receiver.cc",
"acm2/acm_receiver.h",
@ -111,40 +80,34 @@ rtc_static_library("audio_coding") {
"acm2/audio_coding_module.cc",
"acm2/call_statistics.cc",
"acm2/call_statistics.h",
"acm2/codec_manager.cc",
"acm2/codec_manager.h",
"include/audio_coding_module.h",
]
defines = []
if (rtc_include_opus) {
public_deps = [
":webrtc_opus",
]
}
deps = audio_coding_deps + [
"../../system_wrappers:metrics",
"../../api/audio:audio_frame_api",
"..:module_api",
"..:module_api_public",
"../../common_audio:common_audio_c",
"../../rtc_base:deprecation",
"../../rtc_base:checks",
"../../api:array_view",
"../../api/audio_codecs:audio_codecs_api",
deps = [
":audio_coding_module_typedefs",
":neteq",
":neteq_decoder_enum",
":rent_a_codec",
"..:module_api",
"..:module_api_public",
"../..:webrtc_common",
"../../api:array_view",
"../../api/audio:audio_frame_api",
"../../api/audio_codecs:audio_codecs_api",
"../../common_audio:common_audio",
"../../common_audio:common_audio_c",
"../../logging:rtc_event_log_api",
"../../rtc_base:audio_format_to_string",
"../../rtc_base:checks",
"../../rtc_base:deprecation",
"../../rtc_base:rtc_base_approved",
"../../system_wrappers",
"../../system_wrappers:metrics",
"//third_party/abseil-cpp/absl/strings",
"//third_party/abseil-cpp/absl/types:optional",
"../../logging:rtc_event_log_api",
]
defines = audio_coding_defines
}
rtc_static_library("legacy_encoded_audio_frame") {
@ -1280,6 +1243,30 @@ if (rtc_enable_protobuf) {
}
if (rtc_include_tests) {
audio_coding_deps = [
"../../common_audio",
"../../system_wrappers",
"../..:webrtc_common",
":audio_encoder_cng",
":g711",
":g722",
":pcm16b",
]
if (rtc_include_ilbc) {
audio_coding_deps += [ ":ilbc" ]
}
if (rtc_include_opus) {
audio_coding_deps += [ ":webrtc_opus" ]
}
if (current_cpu == "arm") {
audio_coding_deps += [ ":isac_fix" ]
} else {
audio_coding_deps += [ ":isac" ]
}
if (!build_with_mozilla && !build_with_chromium) {
audio_coding_deps += [ ":red" ]
}
rtc_source_set("mocks") {
testonly = true
sources = [
@ -1368,6 +1355,7 @@ if (rtc_include_tests) {
":audio_format_conversion",
":pcm16b_c",
":red",
":webrtc_opus_c",
"..:module_api",
"../..:webrtc_common",
"../../api/audio:audio_frame_api",
@ -2007,8 +1995,6 @@ if (rtc_include_tests) {
"acm2/acm_receiver_unittest.cc",
"acm2/audio_coding_module_unittest.cc",
"acm2/call_statistics_unittest.cc",
"acm2/codec_manager_unittest.cc",
"acm2/rent_a_codec_unittest.cc",
"audio_network_adaptor/audio_network_adaptor_impl_unittest.cc",
"audio_network_adaptor/bitrate_controller_unittest.cc",
"audio_network_adaptor/channel_controller_unittest.cc",

View File

@ -18,7 +18,6 @@
#include "api/array_view.h"
#include "modules/audio_coding/acm2/acm_receiver.h"
#include "modules/audio_coding/acm2/acm_resampler.h"
#include "modules/audio_coding/acm2/codec_manager.h"
#include "modules/audio_coding/acm2/rent_a_codec.h"
#include "modules/include/module_common_types.h"
#include "modules/include/module_common_types_public.h"
@ -34,12 +33,6 @@ namespace webrtc {
namespace {
struct EncoderFactory {
AudioEncoder* external_speech_encoder = nullptr;
acm2::CodecManager codec_manager;
acm2::RentACodec rent_a_codec;
};
class AudioCodingModuleImpl final : public AudioCodingModule {
public:
explicit AudioCodingModuleImpl(const AudioCodingModule::Config& config);
@ -49,12 +42,6 @@ class AudioCodingModuleImpl final : public AudioCodingModule {
// Sender
//
// Can be called multiple times for Codec, CNG, RED.
int RegisterSendCodec(const CodecInst& send_codec) override;
void RegisterExternalSendCodec(
AudioEncoder* external_speech_encoder) override;
void ModifyEncoder(rtc::FunctionView<void(std::unique_ptr<AudioEncoder>*)>
modifier) override;
@ -73,26 +60,10 @@ class AudioCodingModuleImpl final : public AudioCodingModule {
// Add 10 ms of raw (PCM) audio data to the encoder.
int Add10MsData(const AudioFrame& audio_frame) override;
/////////////////////////////////////////
// (RED) Redundant Coding
//
// Configure RED status i.e. on/off.
int SetREDStatus(bool enable_red) override;
// Get RED status.
bool REDStatus() const override;
/////////////////////////////////////////
// (FEC) Forward Error Correction (codec internal)
//
// Configure FEC status i.e. on/off.
int SetCodecFEC(bool enabled_codec_fec) override;
// Get FEC status.
bool CodecFEC() const override;
// Set target packet loss rate
int SetPacketLossRate(int loss_rate) override;
@ -102,14 +73,6 @@ class AudioCodingModuleImpl final : public AudioCodingModule {
// (CNG) Comfort Noise Generation
//
int SetVAD(bool enable_dtx = true,
bool enable_vad = false,
ACMVADMode mode = VADNormal) override;
int VAD(bool* dtx_enabled,
bool* vad_enabled,
ACMVADMode* mode) const override;
int RegisterVADCallback(ACMVADCallback* vad_callback) override;
/////////////////////////////////////////
@ -130,11 +93,6 @@ class AudioCodingModuleImpl final : public AudioCodingModule {
bool RegisterReceiveCodec(int rtp_payload_type,
const SdpAudioFormat& audio_format) override;
int RegisterReceiveCodec(const CodecInst& receive_codec) override;
int RegisterReceiveCodec(
const CodecInst& receive_codec,
rtc::FunctionView<std::unique_ptr<AudioDecoder>()> isac_factory) override;
int RegisterExternalReceiveCodec(int rtp_payload_type,
AudioDecoder* external_decoder,
int sample_rate_hz,
@ -222,11 +180,6 @@ class AudioCodingModuleImpl final : public AudioCodingModule {
const std::string histogram_name_;
};
int RegisterReceiveCodecUnlocked(
const CodecInst& codec,
rtc::FunctionView<std::unique_ptr<AudioDecoder>()> isac_factory)
RTC_EXCLUSIVE_LOCKS_REQUIRED(acm_crit_sect_);
int Add10MsDataInternal(const AudioFrame& audio_frame, InputData* input_data)
RTC_EXCLUSIVE_LOCKS_REQUIRED(acm_crit_sect_);
int Encode(const InputData& input_data)
@ -264,12 +217,7 @@ class AudioCodingModuleImpl final : public AudioCodingModule {
acm2::AcmReceiver receiver_; // AcmReceiver has it's own internal lock.
ChangeLogger bitrate_logger_ RTC_GUARDED_BY(acm_crit_sect_);
std::unique_ptr<EncoderFactory> encoder_factory_
RTC_GUARDED_BY(acm_crit_sect_);
// Current encoder stack, either obtained from
// encoder_factory_->rent_a_codec.RentEncoderStack or provided by a call to
// RegisterEncoder.
// Current encoder stack, provided by a call to RegisterEncoder.
std::unique_ptr<AudioEncoder> encoder_stack_ RTC_GUARDED_BY(acm_crit_sect_);
std::unique_ptr<AudioDecoder> isac_decoder_16k_
@ -405,28 +353,6 @@ class RawAudioEncoderWrapper final : public AudioEncoder {
AudioEncoder* enc_;
};
// Return false on error.
bool CreateSpeechEncoderIfNecessary(EncoderFactory* ef) {
auto* sp = ef->codec_manager.GetStackParams();
if (sp->speech_encoder) {
// Do nothing; we already have a speech encoder.
} else if (ef->codec_manager.GetCodecInst()) {
RTC_DCHECK(!ef->external_speech_encoder);
// We have no speech encoder, but we have a specification for making one.
std::unique_ptr<AudioEncoder> enc =
ef->rent_a_codec.RentEncoder(*ef->codec_manager.GetCodecInst());
if (!enc)
return false; // Encoder spec was bad.
sp->speech_encoder = std::move(enc);
} else if (ef->external_speech_encoder) {
RTC_DCHECK(!ef->codec_manager.GetCodecInst());
// We have an external speech encoder.
sp->speech_encoder = std::unique_ptr<AudioEncoder>(
new RawAudioEncoderWrapper(ef->external_speech_encoder));
}
return true;
}
void AudioCodingModuleImpl::ChangeLogger::MaybeLog(int value) {
if (value != last_value_ || first_time_) {
first_time_ = false;
@ -441,7 +367,6 @@ AudioCodingModuleImpl::AudioCodingModuleImpl(
expected_in_ts_(0xD87F3F9F),
receiver_(config),
bitrate_logger_("WebRTC.Audio.TargetBitrateInKbps"),
encoder_factory_(new EncoderFactory),
encoder_stack_(nullptr),
previous_pltype_(255),
receiver_initialized_(false),
@ -549,69 +474,29 @@ int32_t AudioCodingModuleImpl::Encode(const InputData& input_data) {
// Sender
//
// Can be called multiple times for Codec, CNG, RED.
int AudioCodingModuleImpl::RegisterSendCodec(const CodecInst& send_codec) {
rtc::CritScope lock(&acm_crit_sect_);
if (!encoder_factory_->codec_manager.RegisterEncoder(send_codec)) {
return -1;
}
if (encoder_factory_->codec_manager.GetCodecInst()) {
encoder_factory_->external_speech_encoder = nullptr;
}
if (!CreateSpeechEncoderIfNecessary(encoder_factory_.get())) {
return -1;
}
auto* sp = encoder_factory_->codec_manager.GetStackParams();
if (sp->speech_encoder)
encoder_stack_ = encoder_factory_->rent_a_codec.RentEncoderStack(sp);
return 0;
}
void AudioCodingModuleImpl::RegisterExternalSendCodec(
AudioEncoder* external_speech_encoder) {
rtc::CritScope lock(&acm_crit_sect_);
encoder_factory_->codec_manager.UnsetCodecInst();
encoder_factory_->external_speech_encoder = external_speech_encoder;
RTC_CHECK(CreateSpeechEncoderIfNecessary(encoder_factory_.get()));
auto* sp = encoder_factory_->codec_manager.GetStackParams();
RTC_CHECK(sp->speech_encoder);
encoder_stack_ = encoder_factory_->rent_a_codec.RentEncoderStack(sp);
}
void AudioCodingModuleImpl::ModifyEncoder(
rtc::FunctionView<void(std::unique_ptr<AudioEncoder>*)> modifier) {
rtc::CritScope lock(&acm_crit_sect_);
// Wipe the encoder factory, so that everything that relies on it will fail.
// We don't want the complexity of supporting swapping back and forth.
if (encoder_factory_) {
encoder_factory_.reset();
RTC_CHECK(!encoder_stack_); // Ensure we hadn't started using the factory.
}
modifier(&encoder_stack_);
}
// Get current send codec.
absl::optional<CodecInst> AudioCodingModuleImpl::SendCodec() const {
rtc::CritScope lock(&acm_crit_sect_);
if (encoder_factory_) {
auto* ci = encoder_factory_->codec_manager.GetCodecInst();
if (ci) {
return *ci;
}
CreateSpeechEncoderIfNecessary(encoder_factory_.get());
const std::unique_ptr<AudioEncoder>& enc =
encoder_factory_->codec_manager.GetStackParams()->speech_encoder;
if (enc) {
return acm2::CodecManager::ForgeCodecInst(enc.get());
}
return absl::nullopt;
if (encoder_stack_) {
CodecInst ci;
ci.channels = encoder_stack_->NumChannels();
ci.plfreq = encoder_stack_->SampleRateHz();
ci.pacsize = rtc::CheckedDivExact(
static_cast<int>(encoder_stack_->Max10MsFramesInAPacket() * ci.plfreq),
100);
ci.pltype = -1; // Not valid.
ci.rate = -1; // Not valid.
static const char kName[] = "external";
memcpy(ci.plname, kName, sizeof(kName));
return ci;
} else {
return encoder_stack_
? absl::optional<CodecInst>(
acm2::CodecManager::ForgeCodecInst(encoder_stack_.get()))
: absl::nullopt;
return absl::nullopt;
}
}
@ -808,59 +693,10 @@ int AudioCodingModuleImpl::PreprocessToAddData(const AudioFrame& in_frame,
return 0;
}
/////////////////////////////////////////
// (RED) Redundant Coding
//
bool AudioCodingModuleImpl::REDStatus() const {
rtc::CritScope lock(&acm_crit_sect_);
return encoder_factory_->codec_manager.GetStackParams()->use_red;
}
// Configure RED status i.e on/off.
int AudioCodingModuleImpl::SetREDStatus(bool enable_red) {
#ifdef WEBRTC_CODEC_RED
rtc::CritScope lock(&acm_crit_sect_);
CreateSpeechEncoderIfNecessary(encoder_factory_.get());
if (!encoder_factory_->codec_manager.SetCopyRed(enable_red)) {
return -1;
}
auto* sp = encoder_factory_->codec_manager.GetStackParams();
if (sp->speech_encoder)
encoder_stack_ = encoder_factory_->rent_a_codec.RentEncoderStack(sp);
return 0;
#else
RTC_LOG(LS_WARNING) << " WEBRTC_CODEC_RED is undefined";
return -1;
#endif
}
/////////////////////////////////////////
// (FEC) Forward Error Correction (codec internal)
//
bool AudioCodingModuleImpl::CodecFEC() const {
rtc::CritScope lock(&acm_crit_sect_);
return encoder_factory_->codec_manager.GetStackParams()->use_codec_fec;
}
int AudioCodingModuleImpl::SetCodecFEC(bool enable_codec_fec) {
rtc::CritScope lock(&acm_crit_sect_);
CreateSpeechEncoderIfNecessary(encoder_factory_.get());
if (!encoder_factory_->codec_manager.SetCodecFEC(enable_codec_fec)) {
return -1;
}
auto* sp = encoder_factory_->codec_manager.GetStackParams();
if (sp->speech_encoder)
encoder_stack_ = encoder_factory_->rent_a_codec.RentEncoderStack(sp);
if (enable_codec_fec) {
return sp->use_codec_fec ? 0 : -1;
} else {
RTC_DCHECK(!sp->use_codec_fec);
return 0;
}
}
int AudioCodingModuleImpl::SetPacketLossRate(int loss_rate) {
rtc::CritScope lock(&acm_crit_sect_);
if (HaveValidEncoder("SetPacketLossRate")) {
@ -869,36 +705,6 @@ int AudioCodingModuleImpl::SetPacketLossRate(int loss_rate) {
return 0;
}
/////////////////////////////////////////
// (VAD) Voice Activity Detection
//
int AudioCodingModuleImpl::SetVAD(bool enable_dtx,
bool enable_vad,
ACMVADMode mode) {
// Note: |enable_vad| is not used; VAD is enabled based on the DTX setting.
RTC_DCHECK_EQ(enable_dtx, enable_vad);
rtc::CritScope lock(&acm_crit_sect_);
CreateSpeechEncoderIfNecessary(encoder_factory_.get());
if (!encoder_factory_->codec_manager.SetVAD(enable_dtx, mode)) {
return -1;
}
auto* sp = encoder_factory_->codec_manager.GetStackParams();
if (sp->speech_encoder)
encoder_stack_ = encoder_factory_->rent_a_codec.RentEncoderStack(sp);
return 0;
}
// Get VAD/DTX settings.
int AudioCodingModuleImpl::VAD(bool* dtx_enabled,
bool* vad_enabled,
ACMVADMode* mode) const {
rtc::CritScope lock(&acm_crit_sect_);
const auto* sp = encoder_factory_->codec_manager.GetStackParams();
*dtx_enabled = *vad_enabled = sp->use_cng;
*mode = sp->vad_mode;
return 0;
}
/////////////////////////////////////////
// Receiver
//
@ -957,59 +763,6 @@ bool AudioCodingModuleImpl::RegisterReceiveCodec(
return receiver_.AddCodec(rtp_payload_type, audio_format);
}
int AudioCodingModuleImpl::RegisterReceiveCodec(const CodecInst& codec) {
rtc::CritScope lock(&acm_crit_sect_);
auto* ef = encoder_factory_.get();
return RegisterReceiveCodecUnlocked(
codec, [&] { return ef->rent_a_codec.RentIsacDecoder(codec.plfreq); });
}
int AudioCodingModuleImpl::RegisterReceiveCodec(
const CodecInst& codec,
rtc::FunctionView<std::unique_ptr<AudioDecoder>()> isac_factory) {
rtc::CritScope lock(&acm_crit_sect_);
return RegisterReceiveCodecUnlocked(codec, isac_factory);
}
int AudioCodingModuleImpl::RegisterReceiveCodecUnlocked(
const CodecInst& codec,
rtc::FunctionView<std::unique_ptr<AudioDecoder>()> isac_factory) {
RTC_DCHECK(receiver_initialized_);
if (codec.channels > 2) {
RTC_LOG_F(LS_ERROR) << "Unsupported number of channels: " << codec.channels;
return -1;
}
auto codec_id = acm2::RentACodec::CodecIdByParams(codec.plname, codec.plfreq,
codec.channels);
if (!codec_id) {
RTC_LOG_F(LS_ERROR)
<< "Wrong codec params to be registered as receive codec";
return -1;
}
auto codec_index = acm2::RentACodec::CodecIndexFromId(*codec_id);
RTC_CHECK(codec_index) << "Invalid codec ID: " << static_cast<int>(*codec_id);
// Check if the payload-type is valid.
if (!acm2::RentACodec::IsPayloadTypeValid(codec.pltype)) {
RTC_LOG_F(LS_ERROR) << "Invalid payload type " << codec.pltype << " for "
<< codec.plname;
return -1;
}
AudioDecoder* isac_decoder = nullptr;
if (absl::EqualsIgnoreCase(codec.plname, "isac")) {
std::unique_ptr<AudioDecoder>& saved_isac_decoder =
codec.plfreq == 16000 ? isac_decoder_16k_ : isac_decoder_32k_;
if (!saved_isac_decoder) {
saved_isac_decoder = isac_factory();
}
isac_decoder = saved_isac_decoder.get();
}
return receiver_.AddCodec(*codec_index, codec.pltype, codec.channels,
codec.plfreq, isac_decoder, codec.plname);
}
int AudioCodingModuleImpl::RegisterExternalReceiveCodec(
int rtp_payload_type,
AudioDecoder* external_decoder,

View File

@ -1,253 +0,0 @@
/*
* Copyright (c) 2015 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 "modules/audio_coding/acm2/codec_manager.h"
#include <string.h>
#include <map>
#include <memory>
#include <utility>
#include "absl/strings/match.h"
#include "api/array_view.h"
#include "api/audio_codecs/audio_encoder.h"
#include "modules/audio_coding/acm2/rent_a_codec.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace acm2 {
namespace {
// Check if the given codec is a valid to be registered as send codec.
int IsValidSendCodec(const CodecInst& send_codec) {
if ((send_codec.channels != 1) && (send_codec.channels != 2)) {
RTC_LOG(LS_ERROR) << "Wrong number of channels (" << send_codec.channels
<< "), only mono and stereo are supported)";
return -1;
}
auto maybe_codec_id = RentACodec::CodecIdByInst(send_codec);
if (!maybe_codec_id) {
RTC_LOG(LS_ERROR) << "Invalid codec setting for the send codec.";
return -1;
}
// Telephone-event cannot be a send codec.
if (absl::EqualsIgnoreCase(send_codec.plname, "telephone-event")) {
RTC_LOG(LS_ERROR) << "telephone-event cannot be a send codec";
return -1;
}
if (!RentACodec::IsSupportedNumChannels(*maybe_codec_id, send_codec.channels)
.value_or(false)) {
RTC_LOG(LS_ERROR) << send_codec.channels
<< " number of channels not supported for "
<< send_codec.plname << ".";
return -1;
}
return RentACodec::CodecIndexFromId(*maybe_codec_id).value_or(-1);
}
bool IsOpus(const CodecInst& codec) {
return
#ifdef WEBRTC_CODEC_OPUS
absl::EqualsIgnoreCase(codec.plname, "opus") ||
#endif
false;
}
} // namespace
CodecManager::CodecManager() {
thread_checker_.DetachFromThread();
}
CodecManager::~CodecManager() = default;
bool CodecManager::RegisterEncoder(const CodecInst& send_codec) {
RTC_DCHECK(thread_checker_.CalledOnValidThread());
int codec_id = IsValidSendCodec(send_codec);
// Check for reported errors from function IsValidSendCodec().
if (codec_id < 0) {
return false;
}
switch (RentACodec::RegisterRedPayloadType(
&codec_stack_params_.red_payload_types, send_codec)) {
case RentACodec::RegistrationResult::kOk:
return true;
case RentACodec::RegistrationResult::kBadFreq:
RTC_LOG(LS_ERROR)
<< "RegisterSendCodec() failed, invalid frequency for RED"
" registration";
return false;
case RentACodec::RegistrationResult::kSkip:
break;
}
switch (RentACodec::RegisterCngPayloadType(
&codec_stack_params_.cng_payload_types, send_codec)) {
case RentACodec::RegistrationResult::kOk:
return true;
case RentACodec::RegistrationResult::kBadFreq:
RTC_LOG(LS_ERROR)
<< "RegisterSendCodec() failed, invalid frequency for CNG"
" registration";
return false;
case RentACodec::RegistrationResult::kSkip:
break;
}
if (IsOpus(send_codec)) {
// VAD/DTX not supported.
codec_stack_params_.use_cng = false;
}
send_codec_inst_ = send_codec;
recreate_encoder_ = true; // Caller must recreate it.
return true;
}
CodecInst CodecManager::ForgeCodecInst(
const AudioEncoder* external_speech_encoder) {
CodecInst ci;
ci.channels = external_speech_encoder->NumChannels();
ci.plfreq = external_speech_encoder->SampleRateHz();
ci.pacsize = rtc::CheckedDivExact(
static_cast<int>(external_speech_encoder->Max10MsFramesInAPacket() *
ci.plfreq),
100);
ci.pltype = -1; // Not valid.
ci.rate = -1; // Not valid.
static const char kName[] = "external";
memcpy(ci.plname, kName, sizeof(kName));
return ci;
}
bool CodecManager::SetCopyRed(bool enable) {
if (enable && codec_stack_params_.use_codec_fec) {
RTC_LOG(LS_WARNING) << "Codec internal FEC and RED cannot be co-enabled.";
return false;
}
if (enable && send_codec_inst_ &&
codec_stack_params_.red_payload_types.count(send_codec_inst_->plfreq) <
1) {
RTC_LOG(LS_WARNING) << "Cannot enable RED at " << send_codec_inst_->plfreq
<< " Hz.";
return false;
}
codec_stack_params_.use_red = enable;
return true;
}
bool CodecManager::SetVAD(bool enable, ACMVADMode mode) {
// Sanity check of the mode.
RTC_DCHECK(mode == VADNormal || mode == VADLowBitrate || mode == VADAggr ||
mode == VADVeryAggr);
// Check that the send codec is mono. We don't support VAD/DTX for stereo
// sending.
const bool stereo_send =
codec_stack_params_.speech_encoder
? (codec_stack_params_.speech_encoder->NumChannels() != 1)
: false;
if (enable && stereo_send) {
RTC_LOG(LS_ERROR) << "VAD/DTX not supported for stereo sending";
return false;
}
// TODO(kwiberg): This doesn't protect Opus when injected as an external
// encoder.
if (send_codec_inst_ && IsOpus(*send_codec_inst_)) {
// VAD/DTX not supported, but don't fail.
enable = false;
}
codec_stack_params_.use_cng = enable;
codec_stack_params_.vad_mode = mode;
return true;
}
bool CodecManager::SetCodecFEC(bool enable_codec_fec) {
if (enable_codec_fec && codec_stack_params_.use_red) {
RTC_LOG(LS_WARNING) << "Codec internal FEC and RED cannot be co-enabled.";
return false;
}
codec_stack_params_.use_codec_fec = enable_codec_fec;
return true;
}
bool CodecManager::MakeEncoder(RentACodec* rac, AudioCodingModule* acm) {
RTC_DCHECK(rac);
RTC_DCHECK(acm);
if (!recreate_encoder_) {
bool error = false;
// Try to re-use the speech encoder we've given to the ACM.
acm->ModifyEncoder([&](std::unique_ptr<AudioEncoder>* encoder) {
if (!*encoder) {
// There is no existing encoder.
recreate_encoder_ = true;
return;
}
// Extract the speech encoder from the ACM.
std::unique_ptr<AudioEncoder> enc = std::move(*encoder);
while (true) {
auto sub_enc = enc->ReclaimContainedEncoders();
if (sub_enc.empty()) {
break;
}
RTC_CHECK_EQ(1, sub_enc.size());
// Replace enc with its sub encoder. We need to put the sub encoder in
// a temporary first, since otherwise the old value of enc would be
// destroyed before the new value got assigned, which would be bad
// since the new value is a part of the old value.
auto tmp_enc = std::move(sub_enc[0]);
enc = std::move(tmp_enc);
}
// Wrap it in a new encoder stack and put it back.
codec_stack_params_.speech_encoder = std::move(enc);
*encoder = rac->RentEncoderStack(&codec_stack_params_);
if (!*encoder) {
error = true;
}
});
if (error) {
return false;
}
if (!recreate_encoder_) {
return true;
}
}
if (!send_codec_inst_) {
// We don't have the information we need to create a new speech encoder.
// (This is not an error.)
return true;
}
codec_stack_params_.speech_encoder = rac->RentEncoder(*send_codec_inst_);
auto stack = rac->RentEncoderStack(&codec_stack_params_);
if (!stack) {
return false;
}
acm->SetEncoder(std::move(stack));
recreate_encoder_ = false;
return true;
}
} // namespace acm2
} // namespace webrtc

View File

@ -1,72 +0,0 @@
/*
* Copyright (c) 2015 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 MODULES_AUDIO_CODING_ACM2_CODEC_MANAGER_H_
#define MODULES_AUDIO_CODING_ACM2_CODEC_MANAGER_H_
#include "absl/types/optional.h"
#include "common_types.h" // NOLINT(build/include)
#include "modules/audio_coding/acm2/rent_a_codec.h"
#include "modules/audio_coding/include/audio_coding_module.h"
#include "modules/audio_coding/include/audio_coding_module_typedefs.h"
#include "rtc_base/constructormagic.h"
#include "rtc_base/thread_checker.h"
namespace webrtc {
class AudioEncoder;
namespace acm2 {
class CodecManager final {
public:
CodecManager();
~CodecManager();
// Parses the given specification. On success, returns true and updates the
// stored CodecInst and stack parameters; on error, returns false.
bool RegisterEncoder(const CodecInst& send_codec);
static CodecInst ForgeCodecInst(const AudioEncoder* external_speech_encoder);
const CodecInst* GetCodecInst() const {
return send_codec_inst_ ? &*send_codec_inst_ : nullptr;
}
void UnsetCodecInst() { send_codec_inst_ = absl::nullopt; }
const RentACodec::StackParameters* GetStackParams() const {
return &codec_stack_params_;
}
RentACodec::StackParameters* GetStackParams() { return &codec_stack_params_; }
bool SetCopyRed(bool enable);
bool SetVAD(bool enable, ACMVADMode mode);
bool SetCodecFEC(bool enable_codec_fec);
// Uses the provided Rent-A-Codec to create a new encoder stack, if we have a
// complete specification; if so, it is then passed to set_encoder. On error,
// returns false.
bool MakeEncoder(RentACodec* rac, AudioCodingModule* acm);
private:
rtc::ThreadChecker thread_checker_;
absl::optional<CodecInst> send_codec_inst_;
RentACodec::StackParameters codec_stack_params_;
bool recreate_encoder_ = true; // Need to recreate encoder?
RTC_DISALLOW_COPY_AND_ASSIGN(CodecManager);
};
} // namespace acm2
} // namespace webrtc
#endif // MODULES_AUDIO_CODING_ACM2_CODEC_MANAGER_H_

View File

@ -1,70 +0,0 @@
/*
* Copyright (c) 2015 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 <memory>
#include "modules/audio_coding/acm2/codec_manager.h"
#include "modules/audio_coding/acm2/rent_a_codec.h"
#include "test/gtest.h"
#include "test/mock_audio_encoder.h"
namespace webrtc {
namespace acm2 {
using ::testing::Return;
namespace {
// Create a MockAudioEncoder with some reasonable default behavior.
std::unique_ptr<MockAudioEncoder> CreateMockEncoder() {
auto enc = std::unique_ptr<MockAudioEncoder>(new MockAudioEncoder);
EXPECT_CALL(*enc, SampleRateHz()).WillRepeatedly(Return(8000));
EXPECT_CALL(*enc, NumChannels()).WillRepeatedly(Return(1));
EXPECT_CALL(*enc, Max10MsFramesInAPacket()).WillRepeatedly(Return(1));
return enc;
}
} // namespace
TEST(CodecManagerTest, ExternalEncoderFec) {
auto enc0 = CreateMockEncoder();
auto enc1 = CreateMockEncoder();
auto enc2 = CreateMockEncoder();
{
::testing::InSequence s;
EXPECT_CALL(*enc0, SetFec(false)).WillOnce(Return(true));
EXPECT_CALL(*enc1, SetFec(true)).WillOnce(Return(true));
EXPECT_CALL(*enc2, SetFec(true)).WillOnce(Return(false));
}
CodecManager cm;
RentACodec rac;
// use_codec_fec starts out false.
EXPECT_FALSE(cm.GetStackParams()->use_codec_fec);
cm.GetStackParams()->speech_encoder = std::move(enc0);
EXPECT_TRUE(rac.RentEncoderStack(cm.GetStackParams()));
EXPECT_FALSE(cm.GetStackParams()->use_codec_fec);
// Set it to true.
EXPECT_EQ(true, cm.SetCodecFEC(true));
EXPECT_TRUE(cm.GetStackParams()->use_codec_fec);
cm.GetStackParams()->speech_encoder = std::move(enc1);
EXPECT_TRUE(rac.RentEncoderStack(cm.GetStackParams()));
EXPECT_TRUE(cm.GetStackParams()->use_codec_fec);
// Switch to a codec that doesn't support it.
cm.GetStackParams()->speech_encoder = std::move(enc2);
EXPECT_TRUE(rac.RentEncoderStack(cm.GetStackParams()));
EXPECT_FALSE(cm.GetStackParams()->use_codec_fec);
}
} // namespace acm2
} // namespace webrtc

View File

@ -13,35 +13,9 @@
#include <memory>
#include <utility>
#include "absl/strings/match.h"
#include "modules/audio_coding/codecs/cng/audio_encoder_cng.h"
#include "modules/audio_coding/codecs/g711/audio_encoder_pcm.h"
#include "modules/audio_coding/codecs/g722/audio_encoder_g722.h"
#include "rtc_base/logging.h"
#ifdef WEBRTC_CODEC_ILBC
#include "modules/audio_coding/codecs/ilbc/audio_encoder_ilbc.h" // nogncheck
#endif
#ifdef WEBRTC_CODEC_ISACFX
#include "modules/audio_coding/codecs/isac/fix/include/audio_decoder_isacfix.h" // nogncheck
#include "modules/audio_coding/codecs/isac/fix/include/audio_encoder_isacfix.h" // nogncheck
#endif
#ifdef WEBRTC_CODEC_ISAC
#include "modules/audio_coding/codecs/isac/main/include/audio_decoder_isac.h" // nogncheck
#include "modules/audio_coding/codecs/isac/main/include/audio_encoder_isac.h" // nogncheck
#endif
#ifdef WEBRTC_CODEC_OPUS
#include "modules/audio_coding/codecs/opus/audio_encoder_opus.h"
#endif
#include "modules/audio_coding/codecs/pcm16b/audio_encoder_pcm16b.h"
#ifdef WEBRTC_CODEC_RED
#include "modules/audio_coding/codecs/red/audio_encoder_copy_red.h" // nogncheck
#endif
#include "modules/audio_coding/acm2/acm_codec_database.h"
#if defined(WEBRTC_CODEC_ISACFX) || defined(WEBRTC_CODEC_ISAC)
#include "modules/audio_coding/codecs/isac/locked_bandwidth_info.h"
#endif
namespace webrtc {
namespace acm2 {
@ -107,200 +81,5 @@ absl::optional<NetEqDecoder> RentACodec::NetEqDecoderFromCodecId(
: ned;
}
RentACodec::RegistrationResult RentACodec::RegisterCngPayloadType(
std::map<int, int>* pt_map,
const CodecInst& codec_inst) {
if (!absl::EqualsIgnoreCase(codec_inst.plname, "CN"))
return RegistrationResult::kSkip;
switch (codec_inst.plfreq) {
case 8000:
case 16000:
case 32000:
case 48000:
(*pt_map)[codec_inst.plfreq] = codec_inst.pltype;
return RegistrationResult::kOk;
default:
return RegistrationResult::kBadFreq;
}
}
RentACodec::RegistrationResult RentACodec::RegisterRedPayloadType(
std::map<int, int>* pt_map,
const CodecInst& codec_inst) {
if (!absl::EqualsIgnoreCase(codec_inst.plname, "RED"))
return RegistrationResult::kSkip;
switch (codec_inst.plfreq) {
case 8000:
(*pt_map)[codec_inst.plfreq] = codec_inst.pltype;
return RegistrationResult::kOk;
default:
return RegistrationResult::kBadFreq;
}
}
namespace {
// Returns a new speech encoder, or null on error.
// TODO(kwiberg): Don't handle errors here (bug 5033)
std::unique_ptr<AudioEncoder> CreateEncoder(
const CodecInst& speech_inst,
const rtc::scoped_refptr<LockedIsacBandwidthInfo>& bwinfo) {
#if defined(WEBRTC_CODEC_ISACFX)
if (absl::EqualsIgnoreCase(speech_inst.plname, "isac"))
return std::unique_ptr<AudioEncoder>(
new AudioEncoderIsacFixImpl(speech_inst, bwinfo));
#endif
#if defined(WEBRTC_CODEC_ISAC)
if (absl::EqualsIgnoreCase(speech_inst.plname, "isac"))
return std::unique_ptr<AudioEncoder>(
new AudioEncoderIsacFloatImpl(speech_inst, bwinfo));
#endif
#ifdef WEBRTC_CODEC_OPUS
if (absl::EqualsIgnoreCase(speech_inst.plname, "opus"))
return std::unique_ptr<AudioEncoder>(new AudioEncoderOpusImpl(speech_inst));
#endif
if (absl::EqualsIgnoreCase(speech_inst.plname, "pcmu"))
return std::unique_ptr<AudioEncoder>(new AudioEncoderPcmU(speech_inst));
if (absl::EqualsIgnoreCase(speech_inst.plname, "pcma"))
return std::unique_ptr<AudioEncoder>(new AudioEncoderPcmA(speech_inst));
if (absl::EqualsIgnoreCase(speech_inst.plname, "l16"))
return std::unique_ptr<AudioEncoder>(new AudioEncoderPcm16B(speech_inst));
#ifdef WEBRTC_CODEC_ILBC
if (absl::EqualsIgnoreCase(speech_inst.plname, "ilbc"))
return std::unique_ptr<AudioEncoder>(new AudioEncoderIlbcImpl(speech_inst));
#endif
if (absl::EqualsIgnoreCase(speech_inst.plname, "g722"))
return std::unique_ptr<AudioEncoder>(new AudioEncoderG722Impl(speech_inst));
RTC_LOG_F(LS_ERROR) << "Could not create encoder of type "
<< speech_inst.plname;
return std::unique_ptr<AudioEncoder>();
}
std::unique_ptr<AudioEncoder> CreateRedEncoder(
std::unique_ptr<AudioEncoder> encoder,
int red_payload_type) {
#ifdef WEBRTC_CODEC_RED
AudioEncoderCopyRed::Config config;
config.payload_type = red_payload_type;
config.speech_encoder = std::move(encoder);
return std::unique_ptr<AudioEncoder>(
new AudioEncoderCopyRed(std::move(config)));
#else
return std::unique_ptr<AudioEncoder>();
#endif
}
std::unique_ptr<AudioEncoder> CreateCngEncoder(
std::unique_ptr<AudioEncoder> encoder,
int payload_type,
ACMVADMode vad_mode) {
AudioEncoderCngConfig config;
config.num_channels = encoder->NumChannels();
config.payload_type = payload_type;
config.speech_encoder = std::move(encoder);
switch (vad_mode) {
case VADNormal:
config.vad_mode = Vad::kVadNormal;
break;
case VADLowBitrate:
config.vad_mode = Vad::kVadLowBitrate;
break;
case VADAggr:
config.vad_mode = Vad::kVadAggressive;
break;
case VADVeryAggr:
config.vad_mode = Vad::kVadVeryAggressive;
break;
default:
FATAL();
}
return CreateComfortNoiseEncoder(std::move(config));
}
std::unique_ptr<AudioDecoder> CreateIsacDecoder(
int sample_rate_hz,
const rtc::scoped_refptr<LockedIsacBandwidthInfo>& bwinfo) {
#if defined(WEBRTC_CODEC_ISACFX)
return std::unique_ptr<AudioDecoder>(
new AudioDecoderIsacFixImpl(sample_rate_hz, bwinfo));
#elif defined(WEBRTC_CODEC_ISAC)
return std::unique_ptr<AudioDecoder>(
new AudioDecoderIsacFloatImpl(sample_rate_hz, bwinfo));
#else
FATAL() << "iSAC is not supported.";
return std::unique_ptr<AudioDecoder>();
#endif
}
} // namespace
RentACodec::RentACodec() {
#if defined(WEBRTC_CODEC_ISACFX) || defined(WEBRTC_CODEC_ISAC)
isac_bandwidth_info_ = new LockedIsacBandwidthInfo;
#endif
}
RentACodec::~RentACodec() = default;
std::unique_ptr<AudioEncoder> RentACodec::RentEncoder(
const CodecInst& codec_inst) {
return CreateEncoder(codec_inst, isac_bandwidth_info_);
}
RentACodec::StackParameters::StackParameters() {
// Register the default payload types for RED and CNG.
for (const CodecInst& ci : RentACodec::Database()) {
RentACodec::RegisterCngPayloadType(&cng_payload_types, ci);
RentACodec::RegisterRedPayloadType(&red_payload_types, ci);
}
}
RentACodec::StackParameters::~StackParameters() = default;
std::unique_ptr<AudioEncoder> RentACodec::RentEncoderStack(
StackParameters* param) {
if (!param->speech_encoder)
return nullptr;
if (param->use_codec_fec) {
// Switch FEC on. On failure, remember that FEC is off.
if (!param->speech_encoder->SetFec(true))
param->use_codec_fec = false;
} else {
// Switch FEC off. This shouldn't fail.
const bool success = param->speech_encoder->SetFec(false);
RTC_DCHECK(success);
}
auto pt = [&param](const std::map<int, int>& m) {
auto it = m.find(param->speech_encoder->SampleRateHz());
return it == m.end() ? absl::nullopt : absl::optional<int>(it->second);
};
auto cng_pt = pt(param->cng_payload_types);
param->use_cng =
param->use_cng && cng_pt && param->speech_encoder->NumChannels() == 1;
auto red_pt = pt(param->red_payload_types);
param->use_red = param->use_red && red_pt;
if (param->use_cng || param->use_red) {
// The RED and CNG encoders need to be in sync with the speech encoder, so
// reset the latter to ensure its buffer is empty.
param->speech_encoder->Reset();
}
std::unique_ptr<AudioEncoder> encoder_stack =
std::move(param->speech_encoder);
if (param->use_red) {
encoder_stack = CreateRedEncoder(std::move(encoder_stack), *red_pt);
}
if (param->use_cng) {
encoder_stack =
CreateCngEncoder(std::move(encoder_stack), *cng_pt, param->vad_mode);
}
return encoder_stack;
}
std::unique_ptr<AudioDecoder> RentACodec::RentIsacDecoder(int sample_rate_hz) {
return CreateIsacDecoder(sample_rate_hz, isac_bandwidth_info_);
}
} // namespace acm2
} // namespace webrtc

View File

@ -31,8 +31,7 @@ class LockedIsacBandwidthInfo;
namespace acm2 {
class RentACodec {
public:
struct RentACodec {
enum class CodecId {
#if defined(WEBRTC_CODEC_ISAC) || defined(WEBRTC_CODEC_ISACFX)
kISAC,
@ -141,56 +140,6 @@ class RentACodec {
static absl::optional<NetEqDecoder> NetEqDecoderFromCodecId(
CodecId codec_id,
size_t num_channels);
// Parse codec_inst and extract payload types. If the given CodecInst was for
// the wrong sort of codec, return kSkip; otherwise, if the rate was illegal,
// return kBadFreq; otherwise, update the given RTP timestamp rate (Hz) ->
// payload type map and return kOk.
enum class RegistrationResult { kOk, kSkip, kBadFreq };
static RegistrationResult RegisterCngPayloadType(std::map<int, int>* pt_map,
const CodecInst& codec_inst);
static RegistrationResult RegisterRedPayloadType(std::map<int, int>* pt_map,
const CodecInst& codec_inst);
RentACodec();
~RentACodec();
// Creates and returns an audio encoder built to the given specification.
// Returns null in case of error.
std::unique_ptr<AudioEncoder> RentEncoder(const CodecInst& codec_inst);
struct StackParameters {
StackParameters();
~StackParameters();
std::unique_ptr<AudioEncoder> speech_encoder;
bool use_codec_fec = false;
bool use_red = false;
bool use_cng = false;
ACMVADMode vad_mode = VADNormal;
// Maps from RTP timestamp rate (in Hz) to payload type.
std::map<int, int> cng_payload_types;
std::map<int, int> red_payload_types;
};
// Creates and returns an audio encoder stack constructed to the given
// specification. If the specification isn't compatible with the encoder, it
// will be changed to match (things will be switched off). The speech encoder
// will be stolen. If the specification isn't complete, returns nullptr.
std::unique_ptr<AudioEncoder> RentEncoderStack(StackParameters* param);
// Creates and returns an iSAC decoder.
std::unique_ptr<AudioDecoder> RentIsacDecoder(int sample_rate_hz);
private:
std::unique_ptr<AudioEncoder> speech_encoder_;
std::unique_ptr<AudioEncoder> cng_encoder_;
std::unique_ptr<AudioEncoder> red_encoder_;
rtc::scoped_refptr<LockedIsacBandwidthInfo> isac_bandwidth_info_;
RTC_DISALLOW_COPY_AND_ASSIGN(RentACodec);
};
} // namespace acm2

View File

@ -1,228 +0,0 @@
/*
* Copyright (c) 2015 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 <memory>
#include "common_types.h"
#include "modules/audio_coding/acm2/rent_a_codec.h"
#include "rtc_base/arraysize.h"
#include "test/gtest.h"
#include "test/mock_audio_encoder.h"
namespace webrtc {
namespace acm2 {
using ::testing::Return;
namespace {
const int kDataLengthSamples = 80;
const int kPacketSizeSamples = 2 * kDataLengthSamples;
const int16_t kZeroData[kDataLengthSamples] = {0};
const CodecInst kDefaultCodecInst = {0, "pcmu", 8000, kPacketSizeSamples,
1, 64000};
const int kCngPt = 13;
class Marker final {
public:
MOCK_METHOD1(Mark, void(std::string desc));
};
} // namespace
class RentACodecTestF : public ::testing::Test {
protected:
void CreateCodec() {
auto speech_encoder = rent_a_codec_.RentEncoder(kDefaultCodecInst);
ASSERT_TRUE(speech_encoder);
RentACodec::StackParameters param;
param.use_cng = true;
param.speech_encoder = std::move(speech_encoder);
encoder_ = rent_a_codec_.RentEncoderStack(&param);
}
void EncodeAndVerify(size_t expected_out_length,
uint32_t expected_timestamp,
int expected_payload_type,
int expected_send_even_if_empty) {
rtc::Buffer out;
AudioEncoder::EncodedInfo encoded_info;
encoded_info = encoder_->Encode(timestamp_, kZeroData, &out);
timestamp_ += kDataLengthSamples;
EXPECT_TRUE(encoded_info.redundant.empty());
EXPECT_EQ(expected_out_length, encoded_info.encoded_bytes);
EXPECT_EQ(expected_timestamp, encoded_info.encoded_timestamp);
if (expected_payload_type >= 0)
EXPECT_EQ(expected_payload_type, encoded_info.payload_type);
if (expected_send_even_if_empty >= 0)
EXPECT_EQ(static_cast<bool>(expected_send_even_if_empty),
encoded_info.send_even_if_empty);
}
RentACodec rent_a_codec_;
std::unique_ptr<AudioEncoder> encoder_;
uint32_t timestamp_ = 0;
};
// This test verifies that CNG frames are delivered as expected. Since the frame
// size is set to 20 ms, we expect the first encode call to produce no output
// (which is signaled as 0 bytes output of type kNoEncoding). The next encode
// call should produce one SID frame of 9 bytes. The third call should not
// result in any output (just like the first one). The fourth and final encode
// call should produce an "empty frame", which is like no output, but with
// AudioEncoder::EncodedInfo::send_even_if_empty set to true. (The reason to
// produce an empty frame is to drive sending of DTMF packets in the RTP/RTCP
// module.)
TEST_F(RentACodecTestF, VerifyCngFrames) {
CreateCodec();
uint32_t expected_timestamp = timestamp_;
// Verify no frame.
{
SCOPED_TRACE("First encoding");
EncodeAndVerify(0, expected_timestamp, -1, -1);
}
// Verify SID frame delivered.
{
SCOPED_TRACE("Second encoding");
EncodeAndVerify(9, expected_timestamp, kCngPt, 1);
}
// Verify no frame.
{
SCOPED_TRACE("Third encoding");
EncodeAndVerify(0, expected_timestamp, -1, -1);
}
// Verify NoEncoding.
expected_timestamp += 2 * kDataLengthSamples;
{
SCOPED_TRACE("Fourth encoding");
EncodeAndVerify(0, expected_timestamp, kCngPt, 1);
}
}
TEST(RentACodecTest, ExternalEncoder) {
const int kSampleRateHz = 8000;
auto* external_encoder = new MockAudioEncoder;
EXPECT_CALL(*external_encoder, SampleRateHz())
.WillRepeatedly(Return(kSampleRateHz));
EXPECT_CALL(*external_encoder, NumChannels()).WillRepeatedly(Return(1));
EXPECT_CALL(*external_encoder, SetFec(false)).WillRepeatedly(Return(true));
RentACodec rac;
RentACodec::StackParameters param;
param.speech_encoder = std::unique_ptr<AudioEncoder>(external_encoder);
std::unique_ptr<AudioEncoder> encoder_stack = rac.RentEncoderStack(&param);
EXPECT_EQ(external_encoder, encoder_stack.get());
const int kPacketSizeSamples = kSampleRateHz / 100;
int16_t audio[kPacketSizeSamples] = {0};
rtc::Buffer encoded;
AudioEncoder::EncodedInfo info;
Marker marker;
{
::testing::InSequence s;
info.encoded_timestamp = 0;
EXPECT_CALL(*external_encoder,
EncodeImpl(0, rtc::ArrayView<const int16_t>(audio), &encoded))
.WillOnce(Return(info));
EXPECT_CALL(marker, Mark("A"));
EXPECT_CALL(marker, Mark("B"));
EXPECT_CALL(marker, Mark("C"));
}
info = encoder_stack->Encode(0, audio, &encoded);
EXPECT_EQ(0u, info.encoded_timestamp);
marker.Mark("A");
// Change to internal encoder.
CodecInst codec_inst = kDefaultCodecInst;
codec_inst.pacsize = kPacketSizeSamples;
param.speech_encoder = rac.RentEncoder(codec_inst);
ASSERT_TRUE(param.speech_encoder);
AudioEncoder* enc = param.speech_encoder.get();
std::unique_ptr<AudioEncoder> stack = rac.RentEncoderStack(&param);
EXPECT_EQ(enc, stack.get());
// Don't expect any more calls to the external encoder.
info = stack->Encode(1, audio, &encoded);
marker.Mark("B");
encoder_stack.reset();
marker.Mark("C");
}
// Verify that the speech encoder's Reset method is called when CNG or RED
// (or both) are switched on, but not when they're switched off.
void TestCngAndRedResetSpeechEncoder(bool use_cng, bool use_red) {
auto make_enc = [] {
auto speech_encoder =
std::unique_ptr<MockAudioEncoder>(new MockAudioEncoder);
EXPECT_CALL(*speech_encoder, NumChannels()).WillRepeatedly(Return(1));
EXPECT_CALL(*speech_encoder, Max10MsFramesInAPacket())
.WillRepeatedly(Return(2));
EXPECT_CALL(*speech_encoder, SampleRateHz()).WillRepeatedly(Return(8000));
EXPECT_CALL(*speech_encoder, SetFec(false)).WillRepeatedly(Return(true));
return speech_encoder;
};
auto speech_encoder1 = make_enc();
auto speech_encoder2 = make_enc();
Marker marker;
{
::testing::InSequence s;
EXPECT_CALL(marker, Mark("disabled"));
EXPECT_CALL(marker, Mark("enabled"));
if (use_cng || use_red)
EXPECT_CALL(*speech_encoder2, Reset());
}
RentACodec::StackParameters param1, param2;
param1.speech_encoder = std::move(speech_encoder1);
param2.speech_encoder = std::move(speech_encoder2);
param2.use_cng = use_cng;
param2.use_red = use_red;
marker.Mark("disabled");
RentACodec rac;
rac.RentEncoderStack(&param1);
marker.Mark("enabled");
rac.RentEncoderStack(&param2);
}
TEST(RentACodecTest, CngResetsSpeechEncoder) {
TestCngAndRedResetSpeechEncoder(true, false);
}
TEST(RentACodecTest, RedResetsSpeechEncoder) {
TestCngAndRedResetSpeechEncoder(false, true);
}
TEST(RentACodecTest, CngAndRedResetsSpeechEncoder) {
TestCngAndRedResetSpeechEncoder(true, true);
}
TEST(RentACodecTest, NoCngAndRedNoSpeechEncoderReset) {
TestCngAndRedResetSpeechEncoder(false, false);
}
TEST(RentACodecTest, RentEncoderError) {
const CodecInst codec_inst = {
0, "Robert'); DROP TABLE Students;", 8000, 160, 1, 64000};
RentACodec rent_a_codec;
EXPECT_FALSE(rent_a_codec.RentEncoder(codec_inst));
}
TEST(RentACodecTest, RentEncoderStackWithoutSpeechEncoder) {
RentACodec::StackParameters sp;
EXPECT_EQ(nullptr, sp.speech_encoder);
EXPECT_EQ(nullptr, RentACodec().RentEncoderStack(&sp));
}
} // namespace acm2
} // namespace webrtc

View File

@ -154,40 +154,6 @@ class AudioCodingModule {
// Sender
//
///////////////////////////////////////////////////////////////////////////
// int32_t RegisterSendCodec()
// Registers a codec, specified by |send_codec|, as sending codec.
// This API can be called multiple of times to register Codec. The last codec
// registered overwrites the previous ones.
// The API can also be used to change payload type for CNG and RED, which are
// registered by default to default payload types.
// Note that registering CNG and RED won't overwrite speech codecs.
// This API can be called to set/change the send payload-type, frame-size
// or encoding rate (if applicable for the codec).
//
// Note: If a stereo codec is registered as send codec, VAD/DTX will
// automatically be turned off, since it is not supported for stereo sending.
//
// Note: If a secondary encoder is already registered, and the new send-codec
// has a sampling rate that does not match the secondary encoder, the
// secondary encoder will be unregistered.
//
// Input:
// -send_codec : Parameters of the codec to be registered, c.f.
// common_types.h for the definition of
// CodecInst.
//
// Return value:
// -1 if failed to initialize,
// 0 if succeeded.
//
virtual int32_t RegisterSendCodec(const CodecInst& send_codec) = 0;
// Registers |external_speech_encoder| as encoder. The new encoder will
// replace any previously registered speech encoder (internal or external).
virtual void RegisterExternalSendCodec(
AudioEncoder* external_speech_encoder) = 0;
// |modifier| is called exactly once with one argument: a pointer to the
// unique_ptr that holds the current encoder (which is null if there is no
// current encoder). For the duration of the call, |modifier| has exclusive
@ -257,71 +223,6 @@ class AudioCodingModule {
//
virtual int32_t Add10MsData(const AudioFrame& audio_frame) = 0;
///////////////////////////////////////////////////////////////////////////
// (RED) Redundant Coding
//
///////////////////////////////////////////////////////////////////////////
// int32_t SetREDStatus()
// configure RED status i.e. on/off.
//
// RFC 2198 describes a solution which has a single payload type which
// signifies a packet with redundancy. That packet then becomes a container,
// encapsulating multiple payloads into a single RTP packet.
// Such a scheme is flexible, since any amount of redundancy may be
// encapsulated within a single packet. There is, however, a small overhead
// since each encapsulated payload must be preceded by a header indicating
// the type of data enclosed.
//
// Input:
// -enable_red : if true RED is enabled, otherwise RED is
// disabled.
//
// Return value:
// -1 if failed to set RED status,
// 0 if succeeded.
//
virtual int32_t SetREDStatus(bool enable_red) = 0;
///////////////////////////////////////////////////////////////////////////
// bool REDStatus()
// Get RED status
//
// Return value:
// true if RED is enabled,
// false if RED is disabled.
//
virtual bool REDStatus() const = 0;
///////////////////////////////////////////////////////////////////////////
// (FEC) Forward Error Correction (codec internal)
//
///////////////////////////////////////////////////////////////////////////
// int32_t SetCodecFEC()
// Configures codec internal FEC status i.e. on/off. No effects on codecs that
// do not provide internal FEC.
//
// Input:
// -enable_fec : if true FEC will be enabled otherwise the FEC is
// disabled.
//
// Return value:
// -1 if failed, or the codec does not support FEC
// 0 if succeeded.
//
virtual int SetCodecFEC(bool enable_codec_fec) = 0;
///////////////////////////////////////////////////////////////////////////
// bool CodecFEC()
// Gets status of codec internal FEC.
//
// Return value:
// true if FEC is enabled,
// false if FEC is disabled.
//
virtual bool CodecFEC() const = 0;
///////////////////////////////////////////////////////////////////////////
// int SetPacketLossRate()
// Sets expected packet loss rate for encoding. Some encoders provide packet
@ -343,55 +244,6 @@ class AudioCodingModule {
// (VAD) Voice Activity Detection
//
///////////////////////////////////////////////////////////////////////////
// int32_t SetVAD()
// If DTX is enabled & the codec does not have internal DTX/VAD
// WebRtc VAD will be automatically enabled and |enable_vad| is ignored.
//
// If DTX is disabled but VAD is enabled no DTX packets are send,
// regardless of whether the codec has internal DTX/VAD or not. In this
// case, WebRtc VAD is running to label frames as active/in-active.
//
// NOTE! VAD/DTX is not supported when sending stereo.
//
// Inputs:
// -enable_dtx : if true DTX is enabled,
// otherwise DTX is disabled.
// -enable_vad : if true VAD is enabled,
// otherwise VAD is disabled.
// -vad_mode : determines the aggressiveness of VAD. A more
// aggressive mode results in more frames labeled
// as in-active, c.f. definition of
// ACMVADMode in audio_coding_module_typedefs.h
// for valid values.
//
// Return value:
// -1 if failed to set up VAD/DTX,
// 0 if succeeded.
//
virtual int32_t SetVAD(const bool enable_dtx = true,
const bool enable_vad = false,
const ACMVADMode vad_mode = VADNormal) = 0;
///////////////////////////////////////////////////////////////////////////
// int32_t VAD()
// Get VAD status.
//
// Outputs:
// -dtx_enabled : is set to true if DTX is enabled, otherwise
// is set to false.
// -vad_enabled : is set to true if VAD is enabled, otherwise
// is set to false.
// -vad_mode : is set to the current aggressiveness of VAD.
//
// Return value:
// -1 if fails to retrieve the setting of DTX/VAD,
// 0 if succeeded.
//
virtual int32_t VAD(bool* dtx_enabled,
bool* vad_enabled,
ACMVADMode* vad_mode) const = 0;
///////////////////////////////////////////////////////////////////////////
// int32_t RegisterVADCallback()
// Call this method to register a callback function which is called
@ -455,29 +307,6 @@ class AudioCodingModule {
virtual bool RegisterReceiveCodec(int rtp_payload_type,
const SdpAudioFormat& audio_format) = 0;
///////////////////////////////////////////////////////////////////////////
// int32_t RegisterReceiveCodec()
// Register possible decoders, can be called multiple times for
// codecs, CNG-NB, CNG-WB, CNG-SWB, AVT and RED.
//
// Input:
// -receive_codec : parameters of the codec to be registered, c.f.
// common_types.h for the definition of
// CodecInst.
//
// Return value:
// -1 if failed to register the codec
// 0 if the codec registered successfully.
//
virtual int RegisterReceiveCodec(const CodecInst& receive_codec) = 0;
// Register a decoder; call repeatedly to register multiple decoders. |df| is
// a decoder factory that returns an iSAC decoder; it will be called once if
// the decoder being registered is iSAC.
virtual int RegisterReceiveCodec(
const CodecInst& receive_codec,
rtc::FunctionView<std::unique_ptr<AudioDecoder>()> isac_factory) = 0;
// Registers an external decoder. The name is only used to provide information
// back to the caller about the decoder. Hence, the name is arbitrary, and may
// be empty.