aboutsummaryrefslogtreecommitdiffstats
path: root/src/decoder/ffmpeg_decoder_plugin.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/decoder/ffmpeg_decoder_plugin.c (renamed from src/decoder/ffmpeg_plugin.c)467
1 files changed, 247 insertions, 220 deletions
diff --git a/src/decoder/ffmpeg_plugin.c b/src/decoder/ffmpeg_decoder_plugin.c
index 9bae39793..d47356581 100644
--- a/src/decoder/ffmpeg_plugin.c
+++ b/src/decoder/ffmpeg_decoder_plugin.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2009 The Music Player Daemon Project
+ * Copyright (C) 2003-2010 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,9 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#include "../decoder_api.h"
#include "config.h"
+#include "decoder_api.h"
+#include "audio_check.h"
#include <glib.h>
@@ -39,19 +40,46 @@
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavformat/avio.h>
+#include <libavutil/log.h>
#endif
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "ffmpeg"
-struct ffmpeg_context {
- int audio_stream;
- AVFormatContext *format_context;
- AVCodecContext *codec_context;
- struct decoder *decoder;
- struct input_stream *input;
- struct tag *tag;
-};
+#ifndef OLD_FFMPEG_INCLUDES
+
+static GLogLevelFlags
+level_ffmpeg_to_glib(int level)
+{
+ if (level <= AV_LOG_FATAL)
+ return G_LOG_LEVEL_CRITICAL;
+
+ if (level <= AV_LOG_ERROR)
+ return G_LOG_LEVEL_WARNING;
+
+ if (level <= AV_LOG_INFO)
+ return G_LOG_LEVEL_MESSAGE;
+
+ return G_LOG_LEVEL_DEBUG;
+}
+
+static void
+mpd_ffmpeg_log_callback(G_GNUC_UNUSED void *ptr, int level,
+ const char *fmt, va_list vl)
+{
+ const AVClass * cls = NULL;
+
+ if (ptr != NULL)
+ cls = *(const AVClass *const*)ptr;
+
+ if (cls != NULL) {
+ char *domain = g_strconcat(G_LOG_DOMAIN, "/", cls->item_name(ptr), NULL);
+ g_logv(domain, level_ffmpeg_to_glib(level), fmt, vl);
+ g_free(domain);
+ }
+}
+
+#endif /* !OLD_FFMPEG_INCLUDES */
struct mpd_ffmpeg_stream {
struct decoder *decoder;
@@ -79,7 +107,7 @@ mpd_ffmpeg_stream_seek(void *opaque, int64_t pos, int whence)
if (whence == AVSEEK_SIZE)
return stream->input->size;
- ret = input_stream_seek(stream->input, pos, whence);
+ ret = input_stream_seek(stream->input, pos, whence, NULL);
if (!ret)
return -1;
@@ -115,6 +143,10 @@ mpd_ffmpeg_stream_close(struct mpd_ffmpeg_stream *stream)
static bool
ffmpeg_init(G_GNUC_UNUSED const struct config_param *param)
{
+#ifndef OLD_FFMPEG_INCLUDES
+ av_log_set_callback(mpd_ffmpeg_log_callback);
+#endif
+
av_register_all();
return true;
}
@@ -130,9 +162,93 @@ ffmpeg_find_audio_stream(const AVFormatContext *format_context)
return -1;
}
+/**
+ * On some platforms, libavcodec wants the output buffer aligned to 16
+ * bytes (because it uses SSE/Altivec internally). This function
+ * returns the aligned version of the specified buffer, and corrects
+ * the buffer size.
+ */
+static void *
+align16(void *p, size_t *length_p)
+{
+ unsigned add = 16 - (size_t)p % 16;
+
+ *length_p -= add;
+ return (char *)p + add;
+}
+
+static enum decoder_command
+ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
+ const AVPacket *packet,
+ AVCodecContext *codec_context,
+ const AVRational *time_base)
+{
+ enum decoder_command cmd = DECODE_COMMAND_NONE;
+ uint8_t audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2 + 16];
+ int16_t *aligned_buffer;
+ size_t buffer_size;
+ int len, audio_size;
+ uint8_t *packet_data;
+ int packet_size;
+
+ if (packet->pts != (int64_t)AV_NOPTS_VALUE)
+ decoder_timestamp(decoder,
+ av_rescale_q(packet->pts, *time_base,
+ (AVRational){1, 1}));
+
+ packet_data = packet->data;
+ packet_size = packet->size;
+
+ buffer_size = sizeof(audio_buf);
+ aligned_buffer = align16(audio_buf, &buffer_size);
+
+ while ((packet_size > 0) && (cmd == DECODE_COMMAND_NONE)) {
+ audio_size = buffer_size;
+ len = avcodec_decode_audio2(codec_context,
+ aligned_buffer, &audio_size,
+ packet_data, packet_size);
+
+ if (len < 0) {
+ /* if error, we skip the frame */
+ g_message("decoding failed\n");
+ break;
+ }
+
+ packet_data += len;
+ packet_size -= len;
+
+ if (audio_size <= 0)
+ continue;
+
+ cmd = decoder_data(decoder, is,
+ aligned_buffer, audio_size,
+ codec_context->bit_rate / 1000);
+ }
+ return cmd;
+}
+
+static enum sample_format
+ffmpeg_sample_format(G_GNUC_UNUSED const AVCodecContext *codec_context)
+{
+#if LIBAVCODEC_VERSION_INT >= ((51<<16)+(41<<8)+0)
+ int bits = (uint8_t) av_get_bits_per_sample_format(codec_context->sample_fmt);
+
+ /* XXX implement & test other sample formats */
+
+ switch (bits) {
+ case 16:
+ return SAMPLE_FORMAT_S16;
+ }
+
+ return SAMPLE_FORMAT_UNDEFINED;
+#else
+ /* XXX fixme 16-bit for older ffmpeg (13 Aug 2007) */
+ return SAMPLE_FORMAT_S16;
+#endif
+}
+
static AVInputFormat *
-ffmpeg_probe(struct decoder *decoder, struct input_stream *is,
- const char *uri)
+ffmpeg_probe(struct decoder *decoder, struct input_stream *is)
{
enum {
BUFFER_SIZE = 16384,
@@ -141,7 +257,7 @@ ffmpeg_probe(struct decoder *decoder, struct input_stream *is,
unsigned char *buffer = g_malloc(BUFFER_SIZE);
size_t nbytes = decoder_read(decoder, is, buffer, BUFFER_SIZE);
- if (nbytes <= PADDING || !input_stream_seek(is, 0, SEEK_SET)) {
+ if (nbytes <= PADDING || !input_stream_seek(is, 0, SEEK_SET, NULL)) {
g_free(buffer);
return NULL;
}
@@ -155,7 +271,7 @@ ffmpeg_probe(struct decoder *decoder, struct input_stream *is,
AVProbeData avpd = {
.buf = buffer,
.buf_size = nbytes,
- .filename = uri,
+ .filename = is->uri,
};
AVInputFormat *format = av_probe_input_format(&avpd, true);
@@ -164,15 +280,12 @@ ffmpeg_probe(struct decoder *decoder, struct input_stream *is,
return format;
}
-static bool
-ffmpeg_helper(const char *uri,
- struct decoder *decoder, struct input_stream *input,
- bool (*callback)(struct ffmpeg_context *ctx),
- struct ffmpeg_context *ctx)
+static void
+ffmpeg_decode(struct decoder *decoder, struct input_stream *input)
{
- AVInputFormat *input_format = ffmpeg_probe(decoder, input, uri);
+ AVInputFormat *input_format = ffmpeg_probe(decoder, input);
if (input_format == NULL)
- return false;
+ return;
g_debug("detected input format '%s' (%s)",
input_format->name, input_format->long_name);
@@ -181,28 +294,27 @@ ffmpeg_helper(const char *uri,
mpd_ffmpeg_stream_open(decoder, input);
if (stream == NULL) {
g_warning("Failed to open stream");
- return false;
+ return;
}
AVFormatContext *format_context;
AVCodecContext *codec_context;
AVCodec *codec;
int audio_stream;
- bool ret;
//ffmpeg works with ours "fileops" helper
- if (av_open_input_stream(&format_context, stream->io, uri,
+ if (av_open_input_stream(&format_context, stream->io, input->uri,
input_format, NULL) != 0) {
g_warning("Open failed\n");
mpd_ffmpeg_stream_close(stream);
- return false;
+ return;
}
if (av_find_stream_info(format_context)<0) {
g_warning("Couldn't find stream info\n");
av_close_input_stream(format_context);
mpd_ffmpeg_stream_close(stream);
- return false;
+ return;
}
audio_stream = ffmpeg_find_audio_stream(format_context);
@@ -210,7 +322,7 @@ ffmpeg_helper(const char *uri,
g_warning("No audio stream inside\n");
av_close_input_stream(format_context);
mpd_ffmpeg_stream_close(stream);
- return false;
+ return;
}
codec_context = format_context->streams[audio_stream]->codec;
@@ -223,145 +335,48 @@ ffmpeg_helper(const char *uri,
g_warning("Unsupported audio codec\n");
av_close_input_stream(format_context);
mpd_ffmpeg_stream_close(stream);
- return false;
+ return;
}
if (avcodec_open(codec_context, codec)<0) {
g_warning("Could not open codec\n");
av_close_input_stream(format_context);
mpd_ffmpeg_stream_close(stream);
- return false;
+ return;
}
- if (callback) {
- ctx->audio_stream = audio_stream;
- ctx->format_context = format_context;
- ctx->codec_context = codec_context;
-
- ret = callback(ctx);
- } else
- ret = true;
-
- avcodec_close(codec_context);
- av_close_input_stream(format_context);
- mpd_ffmpeg_stream_close(stream);
-
- return ret;
-}
-
-/**
- * On some platforms, libavcodec wants the output buffer aligned to 16
- * bytes (because it uses SSE/Altivec internally). This function
- * returns the aligned version of the specified buffer, and corrects
- * the buffer size.
- */
-static void *
-align16(void *p, size_t *length_p)
-{
- unsigned add = 16 - (size_t)p % 16;
-
- *length_p -= add;
- return (char *)p + add;
-}
-
-static enum decoder_command
-ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
- const AVPacket *packet,
- AVCodecContext *codec_context,
- const AVRational *time_base)
-{
- enum decoder_command cmd = DECODE_COMMAND_NONE;
- int position;
- uint8_t audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2 + 16];
- int16_t *aligned_buffer;
- size_t buffer_size;
- int len, audio_size;
- uint8_t *packet_data;
- int packet_size;
-
- packet_data = packet->data;
- packet_size = packet->size;
-
- buffer_size = sizeof(audio_buf);
- aligned_buffer = align16(audio_buf, &buffer_size);
-
- while ((packet_size > 0) && (cmd == DECODE_COMMAND_NONE)) {
- audio_size = buffer_size;
- len = avcodec_decode_audio2(codec_context,
- aligned_buffer, &audio_size,
- packet_data, packet_size);
-
- if (len < 0) {
- /* if error, we skip the frame */
- g_message("decoding failed\n");
- break;
- }
-
- packet_data += len;
- packet_size -= len;
-
- if (audio_size <= 0)
- continue;
-
- position = packet->pts != (int64_t)AV_NOPTS_VALUE
- ? av_rescale_q(packet->pts, *time_base,
- (AVRational){1, 1})
- : 0;
-
- cmd = decoder_data(decoder, is,
- aligned_buffer, audio_size,
- position,
- codec_context->bit_rate / 1000, NULL);
- }
- return cmd;
-}
-
-static bool
-ffmpeg_decode_internal(struct ffmpeg_context *ctx)
-{
- struct decoder *decoder = ctx->decoder;
- AVCodecContext *codec_context = ctx->codec_context;
- AVFormatContext *format_context = ctx->format_context;
- AVPacket packet;
+ GError *error = NULL;
struct audio_format audio_format;
- enum decoder_command cmd;
- int total_time;
-
- total_time = 0;
-
-#if LIBAVCODEC_VERSION_INT >= ((51<<16)+(41<<8)+0)
- audio_format.bits = (uint8_t) av_get_bits_per_sample_format(codec_context->sample_fmt);
-#else
- /* XXX fixme 16-bit for older ffmpeg (13 Aug 2007) */
- audio_format.bits = (uint8_t) 16;
-#endif
- audio_format.sample_rate = (unsigned int)codec_context->sample_rate;
- audio_format.channels = codec_context->channels;
-
- if (!audio_format_valid(&audio_format)) {
- g_warning("Invalid audio format: %u:%u:%u\n",
- audio_format.sample_rate, audio_format.bits,
- audio_format.channels);
- return false;
+ if (!audio_format_init_checked(&audio_format,
+ codec_context->sample_rate,
+ ffmpeg_sample_format(codec_context),
+ codec_context->channels, &error)) {
+ g_warning("%s", error->message);
+ g_error_free(error);
+ avcodec_close(codec_context);
+ av_close_input_stream(format_context);
+ mpd_ffmpeg_stream_close(stream);
+ return;
}
- //there is some problem with this on some demux (mp3 at least)
- if (format_context->duration != (int64_t)AV_NOPTS_VALUE) {
- total_time = format_context->duration / AV_TIME_BASE;
- }
+ int total_time = format_context->duration != (int64_t)AV_NOPTS_VALUE
+ ? format_context->duration / AV_TIME_BASE
+ : 0;
decoder_initialized(decoder, &audio_format,
- ctx->input->seekable, total_time);
+ input->seekable, total_time);
+ enum decoder_command cmd;
do {
+ AVPacket packet;
if (av_read_frame(format_context, &packet) < 0)
/* end of file */
break;
- if (packet.stream_index == ctx->audio_stream)
- cmd = ffmpeg_send_packet(decoder, ctx->input,
+ if (packet.stream_index == audio_stream)
+ cmd = ffmpeg_send_packet(decoder, input,
&packet, codec_context,
- &format_context->streams[ctx->audio_stream]->time_base);
+ &format_context->streams[audio_stream]->time_base);
else
cmd = decoder_get_command(decoder);
@@ -378,115 +393,121 @@ ffmpeg_decode_internal(struct ffmpeg_context *ctx)
}
} while (cmd != DECODE_COMMAND_STOP);
- return true;
+ avcodec_close(codec_context);
+ av_close_input_stream(format_context);
+ mpd_ffmpeg_stream_close(stream);
}
-static void
-ffmpeg_decode(struct decoder *decoder, struct input_stream *input)
-{
- struct ffmpeg_context ctx;
-
- ctx.input = input;
- ctx.decoder = decoder;
+#if LIBAVFORMAT_VERSION_INT >= ((52<<16)+(31<<8)+0)
+typedef struct ffmpeg_tag_map {
+ enum tag_type type;
+ const char *name;
+} ffmpeg_tag_map;
- char *uri = decoder_get_uri(decoder);
- ffmpeg_helper(uri, decoder, input,
- ffmpeg_decode_internal, &ctx);
- g_free(uri);
-}
+static const ffmpeg_tag_map ffmpeg_tag_maps[] = {
+ { TAG_TITLE, "title" },
+#if LIBAVFORMAT_VERSION_INT >= ((52<<16)+(50<<8))
+ { TAG_ARTIST, "artist" },
+ { TAG_DATE, "date" },
+#else
+ { TAG_ARTIST, "author" },
+ { TAG_DATE, "year" },
+#endif
+ { TAG_ALBUM, "album" },
+ { TAG_COMMENT, "comment" },
+ { TAG_GENRE, "genre" },
+ { TAG_TRACK, "track" },
+ { TAG_ARTIST_SORT, "author-sort" },
+ { TAG_ALBUM_ARTIST, "album_artist" },
+ { TAG_ALBUM_ARTIST_SORT, "album_artist-sort" },
+ { TAG_COMPOSER, "composer" },
+ { TAG_PERFORMER, "performer" },
+ { TAG_DISC, "disc" },
+};
-#if LIBAVFORMAT_VERSION_INT >= ((52<<16)+(31<<8)+0)
static bool
ffmpeg_copy_metadata(struct tag *tag, AVMetadata *m,
- enum tag_type type, const char *name)
+ const ffmpeg_tag_map tag_map)
{
- AVMetadataTag *mt = av_metadata_get(m, name, NULL, 0);
- if (mt != NULL)
- tag_add_item(tag, type, mt->value);
+ AVMetadataTag *mt = NULL;
+
+ while ((mt = av_metadata_get(m, tag_map.name, mt, 0)) != NULL)
+ tag_add_item(tag, tag_map.type, mt->value);
return mt != NULL;
}
+
#endif
-static bool ffmpeg_tag_internal(struct ffmpeg_context *ctx)
+//no tag reading in ffmpeg, check if playable
+static struct tag *
+ffmpeg_stream_tag(struct input_stream *is)
{
- struct tag *tag = (struct tag *) ctx->tag;
- AVFormatContext *f = ctx->format_context;
+ AVInputFormat *input_format = ffmpeg_probe(NULL, is);
+ if (input_format == NULL)
+ return NULL;
- tag->time = 0;
- if (f->duration != (int64_t)AV_NOPTS_VALUE)
- tag->time = f->duration / AV_TIME_BASE;
+ struct mpd_ffmpeg_stream *stream = mpd_ffmpeg_stream_open(NULL, is);
+ if (stream == NULL)
+ return NULL;
+
+ AVFormatContext *f;
+ if (av_open_input_stream(&f, stream->io, is->uri,
+ input_format, NULL) != 0) {
+ mpd_ffmpeg_stream_close(stream);
+ return NULL;
+ }
+
+ if (av_find_stream_info(f) < 0) {
+ av_close_input_stream(f);
+ mpd_ffmpeg_stream_close(stream);
+ return NULL;
+ }
+
+ struct tag *tag = tag_new();
+
+ tag->time = f->duration != (int64_t)AV_NOPTS_VALUE
+ ? f->duration / AV_TIME_BASE
+ : 0;
#if LIBAVFORMAT_VERSION_INT >= ((52<<16)+(31<<8)+0)
av_metadata_conv(f, NULL, f->iformat->metadata_conv);
- ffmpeg_copy_metadata(tag, f->metadata, TAG_ITEM_TITLE, "title");
-#if LIBAVFORMAT_VERSION_INT >= ((52<<16)+(50<<8))
- ffmpeg_copy_metadata(tag, f->metadata, TAG_ITEM_ARTIST, "artist");
- ffmpeg_copy_metadata(tag, f->metadata, TAG_ITEM_DATE, "date");
-#else
- ffmpeg_copy_metadata(tag, f->metadata, TAG_ITEM_ARTIST, "author");
- ffmpeg_copy_metadata(tag, f->metadata, TAG_ITEM_DATE, "year");
-#endif
- ffmpeg_copy_metadata(tag, f->metadata, TAG_ITEM_ALBUM, "album");
- ffmpeg_copy_metadata(tag, f->metadata, TAG_ITEM_COMMENT, "comment");
- ffmpeg_copy_metadata(tag, f->metadata, TAG_ITEM_GENRE, "genre");
- ffmpeg_copy_metadata(tag, f->metadata, TAG_ITEM_TRACK, "track");
- ffmpeg_copy_metadata(tag, f->metadata, TAG_ITEM_ALBUM_ARTIST, "album_artist");
- ffmpeg_copy_metadata(tag, f->metadata, TAG_ITEM_COMPOSER, "composer");
- ffmpeg_copy_metadata(tag, f->metadata, TAG_ITEM_PERFORMER, "performer");
- ffmpeg_copy_metadata(tag, f->metadata, TAG_ITEM_DISC, "disc");
+ for (unsigned i = 0; i < sizeof(ffmpeg_tag_maps)/sizeof(ffmpeg_tag_map); i++) {
+ int idx = ffmpeg_find_audio_stream(f);
+ ffmpeg_copy_metadata(tag, f->metadata, ffmpeg_tag_maps[i]);
+ if (idx >= 0)
+ ffmpeg_copy_metadata(tag, f->streams[idx]->metadata, ffmpeg_tag_maps[i]);
+ }
#else
if (f->author[0])
- tag_add_item(tag, TAG_ITEM_ARTIST, f->author);
+ tag_add_item(tag, TAG_ARTIST, f->author);
if (f->title[0])
- tag_add_item(tag, TAG_ITEM_TITLE, f->title);
+ tag_add_item(tag, TAG_TITLE, f->title);
if (f->album[0])
- tag_add_item(tag, TAG_ITEM_ALBUM, f->album);
+ tag_add_item(tag, TAG_ALBUM, f->album);
if (f->track > 0) {
char buffer[16];
snprintf(buffer, sizeof(buffer), "%d", f->track);
- tag_add_item(tag, TAG_ITEM_TRACK, buffer);
+ tag_add_item(tag, TAG_TRACK, buffer);
}
if (f->comment[0])
- tag_add_item(tag, TAG_ITEM_COMMENT, f->comment);
+ tag_add_item(tag, TAG_COMMENT, f->comment);
if (f->genre[0])
- tag_add_item(tag, TAG_ITEM_GENRE, f->genre);
+ tag_add_item(tag, TAG_GENRE, f->genre);
if (f->year > 0) {
char buffer[16];
snprintf(buffer, sizeof(buffer), "%d", f->year);
- tag_add_item(tag, TAG_ITEM_DATE, buffer);
+ tag_add_item(tag, TAG_DATE, buffer);
}
#endif
- return true;
-}
-
-//no tag reading in ffmpeg, check if playable
-static struct tag *ffmpeg_tag(const char *file)
-{
- struct input_stream input;
- struct ffmpeg_context ctx;
- bool ret;
-
- if (!input_stream_open(&input, file)) {
- g_warning("failed to open %s\n", file);
- return NULL;
- }
-
- ctx.decoder = NULL;
- ctx.tag = tag_new();
-
- ret = ffmpeg_helper(file, NULL, &input, ffmpeg_tag_internal, &ctx);
- if (!ret) {
- tag_free(ctx.tag);
- ctx.tag = NULL;
- }
- input_stream_close(&input);
+ av_close_input_stream(f);
+ mpd_ffmpeg_stream_close(stream);
- return ctx.tag;
+ return tag;
}
/**
@@ -590,6 +611,12 @@ static const char *const ffmpeg_mime_types[] = {
"video/x-vid",
"video/x-wmv",
"video/x-xvid",
+
+ /* special value for the "ffmpeg" input plugin: all streams by
+ the "ffmpeg" input plugin shall be decoded by this
+ plugin */
+ "audio/x-mpd-ffmpeg",
+
NULL
};
@@ -597,7 +624,7 @@ const struct decoder_plugin ffmpeg_decoder_plugin = {
.name = "ffmpeg",
.init = ffmpeg_init,
.stream_decode = ffmpeg_decode,
- .tag_dup = ffmpeg_tag,
+ .stream_tag = ffmpeg_stream_tag,
.suffixes = ffmpeg_suffixes,
.mime_types = ffmpeg_mime_types
};