From 49bc317fb8b5dad101eb0d995167ce0ccaf9f7fc Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 30 Jun 2010 23:27:45 +0200 Subject: [PATCH] decoder/ffmpeg: fix libavformat 0.6 by using av_open_input_stream() libavformat 0.6 does not pass the original URI pointer to the "open" method, which leads to a crash because MPD was using a dirty hack to pass a pointer to that method. This patch switches to av_open_input_stream() with a custom ByteIOContext class, instead of doing the URI string hack with av_open_input_file(). Loosely based on a patch from Jasper St. Pierre. --- NEWS | 1 + src/decoder/ffmpeg_plugin.c | 134 ++++++++++++++---------------------- 2 files changed, 53 insertions(+), 82 deletions(-) diff --git a/NEWS b/NEWS index 3ae9fc7f3..a68abb0ed 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,7 @@ ver 0.15.11 (2010/??/??) - ffmpeg: fix memory leak - ffmpeg: free AVFormatContext on error - ffmpeg: read more metadata + - ffmpeg: fix libavformat 0.6 by using av_open_input_stream() * playlist: emit IDLE_OPTIONS when resetting single mode * listen: make get_remote_uid() work on BSD diff --git a/src/decoder/ffmpeg_plugin.c b/src/decoder/ffmpeg_plugin.c index f7218e238..9bae39793 100644 --- a/src/decoder/ffmpeg_plugin.c +++ b/src/decoder/ffmpeg_plugin.c @@ -53,48 +53,27 @@ struct ffmpeg_context { struct tag *tag; }; -struct ffmpeg_stream { - /** hack - see url_to_struct() */ - char url[64]; - +struct mpd_ffmpeg_stream { struct decoder *decoder; struct input_stream *input; + + ByteIOContext *io; + unsigned char buffer[8192]; }; -/** - * Convert a faked mpd:// URL to a ffmpeg_stream structure. This is a - * hack because ffmpeg does not provide a nice API for passing a - * user-defined pointer to mpdurl_open(). - */ -static struct ffmpeg_stream *url_to_struct(const char *url) +static int +mpd_ffmpeg_stream_read(void *opaque, uint8_t *buf, int size) { - union { - const char *in; - struct ffmpeg_stream *out; - } u = { .in = url }; - return u.out; -} - -static int mpd_ffmpeg_open(URLContext *h, const char *filename, - G_GNUC_UNUSED int flags) -{ - struct ffmpeg_stream *stream = url_to_struct(filename); - h->priv_data = stream; - h->is_streamed = stream->input->seekable ? 0 : 1; - return 0; -} - -static int mpd_ffmpeg_read(URLContext *h, unsigned char *buf, int size) -{ - struct ffmpeg_stream *stream = (struct ffmpeg_stream *) h->priv_data; + struct mpd_ffmpeg_stream *stream = opaque; return decoder_read(stream->decoder, stream->input, (void *)buf, size); } -static int64_t mpd_ffmpeg_seek(URLContext *h, int64_t pos, int whence) +static int64_t +mpd_ffmpeg_stream_seek(void *opaque, int64_t pos, int whence) { - struct ffmpeg_stream *stream = (struct ffmpeg_stream *) h->priv_data; + struct mpd_ffmpeg_stream *stream = opaque; bool ret; if (whence == AVSEEK_SIZE) @@ -107,25 +86,36 @@ static int64_t mpd_ffmpeg_seek(URLContext *h, int64_t pos, int whence) return stream->input->offset; } -static int mpd_ffmpeg_close(URLContext *h) +static struct mpd_ffmpeg_stream * +mpd_ffmpeg_stream_open(struct decoder *decoder, struct input_stream *input) { - h->priv_data = NULL; - return 0; + struct mpd_ffmpeg_stream *stream = g_new(struct mpd_ffmpeg_stream, 1); + stream->decoder = decoder; + stream->input = input; + stream->io = av_alloc_put_byte(stream->buffer, sizeof(stream->buffer), + false, stream, + mpd_ffmpeg_stream_read, NULL, + input->seekable + ? mpd_ffmpeg_stream_seek : NULL); + if (stream->io == NULL) { + g_free(stream); + return NULL; + } + + return stream; } -static URLProtocol mpd_ffmpeg_fileops = { - .name = "mpd", - .url_open = mpd_ffmpeg_open, - .url_read = mpd_ffmpeg_read, - .url_seek = mpd_ffmpeg_seek, - .url_close = mpd_ffmpeg_close, -}; +static void +mpd_ffmpeg_stream_close(struct mpd_ffmpeg_stream *stream) +{ + av_free(stream->io); + g_free(stream); +} static bool ffmpeg_init(G_GNUC_UNUSED const struct config_param *param) { av_register_all(); - register_protocol(&mpd_ffmpeg_fileops); return true; } @@ -140,26 +130,6 @@ ffmpeg_find_audio_stream(const AVFormatContext *format_context) return -1; } -/** - * Append the suffix of the original URI to the virtual stream URI. - * Without this, libavformat cannot detect some of the codecs - * (e.g. "shorten"). - */ -static void -append_uri_suffix(struct ffmpeg_stream *stream, const char *uri) -{ - assert(stream != NULL); - assert(uri != NULL); - - char *base = g_path_get_basename(uri); - - const char *suffix = strrchr(base, '.'); - if (suffix != NULL && suffix[1] != 0) - g_strlcat(stream->url, suffix, sizeof(stream->url)); - - g_free(base); -} - static AVInputFormat * ffmpeg_probe(struct decoder *decoder, struct input_stream *is, const char *uri) @@ -207,42 +177,39 @@ ffmpeg_helper(const char *uri, g_debug("detected input format '%s' (%s)", input_format->name, input_format->long_name); + struct mpd_ffmpeg_stream *stream = + mpd_ffmpeg_stream_open(decoder, input); + if (stream == NULL) { + g_warning("Failed to open stream"); + return false; + } + 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, input_format, - 0, NULL) != 0) { + if (av_open_input_stream(&format_context, stream->io, uri, + input_format, NULL) != 0) { g_warning("Open failed\n"); + mpd_ffmpeg_stream_close(stream); return false; } if (av_find_stream_info(format_context)<0) { g_warning("Couldn't find stream info\n"); - av_close_input_file(format_context); + av_close_input_stream(format_context); + mpd_ffmpeg_stream_close(stream); return false; } 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); + av_close_input_stream(format_context); + mpd_ffmpeg_stream_close(stream); return false; } @@ -254,13 +221,15 @@ ffmpeg_helper(const char *uri, if (!codec) { g_warning("Unsupported audio codec\n"); - av_close_input_file(format_context); + av_close_input_stream(format_context); + mpd_ffmpeg_stream_close(stream); return false; } if (avcodec_open(codec_context, codec)<0) { g_warning("Could not open codec\n"); - av_close_input_file(format_context); + av_close_input_stream(format_context); + mpd_ffmpeg_stream_close(stream); return false; } @@ -274,7 +243,8 @@ ffmpeg_helper(const char *uri, ret = true; avcodec_close(codec_context); - av_close_input_file(format_context); + av_close_input_stream(format_context); + mpd_ffmpeg_stream_close(stream); return ret; }