blob: 5c1e37b5b0c2f493c067067369ab0c2a60038618 [file] [log] [blame]
/*
* 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 "webrtc/modules/audio_coding/main/acm2/codec_owner.h"
#include "webrtc/base/checks.h"
#include "webrtc/engine_configurations.h"
#include "webrtc/modules/audio_coding/codecs/cng/include/audio_encoder_cng.h"
#include "webrtc/modules/audio_coding/codecs/g711/include/audio_encoder_pcm.h"
#include "webrtc/modules/audio_coding/codecs/g722/include/audio_encoder_g722.h"
#include "webrtc/modules/audio_coding/codecs/ilbc/interface/audio_encoder_ilbc.h"
#include "webrtc/modules/audio_coding/codecs/isac/fix/interface/audio_encoder_isacfix.h"
#include "webrtc/modules/audio_coding/codecs/isac/main/interface/audio_encoder_isac.h"
#include "webrtc/modules/audio_coding/codecs/opus/interface/audio_encoder_opus.h"
#include "webrtc/modules/audio_coding/codecs/pcm16b/include/audio_encoder_pcm16b.h"
#include "webrtc/modules/audio_coding/codecs/red/audio_encoder_copy_red.h"
#include "webrtc/modules/audio_coding/main/acm2/acm_common_defs.h"
#include "webrtc/system_wrappers/interface/metrics.h"
namespace webrtc {
namespace acm2 {
namespace {
bool IsIsac(const CodecInst& codec) {
return
#if (defined(WEBRTC_CODEC_ISAC) || defined(WEBRTC_CODEC_ISACFX))
!STR_CASE_CMP(codec.plname, "isac") ||
#endif
false;
}
bool IsOpus(const CodecInst& codec) {
return
#ifdef WEBRTC_CODEC_OPUS
!STR_CASE_CMP(codec.plname, "opus") ||
#endif
false;
}
bool IsPcmU(const CodecInst& codec) {
return !STR_CASE_CMP(codec.plname, "pcmu");
}
bool IsPcmA(const CodecInst& codec) {
return !STR_CASE_CMP(codec.plname, "pcma");
}
bool IsPcm16B(const CodecInst& codec) {
return
#ifdef WEBRTC_CODEC_PCM16
!STR_CASE_CMP(codec.plname, "l16") ||
#endif
false;
}
bool IsIlbc(const CodecInst& codec) {
return
#ifdef WEBRTC_CODEC_ILBC
!STR_CASE_CMP(codec.plname, "ilbc") ||
#endif
false;
}
bool IsG722(const CodecInst& codec) {
return
#ifdef WEBRTC_CODEC_G722
!STR_CASE_CMP(codec.plname, "g722") ||
#endif
false;
}
} // namespace
CodecOwner::CodecOwner()
: isac_is_encoder_(false), external_speech_encoder_(nullptr) {
}
CodecOwner::~CodecOwner() = default;
namespace {
AudioEncoderDecoderMutableIsac* CreateIsacCodec(const CodecInst& speech_inst) {
#if defined(WEBRTC_CODEC_ISACFX)
return new AudioEncoderDecoderMutableIsacFix(speech_inst);
#elif defined(WEBRTC_CODEC_ISAC)
return new AudioEncoderDecoderMutableIsacFloat(speech_inst);
#else
FATAL() << "iSAC is not supported.";
return nullptr;
#endif
}
void CreateSpeechEncoder(
const CodecInst& speech_inst,
rtc::scoped_ptr<AudioEncoderMutable>* speech_encoder,
rtc::scoped_ptr<AudioEncoderDecoderMutableIsac>* isac_codec,
bool* isac_is_encoder) {
if (IsIsac(speech_inst)) {
if (*isac_codec) {
(*isac_codec)->UpdateSettings(speech_inst);
} else {
isac_codec->reset(CreateIsacCodec(speech_inst));
}
*isac_is_encoder = true;
speech_encoder->reset();
return;
}
if (IsOpus(speech_inst)) {
speech_encoder->reset(new AudioEncoderMutableOpus(speech_inst));
} else if (IsPcmU(speech_inst)) {
speech_encoder->reset(new AudioEncoderMutablePcmU(speech_inst));
} else if (IsPcmA(speech_inst)) {
speech_encoder->reset(new AudioEncoderMutablePcmA(speech_inst));
} else if (IsPcm16B(speech_inst)) {
speech_encoder->reset(new AudioEncoderMutablePcm16B(speech_inst));
} else if (IsIlbc(speech_inst)) {
speech_encoder->reset(new AudioEncoderMutableIlbc(speech_inst));
} else if (IsG722(speech_inst)) {
speech_encoder->reset(new AudioEncoderMutableG722(speech_inst));
} else {
FATAL();
}
*isac_is_encoder = false;
}
AudioEncoder* CreateRedEncoder(int red_payload_type,
AudioEncoder* encoder,
rtc::scoped_ptr<AudioEncoder>* red_encoder) {
if (red_payload_type == -1) {
red_encoder->reset();
return encoder;
}
AudioEncoderCopyRed::Config config;
config.payload_type = red_payload_type;
config.speech_encoder = encoder;
red_encoder->reset(new AudioEncoderCopyRed(config));
return red_encoder->get();
}
void CreateCngEncoder(int cng_payload_type,
ACMVADMode vad_mode,
AudioEncoder* encoder,
rtc::scoped_ptr<AudioEncoder>* cng_encoder) {
if (cng_payload_type == -1) {
cng_encoder->reset();
return;
}
AudioEncoderCng::Config config;
config.num_channels = encoder->NumChannels();
config.payload_type = cng_payload_type;
config.speech_encoder = 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();
}
cng_encoder->reset(new AudioEncoderCng(config));
}
} // namespace
void CodecOwner::SetEncoders(const CodecInst& speech_inst,
int cng_payload_type,
ACMVADMode vad_mode,
int red_payload_type) {
CreateSpeechEncoder(speech_inst, &speech_encoder_, &isac_codec_,
&isac_is_encoder_);
external_speech_encoder_ = nullptr;
ChangeCngAndRed(cng_payload_type, vad_mode, red_payload_type);
RTC_HISTOGRAM_COUNTS_100(HISTOGRAM_NAME_AUDIO_TARGET_BITRATE_IN_KBPS,
SpeechEncoder()->GetTargetBitrate() / 1000);
}
void CodecOwner::SetEncoders(AudioEncoderMutable* external_speech_encoder,
int cng_payload_type,
ACMVADMode vad_mode,
int red_payload_type) {
external_speech_encoder_ = external_speech_encoder;
speech_encoder_.reset();
isac_is_encoder_ = false;
ChangeCngAndRed(cng_payload_type, vad_mode, red_payload_type);
RTC_HISTOGRAM_COUNTS_100(HISTOGRAM_NAME_AUDIO_TARGET_BITRATE_IN_KBPS,
SpeechEncoder()->GetTargetBitrate() / 1000);
}
void CodecOwner::ChangeCngAndRed(int cng_payload_type,
ACMVADMode vad_mode,
int red_payload_type) {
AudioEncoderMutable* speech_encoder = SpeechEncoder();
if (cng_payload_type != -1 || red_payload_type != -1) {
// The RED and CNG encoders need to be in sync with the speech encoder, so
// reset the latter to ensure its buffer is empty.
speech_encoder->Reset();
}
AudioEncoder* encoder =
CreateRedEncoder(red_payload_type, speech_encoder, &red_encoder_);
CreateCngEncoder(cng_payload_type, vad_mode, encoder, &cng_encoder_);
int num_true =
!!speech_encoder_ + !!external_speech_encoder_ + isac_is_encoder_;
DCHECK_EQ(num_true, 1);
DCHECK(!isac_is_encoder_ || isac_codec_);
}
AudioDecoder* CodecOwner::GetIsacDecoder() {
if (!isac_codec_) {
DCHECK(!isac_is_encoder_);
// None of the parameter values in |speech_inst| matter when the codec is
// used only as a decoder.
CodecInst speech_inst;
speech_inst.plfreq = 16000;
speech_inst.rate = -1;
speech_inst.pacsize = 480;
isac_codec_.reset(CreateIsacCodec(speech_inst));
}
return isac_codec_.get();
}
AudioEncoder* CodecOwner::Encoder() {
const auto& const_this = *this;
return const_cast<AudioEncoder*>(const_this.Encoder());
}
const AudioEncoder* CodecOwner::Encoder() const {
if (cng_encoder_)
return cng_encoder_.get();
if (red_encoder_)
return red_encoder_.get();
return SpeechEncoder();
}
AudioEncoderMutable* CodecOwner::SpeechEncoder() {
const auto& const_this = *this;
return const_cast<AudioEncoderMutable*>(const_this.SpeechEncoder());
}
const AudioEncoderMutable* CodecOwner::SpeechEncoder() const {
int num_true =
!!speech_encoder_ + !!external_speech_encoder_ + isac_is_encoder_;
DCHECK_GE(num_true, 0);
DCHECK_LE(num_true, 1);
if (external_speech_encoder_)
return external_speech_encoder_;
if (speech_encoder_)
return speech_encoder_.get();
return isac_is_encoder_ ? isac_codec_.get() : nullptr;
}
} // namespace acm2
} // namespace webrtc