On 10/18/2011 09:41 AM, Luca Barbato wrote:
From: Justin Ruggles<[email protected]>
---
Changelog | 1 +
configure | 3 +-
doc/general.texi | 2 +-
libavcodec/Makefile | 1 +
libavcodec/allcodecs.c | 2 +-
libavcodec/libspeexenc.c | 306 ++++++++++++++++++++++++++++++++++++++++++++++
libavcodec/version.h | 2 +-
7 files changed, 313 insertions(+), 4 deletions(-)
create mode 100644 libavcodec/libspeexenc.c
[...]
+ /* rate control method and parameters */
+ if (avctx->flags& CODEC_FLAG_QSCALE) {
+ /* VBR */
+ s->header.vbr = 1;
+ speex_encoder_ctl(s->enc_state, SPEEX_SET_VBR,&s->header.vbr);
+ s->vbr_quality = av_clipf(avctx->global_quality / (float)FF_QP2LAMBDA,
+ 0.0f, 10.0f);
+ speex_encoder_ctl(s->enc_state, SPEEX_SET_VBR_QUALITY,&s->vbr_quality);
+ avctx->bit_rate = 0;
+ } else {
+ /* CBR */
+ s->header.bitrate = avctx->bit_rate;
+ speex_encoder_ctl(s->enc_state, SPEEX_SET_BITRATE,&s->header.bitrate);
+ speex_encoder_ctl(s->enc_state, SPEEX_GET_BITRATE,&s->header.bitrate);
+ /* stereo side information adds about 800 bps to the base bitrate */
+ avctx->bit_rate = s->header.bitrate + (avctx->channels == 2 ? 800 : 0);
+ }
We could also add a private option for CBR quality. Speex also has ABR,
which could be done as a private option as well.
[...]
+ /* set packet size */
+ s->header.frames_per_packet = 1;
+ if (avctx->frame_size> 0) {
+ /* libspeex doesn't fail cleanly if frames_per_packet is too high, so
+ we're limiting it to 100 */
+ s->header.frames_per_packet = av_clip(avctx->frame_size /
+ s->header.frame_size, 1, 100);
+ }
+ avctx->frame_size = s->header.frame_size * s->header.frames_per_packet;
frames_per_packet would be a good private option. then the user wouldn't
have to know what the frame size is supposed to be for each mode in
order to set multiple frames per packet.
[...]
+ /* allocate extradata and coded_frame */
+ avctx->extradata = av_malloc(header_size + FF_INPUT_BUFFER_PADDING_SIZE);
+ avctx->coded_frame = avcodec_alloc_frame();
+ if (!avctx->extradata || !avctx->coded_frame) {
+ speex_header_free(header_data);
+ speex_encoder_destroy(s->enc_state);
+ av_log(avctx, AV_LOG_ERROR, "memory allocation error\n");
+ return AVERROR(ENOMEM);
+ }
+ avctx->coded_frame->key_frame = 1;
don't need to set key_frame. that's already done in avcodec_alloc_frame().
[...]
+ /* handle last packet, which may have fewer frames-per-packet and/or
+ fewer samples in the last frame */
+ nframes = s->header.frames_per_packet;
+ if (avctx->frame_size< nframes * s->header.frame_size) {
+ nframes = (avctx->frame_size + s->header.frame_size - 1) /
+ s->header.frame_size;
+ if (avctx->frame_size != s->header.frame_size * nframes) {
+ /* allocate new buffer to pad last frame */
+ int new_samples_size;
+ avctx->frame_size = nframes * s->header.frame_size;
+ new_samples_size = avctx->frame_size * avctx->channels *
+ (avctx->sample_fmt == SAMPLE_FMT_FLT ?
+ sizeof(float) : sizeof(int16_t));
+ samples = av_mallocz(new_samples_size);
+ if (!samples)
+ return AVERROR(ENOMEM);
+ memcpy(samples, data, new_samples_size);
+ }
+ }
A better way to handle this would be to set frame_size to 1 frame and
only send encoded output after frames_per_packet have been encoded. Then
we don't need CODEC_CAP_SMALL_LAST_FRAME and don't have to worry about
the malloc/memcpy.
+
+ /* encode Speex frames */
+ speex_bits_reset(&s->bits);
+ if (avctx->sample_fmt == SAMPLE_FMT_FLT) {
+ float *samples_flt = samples;
+
+ /* scale floating point samples to 16-bit range as required by
libspeex */
+ if (avctx->sample_fmt == SAMPLE_FMT_FLT)
+ for (i = 0; i< avctx->frame_size * avctx->channels; i++)
+ samples_flt[i] *= 32767.0;
+
+ for (i = 0; i< nframes; i++) {
+ if (avctx->channels == 2)
+ speex_encode_stereo(samples_flt,
s->header.frame_size,&s->bits);
+ speex_encode(s->enc_state, samples_flt,&s->bits);
+ samples_flt += s->header.frame_size * avctx->channels;
+ }
We can get rid of the float support. The speex API is deceiving. The
float functions just convert to int16 and call the int functions.
[...]
+AVCodec ff_libspeex_encoder = {
+ .name = "libspeex",
+ .type = AVMEDIA_TYPE_AUDIO,
+ .id = CODEC_ID_SPEEX,
+ .priv_data_size = sizeof(LibSpeexEncContext),
+ .init = encode_init,
+ .encode = encode_frame,
+ .close = encode_close,
+ .capabilities = CODEC_CAP_SMALL_LAST_FRAME,
+ .sample_fmts = (const enum SampleFormat[]){ SAMPLE_FMT_S16, SAMPLE_FMT_FLT,
+ SAMPLE_FMT_NONE },
AV_SAMPLE_FMT_*
Thanks for updating this for me. I can work on the changes if you'd like.