#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;
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;
/*
* 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;
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);
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;
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;
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);
*
* 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;
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 ||
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)