]> git.alsa-project.org Git - alsa-plugins.git/commitdiff
a52: Add the support of recent libavcodec / libavutil
authorTakashi Iwai <tiwai@suse.de>
Fri, 30 Nov 2012 16:15:44 +0000 (17:15 +0100)
committerTakashi Iwai <tiwai@suse.de>
Fri, 30 Nov 2012 16:16:04 +0000 (17:16 +0100)
The recent avcodec requires the planar support, which is essentially
non-interleaved formats.  We need to limit the accepted access types
depending on it.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
a52/pcm_a52.c

index 00c7c59f69a07fa013841c490197f09954308f90..1acf446d0945b78bc168c4ac4c3eea9541795a89 100644 (file)
 #include <alsa/pcm_plugin.h>
 #include AVCODEC_HEADER
 
+#if LIBAVCODEC_VERSION_MAJOR >= 53 && LIBAVCODEC_VERSION_MINOR >= 34
+#include <libavutil/audioconvert.h>
+#include <libavutil/mem.h>
+#define USE_AVCODEC_FRAME
+#endif
+
+/* some compatibility wrappers */
+#ifndef AV_VERSION_INT
+#define AV_VERSION_INT(a, b, c) (((a) << 16) | ((b) << 8) | (c))
+#endif
+#ifndef LIBAVCODEC_VERSION_INT
+#define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
+                                               LIBAVCODEC_VERSION_MINOR, \
+                                               LIBAVCODEC_VERSION_MICRO)
+#endif
+
+#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54, 0, 0)
+#ifndef AV_CH_LAYOUT_STEREO
+#define AV_CH_LAYOUT_STEREO    CH_LAYOUT_STEREO
+#define AV_CH_LAYOUT_QUAD      CH_LAYOUT_QUAD
+#define AV_CH_LAYOUT_5POINT1   CH_LAYOUT_5POINT1
+#endif
+#endif
+
+#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(52, 95, 0)
+#ifndef AV_SAMPLE_FMT_S16
+#define AV_SAMPLE_FMT_S16      SAMPLE_FMT_S16
+#endif
+#endif
+
+
 struct a52_ctx {
        snd_pcm_ioplug_t io;
        snd_pcm_t *slave;
        AVCodec *codec;
        AVCodecContext *avctx;
        snd_pcm_format_t format;
+       int av_format;
        unsigned int channels;
        unsigned int rate;
        unsigned int bitrate;
@@ -45,16 +77,44 @@ struct a52_ctx {
        unsigned int slave_period_size;
        unsigned int slave_buffer_size;
        snd_pcm_hw_params_t *hw_params;
+#ifdef USE_AVCODEC_FRAME
+       AVFrame *frame;
+       int is_planar;
+#endif
 };
 
+#ifdef USE_AVCODEC_FRAME
+#define use_planar(rec)                (rec)->is_planar
+#else
+#define use_planar(rec)                0
+#endif
+
+#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53, 34, 0)
+static int do_encode(struct a52_ctx *rec)
+{
+       AVPacket pkt = {
+               .data = rec->outbuf + 8,
+               .size = rec->outbuf_size - 8
+       };
+       int got_frame;
+
+       avcodec_encode_audio2(rec->avctx, &pkt, rec->frame, &got_frame);
+       return pkt.size;
+}
+#else
+static int do_encode(struct a52_ctx *rec)
+{
+       return avcodec_encode_audio(rec->avctx, rec->outbuf + 8,
+                                   rec->outbuf_size - 8,
+                                   rec->inbuf);
+}
+#endif
+
 /* convert the PCM data to A52 stream in IEC958 */
 static void convert_data(struct a52_ctx *rec)
 {
-       int out_bytes;
+       int out_bytes = do_encode(rec);
 
-       out_bytes = avcodec_encode_audio(rec->avctx, rec->outbuf + 8,
-                                        rec->outbuf_size - 8,
-                                        rec->inbuf);
        rec->outbuf[0] = 0xf8; /* sync words */
        rec->outbuf[1] = 0x72;
        rec->outbuf[2] = 0x4e;
@@ -100,6 +160,20 @@ static int write_out_pending(snd_pcm_ioplug_t *io, struct a52_ctx *rec)
 /*
  * drain callback
  */
+#ifdef USE_AVCODEC_FRAME
+static void clear_remaining_planar_data(snd_pcm_ioplug_t *io)
+{
+       struct a52_ctx *rec = io->private_data;
+       int i;
+
+       for (i = 0; i < io->channels; i++)
+               memset(rec->frame->data[i] + rec->filled * 2, 0,
+                      (rec->avctx->frame_size - rec->filled) * 2);
+}
+#else
+#define clear_remaining_planar_data(io) /*NOP*/
+#endif
+
 static int a52_drain(snd_pcm_ioplug_t *io)
 {
        struct a52_ctx *rec = io->private_data;
@@ -109,8 +183,12 @@ static int a52_drain(snd_pcm_ioplug_t *io)
                if ((err = write_out_pending(io, rec)) < 0)
                        return err;
                /* remaining data must be converted and sent out */
-               memset(rec->inbuf + rec->filled * io->channels, 0,
-                      (rec->avctx->frame_size - rec->filled) * io->channels * 2);
+               if (use_planar(rec))
+                       clear_remaining_planar_data(io);
+               else {
+                       memset(rec->inbuf + rec->filled * io->channels, 0,
+                              (rec->avctx->frame_size - rec->filled) * io->channels * 2);
+               }
                convert_data(rec);
        }
        err = write_out_pending(io, rec);
@@ -153,6 +231,17 @@ static int fill_data(snd_pcm_ioplug_t *io,
        short *src, *dst;
        unsigned int src_step;
        int err;
+       static unsigned int ch_index[3][6] = {
+               { 0, 1 },
+               { 0, 1, 2, 3 },
+#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52, 26, 0)
+               /* current libavcodec expects SMPTE order */
+               { 0, 1, 4, 5, 2, 3 },
+#else
+               /* libavcodec older than r18540 expects A52 order */
+               { 0, 4, 1, 2, 3, 5 },
+#endif
+       };
 
        if ((err = write_out_pending(io, rec)) < 0)
                return err;
@@ -161,31 +250,28 @@ static int fill_data(snd_pcm_ioplug_t *io,
                size = len;
 
        dst = rec->inbuf + rec->filled * io->channels;
-       if (interleaved) {
+       if (!use_planar(rec) && interleaved) {
                memcpy(dst, areas->addr + offset * io->channels * 2,
                       size * io->channels * 2);
        } else {
                unsigned int i, ch, dst_step;
                short *dst1;
-               static unsigned int ch_index[3][6] = {
-                       { 0, 1 },
-                       { 0, 1, 2, 3 },
-#if LIBAVCODEC_VERSION_MAJOR > 52 || (LIBAVCODEC_VERSION_MAJOR == 52 && LIBAVCODEC_VERSION_MINOR >= 26)
-                       /* current libavcodec expects SMPTE order */
-                       { 0, 1, 4, 5, 2, 3 },
-#else
-                       /* libavcodec older than r18540 expects A52 order */
-                       { 0, 4, 1, 2, 3, 5 },
-#endif
-               };
+
                /* flatten copy to n-channel interleaved */
                dst_step = io->channels;
                for (ch = 0; ch < io->channels; ch++, dst++) {
                        const snd_pcm_channel_area_t *ap;
                        ap = &areas[ch_index[io->channels / 2 - 1][ch]];
-                       dst1 = dst;
                        src = (short *)(ap->addr +
                                        (ap->first + offset * ap->step) / 8);
+
+#ifdef USE_AVCODEC_FRAME
+                       if (use_planar(rec)) {
+                               memcpy(rec->frame->data[i], src, size * 2);
+                               continue;
+                       }
+#endif
+                       dst1 = dst;
                        src_step = ap->step / 16; /* in word */
                        for (i = 0; i < size; i++) {
                                *dst1 = *src;
@@ -412,6 +498,19 @@ static void a52_free(struct a52_ctx *rec)
                av_free(rec->avctx);
                rec->avctx = NULL;
        }
+
+#ifdef USE_AVCODEC_FRAME
+       if (rec->frame) {
+               av_freep(&rec->frame->data[0]);
+               rec->inbuf = NULL;
+       }
+#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(54, 28, 0)
+       avcodec_free_frame(&rec->frame);
+#else
+       av_freep(&rec->frame);
+#endif
+#endif
+
        free(rec->inbuf);
        rec->inbuf = NULL;
        free(rec->outbuf);
@@ -423,51 +522,80 @@ static void a52_free(struct a52_ctx *rec)
  *
  * Allocate internal buffers and set up libavcodec
  */
-static int a52_prepare(snd_pcm_ioplug_t *io)
+
+#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52, 3, 0)
+static void set_channel_layout(snd_pcm_ioplug_t *io)
 {
        struct a52_ctx *rec = io->private_data;
-
-       a52_free(rec);
-
-       rec->avctx = avcodec_alloc_context();
-       if (! rec->avctx)
-               return -ENOMEM;
-
-       rec->avctx->bit_rate = rec->bitrate * 1000;
-       rec->avctx->sample_rate = io->rate;
-       rec->avctx->channels = io->channels;
-#if LIBAVCODEC_VERSION_MAJOR > 52 || (LIBAVCODEC_VERSION_MAJOR == 52 && LIBAVCODEC_VERSION_MINOR >= 95)
-  rec->avctx->sample_fmt = AV_SAMPLE_FMT_S16;
-#else
-  rec->avctx->sample_fmt = SAMPLE_FMT_S16;
-#endif
-#if LIBAVCODEC_VERSION_MAJOR > 52 || (LIBAVCODEC_VERSION_MAJOR == 52 && LIBAVCODEC_VERSION_MINOR >= 3)
        switch (io->channels) {
        case 2:
-               rec->avctx->channel_layout = CH_LAYOUT_STEREO;
+               rec->avctx->channel_layout = AV_CH_LAYOUT_STEREO;
                break;
        case 4:
-               rec->avctx->channel_layout = CH_LAYOUT_QUAD;
+               rec->avctx->channel_layout = AV_CH_LAYOUT_QUAD;
                break;
        case 6:
-               rec->avctx->channel_layout = CH_LAYOUT_5POINT1;
+               rec->avctx->channel_layout = AV_CH_LAYOUT_5POINT1;
                break;
        default:
                break;
        }
+}
+#else
+#define set_channel_layout(io) /* NOP */
 #endif
 
-       if (avcodec_open(rec->avctx, rec->codec) < 0)
-               return -EINVAL;
+static int alloc_input_buffer(snd_pcm_ioplug_t *io)
+{
+       struct a52_ctx *rec = io->private_data;
 
+#ifdef USE_AVCODEC_FRAME
+       rec->frame = avcodec_alloc_frame();
+       if (!rec->frame)
+               return -ENOMEM;
+       err = av_samples_alloc(rec->frame->data, rec->frame->linesize,
+                              io->channels, rec->avctx->frame_size,
+                              rec->avctx->sample_fmt, 32);
+       if (err < 0)
+               return -ENOMEM;
+       rec->frame->nb_samples = rec->avctx->frame_size;
+       rec->inbuf = (short *)rec->frame->data[0];
+#else
        rec->inbuf = malloc(rec->avctx->frame_size * 2 * io->channels);
-       if (! rec->inbuf)
+#endif
+       if (!rec->inbuf)
                return -ENOMEM;
+       return 0;
+}
+
+static int a52_prepare(snd_pcm_ioplug_t *io)
+{
+       struct a52_ctx *rec = io->private_data;
+
+       a52_free(rec);
+
+       rec->avctx = avcodec_alloc_context();
+       if (! rec->avctx)
+               return -ENOMEM;
+
+       rec->avctx->bit_rate = rec->bitrate * 1000;
+       rec->avctx->sample_rate = io->rate;
+       rec->avctx->channels = io->channels;
+       rec->avctx->sample_fmt = rec->av_format;
+
+       set_channel_layout(io);
+
+       if (avcodec_open(rec->avctx, rec->codec) < 0)
+               return -EINVAL;
+
        rec->outbuf_size = rec->avctx->frame_size * 4;
        rec->outbuf = malloc(rec->outbuf_size);
        if (! rec->outbuf)
                return -ENOMEM;
 
+       if (alloc_input_buffer(io))
+               return -ENOMEM;
+
        rec->transfer = 0;
        rec->remain = 0;
        rec->filled = 0;
@@ -544,20 +672,35 @@ static snd_pcm_ioplug_callback_t a52_ops = {
 
 static int a52_set_hw_constraint(struct a52_ctx *rec)
 {
-       unsigned int accesses[] = {
+       static unsigned int accesses[] = {
                SND_PCM_ACCESS_MMAP_INTERLEAVED,
                SND_PCM_ACCESS_MMAP_NONINTERLEAVED,
                SND_PCM_ACCESS_RW_INTERLEAVED,
                SND_PCM_ACCESS_RW_NONINTERLEAVED
        };
+       static unsigned int accesses_planar[] = {
+               SND_PCM_ACCESS_MMAP_NONINTERLEAVED,
+               SND_PCM_ACCESS_RW_NONINTERLEAVED
+       };
        unsigned int formats[] = { SND_PCM_FORMAT_S16 };
        int err;
        snd_pcm_uframes_t buffer_max;
        unsigned int period_bytes, max_periods;
 
-       if ((err = snd_pcm_ioplug_set_param_list(&rec->io, SND_PCM_IOPLUG_HW_ACCESS,
-                                                ARRAY_SIZE(accesses), accesses)) < 0 ||
-           (err = snd_pcm_ioplug_set_param_list(&rec->io, SND_PCM_IOPLUG_HW_FORMAT,
+       if (use_planar(rec))
+               err = snd_pcm_ioplug_set_param_list(&rec->io,
+                                                   SND_PCM_IOPLUG_HW_ACCESS,
+                                                   ARRAY_SIZE(accesses_planar),
+                                                   accesses_planar);
+       else
+               err = snd_pcm_ioplug_set_param_list(&rec->io,
+                                                   SND_PCM_IOPLUG_HW_ACCESS,
+                                                   ARRAY_SIZE(accesses),
+                                                   accesses);
+       if (err < 0)
+               return err;
+
+       if ((err = snd_pcm_ioplug_set_param_list(&rec->io, SND_PCM_IOPLUG_HW_FORMAT,
                                                 ARRAY_SIZE(formats), formats)) < 0 ||
            (err = snd_pcm_ioplug_set_param_minmax(&rec->io, SND_PCM_IOPLUG_HW_CHANNELS,
                                                   rec->channels, rec->channels)) < 0 ||
@@ -744,6 +887,12 @@ SND_PCM_PLUGIN_DEFINE_FUNC(a52)
        rec->io.mmap_rw = 0;
        rec->io.callback = &a52_ops;
        rec->io.private_data = rec;
+#ifdef USE_AVCODEC_FRAME
+       rec->av_format = rec->codec->sample_fmts[0];
+       rec->is_planar = av_sample_fmt_is_planar(rec->av_format);
+#else
+       rec->av_format = AV_SAMPLE_FMT_S16;
+#endif
 
        err = snd_pcm_ioplug_create(&rec->io, name, stream, mode);
        if (err < 0)