From b8e71427704d22446f570e6f7c258c309b016799 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 30 Nov 2012 17:15:44 +0100 Subject: [PATCH] a52: Add the support of recent libavcodec / libavutil 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 --- a52/pcm_a52.c | 241 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 195 insertions(+), 46 deletions(-) diff --git a/a52/pcm_a52.c b/a52/pcm_a52.c index 00c7c59..1acf446 100644 --- a/a52/pcm_a52.c +++ b/a52/pcm_a52.c @@ -27,12 +27,44 @@ #include #include AVCODEC_HEADER +#if LIBAVCODEC_VERSION_MAJOR >= 53 && LIBAVCODEC_VERSION_MINOR >= 34 +#include +#include +#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) -- 2.47.1