
Two changes in this CL: -Removal of a critical section lock in the constructor of audio coding module -Removal of one unused variable Review URL: http://webrtc-codereview.appspot.com/43001 git-svn-id: http://webrtc.googlecode.com/svn/trunk@116 4adac7df-926f-26a2-2b94-8c16560cd09d
2714 lines
84 KiB
C++
2714 lines
84 KiB
C++
/*
|
|
* 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 <assert.h>
|
|
#include <stdlib.h>
|
|
|
|
#ifdef ACM_QA_TEST
|
|
# include <stdio.h>
|
|
#endif
|
|
|
|
#ifdef TIMED_LOGGING
|
|
char message[500];
|
|
#include "../test/timedtrace.h"
|
|
#include <string.h>
|
|
#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<WebRtc_Word16> (_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<WebRtc_UWord16>(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
|