X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavcodec%2Flibspeexdec.c;h=949a9344c1ce8057947512915a7e5986cbe4f501;hb=104a4289f925c600c13035de6c282fe656510a27;hp=c5cfbd51087b1d1f8b6d0be76a4f06ba91b8cf30;hpb=72415b2adb2c25f95ceede49001bb97ed9247dbb;p=ffmpeg diff --git a/libavcodec/libspeexdec.c b/libavcodec/libspeexdec.c index c5cfbd51087..949a9344c1c 100644 --- a/libavcodec/libspeexdec.c +++ b/libavcodec/libspeexdec.c @@ -1,34 +1,37 @@ /* * Copyright (C) 2008 David Conrad * - * This file is part of FFmpeg. + * This file is part of Libav. * - * FFmpeg is free software; you can redistribute it and/or + * Libav 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, + * Libav 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 + * License along with Libav; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "avcodec.h" #include #include #include #include -typedef struct { +#include "libavutil/channel_layout.h" +#include "libavutil/common.h" +#include "avcodec.h" +#include "internal.h" + +typedef struct LibSpeexContext { SpeexBits bits; SpeexStereoState stereo; void *dec_state; - SpeexHeader *header; int frame_size; } LibSpeexContext; @@ -37,38 +40,50 @@ 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->frame_size = s->header->frame_size; - if (s->header->frames_per_packet) - avctx->frame_size *= s->header->frames_per_packet; - - 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; + SpeexHeader *header = NULL; + int spx_mode; + + avctx->sample_fmt = AV_SAMPLE_FMT_S16; + if (avctx->extradata && avctx->extradata_size >= 80) { + header = speex_packet_to_header(avctx->extradata, + avctx->extradata_size); + if (!header) + av_log(avctx, AV_LOG_WARNING, "Invalid Speex header\n"); + } + if (header) { + avctx->channels = header->nb_channels; + spx_mode = header->mode; + speex_header_free(header); + } else { + switch (avctx->sample_rate) { + case 8000: spx_mode = 0; break; + case 16000: spx_mode = 1; break; + case 32000: spx_mode = 2; break; + default: + /* libspeex can handle any mode if initialized as ultra-wideband */ + av_log(avctx, AV_LOG_WARNING, "Invalid sample rate: %d\n" + "Decoding as 32kHz ultra-wideband\n", + avctx->sample_rate); + spx_mode = 2; } - } 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 are supported.\n"); - return -1; + mode = speex_lib_get_mode(spx_mode); + if (!mode) { + av_log(avctx, AV_LOG_ERROR, "Unknown Speex mode %d", spx_mode); + return AVERROR_INVALIDDATA; + } + avctx->sample_rate = 8000 << spx_mode; + s->frame_size = 160 << spx_mode; + + if (avctx->channels < 1 || avctx->channels > 2) { + /* libspeex can handle mono or stereo if initialized as stereo */ + av_log(avctx, AV_LOG_ERROR, "Invalid channel count: %d.\n" + "Decoding as stereo.\n", avctx->channels); + avctx->channels = 2; } + avctx->channel_layout = avctx->channels == 2 ? AV_CH_LAYOUT_STEREO : + AV_CH_LAYOUT_MONO; speex_bits_init(&s->bits); s->dec_state = speex_decoder_init(mode); @@ -77,10 +92,6 @@ static av_cold int libspeex_decode_init(AVCodecContext *avctx) return -1; } - if (!s->header) { - speex_decoder_ctl(s->dec_state, SPEEX_GET_FRAME_SIZE, &s->frame_size); - } - if (avctx->channels == 2) { SpeexCallback callback; callback.callback_id = SPEEX_INBAND_STEREO; @@ -89,63 +100,83 @@ static av_cold int libspeex_decode_init(AVCodecContext *avctx) 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, - AVPacket *avpkt) +static int libspeex_decode_frame(AVCodecContext *avctx, void *data, + int *got_frame_ptr, AVPacket *avpkt) { - const uint8_t *buf = avpkt->data; + uint8_t *buf = avpkt->data; int buf_size = avpkt->size; LibSpeexContext *s = avctx->priv_data; - int16_t *output = data, *end; - int i, num_samples; - - num_samples = s->frame_size * avctx->channels; - end = output + *data_size / sizeof(*output); - - 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, s->frame_size, &s->stereo); + AVFrame *frame = data; + int16_t *output; + int ret, consumed = 0; + + /* get output buffer */ + frame->nb_samples = s->frame_size; + if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) { + av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n"); + return ret; + } + output = (int16_t *)frame->data[0]; + + /* if there is not enough data left for the smallest possible frame or the + next 5 bits are a terminator code, reset the libspeex buffer using the + current packet, otherwise ignore the current packet and keep decoding + frames from the libspeex buffer. */ + if (speex_bits_remaining(&s->bits) < 5 || + speex_bits_peek_unsigned(&s->bits, 5) == 0xF) { + /* check for flush packet */ + if (!buf || !buf_size) { + *got_frame_ptr = 0; + return buf_size; + } + /* set new buffer */ + speex_bits_read_from(&s->bits, buf, buf_size); + consumed = buf_size; + } - output += num_samples; + /* decode a single frame */ + 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 AVERROR_INVALIDDATA; } + if (avctx->channels == 2) + speex_decode_stereo_int(output, s->frame_size, &s->stereo); - avctx->frame_size = s->frame_size * i; - *data_size = avctx->channels * avctx->frame_size * sizeof(*output); - return buf_size; + *got_frame_ptr = 1; + + return consumed; } 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", - AVMEDIA_TYPE_AUDIO, - CODEC_ID_SPEEX, - sizeof(LibSpeexContext), - libspeex_decode_init, - NULL, - libspeex_decode_close, - libspeex_decode_frame, - .long_name = NULL_IF_CONFIG_SMALL("libspeex Speex"), +static av_cold void libspeex_decode_flush(AVCodecContext *avctx) +{ + LibSpeexContext *s = avctx->priv_data; + speex_bits_reset(&s->bits); +} + +AVCodec ff_libspeex_decoder = { + .name = "libspeex", + .long_name = NULL_IF_CONFIG_SMALL("libspeex Speex"), + .type = AVMEDIA_TYPE_AUDIO, + .id = AV_CODEC_ID_SPEEX, + .priv_data_size = sizeof(LibSpeexContext), + .init = libspeex_decode_init, + .close = libspeex_decode_close, + .decode = libspeex_decode_frame, + .flush = libspeex_decode_flush, + .capabilities = AV_CODEC_CAP_SUBFRAMES | AV_CODEC_CAP_DELAY | AV_CODEC_CAP_DR1, };