From a52cdcd296c40882c3b4f88958990c56f0ce3019 Mon Sep 17 00:00:00 2001 From: Art Clarke Date: Sun, 11 Sep 2011 03:14:14 +0200 Subject: [PATCH] libspeex encoder wraper taken from svn head of xuggle --- configure | 3 +- libavcodec/Makefile | 1 + libavcodec/allcodecs.c | 2 +- libavcodec/libspeexenc.c | 178 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 182 insertions(+), 2 deletions(-) create mode 100644 libavcodec/libspeexenc.c diff --git a/configure b/configure index 10269db2f0..cc00b5b34f 100755 --- a/configure +++ b/configure @@ -178,7 +178,7 @@ External library support: --enable-libopenjpeg enable JPEG 2000 decoding via OpenJPEG [no] --enable-librtmp enable RTMP[E] support via librtmp [no] --enable-libschroedinger enable Dirac support via libschroedinger [no] - --enable-libspeex enable Speex decoding via libspeex [no] + --enable-libspeex enable Speex encoding and decoding via libspeex [no] --enable-libtheora enable Theora encoding via libtheora [no] --enable-libvo-aacenc enable AAC encoding via libvo-aacenc [no] --enable-libvo-amrwbenc enable AMR-WB encoding via libvo-amrwbenc [no] @@ -1429,6 +1429,7 @@ libopenjpeg_decoder_deps="libopenjpeg" libschroedinger_decoder_deps="libschroedinger" libschroedinger_encoder_deps="libschroedinger" libspeex_decoder_deps="libspeex" +libspeex_encoder_deps="libspeex" libtheora_encoder_deps="libtheora" libvo_aacenc_encoder_deps="libvo_aacenc" libvo_amrwbenc_encoder_deps="libvo_amrwbenc" diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 9f8264f7b7..741a9b3653 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -593,6 +593,7 @@ OBJS-$(CONFIG_LIBSCHROEDINGER_ENCODER) += libschroedingerenc.o \ libschroedinger.o \ libdirac_libschro.o OBJS-$(CONFIG_LIBSPEEX_DECODER) += libspeexdec.o +OBJS-$(CONFIG_LIBSPEEX_ENCODER) += libspeexenc.o OBJS-$(CONFIG_LIBTHEORA_ENCODER) += libtheoraenc.o OBJS-$(CONFIG_LIBVO_AACENC_ENCODER) += libvo-aacenc.o mpeg4audio.o OBJS-$(CONFIG_LIBVO_AMRWBENC_ENCODER) += libvo-amrwbenc.o diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index 1a99b07b4f..305e7985b0 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -381,7 +381,7 @@ void avcodec_register_all(void) REGISTER_DECODER (LIBOPENCORE_AMRWB, libopencore_amrwb); REGISTER_DECODER (LIBOPENJPEG, libopenjpeg); REGISTER_ENCDEC (LIBSCHROEDINGER, libschroedinger); - REGISTER_DECODER (LIBSPEEX, libspeex); + REGISTER_ENCDEC (LIBSPEEX, libspeex); REGISTER_ENCODER (LIBTHEORA, libtheora); REGISTER_ENCODER (LIBVO_AACENC, libvo_aacenc); REGISTER_ENCODER (LIBVO_AMRWBENC, libvo_amrwbenc); diff --git a/libavcodec/libspeexenc.c b/libavcodec/libspeexenc.c new file mode 100644 index 0000000000..79a9fb0760 --- /dev/null +++ b/libavcodec/libspeexenc.c @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2009 by Xuggle Incorporated. All rights reserved. + * 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 +#include +#include +#include + +typedef struct { + SpeexBits bits; + void *enc_state; + SpeexHeader header; +} LibSpeexEncContext; + + +static av_cold int libspeex_encode_init(AVCodecContext *avctx) +{ + LibSpeexEncContext *s = (LibSpeexEncContext*)avctx->priv_data; + const SpeexMode *mode; + + if ((avctx->sample_fmt != SAMPLE_FMT_S16 && avctx->sample_fmt != SAMPLE_FMT_FLT) || + avctx->sample_rate <= 0 || + avctx->channels <= 0 || + avctx->channels > 2) + { + av_log(avctx, AV_LOG_ERROR, "Unsupported sample format, rate, or channels for speex"); + return -1; + } + + if (avctx->sample_rate <= 8000) + mode = &speex_nb_mode; + else if (avctx->sample_rate <= 16000) + mode = &speex_wb_mode; + else + mode = &speex_uwb_mode; + + speex_bits_init(&s->bits); + s->enc_state = speex_encoder_init(mode); + if (!s->enc_state) + { + av_log(avctx, AV_LOG_ERROR, "could not initialize speex encoder"); + return -1; + } + + // initialize the header + speex_init_header(&s->header, avctx->sample_rate, + avctx->channels, mode); + + // TODO: It'd be nice to support VBR here, but + // I'm uncertain what AVCodecContext options to use + // to signal whether to turn it on. + if (avctx->flags & CODEC_FLAG_QSCALE) { + spx_int32_t quality = 0; + // Map global_quality's mpeg 1/2/4 scale into Speex's 0-10 scale + if (avctx->global_quality > FF_LAMBDA_MAX) + quality = 0; // lowest possible quality + else + quality = (spx_int32_t)((FF_LAMBDA_MAX-avctx->global_quality)*10.0/FF_LAMBDA_MAX); + speex_encoder_ctl(s->enc_state, SPEEX_SET_QUALITY, &quality); + } else { + // default to CBR + if (avctx->bit_rate > 0) + speex_encoder_ctl(s->enc_state, SPEEX_SET_BITRATE, &avctx->bit_rate); + // otherwise just take the default quality setting + } + // reset the bit-rate to the actual bit rate speex will use + speex_encoder_ctl(s->enc_state, SPEEX_GET_BITRATE, &s->header.bitrate); + avctx->bit_rate = s->header.bitrate; + + // get the actual sample rate + speex_encoder_ctl(s->enc_state, SPEEX_GET_SAMPLING_RATE, &s->header.rate); + avctx->sample_rate = s->header.rate; + + // get the frame-size. To align with FLV, we're going to put 2 frames + // per packet. If someone can tell me how to make this configurable + // from the avcodec contents, I'll mod this so it's not hard-coded. + // but without this, FLV files with speex data won't play correctly + // in flash player 10. + speex_encoder_ctl(s->enc_state, SPEEX_GET_FRAME_SIZE, &s->header.frame_size); + s->header.frames_per_packet = 2; // Need for FLV container support + avctx->frame_size = s->header.frame_size*s->header.frames_per_packet; + + // and we'll put a speex header packet into extradata so that muxers + // can use it. + avctx->extradata = speex_header_to_packet(&s->header, &avctx->extradata_size); + return 0; +} + +static av_cold int libspeex_encode_frame( + AVCodecContext *avctx, uint8_t *frame, + int buf_size, void *data) +{ + LibSpeexEncContext *s = (LibSpeexEncContext*)avctx->priv_data; + int i = 0; + + if (!data) + // nothing to flush + return 0; + + speex_bits_reset(&s->bits); + for(i = 0; i < s->header.frames_per_packet; i++) + { + if (avctx->sample_fmt == SAMPLE_FMT_FLT) + { + if (avctx->channels == 2) { + speex_encode_stereo( + (float*)data+i*s->header.frame_size, + s->header.frame_size, + &s->bits); + } + speex_encode(s->enc_state, + (float*)data+i*s->header.frame_size, &s->bits); + } else { + if (avctx->channels == 2) { + speex_encode_stereo_int( + (spx_int16_t*)data+i*s->header.frame_size, + s->header.frame_size, + &s->bits); + } + speex_encode_int(s->enc_state, + (spx_int16_t*)data+i*s->header.frame_size, &s->bits); + } + } + // put in a terminator so this will fit in a OGG or FLV packet + speex_bits_insert_terminator(&s->bits); + + if (buf_size >= speex_bits_nbytes(&s->bits)) { + return speex_bits_write(&s->bits, frame, buf_size); + } else { + av_log(avctx, AV_LOG_ERROR, "output buffer too small"); + return -1; + } +} + +static av_cold int libspeex_encode_close(AVCodecContext *avctx) +{ + LibSpeexEncContext *s = (LibSpeexEncContext*)avctx->priv_data; + + speex_bits_destroy(&s->bits); + speex_encoder_destroy(s->enc_state); + s->enc_state = 0; + if (avctx->extradata) + speex_header_free(avctx->extradata); + avctx->extradata = 0; + avctx->extradata_size = 0; + + return 0; +} + +AVCodec ff_libspeex_encoder = { + "libspeex", + AVMEDIA_TYPE_AUDIO, + CODEC_ID_SPEEX, + sizeof(LibSpeexEncContext), + libspeex_encode_init, + libspeex_encode_frame, + libspeex_encode_close, + 0, + .capabilities = CODEC_CAP_DELAY, + .supported_samplerates = (const int[]){8000, 16000, 32000, 0}, + .sample_fmts = (enum SampleFormat[]){SAMPLE_FMT_S16,SAMPLE_FMT_FLT,SAMPLE_FMT_NONE}, + .long_name = NULL_IF_CONFIG_SMALL("libspeex Speex Encoder"), +};