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