diff --git a/webrtc/modules/audio_coding/main/acm2/codec_manager.cc b/webrtc/modules/audio_coding/main/acm2/codec_manager.cc index 54cf13b8e9..80229353cf 100644 --- a/webrtc/modules/audio_coding/main/acm2/codec_manager.cc +++ b/webrtc/modules/audio_coding/main/acm2/codec_manager.cc @@ -108,17 +108,7 @@ const CodecInst kEmptyCodecInst = {-1, "noCodecRegistered", 0, 0, 0, 0}; } // namespace CodecManager::CodecManager() - : dtx_enabled_(false), - vad_mode_(VADNormal), - send_codec_inst_(kEmptyCodecInst), - red_enabled_(false), - codec_fec_enabled_(false), - encoder_is_opus_(false) { - // 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); - } + : send_codec_inst_(kEmptyCodecInst), encoder_is_opus_(false) { thread_checker_.DetachFromThread(); } @@ -134,7 +124,8 @@ int CodecManager::RegisterEncoder(const CodecInst& send_codec) { } int dummy_id = 0; - switch (RentACodec::RegisterRedPayloadType(&red_payload_types_, send_codec)) { + switch (RentACodec::RegisterRedPayloadType( + &codec_stack_params_.red_payload_types, send_codec)) { case RentACodec::RegistrationResult::kOk: return 0; case RentACodec::RegistrationResult::kBadFreq: @@ -145,7 +136,8 @@ int CodecManager::RegisterEncoder(const CodecInst& send_codec) { case RentACodec::RegistrationResult::kSkip: break; } - switch (RentACodec::RegisterCngPayloadType(&cng_payload_types_, send_codec)) { + switch (RentACodec::RegisterCngPayloadType( + &codec_stack_params_.cng_payload_types, send_codec)) { case RentACodec::RegistrationResult::kOk: return 0; case RentACodec::RegistrationResult::kBadFreq: @@ -159,11 +151,11 @@ int CodecManager::RegisterEncoder(const CodecInst& send_codec) { // Set Stereo, and make sure VAD and DTX is turned off. if (send_codec.channels != 1) { - if (dtx_enabled_) { + if (codec_stack_params_.use_cng) { WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, dummy_id, "VAD/DTX is turned off, not supported when sending stereo."); } - dtx_enabled_ = false; + codec_stack_params_.use_cng = false; } // Check if the codec is already registered as send codec. @@ -175,10 +167,6 @@ int CodecManager::RegisterEncoder(const CodecInst& send_codec) { new_codec = !old_codec_id || *new_codec_id != *old_codec_id; } - if (RedPayloadType(send_codec.plfreq) == -1) { - red_enabled_ = false; - } - encoder_is_opus_ = IsOpus(send_codec); if (new_codec) { @@ -186,16 +174,17 @@ int CodecManager::RegisterEncoder(const CodecInst& send_codec) { RTC_DCHECK(CodecSupported(send_codec)); if (IsOpus(send_codec)) { // VAD/DTX not supported. - dtx_enabled_ = false; + codec_stack_params_.use_cng = false; } AudioEncoder* enc = rent_a_codec_.RentEncoder(send_codec); if (!enc) return -1; - RentEncoderStack(enc, send_codec.plfreq); + rent_a_codec_.RentEncoderStack(enc, &codec_stack_params_); RTC_DCHECK(CurrentEncoder()); - codec_fec_enabled_ = codec_fec_enabled_ && - enc->SetFec(codec_fec_enabled_); + codec_stack_params_.use_codec_fec = + codec_stack_params_.use_codec_fec && + enc->SetFec(codec_stack_params_.use_codec_fec); send_codec_inst_ = send_codec; return 0; @@ -208,7 +197,7 @@ int CodecManager::RegisterEncoder(const CodecInst& send_codec) { AudioEncoder* enc = rent_a_codec_.RentEncoder(send_codec); if (!enc) return -1; - RentEncoderStack(enc, send_codec.plfreq); + rent_a_codec_.RentEncoderStack(enc, &codec_stack_params_); RTC_DCHECK(CurrentEncoder()); } send_codec_inst_.plfreq = send_codec.plfreq; @@ -222,8 +211,9 @@ int CodecManager::RegisterEncoder(const CodecInst& send_codec) { send_codec_inst_.rate = send_codec.rate; } - codec_fec_enabled_ = - codec_fec_enabled_ && CurrentEncoder()->SetFec(codec_fec_enabled_); + codec_stack_params_.use_codec_fec = + codec_stack_params_.use_codec_fec && + CurrentEncoder()->SetFec(codec_stack_params_.use_codec_fec); return 0; } @@ -242,19 +232,18 @@ void CodecManager::RegisterEncoder(AudioEncoder* external_speech_encoder) { memcpy(send_codec_inst_.plname, kName, sizeof(kName)); if (send_codec_inst_.channels != 1) - dtx_enabled_ = false; - if (codec_fec_enabled_) { + codec_stack_params_.use_cng = false; + if (codec_stack_params_.use_codec_fec) { // Switch FEC on. On failure, remember that FEC is off. if (!external_speech_encoder->SetFec(true)) - codec_fec_enabled_ = false; + codec_stack_params_.use_codec_fec = false; } else { // Switch FEC off. This shouldn't fail. const bool success = external_speech_encoder->SetFec(false); RTC_DCHECK(success); } - RentEncoderStack(external_speech_encoder, - external_speech_encoder->SampleRateHz()); + rent_a_codec_.RentEncoderStack(external_speech_encoder, &codec_stack_params_); } rtc::Optional CodecManager::GetCodecInst() const { @@ -271,20 +260,23 @@ rtc::Optional CodecManager::GetCodecInst() const { } bool CodecManager::SetCopyRed(bool enable) { - if (enable && codec_fec_enabled_) { + if (enable && codec_stack_params_.use_codec_fec) { WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, 0, "Codec internal FEC and RED cannot be co-enabled."); return false; } - if (enable && RedPayloadType(send_codec_inst_.plfreq) == -1) { + if (enable && + codec_stack_params_.red_payload_types.count(send_codec_inst_.plfreq) < + 1) { WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, 0, "Cannot enable RED at %i Hz.", send_codec_inst_.plfreq); return false; } - if (red_enabled_ != enable) { - red_enabled_ = enable; + if (codec_stack_params_.use_red != enable) { + codec_stack_params_.use_red = enable; if (CurrentEncoder()) - RentEncoderStack(rent_a_codec_.GetEncoder(), send_codec_inst_.plfreq); + rent_a_codec_.RentEncoderStack(rent_a_codec_.GetEncoder(), + &codec_stack_params_); } return true; } @@ -301,22 +293,23 @@ int CodecManager::SetVAD(bool enable, ACMVADMode mode) { if (enable && stereo_send) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, 0, "VAD/DTX not supported for stereo sending"); - dtx_enabled_ = false; + codec_stack_params_.use_cng = false; return -1; } // If a send codec is registered, set VAD/DTX for the codec. if (IsOpus(send_codec_inst_)) { // VAD/DTX not supported. - dtx_enabled_ = false; + codec_stack_params_.use_cng = false; return 0; } - if (dtx_enabled_ != enable || vad_mode_ != mode) { - dtx_enabled_ = enable; - vad_mode_ = mode; + if (codec_stack_params_.use_cng != enable || + codec_stack_params_.vad_mode != mode) { + codec_stack_params_.use_cng = enable; + codec_stack_params_.vad_mode = mode; if (enc) - RentEncoderStack(enc, send_codec_inst_.plfreq); + rent_a_codec_.RentEncoderStack(enc, &codec_stack_params_); } return 0; } @@ -324,55 +317,26 @@ int CodecManager::SetVAD(bool enable, ACMVADMode mode) { void CodecManager::VAD(bool* dtx_enabled, bool* vad_enabled, ACMVADMode* mode) const { - *dtx_enabled = dtx_enabled_; - *vad_enabled = dtx_enabled_; - *mode = vad_mode_; + *dtx_enabled = *vad_enabled = codec_stack_params_.use_cng; + *mode = codec_stack_params_.vad_mode; } int CodecManager::SetCodecFEC(bool enable_codec_fec) { - if (enable_codec_fec == true && red_enabled_ == true) { + if (enable_codec_fec && codec_stack_params_.use_red) { WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, 0, "Codec internal FEC and RED cannot be co-enabled."); return -1; } RTC_CHECK(CurrentEncoder()); - codec_fec_enabled_ = + codec_stack_params_.use_codec_fec = CurrentEncoder()->SetFec(enable_codec_fec) && enable_codec_fec; - return codec_fec_enabled_ == enable_codec_fec ? 0 : -1; + return codec_stack_params_.use_codec_fec == enable_codec_fec ? 0 : -1; } AudioDecoder* CodecManager::GetAudioDecoder(const CodecInst& codec) { return IsIsac(codec) ? rent_a_codec_.RentIsacDecoder() : nullptr; } -int CodecManager::CngPayloadType(int rtp_timestamp_rate_hz) const { - RTC_CHECK(rtp_timestamp_rate_hz == 8000 || rtp_timestamp_rate_hz == 16000 || - rtp_timestamp_rate_hz == 32000 || rtp_timestamp_rate_hz == 48000) - << rtp_timestamp_rate_hz << " Hz is not supported"; - auto it = cng_payload_types_.find(rtp_timestamp_rate_hz); - return it == cng_payload_types_.end() ? -1 : it->second; -} - -int CodecManager::RedPayloadType(int rtp_timestamp_rate_hz) const { - RTC_CHECK(rtp_timestamp_rate_hz == 8000 || rtp_timestamp_rate_hz == 16000 || - rtp_timestamp_rate_hz == 32000 || rtp_timestamp_rate_hz == 48000) - << rtp_timestamp_rate_hz << " Hz is not supported"; - auto it = red_payload_types_.find(rtp_timestamp_rate_hz); - return it == red_payload_types_.end() ? -1 : it->second; -} - -void CodecManager::RentEncoderStack(AudioEncoder* speech_encoder, - int sample_rate_hz) { - auto cng_config = - dtx_enabled_ ? rtc::Optional(RentACodec::CngConfig{ - CngPayloadType(sample_rate_hz), vad_mode_}) - : rtc::Optional(); - auto red_pt = red_enabled_ - ? rtc::Optional(RedPayloadType(sample_rate_hz)) - : rtc::Optional(); - rent_a_codec_.RentEncoderStack(speech_encoder, cng_config, red_pt); -} - } // namespace acm2 } // namespace webrtc diff --git a/webrtc/modules/audio_coding/main/acm2/codec_manager.h b/webrtc/modules/audio_coding/main/acm2/codec_manager.h index 3cf303058a..7670bbd2de 100644 --- a/webrtc/modules/audio_coding/main/acm2/codec_manager.h +++ b/webrtc/modules/audio_coding/main/acm2/codec_manager.h @@ -54,9 +54,9 @@ class CodecManager final { // null. AudioDecoder* GetAudioDecoder(const CodecInst& codec); - bool red_enabled() const { return red_enabled_; } + bool red_enabled() const { return codec_stack_params_.use_red; } - bool codec_fec_enabled() const { return codec_fec_enabled_; } + bool codec_fec_enabled() const { return codec_stack_params_.use_codec_fec; } AudioEncoder* CurrentEncoder() { return rent_a_codec_.GetEncoderStack(); } const AudioEncoder* CurrentEncoder() const { @@ -66,21 +66,10 @@ class CodecManager final { bool CurrentEncoderIsOpus() const { return encoder_is_opus_; } private: - int CngPayloadType(int sample_rate_hz) const; - int RedPayloadType(int sample_rate_hz) const; - void RentEncoderStack(AudioEncoder* speech_encoder, int sample_rate_hz); - rtc::ThreadChecker thread_checker_; - bool dtx_enabled_; - ACMVADMode vad_mode_; CodecInst send_codec_inst_; - bool red_enabled_; - bool codec_fec_enabled_; RentACodec rent_a_codec_; - - // Maps from RTP timestamp rate (in Hz) to payload type. - std::map cng_payload_types_; - std::map red_payload_types_; + RentACodec::StackParameters codec_stack_params_; bool encoder_is_opus_; diff --git a/webrtc/modules/audio_coding/main/acm2/rent_a_codec.cc b/webrtc/modules/audio_coding/main/acm2/rent_a_codec.cc index c2a40243ea..328181428b 100644 --- a/webrtc/modules/audio_coding/main/acm2/rent_a_codec.cc +++ b/webrtc/modules/audio_coding/main/acm2/rent_a_codec.cc @@ -187,14 +187,14 @@ rtc::scoped_ptr CreateRedEncoder(AudioEncoder* encoder, #endif } -rtc::scoped_ptr CreateCngEncoder( - AudioEncoder* encoder, - RentACodec::CngConfig cng_config) { +rtc::scoped_ptr CreateCngEncoder(AudioEncoder* encoder, + int payload_type, + ACMVADMode vad_mode) { AudioEncoderCng::Config config; config.num_channels = encoder->NumChannels(); - config.payload_type = cng_config.cng_payload_type; + config.payload_type = payload_type; config.speech_encoder = encoder; - switch (cng_config.vad_mode) { + switch (vad_mode) { case VADNormal: config.vad_mode = Vad::kVadNormal; break; @@ -239,26 +239,45 @@ AudioEncoder* RentACodec::RentEncoder(const CodecInst& codec_inst) { return speech_encoder_.get(); } -AudioEncoder* RentACodec::RentEncoderStack( - AudioEncoder* speech_encoder, - rtc::Optional cng_config, - rtc::Optional red_payload_type) { +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; + +AudioEncoder* RentACodec::RentEncoderStack(AudioEncoder* speech_encoder, + StackParameters* param) { RTC_DCHECK(speech_encoder); - if (cng_config || red_payload_type) { + + auto pt = [&speech_encoder](const std::map& m) { + auto it = m.find(speech_encoder->SampleRateHz()); + return it == m.end() ? rtc::Optional() + : rtc::Optional(it->second); + }; + auto cng_pt = pt(param->cng_payload_types); + param->use_cng = param->use_cng && cng_pt; + 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. speech_encoder->Reset(); } encoder_stack_ = speech_encoder; - if (red_payload_type) { - red_encoder_ = CreateRedEncoder(encoder_stack_, *red_payload_type); + if (param->use_red) { + red_encoder_ = CreateRedEncoder(encoder_stack_, *red_pt); if (red_encoder_) encoder_stack_ = red_encoder_.get(); } else { red_encoder_.reset(); } - if (cng_config) { - cng_encoder_ = CreateCngEncoder(encoder_stack_, *cng_config); + if (param->use_cng) { + cng_encoder_ = CreateCngEncoder(encoder_stack_, *cng_pt, param->vad_mode); encoder_stack_ = cng_encoder_.get(); } else { cng_encoder_.reset(); diff --git a/webrtc/modules/audio_coding/main/acm2/rent_a_codec.h b/webrtc/modules/audio_coding/main/acm2/rent_a_codec.h index db728ae294..45d46bb057 100644 --- a/webrtc/modules/audio_coding/main/acm2/rent_a_codec.h +++ b/webrtc/modules/audio_coding/main/acm2/rent_a_codec.h @@ -200,18 +200,27 @@ class RentACodec { // successful call to this function, or until the Rent-A-Codec is destroyed. AudioEncoder* RentEncoder(const CodecInst& codec_inst); - // Creates and returns an audio encoder stack where the given speech encoder - // is augmented with the specified CNG/VAD and RED encoders. Leave either - // optional field blank if you don't want the corresponding gizmo in the - // stack. The returned encoder is live until the next successful call to this - // function, or until the Rent-A-Codec is destroyed. - struct CngConfig { - int cng_payload_type; - ACMVADMode vad_mode; + struct StackParameters { + StackParameters(); + ~StackParameters(); + + 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 cng_payload_types; + std::map 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 returned + // encoder is live until the next successful call to this function, or until + // the Rent-A-Codec is destroyed. AudioEncoder* RentEncoderStack(AudioEncoder* speech_encoder, - rtc::Optional cng_config, - rtc::Optional red_payload_type); + StackParameters* param); // Get the last return values of RentEncoder and RentEncoderStack, or null if // they haven't been called. diff --git a/webrtc/modules/audio_coding/main/acm2/rent_a_codec_unittest.cc b/webrtc/modules/audio_coding/main/acm2/rent_a_codec_unittest.cc index 6938051644..d2f40ed3d2 100644 --- a/webrtc/modules/audio_coding/main/acm2/rent_a_codec_unittest.cc +++ b/webrtc/modules/audio_coding/main/acm2/rent_a_codec_unittest.cc @@ -32,10 +32,9 @@ class RentACodecTestF : public ::testing::Test { void CreateCodec() { speech_encoder_ = rent_a_codec_.RentEncoder(kDefaultCodecInst); ASSERT_TRUE(speech_encoder_); - encoder_ = rent_a_codec_.RentEncoderStack( - speech_encoder_, rtc::Optional( - RentACodec::CngConfig{kCngPt, VADNormal}), - rtc::Optional()); + RentACodec::StackParameters param; + param.use_cng = true; + encoder_ = rent_a_codec_.RentEncoderStack(speech_encoder_, ¶m); } void EncodeAndVerify(size_t expected_out_length, @@ -104,10 +103,8 @@ TEST_F(RentACodecTestF, VerifyCngFrames) { TEST(RentACodecTest, ExternalEncoder) { MockAudioEncoder external_encoder; RentACodec rac; - EXPECT_EQ(&external_encoder, - rac.RentEncoderStack(&external_encoder, - rtc::Optional(), - rtc::Optional())); + RentACodec::StackParameters param; + EXPECT_EQ(&external_encoder, rac.RentEncoderStack(&external_encoder, ¶m)); const int kSampleRateHz = 8000; const int kPacketSizeSamples = kSampleRateHz / 100; int16_t audio[kPacketSizeSamples] = {0}; @@ -143,19 +140,14 @@ TEST(RentACodecTest, ExternalEncoder) { codec_inst.pacsize = kPacketSizeSamples; AudioEncoder* enc = rac.RentEncoder(codec_inst); ASSERT_TRUE(enc); - EXPECT_EQ(enc, - rac.RentEncoderStack(enc, rtc::Optional(), - rtc::Optional())); + EXPECT_EQ(enc, rac.RentEncoderStack(enc, ¶m)); // Don't expect any more calls to the external encoder. info = rac.GetEncoderStack()->Encode(1, audio, arraysize(encoded), encoded); external_encoder.Mark("B"); // Change back to external encoder again. - EXPECT_EQ(&external_encoder, - rac.RentEncoderStack(&external_encoder, - rtc::Optional(), - rtc::Optional())); + EXPECT_EQ(&external_encoder, rac.RentEncoderStack(&external_encoder, ¶m)); info = rac.GetEncoderStack()->Encode(2, audio, arraysize(encoded), encoded); EXPECT_EQ(2u, info.encoded_timestamp); } @@ -177,16 +169,14 @@ void TestCngAndRedResetSpeechEncoder(bool use_cng, bool use_red) { EXPECT_CALL(speech_encoder, Die()); } - auto cng_cfg = use_cng ? rtc::Optional( - RentACodec::CngConfig{17, VADNormal}) - : rtc::Optional(); - auto red_pt = use_red ? rtc::Optional(19) : rtc::Optional(); + RentACodec::StackParameters param1, param2; + param2.use_cng = use_cng; + param2.use_red = use_red; speech_encoder.Mark("disabled"); RentACodec rac; - rac.RentEncoderStack(&speech_encoder, rtc::Optional(), - rtc::Optional()); + rac.RentEncoderStack(&speech_encoder, ¶m1); speech_encoder.Mark("enabled"); - rac.RentEncoderStack(&speech_encoder, cng_cfg, red_pt); + rac.RentEncoderStack(&speech_encoder, ¶m2); } TEST(RentACodecTest, CngResetsSpeechEncoder) {