/* * 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 "acm_codec_database.h" #include "acm_common_defs.h" #include "acm_dtmf_detection.h" #include "acm_generic_codec.h" #include "acm_resampler.h" #include "audio_coding_module_impl.h" #include "critical_section_wrapper.h" #include "engine_configurations.h" #include "rw_lock_wrapper.h" #include "trace.h" #include #include #ifdef ACM_QA_TEST # include #endif #ifdef TIMED_LOGGING char message[500]; #include "../test/timedtrace.h" #include #define LOGWITHTIME(logString) \ sprintf(message, logString, _id); \ _trace.TimedLogg(message); #else #define LOGWITHTIME(logString) #endif namespace webrtc { enum { kACMToneEnd = 999 }; AudioCodingModuleImpl::AudioCodingModuleImpl( const WebRtc_Word32 id): _packetizationCallback(NULL), _id(id), _lastTimestamp(0), _lastInTimestamp(0), _vadEnabled(false), _dtxEnabled(false), _vadMode(VADNormal), _stereoSend(false), _currentSendCodecIdx(-1), // invalid value _sendCodecRegistered(false), _acmCritSect(CriticalSectionWrapper::CreateCriticalSection()), _vadCallback(NULL), _lastRecvAudioCodecPlType(255), _isFirstRED(true), _fecEnabled(false), _fragmentation(NULL), _lastFECTimestamp(0), _receiveREDPayloadType(255), // invalid value _previousPayloadType(255), _dummyRTPHeader(NULL), _receiverInitialized(false), _dtmfDetector(NULL), _dtmfCallback(NULL), _lastDetectedTone(kACMToneEnd), _callbackCritSect(CriticalSectionWrapper::CreateCriticalSection()) { _lastTimestamp = 0xD87F3F9F; _lastInTimestamp = 0xD87F3F9F; // nullify the codec name strncpy(_sendCodecInst.plname, "noCodecRegistered", 31); ACMCodecDB::initACMCodecDB(); for (WebRtc_Word16 i = 0; i < MAX_NR_OF_CODECS; i++) { _codecs[i] = NULL; _registeredPlTypes[i] = -1; _stereoReceive[i] = false; _slaveCodecs[i] = NULL; _mirrorCodecIdx[i] = -1; } _netEq.SetUniqueId(_id); // Allocate memory for RED _redBuffer = new WebRtc_UWord8[MAX_PAYLOAD_SIZE_BYTE]; _fragmentation = new RTPFragmentationHeader; _fragmentation->fragmentationVectorSize = 2; _fragmentation->fragmentationOffset = new WebRtc_UWord32[2]; _fragmentation->fragmentationLength = new WebRtc_UWord32[2]; _fragmentation->fragmentationTimeDiff = new WebRtc_UWord16[2]; _fragmentation->fragmentationPlType = new WebRtc_UWord8[2]; // Register the default payload type for RED and for // CNG for the three frequencies 8, 16 and 32 kHz for (int i = (ACMCodecDB::_noOfCodecs - 1); i>=0; i--) { if((STR_CASE_CMP(ACMCodecDB::_mycodecs[i].plname, "red") == 0)) { _redPayloadType = ACMCodecDB::_mycodecs[i].pltype; } else if ((STR_CASE_CMP(ACMCodecDB::_mycodecs[i].plname, "CN") == 0)) { if (ACMCodecDB::_mycodecs[i].plfreq == 8000) { memcpy(&_cngNB, &ACMCodecDB::_mycodecs[i], sizeof(_cngNB)); } else if (ACMCodecDB::_mycodecs[i].plfreq == 16000) { memcpy(&_cngWB, &ACMCodecDB::_mycodecs[i], sizeof(_cngWB)); } else if (ACMCodecDB::_mycodecs[i].plfreq == 32000) { memcpy(&_cngSWB, &ACMCodecDB::_mycodecs[i], sizeof(_cngSWB)); } } } if(InitializeReceiverSafe() < 0 ) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "Cannot initialize reciever"); } #ifdef TIMED_LOGGING _trace.SetUp("TimedLogg.txt"); #endif #ifdef ACM_QA_TEST char fileName[500]; sprintf(fileName, "ACM_QA_incomingPL_%03d_%d%d%d%d%d%d.dat", _id, rand() % 10, rand() % 10, rand() % 10, rand() % 10, rand() % 10, rand() % 10); _incomingPL = fopen(fileName, "wb"); sprintf(fileName, "ACM_QA_outgoingPL_%03d_%d%d%d%d%d%d.dat", _id, rand() % 10, rand() % 10, rand() % 10, rand() % 10, rand() % 10, rand() % 10); _outgoingPL = fopen(fileName, "wb"); #endif WEBRTC_TRACE(webrtc::kTraceMemory, webrtc::kTraceAudioCoding, id, "Created"); } AudioCodingModuleImpl::~AudioCodingModuleImpl() { { CriticalSectionScoped lock(*_acmCritSect); _currentSendCodecIdx = -1; for (WebRtc_Word16 i=0; i < MAX_NR_OF_CODECS; i++) { if (_codecs[i] != NULL) { assert(_mirrorCodecIdx[i] > -1); if(_codecs[_mirrorCodecIdx[i]] != NULL) { delete _codecs[_mirrorCodecIdx[i]]; _codecs[_mirrorCodecIdx[i]] = NULL; } _codecs[i] = NULL; } if(_slaveCodecs[i] != NULL) { assert(_mirrorCodecIdx[i] > -1); if(_slaveCodecs[_mirrorCodecIdx[i]] != NULL) { delete _slaveCodecs[_mirrorCodecIdx[i]]; _slaveCodecs[_mirrorCodecIdx[i]] = NULL; } _slaveCodecs[i] = NULL; } } if(_dtmfDetector != NULL) { delete _dtmfDetector; _dtmfDetector = NULL; } if(_dummyRTPHeader != NULL) { delete _dummyRTPHeader; _dummyRTPHeader = NULL; } if(_redBuffer != NULL) { delete [] _redBuffer; _redBuffer = NULL; } if(_fragmentation != NULL) { // Only need to delete fragmentation header, it will clean // up it's own memory delete _fragmentation; _fragmentation = NULL; } } #ifdef ACM_QA_TEST if(_incomingPL != NULL) { fclose(_incomingPL); } if(_outgoingPL != NULL) { fclose(_outgoingPL); } #endif delete _callbackCritSect; _callbackCritSect = NULL; delete _acmCritSect; _acmCritSect = NULL; WEBRTC_TRACE(webrtc::kTraceMemory, webrtc::kTraceAudioCoding, _id, "Destroyed"); } WebRtc_Word32 AudioCodingModuleImpl::ChangeUniqueId( const WebRtc_Word32 id) { WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, "ChangeUniqueId(new id:%d)", id); { CriticalSectionScoped lock(*_acmCritSect); _id = id; #ifdef ACM_QA_TEST if(_incomingPL != NULL) { fclose(_incomingPL); } if(_outgoingPL != NULL) { fclose(_outgoingPL); } char fileName[500]; sprintf(fileName, "ACM_QA_incomingPL_%03d_%d%d%d%d%d%d.dat", _id, rand() % 10, rand() % 10, rand() % 10, rand() % 10, rand() % 10, rand() % 10); _incomingPL = fopen(fileName, "wb"); sprintf(fileName, "ACM_QA_outgoingPL_%03d_%d%d%d%d%d%d.dat", _id, rand() % 10, rand() % 10, rand() % 10, rand() % 10, rand() % 10, rand() % 10); _outgoingPL = fopen(fileName, "wb"); #endif for (WebRtc_Word16 i = 0; i < MAX_NR_OF_CODECS; i++) { if(_codecs[i] != NULL) { _codecs[i]->SetUniqueID(id); } } } _netEq.SetUniqueId(_id); return 0; } WebRtc_Word32 AudioCodingModuleImpl::Version( WebRtc_Word8* version, WebRtc_UWord32& remainingBufferInBytes, WebRtc_UWord32& position) const { WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, "Version()"); if(version == NULL) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "Input buffer is NULL"); return -1; } return GetVersion(version, remainingBufferInBytes, position); } // returns the number of milliseconds until the module want a // worker thread to call Process WebRtc_Word32 AudioCodingModuleImpl::TimeUntilNextProcess() { CriticalSectionScoped lock(*_acmCritSect); if(!HaveValidEncoder("TimeUntilNextProcess")) { return -1; } return _codecs[_currentSendCodecIdx]->SamplesLeftToEncode() / (_sendCodecInst.plfreq / 1000); } // Process any pending tasks such as timeouts WebRtc_Word32 AudioCodingModuleImpl::Process() { WebRtc_UWord8 bitStream[2 * MAX_PAYLOAD_SIZE_BYTE]; // Make room for 1 RED payload WebRtc_Word16 lengthBytes = 2 * MAX_PAYLOAD_SIZE_BYTE; WebRtc_UWord32 rtpTimestamp; WebRtc_Word16 status; WebRtcACMEncodingType encodingType; FrameType frameType = kAudioFrameSpeech; WebRtc_Word16 redLengthBytes; WebRtc_UWord8 currentPayloadType; bool hasDataToSend = false; bool fecActive = false; WebRtc_UWord32 dummyFragLength; // keep the scope of the ACM critical section limited { CriticalSectionScoped lock(*_acmCritSect); if(!HaveValidEncoder("Process")) { return -1; } status = _codecs[_currentSendCodecIdx]->Encode(bitStream, &lengthBytes, &rtpTimestamp, &encodingType); if (status < 0) // Encode failed { // logging error WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "Process(): Encoding Failed"); lengthBytes = 0; return -1; } else if(status == 0) { // Not enough data return 0; } else { switch(encodingType) { case kNoEncoding: { currentPayloadType = _previousPayloadType; frameType = kFrameEmpty; lengthBytes = 0; break; } case kActiveNormalEncoded: case kPassiveNormalEncoded: { currentPayloadType = (WebRtc_UWord8)_sendCodecInst.pltype; frameType = kAudioFrameSpeech; break; } case kPassiveDTXNB: { currentPayloadType = (WebRtc_UWord8)_cngNB.pltype; frameType = kAudioFrameCN; _isFirstRED = true; break; } case kPassiveDTXWB: { currentPayloadType = (WebRtc_UWord8)_cngWB.pltype; frameType = kAudioFrameCN; _isFirstRED = true; break; } case kPassiveDTXSWB: { currentPayloadType = (WebRtc_UWord8)_cngSWB.pltype; frameType = kAudioFrameCN; _isFirstRED = true; break; } default: { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "Process(): Wrong Encoding-Type"); return -1; } } hasDataToSend = true; _previousPayloadType = currentPayloadType; // Redundancy encode is done here, // the two bitstreams packetized into // one RTP packet and the fragmentation points // are set. // Only apply RED on speech data. if((_fecEnabled) && ((encodingType == kActiveNormalEncoded) || (encodingType == kPassiveNormalEncoded))) { // FEC is enabled within this scope. // // Note that, a special solution exists for iSAC since it is the only codec for // which getRedPayload has a non-empty implementation. // // Summary of the FEC scheme below (use iSAC as example): // // 1st (_firstRED is true) encoded iSAC frame (primary #1) => // - call getRedPayload() and store redundancy for packet #1 in second // fragment of RED buffer (old data) // - drop the primary iSAC frame // - don't call SendData // 2nd (_firstRED is false) encoded iSAC frame (primary #2) => // - store primary #2 in 1st fragment of RED buffer and send the combined // packet // - the transmitted packet contains primary #2 (new) and reduncancy for // packet #1 (old) // - call getRedPayload() and store redundancy for packet #2 in second // fragment of RED buffer // // ... // // Nth encoded iSAC frame (primary #N) => // - store primary #N in 1st fragment of RED buffer and send the combined // packet // - the transmitted packet contains primary #N (new) and reduncancy for // packet #(N-1) (old) // - call getRedPayload() and store redundancy for packet #N in second // fragment of RED buffer // // For all other codecs, getRedPayload does nothing and returns -1 => // redundant data is only a copy. // // First combined packet contains : #2 (new) and #1 (old) // Second combined packet contains: #3 (new) and #2 (old) // Third combined packet contains : #4 (new) and #3 (old) // // Hence, even if every second packet is dropped, perfect reconstruction is // possible. fecActive = true; hasDataToSend = false; if(!_isFirstRED) // skip this part for the first packet in a RED session { // Rearrange bitStream such that FEC packets are included. // Replace bitStream now that we have stored current bitStream. memcpy(bitStream + _fragmentation->fragmentationOffset[1], _redBuffer, _fragmentation->fragmentationLength[1]); // Update the fragmentation time difference vector WebRtc_UWord16 timeSinceLastTimestamp = WebRtc_UWord16(rtpTimestamp - _lastFECTimestamp); // Update fragmentation vectors _fragmentation->fragmentationPlType[1] = _fragmentation->fragmentationPlType[0]; _fragmentation->fragmentationTimeDiff[1] = timeSinceLastTimestamp; hasDataToSend = true; } // Insert new packet length. _fragmentation->fragmentationLength[0] = lengthBytes; // Insert new packet payload type. _fragmentation->fragmentationPlType[0] = currentPayloadType; _lastFECTimestamp = rtpTimestamp; // can be modified by the getRedPayload() call if iSAC is utilized redLengthBytes = lengthBytes; // A fragmentation header is provided => packetization according to RFC 2198 // (RTP Payload for Redundant Audio Data) will be used. // First fragment is the current data (new). // Second fragment is the previous data (old). lengthBytes = static_cast (_fragmentation->fragmentationLength[0] + _fragmentation->fragmentationLength[1]); // Get, and store, redundant data from the encoder based on the recently // encoded frame. // NOTE - only iSAC contains an implementation; all other codecs does nothing // and returns -1. if (_codecs[_currentSendCodecIdx]->GetRedPayload(_redBuffer, &redLengthBytes) == -1) { // The codec was not iSAC => use current encoder output as redundant data // instead (trivial FEC scheme) memcpy(_redBuffer, bitStream, redLengthBytes); } // Temporary storing RED length dummyFragLength = redLengthBytes; _isFirstRED = false; // Update payload type with RED payload type currentPayloadType = _redPayloadType; } } } if(hasDataToSend) { CriticalSectionScoped lock(*_callbackCritSect); #ifdef ACM_QA_TEST if(_outgoingPL != NULL) { fwrite(&rtpTimestamp, sizeof(WebRtc_UWord32), 1, _outgoingPL); fwrite(¤tPayloadType, sizeof(WebRtc_UWord8), 1, _outgoingPL); fwrite(&lengthBytes, sizeof(WebRtc_Word16), 1, _outgoingPL); } #endif if(_packetizationCallback != NULL) { if (fecActive) { _packetizationCallback->SendData(frameType, currentPayloadType, rtpTimestamp, bitStream, lengthBytes, _fragmentation); } else { _packetizationCallback->SendData(frameType, currentPayloadType, rtpTimestamp, bitStream, lengthBytes, NULL); } } // This is for test if(_vadCallback != NULL) { _vadCallback->InFrameType(((WebRtc_Word16)encodingType)); } } if (fecActive) { _fragmentation->fragmentationLength[1] = dummyFragLength; } return lengthBytes; } ///////////////////////////////////////// // Sender // // Initialize send codec WebRtc_Word32 AudioCodingModuleImpl::InitializeSender() { WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, "InitializeSender()"); CriticalSectionScoped lock(*_acmCritSect); _sendCodecRegistered = false; _currentSendCodecIdx = -1; // invalid value _sendCodecInst.plname[0] = '\0'; for(WebRtc_Word16 codecCntr = 0; codecCntr < MAX_NR_OF_CODECS; codecCntr++) { if(_codecs[codecCntr] != NULL) { _codecs[codecCntr]->DestructEncoder(); } } // Initialize FEC/RED _isFirstRED = true; if(_fecEnabled) { if(_redBuffer != NULL) { memset(_redBuffer, 0, MAX_PAYLOAD_SIZE_BYTE); } if(_fragmentation != NULL) { _fragmentation->fragmentationVectorSize = 2; _fragmentation->fragmentationOffset[0] = 0; _fragmentation->fragmentationOffset[0] = MAX_PAYLOAD_SIZE_BYTE; memset(_fragmentation->fragmentationLength, 0, sizeof(WebRtc_UWord32) * 2); memset(_fragmentation->fragmentationTimeDiff, 0, sizeof(WebRtc_UWord16) * 2); memset(_fragmentation->fragmentationPlType, 0, sizeof(WebRtc_UWord8) * 2); } } return 0; } WebRtc_Word32 AudioCodingModuleImpl::ResetEncoder() { WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, "ResetEncoder()"); CriticalSectionScoped lock(*_acmCritSect); if(!HaveValidEncoder("ResetEncoder")) { return -1; } return _codecs[_currentSendCodecIdx]->ResetEncoder(); } void AudioCodingModuleImpl::UnregisterSendCodec() { CriticalSectionScoped lock(*_acmCritSect); _sendCodecRegistered = false; _currentSendCodecIdx = -1; // invalid value return; } ACMGenericCodec* AudioCodingModuleImpl::CreateCodec( const CodecInst& codec) { WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, "CreateCodec()"); ACMGenericCodec* myCodec = NULL; myCodec = ACMCodecDB::CreateCodecInstance(&codec); if(myCodec == NULL) { // Error, could not create the codec // logging error WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "ACMCodecDB::CreateCodecInstance() failed in \ CreateCodec()"); return myCodec; } myCodec->SetUniqueID(_id); myCodec->SetNetEqDecodeLock(_netEq.DecodeLock()); return myCodec; } // can be called multiple times for Codec, CNG, RED WebRtc_Word32 AudioCodingModuleImpl::RegisterSendCodec( const CodecInst& sendCodec) { WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, "Registering Send Codec"); if((sendCodec.channels != 1) && (sendCodec.channels != 2)) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "Registering Send codec failed due to wrong number of channels, %d. Only\ mono codecs are supported, i.e. channels=1.", sendCodec.channels); return -1; } WebRtc_Word8 errMsg[500]; WebRtc_Word16 mirrorId; WebRtc_Word16 codecID = ACMCodecDB::CodecNumber(&sendCodec, mirrorId, errMsg, 500); CriticalSectionScoped lock(*_acmCritSect); // Check for reported errors from function CodecNumber() if(codecID < 0) { if(!_sendCodecRegistered) { // This values has to be NULL if there is no codec registered _currentSendCodecIdx = -1; // invalid value } WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, errMsg); // Failed to register Send Codec return -1; } // telephone-event cannot be a send codec if(!STR_CASE_CMP(sendCodec.plname, "telephone-event")) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "telephone-event cannot be registered as send codec"); return -1; } // RED can be registered with other payload type. If not registered a default // payload type is used. if(!STR_CASE_CMP(sendCodec.plname, "red")) { // Check if the payload-type is valid if(ACMCodecDB::ValidPayloadType(sendCodec.pltype) < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "Invalid payload-type %d for %s.", sendCodec.pltype, sendCodec.plname); return -1; } // Set RED payload type _redPayloadType = (WebRtc_UWord8)sendCodec.pltype; return 0; } // CNG can be registered with other payload type. If not registered the // default payload types will be used: CNNB=13 (fixed), CNWB=97, CNSWB=98 if(!STR_CASE_CMP(sendCodec.plname, "CN")) { // CNG is registered switch(sendCodec.plfreq) { case 8000: { memcpy(&_cngNB, &sendCodec, sizeof(_cngNB)); break; } case 16000: { memcpy(&_cngWB, &sendCodec, sizeof(_cngWB)); break; } case 32000: { memcpy(&_cngSWB, &sendCodec, sizeof(_cngSWB)); break; } default : { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "RegisterSendCodec() failed, invalid frequency for CNG registeration"); return -1; } } return 0; } // Check if the payload-type is valid if(ACMCodecDB::ValidPayloadType(sendCodec.pltype) < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "Invalid payload-type %d for %s.", sendCodec.pltype, sendCodec.plname); return -1; } // Check if codec supports the number of channels if(ACMCodecDB::_channelSupport[codecID] < sendCodec.channels) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "%d number of channels not supportedn for %s.", sendCodec.channels, sendCodec.plname); return -1; } // Set Stereo if (sendCodec.channels == 2) { _stereoSend = true; } // check if the codec is already registered as send codec bool oldCodecFamily; if(_sendCodecRegistered) { WebRtc_Word16 sendCodecMirrorID; WebRtc_Word16 sendCodecID = ACMCodecDB::CodecNumber(&_sendCodecInst, sendCodecMirrorID); assert(sendCodecID >= 0); oldCodecFamily = (sendCodecID == codecID) || (mirrorId == sendCodecMirrorID); } else { oldCodecFamily = false; } // If new codec, register if (!oldCodecFamily) { if(_codecs[mirrorId] == NULL) { _codecs[mirrorId] = CreateCodec(sendCodec); if(_codecs[mirrorId] == NULL) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "Cannot Create the codec"); return -1; } _mirrorCodecIdx[mirrorId] = mirrorId; } if(mirrorId != codecID) { _codecs[codecID] = _codecs[mirrorId]; _mirrorCodecIdx[codecID] = mirrorId; } ACMGenericCodec* tmpCodecPtr = _codecs[codecID]; WebRtc_Word16 status; WebRtcACMCodecParams codecParams; memcpy(&(codecParams.codecInstant), &sendCodec, sizeof(CodecInst)); codecParams.enableVAD = _vadEnabled; codecParams.enableDTX = _dtxEnabled; codecParams.vadMode = _vadMode; // force initialization status = tmpCodecPtr->InitEncoder(&codecParams, true); // Check if VAD was turned on, or if error is reported if (status == 1) { _vadEnabled = true; } else if (status < 0) { // could not initialize the encoder // Check if already have a registered codec // Depending on that differet messages are logged if(!_sendCodecRegistered) { _currentSendCodecIdx = -1; // invalid value WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "Cannot Initialize the encoder No Encoder is registered"); } else { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "Cannot Initialize the encoder, continue encoding \ with the previously registered codec"); } return -1; } // Everything is fine so we can replace the previous codec // with this one if(_sendCodecRegistered) { // If we change codec we start fresh with FEC. // This is not strictly required by the standard. _isFirstRED = true; if(tmpCodecPtr->SetVAD(_dtxEnabled, _vadEnabled, _vadMode) < 0){ // SetVAD failed _vadEnabled = false; _dtxEnabled = false; } } _currentSendCodecIdx = codecID; _sendCodecRegistered = true; memcpy(&_sendCodecInst, &sendCodec, sizeof(CodecInst)); _previousPayloadType = _sendCodecInst.pltype; return 0; } else { // If codec is the same as already registers check if any parameters // has changed compared to the current values. // If any parameter is valid then apply it and record. bool forceInit = false; if(mirrorId != codecID) { _codecs[codecID] = _codecs[mirrorId]; _mirrorCodecIdx[codecID] = mirrorId; } // check the payload-type if(sendCodec.pltype != _sendCodecInst.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(sendCodec.pltype) < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _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(_sendCodecInst.plfreq != sendCodec.plfreq) { forceInit = true; // if sampling frequency is changed we have to start fresh with RED. _isFirstRED = true; } if(_sendCodecInst.pacsize != sendCodec.pacsize) { forceInit = true; } if(forceInit) { WebRtcACMCodecParams codecParams; memcpy(&(codecParams.codecInstant), &sendCodec, sizeof(CodecInst)); codecParams.enableVAD = _vadEnabled; codecParams.enableDTX = _dtxEnabled; codecParams.vadMode = _vadMode; // force initialization if(_codecs[_currentSendCodecIdx]->InitEncoder(&codecParams, true) < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "Could not change the codec packet-size."); return -1; } _sendCodecInst.plfreq = sendCodec.plfreq; _sendCodecInst.pacsize = sendCodec.pacsize; } // If the change of sampling frequency has been successful then // we store the payload-type. _sendCodecInst.pltype = sendCodec.pltype; // check if a change in Rate is required if(sendCodec.rate != _sendCodecInst.rate) { if(_codecs[codecID]->SetBitRate(sendCodec.rate) < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "Could not change the codec rate."); return -1; } _sendCodecInst.rate = sendCodec.rate; } _previousPayloadType = _sendCodecInst.pltype; return 0; } } // get current send codec WebRtc_Word32 AudioCodingModuleImpl::SendCodec( CodecInst& currentSendCodec) const { WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, _id, "SendCodec()"); CriticalSectionScoped lock(*_acmCritSect); if(!_sendCodecRegistered) { WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, _id, "SendCodec Failed, no codec is registered"); return -1; } WebRtcACMCodecParams encoderParam; _codecs[_currentSendCodecIdx]->EncoderParams(&encoderParam); encoderParam.codecInstant.pltype = _sendCodecInst.pltype; memcpy(¤tSendCodec, &(encoderParam.codecInstant), sizeof(CodecInst)); return 0; } // get current send freq WebRtc_Word32 AudioCodingModuleImpl::SendFrequency() const { WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, _id, "SendFrequency()"); CriticalSectionScoped lock(*_acmCritSect); if(!_sendCodecRegistered) { WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, _id, "SendFrequency Failed, no codec is registered"); return -1; } return _sendCodecInst.plfreq; } // Get encode bitrate // Adaptive rate codecs return their current encode target rate, while other codecs // return there longterm avarage or their fixed rate. WebRtc_Word32 AudioCodingModuleImpl::SendBitrate() const { WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, "SendBitrate()"); CriticalSectionScoped lock(*_acmCritSect); if(!_sendCodecRegistered) { WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, _id, "SendBitrate Failed, no codec is registered"); return -1; } WebRtcACMCodecParams encoderParam; _codecs[_currentSendCodecIdx]->EncoderParams(&encoderParam); return encoderParam.codecInstant.rate; } // set available bandwidth, inform the encoder about the estimated bandwidth // received from the remote party WebRtc_Word32 AudioCodingModuleImpl::SetReceivedEstimatedBandwidth( const WebRtc_Word32 bw ) { WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, "SetReceivedEstimatedBandwidth()"); return _codecs[_currentSendCodecIdx]->SetEstimatedBandwidth(bw); } // register a transport callback wich will be called to deliver // the encoded buffers WebRtc_Word32 AudioCodingModuleImpl::RegisterTransportCallback( AudioPacketizationCallback* transport) { WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, "RegisterTransportCallback()"); CriticalSectionScoped lock(*_callbackCritSect); _packetizationCallback = transport; return 0; } // Used by the module to deliver messages to the codec module/appliation // AVT(DTMF) WebRtc_Word32 AudioCodingModuleImpl::RegisterIncomingMessagesCallback( #ifndef WEBRTC_DTMF_DETECTION AudioCodingFeedback* /* incomingMessagesCallback */, const ACMCountries /* cpt */) { WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, "RegisterIncomingMessagesCallback()"); return -1; #else AudioCodingFeedback* incomingMessagesCallback, const ACMCountries cpt) { WebRtc_Word16 status = 0; // Enter the critical section for callback { CriticalSectionScoped lock(*_callbackCritSect); _dtmfCallback = incomingMessagesCallback; } // enter the ACM critical section to set up the DTMF class. { CriticalSectionScoped lock(*_acmCritSect); // Check if the call is to disable or enable the callback if(incomingMessagesCallback == NULL) { // callback is disabled, delete DTMF-detector class if(_dtmfDetector != NULL) { delete _dtmfDetector; _dtmfDetector = NULL; } status = 0; } else { status = 0; if(_dtmfDetector == NULL) { _dtmfDetector = new(ACMDTMFDetection); if(_dtmfDetector == NULL) { status = -1; } } if(status >= 0) { status = _dtmfDetector->Enable(cpt); if(status < 0) { // failed to initialize if DTMF-detection was not enabled before, // delete the class, and set the callback to NULL and return -1. delete _dtmfDetector; _dtmfDetector = NULL; } } } } // check if we failed in setting up the DTMF-detector class if((status < 0)) { // we failed, we cannot have the callback CriticalSectionScoped lock(*_callbackCritSect); _dtmfCallback = NULL; } return status; #endif } // Add 10MS of raw (PCM) audio data to the encoder WebRtc_Word32 AudioCodingModuleImpl::Add10MsData( const AudioFrame& audioFrame) { // Do we have a codec registered? CriticalSectionScoped lock(*_acmCritSect); if(!HaveValidEncoder("Add10MsData")) { return -1; } if(audioFrame._payloadDataLengthInSamples == 0) { assert(false); WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "Cannot Add 10 ms audio, payload length is zero"); return -1; } // Allow for 8, 16, 32 and 48kHz input audio if((audioFrame._frequencyInHz != 8000) && (audioFrame._frequencyInHz != 16000) && (audioFrame._frequencyInHz != 32000) && (audioFrame._frequencyInHz != 48000)) { assert(false); WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "Cannot Add 10 ms audio, input frequency not valid"); return -1; } // If the length and frequency matches. We currently just support raw PCM if((audioFrame._frequencyInHz/ 100) != audioFrame._payloadDataLengthInSamples) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "Cannot Add 10 ms audio, input frequency and length doesn't \ match"); return -1; } // Calculate the timestamp that should be pushed to codec. // This might be different from the timestamp of the frame // due to re-sampling bool resamplingRequired = ((WebRtc_Word32)audioFrame._frequencyInHz != _sendCodecInst.plfreq); WebRtc_UWord32 currentTimestamp; WebRtc_Word32 status; // if it is required, we have to do a resampling if(resamplingRequired) { WebRtc_Word16 resampledAudio[WEBRTC_10MS_PCM_AUDIO]; WebRtc_Word32 sendPlFreq = _sendCodecInst.plfreq; WebRtc_UWord32 diffInputTimestamp; WebRtc_Word16 newLengthSmpl; // calculate the timestamp of this frame if(_lastInTimestamp > audioFrame._timeStamp) { // a wrap around has happened diffInputTimestamp = ((WebRtc_UWord32)0xFFFFFFFF - _lastInTimestamp) + audioFrame._timeStamp; } else { diffInputTimestamp = audioFrame._timeStamp - _lastInTimestamp; } currentTimestamp = _lastTimestamp + (WebRtc_UWord32)(diffInputTimestamp * ((double)_sendCodecInst.plfreq / (double)audioFrame._frequencyInHz)); newLengthSmpl = _inputResampler.Resample10Msec( audioFrame._payloadData, audioFrame._frequencyInHz, resampledAudio, sendPlFreq, _sendCodecInst.channels); if(newLengthSmpl < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "Cannot add 10 ms audio, resmapling failed"); return -1; } status = _codecs[_currentSendCodecIdx]->Add10MsData(currentTimestamp, resampledAudio, newLengthSmpl, audioFrame._audioChannel); } else { currentTimestamp = audioFrame._timeStamp; status = _codecs[_currentSendCodecIdx]->Add10MsData(currentTimestamp, audioFrame._payloadData, audioFrame._payloadDataLengthInSamples, audioFrame._audioChannel); } _lastInTimestamp = audioFrame._timeStamp; _lastTimestamp = currentTimestamp; return status; } ///////////////////////////////////////// // (FEC) Forward Error Correction // bool AudioCodingModuleImpl::FECStatus() const { WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, "FECStatus()"); CriticalSectionScoped lock(*_acmCritSect); return _fecEnabled; } // configure FEC status i.e on/off WebRtc_Word32 AudioCodingModuleImpl::SetFECStatus( #ifdef WEBRTC_CODEC_RED const bool enableFEC) { WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, "SetFECStatus()"); CriticalSectionScoped lock(*_acmCritSect); if (_fecEnabled != enableFEC) { // Reset the RED buffer memset(_redBuffer, 0, MAX_PAYLOAD_SIZE_BYTE); // Reset fragmentation buffers _fragmentation->fragmentationVectorSize = 2; _fragmentation->fragmentationOffset[0] = 0; _fragmentation->fragmentationOffset[1] = MAX_PAYLOAD_SIZE_BYTE; memset(_fragmentation->fragmentationLength, 0, sizeof(WebRtc_UWord32) * 2); memset(_fragmentation->fragmentationTimeDiff, 0, sizeof(WebRtc_UWord16) * 2); memset(_fragmentation->fragmentationPlType, 0, sizeof(WebRtc_UWord8) * 2); // set _fecEnabled _fecEnabled = enableFEC; } _isFirstRED = true; // Make sure we restart FEC return 0; #else const bool /* enableFEC */) { _fecEnabled = false; WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, _id, " WEBRTC_CODEC_RED is undefined => _fecEnabled = %d", _fecEnabled); return -1; #endif } ///////////////////////////////////////// // (VAD) Voice Activity Detection // WebRtc_Word32 AudioCodingModuleImpl::SetVAD( const bool enableDTX, const bool enableVAD, const ACMVADMode vadMode) { WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, "SetVAD()"); CriticalSectionScoped lock(*_acmCritSect); // sanity check of the mode if((vadMode != VADNormal) && (vadMode != VADLowBitrate) && (vadMode != VADAggr) && (vadMode != VADVeryAggr)) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "Invalid VAD Mode %d, no change is made to VAD/DTX status", (int)vadMode); return -1; } // If a send codec is registered, set VAD/DTX for the codec if(HaveValidEncoder("SetVAD")) { WebRtc_Word16 status = _codecs[_currentSendCodecIdx]->SetVAD(enableDTX, enableVAD, vadMode); if(status == 1) { // Vad was enabled; _vadEnabled = true; _dtxEnabled = enableDTX; _vadMode = vadMode; return 0; } else if (status < 0) { // SetVAD failed WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "SetVAD failed"); _vadEnabled = false; _dtxEnabled = false; return -1; } } _vadEnabled = enableVAD; _dtxEnabled = enableDTX; _vadMode = vadMode; return 0; } WebRtc_Word32 AudioCodingModuleImpl::VAD( bool& dtxEnabled, bool& vadEnabled, ACMVADMode& vadMode) const { WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, "VAD()"); CriticalSectionScoped lock(*_acmCritSect); dtxEnabled = _dtxEnabled; vadEnabled = _vadEnabled; vadMode = _vadMode; return 0; } ///////////////////////////////////////// // Receiver // WebRtc_Word32 AudioCodingModuleImpl::InitializeReceiver() { CriticalSectionScoped lock(*_acmCritSect); return InitializeReceiverSafe(); } // Initialize receiver, resets codec database etc WebRtc_Word32 AudioCodingModuleImpl::InitializeReceiverSafe() { WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, "InitializeReceiver()"); WebRtc_Word16 noCodecs = ACMCodecDB::NoOfCodecs(); int i = 0; // If the receiver is already initialized then we // also like to destruct decoders if any exist. After a call // to this function, we should have a clean start-up. if(_receiverInitialized) { for(WebRtc_Word16 codecCntr = 0; codecCntr < noCodecs; codecCntr++) { if(UnregisterReceiveCodecSafe(codecCntr) < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "InitializeReceiver() failed, Could not unregister codec"); return -1; } } } if (_netEq.Init() != 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "InitializeReceiver() failed, Could not initialize NetEq"); return -1; } _netEq.SetUniqueId(_id); if (_netEq.AllocatePacketBuffer(ACMCodecDB::NetEqDecoders(), ACMCodecDB::NoNetEqDecoders()) != 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "NetEq cannot allocatePacket Buffer"); return -1; } // Register RED and CN int regInNeteq = 0; for (i = (ACMCodecDB::_noOfCodecs - 1); i>-1; i--) { if((STR_CASE_CMP(ACMCodecDB::_mycodecs[i].plname, "red") == 0)) { regInNeteq = 1; } else if ((STR_CASE_CMP(ACMCodecDB::_mycodecs[i].plname, "CN") == 0)) { regInNeteq = 1; } if (regInNeteq == 1) { if(RegisterRecCodecMSSafe(ACMCodecDB::_mycodecs[i], i, i, ACMNetEQ::masterJB) < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "Cannot register master codec."); return -1; } _registeredPlTypes[i] = ACMCodecDB::_mycodecs[i].pltype; regInNeteq = 0; } } _receiverInitialized = true; return 0; } // Reset the decoder state WebRtc_Word32 AudioCodingModuleImpl::ResetDecoder() { WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, "ResetDecoder()"); CriticalSectionScoped lock(*_acmCritSect); for(WebRtc_Word16 codecCntr = 0; codecCntr < MAX_NR_OF_CODECS; codecCntr++) { if((_codecs[codecCntr] != NULL) && (_registeredPlTypes[codecCntr] != -1)) { if(_codecs[codecCntr]->ResetDecoder(_registeredPlTypes[codecCntr]) < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "ResetDecoder failed:"); return -1; } } } return _netEq.FlushBuffers(); } // get current receive freq WebRtc_Word32 AudioCodingModuleImpl::ReceiveFrequency() const { WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, _id, "ReceiveFrequency()"); WebRtcACMCodecParams codecParams; CriticalSectionScoped lock(*_acmCritSect); if(DecoderParamByPlType(_lastRecvAudioCodecPlType, codecParams) < 0) { return _netEq.CurrentSampFreqHz(); } else { return codecParams.codecInstant.plfreq; } } // get current playout freq WebRtc_Word32 AudioCodingModuleImpl::PlayoutFrequency() const { WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, _id, "PlayoutFrequency()"); CriticalSectionScoped lock(*_acmCritSect); return _netEq.CurrentSampFreqHz(); } // register possible reveive codecs, can be called multiple times, // for codecs, CNG (NB, WB and SWB), DTMF, RED WebRtc_Word32 AudioCodingModuleImpl::RegisterReceiveCodec( const CodecInst& receiveCodec) { CriticalSectionScoped lock(*_acmCritSect); WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, "RegisterReceiveCodec()"); if(receiveCodec.channels > 2) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "More than 2 audio channel is not supported."); return -1; } WebRtc_Word16 mirrorId; WebRtc_Word16 codecId = ACMCodecDB::ReceiverCodecNumber(receiveCodec, mirrorId); if(codecId < 0 || codecId >= ACMCodecDB::NoOfCodecs()) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "Wrong codec params to be registered as receive codec"); return -1; } // Check if the payload-type is valid. if(ACMCodecDB::ValidPayloadType(receiveCodec.pltype) < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "Invalid payload-type %d for %s.", receiveCodec.pltype, receiveCodec.plname); return -1; } if(!_receiverInitialized) { if(InitializeReceiverSafe() < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "Cannot initialize reciver, so failed registering a codec."); return -1; } } // If codec already registered, start with unregistering if(_registeredPlTypes[codecId] != -1) { if(UnregisterReceiveCodecSafe(codecId) < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "Cannot register master codec."); return -1; } } if(RegisterRecCodecMSSafe(receiveCodec, codecId, mirrorId, ACMNetEQ::masterJB) < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "Cannot register master codec."); return -1; } // If receive stereo, make sure we have two instances of NetEQ, one for each channel if(receiveCodec.channels == 2) { if(_netEq.NumSlaves() < 1) { if(_netEq.AddSlave(ACMCodecDB::NetEqDecoders(), ACMCodecDB::NoNetEqDecoders()) < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "Cannot Add Slave jitter buffer to NetEq."); return -1; } } if(RegisterRecCodecMSSafe(receiveCodec, codecId, mirrorId, ACMNetEQ::slaveJB) < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "Cannot register slave codec."); return -1; } if((_stereoReceive[codecId] == false) && (_lastRecvAudioCodecPlType == receiveCodec.pltype)) { _lastRecvAudioCodecPlType = -1; } _stereoReceive[codecId] = true; } else { _stereoReceive[codecId] = false; } _registeredPlTypes[codecId] = receiveCodec.pltype; if(!STR_CASE_CMP(receiveCodec.plname, "RED")) { _receiveREDPayloadType = receiveCodec.pltype; } return 0; } WebRtc_Word32 AudioCodingModuleImpl::RegisterRecCodecMSSafe( const CodecInst& receiveCodec, WebRtc_Word16 codecId, WebRtc_Word16 mirrorId, ACMNetEQ::JB jitterBuffer) { WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, "RegisterReceiveCodecMSSafe()"); ACMGenericCodec** codecArray; if(jitterBuffer == ACMNetEQ::masterJB) { codecArray = &_codecs[0]; } else if(jitterBuffer == ACMNetEQ::slaveJB) { codecArray = &_slaveCodecs[0]; } else { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "RegisterReceiveCodecMSSafe failed, jitterBuffer is neither master or slave "); return -1; } if (codecArray[mirrorId] == NULL) { codecArray[mirrorId] = CreateCodec(receiveCodec); if(codecArray[mirrorId] == NULL) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "Cannot create codec to register as receive codec"); return -1; } _mirrorCodecIdx[mirrorId] = mirrorId; } if(mirrorId != codecId) { codecArray[codecId] = codecArray[mirrorId]; _mirrorCodecIdx[codecId] = mirrorId; } codecArray[codecId]->SetIsMaster(jitterBuffer == ACMNetEQ::masterJB); WebRtc_Word16 status = 0; bool registerInNetEq = true; WebRtcACMCodecParams codecParams; memcpy(&(codecParams.codecInstant), &receiveCodec, sizeof(CodecInst)); codecParams.enableVAD = false; codecParams.enableDTX = false; codecParams.vadMode = VADNormal; if (!codecArray[codecId]->DecoderInitialized()) { // force initialization status = codecArray[codecId]->InitDecoder(&codecParams, true); if(status < 0) { // could not initialize the decoder we don't want to // continue if we could not initialize properly. WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "could not initialize the receive codec, codec not registered"); return -1; } } else if(mirrorId != codecId) { // Currently this only happens for iSAC. // we have to store the decoder parameters codecArray[codecId]->SaveDecoderParam(&codecParams); } if (registerInNetEq) { if(codecArray[codecId]->RegisterInNetEq(&_netEq, receiveCodec) != 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "Receive codec could not be registered in NetEq"); return -1; } // Guaranty that the same payload-type that is // registered in NetEq is stored in the codec. codecArray[codecId]->SaveDecoderParam(&codecParams); } return status; } // Get current received codec WebRtc_Word32 AudioCodingModuleImpl::ReceiveCodec( CodecInst& currentReceiveCodec) const { WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, "ReceiveCodec()"); WebRtc_Word16 decCntr; WebRtcACMCodecParams decoderParam; CriticalSectionScoped lock(*_acmCritSect); for(decCntr = 0; decCntr < MAX_NR_OF_CODECS; decCntr++) { if(_codecs[decCntr] != NULL) { if(_codecs[decCntr]->DecoderInitialized()) { if(_codecs[decCntr]->DecoderParams(&decoderParam, _lastRecvAudioCodecPlType)) { memcpy(¤tReceiveCodec, &decoderParam.codecInstant, sizeof(CodecInst)); return 0; } } } } // if we are here then we haven't found any codec // set codec pltype to -1 to indicate that the structure // is invalid and return -1. currentReceiveCodec.pltype = -1; return -1; } // Incoming packet from network parsed and ready for decode WebRtc_Word32 AudioCodingModuleImpl::IncomingPacket( const WebRtc_Word8* incomingPayload, const WebRtc_Word32 payloadLength, const WebRtcRTPHeader& rtpInfo) { if (payloadLength < 0) { // Log error WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "IncomingPacket() Error, payload-length cannot be negative"); return -1; } { // store the payload Type. this will be used to retrieve "received codec" // and "received frequency." CriticalSectionScoped lock(*_acmCritSect); #ifdef ACM_QA_TEST if(_incomingPL != NULL) { fwrite(&rtpInfo.header.timestamp, sizeof(WebRtc_UWord32), 1, _incomingPL); fwrite(&rtpInfo.header.payloadType, sizeof(WebRtc_UWord8), 1, _incomingPL); fwrite(&payloadLength, sizeof(WebRtc_Word16), 1, _incomingPL); } #endif WebRtc_UWord8 myPayloadType; // Check if this is an RED payload if(rtpInfo.header.payloadType == _receiveREDPayloadType) { // get the primary payload-type. myPayloadType = (WebRtc_UWord8)(incomingPayload[0] & 0x7F); } else { myPayloadType = rtpInfo.header.payloadType; } // If payload is audio, check if received payload is different from previous if((!rtpInfo.type.Audio.isCNG) && (myPayloadType != _cngNB.pltype) && (myPayloadType != _cngWB.pltype) && (myPayloadType != _cngSWB.pltype)) { // This is Audio not CNG if(myPayloadType != _lastRecvAudioCodecPlType) { // We detect a change in payload type. It is necessary for iSAC // we are going to use ONE iSAC instance for decoding both WB and // SWB payloads. If payload is changed there might be a need to reset // sampling rate of decoder. depending what we have received "now". // TODO (tlegrand): Change or remove the following comment // I cannot use the function that BV has written, i.e. // "DecoderParamByPlType()" as for iSAC there is one instance and // multiple payloads. int i; for(i = 0; i < MAX_NR_OF_CODECS; i++) { if(_registeredPlTypes[i] == myPayloadType) { if(_codecs[i] == NULL) { // we found a payload type but the corresponding // codec is NULL this should not happen WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "IncomingPacket() Error, payload type found but corresponding " "codec is NULL"); return -1; } _codecs[i]->UpdateDecoderSampFreq(i); _netEq.SetReceivedStereo(_stereoReceive[i]); break; } } } _lastRecvAudioCodecPlType = myPayloadType; } } return _netEq.RecIn(incomingPayload, payloadLength, rtpInfo); } // Minimum playout delay (Used for lip-sync) WebRtc_Word32 AudioCodingModuleImpl::SetMinimumPlayoutDelay( const WebRtc_Word32 timeMs) { WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, "SetMinimumPlayoutDelay()"); if((timeMs < 0) || (timeMs > 1000)) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "Delay must be in the range of 0-1000 milliseconds."); return -1; } return _netEq.SetExtraDelay(timeMs); } // current play out delay WebRtc_Word32 AudioCodingModuleImpl::Delay( WebRtc_UWord16& delayMs) const { // NetEQ will get an API for this later. WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, "Delay()"); return _netEq.Delay(delayMs); } // Get Dtmf playout status bool AudioCodingModuleImpl::DtmfPlayoutStatus() const { WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, "DtmfPlayoutStatus()"); #ifndef WEBRTC_CODEC_AVT return false; #else return _netEq.AVTPlayout(); #endif } // configure Dtmf playout status i.e on/off // playout the incoming outband Dtmf tone WebRtc_Word32 AudioCodingModuleImpl::SetDtmfPlayoutStatus( #ifndef WEBRTC_CODEC_AVT const bool /* enable */) { WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, _id, "SetDtmfPlayoutStatus() failed: AVT is not supported."); return -1; #else const bool enable) { WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, "SetDtmfPlayoutStatus()"); return _netEq.SetAVTPlayout(enable); #endif } // Estimate the Bandwidth based on the incoming stream // This is also done in the RTP module // need this for one way audio where the RTCP send the BW estimate WebRtc_Word32 AudioCodingModuleImpl::DecoderEstimatedBandwidth() const { WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, "DecoderEstimatedBandwidth()"); CodecInst codecInst; WebRtc_Word16 codecID = -1; int plTypWB; int plTypSWB; // Get iSAC settings for(WebRtc_Word16 codecCntr = 0; codecCntr < ACMCodecDB::NoOfCodecs(); codecCntr++) { // Store codec settings for codec number "codeCntr" in the output struct ACMCodecDB::Codec(codecCntr, &codecInst); if(!STR_CASE_CMP(codecInst.plname, "isac")) { codecID = 1; plTypWB = codecInst.pltype; ACMCodecDB::Codec(codecCntr+1, &codecInst); plTypSWB = codecInst.pltype; break; } } if(codecID < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "DecoderEstimatedBandwidth failed"); return -1; } if ((_lastRecvAudioCodecPlType == plTypWB) || (_lastRecvAudioCodecPlType == plTypSWB)) { return _codecs[codecID]->GetEstimatedBandwidth(); } else { return -1; } } // Set playout mode for: voice, fax, or streaming WebRtc_Word32 AudioCodingModuleImpl::SetPlayoutMode( const AudioPlayoutMode mode) { WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, "SetPlayoutMode()"); if((mode != voice) && (mode != fax) && (mode != streaming)) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "Invalid playout mode."); return -1; } return _netEq.SetPlayoutMode(mode); } // Get playout mode voice, fax AudioPlayoutMode AudioCodingModuleImpl::PlayoutMode() const { WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, "PlayoutMode()"); return _netEq.PlayoutMode(); } // Get 10 milliseconds of raw audio data to play out // automatic resample to the requested frequency WebRtc_Word32 AudioCodingModuleImpl::PlayoutData10Ms( const WebRtc_Word32 desiredFreqHz, AudioFrame& audioFrame) { bool stereoMode; AudioFrame audioFrameTmp; // recOut always returns 10 ms if (_netEq.RecOut(audioFrameTmp) != 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "PlayoutData failed, RecOut Failed"); return -1; } audioFrame._audioChannel = audioFrameTmp._audioChannel; audioFrame._vadActivity = audioFrameTmp._vadActivity; audioFrame._speechType = audioFrameTmp._speechType; stereoMode = (audioFrameTmp._audioChannel > 1); //For stereo playout: // Master and Slave samples are interleaved starting with Master const WebRtc_UWord16 recvFreq = static_cast(audioFrameTmp._frequencyInHz); bool toneDetected = false; WebRtc_Word16 lastDetectedTone; WebRtc_Word16 tone; // limit the scope of ACM Critical section // perhaps we don't need to have output resampler in // critical section, it is supposed to be called in this // function and no where else. However, it won't degrade complexity { CriticalSectionScoped lock(*_acmCritSect); if ((recvFreq != desiredFreqHz) && (desiredFreqHz != -1)) { // resample payloadData WebRtc_Word16 tmpLen = _outputResampler.Resample10Msec( audioFrameTmp._payloadData, recvFreq, audioFrame._payloadData, desiredFreqHz, audioFrameTmp._audioChannel); if(tmpLen < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "PlayoutData failed, resampler failed"); return -1; } //Set the payload data length from the resampler audioFrame._payloadDataLengthInSamples = (WebRtc_UWord16)tmpLen; // set the ssampling frequency audioFrame._frequencyInHz = desiredFreqHz; } else { memcpy(audioFrame._payloadData, audioFrameTmp._payloadData, audioFrameTmp._payloadDataLengthInSamples * audioFrame._audioChannel * sizeof(WebRtc_Word16)); // set the payload length audioFrame._payloadDataLengthInSamples = audioFrameTmp._payloadDataLengthInSamples; // set the sampling frequency audioFrame._frequencyInHz = recvFreq; } //Tone detection done for master channel if(_dtmfDetector != NULL) { // Dtmf Detection if(audioFrame._frequencyInHz == 8000) { // use audioFrame._payloadData then Dtmf detector doesn't // need resampling if(!stereoMode) { _dtmfDetector->Detect(audioFrame._payloadData, audioFrame._payloadDataLengthInSamples, audioFrame._frequencyInHz, toneDetected, tone); } else { // we are in 8 kHz so the master channel needs only 80 samples WebRtc_Word16 masterChannel[80]; for(WebRtc_Word16 n = 0; n < 80; n++) { masterChannel[n] = audioFrame._payloadData[n<<1]; } _dtmfDetector->Detect(audioFrame._payloadData, audioFrame._payloadDataLengthInSamples, audioFrame._frequencyInHz, toneDetected, tone); } } else { // Do the detection on the audio that we got from NetEQ (audioFrameTmp). if(!stereoMode) { _dtmfDetector->Detect(audioFrameTmp._payloadData, audioFrameTmp._payloadDataLengthInSamples, recvFreq, toneDetected, tone); } else { WebRtc_Word16 masterChannel[WEBRTC_10MS_PCM_AUDIO]; WebRtc_Word16 n; for(n = 0; n < audioFrameTmp._payloadDataLengthInSamples; n++) { masterChannel[n] = audioFrameTmp._payloadData[n<<1]; } _dtmfDetector->Detect(audioFrameTmp._payloadData, audioFrameTmp._payloadDataLengthInSamples, recvFreq, toneDetected, tone); } } } // we want to do this while we are in _acmCritSect // doesn't really need to initialize the following // variable but Linux complains if we don't lastDetectedTone = kACMToneEnd; if(toneDetected) { lastDetectedTone = _lastDetectedTone; _lastDetectedTone = tone; } } if(toneDetected) { // we will deal with callback here, so enter callback critical // section CriticalSectionScoped lock(*_callbackCritSect); if(_dtmfCallback != NULL) { if(tone != kACMToneEnd) { // just a tone _dtmfCallback->IncomingDtmf((WebRtc_UWord8)tone, false); } else if((tone == kACMToneEnd) && (lastDetectedTone != kACMToneEnd)) { // The tone is "END" and the previously detected tone is // not "END," so call fir an end. _dtmfCallback->IncomingDtmf((WebRtc_UWord8)lastDetectedTone, true); } } } audioFrame._id = _id; audioFrame._volume = -1; audioFrame._energy = -1; audioFrame._timeStamp = 0; return 0; } ///////////////////////////////////////// // (CNG) Comfort Noise Generation // Generate comfort noise when receiving DTX packets // // Get VAD status on the incoming stream bool AudioCodingModuleImpl::ReceiveVADStatus() const { WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, "ReceiveVADStatus()"); return _netEq.VADStatus(); } // configure VAD status i.e on/off on the incoming stream WebRtc_Word16 AudioCodingModuleImpl::SetReceiveVADStatus( const bool enable) { WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, "SetReceiveVADStatus()"); return _netEq.SetVADStatus(enable); } // Get VAD aggressiveness on the incoming stream ACMVADMode AudioCodingModuleImpl::ReceiveVADMode() const { WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, "ReceiveVADMode()"); return _netEq.VADMode(); } // Configure VAD aggressiveness on the incoming stream WebRtc_Word16 AudioCodingModuleImpl::SetReceiveVADMode( const ACMVADMode mode) { WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, "SetReceiveVADMode()"); return _netEq.SetVADMode(mode); } ///////////////////////////////////////// // statistics // WebRtc_Word32 AudioCodingModuleImpl::NetworkStatistics( ACMNetworkStatistics& statistics) const { WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, "NetworkStatistics()"); WebRtc_Word32 status; status = _netEq.NetworkStatistics(&statistics); return status; } WebRtc_Word32 AudioCodingModuleImpl::JitterStatistics( ACMJitterStatistics& jitterStatistics) const { WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, "JitterStatistics()"); return _netEq.JitterStatistics(&jitterStatistics); } WebRtc_Word32 AudioCodingModuleImpl::PreferredBufferSize( WebRtc_UWord16& prefbufsize) const { WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, "PreferedBufferSize()"); return _netEq.PreferredBufferSize(&prefbufsize); } WebRtc_Word32 AudioCodingModuleImpl::ResetJitterStatistics() const { WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, "ResetJitterStatistics()"); return _netEq.ResetJitterStatistics(); } void AudioCodingModuleImpl::DestructEncoderInst( void* ptrInst) { WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceAudioCoding, _id, "DestructEncoderInst()"); if(!HaveValidEncoder("DestructEncoderInst")) { return; } _codecs[_currentSendCodecIdx]->DestructEncoderInst(ptrInst); } WebRtc_Word16 AudioCodingModuleImpl::AudioBuffer( WebRtcACMAudioBuff& audioBuff) { WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceAudioCoding, _id, "AudioBuffer()"); if(!HaveValidEncoder("AudioBuffer")) { return -1; } audioBuff.lastInTimestamp = _lastInTimestamp; return _codecs[_currentSendCodecIdx]->AudioBuffer(audioBuff); } WebRtc_Word16 AudioCodingModuleImpl::SetAudioBuffer( WebRtcACMAudioBuff& audioBuff) { WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceAudioCoding, _id, "SetAudioBuffer()"); if(!HaveValidEncoder("SetAudioBuffer")) { return -1; } return _codecs[_currentSendCodecIdx]->SetAudioBuffer(audioBuff); } WebRtc_UWord32 AudioCodingModuleImpl::EarliestTimestamp() const { WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceAudioCoding, _id, "EarliestTimestamp()"); if(!HaveValidEncoder("EarliestTimestamp")) { return -1; } return _codecs[_currentSendCodecIdx]->EarliestTimestamp(); } WebRtc_Word32 AudioCodingModuleImpl::RegisterVADCallback( ACMVADCallback* vadCallback) { WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceAudioCoding, _id, "RegisterVADCallback()"); CriticalSectionScoped lock(*_callbackCritSect); _vadCallback = vadCallback; return 0; } WebRtc_Word32 AudioCodingModuleImpl::IncomingPayload( const WebRtc_Word8* incomingPayload, const WebRtc_Word32 payloadLength, const WebRtc_UWord8 payloadType, const WebRtc_UWord32 timestamp) { if (payloadLength < 0) { // Log error in trace file. WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "IncomingPacket() Error, payload-length cannot be negative"); return -1; } if(_dummyRTPHeader == NULL) { // This is the first time that we are using _dummyRTPHeader // so we have to create it. WebRtcACMCodecParams codecParams; _dummyRTPHeader = new WebRtcRTPHeader; if (_dummyRTPHeader == NULL) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "IncomingPacket() Error, out of memory"); return -1; } _dummyRTPHeader->header.payloadType = payloadType; // Don't matter in this case _dummyRTPHeader->header.ssrc = 0; _dummyRTPHeader->header.markerBit = false; // start with random numbers _dummyRTPHeader->header.sequenceNumber = rand(); _dummyRTPHeader->header.timestamp = (((WebRtc_UWord32)rand()) << 16) + (WebRtc_UWord32)rand(); _dummyRTPHeader->type.Audio.channel = 1; if(DecoderParamByPlType(payloadType, codecParams) < 0) { // we didn't find a codec with the given payload. // something is wrong we exit, but we delete _dummyRTPHeader // and set it to NULL to start clean next time delete _dummyRTPHeader; _dummyRTPHeader = NULL; return -1; } _recvPlFrameSizeSmpls = codecParams.codecInstant.pacsize; } if(payloadType != _dummyRTPHeader->header.payloadType) { // payload type has changed since the last time we might need to // update the frame-size WebRtcACMCodecParams codecParams; if(DecoderParamByPlType(payloadType, codecParams) < 0) { // we didn't find a codec with the given payload. // something is wrong we exit return -1; } _recvPlFrameSizeSmpls = codecParams.codecInstant.pacsize; _dummyRTPHeader->header.payloadType = payloadType; } if(timestamp > 0) { _dummyRTPHeader->header.timestamp = timestamp; } // store the payload Type. this will be used to retrieve "received codec" // and "received frequency." _lastRecvAudioCodecPlType = payloadType; // Insert in NetEQ if(_netEq.RecIn(incomingPayload, payloadLength, (*_dummyRTPHeader)) < 0) { return -1; } // get ready for the next payload _dummyRTPHeader->header.sequenceNumber++; _dummyRTPHeader->header.timestamp += _recvPlFrameSizeSmpls; return 0; } WebRtc_Word16 AudioCodingModuleImpl::DecoderParamByPlType( const WebRtc_UWord8 payloadType, WebRtcACMCodecParams& codecParams) const { CriticalSectionScoped lock(*_acmCritSect); for(WebRtc_Word16 codecCntr = 0; codecCntr < MAX_NR_OF_CODECS; codecCntr++) { if(_codecs[codecCntr] != NULL) { if(_codecs[codecCntr]->DecoderInitialized()) { if(_codecs[codecCntr]->DecoderParams(&codecParams, payloadType)) { return 0; } } } } // if we are here it means that we could not find a // codec with that payload type. reset the values to // not acceptable values and return -1; codecParams.codecInstant.plname[0] = '\0'; codecParams.codecInstant.pacsize = 0; codecParams.codecInstant.rate = 0; codecParams.codecInstant.pltype = -1; return -1; } WebRtc_Word16 AudioCodingModuleImpl::DecoderListIDByPlName( const WebRtc_Word8* payloadName, const WebRtc_UWord16 sampFreqHz) const { WebRtcACMCodecParams codecParams; CriticalSectionScoped lock(*_acmCritSect); for(WebRtc_Word16 codecCntr = 0; codecCntr < MAX_NR_OF_CODECS; codecCntr++) { if((_codecs[codecCntr] != NULL)) { if(_codecs[codecCntr]->DecoderInitialized()) { assert(_registeredPlTypes[codecCntr] >= 0); assert(_registeredPlTypes[codecCntr] <= 255); _codecs[codecCntr]->DecoderParams(&codecParams, (WebRtc_UWord8)_registeredPlTypes[codecCntr]); if(!STR_CASE_CMP(codecParams.codecInstant.plname, payloadName)) { // Check if the given sampling frequency matches. // A zero sampling frequency means we matching the names // is sufficient and we don't need to check for the // frequencies. // Currently it is only iSAC which has one name but two // sampling frequencies. if((sampFreqHz == 0) || (codecParams.codecInstant.plfreq == sampFreqHz)) { return codecCntr; } } } } } // if we are here it means that we could not find a // codec with that payload type. return -1; return -1; } WebRtc_Word32 AudioCodingModuleImpl::LastEncodedTimestamp(WebRtc_UWord32& timestamp) const { WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, "LastEncodedTimestamp()"); CriticalSectionScoped lock(*_acmCritSect); if(!HaveValidEncoder("LastEncodedTimestamp")) { return -1; } timestamp = _codecs[_currentSendCodecIdx]->LastEncodedTimestamp(); return 0; } WebRtc_Word32 AudioCodingModuleImpl::ReplaceInternalDTXWithWebRtc(bool useWebRtcDTX) { WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, "ReplaceInternalDTXWithWebRtc()"); CriticalSectionScoped lock(*_acmCritSect); if(!HaveValidEncoder("ReplaceInternalDTXWithWebRtc")) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "Cannot replace codec internal DTX when no send codec is registered."); return -1; } WebRtc_Word32 res = _codecs[_currentSendCodecIdx]->ReplaceInternalDTX(useWebRtcDTX); // Check if VAD is turned on, or if there is any error if(res == 1) { _vadEnabled = true; } else if(res < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "Failed to set ReplaceInternalDTXWithWebRtc(%d)", useWebRtcDTX); return res; } return 0; } WebRtc_Word32 AudioCodingModuleImpl::IsInternalDTXReplacedWithWebRtc(bool& usesWebRtcDTX) { WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, "IsInternalDTXReplacedWithWebRtc()"); CriticalSectionScoped lock(*_acmCritSect); if(!HaveValidEncoder("IsInternalDTXReplacedWithWebRtc")) { return -1; } if(_codecs[_currentSendCodecIdx]->IsInternalDTXReplaced(&usesWebRtcDTX) < 0) { return -1; } return 0; } WebRtc_Word32 AudioCodingModuleImpl::SetISACMaxRate( const WebRtc_UWord32 maxRateBitPerSec) { WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, "SetISACMaxRate()"); CriticalSectionScoped lock(*_acmCritSect); if(!HaveValidEncoder("SetISACMaxRate")) { return -1; } return _codecs[_currentSendCodecIdx]->SetISACMaxRate(maxRateBitPerSec); } WebRtc_Word32 AudioCodingModuleImpl::SetISACMaxPayloadSize( const WebRtc_UWord16 maxPayloadLenBytes) { WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, "SetISACPayloadSize()"); CriticalSectionScoped lock(*_acmCritSect); if(!HaveValidEncoder("SetISACMaxPayloadSize")) { return -1; } return _codecs[_currentSendCodecIdx]->SetISACMaxPayloadSize(maxPayloadLenBytes); } WebRtc_Word32 AudioCodingModuleImpl::ConfigISACBandwidthEstimator( const WebRtc_UWord8 initFrameSizeMsec, const WebRtc_UWord16 initRateBitPerSec, const bool enforceFrameSize) { WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, "ConfigISACBandwidthEstimator()"); CriticalSectionScoped lock(*_acmCritSect); if(!HaveValidEncoder("ConfigISACBandwidthEstimator")) { return -1; } return _codecs[_currentSendCodecIdx]->ConfigISACBandwidthEstimator( initFrameSizeMsec, initRateBitPerSec, enforceFrameSize); } WebRtc_Word32 AudioCodingModuleImpl::SetBackgroundNoiseMode( const ACMBackgroundNoiseMode mode) { WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, "SetBackgroundNoiseMode()"); if((mode < On) || (mode > Off)) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "The specified background noise is out of range.\n"); return -1; } return _netEq.SetBackgroundNoiseMode(mode); } WebRtc_Word32 AudioCodingModuleImpl::BackgroundNoiseMode( ACMBackgroundNoiseMode& mode) { WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, "BackgroundNoiseMode()"); return _netEq.BackgroundNoiseMode(mode); } WebRtc_Word32 AudioCodingModuleImpl::PlayoutTimestamp( WebRtc_UWord32& timestamp) { WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, _id, "PlayoutTimestamp()"); return _netEq.PlayoutTimestamp(timestamp); } bool AudioCodingModuleImpl::HaveValidEncoder( const WebRtc_Word8* callerName) const { WebRtc_Word16 numCodecs = ACMCodecDB::NoOfCodecs(); if((!_sendCodecRegistered) || (_currentSendCodecIdx < 0) || (_currentSendCodecIdx >= numCodecs)) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "%s failed: No send codec is registered.", callerName); return false; } if((_currentSendCodecIdx < 0) || (_currentSendCodecIdx >= numCodecs)) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "%s failed: Send codec index out of range.", callerName); return false; } if(_codecs[_currentSendCodecIdx] == NULL) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "%s failed: Send codec is NULL pointer.", callerName); return false; } return true; } WebRtc_Word32 AudioCodingModuleImpl::UnregisterReceiveCodec( const WebRtc_Word16 payloadType) { WEBRTC_TRACE(webrtc::kTraceModuleCall, webrtc::kTraceAudioCoding, _id, "UnregisterReceiveCodec()"); CriticalSectionScoped lock(*_acmCritSect); WebRtc_Word16 codecID; // Search through the list of registered payload types for (codecID = 0; codecID < MAX_NR_OF_CODECS; codecID++) { if (_registeredPlTypes[codecID] == payloadType) { // we have found the codecID registered with the payload type break; } } if(codecID >= ACMCodecDB::NoOfCodecs()) { // payload type was not registered. No need to unregister return 0; } else if(codecID < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "UnregisterReceiveCodec() failed: the given codec, %s, is not supported", payloadType); return -1; } // Unregister the codec with the given payload type return UnregisterReceiveCodecSafe(codecID); } WebRtc_Word32 AudioCodingModuleImpl::UnregisterReceiveCodecSafe( const WebRtc_Word16 codecID) { WebRtcNetEQDecoder *neteqDecoder = ACMCodecDB::NetEqDecoders(); WebRtc_Word16 mirrorID = ACMCodecDB::MirrorID(codecID); if(_codecs[codecID] != NULL) { if(_registeredPlTypes[codecID] != -1) { // before deleting the decoder instance unregister // from NetEq. if(_netEq.RemoveCodec(neteqDecoder[codecID], _stereoReceive[codecID]) < 0) { CodecInst codecInst; ACMCodecDB::Codec(codecID, &codecInst); WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "Unregistering %s-%d from NetEq failed.", codecInst.plname, codecInst.plfreq); return -1; } // CN is a special case for NetEQ, all three sampling frequencies are // deletad if one is deleted if(STR_CASE_CMP(ACMCodecDB::_mycodecs[codecID].plname, "CN") == 0) { // Search codecs nearby in the database to unregister all CN. for (int i=-2; i<3; i++) { if (STR_CASE_CMP(ACMCodecDB::_mycodecs[codecID+i].plname, "CN") == 0) { _codecs[codecID+i]->DestructDecoder(); if(_stereoReceive[codecID+i]) { _slaveCodecs[codecID+i]->DestructDecoder(); } _registeredPlTypes[codecID+i] = -1; } } } else { if(codecID == mirrorID) { _codecs[codecID]->DestructDecoder(); if(_stereoReceive[codecID]) { _slaveCodecs[codecID]->DestructDecoder(); } } } } } if(_registeredPlTypes[codecID] == _receiveREDPayloadType) { // RED is going to be unregistered. // set the following to an invalid value. _receiveREDPayloadType = 255; } _registeredPlTypes[codecID] = -1; return 0; } WebRtc_Word32 AudioCodingModuleImpl::REDPayloadISAC( const WebRtc_Word32 isacRate, const WebRtc_Word16 isacBwEstimate, WebRtc_UWord8* payload, WebRtc_Word16* payloadLenByte) { if(!HaveValidEncoder("EncodeData")) { return -1; } WebRtc_Word16 status; status = _codecs[_currentSendCodecIdx]->REDPayloadISAC(isacRate, isacBwEstimate, payload, payloadLenByte); return status; } } // namespace webrtc