Mercurial > libavcodec.hg
diff libspeexdec.c @ 8047:ed8906ba4bea libavcodec
Speex decoding via libspeex
author | conrad |
---|---|
date | Fri, 24 Oct 2008 06:29:05 +0000 |
parents | |
children | 4bb782c7793e |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libspeexdec.c Fri Oct 24 06:29:05 2008 +0000 @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2008 David Conrad + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avcodec.h" +#include <speex/speex.h> +#include <speex/speex_header.h> +#include <speex/speex_stereo.h> +#include <speex/speex_callbacks.h> + +typedef struct { + SpeexBits bits; + SpeexStereoState stereo; + void *dec_state; + SpeexHeader *header; +} LibSpeexContext; + + +static av_cold int libspeex_decode_init(AVCodecContext *avctx) +{ + LibSpeexContext *s = avctx->priv_data; + const SpeexMode *mode; + + // defaults in the case of a missing header + if (avctx->sample_rate <= 8000) + mode = &speex_nb_mode; + else if (avctx->sample_rate <= 16000) + mode = &speex_wb_mode; + else + mode = &speex_uwb_mode; + + if (avctx->extradata_size >= 80) + s->header = speex_packet_to_header(avctx->extradata, avctx->extradata_size); + + avctx->sample_fmt = SAMPLE_FMT_S16; + if (s->header) { + avctx->sample_rate = s->header->rate; + avctx->channels = s->header->nb_channels; + avctx->frame_size = s->header->frame_size; + + mode = speex_lib_get_mode(s->header->mode); + if (!mode) { + av_log(avctx, AV_LOG_ERROR, "Unknown Speex mode %d", s->header->mode); + return -1; + } + } else + av_log(avctx, AV_LOG_INFO, "Missing speex header, assuming defaults\n"); + + if (avctx->channels > 2) { + av_log(avctx, AV_LOG_ERROR, "Only stereo and mono supported\n"); + return -1; + } + + speex_bits_init(&s->bits); + s->dec_state = speex_decoder_init(mode); + if (!s->dec_state) { + av_log(avctx, AV_LOG_ERROR, "Error initializing libspeex decoder\n"); + return -1; + } + + if (!s->header) + speex_decoder_ctl(s->dec_state, SPEEX_GET_FRAME_SIZE, &avctx->frame_size); + + if (avctx->channels == 2) { + SpeexCallback callback; + callback.callback_id = SPEEX_INBAND_STEREO; + callback.func = speex_std_stereo_request_handler; + callback.data = &s->stereo; + s->stereo = (SpeexStereoState)SPEEX_STEREO_STATE_INIT; + speex_decoder_ctl(s->dec_state, SPEEX_SET_HANDLER, &callback); + } + return 0; +} + +static int libspeex_decode_frame(AVCodecContext *avctx, + void *data, int *data_size, + const uint8_t *buf, int buf_size) +{ + LibSpeexContext *s = avctx->priv_data; + int16_t *output = data, *end; + int i, num_samples; + + num_samples = avctx->frame_size * avctx->channels; + end = output + *data_size/2; + + speex_bits_read_from(&s->bits, buf, buf_size); + + for (i = 0; speex_bits_remaining(&s->bits) && output + num_samples < end; i++) { + int ret = speex_decode_int(s->dec_state, &s->bits, output); + if (ret <= -2) { + av_log(avctx, AV_LOG_ERROR, "Error decoding speex frame\n"); + return -1; + } else if (ret == -1) + // end of stream + break; + + if (avctx->channels == 2) + speex_decode_stereo_int(output, avctx->frame_size, &s->stereo); + + output += num_samples; + } + + *data_size = i * avctx->channels * avctx->frame_size * 2; + return buf_size; +} + +static av_cold int libspeex_decode_close(AVCodecContext *avctx) +{ + LibSpeexContext *s = avctx->priv_data; + + speex_header_free(s->header); + speex_bits_destroy(&s->bits); + speex_decoder_destroy(s->dec_state); + + return 0; +} + +AVCodec libspeex_decoder = { + "libspeex", + CODEC_TYPE_AUDIO, + CODEC_ID_SPEEX, + sizeof(LibSpeexContext), + libspeex_decode_init, + NULL, + libspeex_decode_close, + libspeex_decode_frame, + .long_name = NULL_IF_CONFIG_SMALL("libspeex"), +};