/* * Copyright (c) 2012 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 // sort #include // malloc #include #include "acm_neteq.h" #include "common_types.h" #include "critical_section_wrapper.h" #include "rw_lock_wrapper.h" #include "signal_processing_library.h" #include "tick_util.h" #include "trace.h" #include "webrtc_neteq.h" #include "webrtc_neteq_internal.h" namespace webrtc { #define RTP_HEADER_SIZE 12 #define NETEQ_INIT_FREQ 8000 #define NETEQ_INIT_FREQ_KHZ (NETEQ_INIT_FREQ/1000) #define NETEQ_ERR_MSG_LEN_BYTE (WEBRTC_NETEQ_MAX_ERROR_NAME + 1) ACMNetEQ::ACMNetEQ() : _id(0), _currentSampFreqKHz(NETEQ_INIT_FREQ_KHZ), _avtPlayout(false), _playoutMode(voice), _netEqCritSect(CriticalSectionWrapper::CreateCriticalSection()), _vadStatus(false), _vadMode(VADNormal), _decodeLock(RWLockWrapper::CreateRWLock()), _numSlaves(0), _receivedStereo(false), _masterSlaveInfo(NULL), _previousAudioActivity(AudioFrame::kVadUnknown), _extraDelay(0), _callbackCritSect(CriticalSectionWrapper::CreateCriticalSection()) { for (int n = 0; n < MAX_NUM_SLAVE_NETEQ + 1; n++) { _isInitialized[n] = false; _ptrVADInst[n] = NULL; _inst[n] = NULL; _instMem[n] = NULL; _netEqPacketBuffer[n] = NULL; } } ACMNetEQ::~ACMNetEQ() { { CriticalSectionScoped lock(_netEqCritSect); RemoveNetEQSafe(0); // Master. RemoveSlavesSafe(); } if (_netEqCritSect != NULL) { delete _netEqCritSect; } if (_decodeLock != NULL) { delete _decodeLock; } if (_callbackCritSect != NULL) { delete _callbackCritSect; } } WebRtc_Word32 ACMNetEQ::Init() { CriticalSectionScoped lock(_netEqCritSect); for (WebRtc_Word16 idx = 0; idx < _numSlaves + 1; idx++) { if (InitByIdxSafe(idx) < 0) { return -1; } // delete VAD instance and start fresh if required. if (_ptrVADInst[idx] != NULL) { WebRtcVad_Free(_ptrVADInst[idx]); _ptrVADInst[idx] = NULL; } if (_vadStatus) { // Has to enable VAD if (EnableVADByIdxSafe(idx) < 0) { // Failed to enable VAD. // Delete VAD instance, if it is created if (_ptrVADInst[idx] != NULL) { WebRtcVad_Free(_ptrVADInst[idx]); _ptrVADInst[idx] = NULL; } // We are at initialization of NetEq, if failed to // enable VAD, we delete the NetEq instance. if (_instMem[idx] != NULL) { free(_instMem[idx]); _instMem[idx] = NULL; _inst[idx] = NULL; } _isInitialized[idx] = false; return -1; } } _isInitialized[idx] = true; } if (EnableVAD() == -1) { return -1; } return 0; } WebRtc_Word16 ACMNetEQ::InitByIdxSafe(const WebRtc_Word16 idx) { int memorySizeBytes; if (WebRtcNetEQ_AssignSize(&memorySizeBytes) != 0) { LogError("AssignSize", idx); return -1; } if (_instMem[idx] != NULL) { free(_instMem[idx]); _instMem[idx] = NULL; } _instMem[idx] = malloc(memorySizeBytes); if (_instMem[idx] == NULL) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "InitByIdxSafe: NetEq Initialization error: could not allocate memory " "for NetEq"); _isInitialized[idx] = false; return -1; } if (WebRtcNetEQ_Assign(&_inst[idx], _instMem[idx]) != 0) { if (_instMem[idx] != NULL) { free(_instMem[idx]); _instMem[idx] = NULL; } LogError("Assign", idx); WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "InitByIdxSafe: NetEq Initialization error: could not Assign"); _isInitialized[idx] = false; return -1; } if (WebRtcNetEQ_Init(_inst[idx], NETEQ_INIT_FREQ) != 0) { if (_instMem[idx] != NULL) { free(_instMem[idx]); _instMem[idx] = NULL; } LogError("Init", idx); WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "InitByIdxSafe: NetEq Initialization error: could not initialize " "NetEq"); _isInitialized[idx] = false; return -1; } _isInitialized[idx] = true; return 0; } WebRtc_Word16 ACMNetEQ::EnableVADByIdxSafe(const WebRtc_Word16 idx) { if (_ptrVADInst[idx] == NULL) { if (WebRtcVad_Create(&_ptrVADInst[idx]) < 0) { _ptrVADInst[idx] = NULL; WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "EnableVADByIdxSafe: NetEq Initialization error: could not " "create VAD"); return -1; } } if (WebRtcNetEQ_SetVADInstance( _inst[idx], _ptrVADInst[idx], (WebRtcNetEQ_VADInitFunction) WebRtcVad_Init, (WebRtcNetEQ_VADSetmodeFunction) WebRtcVad_set_mode, (WebRtcNetEQ_VADFunction) WebRtcVad_Process) < 0) { LogError("setVADinstance", idx); WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "EnableVADByIdxSafe: NetEq Initialization error: could not set " "VAD instance"); return -1; } if (WebRtcNetEQ_SetVADMode(_inst[idx], _vadMode) < 0) { LogError("setVADmode", idx); WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "EnableVADByIdxSafe: NetEq Initialization error: could not set " "VAD mode"); return -1; } return 0; } WebRtc_Word32 ACMNetEQ::AllocatePacketBuffer( const WebRtcNetEQDecoder* usedCodecs, WebRtc_Word16 noOfCodecs) { // Due to WebRtcNetEQ_GetRecommendedBufferSize // the following has to be int otherwise we will have compiler error // if not casted CriticalSectionScoped lock(_netEqCritSect); for (WebRtc_Word16 idx = 0; idx < _numSlaves + 1; idx++) { if (AllocatePacketBufferByIdxSafe(usedCodecs, noOfCodecs, idx) < 0) { return -1; } } return 0; } WebRtc_Word16 ACMNetEQ::AllocatePacketBufferByIdxSafe( const WebRtcNetEQDecoder* usedCodecs, WebRtc_Word16 noOfCodecs, const WebRtc_Word16 idx) { int maxNoPackets; int bufferSizeInBytes; if (!_isInitialized[idx]) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "AllocatePacketBufferByIdxSafe: NetEq is not initialized."); return -1; } if (WebRtcNetEQ_GetRecommendedBufferSize(_inst[idx], usedCodecs, noOfCodecs, kTCPLargeJitter, &maxNoPackets, &bufferSizeInBytes) != 0) { LogError("GetRecommendedBufferSize", idx); return -1; } if (_netEqPacketBuffer[idx] != NULL) { free(_netEqPacketBuffer[idx]); _netEqPacketBuffer[idx] = NULL; } _netEqPacketBuffer[idx] = (WebRtc_Word16 *) malloc(bufferSizeInBytes); if (_netEqPacketBuffer[idx] == NULL) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "AllocatePacketBufferByIdxSafe: NetEq Initialization error: could not " "allocate memory for NetEq Packet Buffer"); return -1; } if (WebRtcNetEQ_AssignBuffer(_inst[idx], maxNoPackets, _netEqPacketBuffer[idx], bufferSizeInBytes) != 0) { if (_netEqPacketBuffer[idx] != NULL) { free(_netEqPacketBuffer[idx]); _netEqPacketBuffer[idx] = NULL; } LogError("AssignBuffer", idx); return -1; } return 0; } WebRtc_Word32 ACMNetEQ::SetExtraDelay(const WebRtc_Word32 delayInMS) { CriticalSectionScoped lock(_netEqCritSect); for (WebRtc_Word16 idx = 0; idx < _numSlaves + 1; idx++) { if (!_isInitialized[idx]) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "SetExtraDelay: NetEq is not initialized."); return -1; } if (WebRtcNetEQ_SetExtraDelay(_inst[idx], delayInMS) < 0) { LogError("SetExtraDelay", idx); return -1; } } _extraDelay = delayInMS; return 0; } WebRtc_Word32 ACMNetEQ::SetAVTPlayout(const bool enable) { CriticalSectionScoped lock(_netEqCritSect); if (_avtPlayout != enable) { for (WebRtc_Word16 idx = 0; idx < _numSlaves + 1; idx++) { if (!_isInitialized[idx]) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "SetAVTPlayout: NetEq is not initialized."); return -1; } if (WebRtcNetEQ_SetAVTPlayout(_inst[idx], (enable) ? 1 : 0) < 0) { LogError("SetAVTPlayout", idx); return -1; } } } _avtPlayout = enable; return 0; } bool ACMNetEQ::AVTPlayout() const { CriticalSectionScoped lock(_netEqCritSect); return _avtPlayout; } WebRtc_Word32 ACMNetEQ::CurrentSampFreqHz() const { CriticalSectionScoped lock(_netEqCritSect); if (!_isInitialized[0]) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "CurrentSampFreqHz: NetEq is not initialized."); return -1; } return (WebRtc_Word32)(1000 * _currentSampFreqKHz); } WebRtc_Word32 ACMNetEQ::SetPlayoutMode(const AudioPlayoutMode mode) { CriticalSectionScoped lock(_netEqCritSect); if (_playoutMode != mode) { for (WebRtc_Word16 idx = 0; idx < _numSlaves + 1; idx++) { if (!_isInitialized[idx]) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "SetPlayoutMode: NetEq is not initialized."); return -1; } enum WebRtcNetEQPlayoutMode playoutMode = kPlayoutOff; switch (mode) { case voice: playoutMode = kPlayoutOn; break; case fax: playoutMode = kPlayoutFax; break; case streaming: playoutMode = kPlayoutStreaming; break; } if (WebRtcNetEQ_SetPlayoutMode(_inst[idx], playoutMode) < 0) { LogError("SetPlayoutMode", idx); return -1; } } _playoutMode = mode; } return 0; } AudioPlayoutMode ACMNetEQ::PlayoutMode() const { CriticalSectionScoped lock(_netEqCritSect); return _playoutMode; } WebRtc_Word32 ACMNetEQ::NetworkStatistics( ACMNetworkStatistics* statistics) const { WebRtcNetEQ_NetworkStatistics stats; CriticalSectionScoped lock(_netEqCritSect); if (!_isInitialized[0]) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "NetworkStatistics: NetEq is not initialized."); return -1; } if (WebRtcNetEQ_GetNetworkStatistics(_inst[0], &stats) == 0) { statistics->currentAccelerateRate = stats.currentAccelerateRate; statistics->currentBufferSize = stats.currentBufferSize; statistics->jitterPeaksFound = (stats.jitterPeaksFound > 0); statistics->currentDiscardRate = stats.currentDiscardRate; statistics->currentExpandRate = stats.currentExpandRate; statistics->currentPacketLossRate = stats.currentPacketLossRate; statistics->currentPreemptiveRate = stats.currentPreemptiveRate; statistics->preferredBufferSize = stats.preferredBufferSize; statistics->clockDriftPPM = stats.clockDriftPPM; } else { LogError("getNetworkStatistics", 0); return -1; } const int kArrayLen = 100; int waiting_times[kArrayLen]; int waiting_times_len = WebRtcNetEQ_GetRawFrameWaitingTimes(_inst[0], kArrayLen, waiting_times); if (waiting_times_len > 0) { std::vector waiting_times_vec(waiting_times, waiting_times + waiting_times_len); std::sort(waiting_times_vec.begin(), waiting_times_vec.end()); size_t size = waiting_times_vec.size(); assert(size == static_cast(waiting_times_len)); if (size % 2 == 0) { statistics->medianWaitingTimeMs = (waiting_times_vec[size / 2 - 1] + waiting_times_vec[size / 2]) / 2; } else { statistics->medianWaitingTimeMs = waiting_times_vec[size / 2]; } statistics->minWaitingTimeMs = waiting_times_vec.front(); statistics->maxWaitingTimeMs = waiting_times_vec.back(); double sum = 0; for (size_t i = 0; i < size; ++i) { sum += waiting_times_vec[i]; } statistics->meanWaitingTimeMs = static_cast(sum / size); } else if (waiting_times_len == 0) { statistics->meanWaitingTimeMs = -1; statistics->medianWaitingTimeMs = -1; statistics->minWaitingTimeMs = -1; statistics->maxWaitingTimeMs = -1; } else { LogError("getRawFrameWaitingTimes", 0); return -1; } return 0; } WebRtc_Word32 ACMNetEQ::RecIn(const WebRtc_UWord8* incomingPayload, const WebRtc_Word32 payloadLength, const WebRtcRTPHeader& rtpInfo) { WebRtc_Word16 payload_length = static_cast(payloadLength); // translate to NetEq struct WebRtcNetEQ_RTPInfo netEqRTPInfo; netEqRTPInfo.payloadType = rtpInfo.header.payloadType; netEqRTPInfo.sequenceNumber = rtpInfo.header.sequenceNumber; netEqRTPInfo.timeStamp = rtpInfo.header.timestamp; netEqRTPInfo.SSRC = rtpInfo.header.ssrc; netEqRTPInfo.markerBit = rtpInfo.header.markerBit; CriticalSectionScoped lock(_netEqCritSect); // Down-cast the time to (32-6)-bit since we only care about // the least significant bits. (32-6) bits cover 2^(32-6) = 67108864 ms. // we masked 6 most significant bits of 32-bit so we don't loose resolution // when do the following multiplication. const WebRtc_UWord32 nowInMs = static_cast( TickTime::MillisecondTimestamp() & 0x03ffffff); WebRtc_UWord32 recvTimestamp = static_cast( _currentSampFreqKHz * nowInMs); int status; // In case of stereo payload, first half of the data should be pushed into // master, and the second half into slave. if (rtpInfo.type.Audio.channel == 2) { payload_length = payload_length / 2; } // Check that master is initialized. if (!_isInitialized[0]) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "RecIn: NetEq is not initialized."); return -1; } // PUSH into Master status = WebRtcNetEQ_RecInRTPStruct(_inst[0], &netEqRTPInfo, incomingPayload, payload_length, recvTimestamp); if (status < 0) { LogError("RecInRTPStruct", 0); WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "RecIn: NetEq, error in pushing in Master"); return -1; } // If the received stream is stereo, insert second half of paket into slave. if (rtpInfo.type.Audio.channel == 2) { if (!_isInitialized[1]) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "RecIn: NetEq is not initialized."); return -1; } // PUSH into Slave status = WebRtcNetEQ_RecInRTPStruct(_inst[1], &netEqRTPInfo, &incomingPayload[payload_length], payload_length, recvTimestamp); if (status < 0) { LogError("RecInRTPStruct", 1); WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "RecIn: NetEq, error in pushing in Slave"); return -1; } } return 0; } WebRtc_Word32 ACMNetEQ::RecOut(AudioFrame& audioFrame) { enum WebRtcNetEQOutputType type; WebRtc_Word16 payloadLenSample; enum WebRtcNetEQOutputType typeMaster; enum WebRtcNetEQOutputType typeSlave; WebRtc_Word16 payloadLenSampleSlave; CriticalSectionScoped lockNetEq(_netEqCritSect); if (!_receivedStereo) { if (!_isInitialized[0]) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "RecOut: NetEq is not initialized."); return -1; } { WriteLockScoped lockCodec(*_decodeLock); if (WebRtcNetEQ_RecOut(_inst[0], &(audioFrame.data_[0]), &payloadLenSample) != 0) { LogError("RecOut", 0); WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "RecOut: NetEq, error in pulling out for mono case"); // Check for errors that can be recovered from: // RECOUT_ERROR_SAMPLEUNDERRUN = 2003 int errorCode = WebRtcNetEQ_GetErrorCode(_inst[0]); if (errorCode != 2003) { // Cannot recover; return an error return -1; } } } WebRtcNetEQ_GetSpeechOutputType(_inst[0], &type); audioFrame.num_channels_ = 1; } else { if (!_isInitialized[0] || !_isInitialized[1]) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "RecOut: NetEq is not initialized."); return -1; } WebRtc_Word16 payloadMaster[480]; WebRtc_Word16 payloadSlave[480]; { WriteLockScoped lockCodec(*_decodeLock); if (WebRtcNetEQ_RecOutMasterSlave(_inst[0], payloadMaster, &payloadLenSample, _masterSlaveInfo, 1) != 0) { LogError("RecOutMasterSlave", 0); WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "RecOut: NetEq, error in pulling out for master"); // Check for errors that can be recovered from: // RECOUT_ERROR_SAMPLEUNDERRUN = 2003 int errorCode = WebRtcNetEQ_GetErrorCode(_inst[0]); if (errorCode != 2003) { // Cannot recover; return an error return -1; } } if (WebRtcNetEQ_RecOutMasterSlave(_inst[1], payloadSlave, &payloadLenSampleSlave, _masterSlaveInfo, 0) != 0) { LogError("RecOutMasterSlave", 1); WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "RecOut: NetEq, error in pulling out for slave"); // Check for errors that can be recovered from: // RECOUT_ERROR_SAMPLEUNDERRUN = 2003 int errorCode = WebRtcNetEQ_GetErrorCode(_inst[1]); if (errorCode != 2003) { // Cannot recover; return an error return -1; } } } if (payloadLenSample != payloadLenSampleSlave) { WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, _id, "RecOut: mismatch between the lenght of the decoded audio by Master " "(%d samples) and Slave (%d samples).", payloadLenSample, payloadLenSampleSlave); if (payloadLenSample > payloadLenSampleSlave) { memset(&payloadSlave[payloadLenSampleSlave], 0, (payloadLenSample - payloadLenSampleSlave) * sizeof(WebRtc_Word16)); } } for (WebRtc_Word16 n = 0; n < payloadLenSample; n++) { audioFrame.data_[n << 1] = payloadMaster[n]; audioFrame.data_[(n << 1) + 1] = payloadSlave[n]; } audioFrame.num_channels_ = 2; WebRtcNetEQ_GetSpeechOutputType(_inst[0], &typeMaster); WebRtcNetEQ_GetSpeechOutputType(_inst[1], &typeSlave); if ((typeMaster == kOutputNormal) || (typeSlave == kOutputNormal)) { type = kOutputNormal; } else { type = typeMaster; } } audioFrame.samples_per_channel_ = static_cast(payloadLenSample); // NetEq always returns 10 ms of audio. _currentSampFreqKHz = static_cast(audioFrame.samples_per_channel_) / 10.0f; audioFrame.sample_rate_hz_ = audioFrame.samples_per_channel_ * 100; if (_vadStatus) { if (type == kOutputVADPassive) { audioFrame.vad_activity_ = AudioFrame::kVadPassive; audioFrame.speech_type_ = AudioFrame::kNormalSpeech; } else if (type == kOutputNormal) { audioFrame.vad_activity_ = AudioFrame::kVadActive; audioFrame.speech_type_ = AudioFrame::kNormalSpeech; } else if (type == kOutputPLC) { audioFrame.vad_activity_ = _previousAudioActivity; audioFrame.speech_type_ = AudioFrame::kPLC; } else if (type == kOutputCNG) { audioFrame.vad_activity_ = AudioFrame::kVadPassive; audioFrame.speech_type_ = AudioFrame::kCNG; } else { audioFrame.vad_activity_ = AudioFrame::kVadPassive; audioFrame.speech_type_ = AudioFrame::kPLCCNG; } } else { // Always return kVadUnknown when receive VAD is inactive audioFrame.vad_activity_ = AudioFrame::kVadUnknown; if (type == kOutputNormal) { audioFrame.speech_type_ = AudioFrame::kNormalSpeech; } else if (type == kOutputPLC) { audioFrame.speech_type_ = AudioFrame::kPLC; } else if (type == kOutputPLCtoCNG) { audioFrame.speech_type_ = AudioFrame::kPLCCNG; } else if (type == kOutputCNG) { audioFrame.speech_type_ = AudioFrame::kCNG; } else { // type is kOutputVADPassive which // we don't expect to get if _vadStatus is false WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioCoding, _id, "RecOut: NetEq returned kVadPassive while _vadStatus is false."); audioFrame.vad_activity_ = AudioFrame::kVadUnknown; audioFrame.speech_type_ = AudioFrame::kNormalSpeech; } } _previousAudioActivity = audioFrame.vad_activity_; return 0; } // When ACMGenericCodec has set the codec specific parameters in codecDef // it calls AddCodec() to add the new codec to the NetEQ database. WebRtc_Word32 ACMNetEQ::AddCodec(WebRtcNetEQ_CodecDef* codecDef, bool toMaster) { if (codecDef == NULL) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "ACMNetEQ::AddCodec: error, codecDef is NULL"); return -1; } CriticalSectionScoped lock(_netEqCritSect); WebRtc_Word16 idx; if (toMaster) { idx = 0; } else { idx = 1; } if (!_isInitialized[idx]) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "ACMNetEQ::AddCodec: NetEq is not initialized."); return -1; } if (WebRtcNetEQ_CodecDbAdd(_inst[idx], codecDef) < 0) { LogError("CodecDB_Add", idx); WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "ACMNetEQ::AddCodec: NetEq, error in adding codec"); return -1; } else { return 0; } } // Creates a Word16 RTP packet out of a Word8 payload and an rtp info struct. // Must be byte order safe. void ACMNetEQ::RTPPack(WebRtc_Word16* rtpPacket, const WebRtc_Word8* payload, const WebRtc_Word32 payloadLengthW8, const WebRtcRTPHeader& rtpInfo) { WebRtc_Word32 idx = 0; WEBRTC_SPL_SET_BYTE(rtpPacket, (WebRtc_Word8) 0x80, idx); idx++; WEBRTC_SPL_SET_BYTE(rtpPacket, rtpInfo.header.payloadType, idx); idx++; WEBRTC_SPL_SET_BYTE(rtpPacket, WEBRTC_SPL_GET_BYTE(&(rtpInfo.header.sequenceNumber), 1), idx); idx++; WEBRTC_SPL_SET_BYTE(rtpPacket, WEBRTC_SPL_GET_BYTE(&(rtpInfo.header.sequenceNumber), 0), idx); idx++; WEBRTC_SPL_SET_BYTE(rtpPacket, WEBRTC_SPL_GET_BYTE(&(rtpInfo.header.timestamp), 3), idx); idx++; WEBRTC_SPL_SET_BYTE(rtpPacket, WEBRTC_SPL_GET_BYTE(&(rtpInfo.header.timestamp), 2), idx); idx++; WEBRTC_SPL_SET_BYTE(rtpPacket, WEBRTC_SPL_GET_BYTE(&(rtpInfo.header.timestamp), 1), idx); idx++; WEBRTC_SPL_SET_BYTE(rtpPacket, WEBRTC_SPL_GET_BYTE(&(rtpInfo.header.timestamp), 0), idx); idx++; WEBRTC_SPL_SET_BYTE(rtpPacket, WEBRTC_SPL_GET_BYTE(&(rtpInfo.header.ssrc), 3), idx); idx++; WEBRTC_SPL_SET_BYTE(rtpPacket, WEBRTC_SPL_GET_BYTE(&(rtpInfo.header.ssrc), 2), idx); idx++; WEBRTC_SPL_SET_BYTE(rtpPacket, WEBRTC_SPL_GET_BYTE(&(rtpInfo.header.ssrc), 1), idx); idx++; WEBRTC_SPL_SET_BYTE(rtpPacket, WEBRTC_SPL_GET_BYTE(&(rtpInfo.header.ssrc), 0), idx); idx++; for (WebRtc_Word16 i = 0; i < payloadLengthW8; i++) { WEBRTC_SPL_SET_BYTE(rtpPacket, payload[i], idx); idx++; } if (payloadLengthW8 & 1) { // Our 16 bits buffer is one byte too large, set that // last byte to zero. WEBRTC_SPL_SET_BYTE(rtpPacket, 0x0, idx); } } WebRtc_Word16 ACMNetEQ::EnableVAD() { CriticalSectionScoped lock(_netEqCritSect); if (_vadStatus) { return 0; } for (WebRtc_Word16 idx = 0; idx < _numSlaves + 1; idx++) { if (!_isInitialized[idx]) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "SetVADStatus: NetEq is not initialized."); return -1; } // VAD was off and we have to turn it on if (EnableVADByIdxSafe(idx) < 0) { return -1; } // Set previous VAD status to PASSIVE _previousAudioActivity = AudioFrame::kVadPassive; } _vadStatus = true; return 0; } ACMVADMode ACMNetEQ::VADMode() const { CriticalSectionScoped lock(_netEqCritSect); return _vadMode; } WebRtc_Word16 ACMNetEQ::SetVADMode(const ACMVADMode mode) { CriticalSectionScoped lock(_netEqCritSect); if ((mode < VADNormal) || (mode > VADVeryAggr)) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "SetVADMode: NetEq error: could not set VAD mode, mode is not " "supported"); return -1; } else { for (WebRtc_Word16 idx = 0; idx < _numSlaves + 1; idx++) { if (!_isInitialized[idx]) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "SetVADMode: NetEq is not initialized."); return -1; } if (WebRtcNetEQ_SetVADMode(_inst[idx], mode) < 0) { LogError("SetVADmode", idx); return -1; } } _vadMode = mode; return 0; } } WebRtc_Word32 ACMNetEQ::FlushBuffers() { CriticalSectionScoped lock(_netEqCritSect); for (WebRtc_Word16 idx = 0; idx < _numSlaves + 1; idx++) { if (!_isInitialized[idx]) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "FlushBuffers: NetEq is not initialized."); return -1; } if (WebRtcNetEQ_FlushBuffers(_inst[idx]) < 0) { LogError("FlushBuffers", idx); return -1; } } return 0; } WebRtc_Word16 ACMNetEQ::RemoveCodec(WebRtcNetEQDecoder codecIdx, bool isStereo) { // sanity check if ((codecIdx <= kDecoderReservedStart) || (codecIdx >= kDecoderReservedEnd)) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "RemoveCodec: NetEq error: could not Remove Codec, codec index out " "of range"); return -1; } CriticalSectionScoped lock(_netEqCritSect); if (!_isInitialized[0]) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "RemoveCodec: NetEq is not initialized."); return -1; } if (WebRtcNetEQ_CodecDbRemove(_inst[0], codecIdx) < 0) { LogError("CodecDB_Remove", 0); return -1; } if (isStereo) { if (WebRtcNetEQ_CodecDbRemove(_inst[1], codecIdx) < 0) { LogError("CodecDB_Remove", 1); return -1; } } return 0; } WebRtc_Word16 ACMNetEQ::SetBackgroundNoiseMode( const ACMBackgroundNoiseMode mode) { CriticalSectionScoped lock(_netEqCritSect); for (WebRtc_Word16 idx = 0; idx < _numSlaves + 1; idx++) { if (!_isInitialized[idx]) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "SetBackgroundNoiseMode: NetEq is not initialized."); return -1; } if (WebRtcNetEQ_SetBGNMode(_inst[idx], (WebRtcNetEQBGNMode) mode) < 0) { LogError("SetBGNMode", idx); return -1; } } return 0; } WebRtc_Word16 ACMNetEQ::BackgroundNoiseMode(ACMBackgroundNoiseMode& mode) { WebRtcNetEQBGNMode myMode; CriticalSectionScoped lock(_netEqCritSect); if (!_isInitialized[0]) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "BackgroundNoiseMode: NetEq is not initialized."); return -1; } if (WebRtcNetEQ_GetBGNMode(_inst[0], &myMode) < 0) { LogError("WebRtcNetEQ_GetBGNMode", 0); return -1; } else { mode = (ACMBackgroundNoiseMode) myMode; } return 0; } void ACMNetEQ::SetUniqueId(WebRtc_Word32 id) { CriticalSectionScoped lock(_netEqCritSect); _id = id; } void ACMNetEQ::LogError(const char* neteqFuncName, const WebRtc_Word16 idx) const { char errorName[NETEQ_ERR_MSG_LEN_BYTE]; char myFuncName[50]; int neteqErrorCode = WebRtcNetEQ_GetErrorCode(_inst[idx]); WebRtcNetEQ_GetErrorName(neteqErrorCode, errorName, NETEQ_ERR_MSG_LEN_BYTE - 1); strncpy(myFuncName, neteqFuncName, 49); errorName[NETEQ_ERR_MSG_LEN_BYTE - 1] = '\0'; myFuncName[49] = '\0'; WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "NetEq-%d Error in function %s, error-code: %d, error-string: %s", idx, myFuncName, neteqErrorCode, errorName); } WebRtc_Word32 ACMNetEQ::PlayoutTimestamp(WebRtc_UWord32& timestamp) { CriticalSectionScoped lock(_netEqCritSect); if (WebRtcNetEQ_GetSpeechTimeStamp(_inst[0], ×tamp) < 0) { LogError("GetSpeechTimeStamp", 0); return -1; } else { return 0; } } void ACMNetEQ::RemoveSlaves() { CriticalSectionScoped lock(_netEqCritSect); RemoveSlavesSafe(); } void ACMNetEQ::RemoveSlavesSafe() { for (int i = 1; i < _numSlaves + 1; i++) { RemoveNetEQSafe(i); } if (_masterSlaveInfo != NULL) { free(_masterSlaveInfo); _masterSlaveInfo = NULL; } _numSlaves = 0; } void ACMNetEQ::RemoveNetEQSafe(int index) { if (_instMem[index] != NULL) { free(_instMem[index]); _instMem[index] = NULL; } if (_netEqPacketBuffer[index] != NULL) { free(_netEqPacketBuffer[index]); _netEqPacketBuffer[index] = NULL; } if (_ptrVADInst[index] != NULL) { WebRtcVad_Free(_ptrVADInst[index]); _ptrVADInst[index] = NULL; } } WebRtc_Word16 ACMNetEQ::AddSlave(const WebRtcNetEQDecoder* usedCodecs, WebRtc_Word16 noOfCodecs) { CriticalSectionScoped lock(_netEqCritSect); const WebRtc_Word16 slaveIdx = 1; if (_numSlaves < 1) { // initialize the receiver, this also sets up VAD. if (InitByIdxSafe(slaveIdx) < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "AddSlave: AddSlave Failed, Could not Initialize"); return -1; } // Allocate buffer. if (AllocatePacketBufferByIdxSafe(usedCodecs, noOfCodecs, slaveIdx) < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "AddSlave: AddSlave Failed, Could not Allocate Packet Buffer"); return -1; } if (_masterSlaveInfo != NULL) { free(_masterSlaveInfo); _masterSlaveInfo = NULL; } int msInfoSize = WebRtcNetEQ_GetMasterSlaveInfoSize(); _masterSlaveInfo = malloc(msInfoSize); if (_masterSlaveInfo == NULL) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "AddSlave: AddSlave Failed, Could not Allocate memory for " "Master-Slave Info"); return -1; } // We accept this as initialized NetEQ, the rest is to synchronize // Slave with Master. _numSlaves = 1; _isInitialized[slaveIdx] = true; // Set Slave delay as all other instances. if (WebRtcNetEQ_SetExtraDelay(_inst[slaveIdx], _extraDelay) < 0) { LogError("SetExtraDelay", slaveIdx); WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "AddSlave: AddSlave Failed, Could not set delay"); return -1; } // Set AVT if (WebRtcNetEQ_SetAVTPlayout(_inst[slaveIdx], (_avtPlayout) ? 1 : 0) < 0) { LogError("SetAVTPlayout", slaveIdx); WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "AddSlave: AddSlave Failed, Could not set AVT playout."); return -1; } // Set Background Noise WebRtcNetEQBGNMode currentMode; if (WebRtcNetEQ_GetBGNMode(_inst[0], ¤tMode) < 0) { LogError("GetBGNMode", 0); WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "AAddSlave: AddSlave Failed, Could not Get BGN form Master."); return -1; } if (WebRtcNetEQ_SetBGNMode(_inst[slaveIdx], (WebRtcNetEQBGNMode) currentMode) < 0) { LogError("SetBGNMode", slaveIdx); WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "AddSlave: AddSlave Failed, Could not set BGN mode."); return -1; } enum WebRtcNetEQPlayoutMode playoutMode = kPlayoutOff; switch (_playoutMode) { case voice: playoutMode = kPlayoutOn; break; case fax: playoutMode = kPlayoutFax; break; case streaming: playoutMode = kPlayoutStreaming; break; } if (WebRtcNetEQ_SetPlayoutMode(_inst[slaveIdx], playoutMode) < 0) { LogError("SetPlayoutMode", 1); WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _id, "AddSlave: AddSlave Failed, Could not Set Playout Mode."); return -1; } } return 0; } void ACMNetEQ::SetReceivedStereo(bool receivedStereo) { CriticalSectionScoped lock(_netEqCritSect); _receivedStereo = receivedStereo; } WebRtc_UWord8 ACMNetEQ::NumSlaves() { CriticalSectionScoped lock(_netEqCritSect); return _numSlaves; } } // namespace webrtc