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.

-Justin

_______________________________________________
libav-devel mailing list
[email protected]
https://lists.libav.org/mailman/listinfo/libav-devel

Reply via email to