191 lines
4.8 KiB
C
191 lines
4.8 KiB
C
|
/*
|
||
|
* iaxclient: a cross-platform IAX softphone library
|
||
|
*
|
||
|
* Copyrights:
|
||
|
* Copyright (C) 2003-2004, Horizon Wimba, Inc.
|
||
|
* Copyright (C) 2007, Wimba, Inc.
|
||
|
*
|
||
|
* Contributors:
|
||
|
* Steve Kann <stevek@stevek.com>
|
||
|
*
|
||
|
* This program is free software, distributed under the terms of
|
||
|
* the GNU Lesser (Library) General Public License.
|
||
|
*/
|
||
|
|
||
|
#include "codec_speex.h"
|
||
|
#include "iaxclient_lib.h"
|
||
|
#include "speex/speex.h"
|
||
|
|
||
|
struct State
|
||
|
{
|
||
|
void *state;
|
||
|
int frame_size;
|
||
|
SpeexBits bits;
|
||
|
};
|
||
|
|
||
|
|
||
|
static void destroy ( struct iaxc_audio_codec *c)
|
||
|
{
|
||
|
struct State * encstate = (struct State *) c->encstate;
|
||
|
struct State * decstate = (struct State *) c->decstate;
|
||
|
|
||
|
speex_bits_destroy(&encstate->bits);
|
||
|
speex_bits_destroy(&decstate->bits);
|
||
|
speex_encoder_destroy(encstate->state);
|
||
|
speex_decoder_destroy(decstate->state);
|
||
|
|
||
|
free(c->encstate);
|
||
|
free(c->decstate);
|
||
|
|
||
|
free(c);
|
||
|
}
|
||
|
|
||
|
|
||
|
static int decode( struct iaxc_audio_codec *c,
|
||
|
int *inlen, unsigned char *in, int *outlen, short *out )
|
||
|
{
|
||
|
struct State * decstate = (struct State *) c->decstate;
|
||
|
|
||
|
if ( *inlen == 0 )
|
||
|
{
|
||
|
speex_decode_int(decstate->state, NULL, out);
|
||
|
*outlen -= decstate->frame_size;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
speex_bits_read_from(&decstate->bits, (char *) in, *inlen);
|
||
|
*inlen = 0;
|
||
|
|
||
|
while ( speex_bits_remaining(&decstate->bits) &&
|
||
|
*outlen >= decstate->frame_size )
|
||
|
{
|
||
|
int ret = speex_decode_int(decstate->state, &decstate->bits, out);
|
||
|
|
||
|
// from speex/speex.h, speex_decode returns:
|
||
|
// @return return status (0 for no error, -1 for end of stream, -2 other)
|
||
|
if (ret == 0)
|
||
|
{
|
||
|
/* one frame of output */
|
||
|
*outlen -= decstate->frame_size;
|
||
|
out += decstate->frame_size;
|
||
|
} else if (ret == -1)
|
||
|
{
|
||
|
/* at end of stream, or just a terminator */
|
||
|
int bits_left = speex_bits_remaining(&decstate->bits) % 8;
|
||
|
if(bits_left >= 5)
|
||
|
speex_bits_advance(&decstate->bits, bits_left);
|
||
|
else
|
||
|
break;
|
||
|
} else
|
||
|
{
|
||
|
/* maybe there's not a whole frame somehow? */
|
||
|
fprintf(stderr, "decode_int returned non-zero => %d\n",ret);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int encode( struct iaxc_audio_codec *c,
|
||
|
int *inlen, short *in, int *outlen, unsigned char *out )
|
||
|
{
|
||
|
int bytes;
|
||
|
struct State * encstate = (struct State *) c->encstate;
|
||
|
|
||
|
/* need to encode minimum of encstate->frame_size samples */
|
||
|
|
||
|
/* only add terminator at end of bits */
|
||
|
speex_bits_reset(&encstate->bits);
|
||
|
|
||
|
/* need to encode minimum of encstate->frame_size samples */
|
||
|
while(*inlen >= encstate->frame_size)
|
||
|
{
|
||
|
//fprintf(stderr, "encode: inlen=%d outlen=%d\n", *inlen, *outlen);
|
||
|
speex_encode_int(encstate->state, in, &encstate->bits);
|
||
|
*inlen -= encstate->frame_size;
|
||
|
in += encstate->frame_size;
|
||
|
}
|
||
|
|
||
|
/* add terminator */
|
||
|
speex_bits_pack(&encstate->bits, 15, 5);
|
||
|
|
||
|
bytes = speex_bits_write(&encstate->bits, (char *) out, *outlen);
|
||
|
|
||
|
/* can an error happen here? no bytes? */
|
||
|
*outlen -= bytes;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
struct iaxc_audio_codec *codec_audio_speex_new(struct iaxc_speex_settings *set)
|
||
|
{
|
||
|
struct State * encstate;
|
||
|
struct State * decstate;
|
||
|
struct iaxc_audio_codec *c = (struct iaxc_audio_codec *)calloc(sizeof(struct iaxc_audio_codec),1);
|
||
|
const SpeexMode *sm;
|
||
|
|
||
|
if(!c)
|
||
|
return c;
|
||
|
|
||
|
strcpy(c->name,"speex");
|
||
|
c->format = IAXC_FORMAT_SPEEX;
|
||
|
c->encode = encode;
|
||
|
c->decode = decode;
|
||
|
c->destroy = destroy;
|
||
|
|
||
|
c->encstate = calloc(sizeof(struct State),1);
|
||
|
c->decstate = calloc(sizeof(struct State),1);
|
||
|
|
||
|
/* leaks a bit on no-memory */
|
||
|
if(!(c->encstate && c->decstate))
|
||
|
return NULL;
|
||
|
|
||
|
encstate = (struct State *) c->encstate;
|
||
|
decstate = (struct State *) c->decstate;
|
||
|
|
||
|
sm = speex_lib_get_mode(SPEEX_MODEID_NB);
|
||
|
|
||
|
encstate->state = speex_encoder_init(sm);
|
||
|
decstate->state = speex_decoder_init(sm);
|
||
|
speex_bits_init(&encstate->bits);
|
||
|
speex_bits_init(&decstate->bits);
|
||
|
speex_bits_reset(&encstate->bits);
|
||
|
speex_bits_reset(&decstate->bits);
|
||
|
|
||
|
speex_decoder_ctl(decstate->state, SPEEX_SET_ENH, &set->decode_enhance);
|
||
|
|
||
|
speex_encoder_ctl(encstate->state, SPEEX_SET_COMPLEXITY, &set->complexity);
|
||
|
|
||
|
if(set->quality >= 0) {
|
||
|
if(set->vbr) {
|
||
|
speex_encoder_ctl(encstate->state, SPEEX_SET_VBR_QUALITY, &set->quality);
|
||
|
} else {
|
||
|
int quality = (int)set->quality;
|
||
|
speex_encoder_ctl(encstate->state, SPEEX_SET_QUALITY, &quality);
|
||
|
}
|
||
|
}
|
||
|
if(set->bitrate >= 0)
|
||
|
speex_encoder_ctl(encstate->state, SPEEX_SET_BITRATE, &set->bitrate);
|
||
|
if(set->vbr)
|
||
|
speex_encoder_ctl(encstate->state, SPEEX_SET_VBR, &set->vbr);
|
||
|
if(set->abr)
|
||
|
speex_encoder_ctl(encstate->state, SPEEX_SET_ABR, &set->abr);
|
||
|
|
||
|
/* set up frame sizes (normally, this is 20ms worth) */
|
||
|
speex_encoder_ctl(encstate->state,SPEEX_GET_FRAME_SIZE,&encstate->frame_size);
|
||
|
speex_decoder_ctl(decstate->state,SPEEX_GET_FRAME_SIZE,&decstate->frame_size);
|
||
|
|
||
|
c->minimum_frame_size = 160;
|
||
|
|
||
|
if(encstate->frame_size > c->minimum_frame_size)
|
||
|
c->minimum_frame_size = encstate->frame_size;
|
||
|
if(decstate->frame_size > c->minimum_frame_size)
|
||
|
c->minimum_frame_size = decstate->frame_size;
|
||
|
|
||
|
if(!(encstate->state && decstate->state))
|
||
|
return NULL;
|
||
|
|
||
|
return c;
|
||
|
}
|
||
|
|