Files
platform-external-webrtc/webrtc/modules/audio_coding/neteq/codec_db.c
tina.legrand@webrtc.org c4590580e8 Opus mono/stereo on the same payloadtype, and fix of memory bug
During call setup Opus should always be signaled as a 48000 Hz stereo codec, not depending on what we plan to send, or how we plan to decode received packets.
The previous implementation had different payload types for mono and stereo, which breaks the proposed standard.

While working on this CL I ran in to the problem reported earlier, that we could get a crash related to deleting decoder memory. This should now be solved in Patch Set 3.

BUG=issue1013, issue1112

Review URL: https://webrtc-codereview.appspot.com/933022

git-svn-id: http://webrtc.googlecode.com/svn/trunk@3177 4adac7df-926f-26a2-2b94-8c16560cd09d
2012-11-28 12:23:29 +00:00

772 lines
20 KiB
C

/*
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
/*
* Implementation of the codec database.
*/
#include "codec_db.h"
#include <string.h> /* to define NULL */
#include "signal_processing_library.h"
#include "neteq_error_codes.h"
/*
* Resets the codec database.
*/
int WebRtcNetEQ_DbReset(CodecDbInst_t *inst)
{
int i;
WebRtcSpl_MemSetW16((WebRtc_Word16*) inst, 0,
sizeof(CodecDbInst_t) / sizeof(WebRtc_Word16));
for (i = 0; i < NUM_TOTAL_CODECS; i++)
{
inst->position[i] = -1;
}
for (i = 0; i < NUM_CODECS; i++)
{
inst->payloadType[i] = -1;
}
for (i = 0; i < NUM_CNG_CODECS; i++)
{
inst->CNGpayloadType[i] = -1;
}
return 0;
}
/*
* Adds a new codec to the database.
*/
int WebRtcNetEQ_DbAdd(CodecDbInst_t *inst, enum WebRtcNetEQDecoder codec,
WebRtc_Word16 payloadType, FuncDecode funcDecode,
FuncDecode funcDecodeRCU, FuncDecodePLC funcDecodePLC,
FuncDecodeInit funcDecodeInit, FuncAddLatePkt funcAddLatePkt,
FuncGetMDinfo funcGetMDinfo, FuncGetPitchInfo funcGetPitch,
FuncUpdBWEst funcUpdBWEst, FuncDurationEst funcDurationEst,
FuncGetErrorCode funcGetErrorCode, void* codec_state,
WebRtc_UWord16 codec_fs)
{
int temp;
int insertCNGcodec = 0, overwriteCNGcodec = 0, CNGpos = -1;
#ifndef NETEQ_RED_CODEC
if (codec == kDecoderRED)
{
return CODEC_DB_UNSUPPORTED_CODEC;
}
#endif
if (((int) codec <= (int) kDecoderReservedStart) || ((int) codec
>= (int) kDecoderReservedEnd))
{
return CODEC_DB_UNSUPPORTED_CODEC;
}
if ((codec_fs != 8000)
#ifdef NETEQ_WIDEBAND
&&(codec_fs!=16000)
#endif
#ifdef NETEQ_32KHZ_WIDEBAND
&&(codec_fs!=32000)
#endif
#if defined(NETEQ_48KHZ_WIDEBAND) || defined(NETEQ_OPUS_CODEC)
&&(codec_fs!=48000)
#endif
)
{
return CODEC_DB_UNSUPPORTED_FS;
}
/* Ensure that the codec type is supported */
switch (codec)
{
#ifdef NETEQ_PCM16B_CODEC
case kDecoderPCM16B :
case kDecoderPCM16B_2ch :
#endif
#ifdef NETEQ_G711_CODEC
case kDecoderPCMu :
case kDecoderPCMa :
case kDecoderPCMu_2ch :
case kDecoderPCMa_2ch :
#endif
#ifdef NETEQ_ILBC_CODEC
case kDecoderILBC :
#endif
#ifdef NETEQ_ISAC_CODEC
case kDecoderISAC :
#endif
#ifdef NETEQ_ISAC_SWB_CODEC
case kDecoderISACswb :
#endif
#ifdef NETEQ_OPUS_CODEC
case kDecoderOpus :
#endif
#ifdef NETEQ_G722_CODEC
case kDecoderG722 :
case kDecoderG722_2ch :
#endif
#ifdef NETEQ_WIDEBAND
case kDecoderPCM16Bwb :
case kDecoderPCM16Bwb_2ch :
#endif
#ifdef NETEQ_32KHZ_WIDEBAND
case kDecoderPCM16Bswb32kHz :
case kDecoderPCM16Bswb32kHz_2ch :
#endif
#ifdef NETEQ_CNG_CODEC
case kDecoderCNG :
#endif
#ifdef NETEQ_ATEVENT_DECODE
case kDecoderAVT :
#endif
#ifdef NETEQ_RED_CODEC
case kDecoderRED :
#endif
#ifdef NETEQ_48KHZ_WIDEBAND
case kDecoderPCM16Bswb48kHz :
#endif
#ifdef NETEQ_ARBITRARY_CODEC
case kDecoderArbitrary:
#endif
#ifdef NETEQ_G729_CODEC
case kDecoderG729:
#endif
#ifdef NETEQ_G729_1_CODEC
case kDecoderG729_1 :
#endif
#ifdef NETEQ_G726_CODEC
case kDecoderG726_16 :
case kDecoderG726_24 :
case kDecoderG726_32 :
case kDecoderG726_40 :
#endif
#ifdef NETEQ_G722_1_CODEC
case kDecoderG722_1_16 :
case kDecoderG722_1_24 :
case kDecoderG722_1_32 :
#endif
#ifdef NETEQ_G722_1C_CODEC
case kDecoderG722_1C_24 :
case kDecoderG722_1C_32 :
case kDecoderG722_1C_48 :
#endif
#ifdef NETEQ_SPEEX_CODEC
case kDecoderSPEEX_8 :
case kDecoderSPEEX_16 :
#endif
#ifdef NETEQ_CELT_CODEC
case kDecoderCELT_32 :
case kDecoderCELT_32_2ch :
#endif
#ifdef NETEQ_GSMFR_CODEC
case kDecoderGSMFR :
#endif
#ifdef NETEQ_AMR_CODEC
case kDecoderAMR :
#endif
#ifdef NETEQ_AMRWB_CODEC
case kDecoderAMRWB :
#endif
{
/* If we end up here, the inserted codec is supported => Do nothing */
break;
}
default:
{
/* If we get to this point, the inserted codec is not supported */
return CODEC_DB_UNSUPPORTED_CODEC;
}
}
/* Check to see if payload type is taken */
if (WebRtcNetEQ_DbGetCodec(inst, payloadType) > 0)
{
return CODEC_DB_PAYLOAD_TAKEN;
}
/* Special case for CNG codecs */
if (codec == kDecoderCNG)
{
/* check if this is first CNG codec to be registered */
if (WebRtcNetEQ_DbGetPayload(inst, codec) == CODEC_DB_NOT_EXIST2)
{
/* no other CNG codec found */
insertCNGcodec = 1;
}
/* find the appropriate insert position in CNG payload vector */
switch (codec_fs)
{
#ifdef NETEQ_WIDEBAND
case 16000:
CNGpos = 1;
break;
#endif
#ifdef NETEQ_32KHZ_WIDEBAND
case 32000:
CNGpos = 2;
break;
#endif
#ifdef NETEQ_48KHZ_WIDEBAND
case 48000:
CNGpos = 3;
break;
#endif
default: /* 8000 Hz case */
CNGpos = 0;
/*
* The 8 kHz CNG payload type is the one associated with the regular codec DB
* should override any other setting.
* Overwrite if this isn't the first CNG
*/
overwriteCNGcodec = !insertCNGcodec;
break;
}
/* insert CNG payload type */
inst->CNGpayloadType[CNGpos] = payloadType;
}
if ((codec != kDecoderCNG) || (insertCNGcodec == 1) || (overwriteCNGcodec == 1))
{
/* Check if we have reached the maximum numbers of simultaneous codecs */
if (inst->nrOfCodecs == NUM_CODECS) return CODEC_DB_FULL;
/* Check that codec has not already been initialized to DB =>
remove it and reinitialize according to new spec */
if ((inst->position[codec] != -1) && (overwriteCNGcodec != 1))
{ /* if registering multiple CNG codecs, don't remove, just overwrite */
WebRtcNetEQ_DbRemove(inst, codec);
}
if (overwriteCNGcodec == 1)
{
temp = inst->position[codec];
}
else
{
temp = inst->nrOfCodecs; /* Store this codecs position */
inst->position[codec] = temp;
inst->nrOfCodecs++;
}
inst->payloadType[temp] = payloadType;
/* Copy to database */
inst->codec_state[temp] = codec_state;
inst->funcDecode[temp] = funcDecode;
inst->funcDecodeRCU[temp] = funcDecodeRCU;
inst->funcAddLatePkt[temp] = funcAddLatePkt;
inst->funcDecodeInit[temp] = funcDecodeInit;
inst->funcDecodePLC[temp] = funcDecodePLC;
inst->funcGetMDinfo[temp] = funcGetMDinfo;
inst->funcGetPitch[temp] = funcGetPitch;
inst->funcUpdBWEst[temp] = funcUpdBWEst;
inst->funcDurationEst[temp] = funcDurationEst;
inst->funcGetErrorCode[temp] = funcGetErrorCode;
inst->codec_fs[temp] = codec_fs;
}
return 0;
}
/*
* Removes a codec from the database.
*/
int WebRtcNetEQ_DbRemove(CodecDbInst_t *inst, enum WebRtcNetEQDecoder codec)
{
int i;
int pos = -1;
#ifndef NETEQ_RED_CODEC
if (codec == kDecoderRED)
{
return CODEC_DB_UNSUPPORTED_CODEC;
}
#endif
if (((int) codec <= (int) kDecoderReservedStart) || ((int) codec
>= (int) kDecoderReservedEnd))
{
return CODEC_DB_UNSUPPORTED_CODEC;
}
pos = inst->position[codec];
if (pos == -1)
{
return CODEC_DB_NOT_EXIST4;
}
else
{
/* Remove this codec */
inst->position[codec] = -1;
for (i = pos; i < (inst->nrOfCodecs - 1); i++)
{
inst->payloadType[i] = inst->payloadType[i + 1];
inst->codec_state[i] = inst->codec_state[i + 1];
inst->funcDecode[i] = inst->funcDecode[i + 1];
inst->funcDecodeRCU[i] = inst->funcDecodeRCU[i + 1];
inst->funcAddLatePkt[i] = inst->funcAddLatePkt[i + 1];
inst->funcDecodeInit[i] = inst->funcDecodeInit[i + 1];
inst->funcDecodePLC[i] = inst->funcDecodePLC[i + 1];
inst->funcGetMDinfo[i] = inst->funcGetMDinfo[i + 1];
inst->funcGetPitch[i] = inst->funcGetPitch[i + 1];
inst->funcUpdBWEst[i] = inst->funcUpdBWEst[i + 1];
inst->funcGetErrorCode[i] = inst->funcGetErrorCode[i + 1];
inst->codec_fs[i] = inst->codec_fs[i + 1];
}
inst->payloadType[i] = -1;
inst->codec_state[i] = NULL;
inst->funcDecode[i] = NULL;
inst->funcDecodeRCU[i] = NULL;
inst->funcAddLatePkt[i] = NULL;
inst->funcDecodeInit[i] = NULL;
inst->funcDecodePLC[i] = NULL;
inst->funcGetMDinfo[i] = NULL;
inst->funcGetPitch[i] = NULL;
inst->funcUpdBWEst[i] = NULL;
inst->funcGetErrorCode[i] = NULL;
inst->codec_fs[i] = 0;
/* Move down all the codecs above this one */
for (i = 0; i < NUM_TOTAL_CODECS; i++)
{
if (inst->position[i] >= pos)
{
inst->position[i] = inst->position[i] - 1;
}
}
inst->nrOfCodecs--;
if (codec == kDecoderCNG)
{
/* also remove all registered CNG payload types */
for (i = 0; i < NUM_CNG_CODECS; i++)
{
inst->CNGpayloadType[i] = -1;
}
}
}
return 0;
}
/*
* Get the decoder function pointers for a codec.
*/
int WebRtcNetEQ_DbGetPtrs(CodecDbInst_t *inst, enum WebRtcNetEQDecoder codec,
CodecFuncInst_t *ptr_inst)
{
int pos = inst->position[codec];
if ((codec <= kDecoderReservedStart) || (codec >= kDecoderReservedEnd) || (codec
> NUM_TOTAL_CODECS))
{
/* ERROR */
pos = -1;
}
if (pos >= 0)
{
ptr_inst->codec_state = inst->codec_state[pos];
ptr_inst->funcAddLatePkt = inst->funcAddLatePkt[pos];
ptr_inst->funcDecode = inst->funcDecode[pos];
ptr_inst->funcDecodeRCU = inst->funcDecodeRCU[pos];
ptr_inst->funcDecodeInit = inst->funcDecodeInit[pos];
ptr_inst->funcDecodePLC = inst->funcDecodePLC[pos];
ptr_inst->funcGetMDinfo = inst->funcGetMDinfo[pos];
ptr_inst->funcUpdBWEst = inst->funcUpdBWEst[pos];
ptr_inst->funcGetErrorCode = inst->funcGetErrorCode[pos];
ptr_inst->codec_fs = inst->codec_fs[pos];
return 0;
}
else
{
WebRtcSpl_MemSetW16((WebRtc_Word16*) ptr_inst, 0,
sizeof(CodecFuncInst_t) / sizeof(WebRtc_Word16));
return CODEC_DB_NOT_EXIST1;
}
}
/*
* Returns payload number given a codec identifier.
*/
int WebRtcNetEQ_DbGetPayload(CodecDbInst_t *inst, enum WebRtcNetEQDecoder codecID)
{
if (inst->position[codecID] == -1)
return CODEC_DB_NOT_EXIST2;
else
return (inst->payloadType[inst->position[codecID]]);
}
/*
* Returns codec identifier given a payload number.
* Returns -1 if the payload type does not exist.
*/
int WebRtcNetEQ_DbGetCodec(const CodecDbInst_t *inst, int payloadType)
{
int i, pos;
for (i = 0; i < NUM_TOTAL_CODECS; i++)
{
pos = inst->position[i];
if (pos != -1)
{
if (inst->payloadType[pos] == payloadType) return i;
}
}
/* did not find payload type */
/* check if it's a CNG codec */
if (WebRtcNetEQ_DbIsCNGPayload(inst, payloadType))
{
return kDecoderCNG;
}
/* found no match */
return CODEC_DB_NOT_EXIST3;
}
/*
* Extracts the Payload Split information of the codec with the specified payloadType.
*/
int WebRtcNetEQ_DbGetSplitInfo(SplitInfo_t *inst, enum WebRtcNetEQDecoder codecID,
int codedsize)
{
switch (codecID)
{
#ifdef NETEQ_ISAC_CODEC
case kDecoderISAC:
#endif
#ifdef NETEQ_ISAC_SWB_CODEC
case kDecoderISACswb:
#endif
#ifdef NETEQ_OPUS_CODEC
case kDecoderOpus:
#endif
#ifdef NETEQ_ARBITRARY_CODEC
case kDecoderArbitrary:
#endif
#ifdef NETEQ_AMR_CODEC
case kDecoderAMR:
#endif
#ifdef NETEQ_AMRWB_CODEC
case kDecoderAMRWB:
#endif
#ifdef NETEQ_G726_CODEC
/* Treat G726 as non-splittable to simplify the implementation */
case kDecoderG726_16:
case kDecoderG726_24:
case kDecoderG726_32:
case kDecoderG726_40:
#endif
#ifdef NETEQ_SPEEX_CODEC
case kDecoderSPEEX_8:
case kDecoderSPEEX_16:
#endif
#ifdef NETEQ_CELT_CODEC
case kDecoderCELT_32 :
case kDecoderCELT_32_2ch :
#endif
#ifdef NETEQ_G729_1_CODEC
case kDecoderG729_1:
#endif
{
/* These codecs' payloads are not splittable */
inst->deltaBytes = NO_SPLIT;
return 0;
}
/*
* Sample based coders are a special case.
* In this case, deltaTime signals the number of bytes per timestamp unit times 2
* in log2 domain.
*/
#if (defined NETEQ_G711_CODEC)
case kDecoderPCMu:
case kDecoderPCMa:
case kDecoderPCMu_2ch:
case kDecoderPCMa_2ch:
{
inst->deltaBytes = -12;
inst->deltaTime = 1;
return 0;
}
#endif
#if (defined NETEQ_G722_CODEC)
case kDecoderG722:
case kDecoderG722_2ch:
{
inst->deltaBytes = -14;
inst->deltaTime = 0;
return 0;
}
#endif
#if (defined NETEQ_PCM16B_CODEC)
case kDecoderPCM16B:
case kDecoderPCM16B_2ch:
{
inst->deltaBytes = -12;
inst->deltaTime = 2;
return 0;
}
#endif
#if ((defined NETEQ_PCM16B_CODEC)&&(defined NETEQ_WIDEBAND))
case kDecoderPCM16Bwb:
case kDecoderPCM16Bwb_2ch:
{
inst->deltaBytes = -14;
inst->deltaTime = 2;
return 0;
}
#endif
#if ((defined NETEQ_PCM16B_CODEC)&&(defined NETEQ_32KHZ_WIDEBAND))
case kDecoderPCM16Bswb32kHz:
case kDecoderPCM16Bswb32kHz_2ch:
{
inst->deltaBytes = -18;
inst->deltaTime = 2;
return 0;
}
#endif
#if ((defined NETEQ_PCM16B_CODEC)&&(defined NETEQ_48KHZ_WIDEBAND))
case kDecoderPCM16Bswb48kHz:
{
inst->deltaBytes = -22;
inst->deltaTime = 2;
return 0;
}
#endif
/* Splittable payloads */
#ifdef NETEQ_G722_1_CODEC
case kDecoderG722_1_16:
{
inst->deltaBytes = 40;
inst->deltaTime = 320;
return 0;
}
case kDecoderG722_1_24:
{
inst->deltaBytes = 60;
inst->deltaTime = 320;
return 0;
}
case kDecoderG722_1_32:
{
inst->deltaBytes = 80;
inst->deltaTime = 320;
return 0;
}
#endif
#ifdef NETEQ_G722_1C_CODEC
case kDecoderG722_1C_24:
{
inst->deltaBytes = 60;
inst->deltaTime = 640;
return 0;
}
case kDecoderG722_1C_32:
{
inst->deltaBytes = 80;
inst->deltaTime = 640;
return 0;
}
case kDecoderG722_1C_48:
{
inst->deltaBytes = 120;
inst->deltaTime = 640;
return 0;
}
#endif
#ifdef NETEQ_G729_CODEC
case kDecoderG729:
{
inst->deltaBytes = 10;
inst->deltaTime = 80;
return 0;
}
#endif
#ifdef NETEQ_ILBC_CODEC
case kDecoderILBC:
{
/* Check for splitting of iLBC packets.
* If payload size is a multiple of 50 bytes it should be split into 30ms frames.
* If payload size is a multiple of 38 bytes it should be split into 20ms frames.
* Least common multiplier between 38 and 50 is 950, so the payload size must be less than
* 950 bytes in order to resolve the frames unambiguously.
* Currently max 12 frames in one bundle.
*/
switch (codedsize)
{
case 50:
case 100:
case 150:
case 200:
case 250:
case 300:
case 350:
case 400:
case 450:
case 500:
case 550:
case 600:
{
inst->deltaBytes = 50;
inst->deltaTime = 240;
break;
}
case 38:
case 76:
case 114:
case 152:
case 190:
case 228:
case 266:
case 304:
case 342:
case 380:
case 418:
case 456:
{
inst->deltaBytes = 38;
inst->deltaTime = 160;
break;
}
default:
{
return AMBIGUOUS_ILBC_FRAME_SIZE; /* Something not supported... */
}
}
return 0;
}
#endif
#ifdef NETEQ_GSMFR_CODEC
case kDecoderGSMFR:
{
inst->deltaBytes = 33;
inst->deltaTime = 160;
return 0;
}
#endif
default:
{ /*Unknown codec */
inst->deltaBytes = NO_SPLIT;
return CODEC_DB_UNKNOWN_CODEC;
}
} /* end of switch */
}
/*
* Returns 1 if codec is multiple description, 0 otherwise.
* NOTE: This function is a stub, since there currently are no MD codecs.
*/
int WebRtcNetEQ_DbIsMDCodec(enum WebRtcNetEQDecoder codecID)
{
if (0) /* Add test for MD codecs here */
return 1;
else
return 0;
}
/*
* Returns 1 if payload type is registered as a CNG codec, 0 otherwise
*/
int WebRtcNetEQ_DbIsCNGPayload(const CodecDbInst_t *inst, int payloadType)
{
#ifdef NETEQ_CNG_CODEC
int i;
for(i=0; i<NUM_CNG_CODECS; i++)
{
if( (inst->CNGpayloadType[i] != -1) && (inst->CNGpayloadType[i] == payloadType) )
{
return 1;
}
}
#endif
return 0;
}
/*
* Return the sample rate for the codec with the given payload type, 0 if error
*/
WebRtc_UWord16 WebRtcNetEQ_DbGetSampleRate(CodecDbInst_t *inst, int payloadType)
{
int i;
CodecFuncInst_t codecInst;
/* Sanity */
if (inst == NULL)
{
/* return 0 Hz */
return 0;
}
/* Check among CNG payloads */
for (i = 0; i < NUM_CNG_CODECS; i++)
{
if ((inst->CNGpayloadType[i] != -1) && (inst->CNGpayloadType[i] == payloadType))
{
switch (i)
{
#ifdef NETEQ_WIDEBAND
case 1:
return 16000;
#endif
#ifdef NETEQ_32KHZ_WIDEBAND
case 2:
return 32000;
#endif
#ifdef NETEQ_48KHZ_WIDEBAND
case 3:
return 48000;
#endif
default:
return 8000;
}
}
}
/* Not a CNG payload, check the other payloads */
i = WebRtcNetEQ_DbGetCodec(inst, payloadType);
if (i >= 0)
{
if (WebRtcNetEQ_DbGetPtrs(inst, (enum WebRtcNetEQDecoder) i, &codecInst) != 0)
{
/* Unexpected error, return 0 Hz */
return 0;
}
return codecInst.codec_fs;
}
/* If we end up here, we got an error, return 0 Hz */
return 0;
}