/* * 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/main/acm2/codec_manager.h" #include "webrtc/base/checks.h" #include "webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.h" namespace webrtc { namespace acm2 { namespace { bool IsCodecRED(const CodecInst* codec) { return (STR_CASE_CMP(codec->plname, "RED") == 0); } bool IsCodecRED(int index) { return (IsCodecRED(&ACMCodecDB::database_[index])); } bool IsCodecCN(const CodecInst* codec) { return (STR_CASE_CMP(codec->plname, "CN") == 0); } bool IsCodecCN(int index) { return (IsCodecCN(&ACMCodecDB::database_[index])); } // Check if the given codec is a valid to be registered as send codec. int IsValidSendCodec(const CodecInst& send_codec, bool is_primary_encoder, int* mirror_id) { 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 (%d, only mono and stereo are " "supported) for %s encoder", send_codec.channels, is_primary_encoder ? "primary" : "secondary"); return -1; } int codec_id = ACMCodecDB::CodecNumber(send_codec, mirror_id); if (codec_id < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, dummy_id, "Invalid codec setting for the send codec."); return -1; } // TODO(tlegrand): Remove this check. Already taken care of in // ACMCodecDB::CodecNumber(). // Check if the payload-type is valid if (!ACMCodecDB::ValidPayloadType(send_codec.pltype)) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, dummy_id, "Invalid payload-type %d for %s.", send_codec.pltype, send_codec.plname); 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"); *mirror_id = -1; return -1; } if (ACMCodecDB::codec_settings_[codec_id].channel_support < send_codec.channels) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, dummy_id, "%d number of channels not supportedn for %s.", send_codec.channels, send_codec.plname); *mirror_id = -1; return -1; } if (!is_primary_encoder) { // If registering the secondary encoder, then RED and CN are not valid // choices as encoder. if (IsCodecRED(&send_codec)) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, dummy_id, "RED cannot be secondary codec"); *mirror_id = -1; return -1; } if (IsCodecCN(&send_codec)) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, dummy_id, "DTX cannot be secondary codec"); *mirror_id = -1; return -1; } } return codec_id; } const CodecInst kEmptyCodecInst = {-1, "noCodecRegistered", 0, 0, 0, 0}; } // namespace CodecManager::CodecManager(AudioCodingModuleImpl* acm) : acm_(acm), cng_nb_pltype_(255), cng_wb_pltype_(255), cng_swb_pltype_(255), cng_fb_pltype_(255), red_nb_pltype_(255), stereo_send_(false), vad_enabled_(false), dtx_enabled_(false), vad_mode_(VADNormal), current_encoder_(nullptr), send_codec_inst_(kEmptyCodecInst), red_enabled_(false), codec_fec_enabled_(false) { for (int i = 0; i < ACMCodecDB::kMaxNumCodecs; i++) { codecs_[i] = nullptr; mirror_codec_idx_[i] = -1; } // Register the default payload type for RED and for CNG at sampling rates of // 8, 16, 32 and 48 kHz. for (int i = (ACMCodecDB::kNumCodecs - 1); i >= 0; i--) { if (IsCodecRED(i) && ACMCodecDB::database_[i].plfreq == 8000) { red_nb_pltype_ = static_cast(ACMCodecDB::database_[i].pltype); } else if (IsCodecCN(i)) { if (ACMCodecDB::database_[i].plfreq == 8000) { cng_nb_pltype_ = static_cast(ACMCodecDB::database_[i].pltype); } else if (ACMCodecDB::database_[i].plfreq == 16000) { cng_wb_pltype_ = static_cast(ACMCodecDB::database_[i].pltype); } else if (ACMCodecDB::database_[i].plfreq == 32000) { cng_swb_pltype_ = static_cast(ACMCodecDB::database_[i].pltype); } else if (ACMCodecDB::database_[i].plfreq == 48000) { cng_fb_pltype_ = static_cast(ACMCodecDB::database_[i].pltype); } } } thread_checker_.DetachFromThread(); } CodecManager::~CodecManager() { for (int i = 0; i < ACMCodecDB::kMaxNumCodecs; i++) { if (codecs_[i] != NULL) { // Mirror index holds the address of the codec memory. assert(mirror_codec_idx_[i] > -1); if (codecs_[mirror_codec_idx_[i]] != NULL) { delete codecs_[mirror_codec_idx_[i]]; codecs_[mirror_codec_idx_[i]] = NULL; } codecs_[i] = NULL; } } } int CodecManager::RegisterSendCodec(const CodecInst& send_codec) { DCHECK(thread_checker_.CalledOnValidThread()); int mirror_id; int codec_id = IsValidSendCodec(send_codec, true, &mirror_id); // Check for reported errors from function IsValidSendCodec(). if (codec_id < 0) { return -1; } int dummy_id = 0; // RED can be registered with other payload type. If not registered a default // payload type is used. if (IsCodecRED(&send_codec)) { // TODO(tlegrand): Remove this check. Already taken care of in // ACMCodecDB::CodecNumber(). // Check if the payload-type is valid if (!ACMCodecDB::ValidPayloadType(send_codec.pltype)) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, dummy_id, "Invalid payload-type %d for %s.", send_codec.pltype, send_codec.plname); return -1; } // Set RED payload type. if (send_codec.plfreq == 8000) { red_nb_pltype_ = static_cast(send_codec.pltype); } else { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, dummy_id, "RegisterSendCodec() failed, invalid frequency for RED " "registration"); return -1; } SetRedPayloadType(send_codec.plfreq, send_codec.pltype); return 0; } // CNG can be registered with other payload type. If not registered the // default payload types from codec database will be used. if (IsCodecCN(&send_codec)) { // CNG is registered. switch (send_codec.plfreq) { case 8000: { cng_nb_pltype_ = static_cast(send_codec.pltype); break; } case 16000: { cng_wb_pltype_ = static_cast(send_codec.pltype); break; } case 32000: { cng_swb_pltype_ = static_cast(send_codec.pltype); break; } case 48000: { cng_fb_pltype_ = static_cast(send_codec.pltype); break; } default: { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, dummy_id, "RegisterSendCodec() failed, invalid frequency for CNG " "registration"); return -1; } } SetCngPayloadType(send_codec.plfreq, send_codec.pltype); return 0; } // Set Stereo, and make sure VAD and DTX is turned off. if (send_codec.channels == 2) { stereo_send_ = true; if (vad_enabled_ || dtx_enabled_) { WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, dummy_id, "VAD/DTX is turned off, not supported when sending stereo."); } vad_enabled_ = false; dtx_enabled_ = false; } else { stereo_send_ = false; } // Check if the codec is already registered as send codec. bool is_send_codec; if (current_encoder_) { int send_codec_mirror_id; int send_codec_id = ACMCodecDB::CodecNumber(send_codec_inst_, &send_codec_mirror_id); assert(send_codec_id >= 0); is_send_codec = (send_codec_id == codec_id) || (mirror_id == send_codec_mirror_id); } else { is_send_codec = false; } // If new codec, or new settings, register. if (!is_send_codec) { if (!codecs_[mirror_id]) { codecs_[mirror_id] = ACMCodecDB::CreateCodecInstance( send_codec, cng_nb_pltype_, cng_wb_pltype_, cng_swb_pltype_, cng_fb_pltype_, red_enabled_, red_nb_pltype_); if (!codecs_[mirror_id]) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, dummy_id, "Cannot Create the codec"); return -1; } mirror_codec_idx_[mirror_id] = mirror_id; } if (mirror_id != codec_id) { codecs_[codec_id] = codecs_[mirror_id]; mirror_codec_idx_[codec_id] = mirror_id; } ACMGenericCodec* codec_ptr = codecs_[codec_id]; WebRtcACMCodecParams codec_params; memcpy(&(codec_params.codec_inst), &send_codec, sizeof(CodecInst)); codec_params.enable_vad = vad_enabled_; codec_params.enable_dtx = dtx_enabled_; codec_params.vad_mode = vad_mode_; // Force initialization. if (codec_ptr->InitEncoder(&codec_params, true) < 0) { // Could not initialize the encoder. // Check if already have a registered codec. // Depending on that different messages are logged. if (!current_encoder_) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, dummy_id, "Cannot Initialize the encoder No Encoder is registered"); } else { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, dummy_id, "Cannot Initialize the encoder, continue encoding with " "the previously registered codec"); } return -1; } // Update states. dtx_enabled_ = codec_params.enable_dtx; vad_enabled_ = codec_params.enable_vad; vad_mode_ = codec_params.vad_mode; // Everything is fine so we can replace the previous codec with this one. if (current_encoder_) { // If we change codec we start fresh with RED. // This is not strictly required by the standard. if (codec_ptr->SetCopyRed(red_enabled_) < 0) { // We tried to preserve the old red status, if failed, it means the // red status has to be flipped. red_enabled_ = !red_enabled_; } codec_ptr->SetVAD(&dtx_enabled_, &vad_enabled_, &vad_mode_); if (!codec_ptr->HasInternalFEC()) { codec_fec_enabled_ = false; } else { if (codec_ptr->SetFEC(codec_fec_enabled_) < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, dummy_id, "Cannot set codec FEC"); return -1; } } } current_encoder_ = codecs_[codec_id]; DCHECK(current_encoder_); memcpy(&send_codec_inst_, &send_codec, sizeof(CodecInst)); return 0; } else { // If codec is the same as already registered check if any parameters // has changed compared to the current values. // If any parameter is valid then apply it and record. bool force_init = false; if (mirror_id != codec_id) { codecs_[codec_id] = codecs_[mirror_id]; mirror_codec_idx_[codec_id] = mirror_id; } // Check the payload type. if (send_codec.pltype != send_codec_inst_.pltype) { // At this point check if the given payload type is valid. // Record it later when the sampling frequency is changed // successfully. if (!ACMCodecDB::ValidPayloadType(send_codec.pltype)) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, dummy_id, "Out of range payload type"); return -1; } } // If there is a codec that ONE instance of codec supports multiple // sampling frequencies, then we need to take care of it here. // one such a codec is iSAC. Both WB and SWB are encoded and decoded // with one iSAC instance. Therefore, we need to update the encoder // frequency if required. if (send_codec_inst_.plfreq != send_codec.plfreq) { force_init = true; } // If packet size or number of channels has changed, we need to // re-initialize the encoder. if (send_codec_inst_.pacsize != send_codec.pacsize) { force_init = true; } if (send_codec_inst_.channels != send_codec.channels) { force_init = true; } if (force_init) { WebRtcACMCodecParams codec_params; memcpy(&(codec_params.codec_inst), &send_codec, sizeof(CodecInst)); codec_params.enable_vad = vad_enabled_; codec_params.enable_dtx = dtx_enabled_; codec_params.vad_mode = vad_mode_; // Force initialization. if (current_encoder_->InitEncoder(&codec_params, true) < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, dummy_id, "Could not change the codec packet-size."); return -1; } send_codec_inst_.plfreq = send_codec.plfreq; send_codec_inst_.pacsize = send_codec.pacsize; send_codec_inst_.channels = send_codec.channels; } // If the change of sampling frequency has been successful then // we store the payload-type. send_codec_inst_.pltype = send_codec.pltype; // Check if a change in Rate is required. if (send_codec.rate != send_codec_inst_.rate) { if (codecs_[codec_id]->SetBitRate(send_codec.rate) < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, dummy_id, "Could not change the codec rate."); return -1; } send_codec_inst_.rate = send_codec.rate; } if (!codecs_[codec_id]->HasInternalFEC()) { codec_fec_enabled_ = false; } else { if (codecs_[codec_id]->SetFEC(codec_fec_enabled_) < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, dummy_id, "Cannot set codec FEC"); return -1; } } return 0; } } int CodecManager::SendCodec(CodecInst* current_codec) const { int dummy_id = 0; WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, dummy_id, "SendCodec()"); if (!current_encoder_) { WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, dummy_id, "SendCodec Failed, no codec is registered"); return -1; } WebRtcACMCodecParams encoder_param; current_encoder_->EncoderParams(&encoder_param); encoder_param.codec_inst.pltype = send_codec_inst_.pltype; memcpy(current_codec, &(encoder_param.codec_inst), sizeof(CodecInst)); return 0; } // Register possible receive codecs, can be called multiple times, // for codecs, CNG (NB, WB and SWB), DTMF, RED. int CodecManager::RegisterReceiveCodec(const CodecInst& codec) { if (codec.channels > 2 || codec.channels < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, 0, "Unsupported number of channels, %d.", codec.channels); return -1; } int mirror_id; int codec_id = ACMCodecDB::ReceiverCodecNumber(codec, &mirror_id); if (codec_id < 0 || codec_id >= ACMCodecDB::kNumCodecs) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, 0, "Wrong codec params to be registered as receive codec"); return -1; } // Check if the payload-type is valid. if (!ACMCodecDB::ValidPayloadType(codec.pltype)) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, 0, "Invalid payload-type %d for %s.", codec.pltype, codec.plname); return -1; } AudioDecoder* decoder = NULL; // Get |decoder| associated with |codec|. |decoder| can be NULL if |codec| // does not own its decoder. if (GetAudioDecoder(codec, codec_id, mirror_id, &decoder) < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, 0, "Wrong codec params to be registered as receive codec"); return -1; } uint8_t payload_type = static_cast(codec.pltype); return acm_->RegisterDecoder(codec_id, payload_type, codec.channels, decoder); } bool CodecManager::SetCopyRed(bool enable) { if (enable && codec_fec_enabled_) { WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, 0, "Codec internal FEC and RED cannot be co-enabled."); return false; } if (current_encoder_ && current_encoder_->SetCopyRed(enable) < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, 0, "SetCopyRed failed"); return false; } red_enabled_ = enable; return true; } int CodecManager::SetVAD(bool enable_dtx, bool enable_vad, ACMVADMode mode) { // Sanity check of the mode. if ((mode != VADNormal) && (mode != VADLowBitrate) && (mode != VADAggr) && (mode != VADVeryAggr)) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, 0, "Invalid VAD Mode %d, no change is made to VAD/DTX status", mode); return -1; } // Check that the send codec is mono. We don't support VAD/DTX for stereo // sending. if ((enable_dtx || enable_vad) && stereo_send_) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, 0, "VAD/DTX not supported for stereo sending"); dtx_enabled_ = false; vad_enabled_ = false; vad_mode_ = mode; return -1; } // Store VAD/DTX settings. Values can be changed in the call to "SetVAD" // below. dtx_enabled_ = enable_dtx; vad_enabled_ = enable_vad; vad_mode_ = mode; // If a send codec is registered, set VAD/DTX for the codec. if (current_encoder_ && current_encoder_->SetVAD(&dtx_enabled_, &vad_enabled_, &vad_mode_) < 0) { // SetVAD failed. WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, 0, "SetVAD failed"); vad_enabled_ = false; dtx_enabled_ = false; return -1; } return 0; } void CodecManager::VAD(bool* dtx_enabled, bool* vad_enabled, ACMVADMode* mode) const { *dtx_enabled = dtx_enabled_; *vad_enabled = vad_enabled_; *mode = vad_mode_; } int CodecManager::SetCodecFEC(bool enable_codec_fec) { if (enable_codec_fec == true && red_enabled_ == true) { WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, 0, "Codec internal FEC and RED cannot be co-enabled."); return -1; } // Set codec FEC. if (current_encoder_ && current_encoder_->SetFEC(enable_codec_fec) < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, 0, "Set codec internal FEC failed."); return -1; } codec_fec_enabled_ = enable_codec_fec; return 0; } void CodecManager::SetCngPayloadType(int sample_rate_hz, int payload_type) { for (auto* codec : codecs_) { if (codec) { codec->SetCngPt(sample_rate_hz, payload_type); } } } void CodecManager::SetRedPayloadType(int sample_rate_hz, int payload_type) { for (auto* codec : codecs_) { if (codec) { codec->SetRedPt(sample_rate_hz, payload_type); } } } int CodecManager::GetAudioDecoder(const CodecInst& codec, int codec_id, int mirror_id, AudioDecoder** decoder) { if (ACMCodecDB::OwnsDecoder(codec_id)) { // This codec has to own its own decoder. Therefore, it should create the // corresponding AudioDecoder class and insert it into NetEq. If the codec // does not exist create it. // // TODO(turajs): this part of the code is common with RegisterSendCodec(), // make a method for it. if (codecs_[mirror_id] == NULL) { codecs_[mirror_id] = ACMCodecDB::CreateCodecInstance( codec, cng_nb_pltype_, cng_wb_pltype_, cng_swb_pltype_, cng_fb_pltype_, red_enabled_, red_nb_pltype_); if (codecs_[mirror_id] == NULL) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, 0, "Cannot Create the codec"); return -1; } mirror_codec_idx_[mirror_id] = mirror_id; } if (mirror_id != codec_id) { codecs_[codec_id] = codecs_[mirror_id]; mirror_codec_idx_[codec_id] = mirror_id; } *decoder = codecs_[codec_id]->Decoder(); if (!*decoder) { assert(false); return -1; } } else { *decoder = NULL; } return 0; } } // namespace acm2 } // namespace webrtc