diff options
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 }; |