From 988aa395866c8e0ab8dafdecea964684ac425a8d Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Mon, 22 Apr 2013 17:07:45 +0200 Subject: libav decoder plugin: refactor managing the format context Simplify, reduce code duplication. --- src/decoder/libav_decoder_plugin.c | 262 +++++++++++++------------------------ 1 file 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; } -- cgit v1.2.3