/* * 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. */ /* * Implementation of the codec database. */ #include "codec_db.h" #include /* 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, 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 #ifdef NETEQ_48KHZ_WIDEBAND &&(codec_fs!=48000) #endif ) { return CODEC_DB_UNSUPPORTED_FS; } /* Ensure that the codec type is supported */ switch (codec) { #ifdef NETEQ_PCM16B_CODEC case kDecoderPCM16B : #endif #ifdef NETEQ_G711_CODEC case kDecoderPCMu : case kDecoderPCMa : #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_G722_CODEC case kDecoderG722 : #endif #ifdef NETEQ_WIDEBAND case kDecoderPCM16Bwb : #endif #ifdef NETEQ_32KHZ_WIDEBAND case kDecoderPCM16Bswb32kHz : #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_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->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(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_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_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: { inst->deltaBytes = -12; inst->deltaTime = 1; return 0; } #endif #if (defined NETEQ_G722_CODEC) case kDecoderG722: { inst->deltaBytes = -14; inst->deltaTime = 0; return 0; } #endif #if (defined NETEQ_PCM16B_CODEC) case kDecoderPCM16B: { inst->deltaBytes = -12; inst->deltaTime = 2; return 0; } #endif #if ((defined NETEQ_PCM16B_CODEC)&&(defined NETEQ_WIDEBAND)) case kDecoderPCM16Bwb: { inst->deltaBytes = -14; inst->deltaTime = 2; return 0; } #endif #if ((defined NETEQ_PCM16B_CODEC)&&(defined NETEQ_32KHZ_WIDEBAND)) case kDecoderPCM16Bswb32kHz: { 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(CodecDbInst_t *inst, int payloadType) { #ifdef NETEQ_CNG_CODEC int i; for(i=0; iCNGpayloadType[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) { case 1: return 16000; case 2: return 32000; case 3: return 48000; 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; }