/* * Copyright (c) 2011 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 #include #include "acm_codec_database.h" #include "acm_common_defs.h" #include "acm_generic_codec.h" #include "acm_neteq.h" #include "trace.h" #include "webrtc_vad.h" #include "webrtc_cng.h" namespace webrtc { // Enum for CNG enum { kMaxPLCParamsCNG = WEBRTC_CNG_MAX_LPC_ORDER, kNewCNGNumPLCParams = 8 }; #define ACM_SID_INTERVAL_MSEC 100 // We set some of the variables to invalid values as a check point // if a proper initialization has happened. Another approach is // to initialize to a default codec that we are sure is always included. ACMGenericCodec::ACMGenericCodec(): _inAudioIxWrite(0), _inAudioIxRead(0), _inTimestampIxWrite(0), _inAudio(NULL), _inTimestamp(NULL), _frameLenSmpl(-1), // invalid value _noChannels(1), _codecID(-1), // invalid value _noMissedSamples(0), _encoderExist(false), _decoderExist(false), _encoderInitialized(false), _decoderInitialized(false), _registeredInNetEq(false), _hasInternalDTX(false), _ptrVADInst(NULL), _vadEnabled(false), _vadMode(VADNormal), _dtxEnabled(false), _ptrDTXInst(NULL), _numLPCParams(kNewCNGNumPLCParams), _sentCNPrevious(false), _isMaster(true), _netEqDecodeLock(NULL), _codecWrapperLock(*RWLockWrapper::CreateRWLock()), _lastEncodedTimestamp(0), _lastTimestamp(0), _isAudioBuffFresh(true), _uniqueID(0) { _lastTimestamp = 0xD87F3F9F; //NullifyCodecInstance(); } ACMGenericCodec::~ACMGenericCodec() { // Check all the members which are pointers and // if they are not NULL delete/free them. if(_ptrVADInst != NULL) { WebRtcVad_Free(_ptrVADInst); _ptrVADInst = NULL; } if (_inAudio != NULL) { delete [] _inAudio; _inAudio = NULL; } if (_inTimestamp != NULL) { delete [] _inTimestamp; _inTimestamp = NULL; } if(_ptrDTXInst != NULL) { WebRtcCng_FreeEnc(_ptrDTXInst); _ptrDTXInst = NULL; } delete &_codecWrapperLock; } WebRtc_Word32 ACMGenericCodec::Add10MsData( const WebRtc_UWord32 timestamp, const WebRtc_Word16* data, const WebRtc_UWord16 lengthSmpl, const WebRtc_UWord8 audioChannel) { WriteLockScoped wl(_codecWrapperLock); return Add10MsDataSafe(timestamp, data, lengthSmpl, audioChannel); } WebRtc_Word32 ACMGenericCodec::Add10MsDataSafe( const WebRtc_UWord32 timestamp, const WebRtc_Word16* data, const WebRtc_UWord16 lengthSmpl, const WebRtc_UWord8 audioChannel) { // The codec expects to get data in correct sampling rate. // get the sampling frequency of the codec WebRtc_UWord16 plFreqHz; if(EncoderSampFreq(plFreqHz) < 0) { // _codecID is not correct, perhaps the codec is not initialized yet. return -1; } // Sanity check, if the length of the input corresponds to 10 ms. if((plFreqHz / 100) != lengthSmpl) { // This is not 10 ms of audio, given the sampling frequency of the // codec return -1; } if(_lastTimestamp == timestamp) { // Same timestamp as the last time, overwrite. if((_inAudioIxWrite >= lengthSmpl) && (_inTimestampIxWrite > 0)) { _inAudioIxWrite -= lengthSmpl; _inTimestampIxWrite--; WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceAudioCoding, _uniqueID, "Adding 10ms with previous timestamp, \ overwriting the previous 10ms"); } else { WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceAudioCoding, _uniqueID, "Adding 10ms with previous timestamp, this will sound bad"); } } _lastTimestamp = timestamp; if ((_inAudioIxWrite + lengthSmpl*audioChannel) > AUDIO_BUFFER_SIZE_W16) { // Get the number of samples to be overwritten WebRtc_Word16 missedSamples = _inAudioIxWrite + lengthSmpl*audioChannel - AUDIO_BUFFER_SIZE_W16; // Move the data (overwite the old data) memmove(_inAudio, _inAudio + missedSamples, (AUDIO_BUFFER_SIZE_W16 - lengthSmpl*audioChannel)*sizeof(WebRtc_Word16)); // Copy the new data memcpy(_inAudio + (AUDIO_BUFFER_SIZE_W16 - lengthSmpl*audioChannel), data, lengthSmpl*audioChannel * sizeof(WebRtc_Word16)); // Get the number of 10 ms blocks which are overwritten WebRtc_Word16 missed10MsecBlocks = (WebRtc_Word16)((missedSamples/audioChannel * 100) / plFreqHz); // Move the timestamps memmove(_inTimestamp, _inTimestamp + missed10MsecBlocks, (_inTimestampIxWrite - missed10MsecBlocks) * sizeof(WebRtc_UWord32)); _inTimestampIxWrite -= missed10MsecBlocks; _inTimestamp[_inTimestampIxWrite] = timestamp; _inTimestampIxWrite++; // Buffer is full _inAudioIxWrite = AUDIO_BUFFER_SIZE_W16; IncreaseNoMissedSamples(missedSamples); _isAudioBuffFresh = false; return -missedSamples; } memcpy(_inAudio + _inAudioIxWrite, data, lengthSmpl*audioChannel * sizeof(WebRtc_Word16)); _inAudioIxWrite += lengthSmpl*audioChannel; assert(_inTimestampIxWrite < TIMESTAMP_BUFFER_SIZE_W32); assert(_inTimestampIxWrite >= 0); _inTimestamp[_inTimestampIxWrite] = timestamp; _inTimestampIxWrite++; _isAudioBuffFresh = false; return 0; } WebRtc_Word16 ACMGenericCodec::Encode( WebRtc_UWord8* bitStream, WebRtc_Word16* bitStreamLenByte, WebRtc_UWord32* timeStamp, WebRtcACMEncodingType* encodingType) { WriteLockScoped lockCodec(_codecWrapperLock); ReadLockScoped lockNetEq(*_netEqDecodeLock); return EncodeSafe(bitStream, bitStreamLenByte, timeStamp, encodingType); } WebRtc_Word16 ACMGenericCodec::EncodeSafe( WebRtc_UWord8* bitStream, WebRtc_Word16* bitStreamLenByte, WebRtc_UWord32* timeStamp, WebRtcACMEncodingType* encodingType) { // Do we have enough data to encode? // we wait until we have a full frame to encode. if(_inAudioIxWrite < _frameLenSmpl*_noChannels) { // There is not enough audio *timeStamp = 0; *bitStreamLenByte = 0; // Doesn't really matter what this parameter set to *encodingType = kNoEncoding; return 0; } // Not all codecs accept the whole frame to be pushed into // encoder at once. "myBasicCodingBlockSmpl" is const WebRtc_Word16 myBasicCodingBlockSmpl = ACMCodecDB::_basicCodingBlockSmpl[_codecID]; if((myBasicCodingBlockSmpl < 0) || (!_encoderInitialized) || (!_encoderExist)) { // This should not happen *timeStamp = 0; *bitStreamLenByte = 0; *encodingType = kNoEncoding; WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "EncodeSafe: error, basic coding sample block is negative"); return -1; } // This makes the internal encoder read from the begining of the buffer _inAudioIxRead = 0; *timeStamp = _inTimestamp[0]; // Process the audio through VAD the function doesn't set _vadLabels. // If VAD is disabled all labels are set to ONE (active) WebRtc_Word16 status = 0; WebRtc_Word16 dtxProcessedSamples = 0; status = ProcessFrameVADDTX(bitStream, bitStreamLenByte, &dtxProcessedSamples); if(status < 0) { *timeStamp = 0; *bitStreamLenByte = 0; *encodingType = kNoEncoding; } else { if(dtxProcessedSamples > 0) { // Dtx have processed some samples may or may not a bit-stream // is generated we should not do any encoding (normally there // will be not enough data) // Setting the following makes that the move of audio data // and timestamps happen correctly _inAudioIxRead = dtxProcessedSamples; // This will let the owner of ACMGenericCodec to know that the // generated bit-stream is DTX to use correct payload type WebRtc_UWord16 sampFreqHz; EncoderSampFreq(sampFreqHz); if (sampFreqHz == 8000) { *encodingType = kPassiveDTXNB; } else if (sampFreqHz == 16000) { *encodingType = kPassiveDTXWB; } else if (sampFreqHz == 32000) { *encodingType = kPassiveDTXSWB; } else { status = -1; WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "EncodeSafe: Wrong sampling frequency for DTX."); } // Transport empty frame if we have an empty bitstream if ((*bitStreamLenByte == 0) && (_sentCNPrevious || ((_inAudioIxWrite - _inAudioIxRead) <= 0)) ) { // Makes sure we transmit an empty frame *bitStreamLenByte = 1; *encodingType = kNoEncoding; } _sentCNPrevious = true; } else { _sentCNPrevious = false; // This will let the caller of the method to know if the frame is // Active or non-Active The caller of the method knows that the // stream is encoded by codec and can use the info for callbacks, // if any registered. if(myBasicCodingBlockSmpl == 0) { // This codec can handle all allowed frame sizes as basic // coding block status = InternalEncode(bitStream, bitStreamLenByte); if(status < 0) { // TODO: // Maybe reseting the encoder to be fresh for the next // frame WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "EncodeSafe: error in internalEncode"); *bitStreamLenByte = 0; *encodingType = kNoEncoding; } } else { // A basic-coding-block for this codec is defined so we loop // over the audio with the steps of the basic-coding-block. // It is not necessary that in each itteration WebRtc_Word16 tmpBitStreamLenByte; // Reset the variables which will be increamented in the loop *bitStreamLenByte = 0; bool done = false; while(!done) { status = InternalEncode(&bitStream[*bitStreamLenByte], &tmpBitStreamLenByte); *bitStreamLenByte += tmpBitStreamLenByte; // Guard Against errors and too large payloads if((status < 0) || (*bitStreamLenByte > MAX_PAYLOAD_SIZE_BYTE)) { // Error has happened if we are in the middle of a full // frame we have to exit. Before exiting, whatever bits // are in the buffer are probably corruptred. Anyways // we ignore them. *bitStreamLenByte = 0; *encodingType = kNoEncoding; // We might have come here because of the second // condition. status = -1; WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "EncodeSafe: error in InternalEncode"); // break from the loop break; } done = _inAudioIxRead >= _frameLenSmpl; } } if(status >= 0) { *encodingType = (_vadLabel[0] == 1)? kActiveNormalEncoded:kPassiveNormalEncoded; // Transport empty frame if we have an empty bitsteram if ((*bitStreamLenByte == 0) && ((_inAudioIxWrite - _inAudioIxRead) <= 0)) { // Makes sure we transmit an empty frame *bitStreamLenByte = 1; *encodingType = kNoEncoding; } } } } // Move the timestampe buffer according to the number of 10 ms blocks // which are read. WebRtc_UWord16 sampFreqHz; EncoderSampFreq(sampFreqHz); WebRtc_Word16 num10MsecBlocks = (WebRtc_Word16)((_inAudioIxRead/_noChannels * 100) / sampFreqHz); if(_inTimestampIxWrite > num10MsecBlocks) { memmove(_inTimestamp, _inTimestamp + num10MsecBlocks, (_inTimestampIxWrite - num10MsecBlocks) * sizeof(WebRtc_Word32)); } _inTimestampIxWrite -= num10MsecBlocks; // We have to move the audio that is not encoded to the beginning // of the buffer and accordingly adjust the read and write indices. if(_inAudioIxRead < _inAudioIxWrite) { memmove(_inAudio, &_inAudio[_inAudioIxRead], (_inAudioIxWrite - _inAudioIxRead)*sizeof(WebRtc_Word16)); } _inAudioIxWrite -= _inAudioIxRead; _inAudioIxRead = 0; _lastEncodedTimestamp = *timeStamp; return (status < 0) ? (-1):(*bitStreamLenByte); } WebRtc_Word16 ACMGenericCodec::Decode( WebRtc_UWord8* bitStream, WebRtc_Word16 bitStreamLenByte, WebRtc_Word16* audio, WebRtc_Word16* audioSamples, WebRtc_Word8* speechType) { WriteLockScoped wl(_codecWrapperLock); return DecodeSafe(bitStream, bitStreamLenByte, audio, audioSamples, speechType); } bool ACMGenericCodec::EncoderInitialized() { ReadLockScoped rl(_codecWrapperLock); return _encoderInitialized; } bool ACMGenericCodec::DecoderInitialized() { ReadLockScoped rl(_codecWrapperLock); return _decoderInitialized; } WebRtc_Word32 ACMGenericCodec::RegisterInNetEq( ACMNetEQ* netEq, const CodecInst& codecInst) { WebRtcNetEQ_CodecDef codecDef; WriteLockScoped wl(_codecWrapperLock); if(CodecDef(codecDef, codecInst) < 0) { // Failed to register WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "RegisterInNetEq: error, failed to register"); _registeredInNetEq = false; return -1; } else { if(netEq->AddCodec(&codecDef, _isMaster) < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "RegisterInNetEq: error, failed to add codec"); _registeredInNetEq = false; return -1; } // Registered _registeredInNetEq = true; return 0; } } WebRtc_Word16 ACMGenericCodec::EncoderParams( WebRtcACMCodecParams* encParams) { ReadLockScoped rl(_codecWrapperLock); return EncoderParamsSafe(encParams); } WebRtc_Word16 ACMGenericCodec::EncoderParamsSafe( WebRtcACMCodecParams* encParams) { // Codec parameters are valid only if the encoder is initialized if(_encoderInitialized) { WebRtc_Word32 currentRate; memcpy(encParams, &_encoderParams, sizeof(WebRtcACMCodecParams)); currentRate = encParams->codecInstant.rate; CurrentRate(currentRate); encParams->codecInstant.rate = currentRate; return 0; } else { encParams->codecInstant.plname[0] = '\0'; encParams->codecInstant.pltype = -1; encParams->codecInstant.pacsize = 0; encParams->codecInstant.rate = 0; WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "EncoderParamsSafe: error, encoder not initialized"); return -1; } } bool ACMGenericCodec::DecoderParams( WebRtcACMCodecParams* decParams, const WebRtc_UWord8 payloadType) { ReadLockScoped rl(_codecWrapperLock); return DecoderParamsSafe(decParams, payloadType); } bool ACMGenericCodec::DecoderParamsSafe( WebRtcACMCodecParams* decParams, const WebRtc_UWord8 payloadType) { // Decoder parameters are valid only if decoder is initialized if(_decoderInitialized) { if(payloadType == _decoderParams.codecInstant.pltype) { memcpy(decParams, &_decoderParams, sizeof(WebRtcACMCodecParams)); return true; } } decParams->codecInstant.plname[0] = '\0'; decParams->codecInstant.pltype = -1; decParams->codecInstant.pacsize = 0; decParams->codecInstant.rate = 0; return false; } WebRtc_Word16 ACMGenericCodec::ResetEncoder() { WriteLockScoped lockCodec(_codecWrapperLock); ReadLockScoped lockNetEq(*_netEqDecodeLock); return ResetEncoderSafe(); } WebRtc_Word16 ACMGenericCodec::ResetEncoderSafe() { if(!_encoderExist || !_encoderInitialized) { // We don't reset if doesn't exists or not initialized yet return 0; } _inAudioIxWrite = 0; _inAudioIxRead = 0; _inTimestampIxWrite = 0; _noMissedSamples = 0; _isAudioBuffFresh = true; memset(_inAudio, 0, AUDIO_BUFFER_SIZE_W16 * sizeof(WebRtc_Word16)); memset(_inTimestamp, 0, TIMESTAMP_BUFFER_SIZE_W32 * sizeof(WebRtc_Word32)); // Store DTX/VAD params bool enableVAD = _vadEnabled; bool enableDTX = _dtxEnabled; ACMVADMode mode = _vadMode; // Reset the encoder if(InternalResetEncoder() < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "ResetEncoderSafe: error in reset encoder"); return -1; } // Disable DTX & VAD this deletes the states // we like to have fresh start DisableDTX(); DisableVAD(); // Set DTX/VAD return SetVADSafe(enableDTX, enableVAD, mode); } WebRtc_Word16 ACMGenericCodec::InternalResetEncoder() { // For most of the codecs it is sufficient to // call their internal initialization. // There are some exceptions. // ---- // For iSAC we don't want to lose BWE history, // so for iSAC we have to over-write this function. // ---- return InternalInitEncoder(&_encoderParams); } WebRtc_Word16 ACMGenericCodec::InitEncoder( WebRtcACMCodecParams* codecParams, bool forceInitialization) { WriteLockScoped lockCodec(_codecWrapperLock); ReadLockScoped lockNetEq(*_netEqDecodeLock); return InitEncoderSafe(codecParams, forceInitialization); } WebRtc_Word16 ACMGenericCodec::InitEncoderSafe( WebRtcACMCodecParams* codecParams, bool forceInitialization) { // Check if we got a valid set of parameters WebRtc_Word16 mirrorID; WebRtc_Word16 codecNumber = ACMCodecDB::CodecNumber(&(codecParams->codecInstant), mirrorID); if(codecNumber < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "InitEncoderSafe: error, codec number negative"); return -1; } // Check if the parameters are for this codec if((_codecID >= 0) && (_codecID != codecNumber) && (_codecID != mirrorID)) { // The current codec is not the same as the one given by codecParams WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "InitEncoderSafe: current codec is not the same as the one given by codecParams"); return -1; } if(!CanChangeEncodingParam(codecParams->codecInstant)) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "InitEncoderSafe: cannot change encoding parameters"); return -1; } if(_encoderInitialized && !forceInitialization) { // The encoder is already initialized return 0; } WebRtc_Word16 status; if(!_encoderExist) { _encoderInitialized = false; status = CreateEncoder(); if(status < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "InitEncoderSafe: cannot create encoder"); return -1; } else { _encoderExist = true; } } _frameLenSmpl = (codecParams->codecInstant).pacsize; status = InternalInitEncoder(codecParams); if(status < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "InitEncoderSafe: error in init encoder"); _encoderInitialized = false; return -1; } else { memcpy(&_encoderParams, codecParams, sizeof(WebRtcACMCodecParams)); _encoderInitialized = true; if(_inAudio == NULL) { _inAudio = new WebRtc_Word16[AUDIO_BUFFER_SIZE_W16]; if(_inAudio == NULL) { return -1; } memset(_inAudio, 0, AUDIO_BUFFER_SIZE_W16 * sizeof(WebRtc_Word16)); } if(_inTimestamp == NULL) { _inTimestamp = new WebRtc_UWord32[TIMESTAMP_BUFFER_SIZE_W32]; if(_inTimestamp == NULL) { return -1; } memset(_inTimestamp, 0, sizeof(WebRtc_UWord32) * TIMESTAMP_BUFFER_SIZE_W32); } _isAudioBuffFresh = true; } status = SetVADSafe(codecParams->enableDTX, codecParams->enableVAD, codecParams->vadMode); _noChannels = codecParams->codecInstant.channels; return status; } bool ACMGenericCodec::CanChangeEncodingParam( CodecInst& /*codecInst*/) { return true; } WebRtc_Word16 ACMGenericCodec::InitDecoder( WebRtcACMCodecParams* codecParams, bool forceInitialization) { WriteLockScoped lockCodc(_codecWrapperLock); WriteLockScoped lockNetEq(*_netEqDecodeLock); return InitDecoderSafe(codecParams, forceInitialization); } WebRtc_Word16 ACMGenericCodec::InitDecoderSafe( WebRtcACMCodecParams* codecParams, bool forceInitialization) { WebRtc_Word16 mirrorID; // Check if we got a valid set of parameters WebRtc_Word16 codecNumber = ACMCodecDB::ReceiverCodecNumber(codecParams->codecInstant, mirrorID); if(codecNumber < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "InitDecoderSafe: error, invalid codec number"); return -1; } // Check if the parameters are for this codec if((_codecID >= 0) && (_codecID != codecNumber) && (_codecID != mirrorID)) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "InitDecoderSafe: current codec is not the same as the one given " "by codecParams"); // The current codec is not the same as the one given by codecParams return -1; } if(_decoderInitialized && !forceInitialization) { // The encoder is already initialized return 0; } WebRtc_Word16 status; if(!_decoderExist) { _decoderInitialized = false; status = CreateDecoder(); if(status < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "InitDecoderSafe: cannot create decoder"); return -1; } else { _decoderExist = true; } } status = InternalInitDecoder(codecParams); if(status < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "InitDecoderSafe: cannot init decoder"); _decoderInitialized = false; return -1; } else { // Store the parameters SaveDecoderParamSafe(codecParams); _decoderInitialized = true; } return 0; } WebRtc_Word16 ACMGenericCodec::ResetDecoder(WebRtc_Word16 payloadType) { WriteLockScoped lockCodec(_codecWrapperLock); WriteLockScoped lockNetEq(*_netEqDecodeLock); return ResetDecoderSafe(payloadType); } WebRtc_Word16 ACMGenericCodec::ResetDecoderSafe(WebRtc_Word16 payloadType) { WebRtcACMCodecParams decoderParams; if(!_decoderExist || !_decoderInitialized) { return 0; } // Initialization of the decoder should work for all // the codec. If there is a codec that has to keep // some states then we need to define a virtual and // overwrite in that codec DecoderParamsSafe(&decoderParams, (WebRtc_UWord8) payloadType); return InternalInitDecoder(&decoderParams); } void ACMGenericCodec::ResetNoMissedSamples() { WriteLockScoped cs(_codecWrapperLock); _noMissedSamples = 0; } void ACMGenericCodec::IncreaseNoMissedSamples( const WebRtc_Word16 noSamples) { _noMissedSamples += noSamples; } // Get the number of missed samples, this can be public WebRtc_UWord32 ACMGenericCodec::NoMissedSamples() const { ReadLockScoped cs(_codecWrapperLock); return _noMissedSamples; } void ACMGenericCodec::DestructEncoder() { WriteLockScoped wl(_codecWrapperLock); // Disable VAD and delete the instance if(_ptrVADInst != NULL) { WebRtcVad_Free(_ptrVADInst); _ptrVADInst = NULL; } _vadEnabled = false; _vadMode = VADNormal; //Disable DTX and delete the instance _dtxEnabled = false; if(_ptrDTXInst != NULL) { WebRtcCng_FreeEnc(_ptrDTXInst); _ptrDTXInst = NULL; } _numLPCParams = kNewCNGNumPLCParams; DestructEncoderSafe(); } void ACMGenericCodec::DestructDecoder() { WriteLockScoped wl(_codecWrapperLock); _decoderParams.codecInstant.pltype = -1; DestructDecoderSafe(); } WebRtc_Word16 ACMGenericCodec::SetBitRate( const WebRtc_Word32 bitRateBPS) { WriteLockScoped wl(_codecWrapperLock); return SetBitRateSafe(bitRateBPS); } WebRtc_Word16 ACMGenericCodec::SetBitRateSafe( const WebRtc_Word32 bitRateBPS) { // If the codec can change the bit-rate this function // should be overwritten, otherewise the only acceptable // value is the one that is in database. CodecInst codecParams; if(ACMCodecDB::Codec(_codecID, &codecParams) < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "SetBitRateSafe: error in ACMCodecDB::Codec"); return -1; } if(codecParams.rate != bitRateBPS) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "SetBitRateSafe: rate value is not acceptable"); return -1; } else { return 0; } } WebRtc_Word32 ACMGenericCodec::GetEstimatedBandwidth() { WriteLockScoped wl(_codecWrapperLock); return GetEstimatedBandwidthSafe(); } WebRtc_Word32 ACMGenericCodec::GetEstimatedBandwidthSafe() { // All codecs but iSAC will return -1 return -1; } WebRtc_Word32 ACMGenericCodec::SetEstimatedBandwidth( WebRtc_Word32 estimatedBandwidth) { WriteLockScoped wl(_codecWrapperLock); return SetEstimatedBandwidthSafe(estimatedBandwidth); } WebRtc_Word32 ACMGenericCodec::SetEstimatedBandwidthSafe( WebRtc_Word32 /*estimatedBandwidth*/) { // All codecs but iSAC will return -1 return -1; } WebRtc_Word32 ACMGenericCodec::GetRedPayload( WebRtc_UWord8* redPayload, WebRtc_Word16* payloadBytes) { WriteLockScoped wl(_codecWrapperLock); return GetRedPayloadSafe(redPayload, payloadBytes); } WebRtc_Word32 ACMGenericCodec::GetRedPayloadSafe( WebRtc_UWord8* /* redPayload */, WebRtc_Word16* /* payloadBytes */) { return -1; // Do nothing by default } WebRtc_Word16 ACMGenericCodec::CreateEncoder() { WebRtc_Word16 status = 0; if(!_encoderExist) { status = InternalCreateEncoder(); // We just created the codec and obviously it is not initialized _encoderInitialized = false; } if(status < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "CreateEncoder: error in internal create encoder"); _encoderExist = false; } else { _encoderExist = true; } return status; } WebRtc_Word16 ACMGenericCodec::CreateDecoder() { WebRtc_Word16 status = 0; if(!_decoderExist) { status = InternalCreateDecoder(); // Decoder just created and obviously it is not initialized _decoderInitialized = false; } if(status < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "CreateDecoder: error in internal create decoder"); _decoderExist = false; } else { _decoderExist = true; } return status; } void ACMGenericCodec::DestructEncoderInst(void* ptrInst) { if(ptrInst != NULL) { WriteLockScoped lockCodec(_codecWrapperLock); ReadLockScoped lockNetEq(*_netEqDecodeLock); InternalDestructEncoderInst(ptrInst); } } WebRtc_Word16 ACMGenericCodec::AudioBuffer( WebRtcACMAudioBuff& audioBuff) { ReadLockScoped cs(_codecWrapperLock); memcpy(audioBuff.inAudio, _inAudio, AUDIO_BUFFER_SIZE_W16 * sizeof(WebRtc_Word16)); audioBuff.inAudioIxRead = _inAudioIxRead; audioBuff.inAudioIxWrite = _inAudioIxWrite; memcpy(audioBuff.inTimestamp, _inTimestamp, TIMESTAMP_BUFFER_SIZE_W32*sizeof(WebRtc_UWord32)); audioBuff.inTimestampIxWrite = _inTimestampIxWrite; audioBuff.lastTimestamp = _lastTimestamp; return 0; } WebRtc_Word16 ACMGenericCodec::SetAudioBuffer( WebRtcACMAudioBuff& audioBuff) { WriteLockScoped cs(_codecWrapperLock); memcpy(_inAudio, audioBuff.inAudio, AUDIO_BUFFER_SIZE_W16 * sizeof(WebRtc_Word16)); _inAudioIxRead = audioBuff.inAudioIxRead; _inAudioIxWrite = audioBuff.inAudioIxWrite; memcpy(_inTimestamp, audioBuff.inTimestamp, TIMESTAMP_BUFFER_SIZE_W32*sizeof(WebRtc_UWord32)); _inTimestampIxWrite = audioBuff.inTimestampIxWrite; _lastTimestamp = audioBuff.lastTimestamp; _isAudioBuffFresh = false; return 0; } WebRtc_UWord32 ACMGenericCodec::LastEncodedTimestamp() const { ReadLockScoped cs(_codecWrapperLock); return _lastEncodedTimestamp; } WebRtc_UWord32 ACMGenericCodec::EarliestTimestamp() const { ReadLockScoped cs(_codecWrapperLock); return _inTimestamp[0]; } WebRtc_Word16 ACMGenericCodec::SetVAD( const bool enableDTX, const bool enableVAD, const ACMVADMode mode) { WriteLockScoped cs(_codecWrapperLock); return SetVADSafe(enableDTX, enableVAD, mode); } WebRtc_Word16 ACMGenericCodec::SetVADSafe( const bool enableDTX, const bool enableVAD, const ACMVADMode mode) { if(enableDTX) { // Make G729 AnnexB a special case if (!STR_CASE_CMP(_encoderParams.codecInstant.plname, "G729") && !_hasInternalDTX) { if (ACMGenericCodec::EnableDTX() < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "SetVADSafe: error in enable DTX"); return -1; } } else { if(EnableDTX() < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "SetVADSafe: error in enable DTX"); return -1; } } if(_hasInternalDTX) { // Codec has internal DTX, practically we don't need WebRtc VAD, // however, we let the user to turn it on if they need call-backs // on silence. Store VAD mode for future even if VAD is off. _vadMode = mode; return (enableVAD)? EnableVAD(mode):DisableVAD(); } else { // Codec does not have internal DTX so enabling DTX requires an // active VAD. 'enableDTX == true' overwrites VAD status. if(EnableVAD(mode) < 0) { // If we cannot create VAD we have to disable DTX if(!_vadEnabled) { DisableDTX(); } WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "SetVADSafe: error in enable VAD"); return -1; } // Return '1', to let the caller know VAD was turned on, even if the // function was called with VAD='false' if (enableVAD == false) { return 1; } else { return 0; } } } else { // Make G729 AnnexB a special case if (!STR_CASE_CMP(_encoderParams.codecInstant.plname, "G729") && !_hasInternalDTX) { ACMGenericCodec::DisableDTX(); } else { DisableDTX(); } return (enableVAD)? EnableVAD(mode):DisableVAD(); } } WebRtc_Word16 ACMGenericCodec::EnableDTX() { if(_hasInternalDTX) { // We should not be here if we have internal DTX // this function should be overwritten by the derived // class in this case return -1; } if(!_dtxEnabled) { if(WebRtcCng_CreateEnc(&_ptrDTXInst) < 0) { _ptrDTXInst = false; return -1; } WebRtc_UWord16 freqHz; EncoderSampFreq(freqHz); if(WebRtcCng_InitEnc(_ptrDTXInst, (WebRtc_Word16)freqHz, ACM_SID_INTERVAL_MSEC, _numLPCParams) < 0) { // Couldn't initialize, has to return -1, and free the memory WebRtcCng_FreeEnc(_ptrDTXInst); _ptrDTXInst = NULL; return -1; } _dtxEnabled = true; } return 0; } WebRtc_Word16 ACMGenericCodec::DisableDTX() { if(_hasInternalDTX) { // We should not be here if we have internal DTX // this function should be overwritten by the derived // class in this case return -1; } if(_ptrDTXInst != NULL) { WebRtcCng_FreeEnc(_ptrDTXInst); _ptrDTXInst = NULL; } _dtxEnabled = false; return 0; } WebRtc_Word16 ACMGenericCodec::EnableVAD( ACMVADMode mode) { if((mode < VADNormal) || (mode > VADVeryAggr)) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "EnableVAD: error in VAD mode range"); return -1; } if(!_vadEnabled) { if(WebRtcVad_Create(&_ptrVADInst) < 0) { _ptrVADInst = NULL; WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "EnableVAD: error in create VAD"); return -1; } if(WebRtcVad_Init(_ptrVADInst) < 0) { WebRtcVad_Free(_ptrVADInst); _ptrVADInst = NULL; WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "EnableVAD: error in init VAD"); return -1; } } // Set the vad mode to the given value if(WebRtcVad_set_mode(_ptrVADInst, mode) < 0) { // We failed to set the mode and we have to return -1. If // we already have a working VAD (_vadEnabled == true) then // we leave it to work. otherwise, the following will be // executed. if(!_vadEnabled) { // We just created the instance but cannot set the mode // we have to free the memomry. WebRtcVad_Free(_ptrVADInst); _ptrVADInst = NULL; } WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceAudioCoding, _uniqueID, "EnableVAD: failed to set the VAD mode"); return -1; } _vadMode = mode; _vadEnabled = true; return 0; } WebRtc_Word16 ACMGenericCodec::DisableVAD() { if(_ptrVADInst != NULL) { WebRtcVad_Free(_ptrVADInst); _ptrVADInst = NULL; } _vadEnabled = false; return 0; } WebRtc_Word32 ACMGenericCodec::ReplaceInternalDTX( const bool replaceInternalDTX) { WriteLockScoped cs(_codecWrapperLock); return ReplaceInternalDTXSafe(replaceInternalDTX); } WebRtc_Word32 ACMGenericCodec::ReplaceInternalDTXSafe( const bool /* replaceInternalDTX */) { return -1; } WebRtc_Word32 ACMGenericCodec::IsInternalDTXReplaced( bool* internalDTXReplaced) { WriteLockScoped cs(_codecWrapperLock); return IsInternalDTXReplacedSafe(internalDTXReplaced); } WebRtc_Word32 ACMGenericCodec::IsInternalDTXReplacedSafe( bool* internalDTXReplaced) { *internalDTXReplaced = false; return 0; } WebRtc_Word16 ACMGenericCodec::ProcessFrameVADDTX( WebRtc_UWord8* bitStream, WebRtc_Word16* bitStreamLenByte, WebRtc_Word16* samplesProcessed) { if(!_vadEnabled) { // VAD not enabled, set all vadLable[] to 1 (speech detected) for(WebRtc_Word16 n = 0; n < MAX_FRAME_SIZE_10MSEC; n++) { _vadLabel[n] = 1; } *samplesProcessed = 0; return 0; } WebRtc_UWord16 freqHz; EncoderSampFreq(freqHz); // Calculate number of samples in 10 ms blocks, and number ms in one frame WebRtc_Word16 samplesIn10Msec = (WebRtc_Word16)(freqHz / 100); WebRtc_Word32 frameLenMsec = (((WebRtc_Word32)_frameLenSmpl * 1000) / freqHz); WebRtc_Word16 status; WebRtc_Word16 vadFlag = 0; // Vector for storing maximum 30 ms of mono audio at 32 kHz WebRtc_Word16 audio[960]; // Calculate number of VAD-blocks to process, and number of samples in each block. int noSamplesToProcess[2]; if (frameLenMsec == 40) { // 20 ms in each VAD block noSamplesToProcess[0] = noSamplesToProcess[1] = 2*samplesIn10Msec; } else { // For 10-30 ms framesizes, second VAD block will be size zero ms, // for 50 and 60 ms first VAD block will be 30 ms. noSamplesToProcess[0] = (frameLenMsec > 30)? 3*samplesIn10Msec : _frameLenSmpl; noSamplesToProcess[1] = _frameLenSmpl-noSamplesToProcess[0]; } int offSet = 0; int loops = (noSamplesToProcess[1]>0) ? 2 : 1; for (int i=0; i 0 once per 100 ms *bitStreamLenByte += bitStreamLen; } // Check if all samples got processed by the DTX if(*samplesProcessed != noSamplesToProcess[i]*_noChannels) { // Set to zero since something went wrong. Shouldn't happen. *samplesProcessed = 0; } } if(*samplesProcessed > 0) { // The block contains inactive speech, and is processed by DTX. // Discontinue running VAD. break; } } return status; } WebRtc_Word16 ACMGenericCodec::SamplesLeftToEncode() { ReadLockScoped rl(_codecWrapperLock); return (_frameLenSmpl <= _inAudioIxWrite)? 0:(_frameLenSmpl - _inAudioIxWrite); } WebRtc_Word32 ACMGenericCodec::UnregisterFromNetEq( ACMNetEQ* netEq, WebRtc_Word16 payloadType) { WriteLockScoped wl(_codecWrapperLock); if(!_registeredInNetEq) { return 0; } if(UnregisterFromNetEqSafe(netEq, payloadType) < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "UnregisterFromNetEq: error, cannot unregister from NetEq"); _registeredInNetEq = true; return -1; } else { _registeredInNetEq = false; return 0; } } void ACMGenericCodec::SetUniqueID( const WebRtc_UWord32 id) { _uniqueID = id; } bool ACMGenericCodec::IsAudioBufferFresh() const { ReadLockScoped rl(_codecWrapperLock); return _isAudioBuffFresh; } // This function is replaced by codec specific functions for some codecs WebRtc_Word16 ACMGenericCodec::EncoderSampFreq(WebRtc_UWord16& sampFreqHz) { WebRtc_Word32 f; f = ACMCodecDB::CodecFreq(_codecID); if(f < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "EncoderSampFreq: codec frequency is negative"); return -1; } else { sampFreqHz = (WebRtc_UWord16)f; return 0; } } WebRtc_Word32 ACMGenericCodec::ConfigISACBandwidthEstimator( const WebRtc_UWord8 /* initFrameSizeMsec */, const WebRtc_UWord16 /* initRateBitPerSec */, const bool /* enforceFrameSize */) { WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, _uniqueID, "The send-codec is not iSAC, failed to config iSAC bandwidth estimator."); return -1; } WebRtc_Word32 ACMGenericCodec::SetISACMaxRate( const WebRtc_UWord32 /* maxRateBitPerSec */) { WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, _uniqueID, "The send-codec is not iSAC, failed to set iSAC max rate."); return -1; } WebRtc_Word32 ACMGenericCodec::SetISACMaxPayloadSize( const WebRtc_UWord16 /* maxPayloadLenBytes */) { WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, _uniqueID, "The send-codec is not iSAC, failed to set iSAC max payload-size."); return -1; } void ACMGenericCodec::SaveDecoderParam( const WebRtcACMCodecParams* codecParams) { WriteLockScoped wl(_codecWrapperLock); SaveDecoderParamSafe(codecParams); } void ACMGenericCodec::SaveDecoderParamSafe( const WebRtcACMCodecParams* codecParams) { memcpy(&_decoderParams, codecParams, sizeof(WebRtcACMCodecParams)); } WebRtc_Word16 ACMGenericCodec::UpdateEncoderSampFreq( WebRtc_UWord16 /* encoderSampFreqHz */) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "It is asked for a change in smapling frequency while the \ current send-codec supports only one sampling rate."); return -1; } void ACMGenericCodec::SetIsMaster( bool isMaster) { WriteLockScoped wl(_codecWrapperLock); _isMaster = isMaster; } WebRtc_Word16 ACMGenericCodec::REDPayloadISAC( const WebRtc_Word32 /* isacRate */, const WebRtc_Word16 /* isacBwEstimate */, WebRtc_UWord8* /* payload */, WebRtc_Word16* /* payloadLenBytes */) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "Error: REDPayloadISAC is an iSAC specific function"); return -1; } } // namespace webrtc