OSDN Git Service

Merge commit '122de16dd8108a59a55d30543c9f28b5f61b02d1'
authorClément Bœsch <cboesch@gopro.com>
Fri, 5 May 2017 11:53:11 +0000 (13:53 +0200)
committerClément Bœsch <cboesch@gopro.com>
Fri, 5 May 2017 12:04:30 +0000 (14:04 +0200)
* commit '122de16dd8108a59a55d30543c9f28b5f61b02d1':
  Replace cmdutils_common_opts.h by a macro

Merged-by: Clément Bœsch <cboesch@gopro.com>
1  2 
Makefile
cmdutils.h
ffmpeg_opt.c
ffplay.c
ffprobe.c
ffserver.c
tests/ref/fate/source

diff --cc Makefile
+++ b/Makefile
@@@ -56,11 -101,9 +56,10 @@@ FFLIBS-$(CONFIG_SWSCALE)    += swscal
  
  FFLIBS := avutil
  
 -DATA_FILES := $(wildcard $(SRC_PATH)/presets/*.avpreset)
 +DATA_FILES := $(wildcard $(SRC_PATH)/presets/*.ffpreset) $(SRC_PATH)/doc/ffprobe.xsd
 +EXAMPLES_FILES := $(wildcard $(SRC_PATH)/doc/examples/*.c) $(SRC_PATH)/doc/examples/Makefile $(SRC_PATH)/doc/examples/README
  
- SKIPHEADERS = cmdutils_common_opts.h                                    \
-               compat/w32pthreads.h
+ SKIPHEADERS = compat/w32pthreads.h
  
  # first so "all" becomes default target
  all: all-yes
diff --cc cmdutils.h
@@@ -206,6 -182,26 +206,59 @@@ typedef struct OptionDef 
  void show_help_options(const OptionDef *options, const char *msg, int req_flags,
                         int rej_flags, int alt_flags);
  
 -    { "loglevel",    HAS_ARG,              { .func_arg = opt_loglevel },     "set libav* logging level", "loglevel" },  \
 -    { "v",           HAS_ARG,              { .func_arg = opt_loglevel },     "set libav* logging level", "loglevel" },  \
 -    { "cpuflags",    HAS_ARG | OPT_EXPERT, { .func_arg = opt_cpuflags },     "set CPU flags mask", "mask" },            \
++#if CONFIG_OPENCL
++#define CMDUTILS_COMMON_OPTIONS_OPENCL                                                                                  \
++    { "opencl_bench", OPT_EXIT, {.func_arg = opt_opencl_bench},                                                         \
++       "run benchmark on all OpenCL devices and show results" },                                                        \
++    { "opencl_options", HAS_ARG, {.func_arg = opt_opencl},                                                              \
++       "set OpenCL environment options" },                                                                              \
++
++#else
++#define CMDUTILS_COMMON_OPTIONS_OPENCL
++#endif
++
++#if CONFIG_AVDEVICE
++#define CMDUTILS_COMMON_OPTIONS_AVDEVICE                                                                                \
++    { "sources"    , OPT_EXIT | HAS_ARG, { .func_arg = show_sources },                                                  \
++      "list sources of the input device", "device" },                                                                   \
++    { "sinks"      , OPT_EXIT | HAS_ARG, { .func_arg = show_sinks },                                                    \
++      "list sinks of the output device", "device" },                                                                    \
++
++#else
++#define CMDUTILS_COMMON_OPTIONS_AVDEVICE
++#endif
++
+ #define CMDUTILS_COMMON_OPTIONS                                                                                         \
+     { "L",           OPT_EXIT,             { .func_arg = show_license },     "show license" },                          \
+     { "h",           OPT_EXIT,             { .func_arg = show_help },        "show help", "topic" },                    \
+     { "?",           OPT_EXIT,             { .func_arg = show_help },        "show help", "topic" },                    \
+     { "help",        OPT_EXIT,             { .func_arg = show_help },        "show help", "topic" },                    \
+     { "-help",       OPT_EXIT,             { .func_arg = show_help },        "show help", "topic" },                    \
+     { "version",     OPT_EXIT,             { .func_arg = show_version },     "show version" },                          \
++    { "buildconf",   OPT_EXIT,             { .func_arg = show_buildconf },   "show build configuration" },              \
+     { "formats",     OPT_EXIT,             { .func_arg = show_formats },     "show available formats" },                \
++    { "muxers",      OPT_EXIT,             { .func_arg = show_muxers },      "show available muxers" },                 \
++    { "demuxers",    OPT_EXIT,             { .func_arg = show_demuxers },    "show available demuxers" },               \
++    { "devices",     OPT_EXIT,             { .func_arg = show_devices },     "show available devices" },                \
+     { "codecs",      OPT_EXIT,             { .func_arg = show_codecs },      "show available codecs" },                 \
+     { "decoders",    OPT_EXIT,             { .func_arg = show_decoders },    "show available decoders" },               \
+     { "encoders",    OPT_EXIT,             { .func_arg = show_encoders },    "show available encoders" },               \
+     { "bsfs",        OPT_EXIT,             { .func_arg = show_bsfs },        "show available bit stream filters" },     \
+     { "protocols",   OPT_EXIT,             { .func_arg = show_protocols },   "show available protocols" },              \
+     { "filters",     OPT_EXIT,             { .func_arg = show_filters },     "show available filters" },                \
+     { "pix_fmts",    OPT_EXIT,             { .func_arg = show_pix_fmts },    "show available pixel formats" },          \
++    { "layouts",     OPT_EXIT,             { .func_arg = show_layouts },     "show standard channel layouts" },         \
+     { "sample_fmts", OPT_EXIT,             { .func_arg = show_sample_fmts }, "show available audio sample formats" },   \
++    { "colors",      OPT_EXIT,             { .func_arg = show_colors },      "show available color names" },            \
++    { "loglevel",    HAS_ARG,              { .func_arg = opt_loglevel },     "set logging level", "loglevel" },         \
++    { "v",           HAS_ARG,              { .func_arg = opt_loglevel },     "set logging level", "loglevel" },         \
++    { "report",      0,                    { (void*)opt_report },            "generate a report" },                     \
++    { "max_alloc",   HAS_ARG,              { .func_arg = opt_max_alloc },    "set maximum size of a single allocated block", "bytes" }, \
++    { "cpuflags",    HAS_ARG | OPT_EXPERT, { .func_arg = opt_cpuflags },     "force specific cpu flags", "flags" },     \
++    { "hide_banner", OPT_BOOL | OPT_EXPERT, {&hide_banner},     "do not show program banner", "hide_banner" },          \
++    CMDUTILS_COMMON_OPTIONS_OPENCL                                                                                      \
++    CMDUTILS_COMMON_OPTIONS_AVDEVICE                                                                                    \
  /**
   * Show help for all options with given flags in class and all its
   * children.
diff --cc ffmpeg_opt.c
index e73a610,0000000..ce85f32
mode 100644,000000..100644
--- /dev/null
@@@ -1,3669 -1,0 +1,3669 @@@
- #include "cmdutils_common_opts.h"
 +/*
 + * ffmpeg option parsing
 + *
 + * This file is part of FFmpeg.
 + *
 + * FFmpeg is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public
 + * License as published by the Free Software Foundation; either
 + * version 2.1 of the License, or (at your option) any later version.
 + *
 + * FFmpeg is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with FFmpeg; if not, write to the Free Software
 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 + */
 +
 +#include <stdint.h>
 +
 +#include "ffmpeg.h"
 +#include "cmdutils.h"
 +
 +#include "libavformat/avformat.h"
 +
 +#include "libavcodec/avcodec.h"
 +
 +#include "libavfilter/avfilter.h"
 +
 +#include "libavutil/avassert.h"
 +#include "libavutil/avstring.h"
 +#include "libavutil/avutil.h"
 +#include "libavutil/channel_layout.h"
 +#include "libavutil/intreadwrite.h"
 +#include "libavutil/fifo.h"
 +#include "libavutil/mathematics.h"
 +#include "libavutil/opt.h"
 +#include "libavutil/parseutils.h"
 +#include "libavutil/pixdesc.h"
 +#include "libavutil/pixfmt.h"
 +
 +#define DEFAULT_PASS_LOGFILENAME_PREFIX "ffmpeg2pass"
 +
 +#define MATCH_PER_STREAM_OPT(name, type, outvar, fmtctx, st)\
 +{\
 +    int i, ret;\
 +    for (i = 0; i < o->nb_ ## name; i++) {\
 +        char *spec = o->name[i].specifier;\
 +        if ((ret = check_stream_specifier(fmtctx, st, spec)) > 0)\
 +            outvar = o->name[i].u.type;\
 +        else if (ret < 0)\
 +            exit_program(1);\
 +    }\
 +}
 +
 +#define MATCH_PER_TYPE_OPT(name, type, outvar, fmtctx, mediatype)\
 +{\
 +    int i;\
 +    for (i = 0; i < o->nb_ ## name; i++) {\
 +        char *spec = o->name[i].specifier;\
 +        if (!strcmp(spec, mediatype))\
 +            outvar = o->name[i].u.type;\
 +    }\
 +}
 +
 +const HWAccel hwaccels[] = {
 +#if HAVE_VDPAU_X11
 +    { "vdpau", vdpau_init, HWACCEL_VDPAU, AV_PIX_FMT_VDPAU },
 +#endif
 +#if HAVE_DXVA2_LIB
 +    { "dxva2", dxva2_init, HWACCEL_DXVA2, AV_PIX_FMT_DXVA2_VLD },
 +#endif
 +#if CONFIG_VDA
 +    { "vda",   videotoolbox_init,   HWACCEL_VDA,   AV_PIX_FMT_VDA },
 +#endif
 +#if CONFIG_VIDEOTOOLBOX
 +    { "videotoolbox",   videotoolbox_init,   HWACCEL_VIDEOTOOLBOX,   AV_PIX_FMT_VIDEOTOOLBOX },
 +#endif
 +#if CONFIG_LIBMFX
 +    { "qsv",   qsv_init,   HWACCEL_QSV,   AV_PIX_FMT_QSV },
 +#endif
 +#if CONFIG_VAAPI
 +    { "vaapi", vaapi_decode_init, HWACCEL_VAAPI, AV_PIX_FMT_VAAPI },
 +#endif
 +#if CONFIG_CUVID
 +    { "cuvid", cuvid_init, HWACCEL_CUVID, AV_PIX_FMT_CUDA },
 +#endif
 +    { 0 },
 +};
 +int hwaccel_lax_profile_check = 0;
 +AVBufferRef *hw_device_ctx;
 +
 +char *vstats_filename;
 +char *sdp_filename;
 +
 +float audio_drift_threshold = 0.1;
 +float dts_delta_threshold   = 10;
 +float dts_error_threshold   = 3600*30;
 +
 +int audio_volume      = 256;
 +int audio_sync_method = 0;
 +int video_sync_method = VSYNC_AUTO;
 +float frame_drop_threshold = 0;
 +int do_deinterlace    = 0;
 +int do_benchmark      = 0;
 +int do_benchmark_all  = 0;
 +int do_hex_dump       = 0;
 +int do_pkt_dump       = 0;
 +int copy_ts           = 0;
 +int start_at_zero     = 0;
 +int copy_tb           = -1;
 +int debug_ts          = 0;
 +int exit_on_error     = 0;
 +int abort_on_flags    = 0;
 +int print_stats       = -1;
 +int qp_hist           = 0;
 +int stdin_interaction = 1;
 +int frame_bits_per_raw_sample = 0;
 +float max_error_rate  = 2.0/3;
 +int filter_nbthreads = 0;
 +int filter_complex_nbthreads = 0;
 +int vstats_version = 2;
 +
 +
 +static int intra_only         = 0;
 +static int file_overwrite     = 0;
 +static int no_file_overwrite  = 0;
 +static int do_psnr            = 0;
 +static int input_sync;
 +static int override_ffserver  = 0;
 +static int input_stream_potentially_available = 0;
 +static int ignore_unknown_streams = 0;
 +static int copy_unknown_streams = 0;
 +
 +static void uninit_options(OptionsContext *o)
 +{
 +    const OptionDef *po = options;
 +    int i;
 +
 +    /* all OPT_SPEC and OPT_STRING can be freed in generic way */
 +    while (po->name) {
 +        void *dst = (uint8_t*)o + po->u.off;
 +
 +        if (po->flags & OPT_SPEC) {
 +            SpecifierOpt **so = dst;
 +            int i, *count = (int*)(so + 1);
 +            for (i = 0; i < *count; i++) {
 +                av_freep(&(*so)[i].specifier);
 +                if (po->flags & OPT_STRING)
 +                    av_freep(&(*so)[i].u.str);
 +            }
 +            av_freep(so);
 +            *count = 0;
 +        } else if (po->flags & OPT_OFFSET && po->flags & OPT_STRING)
 +            av_freep(dst);
 +        po++;
 +    }
 +
 +    for (i = 0; i < o->nb_stream_maps; i++)
 +        av_freep(&o->stream_maps[i].linklabel);
 +    av_freep(&o->stream_maps);
 +    av_freep(&o->audio_channel_maps);
 +    av_freep(&o->streamid_map);
 +    av_freep(&o->attachments);
 +}
 +
 +static void init_options(OptionsContext *o)
 +{
 +    memset(o, 0, sizeof(*o));
 +
 +    o->stop_time = INT64_MAX;
 +    o->mux_max_delay  = 0.7;
 +    o->start_time     = AV_NOPTS_VALUE;
 +    o->start_time_eof = AV_NOPTS_VALUE;
 +    o->recording_time = INT64_MAX;
 +    o->limit_filesize = UINT64_MAX;
 +    o->chapters_input_file = INT_MAX;
 +    o->accurate_seek  = 1;
 +}
 +
 +static int show_hwaccels(void *optctx, const char *opt, const char *arg)
 +{
 +    int i;
 +
 +    printf("Hardware acceleration methods:\n");
 +    for (i = 0; hwaccels[i].name; i++) {
 +        printf("%s\n", hwaccels[i].name);
 +    }
 +    printf("\n");
 +    return 0;
 +}
 +
 +/* return a copy of the input with the stream specifiers removed from the keys */
 +static AVDictionary *strip_specifiers(AVDictionary *dict)
 +{
 +    AVDictionaryEntry *e = NULL;
 +    AVDictionary    *ret = NULL;
 +
 +    while ((e = av_dict_get(dict, "", e, AV_DICT_IGNORE_SUFFIX))) {
 +        char *p = strchr(e->key, ':');
 +
 +        if (p)
 +            *p = 0;
 +        av_dict_set(&ret, e->key, e->value, 0);
 +        if (p)
 +            *p = ':';
 +    }
 +    return ret;
 +}
 +
 +static int opt_abort_on(void *optctx, const char *opt, const char *arg)
 +{
 +    static const AVOption opts[] = {
 +        { "abort_on"        , NULL, 0, AV_OPT_TYPE_FLAGS, { .i64 = 0 }, INT64_MIN, INT64_MAX, .unit = "flags" },
 +        { "empty_output"    , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = ABORT_ON_FLAG_EMPTY_OUTPUT     },    .unit = "flags" },
 +        { NULL },
 +    };
 +    static const AVClass class = {
 +        .class_name = "",
 +        .item_name  = av_default_item_name,
 +        .option     = opts,
 +        .version    = LIBAVUTIL_VERSION_INT,
 +    };
 +    const AVClass *pclass = &class;
 +
 +    return av_opt_eval_flags(&pclass, &opts[0], arg, &abort_on_flags);
 +}
 +
 +static int opt_sameq(void *optctx, const char *opt, const char *arg)
 +{
 +    av_log(NULL, AV_LOG_ERROR, "Option '%s' was removed. "
 +           "If you are looking for an option to preserve the quality (which is not "
 +           "what -%s was for), use -qscale 0 or an equivalent quality factor option.\n",
 +           opt, opt);
 +    return AVERROR(EINVAL);
 +}
 +
 +static int opt_video_channel(void *optctx, const char *opt, const char *arg)
 +{
 +    av_log(NULL, AV_LOG_WARNING, "This option is deprecated, use -channel.\n");
 +    return opt_default(optctx, "channel", arg);
 +}
 +
 +static int opt_video_standard(void *optctx, const char *opt, const char *arg)
 +{
 +    av_log(NULL, AV_LOG_WARNING, "This option is deprecated, use -standard.\n");
 +    return opt_default(optctx, "standard", arg);
 +}
 +
 +static int opt_audio_codec(void *optctx, const char *opt, const char *arg)
 +{
 +    OptionsContext *o = optctx;
 +    return parse_option(o, "codec:a", arg, options);
 +}
 +
 +static int opt_video_codec(void *optctx, const char *opt, const char *arg)
 +{
 +    OptionsContext *o = optctx;
 +    return parse_option(o, "codec:v", arg, options);
 +}
 +
 +static int opt_subtitle_codec(void *optctx, const char *opt, const char *arg)
 +{
 +    OptionsContext *o = optctx;
 +    return parse_option(o, "codec:s", arg, options);
 +}
 +
 +static int opt_data_codec(void *optctx, const char *opt, const char *arg)
 +{
 +    OptionsContext *o = optctx;
 +    return parse_option(o, "codec:d", arg, options);
 +}
 +
 +static int opt_map(void *optctx, const char *opt, const char *arg)
 +{
 +    OptionsContext *o = optctx;
 +    StreamMap *m = NULL;
 +    int i, negative = 0, file_idx;
 +    int sync_file_idx = -1, sync_stream_idx = 0;
 +    char *p, *sync;
 +    char *map;
 +    char *allow_unused;
 +
 +    if (*arg == '-') {
 +        negative = 1;
 +        arg++;
 +    }
 +    map = av_strdup(arg);
 +    if (!map)
 +        return AVERROR(ENOMEM);
 +
 +    /* parse sync stream first, just pick first matching stream */
 +    if (sync = strchr(map, ',')) {
 +        *sync = 0;
 +        sync_file_idx = strtol(sync + 1, &sync, 0);
 +        if (sync_file_idx >= nb_input_files || sync_file_idx < 0) {
 +            av_log(NULL, AV_LOG_FATAL, "Invalid sync file index: %d.\n", sync_file_idx);
 +            exit_program(1);
 +        }
 +        if (*sync)
 +            sync++;
 +        for (i = 0; i < input_files[sync_file_idx]->nb_streams; i++)
 +            if (check_stream_specifier(input_files[sync_file_idx]->ctx,
 +                                       input_files[sync_file_idx]->ctx->streams[i], sync) == 1) {
 +                sync_stream_idx = i;
 +                break;
 +            }
 +        if (i == input_files[sync_file_idx]->nb_streams) {
 +            av_log(NULL, AV_LOG_FATAL, "Sync stream specification in map %s does not "
 +                                       "match any streams.\n", arg);
 +            exit_program(1);
 +        }
 +    }
 +
 +
 +    if (map[0] == '[') {
 +        /* this mapping refers to lavfi output */
 +        const char *c = map + 1;
 +        GROW_ARRAY(o->stream_maps, o->nb_stream_maps);
 +        m = &o->stream_maps[o->nb_stream_maps - 1];
 +        m->linklabel = av_get_token(&c, "]");
 +        if (!m->linklabel) {
 +            av_log(NULL, AV_LOG_ERROR, "Invalid output link label: %s.\n", map);
 +            exit_program(1);
 +        }
 +    } else {
 +        if (allow_unused = strchr(map, '?'))
 +            *allow_unused = 0;
 +        file_idx = strtol(map, &p, 0);
 +        if (file_idx >= nb_input_files || file_idx < 0) {
 +            av_log(NULL, AV_LOG_FATAL, "Invalid input file index: %d.\n", file_idx);
 +            exit_program(1);
 +        }
 +        if (negative)
 +            /* disable some already defined maps */
 +            for (i = 0; i < o->nb_stream_maps; i++) {
 +                m = &o->stream_maps[i];
 +                if (file_idx == m->file_index &&
 +                    check_stream_specifier(input_files[m->file_index]->ctx,
 +                                           input_files[m->file_index]->ctx->streams[m->stream_index],
 +                                           *p == ':' ? p + 1 : p) > 0)
 +                    m->disabled = 1;
 +            }
 +        else
 +            for (i = 0; i < input_files[file_idx]->nb_streams; i++) {
 +                if (check_stream_specifier(input_files[file_idx]->ctx, input_files[file_idx]->ctx->streams[i],
 +                            *p == ':' ? p + 1 : p) <= 0)
 +                    continue;
 +                GROW_ARRAY(o->stream_maps, o->nb_stream_maps);
 +                m = &o->stream_maps[o->nb_stream_maps - 1];
 +
 +                m->file_index   = file_idx;
 +                m->stream_index = i;
 +
 +                if (sync_file_idx >= 0) {
 +                    m->sync_file_index   = sync_file_idx;
 +                    m->sync_stream_index = sync_stream_idx;
 +                } else {
 +                    m->sync_file_index   = file_idx;
 +                    m->sync_stream_index = i;
 +                }
 +            }
 +    }
 +
 +    if (!m) {
 +        if (allow_unused) {
 +            av_log(NULL, AV_LOG_VERBOSE, "Stream map '%s' matches no streams; ignoring.\n", arg);
 +        } else {
 +            av_log(NULL, AV_LOG_FATAL, "Stream map '%s' matches no streams.\n"
 +                                       "To ignore this, add a trailing '?' to the map.\n", arg);
 +            exit_program(1);
 +        }
 +    }
 +
 +    av_freep(&map);
 +    return 0;
 +}
 +
 +static int opt_attach(void *optctx, const char *opt, const char *arg)
 +{
 +    OptionsContext *o = optctx;
 +    GROW_ARRAY(o->attachments, o->nb_attachments);
 +    o->attachments[o->nb_attachments - 1] = arg;
 +    return 0;
 +}
 +
 +static int opt_map_channel(void *optctx, const char *opt, const char *arg)
 +{
 +    OptionsContext *o = optctx;
 +    int n;
 +    AVStream *st;
 +    AudioChannelMap *m;
 +
 +    GROW_ARRAY(o->audio_channel_maps, o->nb_audio_channel_maps);
 +    m = &o->audio_channel_maps[o->nb_audio_channel_maps - 1];
 +
 +    /* muted channel syntax */
 +    n = sscanf(arg, "%d:%d.%d", &m->channel_idx, &m->ofile_idx, &m->ostream_idx);
 +    if ((n == 1 || n == 3) && m->channel_idx == -1) {
 +        m->file_idx = m->stream_idx = -1;
 +        if (n == 1)
 +            m->ofile_idx = m->ostream_idx = -1;
 +        return 0;
 +    }
 +
 +    /* normal syntax */
 +    n = sscanf(arg, "%d.%d.%d:%d.%d",
 +               &m->file_idx,  &m->stream_idx, &m->channel_idx,
 +               &m->ofile_idx, &m->ostream_idx);
 +
 +    if (n != 3 && n != 5) {
 +        av_log(NULL, AV_LOG_FATAL, "Syntax error, mapchan usage: "
 +               "[file.stream.channel|-1][:syncfile:syncstream]\n");
 +        exit_program(1);
 +    }
 +
 +    if (n != 5) // only file.stream.channel specified
 +        m->ofile_idx = m->ostream_idx = -1;
 +
 +    /* check input */
 +    if (m->file_idx < 0 || m->file_idx >= nb_input_files) {
 +        av_log(NULL, AV_LOG_FATAL, "mapchan: invalid input file index: %d\n",
 +               m->file_idx);
 +        exit_program(1);
 +    }
 +    if (m->stream_idx < 0 ||
 +        m->stream_idx >= input_files[m->file_idx]->nb_streams) {
 +        av_log(NULL, AV_LOG_FATAL, "mapchan: invalid input file stream index #%d.%d\n",
 +               m->file_idx, m->stream_idx);
 +        exit_program(1);
 +    }
 +    st = input_files[m->file_idx]->ctx->streams[m->stream_idx];
 +    if (st->codecpar->codec_type != AVMEDIA_TYPE_AUDIO) {
 +        av_log(NULL, AV_LOG_FATAL, "mapchan: stream #%d.%d is not an audio stream.\n",
 +               m->file_idx, m->stream_idx);
 +        exit_program(1);
 +    }
 +    if (m->channel_idx < 0 || m->channel_idx >= st->codecpar->channels) {
 +        av_log(NULL, AV_LOG_FATAL, "mapchan: invalid audio channel #%d.%d.%d\n",
 +               m->file_idx, m->stream_idx, m->channel_idx);
 +        exit_program(1);
 +    }
 +    return 0;
 +}
 +
 +static int opt_sdp_file(void *optctx, const char *opt, const char *arg)
 +{
 +    av_free(sdp_filename);
 +    sdp_filename = av_strdup(arg);
 +    return 0;
 +}
 +
 +#if CONFIG_VAAPI
 +static int opt_vaapi_device(void *optctx, const char *opt, const char *arg)
 +{
 +    int err;
 +    err = vaapi_device_init(arg);
 +    if (err < 0)
 +        exit_program(1);
 +    return 0;
 +}
 +#endif
 +
 +/**
 + * Parse a metadata specifier passed as 'arg' parameter.
 + * @param arg  metadata string to parse
 + * @param type metadata type is written here -- g(lobal)/s(tream)/c(hapter)/p(rogram)
 + * @param index for type c/p, chapter/program index is written here
 + * @param stream_spec for type s, the stream specifier is written here
 + */
 +static void parse_meta_type(char *arg, char *type, int *index, const char **stream_spec)
 +{
 +    if (*arg) {
 +        *type = *arg;
 +        switch (*arg) {
 +        case 'g':
 +            break;
 +        case 's':
 +            if (*(++arg) && *arg != ':') {
 +                av_log(NULL, AV_LOG_FATAL, "Invalid metadata specifier %s.\n", arg);
 +                exit_program(1);
 +            }
 +            *stream_spec = *arg == ':' ? arg + 1 : "";
 +            break;
 +        case 'c':
 +        case 'p':
 +            if (*(++arg) == ':')
 +                *index = strtol(++arg, NULL, 0);
 +            break;
 +        default:
 +            av_log(NULL, AV_LOG_FATAL, "Invalid metadata type %c.\n", *arg);
 +            exit_program(1);
 +        }
 +    } else
 +        *type = 'g';
 +}
 +
 +static int copy_metadata(char *outspec, char *inspec, AVFormatContext *oc, AVFormatContext *ic, OptionsContext *o)
 +{
 +    AVDictionary **meta_in = NULL;
 +    AVDictionary **meta_out = NULL;
 +    int i, ret = 0;
 +    char type_in, type_out;
 +    const char *istream_spec = NULL, *ostream_spec = NULL;
 +    int idx_in = 0, idx_out = 0;
 +
 +    parse_meta_type(inspec,  &type_in,  &idx_in,  &istream_spec);
 +    parse_meta_type(outspec, &type_out, &idx_out, &ostream_spec);
 +
 +    if (!ic) {
 +        if (type_out == 'g' || !*outspec)
 +            o->metadata_global_manual = 1;
 +        if (type_out == 's' || !*outspec)
 +            o->metadata_streams_manual = 1;
 +        if (type_out == 'c' || !*outspec)
 +            o->metadata_chapters_manual = 1;
 +        return 0;
 +    }
 +
 +    if (type_in == 'g' || type_out == 'g')
 +        o->metadata_global_manual = 1;
 +    if (type_in == 's' || type_out == 's')
 +        o->metadata_streams_manual = 1;
 +    if (type_in == 'c' || type_out == 'c')
 +        o->metadata_chapters_manual = 1;
 +
 +    /* ic is NULL when just disabling automatic mappings */
 +    if (!ic)
 +        return 0;
 +
 +#define METADATA_CHECK_INDEX(index, nb_elems, desc)\
 +    if ((index) < 0 || (index) >= (nb_elems)) {\
 +        av_log(NULL, AV_LOG_FATAL, "Invalid %s index %d while processing metadata maps.\n",\
 +                (desc), (index));\
 +        exit_program(1);\
 +    }
 +
 +#define SET_DICT(type, meta, context, index)\
 +        switch (type) {\
 +        case 'g':\
 +            meta = &context->metadata;\
 +            break;\
 +        case 'c':\
 +            METADATA_CHECK_INDEX(index, context->nb_chapters, "chapter")\
 +            meta = &context->chapters[index]->metadata;\
 +            break;\
 +        case 'p':\
 +            METADATA_CHECK_INDEX(index, context->nb_programs, "program")\
 +            meta = &context->programs[index]->metadata;\
 +            break;\
 +        case 's':\
 +            break; /* handled separately below */ \
 +        default: av_assert0(0);\
 +        }\
 +
 +    SET_DICT(type_in, meta_in, ic, idx_in);
 +    SET_DICT(type_out, meta_out, oc, idx_out);
 +
 +    /* for input streams choose first matching stream */
 +    if (type_in == 's') {
 +        for (i = 0; i < ic->nb_streams; i++) {
 +            if ((ret = check_stream_specifier(ic, ic->streams[i], istream_spec)) > 0) {
 +                meta_in = &ic->streams[i]->metadata;
 +                break;
 +            } else if (ret < 0)
 +                exit_program(1);
 +        }
 +        if (!meta_in) {
 +            av_log(NULL, AV_LOG_FATAL, "Stream specifier %s does not match  any streams.\n", istream_spec);
 +            exit_program(1);
 +        }
 +    }
 +
 +    if (type_out == 's') {
 +        for (i = 0; i < oc->nb_streams; i++) {
 +            if ((ret = check_stream_specifier(oc, oc->streams[i], ostream_spec)) > 0) {
 +                meta_out = &oc->streams[i]->metadata;
 +                av_dict_copy(meta_out, *meta_in, AV_DICT_DONT_OVERWRITE);
 +            } else if (ret < 0)
 +                exit_program(1);
 +        }
 +    } else
 +        av_dict_copy(meta_out, *meta_in, AV_DICT_DONT_OVERWRITE);
 +
 +    return 0;
 +}
 +
 +static int opt_recording_timestamp(void *optctx, const char *opt, const char *arg)
 +{
 +    OptionsContext *o = optctx;
 +    char buf[128];
 +    int64_t recording_timestamp = parse_time_or_die(opt, arg, 0) / 1E6;
 +    struct tm time = *gmtime((time_t*)&recording_timestamp);
 +    if (!strftime(buf, sizeof(buf), "creation_time=%Y-%m-%dT%H:%M:%S%z", &time))
 +        return -1;
 +    parse_option(o, "metadata", buf, options);
 +
 +    av_log(NULL, AV_LOG_WARNING, "%s is deprecated, set the 'creation_time' metadata "
 +                                 "tag instead.\n", opt);
 +    return 0;
 +}
 +
 +static AVCodec *find_codec_or_die(const char *name, enum AVMediaType type, int encoder)
 +{
 +    const AVCodecDescriptor *desc;
 +    const char *codec_string = encoder ? "encoder" : "decoder";
 +    AVCodec *codec;
 +
 +    codec = encoder ?
 +        avcodec_find_encoder_by_name(name) :
 +        avcodec_find_decoder_by_name(name);
 +
 +    if (!codec && (desc = avcodec_descriptor_get_by_name(name))) {
 +        codec = encoder ? avcodec_find_encoder(desc->id) :
 +                          avcodec_find_decoder(desc->id);
 +        if (codec)
 +            av_log(NULL, AV_LOG_VERBOSE, "Matched %s '%s' for codec '%s'.\n",
 +                   codec_string, codec->name, desc->name);
 +    }
 +
 +    if (!codec) {
 +        av_log(NULL, AV_LOG_FATAL, "Unknown %s '%s'\n", codec_string, name);
 +        exit_program(1);
 +    }
 +    if (codec->type != type) {
 +        av_log(NULL, AV_LOG_FATAL, "Invalid %s type '%s'\n", codec_string, name);
 +        exit_program(1);
 +    }
 +    return codec;
 +}
 +
 +static AVCodec *choose_decoder(OptionsContext *o, AVFormatContext *s, AVStream *st)
 +{
 +    char *codec_name = NULL;
 +
 +    MATCH_PER_STREAM_OPT(codec_names, str, codec_name, s, st);
 +    if (codec_name) {
 +        AVCodec *codec = find_codec_or_die(codec_name, st->codecpar->codec_type, 0);
 +        st->codecpar->codec_id = codec->id;
 +        return codec;
 +    } else
 +        return avcodec_find_decoder(st->codecpar->codec_id);
 +}
 +
 +/* Add all the streams from the given input file to the global
 + * list of input streams. */
 +static void add_input_streams(OptionsContext *o, AVFormatContext *ic)
 +{
 +    int i, ret;
 +
 +    for (i = 0; i < ic->nb_streams; i++) {
 +        AVStream *st = ic->streams[i];
 +        AVCodecParameters *par = st->codecpar;
 +        InputStream *ist = av_mallocz(sizeof(*ist));
 +        char *framerate = NULL, *hwaccel = NULL, *hwaccel_device = NULL;
 +        char *hwaccel_output_format = NULL;
 +        char *codec_tag = NULL;
 +        char *next;
 +        char *discard_str = NULL;
 +        const AVClass *cc = avcodec_get_class();
 +        const AVOption *discard_opt = av_opt_find(&cc, "skip_frame", NULL, 0, 0);
 +
 +        if (!ist)
 +            exit_program(1);
 +
 +        GROW_ARRAY(input_streams, nb_input_streams);
 +        input_streams[nb_input_streams - 1] = ist;
 +
 +        ist->st = st;
 +        ist->file_index = nb_input_files;
 +        ist->discard = 1;
 +        st->discard  = AVDISCARD_ALL;
 +        ist->nb_samples = 0;
 +        ist->min_pts = INT64_MAX;
 +        ist->max_pts = INT64_MIN;
 +
 +        ist->ts_scale = 1.0;
 +        MATCH_PER_STREAM_OPT(ts_scale, dbl, ist->ts_scale, ic, st);
 +
 +        ist->autorotate = 1;
 +        MATCH_PER_STREAM_OPT(autorotate, i, ist->autorotate, ic, st);
 +
 +        MATCH_PER_STREAM_OPT(codec_tags, str, codec_tag, ic, st);
 +        if (codec_tag) {
 +            uint32_t tag = strtol(codec_tag, &next, 0);
 +            if (*next)
 +                tag = AV_RL32(codec_tag);
 +            st->codecpar->codec_tag = tag;
 +        }
 +
 +        ist->dec = choose_decoder(o, ic, st);
 +        ist->decoder_opts = filter_codec_opts(o->g->codec_opts, ist->st->codecpar->codec_id, ic, st, ist->dec);
 +
 +        ist->reinit_filters = -1;
 +        MATCH_PER_STREAM_OPT(reinit_filters, i, ist->reinit_filters, ic, st);
 +
 +        MATCH_PER_STREAM_OPT(discard, str, discard_str, ic, st);
 +        ist->user_set_discard = AVDISCARD_NONE;
 +        if (discard_str && av_opt_eval_int(&cc, discard_opt, discard_str, &ist->user_set_discard) < 0) {
 +            av_log(NULL, AV_LOG_ERROR, "Error parsing discard %s.\n",
 +                    discard_str);
 +            exit_program(1);
 +        }
 +
 +        ist->filter_in_rescale_delta_last = AV_NOPTS_VALUE;
 +
 +        ist->dec_ctx = avcodec_alloc_context3(ist->dec);
 +        if (!ist->dec_ctx) {
 +            av_log(NULL, AV_LOG_ERROR, "Error allocating the decoder context.\n");
 +            exit_program(1);
 +        }
 +
 +        ret = avcodec_parameters_to_context(ist->dec_ctx, par);
 +        if (ret < 0) {
 +            av_log(NULL, AV_LOG_ERROR, "Error initializing the decoder context.\n");
 +            exit_program(1);
 +        }
 +
 +        switch (par->codec_type) {
 +        case AVMEDIA_TYPE_VIDEO:
 +            if(!ist->dec)
 +                ist->dec = avcodec_find_decoder(par->codec_id);
 +#if FF_API_EMU_EDGE
 +            if (av_codec_get_lowres(st->codec)) {
 +                av_codec_set_lowres(ist->dec_ctx, av_codec_get_lowres(st->codec));
 +                ist->dec_ctx->width  = st->codec->width;
 +                ist->dec_ctx->height = st->codec->height;
 +                ist->dec_ctx->coded_width  = st->codec->coded_width;
 +                ist->dec_ctx->coded_height = st->codec->coded_height;
 +                ist->dec_ctx->flags |= CODEC_FLAG_EMU_EDGE;
 +            }
 +#endif
 +
 +            // avformat_find_stream_info() doesn't set this for us anymore.
 +            ist->dec_ctx->framerate = st->avg_frame_rate;
 +
 +            MATCH_PER_STREAM_OPT(frame_rates, str, framerate, ic, st);
 +            if (framerate && av_parse_video_rate(&ist->framerate,
 +                                                 framerate) < 0) {
 +                av_log(NULL, AV_LOG_ERROR, "Error parsing framerate %s.\n",
 +                       framerate);
 +                exit_program(1);
 +            }
 +
 +            ist->top_field_first = -1;
 +            MATCH_PER_STREAM_OPT(top_field_first, i, ist->top_field_first, ic, st);
 +
 +            MATCH_PER_STREAM_OPT(hwaccels, str, hwaccel, ic, st);
 +            if (hwaccel) {
 +                if (!strcmp(hwaccel, "none"))
 +                    ist->hwaccel_id = HWACCEL_NONE;
 +                else if (!strcmp(hwaccel, "auto"))
 +                    ist->hwaccel_id = HWACCEL_AUTO;
 +                else {
 +                    int i;
 +                    for (i = 0; hwaccels[i].name; i++) {
 +                        if (!strcmp(hwaccels[i].name, hwaccel)) {
 +                            ist->hwaccel_id = hwaccels[i].id;
 +                            break;
 +                        }
 +                    }
 +
 +                    if (!ist->hwaccel_id) {
 +                        av_log(NULL, AV_LOG_FATAL, "Unrecognized hwaccel: %s.\n",
 +                               hwaccel);
 +                        av_log(NULL, AV_LOG_FATAL, "Supported hwaccels: ");
 +                        for (i = 0; hwaccels[i].name; i++)
 +                            av_log(NULL, AV_LOG_FATAL, "%s ", hwaccels[i].name);
 +                        av_log(NULL, AV_LOG_FATAL, "\n");
 +                        exit_program(1);
 +                    }
 +                }
 +            }
 +
 +            MATCH_PER_STREAM_OPT(hwaccel_devices, str, hwaccel_device, ic, st);
 +            if (hwaccel_device) {
 +                ist->hwaccel_device = av_strdup(hwaccel_device);
 +                if (!ist->hwaccel_device)
 +                    exit_program(1);
 +            }
 +
 +            MATCH_PER_STREAM_OPT(hwaccel_output_formats, str,
 +                                 hwaccel_output_format, ic, st);
 +            if (hwaccel_output_format) {
 +                ist->hwaccel_output_format = av_get_pix_fmt(hwaccel_output_format);
 +                if (ist->hwaccel_output_format == AV_PIX_FMT_NONE) {
 +                    av_log(NULL, AV_LOG_FATAL, "Unrecognised hwaccel output "
 +                           "format: %s", hwaccel_output_format);
 +                }
 +            } else {
 +                ist->hwaccel_output_format = AV_PIX_FMT_NONE;
 +            }
 +
 +            ist->hwaccel_pix_fmt = AV_PIX_FMT_NONE;
 +
 +            break;
 +        case AVMEDIA_TYPE_AUDIO:
 +            ist->guess_layout_max = INT_MAX;
 +            MATCH_PER_STREAM_OPT(guess_layout_max, i, ist->guess_layout_max, ic, st);
 +            guess_input_channel_layout(ist);
 +            break;
 +        case AVMEDIA_TYPE_DATA:
 +        case AVMEDIA_TYPE_SUBTITLE: {
 +            char *canvas_size = NULL;
 +            if(!ist->dec)
 +                ist->dec = avcodec_find_decoder(par->codec_id);
 +            MATCH_PER_STREAM_OPT(fix_sub_duration, i, ist->fix_sub_duration, ic, st);
 +            MATCH_PER_STREAM_OPT(canvas_sizes, str, canvas_size, ic, st);
 +            if (canvas_size &&
 +                av_parse_video_size(&ist->dec_ctx->width, &ist->dec_ctx->height, canvas_size) < 0) {
 +                av_log(NULL, AV_LOG_FATAL, "Invalid canvas size: %s.\n", canvas_size);
 +                exit_program(1);
 +            }
 +            break;
 +        }
 +        case AVMEDIA_TYPE_ATTACHMENT:
 +        case AVMEDIA_TYPE_UNKNOWN:
 +            break;
 +        default:
 +            abort();
 +        }
 +
 +        ret = avcodec_parameters_from_context(par, ist->dec_ctx);
 +        if (ret < 0) {
 +            av_log(NULL, AV_LOG_ERROR, "Error initializing the decoder context.\n");
 +            exit_program(1);
 +        }
 +    }
 +}
 +
 +static void assert_file_overwrite(const char *filename)
 +{
 +    if (file_overwrite && no_file_overwrite) {
 +        fprintf(stderr, "Error, both -y and -n supplied. Exiting.\n");
 +        exit_program(1);
 +    }
 +
 +    if (!file_overwrite) {
 +        const char *proto_name = avio_find_protocol_name(filename);
 +        if (proto_name && !strcmp(proto_name, "file") && avio_check(filename, 0) == 0) {
 +            if (stdin_interaction && !no_file_overwrite) {
 +                fprintf(stderr,"File '%s' already exists. Overwrite ? [y/N] ", filename);
 +                fflush(stderr);
 +                term_exit();
 +                signal(SIGINT, SIG_DFL);
 +                if (!read_yesno()) {
 +                    av_log(NULL, AV_LOG_FATAL, "Not overwriting - exiting\n");
 +                    exit_program(1);
 +                }
 +                term_init();
 +            }
 +            else {
 +                av_log(NULL, AV_LOG_FATAL, "File '%s' already exists. Exiting.\n", filename);
 +                exit_program(1);
 +            }
 +        }
 +    }
 +}
 +
 +static void dump_attachment(AVStream *st, const char *filename)
 +{
 +    int ret;
 +    AVIOContext *out = NULL;
 +    AVDictionaryEntry *e;
 +
 +    if (!st->codecpar->extradata_size) {
 +        av_log(NULL, AV_LOG_WARNING, "No extradata to dump in stream #%d:%d.\n",
 +               nb_input_files - 1, st->index);
 +        return;
 +    }
 +    if (!*filename && (e = av_dict_get(st->metadata, "filename", NULL, 0)))
 +        filename = e->value;
 +    if (!*filename) {
 +        av_log(NULL, AV_LOG_FATAL, "No filename specified and no 'filename' tag"
 +               "in stream #%d:%d.\n", nb_input_files - 1, st->index);
 +        exit_program(1);
 +    }
 +
 +    assert_file_overwrite(filename);
 +
 +    if ((ret = avio_open2(&out, filename, AVIO_FLAG_WRITE, &int_cb, NULL)) < 0) {
 +        av_log(NULL, AV_LOG_FATAL, "Could not open file %s for writing.\n",
 +               filename);
 +        exit_program(1);
 +    }
 +
 +    avio_write(out, st->codecpar->extradata, st->codecpar->extradata_size);
 +    avio_flush(out);
 +    avio_close(out);
 +}
 +
 +static int open_input_file(OptionsContext *o, const char *filename)
 +{
 +    InputFile *f;
 +    AVFormatContext *ic;
 +    AVInputFormat *file_iformat = NULL;
 +    int err, i, ret;
 +    int64_t timestamp;
 +    AVDictionary **opts;
 +    AVDictionary *unused_opts = NULL;
 +    AVDictionaryEntry *e = NULL;
 +    int orig_nb_streams;                     // number of streams before avformat_find_stream_info
 +    char *   video_codec_name = NULL;
 +    char *   audio_codec_name = NULL;
 +    char *subtitle_codec_name = NULL;
 +    char *    data_codec_name = NULL;
 +    int scan_all_pmts_set = 0;
 +
 +    if (o->format) {
 +        if (!(file_iformat = av_find_input_format(o->format))) {
 +            av_log(NULL, AV_LOG_FATAL, "Unknown input format: '%s'\n", o->format);
 +            exit_program(1);
 +        }
 +    }
 +
 +    if (!strcmp(filename, "-"))
 +        filename = "pipe:";
 +
 +    stdin_interaction &= strncmp(filename, "pipe:", 5) &&
 +                         strcmp(filename, "/dev/stdin");
 +
 +    /* get default parameters from command line */
 +    ic = avformat_alloc_context();
 +    if (!ic) {
 +        print_error(filename, AVERROR(ENOMEM));
 +        exit_program(1);
 +    }
 +    ic->flags |= AVFMT_FLAG_KEEP_SIDE_DATA;
 +    if (o->nb_audio_sample_rate) {
 +        av_dict_set_int(&o->g->format_opts, "sample_rate", o->audio_sample_rate[o->nb_audio_sample_rate - 1].u.i, 0);
 +    }
 +    if (o->nb_audio_channels) {
 +        /* because we set audio_channels based on both the "ac" and
 +         * "channel_layout" options, we need to check that the specified
 +         * demuxer actually has the "channels" option before setting it */
 +        if (file_iformat && file_iformat->priv_class &&
 +            av_opt_find(&file_iformat->priv_class, "channels", NULL, 0,
 +                        AV_OPT_SEARCH_FAKE_OBJ)) {
 +            av_dict_set_int(&o->g->format_opts, "channels", o->audio_channels[o->nb_audio_channels - 1].u.i, 0);
 +        }
 +    }
 +    if (o->nb_frame_rates) {
 +        /* set the format-level framerate option;
 +         * this is important for video grabbers, e.g. x11 */
 +        if (file_iformat && file_iformat->priv_class &&
 +            av_opt_find(&file_iformat->priv_class, "framerate", NULL, 0,
 +                        AV_OPT_SEARCH_FAKE_OBJ)) {
 +            av_dict_set(&o->g->format_opts, "framerate",
 +                        o->frame_rates[o->nb_frame_rates - 1].u.str, 0);
 +        }
 +    }
 +    if (o->nb_frame_sizes) {
 +        av_dict_set(&o->g->format_opts, "video_size", o->frame_sizes[o->nb_frame_sizes - 1].u.str, 0);
 +    }
 +    if (o->nb_frame_pix_fmts)
 +        av_dict_set(&o->g->format_opts, "pixel_format", o->frame_pix_fmts[o->nb_frame_pix_fmts - 1].u.str, 0);
 +
 +    MATCH_PER_TYPE_OPT(codec_names, str,    video_codec_name, ic, "v");
 +    MATCH_PER_TYPE_OPT(codec_names, str,    audio_codec_name, ic, "a");
 +    MATCH_PER_TYPE_OPT(codec_names, str, subtitle_codec_name, ic, "s");
 +    MATCH_PER_TYPE_OPT(codec_names, str,     data_codec_name, ic, "d");
 +
 +    ic->video_codec_id   = video_codec_name ?
 +        find_codec_or_die(video_codec_name   , AVMEDIA_TYPE_VIDEO   , 0)->id : AV_CODEC_ID_NONE;
 +    ic->audio_codec_id   = audio_codec_name ?
 +        find_codec_or_die(audio_codec_name   , AVMEDIA_TYPE_AUDIO   , 0)->id : AV_CODEC_ID_NONE;
 +    ic->subtitle_codec_id= subtitle_codec_name ?
 +        find_codec_or_die(subtitle_codec_name, AVMEDIA_TYPE_SUBTITLE, 0)->id : AV_CODEC_ID_NONE;
 +    ic->data_codec_id    = data_codec_name ?
 +        find_codec_or_die(data_codec_name, AVMEDIA_TYPE_DATA, 0)->id : AV_CODEC_ID_NONE;
 +
 +    if (video_codec_name)
 +        av_format_set_video_codec   (ic, find_codec_or_die(video_codec_name   , AVMEDIA_TYPE_VIDEO   , 0));
 +    if (audio_codec_name)
 +        av_format_set_audio_codec   (ic, find_codec_or_die(audio_codec_name   , AVMEDIA_TYPE_AUDIO   , 0));
 +    if (subtitle_codec_name)
 +        av_format_set_subtitle_codec(ic, find_codec_or_die(subtitle_codec_name, AVMEDIA_TYPE_SUBTITLE, 0));
 +    if (data_codec_name)
 +        av_format_set_data_codec(ic, find_codec_or_die(data_codec_name, AVMEDIA_TYPE_DATA, 0));
 +
 +    ic->flags |= AVFMT_FLAG_NONBLOCK;
 +    ic->interrupt_callback = int_cb;
 +
 +    if (!av_dict_get(o->g->format_opts, "scan_all_pmts", NULL, AV_DICT_MATCH_CASE)) {
 +        av_dict_set(&o->g->format_opts, "scan_all_pmts", "1", AV_DICT_DONT_OVERWRITE);
 +        scan_all_pmts_set = 1;
 +    }
 +    /* open the input file with generic avformat function */
 +    err = avformat_open_input(&ic, filename, file_iformat, &o->g->format_opts);
 +    if (err < 0) {
 +        print_error(filename, err);
 +        if (err == AVERROR_PROTOCOL_NOT_FOUND)
 +            av_log(NULL, AV_LOG_ERROR, "Did you mean file:%s?\n", filename);
 +        exit_program(1);
 +    }
 +    if (scan_all_pmts_set)
 +        av_dict_set(&o->g->format_opts, "scan_all_pmts", NULL, AV_DICT_MATCH_CASE);
 +    remove_avoptions(&o->g->format_opts, o->g->codec_opts);
 +    assert_avoptions(o->g->format_opts);
 +
 +    /* apply forced codec ids */
 +    for (i = 0; i < ic->nb_streams; i++)
 +        choose_decoder(o, ic, ic->streams[i]);
 +
 +    /* Set AVCodecContext options for avformat_find_stream_info */
 +    opts = setup_find_stream_info_opts(ic, o->g->codec_opts);
 +    orig_nb_streams = ic->nb_streams;
 +
 +    /* If not enough info to get the stream parameters, we decode the
 +       first frames to get it. (used in mpeg case for example) */
 +    ret = avformat_find_stream_info(ic, opts);
 +    if (ret < 0) {
 +        av_log(NULL, AV_LOG_FATAL, "%s: could not find codec parameters\n", filename);
 +        if (ic->nb_streams == 0) {
 +            avformat_close_input(&ic);
 +            exit_program(1);
 +        }
 +    }
 +
 +    if (o->start_time_eof != AV_NOPTS_VALUE) {
 +        if (ic->duration>0) {
 +            o->start_time = o->start_time_eof + ic->duration;
 +        } else
 +            av_log(NULL, AV_LOG_WARNING, "Cannot use -sseof, duration of %s not known\n", filename);
 +    }
 +    timestamp = (o->start_time == AV_NOPTS_VALUE) ? 0 : o->start_time;
 +    /* add the stream start time */
 +    if (!o->seek_timestamp && ic->start_time != AV_NOPTS_VALUE)
 +        timestamp += ic->start_time;
 +
 +    /* if seeking requested, we execute it */
 +    if (o->start_time != AV_NOPTS_VALUE) {
 +        int64_t seek_timestamp = timestamp;
 +
 +        if (!(ic->iformat->flags & AVFMT_SEEK_TO_PTS)) {
 +            int dts_heuristic = 0;
 +            for (i=0; i<ic->nb_streams; i++) {
 +                const AVCodecParameters *par = ic->streams[i]->codecpar;
 +                if (par->video_delay)
 +                    dts_heuristic = 1;
 +            }
 +            if (dts_heuristic) {
 +                seek_timestamp -= 3*AV_TIME_BASE / 23;
 +            }
 +        }
 +        ret = avformat_seek_file(ic, -1, INT64_MIN, seek_timestamp, seek_timestamp, 0);
 +        if (ret < 0) {
 +            av_log(NULL, AV_LOG_WARNING, "%s: could not seek to position %0.3f\n",
 +                   filename, (double)timestamp / AV_TIME_BASE);
 +        }
 +    }
 +
 +    /* update the current parameters so that they match the one of the input stream */
 +    add_input_streams(o, ic);
 +
 +    /* dump the file content */
 +    av_dump_format(ic, nb_input_files, filename, 0);
 +
 +    GROW_ARRAY(input_files, nb_input_files);
 +    f = av_mallocz(sizeof(*f));
 +    if (!f)
 +        exit_program(1);
 +    input_files[nb_input_files - 1] = f;
 +
 +    f->ctx        = ic;
 +    f->ist_index  = nb_input_streams - ic->nb_streams;
 +    f->start_time = o->start_time;
 +    f->recording_time = o->recording_time;
 +    f->input_ts_offset = o->input_ts_offset;
 +    f->ts_offset  = o->input_ts_offset - (copy_ts ? (start_at_zero && ic->start_time != AV_NOPTS_VALUE ? ic->start_time : 0) : timestamp);
 +    f->nb_streams = ic->nb_streams;
 +    f->rate_emu   = o->rate_emu;
 +    f->accurate_seek = o->accurate_seek;
 +    f->loop = o->loop;
 +    f->duration = 0;
 +    f->time_base = (AVRational){ 1, 1 };
 +#if HAVE_PTHREADS
 +    f->thread_queue_size = o->thread_queue_size > 0 ? o->thread_queue_size : 8;
 +#endif
 +
 +    /* check if all codec options have been used */
 +    unused_opts = strip_specifiers(o->g->codec_opts);
 +    for (i = f->ist_index; i < nb_input_streams; i++) {
 +        e = NULL;
 +        while ((e = av_dict_get(input_streams[i]->decoder_opts, "", e,
 +                                AV_DICT_IGNORE_SUFFIX)))
 +            av_dict_set(&unused_opts, e->key, NULL, 0);
 +    }
 +
 +    e = NULL;
 +    while ((e = av_dict_get(unused_opts, "", e, AV_DICT_IGNORE_SUFFIX))) {
 +        const AVClass *class = avcodec_get_class();
 +        const AVOption *option = av_opt_find(&class, e->key, NULL, 0,
 +                                             AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ);
 +        const AVClass *fclass = avformat_get_class();
 +        const AVOption *foption = av_opt_find(&fclass, e->key, NULL, 0,
 +                                             AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ);
 +        if (!option || foption)
 +            continue;
 +
 +
 +        if (!(option->flags & AV_OPT_FLAG_DECODING_PARAM)) {
 +            av_log(NULL, AV_LOG_ERROR, "Codec AVOption %s (%s) specified for "
 +                   "input file #%d (%s) is not a decoding option.\n", e->key,
 +                   option->help ? option->help : "", nb_input_files - 1,
 +                   filename);
 +            exit_program(1);
 +        }
 +
 +        av_log(NULL, AV_LOG_WARNING, "Codec AVOption %s (%s) specified for "
 +               "input file #%d (%s) has not been used for any stream. The most "
 +               "likely reason is either wrong type (e.g. a video option with "
 +               "no video streams) or that it is a private option of some decoder "
 +               "which was not actually used for any stream.\n", e->key,
 +               option->help ? option->help : "", nb_input_files - 1, filename);
 +    }
 +    av_dict_free(&unused_opts);
 +
 +    for (i = 0; i < o->nb_dump_attachment; i++) {
 +        int j;
 +
 +        for (j = 0; j < ic->nb_streams; j++) {
 +            AVStream *st = ic->streams[j];
 +
 +            if (check_stream_specifier(ic, st, o->dump_attachment[i].specifier) == 1)
 +                dump_attachment(st, o->dump_attachment[i].u.str);
 +        }
 +    }
 +
 +    for (i = 0; i < orig_nb_streams; i++)
 +        av_dict_free(&opts[i]);
 +    av_freep(&opts);
 +
 +    input_stream_potentially_available = 1;
 +
 +    return 0;
 +}
 +
 +static uint8_t *get_line(AVIOContext *s)
 +{
 +    AVIOContext *line;
 +    uint8_t *buf;
 +    char c;
 +
 +    if (avio_open_dyn_buf(&line) < 0) {
 +        av_log(NULL, AV_LOG_FATAL, "Could not alloc buffer for reading preset.\n");
 +        exit_program(1);
 +    }
 +
 +    while ((c = avio_r8(s)) && c != '\n')
 +        avio_w8(line, c);
 +    avio_w8(line, 0);
 +    avio_close_dyn_buf(line, &buf);
 +
 +    return buf;
 +}
 +
 +static int get_preset_file_2(const char *preset_name, const char *codec_name, AVIOContext **s)
 +{
 +    int i, ret = -1;
 +    char filename[1000];
 +    const char *base[3] = { getenv("AVCONV_DATADIR"),
 +                            getenv("HOME"),
 +                            AVCONV_DATADIR,
 +                            };
 +
 +    for (i = 0; i < FF_ARRAY_ELEMS(base) && ret < 0; i++) {
 +        if (!base[i])
 +            continue;
 +        if (codec_name) {
 +            snprintf(filename, sizeof(filename), "%s%s/%s-%s.avpreset", base[i],
 +                     i != 1 ? "" : "/.avconv", codec_name, preset_name);
 +            ret = avio_open2(s, filename, AVIO_FLAG_READ, &int_cb, NULL);
 +        }
 +        if (ret < 0) {
 +            snprintf(filename, sizeof(filename), "%s%s/%s.avpreset", base[i],
 +                     i != 1 ? "" : "/.avconv", preset_name);
 +            ret = avio_open2(s, filename, AVIO_FLAG_READ, &int_cb, NULL);
 +        }
 +    }
 +    return ret;
 +}
 +
 +static int choose_encoder(OptionsContext *o, AVFormatContext *s, OutputStream *ost)
 +{
 +    enum AVMediaType type = ost->st->codecpar->codec_type;
 +    char *codec_name = NULL;
 +
 +    if (type == AVMEDIA_TYPE_VIDEO || type == AVMEDIA_TYPE_AUDIO || type == AVMEDIA_TYPE_SUBTITLE) {
 +        MATCH_PER_STREAM_OPT(codec_names, str, codec_name, s, ost->st);
 +        if (!codec_name) {
 +            ost->st->codecpar->codec_id = av_guess_codec(s->oformat, NULL, s->filename,
 +                                                         NULL, ost->st->codecpar->codec_type);
 +            ost->enc = avcodec_find_encoder(ost->st->codecpar->codec_id);
 +            if (!ost->enc) {
 +                av_log(NULL, AV_LOG_FATAL, "Automatic encoder selection failed for "
 +                       "output stream #%d:%d. Default encoder for format %s (codec %s) is "
 +                       "probably disabled. Please choose an encoder manually.\n",
 +                       ost->file_index, ost->index, s->oformat->name,
 +                       avcodec_get_name(ost->st->codecpar->codec_id));
 +                return AVERROR_ENCODER_NOT_FOUND;
 +            }
 +        } else if (!strcmp(codec_name, "copy"))
 +            ost->stream_copy = 1;
 +        else {
 +            ost->enc = find_codec_or_die(codec_name, ost->st->codecpar->codec_type, 1);
 +            ost->st->codecpar->codec_id = ost->enc->id;
 +        }
 +        ost->encoding_needed = !ost->stream_copy;
 +    } else {
 +        /* no encoding supported for other media types */
 +        ost->stream_copy     = 1;
 +        ost->encoding_needed = 0;
 +    }
 +
 +    return 0;
 +}
 +
 +static OutputStream *new_output_stream(OptionsContext *o, AVFormatContext *oc, enum AVMediaType type, int source_index)
 +{
 +    OutputStream *ost;
 +    AVStream *st = avformat_new_stream(oc, NULL);
 +    int idx      = oc->nb_streams - 1, ret = 0;
 +    const char *bsfs = NULL, *time_base = NULL;
 +    char *next, *codec_tag = NULL;
 +    double qscale = -1;
 +    int i;
 +
 +    if (!st) {
 +        av_log(NULL, AV_LOG_FATAL, "Could not alloc stream.\n");
 +        exit_program(1);
 +    }
 +
 +    if (oc->nb_streams - 1 < o->nb_streamid_map)
 +        st->id = o->streamid_map[oc->nb_streams - 1];
 +
 +    GROW_ARRAY(output_streams, nb_output_streams);
 +    if (!(ost = av_mallocz(sizeof(*ost))))
 +        exit_program(1);
 +    output_streams[nb_output_streams - 1] = ost;
 +
 +    ost->file_index = nb_output_files - 1;
 +    ost->index      = idx;
 +    ost->st         = st;
 +    st->codecpar->codec_type = type;
 +
 +    ret = choose_encoder(o, oc, ost);
 +    if (ret < 0) {
 +        av_log(NULL, AV_LOG_FATAL, "Error selecting an encoder for stream "
 +               "%d:%d\n", ost->file_index, ost->index);
 +        exit_program(1);
 +    }
 +
 +    ost->enc_ctx = avcodec_alloc_context3(ost->enc);
 +    if (!ost->enc_ctx) {
 +        av_log(NULL, AV_LOG_ERROR, "Error allocating the encoding context.\n");
 +        exit_program(1);
 +    }
 +    ost->enc_ctx->codec_type = type;
 +
 +    ost->ref_par = avcodec_parameters_alloc();
 +    if (!ost->ref_par) {
 +        av_log(NULL, AV_LOG_ERROR, "Error allocating the encoding parameters.\n");
 +        exit_program(1);
 +    }
 +
 +    if (ost->enc) {
 +        AVIOContext *s = NULL;
 +        char *buf = NULL, *arg = NULL, *preset = NULL;
 +
 +        ost->encoder_opts  = filter_codec_opts(o->g->codec_opts, ost->enc->id, oc, st, ost->enc);
 +
 +        MATCH_PER_STREAM_OPT(presets, str, preset, oc, st);
 +        if (preset && (!(ret = get_preset_file_2(preset, ost->enc->name, &s)))) {
 +            do  {
 +                buf = get_line(s);
 +                if (!buf[0] || buf[0] == '#') {
 +                    av_free(buf);
 +                    continue;
 +                }
 +                if (!(arg = strchr(buf, '='))) {
 +                    av_log(NULL, AV_LOG_FATAL, "Invalid line found in the preset file.\n");
 +                    exit_program(1);
 +                }
 +                *arg++ = 0;
 +                av_dict_set(&ost->encoder_opts, buf, arg, AV_DICT_DONT_OVERWRITE);
 +                av_free(buf);
 +            } while (!s->eof_reached);
 +            avio_closep(&s);
 +        }
 +        if (ret) {
 +            av_log(NULL, AV_LOG_FATAL,
 +                   "Preset %s specified for stream %d:%d, but could not be opened.\n",
 +                   preset, ost->file_index, ost->index);
 +            exit_program(1);
 +        }
 +    } else {
 +        ost->encoder_opts = filter_codec_opts(o->g->codec_opts, AV_CODEC_ID_NONE, oc, st, NULL);
 +    }
 +
 +    MATCH_PER_STREAM_OPT(time_bases, str, time_base, oc, st);
 +    if (time_base) {
 +        AVRational q;
 +        if (av_parse_ratio(&q, time_base, INT_MAX, 0, NULL) < 0 ||
 +            q.num <= 0 || q.den <= 0) {
 +            av_log(NULL, AV_LOG_FATAL, "Invalid time base: %s\n", time_base);
 +            exit_program(1);
 +        }
 +        st->time_base = q;
 +    }
 +
 +    ost->max_frames = INT64_MAX;
 +    MATCH_PER_STREAM_OPT(max_frames, i64, ost->max_frames, oc, st);
 +    for (i = 0; i<o->nb_max_frames; i++) {
 +        char *p = o->max_frames[i].specifier;
 +        if (!*p && type != AVMEDIA_TYPE_VIDEO) {
 +            av_log(NULL, AV_LOG_WARNING, "Applying unspecific -frames to non video streams, maybe you meant -vframes ?\n");
 +            break;
 +        }
 +    }
 +
 +    ost->copy_prior_start = -1;
 +    MATCH_PER_STREAM_OPT(copy_prior_start, i, ost->copy_prior_start, oc ,st);
 +
 +    MATCH_PER_STREAM_OPT(bitstream_filters, str, bsfs, oc, st);
 +    while (bsfs && *bsfs) {
 +        const AVBitStreamFilter *filter;
 +        char *bsf, *bsf_options_str, *bsf_name;
 +
 +        bsf = av_get_token(&bsfs, ",");
 +        if (!bsf)
 +            exit_program(1);
 +        bsf_name = av_strtok(bsf, "=", &bsf_options_str);
 +        if (!bsf_name)
 +            exit_program(1);
 +
 +        filter = av_bsf_get_by_name(bsf_name);
 +        if (!filter) {
 +            av_log(NULL, AV_LOG_FATAL, "Unknown bitstream filter %s\n", bsf_name);
 +            exit_program(1);
 +        }
 +
 +        ost->bsf_ctx = av_realloc_array(ost->bsf_ctx,
 +                                        ost->nb_bitstream_filters + 1,
 +                                        sizeof(*ost->bsf_ctx));
 +        if (!ost->bsf_ctx)
 +            exit_program(1);
 +
 +        ret = av_bsf_alloc(filter, &ost->bsf_ctx[ost->nb_bitstream_filters]);
 +        if (ret < 0) {
 +            av_log(NULL, AV_LOG_ERROR, "Error allocating a bitstream filter context\n");
 +            exit_program(1);
 +        }
 +
 +        ost->nb_bitstream_filters++;
 +
 +        if (bsf_options_str && filter->priv_class) {
 +            const AVOption *opt = av_opt_next(ost->bsf_ctx[ost->nb_bitstream_filters-1]->priv_data, NULL);
 +            const char * shorthand[2] = {NULL};
 +
 +            if (opt)
 +                shorthand[0] = opt->name;
 +
 +            ret = av_opt_set_from_string(ost->bsf_ctx[ost->nb_bitstream_filters-1]->priv_data, bsf_options_str, shorthand, "=", ":");
 +            if (ret < 0) {
 +                av_log(NULL, AV_LOG_ERROR, "Error parsing options for bitstream filter %s\n", bsf_name);
 +                exit_program(1);
 +            }
 +        }
 +        av_freep(&bsf);
 +
 +        if (*bsfs)
 +            bsfs++;
 +    }
 +    if (ost->nb_bitstream_filters) {
 +        ost->bsf_extradata_updated = av_mallocz_array(ost->nb_bitstream_filters, sizeof(*ost->bsf_extradata_updated));
 +        if (!ost->bsf_extradata_updated) {
 +            av_log(NULL, AV_LOG_FATAL, "Bitstream filter memory allocation failed\n");
 +            exit_program(1);
 +        }
 +    }
 +
 +    MATCH_PER_STREAM_OPT(codec_tags, str, codec_tag, oc, st);
 +    if (codec_tag) {
 +        uint32_t tag = strtol(codec_tag, &next, 0);
 +        if (*next)
 +            tag = AV_RL32(codec_tag);
 +        ost->st->codecpar->codec_tag =
 +        ost->enc_ctx->codec_tag = tag;
 +    }
 +
 +    MATCH_PER_STREAM_OPT(qscale, dbl, qscale, oc, st);
 +    if (qscale >= 0) {
 +        ost->enc_ctx->flags |= AV_CODEC_FLAG_QSCALE;
 +        ost->enc_ctx->global_quality = FF_QP2LAMBDA * qscale;
 +    }
 +
 +    MATCH_PER_STREAM_OPT(disposition, str, ost->disposition, oc, st);
 +    ost->disposition = av_strdup(ost->disposition);
 +
 +    ost->max_muxing_queue_size = 128;
 +    MATCH_PER_STREAM_OPT(max_muxing_queue_size, i, ost->max_muxing_queue_size, oc, st);
 +    ost->max_muxing_queue_size *= sizeof(AVPacket);
 +
 +    if (oc->oformat->flags & AVFMT_GLOBALHEADER)
 +        ost->enc_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
 +
 +    av_dict_copy(&ost->sws_dict, o->g->sws_dict, 0);
 +
 +    av_dict_copy(&ost->swr_opts, o->g->swr_opts, 0);
 +    if (ost->enc && av_get_exact_bits_per_sample(ost->enc->id) == 24)
 +        av_dict_set(&ost->swr_opts, "output_sample_bits", "24", 0);
 +
 +    av_dict_copy(&ost->resample_opts, o->g->resample_opts, 0);
 +
 +    ost->source_index = source_index;
 +    if (source_index >= 0) {
 +        ost->sync_ist = input_streams[source_index];
 +        input_streams[source_index]->discard = 0;
 +        input_streams[source_index]->st->discard = input_streams[source_index]->user_set_discard;
 +    }
 +    ost->last_mux_dts = AV_NOPTS_VALUE;
 +
 +    ost->muxing_queue = av_fifo_alloc(8 * sizeof(AVPacket));
 +    if (!ost->muxing_queue)
 +        exit_program(1);
 +
 +    return ost;
 +}
 +
 +static void parse_matrix_coeffs(uint16_t *dest, const char *str)
 +{
 +    int i;
 +    const char *p = str;
 +    for (i = 0;; i++) {
 +        dest[i] = atoi(p);
 +        if (i == 63)
 +            break;
 +        p = strchr(p, ',');
 +        if (!p) {
 +            av_log(NULL, AV_LOG_FATAL, "Syntax error in matrix \"%s\" at coeff %d\n", str, i);
 +            exit_program(1);
 +        }
 +        p++;
 +    }
 +}
 +
 +/* read file contents into a string */
 +static uint8_t *read_file(const char *filename)
 +{
 +    AVIOContext *pb      = NULL;
 +    AVIOContext *dyn_buf = NULL;
 +    int ret = avio_open(&pb, filename, AVIO_FLAG_READ);
 +    uint8_t buf[1024], *str;
 +
 +    if (ret < 0) {
 +        av_log(NULL, AV_LOG_ERROR, "Error opening file %s.\n", filename);
 +        return NULL;
 +    }
 +
 +    ret = avio_open_dyn_buf(&dyn_buf);
 +    if (ret < 0) {
 +        avio_closep(&pb);
 +        return NULL;
 +    }
 +    while ((ret = avio_read(pb, buf, sizeof(buf))) > 0)
 +        avio_write(dyn_buf, buf, ret);
 +    avio_w8(dyn_buf, 0);
 +    avio_closep(&pb);
 +
 +    ret = avio_close_dyn_buf(dyn_buf, &str);
 +    if (ret < 0)
 +        return NULL;
 +    return str;
 +}
 +
 +static char *get_ost_filters(OptionsContext *o, AVFormatContext *oc,
 +                             OutputStream *ost)
 +{
 +    AVStream *st = ost->st;
 +
 +    if (ost->filters_script && ost->filters) {
 +        av_log(NULL, AV_LOG_ERROR, "Both -filter and -filter_script set for "
 +               "output stream #%d:%d.\n", nb_output_files, st->index);
 +        exit_program(1);
 +    }
 +
 +    if (ost->filters_script)
 +        return read_file(ost->filters_script);
 +    else if (ost->filters)
 +        return av_strdup(ost->filters);
 +
 +    return av_strdup(st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ?
 +                     "null" : "anull");
 +}
 +
 +static void check_streamcopy_filters(OptionsContext *o, AVFormatContext *oc,
 +                                     const OutputStream *ost, enum AVMediaType type)
 +{
 +    if (ost->filters_script || ost->filters) {
 +        av_log(NULL, AV_LOG_ERROR,
 +               "%s '%s' was defined for %s output stream %d:%d but codec copy was selected.\n"
 +               "Filtering and streamcopy cannot be used together.\n",
 +               ost->filters ? "Filtergraph" : "Filtergraph script",
 +               ost->filters ? ost->filters : ost->filters_script,
 +               av_get_media_type_string(type), ost->file_index, ost->index);
 +        exit_program(1);
 +    }
 +}
 +
 +static OutputStream *new_video_stream(OptionsContext *o, AVFormatContext *oc, int source_index)
 +{
 +    AVStream *st;
 +    OutputStream *ost;
 +    AVCodecContext *video_enc;
 +    char *frame_rate = NULL, *frame_aspect_ratio = NULL;
 +
 +    ost = new_output_stream(o, oc, AVMEDIA_TYPE_VIDEO, source_index);
 +    st  = ost->st;
 +    video_enc = ost->enc_ctx;
 +
 +    MATCH_PER_STREAM_OPT(frame_rates, str, frame_rate, oc, st);
 +    if (frame_rate && av_parse_video_rate(&ost->frame_rate, frame_rate) < 0) {
 +        av_log(NULL, AV_LOG_FATAL, "Invalid framerate value: %s\n", frame_rate);
 +        exit_program(1);
 +    }
 +    if (frame_rate && video_sync_method == VSYNC_PASSTHROUGH)
 +        av_log(NULL, AV_LOG_ERROR, "Using -vsync 0 and -r can produce invalid output files\n");
 +
 +    MATCH_PER_STREAM_OPT(frame_aspect_ratios, str, frame_aspect_ratio, oc, st);
 +    if (frame_aspect_ratio) {
 +        AVRational q;
 +        if (av_parse_ratio(&q, frame_aspect_ratio, 255, 0, NULL) < 0 ||
 +            q.num <= 0 || q.den <= 0) {
 +            av_log(NULL, AV_LOG_FATAL, "Invalid aspect ratio: %s\n", frame_aspect_ratio);
 +            exit_program(1);
 +        }
 +        ost->frame_aspect_ratio = q;
 +    }
 +
 +    MATCH_PER_STREAM_OPT(filter_scripts, str, ost->filters_script, oc, st);
 +    MATCH_PER_STREAM_OPT(filters,        str, ost->filters,        oc, st);
 +
 +    if (!ost->stream_copy) {
 +        const char *p = NULL;
 +        char *frame_size = NULL;
 +        char *frame_pix_fmt = NULL;
 +        char *intra_matrix = NULL, *inter_matrix = NULL;
 +        char *chroma_intra_matrix = NULL;
 +        int do_pass = 0;
 +        int i;
 +
 +        MATCH_PER_STREAM_OPT(frame_sizes, str, frame_size, oc, st);
 +        if (frame_size && av_parse_video_size(&video_enc->width, &video_enc->height, frame_size) < 0) {
 +            av_log(NULL, AV_LOG_FATAL, "Invalid frame size: %s.\n", frame_size);
 +            exit_program(1);
 +        }
 +
 +        video_enc->bits_per_raw_sample = frame_bits_per_raw_sample;
 +        MATCH_PER_STREAM_OPT(frame_pix_fmts, str, frame_pix_fmt, oc, st);
 +        if (frame_pix_fmt && *frame_pix_fmt == '+') {
 +            ost->keep_pix_fmt = 1;
 +            if (!*++frame_pix_fmt)
 +                frame_pix_fmt = NULL;
 +        }
 +        if (frame_pix_fmt && (video_enc->pix_fmt = av_get_pix_fmt(frame_pix_fmt)) == AV_PIX_FMT_NONE) {
 +            av_log(NULL, AV_LOG_FATAL, "Unknown pixel format requested: %s.\n", frame_pix_fmt);
 +            exit_program(1);
 +        }
 +        st->sample_aspect_ratio = video_enc->sample_aspect_ratio;
 +
 +        if (intra_only)
 +            video_enc->gop_size = 0;
 +        MATCH_PER_STREAM_OPT(intra_matrices, str, intra_matrix, oc, st);
 +        if (intra_matrix) {
 +            if (!(video_enc->intra_matrix = av_mallocz(sizeof(*video_enc->intra_matrix) * 64))) {
 +                av_log(NULL, AV_LOG_FATAL, "Could not allocate memory for intra matrix.\n");
 +                exit_program(1);
 +            }
 +            parse_matrix_coeffs(video_enc->intra_matrix, intra_matrix);
 +        }
 +        MATCH_PER_STREAM_OPT(chroma_intra_matrices, str, chroma_intra_matrix, oc, st);
 +        if (chroma_intra_matrix) {
 +            uint16_t *p = av_mallocz(sizeof(*video_enc->chroma_intra_matrix) * 64);
 +            if (!p) {
 +                av_log(NULL, AV_LOG_FATAL, "Could not allocate memory for intra matrix.\n");
 +                exit_program(1);
 +            }
 +            av_codec_set_chroma_intra_matrix(video_enc, p);
 +            parse_matrix_coeffs(p, chroma_intra_matrix);
 +        }
 +        MATCH_PER_STREAM_OPT(inter_matrices, str, inter_matrix, oc, st);
 +        if (inter_matrix) {
 +            if (!(video_enc->inter_matrix = av_mallocz(sizeof(*video_enc->inter_matrix) * 64))) {
 +                av_log(NULL, AV_LOG_FATAL, "Could not allocate memory for inter matrix.\n");
 +                exit_program(1);
 +            }
 +            parse_matrix_coeffs(video_enc->inter_matrix, inter_matrix);
 +        }
 +
 +        MATCH_PER_STREAM_OPT(rc_overrides, str, p, oc, st);
 +        for (i = 0; p; i++) {
 +            int start, end, q;
 +            int e = sscanf(p, "%d,%d,%d", &start, &end, &q);
 +            if (e != 3) {
 +                av_log(NULL, AV_LOG_FATAL, "error parsing rc_override\n");
 +                exit_program(1);
 +            }
 +            video_enc->rc_override =
 +                av_realloc_array(video_enc->rc_override,
 +                                 i + 1, sizeof(RcOverride));
 +            if (!video_enc->rc_override) {
 +                av_log(NULL, AV_LOG_FATAL, "Could not (re)allocate memory for rc_override.\n");
 +                exit_program(1);
 +            }
 +            video_enc->rc_override[i].start_frame = start;
 +            video_enc->rc_override[i].end_frame   = end;
 +            if (q > 0) {
 +                video_enc->rc_override[i].qscale         = q;
 +                video_enc->rc_override[i].quality_factor = 1.0;
 +            }
 +            else {
 +                video_enc->rc_override[i].qscale         = 0;
 +                video_enc->rc_override[i].quality_factor = -q/100.0;
 +            }
 +            p = strchr(p, '/');
 +            if (p) p++;
 +        }
 +        video_enc->rc_override_count = i;
 +
 +        if (do_psnr)
 +            video_enc->flags|= AV_CODEC_FLAG_PSNR;
 +
 +        /* two pass mode */
 +        MATCH_PER_STREAM_OPT(pass, i, do_pass, oc, st);
 +        if (do_pass) {
 +            if (do_pass & 1) {
 +                video_enc->flags |= AV_CODEC_FLAG_PASS1;
 +                av_dict_set(&ost->encoder_opts, "flags", "+pass1", AV_DICT_APPEND);
 +            }
 +            if (do_pass & 2) {
 +                video_enc->flags |= AV_CODEC_FLAG_PASS2;
 +                av_dict_set(&ost->encoder_opts, "flags", "+pass2", AV_DICT_APPEND);
 +            }
 +        }
 +
 +        MATCH_PER_STREAM_OPT(passlogfiles, str, ost->logfile_prefix, oc, st);
 +        if (ost->logfile_prefix &&
 +            !(ost->logfile_prefix = av_strdup(ost->logfile_prefix)))
 +            exit_program(1);
 +
 +        if (do_pass) {
 +            char logfilename[1024];
 +            FILE *f;
 +
 +            snprintf(logfilename, sizeof(logfilename), "%s-%d.log",
 +                     ost->logfile_prefix ? ost->logfile_prefix :
 +                                           DEFAULT_PASS_LOGFILENAME_PREFIX,
 +                     i);
 +            if (!strcmp(ost->enc->name, "libx264")) {
 +                av_dict_set(&ost->encoder_opts, "stats", logfilename, AV_DICT_DONT_OVERWRITE);
 +            } else {
 +                if (video_enc->flags & AV_CODEC_FLAG_PASS2) {
 +                    char  *logbuffer = read_file(logfilename);
 +
 +                    if (!logbuffer) {
 +                        av_log(NULL, AV_LOG_FATAL, "Error reading log file '%s' for pass-2 encoding\n",
 +                               logfilename);
 +                        exit_program(1);
 +                    }
 +                    video_enc->stats_in = logbuffer;
 +                }
 +                if (video_enc->flags & AV_CODEC_FLAG_PASS1) {
 +                    f = av_fopen_utf8(logfilename, "wb");
 +                    if (!f) {
 +                        av_log(NULL, AV_LOG_FATAL,
 +                               "Cannot write log file '%s' for pass-1 encoding: %s\n",
 +                               logfilename, strerror(errno));
 +                        exit_program(1);
 +                    }
 +                    ost->logfile = f;
 +                }
 +            }
 +        }
 +
 +        MATCH_PER_STREAM_OPT(forced_key_frames, str, ost->forced_keyframes, oc, st);
 +        if (ost->forced_keyframes)
 +            ost->forced_keyframes = av_strdup(ost->forced_keyframes);
 +
 +        MATCH_PER_STREAM_OPT(force_fps, i, ost->force_fps, oc, st);
 +
 +        ost->top_field_first = -1;
 +        MATCH_PER_STREAM_OPT(top_field_first, i, ost->top_field_first, oc, st);
 +
 +
 +        ost->avfilter = get_ost_filters(o, oc, ost);
 +        if (!ost->avfilter)
 +            exit_program(1);
 +    } else {
 +        MATCH_PER_STREAM_OPT(copy_initial_nonkeyframes, i, ost->copy_initial_nonkeyframes, oc ,st);
 +    }
 +
 +    if (ost->stream_copy)
 +        check_streamcopy_filters(o, oc, ost, AVMEDIA_TYPE_VIDEO);
 +
 +    return ost;
 +}
 +
 +static OutputStream *new_audio_stream(OptionsContext *o, AVFormatContext *oc, int source_index)
 +{
 +    int n;
 +    AVStream *st;
 +    OutputStream *ost;
 +    AVCodecContext *audio_enc;
 +
 +    ost = new_output_stream(o, oc, AVMEDIA_TYPE_AUDIO, source_index);
 +    st  = ost->st;
 +
 +    audio_enc = ost->enc_ctx;
 +    audio_enc->codec_type = AVMEDIA_TYPE_AUDIO;
 +
 +    MATCH_PER_STREAM_OPT(filter_scripts, str, ost->filters_script, oc, st);
 +    MATCH_PER_STREAM_OPT(filters,        str, ost->filters,        oc, st);
 +
 +    if (!ost->stream_copy) {
 +        char *sample_fmt = NULL;
 +
 +        MATCH_PER_STREAM_OPT(audio_channels, i, audio_enc->channels, oc, st);
 +
 +        MATCH_PER_STREAM_OPT(sample_fmts, str, sample_fmt, oc, st);
 +        if (sample_fmt &&
 +            (audio_enc->sample_fmt = av_get_sample_fmt(sample_fmt)) == AV_SAMPLE_FMT_NONE) {
 +            av_log(NULL, AV_LOG_FATAL, "Invalid sample format '%s'\n", sample_fmt);
 +            exit_program(1);
 +        }
 +
 +        MATCH_PER_STREAM_OPT(audio_sample_rate, i, audio_enc->sample_rate, oc, st);
 +
 +        MATCH_PER_STREAM_OPT(apad, str, ost->apad, oc, st);
 +        ost->apad = av_strdup(ost->apad);
 +
 +        ost->avfilter = get_ost_filters(o, oc, ost);
 +        if (!ost->avfilter)
 +            exit_program(1);
 +
 +        /* check for channel mapping for this audio stream */
 +        for (n = 0; n < o->nb_audio_channel_maps; n++) {
 +            AudioChannelMap *map = &o->audio_channel_maps[n];
 +            if ((map->ofile_idx   == -1 || ost->file_index == map->ofile_idx) &&
 +                (map->ostream_idx == -1 || ost->st->index  == map->ostream_idx)) {
 +                InputStream *ist;
 +
 +                if (map->channel_idx == -1) {
 +                    ist = NULL;
 +                } else if (ost->source_index < 0) {
 +                    av_log(NULL, AV_LOG_FATAL, "Cannot determine input stream for channel mapping %d.%d\n",
 +                           ost->file_index, ost->st->index);
 +                    continue;
 +                } else {
 +                    ist = input_streams[ost->source_index];
 +                }
 +
 +                if (!ist || (ist->file_index == map->file_idx && ist->st->index == map->stream_idx)) {
 +                    if (av_reallocp_array(&ost->audio_channels_map,
 +                                          ost->audio_channels_mapped + 1,
 +                                          sizeof(*ost->audio_channels_map)
 +                                          ) < 0 )
 +                        exit_program(1);
 +
 +                    ost->audio_channels_map[ost->audio_channels_mapped++] = map->channel_idx;
 +                }
 +            }
 +        }
 +    }
 +
 +    if (ost->stream_copy)
 +        check_streamcopy_filters(o, oc, ost, AVMEDIA_TYPE_AUDIO);
 +
 +    return ost;
 +}
 +
 +static OutputStream *new_data_stream(OptionsContext *o, AVFormatContext *oc, int source_index)
 +{
 +    OutputStream *ost;
 +
 +    ost = new_output_stream(o, oc, AVMEDIA_TYPE_DATA, source_index);
 +    if (!ost->stream_copy) {
 +        av_log(NULL, AV_LOG_FATAL, "Data stream encoding not supported yet (only streamcopy)\n");
 +        exit_program(1);
 +    }
 +
 +    return ost;
 +}
 +
 +static OutputStream *new_unknown_stream(OptionsContext *o, AVFormatContext *oc, int source_index)
 +{
 +    OutputStream *ost;
 +
 +    ost = new_output_stream(o, oc, AVMEDIA_TYPE_UNKNOWN, source_index);
 +    if (!ost->stream_copy) {
 +        av_log(NULL, AV_LOG_FATAL, "Unknown stream encoding not supported yet (only streamcopy)\n");
 +        exit_program(1);
 +    }
 +
 +    return ost;
 +}
 +
 +static OutputStream *new_attachment_stream(OptionsContext *o, AVFormatContext *oc, int source_index)
 +{
 +    OutputStream *ost = new_output_stream(o, oc, AVMEDIA_TYPE_ATTACHMENT, source_index);
 +    ost->stream_copy = 1;
 +    ost->finished    = 1;
 +    return ost;
 +}
 +
 +static OutputStream *new_subtitle_stream(OptionsContext *o, AVFormatContext *oc, int source_index)
 +{
 +    AVStream *st;
 +    OutputStream *ost;
 +    AVCodecContext *subtitle_enc;
 +
 +    ost = new_output_stream(o, oc, AVMEDIA_TYPE_SUBTITLE, source_index);
 +    st  = ost->st;
 +    subtitle_enc = ost->enc_ctx;
 +
 +    subtitle_enc->codec_type = AVMEDIA_TYPE_SUBTITLE;
 +
 +    MATCH_PER_STREAM_OPT(copy_initial_nonkeyframes, i, ost->copy_initial_nonkeyframes, oc, st);
 +
 +    if (!ost->stream_copy) {
 +        char *frame_size = NULL;
 +
 +        MATCH_PER_STREAM_OPT(frame_sizes, str, frame_size, oc, st);
 +        if (frame_size && av_parse_video_size(&subtitle_enc->width, &subtitle_enc->height, frame_size) < 0) {
 +            av_log(NULL, AV_LOG_FATAL, "Invalid frame size: %s.\n", frame_size);
 +            exit_program(1);
 +        }
 +    }
 +
 +    return ost;
 +}
 +
 +/* arg format is "output-stream-index:streamid-value". */
 +static int opt_streamid(void *optctx, const char *opt, const char *arg)
 +{
 +    OptionsContext *o = optctx;
 +    int idx;
 +    char *p;
 +    char idx_str[16];
 +
 +    av_strlcpy(idx_str, arg, sizeof(idx_str));
 +    p = strchr(idx_str, ':');
 +    if (!p) {
 +        av_log(NULL, AV_LOG_FATAL,
 +               "Invalid value '%s' for option '%s', required syntax is 'index:value'\n",
 +               arg, opt);
 +        exit_program(1);
 +    }
 +    *p++ = '\0';
 +    idx = parse_number_or_die(opt, idx_str, OPT_INT, 0, MAX_STREAMS-1);
 +    o->streamid_map = grow_array(o->streamid_map, sizeof(*o->streamid_map), &o->nb_streamid_map, idx+1);
 +    o->streamid_map[idx] = parse_number_or_die(opt, p, OPT_INT, 0, INT_MAX);
 +    return 0;
 +}
 +
 +static int copy_chapters(InputFile *ifile, OutputFile *ofile, int copy_metadata)
 +{
 +    AVFormatContext *is = ifile->ctx;
 +    AVFormatContext *os = ofile->ctx;
 +    AVChapter **tmp;
 +    int i;
 +
 +    tmp = av_realloc_f(os->chapters, is->nb_chapters + os->nb_chapters, sizeof(*os->chapters));
 +    if (!tmp)
 +        return AVERROR(ENOMEM);
 +    os->chapters = tmp;
 +
 +    for (i = 0; i < is->nb_chapters; i++) {
 +        AVChapter *in_ch = is->chapters[i], *out_ch;
 +        int64_t start_time = (ofile->start_time == AV_NOPTS_VALUE) ? 0 : ofile->start_time;
 +        int64_t ts_off   = av_rescale_q(start_time - ifile->ts_offset,
 +                                       AV_TIME_BASE_Q, in_ch->time_base);
 +        int64_t rt       = (ofile->recording_time == INT64_MAX) ? INT64_MAX :
 +                           av_rescale_q(ofile->recording_time, AV_TIME_BASE_Q, in_ch->time_base);
 +
 +
 +        if (in_ch->end < ts_off)
 +            continue;
 +        if (rt != INT64_MAX && in_ch->start > rt + ts_off)
 +            break;
 +
 +        out_ch = av_mallocz(sizeof(AVChapter));
 +        if (!out_ch)
 +            return AVERROR(ENOMEM);
 +
 +        out_ch->id        = in_ch->id;
 +        out_ch->time_base = in_ch->time_base;
 +        out_ch->start     = FFMAX(0,  in_ch->start - ts_off);
 +        out_ch->end       = FFMIN(rt, in_ch->end   - ts_off);
 +
 +        if (copy_metadata)
 +            av_dict_copy(&out_ch->metadata, in_ch->metadata, 0);
 +
 +        os->chapters[os->nb_chapters++] = out_ch;
 +    }
 +    return 0;
 +}
 +
 +static int read_ffserver_streams(OptionsContext *o, AVFormatContext *s, const char *filename)
 +{
 +    int i, err;
 +    AVFormatContext *ic = avformat_alloc_context();
 +
 +    ic->flags |= AVFMT_FLAG_KEEP_SIDE_DATA;
 +    ic->interrupt_callback = int_cb;
 +    err = avformat_open_input(&ic, filename, NULL, NULL);
 +    if (err < 0)
 +        return err;
 +    /* copy stream format */
 +    for(i=0;i<ic->nb_streams;i++) {
 +        AVStream *st;
 +        OutputStream *ost;
 +        AVCodec *codec;
 +        const char *enc_config;
 +
 +        codec = avcodec_find_encoder(ic->streams[i]->codecpar->codec_id);
 +        if (!codec) {
 +            av_log(s, AV_LOG_ERROR, "no encoder found for codec id %i\n", ic->streams[i]->codecpar->codec_id);
 +            return AVERROR(EINVAL);
 +        }
 +        if (codec->type == AVMEDIA_TYPE_AUDIO)
 +            opt_audio_codec(o, "c:a", codec->name);
 +        else if (codec->type == AVMEDIA_TYPE_VIDEO)
 +            opt_video_codec(o, "c:v", codec->name);
 +        ost   = new_output_stream(o, s, codec->type, -1);
 +        st    = ost->st;
 +
 +        avcodec_get_context_defaults3(st->codec, codec);
 +        enc_config = av_stream_get_recommended_encoder_configuration(ic->streams[i]);
 +        if (enc_config) {
 +            AVDictionary *opts = NULL;
 +            av_dict_parse_string(&opts, enc_config, "=", ",", 0);
 +            av_opt_set_dict2(st->codec, &opts, AV_OPT_SEARCH_CHILDREN);
 +            av_dict_free(&opts);
 +        }
 +
 +        if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && !ost->stream_copy)
 +            choose_sample_fmt(st, codec);
 +        else if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && !ost->stream_copy)
 +            choose_pixel_fmt(st, st->codec, codec, st->codecpar->format);
 +        avcodec_copy_context(ost->enc_ctx, st->codec);
 +        if (enc_config)
 +            av_dict_parse_string(&ost->encoder_opts, enc_config, "=", ",", 0);
 +    }
 +
 +    avformat_close_input(&ic);
 +    return err;
 +}
 +
 +static void init_output_filter(OutputFilter *ofilter, OptionsContext *o,
 +                               AVFormatContext *oc)
 +{
 +    OutputStream *ost;
 +
 +    switch (ofilter->type) {
 +    case AVMEDIA_TYPE_VIDEO: ost = new_video_stream(o, oc, -1); break;
 +    case AVMEDIA_TYPE_AUDIO: ost = new_audio_stream(o, oc, -1); break;
 +    default:
 +        av_log(NULL, AV_LOG_FATAL, "Only video and audio filters are supported "
 +               "currently.\n");
 +        exit_program(1);
 +    }
 +
 +    ost->source_index = -1;
 +    ost->filter       = ofilter;
 +
 +    ofilter->ost      = ost;
 +    ofilter->format   = -1;
 +
 +    if (ost->stream_copy) {
 +        av_log(NULL, AV_LOG_ERROR, "Streamcopy requested for output stream %d:%d, "
 +               "which is fed from a complex filtergraph. Filtering and streamcopy "
 +               "cannot be used together.\n", ost->file_index, ost->index);
 +        exit_program(1);
 +    }
 +
 +    if (ost->avfilter && (ost->filters || ost->filters_script)) {
 +        const char *opt = ost->filters ? "-vf/-af/-filter" : "-filter_script";
 +        av_log(NULL, AV_LOG_ERROR,
 +               "%s '%s' was specified through the %s option "
 +               "for output stream %d:%d, which is fed from a complex filtergraph.\n"
 +               "%s and -filter_complex cannot be used together for the same stream.\n",
 +               ost->filters ? "Filtergraph" : "Filtergraph script",
 +               ost->filters ? ost->filters : ost->filters_script,
 +               opt, ost->file_index, ost->index, opt);
 +        exit_program(1);
 +    }
 +
 +    avfilter_inout_free(&ofilter->out_tmp);
 +}
 +
 +static int init_complex_filters(void)
 +{
 +    int i, ret = 0;
 +
 +    for (i = 0; i < nb_filtergraphs; i++) {
 +        ret = init_complex_filtergraph(filtergraphs[i]);
 +        if (ret < 0)
 +            return ret;
 +    }
 +    return 0;
 +}
 +
 +static int open_output_file(OptionsContext *o, const char *filename)
 +{
 +    AVFormatContext *oc;
 +    int i, j, err;
 +    AVOutputFormat *file_oformat;
 +    OutputFile *of;
 +    OutputStream *ost;
 +    InputStream  *ist;
 +    AVDictionary *unused_opts = NULL;
 +    AVDictionaryEntry *e = NULL;
 +    int format_flags = 0;
 +
 +    if (o->stop_time != INT64_MAX && o->recording_time != INT64_MAX) {
 +        o->stop_time = INT64_MAX;
 +        av_log(NULL, AV_LOG_WARNING, "-t and -to cannot be used together; using -t.\n");
 +    }
 +
 +    if (o->stop_time != INT64_MAX && o->recording_time == INT64_MAX) {
 +        int64_t start_time = o->start_time == AV_NOPTS_VALUE ? 0 : o->start_time;
 +        if (o->stop_time <= start_time) {
 +            av_log(NULL, AV_LOG_ERROR, "-to value smaller than -ss; aborting.\n");
 +            exit_program(1);
 +        } else {
 +            o->recording_time = o->stop_time - start_time;
 +        }
 +    }
 +
 +    GROW_ARRAY(output_files, nb_output_files);
 +    of = av_mallocz(sizeof(*of));
 +    if (!of)
 +        exit_program(1);
 +    output_files[nb_output_files - 1] = of;
 +
 +    of->ost_index      = nb_output_streams;
 +    of->recording_time = o->recording_time;
 +    of->start_time     = o->start_time;
 +    of->limit_filesize = o->limit_filesize;
 +    of->shortest       = o->shortest;
 +    av_dict_copy(&of->opts, o->g->format_opts, 0);
 +
 +    if (!strcmp(filename, "-"))
 +        filename = "pipe:";
 +
 +    err = avformat_alloc_output_context2(&oc, NULL, o->format, filename);
 +    if (!oc) {
 +        print_error(filename, err);
 +        exit_program(1);
 +    }
 +
 +    of->ctx = oc;
 +    if (o->recording_time != INT64_MAX)
 +        oc->duration = o->recording_time;
 +
 +    file_oformat= oc->oformat;
 +    oc->interrupt_callback = int_cb;
 +
 +    e = av_dict_get(o->g->format_opts, "fflags", NULL, 0);
 +    if (e) {
 +        const AVOption *o = av_opt_find(oc, "fflags", NULL, 0, 0);
 +        av_opt_eval_flags(oc, o, e->value, &format_flags);
 +    }
 +
 +    /* create streams for all unlabeled output pads */
 +    for (i = 0; i < nb_filtergraphs; i++) {
 +        FilterGraph *fg = filtergraphs[i];
 +        for (j = 0; j < fg->nb_outputs; j++) {
 +            OutputFilter *ofilter = fg->outputs[j];
 +
 +            if (!ofilter->out_tmp || ofilter->out_tmp->name)
 +                continue;
 +
 +            switch (ofilter->type) {
 +            case AVMEDIA_TYPE_VIDEO:    o->video_disable    = 1; break;
 +            case AVMEDIA_TYPE_AUDIO:    o->audio_disable    = 1; break;
 +            case AVMEDIA_TYPE_SUBTITLE: o->subtitle_disable = 1; break;
 +            }
 +            init_output_filter(ofilter, o, oc);
 +        }
 +    }
 +
 +    /* ffserver seeking with date=... needs a date reference */
 +    if (!strcmp(file_oformat->name, "ffm") &&
 +        !(format_flags & AVFMT_FLAG_BITEXACT) &&
 +        av_strstart(filename, "http:", NULL)) {
 +        int err = parse_option(o, "metadata", "creation_time=now", options);
 +        if (err < 0) {
 +            print_error(filename, err);
 +            exit_program(1);
 +        }
 +    }
 +
 +    if (!strcmp(file_oformat->name, "ffm") && !override_ffserver &&
 +        av_strstart(filename, "http:", NULL)) {
 +        int j;
 +        /* special case for files sent to ffserver: we get the stream
 +           parameters from ffserver */
 +        int err = read_ffserver_streams(o, oc, filename);
 +        if (err < 0) {
 +            print_error(filename, err);
 +            exit_program(1);
 +        }
 +        for(j = nb_output_streams - oc->nb_streams; j < nb_output_streams; j++) {
 +            ost = output_streams[j];
 +            for (i = 0; i < nb_input_streams; i++) {
 +                ist = input_streams[i];
 +                if(ist->st->codecpar->codec_type == ost->st->codecpar->codec_type){
 +                    ost->sync_ist= ist;
 +                    ost->source_index= i;
 +                    if(ost->st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) ost->avfilter = av_strdup("anull");
 +                    if(ost->st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) ost->avfilter = av_strdup("null");
 +                    ist->discard = 0;
 +                    ist->st->discard = ist->user_set_discard;
 +                    break;
 +                }
 +            }
 +            if(!ost->sync_ist){
 +                av_log(NULL, AV_LOG_FATAL, "Missing %s stream which is required by this ffm\n", av_get_media_type_string(ost->st->codecpar->codec_type));
 +                exit_program(1);
 +            }
 +        }
 +    } else if (!o->nb_stream_maps) {
 +        char *subtitle_codec_name = NULL;
 +        /* pick the "best" stream of each type */
 +
 +        /* video: highest resolution */
 +        if (!o->video_disable && av_guess_codec(oc->oformat, NULL, filename, NULL, AVMEDIA_TYPE_VIDEO) != AV_CODEC_ID_NONE) {
 +            int area = 0, idx = -1;
 +            int qcr = avformat_query_codec(oc->oformat, oc->oformat->video_codec, 0);
 +            for (i = 0; i < nb_input_streams; i++) {
 +                int new_area;
 +                ist = input_streams[i];
 +                new_area = ist->st->codecpar->width * ist->st->codecpar->height + 100000000*!!ist->st->codec_info_nb_frames;
 +                if((qcr!=MKTAG('A', 'P', 'I', 'C')) && (ist->st->disposition & AV_DISPOSITION_ATTACHED_PIC))
 +                    new_area = 1;
 +                if (ist->st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
 +                    new_area > area) {
 +                    if((qcr==MKTAG('A', 'P', 'I', 'C')) && !(ist->st->disposition & AV_DISPOSITION_ATTACHED_PIC))
 +                        continue;
 +                    area = new_area;
 +                    idx = i;
 +                }
 +            }
 +            if (idx >= 0)
 +                new_video_stream(o, oc, idx);
 +        }
 +
 +        /* audio: most channels */
 +        if (!o->audio_disable && av_guess_codec(oc->oformat, NULL, filename, NULL, AVMEDIA_TYPE_AUDIO) != AV_CODEC_ID_NONE) {
 +            int best_score = 0, idx = -1;
 +            for (i = 0; i < nb_input_streams; i++) {
 +                int score;
 +                ist = input_streams[i];
 +                score = ist->st->codecpar->channels + 100000000*!!ist->st->codec_info_nb_frames;
 +                if (ist->st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO &&
 +                    score > best_score) {
 +                    best_score = score;
 +                    idx = i;
 +                }
 +            }
 +            if (idx >= 0)
 +                new_audio_stream(o, oc, idx);
 +        }
 +
 +        /* subtitles: pick first */
 +        MATCH_PER_TYPE_OPT(codec_names, str, subtitle_codec_name, oc, "s");
 +        if (!o->subtitle_disable && (avcodec_find_encoder(oc->oformat->subtitle_codec) || subtitle_codec_name)) {
 +            for (i = 0; i < nb_input_streams; i++)
 +                if (input_streams[i]->st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) {
 +                    AVCodecDescriptor const *input_descriptor =
 +                        avcodec_descriptor_get(input_streams[i]->st->codecpar->codec_id);
 +                    AVCodecDescriptor const *output_descriptor = NULL;
 +                    AVCodec const *output_codec =
 +                        avcodec_find_encoder(oc->oformat->subtitle_codec);
 +                    int input_props = 0, output_props = 0;
 +                    if (output_codec)
 +                        output_descriptor = avcodec_descriptor_get(output_codec->id);
 +                    if (input_descriptor)
 +                        input_props = input_descriptor->props & (AV_CODEC_PROP_TEXT_SUB | AV_CODEC_PROP_BITMAP_SUB);
 +                    if (output_descriptor)
 +                        output_props = output_descriptor->props & (AV_CODEC_PROP_TEXT_SUB | AV_CODEC_PROP_BITMAP_SUB);
 +                    if (subtitle_codec_name ||
 +                        input_props & output_props ||
 +                        // Map dvb teletext which has neither property to any output subtitle encoder
 +                        input_descriptor && output_descriptor &&
 +                        (!input_descriptor->props ||
 +                         !output_descriptor->props)) {
 +                        new_subtitle_stream(o, oc, i);
 +                        break;
 +                    }
 +                }
 +        }
 +        /* Data only if codec id match */
 +        if (!o->data_disable ) {
 +            enum AVCodecID codec_id = av_guess_codec(oc->oformat, NULL, filename, NULL, AVMEDIA_TYPE_DATA);
 +            for (i = 0; codec_id != AV_CODEC_ID_NONE && i < nb_input_streams; i++) {
 +                if (input_streams[i]->st->codecpar->codec_type == AVMEDIA_TYPE_DATA
 +                    && input_streams[i]->st->codecpar->codec_id == codec_id )
 +                    new_data_stream(o, oc, i);
 +            }
 +        }
 +    } else {
 +        for (i = 0; i < o->nb_stream_maps; i++) {
 +            StreamMap *map = &o->stream_maps[i];
 +
 +            if (map->disabled)
 +                continue;
 +
 +            if (map->linklabel) {
 +                FilterGraph *fg;
 +                OutputFilter *ofilter = NULL;
 +                int j, k;
 +
 +                for (j = 0; j < nb_filtergraphs; j++) {
 +                    fg = filtergraphs[j];
 +                    for (k = 0; k < fg->nb_outputs; k++) {
 +                        AVFilterInOut *out = fg->outputs[k]->out_tmp;
 +                        if (out && !strcmp(out->name, map->linklabel)) {
 +                            ofilter = fg->outputs[k];
 +                            goto loop_end;
 +                        }
 +                    }
 +                }
 +loop_end:
 +                if (!ofilter) {
 +                    av_log(NULL, AV_LOG_FATAL, "Output with label '%s' does not exist "
 +                           "in any defined filter graph, or was already used elsewhere.\n", map->linklabel);
 +                    exit_program(1);
 +                }
 +                init_output_filter(ofilter, o, oc);
 +            } else {
 +                int src_idx = input_files[map->file_index]->ist_index + map->stream_index;
 +
 +                ist = input_streams[input_files[map->file_index]->ist_index + map->stream_index];
 +                if(o->subtitle_disable && ist->st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE)
 +                    continue;
 +                if(o->   audio_disable && ist->st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
 +                    continue;
 +                if(o->   video_disable && ist->st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
 +                    continue;
 +                if(o->    data_disable && ist->st->codecpar->codec_type == AVMEDIA_TYPE_DATA)
 +                    continue;
 +
 +                ost = NULL;
 +                switch (ist->st->codecpar->codec_type) {
 +                case AVMEDIA_TYPE_VIDEO:      ost = new_video_stream     (o, oc, src_idx); break;
 +                case AVMEDIA_TYPE_AUDIO:      ost = new_audio_stream     (o, oc, src_idx); break;
 +                case AVMEDIA_TYPE_SUBTITLE:   ost = new_subtitle_stream  (o, oc, src_idx); break;
 +                case AVMEDIA_TYPE_DATA:       ost = new_data_stream      (o, oc, src_idx); break;
 +                case AVMEDIA_TYPE_ATTACHMENT: ost = new_attachment_stream(o, oc, src_idx); break;
 +                case AVMEDIA_TYPE_UNKNOWN:
 +                    if (copy_unknown_streams) {
 +                        ost = new_unknown_stream   (o, oc, src_idx);
 +                        break;
 +                    }
 +                default:
 +                    av_log(NULL, ignore_unknown_streams ? AV_LOG_WARNING : AV_LOG_FATAL,
 +                           "Cannot map stream #%d:%d - unsupported type.\n",
 +                           map->file_index, map->stream_index);
 +                    if (!ignore_unknown_streams) {
 +                        av_log(NULL, AV_LOG_FATAL,
 +                               "If you want unsupported types ignored instead "
 +                               "of failing, please use the -ignore_unknown option\n"
 +                               "If you want them copied, please use -copy_unknown\n");
 +                        exit_program(1);
 +                    }
 +                }
 +                if (ost)
 +                    ost->sync_ist = input_streams[  input_files[map->sync_file_index]->ist_index
 +                                                  + map->sync_stream_index];
 +            }
 +        }
 +    }
 +
 +    /* handle attached files */
 +    for (i = 0; i < o->nb_attachments; i++) {
 +        AVIOContext *pb;
 +        uint8_t *attachment;
 +        const char *p;
 +        int64_t len;
 +
 +        if ((err = avio_open2(&pb, o->attachments[i], AVIO_FLAG_READ, &int_cb, NULL)) < 0) {
 +            av_log(NULL, AV_LOG_FATAL, "Could not open attachment file %s.\n",
 +                   o->attachments[i]);
 +            exit_program(1);
 +        }
 +        if ((len = avio_size(pb)) <= 0) {
 +            av_log(NULL, AV_LOG_FATAL, "Could not get size of the attachment %s.\n",
 +                   o->attachments[i]);
 +            exit_program(1);
 +        }
 +        if (!(attachment = av_malloc(len))) {
 +            av_log(NULL, AV_LOG_FATAL, "Attachment %s too large to fit into memory.\n",
 +                   o->attachments[i]);
 +            exit_program(1);
 +        }
 +        avio_read(pb, attachment, len);
 +
 +        ost = new_attachment_stream(o, oc, -1);
 +        ost->stream_copy               = 0;
 +        ost->attachment_filename       = o->attachments[i];
 +        ost->st->codecpar->extradata      = attachment;
 +        ost->st->codecpar->extradata_size = len;
 +
 +        p = strrchr(o->attachments[i], '/');
 +        av_dict_set(&ost->st->metadata, "filename", (p && *p) ? p + 1 : o->attachments[i], AV_DICT_DONT_OVERWRITE);
 +        avio_closep(&pb);
 +    }
 +
 +#if FF_API_LAVF_AVCTX
 +    for (i = nb_output_streams - oc->nb_streams; i < nb_output_streams; i++) { //for all streams of this output file
 +        AVDictionaryEntry *e;
 +        ost = output_streams[i];
 +
 +        if ((ost->stream_copy || ost->attachment_filename)
 +            && (e = av_dict_get(o->g->codec_opts, "flags", NULL, AV_DICT_IGNORE_SUFFIX))
 +            && (!e->key[5] || check_stream_specifier(oc, ost->st, e->key+6)))
 +            if (av_opt_set(ost->st->codec, "flags", e->value, 0) < 0)
 +                exit_program(1);
 +    }
 +#endif
 +
 +    if (!oc->nb_streams && !(oc->oformat->flags & AVFMT_NOSTREAMS)) {
 +        av_dump_format(oc, nb_output_files - 1, oc->filename, 1);
 +        av_log(NULL, AV_LOG_ERROR, "Output file #%d does not contain any stream\n", nb_output_files - 1);
 +        exit_program(1);
 +    }
 +
 +    /* check if all codec options have been used */
 +    unused_opts = strip_specifiers(o->g->codec_opts);
 +    for (i = of->ost_index; i < nb_output_streams; i++) {
 +        e = NULL;
 +        while ((e = av_dict_get(output_streams[i]->encoder_opts, "", e,
 +                                AV_DICT_IGNORE_SUFFIX)))
 +            av_dict_set(&unused_opts, e->key, NULL, 0);
 +    }
 +
 +    e = NULL;
 +    while ((e = av_dict_get(unused_opts, "", e, AV_DICT_IGNORE_SUFFIX))) {
 +        const AVClass *class = avcodec_get_class();
 +        const AVOption *option = av_opt_find(&class, e->key, NULL, 0,
 +                                             AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ);
 +        const AVClass *fclass = avformat_get_class();
 +        const AVOption *foption = av_opt_find(&fclass, e->key, NULL, 0,
 +                                              AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ);
 +        if (!option || foption)
 +            continue;
 +
 +
 +        if (!(option->flags & AV_OPT_FLAG_ENCODING_PARAM)) {
 +            av_log(NULL, AV_LOG_ERROR, "Codec AVOption %s (%s) specified for "
 +                   "output file #%d (%s) is not an encoding option.\n", e->key,
 +                   option->help ? option->help : "", nb_output_files - 1,
 +                   filename);
 +            exit_program(1);
 +        }
 +
 +        // gop_timecode is injected by generic code but not always used
 +        if (!strcmp(e->key, "gop_timecode"))
 +            continue;
 +
 +        av_log(NULL, AV_LOG_WARNING, "Codec AVOption %s (%s) specified for "
 +               "output file #%d (%s) has not been used for any stream. The most "
 +               "likely reason is either wrong type (e.g. a video option with "
 +               "no video streams) or that it is a private option of some encoder "
 +               "which was not actually used for any stream.\n", e->key,
 +               option->help ? option->help : "", nb_output_files - 1, filename);
 +    }
 +    av_dict_free(&unused_opts);
 +
 +    /* set the decoding_needed flags and create simple filtergraphs */
 +    for (i = of->ost_index; i < nb_output_streams; i++) {
 +        OutputStream *ost = output_streams[i];
 +
 +        if (ost->encoding_needed && ost->source_index >= 0) {
 +            InputStream *ist = input_streams[ost->source_index];
 +            ist->decoding_needed |= DECODING_FOR_OST;
 +
 +            if (ost->st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ||
 +                ost->st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
 +                err = init_simple_filtergraph(ist, ost);
 +                if (err < 0) {
 +                    av_log(NULL, AV_LOG_ERROR,
 +                           "Error initializing a simple filtergraph between streams "
 +                           "%d:%d->%d:%d\n", ist->file_index, ost->source_index,
 +                           nb_output_files - 1, ost->st->index);
 +                    exit_program(1);
 +                }
 +            }
 +        }
 +
 +        /* set the filter output constraints */
 +        if (ost->filter) {
 +            OutputFilter *f = ost->filter;
 +            int count;
 +            switch (ost->enc_ctx->codec_type) {
 +            case AVMEDIA_TYPE_VIDEO:
 +                f->frame_rate = ost->frame_rate;
 +                f->width      = ost->enc_ctx->width;
 +                f->height     = ost->enc_ctx->height;
 +                if (ost->enc_ctx->pix_fmt != AV_PIX_FMT_NONE) {
 +                    f->format = ost->enc_ctx->pix_fmt;
 +                } else if (ost->enc->pix_fmts) {
 +                    count = 0;
 +                    while (ost->enc->pix_fmts[count] != AV_PIX_FMT_NONE)
 +                        count++;
 +                    f->formats = av_mallocz_array(count + 1, sizeof(*f->formats));
 +                    if (!f->formats)
 +                        exit_program(1);
 +                    memcpy(f->formats, ost->enc->pix_fmts, (count + 1) * sizeof(*f->formats));
 +                }
 +                break;
 +            case AVMEDIA_TYPE_AUDIO:
 +                if (ost->enc_ctx->sample_fmt != AV_SAMPLE_FMT_NONE) {
 +                    f->format = ost->enc_ctx->sample_fmt;
 +                } else if (ost->enc->sample_fmts) {
 +                    count = 0;
 +                    while (ost->enc->sample_fmts[count] != AV_SAMPLE_FMT_NONE)
 +                        count++;
 +                    f->formats = av_mallocz_array(count + 1, sizeof(*f->formats));
 +                    if (!f->formats)
 +                        exit_program(1);
 +                    memcpy(f->formats, ost->enc->sample_fmts, (count + 1) * sizeof(*f->formats));
 +                }
 +                if (ost->enc_ctx->sample_rate) {
 +                    f->sample_rate = ost->enc_ctx->sample_rate;
 +                } else if (ost->enc->supported_samplerates) {
 +                    count = 0;
 +                    while (ost->enc->supported_samplerates[count])
 +                        count++;
 +                    f->sample_rates = av_mallocz_array(count + 1, sizeof(*f->sample_rates));
 +                    if (!f->sample_rates)
 +                        exit_program(1);
 +                    memcpy(f->sample_rates, ost->enc->supported_samplerates,
 +                           (count + 1) * sizeof(*f->sample_rates));
 +                }
 +                if (ost->enc_ctx->channels) {
 +                    f->channel_layout = av_get_default_channel_layout(ost->enc_ctx->channels);
 +                } else if (ost->enc->channel_layouts) {
 +                    count = 0;
 +                    while (ost->enc->channel_layouts[count])
 +                        count++;
 +                    f->channel_layouts = av_mallocz_array(count + 1, sizeof(*f->channel_layouts));
 +                    if (!f->channel_layouts)
 +                        exit_program(1);
 +                    memcpy(f->channel_layouts, ost->enc->channel_layouts,
 +                           (count + 1) * sizeof(*f->channel_layouts));
 +                }
 +                break;
 +            }
 +        }
 +    }
 +
 +    /* check filename in case of an image number is expected */
 +    if (oc->oformat->flags & AVFMT_NEEDNUMBER) {
 +        if (!av_filename_number_test(oc->filename)) {
 +            print_error(oc->filename, AVERROR(EINVAL));
 +            exit_program(1);
 +        }
 +    }
 +
 +    if (!(oc->oformat->flags & AVFMT_NOSTREAMS) && !input_stream_potentially_available) {
 +        av_log(NULL, AV_LOG_ERROR,
 +               "No input streams but output needs an input stream\n");
 +        exit_program(1);
 +    }
 +
 +    if (!(oc->oformat->flags & AVFMT_NOFILE)) {
 +        /* test if it already exists to avoid losing precious files */
 +        assert_file_overwrite(filename);
 +
 +        /* open the file */
 +        if ((err = avio_open2(&oc->pb, filename, AVIO_FLAG_WRITE,
 +                              &oc->interrupt_callback,
 +                              &of->opts)) < 0) {
 +            print_error(filename, err);
 +            exit_program(1);
 +        }
 +    } else if (strcmp(oc->oformat->name, "image2")==0 && !av_filename_number_test(filename))
 +        assert_file_overwrite(filename);
 +
 +    if (o->mux_preload) {
 +        av_dict_set_int(&of->opts, "preload", o->mux_preload*AV_TIME_BASE, 0);
 +    }
 +    oc->max_delay = (int)(o->mux_max_delay * AV_TIME_BASE);
 +
 +    /* copy metadata */
 +    for (i = 0; i < o->nb_metadata_map; i++) {
 +        char *p;
 +        int in_file_index = strtol(o->metadata_map[i].u.str, &p, 0);
 +
 +        if (in_file_index >= nb_input_files) {
 +            av_log(NULL, AV_LOG_FATAL, "Invalid input file index %d while processing metadata maps\n", in_file_index);
 +            exit_program(1);
 +        }
 +        copy_metadata(o->metadata_map[i].specifier, *p ? p + 1 : p, oc,
 +                      in_file_index >= 0 ?
 +                      input_files[in_file_index]->ctx : NULL, o);
 +    }
 +
 +    /* copy chapters */
 +    if (o->chapters_input_file >= nb_input_files) {
 +        if (o->chapters_input_file == INT_MAX) {
 +            /* copy chapters from the first input file that has them*/
 +            o->chapters_input_file = -1;
 +            for (i = 0; i < nb_input_files; i++)
 +                if (input_files[i]->ctx->nb_chapters) {
 +                    o->chapters_input_file = i;
 +                    break;
 +                }
 +        } else {
 +            av_log(NULL, AV_LOG_FATAL, "Invalid input file index %d in chapter mapping.\n",
 +                   o->chapters_input_file);
 +            exit_program(1);
 +        }
 +    }
 +    if (o->chapters_input_file >= 0)
 +        copy_chapters(input_files[o->chapters_input_file], of,
 +                      !o->metadata_chapters_manual);
 +
 +    /* copy global metadata by default */
 +    if (!o->metadata_global_manual && nb_input_files){
 +        av_dict_copy(&oc->metadata, input_files[0]->ctx->metadata,
 +                     AV_DICT_DONT_OVERWRITE);
 +        if(o->recording_time != INT64_MAX)
 +            av_dict_set(&oc->metadata, "duration", NULL, 0);
 +        av_dict_set(&oc->metadata, "creation_time", NULL, 0);
 +    }
 +    if (!o->metadata_streams_manual)
 +        for (i = of->ost_index; i < nb_output_streams; i++) {
 +            InputStream *ist;
 +            if (output_streams[i]->source_index < 0)         /* this is true e.g. for attached files */
 +                continue;
 +            ist = input_streams[output_streams[i]->source_index];
 +            av_dict_copy(&output_streams[i]->st->metadata, ist->st->metadata, AV_DICT_DONT_OVERWRITE);
 +            if (!output_streams[i]->stream_copy) {
 +                av_dict_set(&output_streams[i]->st->metadata, "encoder", NULL, 0);
 +            }
 +        }
 +
 +    /* process manually set programs */
 +    for (i = 0; i < o->nb_program; i++) {
 +        const char *p = o->program[i].u.str;
 +        int progid = i+1;
 +        AVProgram *program;
 +
 +        while(*p) {
 +            const char *p2 = av_get_token(&p, ":");
 +            const char *to_dealloc = p2;
 +            char *key;
 +            if (!p2)
 +                break;
 +
 +            if(*p) p++;
 +
 +            key = av_get_token(&p2, "=");
 +            if (!key || !*p2) {
 +                av_freep(&to_dealloc);
 +                av_freep(&key);
 +                break;
 +            }
 +            p2++;
 +
 +            if (!strcmp(key, "program_num"))
 +                progid = strtol(p2, NULL, 0);
 +            av_freep(&to_dealloc);
 +            av_freep(&key);
 +        }
 +
 +        program = av_new_program(oc, progid);
 +
 +        p = o->program[i].u.str;
 +        while(*p) {
 +            const char *p2 = av_get_token(&p, ":");
 +            const char *to_dealloc = p2;
 +            char *key;
 +            if (!p2)
 +                break;
 +            if(*p) p++;
 +
 +            key = av_get_token(&p2, "=");
 +            if (!key) {
 +                av_log(NULL, AV_LOG_FATAL,
 +                       "No '=' character in program string %s.\n",
 +                       p2);
 +                exit_program(1);
 +            }
 +            if (!*p2)
 +                exit_program(1);
 +            p2++;
 +
 +            if (!strcmp(key, "title")) {
 +                av_dict_set(&program->metadata, "title", p2, 0);
 +            } else if (!strcmp(key, "program_num")) {
 +            } else if (!strcmp(key, "st")) {
 +                int st_num = strtol(p2, NULL, 0);
 +                av_program_add_stream_index(oc, progid, st_num);
 +            } else {
 +                av_log(NULL, AV_LOG_FATAL, "Unknown program key %s.\n", key);
 +                exit_program(1);
 +            }
 +            av_freep(&to_dealloc);
 +            av_freep(&key);
 +        }
 +    }
 +
 +    /* process manually set metadata */
 +    for (i = 0; i < o->nb_metadata; i++) {
 +        AVDictionary **m;
 +        char type, *val;
 +        const char *stream_spec;
 +        int index = 0, j, ret = 0;
 +
 +        val = strchr(o->metadata[i].u.str, '=');
 +        if (!val) {
 +            av_log(NULL, AV_LOG_FATAL, "No '=' character in metadata string %s.\n",
 +                   o->metadata[i].u.str);
 +            exit_program(1);
 +        }
 +        *val++ = 0;
 +
 +        parse_meta_type(o->metadata[i].specifier, &type, &index, &stream_spec);
 +        if (type == 's') {
 +            for (j = 0; j < oc->nb_streams; j++) {
 +                ost = output_streams[nb_output_streams - oc->nb_streams + j];
 +                if ((ret = check_stream_specifier(oc, oc->streams[j], stream_spec)) > 0) {
 +                    if (!strcmp(o->metadata[i].u.str, "rotate")) {
 +                        char *tail;
 +                        double theta = av_strtod(val, &tail);
 +                        if (!*tail) {
 +                            ost->rotate_overridden = 1;
 +                            ost->rotate_override_value = theta;
 +                        }
 +                    } else {
 +                        av_dict_set(&oc->streams[j]->metadata, o->metadata[i].u.str, *val ? val : NULL, 0);
 +                    }
 +                } else if (ret < 0)
 +                    exit_program(1);
 +            }
 +        }
 +        else {
 +            switch (type) {
 +            case 'g':
 +                m = &oc->metadata;
 +                break;
 +            case 'c':
 +                if (index < 0 || index >= oc->nb_chapters) {
 +                    av_log(NULL, AV_LOG_FATAL, "Invalid chapter index %d in metadata specifier.\n", index);
 +                    exit_program(1);
 +                }
 +                m = &oc->chapters[index]->metadata;
 +                break;
 +            case 'p':
 +                if (index < 0 || index >= oc->nb_programs) {
 +                    av_log(NULL, AV_LOG_FATAL, "Invalid program index %d in metadata specifier.\n", index);
 +                    exit_program(1);
 +                }
 +                m = &oc->programs[index]->metadata;
 +                break;
 +            default:
 +                av_log(NULL, AV_LOG_FATAL, "Invalid metadata specifier %s.\n", o->metadata[i].specifier);
 +                exit_program(1);
 +            }
 +            av_dict_set(m, o->metadata[i].u.str, *val ? val : NULL, 0);
 +        }
 +    }
 +
 +    return 0;
 +}
 +
 +static int opt_target(void *optctx, const char *opt, const char *arg)
 +{
 +    OptionsContext *o = optctx;
 +    enum { PAL, NTSC, FILM, UNKNOWN } norm = UNKNOWN;
 +    static const char *const frame_rates[] = { "25", "30000/1001", "24000/1001" };
 +
 +    if (!strncmp(arg, "pal-", 4)) {
 +        norm = PAL;
 +        arg += 4;
 +    } else if (!strncmp(arg, "ntsc-", 5)) {
 +        norm = NTSC;
 +        arg += 5;
 +    } else if (!strncmp(arg, "film-", 5)) {
 +        norm = FILM;
 +        arg += 5;
 +    } else {
 +        /* Try to determine PAL/NTSC by peeking in the input files */
 +        if (nb_input_files) {
 +            int i, j, fr;
 +            for (j = 0; j < nb_input_files; j++) {
 +                for (i = 0; i < input_files[j]->nb_streams; i++) {
 +                    AVStream *st = input_files[j]->ctx->streams[i];
 +                    if (st->codecpar->codec_type != AVMEDIA_TYPE_VIDEO)
 +                        continue;
 +                    fr = st->time_base.den * 1000 / st->time_base.num;
 +                    if (fr == 25000) {
 +                        norm = PAL;
 +                        break;
 +                    } else if ((fr == 29970) || (fr == 23976)) {
 +                        norm = NTSC;
 +                        break;
 +                    }
 +                }
 +                if (norm != UNKNOWN)
 +                    break;
 +            }
 +        }
 +        if (norm != UNKNOWN)
 +            av_log(NULL, AV_LOG_INFO, "Assuming %s for target.\n", norm == PAL ? "PAL" : "NTSC");
 +    }
 +
 +    if (norm == UNKNOWN) {
 +        av_log(NULL, AV_LOG_FATAL, "Could not determine norm (PAL/NTSC/NTSC-Film) for target.\n");
 +        av_log(NULL, AV_LOG_FATAL, "Please prefix target with \"pal-\", \"ntsc-\" or \"film-\",\n");
 +        av_log(NULL, AV_LOG_FATAL, "or set a framerate with \"-r xxx\".\n");
 +        exit_program(1);
 +    }
 +
 +    if (!strcmp(arg, "vcd")) {
 +        opt_video_codec(o, "c:v", "mpeg1video");
 +        opt_audio_codec(o, "c:a", "mp2");
 +        parse_option(o, "f", "vcd", options);
 +
 +        parse_option(o, "s", norm == PAL ? "352x288" : "352x240", options);
 +        parse_option(o, "r", frame_rates[norm], options);
 +        opt_default(NULL, "g", norm == PAL ? "15" : "18");
 +
 +        opt_default(NULL, "b:v", "1150000");
 +        opt_default(NULL, "maxrate:v", "1150000");
 +        opt_default(NULL, "minrate:v", "1150000");
 +        opt_default(NULL, "bufsize:v", "327680"); // 40*1024*8;
 +
 +        opt_default(NULL, "b:a", "224000");
 +        parse_option(o, "ar", "44100", options);
 +        parse_option(o, "ac", "2", options);
 +
 +        opt_default(NULL, "packetsize", "2324");
 +        opt_default(NULL, "muxrate", "1411200"); // 2352 * 75 * 8;
 +
 +        /* We have to offset the PTS, so that it is consistent with the SCR.
 +           SCR starts at 36000, but the first two packs contain only padding
 +           and the first pack from the other stream, respectively, may also have
 +           been written before.
 +           So the real data starts at SCR 36000+3*1200. */
 +        o->mux_preload = (36000 + 3 * 1200) / 90000.0; // 0.44
 +    } else if (!strcmp(arg, "svcd")) {
 +
 +        opt_video_codec(o, "c:v", "mpeg2video");
 +        opt_audio_codec(o, "c:a", "mp2");
 +        parse_option(o, "f", "svcd", options);
 +
 +        parse_option(o, "s", norm == PAL ? "480x576" : "480x480", options);
 +        parse_option(o, "r", frame_rates[norm], options);
 +        parse_option(o, "pix_fmt", "yuv420p", options);
 +        opt_default(NULL, "g", norm == PAL ? "15" : "18");
 +
 +        opt_default(NULL, "b:v", "2040000");
 +        opt_default(NULL, "maxrate:v", "2516000");
 +        opt_default(NULL, "minrate:v", "0"); // 1145000;
 +        opt_default(NULL, "bufsize:v", "1835008"); // 224*1024*8;
 +        opt_default(NULL, "scan_offset", "1");
 +
 +        opt_default(NULL, "b:a", "224000");
 +        parse_option(o, "ar", "44100", options);
 +
 +        opt_default(NULL, "packetsize", "2324");
 +
 +    } else if (!strcmp(arg, "dvd")) {
 +
 +        opt_video_codec(o, "c:v", "mpeg2video");
 +        opt_audio_codec(o, "c:a", "ac3");
 +        parse_option(o, "f", "dvd", options);
 +
 +        parse_option(o, "s", norm == PAL ? "720x576" : "720x480", options);
 +        parse_option(o, "r", frame_rates[norm], options);
 +        parse_option(o, "pix_fmt", "yuv420p", options);
 +        opt_default(NULL, "g", norm == PAL ? "15" : "18");
 +
 +        opt_default(NULL, "b:v", "6000000");
 +        opt_default(NULL, "maxrate:v", "9000000");
 +        opt_default(NULL, "minrate:v", "0"); // 1500000;
 +        opt_default(NULL, "bufsize:v", "1835008"); // 224*1024*8;
 +
 +        opt_default(NULL, "packetsize", "2048");  // from www.mpucoder.com: DVD sectors contain 2048 bytes of data, this is also the size of one pack.
 +        opt_default(NULL, "muxrate", "10080000"); // from mplex project: data_rate = 1260000. mux_rate = data_rate * 8
 +
 +        opt_default(NULL, "b:a", "448000");
 +        parse_option(o, "ar", "48000", options);
 +
 +    } else if (!strncmp(arg, "dv", 2)) {
 +
 +        parse_option(o, "f", "dv", options);
 +
 +        parse_option(o, "s", norm == PAL ? "720x576" : "720x480", options);
 +        parse_option(o, "pix_fmt", !strncmp(arg, "dv50", 4) ? "yuv422p" :
 +                          norm == PAL ? "yuv420p" : "yuv411p", options);
 +        parse_option(o, "r", frame_rates[norm], options);
 +
 +        parse_option(o, "ar", "48000", options);
 +        parse_option(o, "ac", "2", options);
 +
 +    } else {
 +        av_log(NULL, AV_LOG_ERROR, "Unknown target: %s\n", arg);
 +        return AVERROR(EINVAL);
 +    }
 +
 +    av_dict_copy(&o->g->codec_opts,  codec_opts, AV_DICT_DONT_OVERWRITE);
 +    av_dict_copy(&o->g->format_opts, format_opts, AV_DICT_DONT_OVERWRITE);
 +
 +    return 0;
 +}
 +
 +static int opt_vstats_file(void *optctx, const char *opt, const char *arg)
 +{
 +    av_free (vstats_filename);
 +    vstats_filename = av_strdup (arg);
 +    return 0;
 +}
 +
 +static int opt_vstats(void *optctx, const char *opt, const char *arg)
 +{
 +    char filename[40];
 +    time_t today2 = time(NULL);
 +    struct tm *today = localtime(&today2);
 +
 +    if (!today) { // maybe tomorrow
 +        av_log(NULL, AV_LOG_FATAL, "Unable to get current time: %s\n", strerror(errno));
 +        exit_program(1);
 +    }
 +
 +    snprintf(filename, sizeof(filename), "vstats_%02d%02d%02d.log", today->tm_hour, today->tm_min,
 +             today->tm_sec);
 +    return opt_vstats_file(NULL, opt, filename);
 +}
 +
 +static int opt_video_frames(void *optctx, const char *opt, const char *arg)
 +{
 +    OptionsContext *o = optctx;
 +    return parse_option(o, "frames:v", arg, options);
 +}
 +
 +static int opt_audio_frames(void *optctx, const char *opt, const char *arg)
 +{
 +    OptionsContext *o = optctx;
 +    return parse_option(o, "frames:a", arg, options);
 +}
 +
 +static int opt_data_frames(void *optctx, const char *opt, const char *arg)
 +{
 +    OptionsContext *o = optctx;
 +    return parse_option(o, "frames:d", arg, options);
 +}
 +
 +static int opt_default_new(OptionsContext *o, const char *opt, const char *arg)
 +{
 +    int ret;
 +    AVDictionary *cbak = codec_opts;
 +    AVDictionary *fbak = format_opts;
 +    codec_opts = NULL;
 +    format_opts = NULL;
 +
 +    ret = opt_default(NULL, opt, arg);
 +
 +    av_dict_copy(&o->g->codec_opts , codec_opts, 0);
 +    av_dict_copy(&o->g->format_opts, format_opts, 0);
 +    av_dict_free(&codec_opts);
 +    av_dict_free(&format_opts);
 +    codec_opts = cbak;
 +    format_opts = fbak;
 +
 +    return ret;
 +}
 +
 +static int opt_preset(void *optctx, const char *opt, const char *arg)
 +{
 +    OptionsContext *o = optctx;
 +    FILE *f=NULL;
 +    char filename[1000], line[1000], tmp_line[1000];
 +    const char *codec_name = NULL;
 +
 +    tmp_line[0] = *opt;
 +    tmp_line[1] = 0;
 +    MATCH_PER_TYPE_OPT(codec_names, str, codec_name, NULL, tmp_line);
 +
 +    if (!(f = get_preset_file(filename, sizeof(filename), arg, *opt == 'f', codec_name))) {
 +        if(!strncmp(arg, "libx264-lossless", strlen("libx264-lossless"))){
 +            av_log(NULL, AV_LOG_FATAL, "Please use -preset <speed> -qp 0\n");
 +        }else
 +            av_log(NULL, AV_LOG_FATAL, "File for preset '%s' not found\n", arg);
 +        exit_program(1);
 +    }
 +
 +    while (fgets(line, sizeof(line), f)) {
 +        char *key = tmp_line, *value, *endptr;
 +
 +        if (strcspn(line, "#\n\r") == 0)
 +            continue;
 +        av_strlcpy(tmp_line, line, sizeof(tmp_line));
 +        if (!av_strtok(key,   "=",    &value) ||
 +            !av_strtok(value, "\r\n", &endptr)) {
 +            av_log(NULL, AV_LOG_FATAL, "%s: Invalid syntax: '%s'\n", filename, line);
 +            exit_program(1);
 +        }
 +        av_log(NULL, AV_LOG_DEBUG, "ffpreset[%s]: set '%s' = '%s'\n", filename, key, value);
 +
 +        if      (!strcmp(key, "acodec")) opt_audio_codec   (o, key, value);
 +        else if (!strcmp(key, "vcodec")) opt_video_codec   (o, key, value);
 +        else if (!strcmp(key, "scodec")) opt_subtitle_codec(o, key, value);
 +        else if (!strcmp(key, "dcodec")) opt_data_codec    (o, key, value);
 +        else if (opt_default_new(o, key, value) < 0) {
 +            av_log(NULL, AV_LOG_FATAL, "%s: Invalid option or argument: '%s', parsed as '%s' = '%s'\n",
 +                   filename, line, key, value);
 +            exit_program(1);
 +        }
 +    }
 +
 +    fclose(f);
 +
 +    return 0;
 +}
 +
 +static int opt_old2new(void *optctx, const char *opt, const char *arg)
 +{
 +    OptionsContext *o = optctx;
 +    char *s = av_asprintf("%s:%c", opt + 1, *opt);
 +    int ret = parse_option(o, s, arg, options);
 +    av_free(s);
 +    return ret;
 +}
 +
 +static int opt_bitrate(void *optctx, const char *opt, const char *arg)
 +{
 +    OptionsContext *o = optctx;
 +
 +    if(!strcmp(opt, "ab")){
 +        av_dict_set(&o->g->codec_opts, "b:a", arg, 0);
 +        return 0;
 +    } else if(!strcmp(opt, "b")){
 +        av_log(NULL, AV_LOG_WARNING, "Please use -b:a or -b:v, -b is ambiguous\n");
 +        av_dict_set(&o->g->codec_opts, "b:v", arg, 0);
 +        return 0;
 +    }
 +    av_dict_set(&o->g->codec_opts, opt, arg, 0);
 +    return 0;
 +}
 +
 +static int opt_qscale(void *optctx, const char *opt, const char *arg)
 +{
 +    OptionsContext *o = optctx;
 +    char *s;
 +    int ret;
 +    if(!strcmp(opt, "qscale")){
 +        av_log(NULL, AV_LOG_WARNING, "Please use -q:a or -q:v, -qscale is ambiguous\n");
 +        return parse_option(o, "q:v", arg, options);
 +    }
 +    s = av_asprintf("q%s", opt + 6);
 +    ret = parse_option(o, s, arg, options);
 +    av_free(s);
 +    return ret;
 +}
 +
 +static int opt_profile(void *optctx, const char *opt, const char *arg)
 +{
 +    OptionsContext *o = optctx;
 +    if(!strcmp(opt, "profile")){
 +        av_log(NULL, AV_LOG_WARNING, "Please use -profile:a or -profile:v, -profile is ambiguous\n");
 +        av_dict_set(&o->g->codec_opts, "profile:v", arg, 0);
 +        return 0;
 +    }
 +    av_dict_set(&o->g->codec_opts, opt, arg, 0);
 +    return 0;
 +}
 +
 +static int opt_video_filters(void *optctx, const char *opt, const char *arg)
 +{
 +    OptionsContext *o = optctx;
 +    return parse_option(o, "filter:v", arg, options);
 +}
 +
 +static int opt_audio_filters(void *optctx, const char *opt, const char *arg)
 +{
 +    OptionsContext *o = optctx;
 +    return parse_option(o, "filter:a", arg, options);
 +}
 +
 +static int opt_vsync(void *optctx, const char *opt, const char *arg)
 +{
 +    if      (!av_strcasecmp(arg, "cfr"))         video_sync_method = VSYNC_CFR;
 +    else if (!av_strcasecmp(arg, "vfr"))         video_sync_method = VSYNC_VFR;
 +    else if (!av_strcasecmp(arg, "passthrough")) video_sync_method = VSYNC_PASSTHROUGH;
 +    else if (!av_strcasecmp(arg, "drop"))        video_sync_method = VSYNC_DROP;
 +
 +    if (video_sync_method == VSYNC_AUTO)
 +        video_sync_method = parse_number_or_die("vsync", arg, OPT_INT, VSYNC_AUTO, VSYNC_VFR);
 +    return 0;
 +}
 +
 +static int opt_timecode(void *optctx, const char *opt, const char *arg)
 +{
 +    OptionsContext *o = optctx;
 +    char *tcr = av_asprintf("timecode=%s", arg);
 +    int ret = parse_option(o, "metadata:g", tcr, options);
 +    if (ret >= 0)
 +        ret = av_dict_set(&o->g->codec_opts, "gop_timecode", arg, 0);
 +    av_free(tcr);
 +    return 0;
 +}
 +
 +static int opt_channel_layout(void *optctx, const char *opt, const char *arg)
 +{
 +    OptionsContext *o = optctx;
 +    char layout_str[32];
 +    char *stream_str;
 +    char *ac_str;
 +    int ret, channels, ac_str_size;
 +    uint64_t layout;
 +
 +    layout = av_get_channel_layout(arg);
 +    if (!layout) {
 +        av_log(NULL, AV_LOG_ERROR, "Unknown channel layout: %s\n", arg);
 +        return AVERROR(EINVAL);
 +    }
 +    snprintf(layout_str, sizeof(layout_str), "%"PRIu64, layout);
 +    ret = opt_default_new(o, opt, layout_str);
 +    if (ret < 0)
 +        return ret;
 +
 +    /* set 'ac' option based on channel layout */
 +    channels = av_get_channel_layout_nb_channels(layout);
 +    snprintf(layout_str, sizeof(layout_str), "%d", channels);
 +    stream_str = strchr(opt, ':');
 +    ac_str_size = 3 + (stream_str ? strlen(stream_str) : 0);
 +    ac_str = av_mallocz(ac_str_size);
 +    if (!ac_str)
 +        return AVERROR(ENOMEM);
 +    av_strlcpy(ac_str, "ac", 3);
 +    if (stream_str)
 +        av_strlcat(ac_str, stream_str, ac_str_size);
 +    ret = parse_option(o, ac_str, layout_str, options);
 +    av_free(ac_str);
 +
 +    return ret;
 +}
 +
 +static int opt_audio_qscale(void *optctx, const char *opt, const char *arg)
 +{
 +    OptionsContext *o = optctx;
 +    return parse_option(o, "q:a", arg, options);
 +}
 +
 +static int opt_filter_complex(void *optctx, const char *opt, const char *arg)
 +{
 +    GROW_ARRAY(filtergraphs, nb_filtergraphs);
 +    if (!(filtergraphs[nb_filtergraphs - 1] = av_mallocz(sizeof(*filtergraphs[0]))))
 +        return AVERROR(ENOMEM);
 +    filtergraphs[nb_filtergraphs - 1]->index      = nb_filtergraphs - 1;
 +    filtergraphs[nb_filtergraphs - 1]->graph_desc = av_strdup(arg);
 +    if (!filtergraphs[nb_filtergraphs - 1]->graph_desc)
 +        return AVERROR(ENOMEM);
 +
 +    input_stream_potentially_available = 1;
 +
 +    return 0;
 +}
 +
 +static int opt_filter_complex_script(void *optctx, const char *opt, const char *arg)
 +{
 +    uint8_t *graph_desc = read_file(arg);
 +    if (!graph_desc)
 +        return AVERROR(EINVAL);
 +
 +    GROW_ARRAY(filtergraphs, nb_filtergraphs);
 +    if (!(filtergraphs[nb_filtergraphs - 1] = av_mallocz(sizeof(*filtergraphs[0]))))
 +        return AVERROR(ENOMEM);
 +    filtergraphs[nb_filtergraphs - 1]->index      = nb_filtergraphs - 1;
 +    filtergraphs[nb_filtergraphs - 1]->graph_desc = graph_desc;
 +
 +    input_stream_potentially_available = 1;
 +
 +    return 0;
 +}
 +
 +void show_help_default(const char *opt, const char *arg)
 +{
 +    /* per-file options have at least one of those set */
 +    const int per_file = OPT_SPEC | OPT_OFFSET | OPT_PERFILE;
 +    int show_advanced = 0, show_avoptions = 0;
 +
 +    if (opt && *opt) {
 +        if (!strcmp(opt, "long"))
 +            show_advanced = 1;
 +        else if (!strcmp(opt, "full"))
 +            show_advanced = show_avoptions = 1;
 +        else
 +            av_log(NULL, AV_LOG_ERROR, "Unknown help option '%s'.\n", opt);
 +    }
 +
 +    show_usage();
 +
 +    printf("Getting help:\n"
 +           "    -h      -- print basic options\n"
 +           "    -h long -- print more options\n"
 +           "    -h full -- print all options (including all format and codec specific options, very long)\n"
 +           "    -h type=name -- print all options for the named decoder/encoder/demuxer/muxer/filter\n"
 +           "    See man %s for detailed description of the options.\n"
 +           "\n", program_name);
 +
 +    show_help_options(options, "Print help / information / capabilities:",
 +                      OPT_EXIT, 0, 0);
 +
 +    show_help_options(options, "Global options (affect whole program "
 +                      "instead of just one file:",
 +                      0, per_file | OPT_EXIT | OPT_EXPERT, 0);
 +    if (show_advanced)
 +        show_help_options(options, "Advanced global options:", OPT_EXPERT,
 +                          per_file | OPT_EXIT, 0);
 +
 +    show_help_options(options, "Per-file main options:", 0,
 +                      OPT_EXPERT | OPT_AUDIO | OPT_VIDEO | OPT_SUBTITLE |
 +                      OPT_EXIT, per_file);
 +    if (show_advanced)
 +        show_help_options(options, "Advanced per-file options:",
 +                          OPT_EXPERT, OPT_AUDIO | OPT_VIDEO | OPT_SUBTITLE, per_file);
 +
 +    show_help_options(options, "Video options:",
 +                      OPT_VIDEO, OPT_EXPERT | OPT_AUDIO, 0);
 +    if (show_advanced)
 +        show_help_options(options, "Advanced Video options:",
 +                          OPT_EXPERT | OPT_VIDEO, OPT_AUDIO, 0);
 +
 +    show_help_options(options, "Audio options:",
 +                      OPT_AUDIO, OPT_EXPERT | OPT_VIDEO, 0);
 +    if (show_advanced)
 +        show_help_options(options, "Advanced Audio options:",
 +                          OPT_EXPERT | OPT_AUDIO, OPT_VIDEO, 0);
 +    show_help_options(options, "Subtitle options:",
 +                      OPT_SUBTITLE, 0, 0);
 +    printf("\n");
 +
 +    if (show_avoptions) {
 +        int flags = AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_ENCODING_PARAM;
 +        show_help_children(avcodec_get_class(), flags);
 +        show_help_children(avformat_get_class(), flags);
 +#if CONFIG_SWSCALE
 +        show_help_children(sws_get_class(), flags);
 +#endif
 +        show_help_children(swr_get_class(), AV_OPT_FLAG_AUDIO_PARAM);
 +        show_help_children(avfilter_get_class(), AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_FILTERING_PARAM);
 +    }
 +}
 +
 +void show_usage(void)
 +{
 +    av_log(NULL, AV_LOG_INFO, "Hyper fast Audio and Video encoder\n");
 +    av_log(NULL, AV_LOG_INFO, "usage: %s [options] [[infile options] -i infile]... {[outfile options] outfile}...\n", program_name);
 +    av_log(NULL, AV_LOG_INFO, "\n");
 +}
 +
 +enum OptGroup {
 +    GROUP_OUTFILE,
 +    GROUP_INFILE,
 +};
 +
 +static const OptionGroupDef groups[] = {
 +    [GROUP_OUTFILE] = { "output url",  NULL, OPT_OUTPUT },
 +    [GROUP_INFILE]  = { "input url",   "i",  OPT_INPUT },
 +};
 +
 +static int open_files(OptionGroupList *l, const char *inout,
 +                      int (*open_file)(OptionsContext*, const char*))
 +{
 +    int i, ret;
 +
 +    for (i = 0; i < l->nb_groups; i++) {
 +        OptionGroup *g = &l->groups[i];
 +        OptionsContext o;
 +
 +        init_options(&o);
 +        o.g = g;
 +
 +        ret = parse_optgroup(&o, g);
 +        if (ret < 0) {
 +            av_log(NULL, AV_LOG_ERROR, "Error parsing options for %s file "
 +                   "%s.\n", inout, g->arg);
 +            return ret;
 +        }
 +
 +        av_log(NULL, AV_LOG_DEBUG, "Opening an %s file: %s.\n", inout, g->arg);
 +        ret = open_file(&o, g->arg);
 +        uninit_options(&o);
 +        if (ret < 0) {
 +            av_log(NULL, AV_LOG_ERROR, "Error opening %s file %s.\n",
 +                   inout, g->arg);
 +            return ret;
 +        }
 +        av_log(NULL, AV_LOG_DEBUG, "Successfully opened the file.\n");
 +    }
 +
 +    return 0;
 +}
 +
 +int ffmpeg_parse_options(int argc, char **argv)
 +{
 +    OptionParseContext octx;
 +    uint8_t error[128];
 +    int ret;
 +
 +    memset(&octx, 0, sizeof(octx));
 +
 +    /* split the commandline into an internal representation */
 +    ret = split_commandline(&octx, argc, argv, options, groups,
 +                            FF_ARRAY_ELEMS(groups));
 +    if (ret < 0) {
 +        av_log(NULL, AV_LOG_FATAL, "Error splitting the argument list: ");
 +        goto fail;
 +    }
 +
 +    /* apply global options */
 +    ret = parse_optgroup(NULL, &octx.global_opts);
 +    if (ret < 0) {
 +        av_log(NULL, AV_LOG_FATAL, "Error parsing global options: ");
 +        goto fail;
 +    }
 +
 +    /* configure terminal and setup signal handlers */
 +    term_init();
 +
 +    /* open input files */
 +    ret = open_files(&octx.groups[GROUP_INFILE], "input", open_input_file);
 +    if (ret < 0) {
 +        av_log(NULL, AV_LOG_FATAL, "Error opening input files: ");
 +        goto fail;
 +    }
 +
 +    /* create the complex filtergraphs */
 +    ret = init_complex_filters();
 +    if (ret < 0) {
 +        av_log(NULL, AV_LOG_FATAL, "Error initializing complex filters.\n");
 +        goto fail;
 +    }
 +
 +    /* open output files */
 +    ret = open_files(&octx.groups[GROUP_OUTFILE], "output", open_output_file);
 +    if (ret < 0) {
 +        av_log(NULL, AV_LOG_FATAL, "Error opening output files: ");
 +        goto fail;
 +    }
 +
 +    check_filter_outputs();
 +
 +fail:
 +    uninit_parse_context(&octx);
 +    if (ret < 0) {
 +        av_strerror(ret, error, sizeof(error));
 +        av_log(NULL, AV_LOG_FATAL, "%s\n", error);
 +    }
 +    return ret;
 +}
 +
 +static int opt_progress(void *optctx, const char *opt, const char *arg)
 +{
 +    AVIOContext *avio = NULL;
 +    int ret;
 +
 +    if (!strcmp(arg, "-"))
 +        arg = "pipe:";
 +    ret = avio_open2(&avio, arg, AVIO_FLAG_WRITE, &int_cb, NULL);
 +    if (ret < 0) {
 +        av_log(NULL, AV_LOG_ERROR, "Failed to open progress URL \"%s\": %s\n",
 +               arg, av_err2str(ret));
 +        return ret;
 +    }
 +    progress_avio = avio;
 +    return 0;
 +}
 +
 +#define OFFSET(x) offsetof(OptionsContext, x)
 +const OptionDef options[] = {
 +    /* main options */
++    CMDUTILS_COMMON_OPTIONS
 +    { "f",              HAS_ARG | OPT_STRING | OPT_OFFSET |
 +                        OPT_INPUT | OPT_OUTPUT,                      { .off       = OFFSET(format) },
 +        "force format", "fmt" },
 +    { "y",              OPT_BOOL,                                    {              &file_overwrite },
 +        "overwrite output files" },
 +    { "n",              OPT_BOOL,                                    {              &no_file_overwrite },
 +        "never overwrite output files" },
 +    { "ignore_unknown", OPT_BOOL,                                    {              &ignore_unknown_streams },
 +        "Ignore unknown stream types" },
 +    { "copy_unknown",   OPT_BOOL | OPT_EXPERT,                       {              &copy_unknown_streams },
 +        "Copy unknown stream types" },
 +    { "c",              HAS_ARG | OPT_STRING | OPT_SPEC |
 +                        OPT_INPUT | OPT_OUTPUT,                      { .off       = OFFSET(codec_names) },
 +        "codec name", "codec" },
 +    { "codec",          HAS_ARG | OPT_STRING | OPT_SPEC |
 +                        OPT_INPUT | OPT_OUTPUT,                      { .off       = OFFSET(codec_names) },
 +        "codec name", "codec" },
 +    { "pre",            HAS_ARG | OPT_STRING | OPT_SPEC |
 +                        OPT_OUTPUT,                                  { .off       = OFFSET(presets) },
 +        "preset name", "preset" },
 +    { "map",            HAS_ARG | OPT_EXPERT | OPT_PERFILE |
 +                        OPT_OUTPUT,                                  { .func_arg = opt_map },
 +        "set input stream mapping",
 +        "[-]input_file_id[:stream_specifier][,sync_file_id[:stream_specifier]]" },
 +    { "map_channel",    HAS_ARG | OPT_EXPERT | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_map_channel },
 +        "map an audio channel from one stream to another", "file.stream.channel[:syncfile.syncstream]" },
 +    { "map_metadata",   HAS_ARG | OPT_STRING | OPT_SPEC |
 +                        OPT_OUTPUT,                                  { .off       = OFFSET(metadata_map) },
 +        "set metadata information of outfile from infile",
 +        "outfile[,metadata]:infile[,metadata]" },
 +    { "map_chapters",   HAS_ARG | OPT_INT | OPT_EXPERT | OPT_OFFSET |
 +                        OPT_OUTPUT,                                  { .off = OFFSET(chapters_input_file) },
 +        "set chapters mapping", "input_file_index" },
 +    { "t",              HAS_ARG | OPT_TIME | OPT_OFFSET |
 +                        OPT_INPUT | OPT_OUTPUT,                      { .off = OFFSET(recording_time) },
 +        "record or transcode \"duration\" seconds of audio/video",
 +        "duration" },
 +    { "to",             HAS_ARG | OPT_TIME | OPT_OFFSET | OPT_OUTPUT,  { .off = OFFSET(stop_time) },
 +        "record or transcode stop time", "time_stop" },
 +    { "fs",             HAS_ARG | OPT_INT64 | OPT_OFFSET | OPT_OUTPUT, { .off = OFFSET(limit_filesize) },
 +        "set the limit file size in bytes", "limit_size" },
 +    { "ss",             HAS_ARG | OPT_TIME | OPT_OFFSET |
 +                        OPT_INPUT | OPT_OUTPUT,                      { .off = OFFSET(start_time) },
 +        "set the start time offset", "time_off" },
 +    { "sseof",          HAS_ARG | OPT_TIME | OPT_OFFSET |
 +                        OPT_INPUT | OPT_OUTPUT,                      { .off = OFFSET(start_time_eof) },
 +        "set the start time offset relative to EOF", "time_off" },
 +    { "seek_timestamp", HAS_ARG | OPT_INT | OPT_OFFSET |
 +                        OPT_INPUT,                                   { .off = OFFSET(seek_timestamp) },
 +        "enable/disable seeking by timestamp with -ss" },
 +    { "accurate_seek",  OPT_BOOL | OPT_OFFSET | OPT_EXPERT |
 +                        OPT_INPUT,                                   { .off = OFFSET(accurate_seek) },
 +        "enable/disable accurate seeking with -ss" },
 +    { "itsoffset",      HAS_ARG | OPT_TIME | OPT_OFFSET |
 +                        OPT_EXPERT | OPT_INPUT,                      { .off = OFFSET(input_ts_offset) },
 +        "set the input ts offset", "time_off" },
 +    { "itsscale",       HAS_ARG | OPT_DOUBLE | OPT_SPEC |
 +                        OPT_EXPERT | OPT_INPUT,                      { .off = OFFSET(ts_scale) },
 +        "set the input ts scale", "scale" },
 +    { "timestamp",      HAS_ARG | OPT_PERFILE | OPT_OUTPUT,          { .func_arg = opt_recording_timestamp },
 +        "set the recording timestamp ('now' to set the current time)", "time" },
 +    { "metadata",       HAS_ARG | OPT_STRING | OPT_SPEC | OPT_OUTPUT, { .off = OFFSET(metadata) },
 +        "add metadata", "string=string" },
 +    { "program",        HAS_ARG | OPT_STRING | OPT_SPEC | OPT_OUTPUT, { .off = OFFSET(program) },
 +        "add program with specified streams", "title=string:st=number..." },
 +    { "dframes",        HAS_ARG | OPT_PERFILE | OPT_EXPERT |
 +                        OPT_OUTPUT,                                  { .func_arg = opt_data_frames },
 +        "set the number of data frames to output", "number" },
 +    { "benchmark",      OPT_BOOL | OPT_EXPERT,                       { &do_benchmark },
 +        "add timings for benchmarking" },
 +    { "benchmark_all",  OPT_BOOL | OPT_EXPERT,                       { &do_benchmark_all },
 +      "add timings for each task" },
 +    { "progress",       HAS_ARG | OPT_EXPERT,                        { .func_arg = opt_progress },
 +      "write program-readable progress information", "url" },
 +    { "stdin",          OPT_BOOL | OPT_EXPERT,                       { &stdin_interaction },
 +      "enable or disable interaction on standard input" },
 +    { "timelimit",      HAS_ARG | OPT_EXPERT,                        { .func_arg = opt_timelimit },
 +        "set max runtime in seconds", "limit" },
 +    { "dump",           OPT_BOOL | OPT_EXPERT,                       { &do_pkt_dump },
 +        "dump each input packet" },
 +    { "hex",            OPT_BOOL | OPT_EXPERT,                       { &do_hex_dump },
 +        "when dumping packets, also dump the payload" },
 +    { "re",             OPT_BOOL | OPT_EXPERT | OPT_OFFSET |
 +                        OPT_INPUT,                                   { .off = OFFSET(rate_emu) },
 +        "read input at native frame rate", "" },
 +    { "target",         HAS_ARG | OPT_PERFILE | OPT_OUTPUT,          { .func_arg = opt_target },
 +        "specify target file type (\"vcd\", \"svcd\", \"dvd\", \"dv\" or \"dv50\" "
 +        "with optional prefixes \"pal-\", \"ntsc-\" or \"film-\")", "type" },
 +    { "vsync",          HAS_ARG | OPT_EXPERT,                        { .func_arg = opt_vsync },
 +        "video sync method", "" },
 +    { "frame_drop_threshold", HAS_ARG | OPT_FLOAT | OPT_EXPERT,      { &frame_drop_threshold },
 +        "frame drop threshold", "" },
 +    { "async",          HAS_ARG | OPT_INT | OPT_EXPERT,              { &audio_sync_method },
 +        "audio sync method", "" },
 +    { "adrift_threshold", HAS_ARG | OPT_FLOAT | OPT_EXPERT,          { &audio_drift_threshold },
 +        "audio drift threshold", "threshold" },
 +    { "copyts",         OPT_BOOL | OPT_EXPERT,                       { &copy_ts },
 +        "copy timestamps" },
 +    { "start_at_zero",  OPT_BOOL | OPT_EXPERT,                       { &start_at_zero },
 +        "shift input timestamps to start at 0 when using copyts" },
 +    { "copytb",         HAS_ARG | OPT_INT | OPT_EXPERT,              { &copy_tb },
 +        "copy input stream time base when stream copying", "mode" },
 +    { "shortest",       OPT_BOOL | OPT_EXPERT | OPT_OFFSET |
 +                        OPT_OUTPUT,                                  { .off = OFFSET(shortest) },
 +        "finish encoding within shortest input" },
 +    { "apad",           OPT_STRING | HAS_ARG | OPT_SPEC |
 +                        OPT_OUTPUT,                                  { .off = OFFSET(apad) },
 +        "audio pad", "" },
 +    { "dts_delta_threshold", HAS_ARG | OPT_FLOAT | OPT_EXPERT,       { &dts_delta_threshold },
 +        "timestamp discontinuity delta threshold", "threshold" },
 +    { "dts_error_threshold", HAS_ARG | OPT_FLOAT | OPT_EXPERT,       { &dts_error_threshold },
 +        "timestamp error delta threshold", "threshold" },
 +    { "xerror",         OPT_BOOL | OPT_EXPERT,                       { &exit_on_error },
 +        "exit on error", "error" },
 +    { "abort_on",       HAS_ARG | OPT_EXPERT,                        { .func_arg = opt_abort_on },
 +        "abort on the specified condition flags", "flags" },
 +    { "copyinkf",       OPT_BOOL | OPT_EXPERT | OPT_SPEC |
 +                        OPT_OUTPUT,                                  { .off = OFFSET(copy_initial_nonkeyframes) },
 +        "copy initial non-keyframes" },
 +    { "copypriorss",    OPT_INT | HAS_ARG | OPT_EXPERT | OPT_SPEC | OPT_OUTPUT,   { .off = OFFSET(copy_prior_start) },
 +        "copy or discard frames before start time" },
 +    { "frames",         OPT_INT64 | HAS_ARG | OPT_SPEC | OPT_OUTPUT, { .off = OFFSET(max_frames) },
 +        "set the number of frames to output", "number" },
 +    { "tag",            OPT_STRING | HAS_ARG | OPT_SPEC |
 +                        OPT_EXPERT | OPT_OUTPUT | OPT_INPUT,         { .off = OFFSET(codec_tags) },
 +        "force codec tag/fourcc", "fourcc/tag" },
 +    { "q",              HAS_ARG | OPT_EXPERT | OPT_DOUBLE |
 +                        OPT_SPEC | OPT_OUTPUT,                       { .off = OFFSET(qscale) },
 +        "use fixed quality scale (VBR)", "q" },
 +    { "qscale",         HAS_ARG | OPT_EXPERT | OPT_PERFILE |
 +                        OPT_OUTPUT,                                  { .func_arg = opt_qscale },
 +        "use fixed quality scale (VBR)", "q" },
 +    { "profile",        HAS_ARG | OPT_EXPERT | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_profile },
 +        "set profile", "profile" },
 +    { "filter",         HAS_ARG | OPT_STRING | OPT_SPEC | OPT_OUTPUT, { .off = OFFSET(filters) },
 +        "set stream filtergraph", "filter_graph" },
 +    { "filter_threads",  HAS_ARG | OPT_INT,                          { &filter_nbthreads },
 +        "number of non-complex filter threads" },
 +    { "filter_script",  HAS_ARG | OPT_STRING | OPT_SPEC | OPT_OUTPUT, { .off = OFFSET(filter_scripts) },
 +        "read stream filtergraph description from a file", "filename" },
 +    { "reinit_filter",  HAS_ARG | OPT_INT | OPT_SPEC | OPT_INPUT,    { .off = OFFSET(reinit_filters) },
 +        "reinit filtergraph on input parameter changes", "" },
 +    { "filter_complex", HAS_ARG | OPT_EXPERT,                        { .func_arg = opt_filter_complex },
 +        "create a complex filtergraph", "graph_description" },
 +    { "filter_complex_threads", HAS_ARG | OPT_INT,                   { &filter_complex_nbthreads },
 +        "number of threads for -filter_complex" },
 +    { "lavfi",          HAS_ARG | OPT_EXPERT,                        { .func_arg = opt_filter_complex },
 +        "create a complex filtergraph", "graph_description" },
 +    { "filter_complex_script", HAS_ARG | OPT_EXPERT,                 { .func_arg = opt_filter_complex_script },
 +        "read complex filtergraph description from a file", "filename" },
 +    { "stats",          OPT_BOOL,                                    { &print_stats },
 +        "print progress report during encoding", },
 +    { "attach",         HAS_ARG | OPT_PERFILE | OPT_EXPERT |
 +                        OPT_OUTPUT,                                  { .func_arg = opt_attach },
 +        "add an attachment to the output file", "filename" },
 +    { "dump_attachment", HAS_ARG | OPT_STRING | OPT_SPEC |
 +                         OPT_EXPERT | OPT_INPUT,                     { .off = OFFSET(dump_attachment) },
 +        "extract an attachment into a file", "filename" },
 +    { "stream_loop", OPT_INT | HAS_ARG | OPT_EXPERT | OPT_INPUT |
 +                        OPT_OFFSET,                                  { .off = OFFSET(loop) }, "set number of times input stream shall be looped", "loop count" },
 +    { "debug_ts",       OPT_BOOL | OPT_EXPERT,                       { &debug_ts },
 +        "print timestamp debugging info" },
 +    { "max_error_rate",  HAS_ARG | OPT_FLOAT,                        { &max_error_rate },
 +        "maximum error rate", "ratio of errors (0.0: no errors, 1.0: 100% errors) above which ffmpeg returns an error instead of success." },
 +    { "discard",        OPT_STRING | HAS_ARG | OPT_SPEC |
 +                        OPT_INPUT,                                   { .off = OFFSET(discard) },
 +        "discard", "" },
 +    { "disposition",    OPT_STRING | HAS_ARG | OPT_SPEC |
 +                        OPT_OUTPUT,                                  { .off = OFFSET(disposition) },
 +        "disposition", "" },
 +    { "thread_queue_size", HAS_ARG | OPT_INT | OPT_OFFSET | OPT_EXPERT | OPT_INPUT,
 +                                                                     { .off = OFFSET(thread_queue_size) },
 +        "set the maximum number of queued packets from the demuxer" },
 +
 +    /* video options */
 +    { "vframes",      OPT_VIDEO | HAS_ARG  | OPT_PERFILE | OPT_OUTPUT,           { .func_arg = opt_video_frames },
 +        "set the number of video frames to output", "number" },
 +    { "r",            OPT_VIDEO | HAS_ARG  | OPT_STRING | OPT_SPEC |
 +                      OPT_INPUT | OPT_OUTPUT,                                    { .off = OFFSET(frame_rates) },
 +        "set frame rate (Hz value, fraction or abbreviation)", "rate" },
 +    { "s",            OPT_VIDEO | HAS_ARG | OPT_SUBTITLE | OPT_STRING | OPT_SPEC |
 +                      OPT_INPUT | OPT_OUTPUT,                                    { .off = OFFSET(frame_sizes) },
 +        "set frame size (WxH or abbreviation)", "size" },
 +    { "aspect",       OPT_VIDEO | HAS_ARG  | OPT_STRING | OPT_SPEC |
 +                      OPT_OUTPUT,                                                { .off = OFFSET(frame_aspect_ratios) },
 +        "set aspect ratio (4:3, 16:9 or 1.3333, 1.7777)", "aspect" },
 +    { "pix_fmt",      OPT_VIDEO | HAS_ARG | OPT_EXPERT  | OPT_STRING | OPT_SPEC |
 +                      OPT_INPUT | OPT_OUTPUT,                                    { .off = OFFSET(frame_pix_fmts) },
 +        "set pixel format", "format" },
 +    { "bits_per_raw_sample", OPT_VIDEO | OPT_INT | HAS_ARG,                      { &frame_bits_per_raw_sample },
 +        "set the number of bits per raw sample", "number" },
 +    { "intra",        OPT_VIDEO | OPT_BOOL | OPT_EXPERT,                         { &intra_only },
 +        "deprecated use -g 1" },
 +    { "vn",           OPT_VIDEO | OPT_BOOL  | OPT_OFFSET | OPT_INPUT | OPT_OUTPUT,{ .off = OFFSET(video_disable) },
 +        "disable video" },
 +    { "rc_override",  OPT_VIDEO | HAS_ARG | OPT_EXPERT  | OPT_STRING | OPT_SPEC |
 +                      OPT_OUTPUT,                                                { .off = OFFSET(rc_overrides) },
 +        "rate control override for specific intervals", "override" },
 +    { "vcodec",       OPT_VIDEO | HAS_ARG  | OPT_PERFILE | OPT_INPUT |
 +                      OPT_OUTPUT,                                                { .func_arg = opt_video_codec },
 +        "force video codec ('copy' to copy stream)", "codec" },
 +    { "sameq",        OPT_VIDEO | OPT_EXPERT ,                                   { .func_arg = opt_sameq },
 +        "Removed" },
 +    { "same_quant",   OPT_VIDEO | OPT_EXPERT ,                                   { .func_arg = opt_sameq },
 +        "Removed" },
 +    { "timecode",     OPT_VIDEO | HAS_ARG | OPT_PERFILE | OPT_OUTPUT,            { .func_arg = opt_timecode },
 +        "set initial TimeCode value.", "hh:mm:ss[:;.]ff" },
 +    { "pass",         OPT_VIDEO | HAS_ARG | OPT_SPEC | OPT_INT | OPT_OUTPUT,     { .off = OFFSET(pass) },
 +        "select the pass number (1 to 3)", "n" },
 +    { "passlogfile",  OPT_VIDEO | HAS_ARG | OPT_STRING | OPT_EXPERT | OPT_SPEC |
 +                      OPT_OUTPUT,                                                { .off = OFFSET(passlogfiles) },
 +        "select two pass log file name prefix", "prefix" },
 +    { "deinterlace",  OPT_VIDEO | OPT_BOOL | OPT_EXPERT,                         { &do_deinterlace },
 +        "this option is deprecated, use the yadif filter instead" },
 +    { "psnr",         OPT_VIDEO | OPT_BOOL | OPT_EXPERT,                         { &do_psnr },
 +        "calculate PSNR of compressed frames" },
 +    { "vstats",       OPT_VIDEO | OPT_EXPERT ,                                   { .func_arg = opt_vstats },
 +        "dump video coding statistics to file" },
 +    { "vstats_file",  OPT_VIDEO | HAS_ARG | OPT_EXPERT ,                         { .func_arg = opt_vstats_file },
 +        "dump video coding statistics to file", "file" },
 +    { "vstats_version",  OPT_VIDEO | OPT_INT | HAS_ARG | OPT_EXPERT ,            { &vstats_version },
 +        "Version of the vstats format to use."},
 +    { "vf",           OPT_VIDEO | HAS_ARG  | OPT_PERFILE | OPT_OUTPUT,           { .func_arg = opt_video_filters },
 +        "set video filters", "filter_graph" },
 +    { "intra_matrix", OPT_VIDEO | HAS_ARG | OPT_EXPERT  | OPT_STRING | OPT_SPEC |
 +                      OPT_OUTPUT,                                                { .off = OFFSET(intra_matrices) },
 +        "specify intra matrix coeffs", "matrix" },
 +    { "inter_matrix", OPT_VIDEO | HAS_ARG | OPT_EXPERT  | OPT_STRING | OPT_SPEC |
 +                      OPT_OUTPUT,                                                { .off = OFFSET(inter_matrices) },
 +        "specify inter matrix coeffs", "matrix" },
 +    { "chroma_intra_matrix", OPT_VIDEO | HAS_ARG | OPT_EXPERT  | OPT_STRING | OPT_SPEC |
 +                      OPT_OUTPUT,                                                { .off = OFFSET(chroma_intra_matrices) },
 +        "specify intra matrix coeffs", "matrix" },
 +    { "top",          OPT_VIDEO | HAS_ARG | OPT_EXPERT  | OPT_INT| OPT_SPEC |
 +                      OPT_INPUT | OPT_OUTPUT,                                    { .off = OFFSET(top_field_first) },
 +        "top=1/bottom=0/auto=-1 field first", "" },
 +    { "vtag",         OPT_VIDEO | HAS_ARG | OPT_EXPERT  | OPT_PERFILE |
 +                      OPT_INPUT | OPT_OUTPUT,                                    { .func_arg = opt_old2new },
 +        "force video tag/fourcc", "fourcc/tag" },
 +    { "qphist",       OPT_VIDEO | OPT_BOOL | OPT_EXPERT ,                        { &qp_hist },
 +        "show QP histogram" },
 +    { "force_fps",    OPT_VIDEO | OPT_BOOL | OPT_EXPERT  | OPT_SPEC |
 +                      OPT_OUTPUT,                                                { .off = OFFSET(force_fps) },
 +        "force the selected framerate, disable the best supported framerate selection" },
 +    { "streamid",     OPT_VIDEO | HAS_ARG | OPT_EXPERT | OPT_PERFILE |
 +                      OPT_OUTPUT,                                                { .func_arg = opt_streamid },
 +        "set the value of an outfile streamid", "streamIndex:value" },
 +    { "force_key_frames", OPT_VIDEO | OPT_STRING | HAS_ARG | OPT_EXPERT |
 +                          OPT_SPEC | OPT_OUTPUT,                                 { .off = OFFSET(forced_key_frames) },
 +        "force key frames at specified timestamps", "timestamps" },
 +    { "ab",           OPT_VIDEO | HAS_ARG | OPT_PERFILE | OPT_OUTPUT,            { .func_arg = opt_bitrate },
 +        "audio bitrate (please use -b:a)", "bitrate" },
 +    { "b",            OPT_VIDEO | HAS_ARG | OPT_PERFILE | OPT_OUTPUT,            { .func_arg = opt_bitrate },
 +        "video bitrate (please use -b:v)", "bitrate" },
 +    { "hwaccel",          OPT_VIDEO | OPT_STRING | HAS_ARG | OPT_EXPERT |
 +                          OPT_SPEC | OPT_INPUT,                                  { .off = OFFSET(hwaccels) },
 +        "use HW accelerated decoding", "hwaccel name" },
 +    { "hwaccel_device",   OPT_VIDEO | OPT_STRING | HAS_ARG | OPT_EXPERT |
 +                          OPT_SPEC | OPT_INPUT,                                  { .off = OFFSET(hwaccel_devices) },
 +        "select a device for HW acceleration", "devicename" },
 +    { "hwaccel_output_format", OPT_VIDEO | OPT_STRING | HAS_ARG | OPT_EXPERT |
 +                          OPT_SPEC | OPT_INPUT,                                  { .off = OFFSET(hwaccel_output_formats) },
 +        "select output format used with HW accelerated decoding", "format" },
 +#if CONFIG_VDA || CONFIG_VIDEOTOOLBOX
 +    { "videotoolbox_pixfmt", HAS_ARG | OPT_STRING | OPT_EXPERT, { &videotoolbox_pixfmt}, "" },
 +#endif
 +    { "hwaccels",         OPT_EXIT,                                              { .func_arg = show_hwaccels },
 +        "show available HW acceleration methods" },
 +    { "autorotate",       HAS_ARG | OPT_BOOL | OPT_SPEC |
 +                          OPT_EXPERT | OPT_INPUT,                                { .off = OFFSET(autorotate) },
 +        "automatically insert correct rotate filters" },
 +    { "hwaccel_lax_profile_check", OPT_BOOL | OPT_EXPERT,                        { &hwaccel_lax_profile_check},
 +        "attempt to decode anyway if HW accelerated decoder's supported profiles do not exactly match the stream" },
 +
 +    /* audio options */
 +    { "aframes",        OPT_AUDIO | HAS_ARG  | OPT_PERFILE | OPT_OUTPUT,           { .func_arg = opt_audio_frames },
 +        "set the number of audio frames to output", "number" },
 +    { "aq",             OPT_AUDIO | HAS_ARG  | OPT_PERFILE | OPT_OUTPUT,           { .func_arg = opt_audio_qscale },
 +        "set audio quality (codec-specific)", "quality", },
 +    { "ar",             OPT_AUDIO | HAS_ARG  | OPT_INT | OPT_SPEC |
 +                        OPT_INPUT | OPT_OUTPUT,                                    { .off = OFFSET(audio_sample_rate) },
 +        "set audio sampling rate (in Hz)", "rate" },
 +    { "ac",             OPT_AUDIO | HAS_ARG  | OPT_INT | OPT_SPEC |
 +                        OPT_INPUT | OPT_OUTPUT,                                    { .off = OFFSET(audio_channels) },
 +        "set number of audio channels", "channels" },
 +    { "an",             OPT_AUDIO | OPT_BOOL | OPT_OFFSET | OPT_INPUT | OPT_OUTPUT,{ .off = OFFSET(audio_disable) },
 +        "disable audio" },
 +    { "acodec",         OPT_AUDIO | HAS_ARG  | OPT_PERFILE |
 +                        OPT_INPUT | OPT_OUTPUT,                                    { .func_arg = opt_audio_codec },
 +        "force audio codec ('copy' to copy stream)", "codec" },
 +    { "atag",           OPT_AUDIO | HAS_ARG  | OPT_EXPERT | OPT_PERFILE |
 +                        OPT_OUTPUT,                                                { .func_arg = opt_old2new },
 +        "force audio tag/fourcc", "fourcc/tag" },
 +    { "vol",            OPT_AUDIO | HAS_ARG  | OPT_INT,                            { &audio_volume },
 +        "change audio volume (256=normal)" , "volume" },
 +    { "sample_fmt",     OPT_AUDIO | HAS_ARG  | OPT_EXPERT | OPT_SPEC |
 +                        OPT_STRING | OPT_INPUT | OPT_OUTPUT,                       { .off = OFFSET(sample_fmts) },
 +        "set sample format", "format" },
 +    { "channel_layout", OPT_AUDIO | HAS_ARG  | OPT_EXPERT | OPT_PERFILE |
 +                        OPT_INPUT | OPT_OUTPUT,                                    { .func_arg = opt_channel_layout },
 +        "set channel layout", "layout" },
 +    { "af",             OPT_AUDIO | HAS_ARG  | OPT_PERFILE | OPT_OUTPUT,           { .func_arg = opt_audio_filters },
 +        "set audio filters", "filter_graph" },
 +    { "guess_layout_max", OPT_AUDIO | HAS_ARG | OPT_INT | OPT_SPEC | OPT_EXPERT | OPT_INPUT, { .off = OFFSET(guess_layout_max) },
 +      "set the maximum number of channels to try to guess the channel layout" },
 +
 +    /* subtitle options */
 +    { "sn",     OPT_SUBTITLE | OPT_BOOL | OPT_OFFSET | OPT_INPUT | OPT_OUTPUT, { .off = OFFSET(subtitle_disable) },
 +        "disable subtitle" },
 +    { "scodec", OPT_SUBTITLE | HAS_ARG  | OPT_PERFILE | OPT_INPUT | OPT_OUTPUT, { .func_arg = opt_subtitle_codec },
 +        "force subtitle codec ('copy' to copy stream)", "codec" },
 +    { "stag",   OPT_SUBTITLE | HAS_ARG  | OPT_EXPERT  | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_old2new }
 +        , "force subtitle tag/fourcc", "fourcc/tag" },
 +    { "fix_sub_duration", OPT_BOOL | OPT_EXPERT | OPT_SUBTITLE | OPT_SPEC | OPT_INPUT, { .off = OFFSET(fix_sub_duration) },
 +        "fix subtitles duration" },
 +    { "canvas_size", OPT_SUBTITLE | HAS_ARG | OPT_STRING | OPT_SPEC | OPT_INPUT, { .off = OFFSET(canvas_sizes) },
 +        "set canvas size (WxH or abbreviation)", "size" },
 +
 +    /* grab options */
 +    { "vc", HAS_ARG | OPT_EXPERT | OPT_VIDEO, { .func_arg = opt_video_channel },
 +        "deprecated, use -channel", "channel" },
 +    { "tvstd", HAS_ARG | OPT_EXPERT | OPT_VIDEO, { .func_arg = opt_video_standard },
 +        "deprecated, use -standard", "standard" },
 +    { "isync", OPT_BOOL | OPT_EXPERT, { &input_sync }, "this option is deprecated and does nothing", "" },
 +
 +    /* muxer options */
 +    { "muxdelay",   OPT_FLOAT | HAS_ARG | OPT_EXPERT | OPT_OFFSET | OPT_OUTPUT, { .off = OFFSET(mux_max_delay) },
 +        "set the maximum demux-decode delay", "seconds" },
 +    { "muxpreload", OPT_FLOAT | HAS_ARG | OPT_EXPERT | OPT_OFFSET | OPT_OUTPUT, { .off = OFFSET(mux_preload) },
 +        "set the initial demux-decode delay", "seconds" },
 +    { "override_ffserver", OPT_BOOL | OPT_EXPERT | OPT_OUTPUT, { &override_ffserver },
 +        "override the options from ffserver", "" },
 +    { "sdp_file", HAS_ARG | OPT_EXPERT | OPT_OUTPUT, { .func_arg = opt_sdp_file },
 +        "specify a file in which to print sdp information", "file" },
 +
 +    { "time_base", HAS_ARG | OPT_STRING | OPT_EXPERT | OPT_SPEC | OPT_OUTPUT, { .off = OFFSET(time_bases) },
 +        "set the desired time base hint for output stream (1:24, 1:48000 or 0.04166, 2.0833e-5)", "ratio" },
 +
 +    { "bsf", HAS_ARG | OPT_STRING | OPT_SPEC | OPT_EXPERT | OPT_OUTPUT, { .off = OFFSET(bitstream_filters) },
 +        "A comma-separated list of bitstream filters", "bitstream_filters" },
 +    { "absf", HAS_ARG | OPT_AUDIO | OPT_EXPERT| OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_old2new },
 +        "deprecated", "audio bitstream_filters" },
 +    { "vbsf", OPT_VIDEO | HAS_ARG | OPT_EXPERT| OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_old2new },
 +        "deprecated", "video bitstream_filters" },
 +
 +    { "apre", HAS_ARG | OPT_AUDIO | OPT_EXPERT| OPT_PERFILE | OPT_OUTPUT,    { .func_arg = opt_preset },
 +        "set the audio options to the indicated preset", "preset" },
 +    { "vpre", OPT_VIDEO | HAS_ARG | OPT_EXPERT| OPT_PERFILE | OPT_OUTPUT,    { .func_arg = opt_preset },
 +        "set the video options to the indicated preset", "preset" },
 +    { "spre", HAS_ARG | OPT_SUBTITLE | OPT_EXPERT| OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_preset },
 +        "set the subtitle options to the indicated preset", "preset" },
 +    { "fpre", HAS_ARG | OPT_EXPERT| OPT_PERFILE | OPT_OUTPUT,                { .func_arg = opt_preset },
 +        "set options from indicated preset file", "filename" },
 +
 +    { "max_muxing_queue_size", HAS_ARG | OPT_INT | OPT_SPEC | OPT_EXPERT | OPT_OUTPUT, { .off = OFFSET(max_muxing_queue_size) },
 +        "maximum number of packets that can be buffered while waiting for all streams to initialize", "packets" },
 +
 +    /* data codec support */
 +    { "dcodec", HAS_ARG | OPT_DATA | OPT_PERFILE | OPT_EXPERT | OPT_INPUT | OPT_OUTPUT, { .func_arg = opt_data_codec },
 +        "force data codec ('copy' to copy stream)", "codec" },
 +    { "dn", OPT_BOOL | OPT_VIDEO | OPT_OFFSET | OPT_INPUT | OPT_OUTPUT, { .off = OFFSET(data_disable) },
 +        "disable data" },
 +
 +#if CONFIG_VAAPI
 +    { "vaapi_device", HAS_ARG | OPT_EXPERT, { .func_arg = opt_vaapi_device },
 +        "set VAAPI hardware device (DRM path or X11 display name)", "device" },
 +#endif
 +
 +#if CONFIG_QSV
 +    { "qsv_device", HAS_ARG | OPT_STRING | OPT_EXPERT, { &qsv_device },
 +        "set QSV hardware device (DirectX adapter index, DRM path or X11 display name)", "device"},
 +#endif
 +
 +    { NULL, },
 +};
diff --cc ffplay.c
index 139da9f,0000000..c0b326c
mode 100644,000000..100644
--- /dev/null
+++ b/ffplay.c
@@@ -1,3706 -1,0 +1,3706 @@@
- #include "cmdutils_common_opts.h"
 +/*
 + * Copyright (c) 2003 Fabrice Bellard
 + *
 + * This file is part of FFmpeg.
 + *
 + * FFmpeg is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public
 + * License as published by the Free Software Foundation; either
 + * version 2.1 of the License, or (at your option) any later version.
 + *
 + * FFmpeg is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with FFmpeg; if not, write to the Free Software
 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 + */
 +
 +/**
 + * @file
 + * simple media player based on the FFmpeg libraries
 + */
 +
 +#include "config.h"
 +#include <inttypes.h>
 +#include <math.h>
 +#include <limits.h>
 +#include <signal.h>
 +#include <stdint.h>
 +
 +#include "libavutil/avstring.h"
 +#include "libavutil/eval.h"
 +#include "libavutil/mathematics.h"
 +#include "libavutil/pixdesc.h"
 +#include "libavutil/imgutils.h"
 +#include "libavutil/dict.h"
 +#include "libavutil/parseutils.h"
 +#include "libavutil/samplefmt.h"
 +#include "libavutil/avassert.h"
 +#include "libavutil/time.h"
 +#include "libavformat/avformat.h"
 +#include "libavdevice/avdevice.h"
 +#include "libswscale/swscale.h"
 +#include "libavutil/opt.h"
 +#include "libavcodec/avfft.h"
 +#include "libswresample/swresample.h"
 +
 +#if CONFIG_AVFILTER
 +# include "libavfilter/avfilter.h"
 +# include "libavfilter/buffersink.h"
 +# include "libavfilter/buffersrc.h"
 +#endif
 +
 +#include <SDL.h>
 +#include <SDL_thread.h>
 +
 +#include "cmdutils.h"
 +
 +#include <assert.h>
 +
 +const char program_name[] = "ffplay";
 +const int program_birth_year = 2003;
 +
 +#define MAX_QUEUE_SIZE (15 * 1024 * 1024)
 +#define MIN_FRAMES 25
 +#define EXTERNAL_CLOCK_MIN_FRAMES 2
 +#define EXTERNAL_CLOCK_MAX_FRAMES 10
 +
 +/* Minimum SDL audio buffer size, in samples. */
 +#define SDL_AUDIO_MIN_BUFFER_SIZE 512
 +/* Calculate actual buffer size keeping in mind not cause too frequent audio callbacks */
 +#define SDL_AUDIO_MAX_CALLBACKS_PER_SEC 30
 +
 +/* Step size for volume control in dB */
 +#define SDL_VOLUME_STEP (0.75)
 +
 +/* no AV sync correction is done if below the minimum AV sync threshold */
 +#define AV_SYNC_THRESHOLD_MIN 0.04
 +/* AV sync correction is done if above the maximum AV sync threshold */
 +#define AV_SYNC_THRESHOLD_MAX 0.1
 +/* If a frame duration is longer than this, it will not be duplicated to compensate AV sync */
 +#define AV_SYNC_FRAMEDUP_THRESHOLD 0.1
 +/* no AV correction is done if too big error */
 +#define AV_NOSYNC_THRESHOLD 10.0
 +
 +/* maximum audio speed change to get correct sync */
 +#define SAMPLE_CORRECTION_PERCENT_MAX 10
 +
 +/* external clock speed adjustment constants for realtime sources based on buffer fullness */
 +#define EXTERNAL_CLOCK_SPEED_MIN  0.900
 +#define EXTERNAL_CLOCK_SPEED_MAX  1.010
 +#define EXTERNAL_CLOCK_SPEED_STEP 0.001
 +
 +/* we use about AUDIO_DIFF_AVG_NB A-V differences to make the average */
 +#define AUDIO_DIFF_AVG_NB   20
 +
 +/* polls for possible required screen refresh at least this often, should be less than 1/fps */
 +#define REFRESH_RATE 0.01
 +
 +/* NOTE: the size must be big enough to compensate the hardware audio buffersize size */
 +/* TODO: We assume that a decoded and resampled frame fits into this buffer */
 +#define SAMPLE_ARRAY_SIZE (8 * 65536)
 +
 +#define CURSOR_HIDE_DELAY 1000000
 +
 +#define USE_ONEPASS_SUBTITLE_RENDER 1
 +
 +static unsigned sws_flags = SWS_BICUBIC;
 +
 +typedef struct MyAVPacketList {
 +    AVPacket pkt;
 +    struct MyAVPacketList *next;
 +    int serial;
 +} MyAVPacketList;
 +
 +typedef struct PacketQueue {
 +    MyAVPacketList *first_pkt, *last_pkt;
 +    int nb_packets;
 +    int size;
 +    int64_t duration;
 +    int abort_request;
 +    int serial;
 +    SDL_mutex *mutex;
 +    SDL_cond *cond;
 +} PacketQueue;
 +
 +#define VIDEO_PICTURE_QUEUE_SIZE 3
 +#define SUBPICTURE_QUEUE_SIZE 16
 +#define SAMPLE_QUEUE_SIZE 9
 +#define FRAME_QUEUE_SIZE FFMAX(SAMPLE_QUEUE_SIZE, FFMAX(VIDEO_PICTURE_QUEUE_SIZE, SUBPICTURE_QUEUE_SIZE))
 +
 +typedef struct AudioParams {
 +    int freq;
 +    int channels;
 +    int64_t channel_layout;
 +    enum AVSampleFormat fmt;
 +    int frame_size;
 +    int bytes_per_sec;
 +} AudioParams;
 +
 +typedef struct Clock {
 +    double pts;           /* clock base */
 +    double pts_drift;     /* clock base minus time at which we updated the clock */
 +    double last_updated;
 +    double speed;
 +    int serial;           /* clock is based on a packet with this serial */
 +    int paused;
 +    int *queue_serial;    /* pointer to the current packet queue serial, used for obsolete clock detection */
 +} Clock;
 +
 +/* Common struct for handling all types of decoded data and allocated render buffers. */
 +typedef struct Frame {
 +    AVFrame *frame;
 +    AVSubtitle sub;
 +    int serial;
 +    double pts;           /* presentation timestamp for the frame */
 +    double duration;      /* estimated duration of the frame */
 +    int64_t pos;          /* byte position of the frame in the input file */
 +    int width;
 +    int height;
 +    int format;
 +    AVRational sar;
 +    int uploaded;
 +    int flip_v;
 +} Frame;
 +
 +typedef struct FrameQueue {
 +    Frame queue[FRAME_QUEUE_SIZE];
 +    int rindex;
 +    int windex;
 +    int size;
 +    int max_size;
 +    int keep_last;
 +    int rindex_shown;
 +    SDL_mutex *mutex;
 +    SDL_cond *cond;
 +    PacketQueue *pktq;
 +} FrameQueue;
 +
 +enum {
 +    AV_SYNC_AUDIO_MASTER, /* default choice */
 +    AV_SYNC_VIDEO_MASTER,
 +    AV_SYNC_EXTERNAL_CLOCK, /* synchronize to an external clock */
 +};
 +
 +typedef struct Decoder {
 +    AVPacket pkt;
 +    PacketQueue *queue;
 +    AVCodecContext *avctx;
 +    int pkt_serial;
 +    int finished;
 +    int packet_pending;
 +    SDL_cond *empty_queue_cond;
 +    int64_t start_pts;
 +    AVRational start_pts_tb;
 +    int64_t next_pts;
 +    AVRational next_pts_tb;
 +    SDL_Thread *decoder_tid;
 +} Decoder;
 +
 +typedef struct VideoState {
 +    SDL_Thread *read_tid;
 +    AVInputFormat *iformat;
 +    int abort_request;
 +    int force_refresh;
 +    int paused;
 +    int last_paused;
 +    int queue_attachments_req;
 +    int seek_req;
 +    int seek_flags;
 +    int64_t seek_pos;
 +    int64_t seek_rel;
 +    int read_pause_return;
 +    AVFormatContext *ic;
 +    int realtime;
 +
 +    Clock audclk;
 +    Clock vidclk;
 +    Clock extclk;
 +
 +    FrameQueue pictq;
 +    FrameQueue subpq;
 +    FrameQueue sampq;
 +
 +    Decoder auddec;
 +    Decoder viddec;
 +    Decoder subdec;
 +
 +    int audio_stream;
 +
 +    int av_sync_type;
 +
 +    double audio_clock;
 +    int audio_clock_serial;
 +    double audio_diff_cum; /* used for AV difference average computation */
 +    double audio_diff_avg_coef;
 +    double audio_diff_threshold;
 +    int audio_diff_avg_count;
 +    AVStream *audio_st;
 +    PacketQueue audioq;
 +    int audio_hw_buf_size;
 +    uint8_t *audio_buf;
 +    uint8_t *audio_buf1;
 +    unsigned int audio_buf_size; /* in bytes */
 +    unsigned int audio_buf1_size;
 +    int audio_buf_index; /* in bytes */
 +    int audio_write_buf_size;
 +    int audio_volume;
 +    int muted;
 +    struct AudioParams audio_src;
 +#if CONFIG_AVFILTER
 +    struct AudioParams audio_filter_src;
 +#endif
 +    struct AudioParams audio_tgt;
 +    struct SwrContext *swr_ctx;
 +    int frame_drops_early;
 +    int frame_drops_late;
 +
 +    enum ShowMode {
 +        SHOW_MODE_NONE = -1, SHOW_MODE_VIDEO = 0, SHOW_MODE_WAVES, SHOW_MODE_RDFT, SHOW_MODE_NB
 +    } show_mode;
 +    int16_t sample_array[SAMPLE_ARRAY_SIZE];
 +    int sample_array_index;
 +    int last_i_start;
 +    RDFTContext *rdft;
 +    int rdft_bits;
 +    FFTSample *rdft_data;
 +    int xpos;
 +    double last_vis_time;
 +    SDL_Texture *vis_texture;
 +    SDL_Texture *sub_texture;
 +    SDL_Texture *vid_texture;
 +
 +    int subtitle_stream;
 +    AVStream *subtitle_st;
 +    PacketQueue subtitleq;
 +
 +    double frame_timer;
 +    double frame_last_returned_time;
 +    double frame_last_filter_delay;
 +    int video_stream;
 +    AVStream *video_st;
 +    PacketQueue videoq;
 +    double max_frame_duration;      // maximum duration of a frame - above this, we consider the jump a timestamp discontinuity
 +    struct SwsContext *img_convert_ctx;
 +    struct SwsContext *sub_convert_ctx;
 +    int eof;
 +
 +    char *filename;
 +    int width, height, xleft, ytop;
 +    int step;
 +
 +#if CONFIG_AVFILTER
 +    int vfilter_idx;
 +    AVFilterContext *in_video_filter;   // the first filter in the video chain
 +    AVFilterContext *out_video_filter;  // the last filter in the video chain
 +    AVFilterContext *in_audio_filter;   // the first filter in the audio chain
 +    AVFilterContext *out_audio_filter;  // the last filter in the audio chain
 +    AVFilterGraph *agraph;              // audio filter graph
 +#endif
 +
 +    int last_video_stream, last_audio_stream, last_subtitle_stream;
 +
 +    SDL_cond *continue_read_thread;
 +} VideoState;
 +
 +/* options specified by the user */
 +static AVInputFormat *file_iformat;
 +static const char *input_filename;
 +static const char *window_title;
 +static int default_width  = 640;
 +static int default_height = 480;
 +static int screen_width  = 0;
 +static int screen_height = 0;
 +static int audio_disable;
 +static int video_disable;
 +static int subtitle_disable;
 +static const char* wanted_stream_spec[AVMEDIA_TYPE_NB] = {0};
 +static int seek_by_bytes = -1;
 +static int display_disable;
 +static int borderless;
 +static int startup_volume = 100;
 +static int show_status = 1;
 +static int av_sync_type = AV_SYNC_AUDIO_MASTER;
 +static int64_t start_time = AV_NOPTS_VALUE;
 +static int64_t duration = AV_NOPTS_VALUE;
 +static int fast = 0;
 +static int genpts = 0;
 +static int lowres = 0;
 +static int decoder_reorder_pts = -1;
 +static int autoexit;
 +static int exit_on_keydown;
 +static int exit_on_mousedown;
 +static int loop = 1;
 +static int framedrop = -1;
 +static int infinite_buffer = -1;
 +static enum ShowMode show_mode = SHOW_MODE_NONE;
 +static const char *audio_codec_name;
 +static const char *subtitle_codec_name;
 +static const char *video_codec_name;
 +double rdftspeed = 0.02;
 +static int64_t cursor_last_shown;
 +static int cursor_hidden = 0;
 +#if CONFIG_AVFILTER
 +static const char **vfilters_list = NULL;
 +static int nb_vfilters = 0;
 +static char *afilters = NULL;
 +#endif
 +static int autorotate = 1;
 +
 +/* current context */
 +static int is_full_screen;
 +static int64_t audio_callback_time;
 +
 +static AVPacket flush_pkt;
 +
 +#define FF_QUIT_EVENT    (SDL_USEREVENT + 2)
 +
 +static SDL_Window *window;
 +static SDL_Renderer *renderer;
 +
 +#if CONFIG_AVFILTER
 +static int opt_add_vfilter(void *optctx, const char *opt, const char *arg)
 +{
 +    GROW_ARRAY(vfilters_list, nb_vfilters);
 +    vfilters_list[nb_vfilters - 1] = arg;
 +    return 0;
 +}
 +#endif
 +
 +static inline
 +int cmp_audio_fmts(enum AVSampleFormat fmt1, int64_t channel_count1,
 +                   enum AVSampleFormat fmt2, int64_t channel_count2)
 +{
 +    /* If channel count == 1, planar and non-planar formats are the same */
 +    if (channel_count1 == 1 && channel_count2 == 1)
 +        return av_get_packed_sample_fmt(fmt1) != av_get_packed_sample_fmt(fmt2);
 +    else
 +        return channel_count1 != channel_count2 || fmt1 != fmt2;
 +}
 +
 +static inline
 +int64_t get_valid_channel_layout(int64_t channel_layout, int channels)
 +{
 +    if (channel_layout && av_get_channel_layout_nb_channels(channel_layout) == channels)
 +        return channel_layout;
 +    else
 +        return 0;
 +}
 +
 +static int packet_queue_put_private(PacketQueue *q, AVPacket *pkt)
 +{
 +    MyAVPacketList *pkt1;
 +
 +    if (q->abort_request)
 +       return -1;
 +
 +    pkt1 = av_malloc(sizeof(MyAVPacketList));
 +    if (!pkt1)
 +        return -1;
 +    pkt1->pkt = *pkt;
 +    pkt1->next = NULL;
 +    if (pkt == &flush_pkt)
 +        q->serial++;
 +    pkt1->serial = q->serial;
 +
 +    if (!q->last_pkt)
 +        q->first_pkt = pkt1;
 +    else
 +        q->last_pkt->next = pkt1;
 +    q->last_pkt = pkt1;
 +    q->nb_packets++;
 +    q->size += pkt1->pkt.size + sizeof(*pkt1);
 +    q->duration += pkt1->pkt.duration;
 +    /* XXX: should duplicate packet data in DV case */
 +    SDL_CondSignal(q->cond);
 +    return 0;
 +}
 +
 +static int packet_queue_put(PacketQueue *q, AVPacket *pkt)
 +{
 +    int ret;
 +
 +    SDL_LockMutex(q->mutex);
 +    ret = packet_queue_put_private(q, pkt);
 +    SDL_UnlockMutex(q->mutex);
 +
 +    if (pkt != &flush_pkt && ret < 0)
 +        av_packet_unref(pkt);
 +
 +    return ret;
 +}
 +
 +static int packet_queue_put_nullpacket(PacketQueue *q, int stream_index)
 +{
 +    AVPacket pkt1, *pkt = &pkt1;
 +    av_init_packet(pkt);
 +    pkt->data = NULL;
 +    pkt->size = 0;
 +    pkt->stream_index = stream_index;
 +    return packet_queue_put(q, pkt);
 +}
 +
 +/* packet queue handling */
 +static int packet_queue_init(PacketQueue *q)
 +{
 +    memset(q, 0, sizeof(PacketQueue));
 +    q->mutex = SDL_CreateMutex();
 +    if (!q->mutex) {
 +        av_log(NULL, AV_LOG_FATAL, "SDL_CreateMutex(): %s\n", SDL_GetError());
 +        return AVERROR(ENOMEM);
 +    }
 +    q->cond = SDL_CreateCond();
 +    if (!q->cond) {
 +        av_log(NULL, AV_LOG_FATAL, "SDL_CreateCond(): %s\n", SDL_GetError());
 +        return AVERROR(ENOMEM);
 +    }
 +    q->abort_request = 1;
 +    return 0;
 +}
 +
 +static void packet_queue_flush(PacketQueue *q)
 +{
 +    MyAVPacketList *pkt, *pkt1;
 +
 +    SDL_LockMutex(q->mutex);
 +    for (pkt = q->first_pkt; pkt; pkt = pkt1) {
 +        pkt1 = pkt->next;
 +        av_packet_unref(&pkt->pkt);
 +        av_freep(&pkt);
 +    }
 +    q->last_pkt = NULL;
 +    q->first_pkt = NULL;
 +    q->nb_packets = 0;
 +    q->size = 0;
 +    q->duration = 0;
 +    SDL_UnlockMutex(q->mutex);
 +}
 +
 +static void packet_queue_destroy(PacketQueue *q)
 +{
 +    packet_queue_flush(q);
 +    SDL_DestroyMutex(q->mutex);
 +    SDL_DestroyCond(q->cond);
 +}
 +
 +static void packet_queue_abort(PacketQueue *q)
 +{
 +    SDL_LockMutex(q->mutex);
 +
 +    q->abort_request = 1;
 +
 +    SDL_CondSignal(q->cond);
 +
 +    SDL_UnlockMutex(q->mutex);
 +}
 +
 +static void packet_queue_start(PacketQueue *q)
 +{
 +    SDL_LockMutex(q->mutex);
 +    q->abort_request = 0;
 +    packet_queue_put_private(q, &flush_pkt);
 +    SDL_UnlockMutex(q->mutex);
 +}
 +
 +/* return < 0 if aborted, 0 if no packet and > 0 if packet.  */
 +static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block, int *serial)
 +{
 +    MyAVPacketList *pkt1;
 +    int ret;
 +
 +    SDL_LockMutex(q->mutex);
 +
 +    for (;;) {
 +        if (q->abort_request) {
 +            ret = -1;
 +            break;
 +        }
 +
 +        pkt1 = q->first_pkt;
 +        if (pkt1) {
 +            q->first_pkt = pkt1->next;
 +            if (!q->first_pkt)
 +                q->last_pkt = NULL;
 +            q->nb_packets--;
 +            q->size -= pkt1->pkt.size + sizeof(*pkt1);
 +            q->duration -= pkt1->pkt.duration;
 +            *pkt = pkt1->pkt;
 +            if (serial)
 +                *serial = pkt1->serial;
 +            av_free(pkt1);
 +            ret = 1;
 +            break;
 +        } else if (!block) {
 +            ret = 0;
 +            break;
 +        } else {
 +            SDL_CondWait(q->cond, q->mutex);
 +        }
 +    }
 +    SDL_UnlockMutex(q->mutex);
 +    return ret;
 +}
 +
 +static void decoder_init(Decoder *d, AVCodecContext *avctx, PacketQueue *queue, SDL_cond *empty_queue_cond) {
 +    memset(d, 0, sizeof(Decoder));
 +    d->avctx = avctx;
 +    d->queue = queue;
 +    d->empty_queue_cond = empty_queue_cond;
 +    d->start_pts = AV_NOPTS_VALUE;
 +    d->pkt_serial = -1;
 +}
 +
 +static int decoder_decode_frame(Decoder *d, AVFrame *frame, AVSubtitle *sub) {
 +    int ret = AVERROR(EAGAIN);
 +
 +    for (;;) {
 +        AVPacket pkt;
 +
 +        if (d->queue->serial == d->pkt_serial) {
 +            do {
 +                if (d->queue->abort_request)
 +                    return -1;
 +
 +                switch (d->avctx->codec_type) {
 +                    case AVMEDIA_TYPE_VIDEO:
 +                        ret = avcodec_receive_frame(d->avctx, frame);
 +                        if (ret >= 0) {
 +                            if (decoder_reorder_pts == -1) {
 +                                frame->pts = frame->best_effort_timestamp;
 +                            } else if (!decoder_reorder_pts) {
 +                                frame->pts = frame->pkt_dts;
 +                            }
 +                        }
 +                        break;
 +                    case AVMEDIA_TYPE_AUDIO:
 +                        ret = avcodec_receive_frame(d->avctx, frame);
 +                        if (ret >= 0) {
 +                            AVRational tb = (AVRational){1, frame->sample_rate};
 +                            if (frame->pts != AV_NOPTS_VALUE)
 +                                frame->pts = av_rescale_q(frame->pts, av_codec_get_pkt_timebase(d->avctx), tb);
 +                            else if (d->next_pts != AV_NOPTS_VALUE)
 +                                frame->pts = av_rescale_q(d->next_pts, d->next_pts_tb, tb);
 +                            if (frame->pts != AV_NOPTS_VALUE) {
 +                                d->next_pts = frame->pts + frame->nb_samples;
 +                                d->next_pts_tb = tb;
 +                            }
 +                        }
 +                        break;
 +                }
 +                if (ret == AVERROR_EOF) {
 +                    d->finished = d->pkt_serial;
 +                    avcodec_flush_buffers(d->avctx);
 +                    return 0;
 +                }
 +                if (ret >= 0)
 +                    return 1;
 +            } while (ret != AVERROR(EAGAIN));
 +        }
 +
 +        do {
 +            if (d->queue->nb_packets == 0)
 +                SDL_CondSignal(d->empty_queue_cond);
 +            if (d->packet_pending) {
 +                av_packet_move_ref(&pkt, &d->pkt);
 +                d->packet_pending = 0;
 +            } else {
 +                if (packet_queue_get(d->queue, &pkt, 1, &d->pkt_serial) < 0)
 +                    return -1;
 +            }
 +        } while (d->queue->serial != d->pkt_serial);
 +
 +        if (pkt.data == flush_pkt.data) {
 +            avcodec_flush_buffers(d->avctx);
 +            d->finished = 0;
 +            d->next_pts = d->start_pts;
 +            d->next_pts_tb = d->start_pts_tb;
 +        } else {
 +            if (d->avctx->codec_type == AVMEDIA_TYPE_SUBTITLE) {
 +                int got_frame = 0;
 +                ret = avcodec_decode_subtitle2(d->avctx, sub, &got_frame, &pkt);
 +                if (ret < 0) {
 +                    ret = AVERROR(EAGAIN);
 +                } else {
 +                    if (got_frame && !pkt.data) {
 +                       d->packet_pending = 1;
 +                       av_packet_move_ref(&d->pkt, &pkt);
 +                    }
 +                    ret = got_frame ? 0 : (pkt.data ? AVERROR(EAGAIN) : AVERROR_EOF);
 +                }
 +            } else {
 +                if (avcodec_send_packet(d->avctx, &pkt) == AVERROR(EAGAIN)) {
 +                    av_log(d->avctx, AV_LOG_ERROR, "Receive_frame and send_packet both returned EAGAIN, which is an API violation.\n");
 +                    d->packet_pending = 1;
 +                    av_packet_move_ref(&d->pkt, &pkt);
 +                }
 +            }
 +            av_packet_unref(&pkt);
 +        }
 +    }
 +}
 +
 +static void decoder_destroy(Decoder *d) {
 +    av_packet_unref(&d->pkt);
 +    avcodec_free_context(&d->avctx);
 +}
 +
 +static void frame_queue_unref_item(Frame *vp)
 +{
 +    av_frame_unref(vp->frame);
 +    avsubtitle_free(&vp->sub);
 +}
 +
 +static int frame_queue_init(FrameQueue *f, PacketQueue *pktq, int max_size, int keep_last)
 +{
 +    int i;
 +    memset(f, 0, sizeof(FrameQueue));
 +    if (!(f->mutex = SDL_CreateMutex())) {
 +        av_log(NULL, AV_LOG_FATAL, "SDL_CreateMutex(): %s\n", SDL_GetError());
 +        return AVERROR(ENOMEM);
 +    }
 +    if (!(f->cond = SDL_CreateCond())) {
 +        av_log(NULL, AV_LOG_FATAL, "SDL_CreateCond(): %s\n", SDL_GetError());
 +        return AVERROR(ENOMEM);
 +    }
 +    f->pktq = pktq;
 +    f->max_size = FFMIN(max_size, FRAME_QUEUE_SIZE);
 +    f->keep_last = !!keep_last;
 +    for (i = 0; i < f->max_size; i++)
 +        if (!(f->queue[i].frame = av_frame_alloc()))
 +            return AVERROR(ENOMEM);
 +    return 0;
 +}
 +
 +static void frame_queue_destory(FrameQueue *f)
 +{
 +    int i;
 +    for (i = 0; i < f->max_size; i++) {
 +        Frame *vp = &f->queue[i];
 +        frame_queue_unref_item(vp);
 +        av_frame_free(&vp->frame);
 +    }
 +    SDL_DestroyMutex(f->mutex);
 +    SDL_DestroyCond(f->cond);
 +}
 +
 +static void frame_queue_signal(FrameQueue *f)
 +{
 +    SDL_LockMutex(f->mutex);
 +    SDL_CondSignal(f->cond);
 +    SDL_UnlockMutex(f->mutex);
 +}
 +
 +static Frame *frame_queue_peek(FrameQueue *f)
 +{
 +    return &f->queue[(f->rindex + f->rindex_shown) % f->max_size];
 +}
 +
 +static Frame *frame_queue_peek_next(FrameQueue *f)
 +{
 +    return &f->queue[(f->rindex + f->rindex_shown + 1) % f->max_size];
 +}
 +
 +static Frame *frame_queue_peek_last(FrameQueue *f)
 +{
 +    return &f->queue[f->rindex];
 +}
 +
 +static Frame *frame_queue_peek_writable(FrameQueue *f)
 +{
 +    /* wait until we have space to put a new frame */
 +    SDL_LockMutex(f->mutex);
 +    while (f->size >= f->max_size &&
 +           !f->pktq->abort_request) {
 +        SDL_CondWait(f->cond, f->mutex);
 +    }
 +    SDL_UnlockMutex(f->mutex);
 +
 +    if (f->pktq->abort_request)
 +        return NULL;
 +
 +    return &f->queue[f->windex];
 +}
 +
 +static Frame *frame_queue_peek_readable(FrameQueue *f)
 +{
 +    /* wait until we have a readable a new frame */
 +    SDL_LockMutex(f->mutex);
 +    while (f->size - f->rindex_shown <= 0 &&
 +           !f->pktq->abort_request) {
 +        SDL_CondWait(f->cond, f->mutex);
 +    }
 +    SDL_UnlockMutex(f->mutex);
 +
 +    if (f->pktq->abort_request)
 +        return NULL;
 +
 +    return &f->queue[(f->rindex + f->rindex_shown) % f->max_size];
 +}
 +
 +static void frame_queue_push(FrameQueue *f)
 +{
 +    if (++f->windex == f->max_size)
 +        f->windex = 0;
 +    SDL_LockMutex(f->mutex);
 +    f->size++;
 +    SDL_CondSignal(f->cond);
 +    SDL_UnlockMutex(f->mutex);
 +}
 +
 +static void frame_queue_next(FrameQueue *f)
 +{
 +    if (f->keep_last && !f->rindex_shown) {
 +        f->rindex_shown = 1;
 +        return;
 +    }
 +    frame_queue_unref_item(&f->queue[f->rindex]);
 +    if (++f->rindex == f->max_size)
 +        f->rindex = 0;
 +    SDL_LockMutex(f->mutex);
 +    f->size--;
 +    SDL_CondSignal(f->cond);
 +    SDL_UnlockMutex(f->mutex);
 +}
 +
 +/* return the number of undisplayed frames in the queue */
 +static int frame_queue_nb_remaining(FrameQueue *f)
 +{
 +    return f->size - f->rindex_shown;
 +}
 +
 +/* return last shown position */
 +static int64_t frame_queue_last_pos(FrameQueue *f)
 +{
 +    Frame *fp = &f->queue[f->rindex];
 +    if (f->rindex_shown && fp->serial == f->pktq->serial)
 +        return fp->pos;
 +    else
 +        return -1;
 +}
 +
 +static void decoder_abort(Decoder *d, FrameQueue *fq)
 +{
 +    packet_queue_abort(d->queue);
 +    frame_queue_signal(fq);
 +    SDL_WaitThread(d->decoder_tid, NULL);
 +    d->decoder_tid = NULL;
 +    packet_queue_flush(d->queue);
 +}
 +
 +static inline void fill_rectangle(int x, int y, int w, int h)
 +{
 +    SDL_Rect rect;
 +    rect.x = x;
 +    rect.y = y;
 +    rect.w = w;
 +    rect.h = h;
 +    if (w && h)
 +        SDL_RenderFillRect(renderer, &rect);
 +}
 +
 +static int realloc_texture(SDL_Texture **texture, Uint32 new_format, int new_width, int new_height, SDL_BlendMode blendmode, int init_texture)
 +{
 +    Uint32 format;
 +    int access, w, h;
 +    if (SDL_QueryTexture(*texture, &format, &access, &w, &h) < 0 || new_width != w || new_height != h || new_format != format) {
 +        void *pixels;
 +        int pitch;
 +        SDL_DestroyTexture(*texture);
 +        if (!(*texture = SDL_CreateTexture(renderer, new_format, SDL_TEXTUREACCESS_STREAMING, new_width, new_height)))
 +            return -1;
 +        if (SDL_SetTextureBlendMode(*texture, blendmode) < 0)
 +            return -1;
 +        if (init_texture) {
 +            if (SDL_LockTexture(*texture, NULL, &pixels, &pitch) < 0)
 +                return -1;
 +            memset(pixels, 0, pitch * new_height);
 +            SDL_UnlockTexture(*texture);
 +        }
 +    }
 +    return 0;
 +}
 +
 +static void calculate_display_rect(SDL_Rect *rect,
 +                                   int scr_xleft, int scr_ytop, int scr_width, int scr_height,
 +                                   int pic_width, int pic_height, AVRational pic_sar)
 +{
 +    float aspect_ratio;
 +    int width, height, x, y;
 +
 +    if (pic_sar.num == 0)
 +        aspect_ratio = 0;
 +    else
 +        aspect_ratio = av_q2d(pic_sar);
 +
 +    if (aspect_ratio <= 0.0)
 +        aspect_ratio = 1.0;
 +    aspect_ratio *= (float)pic_width / (float)pic_height;
 +
 +    /* XXX: we suppose the screen has a 1.0 pixel ratio */
 +    height = scr_height;
 +    width = lrint(height * aspect_ratio) & ~1;
 +    if (width > scr_width) {
 +        width = scr_width;
 +        height = lrint(width / aspect_ratio) & ~1;
 +    }
 +    x = (scr_width - width) / 2;
 +    y = (scr_height - height) / 2;
 +    rect->x = scr_xleft + x;
 +    rect->y = scr_ytop  + y;
 +    rect->w = FFMAX(width,  1);
 +    rect->h = FFMAX(height, 1);
 +}
 +
 +static int upload_texture(SDL_Texture *tex, AVFrame *frame, struct SwsContext **img_convert_ctx) {
 +    int ret = 0;
 +    switch (frame->format) {
 +        case AV_PIX_FMT_YUV420P:
 +            if (frame->linesize[0] < 0 || frame->linesize[1] < 0 || frame->linesize[2] < 0) {
 +                av_log(NULL, AV_LOG_ERROR, "Negative linesize is not supported for YUV.\n");
 +                return -1;
 +            }
 +            ret = SDL_UpdateYUVTexture(tex, NULL, frame->data[0], frame->linesize[0],
 +                                                  frame->data[1], frame->linesize[1],
 +                                                  frame->data[2], frame->linesize[2]);
 +            break;
 +        case AV_PIX_FMT_BGRA:
 +            if (frame->linesize[0] < 0) {
 +                ret = SDL_UpdateTexture(tex, NULL, frame->data[0] + frame->linesize[0] * (frame->height - 1), -frame->linesize[0]);
 +            } else {
 +                ret = SDL_UpdateTexture(tex, NULL, frame->data[0], frame->linesize[0]);
 +            }
 +            break;
 +        default:
 +            /* This should only happen if we are not using avfilter... */
 +            *img_convert_ctx = sws_getCachedContext(*img_convert_ctx,
 +                frame->width, frame->height, frame->format, frame->width, frame->height,
 +                AV_PIX_FMT_BGRA, sws_flags, NULL, NULL, NULL);
 +            if (*img_convert_ctx != NULL) {
 +                uint8_t *pixels[4];
 +                int pitch[4];
 +                if (!SDL_LockTexture(tex, NULL, (void **)pixels, pitch)) {
 +                    sws_scale(*img_convert_ctx, (const uint8_t * const *)frame->data, frame->linesize,
 +                              0, frame->height, pixels, pitch);
 +                    SDL_UnlockTexture(tex);
 +                }
 +            } else {
 +                av_log(NULL, AV_LOG_FATAL, "Cannot initialize the conversion context\n");
 +                ret = -1;
 +            }
 +            break;
 +    }
 +    return ret;
 +}
 +
 +static void video_image_display(VideoState *is)
 +{
 +    Frame *vp;
 +    Frame *sp = NULL;
 +    SDL_Rect rect;
 +
 +    vp = frame_queue_peek_last(&is->pictq);
 +    if (is->subtitle_st) {
 +        if (frame_queue_nb_remaining(&is->subpq) > 0) {
 +            sp = frame_queue_peek(&is->subpq);
 +
 +            if (vp->pts >= sp->pts + ((float) sp->sub.start_display_time / 1000)) {
 +                if (!sp->uploaded) {
 +                    uint8_t* pixels[4];
 +                    int pitch[4];
 +                    int i;
 +                    if (!sp->width || !sp->height) {
 +                        sp->width = vp->width;
 +                        sp->height = vp->height;
 +                    }
 +                    if (realloc_texture(&is->sub_texture, SDL_PIXELFORMAT_ARGB8888, sp->width, sp->height, SDL_BLENDMODE_BLEND, 1) < 0)
 +                        return;
 +
 +                    for (i = 0; i < sp->sub.num_rects; i++) {
 +                        AVSubtitleRect *sub_rect = sp->sub.rects[i];
 +
 +                        sub_rect->x = av_clip(sub_rect->x, 0, sp->width );
 +                        sub_rect->y = av_clip(sub_rect->y, 0, sp->height);
 +                        sub_rect->w = av_clip(sub_rect->w, 0, sp->width  - sub_rect->x);
 +                        sub_rect->h = av_clip(sub_rect->h, 0, sp->height - sub_rect->y);
 +
 +                        is->sub_convert_ctx = sws_getCachedContext(is->sub_convert_ctx,
 +                            sub_rect->w, sub_rect->h, AV_PIX_FMT_PAL8,
 +                            sub_rect->w, sub_rect->h, AV_PIX_FMT_BGRA,
 +                            0, NULL, NULL, NULL);
 +                        if (!is->sub_convert_ctx) {
 +                            av_log(NULL, AV_LOG_FATAL, "Cannot initialize the conversion context\n");
 +                            return;
 +                        }
 +                        if (!SDL_LockTexture(is->sub_texture, (SDL_Rect *)sub_rect, (void **)pixels, pitch)) {
 +                            sws_scale(is->sub_convert_ctx, (const uint8_t * const *)sub_rect->data, sub_rect->linesize,
 +                                      0, sub_rect->h, pixels, pitch);
 +                            SDL_UnlockTexture(is->sub_texture);
 +                        }
 +                    }
 +                    sp->uploaded = 1;
 +                }
 +            } else
 +                sp = NULL;
 +        }
 +    }
 +
 +    calculate_display_rect(&rect, is->xleft, is->ytop, is->width, is->height, vp->width, vp->height, vp->sar);
 +
 +    if (!vp->uploaded) {
 +        int sdl_pix_fmt = vp->frame->format == AV_PIX_FMT_YUV420P ? SDL_PIXELFORMAT_YV12 : SDL_PIXELFORMAT_ARGB8888;
 +        if (realloc_texture(&is->vid_texture, sdl_pix_fmt, vp->frame->width, vp->frame->height, SDL_BLENDMODE_NONE, 0) < 0)
 +            return;
 +        if (upload_texture(is->vid_texture, vp->frame, &is->img_convert_ctx) < 0)
 +            return;
 +        vp->uploaded = 1;
 +        vp->flip_v = vp->frame->linesize[0] < 0;
 +    }
 +
 +    SDL_RenderCopyEx(renderer, is->vid_texture, NULL, &rect, 0, NULL, vp->flip_v ? SDL_FLIP_VERTICAL : 0);
 +    if (sp) {
 +#if USE_ONEPASS_SUBTITLE_RENDER
 +        SDL_RenderCopy(renderer, is->sub_texture, NULL, &rect);
 +#else
 +        int i;
 +        double xratio = (double)rect.w / (double)sp->width;
 +        double yratio = (double)rect.h / (double)sp->height;
 +        for (i = 0; i < sp->sub.num_rects; i++) {
 +            SDL_Rect *sub_rect = (SDL_Rect*)sp->sub.rects[i];
 +            SDL_Rect target = {.x = rect.x + sub_rect->x * xratio,
 +                               .y = rect.y + sub_rect->y * yratio,
 +                               .w = sub_rect->w * xratio,
 +                               .h = sub_rect->h * yratio};
 +            SDL_RenderCopy(renderer, is->sub_texture, sub_rect, &target);
 +        }
 +#endif
 +    }
 +}
 +
 +static inline int compute_mod(int a, int b)
 +{
 +    return a < 0 ? a%b + b : a%b;
 +}
 +
 +static void video_audio_display(VideoState *s)
 +{
 +    int i, i_start, x, y1, y, ys, delay, n, nb_display_channels;
 +    int ch, channels, h, h2;
 +    int64_t time_diff;
 +    int rdft_bits, nb_freq;
 +
 +    for (rdft_bits = 1; (1 << rdft_bits) < 2 * s->height; rdft_bits++)
 +        ;
 +    nb_freq = 1 << (rdft_bits - 1);
 +
 +    /* compute display index : center on currently output samples */
 +    channels = s->audio_tgt.channels;
 +    nb_display_channels = channels;
 +    if (!s->paused) {
 +        int data_used= s->show_mode == SHOW_MODE_WAVES ? s->width : (2*nb_freq);
 +        n = 2 * channels;
 +        delay = s->audio_write_buf_size;
 +        delay /= n;
 +
 +        /* to be more precise, we take into account the time spent since
 +           the last buffer computation */
 +        if (audio_callback_time) {
 +            time_diff = av_gettime_relative() - audio_callback_time;
 +            delay -= (time_diff * s->audio_tgt.freq) / 1000000;
 +        }
 +
 +        delay += 2 * data_used;
 +        if (delay < data_used)
 +            delay = data_used;
 +
 +        i_start= x = compute_mod(s->sample_array_index - delay * channels, SAMPLE_ARRAY_SIZE);
 +        if (s->show_mode == SHOW_MODE_WAVES) {
 +            h = INT_MIN;
 +            for (i = 0; i < 1000; i += channels) {
 +                int idx = (SAMPLE_ARRAY_SIZE + x - i) % SAMPLE_ARRAY_SIZE;
 +                int a = s->sample_array[idx];
 +                int b = s->sample_array[(idx + 4 * channels) % SAMPLE_ARRAY_SIZE];
 +                int c = s->sample_array[(idx + 5 * channels) % SAMPLE_ARRAY_SIZE];
 +                int d = s->sample_array[(idx + 9 * channels) % SAMPLE_ARRAY_SIZE];
 +                int score = a - d;
 +                if (h < score && (b ^ c) < 0) {
 +                    h = score;
 +                    i_start = idx;
 +                }
 +            }
 +        }
 +
 +        s->last_i_start = i_start;
 +    } else {
 +        i_start = s->last_i_start;
 +    }
 +
 +    if (s->show_mode == SHOW_MODE_WAVES) {
 +        SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
 +
 +        /* total height for one channel */
 +        h = s->height / nb_display_channels;
 +        /* graph height / 2 */
 +        h2 = (h * 9) / 20;
 +        for (ch = 0; ch < nb_display_channels; ch++) {
 +            i = i_start + ch;
 +            y1 = s->ytop + ch * h + (h / 2); /* position of center line */
 +            for (x = 0; x < s->width; x++) {
 +                y = (s->sample_array[i] * h2) >> 15;
 +                if (y < 0) {
 +                    y = -y;
 +                    ys = y1 - y;
 +                } else {
 +                    ys = y1;
 +                }
 +                fill_rectangle(s->xleft + x, ys, 1, y);
 +                i += channels;
 +                if (i >= SAMPLE_ARRAY_SIZE)
 +                    i -= SAMPLE_ARRAY_SIZE;
 +            }
 +        }
 +
 +        SDL_SetRenderDrawColor(renderer, 0, 0, 255, 255);
 +
 +        for (ch = 1; ch < nb_display_channels; ch++) {
 +            y = s->ytop + ch * h;
 +            fill_rectangle(s->xleft, y, s->width, 1);
 +        }
 +    } else {
 +        if (realloc_texture(&s->vis_texture, SDL_PIXELFORMAT_ARGB8888, s->width, s->height, SDL_BLENDMODE_NONE, 1) < 0)
 +            return;
 +
 +        nb_display_channels= FFMIN(nb_display_channels, 2);
 +        if (rdft_bits != s->rdft_bits) {
 +            av_rdft_end(s->rdft);
 +            av_free(s->rdft_data);
 +            s->rdft = av_rdft_init(rdft_bits, DFT_R2C);
 +            s->rdft_bits = rdft_bits;
 +            s->rdft_data = av_malloc_array(nb_freq, 4 *sizeof(*s->rdft_data));
 +        }
 +        if (!s->rdft || !s->rdft_data){
 +            av_log(NULL, AV_LOG_ERROR, "Failed to allocate buffers for RDFT, switching to waves display\n");
 +            s->show_mode = SHOW_MODE_WAVES;
 +        } else {
 +            FFTSample *data[2];
 +            SDL_Rect rect = {.x = s->xpos, .y = 0, .w = 1, .h = s->height};
 +            uint32_t *pixels;
 +            int pitch;
 +            for (ch = 0; ch < nb_display_channels; ch++) {
 +                data[ch] = s->rdft_data + 2 * nb_freq * ch;
 +                i = i_start + ch;
 +                for (x = 0; x < 2 * nb_freq; x++) {
 +                    double w = (x-nb_freq) * (1.0 / nb_freq);
 +                    data[ch][x] = s->sample_array[i] * (1.0 - w * w);
 +                    i += channels;
 +                    if (i >= SAMPLE_ARRAY_SIZE)
 +                        i -= SAMPLE_ARRAY_SIZE;
 +                }
 +                av_rdft_calc(s->rdft, data[ch]);
 +            }
 +            /* Least efficient way to do this, we should of course
 +             * directly access it but it is more than fast enough. */
 +            if (!SDL_LockTexture(s->vis_texture, &rect, (void **)&pixels, &pitch)) {
 +                pitch >>= 2;
 +                pixels += pitch * s->height;
 +                for (y = 0; y < s->height; y++) {
 +                    double w = 1 / sqrt(nb_freq);
 +                    int a = sqrt(w * sqrt(data[0][2 * y + 0] * data[0][2 * y + 0] + data[0][2 * y + 1] * data[0][2 * y + 1]));
 +                    int b = (nb_display_channels == 2 ) ? sqrt(w * hypot(data[1][2 * y + 0], data[1][2 * y + 1]))
 +                                                        : a;
 +                    a = FFMIN(a, 255);
 +                    b = FFMIN(b, 255);
 +                    pixels -= pitch;
 +                    *pixels = (a << 16) + (b << 8) + ((a+b) >> 1);
 +                }
 +                SDL_UnlockTexture(s->vis_texture);
 +            }
 +            SDL_RenderCopy(renderer, s->vis_texture, NULL, NULL);
 +        }
 +        if (!s->paused)
 +            s->xpos++;
 +        if (s->xpos >= s->width)
 +            s->xpos= s->xleft;
 +    }
 +}
 +
 +static void stream_component_close(VideoState *is, int stream_index)
 +{
 +    AVFormatContext *ic = is->ic;
 +    AVCodecParameters *codecpar;
 +
 +    if (stream_index < 0 || stream_index >= ic->nb_streams)
 +        return;
 +    codecpar = ic->streams[stream_index]->codecpar;
 +
 +    switch (codecpar->codec_type) {
 +    case AVMEDIA_TYPE_AUDIO:
 +        decoder_abort(&is->auddec, &is->sampq);
 +        SDL_CloseAudio();
 +        decoder_destroy(&is->auddec);
 +        swr_free(&is->swr_ctx);
 +        av_freep(&is->audio_buf1);
 +        is->audio_buf1_size = 0;
 +        is->audio_buf = NULL;
 +
 +        if (is->rdft) {
 +            av_rdft_end(is->rdft);
 +            av_freep(&is->rdft_data);
 +            is->rdft = NULL;
 +            is->rdft_bits = 0;
 +        }
 +        break;
 +    case AVMEDIA_TYPE_VIDEO:
 +        decoder_abort(&is->viddec, &is->pictq);
 +        decoder_destroy(&is->viddec);
 +        break;
 +    case AVMEDIA_TYPE_SUBTITLE:
 +        decoder_abort(&is->subdec, &is->subpq);
 +        decoder_destroy(&is->subdec);
 +        break;
 +    default:
 +        break;
 +    }
 +
 +    ic->streams[stream_index]->discard = AVDISCARD_ALL;
 +    switch (codecpar->codec_type) {
 +    case AVMEDIA_TYPE_AUDIO:
 +        is->audio_st = NULL;
 +        is->audio_stream = -1;
 +        break;
 +    case AVMEDIA_TYPE_VIDEO:
 +        is->video_st = NULL;
 +        is->video_stream = -1;
 +        break;
 +    case AVMEDIA_TYPE_SUBTITLE:
 +        is->subtitle_st = NULL;
 +        is->subtitle_stream = -1;
 +        break;
 +    default:
 +        break;
 +    }
 +}
 +
 +static void stream_close(VideoState *is)
 +{
 +    /* XXX: use a special url_shutdown call to abort parse cleanly */
 +    is->abort_request = 1;
 +    SDL_WaitThread(is->read_tid, NULL);
 +
 +    /* close each stream */
 +    if (is->audio_stream >= 0)
 +        stream_component_close(is, is->audio_stream);
 +    if (is->video_stream >= 0)
 +        stream_component_close(is, is->video_stream);
 +    if (is->subtitle_stream >= 0)
 +        stream_component_close(is, is->subtitle_stream);
 +
 +    avformat_close_input(&is->ic);
 +
 +    packet_queue_destroy(&is->videoq);
 +    packet_queue_destroy(&is->audioq);
 +    packet_queue_destroy(&is->subtitleq);
 +
 +    /* free all pictures */
 +    frame_queue_destory(&is->pictq);
 +    frame_queue_destory(&is->sampq);
 +    frame_queue_destory(&is->subpq);
 +    SDL_DestroyCond(is->continue_read_thread);
 +    sws_freeContext(is->img_convert_ctx);
 +    sws_freeContext(is->sub_convert_ctx);
 +    av_free(is->filename);
 +    if (is->vis_texture)
 +        SDL_DestroyTexture(is->vis_texture);
 +    if (is->vid_texture)
 +        SDL_DestroyTexture(is->vid_texture);
 +    if (is->sub_texture)
 +        SDL_DestroyTexture(is->sub_texture);
 +    av_free(is);
 +}
 +
 +static void do_exit(VideoState *is)
 +{
 +    if (is) {
 +        stream_close(is);
 +    }
 +    if (renderer)
 +        SDL_DestroyRenderer(renderer);
 +    if (window)
 +        SDL_DestroyWindow(window);
 +    av_lockmgr_register(NULL);
 +    uninit_opts();
 +#if CONFIG_AVFILTER
 +    av_freep(&vfilters_list);
 +#endif
 +    avformat_network_deinit();
 +    if (show_status)
 +        printf("\n");
 +    SDL_Quit();
 +    av_log(NULL, AV_LOG_QUIET, "%s", "");
 +    exit(0);
 +}
 +
 +static void sigterm_handler(int sig)
 +{
 +    exit(123);
 +}
 +
 +static void set_default_window_size(int width, int height, AVRational sar)
 +{
 +    SDL_Rect rect;
 +    calculate_display_rect(&rect, 0, 0, INT_MAX, height, width, height, sar);
 +    default_width  = rect.w;
 +    default_height = rect.h;
 +}
 +
 +static int video_open(VideoState *is)
 +{
 +    int w,h;
 +
 +    if (screen_width) {
 +        w = screen_width;
 +        h = screen_height;
 +    } else {
 +        w = default_width;
 +        h = default_height;
 +    }
 +
 +    if (!window) {
 +        int flags = SDL_WINDOW_SHOWN;
 +        if (!window_title)
 +            window_title = input_filename;
 +        if (is_full_screen)
 +            flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
 +        if (borderless)
 +            flags |= SDL_WINDOW_BORDERLESS;
 +        else
 +            flags |= SDL_WINDOW_RESIZABLE;
 +        window = SDL_CreateWindow(window_title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, w, h, flags);
 +        SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
 +        if (window) {
 +            SDL_RendererInfo info;
 +            renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
 +            if (!renderer) {
 +                av_log(NULL, AV_LOG_WARNING, "Failed to initialize a hardware accelerated renderer: %s\n", SDL_GetError());
 +                renderer = SDL_CreateRenderer(window, -1, 0);
 +            }
 +            if (renderer) {
 +                if (!SDL_GetRendererInfo(renderer, &info))
 +                    av_log(NULL, AV_LOG_VERBOSE, "Initialized %s renderer.\n", info.name);
 +            }
 +        }
 +    } else {
 +        SDL_SetWindowSize(window, w, h);
 +    }
 +
 +    if (!window || !renderer) {
 +        av_log(NULL, AV_LOG_FATAL, "SDL: could not set video mode - exiting\n");
 +        do_exit(is);
 +    }
 +
 +    is->width  = w;
 +    is->height = h;
 +
 +    return 0;
 +}
 +
 +/* display the current picture, if any */
 +static void video_display(VideoState *is)
 +{
 +    if (!window)
 +        video_open(is);
 +
 +    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
 +    SDL_RenderClear(renderer);
 +    if (is->audio_st && is->show_mode != SHOW_MODE_VIDEO)
 +        video_audio_display(is);
 +    else if (is->video_st)
 +        video_image_display(is);
 +    SDL_RenderPresent(renderer);
 +}
 +
 +static double get_clock(Clock *c)
 +{
 +    if (*c->queue_serial != c->serial)
 +        return NAN;
 +    if (c->paused) {
 +        return c->pts;
 +    } else {
 +        double time = av_gettime_relative() / 1000000.0;
 +        return c->pts_drift + time - (time - c->last_updated) * (1.0 - c->speed);
 +    }
 +}
 +
 +static void set_clock_at(Clock *c, double pts, int serial, double time)
 +{
 +    c->pts = pts;
 +    c->last_updated = time;
 +    c->pts_drift = c->pts - time;
 +    c->serial = serial;
 +}
 +
 +static void set_clock(Clock *c, double pts, int serial)
 +{
 +    double time = av_gettime_relative() / 1000000.0;
 +    set_clock_at(c, pts, serial, time);
 +}
 +
 +static void set_clock_speed(Clock *c, double speed)
 +{
 +    set_clock(c, get_clock(c), c->serial);
 +    c->speed = speed;
 +}
 +
 +static void init_clock(Clock *c, int *queue_serial)
 +{
 +    c->speed = 1.0;
 +    c->paused = 0;
 +    c->queue_serial = queue_serial;
 +    set_clock(c, NAN, -1);
 +}
 +
 +static void sync_clock_to_slave(Clock *c, Clock *slave)
 +{
 +    double clock = get_clock(c);
 +    double slave_clock = get_clock(slave);
 +    if (!isnan(slave_clock) && (isnan(clock) || fabs(clock - slave_clock) > AV_NOSYNC_THRESHOLD))
 +        set_clock(c, slave_clock, slave->serial);
 +}
 +
 +static int get_master_sync_type(VideoState *is) {
 +    if (is->av_sync_type == AV_SYNC_VIDEO_MASTER) {
 +        if (is->video_st)
 +            return AV_SYNC_VIDEO_MASTER;
 +        else
 +            return AV_SYNC_AUDIO_MASTER;
 +    } else if (is->av_sync_type == AV_SYNC_AUDIO_MASTER) {
 +        if (is->audio_st)
 +            return AV_SYNC_AUDIO_MASTER;
 +        else
 +            return AV_SYNC_EXTERNAL_CLOCK;
 +    } else {
 +        return AV_SYNC_EXTERNAL_CLOCK;
 +    }
 +}
 +
 +/* get the current master clock value */
 +static double get_master_clock(VideoState *is)
 +{
 +    double val;
 +
 +    switch (get_master_sync_type(is)) {
 +        case AV_SYNC_VIDEO_MASTER:
 +            val = get_clock(&is->vidclk);
 +            break;
 +        case AV_SYNC_AUDIO_MASTER:
 +            val = get_clock(&is->audclk);
 +            break;
 +        default:
 +            val = get_clock(&is->extclk);
 +            break;
 +    }
 +    return val;
 +}
 +
 +static void check_external_clock_speed(VideoState *is) {
 +   if (is->video_stream >= 0 && is->videoq.nb_packets <= EXTERNAL_CLOCK_MIN_FRAMES ||
 +       is->audio_stream >= 0 && is->audioq.nb_packets <= EXTERNAL_CLOCK_MIN_FRAMES) {
 +       set_clock_speed(&is->extclk, FFMAX(EXTERNAL_CLOCK_SPEED_MIN, is->extclk.speed - EXTERNAL_CLOCK_SPEED_STEP));
 +   } else if ((is->video_stream < 0 || is->videoq.nb_packets > EXTERNAL_CLOCK_MAX_FRAMES) &&
 +              (is->audio_stream < 0 || is->audioq.nb_packets > EXTERNAL_CLOCK_MAX_FRAMES)) {
 +       set_clock_speed(&is->extclk, FFMIN(EXTERNAL_CLOCK_SPEED_MAX, is->extclk.speed + EXTERNAL_CLOCK_SPEED_STEP));
 +   } else {
 +       double speed = is->extclk.speed;
 +       if (speed != 1.0)
 +           set_clock_speed(&is->extclk, speed + EXTERNAL_CLOCK_SPEED_STEP * (1.0 - speed) / fabs(1.0 - speed));
 +   }
 +}
 +
 +/* seek in the stream */
 +static void stream_seek(VideoState *is, int64_t pos, int64_t rel, int seek_by_bytes)
 +{
 +    if (!is->seek_req) {
 +        is->seek_pos = pos;
 +        is->seek_rel = rel;
 +        is->seek_flags &= ~AVSEEK_FLAG_BYTE;
 +        if (seek_by_bytes)
 +            is->seek_flags |= AVSEEK_FLAG_BYTE;
 +        is->seek_req = 1;
 +        SDL_CondSignal(is->continue_read_thread);
 +    }
 +}
 +
 +/* pause or resume the video */
 +static void stream_toggle_pause(VideoState *is)
 +{
 +    if (is->paused) {
 +        is->frame_timer += av_gettime_relative() / 1000000.0 - is->vidclk.last_updated;
 +        if (is->read_pause_return != AVERROR(ENOSYS)) {
 +            is->vidclk.paused = 0;
 +        }
 +        set_clock(&is->vidclk, get_clock(&is->vidclk), is->vidclk.serial);
 +    }
 +    set_clock(&is->extclk, get_clock(&is->extclk), is->extclk.serial);
 +    is->paused = is->audclk.paused = is->vidclk.paused = is->extclk.paused = !is->paused;
 +}
 +
 +static void toggle_pause(VideoState *is)
 +{
 +    stream_toggle_pause(is);
 +    is->step = 0;
 +}
 +
 +static void toggle_mute(VideoState *is)
 +{
 +    is->muted = !is->muted;
 +}
 +
 +static void update_volume(VideoState *is, int sign, double step)
 +{
 +    double volume_level = is->audio_volume ? (20 * log(is->audio_volume / (double)SDL_MIX_MAXVOLUME) / log(10)) : -1000.0;
 +    int new_volume = lrint(SDL_MIX_MAXVOLUME * pow(10.0, (volume_level + sign * step) / 20.0));
 +    is->audio_volume = av_clip(is->audio_volume == new_volume ? (is->audio_volume + sign) : new_volume, 0, SDL_MIX_MAXVOLUME);
 +}
 +
 +static void step_to_next_frame(VideoState *is)
 +{
 +    /* if the stream is paused unpause it, then step */
 +    if (is->paused)
 +        stream_toggle_pause(is);
 +    is->step = 1;
 +}
 +
 +static double compute_target_delay(double delay, VideoState *is)
 +{
 +    double sync_threshold, diff = 0;
 +
 +    /* update delay to follow master synchronisation source */
 +    if (get_master_sync_type(is) != AV_SYNC_VIDEO_MASTER) {
 +        /* if video is slave, we try to correct big delays by
 +           duplicating or deleting a frame */
 +        diff = get_clock(&is->vidclk) - get_master_clock(is);
 +
 +        /* skip or repeat frame. We take into account the
 +           delay to compute the threshold. I still don't know
 +           if it is the best guess */
 +        sync_threshold = FFMAX(AV_SYNC_THRESHOLD_MIN, FFMIN(AV_SYNC_THRESHOLD_MAX, delay));
 +        if (!isnan(diff) && fabs(diff) < is->max_frame_duration) {
 +            if (diff <= -sync_threshold)
 +                delay = FFMAX(0, delay + diff);
 +            else if (diff >= sync_threshold && delay > AV_SYNC_FRAMEDUP_THRESHOLD)
 +                delay = delay + diff;
 +            else if (diff >= sync_threshold)
 +                delay = 2 * delay;
 +        }
 +    }
 +
 +    av_log(NULL, AV_LOG_TRACE, "video: delay=%0.3f A-V=%f\n",
 +            delay, -diff);
 +
 +    return delay;
 +}
 +
 +static double vp_duration(VideoState *is, Frame *vp, Frame *nextvp) {
 +    if (vp->serial == nextvp->serial) {
 +        double duration = nextvp->pts - vp->pts;
 +        if (isnan(duration) || duration <= 0 || duration > is->max_frame_duration)
 +            return vp->duration;
 +        else
 +            return duration;
 +    } else {
 +        return 0.0;
 +    }
 +}
 +
 +static void update_video_pts(VideoState *is, double pts, int64_t pos, int serial) {
 +    /* update current video pts */
 +    set_clock(&is->vidclk, pts, serial);
 +    sync_clock_to_slave(&is->extclk, &is->vidclk);
 +}
 +
 +/* called to display each frame */
 +static void video_refresh(void *opaque, double *remaining_time)
 +{
 +    VideoState *is = opaque;
 +    double time;
 +
 +    Frame *sp, *sp2;
 +
 +    if (!is->paused && get_master_sync_type(is) == AV_SYNC_EXTERNAL_CLOCK && is->realtime)
 +        check_external_clock_speed(is);
 +
 +    if (!display_disable && is->show_mode != SHOW_MODE_VIDEO && is->audio_st) {
 +        time = av_gettime_relative() / 1000000.0;
 +        if (is->force_refresh || is->last_vis_time + rdftspeed < time) {
 +            video_display(is);
 +            is->last_vis_time = time;
 +        }
 +        *remaining_time = FFMIN(*remaining_time, is->last_vis_time + rdftspeed - time);
 +    }
 +
 +    if (is->video_st) {
 +retry:
 +        if (frame_queue_nb_remaining(&is->pictq) == 0) {
 +            // nothing to do, no picture to display in the queue
 +        } else {
 +            double last_duration, duration, delay;
 +            Frame *vp, *lastvp;
 +
 +            /* dequeue the picture */
 +            lastvp = frame_queue_peek_last(&is->pictq);
 +            vp = frame_queue_peek(&is->pictq);
 +
 +            if (vp->serial != is->videoq.serial) {
 +                frame_queue_next(&is->pictq);
 +                goto retry;
 +            }
 +
 +            if (lastvp->serial != vp->serial)
 +                is->frame_timer = av_gettime_relative() / 1000000.0;
 +
 +            if (is->paused)
 +                goto display;
 +
 +            /* compute nominal last_duration */
 +            last_duration = vp_duration(is, lastvp, vp);
 +            delay = compute_target_delay(last_duration, is);
 +
 +            time= av_gettime_relative()/1000000.0;
 +            if (time < is->frame_timer + delay) {
 +                *remaining_time = FFMIN(is->frame_timer + delay - time, *remaining_time);
 +                goto display;
 +            }
 +
 +            is->frame_timer += delay;
 +            if (delay > 0 && time - is->frame_timer > AV_SYNC_THRESHOLD_MAX)
 +                is->frame_timer = time;
 +
 +            SDL_LockMutex(is->pictq.mutex);
 +            if (!isnan(vp->pts))
 +                update_video_pts(is, vp->pts, vp->pos, vp->serial);
 +            SDL_UnlockMutex(is->pictq.mutex);
 +
 +            if (frame_queue_nb_remaining(&is->pictq) > 1) {
 +                Frame *nextvp = frame_queue_peek_next(&is->pictq);
 +                duration = vp_duration(is, vp, nextvp);
 +                if(!is->step && (framedrop>0 || (framedrop && get_master_sync_type(is) != AV_SYNC_VIDEO_MASTER)) && time > is->frame_timer + duration){
 +                    is->frame_drops_late++;
 +                    frame_queue_next(&is->pictq);
 +                    goto retry;
 +                }
 +            }
 +
 +            if (is->subtitle_st) {
 +                    while (frame_queue_nb_remaining(&is->subpq) > 0) {
 +                        sp = frame_queue_peek(&is->subpq);
 +
 +                        if (frame_queue_nb_remaining(&is->subpq) > 1)
 +                            sp2 = frame_queue_peek_next(&is->subpq);
 +                        else
 +                            sp2 = NULL;
 +
 +                        if (sp->serial != is->subtitleq.serial
 +                                || (is->vidclk.pts > (sp->pts + ((float) sp->sub.end_display_time / 1000)))
 +                                || (sp2 && is->vidclk.pts > (sp2->pts + ((float) sp2->sub.start_display_time / 1000))))
 +                        {
 +                            if (sp->uploaded) {
 +                                int i;
 +                                for (i = 0; i < sp->sub.num_rects; i++) {
 +                                    AVSubtitleRect *sub_rect = sp->sub.rects[i];
 +                                    uint8_t *pixels;
 +                                    int pitch, j;
 +
 +                                    if (!SDL_LockTexture(is->sub_texture, (SDL_Rect *)sub_rect, (void **)&pixels, &pitch)) {
 +                                        for (j = 0; j < sub_rect->h; j++, pixels += pitch)
 +                                            memset(pixels, 0, sub_rect->w << 2);
 +                                        SDL_UnlockTexture(is->sub_texture);
 +                                    }
 +                                }
 +                            }
 +                            frame_queue_next(&is->subpq);
 +                        } else {
 +                            break;
 +                        }
 +                    }
 +            }
 +
 +            frame_queue_next(&is->pictq);
 +            is->force_refresh = 1;
 +
 +            if (is->step && !is->paused)
 +                stream_toggle_pause(is);
 +        }
 +display:
 +        /* display picture */
 +        if (!display_disable && is->force_refresh && is->show_mode == SHOW_MODE_VIDEO && is->pictq.rindex_shown)
 +            video_display(is);
 +    }
 +    is->force_refresh = 0;
 +    if (show_status) {
 +        static int64_t last_time;
 +        int64_t cur_time;
 +        int aqsize, vqsize, sqsize;
 +        double av_diff;
 +
 +        cur_time = av_gettime_relative();
 +        if (!last_time || (cur_time - last_time) >= 30000) {
 +            aqsize = 0;
 +            vqsize = 0;
 +            sqsize = 0;
 +            if (is->audio_st)
 +                aqsize = is->audioq.size;
 +            if (is->video_st)
 +                vqsize = is->videoq.size;
 +            if (is->subtitle_st)
 +                sqsize = is->subtitleq.size;
 +            av_diff = 0;
 +            if (is->audio_st && is->video_st)
 +                av_diff = get_clock(&is->audclk) - get_clock(&is->vidclk);
 +            else if (is->video_st)
 +                av_diff = get_master_clock(is) - get_clock(&is->vidclk);
 +            else if (is->audio_st)
 +                av_diff = get_master_clock(is) - get_clock(&is->audclk);
 +            av_log(NULL, AV_LOG_INFO,
 +                   "%7.2f %s:%7.3f fd=%4d aq=%5dKB vq=%5dKB sq=%5dB f=%"PRId64"/%"PRId64"   \r",
 +                   get_master_clock(is),
 +                   (is->audio_st && is->video_st) ? "A-V" : (is->video_st ? "M-V" : (is->audio_st ? "M-A" : "   ")),
 +                   av_diff,
 +                   is->frame_drops_early + is->frame_drops_late,
 +                   aqsize / 1024,
 +                   vqsize / 1024,
 +                   sqsize,
 +                   is->video_st ? is->viddec.avctx->pts_correction_num_faulty_dts : 0,
 +                   is->video_st ? is->viddec.avctx->pts_correction_num_faulty_pts : 0);
 +            fflush(stdout);
 +            last_time = cur_time;
 +        }
 +    }
 +}
 +
 +static int queue_picture(VideoState *is, AVFrame *src_frame, double pts, double duration, int64_t pos, int serial)
 +{
 +    Frame *vp;
 +
 +#if defined(DEBUG_SYNC)
 +    printf("frame_type=%c pts=%0.3f\n",
 +           av_get_picture_type_char(src_frame->pict_type), pts);
 +#endif
 +
 +    if (!(vp = frame_queue_peek_writable(&is->pictq)))
 +        return -1;
 +
 +    vp->sar = src_frame->sample_aspect_ratio;
 +    vp->uploaded = 0;
 +
 +    vp->width = src_frame->width;
 +    vp->height = src_frame->height;
 +    vp->format = src_frame->format;
 +
 +    vp->pts = pts;
 +    vp->duration = duration;
 +    vp->pos = pos;
 +    vp->serial = serial;
 +
 +    set_default_window_size(vp->width, vp->height, vp->sar);
 +
 +    av_frame_move_ref(vp->frame, src_frame);
 +    frame_queue_push(&is->pictq);
 +    return 0;
 +}
 +
 +static int get_video_frame(VideoState *is, AVFrame *frame)
 +{
 +    int got_picture;
 +
 +    if ((got_picture = decoder_decode_frame(&is->viddec, frame, NULL)) < 0)
 +        return -1;
 +
 +    if (got_picture) {
 +        double dpts = NAN;
 +
 +        if (frame->pts != AV_NOPTS_VALUE)
 +            dpts = av_q2d(is->video_st->time_base) * frame->pts;
 +
 +        frame->sample_aspect_ratio = av_guess_sample_aspect_ratio(is->ic, is->video_st, frame);
 +
 +        if (framedrop>0 || (framedrop && get_master_sync_type(is) != AV_SYNC_VIDEO_MASTER)) {
 +            if (frame->pts != AV_NOPTS_VALUE) {
 +                double diff = dpts - get_master_clock(is);
 +                if (!isnan(diff) && fabs(diff) < AV_NOSYNC_THRESHOLD &&
 +                    diff - is->frame_last_filter_delay < 0 &&
 +                    is->viddec.pkt_serial == is->vidclk.serial &&
 +                    is->videoq.nb_packets) {
 +                    is->frame_drops_early++;
 +                    av_frame_unref(frame);
 +                    got_picture = 0;
 +                }
 +            }
 +        }
 +    }
 +
 +    return got_picture;
 +}
 +
 +#if CONFIG_AVFILTER
 +static int configure_filtergraph(AVFilterGraph *graph, const char *filtergraph,
 +                                 AVFilterContext *source_ctx, AVFilterContext *sink_ctx)
 +{
 +    int ret, i;
 +    int nb_filters = graph->nb_filters;
 +    AVFilterInOut *outputs = NULL, *inputs = NULL;
 +
 +    if (filtergraph) {
 +        outputs = avfilter_inout_alloc();
 +        inputs  = avfilter_inout_alloc();
 +        if (!outputs || !inputs) {
 +            ret = AVERROR(ENOMEM);
 +            goto fail;
 +        }
 +
 +        outputs->name       = av_strdup("in");
 +        outputs->filter_ctx = source_ctx;
 +        outputs->pad_idx    = 0;
 +        outputs->next       = NULL;
 +
 +        inputs->name        = av_strdup("out");
 +        inputs->filter_ctx  = sink_ctx;
 +        inputs->pad_idx     = 0;
 +        inputs->next        = NULL;
 +
 +        if ((ret = avfilter_graph_parse_ptr(graph, filtergraph, &inputs, &outputs, NULL)) < 0)
 +            goto fail;
 +    } else {
 +        if ((ret = avfilter_link(source_ctx, 0, sink_ctx, 0)) < 0)
 +            goto fail;
 +    }
 +
 +    /* Reorder the filters to ensure that inputs of the custom filters are merged first */
 +    for (i = 0; i < graph->nb_filters - nb_filters; i++)
 +        FFSWAP(AVFilterContext*, graph->filters[i], graph->filters[i + nb_filters]);
 +
 +    ret = avfilter_graph_config(graph, NULL);
 +fail:
 +    avfilter_inout_free(&outputs);
 +    avfilter_inout_free(&inputs);
 +    return ret;
 +}
 +
 +static int configure_video_filters(AVFilterGraph *graph, VideoState *is, const char *vfilters, AVFrame *frame)
 +{
 +    static const enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_YUV420P, AV_PIX_FMT_BGRA, AV_PIX_FMT_NONE };
 +    char sws_flags_str[512] = "";
 +    char buffersrc_args[256];
 +    int ret;
 +    AVFilterContext *filt_src = NULL, *filt_out = NULL, *last_filter = NULL;
 +    AVCodecParameters *codecpar = is->video_st->codecpar;
 +    AVRational fr = av_guess_frame_rate(is->ic, is->video_st, NULL);
 +    AVDictionaryEntry *e = NULL;
 +
 +    while ((e = av_dict_get(sws_dict, "", e, AV_DICT_IGNORE_SUFFIX))) {
 +        if (!strcmp(e->key, "sws_flags")) {
 +            av_strlcatf(sws_flags_str, sizeof(sws_flags_str), "%s=%s:", "flags", e->value);
 +        } else
 +            av_strlcatf(sws_flags_str, sizeof(sws_flags_str), "%s=%s:", e->key, e->value);
 +    }
 +    if (strlen(sws_flags_str))
 +        sws_flags_str[strlen(sws_flags_str)-1] = '\0';
 +
 +    graph->scale_sws_opts = av_strdup(sws_flags_str);
 +
 +    snprintf(buffersrc_args, sizeof(buffersrc_args),
 +             "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
 +             frame->width, frame->height, frame->format,
 +             is->video_st->time_base.num, is->video_st->time_base.den,
 +             codecpar->sample_aspect_ratio.num, FFMAX(codecpar->sample_aspect_ratio.den, 1));
 +    if (fr.num && fr.den)
 +        av_strlcatf(buffersrc_args, sizeof(buffersrc_args), ":frame_rate=%d/%d", fr.num, fr.den);
 +
 +    if ((ret = avfilter_graph_create_filter(&filt_src,
 +                                            avfilter_get_by_name("buffer"),
 +                                            "ffplay_buffer", buffersrc_args, NULL,
 +                                            graph)) < 0)
 +        goto fail;
 +
 +    ret = avfilter_graph_create_filter(&filt_out,
 +                                       avfilter_get_by_name("buffersink"),
 +                                       "ffplay_buffersink", NULL, NULL, graph);
 +    if (ret < 0)
 +        goto fail;
 +
 +    if ((ret = av_opt_set_int_list(filt_out, "pix_fmts", pix_fmts,  AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN)) < 0)
 +        goto fail;
 +
 +    last_filter = filt_out;
 +
 +/* Note: this macro adds a filter before the lastly added filter, so the
 + * processing order of the filters is in reverse */
 +#define INSERT_FILT(name, arg) do {                                          \
 +    AVFilterContext *filt_ctx;                                               \
 +                                                                             \
 +    ret = avfilter_graph_create_filter(&filt_ctx,                            \
 +                                       avfilter_get_by_name(name),           \
 +                                       "ffplay_" name, arg, NULL, graph);    \
 +    if (ret < 0)                                                             \
 +        goto fail;                                                           \
 +                                                                             \
 +    ret = avfilter_link(filt_ctx, 0, last_filter, 0);                        \
 +    if (ret < 0)                                                             \
 +        goto fail;                                                           \
 +                                                                             \
 +    last_filter = filt_ctx;                                                  \
 +} while (0)
 +
 +    if (autorotate) {
 +        double theta  = get_rotation(is->video_st);
 +
 +        if (fabs(theta - 90) < 1.0) {
 +            INSERT_FILT("transpose", "clock");
 +        } else if (fabs(theta - 180) < 1.0) {
 +            INSERT_FILT("hflip", NULL);
 +            INSERT_FILT("vflip", NULL);
 +        } else if (fabs(theta - 270) < 1.0) {
 +            INSERT_FILT("transpose", "cclock");
 +        } else if (fabs(theta) > 1.0) {
 +            char rotate_buf[64];
 +            snprintf(rotate_buf, sizeof(rotate_buf), "%f*PI/180", theta);
 +            INSERT_FILT("rotate", rotate_buf);
 +        }
 +    }
 +
 +    if ((ret = configure_filtergraph(graph, vfilters, filt_src, last_filter)) < 0)
 +        goto fail;
 +
 +    is->in_video_filter  = filt_src;
 +    is->out_video_filter = filt_out;
 +
 +fail:
 +    return ret;
 +}
 +
 +static int configure_audio_filters(VideoState *is, const char *afilters, int force_output_format)
 +{
 +    static const enum AVSampleFormat sample_fmts[] = { AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_NONE };
 +    int sample_rates[2] = { 0, -1 };
 +    int64_t channel_layouts[2] = { 0, -1 };
 +    int channels[2] = { 0, -1 };
 +    AVFilterContext *filt_asrc = NULL, *filt_asink = NULL;
 +    char aresample_swr_opts[512] = "";
 +    AVDictionaryEntry *e = NULL;
 +    char asrc_args[256];
 +    int ret;
 +
 +    avfilter_graph_free(&is->agraph);
 +    if (!(is->agraph = avfilter_graph_alloc()))
 +        return AVERROR(ENOMEM);
 +
 +    while ((e = av_dict_get(swr_opts, "", e, AV_DICT_IGNORE_SUFFIX)))
 +        av_strlcatf(aresample_swr_opts, sizeof(aresample_swr_opts), "%s=%s:", e->key, e->value);
 +    if (strlen(aresample_swr_opts))
 +        aresample_swr_opts[strlen(aresample_swr_opts)-1] = '\0';
 +    av_opt_set(is->agraph, "aresample_swr_opts", aresample_swr_opts, 0);
 +
 +    ret = snprintf(asrc_args, sizeof(asrc_args),
 +                   "sample_rate=%d:sample_fmt=%s:channels=%d:time_base=%d/%d",
 +                   is->audio_filter_src.freq, av_get_sample_fmt_name(is->audio_filter_src.fmt),
 +                   is->audio_filter_src.channels,
 +                   1, is->audio_filter_src.freq);
 +    if (is->audio_filter_src.channel_layout)
 +        snprintf(asrc_args + ret, sizeof(asrc_args) - ret,
 +                 ":channel_layout=0x%"PRIx64,  is->audio_filter_src.channel_layout);
 +
 +    ret = avfilter_graph_create_filter(&filt_asrc,
 +                                       avfilter_get_by_name("abuffer"), "ffplay_abuffer",
 +                                       asrc_args, NULL, is->agraph);
 +    if (ret < 0)
 +        goto end;
 +
 +
 +    ret = avfilter_graph_create_filter(&filt_asink,
 +                                       avfilter_get_by_name("abuffersink"), "ffplay_abuffersink",
 +                                       NULL, NULL, is->agraph);
 +    if (ret < 0)
 +        goto end;
 +
 +    if ((ret = av_opt_set_int_list(filt_asink, "sample_fmts", sample_fmts,  AV_SAMPLE_FMT_NONE, AV_OPT_SEARCH_CHILDREN)) < 0)
 +        goto end;
 +    if ((ret = av_opt_set_int(filt_asink, "all_channel_counts", 1, AV_OPT_SEARCH_CHILDREN)) < 0)
 +        goto end;
 +
 +    if (force_output_format) {
 +        channel_layouts[0] = is->audio_tgt.channel_layout;
 +        channels       [0] = is->audio_tgt.channels;
 +        sample_rates   [0] = is->audio_tgt.freq;
 +        if ((ret = av_opt_set_int(filt_asink, "all_channel_counts", 0, AV_OPT_SEARCH_CHILDREN)) < 0)
 +            goto end;
 +        if ((ret = av_opt_set_int_list(filt_asink, "channel_layouts", channel_layouts,  -1, AV_OPT_SEARCH_CHILDREN)) < 0)
 +            goto end;
 +        if ((ret = av_opt_set_int_list(filt_asink, "channel_counts" , channels       ,  -1, AV_OPT_SEARCH_CHILDREN)) < 0)
 +            goto end;
 +        if ((ret = av_opt_set_int_list(filt_asink, "sample_rates"   , sample_rates   ,  -1, AV_OPT_SEARCH_CHILDREN)) < 0)
 +            goto end;
 +    }
 +
 +
 +    if ((ret = configure_filtergraph(is->agraph, afilters, filt_asrc, filt_asink)) < 0)
 +        goto end;
 +
 +    is->in_audio_filter  = filt_asrc;
 +    is->out_audio_filter = filt_asink;
 +
 +end:
 +    if (ret < 0)
 +        avfilter_graph_free(&is->agraph);
 +    return ret;
 +}
 +#endif  /* CONFIG_AVFILTER */
 +
 +static int audio_thread(void *arg)
 +{
 +    VideoState *is = arg;
 +    AVFrame *frame = av_frame_alloc();
 +    Frame *af;
 +#if CONFIG_AVFILTER
 +    int last_serial = -1;
 +    int64_t dec_channel_layout;
 +    int reconfigure;
 +#endif
 +    int got_frame = 0;
 +    AVRational tb;
 +    int ret = 0;
 +
 +    if (!frame)
 +        return AVERROR(ENOMEM);
 +
 +    do {
 +        if ((got_frame = decoder_decode_frame(&is->auddec, frame, NULL)) < 0)
 +            goto the_end;
 +
 +        if (got_frame) {
 +                tb = (AVRational){1, frame->sample_rate};
 +
 +#if CONFIG_AVFILTER
 +                dec_channel_layout = get_valid_channel_layout(frame->channel_layout, frame->channels);
 +
 +                reconfigure =
 +                    cmp_audio_fmts(is->audio_filter_src.fmt, is->audio_filter_src.channels,
 +                                   frame->format, frame->channels)    ||
 +                    is->audio_filter_src.channel_layout != dec_channel_layout ||
 +                    is->audio_filter_src.freq           != frame->sample_rate ||
 +                    is->auddec.pkt_serial               != last_serial;
 +
 +                if (reconfigure) {
 +                    char buf1[1024], buf2[1024];
 +                    av_get_channel_layout_string(buf1, sizeof(buf1), -1, is->audio_filter_src.channel_layout);
 +                    av_get_channel_layout_string(buf2, sizeof(buf2), -1, dec_channel_layout);
 +                    av_log(NULL, AV_LOG_DEBUG,
 +                           "Audio frame changed from rate:%d ch:%d fmt:%s layout:%s serial:%d to rate:%d ch:%d fmt:%s layout:%s serial:%d\n",
 +                           is->audio_filter_src.freq, is->audio_filter_src.channels, av_get_sample_fmt_name(is->audio_filter_src.fmt), buf1, last_serial,
 +                           frame->sample_rate, frame->channels, av_get_sample_fmt_name(frame->format), buf2, is->auddec.pkt_serial);
 +
 +                    is->audio_filter_src.fmt            = frame->format;
 +                    is->audio_filter_src.channels       = frame->channels;
 +                    is->audio_filter_src.channel_layout = dec_channel_layout;
 +                    is->audio_filter_src.freq           = frame->sample_rate;
 +                    last_serial                         = is->auddec.pkt_serial;
 +
 +                    if ((ret = configure_audio_filters(is, afilters, 1)) < 0)
 +                        goto the_end;
 +                }
 +
 +            if ((ret = av_buffersrc_add_frame(is->in_audio_filter, frame)) < 0)
 +                goto the_end;
 +
 +            while ((ret = av_buffersink_get_frame_flags(is->out_audio_filter, frame, 0)) >= 0) {
 +                tb = av_buffersink_get_time_base(is->out_audio_filter);
 +#endif
 +                if (!(af = frame_queue_peek_writable(&is->sampq)))
 +                    goto the_end;
 +
 +                af->pts = (frame->pts == AV_NOPTS_VALUE) ? NAN : frame->pts * av_q2d(tb);
 +                af->pos = frame->pkt_pos;
 +                af->serial = is->auddec.pkt_serial;
 +                af->duration = av_q2d((AVRational){frame->nb_samples, frame->sample_rate});
 +
 +                av_frame_move_ref(af->frame, frame);
 +                frame_queue_push(&is->sampq);
 +
 +#if CONFIG_AVFILTER
 +                if (is->audioq.serial != is->auddec.pkt_serial)
 +                    break;
 +            }
 +            if (ret == AVERROR_EOF)
 +                is->auddec.finished = is->auddec.pkt_serial;
 +#endif
 +        }
 +    } while (ret >= 0 || ret == AVERROR(EAGAIN) || ret == AVERROR_EOF);
 + the_end:
 +#if CONFIG_AVFILTER
 +    avfilter_graph_free(&is->agraph);
 +#endif
 +    av_frame_free(&frame);
 +    return ret;
 +}
 +
 +static int decoder_start(Decoder *d, int (*fn)(void *), void *arg)
 +{
 +    packet_queue_start(d->queue);
 +    d->decoder_tid = SDL_CreateThread(fn, "decoder", arg);
 +    if (!d->decoder_tid) {
 +        av_log(NULL, AV_LOG_ERROR, "SDL_CreateThread(): %s\n", SDL_GetError());
 +        return AVERROR(ENOMEM);
 +    }
 +    return 0;
 +}
 +
 +static int video_thread(void *arg)
 +{
 +    VideoState *is = arg;
 +    AVFrame *frame = av_frame_alloc();
 +    double pts;
 +    double duration;
 +    int ret;
 +    AVRational tb = is->video_st->time_base;
 +    AVRational frame_rate = av_guess_frame_rate(is->ic, is->video_st, NULL);
 +
 +#if CONFIG_AVFILTER
 +    AVFilterGraph *graph = avfilter_graph_alloc();
 +    AVFilterContext *filt_out = NULL, *filt_in = NULL;
 +    int last_w = 0;
 +    int last_h = 0;
 +    enum AVPixelFormat last_format = -2;
 +    int last_serial = -1;
 +    int last_vfilter_idx = 0;
 +    if (!graph) {
 +        av_frame_free(&frame);
 +        return AVERROR(ENOMEM);
 +    }
 +
 +#endif
 +
 +    if (!frame) {
 +#if CONFIG_AVFILTER
 +        avfilter_graph_free(&graph);
 +#endif
 +        return AVERROR(ENOMEM);
 +    }
 +
 +    for (;;) {
 +        ret = get_video_frame(is, frame);
 +        if (ret < 0)
 +            goto the_end;
 +        if (!ret)
 +            continue;
 +
 +#if CONFIG_AVFILTER
 +        if (   last_w != frame->width
 +            || last_h != frame->height
 +            || last_format != frame->format
 +            || last_serial != is->viddec.pkt_serial
 +            || last_vfilter_idx != is->vfilter_idx) {
 +            av_log(NULL, AV_LOG_DEBUG,
 +                   "Video frame changed from size:%dx%d format:%s serial:%d to size:%dx%d format:%s serial:%d\n",
 +                   last_w, last_h,
 +                   (const char *)av_x_if_null(av_get_pix_fmt_name(last_format), "none"), last_serial,
 +                   frame->width, frame->height,
 +                   (const char *)av_x_if_null(av_get_pix_fmt_name(frame->format), "none"), is->viddec.pkt_serial);
 +            avfilter_graph_free(&graph);
 +            graph = avfilter_graph_alloc();
 +            if ((ret = configure_video_filters(graph, is, vfilters_list ? vfilters_list[is->vfilter_idx] : NULL, frame)) < 0) {
 +                SDL_Event event;
 +                event.type = FF_QUIT_EVENT;
 +                event.user.data1 = is;
 +                SDL_PushEvent(&event);
 +                goto the_end;
 +            }
 +            filt_in  = is->in_video_filter;
 +            filt_out = is->out_video_filter;
 +            last_w = frame->width;
 +            last_h = frame->height;
 +            last_format = frame->format;
 +            last_serial = is->viddec.pkt_serial;
 +            last_vfilter_idx = is->vfilter_idx;
 +            frame_rate = av_buffersink_get_frame_rate(filt_out);
 +        }
 +
 +        ret = av_buffersrc_add_frame(filt_in, frame);
 +        if (ret < 0)
 +            goto the_end;
 +
 +        while (ret >= 0) {
 +            is->frame_last_returned_time = av_gettime_relative() / 1000000.0;
 +
 +            ret = av_buffersink_get_frame_flags(filt_out, frame, 0);
 +            if (ret < 0) {
 +                if (ret == AVERROR_EOF)
 +                    is->viddec.finished = is->viddec.pkt_serial;
 +                ret = 0;
 +                break;
 +            }
 +
 +            is->frame_last_filter_delay = av_gettime_relative() / 1000000.0 - is->frame_last_returned_time;
 +            if (fabs(is->frame_last_filter_delay) > AV_NOSYNC_THRESHOLD / 10.0)
 +                is->frame_last_filter_delay = 0;
 +            tb = av_buffersink_get_time_base(filt_out);
 +#endif
 +            duration = (frame_rate.num && frame_rate.den ? av_q2d((AVRational){frame_rate.den, frame_rate.num}) : 0);
 +            pts = (frame->pts == AV_NOPTS_VALUE) ? NAN : frame->pts * av_q2d(tb);
 +            ret = queue_picture(is, frame, pts, duration, frame->pkt_pos, is->viddec.pkt_serial);
 +            av_frame_unref(frame);
 +#if CONFIG_AVFILTER
 +        }
 +#endif
 +
 +        if (ret < 0)
 +            goto the_end;
 +    }
 + the_end:
 +#if CONFIG_AVFILTER
 +    avfilter_graph_free(&graph);
 +#endif
 +    av_frame_free(&frame);
 +    return 0;
 +}
 +
 +static int subtitle_thread(void *arg)
 +{
 +    VideoState *is = arg;
 +    Frame *sp;
 +    int got_subtitle;
 +    double pts;
 +
 +    for (;;) {
 +        if (!(sp = frame_queue_peek_writable(&is->subpq)))
 +            return 0;
 +
 +        if ((got_subtitle = decoder_decode_frame(&is->subdec, NULL, &sp->sub)) < 0)
 +            break;
 +
 +        pts = 0;
 +
 +        if (got_subtitle && sp->sub.format == 0) {
 +            if (sp->sub.pts != AV_NOPTS_VALUE)
 +                pts = sp->sub.pts / (double)AV_TIME_BASE;
 +            sp->pts = pts;
 +            sp->serial = is->subdec.pkt_serial;
 +            sp->width = is->subdec.avctx->width;
 +            sp->height = is->subdec.avctx->height;
 +            sp->uploaded = 0;
 +
 +            /* now we can update the picture count */
 +            frame_queue_push(&is->subpq);
 +        } else if (got_subtitle) {
 +            avsubtitle_free(&sp->sub);
 +        }
 +    }
 +    return 0;
 +}
 +
 +/* copy samples for viewing in editor window */
 +static void update_sample_display(VideoState *is, short *samples, int samples_size)
 +{
 +    int size, len;
 +
 +    size = samples_size / sizeof(short);
 +    while (size > 0) {
 +        len = SAMPLE_ARRAY_SIZE - is->sample_array_index;
 +        if (len > size)
 +            len = size;
 +        memcpy(is->sample_array + is->sample_array_index, samples, len * sizeof(short));
 +        samples += len;
 +        is->sample_array_index += len;
 +        if (is->sample_array_index >= SAMPLE_ARRAY_SIZE)
 +            is->sample_array_index = 0;
 +        size -= len;
 +    }
 +}
 +
 +/* return the wanted number of samples to get better sync if sync_type is video
 + * or external master clock */
 +static int synchronize_audio(VideoState *is, int nb_samples)
 +{
 +    int wanted_nb_samples = nb_samples;
 +
 +    /* if not master, then we try to remove or add samples to correct the clock */
 +    if (get_master_sync_type(is) != AV_SYNC_AUDIO_MASTER) {
 +        double diff, avg_diff;
 +        int min_nb_samples, max_nb_samples;
 +
 +        diff = get_clock(&is->audclk) - get_master_clock(is);
 +
 +        if (!isnan(diff) && fabs(diff) < AV_NOSYNC_THRESHOLD) {
 +            is->audio_diff_cum = diff + is->audio_diff_avg_coef * is->audio_diff_cum;
 +            if (is->audio_diff_avg_count < AUDIO_DIFF_AVG_NB) {
 +                /* not enough measures to have a correct estimate */
 +                is->audio_diff_avg_count++;
 +            } else {
 +                /* estimate the A-V difference */
 +                avg_diff = is->audio_diff_cum * (1.0 - is->audio_diff_avg_coef);
 +
 +                if (fabs(avg_diff) >= is->audio_diff_threshold) {
 +                    wanted_nb_samples = nb_samples + (int)(diff * is->audio_src.freq);
 +                    min_nb_samples = ((nb_samples * (100 - SAMPLE_CORRECTION_PERCENT_MAX) / 100));
 +                    max_nb_samples = ((nb_samples * (100 + SAMPLE_CORRECTION_PERCENT_MAX) / 100));
 +                    wanted_nb_samples = av_clip(wanted_nb_samples, min_nb_samples, max_nb_samples);
 +                }
 +                av_log(NULL, AV_LOG_TRACE, "diff=%f adiff=%f sample_diff=%d apts=%0.3f %f\n",
 +                        diff, avg_diff, wanted_nb_samples - nb_samples,
 +                        is->audio_clock, is->audio_diff_threshold);
 +            }
 +        } else {
 +            /* too big difference : may be initial PTS errors, so
 +               reset A-V filter */
 +            is->audio_diff_avg_count = 0;
 +            is->audio_diff_cum       = 0;
 +        }
 +    }
 +
 +    return wanted_nb_samples;
 +}
 +
 +/**
 + * Decode one audio frame and return its uncompressed size.
 + *
 + * The processed audio frame is decoded, converted if required, and
 + * stored in is->audio_buf, with size in bytes given by the return
 + * value.
 + */
 +static int audio_decode_frame(VideoState *is)
 +{
 +    int data_size, resampled_data_size;
 +    int64_t dec_channel_layout;
 +    av_unused double audio_clock0;
 +    int wanted_nb_samples;
 +    Frame *af;
 +
 +    if (is->paused)
 +        return -1;
 +
 +    do {
 +#if defined(_WIN32)
 +        while (frame_queue_nb_remaining(&is->sampq) == 0) {
 +            if ((av_gettime_relative() - audio_callback_time) > 1000000LL * is->audio_hw_buf_size / is->audio_tgt.bytes_per_sec / 2)
 +                return -1;
 +            av_usleep (1000);
 +        }
 +#endif
 +        if (!(af = frame_queue_peek_readable(&is->sampq)))
 +            return -1;
 +        frame_queue_next(&is->sampq);
 +    } while (af->serial != is->audioq.serial);
 +
 +    data_size = av_samples_get_buffer_size(NULL, af->frame->channels,
 +                                           af->frame->nb_samples,
 +                                           af->frame->format, 1);
 +
 +    dec_channel_layout =
 +        (af->frame->channel_layout && af->frame->channels == av_get_channel_layout_nb_channels(af->frame->channel_layout)) ?
 +        af->frame->channel_layout : av_get_default_channel_layout(af->frame->channels);
 +    wanted_nb_samples = synchronize_audio(is, af->frame->nb_samples);
 +
 +    if (af->frame->format        != is->audio_src.fmt            ||
 +        dec_channel_layout       != is->audio_src.channel_layout ||
 +        af->frame->sample_rate   != is->audio_src.freq           ||
 +        (wanted_nb_samples       != af->frame->nb_samples && !is->swr_ctx)) {
 +        swr_free(&is->swr_ctx);
 +        is->swr_ctx = swr_alloc_set_opts(NULL,
 +                                         is->audio_tgt.channel_layout, is->audio_tgt.fmt, is->audio_tgt.freq,
 +                                         dec_channel_layout,           af->frame->format, af->frame->sample_rate,
 +                                         0, NULL);
 +        if (!is->swr_ctx || swr_init(is->swr_ctx) < 0) {
 +            av_log(NULL, AV_LOG_ERROR,
 +                   "Cannot create sample rate converter for conversion of %d Hz %s %d channels to %d Hz %s %d channels!\n",
 +                    af->frame->sample_rate, av_get_sample_fmt_name(af->frame->format), af->frame->channels,
 +                    is->audio_tgt.freq, av_get_sample_fmt_name(is->audio_tgt.fmt), is->audio_tgt.channels);
 +            swr_free(&is->swr_ctx);
 +            return -1;
 +        }
 +        is->audio_src.channel_layout = dec_channel_layout;
 +        is->audio_src.channels       = af->frame->channels;
 +        is->audio_src.freq = af->frame->sample_rate;
 +        is->audio_src.fmt = af->frame->format;
 +    }
 +
 +    if (is->swr_ctx) {
 +        const uint8_t **in = (const uint8_t **)af->frame->extended_data;
 +        uint8_t **out = &is->audio_buf1;
 +        int out_count = (int64_t)wanted_nb_samples * is->audio_tgt.freq / af->frame->sample_rate + 256;
 +        int out_size  = av_samples_get_buffer_size(NULL, is->audio_tgt.channels, out_count, is->audio_tgt.fmt, 0);
 +        int len2;
 +        if (out_size < 0) {
 +            av_log(NULL, AV_LOG_ERROR, "av_samples_get_buffer_size() failed\n");
 +            return -1;
 +        }
 +        if (wanted_nb_samples != af->frame->nb_samples) {
 +            if (swr_set_compensation(is->swr_ctx, (wanted_nb_samples - af->frame->nb_samples) * is->audio_tgt.freq / af->frame->sample_rate,
 +                                        wanted_nb_samples * is->audio_tgt.freq / af->frame->sample_rate) < 0) {
 +                av_log(NULL, AV_LOG_ERROR, "swr_set_compensation() failed\n");
 +                return -1;
 +            }
 +        }
 +        av_fast_malloc(&is->audio_buf1, &is->audio_buf1_size, out_size);
 +        if (!is->audio_buf1)
 +            return AVERROR(ENOMEM);
 +        len2 = swr_convert(is->swr_ctx, out, out_count, in, af->frame->nb_samples);
 +        if (len2 < 0) {
 +            av_log(NULL, AV_LOG_ERROR, "swr_convert() failed\n");
 +            return -1;
 +        }
 +        if (len2 == out_count) {
 +            av_log(NULL, AV_LOG_WARNING, "audio buffer is probably too small\n");
 +            if (swr_init(is->swr_ctx) < 0)
 +                swr_free(&is->swr_ctx);
 +        }
 +        is->audio_buf = is->audio_buf1;
 +        resampled_data_size = len2 * is->audio_tgt.channels * av_get_bytes_per_sample(is->audio_tgt.fmt);
 +    } else {
 +        is->audio_buf = af->frame->data[0];
 +        resampled_data_size = data_size;
 +    }
 +
 +    audio_clock0 = is->audio_clock;
 +    /* update the audio clock with the pts */
 +    if (!isnan(af->pts))
 +        is->audio_clock = af->pts + (double) af->frame->nb_samples / af->frame->sample_rate;
 +    else
 +        is->audio_clock = NAN;
 +    is->audio_clock_serial = af->serial;
 +#ifdef DEBUG
 +    {
 +        static double last_clock;
 +        printf("audio: delay=%0.3f clock=%0.3f clock0=%0.3f\n",
 +               is->audio_clock - last_clock,
 +               is->audio_clock, audio_clock0);
 +        last_clock = is->audio_clock;
 +    }
 +#endif
 +    return resampled_data_size;
 +}
 +
 +/* prepare a new audio buffer */
 +static void sdl_audio_callback(void *opaque, Uint8 *stream, int len)
 +{
 +    VideoState *is = opaque;
 +    int audio_size, len1;
 +
 +    audio_callback_time = av_gettime_relative();
 +
 +    while (len > 0) {
 +        if (is->audio_buf_index >= is->audio_buf_size) {
 +           audio_size = audio_decode_frame(is);
 +           if (audio_size < 0) {
 +                /* if error, just output silence */
 +               is->audio_buf = NULL;
 +               is->audio_buf_size = SDL_AUDIO_MIN_BUFFER_SIZE / is->audio_tgt.frame_size * is->audio_tgt.frame_size;
 +           } else {
 +               if (is->show_mode != SHOW_MODE_VIDEO)
 +                   update_sample_display(is, (int16_t *)is->audio_buf, audio_size);
 +               is->audio_buf_size = audio_size;
 +           }
 +           is->audio_buf_index = 0;
 +        }
 +        len1 = is->audio_buf_size - is->audio_buf_index;
 +        if (len1 > len)
 +            len1 = len;
 +        if (!is->muted && is->audio_buf && is->audio_volume == SDL_MIX_MAXVOLUME)
 +            memcpy(stream, (uint8_t *)is->audio_buf + is->audio_buf_index, len1);
 +        else {
 +            memset(stream, 0, len1);
 +            if (!is->muted && is->audio_buf)
 +                SDL_MixAudio(stream, (uint8_t *)is->audio_buf + is->audio_buf_index, len1, is->audio_volume);
 +        }
 +        len -= len1;
 +        stream += len1;
 +        is->audio_buf_index += len1;
 +    }
 +    is->audio_write_buf_size = is->audio_buf_size - is->audio_buf_index;
 +    /* Let's assume the audio driver that is used by SDL has two periods. */
 +    if (!isnan(is->audio_clock)) {
 +        set_clock_at(&is->audclk, is->audio_clock - (double)(2 * is->audio_hw_buf_size + is->audio_write_buf_size) / is->audio_tgt.bytes_per_sec, is->audio_clock_serial, audio_callback_time / 1000000.0);
 +        sync_clock_to_slave(&is->extclk, &is->audclk);
 +    }
 +}
 +
 +static int audio_open(void *opaque, int64_t wanted_channel_layout, int wanted_nb_channels, int wanted_sample_rate, struct AudioParams *audio_hw_params)
 +{
 +    SDL_AudioSpec wanted_spec, spec;
 +    const char *env;
 +    static const int next_nb_channels[] = {0, 0, 1, 6, 2, 6, 4, 6};
 +    static const int next_sample_rates[] = {0, 44100, 48000, 96000, 192000};
 +    int next_sample_rate_idx = FF_ARRAY_ELEMS(next_sample_rates) - 1;
 +
 +    env = SDL_getenv("SDL_AUDIO_CHANNELS");
 +    if (env) {
 +        wanted_nb_channels = atoi(env);
 +        wanted_channel_layout = av_get_default_channel_layout(wanted_nb_channels);
 +    }
 +    if (!wanted_channel_layout || wanted_nb_channels != av_get_channel_layout_nb_channels(wanted_channel_layout)) {
 +        wanted_channel_layout = av_get_default_channel_layout(wanted_nb_channels);
 +        wanted_channel_layout &= ~AV_CH_LAYOUT_STEREO_DOWNMIX;
 +    }
 +    wanted_nb_channels = av_get_channel_layout_nb_channels(wanted_channel_layout);
 +    wanted_spec.channels = wanted_nb_channels;
 +    wanted_spec.freq = wanted_sample_rate;
 +    if (wanted_spec.freq <= 0 || wanted_spec.channels <= 0) {
 +        av_log(NULL, AV_LOG_ERROR, "Invalid sample rate or channel count!\n");
 +        return -1;
 +    }
 +    while (next_sample_rate_idx && next_sample_rates[next_sample_rate_idx] >= wanted_spec.freq)
 +        next_sample_rate_idx--;
 +    wanted_spec.format = AUDIO_S16SYS;
 +    wanted_spec.silence = 0;
 +    wanted_spec.samples = FFMAX(SDL_AUDIO_MIN_BUFFER_SIZE, 2 << av_log2(wanted_spec.freq / SDL_AUDIO_MAX_CALLBACKS_PER_SEC));
 +    wanted_spec.callback = sdl_audio_callback;
 +    wanted_spec.userdata = opaque;
 +    while (SDL_OpenAudio(&wanted_spec, &spec) < 0) {
 +        av_log(NULL, AV_LOG_WARNING, "SDL_OpenAudio (%d channels, %d Hz): %s\n",
 +               wanted_spec.channels, wanted_spec.freq, SDL_GetError());
 +        wanted_spec.channels = next_nb_channels[FFMIN(7, wanted_spec.channels)];
 +        if (!wanted_spec.channels) {
 +            wanted_spec.freq = next_sample_rates[next_sample_rate_idx--];
 +            wanted_spec.channels = wanted_nb_channels;
 +            if (!wanted_spec.freq) {
 +                av_log(NULL, AV_LOG_ERROR,
 +                       "No more combinations to try, audio open failed\n");
 +                return -1;
 +            }
 +        }
 +        wanted_channel_layout = av_get_default_channel_layout(wanted_spec.channels);
 +    }
 +    if (spec.format != AUDIO_S16SYS) {
 +        av_log(NULL, AV_LOG_ERROR,
 +               "SDL advised audio format %d is not supported!\n", spec.format);
 +        return -1;
 +    }
 +    if (spec.channels != wanted_spec.channels) {
 +        wanted_channel_layout = av_get_default_channel_layout(spec.channels);
 +        if (!wanted_channel_layout) {
 +            av_log(NULL, AV_LOG_ERROR,
 +                   "SDL advised channel count %d is not supported!\n", spec.channels);
 +            return -1;
 +        }
 +    }
 +
 +    audio_hw_params->fmt = AV_SAMPLE_FMT_S16;
 +    audio_hw_params->freq = spec.freq;
 +    audio_hw_params->channel_layout = wanted_channel_layout;
 +    audio_hw_params->channels =  spec.channels;
 +    audio_hw_params->frame_size = av_samples_get_buffer_size(NULL, audio_hw_params->channels, 1, audio_hw_params->fmt, 1);
 +    audio_hw_params->bytes_per_sec = av_samples_get_buffer_size(NULL, audio_hw_params->channels, audio_hw_params->freq, audio_hw_params->fmt, 1);
 +    if (audio_hw_params->bytes_per_sec <= 0 || audio_hw_params->frame_size <= 0) {
 +        av_log(NULL, AV_LOG_ERROR, "av_samples_get_buffer_size failed\n");
 +        return -1;
 +    }
 +    return spec.size;
 +}
 +
 +/* open a given stream. Return 0 if OK */
 +static int stream_component_open(VideoState *is, int stream_index)
 +{
 +    AVFormatContext *ic = is->ic;
 +    AVCodecContext *avctx;
 +    AVCodec *codec;
 +    const char *forced_codec_name = NULL;
 +    AVDictionary *opts = NULL;
 +    AVDictionaryEntry *t = NULL;
 +    int sample_rate, nb_channels;
 +    int64_t channel_layout;
 +    int ret = 0;
 +    int stream_lowres = lowres;
 +
 +    if (stream_index < 0 || stream_index >= ic->nb_streams)
 +        return -1;
 +
 +    avctx = avcodec_alloc_context3(NULL);
 +    if (!avctx)
 +        return AVERROR(ENOMEM);
 +
 +    ret = avcodec_parameters_to_context(avctx, ic->streams[stream_index]->codecpar);
 +    if (ret < 0)
 +        goto fail;
 +    av_codec_set_pkt_timebase(avctx, ic->streams[stream_index]->time_base);
 +
 +    codec = avcodec_find_decoder(avctx->codec_id);
 +
 +    switch(avctx->codec_type){
 +        case AVMEDIA_TYPE_AUDIO   : is->last_audio_stream    = stream_index; forced_codec_name =    audio_codec_name; break;
 +        case AVMEDIA_TYPE_SUBTITLE: is->last_subtitle_stream = stream_index; forced_codec_name = subtitle_codec_name; break;
 +        case AVMEDIA_TYPE_VIDEO   : is->last_video_stream    = stream_index; forced_codec_name =    video_codec_name; break;
 +    }
 +    if (forced_codec_name)
 +        codec = avcodec_find_decoder_by_name(forced_codec_name);
 +    if (!codec) {
 +        if (forced_codec_name) av_log(NULL, AV_LOG_WARNING,
 +                                      "No codec could be found with name '%s'\n", forced_codec_name);
 +        else                   av_log(NULL, AV_LOG_WARNING,
 +                                      "No codec could be found with id %d\n", avctx->codec_id);
 +        ret = AVERROR(EINVAL);
 +        goto fail;
 +    }
 +
 +    avctx->codec_id = codec->id;
 +    if(stream_lowres > av_codec_get_max_lowres(codec)){
 +        av_log(avctx, AV_LOG_WARNING, "The maximum value for lowres supported by the decoder is %d\n",
 +                av_codec_get_max_lowres(codec));
 +        stream_lowres = av_codec_get_max_lowres(codec);
 +    }
 +    av_codec_set_lowres(avctx, stream_lowres);
 +
 +#if FF_API_EMU_EDGE
 +    if(stream_lowres) avctx->flags |= CODEC_FLAG_EMU_EDGE;
 +#endif
 +    if (fast)
 +        avctx->flags2 |= AV_CODEC_FLAG2_FAST;
 +#if FF_API_EMU_EDGE
 +    if(codec->capabilities & AV_CODEC_CAP_DR1)
 +        avctx->flags |= CODEC_FLAG_EMU_EDGE;
 +#endif
 +
 +    opts = filter_codec_opts(codec_opts, avctx->codec_id, ic, ic->streams[stream_index], codec);
 +    if (!av_dict_get(opts, "threads", NULL, 0))
 +        av_dict_set(&opts, "threads", "auto", 0);
 +    if (stream_lowres)
 +        av_dict_set_int(&opts, "lowres", stream_lowres, 0);
 +    if (avctx->codec_type == AVMEDIA_TYPE_VIDEO || avctx->codec_type == AVMEDIA_TYPE_AUDIO)
 +        av_dict_set(&opts, "refcounted_frames", "1", 0);
 +    if ((ret = avcodec_open2(avctx, codec, &opts)) < 0) {
 +        goto fail;
 +    }
 +    if ((t = av_dict_get(opts, "", NULL, AV_DICT_IGNORE_SUFFIX))) {
 +        av_log(NULL, AV_LOG_ERROR, "Option %s not found.\n", t->key);
 +        ret =  AVERROR_OPTION_NOT_FOUND;
 +        goto fail;
 +    }
 +
 +    is->eof = 0;
 +    ic->streams[stream_index]->discard = AVDISCARD_DEFAULT;
 +    switch (avctx->codec_type) {
 +    case AVMEDIA_TYPE_AUDIO:
 +#if CONFIG_AVFILTER
 +        {
 +            AVFilterContext *sink;
 +
 +            is->audio_filter_src.freq           = avctx->sample_rate;
 +            is->audio_filter_src.channels       = avctx->channels;
 +            is->audio_filter_src.channel_layout = get_valid_channel_layout(avctx->channel_layout, avctx->channels);
 +            is->audio_filter_src.fmt            = avctx->sample_fmt;
 +            if ((ret = configure_audio_filters(is, afilters, 0)) < 0)
 +                goto fail;
 +            sink = is->out_audio_filter;
 +            sample_rate    = av_buffersink_get_sample_rate(sink);
 +            nb_channels    = av_buffersink_get_channels(sink);
 +            channel_layout = av_buffersink_get_channel_layout(sink);
 +        }
 +#else
 +        sample_rate    = avctx->sample_rate;
 +        nb_channels    = avctx->channels;
 +        channel_layout = avctx->channel_layout;
 +#endif
 +
 +        /* prepare audio output */
 +        if ((ret = audio_open(is, channel_layout, nb_channels, sample_rate, &is->audio_tgt)) < 0)
 +            goto fail;
 +        is->audio_hw_buf_size = ret;
 +        is->audio_src = is->audio_tgt;
 +        is->audio_buf_size  = 0;
 +        is->audio_buf_index = 0;
 +
 +        /* init averaging filter */
 +        is->audio_diff_avg_coef  = exp(log(0.01) / AUDIO_DIFF_AVG_NB);
 +        is->audio_diff_avg_count = 0;
 +        /* since we do not have a precise anough audio FIFO fullness,
 +           we correct audio sync only if larger than this threshold */
 +        is->audio_diff_threshold = (double)(is->audio_hw_buf_size) / is->audio_tgt.bytes_per_sec;
 +
 +        is->audio_stream = stream_index;
 +        is->audio_st = ic->streams[stream_index];
 +
 +        decoder_init(&is->auddec, avctx, &is->audioq, is->continue_read_thread);
 +        if ((is->ic->iformat->flags & (AVFMT_NOBINSEARCH | AVFMT_NOGENSEARCH | AVFMT_NO_BYTE_SEEK)) && !is->ic->iformat->read_seek) {
 +            is->auddec.start_pts = is->audio_st->start_time;
 +            is->auddec.start_pts_tb = is->audio_st->time_base;
 +        }
 +        if ((ret = decoder_start(&is->auddec, audio_thread, is)) < 0)
 +            goto out;
 +        SDL_PauseAudio(0);
 +        break;
 +    case AVMEDIA_TYPE_VIDEO:
 +        is->video_stream = stream_index;
 +        is->video_st = ic->streams[stream_index];
 +
 +        decoder_init(&is->viddec, avctx, &is->videoq, is->continue_read_thread);
 +        if ((ret = decoder_start(&is->viddec, video_thread, is)) < 0)
 +            goto out;
 +        is->queue_attachments_req = 1;
 +        break;
 +    case AVMEDIA_TYPE_SUBTITLE:
 +        is->subtitle_stream = stream_index;
 +        is->subtitle_st = ic->streams[stream_index];
 +
 +        decoder_init(&is->subdec, avctx, &is->subtitleq, is->continue_read_thread);
 +        if ((ret = decoder_start(&is->subdec, subtitle_thread, is)) < 0)
 +            goto out;
 +        break;
 +    default:
 +        break;
 +    }
 +    goto out;
 +
 +fail:
 +    avcodec_free_context(&avctx);
 +out:
 +    av_dict_free(&opts);
 +
 +    return ret;
 +}
 +
 +static int decode_interrupt_cb(void *ctx)
 +{
 +    VideoState *is = ctx;
 +    return is->abort_request;
 +}
 +
 +static int stream_has_enough_packets(AVStream *st, int stream_id, PacketQueue *queue) {
 +    return stream_id < 0 ||
 +           queue->abort_request ||
 +           (st->disposition & AV_DISPOSITION_ATTACHED_PIC) ||
 +           queue->nb_packets > MIN_FRAMES && (!queue->duration || av_q2d(st->time_base) * queue->duration > 1.0);
 +}
 +
 +static int is_realtime(AVFormatContext *s)
 +{
 +    if(   !strcmp(s->iformat->name, "rtp")
 +       || !strcmp(s->iformat->name, "rtsp")
 +       || !strcmp(s->iformat->name, "sdp")
 +    )
 +        return 1;
 +
 +    if(s->pb && (   !strncmp(s->filename, "rtp:", 4)
 +                 || !strncmp(s->filename, "udp:", 4)
 +                )
 +    )
 +        return 1;
 +    return 0;
 +}
 +
 +/* this thread gets the stream from the disk or the network */
 +static int read_thread(void *arg)
 +{
 +    VideoState *is = arg;
 +    AVFormatContext *ic = NULL;
 +    int err, i, ret;
 +    int st_index[AVMEDIA_TYPE_NB];
 +    AVPacket pkt1, *pkt = &pkt1;
 +    int64_t stream_start_time;
 +    int pkt_in_play_range = 0;
 +    AVDictionaryEntry *t;
 +    AVDictionary **opts;
 +    int orig_nb_streams;
 +    SDL_mutex *wait_mutex = SDL_CreateMutex();
 +    int scan_all_pmts_set = 0;
 +    int64_t pkt_ts;
 +
 +    if (!wait_mutex) {
 +        av_log(NULL, AV_LOG_FATAL, "SDL_CreateMutex(): %s\n", SDL_GetError());
 +        ret = AVERROR(ENOMEM);
 +        goto fail;
 +    }
 +
 +    memset(st_index, -1, sizeof(st_index));
 +    is->last_video_stream = is->video_stream = -1;
 +    is->last_audio_stream = is->audio_stream = -1;
 +    is->last_subtitle_stream = is->subtitle_stream = -1;
 +    is->eof = 0;
 +
 +    ic = avformat_alloc_context();
 +    if (!ic) {
 +        av_log(NULL, AV_LOG_FATAL, "Could not allocate context.\n");
 +        ret = AVERROR(ENOMEM);
 +        goto fail;
 +    }
 +    ic->interrupt_callback.callback = decode_interrupt_cb;
 +    ic->interrupt_callback.opaque = is;
 +    if (!av_dict_get(format_opts, "scan_all_pmts", NULL, AV_DICT_MATCH_CASE)) {
 +        av_dict_set(&format_opts, "scan_all_pmts", "1", AV_DICT_DONT_OVERWRITE);
 +        scan_all_pmts_set = 1;
 +    }
 +    err = avformat_open_input(&ic, is->filename, is->iformat, &format_opts);
 +    if (err < 0) {
 +        print_error(is->filename, err);
 +        ret = -1;
 +        goto fail;
 +    }
 +    if (scan_all_pmts_set)
 +        av_dict_set(&format_opts, "scan_all_pmts", NULL, AV_DICT_MATCH_CASE);
 +
 +    if ((t = av_dict_get(format_opts, "", NULL, AV_DICT_IGNORE_SUFFIX))) {
 +        av_log(NULL, AV_LOG_ERROR, "Option %s not found.\n", t->key);
 +        ret = AVERROR_OPTION_NOT_FOUND;
 +        goto fail;
 +    }
 +    is->ic = ic;
 +
 +    if (genpts)
 +        ic->flags |= AVFMT_FLAG_GENPTS;
 +
 +    av_format_inject_global_side_data(ic);
 +
 +    opts = setup_find_stream_info_opts(ic, codec_opts);
 +    orig_nb_streams = ic->nb_streams;
 +
 +    err = avformat_find_stream_info(ic, opts);
 +
 +    for (i = 0; i < orig_nb_streams; i++)
 +        av_dict_free(&opts[i]);
 +    av_freep(&opts);
 +
 +    if (err < 0) {
 +        av_log(NULL, AV_LOG_WARNING,
 +               "%s: could not find codec parameters\n", is->filename);
 +        ret = -1;
 +        goto fail;
 +    }
 +
 +    if (ic->pb)
 +        ic->pb->eof_reached = 0; // FIXME hack, ffplay maybe should not use avio_feof() to test for the end
 +
 +    if (seek_by_bytes < 0)
 +        seek_by_bytes = !!(ic->iformat->flags & AVFMT_TS_DISCONT) && strcmp("ogg", ic->iformat->name);
 +
 +    is->max_frame_duration = (ic->iformat->flags & AVFMT_TS_DISCONT) ? 10.0 : 3600.0;
 +
 +    if (!window_title && (t = av_dict_get(ic->metadata, "title", NULL, 0)))
 +        window_title = av_asprintf("%s - %s", t->value, input_filename);
 +
 +    /* if seeking requested, we execute it */
 +    if (start_time != AV_NOPTS_VALUE) {
 +        int64_t timestamp;
 +
 +        timestamp = start_time;
 +        /* add the stream start time */
 +        if (ic->start_time != AV_NOPTS_VALUE)
 +            timestamp += ic->start_time;
 +        ret = avformat_seek_file(ic, -1, INT64_MIN, timestamp, INT64_MAX, 0);
 +        if (ret < 0) {
 +            av_log(NULL, AV_LOG_WARNING, "%s: could not seek to position %0.3f\n",
 +                    is->filename, (double)timestamp / AV_TIME_BASE);
 +        }
 +    }
 +
 +    is->realtime = is_realtime(ic);
 +
 +    if (show_status)
 +        av_dump_format(ic, 0, is->filename, 0);
 +
 +    for (i = 0; i < ic->nb_streams; i++) {
 +        AVStream *st = ic->streams[i];
 +        enum AVMediaType type = st->codecpar->codec_type;
 +        st->discard = AVDISCARD_ALL;
 +        if (type >= 0 && wanted_stream_spec[type] && st_index[type] == -1)
 +            if (avformat_match_stream_specifier(ic, st, wanted_stream_spec[type]) > 0)
 +                st_index[type] = i;
 +    }
 +    for (i = 0; i < AVMEDIA_TYPE_NB; i++) {
 +        if (wanted_stream_spec[i] && st_index[i] == -1) {
 +            av_log(NULL, AV_LOG_ERROR, "Stream specifier %s does not match any %s stream\n", wanted_stream_spec[i], av_get_media_type_string(i));
 +            st_index[i] = INT_MAX;
 +        }
 +    }
 +
 +    if (!video_disable)
 +        st_index[AVMEDIA_TYPE_VIDEO] =
 +            av_find_best_stream(ic, AVMEDIA_TYPE_VIDEO,
 +                                st_index[AVMEDIA_TYPE_VIDEO], -1, NULL, 0);
 +    if (!audio_disable)
 +        st_index[AVMEDIA_TYPE_AUDIO] =
 +            av_find_best_stream(ic, AVMEDIA_TYPE_AUDIO,
 +                                st_index[AVMEDIA_TYPE_AUDIO],
 +                                st_index[AVMEDIA_TYPE_VIDEO],
 +                                NULL, 0);
 +    if (!video_disable && !subtitle_disable)
 +        st_index[AVMEDIA_TYPE_SUBTITLE] =
 +            av_find_best_stream(ic, AVMEDIA_TYPE_SUBTITLE,
 +                                st_index[AVMEDIA_TYPE_SUBTITLE],
 +                                (st_index[AVMEDIA_TYPE_AUDIO] >= 0 ?
 +                                 st_index[AVMEDIA_TYPE_AUDIO] :
 +                                 st_index[AVMEDIA_TYPE_VIDEO]),
 +                                NULL, 0);
 +
 +    is->show_mode = show_mode;
 +    if (st_index[AVMEDIA_TYPE_VIDEO] >= 0) {
 +        AVStream *st = ic->streams[st_index[AVMEDIA_TYPE_VIDEO]];
 +        AVCodecParameters *codecpar = st->codecpar;
 +        AVRational sar = av_guess_sample_aspect_ratio(ic, st, NULL);
 +        if (codecpar->width)
 +            set_default_window_size(codecpar->width, codecpar->height, sar);
 +    }
 +
 +    /* open the streams */
 +    if (st_index[AVMEDIA_TYPE_AUDIO] >= 0) {
 +        stream_component_open(is, st_index[AVMEDIA_TYPE_AUDIO]);
 +    }
 +
 +    ret = -1;
 +    if (st_index[AVMEDIA_TYPE_VIDEO] >= 0) {
 +        ret = stream_component_open(is, st_index[AVMEDIA_TYPE_VIDEO]);
 +    }
 +    if (is->show_mode == SHOW_MODE_NONE)
 +        is->show_mode = ret >= 0 ? SHOW_MODE_VIDEO : SHOW_MODE_RDFT;
 +
 +    if (st_index[AVMEDIA_TYPE_SUBTITLE] >= 0) {
 +        stream_component_open(is, st_index[AVMEDIA_TYPE_SUBTITLE]);
 +    }
 +
 +    if (is->video_stream < 0 && is->audio_stream < 0) {
 +        av_log(NULL, AV_LOG_FATAL, "Failed to open file '%s' or configure filtergraph\n",
 +               is->filename);
 +        ret = -1;
 +        goto fail;
 +    }
 +
 +    if (infinite_buffer < 0 && is->realtime)
 +        infinite_buffer = 1;
 +
 +    for (;;) {
 +        if (is->abort_request)
 +            break;
 +        if (is->paused != is->last_paused) {
 +            is->last_paused = is->paused;
 +            if (is->paused)
 +                is->read_pause_return = av_read_pause(ic);
 +            else
 +                av_read_play(ic);
 +        }
 +#if CONFIG_RTSP_DEMUXER || CONFIG_MMSH_PROTOCOL
 +        if (is->paused &&
 +                (!strcmp(ic->iformat->name, "rtsp") ||
 +                 (ic->pb && !strncmp(input_filename, "mmsh:", 5)))) {
 +            /* wait 10 ms to avoid trying to get another packet */
 +            /* XXX: horrible */
 +            SDL_Delay(10);
 +            continue;
 +        }
 +#endif
 +        if (is->seek_req) {
 +            int64_t seek_target = is->seek_pos;
 +            int64_t seek_min    = is->seek_rel > 0 ? seek_target - is->seek_rel + 2: INT64_MIN;
 +            int64_t seek_max    = is->seek_rel < 0 ? seek_target - is->seek_rel - 2: INT64_MAX;
 +// FIXME the +-2 is due to rounding being not done in the correct direction in generation
 +//      of the seek_pos/seek_rel variables
 +
 +            ret = avformat_seek_file(is->ic, -1, seek_min, seek_target, seek_max, is->seek_flags);
 +            if (ret < 0) {
 +                av_log(NULL, AV_LOG_ERROR,
 +                       "%s: error while seeking\n", is->ic->filename);
 +            } else {
 +                if (is->audio_stream >= 0) {
 +                    packet_queue_flush(&is->audioq);
 +                    packet_queue_put(&is->audioq, &flush_pkt);
 +                }
 +                if (is->subtitle_stream >= 0) {
 +                    packet_queue_flush(&is->subtitleq);
 +                    packet_queue_put(&is->subtitleq, &flush_pkt);
 +                }
 +                if (is->video_stream >= 0) {
 +                    packet_queue_flush(&is->videoq);
 +                    packet_queue_put(&is->videoq, &flush_pkt);
 +                }
 +                if (is->seek_flags & AVSEEK_FLAG_BYTE) {
 +                   set_clock(&is->extclk, NAN, 0);
 +                } else {
 +                   set_clock(&is->extclk, seek_target / (double)AV_TIME_BASE, 0);
 +                }
 +            }
 +            is->seek_req = 0;
 +            is->queue_attachments_req = 1;
 +            is->eof = 0;
 +            if (is->paused)
 +                step_to_next_frame(is);
 +        }
 +        if (is->queue_attachments_req) {
 +            if (is->video_st && is->video_st->disposition & AV_DISPOSITION_ATTACHED_PIC) {
 +                AVPacket copy;
 +                if ((ret = av_copy_packet(&copy, &is->video_st->attached_pic)) < 0)
 +                    goto fail;
 +                packet_queue_put(&is->videoq, &copy);
 +                packet_queue_put_nullpacket(&is->videoq, is->video_stream);
 +            }
 +            is->queue_attachments_req = 0;
 +        }
 +
 +        /* if the queue are full, no need to read more */
 +        if (infinite_buffer<1 &&
 +              (is->audioq.size + is->videoq.size + is->subtitleq.size > MAX_QUEUE_SIZE
 +            || (stream_has_enough_packets(is->audio_st, is->audio_stream, &is->audioq) &&
 +                stream_has_enough_packets(is->video_st, is->video_stream, &is->videoq) &&
 +                stream_has_enough_packets(is->subtitle_st, is->subtitle_stream, &is->subtitleq)))) {
 +            /* wait 10 ms */
 +            SDL_LockMutex(wait_mutex);
 +            SDL_CondWaitTimeout(is->continue_read_thread, wait_mutex, 10);
 +            SDL_UnlockMutex(wait_mutex);
 +            continue;
 +        }
 +        if (!is->paused &&
 +            (!is->audio_st || (is->auddec.finished == is->audioq.serial && frame_queue_nb_remaining(&is->sampq) == 0)) &&
 +            (!is->video_st || (is->viddec.finished == is->videoq.serial && frame_queue_nb_remaining(&is->pictq) == 0))) {
 +            if (loop != 1 && (!loop || --loop)) {
 +                stream_seek(is, start_time != AV_NOPTS_VALUE ? start_time : 0, 0, 0);
 +            } else if (autoexit) {
 +                ret = AVERROR_EOF;
 +                goto fail;
 +            }
 +        }
 +        ret = av_read_frame(ic, pkt);
 +        if (ret < 0) {
 +            if ((ret == AVERROR_EOF || avio_feof(ic->pb)) && !is->eof) {
 +                if (is->video_stream >= 0)
 +                    packet_queue_put_nullpacket(&is->videoq, is->video_stream);
 +                if (is->audio_stream >= 0)
 +                    packet_queue_put_nullpacket(&is->audioq, is->audio_stream);
 +                if (is->subtitle_stream >= 0)
 +                    packet_queue_put_nullpacket(&is->subtitleq, is->subtitle_stream);
 +                is->eof = 1;
 +            }
 +            if (ic->pb && ic->pb->error)
 +                break;
 +            SDL_LockMutex(wait_mutex);
 +            SDL_CondWaitTimeout(is->continue_read_thread, wait_mutex, 10);
 +            SDL_UnlockMutex(wait_mutex);
 +            continue;
 +        } else {
 +            is->eof = 0;
 +        }
 +        /* check if packet is in play range specified by user, then queue, otherwise discard */
 +        stream_start_time = ic->streams[pkt->stream_index]->start_time;
 +        pkt_ts = pkt->pts == AV_NOPTS_VALUE ? pkt->dts : pkt->pts;
 +        pkt_in_play_range = duration == AV_NOPTS_VALUE ||
 +                (pkt_ts - (stream_start_time != AV_NOPTS_VALUE ? stream_start_time : 0)) *
 +                av_q2d(ic->streams[pkt->stream_index]->time_base) -
 +                (double)(start_time != AV_NOPTS_VALUE ? start_time : 0) / 1000000
 +                <= ((double)duration / 1000000);
 +        if (pkt->stream_index == is->audio_stream && pkt_in_play_range) {
 +            packet_queue_put(&is->audioq, pkt);
 +        } else if (pkt->stream_index == is->video_stream && pkt_in_play_range
 +                   && !(is->video_st->disposition & AV_DISPOSITION_ATTACHED_PIC)) {
 +            packet_queue_put(&is->videoq, pkt);
 +        } else if (pkt->stream_index == is->subtitle_stream && pkt_in_play_range) {
 +            packet_queue_put(&is->subtitleq, pkt);
 +        } else {
 +            av_packet_unref(pkt);
 +        }
 +    }
 +
 +    ret = 0;
 + fail:
 +    if (ic && !is->ic)
 +        avformat_close_input(&ic);
 +
 +    if (ret != 0) {
 +        SDL_Event event;
 +
 +        event.type = FF_QUIT_EVENT;
 +        event.user.data1 = is;
 +        SDL_PushEvent(&event);
 +    }
 +    SDL_DestroyMutex(wait_mutex);
 +    return 0;
 +}
 +
 +static VideoState *stream_open(const char *filename, AVInputFormat *iformat)
 +{
 +    VideoState *is;
 +
 +    is = av_mallocz(sizeof(VideoState));
 +    if (!is)
 +        return NULL;
 +    is->filename = av_strdup(filename);
 +    if (!is->filename)
 +        goto fail;
 +    is->iformat = iformat;
 +    is->ytop    = 0;
 +    is->xleft   = 0;
 +
 +    /* start video display */
 +    if (frame_queue_init(&is->pictq, &is->videoq, VIDEO_PICTURE_QUEUE_SIZE, 1) < 0)
 +        goto fail;
 +    if (frame_queue_init(&is->subpq, &is->subtitleq, SUBPICTURE_QUEUE_SIZE, 0) < 0)
 +        goto fail;
 +    if (frame_queue_init(&is->sampq, &is->audioq, SAMPLE_QUEUE_SIZE, 1) < 0)
 +        goto fail;
 +
 +    if (packet_queue_init(&is->videoq) < 0 ||
 +        packet_queue_init(&is->audioq) < 0 ||
 +        packet_queue_init(&is->subtitleq) < 0)
 +        goto fail;
 +
 +    if (!(is->continue_read_thread = SDL_CreateCond())) {
 +        av_log(NULL, AV_LOG_FATAL, "SDL_CreateCond(): %s\n", SDL_GetError());
 +        goto fail;
 +    }
 +
 +    init_clock(&is->vidclk, &is->videoq.serial);
 +    init_clock(&is->audclk, &is->audioq.serial);
 +    init_clock(&is->extclk, &is->extclk.serial);
 +    is->audio_clock_serial = -1;
 +    if (startup_volume < 0)
 +        av_log(NULL, AV_LOG_WARNING, "-volume=%d < 0, setting to 0\n", startup_volume);
 +    if (startup_volume > 100)
 +        av_log(NULL, AV_LOG_WARNING, "-volume=%d > 100, setting to 100\n", startup_volume);
 +    startup_volume = av_clip(startup_volume, 0, 100);
 +    startup_volume = av_clip(SDL_MIX_MAXVOLUME * startup_volume / 100, 0, SDL_MIX_MAXVOLUME);
 +    is->audio_volume = startup_volume;
 +    is->muted = 0;
 +    is->av_sync_type = av_sync_type;
 +    is->read_tid     = SDL_CreateThread(read_thread, "read_thread", is);
 +    if (!is->read_tid) {
 +        av_log(NULL, AV_LOG_FATAL, "SDL_CreateThread(): %s\n", SDL_GetError());
 +fail:
 +        stream_close(is);
 +        return NULL;
 +    }
 +    return is;
 +}
 +
 +static void stream_cycle_channel(VideoState *is, int codec_type)
 +{
 +    AVFormatContext *ic = is->ic;
 +    int start_index, stream_index;
 +    int old_index;
 +    AVStream *st;
 +    AVProgram *p = NULL;
 +    int nb_streams = is->ic->nb_streams;
 +
 +    if (codec_type == AVMEDIA_TYPE_VIDEO) {
 +        start_index = is->last_video_stream;
 +        old_index = is->video_stream;
 +    } else if (codec_type == AVMEDIA_TYPE_AUDIO) {
 +        start_index = is->last_audio_stream;
 +        old_index = is->audio_stream;
 +    } else {
 +        start_index = is->last_subtitle_stream;
 +        old_index = is->subtitle_stream;
 +    }
 +    stream_index = start_index;
 +
 +    if (codec_type != AVMEDIA_TYPE_VIDEO && is->video_stream != -1) {
 +        p = av_find_program_from_stream(ic, NULL, is->video_stream);
 +        if (p) {
 +            nb_streams = p->nb_stream_indexes;
 +            for (start_index = 0; start_index < nb_streams; start_index++)
 +                if (p->stream_index[start_index] == stream_index)
 +                    break;
 +            if (start_index == nb_streams)
 +                start_index = -1;
 +            stream_index = start_index;
 +        }
 +    }
 +
 +    for (;;) {
 +        if (++stream_index >= nb_streams)
 +        {
 +            if (codec_type == AVMEDIA_TYPE_SUBTITLE)
 +            {
 +                stream_index = -1;
 +                is->last_subtitle_stream = -1;
 +                goto the_end;
 +            }
 +            if (start_index == -1)
 +                return;
 +            stream_index = 0;
 +        }
 +        if (stream_index == start_index)
 +            return;
 +        st = is->ic->streams[p ? p->stream_index[stream_index] : stream_index];
 +        if (st->codecpar->codec_type == codec_type) {
 +            /* check that parameters are OK */
 +            switch (codec_type) {
 +            case AVMEDIA_TYPE_AUDIO:
 +                if (st->codecpar->sample_rate != 0 &&
 +                    st->codecpar->channels != 0)
 +                    goto the_end;
 +                break;
 +            case AVMEDIA_TYPE_VIDEO:
 +            case AVMEDIA_TYPE_SUBTITLE:
 +                goto the_end;
 +            default:
 +                break;
 +            }
 +        }
 +    }
 + the_end:
 +    if (p && stream_index != -1)
 +        stream_index = p->stream_index[stream_index];
 +    av_log(NULL, AV_LOG_INFO, "Switch %s stream from #%d to #%d\n",
 +           av_get_media_type_string(codec_type),
 +           old_index,
 +           stream_index);
 +
 +    stream_component_close(is, old_index);
 +    stream_component_open(is, stream_index);
 +}
 +
 +
 +static void toggle_full_screen(VideoState *is)
 +{
 +    is_full_screen = !is_full_screen;
 +    SDL_SetWindowFullscreen(window, is_full_screen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
 +}
 +
 +static void toggle_audio_display(VideoState *is)
 +{
 +    int next = is->show_mode;
 +    do {
 +        next = (next + 1) % SHOW_MODE_NB;
 +    } while (next != is->show_mode && (next == SHOW_MODE_VIDEO && !is->video_st || next != SHOW_MODE_VIDEO && !is->audio_st));
 +    if (is->show_mode != next) {
 +        is->force_refresh = 1;
 +        is->show_mode = next;
 +    }
 +}
 +
 +static void refresh_loop_wait_event(VideoState *is, SDL_Event *event) {
 +    double remaining_time = 0.0;
 +    SDL_PumpEvents();
 +    while (!SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT)) {
 +        if (!cursor_hidden && av_gettime_relative() - cursor_last_shown > CURSOR_HIDE_DELAY) {
 +            SDL_ShowCursor(0);
 +            cursor_hidden = 1;
 +        }
 +        if (remaining_time > 0.0)
 +            av_usleep((int64_t)(remaining_time * 1000000.0));
 +        remaining_time = REFRESH_RATE;
 +        if (is->show_mode != SHOW_MODE_NONE && (!is->paused || is->force_refresh))
 +            video_refresh(is, &remaining_time);
 +        SDL_PumpEvents();
 +    }
 +}
 +
 +static void seek_chapter(VideoState *is, int incr)
 +{
 +    int64_t pos = get_master_clock(is) * AV_TIME_BASE;
 +    int i;
 +
 +    if (!is->ic->nb_chapters)
 +        return;
 +
 +    /* find the current chapter */
 +    for (i = 0; i < is->ic->nb_chapters; i++) {
 +        AVChapter *ch = is->ic->chapters[i];
 +        if (av_compare_ts(pos, AV_TIME_BASE_Q, ch->start, ch->time_base) < 0) {
 +            i--;
 +            break;
 +        }
 +    }
 +
 +    i += incr;
 +    i = FFMAX(i, 0);
 +    if (i >= is->ic->nb_chapters)
 +        return;
 +
 +    av_log(NULL, AV_LOG_VERBOSE, "Seeking to chapter %d.\n", i);
 +    stream_seek(is, av_rescale_q(is->ic->chapters[i]->start, is->ic->chapters[i]->time_base,
 +                                 AV_TIME_BASE_Q), 0, 0);
 +}
 +
 +/* handle an event sent by the GUI */
 +static void event_loop(VideoState *cur_stream)
 +{
 +    SDL_Event event;
 +    double incr, pos, frac;
 +
 +    for (;;) {
 +        double x;
 +        refresh_loop_wait_event(cur_stream, &event);
 +        switch (event.type) {
 +        case SDL_KEYDOWN:
 +            if (exit_on_keydown) {
 +                do_exit(cur_stream);
 +                break;
 +            }
 +            switch (event.key.keysym.sym) {
 +            case SDLK_ESCAPE:
 +            case SDLK_q:
 +                do_exit(cur_stream);
 +                break;
 +            case SDLK_f:
 +                toggle_full_screen(cur_stream);
 +                cur_stream->force_refresh = 1;
 +                break;
 +            case SDLK_p:
 +            case SDLK_SPACE:
 +                toggle_pause(cur_stream);
 +                break;
 +            case SDLK_m:
 +                toggle_mute(cur_stream);
 +                break;
 +            case SDLK_KP_MULTIPLY:
 +            case SDLK_0:
 +                update_volume(cur_stream, 1, SDL_VOLUME_STEP);
 +                break;
 +            case SDLK_KP_DIVIDE:
 +            case SDLK_9:
 +                update_volume(cur_stream, -1, SDL_VOLUME_STEP);
 +                break;
 +            case SDLK_s: // S: Step to next frame
 +                step_to_next_frame(cur_stream);
 +                break;
 +            case SDLK_a:
 +                stream_cycle_channel(cur_stream, AVMEDIA_TYPE_AUDIO);
 +                break;
 +            case SDLK_v:
 +                stream_cycle_channel(cur_stream, AVMEDIA_TYPE_VIDEO);
 +                break;
 +            case SDLK_c:
 +                stream_cycle_channel(cur_stream, AVMEDIA_TYPE_VIDEO);
 +                stream_cycle_channel(cur_stream, AVMEDIA_TYPE_AUDIO);
 +                stream_cycle_channel(cur_stream, AVMEDIA_TYPE_SUBTITLE);
 +                break;
 +            case SDLK_t:
 +                stream_cycle_channel(cur_stream, AVMEDIA_TYPE_SUBTITLE);
 +                break;
 +            case SDLK_w:
 +#if CONFIG_AVFILTER
 +                if (cur_stream->show_mode == SHOW_MODE_VIDEO && cur_stream->vfilter_idx < nb_vfilters - 1) {
 +                    if (++cur_stream->vfilter_idx >= nb_vfilters)
 +                        cur_stream->vfilter_idx = 0;
 +                } else {
 +                    cur_stream->vfilter_idx = 0;
 +                    toggle_audio_display(cur_stream);
 +                }
 +#else
 +                toggle_audio_display(cur_stream);
 +#endif
 +                break;
 +            case SDLK_PAGEUP:
 +                if (cur_stream->ic->nb_chapters <= 1) {
 +                    incr = 600.0;
 +                    goto do_seek;
 +                }
 +                seek_chapter(cur_stream, 1);
 +                break;
 +            case SDLK_PAGEDOWN:
 +                if (cur_stream->ic->nb_chapters <= 1) {
 +                    incr = -600.0;
 +                    goto do_seek;
 +                }
 +                seek_chapter(cur_stream, -1);
 +                break;
 +            case SDLK_LEFT:
 +                incr = -10.0;
 +                goto do_seek;
 +            case SDLK_RIGHT:
 +                incr = 10.0;
 +                goto do_seek;
 +            case SDLK_UP:
 +                incr = 60.0;
 +                goto do_seek;
 +            case SDLK_DOWN:
 +                incr = -60.0;
 +            do_seek:
 +                    if (seek_by_bytes) {
 +                        pos = -1;
 +                        if (pos < 0 && cur_stream->video_stream >= 0)
 +                            pos = frame_queue_last_pos(&cur_stream->pictq);
 +                        if (pos < 0 && cur_stream->audio_stream >= 0)
 +                            pos = frame_queue_last_pos(&cur_stream->sampq);
 +                        if (pos < 0)
 +                            pos = avio_tell(cur_stream->ic->pb);
 +                        if (cur_stream->ic->bit_rate)
 +                            incr *= cur_stream->ic->bit_rate / 8.0;
 +                        else
 +                            incr *= 180000.0;
 +                        pos += incr;
 +                        stream_seek(cur_stream, pos, incr, 1);
 +                    } else {
 +                        pos = get_master_clock(cur_stream);
 +                        if (isnan(pos))
 +                            pos = (double)cur_stream->seek_pos / AV_TIME_BASE;
 +                        pos += incr;
 +                        if (cur_stream->ic->start_time != AV_NOPTS_VALUE && pos < cur_stream->ic->start_time / (double)AV_TIME_BASE)
 +                            pos = cur_stream->ic->start_time / (double)AV_TIME_BASE;
 +                        stream_seek(cur_stream, (int64_t)(pos * AV_TIME_BASE), (int64_t)(incr * AV_TIME_BASE), 0);
 +                    }
 +                break;
 +            default:
 +                break;
 +            }
 +            break;
 +        case SDL_MOUSEBUTTONDOWN:
 +            if (exit_on_mousedown) {
 +                do_exit(cur_stream);
 +                break;
 +            }
 +            if (event.button.button == SDL_BUTTON_LEFT) {
 +                static int64_t last_mouse_left_click = 0;
 +                if (av_gettime_relative() - last_mouse_left_click <= 500000) {
 +                    toggle_full_screen(cur_stream);
 +                    cur_stream->force_refresh = 1;
 +                    last_mouse_left_click = 0;
 +                } else {
 +                    last_mouse_left_click = av_gettime_relative();
 +                }
 +            }
 +        case SDL_MOUSEMOTION:
 +            if (cursor_hidden) {
 +                SDL_ShowCursor(1);
 +                cursor_hidden = 0;
 +            }
 +            cursor_last_shown = av_gettime_relative();
 +            if (event.type == SDL_MOUSEBUTTONDOWN) {
 +                if (event.button.button != SDL_BUTTON_RIGHT)
 +                    break;
 +                x = event.button.x;
 +            } else {
 +                if (!(event.motion.state & SDL_BUTTON_RMASK))
 +                    break;
 +                x = event.motion.x;
 +            }
 +                if (seek_by_bytes || cur_stream->ic->duration <= 0) {
 +                    uint64_t size =  avio_size(cur_stream->ic->pb);
 +                    stream_seek(cur_stream, size*x/cur_stream->width, 0, 1);
 +                } else {
 +                    int64_t ts;
 +                    int ns, hh, mm, ss;
 +                    int tns, thh, tmm, tss;
 +                    tns  = cur_stream->ic->duration / 1000000LL;
 +                    thh  = tns / 3600;
 +                    tmm  = (tns % 3600) / 60;
 +                    tss  = (tns % 60);
 +                    frac = x / cur_stream->width;
 +                    ns   = frac * tns;
 +                    hh   = ns / 3600;
 +                    mm   = (ns % 3600) / 60;
 +                    ss   = (ns % 60);
 +                    av_log(NULL, AV_LOG_INFO,
 +                           "Seek to %2.0f%% (%2d:%02d:%02d) of total duration (%2d:%02d:%02d)       \n", frac*100,
 +                            hh, mm, ss, thh, tmm, tss);
 +                    ts = frac * cur_stream->ic->duration;
 +                    if (cur_stream->ic->start_time != AV_NOPTS_VALUE)
 +                        ts += cur_stream->ic->start_time;
 +                    stream_seek(cur_stream, ts, 0, 0);
 +                }
 +            break;
 +        case SDL_WINDOWEVENT:
 +            switch (event.window.event) {
 +                case SDL_WINDOWEVENT_RESIZED:
 +                    screen_width  = cur_stream->width  = event.window.data1;
 +                    screen_height = cur_stream->height = event.window.data2;
 +                    if (cur_stream->vis_texture) {
 +                        SDL_DestroyTexture(cur_stream->vis_texture);
 +                        cur_stream->vis_texture = NULL;
 +                    }
 +                case SDL_WINDOWEVENT_EXPOSED:
 +                    cur_stream->force_refresh = 1;
 +            }
 +            break;
 +        case SDL_QUIT:
 +        case FF_QUIT_EVENT:
 +            do_exit(cur_stream);
 +            break;
 +        default:
 +            break;
 +        }
 +    }
 +}
 +
 +static int opt_frame_size(void *optctx, const char *opt, const char *arg)
 +{
 +    av_log(NULL, AV_LOG_WARNING, "Option -s is deprecated, use -video_size.\n");
 +    return opt_default(NULL, "video_size", arg);
 +}
 +
 +static int opt_width(void *optctx, const char *opt, const char *arg)
 +{
 +    screen_width = parse_number_or_die(opt, arg, OPT_INT64, 1, INT_MAX);
 +    return 0;
 +}
 +
 +static int opt_height(void *optctx, const char *opt, const char *arg)
 +{
 +    screen_height = parse_number_or_die(opt, arg, OPT_INT64, 1, INT_MAX);
 +    return 0;
 +}
 +
 +static int opt_format(void *optctx, const char *opt, const char *arg)
 +{
 +    file_iformat = av_find_input_format(arg);
 +    if (!file_iformat) {
 +        av_log(NULL, AV_LOG_FATAL, "Unknown input format: %s\n", arg);
 +        return AVERROR(EINVAL);
 +    }
 +    return 0;
 +}
 +
 +static int opt_frame_pix_fmt(void *optctx, const char *opt, const char *arg)
 +{
 +    av_log(NULL, AV_LOG_WARNING, "Option -pix_fmt is deprecated, use -pixel_format.\n");
 +    return opt_default(NULL, "pixel_format", arg);
 +}
 +
 +static int opt_sync(void *optctx, const char *opt, const char *arg)
 +{
 +    if (!strcmp(arg, "audio"))
 +        av_sync_type = AV_SYNC_AUDIO_MASTER;
 +    else if (!strcmp(arg, "video"))
 +        av_sync_type = AV_SYNC_VIDEO_MASTER;
 +    else if (!strcmp(arg, "ext"))
 +        av_sync_type = AV_SYNC_EXTERNAL_CLOCK;
 +    else {
 +        av_log(NULL, AV_LOG_ERROR, "Unknown value for %s: %s\n", opt, arg);
 +        exit(1);
 +    }
 +    return 0;
 +}
 +
 +static int opt_seek(void *optctx, const char *opt, const char *arg)
 +{
 +    start_time = parse_time_or_die(opt, arg, 1);
 +    return 0;
 +}
 +
 +static int opt_duration(void *optctx, const char *opt, const char *arg)
 +{
 +    duration = parse_time_or_die(opt, arg, 1);
 +    return 0;
 +}
 +
 +static int opt_show_mode(void *optctx, const char *opt, const char *arg)
 +{
 +    show_mode = !strcmp(arg, "video") ? SHOW_MODE_VIDEO :
 +                !strcmp(arg, "waves") ? SHOW_MODE_WAVES :
 +                !strcmp(arg, "rdft" ) ? SHOW_MODE_RDFT  :
 +                parse_number_or_die(opt, arg, OPT_INT, 0, SHOW_MODE_NB-1);
 +    return 0;
 +}
 +
 +static void opt_input_file(void *optctx, const char *filename)
 +{
 +    if (input_filename) {
 +        av_log(NULL, AV_LOG_FATAL,
 +               "Argument '%s' provided as input filename, but '%s' was already specified.\n",
 +                filename, input_filename);
 +        exit(1);
 +    }
 +    if (!strcmp(filename, "-"))
 +        filename = "pipe:";
 +    input_filename = filename;
 +}
 +
 +static int opt_codec(void *optctx, const char *opt, const char *arg)
 +{
 +   const char *spec = strchr(opt, ':');
 +   if (!spec) {
 +       av_log(NULL, AV_LOG_ERROR,
 +              "No media specifier was specified in '%s' in option '%s'\n",
 +               arg, opt);
 +       return AVERROR(EINVAL);
 +   }
 +   spec++;
 +   switch (spec[0]) {
 +   case 'a' :    audio_codec_name = arg; break;
 +   case 's' : subtitle_codec_name = arg; break;
 +   case 'v' :    video_codec_name = arg; break;
 +   default:
 +       av_log(NULL, AV_LOG_ERROR,
 +              "Invalid media specifier '%s' in option '%s'\n", spec, opt);
 +       return AVERROR(EINVAL);
 +   }
 +   return 0;
 +}
 +
 +static int dummy;
 +
 +static const OptionDef options[] = {
++    CMDUTILS_COMMON_OPTIONS
 +    { "x", HAS_ARG, { .func_arg = opt_width }, "force displayed width", "width" },
 +    { "y", HAS_ARG, { .func_arg = opt_height }, "force displayed height", "height" },
 +    { "s", HAS_ARG | OPT_VIDEO, { .func_arg = opt_frame_size }, "set frame size (WxH or abbreviation)", "size" },
 +    { "fs", OPT_BOOL, { &is_full_screen }, "force full screen" },
 +    { "an", OPT_BOOL, { &audio_disable }, "disable audio" },
 +    { "vn", OPT_BOOL, { &video_disable }, "disable video" },
 +    { "sn", OPT_BOOL, { &subtitle_disable }, "disable subtitling" },
 +    { "ast", OPT_STRING | HAS_ARG | OPT_EXPERT, { &wanted_stream_spec[AVMEDIA_TYPE_AUDIO] }, "select desired audio stream", "stream_specifier" },
 +    { "vst", OPT_STRING | HAS_ARG | OPT_EXPERT, { &wanted_stream_spec[AVMEDIA_TYPE_VIDEO] }, "select desired video stream", "stream_specifier" },
 +    { "sst", OPT_STRING | HAS_ARG | OPT_EXPERT, { &wanted_stream_spec[AVMEDIA_TYPE_SUBTITLE] }, "select desired subtitle stream", "stream_specifier" },
 +    { "ss", HAS_ARG, { .func_arg = opt_seek }, "seek to a given position in seconds", "pos" },
 +    { "t", HAS_ARG, { .func_arg = opt_duration }, "play  \"duration\" seconds of audio/video", "duration" },
 +    { "bytes", OPT_INT | HAS_ARG, { &seek_by_bytes }, "seek by bytes 0=off 1=on -1=auto", "val" },
 +    { "nodisp", OPT_BOOL, { &display_disable }, "disable graphical display" },
 +    { "noborder", OPT_BOOL, { &borderless }, "borderless window" },
 +    { "volume", OPT_INT | HAS_ARG, { &startup_volume}, "set startup volume 0=min 100=max", "volume" },
 +    { "f", HAS_ARG, { .func_arg = opt_format }, "force format", "fmt" },
 +    { "pix_fmt", HAS_ARG | OPT_EXPERT | OPT_VIDEO, { .func_arg = opt_frame_pix_fmt }, "set pixel format", "format" },
 +    { "stats", OPT_BOOL | OPT_EXPERT, { &show_status }, "show status", "" },
 +    { "fast", OPT_BOOL | OPT_EXPERT, { &fast }, "non spec compliant optimizations", "" },
 +    { "genpts", OPT_BOOL | OPT_EXPERT, { &genpts }, "generate pts", "" },
 +    { "drp", OPT_INT | HAS_ARG | OPT_EXPERT, { &decoder_reorder_pts }, "let decoder reorder pts 0=off 1=on -1=auto", ""},
 +    { "lowres", OPT_INT | HAS_ARG | OPT_EXPERT, { &lowres }, "", "" },
 +    { "sync", HAS_ARG | OPT_EXPERT, { .func_arg = opt_sync }, "set audio-video sync. type (type=audio/video/ext)", "type" },
 +    { "autoexit", OPT_BOOL | OPT_EXPERT, { &autoexit }, "exit at the end", "" },
 +    { "exitonkeydown", OPT_BOOL | OPT_EXPERT, { &exit_on_keydown }, "exit on key down", "" },
 +    { "exitonmousedown", OPT_BOOL | OPT_EXPERT, { &exit_on_mousedown }, "exit on mouse down", "" },
 +    { "loop", OPT_INT | HAS_ARG | OPT_EXPERT, { &loop }, "set number of times the playback shall be looped", "loop count" },
 +    { "framedrop", OPT_BOOL | OPT_EXPERT, { &framedrop }, "drop frames when cpu is too slow", "" },
 +    { "infbuf", OPT_BOOL | OPT_EXPERT, { &infinite_buffer }, "don't limit the input buffer size (useful with realtime streams)", "" },
 +    { "window_title", OPT_STRING | HAS_ARG, { &window_title }, "set window title", "window title" },
 +#if CONFIG_AVFILTER
 +    { "vf", OPT_EXPERT | HAS_ARG, { .func_arg = opt_add_vfilter }, "set video filters", "filter_graph" },
 +    { "af", OPT_STRING | HAS_ARG, { &afilters }, "set audio filters", "filter_graph" },
 +#endif
 +    { "rdftspeed", OPT_INT | HAS_ARG| OPT_AUDIO | OPT_EXPERT, { &rdftspeed }, "rdft speed", "msecs" },
 +    { "showmode", HAS_ARG, { .func_arg = opt_show_mode}, "select show mode (0 = video, 1 = waves, 2 = RDFT)", "mode" },
 +    { "default", HAS_ARG | OPT_AUDIO | OPT_VIDEO | OPT_EXPERT, { .func_arg = opt_default }, "generic catch all option", "" },
 +    { "i", OPT_BOOL, { &dummy}, "read specified file", "input_file"},
 +    { "codec", HAS_ARG, { .func_arg = opt_codec}, "force decoder", "decoder_name" },
 +    { "acodec", HAS_ARG | OPT_STRING | OPT_EXPERT, {    &audio_codec_name }, "force audio decoder",    "decoder_name" },
 +    { "scodec", HAS_ARG | OPT_STRING | OPT_EXPERT, { &subtitle_codec_name }, "force subtitle decoder", "decoder_name" },
 +    { "vcodec", HAS_ARG | OPT_STRING | OPT_EXPERT, {    &video_codec_name }, "force video decoder",    "decoder_name" },
 +    { "autorotate", OPT_BOOL, { &autorotate }, "automatically rotate video", "" },
 +    { NULL, },
 +};
 +
 +static void show_usage(void)
 +{
 +    av_log(NULL, AV_LOG_INFO, "Simple media player\n");
 +    av_log(NULL, AV_LOG_INFO, "usage: %s [options] input_file\n", program_name);
 +    av_log(NULL, AV_LOG_INFO, "\n");
 +}
 +
 +void show_help_default(const char *opt, const char *arg)
 +{
 +    av_log_set_callback(log_callback_help);
 +    show_usage();
 +    show_help_options(options, "Main options:", 0, OPT_EXPERT, 0);
 +    show_help_options(options, "Advanced options:", OPT_EXPERT, 0, 0);
 +    printf("\n");
 +    show_help_children(avcodec_get_class(), AV_OPT_FLAG_DECODING_PARAM);
 +    show_help_children(avformat_get_class(), AV_OPT_FLAG_DECODING_PARAM);
 +#if !CONFIG_AVFILTER
 +    show_help_children(sws_get_class(), AV_OPT_FLAG_ENCODING_PARAM);
 +#else
 +    show_help_children(avfilter_get_class(), AV_OPT_FLAG_FILTERING_PARAM);
 +#endif
 +    printf("\nWhile playing:\n"
 +           "q, ESC              quit\n"
 +           "f                   toggle full screen\n"
 +           "p, SPC              pause\n"
 +           "m                   toggle mute\n"
 +           "9, 0                decrease and increase volume respectively\n"
 +           "/, *                decrease and increase volume respectively\n"
 +           "a                   cycle audio channel in the current program\n"
 +           "v                   cycle video channel\n"
 +           "t                   cycle subtitle channel in the current program\n"
 +           "c                   cycle program\n"
 +           "w                   cycle video filters or show modes\n"
 +           "s                   activate frame-step mode\n"
 +           "left/right          seek backward/forward 10 seconds\n"
 +           "down/up             seek backward/forward 1 minute\n"
 +           "page down/page up   seek backward/forward 10 minutes\n"
 +           "right mouse click   seek to percentage in file corresponding to fraction of width\n"
 +           "left double-click   toggle full screen\n"
 +           );
 +}
 +
 +static int lockmgr(void **mtx, enum AVLockOp op)
 +{
 +   switch(op) {
 +      case AV_LOCK_CREATE:
 +          *mtx = SDL_CreateMutex();
 +          if(!*mtx) {
 +              av_log(NULL, AV_LOG_FATAL, "SDL_CreateMutex(): %s\n", SDL_GetError());
 +              return 1;
 +          }
 +          return 0;
 +      case AV_LOCK_OBTAIN:
 +          return !!SDL_LockMutex(*mtx);
 +      case AV_LOCK_RELEASE:
 +          return !!SDL_UnlockMutex(*mtx);
 +      case AV_LOCK_DESTROY:
 +          SDL_DestroyMutex(*mtx);
 +          return 0;
 +   }
 +   return 1;
 +}
 +
 +/* Called from the main */
 +int main(int argc, char **argv)
 +{
 +    int flags;
 +    VideoState *is;
 +
 +    init_dynload();
 +
 +    av_log_set_flags(AV_LOG_SKIP_REPEATED);
 +    parse_loglevel(argc, argv, options);
 +
 +    /* register all codecs, demux and protocols */
 +#if CONFIG_AVDEVICE
 +    avdevice_register_all();
 +#endif
 +#if CONFIG_AVFILTER
 +    avfilter_register_all();
 +#endif
 +    av_register_all();
 +    avformat_network_init();
 +
 +    init_opts();
 +
 +    signal(SIGINT , sigterm_handler); /* Interrupt (ANSI).    */
 +    signal(SIGTERM, sigterm_handler); /* Termination (ANSI).  */
 +
 +    show_banner(argc, argv, options);
 +
 +    parse_options(NULL, argc, argv, options, opt_input_file);
 +
 +    if (!input_filename) {
 +        show_usage();
 +        av_log(NULL, AV_LOG_FATAL, "An input file must be specified\n");
 +        av_log(NULL, AV_LOG_FATAL,
 +               "Use -h to get full help or, even better, run 'man %s'\n", program_name);
 +        exit(1);
 +    }
 +
 +    if (display_disable) {
 +        video_disable = 1;
 +    }
 +    flags = SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER;
 +    if (audio_disable)
 +        flags &= ~SDL_INIT_AUDIO;
 +    else {
 +        /* Try to work around an occasional ALSA buffer underflow issue when the
 +         * period size is NPOT due to ALSA resampling by forcing the buffer size. */
 +        if (!SDL_getenv("SDL_AUDIO_ALSA_SET_BUFFER_SIZE"))
 +            SDL_setenv("SDL_AUDIO_ALSA_SET_BUFFER_SIZE","1", 1);
 +    }
 +    if (display_disable)
 +        flags &= ~SDL_INIT_VIDEO;
 +    if (SDL_Init (flags)) {
 +        av_log(NULL, AV_LOG_FATAL, "Could not initialize SDL - %s\n", SDL_GetError());
 +        av_log(NULL, AV_LOG_FATAL, "(Did you set the DISPLAY variable?)\n");
 +        exit(1);
 +    }
 +
 +    SDL_EventState(SDL_SYSWMEVENT, SDL_IGNORE);
 +    SDL_EventState(SDL_USEREVENT, SDL_IGNORE);
 +
 +    if (av_lockmgr_register(lockmgr)) {
 +        av_log(NULL, AV_LOG_FATAL, "Could not initialize lock manager!\n");
 +        do_exit(NULL);
 +    }
 +
 +    av_init_packet(&flush_pkt);
 +    flush_pkt.data = (uint8_t *)&flush_pkt;
 +
 +    is = stream_open(input_filename, file_iformat);
 +    if (!is) {
 +        av_log(NULL, AV_LOG_FATAL, "Failed to initialize VideoState!\n");
 +        do_exit(NULL);
 +    }
 +
 +    event_loop(is);
 +
 +    /* never returns */
 +
 +    return 0;
 +}
diff --cc ffprobe.c
index ce964e0,0000000..dd4358d
mode 100644,000000..100644
--- /dev/null
+++ b/ffprobe.c
@@@ -1,3593 -1,0 +1,3593 @@@
- #include "cmdutils_common_opts.h"
 +/*
 + * Copyright (c) 2007-2010 Stefano Sabatini
 + *
 + * This file is part of FFmpeg.
 + *
 + * FFmpeg is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public
 + * License as published by the Free Software Foundation; either
 + * version 2.1 of the License, or (at your option) any later version.
 + *
 + * FFmpeg is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with FFmpeg; if not, write to the Free Software
 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 + */
 +
 +/**
 + * @file
 + * simple media prober based on the FFmpeg libraries
 + */
 +
 +#include "config.h"
 +#include "libavutil/ffversion.h"
 +
 +#include <string.h>
 +
 +#include "libavformat/avformat.h"
 +#include "libavcodec/avcodec.h"
 +#include "libavutil/avassert.h"
 +#include "libavutil/avstring.h"
 +#include "libavutil/bprint.h"
 +#include "libavutil/display.h"
 +#include "libavutil/hash.h"
 +#include "libavutil/opt.h"
 +#include "libavutil/pixdesc.h"
 +#include "libavutil/spherical.h"
 +#include "libavutil/stereo3d.h"
 +#include "libavutil/dict.h"
 +#include "libavutil/intreadwrite.h"
 +#include "libavutil/libm.h"
 +#include "libavutil/parseutils.h"
 +#include "libavutil/timecode.h"
 +#include "libavutil/timestamp.h"
 +#include "libavdevice/avdevice.h"
 +#include "libswscale/swscale.h"
 +#include "libswresample/swresample.h"
 +#include "libpostproc/postprocess.h"
 +#include "cmdutils.h"
 +
 +#include "libavutil/thread.h"
 +
 +#if !HAVE_THREADS
 +#  ifdef pthread_mutex_lock
 +#    undef pthread_mutex_lock
 +#  endif
 +#  define pthread_mutex_lock(a)
 +#  ifdef pthread_mutex_unlock
 +#    undef pthread_mutex_unlock
 +#  endif
 +#  define pthread_mutex_unlock(a)
 +#endif
 +
 +typedef struct InputStream {
 +    AVStream *st;
 +
 +    AVCodecContext *dec_ctx;
 +} InputStream;
 +
 +typedef struct InputFile {
 +    AVFormatContext *fmt_ctx;
 +
 +    InputStream *streams;
 +    int       nb_streams;
 +} InputFile;
 +
 +const char program_name[] = "ffprobe";
 +const int program_birth_year = 2007;
 +
 +static int do_bitexact = 0;
 +static int do_count_frames = 0;
 +static int do_count_packets = 0;
 +static int do_read_frames  = 0;
 +static int do_read_packets = 0;
 +static int do_show_chapters = 0;
 +static int do_show_error   = 0;
 +static int do_show_format  = 0;
 +static int do_show_frames  = 0;
 +static int do_show_packets = 0;
 +static int do_show_programs = 0;
 +static int do_show_streams = 0;
 +static int do_show_stream_disposition = 0;
 +static int do_show_data    = 0;
 +static int do_show_program_version  = 0;
 +static int do_show_library_versions = 0;
 +static int do_show_pixel_formats = 0;
 +static int do_show_pixel_format_flags = 0;
 +static int do_show_pixel_format_components = 0;
 +static int do_show_log = 0;
 +
 +static int do_show_chapter_tags = 0;
 +static int do_show_format_tags = 0;
 +static int do_show_frame_tags = 0;
 +static int do_show_program_tags = 0;
 +static int do_show_stream_tags = 0;
 +static int do_show_packet_tags = 0;
 +
 +static int show_value_unit              = 0;
 +static int use_value_prefix             = 0;
 +static int use_byte_value_binary_prefix = 0;
 +static int use_value_sexagesimal_format = 0;
 +static int show_private_data            = 1;
 +
 +static char *print_format;
 +static char *stream_specifier;
 +static char *show_data_hash;
 +
 +typedef struct ReadInterval {
 +    int id;             ///< identifier
 +    int64_t start, end; ///< start, end in second/AV_TIME_BASE units
 +    int has_start, has_end;
 +    int start_is_offset, end_is_offset;
 +    int duration_frames;
 +} ReadInterval;
 +
 +static ReadInterval *read_intervals;
 +static int read_intervals_nb = 0;
 +
 +/* section structure definition */
 +
 +#define SECTION_MAX_NB_CHILDREN 10
 +
 +struct section {
 +    int id;             ///< unique id identifying a section
 +    const char *name;
 +
 +#define SECTION_FLAG_IS_WRAPPER      1 ///< the section only contains other sections, but has no data at its own level
 +#define SECTION_FLAG_IS_ARRAY        2 ///< the section contains an array of elements of the same type
 +#define SECTION_FLAG_HAS_VARIABLE_FIELDS 4 ///< the section may contain a variable number of fields with variable keys.
 +                                           ///  For these sections the element_name field is mandatory.
 +    int flags;
 +    int children_ids[SECTION_MAX_NB_CHILDREN+1]; ///< list of children section IDS, terminated by -1
 +    const char *element_name; ///< name of the contained element, if provided
 +    const char *unique_name;  ///< unique section name, in case the name is ambiguous
 +    AVDictionary *entries_to_show;
 +    int show_all_entries;
 +};
 +
 +typedef enum {
 +    SECTION_ID_NONE = -1,
 +    SECTION_ID_CHAPTER,
 +    SECTION_ID_CHAPTER_TAGS,
 +    SECTION_ID_CHAPTERS,
 +    SECTION_ID_ERROR,
 +    SECTION_ID_FORMAT,
 +    SECTION_ID_FORMAT_TAGS,
 +    SECTION_ID_FRAME,
 +    SECTION_ID_FRAMES,
 +    SECTION_ID_FRAME_TAGS,
 +    SECTION_ID_FRAME_SIDE_DATA_LIST,
 +    SECTION_ID_FRAME_SIDE_DATA,
 +    SECTION_ID_FRAME_LOG,
 +    SECTION_ID_FRAME_LOGS,
 +    SECTION_ID_LIBRARY_VERSION,
 +    SECTION_ID_LIBRARY_VERSIONS,
 +    SECTION_ID_PACKET,
 +    SECTION_ID_PACKET_TAGS,
 +    SECTION_ID_PACKETS,
 +    SECTION_ID_PACKETS_AND_FRAMES,
 +    SECTION_ID_PACKET_SIDE_DATA_LIST,
 +    SECTION_ID_PACKET_SIDE_DATA,
 +    SECTION_ID_PIXEL_FORMAT,
 +    SECTION_ID_PIXEL_FORMAT_FLAGS,
 +    SECTION_ID_PIXEL_FORMAT_COMPONENT,
 +    SECTION_ID_PIXEL_FORMAT_COMPONENTS,
 +    SECTION_ID_PIXEL_FORMATS,
 +    SECTION_ID_PROGRAM_STREAM_DISPOSITION,
 +    SECTION_ID_PROGRAM_STREAM_TAGS,
 +    SECTION_ID_PROGRAM,
 +    SECTION_ID_PROGRAM_STREAMS,
 +    SECTION_ID_PROGRAM_STREAM,
 +    SECTION_ID_PROGRAM_TAGS,
 +    SECTION_ID_PROGRAM_VERSION,
 +    SECTION_ID_PROGRAMS,
 +    SECTION_ID_ROOT,
 +    SECTION_ID_STREAM,
 +    SECTION_ID_STREAM_DISPOSITION,
 +    SECTION_ID_STREAMS,
 +    SECTION_ID_STREAM_TAGS,
 +    SECTION_ID_STREAM_SIDE_DATA_LIST,
 +    SECTION_ID_STREAM_SIDE_DATA,
 +    SECTION_ID_SUBTITLE,
 +} SectionID;
 +
 +static struct section sections[] = {
 +    [SECTION_ID_CHAPTERS] =           { SECTION_ID_CHAPTERS, "chapters", SECTION_FLAG_IS_ARRAY, { SECTION_ID_CHAPTER, -1 } },
 +    [SECTION_ID_CHAPTER] =            { SECTION_ID_CHAPTER, "chapter", 0, { SECTION_ID_CHAPTER_TAGS, -1 } },
 +    [SECTION_ID_CHAPTER_TAGS] =       { SECTION_ID_CHAPTER_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "chapter_tags" },
 +    [SECTION_ID_ERROR] =              { SECTION_ID_ERROR, "error", 0, { -1 } },
 +    [SECTION_ID_FORMAT] =             { SECTION_ID_FORMAT, "format", 0, { SECTION_ID_FORMAT_TAGS, -1 } },
 +    [SECTION_ID_FORMAT_TAGS] =        { SECTION_ID_FORMAT_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "format_tags" },
 +    [SECTION_ID_FRAMES] =             { SECTION_ID_FRAMES, "frames", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME, SECTION_ID_SUBTITLE, -1 } },
 +    [SECTION_ID_FRAME] =              { SECTION_ID_FRAME, "frame", 0, { SECTION_ID_FRAME_TAGS, SECTION_ID_FRAME_SIDE_DATA_LIST, SECTION_ID_FRAME_LOGS, -1 } },
 +    [SECTION_ID_FRAME_TAGS] =         { SECTION_ID_FRAME_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "frame_tags" },
 +    [SECTION_ID_FRAME_SIDE_DATA_LIST] ={ SECTION_ID_FRAME_SIDE_DATA_LIST, "side_data_list", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME_SIDE_DATA, -1 }, .element_name = "side_data", .unique_name = "frame_side_data_list" },
 +    [SECTION_ID_FRAME_SIDE_DATA] =     { SECTION_ID_FRAME_SIDE_DATA, "side_data", 0, { -1 } },
 +    [SECTION_ID_FRAME_LOGS] =         { SECTION_ID_FRAME_LOGS, "logs", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME_LOG, -1 } },
 +    [SECTION_ID_FRAME_LOG] =          { SECTION_ID_FRAME_LOG, "log", 0, { -1 },  },
 +    [SECTION_ID_LIBRARY_VERSIONS] =   { SECTION_ID_LIBRARY_VERSIONS, "library_versions", SECTION_FLAG_IS_ARRAY, { SECTION_ID_LIBRARY_VERSION, -1 } },
 +    [SECTION_ID_LIBRARY_VERSION] =    { SECTION_ID_LIBRARY_VERSION, "library_version", 0, { -1 } },
 +    [SECTION_ID_PACKETS] =            { SECTION_ID_PACKETS, "packets", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PACKET, -1} },
 +    [SECTION_ID_PACKETS_AND_FRAMES] = { SECTION_ID_PACKETS_AND_FRAMES, "packets_and_frames", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PACKET, -1} },
 +    [SECTION_ID_PACKET] =             { SECTION_ID_PACKET, "packet", 0, { SECTION_ID_PACKET_TAGS, SECTION_ID_PACKET_SIDE_DATA_LIST, -1 } },
 +    [SECTION_ID_PACKET_TAGS] =        { SECTION_ID_PACKET_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "packet_tags" },
 +    [SECTION_ID_PACKET_SIDE_DATA_LIST] ={ SECTION_ID_PACKET_SIDE_DATA_LIST, "side_data_list", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PACKET_SIDE_DATA, -1 }, .element_name = "side_data", .unique_name = "packet_side_data_list" },
 +    [SECTION_ID_PACKET_SIDE_DATA] =     { SECTION_ID_PACKET_SIDE_DATA, "side_data", 0, { -1 } },
 +    [SECTION_ID_PIXEL_FORMATS] =      { SECTION_ID_PIXEL_FORMATS, "pixel_formats", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PIXEL_FORMAT, -1 } },
 +    [SECTION_ID_PIXEL_FORMAT] =       { SECTION_ID_PIXEL_FORMAT, "pixel_format", 0, { SECTION_ID_PIXEL_FORMAT_FLAGS, SECTION_ID_PIXEL_FORMAT_COMPONENTS, -1 } },
 +    [SECTION_ID_PIXEL_FORMAT_FLAGS] = { SECTION_ID_PIXEL_FORMAT_FLAGS, "flags", 0, { -1 }, .unique_name = "pixel_format_flags" },
 +    [SECTION_ID_PIXEL_FORMAT_COMPONENTS] = { SECTION_ID_PIXEL_FORMAT_COMPONENTS, "components", SECTION_FLAG_IS_ARRAY, {SECTION_ID_PIXEL_FORMAT_COMPONENT, -1 }, .unique_name = "pixel_format_components" },
 +    [SECTION_ID_PIXEL_FORMAT_COMPONENT]  = { SECTION_ID_PIXEL_FORMAT_COMPONENT, "component", 0, { -1 } },
 +    [SECTION_ID_PROGRAM_STREAM_DISPOSITION] = { SECTION_ID_PROGRAM_STREAM_DISPOSITION, "disposition", 0, { -1 }, .unique_name = "program_stream_disposition" },
 +    [SECTION_ID_PROGRAM_STREAM_TAGS] =        { SECTION_ID_PROGRAM_STREAM_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "program_stream_tags" },
 +    [SECTION_ID_PROGRAM] =                    { SECTION_ID_PROGRAM, "program", 0, { SECTION_ID_PROGRAM_TAGS, SECTION_ID_PROGRAM_STREAMS, -1 } },
 +    [SECTION_ID_PROGRAM_STREAMS] =            { SECTION_ID_PROGRAM_STREAMS, "streams", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PROGRAM_STREAM, -1 }, .unique_name = "program_streams" },
 +    [SECTION_ID_PROGRAM_STREAM] =             { SECTION_ID_PROGRAM_STREAM, "stream", 0, { SECTION_ID_PROGRAM_STREAM_DISPOSITION, SECTION_ID_PROGRAM_STREAM_TAGS, -1 }, .unique_name = "program_stream" },
 +    [SECTION_ID_PROGRAM_TAGS] =               { SECTION_ID_PROGRAM_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "program_tags" },
 +    [SECTION_ID_PROGRAM_VERSION] =    { SECTION_ID_PROGRAM_VERSION, "program_version", 0, { -1 } },
 +    [SECTION_ID_PROGRAMS] =                   { SECTION_ID_PROGRAMS, "programs", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PROGRAM, -1 } },
 +    [SECTION_ID_ROOT] =               { SECTION_ID_ROOT, "root", SECTION_FLAG_IS_WRAPPER,
 +                                        { SECTION_ID_CHAPTERS, SECTION_ID_FORMAT, SECTION_ID_FRAMES, SECTION_ID_PROGRAMS, SECTION_ID_STREAMS,
 +                                          SECTION_ID_PACKETS, SECTION_ID_ERROR, SECTION_ID_PROGRAM_VERSION, SECTION_ID_LIBRARY_VERSIONS,
 +                                          SECTION_ID_PIXEL_FORMATS, -1} },
 +    [SECTION_ID_STREAMS] =            { SECTION_ID_STREAMS, "streams", SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM, -1 } },
 +    [SECTION_ID_STREAM] =             { SECTION_ID_STREAM, "stream", 0, { SECTION_ID_STREAM_DISPOSITION, SECTION_ID_STREAM_TAGS, SECTION_ID_STREAM_SIDE_DATA_LIST, -1 } },
 +    [SECTION_ID_STREAM_DISPOSITION] = { SECTION_ID_STREAM_DISPOSITION, "disposition", 0, { -1 }, .unique_name = "stream_disposition" },
 +    [SECTION_ID_STREAM_TAGS] =        { SECTION_ID_STREAM_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "stream_tags" },
 +    [SECTION_ID_STREAM_SIDE_DATA_LIST] ={ SECTION_ID_STREAM_SIDE_DATA_LIST, "side_data_list", SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM_SIDE_DATA, -1 }, .element_name = "side_data", .unique_name = "stream_side_data_list" },
 +    [SECTION_ID_STREAM_SIDE_DATA] =     { SECTION_ID_STREAM_SIDE_DATA, "side_data", 0, { -1 } },
 +    [SECTION_ID_SUBTITLE] =           { SECTION_ID_SUBTITLE, "subtitle", 0, { -1 } },
 +};
 +
 +static const OptionDef *options;
 +
 +/* FFprobe context */
 +static const char *input_filename;
 +static AVInputFormat *iformat = NULL;
 +
 +static struct AVHashContext *hash;
 +
 +static const struct {
 +    double bin_val;
 +    double dec_val;
 +    const char *bin_str;
 +    const char *dec_str;
 +} si_prefixes[] = {
 +    { 1.0, 1.0, "", "" },
 +    { 1.024e3, 1e3, "Ki", "K" },
 +    { 1.048576e6, 1e6, "Mi", "M" },
 +    { 1.073741824e9, 1e9, "Gi", "G" },
 +    { 1.099511627776e12, 1e12, "Ti", "T" },
 +    { 1.125899906842624e15, 1e15, "Pi", "P" },
 +};
 +
 +static const char unit_second_str[]         = "s"    ;
 +static const char unit_hertz_str[]          = "Hz"   ;
 +static const char unit_byte_str[]           = "byte" ;
 +static const char unit_bit_per_second_str[] = "bit/s";
 +
 +static int nb_streams;
 +static uint64_t *nb_streams_packets;
 +static uint64_t *nb_streams_frames;
 +static int *selected_streams;
 +
 +#if HAVE_THREADS
 +pthread_mutex_t log_mutex;
 +#endif
 +typedef struct LogBuffer {
 +    char *context_name;
 +    int log_level;
 +    char *log_message;
 +    AVClassCategory category;
 +    char *parent_name;
 +    AVClassCategory parent_category;
 +}LogBuffer;
 +
 +static LogBuffer *log_buffer;
 +static int log_buffer_size;
 +
 +static void log_callback(void *ptr, int level, const char *fmt, va_list vl)
 +{
 +    AVClass* avc = ptr ? *(AVClass **) ptr : NULL;
 +    va_list vl2;
 +    char line[1024];
 +    static int print_prefix = 1;
 +    void *new_log_buffer;
 +
 +    va_copy(vl2, vl);
 +    av_log_default_callback(ptr, level, fmt, vl);
 +    av_log_format_line(ptr, level, fmt, vl2, line, sizeof(line), &print_prefix);
 +    va_end(vl2);
 +
 +#if HAVE_THREADS
 +    pthread_mutex_lock(&log_mutex);
 +
 +    new_log_buffer = av_realloc_array(log_buffer, log_buffer_size + 1, sizeof(*log_buffer));
 +    if (new_log_buffer) {
 +        char *msg;
 +        int i;
 +
 +        log_buffer = new_log_buffer;
 +        memset(&log_buffer[log_buffer_size], 0, sizeof(log_buffer[log_buffer_size]));
 +        log_buffer[log_buffer_size].context_name= avc ? av_strdup(avc->item_name(ptr)) : NULL;
 +        if (avc) {
 +            if (avc->get_category) log_buffer[log_buffer_size].category = avc->get_category(ptr);
 +            else                   log_buffer[log_buffer_size].category = avc->category;
 +        }
 +        log_buffer[log_buffer_size].log_level   = level;
 +        msg = log_buffer[log_buffer_size].log_message = av_strdup(line);
 +        for (i=strlen(msg) - 1; i>=0 && msg[i] == '\n'; i--) {
 +            msg[i] = 0;
 +        }
 +        if (avc && avc->parent_log_context_offset) {
 +            AVClass** parent = *(AVClass ***) (((uint8_t *) ptr) +
 +                                   avc->parent_log_context_offset);
 +            if (parent && *parent) {
 +                log_buffer[log_buffer_size].parent_name = av_strdup((*parent)->item_name(parent));
 +                log_buffer[log_buffer_size].parent_category =
 +                    (*parent)->get_category ? (*parent)->get_category(parent) :(*parent)->category;
 +            }
 +        }
 +        log_buffer_size ++;
 +    }
 +
 +    pthread_mutex_unlock(&log_mutex);
 +#endif
 +}
 +
 +static void ffprobe_cleanup(int ret)
 +{
 +    int i;
 +    for (i = 0; i < FF_ARRAY_ELEMS(sections); i++)
 +        av_dict_free(&(sections[i].entries_to_show));
 +
 +#if HAVE_THREADS
 +    pthread_mutex_destroy(&log_mutex);
 +#endif
 +}
 +
 +struct unit_value {
 +    union { double d; long long int i; } val;
 +    const char *unit;
 +};
 +
 +static char *value_string(char *buf, int buf_size, struct unit_value uv)
 +{
 +    double vald;
 +    long long int vali;
 +    int show_float = 0;
 +
 +    if (uv.unit == unit_second_str) {
 +        vald = uv.val.d;
 +        show_float = 1;
 +    } else {
 +        vald = vali = uv.val.i;
 +    }
 +
 +    if (uv.unit == unit_second_str && use_value_sexagesimal_format) {
 +        double secs;
 +        int hours, mins;
 +        secs  = vald;
 +        mins  = (int)secs / 60;
 +        secs  = secs - mins * 60;
 +        hours = mins / 60;
 +        mins %= 60;
 +        snprintf(buf, buf_size, "%d:%02d:%09.6f", hours, mins, secs);
 +    } else {
 +        const char *prefix_string = "";
 +
 +        if (use_value_prefix && vald > 1) {
 +            long long int index;
 +
 +            if (uv.unit == unit_byte_str && use_byte_value_binary_prefix) {
 +                index = (long long int) (log2(vald)) / 10;
 +                index = av_clip(index, 0, FF_ARRAY_ELEMS(si_prefixes) - 1);
 +                vald /= si_prefixes[index].bin_val;
 +                prefix_string = si_prefixes[index].bin_str;
 +            } else {
 +                index = (long long int) (log10(vald)) / 3;
 +                index = av_clip(index, 0, FF_ARRAY_ELEMS(si_prefixes) - 1);
 +                vald /= si_prefixes[index].dec_val;
 +                prefix_string = si_prefixes[index].dec_str;
 +            }
 +            vali = vald;
 +        }
 +
 +        if (show_float || (use_value_prefix && vald != (long long int)vald))
 +            snprintf(buf, buf_size, "%f", vald);
 +        else
 +            snprintf(buf, buf_size, "%lld", vali);
 +        av_strlcatf(buf, buf_size, "%s%s%s", *prefix_string || show_value_unit ? " " : "",
 +                 prefix_string, show_value_unit ? uv.unit : "");
 +    }
 +
 +    return buf;
 +}
 +
 +/* WRITERS API */
 +
 +typedef struct WriterContext WriterContext;
 +
 +#define WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS 1
 +#define WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER 2
 +
 +typedef enum {
 +    WRITER_STRING_VALIDATION_FAIL,
 +    WRITER_STRING_VALIDATION_REPLACE,
 +    WRITER_STRING_VALIDATION_IGNORE,
 +    WRITER_STRING_VALIDATION_NB
 +} StringValidation;
 +
 +typedef struct Writer {
 +    const AVClass *priv_class;      ///< private class of the writer, if any
 +    int priv_size;                  ///< private size for the writer context
 +    const char *name;
 +
 +    int  (*init)  (WriterContext *wctx);
 +    void (*uninit)(WriterContext *wctx);
 +
 +    void (*print_section_header)(WriterContext *wctx);
 +    void (*print_section_footer)(WriterContext *wctx);
 +    void (*print_integer)       (WriterContext *wctx, const char *, long long int);
 +    void (*print_rational)      (WriterContext *wctx, AVRational *q, char *sep);
 +    void (*print_string)        (WriterContext *wctx, const char *, const char *);
 +    int flags;                  ///< a combination or WRITER_FLAG_*
 +} Writer;
 +
 +#define SECTION_MAX_NB_LEVELS 10
 +
 +struct WriterContext {
 +    const AVClass *class;           ///< class of the writer
 +    const Writer *writer;           ///< the Writer of which this is an instance
 +    char *name;                     ///< name of this writer instance
 +    void *priv;                     ///< private data for use by the filter
 +
 +    const struct section *sections; ///< array containing all sections
 +    int nb_sections;                ///< number of sections
 +
 +    int level;                      ///< current level, starting from 0
 +
 +    /** number of the item printed in the given section, starting from 0 */
 +    unsigned int nb_item[SECTION_MAX_NB_LEVELS];
 +
 +    /** section per each level */
 +    const struct section *section[SECTION_MAX_NB_LEVELS];
 +    AVBPrint section_pbuf[SECTION_MAX_NB_LEVELS]; ///< generic print buffer dedicated to each section,
 +                                                  ///  used by various writers
 +
 +    unsigned int nb_section_packet; ///< number of the packet section in case we are in "packets_and_frames" section
 +    unsigned int nb_section_frame;  ///< number of the frame  section in case we are in "packets_and_frames" section
 +    unsigned int nb_section_packet_frame; ///< nb_section_packet or nb_section_frame according if is_packets_and_frames
 +
 +    int string_validation;
 +    char *string_validation_replacement;
 +    unsigned int string_validation_utf8_flags;
 +};
 +
 +static const char *writer_get_name(void *p)
 +{
 +    WriterContext *wctx = p;
 +    return wctx->writer->name;
 +}
 +
 +#define OFFSET(x) offsetof(WriterContext, x)
 +
 +static const AVOption writer_options[] = {
 +    { "string_validation", "set string validation mode",
 +      OFFSET(string_validation), AV_OPT_TYPE_INT, {.i64=WRITER_STRING_VALIDATION_REPLACE}, 0, WRITER_STRING_VALIDATION_NB-1, .unit = "sv" },
 +    { "sv", "set string validation mode",
 +      OFFSET(string_validation), AV_OPT_TYPE_INT, {.i64=WRITER_STRING_VALIDATION_REPLACE}, 0, WRITER_STRING_VALIDATION_NB-1, .unit = "sv" },
 +    { "ignore",  NULL, 0, AV_OPT_TYPE_CONST, {.i64 = WRITER_STRING_VALIDATION_IGNORE},  .unit = "sv" },
 +    { "replace", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = WRITER_STRING_VALIDATION_REPLACE}, .unit = "sv" },
 +    { "fail",    NULL, 0, AV_OPT_TYPE_CONST, {.i64 = WRITER_STRING_VALIDATION_FAIL},    .unit = "sv" },
 +    { "string_validation_replacement", "set string validation replacement string", OFFSET(string_validation_replacement), AV_OPT_TYPE_STRING, {.str=""}},
 +    { "svr", "set string validation replacement string", OFFSET(string_validation_replacement), AV_OPT_TYPE_STRING, {.str="\xEF\xBF\xBD"}},
 +    { NULL }
 +};
 +
 +static void *writer_child_next(void *obj, void *prev)
 +{
 +    WriterContext *ctx = obj;
 +    if (!prev && ctx->writer && ctx->writer->priv_class && ctx->priv)
 +        return ctx->priv;
 +    return NULL;
 +}
 +
 +static const AVClass writer_class = {
 +    .class_name = "Writer",
 +    .item_name  = writer_get_name,
 +    .option     = writer_options,
 +    .version    = LIBAVUTIL_VERSION_INT,
 +    .child_next = writer_child_next,
 +};
 +
 +static void writer_close(WriterContext **wctx)
 +{
 +    int i;
 +
 +    if (!*wctx)
 +        return;
 +
 +    if ((*wctx)->writer->uninit)
 +        (*wctx)->writer->uninit(*wctx);
 +    for (i = 0; i < SECTION_MAX_NB_LEVELS; i++)
 +        av_bprint_finalize(&(*wctx)->section_pbuf[i], NULL);
 +    if ((*wctx)->writer->priv_class)
 +        av_opt_free((*wctx)->priv);
 +    av_freep(&((*wctx)->priv));
 +    av_opt_free(*wctx);
 +    av_freep(wctx);
 +}
 +
 +static void bprint_bytes(AVBPrint *bp, const uint8_t *ubuf, size_t ubuf_size)
 +{
 +    int i;
 +    av_bprintf(bp, "0X");
 +    for (i = 0; i < ubuf_size; i++)
 +        av_bprintf(bp, "%02X", ubuf[i]);
 +}
 +
 +
 +static int writer_open(WriterContext **wctx, const Writer *writer, const char *args,
 +                       const struct section *sections, int nb_sections)
 +{
 +    int i, ret = 0;
 +
 +    if (!(*wctx = av_mallocz(sizeof(WriterContext)))) {
 +        ret = AVERROR(ENOMEM);
 +        goto fail;
 +    }
 +
 +    if (!((*wctx)->priv = av_mallocz(writer->priv_size))) {
 +        ret = AVERROR(ENOMEM);
 +        goto fail;
 +    }
 +
 +    (*wctx)->class = &writer_class;
 +    (*wctx)->writer = writer;
 +    (*wctx)->level = -1;
 +    (*wctx)->sections = sections;
 +    (*wctx)->nb_sections = nb_sections;
 +
 +    av_opt_set_defaults(*wctx);
 +
 +    if (writer->priv_class) {
 +        void *priv_ctx = (*wctx)->priv;
 +        *((const AVClass **)priv_ctx) = writer->priv_class;
 +        av_opt_set_defaults(priv_ctx);
 +    }
 +
 +    /* convert options to dictionary */
 +    if (args) {
 +        AVDictionary *opts = NULL;
 +        AVDictionaryEntry *opt = NULL;
 +
 +        if ((ret = av_dict_parse_string(&opts, args, "=", ":", 0)) < 0) {
 +            av_log(*wctx, AV_LOG_ERROR, "Failed to parse option string '%s' provided to writer context\n", args);
 +            av_dict_free(&opts);
 +            goto fail;
 +        }
 +
 +        while ((opt = av_dict_get(opts, "", opt, AV_DICT_IGNORE_SUFFIX))) {
 +            if ((ret = av_opt_set(*wctx, opt->key, opt->value, AV_OPT_SEARCH_CHILDREN)) < 0) {
 +                av_log(*wctx, AV_LOG_ERROR, "Failed to set option '%s' with value '%s' provided to writer context\n",
 +                       opt->key, opt->value);
 +                av_dict_free(&opts);
 +                goto fail;
 +            }
 +        }
 +
 +        av_dict_free(&opts);
 +    }
 +
 +    /* validate replace string */
 +    {
 +        const uint8_t *p = (*wctx)->string_validation_replacement;
 +        const uint8_t *endp = p + strlen(p);
 +        while (*p) {
 +            const uint8_t *p0 = p;
 +            int32_t code;
 +            ret = av_utf8_decode(&code, &p, endp, (*wctx)->string_validation_utf8_flags);
 +            if (ret < 0) {
 +                AVBPrint bp;
 +                av_bprint_init(&bp, 0, AV_BPRINT_SIZE_AUTOMATIC);
 +                bprint_bytes(&bp, p0, p-p0),
 +                    av_log(wctx, AV_LOG_ERROR,
 +                           "Invalid UTF8 sequence %s found in string validation replace '%s'\n",
 +                           bp.str, (*wctx)->string_validation_replacement);
 +                return ret;
 +            }
 +        }
 +    }
 +
 +    for (i = 0; i < SECTION_MAX_NB_LEVELS; i++)
 +        av_bprint_init(&(*wctx)->section_pbuf[i], 1, AV_BPRINT_SIZE_UNLIMITED);
 +
 +    if ((*wctx)->writer->init)
 +        ret = (*wctx)->writer->init(*wctx);
 +    if (ret < 0)
 +        goto fail;
 +
 +    return 0;
 +
 +fail:
 +    writer_close(wctx);
 +    return ret;
 +}
 +
 +static inline void writer_print_section_header(WriterContext *wctx,
 +                                               int section_id)
 +{
 +    int parent_section_id;
 +    wctx->level++;
 +    av_assert0(wctx->level < SECTION_MAX_NB_LEVELS);
 +    parent_section_id = wctx->level ?
 +        (wctx->section[wctx->level-1])->id : SECTION_ID_NONE;
 +
 +    wctx->nb_item[wctx->level] = 0;
 +    wctx->section[wctx->level] = &wctx->sections[section_id];
 +
 +    if (section_id == SECTION_ID_PACKETS_AND_FRAMES) {
 +        wctx->nb_section_packet = wctx->nb_section_frame =
 +        wctx->nb_section_packet_frame = 0;
 +    } else if (parent_section_id == SECTION_ID_PACKETS_AND_FRAMES) {
 +        wctx->nb_section_packet_frame = section_id == SECTION_ID_PACKET ?
 +            wctx->nb_section_packet : wctx->nb_section_frame;
 +    }
 +
 +    if (wctx->writer->print_section_header)
 +        wctx->writer->print_section_header(wctx);
 +}
 +
 +static inline void writer_print_section_footer(WriterContext *wctx)
 +{
 +    int section_id = wctx->section[wctx->level]->id;
 +    int parent_section_id = wctx->level ?
 +        wctx->section[wctx->level-1]->id : SECTION_ID_NONE;
 +
 +    if (parent_section_id != SECTION_ID_NONE)
 +        wctx->nb_item[wctx->level-1]++;
 +    if (parent_section_id == SECTION_ID_PACKETS_AND_FRAMES) {
 +        if (section_id == SECTION_ID_PACKET) wctx->nb_section_packet++;
 +        else                                     wctx->nb_section_frame++;
 +    }
 +    if (wctx->writer->print_section_footer)
 +        wctx->writer->print_section_footer(wctx);
 +    wctx->level--;
 +}
 +
 +static inline void writer_print_integer(WriterContext *wctx,
 +                                        const char *key, long long int val)
 +{
 +    const struct section *section = wctx->section[wctx->level];
 +
 +    if (section->show_all_entries || av_dict_get(section->entries_to_show, key, NULL, 0)) {
 +        wctx->writer->print_integer(wctx, key, val);
 +        wctx->nb_item[wctx->level]++;
 +    }
 +}
 +
 +static inline int validate_string(WriterContext *wctx, char **dstp, const char *src)
 +{
 +    const uint8_t *p, *endp;
 +    AVBPrint dstbuf;
 +    int invalid_chars_nb = 0, ret = 0;
 +
 +    av_bprint_init(&dstbuf, 0, AV_BPRINT_SIZE_UNLIMITED);
 +
 +    endp = src + strlen(src);
 +    for (p = (uint8_t *)src; *p;) {
 +        uint32_t code;
 +        int invalid = 0;
 +        const uint8_t *p0 = p;
 +
 +        if (av_utf8_decode(&code, &p, endp, wctx->string_validation_utf8_flags) < 0) {
 +            AVBPrint bp;
 +            av_bprint_init(&bp, 0, AV_BPRINT_SIZE_AUTOMATIC);
 +            bprint_bytes(&bp, p0, p-p0);
 +            av_log(wctx, AV_LOG_DEBUG,
 +                   "Invalid UTF-8 sequence %s found in string '%s'\n", bp.str, src);
 +            invalid = 1;
 +        }
 +
 +        if (invalid) {
 +            invalid_chars_nb++;
 +
 +            switch (wctx->string_validation) {
 +            case WRITER_STRING_VALIDATION_FAIL:
 +                av_log(wctx, AV_LOG_ERROR,
 +                       "Invalid UTF-8 sequence found in string '%s'\n", src);
 +                ret = AVERROR_INVALIDDATA;
 +                goto end;
 +                break;
 +
 +            case WRITER_STRING_VALIDATION_REPLACE:
 +                av_bprintf(&dstbuf, "%s", wctx->string_validation_replacement);
 +                break;
 +            }
 +        }
 +
 +        if (!invalid || wctx->string_validation == WRITER_STRING_VALIDATION_IGNORE)
 +            av_bprint_append_data(&dstbuf, p0, p-p0);
 +    }
 +
 +    if (invalid_chars_nb && wctx->string_validation == WRITER_STRING_VALIDATION_REPLACE) {
 +        av_log(wctx, AV_LOG_WARNING,
 +               "%d invalid UTF-8 sequence(s) found in string '%s', replaced with '%s'\n",
 +               invalid_chars_nb, src, wctx->string_validation_replacement);
 +    }
 +
 +end:
 +    av_bprint_finalize(&dstbuf, dstp);
 +    return ret;
 +}
 +
 +#define PRINT_STRING_OPT      1
 +#define PRINT_STRING_VALIDATE 2
 +
 +static inline int writer_print_string(WriterContext *wctx,
 +                                      const char *key, const char *val, int flags)
 +{
 +    const struct section *section = wctx->section[wctx->level];
 +    int ret = 0;
 +
 +    if ((flags & PRINT_STRING_OPT)
 +        && !(wctx->writer->flags & WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS))
 +        return 0;
 +
 +    if (section->show_all_entries || av_dict_get(section->entries_to_show, key, NULL, 0)) {
 +        if (flags & PRINT_STRING_VALIDATE) {
 +            char *key1 = NULL, *val1 = NULL;
 +            ret = validate_string(wctx, &key1, key);
 +            if (ret < 0) goto end;
 +            ret = validate_string(wctx, &val1, val);
 +            if (ret < 0) goto end;
 +            wctx->writer->print_string(wctx, key1, val1);
 +        end:
 +            if (ret < 0) {
 +                av_log(wctx, AV_LOG_ERROR,
 +                       "Invalid key=value string combination %s=%s in section %s\n",
 +                       key, val, section->unique_name);
 +            }
 +            av_free(key1);
 +            av_free(val1);
 +        } else {
 +            wctx->writer->print_string(wctx, key, val);
 +        }
 +
 +        wctx->nb_item[wctx->level]++;
 +    }
 +
 +    return ret;
 +}
 +
 +static inline void writer_print_rational(WriterContext *wctx,
 +                                         const char *key, AVRational q, char sep)
 +{
 +    AVBPrint buf;
 +    av_bprint_init(&buf, 0, AV_BPRINT_SIZE_AUTOMATIC);
 +    av_bprintf(&buf, "%d%c%d", q.num, sep, q.den);
 +    writer_print_string(wctx, key, buf.str, 0);
 +}
 +
 +static void writer_print_time(WriterContext *wctx, const char *key,
 +                              int64_t ts, const AVRational *time_base, int is_duration)
 +{
 +    char buf[128];
 +
 +    if ((!is_duration && ts == AV_NOPTS_VALUE) || (is_duration && ts == 0)) {
 +        writer_print_string(wctx, key, "N/A", PRINT_STRING_OPT);
 +    } else {
 +        double d = ts * av_q2d(*time_base);
 +        struct unit_value uv;
 +        uv.val.d = d;
 +        uv.unit = unit_second_str;
 +        value_string(buf, sizeof(buf), uv);
 +        writer_print_string(wctx, key, buf, 0);
 +    }
 +}
 +
 +static void writer_print_ts(WriterContext *wctx, const char *key, int64_t ts, int is_duration)
 +{
 +    if ((!is_duration && ts == AV_NOPTS_VALUE) || (is_duration && ts == 0)) {
 +        writer_print_string(wctx, key, "N/A", PRINT_STRING_OPT);
 +    } else {
 +        writer_print_integer(wctx, key, ts);
 +    }
 +}
 +
 +static void writer_print_data(WriterContext *wctx, const char *name,
 +                              uint8_t *data, int size)
 +{
 +    AVBPrint bp;
 +    int offset = 0, l, i;
 +
 +    av_bprint_init(&bp, 0, AV_BPRINT_SIZE_UNLIMITED);
 +    av_bprintf(&bp, "\n");
 +    while (size) {
 +        av_bprintf(&bp, "%08x: ", offset);
 +        l = FFMIN(size, 16);
 +        for (i = 0; i < l; i++) {
 +            av_bprintf(&bp, "%02x", data[i]);
 +            if (i & 1)
 +                av_bprintf(&bp, " ");
 +        }
 +        av_bprint_chars(&bp, ' ', 41 - 2 * i - i / 2);
 +        for (i = 0; i < l; i++)
 +            av_bprint_chars(&bp, data[i] - 32U < 95 ? data[i] : '.', 1);
 +        av_bprintf(&bp, "\n");
 +        offset += l;
 +        data   += l;
 +        size   -= l;
 +    }
 +    writer_print_string(wctx, name, bp.str, 0);
 +    av_bprint_finalize(&bp, NULL);
 +}
 +
 +static void writer_print_data_hash(WriterContext *wctx, const char *name,
 +                                   uint8_t *data, int size)
 +{
 +    char *p, buf[AV_HASH_MAX_SIZE * 2 + 64] = { 0 };
 +
 +    if (!hash)
 +        return;
 +    av_hash_init(hash);
 +    av_hash_update(hash, data, size);
 +    snprintf(buf, sizeof(buf), "%s:", av_hash_get_name(hash));
 +    p = buf + strlen(buf);
 +    av_hash_final_hex(hash, p, buf + sizeof(buf) - p);
 +    writer_print_string(wctx, name, buf, 0);
 +}
 +
 +static void writer_print_integers(WriterContext *wctx, const char *name,
 +                                  uint8_t *data, int size, const char *format,
 +                                  int columns, int bytes, int offset_add)
 +{
 +    AVBPrint bp;
 +    int offset = 0, l, i;
 +
 +    av_bprint_init(&bp, 0, AV_BPRINT_SIZE_UNLIMITED);
 +    av_bprintf(&bp, "\n");
 +    while (size) {
 +        av_bprintf(&bp, "%08x: ", offset);
 +        l = FFMIN(size, columns);
 +        for (i = 0; i < l; i++) {
 +            if      (bytes == 1) av_bprintf(&bp, format, *data);
 +            else if (bytes == 2) av_bprintf(&bp, format, AV_RN16(data));
 +            else if (bytes == 4) av_bprintf(&bp, format, AV_RN32(data));
 +            data += bytes;
 +            size --;
 +        }
 +        av_bprintf(&bp, "\n");
 +        offset += offset_add;
 +    }
 +    writer_print_string(wctx, name, bp.str, 0);
 +    av_bprint_finalize(&bp, NULL);
 +}
 +
 +#define MAX_REGISTERED_WRITERS_NB 64
 +
 +static const Writer *registered_writers[MAX_REGISTERED_WRITERS_NB + 1];
 +
 +static int writer_register(const Writer *writer)
 +{
 +    static int next_registered_writer_idx = 0;
 +
 +    if (next_registered_writer_idx == MAX_REGISTERED_WRITERS_NB)
 +        return AVERROR(ENOMEM);
 +
 +    registered_writers[next_registered_writer_idx++] = writer;
 +    return 0;
 +}
 +
 +static const Writer *writer_get_by_name(const char *name)
 +{
 +    int i;
 +
 +    for (i = 0; registered_writers[i]; i++)
 +        if (!strcmp(registered_writers[i]->name, name))
 +            return registered_writers[i];
 +
 +    return NULL;
 +}
 +
 +
 +/* WRITERS */
 +
 +#define DEFINE_WRITER_CLASS(name)                   \
 +static const char *name##_get_name(void *ctx)       \
 +{                                                   \
 +    return #name ;                                  \
 +}                                                   \
 +static const AVClass name##_class = {               \
 +    .class_name = #name,                            \
 +    .item_name  = name##_get_name,                  \
 +    .option     = name##_options                    \
 +}
 +
 +/* Default output */
 +
 +typedef struct DefaultContext {
 +    const AVClass *class;
 +    int nokey;
 +    int noprint_wrappers;
 +    int nested_section[SECTION_MAX_NB_LEVELS];
 +} DefaultContext;
 +
 +#undef OFFSET
 +#define OFFSET(x) offsetof(DefaultContext, x)
 +
 +static const AVOption default_options[] = {
 +    { "noprint_wrappers", "do not print headers and footers", OFFSET(noprint_wrappers), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
 +    { "nw",               "do not print headers and footers", OFFSET(noprint_wrappers), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
 +    { "nokey",          "force no key printing",     OFFSET(nokey),          AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
 +    { "nk",             "force no key printing",     OFFSET(nokey),          AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
 +    {NULL},
 +};
 +
 +DEFINE_WRITER_CLASS(default);
 +
 +/* lame uppercasing routine, assumes the string is lower case ASCII */
 +static inline char *upcase_string(char *dst, size_t dst_size, const char *src)
 +{
 +    int i;
 +    for (i = 0; src[i] && i < dst_size-1; i++)
 +        dst[i] = av_toupper(src[i]);
 +    dst[i] = 0;
 +    return dst;
 +}
 +
 +static void default_print_section_header(WriterContext *wctx)
 +{
 +    DefaultContext *def = wctx->priv;
 +    char buf[32];
 +    const struct section *section = wctx->section[wctx->level];
 +    const struct section *parent_section = wctx->level ?
 +        wctx->section[wctx->level-1] : NULL;
 +
 +    av_bprint_clear(&wctx->section_pbuf[wctx->level]);
 +    if (parent_section &&
 +        !(parent_section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY))) {
 +        def->nested_section[wctx->level] = 1;
 +        av_bprintf(&wctx->section_pbuf[wctx->level], "%s%s:",
 +                   wctx->section_pbuf[wctx->level-1].str,
 +                   upcase_string(buf, sizeof(buf),
 +                                 av_x_if_null(section->element_name, section->name)));
 +    }
 +
 +    if (def->noprint_wrappers || def->nested_section[wctx->level])
 +        return;
 +
 +    if (!(section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY)))
 +        printf("[%s]\n", upcase_string(buf, sizeof(buf), section->name));
 +}
 +
 +static void default_print_section_footer(WriterContext *wctx)
 +{
 +    DefaultContext *def = wctx->priv;
 +    const struct section *section = wctx->section[wctx->level];
 +    char buf[32];
 +
 +    if (def->noprint_wrappers || def->nested_section[wctx->level])
 +        return;
 +
 +    if (!(section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY)))
 +        printf("[/%s]\n", upcase_string(buf, sizeof(buf), section->name));
 +}
 +
 +static void default_print_str(WriterContext *wctx, const char *key, const char *value)
 +{
 +    DefaultContext *def = wctx->priv;
 +
 +    if (!def->nokey)
 +        printf("%s%s=", wctx->section_pbuf[wctx->level].str, key);
 +    printf("%s\n", value);
 +}
 +
 +static void default_print_int(WriterContext *wctx, const char *key, long long int value)
 +{
 +    DefaultContext *def = wctx->priv;
 +
 +    if (!def->nokey)
 +        printf("%s%s=", wctx->section_pbuf[wctx->level].str, key);
 +    printf("%lld\n", value);
 +}
 +
 +static const Writer default_writer = {
 +    .name                  = "default",
 +    .priv_size             = sizeof(DefaultContext),
 +    .print_section_header  = default_print_section_header,
 +    .print_section_footer  = default_print_section_footer,
 +    .print_integer         = default_print_int,
 +    .print_string          = default_print_str,
 +    .flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS,
 +    .priv_class            = &default_class,
 +};
 +
 +/* Compact output */
 +
 +/**
 + * Apply C-language-like string escaping.
 + */
 +static const char *c_escape_str(AVBPrint *dst, const char *src, const char sep, void *log_ctx)
 +{
 +    const char *p;
 +
 +    for (p = src; *p; p++) {
 +        switch (*p) {
 +        case '\b': av_bprintf(dst, "%s", "\\b");  break;
 +        case '\f': av_bprintf(dst, "%s", "\\f");  break;
 +        case '\n': av_bprintf(dst, "%s", "\\n");  break;
 +        case '\r': av_bprintf(dst, "%s", "\\r");  break;
 +        case '\\': av_bprintf(dst, "%s", "\\\\"); break;
 +        default:
 +            if (*p == sep)
 +                av_bprint_chars(dst, '\\', 1);
 +            av_bprint_chars(dst, *p, 1);
 +        }
 +    }
 +    return dst->str;
 +}
 +
 +/**
 + * Quote fields containing special characters, check RFC4180.
 + */
 +static const char *csv_escape_str(AVBPrint *dst, const char *src, const char sep, void *log_ctx)
 +{
 +    char meta_chars[] = { sep, '"', '\n', '\r', '\0' };
 +    int needs_quoting = !!src[strcspn(src, meta_chars)];
 +
 +    if (needs_quoting)
 +        av_bprint_chars(dst, '"', 1);
 +
 +    for (; *src; src++) {
 +        if (*src == '"')
 +            av_bprint_chars(dst, '"', 1);
 +        av_bprint_chars(dst, *src, 1);
 +    }
 +    if (needs_quoting)
 +        av_bprint_chars(dst, '"', 1);
 +    return dst->str;
 +}
 +
 +static const char *none_escape_str(AVBPrint *dst, const char *src, const char sep, void *log_ctx)
 +{
 +    return src;
 +}
 +
 +typedef struct CompactContext {
 +    const AVClass *class;
 +    char *item_sep_str;
 +    char item_sep;
 +    int nokey;
 +    int print_section;
 +    char *escape_mode_str;
 +    const char * (*escape_str)(AVBPrint *dst, const char *src, const char sep, void *log_ctx);
 +    int nested_section[SECTION_MAX_NB_LEVELS];
 +    int has_nested_elems[SECTION_MAX_NB_LEVELS];
 +    int terminate_line[SECTION_MAX_NB_LEVELS];
 +} CompactContext;
 +
 +#undef OFFSET
 +#define OFFSET(x) offsetof(CompactContext, x)
 +
 +static const AVOption compact_options[]= {
 +    {"item_sep", "set item separator",    OFFSET(item_sep_str),    AV_OPT_TYPE_STRING, {.str="|"},  CHAR_MIN, CHAR_MAX },
 +    {"s",        "set item separator",    OFFSET(item_sep_str),    AV_OPT_TYPE_STRING, {.str="|"},  CHAR_MIN, CHAR_MAX },
 +    {"nokey",    "force no key printing", OFFSET(nokey),           AV_OPT_TYPE_BOOL,   {.i64=0},    0,        1        },
 +    {"nk",       "force no key printing", OFFSET(nokey),           AV_OPT_TYPE_BOOL,   {.i64=0},    0,        1        },
 +    {"escape",   "set escape mode",       OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="c"},  CHAR_MIN, CHAR_MAX },
 +    {"e",        "set escape mode",       OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="c"},  CHAR_MIN, CHAR_MAX },
 +    {"print_section", "print section name", OFFSET(print_section), AV_OPT_TYPE_BOOL,   {.i64=1},    0,        1        },
 +    {"p",             "print section name", OFFSET(print_section), AV_OPT_TYPE_BOOL,   {.i64=1},    0,        1        },
 +    {NULL},
 +};
 +
 +DEFINE_WRITER_CLASS(compact);
 +
 +static av_cold int compact_init(WriterContext *wctx)
 +{
 +    CompactContext *compact = wctx->priv;
 +
 +    if (strlen(compact->item_sep_str) != 1) {
 +        av_log(wctx, AV_LOG_ERROR, "Item separator '%s' specified, but must contain a single character\n",
 +               compact->item_sep_str);
 +        return AVERROR(EINVAL);
 +    }
 +    compact->item_sep = compact->item_sep_str[0];
 +
 +    if      (!strcmp(compact->escape_mode_str, "none")) compact->escape_str = none_escape_str;
 +    else if (!strcmp(compact->escape_mode_str, "c"   )) compact->escape_str = c_escape_str;
 +    else if (!strcmp(compact->escape_mode_str, "csv" )) compact->escape_str = csv_escape_str;
 +    else {
 +        av_log(wctx, AV_LOG_ERROR, "Unknown escape mode '%s'\n", compact->escape_mode_str);
 +        return AVERROR(EINVAL);
 +    }
 +
 +    return 0;
 +}
 +
 +static void compact_print_section_header(WriterContext *wctx)
 +{
 +    CompactContext *compact = wctx->priv;
 +    const struct section *section = wctx->section[wctx->level];
 +    const struct section *parent_section = wctx->level ?
 +        wctx->section[wctx->level-1] : NULL;
 +    compact->terminate_line[wctx->level] = 1;
 +    compact->has_nested_elems[wctx->level] = 0;
 +
 +    av_bprint_clear(&wctx->section_pbuf[wctx->level]);
 +    if (!(section->flags & SECTION_FLAG_IS_ARRAY) && parent_section &&
 +        !(parent_section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY))) {
 +        compact->nested_section[wctx->level] = 1;
 +        compact->has_nested_elems[wctx->level-1] = 1;
 +        av_bprintf(&wctx->section_pbuf[wctx->level], "%s%s:",
 +                   wctx->section_pbuf[wctx->level-1].str,
 +                   (char *)av_x_if_null(section->element_name, section->name));
 +        wctx->nb_item[wctx->level] = wctx->nb_item[wctx->level-1];
 +    } else {
 +        if (parent_section && compact->has_nested_elems[wctx->level-1] &&
 +            (section->flags & SECTION_FLAG_IS_ARRAY)) {
 +            compact->terminate_line[wctx->level-1] = 0;
 +            printf("\n");
 +        }
 +        if (compact->print_section &&
 +            !(section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY)))
 +            printf("%s%c", section->name, compact->item_sep);
 +    }
 +}
 +
 +static void compact_print_section_footer(WriterContext *wctx)
 +{
 +    CompactContext *compact = wctx->priv;
 +
 +    if (!compact->nested_section[wctx->level] &&
 +        compact->terminate_line[wctx->level] &&
 +        !(wctx->section[wctx->level]->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY)))
 +        printf("\n");
 +}
 +
 +static void compact_print_str(WriterContext *wctx, const char *key, const char *value)
 +{
 +    CompactContext *compact = wctx->priv;
 +    AVBPrint buf;
 +
 +    if (wctx->nb_item[wctx->level]) printf("%c", compact->item_sep);
 +    if (!compact->nokey)
 +        printf("%s%s=", wctx->section_pbuf[wctx->level].str, key);
 +    av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
 +    printf("%s", compact->escape_str(&buf, value, compact->item_sep, wctx));
 +    av_bprint_finalize(&buf, NULL);
 +}
 +
 +static void compact_print_int(WriterContext *wctx, const char *key, long long int value)
 +{
 +    CompactContext *compact = wctx->priv;
 +
 +    if (wctx->nb_item[wctx->level]) printf("%c", compact->item_sep);
 +    if (!compact->nokey)
 +        printf("%s%s=", wctx->section_pbuf[wctx->level].str, key);
 +    printf("%lld", value);
 +}
 +
 +static const Writer compact_writer = {
 +    .name                 = "compact",
 +    .priv_size            = sizeof(CompactContext),
 +    .init                 = compact_init,
 +    .print_section_header = compact_print_section_header,
 +    .print_section_footer = compact_print_section_footer,
 +    .print_integer        = compact_print_int,
 +    .print_string         = compact_print_str,
 +    .flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS,
 +    .priv_class           = &compact_class,
 +};
 +
 +/* CSV output */
 +
 +#undef OFFSET
 +#define OFFSET(x) offsetof(CompactContext, x)
 +
 +static const AVOption csv_options[] = {
 +    {"item_sep", "set item separator",    OFFSET(item_sep_str),    AV_OPT_TYPE_STRING, {.str=","},  CHAR_MIN, CHAR_MAX },
 +    {"s",        "set item separator",    OFFSET(item_sep_str),    AV_OPT_TYPE_STRING, {.str=","},  CHAR_MIN, CHAR_MAX },
 +    {"nokey",    "force no key printing", OFFSET(nokey),           AV_OPT_TYPE_BOOL,   {.i64=1},    0,        1        },
 +    {"nk",       "force no key printing", OFFSET(nokey),           AV_OPT_TYPE_BOOL,   {.i64=1},    0,        1        },
 +    {"escape",   "set escape mode",       OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="csv"}, CHAR_MIN, CHAR_MAX },
 +    {"e",        "set escape mode",       OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="csv"}, CHAR_MIN, CHAR_MAX },
 +    {"print_section", "print section name", OFFSET(print_section), AV_OPT_TYPE_BOOL,   {.i64=1},    0,        1        },
 +    {"p",             "print section name", OFFSET(print_section), AV_OPT_TYPE_BOOL,   {.i64=1},    0,        1        },
 +    {NULL},
 +};
 +
 +DEFINE_WRITER_CLASS(csv);
 +
 +static const Writer csv_writer = {
 +    .name                 = "csv",
 +    .priv_size            = sizeof(CompactContext),
 +    .init                 = compact_init,
 +    .print_section_header = compact_print_section_header,
 +    .print_section_footer = compact_print_section_footer,
 +    .print_integer        = compact_print_int,
 +    .print_string         = compact_print_str,
 +    .flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS,
 +    .priv_class           = &csv_class,
 +};
 +
 +/* Flat output */
 +
 +typedef struct FlatContext {
 +    const AVClass *class;
 +    const char *sep_str;
 +    char sep;
 +    int hierarchical;
 +} FlatContext;
 +
 +#undef OFFSET
 +#define OFFSET(x) offsetof(FlatContext, x)
 +
 +static const AVOption flat_options[]= {
 +    {"sep_char", "set separator",    OFFSET(sep_str),    AV_OPT_TYPE_STRING, {.str="."},  CHAR_MIN, CHAR_MAX },
 +    {"s",        "set separator",    OFFSET(sep_str),    AV_OPT_TYPE_STRING, {.str="."},  CHAR_MIN, CHAR_MAX },
 +    {"hierarchical", "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 },
 +    {"h",            "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 },
 +    {NULL},
 +};
 +
 +DEFINE_WRITER_CLASS(flat);
 +
 +static av_cold int flat_init(WriterContext *wctx)
 +{
 +    FlatContext *flat = wctx->priv;
 +
 +    if (strlen(flat->sep_str) != 1) {
 +        av_log(wctx, AV_LOG_ERROR, "Item separator '%s' specified, but must contain a single character\n",
 +               flat->sep_str);
 +        return AVERROR(EINVAL);
 +    }
 +    flat->sep = flat->sep_str[0];
 +
 +    return 0;
 +}
 +
 +static const char *flat_escape_key_str(AVBPrint *dst, const char *src, const char sep)
 +{
 +    const char *p;
 +
 +    for (p = src; *p; p++) {
 +        if (!((*p >= '0' && *p <= '9') ||
 +              (*p >= 'a' && *p <= 'z') ||
 +              (*p >= 'A' && *p <= 'Z')))
 +            av_bprint_chars(dst, '_', 1);
 +        else
 +            av_bprint_chars(dst, *p, 1);
 +    }
 +    return dst->str;
 +}
 +
 +static const char *flat_escape_value_str(AVBPrint *dst, const char *src)
 +{
 +    const char *p;
 +
 +    for (p = src; *p; p++) {
 +        switch (*p) {
 +        case '\n': av_bprintf(dst, "%s", "\\n");  break;
 +        case '\r': av_bprintf(dst, "%s", "\\r");  break;
 +        case '\\': av_bprintf(dst, "%s", "\\\\"); break;
 +        case '"':  av_bprintf(dst, "%s", "\\\""); break;
 +        case '`':  av_bprintf(dst, "%s", "\\`");  break;
 +        case '$':  av_bprintf(dst, "%s", "\\$");  break;
 +        default:   av_bprint_chars(dst, *p, 1);   break;
 +        }
 +    }
 +    return dst->str;
 +}
 +
 +static void flat_print_section_header(WriterContext *wctx)
 +{
 +    FlatContext *flat = wctx->priv;
 +    AVBPrint *buf = &wctx->section_pbuf[wctx->level];
 +    const struct section *section = wctx->section[wctx->level];
 +    const struct section *parent_section = wctx->level ?
 +        wctx->section[wctx->level-1] : NULL;
 +
 +    /* build section header */
 +    av_bprint_clear(buf);
 +    if (!parent_section)
 +        return;
 +    av_bprintf(buf, "%s", wctx->section_pbuf[wctx->level-1].str);
 +
 +    if (flat->hierarchical ||
 +        !(section->flags & (SECTION_FLAG_IS_ARRAY|SECTION_FLAG_IS_WRAPPER))) {
 +        av_bprintf(buf, "%s%s", wctx->section[wctx->level]->name, flat->sep_str);
 +
 +        if (parent_section->flags & SECTION_FLAG_IS_ARRAY) {
 +            int n = parent_section->id == SECTION_ID_PACKETS_AND_FRAMES ?
 +                wctx->nb_section_packet_frame : wctx->nb_item[wctx->level-1];
 +            av_bprintf(buf, "%d%s", n, flat->sep_str);
 +        }
 +    }
 +}
 +
 +static void flat_print_int(WriterContext *wctx, const char *key, long long int value)
 +{
 +    printf("%s%s=%lld\n", wctx->section_pbuf[wctx->level].str, key, value);
 +}
 +
 +static void flat_print_str(WriterContext *wctx, const char *key, const char *value)
 +{
 +    FlatContext *flat = wctx->priv;
 +    AVBPrint buf;
 +
 +    printf("%s", wctx->section_pbuf[wctx->level].str);
 +    av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
 +    printf("%s=", flat_escape_key_str(&buf, key, flat->sep));
 +    av_bprint_clear(&buf);
 +    printf("\"%s\"\n", flat_escape_value_str(&buf, value));
 +    av_bprint_finalize(&buf, NULL);
 +}
 +
 +static const Writer flat_writer = {
 +    .name                  = "flat",
 +    .priv_size             = sizeof(FlatContext),
 +    .init                  = flat_init,
 +    .print_section_header  = flat_print_section_header,
 +    .print_integer         = flat_print_int,
 +    .print_string          = flat_print_str,
 +    .flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS|WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER,
 +    .priv_class            = &flat_class,
 +};
 +
 +/* INI format output */
 +
 +typedef struct INIContext {
 +    const AVClass *class;
 +    int hierarchical;
 +} INIContext;
 +
 +#undef OFFSET
 +#define OFFSET(x) offsetof(INIContext, x)
 +
 +static const AVOption ini_options[] = {
 +    {"hierarchical", "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 },
 +    {"h",            "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 },
 +    {NULL},
 +};
 +
 +DEFINE_WRITER_CLASS(ini);
 +
 +static char *ini_escape_str(AVBPrint *dst, const char *src)
 +{
 +    int i = 0;
 +    char c = 0;
 +
 +    while (c = src[i++]) {
 +        switch (c) {
 +        case '\b': av_bprintf(dst, "%s", "\\b"); break;
 +        case '\f': av_bprintf(dst, "%s", "\\f"); break;
 +        case '\n': av_bprintf(dst, "%s", "\\n"); break;
 +        case '\r': av_bprintf(dst, "%s", "\\r"); break;
 +        case '\t': av_bprintf(dst, "%s", "\\t"); break;
 +        case '\\':
 +        case '#' :
 +        case '=' :
 +        case ':' : av_bprint_chars(dst, '\\', 1);
 +        default:
 +            if ((unsigned char)c < 32)
 +                av_bprintf(dst, "\\x00%02x", c & 0xff);
 +            else
 +                av_bprint_chars(dst, c, 1);
 +            break;
 +        }
 +    }
 +    return dst->str;
 +}
 +
 +static void ini_print_section_header(WriterContext *wctx)
 +{
 +    INIContext *ini = wctx->priv;
 +    AVBPrint *buf = &wctx->section_pbuf[wctx->level];
 +    const struct section *section = wctx->section[wctx->level];
 +    const struct section *parent_section = wctx->level ?
 +        wctx->section[wctx->level-1] : NULL;
 +
 +    av_bprint_clear(buf);
 +    if (!parent_section) {
 +        printf("# ffprobe output\n\n");
 +        return;
 +    }
 +
 +    if (wctx->nb_item[wctx->level-1])
 +        printf("\n");
 +
 +    av_bprintf(buf, "%s", wctx->section_pbuf[wctx->level-1].str);
 +    if (ini->hierarchical ||
 +        !(section->flags & (SECTION_FLAG_IS_ARRAY|SECTION_FLAG_IS_WRAPPER))) {
 +        av_bprintf(buf, "%s%s", buf->str[0] ? "." : "", wctx->section[wctx->level]->name);
 +
 +        if (parent_section->flags & SECTION_FLAG_IS_ARRAY) {
 +            int n = parent_section->id == SECTION_ID_PACKETS_AND_FRAMES ?
 +                wctx->nb_section_packet_frame : wctx->nb_item[wctx->level-1];
 +            av_bprintf(buf, ".%d", n);
 +        }
 +    }
 +
 +    if (!(section->flags & (SECTION_FLAG_IS_ARRAY|SECTION_FLAG_IS_WRAPPER)))
 +        printf("[%s]\n", buf->str);
 +}
 +
 +static void ini_print_str(WriterContext *wctx, const char *key, const char *value)
 +{
 +    AVBPrint buf;
 +
 +    av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
 +    printf("%s=", ini_escape_str(&buf, key));
 +    av_bprint_clear(&buf);
 +    printf("%s\n", ini_escape_str(&buf, value));
 +    av_bprint_finalize(&buf, NULL);
 +}
 +
 +static void ini_print_int(WriterContext *wctx, const char *key, long long int value)
 +{
 +    printf("%s=%lld\n", key, value);
 +}
 +
 +static const Writer ini_writer = {
 +    .name                  = "ini",
 +    .priv_size             = sizeof(INIContext),
 +    .print_section_header  = ini_print_section_header,
 +    .print_integer         = ini_print_int,
 +    .print_string          = ini_print_str,
 +    .flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS|WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER,
 +    .priv_class            = &ini_class,
 +};
 +
 +/* JSON output */
 +
 +typedef struct JSONContext {
 +    const AVClass *class;
 +    int indent_level;
 +    int compact;
 +    const char *item_sep, *item_start_end;
 +} JSONContext;
 +
 +#undef OFFSET
 +#define OFFSET(x) offsetof(JSONContext, x)
 +
 +static const AVOption json_options[]= {
 +    { "compact", "enable compact output", OFFSET(compact), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
 +    { "c",       "enable compact output", OFFSET(compact), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
 +    { NULL }
 +};
 +
 +DEFINE_WRITER_CLASS(json);
 +
 +static av_cold int json_init(WriterContext *wctx)
 +{
 +    JSONContext *json = wctx->priv;
 +
 +    json->item_sep       = json->compact ? ", " : ",\n";
 +    json->item_start_end = json->compact ? " "  : "\n";
 +
 +    return 0;
 +}
 +
 +static const char *json_escape_str(AVBPrint *dst, const char *src, void *log_ctx)
 +{
 +    static const char json_escape[] = {'"', '\\', '\b', '\f', '\n', '\r', '\t', 0};
 +    static const char json_subst[]  = {'"', '\\',  'b',  'f',  'n',  'r',  't', 0};
 +    const char *p;
 +
 +    for (p = src; *p; p++) {
 +        char *s = strchr(json_escape, *p);
 +        if (s) {
 +            av_bprint_chars(dst, '\\', 1);
 +            av_bprint_chars(dst, json_subst[s - json_escape], 1);
 +        } else if ((unsigned char)*p < 32) {
 +            av_bprintf(dst, "\\u00%02x", *p & 0xff);
 +        } else {
 +            av_bprint_chars(dst, *p, 1);
 +        }
 +    }
 +    return dst->str;
 +}
 +
 +#define JSON_INDENT() printf("%*c", json->indent_level * 4, ' ')
 +
 +static void json_print_section_header(WriterContext *wctx)
 +{
 +    JSONContext *json = wctx->priv;
 +    AVBPrint buf;
 +    const struct section *section = wctx->section[wctx->level];
 +    const struct section *parent_section = wctx->level ?
 +        wctx->section[wctx->level-1] : NULL;
 +
 +    if (wctx->level && wctx->nb_item[wctx->level-1])
 +        printf(",\n");
 +
 +    if (section->flags & SECTION_FLAG_IS_WRAPPER) {
 +        printf("{\n");
 +        json->indent_level++;
 +    } else {
 +        av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
 +        json_escape_str(&buf, section->name, wctx);
 +        JSON_INDENT();
 +
 +        json->indent_level++;
 +        if (section->flags & SECTION_FLAG_IS_ARRAY) {
 +            printf("\"%s\": [\n", buf.str);
 +        } else if (parent_section && !(parent_section->flags & SECTION_FLAG_IS_ARRAY)) {
 +            printf("\"%s\": {%s", buf.str, json->item_start_end);
 +        } else {
 +            printf("{%s", json->item_start_end);
 +
 +            /* this is required so the parser can distinguish between packets and frames */
 +            if (parent_section && parent_section->id == SECTION_ID_PACKETS_AND_FRAMES) {
 +                if (!json->compact)
 +                    JSON_INDENT();
 +                printf("\"type\": \"%s\"%s", section->name, json->item_sep);
 +            }
 +        }
 +        av_bprint_finalize(&buf, NULL);
 +    }
 +}
 +
 +static void json_print_section_footer(WriterContext *wctx)
 +{
 +    JSONContext *json = wctx->priv;
 +    const struct section *section = wctx->section[wctx->level];
 +
 +    if (wctx->level == 0) {
 +        json->indent_level--;
 +        printf("\n}\n");
 +    } else if (section->flags & SECTION_FLAG_IS_ARRAY) {
 +        printf("\n");
 +        json->indent_level--;
 +        JSON_INDENT();
 +        printf("]");
 +    } else {
 +        printf("%s", json->item_start_end);
 +        json->indent_level--;
 +        if (!json->compact)
 +            JSON_INDENT();
 +        printf("}");
 +    }
 +}
 +
 +static inline void json_print_item_str(WriterContext *wctx,
 +                                       const char *key, const char *value)
 +{
 +    AVBPrint buf;
 +
 +    av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
 +    printf("\"%s\":", json_escape_str(&buf, key,   wctx));
 +    av_bprint_clear(&buf);
 +    printf(" \"%s\"", json_escape_str(&buf, value, wctx));
 +    av_bprint_finalize(&buf, NULL);
 +}
 +
 +static void json_print_str(WriterContext *wctx, const char *key, const char *value)
 +{
 +    JSONContext *json = wctx->priv;
 +
 +    if (wctx->nb_item[wctx->level])
 +        printf("%s", json->item_sep);
 +    if (!json->compact)
 +        JSON_INDENT();
 +    json_print_item_str(wctx, key, value);
 +}
 +
 +static void json_print_int(WriterContext *wctx, const char *key, long long int value)
 +{
 +    JSONContext *json = wctx->priv;
 +    AVBPrint buf;
 +
 +    if (wctx->nb_item[wctx->level])
 +        printf("%s", json->item_sep);
 +    if (!json->compact)
 +        JSON_INDENT();
 +
 +    av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
 +    printf("\"%s\": %lld", json_escape_str(&buf, key, wctx), value);
 +    av_bprint_finalize(&buf, NULL);
 +}
 +
 +static const Writer json_writer = {
 +    .name                 = "json",
 +    .priv_size            = sizeof(JSONContext),
 +    .init                 = json_init,
 +    .print_section_header = json_print_section_header,
 +    .print_section_footer = json_print_section_footer,
 +    .print_integer        = json_print_int,
 +    .print_string         = json_print_str,
 +    .flags = WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER,
 +    .priv_class           = &json_class,
 +};
 +
 +/* XML output */
 +
 +typedef struct XMLContext {
 +    const AVClass *class;
 +    int within_tag;
 +    int indent_level;
 +    int fully_qualified;
 +    int xsd_strict;
 +} XMLContext;
 +
 +#undef OFFSET
 +#define OFFSET(x) offsetof(XMLContext, x)
 +
 +static const AVOption xml_options[] = {
 +    {"fully_qualified", "specify if the output should be fully qualified", OFFSET(fully_qualified), AV_OPT_TYPE_BOOL, {.i64=0},  0, 1 },
 +    {"q",               "specify if the output should be fully qualified", OFFSET(fully_qualified), AV_OPT_TYPE_BOOL, {.i64=0},  0, 1 },
 +    {"xsd_strict",      "ensure that the output is XSD compliant",         OFFSET(xsd_strict),      AV_OPT_TYPE_BOOL, {.i64=0},  0, 1 },
 +    {"x",               "ensure that the output is XSD compliant",         OFFSET(xsd_strict),      AV_OPT_TYPE_BOOL, {.i64=0},  0, 1 },
 +    {NULL},
 +};
 +
 +DEFINE_WRITER_CLASS(xml);
 +
 +static av_cold int xml_init(WriterContext *wctx)
 +{
 +    XMLContext *xml = wctx->priv;
 +
 +    if (xml->xsd_strict) {
 +        xml->fully_qualified = 1;
 +#define CHECK_COMPLIANCE(opt, opt_name)                                 \
 +        if (opt) {                                                      \
 +            av_log(wctx, AV_LOG_ERROR,                                  \
 +                   "XSD-compliant output selected but option '%s' was selected, XML output may be non-compliant.\n" \
 +                   "You need to disable such option with '-no%s'\n", opt_name, opt_name); \
 +            return AVERROR(EINVAL);                                     \
 +        }
 +        CHECK_COMPLIANCE(show_private_data, "private");
 +        CHECK_COMPLIANCE(show_value_unit,   "unit");
 +        CHECK_COMPLIANCE(use_value_prefix,  "prefix");
 +
 +        if (do_show_frames && do_show_packets) {
 +            av_log(wctx, AV_LOG_ERROR,
 +                   "Interleaved frames and packets are not allowed in XSD. "
 +                   "Select only one between the -show_frames and the -show_packets options.\n");
 +            return AVERROR(EINVAL);
 +        }
 +    }
 +
 +    return 0;
 +}
 +
 +static const char *xml_escape_str(AVBPrint *dst, const char *src, void *log_ctx)
 +{
 +    const char *p;
 +
 +    for (p = src; *p; p++) {
 +        switch (*p) {
 +        case '&' : av_bprintf(dst, "%s", "&amp;");  break;
 +        case '<' : av_bprintf(dst, "%s", "&lt;");   break;
 +        case '>' : av_bprintf(dst, "%s", "&gt;");   break;
 +        case '"' : av_bprintf(dst, "%s", "&quot;"); break;
 +        case '\'': av_bprintf(dst, "%s", "&apos;"); break;
 +        default: av_bprint_chars(dst, *p, 1);
 +        }
 +    }
 +
 +    return dst->str;
 +}
 +
 +#define XML_INDENT() printf("%*c", xml->indent_level * 4, ' ')
 +
 +static void xml_print_section_header(WriterContext *wctx)
 +{
 +    XMLContext *xml = wctx->priv;
 +    const struct section *section = wctx->section[wctx->level];
 +    const struct section *parent_section = wctx->level ?
 +        wctx->section[wctx->level-1] : NULL;
 +
 +    if (wctx->level == 0) {
 +        const char *qual = " xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' "
 +            "xmlns:ffprobe='http://www.ffmpeg.org/schema/ffprobe' "
 +            "xsi:schemaLocation='http://www.ffmpeg.org/schema/ffprobe ffprobe.xsd'";
 +
 +        printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
 +        printf("<%sffprobe%s>\n",
 +               xml->fully_qualified ? "ffprobe:" : "",
 +               xml->fully_qualified ? qual : "");
 +        return;
 +    }
 +
 +    if (xml->within_tag) {
 +        xml->within_tag = 0;
 +        printf(">\n");
 +    }
 +    if (section->flags & SECTION_FLAG_HAS_VARIABLE_FIELDS) {
 +        xml->indent_level++;
 +    } else {
 +        if (parent_section && (parent_section->flags & SECTION_FLAG_IS_WRAPPER) &&
 +            wctx->level && wctx->nb_item[wctx->level-1])
 +            printf("\n");
 +        xml->indent_level++;
 +
 +        if (section->flags & SECTION_FLAG_IS_ARRAY) {
 +            XML_INDENT(); printf("<%s>\n", section->name);
 +        } else {
 +            XML_INDENT(); printf("<%s ", section->name);
 +            xml->within_tag = 1;
 +        }
 +    }
 +}
 +
 +static void xml_print_section_footer(WriterContext *wctx)
 +{
 +    XMLContext *xml = wctx->priv;
 +    const struct section *section = wctx->section[wctx->level];
 +
 +    if (wctx->level == 0) {
 +        printf("</%sffprobe>\n", xml->fully_qualified ? "ffprobe:" : "");
 +    } else if (xml->within_tag) {
 +        xml->within_tag = 0;
 +        printf("/>\n");
 +        xml->indent_level--;
 +    } else if (section->flags & SECTION_FLAG_HAS_VARIABLE_FIELDS) {
 +        xml->indent_level--;
 +    } else {
 +        XML_INDENT(); printf("</%s>\n", section->name);
 +        xml->indent_level--;
 +    }
 +}
 +
 +static void xml_print_str(WriterContext *wctx, const char *key, const char *value)
 +{
 +    AVBPrint buf;
 +    XMLContext *xml = wctx->priv;
 +    const struct section *section = wctx->section[wctx->level];
 +
 +    av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
 +
 +    if (section->flags & SECTION_FLAG_HAS_VARIABLE_FIELDS) {
 +        XML_INDENT();
 +        printf("<%s key=\"%s\"",
 +               section->element_name, xml_escape_str(&buf, key, wctx));
 +        av_bprint_clear(&buf);
 +        printf(" value=\"%s\"/>\n", xml_escape_str(&buf, value, wctx));
 +    } else {
 +        if (wctx->nb_item[wctx->level])
 +            printf(" ");
 +        printf("%s=\"%s\"", key, xml_escape_str(&buf, value, wctx));
 +    }
 +
 +    av_bprint_finalize(&buf, NULL);
 +}
 +
 +static void xml_print_int(WriterContext *wctx, const char *key, long long int value)
 +{
 +    if (wctx->nb_item[wctx->level])
 +        printf(" ");
 +    printf("%s=\"%lld\"", key, value);
 +}
 +
 +static Writer xml_writer = {
 +    .name                 = "xml",
 +    .priv_size            = sizeof(XMLContext),
 +    .init                 = xml_init,
 +    .print_section_header = xml_print_section_header,
 +    .print_section_footer = xml_print_section_footer,
 +    .print_integer        = xml_print_int,
 +    .print_string         = xml_print_str,
 +    .flags = WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER,
 +    .priv_class           = &xml_class,
 +};
 +
 +static void writer_register_all(void)
 +{
 +    static int initialized;
 +
 +    if (initialized)
 +        return;
 +    initialized = 1;
 +
 +    writer_register(&default_writer);
 +    writer_register(&compact_writer);
 +    writer_register(&csv_writer);
 +    writer_register(&flat_writer);
 +    writer_register(&ini_writer);
 +    writer_register(&json_writer);
 +    writer_register(&xml_writer);
 +}
 +
 +#define print_fmt(k, f, ...) do {              \
 +    av_bprint_clear(&pbuf);                    \
 +    av_bprintf(&pbuf, f, __VA_ARGS__);         \
 +    writer_print_string(w, k, pbuf.str, 0);    \
 +} while (0)
 +
 +#define print_int(k, v)         writer_print_integer(w, k, v)
 +#define print_q(k, v, s)        writer_print_rational(w, k, v, s)
 +#define print_str(k, v)         writer_print_string(w, k, v, 0)
 +#define print_str_opt(k, v)     writer_print_string(w, k, v, PRINT_STRING_OPT)
 +#define print_str_validate(k, v) writer_print_string(w, k, v, PRINT_STRING_VALIDATE)
 +#define print_time(k, v, tb)    writer_print_time(w, k, v, tb, 0)
 +#define print_ts(k, v)          writer_print_ts(w, k, v, 0)
 +#define print_duration_time(k, v, tb) writer_print_time(w, k, v, tb, 1)
 +#define print_duration_ts(k, v)       writer_print_ts(w, k, v, 1)
 +#define print_val(k, v, u) do {                                     \
 +    struct unit_value uv;                                           \
 +    uv.val.i = v;                                                   \
 +    uv.unit = u;                                                    \
 +    writer_print_string(w, k, value_string(val_str, sizeof(val_str), uv), 0); \
 +} while (0)
 +
 +#define print_section_header(s) writer_print_section_header(w, s)
 +#define print_section_footer(s) writer_print_section_footer(w, s)
 +
 +#define REALLOCZ_ARRAY_STREAM(ptr, cur_n, new_n)                        \
 +{                                                                       \
 +    ret = av_reallocp_array(&(ptr), (new_n), sizeof(*(ptr)));           \
 +    if (ret < 0)                                                        \
 +        goto end;                                                       \
 +    memset( (ptr) + (cur_n), 0, ((new_n) - (cur_n)) * sizeof(*(ptr)) ); \
 +}
 +
 +static inline int show_tags(WriterContext *w, AVDictionary *tags, int section_id)
 +{
 +    AVDictionaryEntry *tag = NULL;
 +    int ret = 0;
 +
 +    if (!tags)
 +        return 0;
 +    writer_print_section_header(w, section_id);
 +
 +    while ((tag = av_dict_get(tags, "", tag, AV_DICT_IGNORE_SUFFIX))) {
 +        if ((ret = print_str_validate(tag->key, tag->value)) < 0)
 +            break;
 +    }
 +    writer_print_section_footer(w);
 +
 +    return ret;
 +}
 +
 +static void print_pkt_side_data(WriterContext *w,
 +                                AVCodecParameters *par,
 +                                const AVPacketSideData *side_data,
 +                                int nb_side_data,
 +                                SectionID id_data_list,
 +                                SectionID id_data)
 +{
 +    int i;
 +
 +    writer_print_section_header(w, id_data_list);
 +    for (i = 0; i < nb_side_data; i++) {
 +        const AVPacketSideData *sd = &side_data[i];
 +        const char *name = av_packet_side_data_name(sd->type);
 +
 +        writer_print_section_header(w, id_data);
 +        print_str("side_data_type", name ? name : "unknown");
 +        if (sd->type == AV_PKT_DATA_DISPLAYMATRIX && sd->size >= 9*4) {
 +            writer_print_integers(w, "displaymatrix", sd->data, 9, " %11d", 3, 4, 1);
 +            print_int("rotation", av_display_rotation_get((int32_t *)sd->data));
 +        } else if (sd->type == AV_PKT_DATA_STEREO3D) {
 +            const AVStereo3D *stereo = (AVStereo3D *)sd->data;
 +            print_str("type", av_stereo3d_type_name(stereo->type));
 +            print_int("inverted", !!(stereo->flags & AV_STEREO3D_FLAG_INVERT));
 +        } else if (sd->type == AV_PKT_DATA_SPHERICAL) {
 +            const AVSphericalMapping *spherical = (AVSphericalMapping *)sd->data;
 +            print_str("projection", av_spherical_projection_name(spherical->projection));
 +            if (spherical->projection == AV_SPHERICAL_CUBEMAP) {
 +                print_int("padding", spherical->padding);
 +            } else if (spherical->projection == AV_SPHERICAL_EQUIRECTANGULAR_TILE) {
 +                size_t l, t, r, b;
 +                av_spherical_tile_bounds(spherical, par->width, par->height,
 +                                         &l, &t, &r, &b);
 +                print_int("bound_left", l);
 +                print_int("bound_top", t);
 +                print_int("bound_right", r);
 +                print_int("bound_bottom", b);
 +            }
 +
 +            print_int("yaw", (double) spherical->yaw / (1 << 16));
 +            print_int("pitch", (double) spherical->pitch / (1 << 16));
 +            print_int("roll", (double) spherical->roll / (1 << 16));
 +        } else if (sd->type == AV_PKT_DATA_SKIP_SAMPLES && sd->size == 10) {
 +            print_int("skip_samples",    AV_RL32(sd->data));
 +            print_int("discard_padding", AV_RL32(sd->data + 4));
 +            print_int("skip_reason",     AV_RL8(sd->data + 8));
 +            print_int("discard_reason",  AV_RL8(sd->data + 9));
 +        }
 +        writer_print_section_footer(w);
 +    }
 +    writer_print_section_footer(w);
 +}
 +
 +static void clear_log(int need_lock)
 +{
 +    int i;
 +
 +    if (need_lock)
 +        pthread_mutex_lock(&log_mutex);
 +    for (i=0; i<log_buffer_size; i++) {
 +        av_freep(&log_buffer[i].context_name);
 +        av_freep(&log_buffer[i].parent_name);
 +        av_freep(&log_buffer[i].log_message);
 +    }
 +    log_buffer_size = 0;
 +    if(need_lock)
 +        pthread_mutex_unlock(&log_mutex);
 +}
 +
 +static int show_log(WriterContext *w, int section_ids, int section_id, int log_level)
 +{
 +    int i;
 +    pthread_mutex_lock(&log_mutex);
 +    if (!log_buffer_size) {
 +        pthread_mutex_unlock(&log_mutex);
 +        return 0;
 +    }
 +    writer_print_section_header(w, section_ids);
 +
 +    for (i=0; i<log_buffer_size; i++) {
 +        if (log_buffer[i].log_level <= log_level) {
 +            writer_print_section_header(w, section_id);
 +            print_str("context", log_buffer[i].context_name);
 +            print_int("level", log_buffer[i].log_level);
 +            print_int("category", log_buffer[i].category);
 +            if (log_buffer[i].parent_name) {
 +                print_str("parent_context", log_buffer[i].parent_name);
 +                print_int("parent_category", log_buffer[i].parent_category);
 +            } else {
 +                print_str_opt("parent_context", "N/A");
 +                print_str_opt("parent_category", "N/A");
 +            }
 +            print_str("message", log_buffer[i].log_message);
 +            writer_print_section_footer(w);
 +        }
 +    }
 +    clear_log(0);
 +    pthread_mutex_unlock(&log_mutex);
 +
 +    writer_print_section_footer(w);
 +
 +    return 0;
 +}
 +
 +static void show_packet(WriterContext *w, InputFile *ifile, AVPacket *pkt, int packet_idx)
 +{
 +    char val_str[128];
 +    AVStream *st = ifile->streams[pkt->stream_index].st;
 +    AVBPrint pbuf;
 +    const char *s;
 +
 +    av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
 +
 +    writer_print_section_header(w, SECTION_ID_PACKET);
 +
 +    s = av_get_media_type_string(st->codecpar->codec_type);
 +    if (s) print_str    ("codec_type", s);
 +    else   print_str_opt("codec_type", "unknown");
 +    print_int("stream_index",     pkt->stream_index);
 +    print_ts  ("pts",             pkt->pts);
 +    print_time("pts_time",        pkt->pts, &st->time_base);
 +    print_ts  ("dts",             pkt->dts);
 +    print_time("dts_time",        pkt->dts, &st->time_base);
 +    print_duration_ts("duration",        pkt->duration);
 +    print_duration_time("duration_time", pkt->duration, &st->time_base);
 +    print_duration_ts("convergence_duration", pkt->convergence_duration);
 +    print_duration_time("convergence_duration_time", pkt->convergence_duration, &st->time_base);
 +    print_val("size",             pkt->size, unit_byte_str);
 +    if (pkt->pos != -1) print_fmt    ("pos", "%"PRId64, pkt->pos);
 +    else                print_str_opt("pos", "N/A");
 +    print_fmt("flags", "%c%c",      pkt->flags & AV_PKT_FLAG_KEY ? 'K' : '_',
 +              pkt->flags & AV_PKT_FLAG_DISCARD ? 'D' : '_');
 +
 +    if (pkt->side_data_elems) {
 +        int size;
 +        const uint8_t *side_metadata;
 +
 +        side_metadata = av_packet_get_side_data(pkt, AV_PKT_DATA_STRINGS_METADATA, &size);
 +        if (side_metadata && size && do_show_packet_tags) {
 +            AVDictionary *dict = NULL;
 +            if (av_packet_unpack_dictionary(side_metadata, size, &dict) >= 0)
 +                show_tags(w, dict, SECTION_ID_PACKET_TAGS);
 +            av_dict_free(&dict);
 +        }
 +
 +        print_pkt_side_data(w, st->codecpar, pkt->side_data, pkt->side_data_elems,
 +                            SECTION_ID_PACKET_SIDE_DATA_LIST,
 +                            SECTION_ID_PACKET_SIDE_DATA);
 +    }
 +
 +    if (do_show_data)
 +        writer_print_data(w, "data", pkt->data, pkt->size);
 +    writer_print_data_hash(w, "data_hash", pkt->data, pkt->size);
 +    writer_print_section_footer(w);
 +
 +    av_bprint_finalize(&pbuf, NULL);
 +    fflush(stdout);
 +}
 +
 +static void show_subtitle(WriterContext *w, AVSubtitle *sub, AVStream *stream,
 +                          AVFormatContext *fmt_ctx)
 +{
 +    AVBPrint pbuf;
 +
 +    av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
 +
 +    writer_print_section_header(w, SECTION_ID_SUBTITLE);
 +
 +    print_str ("media_type",         "subtitle");
 +    print_ts  ("pts",                 sub->pts);
 +    print_time("pts_time",            sub->pts, &AV_TIME_BASE_Q);
 +    print_int ("format",              sub->format);
 +    print_int ("start_display_time",  sub->start_display_time);
 +    print_int ("end_display_time",    sub->end_display_time);
 +    print_int ("num_rects",           sub->num_rects);
 +
 +    writer_print_section_footer(w);
 +
 +    av_bprint_finalize(&pbuf, NULL);
 +    fflush(stdout);
 +}
 +
 +static void show_frame(WriterContext *w, AVFrame *frame, AVStream *stream,
 +                       AVFormatContext *fmt_ctx)
 +{
 +    AVBPrint pbuf;
 +    char val_str[128];
 +    const char *s;
 +    int i;
 +
 +    av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
 +
 +    writer_print_section_header(w, SECTION_ID_FRAME);
 +
 +    s = av_get_media_type_string(stream->codecpar->codec_type);
 +    if (s) print_str    ("media_type", s);
 +    else   print_str_opt("media_type", "unknown");
 +    print_int("stream_index",           stream->index);
 +    print_int("key_frame",              frame->key_frame);
 +    print_ts  ("pkt_pts",               frame->pts);
 +    print_time("pkt_pts_time",          frame->pts, &stream->time_base);
 +    print_ts  ("pkt_dts",               frame->pkt_dts);
 +    print_time("pkt_dts_time",          frame->pkt_dts, &stream->time_base);
 +    print_ts  ("best_effort_timestamp", frame->best_effort_timestamp);
 +    print_time("best_effort_timestamp_time", frame->best_effort_timestamp, &stream->time_base);
 +    print_duration_ts  ("pkt_duration",      frame->pkt_duration);
 +    print_duration_time("pkt_duration_time", frame->pkt_duration, &stream->time_base);
 +    if (frame->pkt_pos != -1) print_fmt    ("pkt_pos", "%"PRId64, frame->pkt_pos);
 +    else                      print_str_opt("pkt_pos", "N/A");
 +    if (frame->pkt_size != -1) print_val    ("pkt_size", frame->pkt_size, unit_byte_str);
 +    else                       print_str_opt("pkt_size", "N/A");
 +
 +    switch (stream->codecpar->codec_type) {
 +        AVRational sar;
 +
 +    case AVMEDIA_TYPE_VIDEO:
 +        print_int("width",                  frame->width);
 +        print_int("height",                 frame->height);
 +        s = av_get_pix_fmt_name(frame->format);
 +        if (s) print_str    ("pix_fmt", s);
 +        else   print_str_opt("pix_fmt", "unknown");
 +        sar = av_guess_sample_aspect_ratio(fmt_ctx, stream, frame);
 +        if (sar.num) {
 +            print_q("sample_aspect_ratio", sar, ':');
 +        } else {
 +            print_str_opt("sample_aspect_ratio", "N/A");
 +        }
 +        print_fmt("pict_type",              "%c", av_get_picture_type_char(frame->pict_type));
 +        print_int("coded_picture_number",   frame->coded_picture_number);
 +        print_int("display_picture_number", frame->display_picture_number);
 +        print_int("interlaced_frame",       frame->interlaced_frame);
 +        print_int("top_field_first",        frame->top_field_first);
 +        print_int("repeat_pict",            frame->repeat_pict);
 +        break;
 +
 +    case AVMEDIA_TYPE_AUDIO:
 +        s = av_get_sample_fmt_name(frame->format);
 +        if (s) print_str    ("sample_fmt", s);
 +        else   print_str_opt("sample_fmt", "unknown");
 +        print_int("nb_samples",         frame->nb_samples);
 +        print_int("channels", frame->channels);
 +        if (frame->channel_layout) {
 +            av_bprint_clear(&pbuf);
 +            av_bprint_channel_layout(&pbuf, frame->channels,
 +                                     frame->channel_layout);
 +            print_str    ("channel_layout", pbuf.str);
 +        } else
 +            print_str_opt("channel_layout", "unknown");
 +        break;
 +    }
 +    if (do_show_frame_tags)
 +        show_tags(w, frame->metadata, SECTION_ID_FRAME_TAGS);
 +    if (do_show_log)
 +        show_log(w, SECTION_ID_FRAME_LOGS, SECTION_ID_FRAME_LOG, do_show_log);
 +    if (frame->nb_side_data) {
 +        writer_print_section_header(w, SECTION_ID_FRAME_SIDE_DATA_LIST);
 +        for (i = 0; i < frame->nb_side_data; i++) {
 +            AVFrameSideData *sd = frame->side_data[i];
 +            const char *name;
 +
 +            writer_print_section_header(w, SECTION_ID_FRAME_SIDE_DATA);
 +            name = av_frame_side_data_name(sd->type);
 +            print_str("side_data_type", name ? name : "unknown");
 +            if (sd->type == AV_FRAME_DATA_DISPLAYMATRIX && sd->size >= 9*4) {
 +                writer_print_integers(w, "displaymatrix", sd->data, 9, " %11d", 3, 4, 1);
 +                print_int("rotation", av_display_rotation_get((int32_t *)sd->data));
 +            } else if (sd->type == AV_FRAME_DATA_GOP_TIMECODE && sd->size >= 8) {
 +                char tcbuf[AV_TIMECODE_STR_SIZE];
 +                av_timecode_make_mpeg_tc_string(tcbuf, *(int64_t *)(sd->data));
 +                print_str("timecode", tcbuf);
 +            }
 +            writer_print_section_footer(w);
 +        }
 +        writer_print_section_footer(w);
 +    }
 +
 +    writer_print_section_footer(w);
 +
 +    av_bprint_finalize(&pbuf, NULL);
 +    fflush(stdout);
 +}
 +
 +static av_always_inline int process_frame(WriterContext *w,
 +                                          InputFile *ifile,
 +                                          AVFrame *frame, AVPacket *pkt,
 +                                          int *packet_new)
 +{
 +    AVFormatContext *fmt_ctx = ifile->fmt_ctx;
 +    AVCodecContext *dec_ctx = ifile->streams[pkt->stream_index].dec_ctx;
 +    AVCodecParameters *par = ifile->streams[pkt->stream_index].st->codecpar;
 +    AVSubtitle sub;
 +    int ret = 0, got_frame = 0;
 +
 +    clear_log(1);
 +    if (dec_ctx && dec_ctx->codec) {
 +        switch (par->codec_type) {
 +        case AVMEDIA_TYPE_VIDEO:
 +        case AVMEDIA_TYPE_AUDIO:
 +            if (*packet_new) {
 +                ret = avcodec_send_packet(dec_ctx, pkt);
 +                if (ret == AVERROR(EAGAIN)) {
 +                    ret = 0;
 +                } else if (ret >= 0 || ret == AVERROR_EOF) {
 +                    ret = 0;
 +                    *packet_new = 0;
 +                }
 +            }
 +            if (ret >= 0) {
 +                ret = avcodec_receive_frame(dec_ctx, frame);
 +                if (ret >= 0) {
 +                    got_frame = 1;
 +                } else if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
 +                    ret = 0;
 +                }
 +            }
 +            break;
 +
 +        case AVMEDIA_TYPE_SUBTITLE:
 +            ret = avcodec_decode_subtitle2(dec_ctx, &sub, &got_frame, pkt);
 +            *packet_new = 0;
 +            break;
 +        default:
 +            *packet_new = 0;
 +        }
 +    } else {
 +        *packet_new = 0;
 +    }
 +
 +    if (ret < 0)
 +        return ret;
 +    if (got_frame) {
 +        int is_sub = (par->codec_type == AVMEDIA_TYPE_SUBTITLE);
 +        nb_streams_frames[pkt->stream_index]++;
 +        if (do_show_frames)
 +            if (is_sub)
 +                show_subtitle(w, &sub, ifile->streams[pkt->stream_index].st, fmt_ctx);
 +            else
 +                show_frame(w, frame, ifile->streams[pkt->stream_index].st, fmt_ctx);
 +        if (is_sub)
 +            avsubtitle_free(&sub);
 +    }
 +    return got_frame || *packet_new;
 +}
 +
 +static void log_read_interval(const ReadInterval *interval, void *log_ctx, int log_level)
 +{
 +    av_log(log_ctx, log_level, "id:%d", interval->id);
 +
 +    if (interval->has_start) {
 +        av_log(log_ctx, log_level, " start:%s%s", interval->start_is_offset ? "+" : "",
 +               av_ts2timestr(interval->start, &AV_TIME_BASE_Q));
 +    } else {
 +        av_log(log_ctx, log_level, " start:N/A");
 +    }
 +
 +    if (interval->has_end) {
 +        av_log(log_ctx, log_level, " end:%s", interval->end_is_offset ? "+" : "");
 +        if (interval->duration_frames)
 +            av_log(log_ctx, log_level, "#%"PRId64, interval->end);
 +        else
 +            av_log(log_ctx, log_level, "%s", av_ts2timestr(interval->end, &AV_TIME_BASE_Q));
 +    } else {
 +        av_log(log_ctx, log_level, " end:N/A");
 +    }
 +
 +    av_log(log_ctx, log_level, "\n");
 +}
 +
 +static int read_interval_packets(WriterContext *w, InputFile *ifile,
 +                                 const ReadInterval *interval, int64_t *cur_ts)
 +{
 +    AVFormatContext *fmt_ctx = ifile->fmt_ctx;
 +    AVPacket pkt;
 +    AVFrame *frame = NULL;
 +    int ret = 0, i = 0, frame_count = 0;
 +    int64_t start = -INT64_MAX, end = interval->end;
 +    int has_start = 0, has_end = interval->has_end && !interval->end_is_offset;
 +
 +    av_init_packet(&pkt);
 +
 +    av_log(NULL, AV_LOG_VERBOSE, "Processing read interval ");
 +    log_read_interval(interval, NULL, AV_LOG_VERBOSE);
 +
 +    if (interval->has_start) {
 +        int64_t target;
 +        if (interval->start_is_offset) {
 +            if (*cur_ts == AV_NOPTS_VALUE) {
 +                av_log(NULL, AV_LOG_ERROR,
 +                       "Could not seek to relative position since current "
 +                       "timestamp is not defined\n");
 +                ret = AVERROR(EINVAL);
 +                goto end;
 +            }
 +            target = *cur_ts + interval->start;
 +        } else {
 +            target = interval->start;
 +        }
 +
 +        av_log(NULL, AV_LOG_VERBOSE, "Seeking to read interval start point %s\n",
 +               av_ts2timestr(target, &AV_TIME_BASE_Q));
 +        if ((ret = avformat_seek_file(fmt_ctx, -1, -INT64_MAX, target, INT64_MAX, 0)) < 0) {
 +            av_log(NULL, AV_LOG_ERROR, "Could not seek to position %"PRId64": %s\n",
 +                   interval->start, av_err2str(ret));
 +            goto end;
 +        }
 +    }
 +
 +    frame = av_frame_alloc();
 +    if (!frame) {
 +        ret = AVERROR(ENOMEM);
 +        goto end;
 +    }
 +    while (!av_read_frame(fmt_ctx, &pkt)) {
 +        if (ifile->nb_streams > nb_streams) {
 +            REALLOCZ_ARRAY_STREAM(nb_streams_frames,  nb_streams, fmt_ctx->nb_streams);
 +            REALLOCZ_ARRAY_STREAM(nb_streams_packets, nb_streams, fmt_ctx->nb_streams);
 +            REALLOCZ_ARRAY_STREAM(selected_streams,   nb_streams, fmt_ctx->nb_streams);
 +            nb_streams = ifile->nb_streams;
 +        }
 +        if (selected_streams[pkt.stream_index]) {
 +            AVRational tb = ifile->streams[pkt.stream_index].st->time_base;
 +
 +            if (pkt.pts != AV_NOPTS_VALUE)
 +                *cur_ts = av_rescale_q(pkt.pts, tb, AV_TIME_BASE_Q);
 +
 +            if (!has_start && *cur_ts != AV_NOPTS_VALUE) {
 +                start = *cur_ts;
 +                has_start = 1;
 +            }
 +
 +            if (has_start && !has_end && interval->end_is_offset) {
 +                end = start + interval->end;
 +                has_end = 1;
 +            }
 +
 +            if (interval->end_is_offset && interval->duration_frames) {
 +                if (frame_count >= interval->end)
 +                    break;
 +            } else if (has_end && *cur_ts != AV_NOPTS_VALUE && *cur_ts >= end) {
 +                break;
 +            }
 +
 +            frame_count++;
 +            if (do_read_packets) {
 +                if (do_show_packets)
 +                    show_packet(w, ifile, &pkt, i++);
 +                nb_streams_packets[pkt.stream_index]++;
 +            }
 +            if (do_read_frames) {
 +                int packet_new = 1;
 +                while (process_frame(w, ifile, frame, &pkt, &packet_new) > 0);
 +            }
 +        }
 +        av_packet_unref(&pkt);
 +    }
 +    av_init_packet(&pkt);
 +    pkt.data = NULL;
 +    pkt.size = 0;
 +    //Flush remaining frames that are cached in the decoder
 +    for (i = 0; i < fmt_ctx->nb_streams; i++) {
 +        pkt.stream_index = i;
 +        if (do_read_frames)
 +            while (process_frame(w, ifile, frame, &pkt, &(int){1}) > 0);
 +    }
 +
 +end:
 +    av_frame_free(&frame);
 +    if (ret < 0) {
 +        av_log(NULL, AV_LOG_ERROR, "Could not read packets in interval ");
 +        log_read_interval(interval, NULL, AV_LOG_ERROR);
 +    }
 +    return ret;
 +}
 +
 +static int read_packets(WriterContext *w, InputFile *ifile)
 +{
 +    AVFormatContext *fmt_ctx = ifile->fmt_ctx;
 +    int i, ret = 0;
 +    int64_t cur_ts = fmt_ctx->start_time;
 +
 +    if (read_intervals_nb == 0) {
 +        ReadInterval interval = (ReadInterval) { .has_start = 0, .has_end = 0 };
 +        ret = read_interval_packets(w, ifile, &interval, &cur_ts);
 +    } else {
 +        for (i = 0; i < read_intervals_nb; i++) {
 +            ret = read_interval_packets(w, ifile, &read_intervals[i], &cur_ts);
 +            if (ret < 0)
 +                break;
 +        }
 +    }
 +
 +    return ret;
 +}
 +
 +static int show_stream(WriterContext *w, AVFormatContext *fmt_ctx, int stream_idx, InputStream *ist, int in_program)
 +{
 +    AVStream *stream = ist->st;
 +    AVCodecParameters *par;
 +    AVCodecContext *dec_ctx;
 +    char val_str[128];
 +    const char *s;
 +    AVRational sar, dar;
 +    AVBPrint pbuf;
 +    const AVCodecDescriptor *cd;
 +    int ret = 0;
 +    const char *profile = NULL;
 +
 +    av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
 +
 +    writer_print_section_header(w, in_program ? SECTION_ID_PROGRAM_STREAM : SECTION_ID_STREAM);
 +
 +    print_int("index", stream->index);
 +
 +    par     = stream->codecpar;
 +    dec_ctx = ist->dec_ctx;
 +    if (cd = avcodec_descriptor_get(par->codec_id)) {
 +        print_str("codec_name", cd->name);
 +        if (!do_bitexact) {
 +            print_str("codec_long_name",
 +                      cd->long_name ? cd->long_name : "unknown");
 +        }
 +    } else {
 +        print_str_opt("codec_name", "unknown");
 +        if (!do_bitexact) {
 +            print_str_opt("codec_long_name", "unknown");
 +        }
 +    }
 +
 +    if (!do_bitexact && (profile = avcodec_profile_name(par->codec_id, par->profile)))
 +        print_str("profile", profile);
 +    else {
 +        if (par->profile != FF_PROFILE_UNKNOWN) {
 +            char profile_num[12];
 +            snprintf(profile_num, sizeof(profile_num), "%d", par->profile);
 +            print_str("profile", profile_num);
 +        } else
 +            print_str_opt("profile", "unknown");
 +    }
 +
 +    s = av_get_media_type_string(par->codec_type);
 +    if (s) print_str    ("codec_type", s);
 +    else   print_str_opt("codec_type", "unknown");
 +#if FF_API_LAVF_AVCTX
 +    if (dec_ctx)
 +        print_q("codec_time_base", dec_ctx->time_base, '/');
 +#endif
 +
 +    /* print AVI/FourCC tag */
 +    print_str("codec_tag_string",    av_fourcc2str(par->codec_tag));
 +    print_fmt("codec_tag", "0x%04"PRIx32, par->codec_tag);
 +
 +    switch (par->codec_type) {
 +    case AVMEDIA_TYPE_VIDEO:
 +        print_int("width",        par->width);
 +        print_int("height",       par->height);
 +        if (dec_ctx) {
 +            print_int("coded_width",  dec_ctx->coded_width);
 +            print_int("coded_height", dec_ctx->coded_height);
 +        }
 +        print_int("has_b_frames", par->video_delay);
 +        sar = av_guess_sample_aspect_ratio(fmt_ctx, stream, NULL);
 +        if (sar.den) {
 +            print_q("sample_aspect_ratio", sar, ':');
 +            av_reduce(&dar.num, &dar.den,
 +                      par->width  * sar.num,
 +                      par->height * sar.den,
 +                      1024*1024);
 +            print_q("display_aspect_ratio", dar, ':');
 +        } else {
 +            print_str_opt("sample_aspect_ratio", "N/A");
 +            print_str_opt("display_aspect_ratio", "N/A");
 +        }
 +        s = av_get_pix_fmt_name(par->format);
 +        if (s) print_str    ("pix_fmt", s);
 +        else   print_str_opt("pix_fmt", "unknown");
 +        print_int("level",   par->level);
 +        if (par->color_range != AVCOL_RANGE_UNSPECIFIED)
 +            print_str    ("color_range", av_color_range_name(par->color_range));
 +        else
 +            print_str_opt("color_range", "N/A");
 +
 +        s = av_get_colorspace_name(par->color_space);
 +        if (s) print_str    ("color_space", s);
 +        else   print_str_opt("color_space", "unknown");
 +
 +        if (par->color_trc != AVCOL_TRC_UNSPECIFIED)
 +            print_str("color_transfer", av_color_transfer_name(par->color_trc));
 +        else
 +            print_str_opt("color_transfer", av_color_transfer_name(par->color_trc));
 +
 +        if (par->color_primaries != AVCOL_PRI_UNSPECIFIED)
 +            print_str("color_primaries", av_color_primaries_name(par->color_primaries));
 +        else
 +            print_str_opt("color_primaries", av_color_primaries_name(par->color_primaries));
 +
 +        if (par->chroma_location != AVCHROMA_LOC_UNSPECIFIED)
 +            print_str("chroma_location", av_chroma_location_name(par->chroma_location));
 +        else
 +            print_str_opt("chroma_location", av_chroma_location_name(par->chroma_location));
 +
 +        if (par->field_order == AV_FIELD_PROGRESSIVE)
 +            print_str("field_order", "progressive");
 +        else if (par->field_order == AV_FIELD_TT)
 +            print_str("field_order", "tt");
 +        else if (par->field_order == AV_FIELD_BB)
 +            print_str("field_order", "bb");
 +        else if (par->field_order == AV_FIELD_TB)
 +            print_str("field_order", "tb");
 +        else if (par->field_order == AV_FIELD_BT)
 +            print_str("field_order", "bt");
 +        else
 +            print_str_opt("field_order", "unknown");
 +
 +#if FF_API_PRIVATE_OPT
 +        if (dec_ctx && dec_ctx->timecode_frame_start >= 0) {
 +            char tcbuf[AV_TIMECODE_STR_SIZE];
 +            av_timecode_make_mpeg_tc_string(tcbuf, dec_ctx->timecode_frame_start);
 +            print_str("timecode", tcbuf);
 +        } else {
 +            print_str_opt("timecode", "N/A");
 +        }
 +#endif
 +        if (dec_ctx)
 +            print_int("refs", dec_ctx->refs);
 +        break;
 +
 +    case AVMEDIA_TYPE_AUDIO:
 +        s = av_get_sample_fmt_name(par->format);
 +        if (s) print_str    ("sample_fmt", s);
 +        else   print_str_opt("sample_fmt", "unknown");
 +        print_val("sample_rate",     par->sample_rate, unit_hertz_str);
 +        print_int("channels",        par->channels);
 +
 +        if (par->channel_layout) {
 +            av_bprint_clear(&pbuf);
 +            av_bprint_channel_layout(&pbuf, par->channels, par->channel_layout);
 +            print_str    ("channel_layout", pbuf.str);
 +        } else {
 +            print_str_opt("channel_layout", "unknown");
 +        }
 +
 +        print_int("bits_per_sample", av_get_bits_per_sample(par->codec_id));
 +        break;
 +
 +    case AVMEDIA_TYPE_SUBTITLE:
 +        if (par->width)
 +            print_int("width",       par->width);
 +        else
 +            print_str_opt("width",   "N/A");
 +        if (par->height)
 +            print_int("height",      par->height);
 +        else
 +            print_str_opt("height",  "N/A");
 +        break;
 +    }
 +
 +    if (dec_ctx && dec_ctx->codec && dec_ctx->codec->priv_class && show_private_data) {
 +        const AVOption *opt = NULL;
 +        while (opt = av_opt_next(dec_ctx->priv_data,opt)) {
 +            uint8_t *str;
 +            if (opt->flags) continue;
 +            if (av_opt_get(dec_ctx->priv_data, opt->name, 0, &str) >= 0) {
 +                print_str(opt->name, str);
 +                av_free(str);
 +            }
 +        }
 +    }
 +
 +    if (fmt_ctx->iformat->flags & AVFMT_SHOW_IDS) print_fmt    ("id", "0x%x", stream->id);
 +    else                                          print_str_opt("id", "N/A");
 +    print_q("r_frame_rate",   stream->r_frame_rate,   '/');
 +    print_q("avg_frame_rate", stream->avg_frame_rate, '/');
 +    print_q("time_base",      stream->time_base,      '/');
 +    print_ts  ("start_pts",   stream->start_time);
 +    print_time("start_time",  stream->start_time, &stream->time_base);
 +    print_ts  ("duration_ts", stream->duration);
 +    print_time("duration",    stream->duration, &stream->time_base);
 +    if (par->bit_rate > 0)     print_val    ("bit_rate", par->bit_rate, unit_bit_per_second_str);
 +    else                       print_str_opt("bit_rate", "N/A");
 +#if FF_API_LAVF_AVCTX
 +    if (stream->codec->rc_max_rate > 0) print_val ("max_bit_rate", stream->codec->rc_max_rate, unit_bit_per_second_str);
 +    else                                print_str_opt("max_bit_rate", "N/A");
 +#endif
 +    if (dec_ctx && dec_ctx->bits_per_raw_sample > 0) print_fmt("bits_per_raw_sample", "%d", dec_ctx->bits_per_raw_sample);
 +    else                                             print_str_opt("bits_per_raw_sample", "N/A");
 +    if (stream->nb_frames) print_fmt    ("nb_frames", "%"PRId64, stream->nb_frames);
 +    else                   print_str_opt("nb_frames", "N/A");
 +    if (nb_streams_frames[stream_idx])  print_fmt    ("nb_read_frames", "%"PRIu64, nb_streams_frames[stream_idx]);
 +    else                                print_str_opt("nb_read_frames", "N/A");
 +    if (nb_streams_packets[stream_idx]) print_fmt    ("nb_read_packets", "%"PRIu64, nb_streams_packets[stream_idx]);
 +    else                                print_str_opt("nb_read_packets", "N/A");
 +    if (do_show_data)
 +        writer_print_data(w, "extradata", par->extradata,
 +                                          par->extradata_size);
 +    writer_print_data_hash(w, "extradata_hash", par->extradata,
 +                                                par->extradata_size);
 +
 +    /* Print disposition information */
 +#define PRINT_DISPOSITION(flagname, name) do {                                \
 +        print_int(name, !!(stream->disposition & AV_DISPOSITION_##flagname)); \
 +    } while (0)
 +
 +    if (do_show_stream_disposition) {
 +    writer_print_section_header(w, in_program ? SECTION_ID_PROGRAM_STREAM_DISPOSITION : SECTION_ID_STREAM_DISPOSITION);
 +    PRINT_DISPOSITION(DEFAULT,          "default");
 +    PRINT_DISPOSITION(DUB,              "dub");
 +    PRINT_DISPOSITION(ORIGINAL,         "original");
 +    PRINT_DISPOSITION(COMMENT,          "comment");
 +    PRINT_DISPOSITION(LYRICS,           "lyrics");
 +    PRINT_DISPOSITION(KARAOKE,          "karaoke");
 +    PRINT_DISPOSITION(FORCED,           "forced");
 +    PRINT_DISPOSITION(HEARING_IMPAIRED, "hearing_impaired");
 +    PRINT_DISPOSITION(VISUAL_IMPAIRED,  "visual_impaired");
 +    PRINT_DISPOSITION(CLEAN_EFFECTS,    "clean_effects");
 +    PRINT_DISPOSITION(ATTACHED_PIC,     "attached_pic");
 +    PRINT_DISPOSITION(TIMED_THUMBNAILS, "timed_thumbnails");
 +    writer_print_section_footer(w);
 +    }
 +
 +    if (do_show_stream_tags)
 +        ret = show_tags(w, stream->metadata, in_program ? SECTION_ID_PROGRAM_STREAM_TAGS : SECTION_ID_STREAM_TAGS);
 +
 +    if (stream->nb_side_data) {
 +        print_pkt_side_data(w, stream->codecpar, stream->side_data, stream->nb_side_data,
 +                            SECTION_ID_STREAM_SIDE_DATA_LIST,
 +                            SECTION_ID_STREAM_SIDE_DATA);
 +    }
 +
 +    writer_print_section_footer(w);
 +    av_bprint_finalize(&pbuf, NULL);
 +    fflush(stdout);
 +
 +    return ret;
 +}
 +
 +static int show_streams(WriterContext *w, InputFile *ifile)
 +{
 +    AVFormatContext *fmt_ctx = ifile->fmt_ctx;
 +    int i, ret = 0;
 +
 +    writer_print_section_header(w, SECTION_ID_STREAMS);
 +    for (i = 0; i < ifile->nb_streams; i++)
 +        if (selected_streams[i]) {
 +            ret = show_stream(w, fmt_ctx, i, &ifile->streams[i], 0);
 +            if (ret < 0)
 +                break;
 +        }
 +    writer_print_section_footer(w);
 +
 +    return ret;
 +}
 +
 +static int show_program(WriterContext *w, InputFile *ifile, AVProgram *program)
 +{
 +    AVFormatContext *fmt_ctx = ifile->fmt_ctx;
 +    int i, ret = 0;
 +
 +    writer_print_section_header(w, SECTION_ID_PROGRAM);
 +    print_int("program_id", program->id);
 +    print_int("program_num", program->program_num);
 +    print_int("nb_streams", program->nb_stream_indexes);
 +    print_int("pmt_pid", program->pmt_pid);
 +    print_int("pcr_pid", program->pcr_pid);
 +    print_ts("start_pts", program->start_time);
 +    print_time("start_time", program->start_time, &AV_TIME_BASE_Q);
 +    print_ts("end_pts", program->end_time);
 +    print_time("end_time", program->end_time, &AV_TIME_BASE_Q);
 +    if (do_show_program_tags)
 +        ret = show_tags(w, program->metadata, SECTION_ID_PROGRAM_TAGS);
 +    if (ret < 0)
 +        goto end;
 +
 +    writer_print_section_header(w, SECTION_ID_PROGRAM_STREAMS);
 +    for (i = 0; i < program->nb_stream_indexes; i++) {
 +        if (selected_streams[program->stream_index[i]]) {
 +            ret = show_stream(w, fmt_ctx, program->stream_index[i], &ifile->streams[program->stream_index[i]], 1);
 +            if (ret < 0)
 +                break;
 +        }
 +    }
 +    writer_print_section_footer(w);
 +
 +end:
 +    writer_print_section_footer(w);
 +    return ret;
 +}
 +
 +static int show_programs(WriterContext *w, InputFile *ifile)
 +{
 +    AVFormatContext *fmt_ctx = ifile->fmt_ctx;
 +    int i, ret = 0;
 +
 +    writer_print_section_header(w, SECTION_ID_PROGRAMS);
 +    for (i = 0; i < fmt_ctx->nb_programs; i++) {
 +        AVProgram *program = fmt_ctx->programs[i];
 +        if (!program)
 +            continue;
 +        ret = show_program(w, ifile, program);
 +        if (ret < 0)
 +            break;
 +    }
 +    writer_print_section_footer(w);
 +    return ret;
 +}
 +
 +static int show_chapters(WriterContext *w, InputFile *ifile)
 +{
 +    AVFormatContext *fmt_ctx = ifile->fmt_ctx;
 +    int i, ret = 0;
 +
 +    writer_print_section_header(w, SECTION_ID_CHAPTERS);
 +    for (i = 0; i < fmt_ctx->nb_chapters; i++) {
 +        AVChapter *chapter = fmt_ctx->chapters[i];
 +
 +        writer_print_section_header(w, SECTION_ID_CHAPTER);
 +        print_int("id", chapter->id);
 +        print_q  ("time_base", chapter->time_base, '/');
 +        print_int("start", chapter->start);
 +        print_time("start_time", chapter->start, &chapter->time_base);
 +        print_int("end", chapter->end);
 +        print_time("end_time", chapter->end, &chapter->time_base);
 +        if (do_show_chapter_tags)
 +            ret = show_tags(w, chapter->metadata, SECTION_ID_CHAPTER_TAGS);
 +        writer_print_section_footer(w);
 +    }
 +    writer_print_section_footer(w);
 +
 +    return ret;
 +}
 +
 +static int show_format(WriterContext *w, InputFile *ifile)
 +{
 +    AVFormatContext *fmt_ctx = ifile->fmt_ctx;
 +    char val_str[128];
 +    int64_t size = fmt_ctx->pb ? avio_size(fmt_ctx->pb) : -1;
 +    int ret = 0;
 +
 +    writer_print_section_header(w, SECTION_ID_FORMAT);
 +    print_str_validate("filename", fmt_ctx->filename);
 +    print_int("nb_streams",       fmt_ctx->nb_streams);
 +    print_int("nb_programs",      fmt_ctx->nb_programs);
 +    print_str("format_name",      fmt_ctx->iformat->name);
 +    if (!do_bitexact) {
 +        if (fmt_ctx->iformat->long_name) print_str    ("format_long_name", fmt_ctx->iformat->long_name);
 +        else                             print_str_opt("format_long_name", "unknown");
 +    }
 +    print_time("start_time",      fmt_ctx->start_time, &AV_TIME_BASE_Q);
 +    print_time("duration",        fmt_ctx->duration,   &AV_TIME_BASE_Q);
 +    if (size >= 0) print_val    ("size", size, unit_byte_str);
 +    else           print_str_opt("size", "N/A");
 +    if (fmt_ctx->bit_rate > 0) print_val    ("bit_rate", fmt_ctx->bit_rate, unit_bit_per_second_str);
 +    else                       print_str_opt("bit_rate", "N/A");
 +    print_int("probe_score", av_format_get_probe_score(fmt_ctx));
 +    if (do_show_format_tags)
 +        ret = show_tags(w, fmt_ctx->metadata, SECTION_ID_FORMAT_TAGS);
 +
 +    writer_print_section_footer(w);
 +    fflush(stdout);
 +    return ret;
 +}
 +
 +static void show_error(WriterContext *w, int err)
 +{
 +    char errbuf[128];
 +    const char *errbuf_ptr = errbuf;
 +
 +    if (av_strerror(err, errbuf, sizeof(errbuf)) < 0)
 +        errbuf_ptr = strerror(AVUNERROR(err));
 +
 +    writer_print_section_header(w, SECTION_ID_ERROR);
 +    print_int("code", err);
 +    print_str("string", errbuf_ptr);
 +    writer_print_section_footer(w);
 +}
 +
 +static int open_input_file(InputFile *ifile, const char *filename)
 +{
 +    int err, i, orig_nb_streams;
 +    AVFormatContext *fmt_ctx = NULL;
 +    AVDictionaryEntry *t;
 +    AVDictionary **opts;
 +    int scan_all_pmts_set = 0;
 +
 +    fmt_ctx = avformat_alloc_context();
 +    if (!fmt_ctx) {
 +        print_error(filename, AVERROR(ENOMEM));
 +        exit_program(1);
 +    }
 +
 +    fmt_ctx->flags |= AVFMT_FLAG_KEEP_SIDE_DATA;
 +
 +    if (!av_dict_get(format_opts, "scan_all_pmts", NULL, AV_DICT_MATCH_CASE)) {
 +        av_dict_set(&format_opts, "scan_all_pmts", "1", AV_DICT_DONT_OVERWRITE);
 +        scan_all_pmts_set = 1;
 +    }
 +    if ((err = avformat_open_input(&fmt_ctx, filename,
 +                                   iformat, &format_opts)) < 0) {
 +        print_error(filename, err);
 +        return err;
 +    }
 +    ifile->fmt_ctx = fmt_ctx;
 +    if (scan_all_pmts_set)
 +        av_dict_set(&format_opts, "scan_all_pmts", NULL, AV_DICT_MATCH_CASE);
 +    if ((t = av_dict_get(format_opts, "", NULL, AV_DICT_IGNORE_SUFFIX))) {
 +        av_log(NULL, AV_LOG_ERROR, "Option %s not found.\n", t->key);
 +        return AVERROR_OPTION_NOT_FOUND;
 +    }
 +
 +    /* fill the streams in the format context */
 +    opts = setup_find_stream_info_opts(fmt_ctx, codec_opts);
 +    orig_nb_streams = fmt_ctx->nb_streams;
 +
 +    err = avformat_find_stream_info(fmt_ctx, opts);
 +
 +    for (i = 0; i < orig_nb_streams; i++)
 +        av_dict_free(&opts[i]);
 +    av_freep(&opts);
 +
 +    if (err < 0) {
 +        print_error(filename, err);
 +        return err;
 +    }
 +
 +    av_dump_format(fmt_ctx, 0, filename, 0);
 +
 +    ifile->streams = av_mallocz_array(fmt_ctx->nb_streams,
 +                                      sizeof(*ifile->streams));
 +    if (!ifile->streams)
 +        exit(1);
 +    ifile->nb_streams = fmt_ctx->nb_streams;
 +
 +    /* bind a decoder to each input stream */
 +    for (i = 0; i < fmt_ctx->nb_streams; i++) {
 +        InputStream *ist = &ifile->streams[i];
 +        AVStream *stream = fmt_ctx->streams[i];
 +        AVCodec *codec;
 +
 +        ist->st = stream;
 +
 +        if (stream->codecpar->codec_id == AV_CODEC_ID_PROBE) {
 +            av_log(NULL, AV_LOG_WARNING,
 +                   "Failed to probe codec for input stream %d\n",
 +                    stream->index);
 +            continue;
 +        }
 +
 +        codec = avcodec_find_decoder(stream->codecpar->codec_id);
 +        if (!codec) {
 +            av_log(NULL, AV_LOG_WARNING,
 +                    "Unsupported codec with id %d for input stream %d\n",
 +                    stream->codecpar->codec_id, stream->index);
 +            continue;
 +        }
 +        {
 +            AVDictionary *opts = filter_codec_opts(codec_opts, stream->codecpar->codec_id,
 +                                                   fmt_ctx, stream, codec);
 +
 +            ist->dec_ctx = avcodec_alloc_context3(codec);
 +            if (!ist->dec_ctx)
 +                exit(1);
 +
 +            err = avcodec_parameters_to_context(ist->dec_ctx, stream->codecpar);
 +            if (err < 0)
 +                exit(1);
 +
 +            if (do_show_log) {
 +                // For loging it is needed to disable at least frame threads as otherwise
 +                // the log information would need to be reordered and matches up to contexts and frames
 +                // That is in fact possible but not trivial
 +                av_dict_set(&codec_opts, "threads", "1", 0);
 +            }
 +
 +            av_codec_set_pkt_timebase(ist->dec_ctx, stream->time_base);
 +            ist->dec_ctx->framerate = stream->avg_frame_rate;
 +
 +            if (avcodec_open2(ist->dec_ctx, codec, &opts) < 0) {
 +                av_log(NULL, AV_LOG_WARNING, "Could not open codec for input stream %d\n",
 +                       stream->index);
 +                exit(1);
 +            }
 +
 +            if ((t = av_dict_get(opts, "", NULL, AV_DICT_IGNORE_SUFFIX))) {
 +                av_log(NULL, AV_LOG_ERROR, "Option %s for input stream %d not found\n",
 +                       t->key, stream->index);
 +                return AVERROR_OPTION_NOT_FOUND;
 +            }
 +        }
 +    }
 +
 +    ifile->fmt_ctx = fmt_ctx;
 +    return 0;
 +}
 +
 +static void close_input_file(InputFile *ifile)
 +{
 +    int i;
 +
 +    /* close decoder for each stream */
 +    for (i = 0; i < ifile->nb_streams; i++)
 +        if (ifile->streams[i].st->codecpar->codec_id != AV_CODEC_ID_NONE)
 +            avcodec_free_context(&ifile->streams[i].dec_ctx);
 +
 +    av_freep(&ifile->streams);
 +    ifile->nb_streams = 0;
 +
 +    avformat_close_input(&ifile->fmt_ctx);
 +}
 +
 +static int probe_file(WriterContext *wctx, const char *filename)
 +{
 +    InputFile ifile = { 0 };
 +    int ret, i;
 +    int section_id;
 +
 +    do_read_frames = do_show_frames || do_count_frames;
 +    do_read_packets = do_show_packets || do_count_packets;
 +
 +    ret = open_input_file(&ifile, filename);
 +    if (ret < 0)
 +        goto end;
 +
 +#define CHECK_END if (ret < 0) goto end
 +
 +    nb_streams = ifile.fmt_ctx->nb_streams;
 +    REALLOCZ_ARRAY_STREAM(nb_streams_frames,0,ifile.fmt_ctx->nb_streams);
 +    REALLOCZ_ARRAY_STREAM(nb_streams_packets,0,ifile.fmt_ctx->nb_streams);
 +    REALLOCZ_ARRAY_STREAM(selected_streams,0,ifile.fmt_ctx->nb_streams);
 +
 +    for (i = 0; i < ifile.fmt_ctx->nb_streams; i++) {
 +        if (stream_specifier) {
 +            ret = avformat_match_stream_specifier(ifile.fmt_ctx,
 +                                                  ifile.fmt_ctx->streams[i],
 +                                                  stream_specifier);
 +            CHECK_END;
 +            else
 +                selected_streams[i] = ret;
 +            ret = 0;
 +        } else {
 +            selected_streams[i] = 1;
 +        }
 +    }
 +
 +    if (do_read_frames || do_read_packets) {
 +        if (do_show_frames && do_show_packets &&
 +            wctx->writer->flags & WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER)
 +            section_id = SECTION_ID_PACKETS_AND_FRAMES;
 +        else if (do_show_packets && !do_show_frames)
 +            section_id = SECTION_ID_PACKETS;
 +        else // (!do_show_packets && do_show_frames)
 +            section_id = SECTION_ID_FRAMES;
 +        if (do_show_frames || do_show_packets)
 +            writer_print_section_header(wctx, section_id);
 +        ret = read_packets(wctx, &ifile);
 +        if (do_show_frames || do_show_packets)
 +            writer_print_section_footer(wctx);
 +        CHECK_END;
 +    }
 +
 +    if (do_show_programs) {
 +        ret = show_programs(wctx, &ifile);
 +        CHECK_END;
 +    }
 +
 +    if (do_show_streams) {
 +        ret = show_streams(wctx, &ifile);
 +        CHECK_END;
 +    }
 +    if (do_show_chapters) {
 +        ret = show_chapters(wctx, &ifile);
 +        CHECK_END;
 +    }
 +    if (do_show_format) {
 +        ret = show_format(wctx, &ifile);
 +        CHECK_END;
 +    }
 +
 +end:
 +    if (ifile.fmt_ctx)
 +        close_input_file(&ifile);
 +    av_freep(&nb_streams_frames);
 +    av_freep(&nb_streams_packets);
 +    av_freep(&selected_streams);
 +
 +    return ret;
 +}
 +
 +static void show_usage(void)
 +{
 +    av_log(NULL, AV_LOG_INFO, "Simple multimedia streams analyzer\n");
 +    av_log(NULL, AV_LOG_INFO, "usage: %s [OPTIONS] [INPUT_FILE]\n", program_name);
 +    av_log(NULL, AV_LOG_INFO, "\n");
 +}
 +
 +static void ffprobe_show_program_version(WriterContext *w)
 +{
 +    AVBPrint pbuf;
 +    av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
 +
 +    writer_print_section_header(w, SECTION_ID_PROGRAM_VERSION);
 +    print_str("version", FFMPEG_VERSION);
 +    print_fmt("copyright", "Copyright (c) %d-%d the FFmpeg developers",
 +              program_birth_year, CONFIG_THIS_YEAR);
 +    print_str("compiler_ident", CC_IDENT);
 +    print_str("configuration", FFMPEG_CONFIGURATION);
 +    writer_print_section_footer(w);
 +
 +    av_bprint_finalize(&pbuf, NULL);
 +}
 +
 +#define SHOW_LIB_VERSION(libname, LIBNAME)                              \
 +    do {                                                                \
 +        if (CONFIG_##LIBNAME) {                                         \
 +            unsigned int version = libname##_version();                 \
 +            writer_print_section_header(w, SECTION_ID_LIBRARY_VERSION); \
 +            print_str("name",    "lib" #libname);                       \
 +            print_int("major",   LIB##LIBNAME##_VERSION_MAJOR);         \
 +            print_int("minor",   LIB##LIBNAME##_VERSION_MINOR);         \
 +            print_int("micro",   LIB##LIBNAME##_VERSION_MICRO);         \
 +            print_int("version", version);                              \
 +            print_str("ident",   LIB##LIBNAME##_IDENT);                 \
 +            writer_print_section_footer(w);                             \
 +        }                                                               \
 +    } while (0)
 +
 +static void ffprobe_show_library_versions(WriterContext *w)
 +{
 +    writer_print_section_header(w, SECTION_ID_LIBRARY_VERSIONS);
 +    SHOW_LIB_VERSION(avutil,     AVUTIL);
 +    SHOW_LIB_VERSION(avcodec,    AVCODEC);
 +    SHOW_LIB_VERSION(avformat,   AVFORMAT);
 +    SHOW_LIB_VERSION(avdevice,   AVDEVICE);
 +    SHOW_LIB_VERSION(avfilter,   AVFILTER);
 +    SHOW_LIB_VERSION(swscale,    SWSCALE);
 +    SHOW_LIB_VERSION(swresample, SWRESAMPLE);
 +    SHOW_LIB_VERSION(postproc,   POSTPROC);
 +    writer_print_section_footer(w);
 +}
 +
 +#define PRINT_PIX_FMT_FLAG(flagname, name)                                \
 +    do {                                                                  \
 +        print_int(name, !!(pixdesc->flags & AV_PIX_FMT_FLAG_##flagname)); \
 +    } while (0)
 +
 +static void ffprobe_show_pixel_formats(WriterContext *w)
 +{
 +    const AVPixFmtDescriptor *pixdesc = NULL;
 +    int i, n;
 +
 +    writer_print_section_header(w, SECTION_ID_PIXEL_FORMATS);
 +    while (pixdesc = av_pix_fmt_desc_next(pixdesc)) {
 +        writer_print_section_header(w, SECTION_ID_PIXEL_FORMAT);
 +        print_str("name", pixdesc->name);
 +        print_int("nb_components", pixdesc->nb_components);
 +        if ((pixdesc->nb_components >= 3) && !(pixdesc->flags & AV_PIX_FMT_FLAG_RGB)) {
 +            print_int    ("log2_chroma_w", pixdesc->log2_chroma_w);
 +            print_int    ("log2_chroma_h", pixdesc->log2_chroma_h);
 +        } else {
 +            print_str_opt("log2_chroma_w", "N/A");
 +            print_str_opt("log2_chroma_h", "N/A");
 +        }
 +        n = av_get_bits_per_pixel(pixdesc);
 +        if (n) print_int    ("bits_per_pixel", n);
 +        else   print_str_opt("bits_per_pixel", "N/A");
 +        if (do_show_pixel_format_flags) {
 +            writer_print_section_header(w, SECTION_ID_PIXEL_FORMAT_FLAGS);
 +            PRINT_PIX_FMT_FLAG(BE,        "big_endian");
 +            PRINT_PIX_FMT_FLAG(PAL,       "palette");
 +            PRINT_PIX_FMT_FLAG(BITSTREAM, "bitstream");
 +            PRINT_PIX_FMT_FLAG(HWACCEL,   "hwaccel");
 +            PRINT_PIX_FMT_FLAG(PLANAR,    "planar");
 +            PRINT_PIX_FMT_FLAG(RGB,       "rgb");
 +            PRINT_PIX_FMT_FLAG(PSEUDOPAL, "pseudopal");
 +            PRINT_PIX_FMT_FLAG(ALPHA,     "alpha");
 +            writer_print_section_footer(w);
 +        }
 +        if (do_show_pixel_format_components && (pixdesc->nb_components > 0)) {
 +            writer_print_section_header(w, SECTION_ID_PIXEL_FORMAT_COMPONENTS);
 +            for (i = 0; i < pixdesc->nb_components; i++) {
 +                writer_print_section_header(w, SECTION_ID_PIXEL_FORMAT_COMPONENT);
 +                print_int("index", i + 1);
 +                print_int("bit_depth", pixdesc->comp[i].depth);
 +                writer_print_section_footer(w);
 +            }
 +            writer_print_section_footer(w);
 +        }
 +        writer_print_section_footer(w);
 +    }
 +    writer_print_section_footer(w);
 +}
 +
 +static int opt_format(void *optctx, const char *opt, const char *arg)
 +{
 +    iformat = av_find_input_format(arg);
 +    if (!iformat) {
 +        av_log(NULL, AV_LOG_ERROR, "Unknown input format: %s\n", arg);
 +        return AVERROR(EINVAL);
 +    }
 +    return 0;
 +}
 +
 +static inline void mark_section_show_entries(SectionID section_id,
 +                                             int show_all_entries, AVDictionary *entries)
 +{
 +    struct section *section = &sections[section_id];
 +
 +    section->show_all_entries = show_all_entries;
 +    if (show_all_entries) {
 +        SectionID *id;
 +        for (id = section->children_ids; *id != -1; id++)
 +            mark_section_show_entries(*id, show_all_entries, entries);
 +    } else {
 +        av_dict_copy(&section->entries_to_show, entries, 0);
 +    }
 +}
 +
 +static int match_section(const char *section_name,
 +                         int show_all_entries, AVDictionary *entries)
 +{
 +    int i, ret = 0;
 +
 +    for (i = 0; i < FF_ARRAY_ELEMS(sections); i++) {
 +        const struct section *section = &sections[i];
 +        if (!strcmp(section_name, section->name) ||
 +            (section->unique_name && !strcmp(section_name, section->unique_name))) {
 +            av_log(NULL, AV_LOG_DEBUG,
 +                   "'%s' matches section with unique name '%s'\n", section_name,
 +                   (char *)av_x_if_null(section->unique_name, section->name));
 +            ret++;
 +            mark_section_show_entries(section->id, show_all_entries, entries);
 +        }
 +    }
 +    return ret;
 +}
 +
 +static int opt_show_entries(void *optctx, const char *opt, const char *arg)
 +{
 +    const char *p = arg;
 +    int ret = 0;
 +
 +    while (*p) {
 +        AVDictionary *entries = NULL;
 +        char *section_name = av_get_token(&p, "=:");
 +        int show_all_entries = 0;
 +
 +        if (!section_name) {
 +            av_log(NULL, AV_LOG_ERROR,
 +                   "Missing section name for option '%s'\n", opt);
 +            return AVERROR(EINVAL);
 +        }
 +
 +        if (*p == '=') {
 +            p++;
 +            while (*p && *p != ':') {
 +                char *entry = av_get_token(&p, ",:");
 +                if (!entry)
 +                    break;
 +                av_log(NULL, AV_LOG_VERBOSE,
 +                       "Adding '%s' to the entries to show in section '%s'\n",
 +                       entry, section_name);
 +                av_dict_set(&entries, entry, "", AV_DICT_DONT_STRDUP_KEY);
 +                if (*p == ',')
 +                    p++;
 +            }
 +        } else {
 +            show_all_entries = 1;
 +        }
 +
 +        ret = match_section(section_name, show_all_entries, entries);
 +        if (ret == 0) {
 +            av_log(NULL, AV_LOG_ERROR, "No match for section '%s'\n", section_name);
 +            ret = AVERROR(EINVAL);
 +        }
 +        av_dict_free(&entries);
 +        av_free(section_name);
 +
 +        if (ret <= 0)
 +            break;
 +        if (*p)
 +            p++;
 +    }
 +
 +    return ret;
 +}
 +
 +static int opt_show_format_entry(void *optctx, const char *opt, const char *arg)
 +{
 +    char *buf = av_asprintf("format=%s", arg);
 +    int ret;
 +
 +    if (!buf)
 +        return AVERROR(ENOMEM);
 +
 +    av_log(NULL, AV_LOG_WARNING,
 +           "Option '%s' is deprecated, use '-show_entries format=%s' instead\n",
 +           opt, arg);
 +    ret = opt_show_entries(optctx, opt, buf);
 +    av_free(buf);
 +    return ret;
 +}
 +
 +static void opt_input_file(void *optctx, const char *arg)
 +{
 +    if (input_filename) {
 +        av_log(NULL, AV_LOG_ERROR,
 +                "Argument '%s' provided as input filename, but '%s' was already specified.\n",
 +                arg, input_filename);
 +        exit_program(1);
 +    }
 +    if (!strcmp(arg, "-"))
 +        arg = "pipe:";
 +    input_filename = arg;
 +}
 +
 +static int opt_input_file_i(void *optctx, const char *opt, const char *arg)
 +{
 +    opt_input_file(optctx, arg);
 +    return 0;
 +}
 +
 +void show_help_default(const char *opt, const char *arg)
 +{
 +    av_log_set_callback(log_callback_help);
 +    show_usage();
 +    show_help_options(options, "Main options:", 0, 0, 0);
 +    printf("\n");
 +
 +    show_help_children(avformat_get_class(), AV_OPT_FLAG_DECODING_PARAM);
 +    show_help_children(avcodec_get_class(), AV_OPT_FLAG_DECODING_PARAM);
 +}
 +
 +/**
 + * Parse interval specification, according to the format:
 + * INTERVAL ::= [START|+START_OFFSET][%[END|+END_OFFSET]]
 + * INTERVALS ::= INTERVAL[,INTERVALS]
 +*/
 +static int parse_read_interval(const char *interval_spec,
 +                               ReadInterval *interval)
 +{
 +    int ret = 0;
 +    char *next, *p, *spec = av_strdup(interval_spec);
 +    if (!spec)
 +        return AVERROR(ENOMEM);
 +
 +    if (!*spec) {
 +        av_log(NULL, AV_LOG_ERROR, "Invalid empty interval specification\n");
 +        ret = AVERROR(EINVAL);
 +        goto end;
 +    }
 +
 +    p = spec;
 +    next = strchr(spec, '%');
 +    if (next)
 +        *next++ = 0;
 +
 +    /* parse first part */
 +    if (*p) {
 +        interval->has_start = 1;
 +
 +        if (*p == '+') {
 +            interval->start_is_offset = 1;
 +            p++;
 +        } else {
 +            interval->start_is_offset = 0;
 +        }
 +
 +        ret = av_parse_time(&interval->start, p, 1);
 +        if (ret < 0) {
 +            av_log(NULL, AV_LOG_ERROR, "Invalid interval start specification '%s'\n", p);
 +            goto end;
 +        }
 +    } else {
 +        interval->has_start = 0;
 +    }
 +
 +    /* parse second part */
 +    p = next;
 +    if (p && *p) {
 +        int64_t us;
 +        interval->has_end = 1;
 +
 +        if (*p == '+') {
 +            interval->end_is_offset = 1;
 +            p++;
 +        } else {
 +            interval->end_is_offset = 0;
 +        }
 +
 +        if (interval->end_is_offset && *p == '#') {
 +            long long int lli;
 +            char *tail;
 +            interval->duration_frames = 1;
 +            p++;
 +            lli = strtoll(p, &tail, 10);
 +            if (*tail || lli < 0) {
 +                av_log(NULL, AV_LOG_ERROR,
 +                       "Invalid or negative value '%s' for duration number of frames\n", p);
 +                goto end;
 +            }
 +            interval->end = lli;
 +        } else {
 +            ret = av_parse_time(&us, p, 1);
 +            if (ret < 0) {
 +                av_log(NULL, AV_LOG_ERROR, "Invalid interval end/duration specification '%s'\n", p);
 +                goto end;
 +            }
 +            interval->end = us;
 +        }
 +    } else {
 +        interval->has_end = 0;
 +    }
 +
 +end:
 +    av_free(spec);
 +    return ret;
 +}
 +
 +static int parse_read_intervals(const char *intervals_spec)
 +{
 +    int ret, n, i;
 +    char *p, *spec = av_strdup(intervals_spec);
 +    if (!spec)
 +        return AVERROR(ENOMEM);
 +
 +    /* preparse specification, get number of intervals */
 +    for (n = 0, p = spec; *p; p++)
 +        if (*p == ',')
 +            n++;
 +    n++;
 +
 +    read_intervals = av_malloc_array(n, sizeof(*read_intervals));
 +    if (!read_intervals) {
 +        ret = AVERROR(ENOMEM);
 +        goto end;
 +    }
 +    read_intervals_nb = n;
 +
 +    /* parse intervals */
 +    p = spec;
 +    for (i = 0; p; i++) {
 +        char *next;
 +
 +        av_assert0(i < read_intervals_nb);
 +        next = strchr(p, ',');
 +        if (next)
 +            *next++ = 0;
 +
 +        read_intervals[i].id = i;
 +        ret = parse_read_interval(p, &read_intervals[i]);
 +        if (ret < 0) {
 +            av_log(NULL, AV_LOG_ERROR, "Error parsing read interval #%d '%s'\n",
 +                   i, p);
 +            goto end;
 +        }
 +        av_log(NULL, AV_LOG_VERBOSE, "Parsed log interval ");
 +        log_read_interval(&read_intervals[i], NULL, AV_LOG_VERBOSE);
 +        p = next;
 +    }
 +    av_assert0(i == read_intervals_nb);
 +
 +end:
 +    av_free(spec);
 +    return ret;
 +}
 +
 +static int opt_read_intervals(void *optctx, const char *opt, const char *arg)
 +{
 +    return parse_read_intervals(arg);
 +}
 +
 +static int opt_pretty(void *optctx, const char *opt, const char *arg)
 +{
 +    show_value_unit              = 1;
 +    use_value_prefix             = 1;
 +    use_byte_value_binary_prefix = 1;
 +    use_value_sexagesimal_format = 1;
 +    return 0;
 +}
 +
 +static void print_section(SectionID id, int level)
 +{
 +    const SectionID *pid;
 +    const struct section *section = &sections[id];
 +    printf("%c%c%c",
 +           section->flags & SECTION_FLAG_IS_WRAPPER           ? 'W' : '.',
 +           section->flags & SECTION_FLAG_IS_ARRAY             ? 'A' : '.',
 +           section->flags & SECTION_FLAG_HAS_VARIABLE_FIELDS  ? 'V' : '.');
 +    printf("%*c  %s", level * 4, ' ', section->name);
 +    if (section->unique_name)
 +        printf("/%s", section->unique_name);
 +    printf("\n");
 +
 +    for (pid = section->children_ids; *pid != -1; pid++)
 +        print_section(*pid, level+1);
 +}
 +
 +static int opt_sections(void *optctx, const char *opt, const char *arg)
 +{
 +    printf("Sections:\n"
 +           "W.. = Section is a wrapper (contains other sections, no local entries)\n"
 +           ".A. = Section contains an array of elements of the same type\n"
 +           "..V = Section may contain a variable number of fields with variable keys\n"
 +           "FLAGS NAME/UNIQUE_NAME\n"
 +           "---\n");
 +    print_section(SECTION_ID_ROOT, 0);
 +    return 0;
 +}
 +
 +static int opt_show_versions(const char *opt, const char *arg)
 +{
 +    mark_section_show_entries(SECTION_ID_PROGRAM_VERSION, 1, NULL);
 +    mark_section_show_entries(SECTION_ID_LIBRARY_VERSION, 1, NULL);
 +    return 0;
 +}
 +
 +#define DEFINE_OPT_SHOW_SECTION(section, target_section_id)             \
 +    static int opt_show_##section(const char *opt, const char *arg)     \
 +    {                                                                   \
 +        mark_section_show_entries(SECTION_ID_##target_section_id, 1, NULL); \
 +        return 0;                                                       \
 +    }
 +
 +DEFINE_OPT_SHOW_SECTION(chapters,         CHAPTERS)
 +DEFINE_OPT_SHOW_SECTION(error,            ERROR)
 +DEFINE_OPT_SHOW_SECTION(format,           FORMAT)
 +DEFINE_OPT_SHOW_SECTION(frames,           FRAMES)
 +DEFINE_OPT_SHOW_SECTION(library_versions, LIBRARY_VERSIONS)
 +DEFINE_OPT_SHOW_SECTION(packets,          PACKETS)
 +DEFINE_OPT_SHOW_SECTION(pixel_formats,    PIXEL_FORMATS)
 +DEFINE_OPT_SHOW_SECTION(program_version,  PROGRAM_VERSION)
 +DEFINE_OPT_SHOW_SECTION(streams,          STREAMS)
 +DEFINE_OPT_SHOW_SECTION(programs,         PROGRAMS)
 +
 +static const OptionDef real_options[] = {
++    CMDUTILS_COMMON_OPTIONS
 +    { "f", HAS_ARG, {.func_arg = opt_format}, "force format", "format" },
 +    { "unit", OPT_BOOL, {&show_value_unit}, "show unit of the displayed values" },
 +    { "prefix", OPT_BOOL, {&use_value_prefix}, "use SI prefixes for the displayed values" },
 +    { "byte_binary_prefix", OPT_BOOL, {&use_byte_value_binary_prefix},
 +      "use binary prefixes for byte units" },
 +    { "sexagesimal", OPT_BOOL,  {&use_value_sexagesimal_format},
 +      "use sexagesimal format HOURS:MM:SS.MICROSECONDS for time units" },
 +    { "pretty", 0, {.func_arg = opt_pretty},
 +      "prettify the format of displayed values, make it more human readable" },
 +    { "print_format", OPT_STRING | HAS_ARG, {(void*)&print_format},
 +      "set the output printing format (available formats are: default, compact, csv, flat, ini, json, xml)", "format" },
 +    { "of", OPT_STRING | HAS_ARG, {(void*)&print_format}, "alias for -print_format", "format" },
 +    { "select_streams", OPT_STRING | HAS_ARG, {(void*)&stream_specifier}, "select the specified streams", "stream_specifier" },
 +    { "sections", OPT_EXIT, {.func_arg = opt_sections}, "print sections structure and section information, and exit" },
 +    { "show_data",    OPT_BOOL, {(void*)&do_show_data}, "show packets data" },
 +    { "show_data_hash", OPT_STRING | HAS_ARG, {(void*)&show_data_hash}, "show packets data hash" },
 +    { "show_error",   0, {(void*)&opt_show_error},  "show probing error" },
 +    { "show_format",  0, {(void*)&opt_show_format}, "show format/container info" },
 +    { "show_frames",  0, {(void*)&opt_show_frames}, "show frames info" },
 +    { "show_format_entry", HAS_ARG, {.func_arg = opt_show_format_entry},
 +      "show a particular entry from the format/container info", "entry" },
 +    { "show_entries", HAS_ARG, {.func_arg = opt_show_entries},
 +      "show a set of specified entries", "entry_list" },
 +#if HAVE_THREADS
 +    { "show_log", OPT_INT|HAS_ARG, {(void*)&do_show_log}, "show log" },
 +#endif
 +    { "show_packets", 0, {(void*)&opt_show_packets}, "show packets info" },
 +    { "show_programs", 0, {(void*)&opt_show_programs}, "show programs info" },
 +    { "show_streams", 0, {(void*)&opt_show_streams}, "show streams info" },
 +    { "show_chapters", 0, {(void*)&opt_show_chapters}, "show chapters info" },
 +    { "count_frames", OPT_BOOL, {(void*)&do_count_frames}, "count the number of frames per stream" },
 +    { "count_packets", OPT_BOOL, {(void*)&do_count_packets}, "count the number of packets per stream" },
 +    { "show_program_version",  0, {(void*)&opt_show_program_version},  "show ffprobe version" },
 +    { "show_library_versions", 0, {(void*)&opt_show_library_versions}, "show library versions" },
 +    { "show_versions",         0, {(void*)&opt_show_versions}, "show program and library versions" },
 +    { "show_pixel_formats", 0, {(void*)&opt_show_pixel_formats}, "show pixel format descriptions" },
 +    { "show_private_data", OPT_BOOL, {(void*)&show_private_data}, "show private data" },
 +    { "private",           OPT_BOOL, {(void*)&show_private_data}, "same as show_private_data" },
 +    { "bitexact", OPT_BOOL, {&do_bitexact}, "force bitexact output" },
 +    { "read_intervals", HAS_ARG, {.func_arg = opt_read_intervals}, "set read intervals", "read_intervals" },
 +    { "default", HAS_ARG | OPT_AUDIO | OPT_VIDEO | OPT_EXPERT, {.func_arg = opt_default}, "generic catch all option", "" },
 +    { "i", HAS_ARG, {.func_arg = opt_input_file_i}, "read specified file", "input_file"},
 +    { NULL, },
 +};
 +
 +static inline int check_section_show_entries(int section_id)
 +{
 +    int *id;
 +    struct section *section = &sections[section_id];
 +    if (sections[section_id].show_all_entries || sections[section_id].entries_to_show)
 +        return 1;
 +    for (id = section->children_ids; *id != -1; id++)
 +        if (check_section_show_entries(*id))
 +            return 1;
 +    return 0;
 +}
 +
 +#define SET_DO_SHOW(id, varname) do {                                   \
 +        if (check_section_show_entries(SECTION_ID_##id))                \
 +            do_show_##varname = 1;                                      \
 +    } while (0)
 +
 +int main(int argc, char **argv)
 +{
 +    const Writer *w;
 +    WriterContext *wctx;
 +    char *buf;
 +    char *w_name = NULL, *w_args = NULL;
 +    int ret, i;
 +
 +    init_dynload();
 +
 +#if HAVE_THREADS
 +    ret = pthread_mutex_init(&log_mutex, NULL);
 +    if (ret != 0) {
 +        goto end;
 +    }
 +#endif
 +    av_log_set_flags(AV_LOG_SKIP_REPEATED);
 +    register_exit(ffprobe_cleanup);
 +
 +    options = real_options;
 +    parse_loglevel(argc, argv, options);
 +    av_register_all();
 +    avformat_network_init();
 +    init_opts();
 +#if CONFIG_AVDEVICE
 +    avdevice_register_all();
 +#endif
 +
 +    show_banner(argc, argv, options);
 +    parse_options(NULL, argc, argv, options, opt_input_file);
 +
 +    if (do_show_log)
 +        av_log_set_callback(log_callback);
 +
 +    /* mark things to show, based on -show_entries */
 +    SET_DO_SHOW(CHAPTERS, chapters);
 +    SET_DO_SHOW(ERROR, error);
 +    SET_DO_SHOW(FORMAT, format);
 +    SET_DO_SHOW(FRAMES, frames);
 +    SET_DO_SHOW(LIBRARY_VERSIONS, library_versions);
 +    SET_DO_SHOW(PACKETS, packets);
 +    SET_DO_SHOW(PIXEL_FORMATS, pixel_formats);
 +    SET_DO_SHOW(PIXEL_FORMAT_FLAGS, pixel_format_flags);
 +    SET_DO_SHOW(PIXEL_FORMAT_COMPONENTS, pixel_format_components);
 +    SET_DO_SHOW(PROGRAM_VERSION, program_version);
 +    SET_DO_SHOW(PROGRAMS, programs);
 +    SET_DO_SHOW(STREAMS, streams);
 +    SET_DO_SHOW(STREAM_DISPOSITION, stream_disposition);
 +    SET_DO_SHOW(PROGRAM_STREAM_DISPOSITION, stream_disposition);
 +
 +    SET_DO_SHOW(CHAPTER_TAGS, chapter_tags);
 +    SET_DO_SHOW(FORMAT_TAGS, format_tags);
 +    SET_DO_SHOW(FRAME_TAGS, frame_tags);
 +    SET_DO_SHOW(PROGRAM_TAGS, program_tags);
 +    SET_DO_SHOW(STREAM_TAGS, stream_tags);
 +    SET_DO_SHOW(PROGRAM_STREAM_TAGS, stream_tags);
 +    SET_DO_SHOW(PACKET_TAGS, packet_tags);
 +
 +    if (do_bitexact && (do_show_program_version || do_show_library_versions)) {
 +        av_log(NULL, AV_LOG_ERROR,
 +               "-bitexact and -show_program_version or -show_library_versions "
 +               "options are incompatible\n");
 +        ret = AVERROR(EINVAL);
 +        goto end;
 +    }
 +
 +    writer_register_all();
 +
 +    if (!print_format)
 +        print_format = av_strdup("default");
 +    if (!print_format) {
 +        ret = AVERROR(ENOMEM);
 +        goto end;
 +    }
 +    w_name = av_strtok(print_format, "=", &buf);
 +    if (!w_name) {
 +        av_log(NULL, AV_LOG_ERROR,
 +               "No name specified for the output format\n");
 +        ret = AVERROR(EINVAL);
 +        goto end;
 +    }
 +    w_args = buf;
 +
 +    if (show_data_hash) {
 +        if ((ret = av_hash_alloc(&hash, show_data_hash)) < 0) {
 +            if (ret == AVERROR(EINVAL)) {
 +                const char *n;
 +                av_log(NULL, AV_LOG_ERROR,
 +                       "Unknown hash algorithm '%s'\nKnown algorithms:",
 +                       show_data_hash);
 +                for (i = 0; (n = av_hash_names(i)); i++)
 +                    av_log(NULL, AV_LOG_ERROR, " %s", n);
 +                av_log(NULL, AV_LOG_ERROR, "\n");
 +            }
 +            goto end;
 +        }
 +    }
 +
 +    w = writer_get_by_name(w_name);
 +    if (!w) {
 +        av_log(NULL, AV_LOG_ERROR, "Unknown output format with name '%s'\n", w_name);
 +        ret = AVERROR(EINVAL);
 +        goto end;
 +    }
 +
 +    if ((ret = writer_open(&wctx, w, w_args,
 +                           sections, FF_ARRAY_ELEMS(sections))) >= 0) {
 +        if (w == &xml_writer)
 +            wctx->string_validation_utf8_flags |= AV_UTF8_FLAG_EXCLUDE_XML_INVALID_CONTROL_CODES;
 +
 +        writer_print_section_header(wctx, SECTION_ID_ROOT);
 +
 +        if (do_show_program_version)
 +            ffprobe_show_program_version(wctx);
 +        if (do_show_library_versions)
 +            ffprobe_show_library_versions(wctx);
 +        if (do_show_pixel_formats)
 +            ffprobe_show_pixel_formats(wctx);
 +
 +        if (!input_filename &&
 +            ((do_show_format || do_show_programs || do_show_streams || do_show_chapters || do_show_packets || do_show_error) ||
 +             (!do_show_program_version && !do_show_library_versions && !do_show_pixel_formats))) {
 +            show_usage();
 +            av_log(NULL, AV_LOG_ERROR, "You have to specify one input file.\n");
 +            av_log(NULL, AV_LOG_ERROR, "Use -h to get full help or, even better, run 'man %s'.\n", program_name);
 +            ret = AVERROR(EINVAL);
 +        } else if (input_filename) {
 +            ret = probe_file(wctx, input_filename);
 +            if (ret < 0 && do_show_error)
 +                show_error(wctx, ret);
 +        }
 +
 +        writer_print_section_footer(wctx);
 +        writer_close(&wctx);
 +    }
 +
 +end:
 +    av_freep(&print_format);
 +    av_freep(&read_intervals);
 +    av_hash_freep(&hash);
 +
 +    uninit_opts();
 +    for (i = 0; i < FF_ARRAY_ELEMS(sections); i++)
 +        av_dict_free(&(sections[i].entries_to_show));
 +
 +    avformat_network_deinit();
 +
 +    return ret < 0;
 +}
diff --cc ffserver.c
index 8b819b6,0000000..f9f987a
mode 100644,000000..100644
--- /dev/null
@@@ -1,4026 -1,0 +1,4026 @@@
- #include "cmdutils_common_opts.h"
 +/*
 + * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
 + *
 + * This file is part of FFmpeg.
 + *
 + * FFmpeg is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public
 + * License as published by the Free Software Foundation; either
 + * version 2.1 of the License, or (at your option) any later version.
 + *
 + * FFmpeg is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with FFmpeg; if not, write to the Free Software
 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 + */
 +
 +/**
 + * @file
 + * multiple format streaming server based on the FFmpeg libraries
 + */
 +
 +#include "config.h"
 +#if !HAVE_CLOSESOCKET
 +#define closesocket close
 +#endif
 +#include <string.h>
 +#include <stdlib.h>
 +#include <stdio.h>
 +#include "libavformat/avformat.h"
 +/* FIXME: those are internal headers, ffserver _really_ shouldn't use them */
 +#include "libavformat/rtpproto.h"
 +#include "libavformat/rtsp.h"
 +#include "libavformat/avio_internal.h"
 +#include "libavformat/internal.h"
 +
 +#include "libavutil/avassert.h"
 +#include "libavutil/avstring.h"
 +#include "libavutil/lfg.h"
 +#include "libavutil/dict.h"
 +#include "libavutil/intreadwrite.h"
 +#include "libavutil/mathematics.h"
 +#include "libavutil/random_seed.h"
 +#include "libavutil/rational.h"
 +#include "libavutil/parseutils.h"
 +#include "libavutil/opt.h"
 +#include "libavutil/time.h"
 +
 +#include <stdarg.h>
 +#if HAVE_UNISTD_H
 +#include <unistd.h>
 +#endif
 +#include <fcntl.h>
 +#include <sys/ioctl.h>
 +#if HAVE_POLL_H
 +#include <poll.h>
 +#endif
 +#include <errno.h>
 +#include <time.h>
 +#include <sys/wait.h>
 +#include <signal.h>
 +
 +#include "cmdutils.h"
 +#include "ffserver_config.h"
 +
 +#define PATH_LENGTH 1024
 +
 +const char program_name[] = "ffserver";
 +const int program_birth_year = 2000;
 +
 +static const OptionDef options[];
 +
 +enum HTTPState {
 +    HTTPSTATE_WAIT_REQUEST,
 +    HTTPSTATE_SEND_HEADER,
 +    HTTPSTATE_SEND_DATA_HEADER,
 +    HTTPSTATE_SEND_DATA,          /* sending TCP or UDP data */
 +    HTTPSTATE_SEND_DATA_TRAILER,
 +    HTTPSTATE_RECEIVE_DATA,
 +    HTTPSTATE_WAIT_FEED,          /* wait for data from the feed */
 +    HTTPSTATE_READY,
 +
 +    RTSPSTATE_WAIT_REQUEST,
 +    RTSPSTATE_SEND_REPLY,
 +    RTSPSTATE_SEND_PACKET,
 +};
 +
 +static const char * const http_state[] = {
 +    "HTTP_WAIT_REQUEST",
 +    "HTTP_SEND_HEADER",
 +
 +    "SEND_DATA_HEADER",
 +    "SEND_DATA",
 +    "SEND_DATA_TRAILER",
 +    "RECEIVE_DATA",
 +    "WAIT_FEED",
 +    "READY",
 +
 +    "RTSP_WAIT_REQUEST",
 +    "RTSP_SEND_REPLY",
 +    "RTSP_SEND_PACKET",
 +};
 +
 +#define IOBUFFER_INIT_SIZE 8192
 +
 +/* timeouts are in ms */
 +#define HTTP_REQUEST_TIMEOUT (15 * 1000)
 +#define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
 +
 +#define SYNC_TIMEOUT (10 * 1000)
 +
 +typedef struct RTSPActionServerSetup {
 +    uint32_t ipaddr;
 +    char transport_option[512];
 +} RTSPActionServerSetup;
 +
 +typedef struct {
 +    int64_t count1, count2;
 +    int64_t time1, time2;
 +} DataRateData;
 +
 +/* context associated with one connection */
 +typedef struct HTTPContext {
 +    enum HTTPState state;
 +    int fd; /* socket file descriptor */
 +    struct sockaddr_in from_addr; /* origin */
 +    struct pollfd *poll_entry; /* used when polling */
 +    int64_t timeout;
 +    uint8_t *buffer_ptr, *buffer_end;
 +    int http_error;
 +    int post;
 +    int chunked_encoding;
 +    int chunk_size;               /* 0 if it needs to be read */
 +    struct HTTPContext *next;
 +    int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
 +    int64_t data_count;
 +    /* feed input */
 +    int feed_fd;
 +    /* input format handling */
 +    AVFormatContext *fmt_in;
 +    int64_t start_time;            /* In milliseconds - this wraps fairly often */
 +    int64_t first_pts;            /* initial pts value */
 +    int64_t cur_pts;             /* current pts value from the stream in us */
 +    int64_t cur_frame_duration;  /* duration of the current frame in us */
 +    int cur_frame_bytes;       /* output frame size, needed to compute
 +                                  the time at which we send each
 +                                  packet */
 +    int pts_stream_index;        /* stream we choose as clock reference */
 +    int64_t cur_clock;           /* current clock reference value in us */
 +    /* output format handling */
 +    struct FFServerStream *stream;
 +    /* -1 is invalid stream */
 +    int feed_streams[FFSERVER_MAX_STREAMS]; /* index of streams in the feed */
 +    int switch_feed_streams[FFSERVER_MAX_STREAMS]; /* index of streams in the feed */
 +    int switch_pending;
 +    AVFormatContext *pfmt_ctx; /* instance of FFServerStream for one user */
 +    int last_packet_sent; /* true if last data packet was sent */
 +    int suppress_log;
 +    DataRateData datarate;
 +    int wmp_client_id;
 +    char protocol[16];
 +    char method[16];
 +    char url[128];
 +    char clean_url[128*7];
 +    int buffer_size;
 +    uint8_t *buffer;
 +    int is_packetized; /* if true, the stream is packetized */
 +    int packet_stream_index; /* current stream for output in state machine */
 +
 +    /* RTSP state specific */
 +    uint8_t *pb_buffer; /* XXX: use that in all the code */
 +    AVIOContext *pb;
 +    int seq; /* RTSP sequence number */
 +
 +    /* RTP state specific */
 +    enum RTSPLowerTransport rtp_protocol;
 +    char session_id[32]; /* session id */
 +    AVFormatContext *rtp_ctx[FFSERVER_MAX_STREAMS];
 +
 +    /* RTP/UDP specific */
 +    URLContext *rtp_handles[FFSERVER_MAX_STREAMS];
 +
 +    /* RTP/TCP specific */
 +    struct HTTPContext *rtsp_c;
 +    uint8_t *packet_buffer, *packet_buffer_ptr, *packet_buffer_end;
 +} HTTPContext;
 +
 +static HTTPContext *first_http_ctx;
 +
 +static FFServerConfig config = {
 +    .nb_max_http_connections = 2000,
 +    .nb_max_connections = 5,
 +    .max_bandwidth = 1000,
 +    .use_defaults = 1,
 +};
 +
 +static void new_connection(int server_fd, int is_rtsp);
 +static void close_connection(HTTPContext *c);
 +
 +/* HTTP handling */
 +static int handle_connection(HTTPContext *c);
 +static inline void print_stream_params(AVIOContext *pb, FFServerStream *stream);
 +static void compute_status(HTTPContext *c);
 +static int open_input_stream(HTTPContext *c, const char *info);
 +static int http_parse_request(HTTPContext *c);
 +static int http_send_data(HTTPContext *c);
 +static int http_start_receive_data(HTTPContext *c);
 +static int http_receive_data(HTTPContext *c);
 +
 +/* RTSP handling */
 +static int rtsp_parse_request(HTTPContext *c);
 +static void rtsp_cmd_describe(HTTPContext *c, const char *url);
 +static void rtsp_cmd_options(HTTPContext *c, const char *url);
 +static void rtsp_cmd_setup(HTTPContext *c, const char *url,
 +                           RTSPMessageHeader *h);
 +static void rtsp_cmd_play(HTTPContext *c, const char *url,
 +                          RTSPMessageHeader *h);
 +static void rtsp_cmd_interrupt(HTTPContext *c, const char *url,
 +                               RTSPMessageHeader *h, int pause_only);
 +
 +/* SDP handling */
 +static int prepare_sdp_description(FFServerStream *stream, uint8_t **pbuffer,
 +                                   struct in_addr my_ip);
 +
 +/* RTP handling */
 +static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
 +                                       FFServerStream *stream,
 +                                       const char *session_id,
 +                                       enum RTSPLowerTransport rtp_protocol);
 +static int rtp_new_av_stream(HTTPContext *c,
 +                             int stream_index, struct sockaddr_in *dest_addr,
 +                             HTTPContext *rtsp_c);
 +/* utils */
 +static size_t htmlencode (const char *src, char **dest);
 +static inline void cp_html_entity (char *buffer, const char *entity);
 +static inline int check_codec_match(LayeredAVStream *ccf, AVStream *ccs, int stream);
 +
 +static const char *my_program_name;
 +
 +static int no_launch;
 +static int need_to_start_children;
 +
 +/* maximum number of simultaneous HTTP connections */
 +static unsigned int nb_connections;
 +
 +static uint64_t current_bandwidth;
 +
 +/* Making this global saves on passing it around everywhere */
 +static int64_t cur_time;
 +
 +static AVLFG random_state;
 +
 +static FILE *logfile = NULL;
 +
 +static void unlayer_stream(AVStream *st, LayeredAVStream *lst)
 +{
 +    avcodec_free_context(&st->codec);
 +    avcodec_parameters_free(&st->codecpar);
 +#define COPY(a) st->a = lst->a;
 +    COPY(index)
 +    COPY(id)
 +    COPY(codec)
 +    COPY(codecpar)
 +    COPY(time_base)
 +    COPY(pts_wrap_bits)
 +    COPY(sample_aspect_ratio)
 +    COPY(recommended_encoder_configuration)
 +}
 +
 +static inline void cp_html_entity (char *buffer, const char *entity) {
 +    if (!buffer || !entity)
 +        return;
 +    while (*entity)
 +        *buffer++ = *entity++;
 +}
 +
 +/**
 + * Substitutes known conflicting chars on a text string with
 + * their corresponding HTML entities.
 + *
 + * Returns the number of bytes in the 'encoded' representation
 + * not including the terminating NUL.
 + */
 +static size_t htmlencode (const char *src, char **dest) {
 +    const char *amp = "&amp;";
 +    const char *lt  = "&lt;";
 +    const char *gt  = "&gt;";
 +    const char *start;
 +    char *tmp;
 +    size_t final_size = 0;
 +
 +    if (!src)
 +        return 0;
 +
 +    start = src;
 +
 +    /* Compute needed dest size */
 +    while (*src != '\0') {
 +        switch(*src) {
 +            case 38: /* & */
 +                final_size += 5;
 +                break;
 +            case 60: /* < */
 +            case 62: /* > */
 +                final_size += 4;
 +                break;
 +            default:
 +                final_size++;
 +        }
 +        src++;
 +    }
 +
 +    src = start;
 +    *dest = av_mallocz(final_size + 1);
 +    if (!*dest)
 +        return 0;
 +
 +    /* Build dest */
 +    tmp = *dest;
 +    while (*src != '\0') {
 +        switch(*src) {
 +            case 38: /* & */
 +                cp_html_entity (tmp, amp);
 +                tmp += 5;
 +                break;
 +            case 60: /* < */
 +                cp_html_entity (tmp, lt);
 +                tmp += 4;
 +                break;
 +            case 62: /* > */
 +                cp_html_entity (tmp, gt);
 +                tmp += 4;
 +                break;
 +            default:
 +                *tmp = *src;
 +                tmp += 1;
 +        }
 +        src++;
 +    }
 +    *tmp = '\0';
 +
 +    return final_size;
 +}
 +
 +static int64_t ffm_read_write_index(int fd)
 +{
 +    uint8_t buf[8];
 +
 +    if (lseek(fd, 8, SEEK_SET) < 0)
 +        return AVERROR(EIO);
 +    if (read(fd, buf, 8) != 8)
 +        return AVERROR(EIO);
 +    return AV_RB64(buf);
 +}
 +
 +static int ffm_write_write_index(int fd, int64_t pos)
 +{
 +    uint8_t buf[8];
 +    int i;
 +
 +    for(i=0;i<8;i++)
 +        buf[i] = (pos >> (56 - i * 8)) & 0xff;
 +    if (lseek(fd, 8, SEEK_SET) < 0)
 +        goto bail_eio;
 +    if (write(fd, buf, 8) != 8)
 +        goto bail_eio;
 +
 +    return 8;
 +
 +bail_eio:
 +    return AVERROR(EIO);
 +}
 +
 +static void ffm_set_write_index(AVFormatContext *s, int64_t pos,
 +                                int64_t file_size)
 +{
 +    av_opt_set_int(s, "server_attached", 1, AV_OPT_SEARCH_CHILDREN);
 +    av_opt_set_int(s, "ffm_write_index", pos, AV_OPT_SEARCH_CHILDREN);
 +    av_opt_set_int(s, "ffm_file_size", file_size, AV_OPT_SEARCH_CHILDREN);
 +}
 +
 +static char *ctime1(char *buf2, size_t buf_size)
 +{
 +    time_t ti;
 +    char *p;
 +
 +    ti = time(NULL);
 +    p = ctime(&ti);
 +    if (!p || !*p) {
 +        *buf2 = '\0';
 +        return buf2;
 +    }
 +    av_strlcpy(buf2, p, buf_size);
 +    p = buf2 + strlen(buf2) - 1;
 +    if (*p == '\n')
 +        *p = '\0';
 +    return buf2;
 +}
 +
 +static void http_vlog(const char *fmt, va_list vargs)
 +{
 +    static int print_prefix = 1;
 +    char buf[32];
 +
 +    if (!logfile)
 +        return;
 +
 +    if (print_prefix) {
 +        ctime1(buf, sizeof(buf));
 +        fprintf(logfile, "%s ", buf);
 +    }
 +    print_prefix = strstr(fmt, "\n") != NULL;
 +    vfprintf(logfile, fmt, vargs);
 +    fflush(logfile);
 +}
 +
 +#ifdef __GNUC__
 +__attribute__ ((format (printf, 1, 2)))
 +#endif
 +static void http_log(const char *fmt, ...)
 +{
 +    va_list vargs;
 +    va_start(vargs, fmt);
 +    http_vlog(fmt, vargs);
 +    va_end(vargs);
 +}
 +
 +static void http_av_log(void *ptr, int level, const char *fmt, va_list vargs)
 +{
 +    static int print_prefix = 1;
 +    AVClass *avc = ptr ? *(AVClass**)ptr : NULL;
 +    if (level > av_log_get_level())
 +        return;
 +    if (print_prefix && avc)
 +        http_log("[%s @ %p]", avc->item_name(ptr), ptr);
 +    print_prefix = strstr(fmt, "\n") != NULL;
 +    http_vlog(fmt, vargs);
 +}
 +
 +static void log_connection(HTTPContext *c)
 +{
 +    if (c->suppress_log)
 +        return;
 +
 +    http_log("%s - - [%s] \"%s %s\" %d %"PRId64"\n",
 +             inet_ntoa(c->from_addr.sin_addr), c->method, c->url,
 +             c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
 +}
 +
 +static void update_datarate(DataRateData *drd, int64_t count)
 +{
 +    if (!drd->time1 && !drd->count1) {
 +        drd->time1 = drd->time2 = cur_time;
 +        drd->count1 = drd->count2 = count;
 +    } else if (cur_time - drd->time2 > 5000) {
 +        drd->time1 = drd->time2;
 +        drd->count1 = drd->count2;
 +        drd->time2 = cur_time;
 +        drd->count2 = count;
 +    }
 +}
 +
 +/* In bytes per second */
 +static int compute_datarate(DataRateData *drd, int64_t count)
 +{
 +    if (cur_time == drd->time1)
 +        return 0;
 +
 +    return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
 +}
 +
 +
 +static void start_children(FFServerStream *feed)
 +{
 +    char *pathname;
 +    char *slash;
 +    int i;
 +    size_t cmd_length;
 +
 +    if (no_launch)
 +        return;
 +
 +    cmd_length = strlen(my_program_name);
 +
 +   /**
 +    * FIXME: WIP Safeguard. Remove after clearing all harcoded
 +    * '1024' path lengths
 +    */
 +    if (cmd_length > PATH_LENGTH - 1) {
 +        http_log("Could not start children. Command line: '%s' exceeds "
 +                    "path length limit (%d)\n", my_program_name, PATH_LENGTH);
 +        return;
 +    }
 +
 +    slash = strrchr(my_program_name, '/');
 +    if (!slash) {
 +        pathname = av_mallocz(sizeof("ffmpeg"));
 +    } else {
 +        pathname = av_mallocz(slash - my_program_name + sizeof("ffmpeg"));
 +        if (pathname != NULL) {
 +            memcpy(pathname, my_program_name, slash - my_program_name);
 +        }
 +    }
 +    if (!pathname) {
 +        http_log("Could not allocate memory for children cmd line\n");
 +        return;
 +    }
 +   /* use "ffmpeg" in the path of current program. Ignore user provided path */
 +
 +    strcat(pathname, "ffmpeg");
 +
 +    for (; feed; feed = feed->next) {
 +
 +        if (!feed->child_argv || feed->pid)
 +            continue;
 +
 +        feed->pid_start = time(0);
 +
 +        feed->pid = fork();
 +        if (feed->pid < 0) {
 +            http_log("Unable to create children: %s\n", strerror(errno));
 +            av_free (pathname);
 +            exit(EXIT_FAILURE);
 +        }
 +
 +        if (feed->pid)
 +            continue;
 +
 +        /* In child */
 +
 +        http_log("Launch command line: ");
 +        http_log("%s ", pathname);
 +
 +        for (i = 1; feed->child_argv[i] && feed->child_argv[i][0]; i++)
 +            http_log("%s ", feed->child_argv[i]);
 +        http_log("\n");
 +
 +        for (i = 3; i < 256; i++)
 +            close(i);
 +
 +        if (!config.debug) {
 +            if (!freopen("/dev/null", "r", stdin))
 +                http_log("failed to redirect STDIN to /dev/null\n;");
 +            if (!freopen("/dev/null", "w", stdout))
 +                http_log("failed to redirect STDOUT to /dev/null\n;");
 +            if (!freopen("/dev/null", "w", stderr))
 +                http_log("failed to redirect STDERR to /dev/null\n;");
 +        }
 +
 +        signal(SIGPIPE, SIG_DFL);
 +        execvp(pathname, feed->child_argv);
 +        av_free (pathname);
 +        _exit(1);
 +    }
 +    av_free (pathname);
 +}
 +
 +/* open a listening socket */
 +static int socket_open_listen(struct sockaddr_in *my_addr)
 +{
 +    int server_fd, tmp;
 +
 +    server_fd = socket(AF_INET,SOCK_STREAM,0);
 +    if (server_fd < 0) {
 +        perror ("socket");
 +        return -1;
 +    }
 +
 +    tmp = 1;
 +    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp)))
 +        av_log(NULL, AV_LOG_WARNING, "setsockopt SO_REUSEADDR failed\n");
 +
 +    my_addr->sin_family = AF_INET;
 +    if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
 +        char bindmsg[32];
 +        snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)",
 +                 ntohs(my_addr->sin_port));
 +        perror (bindmsg);
 +        goto fail;
 +    }
 +
 +    if (listen (server_fd, 5) < 0) {
 +        perror ("listen");
 +        goto fail;
 +    }
 +
 +    if (ff_socket_nonblock(server_fd, 1) < 0)
 +        av_log(NULL, AV_LOG_WARNING, "ff_socket_nonblock failed\n");
 +
 +    return server_fd;
 +
 +fail:
 +    closesocket(server_fd);
 +    return -1;
 +}
 +
 +/* start all multicast streams */
 +static void start_multicast(void)
 +{
 +    FFServerStream *stream;
 +    char session_id[32];
 +    HTTPContext *rtp_c;
 +    struct sockaddr_in dest_addr = {0};
 +    int default_port, stream_index;
 +    unsigned int random0, random1;
 +
 +    default_port = 6000;
 +    for(stream = config.first_stream; stream; stream = stream->next) {
 +
 +        if (!stream->is_multicast)
 +            continue;
 +
 +        random0 = av_lfg_get(&random_state);
 +        random1 = av_lfg_get(&random_state);
 +
 +        /* open the RTP connection */
 +        snprintf(session_id, sizeof(session_id), "%08x%08x", random0, random1);
 +
 +        /* choose a port if none given */
 +        if (stream->multicast_port == 0) {
 +            stream->multicast_port = default_port;
 +            default_port += 100;
 +        }
 +
 +        dest_addr.sin_family = AF_INET;
 +        dest_addr.sin_addr = stream->multicast_ip;
 +        dest_addr.sin_port = htons(stream->multicast_port);
 +
 +        rtp_c = rtp_new_connection(&dest_addr, stream, session_id,
 +                                   RTSP_LOWER_TRANSPORT_UDP_MULTICAST);
 +        if (!rtp_c)
 +            continue;
 +
 +        if (open_input_stream(rtp_c, "") < 0) {
 +            http_log("Could not open input stream for stream '%s'\n",
 +                     stream->filename);
 +            continue;
 +        }
 +
 +        /* open each RTP stream */
 +        for(stream_index = 0; stream_index < stream->nb_streams;
 +            stream_index++) {
 +            dest_addr.sin_port = htons(stream->multicast_port +
 +                                       2 * stream_index);
 +            if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) >= 0)
 +                continue;
 +
 +            http_log("Could not open output stream '%s/streamid=%d'\n",
 +                     stream->filename, stream_index);
 +            exit(1);
 +        }
 +
 +        rtp_c->state = HTTPSTATE_SEND_DATA;
 +    }
 +}
 +
 +/* main loop of the HTTP server */
 +static int http_server(void)
 +{
 +    int server_fd = 0, rtsp_server_fd = 0;
 +    int ret, delay;
 +    struct pollfd *poll_table, *poll_entry;
 +    HTTPContext *c, *c_next;
 +
 +    poll_table = av_mallocz_array(config.nb_max_http_connections + 2,
 +                                  sizeof(*poll_table));
 +    if(!poll_table) {
 +        http_log("Impossible to allocate a poll table handling %d "
 +                 "connections.\n", config.nb_max_http_connections);
 +        return -1;
 +    }
 +
 +    if (config.http_addr.sin_port) {
 +        server_fd = socket_open_listen(&config.http_addr);
 +        if (server_fd < 0)
 +            goto quit;
 +    }
 +
 +    if (config.rtsp_addr.sin_port) {
 +        rtsp_server_fd = socket_open_listen(&config.rtsp_addr);
 +        if (rtsp_server_fd < 0) {
 +            closesocket(server_fd);
 +            goto quit;
 +        }
 +    }
 +
 +    if (!rtsp_server_fd && !server_fd) {
 +        http_log("HTTP and RTSP disabled.\n");
 +        goto quit;
 +    }
 +
 +    http_log("FFserver started.\n");
 +
 +    start_children(config.first_feed);
 +
 +    start_multicast();
 +
 +    for(;;) {
 +        poll_entry = poll_table;
 +        if (server_fd) {
 +            poll_entry->fd = server_fd;
 +            poll_entry->events = POLLIN;
 +            poll_entry++;
 +        }
 +        if (rtsp_server_fd) {
 +            poll_entry->fd = rtsp_server_fd;
 +            poll_entry->events = POLLIN;
 +            poll_entry++;
 +        }
 +
 +        /* wait for events on each HTTP handle */
 +        c = first_http_ctx;
 +        delay = 1000;
 +        while (c) {
 +            int fd;
 +            fd = c->fd;
 +            switch(c->state) {
 +            case HTTPSTATE_SEND_HEADER:
 +            case RTSPSTATE_SEND_REPLY:
 +            case RTSPSTATE_SEND_PACKET:
 +                c->poll_entry = poll_entry;
 +                poll_entry->fd = fd;
 +                poll_entry->events = POLLOUT;
 +                poll_entry++;
 +                break;
 +            case HTTPSTATE_SEND_DATA_HEADER:
 +            case HTTPSTATE_SEND_DATA:
 +            case HTTPSTATE_SEND_DATA_TRAILER:
 +                if (!c->is_packetized) {
 +                    /* for TCP, we output as much as we can
 +                     * (may need to put a limit) */
 +                    c->poll_entry = poll_entry;
 +                    poll_entry->fd = fd;
 +                    poll_entry->events = POLLOUT;
 +                    poll_entry++;
 +                } else {
 +                    /* when ffserver is doing the timing, we work by
 +                     * looking at which packet needs to be sent every
 +                     * 10 ms (one tick wait XXX: 10 ms assumed) */
 +                    if (delay > 10)
 +                        delay = 10;
 +                }
 +                break;
 +            case HTTPSTATE_WAIT_REQUEST:
 +            case HTTPSTATE_RECEIVE_DATA:
 +            case HTTPSTATE_WAIT_FEED:
 +            case RTSPSTATE_WAIT_REQUEST:
 +                /* need to catch errors */
 +                c->poll_entry = poll_entry;
 +                poll_entry->fd = fd;
 +                poll_entry->events = POLLIN;/* Maybe this will work */
 +                poll_entry++;
 +                break;
 +            default:
 +                c->poll_entry = NULL;
 +                break;
 +            }
 +            c = c->next;
 +        }
 +
 +        /* wait for an event on one connection. We poll at least every
 +         * second to handle timeouts */
 +        do {
 +            ret = poll(poll_table, poll_entry - poll_table, delay);
 +            if (ret < 0 && ff_neterrno() != AVERROR(EAGAIN) &&
 +                ff_neterrno() != AVERROR(EINTR)) {
 +                goto quit;
 +            }
 +        } while (ret < 0);
 +
 +        cur_time = av_gettime() / 1000;
 +
 +        if (need_to_start_children) {
 +            need_to_start_children = 0;
 +            start_children(config.first_feed);
 +        }
 +
 +        /* now handle the events */
 +        for(c = first_http_ctx; c; c = c_next) {
 +            c_next = c->next;
 +            if (handle_connection(c) < 0) {
 +                log_connection(c);
 +                /* close and free the connection */
 +                close_connection(c);
 +            }
 +        }
 +
 +        poll_entry = poll_table;
 +        if (server_fd) {
 +            /* new HTTP connection request ? */
 +            if (poll_entry->revents & POLLIN)
 +                new_connection(server_fd, 0);
 +            poll_entry++;
 +        }
 +        if (rtsp_server_fd) {
 +            /* new RTSP connection request ? */
 +            if (poll_entry->revents & POLLIN)
 +                new_connection(rtsp_server_fd, 1);
 +        }
 +    }
 +
 +quit:
 +    av_free(poll_table);
 +    return -1;
 +}
 +
 +/* start waiting for a new HTTP/RTSP request */
 +static void start_wait_request(HTTPContext *c, int is_rtsp)
 +{
 +    c->buffer_ptr = c->buffer;
 +    c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
 +
 +    c->state = is_rtsp ? RTSPSTATE_WAIT_REQUEST : HTTPSTATE_WAIT_REQUEST;
 +    c->timeout = cur_time +
 +                 (is_rtsp ? RTSP_REQUEST_TIMEOUT : HTTP_REQUEST_TIMEOUT);
 +}
 +
 +static void http_send_too_busy_reply(int fd)
 +{
 +    char buffer[400];
 +    int len = snprintf(buffer, sizeof(buffer),
 +                       "HTTP/1.0 503 Server too busy\r\n"
 +                       "Content-type: text/html\r\n"
 +                       "\r\n"
 +                       "<!DOCTYPE html>\n"
 +                       "<html><head><title>Too busy</title></head><body>\r\n"
 +                       "<p>The server is too busy to serve your request at "
 +                       "this time.</p>\r\n"
 +                       "<p>The number of current connections is %u, and this "
 +                       "exceeds the limit of %u.</p>\r\n"
 +                       "</body></html>\r\n",
 +                       nb_connections, config.nb_max_connections);
 +    av_assert0(len < sizeof(buffer));
 +    if (send(fd, buffer, len, 0) < len)
 +        av_log(NULL, AV_LOG_WARNING,
 +               "Could not send too-busy reply, send() failed\n");
 +}
 +
 +
 +static void new_connection(int server_fd, int is_rtsp)
 +{
 +    struct sockaddr_in from_addr;
 +    socklen_t len;
 +    int fd;
 +    HTTPContext *c = NULL;
 +
 +    len = sizeof(from_addr);
 +    fd = accept(server_fd, (struct sockaddr *)&from_addr,
 +                &len);
 +    if (fd < 0) {
 +        http_log("error during accept %s\n", strerror(errno));
 +        return;
 +    }
 +    if (ff_socket_nonblock(fd, 1) < 0)
 +        av_log(NULL, AV_LOG_WARNING, "ff_socket_nonblock failed\n");
 +
 +    if (nb_connections >= config.nb_max_connections) {
 +        http_send_too_busy_reply(fd);
 +        goto fail;
 +    }
 +
 +    /* add a new connection */
 +    c = av_mallocz(sizeof(HTTPContext));
 +    if (!c)
 +        goto fail;
 +
 +    c->fd = fd;
 +    c->poll_entry = NULL;
 +    c->from_addr = from_addr;
 +    c->buffer_size = IOBUFFER_INIT_SIZE;
 +    c->buffer = av_malloc(c->buffer_size);
 +    if (!c->buffer)
 +        goto fail;
 +
 +    c->next = first_http_ctx;
 +    first_http_ctx = c;
 +    nb_connections++;
 +
 +    start_wait_request(c, is_rtsp);
 +
 +    return;
 +
 + fail:
 +    if (c) {
 +        av_freep(&c->buffer);
 +        av_free(c);
 +    }
 +    closesocket(fd);
 +}
 +
 +static void close_connection(HTTPContext *c)
 +{
 +    HTTPContext **cp, *c1;
 +    int i, nb_streams;
 +    AVFormatContext *ctx;
 +    AVStream *st;
 +
 +    /* remove connection from list */
 +    cp = &first_http_ctx;
 +    while (*cp) {
 +        c1 = *cp;
 +        if (c1 == c)
 +            *cp = c->next;
 +        else
 +            cp = &c1->next;
 +    }
 +
 +    /* remove references, if any (XXX: do it faster) */
 +    for(c1 = first_http_ctx; c1; c1 = c1->next) {
 +        if (c1->rtsp_c == c)
 +            c1->rtsp_c = NULL;
 +    }
 +
 +    /* remove connection associated resources */
 +    if (c->fd >= 0)
 +        closesocket(c->fd);
 +    if (c->fmt_in) {
 +        /* close each frame parser */
 +        for(i=0;i<c->fmt_in->nb_streams;i++) {
 +            st = c->fmt_in->streams[i];
 +            if (st->codec->codec)
 +                avcodec_close(st->codec);
 +        }
 +        avformat_close_input(&c->fmt_in);
 +    }
 +
 +    /* free RTP output streams if any */
 +    nb_streams = 0;
 +    if (c->stream)
 +        nb_streams = c->stream->nb_streams;
 +
 +    for(i=0;i<nb_streams;i++) {
 +        ctx = c->rtp_ctx[i];
 +        if (ctx) {
 +            av_write_trailer(ctx);
 +            av_dict_free(&ctx->metadata);
 +            av_freep(&ctx->streams[0]);
 +            av_freep(&ctx);
 +        }
 +        ffurl_close(c->rtp_handles[i]);
 +    }
 +
 +    ctx = c->pfmt_ctx;
 +
 +    if (ctx) {
 +        if (!c->last_packet_sent && c->state == HTTPSTATE_SEND_DATA_TRAILER) {
 +            /* prepare header */
 +            if (ctx->oformat && avio_open_dyn_buf(&ctx->pb) >= 0) {
 +                av_write_trailer(ctx);
 +                av_freep(&c->pb_buffer);
 +                avio_close_dyn_buf(ctx->pb, &c->pb_buffer);
 +            }
 +        }
 +        for(i=0; i<ctx->nb_streams; i++)
 +            av_freep(&ctx->streams[i]);
 +        av_freep(&ctx->streams);
 +        av_freep(&ctx->priv_data);
 +        }
 +
 +    if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE)
 +        current_bandwidth -= c->stream->bandwidth;
 +
 +    /* signal that there is no feed if we are the feeder socket */
 +    if (c->state == HTTPSTATE_RECEIVE_DATA && c->stream) {
 +        c->stream->feed_opened = 0;
 +        close(c->feed_fd);
 +    }
 +
 +    av_freep(&c->pb_buffer);
 +    av_freep(&c->packet_buffer);
 +    av_freep(&c->buffer);
 +    av_free(c);
 +    nb_connections--;
 +}
 +
 +static int handle_connection(HTTPContext *c)
 +{
 +    int len, ret;
 +    uint8_t *ptr;
 +
 +    switch(c->state) {
 +    case HTTPSTATE_WAIT_REQUEST:
 +    case RTSPSTATE_WAIT_REQUEST:
 +        /* timeout ? */
 +        if ((c->timeout - cur_time) < 0)
 +            return -1;
 +        if (c->poll_entry->revents & (POLLERR | POLLHUP))
 +            return -1;
 +
 +        /* no need to read if no events */
 +        if (!(c->poll_entry->revents & POLLIN))
 +            return 0;
 +        /* read the data */
 +    read_loop:
 +        if (!(len = recv(c->fd, c->buffer_ptr, 1, 0)))
 +            return -1;
 +
 +        if (len < 0) {
 +            if (ff_neterrno() != AVERROR(EAGAIN) &&
 +                ff_neterrno() != AVERROR(EINTR))
 +                return -1;
 +            break;
 +        }
 +        /* search for end of request. */
 +        c->buffer_ptr += len;
 +        ptr = c->buffer_ptr;
 +        if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
 +            (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
 +            /* request found : parse it and reply */
 +            if (c->state == HTTPSTATE_WAIT_REQUEST)
 +                ret = http_parse_request(c);
 +            else
 +                ret = rtsp_parse_request(c);
 +
 +            if (ret < 0)
 +                return -1;
 +        } else if (ptr >= c->buffer_end) {
 +            /* request too long: cannot do anything */
 +            return -1;
 +        } else goto read_loop;
 +
 +        break;
 +
 +    case HTTPSTATE_SEND_HEADER:
 +        if (c->poll_entry->revents & (POLLERR | POLLHUP))
 +            return -1;
 +
 +        /* no need to write if no events */
 +        if (!(c->poll_entry->revents & POLLOUT))
 +            return 0;
 +        len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
 +        if (len < 0) {
 +            if (ff_neterrno() != AVERROR(EAGAIN) &&
 +                ff_neterrno() != AVERROR(EINTR)) {
 +                goto close_connection;
 +            }
 +            break;
 +        }
 +        c->buffer_ptr += len;
 +        if (c->stream)
 +            c->stream->bytes_served += len;
 +        c->data_count += len;
 +        if (c->buffer_ptr >= c->buffer_end) {
 +            av_freep(&c->pb_buffer);
 +            /* if error, exit */
 +            if (c->http_error)
 +                return -1;
 +            /* all the buffer was sent : synchronize to the incoming
 +             * stream */
 +            c->state = HTTPSTATE_SEND_DATA_HEADER;
 +            c->buffer_ptr = c->buffer_end = c->buffer;
 +        }
 +        break;
 +
 +    case HTTPSTATE_SEND_DATA:
 +    case HTTPSTATE_SEND_DATA_HEADER:
 +    case HTTPSTATE_SEND_DATA_TRAILER:
 +        /* for packetized output, we consider we can always write (the
 +         * input streams set the speed). It may be better to verify
 +         * that we do not rely too much on the kernel queues */
 +        if (!c->is_packetized) {
 +            if (c->poll_entry->revents & (POLLERR | POLLHUP))
 +                return -1;
 +
 +            /* no need to read if no events */
 +            if (!(c->poll_entry->revents & POLLOUT))
 +                return 0;
 +        }
 +        if (http_send_data(c) < 0)
 +            return -1;
 +        /* close connection if trailer sent */
 +        if (c->state == HTTPSTATE_SEND_DATA_TRAILER)
 +            return -1;
 +        /* Check if it is a single jpeg frame 123 */
 +        if (c->stream->single_frame && c->data_count > c->cur_frame_bytes && c->cur_frame_bytes > 0) {
 +            close_connection(c);
 +        }
 +        break;
 +    case HTTPSTATE_RECEIVE_DATA:
 +        /* no need to read if no events */
 +        if (c->poll_entry->revents & (POLLERR | POLLHUP))
 +            return -1;
 +        if (!(c->poll_entry->revents & POLLIN))
 +            return 0;
 +        if (http_receive_data(c) < 0)
 +            return -1;
 +        break;
 +    case HTTPSTATE_WAIT_FEED:
 +        /* no need to read if no events */
 +        if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
 +            return -1;
 +
 +        /* nothing to do, we'll be waken up by incoming feed packets */
 +        break;
 +
 +    case RTSPSTATE_SEND_REPLY:
 +        if (c->poll_entry->revents & (POLLERR | POLLHUP))
 +            goto close_connection;
 +        /* no need to write if no events */
 +        if (!(c->poll_entry->revents & POLLOUT))
 +            return 0;
 +        len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
 +        if (len < 0) {
 +            if (ff_neterrno() != AVERROR(EAGAIN) &&
 +                ff_neterrno() != AVERROR(EINTR)) {
 +                goto close_connection;
 +            }
 +            break;
 +        }
 +        c->buffer_ptr += len;
 +        c->data_count += len;
 +        if (c->buffer_ptr >= c->buffer_end) {
 +            /* all the buffer was sent : wait for a new request */
 +            av_freep(&c->pb_buffer);
 +            start_wait_request(c, 1);
 +        }
 +        break;
 +    case RTSPSTATE_SEND_PACKET:
 +        if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
 +            av_freep(&c->packet_buffer);
 +            return -1;
 +        }
 +        /* no need to write if no events */
 +        if (!(c->poll_entry->revents & POLLOUT))
 +            return 0;
 +        len = send(c->fd, c->packet_buffer_ptr,
 +                    c->packet_buffer_end - c->packet_buffer_ptr, 0);
 +        if (len < 0) {
 +            if (ff_neterrno() != AVERROR(EAGAIN) &&
 +                ff_neterrno() != AVERROR(EINTR)) {
 +                /* error : close connection */
 +                av_freep(&c->packet_buffer);
 +                return -1;
 +            }
 +            break;
 +        }
 +        c->packet_buffer_ptr += len;
 +        if (c->packet_buffer_ptr >= c->packet_buffer_end) {
 +            /* all the buffer was sent : wait for a new request */
 +            av_freep(&c->packet_buffer);
 +            c->state = RTSPSTATE_WAIT_REQUEST;
 +        }
 +        break;
 +    case HTTPSTATE_READY:
 +        /* nothing to do */
 +        break;
 +    default:
 +        return -1;
 +    }
 +    return 0;
 +
 +close_connection:
 +    av_freep(&c->pb_buffer);
 +    return -1;
 +}
 +
 +static int extract_rates(char *rates, int ratelen, const char *request)
 +{
 +    const char *p;
 +
 +    for (p = request; *p && *p != '\r' && *p != '\n'; ) {
 +        if (av_strncasecmp(p, "Pragma:", 7) == 0) {
 +            const char *q = p + 7;
 +
 +            while (*q && *q != '\n' && av_isspace(*q))
 +                q++;
 +
 +            if (av_strncasecmp(q, "stream-switch-entry=", 20) == 0) {
 +                int stream_no;
 +                int rate_no;
 +
 +                q += 20;
 +
 +                memset(rates, 0xff, ratelen);
 +
 +                while (1) {
 +                    while (*q && *q != '\n' && *q != ':')
 +                        q++;
 +
 +                    if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2)
 +                        break;
 +
 +                    stream_no--;
 +                    if (stream_no < ratelen && stream_no >= 0)
 +                        rates[stream_no] = rate_no;
 +
 +                    while (*q && *q != '\n' && !av_isspace(*q))
 +                        q++;
 +                }
 +
 +                return 1;
 +            }
 +        }
 +        p = strchr(p, '\n');
 +        if (!p)
 +            break;
 +
 +        p++;
 +    }
 +
 +    return 0;
 +}
 +
 +static int find_stream_in_feed(FFServerStream *feed, AVCodecParameters *codec,
 +                               int bit_rate)
 +{
 +    int i;
 +    int best_bitrate = 100000000;
 +    int best = -1;
 +
 +    for (i = 0; i < feed->nb_streams; i++) {
 +        AVCodecParameters *feed_codec = feed->streams[i]->codecpar;
 +
 +        if (feed_codec->codec_id != codec->codec_id ||
 +            feed_codec->sample_rate != codec->sample_rate ||
 +            feed_codec->width != codec->width ||
 +            feed_codec->height != codec->height)
 +            continue;
 +
 +        /* Potential stream */
 +
 +        /* We want the fastest stream less than bit_rate, or the slowest
 +         * faster than bit_rate
 +         */
 +
 +        if (feed_codec->bit_rate <= bit_rate) {
 +            if (best_bitrate > bit_rate ||
 +                feed_codec->bit_rate > best_bitrate) {
 +                best_bitrate = feed_codec->bit_rate;
 +                best = i;
 +            }
 +            continue;
 +        }
 +        if (feed_codec->bit_rate < best_bitrate) {
 +            best_bitrate = feed_codec->bit_rate;
 +            best = i;
 +        }
 +    }
 +    return best;
 +}
 +
 +static int modify_current_stream(HTTPContext *c, char *rates)
 +{
 +    int i;
 +    FFServerStream *req = c->stream;
 +    int action_required = 0;
 +
 +    /* Not much we can do for a feed */
 +    if (!req->feed)
 +        return 0;
 +
 +    for (i = 0; i < req->nb_streams; i++) {
 +        AVCodecParameters *codec = req->streams[i]->codecpar;
 +
 +        switch(rates[i]) {
 +            case 0:
 +                c->switch_feed_streams[i] = req->feed_streams[i];
 +                break;
 +            case 1:
 +                c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
 +                break;
 +            case 2:
 +                /* Wants off or slow */
 +                c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
 +#ifdef WANTS_OFF
 +                /* This doesn't work well when it turns off the only stream! */
 +                c->switch_feed_streams[i] = -2;
 +                c->feed_streams[i] = -2;
 +#endif
 +                break;
 +        }
 +
 +        if (c->switch_feed_streams[i] >= 0 &&
 +            c->switch_feed_streams[i] != c->feed_streams[i]) {
 +            action_required = 1;
 +        }
 +    }
 +
 +    return action_required;
 +}
 +
 +static void get_word(char *buf, int buf_size, const char **pp)
 +{
 +    const char *p;
 +    char *q;
 +
 +#define SPACE_CHARS " \t\r\n"
 +
 +    p = *pp;
 +    p += strspn(p, SPACE_CHARS);
 +    q = buf;
 +    while (!av_isspace(*p) && *p != '\0') {
 +        if ((q - buf) < buf_size - 1)
 +            *q++ = *p;
 +        p++;
 +    }
 +    if (buf_size > 0)
 +        *q = '\0';
 +    *pp = p;
 +}
 +
 +static FFServerIPAddressACL* parse_dynamic_acl(FFServerStream *stream,
 +                                               HTTPContext *c)
 +{
 +    FILE* f;
 +    char line[1024];
 +    char  cmd[1024];
 +    FFServerIPAddressACL *acl = NULL;
 +    int line_num = 0;
 +    const char *p;
 +
 +    f = fopen(stream->dynamic_acl, "r");
 +    if (!f) {
 +        perror(stream->dynamic_acl);
 +        return NULL;
 +    }
 +
 +    acl = av_mallocz(sizeof(FFServerIPAddressACL));
 +    if (!acl) {
 +        fclose(f);
 +        return NULL;
 +    }
 +
 +    /* Build ACL */
 +    while (fgets(line, sizeof(line), f)) {
 +        line_num++;
 +        p = line;
 +        while (av_isspace(*p))
 +            p++;
 +        if (*p == '\0' || *p == '#')
 +            continue;
 +        ffserver_get_arg(cmd, sizeof(cmd), &p);
 +
 +        if (!av_strcasecmp(cmd, "ACL"))
 +            ffserver_parse_acl_row(NULL, NULL, acl, p, stream->dynamic_acl,
 +                                   line_num);
 +    }
 +    fclose(f);
 +    return acl;
 +}
 +
 +
 +static void free_acl_list(FFServerIPAddressACL *in_acl)
 +{
 +    FFServerIPAddressACL *pacl, *pacl2;
 +
 +    pacl = in_acl;
 +    while(pacl) {
 +        pacl2 = pacl;
 +        pacl = pacl->next;
 +        av_freep(pacl2);
 +    }
 +}
 +
 +static int validate_acl_list(FFServerIPAddressACL *in_acl, HTTPContext *c)
 +{
 +    enum FFServerIPAddressAction last_action = IP_DENY;
 +    FFServerIPAddressACL *acl;
 +    struct in_addr *src = &c->from_addr.sin_addr;
 +    unsigned long src_addr = src->s_addr;
 +
 +    for (acl = in_acl; acl; acl = acl->next) {
 +        if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr)
 +            return (acl->action == IP_ALLOW) ? 1 : 0;
 +        last_action = acl->action;
 +    }
 +
 +    /* Nothing matched, so return not the last action */
 +    return (last_action == IP_DENY) ? 1 : 0;
 +}
 +
 +static int validate_acl(FFServerStream *stream, HTTPContext *c)
 +{
 +    int ret = 0;
 +    FFServerIPAddressACL *acl;
 +
 +    /* if stream->acl is null validate_acl_list will return 1 */
 +    ret = validate_acl_list(stream->acl, c);
 +
 +    if (stream->dynamic_acl[0]) {
 +        acl = parse_dynamic_acl(stream, c);
 +        ret = validate_acl_list(acl, c);
 +        free_acl_list(acl);
 +    }
 +
 +    return ret;
 +}
 +
 +/**
 + * compute the real filename of a file by matching it without its
 + * extensions to all the stream's filenames
 + */
 +static void compute_real_filename(char *filename, int max_size)
 +{
 +    char file1[1024];
 +    char file2[1024];
 +    char *p;
 +    FFServerStream *stream;
 +
 +    av_strlcpy(file1, filename, sizeof(file1));
 +    p = strrchr(file1, '.');
 +    if (p)
 +        *p = '\0';
 +    for(stream = config.first_stream; stream; stream = stream->next) {
 +        av_strlcpy(file2, stream->filename, sizeof(file2));
 +        p = strrchr(file2, '.');
 +        if (p)
 +            *p = '\0';
 +        if (!strcmp(file1, file2)) {
 +            av_strlcpy(filename, stream->filename, max_size);
 +            break;
 +        }
 +    }
 +}
 +
 +enum RedirType {
 +    REDIR_NONE,
 +    REDIR_ASX,
 +    REDIR_RAM,
 +    REDIR_ASF,
 +    REDIR_RTSP,
 +    REDIR_SDP,
 +};
 +
 +/* parse HTTP request and prepare header */
 +static int http_parse_request(HTTPContext *c)
 +{
 +    const char *p;
 +    char *p1;
 +    enum RedirType redir_type;
 +    char cmd[32];
 +    char info[1024], filename[1024];
 +    char url[1024], *q;
 +    char protocol[32];
 +    char msg[1024];
 +    char *encoded_msg = NULL;
 +    const char *mime_type;
 +    FFServerStream *stream;
 +    int i;
 +    char ratebuf[32];
 +    const char *useragent = 0;
 +
 +    p = c->buffer;
 +    get_word(cmd, sizeof(cmd), &p);
 +    av_strlcpy(c->method, cmd, sizeof(c->method));
 +
 +    if (!strcmp(cmd, "GET"))
 +        c->post = 0;
 +    else if (!strcmp(cmd, "POST"))
 +        c->post = 1;
 +    else
 +        return -1;
 +
 +    get_word(url, sizeof(url), &p);
 +    av_strlcpy(c->url, url, sizeof(c->url));
 +
 +    get_word(protocol, sizeof(protocol), (const char **)&p);
 +    if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
 +        return -1;
 +
 +    av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
 +
 +    if (config.debug)
 +        http_log("%s - - New connection: %s %s\n",
 +                 inet_ntoa(c->from_addr.sin_addr), cmd, url);
 +
 +    /* find the filename and the optional info string in the request */
 +    p1 = strchr(url, '?');
 +    if (p1) {
 +        av_strlcpy(info, p1, sizeof(info));
 +        *p1 = '\0';
 +    } else
 +        info[0] = '\0';
 +
 +    av_strlcpy(filename, url + ((*url == '/') ? 1 : 0), sizeof(filename)-1);
 +
 +    for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
 +        if (av_strncasecmp(p, "User-Agent:", 11) == 0) {
 +            useragent = p + 11;
 +            if (*useragent && *useragent != '\n' && av_isspace(*useragent))
 +                useragent++;
 +            break;
 +        }
 +        p = strchr(p, '\n');
 +        if (!p)
 +            break;
 +
 +        p++;
 +    }
 +
 +    redir_type = REDIR_NONE;
 +    if (av_match_ext(filename, "asx")) {
 +        redir_type = REDIR_ASX;
 +        filename[strlen(filename)-1] = 'f';
 +    } else if (av_match_ext(filename, "asf") &&
 +        (!useragent || av_strncasecmp(useragent, "NSPlayer", 8))) {
 +        /* if this isn't WMP or lookalike, return the redirector file */
 +        redir_type = REDIR_ASF;
 +    } else if (av_match_ext(filename, "rpm,ram")) {
 +        redir_type = REDIR_RAM;
 +        strcpy(filename + strlen(filename)-2, "m");
 +    } else if (av_match_ext(filename, "rtsp")) {
 +        redir_type = REDIR_RTSP;
 +        compute_real_filename(filename, sizeof(filename) - 1);
 +    } else if (av_match_ext(filename, "sdp")) {
 +        redir_type = REDIR_SDP;
 +        compute_real_filename(filename, sizeof(filename) - 1);
 +    }
 +
 +    /* "redirect" request to index.html */
 +    if (!strlen(filename))
 +        av_strlcpy(filename, "index.html", sizeof(filename) - 1);
 +
 +    stream = config.first_stream;
 +    while (stream) {
 +        if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
 +            break;
 +        stream = stream->next;
 +    }
 +    if (!stream) {
 +        snprintf(msg, sizeof(msg), "File '%s' not found", url);
 +        http_log("File '%s' not found\n", url);
 +        goto send_error;
 +    }
 +
 +    c->stream = stream;
 +    memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
 +    memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
 +
 +    if (stream->stream_type == STREAM_TYPE_REDIRECT) {
 +        c->http_error = 301;
 +        q = c->buffer;
 +        snprintf(q, c->buffer_size,
 +                      "HTTP/1.0 301 Moved\r\n"
 +                      "Location: %s\r\n"
 +                      "Content-type: text/html\r\n"
 +                      "\r\n"
 +                      "<!DOCTYPE html>\n"
 +                      "<html><head><title>Moved</title></head><body>\r\n"
 +                      "You should be <a href=\"%s\">redirected</a>.\r\n"
 +                      "</body></html>\r\n",
 +                 stream->feed_filename, stream->feed_filename);
 +        q += strlen(q);
 +        /* prepare output buffer */
 +        c->buffer_ptr = c->buffer;
 +        c->buffer_end = q;
 +        c->state = HTTPSTATE_SEND_HEADER;
 +        return 0;
 +    }
 +
 +    /* If this is WMP, get the rate information */
 +    if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
 +        if (modify_current_stream(c, ratebuf)) {
 +            for (i = 0; i < FF_ARRAY_ELEMS(c->feed_streams); i++) {
 +                if (c->switch_feed_streams[i] >= 0)
 +                    c->switch_feed_streams[i] = -1;
 +            }
 +        }
 +    }
 +
 +    if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE)
 +        current_bandwidth += stream->bandwidth;
 +
 +    /* If already streaming this feed, do not let another feeder start */
 +    if (stream->feed_opened) {
 +        snprintf(msg, sizeof(msg), "This feed is already being received.");
 +        http_log("Feed '%s' already being received\n", stream->feed_filename);
 +        goto send_error;
 +    }
 +
 +    if (c->post == 0 && config.max_bandwidth < current_bandwidth) {
 +        c->http_error = 503;
 +        q = c->buffer;
 +        snprintf(q, c->buffer_size,
 +                      "HTTP/1.0 503 Server too busy\r\n"
 +                      "Content-type: text/html\r\n"
 +                      "\r\n"
 +                      "<!DOCTYPE html>\n"
 +                      "<html><head><title>Too busy</title></head><body>\r\n"
 +                      "<p>The server is too busy to serve your request at "
 +                      "this time.</p>\r\n"
 +                      "<p>The bandwidth being served (including your stream) "
 +                      "is %"PRIu64"kbit/s, and this exceeds the limit of "
 +                      "%"PRIu64"kbit/s.</p>\r\n"
 +                      "</body></html>\r\n",
 +                 current_bandwidth, config.max_bandwidth);
 +        q += strlen(q);
 +        /* prepare output buffer */
 +        c->buffer_ptr = c->buffer;
 +        c->buffer_end = q;
 +        c->state = HTTPSTATE_SEND_HEADER;
 +        return 0;
 +    }
 +
 +    if (redir_type != REDIR_NONE) {
 +        const char *hostinfo = 0;
 +
 +        for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
 +            if (av_strncasecmp(p, "Host:", 5) == 0) {
 +                hostinfo = p + 5;
 +                break;
 +            }
 +            p = strchr(p, '\n');
 +            if (!p)
 +                break;
 +
 +            p++;
 +        }
 +
 +        if (hostinfo) {
 +            char *eoh;
 +            char hostbuf[260];
 +
 +            while (av_isspace(*hostinfo))
 +                hostinfo++;
 +
 +            eoh = strchr(hostinfo, '\n');
 +            if (eoh) {
 +                if (eoh[-1] == '\r')
 +                    eoh--;
 +
 +                if (eoh - hostinfo < sizeof(hostbuf) - 1) {
 +                    memcpy(hostbuf, hostinfo, eoh - hostinfo);
 +                    hostbuf[eoh - hostinfo] = 0;
 +
 +                    c->http_error = 200;
 +                    q = c->buffer;
 +                    switch(redir_type) {
 +                    case REDIR_ASX:
 +                        snprintf(q, c->buffer_size,
 +                                      "HTTP/1.0 200 ASX Follows\r\n"
 +                                      "Content-type: video/x-ms-asf\r\n"
 +                                      "\r\n"
 +                                      "<ASX Version=\"3\">\r\n"
 +                                      //"<!-- Autogenerated by ffserver -->\r\n"
 +                                      "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n"
 +                                      "</ASX>\r\n", hostbuf, filename, info);
 +                        q += strlen(q);
 +                        break;
 +                    case REDIR_RAM:
 +                        snprintf(q, c->buffer_size,
 +                                      "HTTP/1.0 200 RAM Follows\r\n"
 +                                      "Content-type: audio/x-pn-realaudio\r\n"
 +                                      "\r\n"
 +                                      "# Autogenerated by ffserver\r\n"
 +                                      "http://%s/%s%s\r\n", hostbuf, filename, info);
 +                        q += strlen(q);
 +                        break;
 +                    case REDIR_ASF:
 +                        snprintf(q, c->buffer_size,
 +                                      "HTTP/1.0 200 ASF Redirect follows\r\n"
 +                                      "Content-type: video/x-ms-asf\r\n"
 +                                      "\r\n"
 +                                      "[Reference]\r\n"
 +                                      "Ref1=http://%s/%s%s\r\n", hostbuf, filename, info);
 +                        q += strlen(q);
 +                        break;
 +                    case REDIR_RTSP:
 +                        {
 +                            char hostname[256], *p;
 +                            /* extract only hostname */
 +                            av_strlcpy(hostname, hostbuf, sizeof(hostname));
 +                            p = strrchr(hostname, ':');
 +                            if (p)
 +                                *p = '\0';
 +                            snprintf(q, c->buffer_size,
 +                                          "HTTP/1.0 200 RTSP Redirect follows\r\n"
 +                                          /* XXX: incorrect MIME type ? */
 +                                          "Content-type: application/x-rtsp\r\n"
 +                                          "\r\n"
 +                                          "rtsp://%s:%d/%s\r\n", hostname, ntohs(config.rtsp_addr.sin_port), filename);
 +                            q += strlen(q);
 +                        }
 +                        break;
 +                    case REDIR_SDP:
 +                        {
 +                            uint8_t *sdp_data;
 +                            int sdp_data_size;
 +                            socklen_t len;
 +                            struct sockaddr_in my_addr;
 +
 +                            snprintf(q, c->buffer_size,
 +                                          "HTTP/1.0 200 OK\r\n"
 +                                          "Content-type: application/sdp\r\n"
 +                                          "\r\n");
 +                            q += strlen(q);
 +
 +                            len = sizeof(my_addr);
 +
 +                            /* XXX: Should probably fail? */
 +                            if (getsockname(c->fd, (struct sockaddr *)&my_addr, &len))
 +                                http_log("getsockname() failed\n");
 +
 +                            /* XXX: should use a dynamic buffer */
 +                            sdp_data_size = prepare_sdp_description(stream,
 +                                                                    &sdp_data,
 +                                                                    my_addr.sin_addr);
 +                            if (sdp_data_size > 0) {
 +                                memcpy(q, sdp_data, sdp_data_size);
 +                                q += sdp_data_size;
 +                                *q = '\0';
 +                                av_freep(&sdp_data);
 +                            }
 +                        }
 +                        break;
 +                    default:
 +                        abort();
 +                        break;
 +                    }
 +
 +                    /* prepare output buffer */
 +                    c->buffer_ptr = c->buffer;
 +                    c->buffer_end = q;
 +                    c->state = HTTPSTATE_SEND_HEADER;
 +                    return 0;
 +                }
 +            }
 +        }
 +
 +        snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
 +        goto send_error;
 +    }
 +
 +    stream->conns_served++;
 +
 +    /* XXX: add there authenticate and IP match */
 +
 +    if (c->post) {
 +        /* if post, it means a feed is being sent */
 +        if (!stream->is_feed) {
 +            /* However it might be a status report from WMP! Let us log the
 +             * data as it might come handy one day. */
 +            const char *logline = 0;
 +            int client_id = 0;
 +
 +            for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
 +                if (av_strncasecmp(p, "Pragma: log-line=", 17) == 0) {
 +                    logline = p;
 +                    break;
 +                }
 +                if (av_strncasecmp(p, "Pragma: client-id=", 18) == 0)
 +                    client_id = strtol(p + 18, 0, 10);
 +                p = strchr(p, '\n');
 +                if (!p)
 +                    break;
 +
 +                p++;
 +            }
 +
 +            if (logline) {
 +                char *eol = strchr(logline, '\n');
 +
 +                logline += 17;
 +
 +                if (eol) {
 +                    if (eol[-1] == '\r')
 +                        eol--;
 +                    http_log("%.*s\n", (int) (eol - logline), logline);
 +                    c->suppress_log = 1;
 +                }
 +            }
 +
 +#ifdef DEBUG
 +            http_log("\nGot request:\n%s\n", c->buffer);
 +#endif
 +
 +            if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
 +                HTTPContext *wmpc;
 +
 +                /* Now we have to find the client_id */
 +                for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
 +                    if (wmpc->wmp_client_id == client_id)
 +                        break;
 +                }
 +
 +                if (wmpc && modify_current_stream(wmpc, ratebuf))
 +                    wmpc->switch_pending = 1;
 +            }
 +
 +            snprintf(msg, sizeof(msg), "POST command not handled");
 +            c->stream = 0;
 +            goto send_error;
 +        }
 +        if (http_start_receive_data(c) < 0) {
 +            snprintf(msg, sizeof(msg), "could not open feed");
 +            goto send_error;
 +        }
 +        c->http_error = 0;
 +        c->state = HTTPSTATE_RECEIVE_DATA;
 +        return 0;
 +    }
 +
 +#ifdef DEBUG
 +    if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0)
 +        http_log("\nGot request:\n%s\n", c->buffer);
 +#endif
 +
 +    if (c->stream->stream_type == STREAM_TYPE_STATUS)
 +        goto send_status;
 +
 +    /* open input stream */
 +    if (open_input_stream(c, info) < 0) {
 +        snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
 +        goto send_error;
 +    }
 +
 +    /* prepare HTTP header */
 +    c->buffer[0] = 0;
 +    av_strlcatf(c->buffer, c->buffer_size, "HTTP/1.0 200 OK\r\n");
 +    mime_type = c->stream->fmt->mime_type;
 +    if (!mime_type)
 +        mime_type = "application/x-octet-stream";
 +    av_strlcatf(c->buffer, c->buffer_size, "Pragma: no-cache\r\n");
 +
 +    /* for asf, we need extra headers */
 +    if (!strcmp(c->stream->fmt->name,"asf_stream")) {
 +        /* Need to allocate a client id */
 +
 +        c->wmp_client_id = av_lfg_get(&random_state);
 +
 +        av_strlcatf(c->buffer, c->buffer_size, "Server: Cougar 4.1.0.3923\r\nCache-Control: no-cache\r\nPragma: client-id=%d\r\nPragma: features=\"broadcast\"\r\n", c->wmp_client_id);
 +    }
 +    av_strlcatf(c->buffer, c->buffer_size, "Content-Type: %s\r\n", mime_type);
 +    av_strlcatf(c->buffer, c->buffer_size, "\r\n");
 +    q = c->buffer + strlen(c->buffer);
 +
 +    /* prepare output buffer */
 +    c->http_error = 0;
 +    c->buffer_ptr = c->buffer;
 +    c->buffer_end = q;
 +    c->state = HTTPSTATE_SEND_HEADER;
 +    return 0;
 + send_error:
 +    c->http_error = 404;
 +    q = c->buffer;
 +    if (!htmlencode(msg, &encoded_msg)) {
 +        http_log("Could not encode filename '%s' as HTML\n", msg);
 +    }
 +    snprintf(q, c->buffer_size,
 +                  "HTTP/1.0 404 Not Found\r\n"
 +                  "Content-type: text/html\r\n"
 +                  "\r\n"
 +                  "<!DOCTYPE html>\n"
 +                  "<html>\n"
 +                  "<head>\n"
 +                  "<meta charset=\"UTF-8\">\n"
 +                  "<title>404 Not Found</title>\n"
 +                  "</head>\n"
 +                  "<body>%s</body>\n"
 +                  "</html>\n", encoded_msg? encoded_msg : "File not found");
 +    q += strlen(q);
 +    /* prepare output buffer */
 +    c->buffer_ptr = c->buffer;
 +    c->buffer_end = q;
 +    c->state = HTTPSTATE_SEND_HEADER;
 +    av_freep(&encoded_msg);
 +    return 0;
 + send_status:
 +    compute_status(c);
 +    /* horrible: we use this value to avoid
 +     * going to the send data state */
 +    c->http_error = 200;
 +    c->state = HTTPSTATE_SEND_HEADER;
 +    return 0;
 +}
 +
 +static void fmt_bytecount(AVIOContext *pb, int64_t count)
 +{
 +    static const char suffix[] = " kMGTP";
 +    const char *s;
 +
 +    for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++);
 +
 +    avio_printf(pb, "%"PRId64"%c", count, *s);
 +}
 +
 +static inline void print_stream_params(AVIOContext *pb, FFServerStream *stream)
 +{
 +    int i, stream_no;
 +    const char *type = "unknown";
 +    char parameters[64];
 +    LayeredAVStream *st;
 +    AVCodec *codec;
 +
 +    stream_no = stream->nb_streams;
 +
 +    avio_printf(pb, "<table><tr><th>Stream<th>"
 +                    "type<th>kbit/s<th>codec<th>"
 +                    "Parameters\n");
 +
 +    for (i = 0; i < stream_no; i++) {
 +        st = stream->streams[i];
 +        codec = avcodec_find_encoder(st->codecpar->codec_id);
 +
 +        parameters[0] = 0;
 +
 +        switch(st->codecpar->codec_type) {
 +        case AVMEDIA_TYPE_AUDIO:
 +            type = "audio";
 +            snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz",
 +                     st->codecpar->channels, st->codecpar->sample_rate);
 +            break;
 +        case AVMEDIA_TYPE_VIDEO:
 +            type = "video";
 +            snprintf(parameters, sizeof(parameters),
 +                     "%dx%d, q=%d-%d, fps=%d", st->codecpar->width,
 +                     st->codecpar->height, st->codec->qmin, st->codec->qmax,
 +                     st->time_base.den / st->time_base.num);
 +            break;
 +        default:
 +            abort();
 +        }
 +
 +        avio_printf(pb, "<tr><td>%d<td>%s<td>%"PRId64
 +                        "<td>%s<td>%s\n",
 +                    i, type, (int64_t)st->codecpar->bit_rate/1000,
 +                    codec ? codec->name : "", parameters);
 +     }
 +
 +     avio_printf(pb, "</table>\n");
 +}
 +
 +static void clean_html(char *clean, int clean_len, char *dirty)
 +{
 +    int i, o;
 +
 +    for (o = i = 0; o+10 < clean_len && dirty[i];) {
 +        int len = strspn(dirty+i, "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$-_.+!*(),?/ :;%");
 +        if (len) {
 +            if (o + len >= clean_len)
 +                break;
 +            memcpy(clean + o, dirty + i, len);
 +            i += len;
 +            o += len;
 +        } else {
 +            int c = dirty[i++];
 +            switch (c) {
 +            case  '&': av_strlcat(clean+o, "&amp;"  , clean_len - o); break;
 +            case  '<': av_strlcat(clean+o, "&lt;"   , clean_len - o); break;
 +            case  '>': av_strlcat(clean+o, "&gt;"   , clean_len - o); break;
 +            case '\'': av_strlcat(clean+o, "&apos;" , clean_len - o); break;
 +            case '\"': av_strlcat(clean+o, "&quot;" , clean_len - o); break;
 +            default:   av_strlcat(clean+o, "&#9785;", clean_len - o); break;
 +            }
 +            o += strlen(clean+o);
 +        }
 +    }
 +    clean[o] = 0;
 +}
 +
 +static void compute_status(HTTPContext *c)
 +{
 +    HTTPContext *c1;
 +    FFServerStream *stream;
 +    char *p;
 +    time_t ti;
 +    int i, len;
 +    AVIOContext *pb;
 +
 +    if (avio_open_dyn_buf(&pb) < 0) {
 +        /* XXX: return an error ? */
 +        c->buffer_ptr = c->buffer;
 +        c->buffer_end = c->buffer;
 +        return;
 +    }
 +
 +    avio_printf(pb, "HTTP/1.0 200 OK\r\n");
 +    avio_printf(pb, "Content-type: text/html\r\n");
 +    avio_printf(pb, "Pragma: no-cache\r\n");
 +    avio_printf(pb, "\r\n");
 +
 +    avio_printf(pb, "<!DOCTYPE html>\n");
 +    avio_printf(pb, "<html><head><title>%s Status</title>\n", program_name);
 +    if (c->stream->feed_filename[0])
 +        avio_printf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n",
 +                    c->stream->feed_filename);
 +    avio_printf(pb, "</head>\n<body>");
 +    avio_printf(pb, "<h1>%s Status</h1>\n", program_name);
 +    /* format status */
 +    avio_printf(pb, "<h2>Available Streams</h2>\n");
 +    avio_printf(pb, "<table>\n");
 +    avio_printf(pb, "<tr><th>Path<th>Served<br>Conns<th><br>bytes<th>Format<th>Bit rate<br>kbit/s<th>Video<br>kbit/s<th><br>Codec<th>Audio<br>kbit/s<th><br>Codec<th>Feed\n");
 +    stream = config.first_stream;
 +    while (stream) {
 +        char sfilename[1024];
 +        char *eosf;
 +
 +        if (stream->feed == stream) {
 +            stream = stream->next;
 +            continue;
 +        }
 +
 +        av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10);
 +        eosf = sfilename + strlen(sfilename);
 +        if (eosf - sfilename >= 4) {
 +            if (strcmp(eosf - 4, ".asf") == 0)
 +                strcpy(eosf - 4, ".asx");
 +            else if (strcmp(eosf - 3, ".rm") == 0)
 +                strcpy(eosf - 3, ".ram");
 +            else if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
 +                /* generate a sample RTSP director if
 +                 * unicast. Generate an SDP redirector if
 +                 * multicast */
 +                eosf = strrchr(sfilename, '.');
 +                if (!eosf)
 +                    eosf = sfilename + strlen(sfilename);
 +                if (stream->is_multicast)
 +                    strcpy(eosf, ".sdp");
 +                else
 +                    strcpy(eosf, ".rtsp");
 +            }
 +        }
 +
 +        avio_printf(pb, "<tr><td><a href=\"/%s\">%s</a> ",
 +                    sfilename, stream->filename);
 +        avio_printf(pb, "<td> %d <td> ",
 +                    stream->conns_served);
 +        // TODO: Investigate if we can make http bitexact so it always produces the same count of bytes
 +        if (!config.bitexact)
 +            fmt_bytecount(pb, stream->bytes_served);
 +
 +        switch(stream->stream_type) {
 +        case STREAM_TYPE_LIVE: {
 +            int audio_bit_rate = 0;
 +            int video_bit_rate = 0;
 +            const char *audio_codec_name = "";
 +            const char *video_codec_name = "";
 +            const char *audio_codec_name_extra = "";
 +            const char *video_codec_name_extra = "";
 +
 +            for(i=0;i<stream->nb_streams;i++) {
 +                LayeredAVStream *st = stream->streams[i];
 +                AVCodec *codec = avcodec_find_encoder(st->codecpar->codec_id);
 +
 +                switch(st->codecpar->codec_type) {
 +                case AVMEDIA_TYPE_AUDIO:
 +                    audio_bit_rate += st->codecpar->bit_rate;
 +                    if (codec) {
 +                        if (*audio_codec_name)
 +                            audio_codec_name_extra = "...";
 +                        audio_codec_name = codec->name;
 +                    }
 +                    break;
 +                case AVMEDIA_TYPE_VIDEO:
 +                    video_bit_rate += st->codecpar->bit_rate;
 +                    if (codec) {
 +                        if (*video_codec_name)
 +                            video_codec_name_extra = "...";
 +                        video_codec_name = codec->name;
 +                    }
 +                    break;
 +                case AVMEDIA_TYPE_DATA:
 +                    video_bit_rate += st->codecpar->bit_rate;
 +                    break;
 +                default:
 +                    abort();
 +                }
 +            }
 +
 +            avio_printf(pb, "<td> %s <td> %d <td> %d <td> %s %s <td> "
 +                            "%d <td> %s %s",
 +                        stream->fmt->name, stream->bandwidth,
 +                        video_bit_rate / 1000, video_codec_name,
 +                        video_codec_name_extra, audio_bit_rate / 1000,
 +                        audio_codec_name, audio_codec_name_extra);
 +
 +            if (stream->feed)
 +                avio_printf(pb, "<td>%s", stream->feed->filename);
 +            else
 +                avio_printf(pb, "<td>%s", stream->feed_filename);
 +            avio_printf(pb, "\n");
 +        }
 +            break;
 +        default:
 +            avio_printf(pb, "<td> - <td> - "
 +                            "<td> - <td><td> - <td>\n");
 +            break;
 +        }
 +        stream = stream->next;
 +    }
 +    avio_printf(pb, "</table>\n");
 +
 +    stream = config.first_stream;
 +    while (stream) {
 +
 +        if (stream->feed != stream) {
 +            stream = stream->next;
 +            continue;
 +        }
 +
 +        avio_printf(pb, "<h2>Feed %s</h2>", stream->filename);
 +        if (stream->pid) {
 +            avio_printf(pb, "Running as pid %"PRId64".\n", (int64_t) stream->pid);
 +
 +#if defined(linux)
 +            {
 +                FILE *pid_stat;
 +                char ps_cmd[64];
 +
 +                /* This is somewhat linux specific I guess */
 +                snprintf(ps_cmd, sizeof(ps_cmd),
 +                         "ps -o \"%%cpu,cputime\" --no-headers %"PRId64"",
 +                         (int64_t) stream->pid);
 +
 +                 pid_stat = popen(ps_cmd, "r");
 +                 if (pid_stat) {
 +                     char cpuperc[10];
 +                     char cpuused[64];
 +
 +                     if (fscanf(pid_stat, "%9s %63s", cpuperc, cpuused) == 2) {
 +                         avio_printf(pb, "Currently using %s%% of the cpu. "
 +                                         "Total time used %s.\n",
 +                                     cpuperc, cpuused);
 +                     }
 +                     fclose(pid_stat);
 +                 }
 +            }
 +#endif
 +
 +            avio_printf(pb, "<p>");
 +        }
 +
 +        print_stream_params(pb, stream);
 +        stream = stream->next;
 +    }
 +
 +    /* connection status */
 +    avio_printf(pb, "<h2>Connection Status</h2>\n");
 +
 +    avio_printf(pb, "Number of connections: %d / %d<br>\n",
 +                nb_connections, config.nb_max_connections);
 +
 +    avio_printf(pb, "Bandwidth in use: %"PRIu64"k / %"PRIu64"k<br>\n",
 +                current_bandwidth, config.max_bandwidth);
 +
 +    avio_printf(pb, "<table>\n");
 +    avio_printf(pb, "<tr><th>#<th>File<th>IP<th>URL<th>Proto<th>State<th>Target "
 +                    "bit/s<th>Actual bit/s<th>Bytes transferred\n");
 +    c1 = first_http_ctx;
 +    i = 0;
 +    while (c1) {
 +        int bitrate;
 +        int j;
 +
 +        bitrate = 0;
 +        if (c1->stream) {
 +            for (j = 0; j < c1->stream->nb_streams; j++) {
 +                if (!c1->stream->feed)
 +                    bitrate += c1->stream->streams[j]->codecpar->bit_rate;
 +                else if (c1->feed_streams[j] >= 0)
 +                    bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codecpar->bit_rate;
 +            }
 +        }
 +
 +        i++;
 +        p = inet_ntoa(c1->from_addr.sin_addr);
 +        clean_html(c1->clean_url, sizeof(c1->clean_url), c1->url);
 +        avio_printf(pb, "<tr><td><b>%d</b><td>%s%s<td>%s<td>%s<td>%s<td>%s"
 +                        "<td>",
 +                    i, c1->stream ? c1->stream->filename : "",
 +                    c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
 +                    p,
 +                    c1->clean_url,
 +                    c1->protocol, http_state[c1->state]);
 +        fmt_bytecount(pb, bitrate);
 +        avio_printf(pb, "<td>");
 +        fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
 +        avio_printf(pb, "<td>");
 +        fmt_bytecount(pb, c1->data_count);
 +        avio_printf(pb, "\n");
 +        c1 = c1->next;
 +    }
 +    avio_printf(pb, "</table>\n");
 +
 +    if (!config.bitexact) {
 +        /* date */
 +        ti = time(NULL);
 +        p = ctime(&ti);
 +        avio_printf(pb, "<hr>Generated at %s", p);
 +    }
 +    avio_printf(pb, "</body>\n</html>\n");
 +
 +    len = avio_close_dyn_buf(pb, &c->pb_buffer);
 +    c->buffer_ptr = c->pb_buffer;
 +    c->buffer_end = c->pb_buffer + len;
 +}
 +
 +static int open_input_stream(HTTPContext *c, const char *info)
 +{
 +    char buf[128];
 +    char input_filename[1024];
 +    AVFormatContext *s = NULL;
 +    int buf_size, i, ret;
 +    int64_t stream_pos;
 +
 +    /* find file name */
 +    if (c->stream->feed) {
 +        strcpy(input_filename, c->stream->feed->feed_filename);
 +        buf_size = FFM_PACKET_SIZE;
 +        /* compute position (absolute time) */
 +        if (av_find_info_tag(buf, sizeof(buf), "date", info)) {
 +            if ((ret = av_parse_time(&stream_pos, buf, 0)) < 0) {
 +                http_log("Invalid date specification '%s' for stream\n", buf);
 +                return ret;
 +            }
 +        } else if (av_find_info_tag(buf, sizeof(buf), "buffer", info)) {
 +            int prebuffer = strtol(buf, 0, 10);
 +            stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
 +        } else
 +            stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
 +    } else {
 +        strcpy(input_filename, c->stream->feed_filename);
 +        buf_size = 0;
 +        /* compute position (relative time) */
 +        if (av_find_info_tag(buf, sizeof(buf), "date", info)) {
 +            if ((ret = av_parse_time(&stream_pos, buf, 1)) < 0) {
 +                http_log("Invalid date specification '%s' for stream\n", buf);
 +                return ret;
 +            }
 +        } else
 +            stream_pos = 0;
 +    }
 +    if (!input_filename[0]) {
 +        http_log("No filename was specified for stream\n");
 +        return AVERROR(EINVAL);
 +    }
 +
 +    /* open stream */
 +    ret = avformat_open_input(&s, input_filename, c->stream->ifmt,
 +                              &c->stream->in_opts);
 +    if (ret < 0) {
 +        http_log("Could not open input '%s': %s\n",
 +                 input_filename, av_err2str(ret));
 +        return ret;
 +    }
 +
 +    /* set buffer size */
 +    if (buf_size > 0) {
 +        ret = ffio_set_buf_size(s->pb, buf_size);
 +        if (ret < 0) {
 +            http_log("Failed to set buffer size\n");
 +            return ret;
 +        }
 +    }
 +
 +    s->flags |= AVFMT_FLAG_GENPTS;
 +    c->fmt_in = s;
 +    if (strcmp(s->iformat->name, "ffm") &&
 +        (ret = avformat_find_stream_info(c->fmt_in, NULL)) < 0) {
 +        http_log("Could not find stream info for input '%s'\n", input_filename);
 +        avformat_close_input(&s);
 +        return ret;
 +    }
 +
 +    /* choose stream as clock source (we favor the video stream if
 +     * present) for packet sending */
 +    c->pts_stream_index = 0;
 +    for(i=0;i<c->stream->nb_streams;i++) {
 +        if (c->pts_stream_index == 0 &&
 +            c->stream->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
 +            c->pts_stream_index = i;
 +        }
 +    }
 +
 +    if (c->fmt_in->iformat->read_seek)
 +        av_seek_frame(c->fmt_in, -1, stream_pos, 0);
 +    /* set the start time (needed for maxtime and RTP packet timing) */
 +    c->start_time = cur_time;
 +    c->first_pts = AV_NOPTS_VALUE;
 +    return 0;
 +}
 +
 +/* return the server clock (in us) */
 +static int64_t get_server_clock(HTTPContext *c)
 +{
 +    /* compute current pts value from system time */
 +    return (cur_time - c->start_time) * 1000;
 +}
 +
 +/* return the estimated time (in us) at which the current packet must be sent */
 +static int64_t get_packet_send_clock(HTTPContext *c)
 +{
 +    int bytes_left, bytes_sent, frame_bytes;
 +
 +    frame_bytes = c->cur_frame_bytes;
 +    if (frame_bytes <= 0)
 +        return c->cur_pts;
 +
 +    bytes_left = c->buffer_end - c->buffer_ptr;
 +    bytes_sent = frame_bytes - bytes_left;
 +    return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
 +}
 +
 +
 +static int http_prepare_data(HTTPContext *c)
 +{
 +    int i, len, ret;
 +    AVFormatContext *ctx;
 +
 +    av_freep(&c->pb_buffer);
 +    switch(c->state) {
 +    case HTTPSTATE_SEND_DATA_HEADER:
 +        ctx = avformat_alloc_context();
 +        if (!ctx)
 +            return AVERROR(ENOMEM);
 +        c->pfmt_ctx = ctx;
 +        av_dict_copy(&(c->pfmt_ctx->metadata), c->stream->metadata, 0);
 +
 +        for(i=0;i<c->stream->nb_streams;i++) {
 +            LayeredAVStream *src;
 +            AVStream *st = avformat_new_stream(c->pfmt_ctx, NULL);
 +            if (!st)
 +                return AVERROR(ENOMEM);
 +
 +            /* if file or feed, then just take streams from FFServerStream
 +             * struct */
 +            if (!c->stream->feed ||
 +                c->stream->feed == c->stream)
 +                src = c->stream->streams[i];
 +            else
 +                src = c->stream->feed->streams[c->stream->feed_streams[i]];
 +
 +            unlayer_stream(c->pfmt_ctx->streams[i], src); //TODO we no longer copy st->internal, does this matter?
 +            av_assert0(!c->pfmt_ctx->streams[i]->priv_data);
 +
 +            if (src->codec->flags & AV_CODEC_FLAG_BITEXACT)
 +                c->pfmt_ctx->flags |= AVFMT_FLAG_BITEXACT;
 +        }
 +        /* set output format parameters */
 +        c->pfmt_ctx->oformat = c->stream->fmt;
 +        av_assert0(c->pfmt_ctx->nb_streams == c->stream->nb_streams);
 +
 +        c->got_key_frame = 0;
 +
 +        /* prepare header and save header data in a stream */
 +        if (avio_open_dyn_buf(&c->pfmt_ctx->pb) < 0) {
 +            /* XXX: potential leak */
 +            return -1;
 +        }
 +        c->pfmt_ctx->pb->seekable = 0;
 +
 +        /*
 +         * HACK to avoid MPEG-PS muxer to spit many underflow errors
 +         * Default value from FFmpeg
 +         * Try to set it using configuration option
 +         */
 +        c->pfmt_ctx->max_delay = (int)(0.7*AV_TIME_BASE);
 +
 +        if ((ret = avformat_write_header(c->pfmt_ctx, NULL)) < 0) {
 +            http_log("Error writing output header for stream '%s': %s\n",
 +                     c->stream->filename, av_err2str(ret));
 +            return ret;
 +        }
 +        av_dict_free(&c->pfmt_ctx->metadata);
 +
 +        len = avio_close_dyn_buf(c->pfmt_ctx->pb, &c->pb_buffer);
 +        c->buffer_ptr = c->pb_buffer;
 +        c->buffer_end = c->pb_buffer + len;
 +
 +        c->state = HTTPSTATE_SEND_DATA;
 +        c->last_packet_sent = 0;
 +        break;
 +    case HTTPSTATE_SEND_DATA:
 +        /* find a new packet */
 +        /* read a packet from the input stream */
 +        if (c->stream->feed)
 +            ffm_set_write_index(c->fmt_in,
 +                                c->stream->feed->feed_write_index,
 +                                c->stream->feed->feed_size);
 +
 +        if (c->stream->max_time &&
 +            c->stream->max_time + c->start_time - cur_time < 0)
 +            /* We have timed out */
 +            c->state = HTTPSTATE_SEND_DATA_TRAILER;
 +        else {
 +            AVPacket pkt;
 +        redo:
 +            ret = av_read_frame(c->fmt_in, &pkt);
 +            if (ret < 0) {
 +                if (c->stream->feed) {
 +                    /* if coming from feed, it means we reached the end of the
 +                     * ffm file, so must wait for more data */
 +                    c->state = HTTPSTATE_WAIT_FEED;
 +                    return 1; /* state changed */
 +                }
 +                if (ret == AVERROR(EAGAIN)) {
 +                    /* input not ready, come back later */
 +                    return 0;
 +                }
 +                if (c->stream->loop) {
 +                    avformat_close_input(&c->fmt_in);
 +                    if (open_input_stream(c, "") < 0)
 +                        goto no_loop;
 +                    goto redo;
 +                } else {
 +                    no_loop:
 +                        /* must send trailer now because EOF or error */
 +                        c->state = HTTPSTATE_SEND_DATA_TRAILER;
 +                }
 +            } else {
 +                int source_index = pkt.stream_index;
 +                /* update first pts if needed */
 +                if (c->first_pts == AV_NOPTS_VALUE && pkt.dts != AV_NOPTS_VALUE) {
 +                    c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
 +                    c->start_time = cur_time;
 +                }
 +                /* send it to the appropriate stream */
 +                if (c->stream->feed) {
 +                    /* if coming from a feed, select the right stream */
 +                    if (c->switch_pending) {
 +                        c->switch_pending = 0;
 +                        for(i=0;i<c->stream->nb_streams;i++) {
 +                            if (c->switch_feed_streams[i] == pkt.stream_index)
 +                                if (pkt.flags & AV_PKT_FLAG_KEY)
 +                                    c->switch_feed_streams[i] = -1;
 +                            if (c->switch_feed_streams[i] >= 0)
 +                                c->switch_pending = 1;
 +                        }
 +                    }
 +                    for(i=0;i<c->stream->nb_streams;i++) {
 +                        if (c->stream->feed_streams[i] == pkt.stream_index) {
 +                            AVStream *st = c->fmt_in->streams[source_index];
 +                            pkt.stream_index = i;
 +                            if (pkt.flags & AV_PKT_FLAG_KEY &&
 +                                (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ||
 +                                 c->stream->nb_streams == 1))
 +                                c->got_key_frame = 1;
 +                            if (!c->stream->send_on_key || c->got_key_frame)
 +                                goto send_it;
 +                        }
 +                    }
 +                } else {
 +                    AVStream *ist, *ost;
 +                send_it:
 +                    ist = c->fmt_in->streams[source_index];
 +                    /* specific handling for RTP: we use several
 +                     * output streams (one for each RTP connection).
 +                     * XXX: need more abstract handling */
 +                    if (c->is_packetized) {
 +                        /* compute send time and duration */
 +                        if (pkt.dts != AV_NOPTS_VALUE) {
 +                            c->cur_pts = av_rescale_q(pkt.dts, ist->time_base, AV_TIME_BASE_Q);
 +                            c->cur_pts -= c->first_pts;
 +                        }
 +                        c->cur_frame_duration = av_rescale_q(pkt.duration, ist->time_base, AV_TIME_BASE_Q);
 +                        /* find RTP context */
 +                        c->packet_stream_index = pkt.stream_index;
 +                        ctx = c->rtp_ctx[c->packet_stream_index];
 +                        if(!ctx) {
 +                            av_packet_unref(&pkt);
 +                            break;
 +                        }
 +                        /* only one stream per RTP connection */
 +                        pkt.stream_index = 0;
 +                    } else {
 +                        ctx = c->pfmt_ctx;
 +                        /* Fudge here */
 +                    }
 +
 +                    if (c->is_packetized) {
 +                        int max_packet_size;
 +                        if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP)
 +                            max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
 +                        else
 +                            max_packet_size = c->rtp_handles[c->packet_stream_index]->max_packet_size;
 +                        ret = ffio_open_dyn_packet_buf(&ctx->pb,
 +                                                       max_packet_size);
 +                    } else
 +                        ret = avio_open_dyn_buf(&ctx->pb);
 +
 +                    if (ret < 0) {
 +                        /* XXX: potential leak */
 +                        return -1;
 +                    }
 +                    ost = ctx->streams[pkt.stream_index];
 +
 +                    ctx->pb->seekable = 0;
 +                    if (pkt.dts != AV_NOPTS_VALUE)
 +                        pkt.dts = av_rescale_q(pkt.dts, ist->time_base,
 +                                               ost->time_base);
 +                    if (pkt.pts != AV_NOPTS_VALUE)
 +                        pkt.pts = av_rescale_q(pkt.pts, ist->time_base,
 +                                               ost->time_base);
 +                    pkt.duration = av_rescale_q(pkt.duration, ist->time_base,
 +                                                ost->time_base);
 +                    if ((ret = av_write_frame(ctx, &pkt)) < 0) {
 +                        http_log("Error writing frame to output for stream '%s': %s\n",
 +                                 c->stream->filename, av_err2str(ret));
 +                        c->state = HTTPSTATE_SEND_DATA_TRAILER;
 +                    }
 +
 +                    av_freep(&c->pb_buffer);
 +                    len = avio_close_dyn_buf(ctx->pb, &c->pb_buffer);
 +                    ctx->pb = NULL;
 +                    c->cur_frame_bytes = len;
 +                    c->buffer_ptr = c->pb_buffer;
 +                    c->buffer_end = c->pb_buffer + len;
 +
 +                    if (len == 0) {
 +                        av_packet_unref(&pkt);
 +                        goto redo;
 +                    }
 +                }
 +                av_packet_unref(&pkt);
 +            }
 +        }
 +        break;
 +    default:
 +    case HTTPSTATE_SEND_DATA_TRAILER:
 +        /* last packet test ? */
 +        if (c->last_packet_sent || c->is_packetized)
 +            return -1;
 +        ctx = c->pfmt_ctx;
 +        /* prepare header */
 +        if (avio_open_dyn_buf(&ctx->pb) < 0) {
 +            /* XXX: potential leak */
 +            return -1;
 +        }
 +        c->pfmt_ctx->pb->seekable = 0;
 +        av_write_trailer(ctx);
 +        len = avio_close_dyn_buf(ctx->pb, &c->pb_buffer);
 +        c->buffer_ptr = c->pb_buffer;
 +        c->buffer_end = c->pb_buffer + len;
 +
 +        c->last_packet_sent = 1;
 +        break;
 +    }
 +    return 0;
 +}
 +
 +/* should convert the format at the same time */
 +/* send data starting at c->buffer_ptr to the output connection
 + * (either UDP or TCP)
 + */
 +static int http_send_data(HTTPContext *c)
 +{
 +    int len, ret;
 +
 +    for(;;) {
 +        if (c->buffer_ptr >= c->buffer_end) {
 +            ret = http_prepare_data(c);
 +            if (ret < 0)
 +                return -1;
 +            else if (ret)
 +                /* state change requested */
 +                break;
 +        } else {
 +            if (c->is_packetized) {
 +                /* RTP data output */
 +                len = c->buffer_end - c->buffer_ptr;
 +                if (len < 4) {
 +                    /* fail safe - should never happen */
 +                fail1:
 +                    c->buffer_ptr = c->buffer_end;
 +                    return 0;
 +                }
 +                len = (c->buffer_ptr[0] << 24) |
 +                    (c->buffer_ptr[1] << 16) |
 +                    (c->buffer_ptr[2] << 8) |
 +                    (c->buffer_ptr[3]);
 +                if (len > (c->buffer_end - c->buffer_ptr))
 +                    goto fail1;
 +                if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
 +                    /* nothing to send yet: we can wait */
 +                    return 0;
 +                }
 +
 +                c->data_count += len;
 +                update_datarate(&c->datarate, c->data_count);
 +                if (c->stream)
 +                    c->stream->bytes_served += len;
 +
 +                if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP) {
 +                    /* RTP packets are sent inside the RTSP TCP connection */
 +                    AVIOContext *pb;
 +                    int interleaved_index, size;
 +                    uint8_t header[4];
 +                    HTTPContext *rtsp_c;
 +
 +                    rtsp_c = c->rtsp_c;
 +                    /* if no RTSP connection left, error */
 +                    if (!rtsp_c)
 +                        return -1;
 +                    /* if already sending something, then wait. */
 +                    if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST)
 +                        break;
 +                    if (avio_open_dyn_buf(&pb) < 0)
 +                        goto fail1;
 +                    interleaved_index = c->packet_stream_index * 2;
 +                    /* RTCP packets are sent at odd indexes */
 +                    if (c->buffer_ptr[1] == 200)
 +                        interleaved_index++;
 +                    /* write RTSP TCP header */
 +                    header[0] = '$';
 +                    header[1] = interleaved_index;
 +                    header[2] = len >> 8;
 +                    header[3] = len;
 +                    avio_write(pb, header, 4);
 +                    /* write RTP packet data */
 +                    c->buffer_ptr += 4;
 +                    avio_write(pb, c->buffer_ptr, len);
 +                    size = avio_close_dyn_buf(pb, &c->packet_buffer);
 +                    /* prepare asynchronous TCP sending */
 +                    rtsp_c->packet_buffer_ptr = c->packet_buffer;
 +                    rtsp_c->packet_buffer_end = c->packet_buffer + size;
 +                    c->buffer_ptr += len;
 +
 +                    /* send everything we can NOW */
 +                    len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
 +                               rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
 +                    if (len > 0)
 +                        rtsp_c->packet_buffer_ptr += len;
 +                    if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
 +                        /* if we could not send all the data, we will
 +                         * send it later, so a new state is needed to
 +                         * "lock" the RTSP TCP connection */
 +                        rtsp_c->state = RTSPSTATE_SEND_PACKET;
 +                        break;
 +                    } else
 +                        /* all data has been sent */
 +                        av_freep(&c->packet_buffer);
 +                } else {
 +                    /* send RTP packet directly in UDP */
 +                    c->buffer_ptr += 4;
 +                    ffurl_write(c->rtp_handles[c->packet_stream_index],
 +                                c->buffer_ptr, len);
 +                    c->buffer_ptr += len;
 +                    /* here we continue as we can send several packets
 +                     * per 10 ms slot */
 +                }
 +            } else {
 +                /* TCP data output */
 +                len = send(c->fd, c->buffer_ptr,
 +                           c->buffer_end - c->buffer_ptr, 0);
 +                if (len < 0) {
 +                    if (ff_neterrno() != AVERROR(EAGAIN) &&
 +                        ff_neterrno() != AVERROR(EINTR))
 +                        /* error : close connection */
 +                        return -1;
 +                    else
 +                        return 0;
 +                }
 +                c->buffer_ptr += len;
 +
 +                c->data_count += len;
 +                update_datarate(&c->datarate, c->data_count);
 +                if (c->stream)
 +                    c->stream->bytes_served += len;
 +                break;
 +            }
 +        }
 +    } /* for(;;) */
 +    return 0;
 +}
 +
 +static int http_start_receive_data(HTTPContext *c)
 +{
 +    int fd;
 +    int ret;
 +    int64_t ret64;
 +
 +    if (c->stream->feed_opened) {
 +        http_log("Stream feed '%s' was not opened\n",
 +                 c->stream->feed_filename);
 +        return AVERROR(EINVAL);
 +    }
 +
 +    /* Don't permit writing to this one */
 +    if (c->stream->readonly) {
 +        http_log("Cannot write to read-only file '%s'\n",
 +                 c->stream->feed_filename);
 +        return AVERROR(EINVAL);
 +    }
 +
 +    /* open feed */
 +    fd = open(c->stream->feed_filename, O_RDWR);
 +    if (fd < 0) {
 +        ret = AVERROR(errno);
 +        http_log("Could not open feed file '%s': %s\n",
 +                 c->stream->feed_filename, strerror(errno));
 +        return ret;
 +    }
 +    c->feed_fd = fd;
 +
 +    if (c->stream->truncate) {
 +        /* truncate feed file */
 +        ffm_write_write_index(c->feed_fd, FFM_PACKET_SIZE);
 +        http_log("Truncating feed file '%s'\n", c->stream->feed_filename);
 +        if (ftruncate(c->feed_fd, FFM_PACKET_SIZE) < 0) {
 +            ret = AVERROR(errno);
 +            http_log("Error truncating feed file '%s': %s\n",
 +                     c->stream->feed_filename, strerror(errno));
 +            return ret;
 +        }
 +    } else {
 +        ret64 = ffm_read_write_index(fd);
 +        if (ret64 < 0) {
 +            http_log("Error reading write index from feed file '%s': %s\n",
 +                     c->stream->feed_filename, strerror(errno));
 +            return ret64;
 +        }
 +        c->stream->feed_write_index = ret64;
 +    }
 +
 +    c->stream->feed_write_index = FFMAX(ffm_read_write_index(fd),
 +                                        FFM_PACKET_SIZE);
 +    c->stream->feed_size = lseek(fd, 0, SEEK_END);
 +    lseek(fd, 0, SEEK_SET);
 +
 +    /* init buffer input */
 +    c->buffer_ptr = c->buffer;
 +    c->buffer_end = c->buffer + FFM_PACKET_SIZE;
 +    c->stream->feed_opened = 1;
 +    c->chunked_encoding = !!av_stristr(c->buffer, "Transfer-Encoding: chunked");
 +    return 0;
 +}
 +
 +static int http_receive_data(HTTPContext *c)
 +{
 +    HTTPContext *c1;
 +    int len, loop_run = 0;
 +
 +    while (c->chunked_encoding && !c->chunk_size &&
 +           c->buffer_end > c->buffer_ptr) {
 +        /* read chunk header, if present */
 +        len = recv(c->fd, c->buffer_ptr, 1, 0);
 +
 +        if (len < 0) {
 +            if (ff_neterrno() != AVERROR(EAGAIN) &&
 +                ff_neterrno() != AVERROR(EINTR))
 +                /* error : close connection */
 +                goto fail;
 +            return 0;
 +        } else if (len == 0) {
 +            /* end of connection : close it */
 +            goto fail;
 +        } else if (c->buffer_ptr - c->buffer >= 2 &&
 +                   !memcmp(c->buffer_ptr - 1, "\r\n", 2)) {
 +            c->chunk_size = strtol(c->buffer, 0, 16);
 +            if (c->chunk_size <= 0) { // end of stream or invalid chunk size
 +                c->chunk_size = 0;
 +                goto fail;
 +            }
 +            c->buffer_ptr = c->buffer;
 +            break;
 +        } else if (++loop_run > 10)
 +            /* no chunk header, abort */
 +            goto fail;
 +        else
 +            c->buffer_ptr++;
 +    }
 +
 +    if (c->buffer_end > c->buffer_ptr) {
 +        len = recv(c->fd, c->buffer_ptr,
 +                   FFMIN(c->chunk_size, c->buffer_end - c->buffer_ptr), 0);
 +        if (len < 0) {
 +            if (ff_neterrno() != AVERROR(EAGAIN) &&
 +                ff_neterrno() != AVERROR(EINTR))
 +                /* error : close connection */
 +                goto fail;
 +        } else if (len == 0)
 +            /* end of connection : close it */
 +            goto fail;
 +        else {
 +            av_assert0(len <= c->chunk_size);
 +            c->chunk_size -= len;
 +            c->buffer_ptr += len;
 +            c->data_count += len;
 +            update_datarate(&c->datarate, c->data_count);
 +        }
 +    }
 +
 +    if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
 +        if (c->buffer[0] != 'f' ||
 +            c->buffer[1] != 'm') {
 +            http_log("Feed stream has become desynchronized -- disconnecting\n");
 +            goto fail;
 +        }
 +    }
 +
 +    if (c->buffer_ptr >= c->buffer_end) {
 +        FFServerStream *feed = c->stream;
 +        /* a packet has been received : write it in the store, except
 +         * if header */
 +        if (c->data_count > FFM_PACKET_SIZE) {
 +            /* XXX: use llseek or url_seek
 +             * XXX: Should probably fail? */
 +            if (lseek(c->feed_fd, feed->feed_write_index, SEEK_SET) == -1)
 +                http_log("Seek to %"PRId64" failed\n", feed->feed_write_index);
 +
 +            if (write(c->feed_fd, c->buffer, FFM_PACKET_SIZE) < 0) {
 +                http_log("Error writing to feed file: %s\n", strerror(errno));
 +                goto fail;
 +            }
 +
 +            feed->feed_write_index += FFM_PACKET_SIZE;
 +            /* update file size */
 +            if (feed->feed_write_index > c->stream->feed_size)
 +                feed->feed_size = feed->feed_write_index;
 +
 +            /* handle wrap around if max file size reached */
 +            if (c->stream->feed_max_size &&
 +                feed->feed_write_index >= c->stream->feed_max_size)
 +                feed->feed_write_index = FFM_PACKET_SIZE;
 +
 +            /* write index */
 +            if (ffm_write_write_index(c->feed_fd, feed->feed_write_index) < 0) {
 +                http_log("Error writing index to feed file: %s\n",
 +                         strerror(errno));
 +                goto fail;
 +            }
 +
 +            /* wake up any waiting connections */
 +            for(c1 = first_http_ctx; c1; c1 = c1->next) {
 +                if (c1->state == HTTPSTATE_WAIT_FEED &&
 +                    c1->stream->feed == c->stream->feed)
 +                    c1->state = HTTPSTATE_SEND_DATA;
 +            }
 +        } else {
 +            /* We have a header in our hands that contains useful data */
 +            AVFormatContext *s = avformat_alloc_context();
 +            AVIOContext *pb;
 +            AVInputFormat *fmt_in;
 +            int i;
 +
 +            if (!s)
 +                goto fail;
 +
 +            /* use feed output format name to find corresponding input format */
 +            fmt_in = av_find_input_format(feed->fmt->name);
 +            if (!fmt_in)
 +                goto fail;
 +
 +            pb = avio_alloc_context(c->buffer, c->buffer_end - c->buffer,
 +                                    0, NULL, NULL, NULL, NULL);
 +            if (!pb)
 +                goto fail;
 +
 +            pb->seekable = 0;
 +
 +            s->pb = pb;
 +            if (avformat_open_input(&s, c->stream->feed_filename, fmt_in, NULL) < 0) {
 +                av_freep(&pb);
 +                goto fail;
 +            }
 +
 +            /* Now we have the actual streams */
 +            if (s->nb_streams != feed->nb_streams) {
 +                avformat_close_input(&s);
 +                av_freep(&pb);
 +                http_log("Feed '%s' stream number does not match registered feed\n",
 +                         c->stream->feed_filename);
 +                goto fail;
 +            }
 +
 +            for (i = 0; i < s->nb_streams; i++) {
 +                LayeredAVStream *fst = feed->streams[i];
 +                AVStream *st = s->streams[i];
 +                avcodec_parameters_to_context(fst->codec, st->codecpar);
 +                avcodec_parameters_from_context(fst->codecpar, fst->codec);
 +            }
 +
 +            avformat_close_input(&s);
 +            av_freep(&pb);
 +        }
 +        c->buffer_ptr = c->buffer;
 +    }
 +
 +    return 0;
 + fail:
 +    c->stream->feed_opened = 0;
 +    close(c->feed_fd);
 +    /* wake up any waiting connections to stop waiting for feed */
 +    for(c1 = first_http_ctx; c1; c1 = c1->next) {
 +        if (c1->state == HTTPSTATE_WAIT_FEED &&
 +            c1->stream->feed == c->stream->feed)
 +            c1->state = HTTPSTATE_SEND_DATA_TRAILER;
 +    }
 +    return -1;
 +}
 +
 +/********************************************************************/
 +/* RTSP handling */
 +
 +static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
 +{
 +    const char *str;
 +    time_t ti;
 +    struct tm *tm;
 +    char buf2[32];
 +
 +    str = RTSP_STATUS_CODE2STRING(error_number);
 +    if (!str)
 +        str = "Unknown Error";
 +
 +    avio_printf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
 +    avio_printf(c->pb, "CSeq: %d\r\n", c->seq);
 +
 +    /* output GMT time */
 +    ti = time(NULL);
 +    tm = gmtime(&ti);
 +    strftime(buf2, sizeof(buf2), "%a, %d %b %Y %H:%M:%S", tm);
 +    avio_printf(c->pb, "Date: %s GMT\r\n", buf2);
 +}
 +
 +static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
 +{
 +    rtsp_reply_header(c, error_number);
 +    avio_printf(c->pb, "\r\n");
 +}
 +
 +static int rtsp_parse_request(HTTPContext *c)
 +{
 +    const char *p, *p1, *p2;
 +    char cmd[32];
 +    char url[1024];
 +    char protocol[32];
 +    char line[1024];
 +    int len;
 +    RTSPMessageHeader header1 = { 0 }, *header = &header1;
 +
 +    c->buffer_ptr[0] = '\0';
 +    p = c->buffer;
 +
 +    get_word(cmd, sizeof(cmd), &p);
 +    get_word(url, sizeof(url), &p);
 +    get_word(protocol, sizeof(protocol), &p);
 +
 +    av_strlcpy(c->method, cmd, sizeof(c->method));
 +    av_strlcpy(c->url, url, sizeof(c->url));
 +    av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
 +
 +    if (avio_open_dyn_buf(&c->pb) < 0) {
 +        /* XXX: cannot do more */
 +        c->pb = NULL; /* safety */
 +        return -1;
 +    }
 +
 +    /* check version name */
 +    if (strcmp(protocol, "RTSP/1.0")) {
 +        rtsp_reply_error(c, RTSP_STATUS_VERSION);
 +        goto the_end;
 +    }
 +
 +    /* parse each header line */
 +    /* skip to next line */
 +    while (*p != '\n' && *p != '\0')
 +        p++;
 +    if (*p == '\n')
 +        p++;
 +    while (*p != '\0') {
 +        p1 = memchr(p, '\n', (char *)c->buffer_ptr - p);
 +        if (!p1)
 +            break;
 +        p2 = p1;
 +        if (p2 > p && p2[-1] == '\r')
 +            p2--;
 +        /* skip empty line */
 +        if (p2 == p)
 +            break;
 +        len = p2 - p;
 +        if (len > sizeof(line) - 1)
 +            len = sizeof(line) - 1;
 +        memcpy(line, p, len);
 +        line[len] = '\0';
 +        ff_rtsp_parse_line(NULL, header, line, NULL, NULL);
 +        p = p1 + 1;
 +    }
 +
 +    /* handle sequence number */
 +    c->seq = header->seq;
 +
 +    if (!strcmp(cmd, "DESCRIBE"))
 +        rtsp_cmd_describe(c, url);
 +    else if (!strcmp(cmd, "OPTIONS"))
 +        rtsp_cmd_options(c, url);
 +    else if (!strcmp(cmd, "SETUP"))
 +        rtsp_cmd_setup(c, url, header);
 +    else if (!strcmp(cmd, "PLAY"))
 +        rtsp_cmd_play(c, url, header);
 +    else if (!strcmp(cmd, "PAUSE"))
 +        rtsp_cmd_interrupt(c, url, header, 1);
 +    else if (!strcmp(cmd, "TEARDOWN"))
 +        rtsp_cmd_interrupt(c, url, header, 0);
 +    else
 +        rtsp_reply_error(c, RTSP_STATUS_METHOD);
 +
 + the_end:
 +    len = avio_close_dyn_buf(c->pb, &c->pb_buffer);
 +    c->pb = NULL; /* safety */
 +    if (len < 0)
 +        /* XXX: cannot do more */
 +        return -1;
 +
 +    c->buffer_ptr = c->pb_buffer;
 +    c->buffer_end = c->pb_buffer + len;
 +    c->state = RTSPSTATE_SEND_REPLY;
 +    return 0;
 +}
 +
 +static int prepare_sdp_description(FFServerStream *stream, uint8_t **pbuffer,
 +                                   struct in_addr my_ip)
 +{
 +    AVFormatContext *avc;
 +    AVOutputFormat *rtp_format = av_guess_format("rtp", NULL, NULL);
 +    AVDictionaryEntry *entry = av_dict_get(stream->metadata, "title", NULL, 0);
 +    int i;
 +
 +    *pbuffer = NULL;
 +
 +    avc =  avformat_alloc_context();
 +    if (!avc || !rtp_format)
 +        return -1;
 +
 +    avc->oformat = rtp_format;
 +    av_dict_set(&avc->metadata, "title",
 +                entry ? entry->value : "No Title", 0);
 +    if (stream->is_multicast) {
 +        snprintf(avc->filename, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
 +                 inet_ntoa(stream->multicast_ip),
 +                 stream->multicast_port, stream->multicast_ttl);
 +    } else
 +        snprintf(avc->filename, 1024, "rtp://0.0.0.0");
 +
 +    for(i = 0; i < stream->nb_streams; i++) {
 +        AVStream *st = avformat_new_stream(avc, NULL);
 +        if (!st)
 +            goto sdp_done;
 +        avcodec_parameters_from_context(stream->streams[i]->codecpar, stream->streams[i]->codec);
 +        unlayer_stream(st, stream->streams[i]);
 +    }
 +#define PBUFFER_SIZE 2048
 +    *pbuffer = av_mallocz(PBUFFER_SIZE);
 +    if (!*pbuffer)
 +        goto sdp_done;
 +    av_sdp_create(&avc, 1, *pbuffer, PBUFFER_SIZE);
 +
 + sdp_done:
 +    av_freep(&avc->streams);
 +    av_dict_free(&avc->metadata);
 +    av_free(avc);
 +
 +    return *pbuffer ? strlen(*pbuffer) : AVERROR(ENOMEM);
 +}
 +
 +static void rtsp_cmd_options(HTTPContext *c, const char *url)
 +{
 +    /* rtsp_reply_header(c, RTSP_STATUS_OK); */
 +    avio_printf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
 +    avio_printf(c->pb, "CSeq: %d\r\n", c->seq);
 +    avio_printf(c->pb, "Public: %s\r\n",
 +                "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
 +    avio_printf(c->pb, "\r\n");
 +}
 +
 +static void rtsp_cmd_describe(HTTPContext *c, const char *url)
 +{
 +    FFServerStream *stream;
 +    char path1[1024];
 +    const char *path;
 +    uint8_t *content;
 +    int content_length;
 +    socklen_t len;
 +    struct sockaddr_in my_addr;
 +
 +    /* find which URL is asked */
 +    av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
 +    path = path1;
 +    if (*path == '/')
 +        path++;
 +
 +    for(stream = config.first_stream; stream; stream = stream->next) {
 +        if (!stream->is_feed &&
 +            stream->fmt && !strcmp(stream->fmt->name, "rtp") &&
 +            !strcmp(path, stream->filename)) {
 +            goto found;
 +        }
 +    }
 +    /* no stream found */
 +    rtsp_reply_error(c, RTSP_STATUS_NOT_FOUND);
 +    return;
 +
 + found:
 +    /* prepare the media description in SDP format */
 +
 +    /* get the host IP */
 +    len = sizeof(my_addr);
 +    getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
 +    content_length = prepare_sdp_description(stream, &content,
 +                                             my_addr.sin_addr);
 +    if (content_length < 0) {
 +        rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
 +        return;
 +    }
 +    rtsp_reply_header(c, RTSP_STATUS_OK);
 +    avio_printf(c->pb, "Content-Base: %s/\r\n", url);
 +    avio_printf(c->pb, "Content-Type: application/sdp\r\n");
 +    avio_printf(c->pb, "Content-Length: %d\r\n", content_length);
 +    avio_printf(c->pb, "\r\n");
 +    avio_write(c->pb, content, content_length);
 +    av_free(content);
 +}
 +
 +static HTTPContext *find_rtp_session(const char *session_id)
 +{
 +    HTTPContext *c;
 +
 +    if (session_id[0] == '\0')
 +        return NULL;
 +
 +    for(c = first_http_ctx; c; c = c->next) {
 +        if (!strcmp(c->session_id, session_id))
 +            return c;
 +    }
 +    return NULL;
 +}
 +
 +static RTSPTransportField *find_transport(RTSPMessageHeader *h, enum RTSPLowerTransport lower_transport)
 +{
 +    RTSPTransportField *th;
 +    int i;
 +
 +    for(i=0;i<h->nb_transports;i++) {
 +        th = &h->transports[i];
 +        if (th->lower_transport == lower_transport)
 +            return th;
 +    }
 +    return NULL;
 +}
 +
 +static void rtsp_cmd_setup(HTTPContext *c, const char *url,
 +                           RTSPMessageHeader *h)
 +{
 +    FFServerStream *stream;
 +    int stream_index, rtp_port, rtcp_port;
 +    char buf[1024];
 +    char path1[1024];
 +    const char *path;
 +    HTTPContext *rtp_c;
 +    RTSPTransportField *th;
 +    struct sockaddr_in dest_addr;
 +    RTSPActionServerSetup setup;
 +
 +    /* find which URL is asked */
 +    av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
 +    path = path1;
 +    if (*path == '/')
 +        path++;
 +
 +    /* now check each stream */
 +    for(stream = config.first_stream; stream; stream = stream->next) {
 +        if (stream->is_feed || !stream->fmt ||
 +            strcmp(stream->fmt->name, "rtp")) {
 +            continue;
 +        }
 +        /* accept aggregate filenames only if single stream */
 +        if (!strcmp(path, stream->filename)) {
 +            if (stream->nb_streams != 1) {
 +                rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
 +                return;
 +            }
 +            stream_index = 0;
 +            goto found;
 +        }
 +
 +        for(stream_index = 0; stream_index < stream->nb_streams;
 +            stream_index++) {
 +            snprintf(buf, sizeof(buf), "%s/streamid=%d",
 +                     stream->filename, stream_index);
 +            if (!strcmp(path, buf))
 +                goto found;
 +        }
 +    }
 +    /* no stream found */
 +    rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
 +    return;
 + found:
 +
 +    /* generate session id if needed */
 +    if (h->session_id[0] == '\0') {
 +        unsigned random0 = av_lfg_get(&random_state);
 +        unsigned random1 = av_lfg_get(&random_state);
 +        snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
 +                 random0, random1);
 +    }
 +
 +    /* find RTP session, and create it if none found */
 +    rtp_c = find_rtp_session(h->session_id);
 +    if (!rtp_c) {
 +        /* always prefer UDP */
 +        th = find_transport(h, RTSP_LOWER_TRANSPORT_UDP);
 +        if (!th) {
 +            th = find_transport(h, RTSP_LOWER_TRANSPORT_TCP);
 +            if (!th) {
 +                rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
 +                return;
 +            }
 +        }
 +
 +        rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
 +                                   th->lower_transport);
 +        if (!rtp_c) {
 +            rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
 +            return;
 +        }
 +
 +        /* open input stream */
 +        if (open_input_stream(rtp_c, "") < 0) {
 +            rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
 +            return;
 +        }
 +    }
 +
 +    /* test if stream is OK (test needed because several SETUP needs
 +     * to be done for a given file) */
 +    if (rtp_c->stream != stream) {
 +        rtsp_reply_error(c, RTSP_STATUS_SERVICE);
 +        return;
 +    }
 +
 +    /* test if stream is already set up */
 +    if (rtp_c->rtp_ctx[stream_index]) {
 +        rtsp_reply_error(c, RTSP_STATUS_STATE);
 +        return;
 +    }
 +
 +    /* check transport */
 +    th = find_transport(h, rtp_c->rtp_protocol);
 +    if (!th || (th->lower_transport == RTSP_LOWER_TRANSPORT_UDP &&
 +                th->client_port_min <= 0)) {
 +        rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
 +        return;
 +    }
 +
 +    /* setup default options */
 +    setup.transport_option[0] = '\0';
 +    dest_addr = rtp_c->from_addr;
 +    dest_addr.sin_port = htons(th->client_port_min);
 +
 +    /* setup stream */
 +    if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
 +        rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
 +        return;
 +    }
 +
 +    /* now everything is OK, so we can send the connection parameters */
 +    rtsp_reply_header(c, RTSP_STATUS_OK);
 +    /* session ID */
 +    avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
 +
 +    switch(rtp_c->rtp_protocol) {
 +    case RTSP_LOWER_TRANSPORT_UDP:
 +        rtp_port = ff_rtp_get_local_rtp_port(rtp_c->rtp_handles[stream_index]);
 +        rtcp_port = ff_rtp_get_local_rtcp_port(rtp_c->rtp_handles[stream_index]);
 +        avio_printf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
 +                    "client_port=%d-%d;server_port=%d-%d",
 +                    th->client_port_min, th->client_port_max,
 +                    rtp_port, rtcp_port);
 +        break;
 +    case RTSP_LOWER_TRANSPORT_TCP:
 +        avio_printf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
 +                    stream_index * 2, stream_index * 2 + 1);
 +        break;
 +    default:
 +        break;
 +    }
 +    if (setup.transport_option[0] != '\0')
 +        avio_printf(c->pb, ";%s", setup.transport_option);
 +    avio_printf(c->pb, "\r\n");
 +
 +
 +    avio_printf(c->pb, "\r\n");
 +}
 +
 +
 +/**
 + * find an RTP connection by using the session ID. Check consistency
 + * with filename
 + */
 +static HTTPContext *find_rtp_session_with_url(const char *url,
 +                                              const char *session_id)
 +{
 +    HTTPContext *rtp_c;
 +    char path1[1024];
 +    const char *path;
 +    char buf[1024];
 +    int s, len;
 +
 +    rtp_c = find_rtp_session(session_id);
 +    if (!rtp_c)
 +        return NULL;
 +
 +    /* find which URL is asked */
 +    av_url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
 +    path = path1;
 +    if (*path == '/')
 +        path++;
 +    if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
 +    for(s=0; s<rtp_c->stream->nb_streams; ++s) {
 +      snprintf(buf, sizeof(buf), "%s/streamid=%d",
 +        rtp_c->stream->filename, s);
 +      if(!strncmp(path, buf, sizeof(buf)))
 +        /* XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE
 +         * if nb_streams>1? */
 +        return rtp_c;
 +    }
 +    len = strlen(path);
 +    if (len > 0 && path[len - 1] == '/' &&
 +        !strncmp(path, rtp_c->stream->filename, len - 1))
 +        return rtp_c;
 +    return NULL;
 +}
 +
 +static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPMessageHeader *h)
 +{
 +    HTTPContext *rtp_c;
 +
 +    rtp_c = find_rtp_session_with_url(url, h->session_id);
 +    if (!rtp_c) {
 +        rtsp_reply_error(c, RTSP_STATUS_SESSION);
 +        return;
 +    }
 +
 +    if (rtp_c->state != HTTPSTATE_SEND_DATA &&
 +        rtp_c->state != HTTPSTATE_WAIT_FEED &&
 +        rtp_c->state != HTTPSTATE_READY) {
 +        rtsp_reply_error(c, RTSP_STATUS_STATE);
 +        return;
 +    }
 +
 +    rtp_c->state = HTTPSTATE_SEND_DATA;
 +
 +    /* now everything is OK, so we can send the connection parameters */
 +    rtsp_reply_header(c, RTSP_STATUS_OK);
 +    /* session ID */
 +    avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
 +    avio_printf(c->pb, "\r\n");
 +}
 +
 +static void rtsp_cmd_interrupt(HTTPContext *c, const char *url,
 +                               RTSPMessageHeader *h, int pause_only)
 +{
 +    HTTPContext *rtp_c;
 +
 +    rtp_c = find_rtp_session_with_url(url, h->session_id);
 +    if (!rtp_c) {
 +        rtsp_reply_error(c, RTSP_STATUS_SESSION);
 +        return;
 +    }
 +
 +    if (pause_only) {
 +        if (rtp_c->state != HTTPSTATE_SEND_DATA &&
 +            rtp_c->state != HTTPSTATE_WAIT_FEED) {
 +            rtsp_reply_error(c, RTSP_STATUS_STATE);
 +            return;
 +        }
 +        rtp_c->state = HTTPSTATE_READY;
 +        rtp_c->first_pts = AV_NOPTS_VALUE;
 +    }
 +
 +    /* now everything is OK, so we can send the connection parameters */
 +    rtsp_reply_header(c, RTSP_STATUS_OK);
 +    /* session ID */
 +    avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
 +    avio_printf(c->pb, "\r\n");
 +
 +    if (!pause_only)
 +        close_connection(rtp_c);
 +}
 +
 +/********************************************************************/
 +/* RTP handling */
 +
 +static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
 +                                       FFServerStream *stream,
 +                                       const char *session_id,
 +                                       enum RTSPLowerTransport rtp_protocol)
 +{
 +    HTTPContext *c = NULL;
 +    const char *proto_str;
 +
 +    /* XXX: should output a warning page when coming
 +     * close to the connection limit */
 +    if (nb_connections >= config.nb_max_connections)
 +        goto fail;
 +
 +    /* add a new connection */
 +    c = av_mallocz(sizeof(HTTPContext));
 +    if (!c)
 +        goto fail;
 +
 +    c->fd = -1;
 +    c->poll_entry = NULL;
 +    c->from_addr = *from_addr;
 +    c->buffer_size = IOBUFFER_INIT_SIZE;
 +    c->buffer = av_malloc(c->buffer_size);
 +    if (!c->buffer)
 +        goto fail;
 +    nb_connections++;
 +    c->stream = stream;
 +    av_strlcpy(c->session_id, session_id, sizeof(c->session_id));
 +    c->state = HTTPSTATE_READY;
 +    c->is_packetized = 1;
 +    c->rtp_protocol = rtp_protocol;
 +
 +    /* protocol is shown in statistics */
 +    switch(c->rtp_protocol) {
 +    case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
 +        proto_str = "MCAST";
 +        break;
 +    case RTSP_LOWER_TRANSPORT_UDP:
 +        proto_str = "UDP";
 +        break;
 +    case RTSP_LOWER_TRANSPORT_TCP:
 +        proto_str = "TCP";
 +        break;
 +    default:
 +        proto_str = "???";
 +        break;
 +    }
 +    av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol));
 +    av_strlcat(c->protocol, proto_str, sizeof(c->protocol));
 +
 +    current_bandwidth += stream->bandwidth;
 +
 +    c->next = first_http_ctx;
 +    first_http_ctx = c;
 +    return c;
 +
 + fail:
 +    if (c) {
 +        av_freep(&c->buffer);
 +        av_free(c);
 +    }
 +    return NULL;
 +}
 +
 +/**
 + * add a new RTP stream in an RTP connection (used in RTSP SETUP
 + * command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
 + * used.
 + */
 +static int rtp_new_av_stream(HTTPContext *c,
 +                             int stream_index, struct sockaddr_in *dest_addr,
 +                             HTTPContext *rtsp_c)
 +{
 +    AVFormatContext *ctx;
 +    AVStream *st;
 +    char *ipaddr;
 +    URLContext *h = NULL;
 +    uint8_t *dummy_buf;
 +    int max_packet_size;
 +    void *st_internal;
 +
 +    /* now we can open the relevant output stream */
 +    ctx = avformat_alloc_context();
 +    if (!ctx)
 +        return -1;
 +    ctx->oformat = av_guess_format("rtp", NULL, NULL);
 +
 +    st = avformat_new_stream(ctx, NULL);
 +    if (!st)
 +        goto fail;
 +
 +    st_internal = st->internal;
 +
 +    if (!c->stream->feed ||
 +        c->stream->feed == c->stream)
 +        unlayer_stream(st, c->stream->streams[stream_index]);
 +    else
 +        unlayer_stream(st,
 +               c->stream->feed->streams[c->stream->feed_streams[stream_index]]);
 +    av_assert0(st->priv_data == NULL);
 +    av_assert0(st->internal == st_internal);
 +
 +    /* build destination RTP address */
 +    ipaddr = inet_ntoa(dest_addr->sin_addr);
 +
 +    switch(c->rtp_protocol) {
 +    case RTSP_LOWER_TRANSPORT_UDP:
 +    case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
 +        /* RTP/UDP case */
 +
 +        /* XXX: also pass as parameter to function ? */
 +        if (c->stream->is_multicast) {
 +            int ttl;
 +            ttl = c->stream->multicast_ttl;
 +            if (!ttl)
 +                ttl = 16;
 +            snprintf(ctx->filename, sizeof(ctx->filename),
 +                     "rtp://%s:%d?multicast=1&ttl=%d",
 +                     ipaddr, ntohs(dest_addr->sin_port), ttl);
 +        } else {
 +            snprintf(ctx->filename, sizeof(ctx->filename),
 +                     "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
 +        }
 +
 +        if (ffurl_open(&h, ctx->filename, AVIO_FLAG_WRITE, NULL, NULL) < 0)
 +            goto fail;
 +        c->rtp_handles[stream_index] = h;
 +        max_packet_size = h->max_packet_size;
 +        break;
 +    case RTSP_LOWER_TRANSPORT_TCP:
 +        /* RTP/TCP case */
 +        c->rtsp_c = rtsp_c;
 +        max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
 +        break;
 +    default:
 +        goto fail;
 +    }
 +
 +    http_log("%s:%d - - \"PLAY %s/streamid=%d %s\"\n",
 +             ipaddr, ntohs(dest_addr->sin_port),
 +             c->stream->filename, stream_index, c->protocol);
 +
 +    /* normally, no packets should be output here, but the packet size may
 +     * be checked */
 +    if (ffio_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0)
 +        /* XXX: close stream */
 +        goto fail;
 +
 +    if (avformat_write_header(ctx, NULL) < 0) {
 +    fail:
 +        if (h)
 +            ffurl_close(h);
 +        av_free(st);
 +        av_free(ctx);
 +        return -1;
 +    }
 +    avio_close_dyn_buf(ctx->pb, &dummy_buf);
 +    ctx->pb = NULL;
 +    av_free(dummy_buf);
 +
 +    c->rtp_ctx[stream_index] = ctx;
 +    return 0;
 +}
 +
 +/********************************************************************/
 +/* ffserver initialization */
 +
 +/* FIXME: This code should use avformat_new_stream() */
 +static LayeredAVStream *add_av_stream1(FFServerStream *stream,
 +                                AVCodecContext *codec, int copy)
 +{
 +    LayeredAVStream *fst;
 +
 +    if(stream->nb_streams >= FF_ARRAY_ELEMS(stream->streams))
 +        return NULL;
 +
 +    fst = av_mallocz(sizeof(*fst));
 +    if (!fst)
 +        return NULL;
 +    if (copy) {
 +        fst->codec = avcodec_alloc_context3(codec->codec);
 +        if (!fst->codec) {
 +            av_free(fst);
 +            return NULL;
 +        }
 +        avcodec_copy_context(fst->codec, codec);
 +    } else
 +        /* live streams must use the actual feed's codec since it may be
 +         * updated later to carry extradata needed by them.
 +         */
 +        fst->codec = codec;
 +
 +    //NOTE we previously allocated internal & internal->avctx, these seemed uneeded though
 +    fst->codecpar = avcodec_parameters_alloc();
 +    fst->index = stream->nb_streams;
 +    fst->time_base = codec->time_base;
 +    fst->pts_wrap_bits = 33;
 +    fst->sample_aspect_ratio = codec->sample_aspect_ratio;
 +    stream->streams[stream->nb_streams++] = fst;
 +    return fst;
 +}
 +
 +/* return the stream number in the feed */
 +static int add_av_stream(FFServerStream *feed, LayeredAVStream *st)
 +{
 +    LayeredAVStream *fst;
 +    AVCodecContext *av, *av1;
 +    int i;
 +
 +    av = st->codec;
 +    for(i=0;i<feed->nb_streams;i++) {
 +        av1 = feed->streams[i]->codec;
 +        if (av1->codec_id == av->codec_id &&
 +            av1->codec_type == av->codec_type &&
 +            av1->bit_rate == av->bit_rate) {
 +
 +            switch(av->codec_type) {
 +            case AVMEDIA_TYPE_AUDIO:
 +                if (av1->channels == av->channels &&
 +                    av1->sample_rate == av->sample_rate)
 +                    return i;
 +                break;
 +            case AVMEDIA_TYPE_VIDEO:
 +                if (av1->width == av->width &&
 +                    av1->height == av->height &&
 +                    av1->time_base.den == av->time_base.den &&
 +                    av1->time_base.num == av->time_base.num &&
 +                    av1->gop_size == av->gop_size)
 +                    return i;
 +                break;
 +            default:
 +                abort();
 +            }
 +        }
 +    }
 +
 +    fst = add_av_stream1(feed, av, 0);
 +    if (!fst)
 +        return -1;
 +    if (st->recommended_encoder_configuration)
 +        fst->recommended_encoder_configuration =
 +            av_strdup(st->recommended_encoder_configuration);
 +    return feed->nb_streams - 1;
 +}
 +
 +static void remove_stream(FFServerStream *stream)
 +{
 +    FFServerStream **ps;
 +    ps = &config.first_stream;
 +    while (*ps) {
 +        if (*ps == stream)
 +            *ps = (*ps)->next;
 +        else
 +            ps = &(*ps)->next;
 +    }
 +}
 +
 +/* compute the needed AVStream for each file */
 +static void build_file_streams(void)
 +{
 +    FFServerStream *stream;
 +    AVFormatContext *infile;
 +    int i, ret;
 +
 +    /* gather all streams */
 +    for(stream = config.first_stream; stream; stream = stream->next) {
 +        infile = NULL;
 +
 +        if (stream->stream_type != STREAM_TYPE_LIVE || stream->feed)
 +            continue;
 +
 +        /* the stream comes from a file */
 +        /* try to open the file */
 +        /* open stream */
 +
 +
 +        /* specific case: if transport stream output to RTP,
 +         * we use a raw transport stream reader */
 +        if (stream->fmt && !strcmp(stream->fmt->name, "rtp"))
 +            av_dict_set(&stream->in_opts, "mpeg2ts_compute_pcr", "1", 0);
 +
 +        if (!stream->feed_filename[0]) {
 +            http_log("Unspecified feed file for stream '%s'\n",
 +                     stream->filename);
 +            goto fail;
 +        }
 +
 +        http_log("Opening feed file '%s' for stream '%s'\n",
 +                 stream->feed_filename, stream->filename);
 +
 +        ret = avformat_open_input(&infile, stream->feed_filename,
 +                                  stream->ifmt, &stream->in_opts);
 +        if (ret < 0) {
 +            http_log("Could not open '%s': %s\n", stream->feed_filename,
 +                     av_err2str(ret));
 +            /* remove stream (no need to spend more time on it) */
 +        fail:
 +            remove_stream(stream);
 +        } else {
 +            /* find all the AVStreams inside and reference them in
 +             * 'stream' */
 +            if (avformat_find_stream_info(infile, NULL) < 0) {
 +                http_log("Could not find codec parameters from '%s'\n",
 +                         stream->feed_filename);
 +                avformat_close_input(&infile);
 +                goto fail;
 +            }
 +
 +            for(i=0;i<infile->nb_streams;i++)
 +                add_av_stream1(stream, infile->streams[i]->codec, 1);
 +
 +            avformat_close_input(&infile);
 +        }
 +    }
 +}
 +
 +static inline
 +int check_codec_match(LayeredAVStream *ccf, AVStream *ccs, int stream)
 +{
 +    int matches = 1;
 +
 +/* FIXME: Missed check on AVCodecContext.flags */
 +#define CHECK_CODEC(x)  (ccf->codecpar->x != ccs->codecpar->x)
 +    if (CHECK_CODEC(codec_id) || CHECK_CODEC(codec_type)) {
 +        http_log("Codecs do not match for stream %d\n", stream);
 +        matches = 0;
 +    } else if (CHECK_CODEC(bit_rate)) {
 +        http_log("Codec bitrates do not match for stream %d\n", stream);
 +        matches = 0;
 +    } else if (ccf->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
 +        if (av_cmp_q(ccf->time_base, ccs->time_base) ||
 +            CHECK_CODEC(width) || CHECK_CODEC(height)) {
 +            http_log("Codec width, height or framerate do not match for stream %d\n", stream);
 +            matches = 0;
 +        }
 +    } else if (ccf->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
 +        if (CHECK_CODEC(sample_rate) ||
 +            CHECK_CODEC(channels) ||
 +            CHECK_CODEC(frame_size)) {
 +            http_log("Codec sample_rate, channels, frame_size do not match for stream %d\n", stream);
 +            matches = 0;
 +        }
 +    } else {
 +        http_log("Unknown codec type for stream %d\n", stream);
 +        matches = 0;
 +    }
 +
 +    return matches;
 +}
 +
 +/* compute the needed AVStream for each feed */
 +static int build_feed_streams(void)
 +{
 +    FFServerStream *stream, *feed;
 +    int i, fd;
 +
 +    /* gather all streams */
 +    for(stream = config.first_stream; stream; stream = stream->next) {
 +        feed = stream->feed;
 +        if (!feed)
 +            continue;
 +
 +        if (stream->is_feed) {
 +            for(i=0;i<stream->nb_streams;i++)
 +                stream->feed_streams[i] = i;
 +            continue;
 +        }
 +        /* we handle a stream coming from a feed */
 +        for(i=0;i<stream->nb_streams;i++)
 +            stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
 +    }
 +
 +    /* create feed files if needed */
 +    for(feed = config.first_feed; feed; feed = feed->next_feed) {
 +
 +        if (avio_check(feed->feed_filename, AVIO_FLAG_READ) > 0) {
 +            AVFormatContext *s = NULL;
 +            int matches = 0;
 +
 +            /* See if it matches */
 +
 +            if (avformat_open_input(&s, feed->feed_filename, NULL, NULL) < 0) {
 +                http_log("Deleting feed file '%s' as it appears "
 +                            "to be corrupt\n",
 +                         feed->feed_filename);
 +                goto drop;
 +            }
 +
 +            /* set buffer size */
 +            if (ffio_set_buf_size(s->pb, FFM_PACKET_SIZE) < 0) {
 +                http_log("Failed to set buffer size\n");
 +                avformat_close_input(&s);
 +                goto bail;
 +            }
 +
 +            /* Now see if it matches */
 +            if (s->nb_streams != feed->nb_streams) {
 +                http_log("Deleting feed file '%s' as stream counts "
 +                            "differ (%d != %d)\n",
 +                         feed->feed_filename, s->nb_streams, feed->nb_streams);
 +                goto drop;
 +            }
 +
 +            matches = 1;
 +            for(i=0;i<s->nb_streams;i++) {
 +                AVStream *ss;
 +                LayeredAVStream *sf;
 +
 +                sf = feed->streams[i];
 +                ss = s->streams[i];
 +
 +                if (sf->index != ss->index || sf->id != ss->id) {
 +                    http_log("Index & Id do not match for stream %d (%s)\n",
 +                             i, feed->feed_filename);
 +                    matches = 0;
 +                    break;
 +                }
 +
 +                matches = check_codec_match (sf, ss, i);
 +                if (!matches)
 +                    break;
 +            }
 +
 +drop:
 +            if (s)
 +                avformat_close_input(&s);
 +
 +            if (!matches) {
 +                if (feed->readonly) {
 +                    http_log("Unable to delete read-only feed file '%s'\n",
 +                             feed->feed_filename);
 +                    goto bail;
 +                }
 +                unlink(feed->feed_filename);
 +            }
 +        }
 +
 +        if (avio_check(feed->feed_filename, AVIO_FLAG_WRITE) <= 0) {
 +            AVFormatContext *s = avformat_alloc_context();
 +
 +            if (!s) {
 +                http_log("Failed to allocate context\n");
 +                goto bail;
 +            }
 +
 +            if (feed->readonly) {
 +                http_log("Unable to create feed file '%s' as it is "
 +                            "marked readonly\n",
 +                         feed->feed_filename);
 +                avformat_free_context(s);
 +                goto bail;
 +            }
 +
 +            /* only write the header of the ffm file */
 +            if (avio_open(&s->pb, feed->feed_filename, AVIO_FLAG_WRITE) < 0) {
 +                http_log("Could not open output feed file '%s'\n",
 +                         feed->feed_filename);
 +                avformat_free_context(s);
 +                goto bail;
 +            }
 +            s->oformat = feed->fmt;
 +            for (i = 0; i<feed->nb_streams; i++) {
 +                AVStream *st = avformat_new_stream(s, NULL); // FIXME free this
 +                if (!st) {
 +                    http_log("Failed to allocate stream\n");
 +                    goto bail;
 +                }
 +                unlayer_stream(st, feed->streams[i]);
 +            }
 +            if (avformat_write_header(s, NULL) < 0) {
 +                http_log("Container doesn't support the required parameters\n");
 +                avio_closep(&s->pb);
 +                s->streams = NULL;
 +                s->nb_streams = 0;
 +                avformat_free_context(s);
 +                goto bail;
 +            }
 +            /* XXX: need better API */
 +            av_freep(&s->priv_data);
 +            avio_closep(&s->pb);
 +            s->streams = NULL;
 +            s->nb_streams = 0;
 +            avformat_free_context(s);
 +        }
 +
 +        /* get feed size and write index */
 +        fd = open(feed->feed_filename, O_RDONLY);
 +        if (fd < 0) {
 +            http_log("Could not open output feed file '%s'\n",
 +                    feed->feed_filename);
 +            goto bail;
 +        }
 +
 +        feed->feed_write_index = FFMAX(ffm_read_write_index(fd),
 +                                       FFM_PACKET_SIZE);
 +        feed->feed_size = lseek(fd, 0, SEEK_END);
 +        /* ensure that we do not wrap before the end of file */
 +        if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
 +            feed->feed_max_size = feed->feed_size;
 +
 +        close(fd);
 +    }
 +    return 0;
 +
 +bail:
 +    return -1;
 +}
 +
 +/* compute the bandwidth used by each stream */
 +static void compute_bandwidth(void)
 +{
 +    unsigned bandwidth;
 +    int i;
 +    FFServerStream *stream;
 +
 +    for(stream = config.first_stream; stream; stream = stream->next) {
 +        bandwidth = 0;
 +        for(i=0;i<stream->nb_streams;i++) {
 +            LayeredAVStream *st = stream->streams[i];
 +            switch(st->codec->codec_type) {
 +            case AVMEDIA_TYPE_AUDIO:
 +            case AVMEDIA_TYPE_VIDEO:
 +                bandwidth += st->codec->bit_rate;
 +                break;
 +            default:
 +                break;
 +            }
 +        }
 +        stream->bandwidth = (bandwidth + 999) / 1000;
 +    }
 +}
 +
 +static void handle_child_exit(int sig)
 +{
 +    pid_t pid;
 +    int status;
 +    time_t uptime;
 +
 +    while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
 +        FFServerStream *feed;
 +
 +        for (feed = config.first_feed; feed; feed = feed->next) {
 +            if (feed->pid != pid)
 +                continue;
 +
 +            uptime = time(0) - feed->pid_start;
 +            feed->pid = 0;
 +            fprintf(stderr,
 +                    "%s: Pid %"PRId64" exited with status %d after %"PRId64" "
 +                        "seconds\n",
 +                    feed->filename, (int64_t) pid, status, (int64_t)uptime);
 +
 +            if (uptime < 30)
 +                /* Turn off any more restarts */
 +                ffserver_free_child_args(&feed->child_argv);
 +        }
 +    }
 +
 +    need_to_start_children = 1;
 +}
 +
 +static void opt_debug(void)
 +{
 +    config.debug = 1;
 +    snprintf(config.logfilename, sizeof(config.logfilename), "-");
 +}
 +
 +void show_help_default(const char *opt, const char *arg)
 +{
 +    printf("usage: ffserver [options]\n"
 +           "Hyper fast multi format Audio/Video streaming server\n");
 +    printf("\n");
 +    show_help_options(options, "Main options:", 0, 0, 0);
 +}
 +
 +static const OptionDef options[] = {
++    CMDUTILS_COMMON_OPTIONS
 +    { "n", OPT_BOOL, {(void *)&no_launch }, "enable no-launch mode" },
 +    { "d", 0, {(void*)opt_debug}, "enable debug mode" },
 +    { "f", HAS_ARG | OPT_STRING, {(void*)&config.filename }, "use configfile instead of /etc/ffserver.conf", "configfile" },
 +    { NULL },
 +};
 +
 +int main(int argc, char **argv)
 +{
 +    struct sigaction sigact = { { 0 } };
 +    int cfg_parsed;
 +    int ret = EXIT_FAILURE;
 +
 +    init_dynload();
 +
 +    config.filename = av_strdup("/etc/ffserver.conf");
 +
 +    parse_loglevel(argc, argv, options);
 +    av_register_all();
 +    avformat_network_init();
 +
 +    show_banner(argc, argv, options);
 +
 +    my_program_name = argv[0];
 +
 +    parse_options(NULL, argc, argv, options, NULL);
 +
 +    unsetenv("http_proxy");             /* Kill the http_proxy */
 +
 +    av_lfg_init(&random_state, av_get_random_seed());
 +
 +    sigact.sa_handler = handle_child_exit;
 +    sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
 +    sigaction(SIGCHLD, &sigact, 0);
 +
 +    if ((cfg_parsed = ffserver_parse_ffconfig(config.filename, &config)) < 0) {
 +        fprintf(stderr, "Error reading configuration file '%s': %s\n",
 +                config.filename, av_err2str(cfg_parsed));
 +        goto bail;
 +    }
 +
 +    /* open log file if needed */
 +    if (config.logfilename[0] != '\0') {
 +        if (!strcmp(config.logfilename, "-"))
 +            logfile = stdout;
 +        else
 +            logfile = fopen(config.logfilename, "a");
 +        av_log_set_callback(http_av_log);
 +    }
 +
 +    build_file_streams();
 +
 +    if (build_feed_streams() < 0) {
 +        http_log("Could not setup feed streams\n");
 +        goto bail;
 +    }
 +
 +    compute_bandwidth();
 +
 +    /* signal init */
 +    signal(SIGPIPE, SIG_IGN);
 +
 +    if (http_server() < 0) {
 +        http_log("Could not start server\n");
 +        goto bail;
 +    }
 +
 +    ret=EXIT_SUCCESS;
 +
 +bail:
 +    av_freep (&config.filename);
 +    avformat_network_deinit();
 +    return ret;
 +}
index bfda026,0000000..bb34e51
mode 100644,000000..100644
--- /dev/null
@@@ -1,35 -1,0 +1,33 @@@
- cmdutils_common_opts.h
 +Files without standard license headers:
- cmdutils_common_opts.h
 +compat/avisynth/windowsPorts/basicDataTypeConversions.h
 +compat/avisynth/windowsPorts/windows2linux.h
 +libavcodec/file_open.c
 +libavcodec/interplayacm.c
 +libavcodec/log2_tab.c
 +libavcodec/reverse.c
 +libavdevice/file_open.c
 +libavfilter/log2_tab.c
 +libavformat/file_open.c
 +libavformat/golomb_tab.c
 +libavformat/log2_tab.c
 +libswresample/log2_tab.c
 +libswscale/log2_tab.c
 +tools/uncoded_frame.c
 +tools/yuvcmp.c
 +Headers without standard inclusion guards:
 +compat/avisynth/avisynth_c.h
 +compat/avisynth/avs/capi.h
 +compat/avisynth/avs/config.h
 +compat/avisynth/avs/types.h
 +compat/avisynth/avxsynth_c.h
 +compat/avisynth/windowsPorts/basicDataTypeConversions.h
 +compat/avisynth/windowsPorts/windows2linux.h
 +compat/cuda/dynlink_cuda.h
 +compat/cuda/dynlink_cuviddec.h
 +compat/cuda/dynlink_loader.h
 +compat/cuda/dynlink_nvcuvid.h
 +compat/float/float.h
 +compat/float/limits.h
 +compat/nvenc/nvEncodeAPI.h
 +Use of av_clip() where av_clip_uintp2() could be used:
 +Use of av_clip() where av_clip_intp2() could be used: