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