aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnton Khirnov <anton@khirnov.net>2013-04-22 17:07:45 +0200
committerAnton Khirnov <anton@khirnov.net>2013-11-04 11:02:12 +0100
commit988aa395866c8e0ab8dafdecea964684ac425a8d (patch)
tree3e99bf95267a3b97a2878453d421b859d7c7cf00
parent96aaffd8a0abd4d010adc46ea1748c23846e06d8 (diff)
libav decoder plugin: refactor managing the format context
Simplify, reduce code duplication.
-rw-r--r--src/decoder/libav_decoder_plugin.c262
1 files changed, 92 insertions, 170 deletions
diff --git a/src/decoder/libav_decoder_plugin.c b/src/decoder/libav_decoder_plugin.c
index fd404e29..39fda6e3 100644
--- a/src/decoder/libav_decoder_plugin.c
+++ b/src/decoder/libav_decoder_plugin.c
@@ -50,6 +50,8 @@ typedef struct LibavDecContext {
struct input_stream *input;
AVIOContext *io;
+ AVFormatContext *fmt_ctx;
+ AVStream *ast;
} LibavDecContext;
static GLogLevelFlags
@@ -106,15 +108,30 @@ mpd_libav_stream_seek(void *opaque, int64_t pos, int whence)
return stream->input->offset;
}
+static void libav_close(LibavDecContext *s)
+{
+ if (s->fmt_ctx)
+ avformat_close_input(&s->fmt_ctx);
+ if (s->io)
+ av_freep(&s->io->buffer);
+ av_freep(&s->io);
+}
+
+
#define INPUT_BUFFER_SIZE 16384
-static int mpd_libav_stream_open(LibavDecContext *s, struct input_stream *input)
+static int libav_open(LibavDecContext *s, struct input_stream *input)
{
- uint8_t *buf = av_malloc(INPUT_BUFFER_SIZE);
+ const char *filename = input->uri;
+ uint8_t *buf;
+ int ret = 0;
+ s->input = input;
+
+ /* init IO context */
+ buf = av_malloc(INPUT_BUFFER_SIZE);
if (!buf)
return AVERROR(ENOMEM);
- s->input = input;
s->io = avio_alloc_context(buf, INPUT_BUFFER_SIZE, 0,
s, mpd_libav_stream_read, NULL,
input->seekable ? mpd_libav_stream_seek : NULL);
@@ -123,33 +140,44 @@ static int mpd_libav_stream_open(LibavDecContext *s, struct input_stream *input)
return AVERROR(ENOMEM);
}
- return 0;
-}
+ /* open the input stream */
+ s->fmt_ctx = avformat_alloc_context();
+ if (!s->fmt_ctx) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+ s->fmt_ctx->pb = s->io;
-/**
- * API compatibility wrapper for av_open_input_stream() and
- * avformat_open_input().
- */
-static int
-mpd_libav_open_input(AVFormatContext **ic_ptr,
- AVIOContext *pb,
- const char *filename,
- AVInputFormat *fmt)
-{
- AVFormatContext *context = avformat_alloc_context();
- if (context == NULL)
- return AVERROR(ENOMEM);
+ ret = avformat_open_input(&s->fmt_ctx, filename, NULL, NULL);
+ if (ret < 0)
+ goto fail;
- context->pb = pb;
- *ic_ptr = context;
- return avformat_open_input(ic_ptr, filename, fmt, NULL);
-}
+ g_debug("Successfully opened input stream '%s', detected input format '%s' (%s)\n",
+ filename, s->fmt_ctx->iformat->name, s->fmt_ctx->iformat->long_name);
-static void
-mpd_libav_stream_close(LibavDecContext *stream)
-{
- av_freep(&stream->io->buffer);
- av_free(stream->io);
+ ret = avformat_find_stream_info(s->fmt_ctx, NULL);
+ if (ret < 0) {
+ g_warning("Couldn't find stream info\n");
+ goto fail;
+ }
+
+ for (int i = 0; i < s->fmt_ctx->nb_streams; i++)
+ if (s->fmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
+ s->ast = s->fmt_ctx->streams[i];
+ break;
+ }
+ if (!s->ast) {
+ g_warning("No audio stream in the file.\n");
+ ret = AVERROR(EINVAL);
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ g_warning("Failed to open input stream '%s'.\n", filename);
+ libav_close(s);
+ return ret;
}
static bool
@@ -161,17 +189,6 @@ libav_init(G_GNUC_UNUSED const struct config_param *param)
return true;
}
-static int
-libav_find_audio_stream(const AVFormatContext *format_context)
-{
- for (unsigned i = 0; i < format_context->nb_streams; ++i)
- if (format_context->streams[i]->codec->codec_type ==
- AVMEDIA_TYPE_AUDIO)
- return i;
-
- return -1;
-}
-
G_GNUC_CONST
static double
time_from_libav(int64_t t, const AVRational time_base)
@@ -299,70 +316,33 @@ libav_sample_format(G_GNUC_UNUSED const AVCodecContext *codec_context)
static void libav_decode(struct decoder *decoder, struct input_stream *input)
{
LibavDecContext s = { .decoder = decoder };
+ AVCodecContext *dec = NULL;
int ret;
- s.decoder = decoder;
- ret = mpd_libav_stream_open(&s, input);
- if (ret < 0) {
- g_warning("Failed to open stream");
- return;
- }
-
- //ffmpeg works with ours "fileops" helper
- AVFormatContext *format_context = NULL;
- if (mpd_libav_open_input(&format_context, s.io, input->uri, NULL) != 0) {
- g_warning("Open failed\n");
- mpd_libav_stream_close(&s);
- return;
- }
-
- g_debug("Successfully opened input stream '%s', detected input format '%s' (%s)",
- input->uri, format_context->iformat->name, format_context->iformat->long_name);
-
-
- const int find_result =
- avformat_find_stream_info(format_context, NULL);
- if (find_result < 0) {
- g_warning("Couldn't find stream info\n");
- avformat_close_input(&format_context);
- mpd_libav_stream_close(&s);
- return;
- }
-
- int audio_stream = libav_find_audio_stream(format_context);
- if (audio_stream == -1) {
- g_warning("No audio stream inside\n");
- avformat_close_input(&format_context);
- mpd_libav_stream_close(&s);
+ ret = libav_open(&s, input);
+ if (ret < 0)
return;
- }
- AVStream *av_stream = format_context->streams[audio_stream];
+ dec = s.ast->codec;
+ if (dec->codec_name[0] != 0)
+ g_debug("codec '%s'", dec->codec_name);
- AVCodecContext *codec_context = av_stream->codec;
- if (codec_context->codec_name[0] != 0)
- g_debug("codec '%s'", codec_context->codec_name);
-
- AVCodec *codec = avcodec_find_decoder(codec_context->codec_id);
+ AVCodec *codec = avcodec_find_decoder(dec->codec_id);
if (!codec) {
g_warning("Unsupported audio codec\n");
- avformat_close_input(&format_context);
- mpd_libav_stream_close(&s);
- return;
+ goto finish;
}
GError *error = NULL;
struct audio_format audio_format;
if (!audio_format_init_checked(&audio_format,
- codec_context->sample_rate,
- libav_sample_format(codec_context),
- codec_context->channels, &error)) {
+ dec->sample_rate,
+ libav_sample_format(dec),
+ dec->channels, &error)) {
g_warning("%s", error->message);
g_error_free(error);
- avformat_close_input(&format_context);
- mpd_libav_stream_close(&s);
- return;
+ goto finish;
}
/* the audio format must be read from AVCodecContext by now,
@@ -370,17 +350,14 @@ static void libav_decode(struct decoder *decoder, struct input_stream *input)
values into AVCodecContext.channels - a change that will be
reverted later by avcodec_decode_audio3() */
- const int open_result = avcodec_open2(codec_context, codec, NULL);
+ const int open_result = avcodec_open2(dec, codec, NULL);
if (open_result < 0) {
g_warning("Could not open codec\n");
- avformat_close_input(&format_context);
- mpd_libav_stream_close(&s);
- return;
+ goto finish;
}
- int total_time = format_context->duration != (int64_t)AV_NOPTS_VALUE
- ? format_context->duration / AV_TIME_BASE
- : 0;
+ int total_time = s.fmt_ctx->duration != (int64_t)AV_NOPTS_VALUE ?
+ s.fmt_ctx->duration / AV_TIME_BASE : 0;
decoder_initialized(decoder, &audio_format,
input->seekable, total_time);
@@ -388,14 +365,14 @@ static void libav_decode(struct decoder *decoder, struct input_stream *input)
enum decoder_command cmd;
do {
AVPacket packet;
- if (av_read_frame(format_context, &packet) < 0)
+ if (av_read_frame(s.fmt_ctx, &packet) < 0)
/* end of file */
break;
- if (packet.stream_index == audio_stream)
+ if (packet.stream_index == s.ast->index)
cmd = libav_send_packet(decoder, input,
- &packet, codec_context,
- &av_stream->time_base);
+ &packet, dec,
+ &s.ast->time_base);
else
cmd = decoder_get_command(decoder);
@@ -404,100 +381,45 @@ static void libav_decode(struct decoder *decoder, struct input_stream *input)
if (cmd == DECODE_COMMAND_SEEK) {
int64_t where =
time_to_libav(decoder_seek_where(decoder),
- av_stream->time_base);
+ s.ast->time_base);
- if (av_seek_frame(format_context, audio_stream, where,
+ if (av_seek_frame(s.fmt_ctx, s.ast->index, where,
AV_TIME_BASE) < 0)
decoder_seek_error(decoder);
else {
- avcodec_flush_buffers(codec_context);
+ avcodec_flush_buffers(dec);
decoder_command_finished(decoder);
}
}
} while (cmd != DECODE_COMMAND_STOP);
- avcodec_close(codec_context);
- avformat_close_input(&format_context);
- mpd_libav_stream_close(&s);
-}
-
-static AVInputFormat *libav_probe(struct decoder *decoder, struct input_stream *is)
-{
- enum {
- BUFFER_SIZE = 16384,
- PADDING = 16,
- };
-
- unsigned char *buffer = g_malloc(BUFFER_SIZE);
- size_t nbytes = decoder_read(decoder, is, buffer, BUFFER_SIZE);
- if (nbytes <= PADDING ||
- !input_stream_lock_seek(is, 0, SEEK_SET, NULL)) {
- g_free(buffer);
- return NULL;
- }
-
- /* some ffmpeg parsers (e.g. ac3_parser.c) read a few bytes
- beyond the declared buffer limit, which makes valgrind
- angry; this workaround removes some padding from the buffer
- size */
- nbytes -= PADDING;
-
- AVProbeData avpd = {
- .buf = buffer,
- .buf_size = nbytes,
- .filename = is->uri,
- };
-
- AVInputFormat *format = av_probe_input_format(&avpd, true);
- g_free(buffer);
-
- return format;
+finish:
+ if (dec)
+ avcodec_close(dec);
+ libav_close(&s);
}
-
//no tag reading in ffmpeg, check if playable
-static bool
-libav_scan_stream(struct input_stream *is,
- const struct tag_handler *handler, void *handler_ctx)
+static bool libav_scan_stream(struct input_stream *is,
+ const struct tag_handler *handler,
+ void *handler_ctx)
{
LibavDecContext s = { NULL };
int ret;
- AVInputFormat *input_format = libav_probe(NULL, is);
- if (input_format == NULL)
- return false;
- ret = mpd_libav_stream_open(&s, is);
+ ret = libav_open(&s, is);
if (ret < 0)
- return false;
-
- AVFormatContext *f = NULL;
- if (mpd_libav_open_input(&f, s.io, is->uri,
- input_format) != 0) {
- mpd_libav_stream_close(&s);
- return false;
- }
-
- const int find_result =
- avformat_find_stream_info(f, NULL);
- if (find_result < 0) {
- avformat_close_input(&f);
- mpd_libav_stream_close(&s);
- return false;
- }
+ return 0;
- if (f->duration != (int64_t)AV_NOPTS_VALUE)
+ if (s.fmt_ctx->duration != (int64_t)AV_NOPTS_VALUE)
tag_handler_invoke_duration(handler, handler_ctx,
- f->duration / AV_TIME_BASE);
+ s.fmt_ctx->duration / AV_TIME_BASE);
- libav_scan_dictionary(f->metadata, handler, handler_ctx);
- int idx = libav_find_audio_stream(f);
- if (idx >= 0)
- libav_scan_dictionary(f->streams[idx]->metadata,
- handler, handler_ctx);
+ libav_scan_dictionary(s.fmt_ctx->metadata, handler, handler_ctx);
+ libav_scan_dictionary(s.ast->metadata, handler, handler_ctx);
- avformat_close_input(&f);
- mpd_libav_stream_close(&s);
+ libav_close(&s);
return true;
}