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.
This commit is contained in:
parent
375a09d6f6
commit
49bc317fb8
1
NEWS
1
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
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue