On 10/18/11 7:25 AM, Justin Ruggles wrote:
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.

We can split the work, I'd like to get it in shape by today =)

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

Reply via email to