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)379
1 files changed, 199 insertions, 180 deletions
diff --git a/src/decoder/ffmpeg_plugin.c b/src/decoder/ffmpeg_decoder_plugin.c
index 2a46601f4..d2ea28681 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 ffmpeg_stream {
/** hack - see url_to_struct() */
@@ -100,7 +128,7 @@ static int64_t mpd_ffmpeg_seek(URLContext *h, 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;
@@ -124,6 +152,10 @@ static URLProtocol mpd_ffmpeg_fileops = {
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();
register_protocol(&mpd_ffmpeg_fileops);
return true;
@@ -160,78 +192,6 @@ append_uri_suffix(struct ffmpeg_stream *stream, const char *uri)
g_free(base);
}
-static bool
-ffmpeg_helper(const char *uri, struct input_stream *input,
- bool (*callback)(struct ffmpeg_context *ctx),
- struct ffmpeg_context *ctx)
-{
- AVFormatContext *format_context;
- AVCodecContext *codec_context;
- AVCodec *codec;
- int audio_stream;
- struct ffmpeg_stream stream = {
- .url = "mpd://X", /* only the mpd:// prefix matters */
- };
- bool ret;
-
- if (uri != NULL)
- append_uri_suffix(&stream, uri);
-
- stream.input = input;
- if (ctx && ctx->decoder) {
- stream.decoder = ctx->decoder; //are we in decoding loop ?
- } else {
- stream.decoder = NULL;
- }
-
- //ffmpeg works with ours "fileops" helper
- if (av_open_input_file(&format_context, stream.url, NULL, 0, NULL) != 0) {
- g_warning("Open failed\n");
- return false;
- }
-
- if (av_find_stream_info(format_context)<0) {
- g_warning("Couldn't find stream info\n");
- return false;
- }
-
- audio_stream = ffmpeg_find_audio_stream(format_context);
- if (audio_stream == -1) {
- g_warning("No audio stream inside\n");
- return false;
- }
-
- codec_context = format_context->streams[audio_stream]->codec;
- if (codec_context->codec_name[0] != 0)
- g_debug("codec '%s'", codec_context->codec_name);
-
- codec = avcodec_find_decoder(codec_context->codec_id);
-
- if (!codec) {
- g_warning("Unsupported audio codec\n");
- return false;
- }
-
- if (avcodec_open(codec_context, codec)<0) {
- g_warning("Could not open codec\n");
- return false;
- }
-
- 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_file(format_context);
-
- return ret;
-}
-
/**
* On some platforms, libavcodec wants the output buffer aligned to 16
* bytes (because it uses SSE/Altivec internally). This function
@@ -254,7 +214,6 @@ ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
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;
@@ -262,6 +221,11 @@ ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
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;
@@ -286,65 +250,117 @@ ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
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);
+ codec_context->bit_rate / 1000);
}
return cmd;
}
-static bool
-ffmpeg_decode_internal(struct ffmpeg_context *ctx)
+static enum sample_format
+ffmpeg_sample_format(G_GNUC_UNUSED const AVCodecContext *codec_context)
{
- struct decoder *decoder = ctx->decoder;
- AVCodecContext *codec_context = ctx->codec_context;
- AVFormatContext *format_context = ctx->format_context;
- AVPacket packet;
- struct audio_format audio_format;
- enum decoder_command cmd;
- int total_time;
+#if LIBAVCODEC_VERSION_INT >= ((51<<16)+(41<<8)+0)
+ int bits = (uint8_t) av_get_bits_per_sample_format(codec_context->sample_fmt);
- total_time = 0;
+ /* XXX implement & test other sample formats */
-#if LIBAVCODEC_VERSION_INT >= ((51<<16)+(41<<8)+0)
- audio_format.bits = (uint8_t) av_get_bits_per_sample_format(codec_context->sample_fmt);
+ switch (bits) {
+ case 16:
+ return SAMPLE_FORMAT_S16;
+ }
+
+ return SAMPLE_FORMAT_UNDEFINED;
#else
/* XXX fixme 16-bit for older ffmpeg (13 Aug 2007) */
- audio_format.bits = (uint8_t) 16;
+ return SAMPLE_FORMAT_S16;
#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;
+}
+
+static void
+ffmpeg_decode(struct decoder *decoder, struct input_stream *input)
+{
+ struct ffmpeg_stream stream = {
+ .url = "mpd://X", /* only the mpd:// prefix matters */
+ .decoder = decoder,
+ .input = input,
+ };
+ AVFormatContext *format_context;
+ AVCodecContext *codec_context;
+ AVCodec *codec;
+ int audio_stream;
+
+ if (input->uri != NULL)
+ append_uri_suffix(&stream, input->uri);
+
+ //ffmpeg works with ours "fileops" helper
+ if (av_open_input_file(&format_context, stream.url, NULL, 0, NULL) != 0) {
+ g_warning("Open failed\n");
+ return;
+ }
+
+ if (av_find_stream_info(format_context)<0) {
+ g_warning("Couldn't find stream info\n");
+ av_close_input_file(format_context);
+ 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;
+ audio_stream = ffmpeg_find_audio_stream(format_context);
+ if (audio_stream == -1) {
+ g_warning("No audio stream inside\n");
+ av_close_input_file(format_context);
+ return;
+ }
+
+ codec_context = format_context->streams[audio_stream]->codec;
+ if (codec_context->codec_name[0] != 0)
+ g_debug("codec '%s'", codec_context->codec_name);
+
+ codec = avcodec_find_decoder(codec_context->codec_id);
+
+ if (!codec) {
+ g_warning("Unsupported audio codec\n");
+ av_close_input_file(format_context);
+ return;
+ }
+
+ if (avcodec_open(codec_context, codec)<0) {
+ g_warning("Could not open codec\n");
+ av_close_input_file(format_context);
+ return;
+ }
+
+ GError *error = NULL;
+ struct audio_format audio_format;
+ 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_file(format_context);
+ return;
}
+ 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);
@@ -361,19 +377,8 @@ ffmpeg_decode_internal(struct ffmpeg_context *ctx)
}
} while (cmd != DECODE_COMMAND_STOP);
- return true;
-}
-
-static void
-ffmpeg_decode(struct decoder *decoder, struct input_stream *input)
-{
- struct ffmpeg_context ctx;
-
- ctx.input = input;
- ctx.decoder = decoder;
-
- ffmpeg_helper(decoder_get_uri(decoder), input,
- ffmpeg_decode_internal, &ctx);
+ avcodec_close(codec_context);
+ av_close_input_file(format_context);
}
#if LIBAVFORMAT_VERSION_INT >= ((52<<16)+(31<<8)+0)
@@ -381,84 +386,92 @@ static bool
ffmpeg_copy_metadata(struct tag *tag, AVMetadata *m,
enum tag_type type, const char *name)
{
- AVMetadataTag *mt = av_metadata_get(m, name, NULL, 0);
- if (mt != NULL)
+ AVMetadataTag *mt = NULL;
+
+ while ((mt = av_metadata_get(m, name, mt, 0)) != NULL)
tag_add_item(tag, 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;
+ struct ffmpeg_stream stream = {
+ .url = "mpd://X", /* only the mpd:// prefix matters */
+ .decoder = NULL,
+ .input = is,
+ };
+
+ if (is->uri != NULL)
+ append_uri_suffix(&stream, is->uri);
+
+ AVFormatContext *f;
+ if (av_open_input_file(&f, stream.url, NULL, 0, NULL) != 0)
+ return NULL;
- tag->time = 0;
- if (f->duration != (int64_t)AV_NOPTS_VALUE)
- tag->time = f->duration / AV_TIME_BASE;
+ if (av_find_stream_info(f) < 0) {
+ av_close_input_file(f);
+ 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");
- ffmpeg_copy_metadata(tag, f->metadata, TAG_ITEM_ARTIST, "author");
- 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_DATE, "year");
+ ffmpeg_copy_metadata(tag, f->metadata, TAG_TITLE, "title");
+#if LIBAVFORMAT_VERSION_INT >= ((52<<16)+(50<<8))
+ ffmpeg_copy_metadata(tag, f->metadata, TAG_ARTIST, "artist");
+ ffmpeg_copy_metadata(tag, f->metadata, TAG_DATE, "date");
+#else
+ ffmpeg_copy_metadata(tag, f->metadata, TAG_ARTIST, "author");
+ ffmpeg_copy_metadata(tag, f->metadata, TAG_DATE, "year");
+#endif
+ ffmpeg_copy_metadata(tag, f->metadata, TAG_ALBUM, "album");
+ ffmpeg_copy_metadata(tag, f->metadata, TAG_COMMENT, "comment");
+ ffmpeg_copy_metadata(tag, f->metadata, TAG_GENRE, "genre");
+ ffmpeg_copy_metadata(tag, f->metadata, TAG_TRACK, "track");
+ ffmpeg_copy_metadata(tag, f->metadata, TAG_ARTIST_SORT, "author-sort");
+ ffmpeg_copy_metadata(tag, f->metadata, TAG_ALBUM_ARTIST, "album_artist");
+ ffmpeg_copy_metadata(tag, f->metadata, TAG_ALBUM_ARTIST_SORT, "album_artist-sort");
+ ffmpeg_copy_metadata(tag, f->metadata, TAG_COMPOSER, "composer");
+ ffmpeg_copy_metadata(tag, f->metadata, TAG_PERFORMER, "performer");
+ ffmpeg_copy_metadata(tag, f->metadata, TAG_DISC, "disc");
#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();
+ av_close_input_file(f);
- ret = ffmpeg_helper(file, &input, ffmpeg_tag_internal, &ctx);
- if (!ret) {
- tag_free(ctx.tag);
- ctx.tag = NULL;
- }
-
- input_stream_close(&input);
-
- return ctx.tag;
+ return tag;
}
/**
@@ -562,6 +575,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
};
@@ -569,7 +588,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
};