/* * 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/acm2/codec_manager.h" #include "webrtc/base/checks.h" #include "webrtc/base/format_macros.h" #include "webrtc/engine_configurations.h" #include "webrtc/modules/audio_coding/acm2/rent_a_codec.h" #include "webrtc/system_wrappers/include/trace.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) { int dummy_id = 0; if ((send_codec.channels != 1) && (send_codec.channels != 2)) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, dummy_id, "Wrong number of channels (%" PRIuS ", only mono and stereo " "are supported)", send_codec.channels); return -1; } auto maybe_codec_id = RentACodec::CodecIdByInst(send_codec); if (!maybe_codec_id) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, dummy_id, "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")) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, dummy_id, "telephone-event cannot be a send codec"); return -1; } if (!RentACodec::IsSupportedNumChannels(*maybe_codec_id, send_codec.channels) .value_or(false)) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, dummy_id, "%" PRIuS " number of channels not supportedn for %s.", send_codec.channels, 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; } int dummy_id = 0; switch (RentACodec::RegisterRedPayloadType( &codec_stack_params_.red_payload_types, send_codec)) { case RentACodec::RegistrationResult::kOk: return true; case RentACodec::RegistrationResult::kBadFreq: WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, dummy_id, "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: WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, dummy_id, "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_ = rtc::Optional(send_codec); codec_stack_params_.speech_encoder = nullptr; // 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) { WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, 0, "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) { WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, 0, "Cannot enable RED at %i Hz.", send_codec_inst_->plfreq); 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) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, 0, "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) { WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, 0, "Codec internal FEC and RED cannot be co-enabled."); return false; } codec_stack_params_.use_codec_fec = enable_codec_fec; return true; } } // namespace acm2 } // namespace webrtc