/* * 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 "rtc_base/checks.h" //#include "rtc_base/format_macros.h" #include "modules/audio_coding/acm2/rent_a_codec.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 (!STR_CASE_CMP(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 !STR_CASE_CMP(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(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* encoder) { if (!*encoder) { // There is no existing encoder. recreate_encoder_ = true; return; } // Extract the speech encoder from the ACM. std::unique_ptr 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