diff -ur src/plugins/avcodec.orig/avcodec.c src/plugins/avcodec/avcodec.c --- src/plugins/avcodec.orig/avcodec.c 2016-02-24 11:59:39.000000000 -0800 +++ src/plugins/avcodec/avcodec.c 2016-02-24 12:01:27.000000000 -0800 @@ -1,7 +1,7 @@ /** @file avcodec.c * Decoder plugin for ffmpeg avcodec formats * - * Copyright (C) 2006-2011 XMMS2 Team + * Copyright (C) 2006-2014 XMMS2 Team * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -14,20 +14,20 @@ * Lesser General Public License for more details. */ -#include "xmms_configuration.h" -#include "xmms/xmms_xformplugin.h" -#include "xmms/xmms_sample.h" -#include "xmms/xmms_log.h" +#include <xmms_configuration.h> +#include <xmms/xmms_xformplugin.h> +#include <xmms/xmms_sample.h> +#include <xmms/xmms_log.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <glib.h> +#include <libavutil/mem.h> #include "avcodec_compat.h" #define AVCODEC_BUFFER_SIZE 16384 -#define MAX_AUDIO_FRAME_SIZE 192000 // 1 second of 48khz 32bit audio typedef struct { AVCodecContext *codecctx; @@ -37,6 +37,8 @@ guint buffer_size; gboolean no_demuxer; + AVFrame *read_out_frame; + guint channels; guint samplerate; xmms_sample_format_t sampleformat; @@ -54,10 +56,14 @@ static gboolean xmms_avcodec_plugin_setup (xmms_xform_plugin_t *xform_plugin); static gboolean xmms_avcodec_init (xmms_xform_t *xform); static void xmms_avcodec_destroy (xmms_xform_t *xform); +static gint xmms_avcodec_internal_read_some (xmms_xform_t *xform, xmms_avcodec_data_t *data, xmms_error_t *error); +static gint xmms_avcodec_internal_decode_some (xmms_avcodec_data_t *data); +static void xmms_avcodec_internal_append (xmms_avcodec_data_t *data); static gint xmms_avcodec_read (xmms_xform_t *xform, xmms_sample_t *buf, gint len, xmms_error_t *error); static gint64 xmms_avcodec_seek (xmms_xform_t *xform, gint64 samples, xmms_xform_seek_mode_t whence, xmms_error_t *err); +static xmms_sample_format_t xmms_avcodec_translate_sample_format (enum AVSampleFormat av_sample_format); /* * Plugin header @@ -93,6 +99,16 @@ "audio/x-ffmpeg-*", NULL); + XMMS_DBG ("avcodec version at build time is %d.%d.%d", + (LIBAVCODEC_VERSION_INT >> 16), + (LIBAVCODEC_VERSION_INT >> 8) & 0xff, + LIBAVCODEC_VERSION_INT & 0xff); + XMMS_DBG ("avcodec version at run time is %d.%d.%d", + (avcodec_version() >> 16), + (avcodec_version() >> 8) & 0xff, + avcodec_version() & 0xff); + XMMS_DBG ("avcodec configuration is %s", avcodec_configuration()); + return TRUE; } @@ -108,6 +124,7 @@ avcodec_close (data->codecctx); av_free (data->codecctx); + av_frame_free (&data->read_out_frame); g_string_free (data->outbuf, TRUE); g_free (data->buffer); @@ -133,8 +150,11 @@ data->buffer_size = AVCODEC_BUFFER_SIZE; data->codecctx = NULL; + data->read_out_frame = av_frame_alloc (); + xmms_xform_private_data_set (xform, data); + avcodec_init (); avcodec_register_all (); mimetype = xmms_xform_indata_get_str (xform, @@ -161,12 +181,12 @@ data->channels = ret; } - /* bitrate required for WMA files */ + /* Required by WMA xform. */ xmms_xform_auxdata_get_int (xform, "bitrate", &data->bitrate); - /* ALAC and MAC require bits per sample field to be 16 */ + /* Required by tta and apefile xforms. */ xmms_xform_auxdata_get_int (xform, "samplebits", &data->samplebits); @@ -197,11 +217,11 @@ } else { /* A demuxer plugin forgot to give decoder config? */ xmms_log_error ("Decoder config data not found!"); - return FALSE; + goto err; } } - data->codecctx = avcodec_alloc_context3 (NULL); + data->codecctx = avcodec_alloc_context3 (codec); data->codecctx->sample_rate = data->samplerate; data->codecctx->channels = data->channels; data->codecctx->bit_rate = data->bitrate; @@ -221,7 +241,7 @@ /* some codecs need to have something read before they set * the samplerate and channels correctly, unfortunately... */ - if ((ret = xmms_avcodec_read (xform, buf, 42, &error)) > 0) { + if ((ret = xmms_avcodec_read (xform, buf, sizeof (buf), &error)) > 0) { g_string_insert_len (data->outbuf, 0, buf, ret); } else { XMMS_DBG ("First read failed, codec is not working..."); @@ -232,12 +252,17 @@ data->samplerate = data->codecctx->sample_rate; data->channels = data->codecctx->channels; + data->sampleformat = xmms_avcodec_translate_sample_format (data->codecctx->sample_fmt); + if (data->sampleformat == XMMS_SAMPLE_FORMAT_UNKNOWN) { + avcodec_close (data->codecctx); + goto err; + } xmms_xform_outdata_type_add (xform, XMMS_STREAM_TYPE_MIMETYPE, "audio/pcm", XMMS_STREAM_TYPE_FMT_FORMAT, - XMMS_SAMPLE_FORMAT_S16, + data->sampleformat, XMMS_STREAM_TYPE_FMT_CHANNELS, data->channels, XMMS_STREAM_TYPE_FMT_SAMPLERATE, @@ -252,6 +277,9 @@ if (data->codecctx) { av_free (data->codecctx); } + if (data->read_out_frame) { + avcodec_free_frame (&data->read_out_frame); + } g_string_free (data->outbuf, TRUE); g_free (data->extradata); g_free (data); @@ -264,102 +292,24 @@ xmms_error_t *error) { xmms_avcodec_data_t *data; - char outbuf[MAX_AUDIO_FRAME_SIZE]; - gint outbufsize, bytes_read = 0; guint size; data = xmms_xform_private_data_get (xform); g_return_val_if_fail (data, -1); - size = MIN (data->outbuf->len, len); - while (size == 0) { - AVPacket packet; - av_init_packet (&packet); + while (0 == (size = MIN (data->outbuf->len, len))) { + gint res; if (data->no_demuxer || data->buffer_length == 0) { - gint read_total; - - bytes_read = xmms_xform_read (xform, - (gchar *) (data->buffer + data->buffer_length), - data->buffer_size - data->buffer_length, - error); - - if (bytes_read < 0) { - XMMS_DBG ("Error while reading data"); - return bytes_read; - } else if (bytes_read == 0) { - XMMS_DBG ("EOF"); - return 0; - } + gint bytes_read; - read_total = bytes_read; - - /* If we have a demuxer plugin, make sure we read the whole packet */ - while (read_total == data->buffer_size && !data->no_demuxer) { - /* multiply the buffer size and try to read again */ - data->buffer = g_realloc (data->buffer, data->buffer_size * 2); - bytes_read = xmms_xform_read (xform, - (gchar *) data->buffer + - data->buffer_size, - data->buffer_size, - error); - data->buffer_size *= 2; - - if (bytes_read < 0) { - XMMS_DBG ("Error while reading data"); - return bytes_read; - } - - read_total += bytes_read; - - if (read_total < data->buffer_size) { - /* finally double the buffer size for performance reasons, the - * hotspot handling likes to fit two frames in the buffer */ - data->buffer = g_realloc (data->buffer, data->buffer_size * 2); - data->buffer_size *= 2; - XMMS_DBG ("Reallocated avcodec internal buffer to be %d bytes", - data->buffer_size); - - break; - } - } - - /* Update the buffer length */ - data->buffer_length += read_total; + bytes_read = xmms_avcodec_internal_read_some (xform, data, error); + if (bytes_read <= 0) { return bytes_read; } } - packet.data = data->buffer; - packet.size = data->buffer_length; - - outbufsize = sizeof (outbuf); - bytes_read = avcodec_decode_audio3 (data->codecctx, (short *) outbuf, - &outbufsize, &packet); - - /* The DTS decoder of ffmpeg is buggy and always returns - * the input buffer length, get frame length from header */ - if (!strcmp (data->codec_id, "dca") && bytes_read > 0) { - bytes_read = ((int)data->buffer[5] << 12) | - ((int)data->buffer[6] << 4) | - ((int)data->buffer[7] >> 4); - bytes_read = (bytes_read & 0x3fff) + 1; - } - - if (bytes_read < 0 || bytes_read > data->buffer_length) { - XMMS_DBG ("Error decoding data!"); - return -1; - } else if (bytes_read != data->buffer_length) { - g_memmove (data->buffer, - data->buffer + bytes_read, - data->buffer_length - bytes_read); - } - - data->buffer_length -= bytes_read; - - if (outbufsize > 0) { - g_string_append_len (data->outbuf, outbuf, outbufsize); - } - - size = MIN (data->outbuf->len, len); + res = xmms_avcodec_internal_decode_some (data); + if (res < 0) { return res; } + if (res > 0) { xmms_avcodec_internal_append (data); } } memcpy (buf, data->outbuf->str, size); @@ -372,8 +322,6 @@ xmms_avcodec_seek (xmms_xform_t *xform, gint64 samples, xmms_xform_seek_mode_t whence, xmms_error_t *err) { xmms_avcodec_data_t *data; - char outbuf[MAX_AUDIO_FRAME_SIZE]; - gint outbufsize, bytes_read = 0; gint64 ret = -1; g_return_val_if_fail (xform, -1); @@ -391,23 +339,11 @@ /* The buggy ape decoder doesn't flush buffers, so we need to finish decoding * the frame before seeking to avoid segfaults... this hack sucks */ + /* FIXME: Is ^^^ still true? */ while (data->buffer_length > 0) { - AVPacket packet; - av_init_packet (&packet); - packet.data = data->buffer; - packet.size = data->buffer_length; - - outbufsize = sizeof (outbuf); - bytes_read = avcodec_decode_audio3 (data->codecctx, (short *) outbuf, - &outbufsize, &packet); - - if (bytes_read < 0 || bytes_read > data->buffer_length) { - XMMS_DBG ("Error decoding data!"); + if (xmms_avcodec_internal_decode_some (data) < 0) { return -1; } - - data->buffer_length -= bytes_read; - g_memmove (data->buffer, data->buffer + bytes_read, data->buffer_length); } ret = xmms_xform_seek (xform, samples, whence, err); @@ -421,3 +357,178 @@ return ret; } + +static xmms_sample_format_t +xmms_avcodec_translate_sample_format (enum AVSampleFormat av_sample_format) +{ + switch (av_sample_format) { + case AV_SAMPLE_FMT_U8: + case AV_SAMPLE_FMT_U8P: + return XMMS_SAMPLE_FORMAT_U8; + case AV_SAMPLE_FMT_S16: + case AV_SAMPLE_FMT_S16P: + return XMMS_SAMPLE_FORMAT_S16; + case AV_SAMPLE_FMT_S32: + case AV_SAMPLE_FMT_S32P: + return XMMS_SAMPLE_FORMAT_S32; + case AV_SAMPLE_FMT_FLT: + case AV_SAMPLE_FMT_FLTP: + return XMMS_SAMPLE_FORMAT_FLOAT; + case AV_SAMPLE_FMT_DBL: + case AV_SAMPLE_FMT_DBLP: + return XMMS_SAMPLE_FORMAT_DOUBLE; + default: + XMMS_DBG ("AVSampleFormat (%i: %s) not supported.", av_sample_format, + av_get_sample_fmt_name (av_sample_format)); + return XMMS_SAMPLE_FORMAT_UNKNOWN; + } +} + +/* +Read some data from our source of data to data->buffer, updating buffer_length +and buffer_size as needed. + +Returns: on error: negative + on EOF: zero + otherwise: number of bytes read. +*/ +static gint +xmms_avcodec_internal_read_some (xmms_xform_t *xform, + xmms_avcodec_data_t *data, + xmms_error_t *error) +{ + gint bytes_read, read_total; + + bytes_read = xmms_xform_read (xform, + (gchar *) (data->buffer + data->buffer_length), + data->buffer_size - data->buffer_length, + error); + + if (bytes_read < 0) { + XMMS_DBG ("Error while reading data"); + return bytes_read; + } else if (bytes_read == 0) { + XMMS_DBG ("EOF"); + return 0; + } + + read_total = bytes_read; + + /* If we have a demuxer plugin, make sure we read the whole packet */ + while (read_total == data->buffer_size && !data->no_demuxer) { + /* multiply the buffer size and try to read again */ + data->buffer = g_realloc (data->buffer, data->buffer_size * 2); + bytes_read = xmms_xform_read (xform, + (gchar *) data->buffer + + data->buffer_size, + data->buffer_size, + error); + data->buffer_size *= 2; + + if (bytes_read < 0) { + XMMS_DBG ("Error while reading data"); + return bytes_read; + } + + read_total += bytes_read; + + if (read_total < data->buffer_size) { + /* finally double the buffer size for performance reasons, the + * hotspot handling likes to fit two frames in the buffer */ + data->buffer = g_realloc (data->buffer, data->buffer_size * 2); + data->buffer_size *= 2; + XMMS_DBG ("Reallocated avcodec internal buffer to be %d bytes", + data->buffer_size); + + break; + } + } + + /* Update the buffer length */ + data->buffer_length += read_total; + + return read_total; +} + +/* +Decode some data from data->buffer[0..data->buffer_length-1] to +data->read_out_frame + +Returns: on error: negative + on no new data produced: zero + otherwise: positive + +FIXME: data->buffer should be at least data->buffer_length + +FF_INPUT_BUFFER_PADDING_SIZE long. +*/ +static gint +xmms_avcodec_internal_decode_some (xmms_avcodec_data_t *data) +{ + int got_frame = 0; + gint bytes_read = 0; + AVPacket packet; + + av_init_packet (&packet); + packet.data = data->buffer; + packet.size = data->buffer_length; + + /* clear buffers and reset fields to defaults */ + av_frame_unref (data->read_out_frame); + + bytes_read = avcodec_decode_audio4 ( + data->codecctx, data->read_out_frame, &got_frame, &packet); + + /* The DTS decoder of ffmpeg is buggy and always returns + * the input buffer length, get frame length from header */ + /* FIXME: Is ^^^^ still true? */ + if (!strcmp (data->codec_id, "dca") && bytes_read > 0) { + bytes_read = ((int)data->buffer[5] << 12) | + ((int)data->buffer[6] << 4) | + ((int)data->buffer[7] >> 4); + bytes_read = (bytes_read & 0x3fff) + 1; + } + + if (bytes_read < 0 || bytes_read > data->buffer_length) { + XMMS_DBG ("Error decoding data!"); + return -1; + } + + if (bytes_read < data->buffer_length) { + data->buffer_length -= bytes_read; + g_memmove (data->buffer, + data->buffer + bytes_read, + data->buffer_length); + } else { + data->buffer_length = 0; + } + + return got_frame ? 1 : 0; +} + +static void +xmms_avcodec_internal_append (xmms_avcodec_data_t *data) +{ + enum AVSampleFormat fmt = (enum AVSampleFormat) data->read_out_frame->format; + int samples = data->read_out_frame->nb_samples; + int channels = data->codecctx->channels; + int bps = av_get_bytes_per_sample (fmt); + + if (av_sample_fmt_is_planar (fmt)) { + /* Convert from planar to packed format */ + gint i, j; + + for (i = 0; i < samples; i++) { + for (j = 0; j < channels; j++) { + g_string_append_len ( + data->outbuf, + (gchar *) (data->read_out_frame->extended_data[j] + i*bps), + bps + ); + } + } + } else { + g_string_append_len (data->outbuf, + (gchar *) data->read_out_frame->extended_data[0], + samples * channels * bps); + } +} diff -ur src/plugins/avcodec.orig/avcodec_compat.h src/plugins/avcodec/avcodec_compat.h --- src/plugins/avcodec.orig/avcodec_compat.h 2016-02-24 11:59:39.000000000 -0800 +++ src/plugins/avcodec/avcodec_compat.h 2016-02-24 12:02:03.000000000 -0800 @@ -1,7 +1,7 @@ /** @file avcodec_compat.h * Compatibility header for libavcodec backwards compatibility * - * Copyright (C) 2011 XMMS2 Team + * Copyright (C) 2011-2014 XMMS2 Team * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -65,3 +65,35 @@ # define AVMEDIA_TYPE_AUDIO CODEC_TYPE_AUDIO #endif +/* Calling avcodec_init is not necessary after 53.04 (ffmpeg 0.9) */ +#if LIBAVCODEC_VERSION_INT >= 0x350400 +# define avcodec_init() +#endif + +/* Map avcodec_alloc_context3 into the deprecated version + * avcodec_alloc_context in versions earlier than 53.04 (ffmpeg 0.9) */ +#if LIBAVCODEC_VERSION_INT < 0x350400 +# define avcodec_alloc_context3(codec) \ + avcodec_alloc_context() +#endif + +/* Map avcodec_open2 into the deprecated version + * avcodec_open in versions earlier than 53.04 (ffmpeg 0.9) */ +#if LIBAVCODEC_VERSION_INT < 0x350400 +# define avcodec_open2(avctx, codec, options) \ + avcodec_open(avctx, codec) +#endif + +/* Map avcodec_free_frame to av_freep if the former doesn't exist. + * (This is in versions earlier than 54.28.0 (libav) or 54.59.100 (ffmpeg)) */ +#if ! HAVE_AVCODEC_FREE_FRAME +# define avcodec_free_frame av_freep +#endif + +/* Map av_frame_alloc, av_frame_unref, av_frame_free into their + * deprecated versions in versions earlier than 55.28.1 */ +#if LIBAVCODEC_VERSION_INT < 0x371c01 +# define av_frame_alloc avcodec_alloc_frame +# define av_frame_unref avcodec_get_frame_defaults +# define av_frame_free avcodec_free_frame +#endif diff -ur src/plugins/avcodec.orig/wscript src/plugins/avcodec/wscript --- src/plugins/avcodec.orig/wscript 2016-02-24 11:59:39.000000000 -0800 +++ src/plugins/avcodec/wscript 2016-02-24 12:00:41.000000000 -0800 @@ -1,10 +1,33 @@ from waftools.plugin import plugin +## Code fragments for configuration +avcodec_free_frame_fragment = """ +#ifdef HAVE_LIBAVCODEC_AVCODEC_H +# include "libavcodec/avcodec.h" +#else +# include "avcodec.h" +#endif +int main(void) { + AVFrame *frame; + + avcodec_free_frame (&frame); + + return 0; +} +""" + def plugin_configure(conf): conf.check_cfg(package="libavcodec", uselib_store="avcodec", args="--cflags --libs") conf.check_cc(header_name="avcodec.h", uselib="avcodec", type="cshlib", mandatory=False) conf.check_cc(header_name="libavcodec/avcodec.h", uselib="avcodec", type="cshlib", mandatory=False) + # non-mandatory function avcodec_free_frame since + # * ffmpeg: commit 46a3595, lavc 54.59.100, release 1.0 + # * libav: commit a42aada, lavc 54.28.0, release 9 + conf.check_cc(fragment=avcodec_free_frame_fragment, uselib="avcodec", + uselib_store="avcodec_free_frame", + msg="Checking for function avcodec_free_frame", mandatory=False) + configure, build = plugin('avcodec', configure=plugin_configure, libs=["avcodec"])