/* $Id$ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include /* * Only build this file if PJMEDIA_HAS_SPEEX_CODEC != 0 */ #if defined(PJMEDIA_HAS_SPEEX_CODEC) && PJMEDIA_HAS_SPEEX_CODEC!=0 #define THIS_FILE "speex_codec.c" /* Prototypes for Speex factory */ static pj_status_t spx_test_alloc( pjmedia_codec_factory *factory, const pjmedia_codec_info *id ); static pj_status_t spx_default_attr( pjmedia_codec_factory *factory, const pjmedia_codec_info *id, pjmedia_codec_param *attr ); static pj_status_t spx_enum_codecs( pjmedia_codec_factory *factory, unsigned *count, pjmedia_codec_info codecs[]); static pj_status_t spx_alloc_codec( pjmedia_codec_factory *factory, const pjmedia_codec_info *id, pjmedia_codec **p_codec); static pj_status_t spx_dealloc_codec( pjmedia_codec_factory *factory, pjmedia_codec *codec ); /* Prototypes for Speex implementation. */ static pj_status_t spx_codec_init( pjmedia_codec *codec, pj_pool_t *pool ); static pj_status_t spx_codec_open( pjmedia_codec *codec, pjmedia_codec_param *attr ); static pj_status_t spx_codec_close( pjmedia_codec *codec ); static pj_status_t spx_codec_modify(pjmedia_codec *codec, const pjmedia_codec_param *attr ); static pj_status_t spx_codec_parse( pjmedia_codec *codec, void *pkt, pj_size_t pkt_size, const pj_timestamp *ts, unsigned *frame_cnt, pjmedia_frame frames[]); static pj_status_t spx_codec_encode( pjmedia_codec *codec, const struct pjmedia_frame *input, unsigned output_buf_len, struct pjmedia_frame *output); static pj_status_t spx_codec_decode( pjmedia_codec *codec, const struct pjmedia_frame *input, unsigned output_buf_len, struct pjmedia_frame *output); static pj_status_t spx_codec_recover(pjmedia_codec *codec, unsigned output_buf_len, struct pjmedia_frame *output); /* Definition for Speex codec operations. */ static pjmedia_codec_op spx_op = { &spx_codec_init, &spx_codec_open, &spx_codec_close, &spx_codec_modify, &spx_codec_parse, &spx_codec_encode, &spx_codec_decode, &spx_codec_recover }; /* Definition for Speex codec factory operations. */ static pjmedia_codec_factory_op spx_factory_op = { &spx_test_alloc, &spx_default_attr, &spx_enum_codecs, &spx_alloc_codec, &spx_dealloc_codec, &pjmedia_codec_speex_deinit }; /* Index to Speex parameter. */ enum { PARAM_NB, /* Index for narrowband parameter. */ PARAM_WB, /* Index for wideband parameter. */ PARAM_UWB, /* Index for ultra-wideband parameter */ }; /* Speex default parameter */ struct speex_param { int enabled; /* Is this mode enabled? */ const SpeexMode *mode; /* Speex mode. */ int pt; /* Payload type. */ unsigned clock_rate; /* Default sampling rate to be used.*/ int quality; /* Default encoder quality. */ int complexity; /* Default encoder complexity. */ int samples_per_frame; /* Samples per frame. */ int framesize; /* Frame size for current mode. */ int bitrate; /* Bit rate for current mode. */ int max_bitrate; /* Max bit rate for current mode. */ }; /* Speex factory */ static struct spx_factory { pjmedia_codec_factory base; pjmedia_endpt *endpt; pj_pool_t *pool; pj_mutex_t *mutex; pjmedia_codec codec_list; struct speex_param speex_param[3]; } spx_factory; /* Speex codec private data. */ struct spx_private { int param_id; /**< Index to speex param. */ void *enc; /**< Encoder state. */ SpeexBits enc_bits; /**< Encoder bits. */ void *dec; /**< Decoder state. */ SpeexBits dec_bits; /**< Decoder bits. */ }; /* * Get codec bitrate and frame size. */ static pj_status_t get_speex_info( struct speex_param *p ) { void *state; int tmp; /* Create temporary encoder */ state = speex_encoder_init(p->mode); if (!state) return PJMEDIA_CODEC_EFAILED; /* Set the quality */ if (p->quality != -1) speex_encoder_ctl(state, SPEEX_SET_QUALITY, &p->quality); /* Sampling rate. */ speex_encoder_ctl(state, SPEEX_SET_SAMPLING_RATE, &p->clock_rate); /* VAD off to have max bitrate */ tmp = 0; speex_encoder_ctl(state, SPEEX_SET_VAD, &tmp); /* Complexity. */ if (p->complexity != -1) speex_encoder_ctl(state, SPEEX_SET_COMPLEXITY, &p->complexity); /* Now get the frame size */ speex_encoder_ctl(state, SPEEX_GET_FRAME_SIZE, &p->samples_per_frame); /* Now get the average bitrate */ speex_encoder_ctl(state, SPEEX_GET_BITRATE, &p->bitrate); /* Calculate framesize. */ p->framesize = p->bitrate * 20 / 1000; /* Now get the maximum bitrate by using maximum quality (=10) */ tmp = 10; speex_encoder_ctl(state, SPEEX_SET_QUALITY, &tmp); speex_encoder_ctl(state, SPEEX_GET_BITRATE, &p->max_bitrate); /* Destroy encoder. */ speex_encoder_destroy(state); return PJ_SUCCESS; } /* * Initialize and register Speex codec factory to pjmedia endpoint. */ PJ_DEF(pj_status_t) pjmedia_codec_speex_init( pjmedia_endpt *endpt, unsigned options, int quality, int complexity ) { pjmedia_codec_mgr *codec_mgr; unsigned i; pj_status_t status; if (spx_factory.pool != NULL) { /* Already initialized. */ return PJ_SUCCESS; } /* Get defaults */ if (quality < 0) quality = PJMEDIA_CODEC_SPEEX_DEFAULT_QUALITY; if (complexity < 0) complexity = PJMEDIA_CODEC_SPEEX_DEFAULT_COMPLEXITY; /* Validate quality & complexity */ PJ_ASSERT_RETURN(quality >= 0 && quality <= 10, PJ_EINVAL); PJ_ASSERT_RETURN(complexity >= 1 && complexity <= 10, PJ_EINVAL); /* Create Speex codec factory. */ spx_factory.base.op = &spx_factory_op; spx_factory.base.factory_data = NULL; spx_factory.endpt = endpt; spx_factory.pool = pjmedia_endpt_create_pool(endpt, "speex", 4000, 4000); if (!spx_factory.pool) return PJ_ENOMEM; pj_list_init(&spx_factory.codec_list); /* Create mutex. */ status = pj_mutex_create_simple(spx_factory.pool, "speex", &spx_factory.mutex); if (status != PJ_SUCCESS) goto on_error; /* Initialize default Speex parameter. */ spx_factory.speex_param[PARAM_NB].enabled = ((options & PJMEDIA_SPEEX_NO_NB) == 0); spx_factory.speex_param[PARAM_NB].pt = PJMEDIA_RTP_PT_SPEEX_NB; spx_factory.speex_param[PARAM_NB].mode = speex_lib_get_mode(SPEEX_MODEID_NB); spx_factory.speex_param[PARAM_NB].clock_rate = 8000; spx_factory.speex_param[PARAM_NB].quality = quality; spx_factory.speex_param[PARAM_NB].complexity = complexity; spx_factory.speex_param[PARAM_WB].enabled = ((options & PJMEDIA_SPEEX_NO_WB) == 0); spx_factory.speex_param[PARAM_WB].pt = PJMEDIA_RTP_PT_SPEEX_WB; spx_factory.speex_param[PARAM_WB].mode = speex_lib_get_mode(SPEEX_MODEID_WB); spx_factory.speex_param[PARAM_WB].clock_rate = 16000; spx_factory.speex_param[PARAM_WB].quality = quality; spx_factory.speex_param[PARAM_WB].complexity = complexity; spx_factory.speex_param[PARAM_UWB].enabled = ((options & PJMEDIA_SPEEX_NO_UWB) == 0); spx_factory.speex_param[PARAM_UWB].pt = PJMEDIA_RTP_PT_SPEEX_UWB; spx_factory.speex_param[PARAM_UWB].mode = speex_lib_get_mode(SPEEX_MODEID_UWB); spx_factory.speex_param[PARAM_UWB].clock_rate = 32000; spx_factory.speex_param[PARAM_UWB].quality = quality; spx_factory.speex_param[PARAM_UWB].complexity = complexity; /* Somehow quality <=4 is broken in linux. */ if (quality <= 4 && quality >= 0) { PJ_LOG(5,(THIS_FILE, "Adjusting quality to 5 for uwb")); spx_factory.speex_param[PARAM_UWB].quality = 5; } /* Get codec framesize and avg bitrate for each mode. */ for (i=0; i= 0 && quality <= 10, PJ_EINVAL); PJ_ASSERT_RETURN(complexity >= 1 && complexity <= 10, PJ_EINVAL); /* Apply the settings */ for (i=0; i= 0) { PJ_LOG(5,(THIS_FILE, "Adjusting quality to 5 for uwb")); spx_factory.speex_param[PARAM_UWB].quality = 5; } status = get_speex_info(&spx_factory.speex_param[i]); return status; } } return PJ_EINVAL; } /* * Unregister Speex codec factory from pjmedia endpoint and deinitialize * the Speex codec library. */ PJ_DEF(pj_status_t) pjmedia_codec_speex_deinit(void) { pjmedia_codec_mgr *codec_mgr; pj_status_t status; if (spx_factory.pool == NULL) { /* Already deinitialized */ return PJ_SUCCESS; } pj_mutex_lock(spx_factory.mutex); /* We don't want to deinit if there's outstanding codec. */ /* This is silly, as we'll always have codec in the list if we ever allocate a codec! A better behavior maybe is to deallocate all codecs in the list. if (!pj_list_empty(&spx_factory.codec_list)) { pj_mutex_unlock(spx_factory.mutex); return PJ_EBUSY; } */ /* Get the codec manager. */ codec_mgr = pjmedia_endpt_get_codec_mgr(spx_factory.endpt); if (!codec_mgr) { pj_pool_release(spx_factory.pool); spx_factory.pool = NULL; return PJ_EINVALIDOP; } /* Unregister Speex codec factory. */ status = pjmedia_codec_mgr_unregister_factory(codec_mgr, &spx_factory.base); /* Destroy mutex. */ pj_mutex_unlock(spx_factory.mutex); pj_mutex_destroy(spx_factory.mutex); spx_factory.mutex = NULL; /* Destroy pool. */ pj_pool_release(spx_factory.pool); spx_factory.pool = NULL; return status; } /* * Check if factory can allocate the specified codec. */ static pj_status_t spx_test_alloc( pjmedia_codec_factory *factory, const pjmedia_codec_info *info ) { const pj_str_t speex_tag = { "speex", 5}; unsigned i; PJ_UNUSED_ARG(factory); /* Type MUST be audio. */ if (info->type != PJMEDIA_TYPE_AUDIO) return PJMEDIA_CODEC_EUNSUP; /* Check encoding name. */ if (pj_stricmp(&info->encoding_name, &speex_tag) != 0) return PJMEDIA_CODEC_EUNSUP; /* Check clock-rate */ for (i=0; iclock_rate == spx_factory.speex_param[i].clock_rate) { /* Okay, let's Speex! */ return PJ_SUCCESS; } } /* Unsupported, or mode is disabled. */ return PJMEDIA_CODEC_EUNSUP; } /* * Generate default attribute. */ static pj_status_t spx_default_attr (pjmedia_codec_factory *factory, const pjmedia_codec_info *id, pjmedia_codec_param *attr ) { PJ_ASSERT_RETURN(factory==&spx_factory.base, PJ_EINVAL); pj_bzero(attr, sizeof(pjmedia_codec_param)); attr->info.pt = (pj_uint8_t)id->pt; attr->info.channel_cnt = 1; if (id->clock_rate <= 8000) { attr->info.clock_rate = spx_factory.speex_param[PARAM_NB].clock_rate; attr->info.avg_bps = spx_factory.speex_param[PARAM_NB].bitrate; attr->info.max_bps = spx_factory.speex_param[PARAM_NB].max_bitrate; } else if (id->clock_rate <= 16000) { attr->info.clock_rate = spx_factory.speex_param[PARAM_WB].clock_rate; attr->info.avg_bps = spx_factory.speex_param[PARAM_WB].bitrate; attr->info.max_bps = spx_factory.speex_param[PARAM_WB].max_bitrate; } else { /* Wow.. somebody is doing ultra-wideband. Cool...! */ attr->info.clock_rate = spx_factory.speex_param[PARAM_UWB].clock_rate; attr->info.avg_bps = spx_factory.speex_param[PARAM_UWB].bitrate; attr->info.max_bps = spx_factory.speex_param[PARAM_UWB].max_bitrate; } attr->info.pcm_bits_per_sample = 16; attr->info.frm_ptime = 20; attr->info.pt = (pj_uint8_t)id->pt; attr->setting.frm_per_pkt = 1; /* Default flags. */ attr->setting.cng = 1; attr->setting.plc = 1; attr->setting.penh =1 ; attr->setting.vad = 1; return PJ_SUCCESS; } /* * Enum codecs supported by this factory (i.e. only Speex!). */ static pj_status_t spx_enum_codecs(pjmedia_codec_factory *factory, unsigned *count, pjmedia_codec_info codecs[]) { unsigned max; int i; /* Must be signed */ PJ_UNUSED_ARG(factory); PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL); max = *count; *count = 0; /* * We return three codecs here, and in this order: * - ultra-wideband, wideband, and narrowband. */ for (i=PJ_ARRAY_SIZE(spx_factory.speex_param)-1; i>=0 && *countop = &spx_op; codec->factory = factory; codec->codec_data = pj_pool_alloc(spx_factory.pool, sizeof(struct spx_private)); } pj_mutex_unlock(spx_factory.mutex); spx = (struct spx_private*) codec->codec_data; spx->enc = NULL; spx->dec = NULL; if (id->clock_rate <= 8000) spx->param_id = PARAM_NB; else if (id->clock_rate <= 16000) spx->param_id = PARAM_WB; else spx->param_id = PARAM_UWB; *p_codec = codec; return PJ_SUCCESS; } /* * Free codec. */ static pj_status_t spx_dealloc_codec( pjmedia_codec_factory *factory, pjmedia_codec *codec ) { struct spx_private *spx; PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL); PJ_ASSERT_RETURN(factory == &spx_factory.base, PJ_EINVAL); /* Close codec, if it's not closed. */ spx = (struct spx_private*) codec->codec_data; if (spx->enc != NULL || spx->dec != NULL) { spx_codec_close(codec); } /* Put in the free list. */ pj_mutex_lock(spx_factory.mutex); pj_list_push_front(&spx_factory.codec_list, codec); pj_mutex_unlock(spx_factory.mutex); return PJ_SUCCESS; } /* * Init codec. */ static pj_status_t spx_codec_init( pjmedia_codec *codec, pj_pool_t *pool ) { PJ_UNUSED_ARG(codec); PJ_UNUSED_ARG(pool); return PJ_SUCCESS; } /* * Open codec. */ static pj_status_t spx_codec_open( pjmedia_codec *codec, pjmedia_codec_param *attr ) { struct spx_private *spx; int id, tmp; spx = (struct spx_private*) codec->codec_data; id = spx->param_id; /* * Create and initialize encoder. */ spx->enc = speex_encoder_init(spx_factory.speex_param[id].mode); if (!spx->enc) return PJMEDIA_CODEC_EFAILED; speex_bits_init(&spx->enc_bits); /* Set the quality*/ if (spx_factory.speex_param[id].quality != -1) { speex_encoder_ctl(spx->enc, SPEEX_SET_QUALITY, &spx_factory.speex_param[id].quality); } /* Sampling rate. */ tmp = attr->info.clock_rate; speex_encoder_ctl(spx->enc, SPEEX_SET_SAMPLING_RATE, &spx_factory.speex_param[id].clock_rate); /* VAD */ tmp = (attr->setting.vad != 0); speex_encoder_ctl(spx->enc, SPEEX_SET_VAD, &tmp); speex_encoder_ctl(spx->enc, SPEEX_SET_DTX, &tmp); /* Complexity */ if (spx_factory.speex_param[id].complexity != -1) { speex_encoder_ctl(spx->enc, SPEEX_SET_COMPLEXITY, &spx_factory.speex_param[id].complexity); } /* * Create and initialize decoder. */ spx->dec = speex_decoder_init(spx_factory.speex_param[id].mode); if (!spx->dec) { spx_codec_close(codec); return PJMEDIA_CODEC_EFAILED; } speex_bits_init(&spx->dec_bits); /* Sampling rate. */ speex_decoder_ctl(spx->dec, SPEEX_SET_SAMPLING_RATE, &spx_factory.speex_param[id].clock_rate); /* PENH */ tmp = attr->setting.penh; speex_decoder_ctl(spx->dec, SPEEX_SET_ENH, &tmp); return PJ_SUCCESS; } /* * Close codec. */ static pj_status_t spx_codec_close( pjmedia_codec *codec ) { struct spx_private *spx; spx = (struct spx_private*) codec->codec_data; /* Destroy encoder*/ if (spx->enc) { speex_encoder_destroy( spx->enc ); spx->enc = NULL; speex_bits_destroy( &spx->enc_bits ); } /* Destroy decoder */ if (spx->dec) { speex_decoder_destroy( spx->dec); spx->dec = NULL; speex_bits_destroy( &spx->dec_bits ); } return PJ_SUCCESS; } /* * Modify codec settings. */ static pj_status_t spx_codec_modify(pjmedia_codec *codec, const pjmedia_codec_param *attr ) { struct spx_private *spx; int tmp; spx = (struct spx_private*) codec->codec_data; /* VAD */ tmp = (attr->setting.vad != 0); speex_encoder_ctl(spx->enc, SPEEX_SET_VAD, &tmp); speex_encoder_ctl(spx->enc, SPEEX_SET_DTX, &tmp); /* PENH */ tmp = attr->setting.penh; speex_decoder_ctl(spx->dec, SPEEX_SET_ENH, &tmp); return PJ_SUCCESS; } #if 0 # define TRACE__(args) PJ_LOG(5,args) #else # define TRACE__(args) #endif #undef THIS_FUNC #define THIS_FUNC "speex_get_next_frame" #define NB_SUBMODES 16 #define NB_SUBMODE_BITS 4 #define SB_SUBMODES 8 #define SB_SUBMODE_BITS 3 /* This function will iterate frames & submodes in the Speex bits. * Returns 0 if a frame found, otherwise returns -1. */ static int speex_get_next_frame(SpeexBits *bits) { static const int inband_skip_table[NB_SUBMODES] = {1, 1, 4, 4, 4, 4, 4, 4, 8, 8, 16, 16, 32, 32, 64, 64 }; static const int wb_skip_table[SB_SUBMODES] = {SB_SUBMODE_BITS+1, 36, 112, 192, 352, -1, -1, -1}; unsigned submode; unsigned nb_count = 0; while (speex_bits_remaining(bits) >= 5) { unsigned wb_count = 0; unsigned bit_ptr = bits->bitPtr; unsigned char_ptr = bits->charPtr; /* WB frame */ while ((speex_bits_remaining(bits) >= 4) && speex_bits_unpack_unsigned(bits, 1)) { int advance; submode = speex_bits_unpack_unsigned(bits, 3); advance = wb_skip_table[submode]; if (advance < 0) { TRACE__((THIS_FUNC, "Invalid mode encountered. " "The stream is corrupted.")); return -1; } TRACE__((THIS_FUNC, "WB layer skipped: %d bits", advance)); advance -= (SB_SUBMODE_BITS+1); speex_bits_advance(bits, advance); bit_ptr = bits->bitPtr; char_ptr = bits->charPtr; /* Consecutive subband frames may not exceed 2 frames */ if (++wb_count > 2) return -1; } /* End of bits, return the frame */ if (speex_bits_remaining(bits) < 4) { TRACE__((THIS_FUNC, "End of stream")); return 0; } /* Stop iteration, return the frame */ if (nb_count > 0) { bits->bitPtr = bit_ptr; bits->charPtr = char_ptr; return 0; } /* Get control bits */ submode = speex_bits_unpack_unsigned(bits, 4); TRACE__((THIS_FUNC, "Control bits: %d at %d", submode, bits->charPtr*8+bits->bitPtr)); if (submode == 15) { TRACE__((THIS_FUNC, "Found submode: terminator")); return -1; } else if (submode == 14) { /* in-band signal; next 4 bits contain signal id */ submode = speex_bits_unpack_unsigned(bits, 4); TRACE__((THIS_FUNC, "Found submode: in-band %d bits", inband_skip_table[submode])); speex_bits_advance(bits, inband_skip_table[submode]); } else if (submode == 13) { /* user in-band; next 5 bits contain msg len */ submode = speex_bits_unpack_unsigned(bits, 5); TRACE__((THIS_FUNC, "Found submode: user-band %d bytes", submode)); speex_bits_advance(bits, submode * 8); } else if (submode > 8) { TRACE__((THIS_FUNC, "Unknown sub-mode %d", submode)); return -1; } else { /* NB frame */ int advance = submode; speex_mode_query(&speex_nb_mode, SPEEX_SUBMODE_BITS_PER_FRAME, &advance); if (advance < 0) { TRACE__((THIS_FUNC, "Invalid mode encountered. " "The stream is corrupted.")); return -1; } TRACE__((THIS_FUNC, "Submode %d: %d bits", submode, advance)); advance -= (NB_SUBMODE_BITS+1); speex_bits_advance(bits, advance); ++nb_count; } } return 0; } /* * Get frames in the packet. */ static pj_status_t spx_codec_parse( pjmedia_codec *codec, void *pkt, pj_size_t pkt_size, const pj_timestamp *ts, unsigned *frame_cnt, pjmedia_frame frames[]) { struct spx_private *spx = (struct spx_private*) codec->codec_data; unsigned samples_per_frame; unsigned count = 0; int char_ptr = 0; int bit_ptr = 0; samples_per_frame=spx_factory.speex_param[spx->param_id].samples_per_frame; /* Copy the data into the speex bit-stream */ speex_bits_read_from(&spx->dec_bits, (char*)pkt, (int)pkt_size); while (speex_get_next_frame(&spx->dec_bits) == 0 && spx->dec_bits.charPtr != char_ptr) { frames[count].buf = (char*)pkt + char_ptr; /* Bit info contains start bit offset of the frame */ frames[count].bit_info = bit_ptr; frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO; frames[count].timestamp.u64 = ts->u64 + count * samples_per_frame; frames[count].size = spx->dec_bits.charPtr - char_ptr; if (spx->dec_bits.bitPtr) ++frames[count].size; bit_ptr = spx->dec_bits.bitPtr; char_ptr = spx->dec_bits.charPtr; ++count; } *frame_cnt = count; return PJ_SUCCESS; } /* * Encode frames. */ static pj_status_t spx_codec_encode( pjmedia_codec *codec, const struct pjmedia_frame *input, unsigned output_buf_len, struct pjmedia_frame *output) { struct spx_private *spx; unsigned samples_per_frame; int tx = 0; spx_int16_t *pcm_in = (spx_int16_t*)input->buf; pj_size_t nsamples; spx = (struct spx_private*) codec->codec_data; if (input->type != PJMEDIA_FRAME_TYPE_AUDIO) { output->size = 0; output->buf = NULL; output->timestamp = input->timestamp; output->type = input->type; return PJ_SUCCESS; } nsamples = input->size >> 1; samples_per_frame=spx_factory.speex_param[spx->param_id].samples_per_frame; PJ_ASSERT_RETURN(nsamples % samples_per_frame == 0, PJMEDIA_CODEC_EPCMFRMINLEN); /* Flush all the bits in the struct so we can encode a new frame */ speex_bits_reset(&spx->enc_bits); /* Encode the frames */ while (nsamples >= samples_per_frame) { tx += speex_encode_int(spx->enc, pcm_in, &spx->enc_bits); pcm_in += samples_per_frame; nsamples -= samples_per_frame; } /* Check if we need not to transmit the frame (DTX) */ if (tx == 0) { output->buf = NULL; output->size = 0; output->timestamp.u64 = input->timestamp.u64; output->type = PJMEDIA_FRAME_TYPE_NONE; return PJ_SUCCESS; } /* Check size. */ pj_assert(speex_bits_nbytes(&spx->enc_bits) <= (int)output_buf_len); /* Copy the bits to an array of char that can be written */ output->size = speex_bits_write(&spx->enc_bits, (char*)output->buf, output_buf_len); output->type = PJMEDIA_FRAME_TYPE_AUDIO; output->timestamp = input->timestamp; return PJ_SUCCESS; } /* * Decode frame. */ static pj_status_t spx_codec_decode( pjmedia_codec *codec, const struct pjmedia_frame *input, unsigned output_buf_len, struct pjmedia_frame *output) { struct spx_private *spx; unsigned samples_per_frame; spx = (struct spx_private*) codec->codec_data; samples_per_frame=spx_factory.speex_param[spx->param_id].samples_per_frame; PJ_ASSERT_RETURN(output_buf_len >= samples_per_frame << 1, PJMEDIA_CODEC_EPCMTOOSHORT); if (input->type != PJMEDIA_FRAME_TYPE_AUDIO) { pjmedia_zero_samples((pj_int16_t*)output->buf, samples_per_frame); output->size = samples_per_frame << 1; output->timestamp.u64 = input->timestamp.u64; output->type = PJMEDIA_FRAME_TYPE_AUDIO; return PJ_SUCCESS; } /* Copy the data into the bit-stream struct */ speex_bits_read_from(&spx->dec_bits, (char*)input->buf, (int)input->size); /* Set Speex dec_bits pointer to the start bit of the frame */ speex_bits_advance(&spx->dec_bits, input->bit_info); /* Decode the data */ speex_decode_int(spx->dec, &spx->dec_bits, (spx_int16_t*)output->buf); output->type = PJMEDIA_FRAME_TYPE_AUDIO; output->size = samples_per_frame << 1; output->timestamp.u64 = input->timestamp.u64; return PJ_SUCCESS; } /* * Recover lost frame. */ static pj_status_t spx_codec_recover(pjmedia_codec *codec, unsigned output_buf_len, struct pjmedia_frame *output) { struct spx_private *spx; unsigned count; /* output_buf_len is unreferenced when building in Release mode */ PJ_UNUSED_ARG(output_buf_len); spx = (struct spx_private*) codec->codec_data; count = spx_factory.speex_param[spx->param_id].clock_rate * 20 / 1000; pj_assert(count <= output_buf_len/2); /* Recover packet loss */ speex_decode_int(spx->dec, NULL, (spx_int16_t*) output->buf); output->size = count * 2; return PJ_SUCCESS; } #endif /* PJMEDIA_HAS_SPEEX_CODEC */