[alsa-devel] [PATCH 4/4] a52: support new versions of libavcodec.

Anton Khirnov anton at khirnov.net
Sun Nov 11 11:30:04 CET 2012


AC3 encoder in recent libavcodec versions requires planar
(non-interleaved) input.
---
 a52/pcm_a52.c |  120 +++++++++++++++++++++++++++++++++++++++------------------
 1 file changed, 82 insertions(+), 38 deletions(-)

diff --git a/a52/pcm_a52.c b/a52/pcm_a52.c
index b9ffddb..b60ec2e 100644
--- a/a52/pcm_a52.c
+++ b/a52/pcm_a52.c
@@ -28,6 +28,9 @@
 #include <libavcodec/avcodec.h>
 #include <libavutil/audioconvert.h>
 #include <libavutil/mem.h>
+#include <libavutil/samplefmt.h>
+
+#define MAX_CHANNELS 6
 
 struct a52_ctx {
 	snd_pcm_ioplug_t io;
@@ -35,11 +38,11 @@ struct a52_ctx {
 	AVCodec *codec;
 	AVCodecContext *avctx;
 	AVFrame *frame;
+	int planar;
 	snd_pcm_format_t format;
 	unsigned int channels;
 	unsigned int rate;
 	unsigned int bitrate;
-	short *inbuf;
 	unsigned char *outbuf;
 	int outbuf_size;
 	snd_pcm_uframes_t transfer;
@@ -109,11 +112,16 @@ static int a52_drain(snd_pcm_ioplug_t *io)
 	int err;
 
 	if (rec->filled) {
+		int planes = rec->planar ? io->channels : 1;
+		int blocksize = 2 * (rec->planar ? 1 : io->channels);
+		int i;
+
 		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);
+		for (i = 0; i < planes; i++)
+			memset(rec->frame->data[i] + rec->filled * blocksize, 0,
+				   (rec->avctx->frame_size - rec->filled) * blocksize);
 		convert_data(rec);
 	}
 	err = write_out_pending(io, rec);
@@ -151,6 +159,12 @@ static int fill_data(snd_pcm_ioplug_t *io,
 		     unsigned int offset, unsigned int size,
 		     int interleaved)
 {
+	static unsigned int ch_index[3][6] = {
+		{ 0, 1 },
+		{ 0, 1, 2, 3 },
+		/* current libavcodec expects SMPTE order */
+		{ 0, 1, 4, 5, 2, 3 },
+	};
 	struct a52_ctx *rec = io->private_data;
 	unsigned int len = rec->avctx->frame_size - rec->filled;
 	short *src, *dst;
@@ -163,35 +177,42 @@ static int fill_data(snd_pcm_ioplug_t *io,
 	if (size > len)
 		size = len;
 
-	dst = rec->inbuf + rec->filled * io->channels;
-	if (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 },
-			/* current libavcodec expects SMPTE order */
-			{ 0, 1, 4, 5, 2, 3 },
-		};
-		/* flatten copy to n-channel interleaved */
-		dst_step = io->channels;
-		for (ch = 0; ch < io->channels; ch++, dst++) {
+	if (rec->planar) {
+		unsigned int i;
+
+		for (i = 0; i < io->channels; i++) {
 			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);
-			src_step = ap->step / 16; /* in word */
-			for (i = 0; i < size; i++) {
-				*dst1 = *src;
-				src += src_step;
-				dst1 += dst_step;
+			ap = &areas[ch_index[io->channels / 2 - 1][i]];
+			memcpy(rec->frame->data[i], ap->addr +
+				   (ap->first + offset * ap->step) / 8, size * 2);
+		}
+	} else {
+		dst = (short*)(rec->frame->data[0] + rec->filled * io->channels * 2);
+		if (interleaved) {
+			memcpy(dst, areas->addr + offset * io->channels * 2,
+				   size * io->channels * 2);
+		} else {
+			unsigned int i, ch, dst_step;
+			short *dst1;
+
+			/* 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);
+				src_step = ap->step / 16; /* in word */
+				for (i = 0; i < size; i++) {
+					*dst1 = *src;
+					src += src_step;
+					dst1 += dst_step;
+				}
 			}
 		}
 	}
+
 	rec->filled += size;
 	if (rec->filled == rec->avctx->frame_size) {
 		convert_data(rec);
@@ -410,14 +431,14 @@ static void a52_free(struct a52_ctx *rec)
 		av_free(rec->avctx);
 		rec->avctx = NULL;
 	}
+	if (rec->frame)
+		av_freep(&rec->frame->data[0]);
 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(54, 28, 0)
 	avcodec_free_frame(&rec->frame);
 #else
 	av_freep(&rec->frame);
 #endif
 
-	free(rec->inbuf);
-	rec->inbuf = NULL;
 	free(rec->outbuf);
 	rec->outbuf = NULL;
 }
@@ -430,6 +451,7 @@ static void a52_free(struct a52_ctx *rec)
 static int a52_prepare(snd_pcm_ioplug_t *io)
 {
 	struct a52_ctx *rec = io->private_data;
+	int err;
 
 	a52_free(rec);
 
@@ -444,7 +466,7 @@ static int a52_prepare(snd_pcm_ioplug_t *io)
 	rec->avctx->bit_rate = rec->bitrate * 1000;
 	rec->avctx->sample_rate = io->rate;
 	rec->avctx->channels = io->channels;
-  rec->avctx->sample_fmt = AV_SAMPLE_FMT_S16;
+	rec->avctx->sample_fmt = rec->codec->sample_fmts[0];
 	switch (io->channels) {
 	case 2:
 		rec->avctx->channel_layout = AV_CH_LAYOUT_STEREO;
@@ -462,16 +484,18 @@ static int a52_prepare(snd_pcm_ioplug_t *io)
 	if (avcodec_open2(rec->avctx, rec->codec, NULL) < 0)
 		return -EINVAL;
 
-	rec->inbuf = malloc(rec->avctx->frame_size * 2 * io->channels);
-	if (! rec->inbuf)
-		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->outbuf_size = rec->avctx->frame_size * 4;
 	rec->outbuf = malloc(rec->outbuf_size);
 	if (! rec->outbuf)
 		return -ENOMEM;
 
-	rec->frame->data[0]     = (uint8_t*)rec->inbuf;
-	rec->frame->linesize[0] = rec->avctx->frame_size * 2 * io->channels;
+	rec->planar = av_sample_fmt_is_planar(rec->avctx->sample_fmt);
 	rec->frame->nb_samples  = rec->avctx->frame_size;
 
 	rec->transfer = 0;
@@ -550,19 +574,31 @@ static snd_pcm_ioplug_callback_t a52_ops = {
 
 static int a52_set_hw_constraint(struct a52_ctx *rec)
 {
-	unsigned int accesses[] = {
+	unsigned int accesses_all[] = {
 		SND_PCM_ACCESS_MMAP_INTERLEAVED,
 		SND_PCM_ACCESS_MMAP_NONINTERLEAVED,
 		SND_PCM_ACCESS_RW_INTERLEAVED,
 		SND_PCM_ACCESS_RW_NONINTERLEAVED
 	};
+	unsigned int accesses_planar[] = {
+		SND_PCM_ACCESS_MMAP_NONINTERLEAVED,
+		SND_PCM_ACCESS_RW_NONINTERLEAVED,
+	};
+	unsigned int *accesses = accesses_all;
+	int accesses_size = ARRAY_SIZE(accesses_all);
 	unsigned int formats[] = { SND_PCM_FORMAT_S16 };
 	int err;
 	snd_pcm_uframes_t buffer_max;
 	unsigned int period_bytes, max_periods;
 
+
+	if (av_sample_fmt_is_planar(rec->codec->sample_fmts[0])) {
+		accesses = accesses_planar;
+		accesses_size = ARRAY_SIZE(accesses_planar);
+	}
+
 	if ((err = snd_pcm_ioplug_set_param_list(&rec->io, SND_PCM_IOPLUG_HW_ACCESS,
-						 ARRAY_SIZE(accesses), accesses)) < 0 ||
+						 accesses_size, accesses)) < 0 ||
 	    (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,
@@ -721,6 +757,14 @@ SND_PCM_PLUGIN_DEFINE_FUNC(a52)
 		goto error;
 	}
 
+	if (!rec->codec->sample_fmts ||
+		(rec->codec->sample_fmts[0] != AV_SAMPLE_FMT_S16 &&
+		 rec->codec->sample_fmts[0] != AV_SAMPLE_FMT_S16P)) {
+		SNDERR("AC3 encoder does not support s16 audio.");
+		err = -EINVAL;
+		goto error;
+	}
+
 	if (! pcm_string) {
 		snprintf(devstr, sizeof(devstr),
 			 "iec958:{AES0 0x%x AES1 0x%x AES2 0x%x AES3 0x%x %s%s}",
-- 
1.7.10.4



More information about the Alsa-devel mailing list