| /* | |
| * 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<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 |