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