/* * 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_neteq.h" #include "acm_speex.h" #include "trace.h" #include "webrtc_neteq.h" #include "webrtc_neteq_help_macros.h" #ifdef WEBRTC_CODEC_SPEEX // NOTE! Speex is not included in the open-source package. Modify this file or your codec // API to match the function call and name of used Speex API file. // #include "speex_interface.h" #endif namespace webrtc { #ifndef WEBRTC_CODEC_SPEEX ACMSPEEX::ACMSPEEX(WebRtc_Word16 /* codecID*/) { return; } ACMSPEEX::~ACMSPEEX() { return; } WebRtc_Word16 ACMSPEEX::InternalEncode( WebRtc_UWord8* /* bitStream */, WebRtc_Word16* /* bitStreamLenByte */) { return -1; } WebRtc_Word16 ACMSPEEX::DecodeSafe( WebRtc_UWord8* /* bitStream */, WebRtc_Word16 /* bitStreamLenByte */, WebRtc_Word16* /* audio */, WebRtc_Word16* /* audioSamples */, WebRtc_Word8* /* speechType */) { return -1; } WebRtc_Word16 ACMSPEEX::EnableDTX() { return -1; } WebRtc_Word16 ACMSPEEX::DisableDTX() { return -1; } WebRtc_Word16 ACMSPEEX::InternalInitEncoder( WebRtcACMCodecParams* /* codecParams */) { return -1; } WebRtc_Word16 ACMSPEEX::InternalInitDecoder( WebRtcACMCodecParams* /* codecParams */) { return -1; } WebRtc_Word32 ACMSPEEX::CodecDef( WebRtcNetEQ_CodecDef& /* codecDef */, const CodecInst& /* codecInst */) { return -1; } ACMGenericCodec* ACMSPEEX::CreateInstance(void) { return NULL; } WebRtc_Word16 ACMSPEEX::InternalCreateEncoder() { return -1; } void ACMSPEEX::DestructEncoderSafe() { return; } WebRtc_Word16 ACMSPEEX::InternalCreateDecoder() { return -1; } void ACMSPEEX::DestructDecoderSafe() { return; } WebRtc_Word16 ACMSPEEX::SetBitRateSafe( const WebRtc_Word32 /* rate */) { return -1; } void ACMSPEEX::InternalDestructEncoderInst( void* /* ptrInst */) { return; } WebRtc_Word16 ACMSPEEX::UnregisterFromNetEqSafe( ACMNetEQ* /* netEq */, WebRtc_Word16 /* payloadType */) { return -1; } #ifdef UNUSEDSPEEX WebRtc_Word16 ACMSPEEX::EnableVBR() { return -1; } WebRtc_Word16 ACMSPEEX::DisableVBR() { return -1; } WebRtc_Word16 ACMSPEEX::SetComplMode( WebRtc_Word16 mode) { return -1; } #endif #else //===================== Actual Implementation ======================= // Remove when integrating a real Speex wrapper extern WebRtc_Word16 WebRtcSpeex_CreateEnc(SPEEX_encinst_t_** inst, WebRtc_Word16 samplFreq); extern WebRtc_Word16 WebRtcSpeex_CreateDec(SPEEX_decinst_t_** inst, WebRtc_Word16 samplFreq, WebRtc_Word16 mode); extern WebRtc_Word16 WebRtcSpeex_FreeEnc(SPEEX_encinst_t_* inst); extern WebRtc_Word16 WebRtcSpeex_FreeDec(SPEEX_decinst_t_* inst); extern WebRtc_Word16 WebRtcSpeex_Encode(SPEEX_encinst_t_* encInst, WebRtc_Word16* input, WebRtc_Word16 rate); extern WebRtc_Word16 WebRtcSpeex_EncoderInit(SPEEX_encinst_t_* encInst, WebRtc_Word16 samplFreq, WebRtc_Word16 mode, WebRtc_Word16 vbrFlag); extern WebRtc_Word16 WebRtcSpeex_GetBitstream(SPEEX_encinst_t_* encInst, WebRtc_Word16* output); extern WebRtc_Word16 WebRtcSpeex_Decode(SPEEX_decinst_t_* decInst); extern WebRtc_Word16 WebRtcSpeex_DecodePlc(SPEEX_decinst_t_* decInst); extern WebRtc_Word16 WebRtcSpeex_DecoderInit(SPEEX_decinst_t_* decInst); ACMSPEEX::ACMSPEEX(WebRtc_Word16 codecID): _encoderInstPtr(NULL), _decoderInstPtr(NULL) { _codecID = codecID; // Set sampling frequency, frame size and rate Speex if(_codecID == ACMCodecDB::speex8) { _samplingFrequency = 8000; _samplesIn20MsAudio = 160; _encodingRate = 11000; } else if(_codecID == ACMCodecDB::speex16) { _samplingFrequency = 16000; _samplesIn20MsAudio = 320; _encodingRate = 22000; } else { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "Wrong codec id for Speex."); _samplingFrequency = -1; _samplesIn20MsAudio = -1; _encodingRate = -1; } _hasInternalDTX = true; _dtxEnabled = false; _vbrEnabled = false; _complMode = 3; // default complexity value return; } ACMSPEEX::~ACMSPEEX() { if(_encoderInstPtr != NULL) { WebRtcSpeex_FreeEnc(_encoderInstPtr); _encoderInstPtr = NULL; } if(_decoderInstPtr != NULL) { WebRtcSpeex_FreeDec(_decoderInstPtr); _decoderInstPtr = NULL; } return; } WebRtc_Word16 ACMSPEEX::InternalEncode( WebRtc_UWord8* bitStream, WebRtc_Word16* bitStreamLenByte) { WebRtc_Word16 status; WebRtc_Word16 numEncodedSamples = 0; WebRtc_Word16 n = 0; while( numEncodedSamples < _frameLenSmpl) { status = WebRtcSpeex_Encode(_encoderInstPtr, &_inAudio[_inAudioIxRead], _encodingRate); // increment the read index this tell the caller that how far // we have gone forward in reading the audio buffer _inAudioIxRead += _samplesIn20MsAudio; numEncodedSamples += _samplesIn20MsAudio; if(status < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "Error in Speex encoder"); return status; } // Update VAD, if internal DTX is used if(_hasInternalDTX && _dtxEnabled) { _vadLabel[n++] = status; _vadLabel[n++] = status; } if(status == 0) { // This frame is detected as inactive. We need send whatever // encoded so far. *bitStreamLenByte = WebRtcSpeex_GetBitstream(_encoderInstPtr, (WebRtc_Word16*)bitStream); return *bitStreamLenByte; } } *bitStreamLenByte = WebRtcSpeex_GetBitstream(_encoderInstPtr, (WebRtc_Word16*)bitStream); return *bitStreamLenByte; } WebRtc_Word16 ACMSPEEX::DecodeSafe( WebRtc_UWord8* /* bitStream */, WebRtc_Word16 /* bitStreamLenByte */, WebRtc_Word16* /* audio */, WebRtc_Word16* /* audioSamples */, WebRtc_Word8* /* speechType */) { return 0; } WebRtc_Word16 ACMSPEEX::EnableDTX() { if(_dtxEnabled) { return 0; } else if(_encoderExist) // check if encoder exist { // enable DTX if(WebRtcSpeex_EncoderInit(_encoderInstPtr, (_vbrEnabled ? 1:0), _complMode, 1) < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "Cannot enable DTX for Speex"); return -1; } _dtxEnabled = true; return 0; } else { return -1; } return 0; } WebRtc_Word16 ACMSPEEX::DisableDTX() { if(!_dtxEnabled) { return 0; } else if(_encoderExist) // check if encoder exist { // disable DTX if(WebRtcSpeex_EncoderInit(_encoderInstPtr, (_vbrEnabled ? 1:0), _complMode, 0) < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "Cannot disable DTX for Speex"); return -1; } _dtxEnabled = false; return 0; } else { // encoder doesn't exists, therefore disabling is harmless return 0; } return 0; } WebRtc_Word16 ACMSPEEX::InternalInitEncoder( WebRtcACMCodecParams* codecParams) { // sanity check if (_encoderInstPtr == NULL) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "Cannot initialize Speex encoder, instance does not exist"); return -1; } WebRtc_Word16 status = SetBitRateSafe((codecParams->codecInstant).rate); status += (WebRtcSpeex_EncoderInit(_encoderInstPtr, _vbrEnabled, _complMode, ((codecParams->enableDTX)? 1:0)) < 0)? -1:0; if (status >= 0) { return 0; } else { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "Error in initialization of Speex encoder"); return -1; } } WebRtc_Word16 ACMSPEEX::InternalInitDecoder( WebRtcACMCodecParams* /* codecParams */) { WebRtc_Word16 status; // sanity check if (_decoderInstPtr == NULL) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "Cannot initialize Speex decoder, instance does not exist"); return -1; } status = ((WebRtcSpeex_DecoderInit(_decoderInstPtr) < 0)? -1:0); if (status >= 0) { return 0; } else { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "Error in initialization of Speex decoder"); return -1; } } WebRtc_Word32 ACMSPEEX::CodecDef( WebRtcNetEQ_CodecDef& codecDef, const CodecInst& codecInst) { if (!_decoderInitialized) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "Error, Speex decoder is not initialized"); return -1; } // Fill up the structure by calling // "SET_CODEC_PAR" & "SET_SPEEX_FUNCTION." // Then call NetEQ to add the codec to it's // database. switch(_samplingFrequency) { case 8000: { SET_CODEC_PAR((codecDef), kDecoderSPEEX_8, codecInst.pltype, _decoderInstPtr, 8000); break; } case 16000: { SET_CODEC_PAR((codecDef), kDecoderSPEEX_16, codecInst.pltype, _decoderInstPtr, 16000); break; } default: { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "Unsupported sampling frequency for Speex"); return -1; break; } } SET_SPEEX_FUNCTIONS((codecDef)); return 0; } ACMGenericCodec* ACMSPEEX::CreateInstance(void) { return NULL; } WebRtc_Word16 ACMSPEEX::InternalCreateEncoder() { return WebRtcSpeex_CreateEnc(&_encoderInstPtr, _samplingFrequency); } void ACMSPEEX::DestructEncoderSafe() { if(_encoderInstPtr != NULL) { WebRtcSpeex_FreeEnc(_encoderInstPtr); _encoderInstPtr = NULL; } // there is no encoder set the following _encoderExist = false; _encoderInitialized = false; _encodingRate = 0; } WebRtc_Word16 ACMSPEEX::InternalCreateDecoder() { return WebRtcSpeex_CreateDec(&_decoderInstPtr, _samplingFrequency, 1); } void ACMSPEEX::DestructDecoderSafe() { if(_decoderInstPtr != NULL) { WebRtcSpeex_FreeDec(_decoderInstPtr); _decoderInstPtr = NULL; } // there is no encoder instance set the followings _decoderExist = false; _decoderInitialized = false; } WebRtc_Word16 ACMSPEEX::SetBitRateSafe( const WebRtc_Word32 rate) { // Check if changed rate if (rate == _encodingRate) { return 0; } else if (rate > 2000) { _encodingRate = rate; _encoderParams.codecInstant.rate = rate; } else { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "Unsupported encoding rate for Speex"); return -1; } return 0; } void ACMSPEEX::InternalDestructEncoderInst( void* ptrInst) { if(ptrInst != NULL) { WebRtcSpeex_FreeEnc((SPEEX_encinst_t_*)ptrInst); } return; } WebRtc_Word16 ACMSPEEX::UnregisterFromNetEqSafe( ACMNetEQ* netEq, WebRtc_Word16 payloadType) { if(payloadType != _decoderParams.codecInstant.pltype) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "Cannot unregister codec %s given payload-type %d does not match \ the stored payload type", _decoderParams.codecInstant.plname, payloadType, _decoderParams.codecInstant.pltype); return -1; } switch(_samplingFrequency) { case 8000: { return netEq->RemoveCodec(kDecoderSPEEX_8); } case 16000: { return netEq->RemoveCodec(kDecoderSPEEX_16); } default: { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "Could not unregister Speex from NetEQ. Sampling frequency doesn't match"); return -1; } } } #ifdef UNUSEDSPEEX // This API is currently not in use. If requested to be able to enable/disable VBR // an ACM API need to be added. WebRtc_Word16 ACMSPEEX::EnableVBR() { if(_vbrEnabled) { return 0; } else if(_encoderExist) // check if encoder exist { // enable Variable Bit Rate (VBR) if(WebRtcSpeex_EncoderInit(_encoderInstPtr, 1, _complMode, (_dtxEnabled? 1:0)) < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "Cannot enable VBR mode for Speex"); return -1; } _vbrEnabled = true; return 0; } else { return -1; } } // This API is currently not in use. If requested to be able to enable/disable VBR // an ACM API need to be added. WebRtc_Word16 ACMSPEEX::DisableVBR() { if(!_vbrEnabled) { return 0; } else if(_encoderExist) // check if encoder exist { // disable DTX if(WebRtcSpeex_EncoderInit(_encoderInstPtr, 0, _complMode, (_dtxEnabled? 1:0)) < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "Cannot disable DTX for Speex"); return -1; } _vbrEnabled = false; return 0; } else { // encoder doesn't exists, therefore disabling is harmless return 0; } } // This API is currently not in use. If requested to be able to set complexity // an ACM API need to be added. WebRtc_Word16 ACMSPEEX::SetComplMode( WebRtc_Word16 mode) { // Check if new mode if(mode == _complMode) { return 0; } else if(_encoderExist) // check if encoder exist { // Set new mode if(WebRtcSpeex_EncoderInit(_encoderInstPtr, 0, mode, (_dtxEnabled? 1:0)) < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "Error in complexity mode for Speex"); return -1; } _complMode = mode; return 0; } else { // encoder doesn't exists, therefore disabling is harmless return 0; } } #endif #endif } // namespace webrtc