Commit e2e8d0d2 authored by Max Kellermann's avatar Max Kellermann

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.
parent b2e3c075
...@@ -81,48 +81,27 @@ mpd_ffmpeg_log_callback(G_GNUC_UNUSED void *ptr, int level, ...@@ -81,48 +81,27 @@ mpd_ffmpeg_log_callback(G_GNUC_UNUSED void *ptr, int level,
#endif /* !OLD_FFMPEG_INCLUDES */ #endif /* !OLD_FFMPEG_INCLUDES */
struct ffmpeg_stream { struct mpd_ffmpeg_stream {
/** hack - see url_to_struct() */
char url[64];
struct decoder *decoder; struct decoder *decoder;
struct input_stream *input; struct input_stream *input;
};
/** ByteIOContext *io;
* Convert a faked mpd:// URL to a ffmpeg_stream structure. This is a unsigned char buffer[8192];
* 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)
{
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) static int
mpd_ffmpeg_stream_read(void *opaque, uint8_t *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, return decoder_read(stream->decoder, stream->input,
(void *)buf, size); (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; bool ret;
if (whence == AVSEEK_SIZE) if (whence == AVSEEK_SIZE)
...@@ -135,19 +114,31 @@ static int64_t mpd_ffmpeg_seek(URLContext *h, int64_t pos, int whence) ...@@ -135,19 +114,31 @@ static int64_t mpd_ffmpeg_seek(URLContext *h, int64_t pos, int whence)
return stream->input->offset; 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; struct mpd_ffmpeg_stream *stream = g_new(struct mpd_ffmpeg_stream, 1);
return 0; 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 = { static void
.name = "mpd", mpd_ffmpeg_stream_close(struct mpd_ffmpeg_stream *stream)
.url_open = mpd_ffmpeg_open, {
.url_read = mpd_ffmpeg_read, av_free(stream->io);
.url_seek = mpd_ffmpeg_seek, g_free(stream);
.url_close = mpd_ffmpeg_close, }
};
static bool static bool
ffmpeg_init(G_GNUC_UNUSED const struct config_param *param) ffmpeg_init(G_GNUC_UNUSED const struct config_param *param)
...@@ -157,7 +148,6 @@ ffmpeg_init(G_GNUC_UNUSED const struct config_param *param) ...@@ -157,7 +148,6 @@ ffmpeg_init(G_GNUC_UNUSED const struct config_param *param)
#endif #endif
av_register_all(); av_register_all();
register_protocol(&mpd_ffmpeg_fileops);
return true; return true;
} }
...@@ -173,26 +163,6 @@ ffmpeg_find_audio_stream(const AVFormatContext *format_context) ...@@ -173,26 +163,6 @@ ffmpeg_find_audio_stream(const AVFormatContext *format_context)
} }
/** /**
* 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);
}
/**
* On some platforms, libavcodec wants the output buffer aligned to 16 * On some platforms, libavcodec wants the output buffer aligned to 16
* bytes (because it uses SSE/Altivec internally). This function * bytes (because it uses SSE/Altivec internally). This function
* returns the aligned version of the specified buffer, and corrects * returns the aligned version of the specified buffer, and corrects
...@@ -320,36 +290,38 @@ ffmpeg_decode(struct decoder *decoder, struct input_stream *input) ...@@ -320,36 +290,38 @@ ffmpeg_decode(struct decoder *decoder, struct input_stream *input)
g_debug("detected input format '%s' (%s)", g_debug("detected input format '%s' (%s)",
input_format->name, input_format->long_name); input_format->name, input_format->long_name);
struct ffmpeg_stream stream = { struct mpd_ffmpeg_stream *stream =
.url = "mpd://X", /* only the mpd:// prefix matters */ mpd_ffmpeg_stream_open(decoder, input);
.decoder = decoder, if (stream == NULL) {
.input = input, g_warning("Failed to open stream");
}; return;
}
AVFormatContext *format_context; AVFormatContext *format_context;
AVCodecContext *codec_context; AVCodecContext *codec_context;
AVCodec *codec; AVCodec *codec;
int audio_stream; int audio_stream;
if (input->uri != NULL)
append_uri_suffix(&stream, input->uri);
//ffmpeg works with ours "fileops" helper //ffmpeg works with ours "fileops" helper
if (av_open_input_file(&format_context, stream.url, input_format, if (av_open_input_stream(&format_context, stream->io, input->uri,
0, NULL) != 0) { input_format, NULL) != 0) {
g_warning("Open failed\n"); g_warning("Open failed\n");
mpd_ffmpeg_stream_close(stream);
return; return;
} }
if (av_find_stream_info(format_context)<0) { if (av_find_stream_info(format_context)<0) {
g_warning("Couldn't find stream info\n"); 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; return;
} }
audio_stream = ffmpeg_find_audio_stream(format_context); audio_stream = ffmpeg_find_audio_stream(format_context);
if (audio_stream == -1) { if (audio_stream == -1) {
g_warning("No audio stream inside\n"); 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; return;
} }
...@@ -361,13 +333,15 @@ ffmpeg_decode(struct decoder *decoder, struct input_stream *input) ...@@ -361,13 +333,15 @@ ffmpeg_decode(struct decoder *decoder, struct input_stream *input)
if (!codec) { if (!codec) {
g_warning("Unsupported audio codec\n"); g_warning("Unsupported audio codec\n");
av_close_input_file(format_context); av_close_input_stream(format_context);
mpd_ffmpeg_stream_close(stream);
return; return;
} }
if (avcodec_open(codec_context, codec)<0) { if (avcodec_open(codec_context, codec)<0) {
g_warning("Could not open codec\n"); 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; return;
} }
...@@ -380,7 +354,8 @@ ffmpeg_decode(struct decoder *decoder, struct input_stream *input) ...@@ -380,7 +354,8 @@ ffmpeg_decode(struct decoder *decoder, struct input_stream *input)
g_warning("%s", error->message); g_warning("%s", error->message);
g_error_free(error); g_error_free(error);
avcodec_close(codec_context); avcodec_close(codec_context);
av_close_input_file(format_context); av_close_input_stream(format_context);
mpd_ffmpeg_stream_close(stream);
return; return;
} }
...@@ -419,7 +394,8 @@ ffmpeg_decode(struct decoder *decoder, struct input_stream *input) ...@@ -419,7 +394,8 @@ ffmpeg_decode(struct decoder *decoder, struct input_stream *input)
} while (cmd != DECODE_COMMAND_STOP); } while (cmd != DECODE_COMMAND_STOP);
avcodec_close(codec_context); avcodec_close(codec_context);
av_close_input_file(format_context); av_close_input_stream(format_context);
mpd_ffmpeg_stream_close(stream);
} }
#if LIBAVFORMAT_VERSION_INT >= ((52<<16)+(31<<8)+0) #if LIBAVFORMAT_VERSION_INT >= ((52<<16)+(31<<8)+0)
...@@ -470,21 +446,20 @@ ffmpeg_stream_tag(struct input_stream *is) ...@@ -470,21 +446,20 @@ ffmpeg_stream_tag(struct input_stream *is)
if (input_format == NULL) if (input_format == NULL)
return NULL; return NULL;
struct ffmpeg_stream stream = { struct mpd_ffmpeg_stream *stream = mpd_ffmpeg_stream_open(NULL, is);
.url = "mpd://X", /* only the mpd:// prefix matters */ if (stream == NULL)
.decoder = NULL, return NULL;
.input = is,
};
if (is->uri != NULL)
append_uri_suffix(&stream, is->uri);
AVFormatContext *f; AVFormatContext *f;
if (av_open_input_file(&f, stream.url, input_format, 0, NULL) != 0) if (av_open_input_stream(&f, stream->io, is->uri,
input_format, NULL) != 0) {
mpd_ffmpeg_stream_close(stream);
return NULL; return NULL;
}
if (av_find_stream_info(f) < 0) { if (av_find_stream_info(f) < 0) {
av_close_input_file(f); av_close_input_stream(f);
mpd_ffmpeg_stream_close(stream);
return NULL; return NULL;
} }
...@@ -529,7 +504,8 @@ ffmpeg_stream_tag(struct input_stream *is) ...@@ -529,7 +504,8 @@ ffmpeg_stream_tag(struct input_stream *is)
#endif #endif
av_close_input_file(f); av_close_input_stream(f);
mpd_ffmpeg_stream_close(stream);
return tag; return tag;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment