[FFmpeg-cvslog] Merge commit 'beb62dac629603eb074a44c44389c230b5caac7c'

Hendrik Leppkes git at videolan.org
Fri Oct 7 14:16:59 EEST 2016


ffmpeg | branch: master | Hendrik Leppkes <h.leppkes at gmail.com> | Fri Oct  7 13:16:36 2016 +0200| [6f74e3cde6147b9a9bda5071e505e9ffbb99096a] | committer: Hendrik Leppkes

Merge commit 'beb62dac629603eb074a44c44389c230b5caac7c'

* commit 'beb62dac629603eb074a44c44389c230b5caac7c':
  Use AVFrame.pts instead of deprecated pkt_pts.

Merged-by: Hendrik Leppkes <h.leppkes at gmail.com>

> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=6f74e3cde6147b9a9bda5071e505e9ffbb99096a
---

 ffmpeg.c                  |  3 +--
 ffplay.c                  |  8 ++------
 ffprobe.c                 |  4 ++--
 tests/api/api-h264-test.c |  2 +-
 tests/api/api-seek-test.c | 10 +++++-----
 5 files changed, 11 insertions(+), 16 deletions(-)

diff --git a/ffmpeg.c b/ffmpeg.c
index 6748ad8..0060511 100644
--- a/ffmpeg.c
+++ b/ffmpeg.c
@@ -2108,8 +2108,7 @@ static int decode_audio(InputStream *ist, AVPacket *pkt, int *got_output)
             }
     }
 
-    if (decoded_frame->pkt_pts != AV_NOPTS_VALUE) {
-        decoded_frame->pts = decoded_frame->pkt_pts;
+    if (decoded_frame->pts != AV_NOPTS_VALUE) {
         decoded_frame_tb   = ist->st->time_base;
     } else if (pkt && pkt->pts != AV_NOPTS_VALUE) {
         decoded_frame->pts = pkt->pts;
diff --git a/ffplay.c b/ffplay.c
index 6d43191..79dc768 100644
--- a/ffplay.c
+++ b/ffplay.c
@@ -588,9 +588,7 @@ static int decoder_decode_frame(Decoder *d, AVFrame *frame, AVSubtitle *sub) {
                 if (got_frame) {
                     if (decoder_reorder_pts == -1) {
                         frame->pts = av_frame_get_best_effort_timestamp(frame);
-                    } else if (decoder_reorder_pts) {
-                        frame->pts = frame->pkt_pts;
-                    } else {
+                    } else if (!decoder_reorder_pts) {
                         frame->pts = frame->pkt_dts;
                     }
                 }
@@ -600,9 +598,7 @@ static int decoder_decode_frame(Decoder *d, AVFrame *frame, AVSubtitle *sub) {
                 if (got_frame) {
                     AVRational tb = (AVRational){1, frame->sample_rate};
                     if (frame->pts != AV_NOPTS_VALUE)
-                        frame->pts = av_rescale_q(frame->pts, d->avctx->time_base, tb);
-                    else if (frame->pkt_pts != AV_NOPTS_VALUE)
-                        frame->pts = av_rescale_q(frame->pkt_pts, av_codec_get_pkt_timebase(d->avctx), tb);
+                        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) {
diff --git a/ffprobe.c b/ffprobe.c
index bb3979c..662137c 100644
--- a/ffprobe.c
+++ b/ffprobe.c
@@ -1884,8 +1884,8 @@ static void show_frame(WriterContext *w, AVFrame *frame, AVStream *stream,
     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->pkt_pts);
-    print_time("pkt_pts_time",          frame->pkt_pts, &stream->time_base);
+    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", av_frame_get_best_effort_timestamp(frame));
diff --git a/tests/api/api-h264-test.c b/tests/api/api-h264-test.c
index ef3a1fe..7bdf92b 100644
--- a/tests/api/api-h264-test.c
+++ b/tests/api/api-h264-test.c
@@ -132,7 +132,7 @@ static int video_decode_example(const char *input_filename)
                     return number_of_written_bytes;
                 }
                 printf("%d, %10"PRId64", %10"PRId64", %8"PRId64", %8d, 0x%08lx\n", video_stream,
-                        fr->pkt_pts, fr->pkt_dts, av_frame_get_pkt_duration(fr),
+                        fr->pts, fr->pkt_dts, av_frame_get_pkt_duration(fr),
                         number_of_written_bytes, av_adler32_update(0, (const uint8_t*)byte_buffer, number_of_written_bytes));
             }
             av_packet_unref(&pkt);
diff --git a/tests/api/api-seek-test.c b/tests/api/api-seek-test.c
index df47a5f..8117df4 100644
--- a/tests/api/api-seek-test.c
+++ b/tests/api/api-seek-test.c
@@ -129,23 +129,23 @@ static int compute_crc_of_packets(AVFormatContext *fmt_ctx, int video_stream,
                     av_log(NULL, AV_LOG_ERROR, "Can't copy image to buffer\n");
                     return number_of_written_bytes;
                 }
-                if ((fr->pkt_pts > ts_end) && (!no_seeking))
+                if ((fr->pts > ts_end) && (!no_seeking))
                     break;
                 crc = av_adler32_update(0, (const uint8_t*)byte_buffer, number_of_written_bytes);
-                printf("%10"PRId64", 0x%08lx\n", fr->pkt_pts, crc);
+                printf("%10"PRId64", 0x%08lx\n", fr->pts, crc);
                 if (no_seeking) {
-                    if (add_crc_to_array(crc, fr->pkt_pts) < 0)
+                    if (add_crc_to_array(crc, fr->pts) < 0)
                         return -1;
                 }
                 else {
-                    if (compare_crc_in_array(crc, fr->pkt_pts) < 0)
+                    if (compare_crc_in_array(crc, fr->pts) < 0)
                         return -1;
                 }
             }
         }
         av_packet_unref(&pkt);
         av_init_packet(&pkt);
-    } while ((!end_of_stream || got_frame) && (no_seeking || (fr->pkt_pts + av_frame_get_pkt_duration(fr) <= ts_end)));
+    } while ((!end_of_stream || got_frame) && (no_seeking || (fr->pts + av_frame_get_pkt_duration(fr) <= ts_end)));
 
     av_packet_unref(&pkt);
     av_freep(&byte_buffer);


======================================================================

diff --cc ffmpeg.c
index 6748ad8,0000000..0060511
mode 100644,000000..100644
--- a/ffmpeg.c
+++ b/ffmpeg.c
@@@ -1,4527 -1,0 +1,4526 @@@
 +/*
 + * Copyright (c) 2000-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
 + * multimedia converter based on the FFmpeg libraries
 + */
 +
 +#include "config.h"
 +#include <ctype.h>
 +#include <string.h>
 +#include <math.h>
 +#include <stdlib.h>
 +#include <errno.h>
 +#include <limits.h>
 +#include <stdint.h>
 +
 +#if HAVE_IO_H
 +#include <io.h>
 +#endif
 +#if HAVE_UNISTD_H
 +#include <unistd.h>
 +#endif
 +
 +#include "libavformat/avformat.h"
 +#include "libavdevice/avdevice.h"
 +#include "libswresample/swresample.h"
 +#include "libavutil/opt.h"
 +#include "libavutil/channel_layout.h"
 +#include "libavutil/parseutils.h"
 +#include "libavutil/samplefmt.h"
 +#include "libavutil/fifo.h"
 +#include "libavutil/internal.h"
 +#include "libavutil/intreadwrite.h"
 +#include "libavutil/dict.h"
 +#include "libavutil/mathematics.h"
 +#include "libavutil/pixdesc.h"
 +#include "libavutil/avstring.h"
 +#include "libavutil/libm.h"
 +#include "libavutil/imgutils.h"
 +#include "libavutil/timestamp.h"
 +#include "libavutil/bprint.h"
 +#include "libavutil/time.h"
 +#include "libavutil/threadmessage.h"
 +#include "libavcodec/mathops.h"
 +#include "libavformat/os_support.h"
 +
 +# include "libavfilter/avfilter.h"
 +# include "libavfilter/buffersrc.h"
 +# include "libavfilter/buffersink.h"
 +
 +#if HAVE_SYS_RESOURCE_H
 +#include <sys/time.h>
 +#include <sys/types.h>
 +#include <sys/resource.h>
 +#elif HAVE_GETPROCESSTIMES
 +#include <windows.h>
 +#endif
 +#if HAVE_GETPROCESSMEMORYINFO
 +#include <windows.h>
 +#include <psapi.h>
 +#endif
 +#if HAVE_SETCONSOLECTRLHANDLER
 +#include <windows.h>
 +#endif
 +
 +
 +#if HAVE_SYS_SELECT_H
 +#include <sys/select.h>
 +#endif
 +
 +#if HAVE_TERMIOS_H
 +#include <fcntl.h>
 +#include <sys/ioctl.h>
 +#include <sys/time.h>
 +#include <termios.h>
 +#elif HAVE_KBHIT
 +#include <conio.h>
 +#endif
 +
 +#if HAVE_PTHREADS
 +#include <pthread.h>
 +#endif
 +
 +#include <time.h>
 +
 +#include "ffmpeg.h"
 +#include "cmdutils.h"
 +
 +#include "libavutil/avassert.h"
 +
 +const char program_name[] = "ffmpeg";
 +const int program_birth_year = 2000;
 +
 +static FILE *vstats_file;
 +
 +const char *const forced_keyframes_const_names[] = {
 +    "n",
 +    "n_forced",
 +    "prev_forced_n",
 +    "prev_forced_t",
 +    "t",
 +    NULL
 +};
 +
 +static void do_video_stats(OutputStream *ost, int frame_size);
 +static int64_t getutime(void);
 +static int64_t getmaxrss(void);
 +
 +static int run_as_daemon  = 0;
 +static int nb_frames_dup = 0;
 +static int nb_frames_drop = 0;
 +static int64_t decode_error_stat[2];
 +
 +static int current_time;
 +AVIOContext *progress_avio = NULL;
 +
 +static uint8_t *subtitle_out;
 +
 +InputStream **input_streams = NULL;
 +int        nb_input_streams = 0;
 +InputFile   **input_files   = NULL;
 +int        nb_input_files   = 0;
 +
 +OutputStream **output_streams = NULL;
 +int         nb_output_streams = 0;
 +OutputFile   **output_files   = NULL;
 +int         nb_output_files   = 0;
 +
 +FilterGraph **filtergraphs;
 +int        nb_filtergraphs;
 +
 +#if HAVE_TERMIOS_H
 +
 +/* init terminal so that we can grab keys */
 +static struct termios oldtty;
 +static int restore_tty;
 +#endif
 +
 +#if HAVE_PTHREADS
 +static void free_input_threads(void);
 +#endif
 +
 +/* sub2video hack:
 +   Convert subtitles to video with alpha to insert them in filter graphs.
 +   This is a temporary solution until libavfilter gets real subtitles support.
 + */
 +
 +static int sub2video_get_blank_frame(InputStream *ist)
 +{
 +    int ret;
 +    AVFrame *frame = ist->sub2video.frame;
 +
 +    av_frame_unref(frame);
 +    ist->sub2video.frame->width  = ist->dec_ctx->width  ? ist->dec_ctx->width  : ist->sub2video.w;
 +    ist->sub2video.frame->height = ist->dec_ctx->height ? ist->dec_ctx->height : ist->sub2video.h;
 +    ist->sub2video.frame->format = AV_PIX_FMT_RGB32;
 +    if ((ret = av_frame_get_buffer(frame, 32)) < 0)
 +        return ret;
 +    memset(frame->data[0], 0, frame->height * frame->linesize[0]);
 +    return 0;
 +}
 +
 +static void sub2video_copy_rect(uint8_t *dst, int dst_linesize, int w, int h,
 +                                AVSubtitleRect *r)
 +{
 +    uint32_t *pal, *dst2;
 +    uint8_t *src, *src2;
 +    int x, y;
 +
 +    if (r->type != SUBTITLE_BITMAP) {
 +        av_log(NULL, AV_LOG_WARNING, "sub2video: non-bitmap subtitle\n");
 +        return;
 +    }
 +    if (r->x < 0 || r->x + r->w > w || r->y < 0 || r->y + r->h > h) {
 +        av_log(NULL, AV_LOG_WARNING, "sub2video: rectangle (%d %d %d %d) overflowing %d %d\n",
 +            r->x, r->y, r->w, r->h, w, h
 +        );
 +        return;
 +    }
 +
 +    dst += r->y * dst_linesize + r->x * 4;
 +    src = r->data[0];
 +    pal = (uint32_t *)r->data[1];
 +    for (y = 0; y < r->h; y++) {
 +        dst2 = (uint32_t *)dst;
 +        src2 = src;
 +        for (x = 0; x < r->w; x++)
 +            *(dst2++) = pal[*(src2++)];
 +        dst += dst_linesize;
 +        src += r->linesize[0];
 +    }
 +}
 +
 +static void sub2video_push_ref(InputStream *ist, int64_t pts)
 +{
 +    AVFrame *frame = ist->sub2video.frame;
 +    int i;
 +
 +    av_assert1(frame->data[0]);
 +    ist->sub2video.last_pts = frame->pts = pts;
 +    for (i = 0; i < ist->nb_filters; i++)
 +        av_buffersrc_add_frame_flags(ist->filters[i]->filter, frame,
 +                                     AV_BUFFERSRC_FLAG_KEEP_REF |
 +                                     AV_BUFFERSRC_FLAG_PUSH);
 +}
 +
 +static void sub2video_update(InputStream *ist, AVSubtitle *sub)
 +{
 +    AVFrame *frame = ist->sub2video.frame;
 +    int8_t *dst;
 +    int     dst_linesize;
 +    int num_rects, i;
 +    int64_t pts, end_pts;
 +
 +    if (!frame)
 +        return;
 +    if (sub) {
 +        pts       = av_rescale_q(sub->pts + sub->start_display_time * 1000LL,
 +                                 AV_TIME_BASE_Q, ist->st->time_base);
 +        end_pts   = av_rescale_q(sub->pts + sub->end_display_time   * 1000LL,
 +                                 AV_TIME_BASE_Q, ist->st->time_base);
 +        num_rects = sub->num_rects;
 +    } else {
 +        pts       = ist->sub2video.end_pts;
 +        end_pts   = INT64_MAX;
 +        num_rects = 0;
 +    }
 +    if (sub2video_get_blank_frame(ist) < 0) {
 +        av_log(ist->dec_ctx, AV_LOG_ERROR,
 +               "Impossible to get a blank canvas.\n");
 +        return;
 +    }
 +    dst          = frame->data    [0];
 +    dst_linesize = frame->linesize[0];
 +    for (i = 0; i < num_rects; i++)
 +        sub2video_copy_rect(dst, dst_linesize, frame->width, frame->height, sub->rects[i]);
 +    sub2video_push_ref(ist, pts);
 +    ist->sub2video.end_pts = end_pts;
 +}
 +
 +static void sub2video_heartbeat(InputStream *ist, int64_t pts)
 +{
 +    InputFile *infile = input_files[ist->file_index];
 +    int i, j, nb_reqs;
 +    int64_t pts2;
 +
 +    /* When a frame is read from a file, examine all sub2video streams in
 +       the same file and send the sub2video frame again. Otherwise, decoded
 +       video frames could be accumulating in the filter graph while a filter
 +       (possibly overlay) is desperately waiting for a subtitle frame. */
 +    for (i = 0; i < infile->nb_streams; i++) {
 +        InputStream *ist2 = input_streams[infile->ist_index + i];
 +        if (!ist2->sub2video.frame)
 +            continue;
 +        /* subtitles seem to be usually muxed ahead of other streams;
 +           if not, subtracting a larger time here is necessary */
 +        pts2 = av_rescale_q(pts, ist->st->time_base, ist2->st->time_base) - 1;
 +        /* do not send the heartbeat frame if the subtitle is already ahead */
 +        if (pts2 <= ist2->sub2video.last_pts)
 +            continue;
 +        if (pts2 >= ist2->sub2video.end_pts || !ist2->sub2video.frame->data[0])
 +            sub2video_update(ist2, NULL);
 +        for (j = 0, nb_reqs = 0; j < ist2->nb_filters; j++)
 +            nb_reqs += av_buffersrc_get_nb_failed_requests(ist2->filters[j]->filter);
 +        if (nb_reqs)
 +            sub2video_push_ref(ist2, pts2);
 +    }
 +}
 +
 +static void sub2video_flush(InputStream *ist)
 +{
 +    int i;
 +
 +    if (ist->sub2video.end_pts < INT64_MAX)
 +        sub2video_update(ist, NULL);
 +    for (i = 0; i < ist->nb_filters; i++)
 +        av_buffersrc_add_frame(ist->filters[i]->filter, NULL);
 +}
 +
 +/* end of sub2video hack */
 +
 +static void term_exit_sigsafe(void)
 +{
 +#if HAVE_TERMIOS_H
 +    if(restore_tty)
 +        tcsetattr (0, TCSANOW, &oldtty);
 +#endif
 +}
 +
 +void term_exit(void)
 +{
 +    av_log(NULL, AV_LOG_QUIET, "%s", "");
 +    term_exit_sigsafe();
 +}
 +
 +static volatile int received_sigterm = 0;
 +static volatile int received_nb_signals = 0;
 +static volatile int transcode_init_done = 0;
 +static volatile int ffmpeg_exited = 0;
 +static int main_return_code = 0;
 +
 +static void
 +sigterm_handler(int sig)
 +{
 +    received_sigterm = sig;
 +    received_nb_signals++;
 +    term_exit_sigsafe();
 +    if(received_nb_signals > 3) {
 +        write(2/*STDERR_FILENO*/, "Received > 3 system signals, hard exiting\n",
 +                           strlen("Received > 3 system signals, hard exiting\n"));
 +
 +        exit(123);
 +    }
 +}
 +
 +#if HAVE_SETCONSOLECTRLHANDLER
 +static BOOL WINAPI CtrlHandler(DWORD fdwCtrlType)
 +{
 +    av_log(NULL, AV_LOG_DEBUG, "\nReceived windows signal %ld\n", fdwCtrlType);
 +
 +    switch (fdwCtrlType)
 +    {
 +    case CTRL_C_EVENT:
 +    case CTRL_BREAK_EVENT:
 +        sigterm_handler(SIGINT);
 +        return TRUE;
 +
 +    case CTRL_CLOSE_EVENT:
 +    case CTRL_LOGOFF_EVENT:
 +    case CTRL_SHUTDOWN_EVENT:
 +        sigterm_handler(SIGTERM);
 +        /* Basically, with these 3 events, when we return from this method the
 +           process is hard terminated, so stall as long as we need to
 +           to try and let the main thread(s) clean up and gracefully terminate
 +           (we have at most 5 seconds, but should be done far before that). */
 +        while (!ffmpeg_exited) {
 +            Sleep(0);
 +        }
 +        return TRUE;
 +
 +    default:
 +        av_log(NULL, AV_LOG_ERROR, "Received unknown windows signal %ld\n", fdwCtrlType);
 +        return FALSE;
 +    }
 +}
 +#endif
 +
 +void term_init(void)
 +{
 +#if HAVE_TERMIOS_H
 +    if (!run_as_daemon && stdin_interaction) {
 +        struct termios tty;
 +        if (tcgetattr (0, &tty) == 0) {
 +            oldtty = tty;
 +            restore_tty = 1;
 +
 +            tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP
 +                             |INLCR|IGNCR|ICRNL|IXON);
 +            tty.c_oflag |= OPOST;
 +            tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN);
 +            tty.c_cflag &= ~(CSIZE|PARENB);
 +            tty.c_cflag |= CS8;
 +            tty.c_cc[VMIN] = 1;
 +            tty.c_cc[VTIME] = 0;
 +
 +            tcsetattr (0, TCSANOW, &tty);
 +        }
 +        signal(SIGQUIT, sigterm_handler); /* Quit (POSIX).  */
 +    }
 +#endif
 +
 +    signal(SIGINT , sigterm_handler); /* Interrupt (ANSI).    */
 +    signal(SIGTERM, sigterm_handler); /* Termination (ANSI).  */
 +#ifdef SIGXCPU
 +    signal(SIGXCPU, sigterm_handler);
 +#endif
 +#if HAVE_SETCONSOLECTRLHANDLER
 +    SetConsoleCtrlHandler((PHANDLER_ROUTINE) CtrlHandler, TRUE);
 +#endif
 +}
 +
 +/* read a key without blocking */
 +static int read_key(void)
 +{
 +    unsigned char ch;
 +#if HAVE_TERMIOS_H
 +    int n = 1;
 +    struct timeval tv;
 +    fd_set rfds;
 +
 +    FD_ZERO(&rfds);
 +    FD_SET(0, &rfds);
 +    tv.tv_sec = 0;
 +    tv.tv_usec = 0;
 +    n = select(1, &rfds, NULL, NULL, &tv);
 +    if (n > 0) {
 +        n = read(0, &ch, 1);
 +        if (n == 1)
 +            return ch;
 +
 +        return n;
 +    }
 +#elif HAVE_KBHIT
 +#    if HAVE_PEEKNAMEDPIPE
 +    static int is_pipe;
 +    static HANDLE input_handle;
 +    DWORD dw, nchars;
 +    if(!input_handle){
 +        input_handle = GetStdHandle(STD_INPUT_HANDLE);
 +        is_pipe = !GetConsoleMode(input_handle, &dw);
 +    }
 +
 +    if (is_pipe) {
 +        /* When running under a GUI, you will end here. */
 +        if (!PeekNamedPipe(input_handle, NULL, 0, NULL, &nchars, NULL)) {
 +            // input pipe may have been closed by the program that ran ffmpeg
 +            return -1;
 +        }
 +        //Read it
 +        if(nchars != 0) {
 +            read(0, &ch, 1);
 +            return ch;
 +        }else{
 +            return -1;
 +        }
 +    }
 +#    endif
 +    if(kbhit())
 +        return(getch());
 +#endif
 +    return -1;
 +}
 +
 +static int decode_interrupt_cb(void *ctx)
 +{
 +    return received_nb_signals > transcode_init_done;
 +}
 +
 +const AVIOInterruptCB int_cb = { decode_interrupt_cb, NULL };
 +
 +static void ffmpeg_cleanup(int ret)
 +{
 +    int i, j;
 +
 +    if (do_benchmark) {
 +        int maxrss = getmaxrss() / 1024;
 +        av_log(NULL, AV_LOG_INFO, "bench: maxrss=%ikB\n", maxrss);
 +    }
 +
 +    for (i = 0; i < nb_filtergraphs; i++) {
 +        FilterGraph *fg = filtergraphs[i];
 +        avfilter_graph_free(&fg->graph);
 +        for (j = 0; j < fg->nb_inputs; j++) {
 +            av_freep(&fg->inputs[j]->name);
 +            av_freep(&fg->inputs[j]);
 +        }
 +        av_freep(&fg->inputs);
 +        for (j = 0; j < fg->nb_outputs; j++) {
 +            av_freep(&fg->outputs[j]->name);
 +            av_freep(&fg->outputs[j]);
 +        }
 +        av_freep(&fg->outputs);
 +        av_freep(&fg->graph_desc);
 +
 +        av_freep(&filtergraphs[i]);
 +    }
 +    av_freep(&filtergraphs);
 +
 +    av_freep(&subtitle_out);
 +
 +    /* close files */
 +    for (i = 0; i < nb_output_files; i++) {
 +        OutputFile *of = output_files[i];
 +        AVFormatContext *s;
 +        if (!of)
 +            continue;
 +        s = of->ctx;
 +        if (s && s->oformat && !(s->oformat->flags & AVFMT_NOFILE))
 +            avio_closep(&s->pb);
 +        avformat_free_context(s);
 +        av_dict_free(&of->opts);
 +
 +        av_freep(&output_files[i]);
 +    }
 +    for (i = 0; i < nb_output_streams; i++) {
 +        OutputStream *ost = output_streams[i];
 +
 +        if (!ost)
 +            continue;
 +
 +        for (j = 0; j < ost->nb_bitstream_filters; j++)
 +            av_bsf_free(&ost->bsf_ctx[j]);
 +        av_freep(&ost->bsf_ctx);
 +        av_freep(&ost->bsf_extradata_updated);
 +
 +        av_frame_free(&ost->filtered_frame);
 +        av_frame_free(&ost->last_frame);
 +        av_dict_free(&ost->encoder_opts);
 +
 +        av_parser_close(ost->parser);
 +        avcodec_free_context(&ost->parser_avctx);
 +
 +        av_freep(&ost->forced_keyframes);
 +        av_expr_free(ost->forced_keyframes_pexpr);
 +        av_freep(&ost->avfilter);
 +        av_freep(&ost->logfile_prefix);
 +
 +        av_freep(&ost->audio_channels_map);
 +        ost->audio_channels_mapped = 0;
 +
 +        av_dict_free(&ost->sws_dict);
 +
 +        avcodec_free_context(&ost->enc_ctx);
 +        avcodec_parameters_free(&ost->ref_par);
 +
 +        av_freep(&output_streams[i]);
 +    }
 +#if HAVE_PTHREADS
 +    free_input_threads();
 +#endif
 +    for (i = 0; i < nb_input_files; i++) {
 +        avformat_close_input(&input_files[i]->ctx);
 +        av_freep(&input_files[i]);
 +    }
 +    for (i = 0; i < nb_input_streams; i++) {
 +        InputStream *ist = input_streams[i];
 +
 +        av_frame_free(&ist->decoded_frame);
 +        av_frame_free(&ist->filter_frame);
 +        av_dict_free(&ist->decoder_opts);
 +        avsubtitle_free(&ist->prev_sub.subtitle);
 +        av_frame_free(&ist->sub2video.frame);
 +        av_freep(&ist->filters);
 +        av_freep(&ist->hwaccel_device);
 +        av_freep(&ist->dts_buffer);
 +
 +        avcodec_free_context(&ist->dec_ctx);
 +
 +        av_freep(&input_streams[i]);
 +    }
 +
 +    if (vstats_file) {
 +        if (fclose(vstats_file))
 +            av_log(NULL, AV_LOG_ERROR,
 +                   "Error closing vstats file, loss of information possible: %s\n",
 +                   av_err2str(AVERROR(errno)));
 +    }
 +    av_freep(&vstats_filename);
 +
 +    av_freep(&input_streams);
 +    av_freep(&input_files);
 +    av_freep(&output_streams);
 +    av_freep(&output_files);
 +
 +    uninit_opts();
 +
 +    avformat_network_deinit();
 +
 +    if (received_sigterm) {
 +        av_log(NULL, AV_LOG_INFO, "Exiting normally, received signal %d.\n",
 +               (int) received_sigterm);
 +    } else if (ret && transcode_init_done) {
 +        av_log(NULL, AV_LOG_INFO, "Conversion failed!\n");
 +    }
 +    term_exit();
 +    ffmpeg_exited = 1;
 +}
 +
 +void remove_avoptions(AVDictionary **a, AVDictionary *b)
 +{
 +    AVDictionaryEntry *t = NULL;
 +
 +    while ((t = av_dict_get(b, "", t, AV_DICT_IGNORE_SUFFIX))) {
 +        av_dict_set(a, t->key, NULL, AV_DICT_MATCH_CASE);
 +    }
 +}
 +
 +void assert_avoptions(AVDictionary *m)
 +{
 +    AVDictionaryEntry *t;
 +    if ((t = av_dict_get(m, "", NULL, AV_DICT_IGNORE_SUFFIX))) {
 +        av_log(NULL, AV_LOG_FATAL, "Option %s not found.\n", t->key);
 +        exit_program(1);
 +    }
 +}
 +
 +static void abort_codec_experimental(AVCodec *c, int encoder)
 +{
 +    exit_program(1);
 +}
 +
 +static void update_benchmark(const char *fmt, ...)
 +{
 +    if (do_benchmark_all) {
 +        int64_t t = getutime();
 +        va_list va;
 +        char buf[1024];
 +
 +        if (fmt) {
 +            va_start(va, fmt);
 +            vsnprintf(buf, sizeof(buf), fmt, va);
 +            va_end(va);
 +            av_log(NULL, AV_LOG_INFO, "bench: %8"PRIu64" %s \n", t - current_time, buf);
 +        }
 +        current_time = t;
 +    }
 +}
 +
 +static void close_all_output_streams(OutputStream *ost, OSTFinished this_stream, OSTFinished others)
 +{
 +    int i;
 +    for (i = 0; i < nb_output_streams; i++) {
 +        OutputStream *ost2 = output_streams[i];
 +        ost2->finished |= ost == ost2 ? this_stream : others;
 +    }
 +}
 +
 +static void write_packet(AVFormatContext *s, AVPacket *pkt, OutputStream *ost)
 +{
 +    AVStream *st = ost->st;
 +    int ret;
 +
 +    if ((st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && video_sync_method == VSYNC_DROP) ||
 +        (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && audio_sync_method < 0))
 +        pkt->pts = pkt->dts = AV_NOPTS_VALUE;
 +
 +    /*
 +     * Audio encoders may split the packets --  #frames in != #packets out.
 +     * But there is no reordering, so we can limit the number of output packets
 +     * by simply dropping them here.
 +     * Counting encoded video frames needs to be done separately because of
 +     * reordering, see do_video_out()
 +     */
 +    if (!(st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && ost->encoding_needed)) {
 +        if (ost->frame_number >= ost->max_frames) {
 +            av_packet_unref(pkt);
 +            return;
 +        }
 +        ost->frame_number++;
 +    }
 +    if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
 +        int i;
 +        uint8_t *sd = av_packet_get_side_data(pkt, AV_PKT_DATA_QUALITY_STATS,
 +                                              NULL);
 +        ost->quality = sd ? AV_RL32(sd) : -1;
 +        ost->pict_type = sd ? sd[4] : AV_PICTURE_TYPE_NONE;
 +
 +        for (i = 0; i<FF_ARRAY_ELEMS(ost->error); i++) {
 +            if (sd && i < sd[5])
 +                ost->error[i] = AV_RL64(sd + 8 + 8*i);
 +            else
 +                ost->error[i] = -1;
 +        }
 +
 +        if (ost->frame_rate.num && ost->is_cfr) {
 +            if (pkt->duration > 0)
 +                av_log(NULL, AV_LOG_WARNING, "Overriding packet duration by frame rate, this should not happen\n");
 +            pkt->duration = av_rescale_q(1, av_inv_q(ost->frame_rate),
 +                                         ost->st->time_base);
 +        }
 +    }
 +
 +    if (!(s->oformat->flags & AVFMT_NOTIMESTAMPS)) {
 +        if (pkt->dts != AV_NOPTS_VALUE &&
 +            pkt->pts != AV_NOPTS_VALUE &&
 +            pkt->dts > pkt->pts) {
 +            av_log(s, AV_LOG_WARNING, "Invalid DTS: %"PRId64" PTS: %"PRId64" in output stream %d:%d, replacing by guess\n",
 +                   pkt->dts, pkt->pts,
 +                   ost->file_index, ost->st->index);
 +            pkt->pts =
 +            pkt->dts = pkt->pts + pkt->dts + ost->last_mux_dts + 1
 +                     - FFMIN3(pkt->pts, pkt->dts, ost->last_mux_dts + 1)
 +                     - FFMAX3(pkt->pts, pkt->dts, ost->last_mux_dts + 1);
 +        }
 +        if ((st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO || st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) &&
 +            pkt->dts != AV_NOPTS_VALUE &&
 +            !(st->codecpar->codec_id == AV_CODEC_ID_VP9 && ost->stream_copy) &&
 +            ost->last_mux_dts != AV_NOPTS_VALUE) {
 +            int64_t max = ost->last_mux_dts + !(s->oformat->flags & AVFMT_TS_NONSTRICT);
 +            if (pkt->dts < max) {
 +                int loglevel = max - pkt->dts > 2 || st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ? AV_LOG_WARNING : AV_LOG_DEBUG;
 +                av_log(s, loglevel, "Non-monotonous DTS in output stream "
 +                       "%d:%d; previous: %"PRId64", current: %"PRId64"; ",
 +                       ost->file_index, ost->st->index, ost->last_mux_dts, pkt->dts);
 +                if (exit_on_error) {
 +                    av_log(NULL, AV_LOG_FATAL, "aborting.\n");
 +                    exit_program(1);
 +                }
 +                av_log(s, loglevel, "changing to %"PRId64". This may result "
 +                       "in incorrect timestamps in the output file.\n",
 +                       max);
 +                if (pkt->pts >= pkt->dts)
 +                    pkt->pts = FFMAX(pkt->pts, max);
 +                pkt->dts = max;
 +            }
 +        }
 +    }
 +    ost->last_mux_dts = pkt->dts;
 +
 +    ost->data_size += pkt->size;
 +    ost->packets_written++;
 +
 +    pkt->stream_index = ost->index;
 +
 +    if (debug_ts) {
 +        av_log(NULL, AV_LOG_INFO, "muxer <- type:%s "
 +                "pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s size:%d\n",
 +                av_get_media_type_string(ost->enc_ctx->codec_type),
 +                av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &ost->st->time_base),
 +                av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &ost->st->time_base),
 +                pkt->size
 +              );
 +    }
 +
 +    ret = av_interleaved_write_frame(s, pkt);
 +    if (ret < 0) {
 +        print_error("av_interleaved_write_frame()", ret);
 +        main_return_code = 1;
 +        close_all_output_streams(ost, MUXER_FINISHED | ENCODER_FINISHED, ENCODER_FINISHED);
 +    }
 +    av_packet_unref(pkt);
 +}
 +
 +static void close_output_stream(OutputStream *ost)
 +{
 +    OutputFile *of = output_files[ost->file_index];
 +
 +    ost->finished |= ENCODER_FINISHED;
 +    if (of->shortest) {
 +        int64_t end = av_rescale_q(ost->sync_opts - ost->first_pts, ost->enc_ctx->time_base, AV_TIME_BASE_Q);
 +        of->recording_time = FFMIN(of->recording_time, end);
 +    }
 +}
 +
 +static void output_packet(AVFormatContext *s, AVPacket *pkt, OutputStream *ost)
 +{
 +    int ret = 0;
 +
 +    /* apply the output bitstream filters, if any */
 +    if (ost->nb_bitstream_filters) {
 +        int idx;
 +
 +        ret = av_bsf_send_packet(ost->bsf_ctx[0], pkt);
 +        if (ret < 0)
 +            goto finish;
 +
 +        idx = 1;
 +        while (idx) {
 +            /* get a packet from the previous filter up the chain */
 +            ret = av_bsf_receive_packet(ost->bsf_ctx[idx - 1], pkt);
 +            /* HACK! - aac_adtstoasc updates extradata after filtering the first frame when
 +             * the api states this shouldn't happen after init(). Propagate it here to the
 +             * muxer and to the next filters in the chain to workaround this.
 +             * TODO/FIXME - Make aac_adtstoasc use new packet side data instead of changing
 +             * par_out->extradata and adapt muxers accordingly to get rid of this. */
 +            if (!(ost->bsf_extradata_updated[idx - 1] & 1)) {
 +                ret = avcodec_parameters_copy(ost->st->codecpar, ost->bsf_ctx[idx - 1]->par_out);
 +                if (ret < 0)
 +                    goto finish;
 +                ost->bsf_extradata_updated[idx - 1] |= 1;
 +            }
 +            if (ret == AVERROR(EAGAIN)) {
 +                ret = 0;
 +                idx--;
 +                continue;
 +            } else if (ret < 0)
 +                goto finish;
 +
 +            /* send it to the next filter down the chain or to the muxer */
 +            if (idx < ost->nb_bitstream_filters) {
 +                /* HACK/FIXME! - See above */
 +                if (!(ost->bsf_extradata_updated[idx] & 2)) {
 +                    ret = avcodec_parameters_copy(ost->bsf_ctx[idx]->par_out, ost->bsf_ctx[idx - 1]->par_out);
 +                    if (ret < 0)
 +                        goto finish;
 +                    ost->bsf_extradata_updated[idx] |= 2;
 +                }
 +                ret = av_bsf_send_packet(ost->bsf_ctx[idx], pkt);
 +                if (ret < 0)
 +                    goto finish;
 +                idx++;
 +            } else
 +                write_packet(s, pkt, ost);
 +        }
 +    } else
 +        write_packet(s, pkt, ost);
 +
 +finish:
 +    if (ret < 0 && ret != AVERROR_EOF) {
 +        av_log(NULL, AV_LOG_ERROR, "Error applying bitstream filters to an output "
 +               "packet for stream #%d:%d.\n", ost->file_index, ost->index);
 +        if(exit_on_error)
 +            exit_program(1);
 +    }
 +}
 +
 +static int check_recording_time(OutputStream *ost)
 +{
 +    OutputFile *of = output_files[ost->file_index];
 +
 +    if (of->recording_time != INT64_MAX &&
 +        av_compare_ts(ost->sync_opts - ost->first_pts, ost->enc_ctx->time_base, of->recording_time,
 +                      AV_TIME_BASE_Q) >= 0) {
 +        close_output_stream(ost);
 +        return 0;
 +    }
 +    return 1;
 +}
 +
 +static void do_audio_out(AVFormatContext *s, OutputStream *ost,
 +                         AVFrame *frame)
 +{
 +    AVCodecContext *enc = ost->enc_ctx;
 +    AVPacket pkt;
 +    int ret;
 +
 +    av_init_packet(&pkt);
 +    pkt.data = NULL;
 +    pkt.size = 0;
 +
 +    if (!check_recording_time(ost))
 +        return;
 +
 +    if (frame->pts == AV_NOPTS_VALUE || audio_sync_method < 0)
 +        frame->pts = ost->sync_opts;
 +    ost->sync_opts = frame->pts + frame->nb_samples;
 +    ost->samples_encoded += frame->nb_samples;
 +    ost->frames_encoded++;
 +
 +    av_assert0(pkt.size || !pkt.data);
 +    update_benchmark(NULL);
 +    if (debug_ts) {
 +        av_log(NULL, AV_LOG_INFO, "encoder <- type:audio "
 +               "frame_pts:%s frame_pts_time:%s time_base:%d/%d\n",
 +               av_ts2str(frame->pts), av_ts2timestr(frame->pts, &enc->time_base),
 +               enc->time_base.num, enc->time_base.den);
 +    }
 +
 +    ret = avcodec_send_frame(enc, frame);
 +    if (ret < 0)
 +        goto error;
 +
 +    while (1) {
 +        ret = avcodec_receive_packet(enc, &pkt);
 +        if (ret == AVERROR(EAGAIN))
 +            break;
 +        if (ret < 0)
 +            goto error;
 +
 +        update_benchmark("encode_audio %d.%d", ost->file_index, ost->index);
 +
 +        av_packet_rescale_ts(&pkt, enc->time_base, ost->st->time_base);
 +
 +        if (debug_ts) {
 +            av_log(NULL, AV_LOG_INFO, "encoder -> type:audio "
 +                   "pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s\n",
 +                   av_ts2str(pkt.pts), av_ts2timestr(pkt.pts, &ost->st->time_base),
 +                   av_ts2str(pkt.dts), av_ts2timestr(pkt.dts, &ost->st->time_base));
 +        }
 +
 +        output_packet(s, &pkt, ost);
 +    }
 +
 +    return;
 +error:
 +    av_log(NULL, AV_LOG_FATAL, "Audio encoding failed\n");
 +    exit_program(1);
 +}
 +
 +static void do_subtitle_out(AVFormatContext *s,
 +                            OutputStream *ost,
 +                            InputStream *ist,
 +                            AVSubtitle *sub)
 +{
 +    int subtitle_out_max_size = 1024 * 1024;
 +    int subtitle_out_size, nb, i;
 +    AVCodecContext *enc;
 +    AVPacket pkt;
 +    int64_t pts;
 +
 +    if (sub->pts == AV_NOPTS_VALUE) {
 +        av_log(NULL, AV_LOG_ERROR, "Subtitle packets must have a pts\n");
 +        if (exit_on_error)
 +            exit_program(1);
 +        return;
 +    }
 +
 +    enc = ost->enc_ctx;
 +
 +    if (!subtitle_out) {
 +        subtitle_out = av_malloc(subtitle_out_max_size);
 +        if (!subtitle_out) {
 +            av_log(NULL, AV_LOG_FATAL, "Failed to allocate subtitle_out\n");
 +            exit_program(1);
 +        }
 +    }
 +
 +    /* Note: DVB subtitle need one packet to draw them and one other
 +       packet to clear them */
 +    /* XXX: signal it in the codec context ? */
 +    if (enc->codec_id == AV_CODEC_ID_DVB_SUBTITLE)
 +        nb = 2;
 +    else
 +        nb = 1;
 +
 +    /* shift timestamp to honor -ss and make check_recording_time() work with -t */
 +    pts = sub->pts;
 +    if (output_files[ost->file_index]->start_time != AV_NOPTS_VALUE)
 +        pts -= output_files[ost->file_index]->start_time;
 +    for (i = 0; i < nb; i++) {
 +        unsigned save_num_rects = sub->num_rects;
 +
 +        ost->sync_opts = av_rescale_q(pts, AV_TIME_BASE_Q, enc->time_base);
 +        if (!check_recording_time(ost))
 +            return;
 +
 +        sub->pts = pts;
 +        // start_display_time is required to be 0
 +        sub->pts               += av_rescale_q(sub->start_display_time, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q);
 +        sub->end_display_time  -= sub->start_display_time;
 +        sub->start_display_time = 0;
 +        if (i == 1)
 +            sub->num_rects = 0;
 +
 +        ost->frames_encoded++;
 +
 +        subtitle_out_size = avcodec_encode_subtitle(enc, subtitle_out,
 +                                                    subtitle_out_max_size, sub);
 +        if (i == 1)
 +            sub->num_rects = save_num_rects;
 +        if (subtitle_out_size < 0) {
 +            av_log(NULL, AV_LOG_FATAL, "Subtitle encoding failed\n");
 +            exit_program(1);
 +        }
 +
 +        av_init_packet(&pkt);
 +        pkt.data = subtitle_out;
 +        pkt.size = subtitle_out_size;
 +        pkt.pts  = av_rescale_q(sub->pts, AV_TIME_BASE_Q, ost->st->time_base);
 +        pkt.duration = av_rescale_q(sub->end_display_time, (AVRational){ 1, 1000 }, ost->st->time_base);
 +        if (enc->codec_id == AV_CODEC_ID_DVB_SUBTITLE) {
 +            /* XXX: the pts correction is handled here. Maybe handling
 +               it in the codec would be better */
 +            if (i == 0)
 +                pkt.pts += 90 * sub->start_display_time;
 +            else
 +                pkt.pts += 90 * sub->end_display_time;
 +        }
 +        pkt.dts = pkt.pts;
 +        output_packet(s, &pkt, ost);
 +    }
 +}
 +
 +static void do_video_out(AVFormatContext *s,
 +                         OutputStream *ost,
 +                         AVFrame *next_picture,
 +                         double sync_ipts)
 +{
 +    int ret, format_video_sync;
 +    AVPacket pkt;
 +    AVCodecContext *enc = ost->enc_ctx;
 +    AVCodecParameters *mux_par = ost->st->codecpar;
 +    int nb_frames, nb0_frames, i;
 +    double delta, delta0;
 +    double duration = 0;
 +    int frame_size = 0;
 +    InputStream *ist = NULL;
 +    AVFilterContext *filter = ost->filter->filter;
 +
 +    if (ost->source_index >= 0)
 +        ist = input_streams[ost->source_index];
 +
 +    if (filter->inputs[0]->frame_rate.num > 0 &&
 +        filter->inputs[0]->frame_rate.den > 0)
 +        duration = 1/(av_q2d(filter->inputs[0]->frame_rate) * av_q2d(enc->time_base));
 +
 +    if(ist && ist->st->start_time != AV_NOPTS_VALUE && ist->st->first_dts != AV_NOPTS_VALUE && ost->frame_rate.num)
 +        duration = FFMIN(duration, 1/(av_q2d(ost->frame_rate) * av_q2d(enc->time_base)));
 +
 +    if (!ost->filters_script &&
 +        !ost->filters &&
 +        next_picture &&
 +        ist &&
 +        lrintf(av_frame_get_pkt_duration(next_picture) * av_q2d(ist->st->time_base) / av_q2d(enc->time_base)) > 0) {
 +        duration = lrintf(av_frame_get_pkt_duration(next_picture) * av_q2d(ist->st->time_base) / av_q2d(enc->time_base));
 +    }
 +
 +    if (!next_picture) {
 +        //end, flushing
 +        nb0_frames = nb_frames = mid_pred(ost->last_nb0_frames[0],
 +                                          ost->last_nb0_frames[1],
 +                                          ost->last_nb0_frames[2]);
 +    } else {
 +        delta0 = sync_ipts - ost->sync_opts; // delta0 is the "drift" between the input frame (next_picture) and where it would fall in the output.
 +        delta  = delta0 + duration;
 +
 +        /* by default, we output a single frame */
 +        nb0_frames = 0; // tracks the number of times the PREVIOUS frame should be duplicated, mostly for variable framerate (VFR)
 +        nb_frames = 1;
 +
 +        format_video_sync = video_sync_method;
 +        if (format_video_sync == VSYNC_AUTO) {
 +            if(!strcmp(s->oformat->name, "avi")) {
 +                format_video_sync = VSYNC_VFR;
 +            } else
 +                format_video_sync = (s->oformat->flags & AVFMT_VARIABLE_FPS) ? ((s->oformat->flags & AVFMT_NOTIMESTAMPS) ? VSYNC_PASSTHROUGH : VSYNC_VFR) : VSYNC_CFR;
 +            if (   ist
 +                && format_video_sync == VSYNC_CFR
 +                && input_files[ist->file_index]->ctx->nb_streams == 1
 +                && input_files[ist->file_index]->input_ts_offset == 0) {
 +                format_video_sync = VSYNC_VSCFR;
 +            }
 +            if (format_video_sync == VSYNC_CFR && copy_ts) {
 +                format_video_sync = VSYNC_VSCFR;
 +            }
 +        }
 +        ost->is_cfr = (format_video_sync == VSYNC_CFR || format_video_sync == VSYNC_VSCFR);
 +
 +        if (delta0 < 0 &&
 +            delta > 0 &&
 +            format_video_sync != VSYNC_PASSTHROUGH &&
 +            format_video_sync != VSYNC_DROP) {
 +            if (delta0 < -0.6) {
 +                av_log(NULL, AV_LOG_WARNING, "Past duration %f too large\n", -delta0);
 +            } else
 +                av_log(NULL, AV_LOG_DEBUG, "Clipping frame in rate conversion by %f\n", -delta0);
 +            sync_ipts = ost->sync_opts;
 +            duration += delta0;
 +            delta0 = 0;
 +        }
 +
 +        switch (format_video_sync) {
 +        case VSYNC_VSCFR:
 +            if (ost->frame_number == 0 && delta0 >= 0.5) {
 +                av_log(NULL, AV_LOG_DEBUG, "Not duplicating %d initial frames\n", (int)lrintf(delta0));
 +                delta = duration;
 +                delta0 = 0;
 +                ost->sync_opts = lrint(sync_ipts);
 +            }
 +        case VSYNC_CFR:
 +            // FIXME set to 0.5 after we fix some dts/pts bugs like in avidec.c
 +            if (frame_drop_threshold && delta < frame_drop_threshold && ost->frame_number) {
 +                nb_frames = 0;
 +            } else if (delta < -1.1)
 +                nb_frames = 0;
 +            else if (delta > 1.1) {
 +                nb_frames = lrintf(delta);
 +                if (delta0 > 1.1)
 +                    nb0_frames = lrintf(delta0 - 0.6);
 +            }
 +            break;
 +        case VSYNC_VFR:
 +            if (delta <= -0.6)
 +                nb_frames = 0;
 +            else if (delta > 0.6)
 +                ost->sync_opts = lrint(sync_ipts);
 +            break;
 +        case VSYNC_DROP:
 +        case VSYNC_PASSTHROUGH:
 +            ost->sync_opts = lrint(sync_ipts);
 +            break;
 +        default:
 +            av_assert0(0);
 +        }
 +    }
 +
 +    nb_frames = FFMIN(nb_frames, ost->max_frames - ost->frame_number);
 +    nb0_frames = FFMIN(nb0_frames, nb_frames);
 +
 +    memmove(ost->last_nb0_frames + 1,
 +            ost->last_nb0_frames,
 +            sizeof(ost->last_nb0_frames[0]) * (FF_ARRAY_ELEMS(ost->last_nb0_frames) - 1));
 +    ost->last_nb0_frames[0] = nb0_frames;
 +
 +    if (nb0_frames == 0 && ost->last_dropped) {
 +        nb_frames_drop++;
 +        av_log(NULL, AV_LOG_VERBOSE,
 +               "*** dropping frame %d from stream %d at ts %"PRId64"\n",
 +               ost->frame_number, ost->st->index, ost->last_frame->pts);
 +    }
 +    if (nb_frames > (nb0_frames && ost->last_dropped) + (nb_frames > nb0_frames)) {
 +        if (nb_frames > dts_error_threshold * 30) {
 +            av_log(NULL, AV_LOG_ERROR, "%d frame duplication too large, skipping\n", nb_frames - 1);
 +            nb_frames_drop++;
 +            return;
 +        }
 +        nb_frames_dup += nb_frames - (nb0_frames && ost->last_dropped) - (nb_frames > nb0_frames);
 +        av_log(NULL, AV_LOG_VERBOSE, "*** %d dup!\n", nb_frames - 1);
 +    }
 +    ost->last_dropped = nb_frames == nb0_frames && next_picture;
 +
 +  /* duplicates frame if needed */
 +  for (i = 0; i < nb_frames; i++) {
 +    AVFrame *in_picture;
 +    av_init_packet(&pkt);
 +    pkt.data = NULL;
 +    pkt.size = 0;
 +
 +    if (i < nb0_frames && ost->last_frame) {
 +        in_picture = ost->last_frame;
 +    } else
 +        in_picture = next_picture;
 +
 +    if (!in_picture)
 +        return;
 +
 +    in_picture->pts = ost->sync_opts;
 +
 +#if 1
 +    if (!check_recording_time(ost))
 +#else
 +    if (ost->frame_number >= ost->max_frames)
 +#endif
 +        return;
 +
 +#if FF_API_LAVF_FMT_RAWPICTURE
 +    if (s->oformat->flags & AVFMT_RAWPICTURE &&
 +        enc->codec->id == AV_CODEC_ID_RAWVIDEO) {
 +        /* raw pictures are written as AVPicture structure to
 +           avoid any copies. We support temporarily the older
 +           method. */
 +        if (in_picture->interlaced_frame)
 +            mux_par->field_order = in_picture->top_field_first ? AV_FIELD_TB:AV_FIELD_BT;
 +        else
 +            mux_par->field_order = AV_FIELD_PROGRESSIVE;
 +        pkt.data   = (uint8_t *)in_picture;
 +        pkt.size   =  sizeof(AVPicture);
 +        pkt.pts    = av_rescale_q(in_picture->pts, enc->time_base, ost->st->time_base);
 +        pkt.flags |= AV_PKT_FLAG_KEY;
 +
 +        output_packet(s, &pkt, ost);
 +    } else
 +#endif
 +    {
 +        int forced_keyframe = 0;
 +        double pts_time;
 +
 +        if (enc->flags & (AV_CODEC_FLAG_INTERLACED_DCT | AV_CODEC_FLAG_INTERLACED_ME) &&
 +            ost->top_field_first >= 0)
 +            in_picture->top_field_first = !!ost->top_field_first;
 +
 +        if (in_picture->interlaced_frame) {
 +            if (enc->codec->id == AV_CODEC_ID_MJPEG)
 +                mux_par->field_order = in_picture->top_field_first ? AV_FIELD_TT:AV_FIELD_BB;
 +            else
 +                mux_par->field_order = in_picture->top_field_first ? AV_FIELD_TB:AV_FIELD_BT;
 +        } else
 +            mux_par->field_order = AV_FIELD_PROGRESSIVE;
 +
 +        in_picture->quality = enc->global_quality;
 +        in_picture->pict_type = 0;
 +
 +        pts_time = in_picture->pts != AV_NOPTS_VALUE ?
 +            in_picture->pts * av_q2d(enc->time_base) : NAN;
 +        if (ost->forced_kf_index < ost->forced_kf_count &&
 +            in_picture->pts >= ost->forced_kf_pts[ost->forced_kf_index]) {
 +            ost->forced_kf_index++;
 +            forced_keyframe = 1;
 +        } else if (ost->forced_keyframes_pexpr) {
 +            double res;
 +            ost->forced_keyframes_expr_const_values[FKF_T] = pts_time;
 +            res = av_expr_eval(ost->forced_keyframes_pexpr,
 +                               ost->forced_keyframes_expr_const_values, NULL);
 +            ff_dlog(NULL, "force_key_frame: n:%f n_forced:%f prev_forced_n:%f t:%f prev_forced_t:%f -> res:%f\n",
 +                    ost->forced_keyframes_expr_const_values[FKF_N],
 +                    ost->forced_keyframes_expr_const_values[FKF_N_FORCED],
 +                    ost->forced_keyframes_expr_const_values[FKF_PREV_FORCED_N],
 +                    ost->forced_keyframes_expr_const_values[FKF_T],
 +                    ost->forced_keyframes_expr_const_values[FKF_PREV_FORCED_T],
 +                    res);
 +            if (res) {
 +                forced_keyframe = 1;
 +                ost->forced_keyframes_expr_const_values[FKF_PREV_FORCED_N] =
 +                    ost->forced_keyframes_expr_const_values[FKF_N];
 +                ost->forced_keyframes_expr_const_values[FKF_PREV_FORCED_T] =
 +                    ost->forced_keyframes_expr_const_values[FKF_T];
 +                ost->forced_keyframes_expr_const_values[FKF_N_FORCED] += 1;
 +            }
 +
 +            ost->forced_keyframes_expr_const_values[FKF_N] += 1;
 +        } else if (   ost->forced_keyframes
 +                   && !strncmp(ost->forced_keyframes, "source", 6)
 +                   && in_picture->key_frame==1) {
 +            forced_keyframe = 1;
 +        }
 +
 +        if (forced_keyframe) {
 +            in_picture->pict_type = AV_PICTURE_TYPE_I;
 +            av_log(NULL, AV_LOG_DEBUG, "Forced keyframe at time %f\n", pts_time);
 +        }
 +
 +        update_benchmark(NULL);
 +        if (debug_ts) {
 +            av_log(NULL, AV_LOG_INFO, "encoder <- type:video "
 +                   "frame_pts:%s frame_pts_time:%s time_base:%d/%d\n",
 +                   av_ts2str(in_picture->pts), av_ts2timestr(in_picture->pts, &enc->time_base),
 +                   enc->time_base.num, enc->time_base.den);
 +        }
 +
 +        ost->frames_encoded++;
 +
 +        ret = avcodec_send_frame(enc, in_picture);
 +        if (ret < 0)
 +            goto error;
 +
 +        while (1) {
 +            ret = avcodec_receive_packet(enc, &pkt);
 +            update_benchmark("encode_video %d.%d", ost->file_index, ost->index);
 +            if (ret == AVERROR(EAGAIN))
 +                break;
 +            if (ret < 0)
 +                goto error;
 +
 +            if (debug_ts) {
 +                av_log(NULL, AV_LOG_INFO, "encoder -> type:video "
 +                       "pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s\n",
 +                       av_ts2str(pkt.pts), av_ts2timestr(pkt.pts, &enc->time_base),
 +                       av_ts2str(pkt.dts), av_ts2timestr(pkt.dts, &enc->time_base));
 +            }
 +
 +            if (pkt.pts == AV_NOPTS_VALUE && !(enc->codec->capabilities & AV_CODEC_CAP_DELAY))
 +                pkt.pts = ost->sync_opts;
 +
 +            av_packet_rescale_ts(&pkt, enc->time_base, ost->st->time_base);
 +
 +            if (debug_ts) {
 +                av_log(NULL, AV_LOG_INFO, "encoder -> type:video "
 +                    "pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s\n",
 +                    av_ts2str(pkt.pts), av_ts2timestr(pkt.pts, &ost->st->time_base),
 +                    av_ts2str(pkt.dts), av_ts2timestr(pkt.dts, &ost->st->time_base));
 +            }
 +
 +            frame_size = pkt.size;
 +            output_packet(s, &pkt, ost);
 +
 +            /* if two pass, output log */
 +            if (ost->logfile && enc->stats_out) {
 +                fprintf(ost->logfile, "%s", enc->stats_out);
 +            }
 +        }
 +    }
 +    ost->sync_opts++;
 +    /*
 +     * For video, number of frames in == number of packets out.
 +     * But there may be reordering, so we can't throw away frames on encoder
 +     * flush, we need to limit them here, before they go into encoder.
 +     */
 +    ost->frame_number++;
 +
 +    if (vstats_filename && frame_size)
 +        do_video_stats(ost, frame_size);
 +  }
 +
 +    if (!ost->last_frame)
 +        ost->last_frame = av_frame_alloc();
 +    av_frame_unref(ost->last_frame);
 +    if (next_picture && ost->last_frame)
 +        av_frame_ref(ost->last_frame, next_picture);
 +    else
 +        av_frame_free(&ost->last_frame);
 +
 +    return;
 +error:
 +    av_log(NULL, AV_LOG_FATAL, "Video encoding failed\n");
 +    exit_program(1);
 +}
 +
 +static double psnr(double d)
 +{
 +    return -10.0 * log10(d);
 +}
 +
 +static void do_video_stats(OutputStream *ost, int frame_size)
 +{
 +    AVCodecContext *enc;
 +    int frame_number;
 +    double ti1, bitrate, avg_bitrate;
 +
 +    /* this is executed just the first time do_video_stats is called */
 +    if (!vstats_file) {
 +        vstats_file = fopen(vstats_filename, "w");
 +        if (!vstats_file) {
 +            perror("fopen");
 +            exit_program(1);
 +        }
 +    }
 +
 +    enc = ost->enc_ctx;
 +    if (enc->codec_type == AVMEDIA_TYPE_VIDEO) {
 +        frame_number = ost->st->nb_frames;
 +        fprintf(vstats_file, "frame= %5d q= %2.1f ", frame_number,
 +                ost->quality / (float)FF_QP2LAMBDA);
 +
 +        if (ost->error[0]>=0 && (enc->flags & AV_CODEC_FLAG_PSNR))
 +            fprintf(vstats_file, "PSNR= %6.2f ", psnr(ost->error[0] / (enc->width * enc->height * 255.0 * 255.0)));
 +
 +        fprintf(vstats_file,"f_size= %6d ", frame_size);
 +        /* compute pts value */
 +        ti1 = av_stream_get_end_pts(ost->st) * av_q2d(ost->st->time_base);
 +        if (ti1 < 0.01)
 +            ti1 = 0.01;
 +
 +        bitrate     = (frame_size * 8) / av_q2d(enc->time_base) / 1000.0;
 +        avg_bitrate = (double)(ost->data_size * 8) / ti1 / 1000.0;
 +        fprintf(vstats_file, "s_size= %8.0fkB time= %0.3f br= %7.1fkbits/s avg_br= %7.1fkbits/s ",
 +               (double)ost->data_size / 1024, ti1, bitrate, avg_bitrate);
 +        fprintf(vstats_file, "type= %c\n", av_get_picture_type_char(ost->pict_type));
 +    }
 +}
 +
 +static void finish_output_stream(OutputStream *ost)
 +{
 +    OutputFile *of = output_files[ost->file_index];
 +    int i;
 +
 +    ost->finished = ENCODER_FINISHED | MUXER_FINISHED;
 +
 +    if (of->shortest) {
 +        for (i = 0; i < of->ctx->nb_streams; i++)
 +            output_streams[of->ost_index + i]->finished = ENCODER_FINISHED | MUXER_FINISHED;
 +    }
 +}
 +
 +/**
 + * Get and encode new output from any of the filtergraphs, without causing
 + * activity.
 + *
 + * @return  0 for success, <0 for severe errors
 + */
 +static int reap_filters(int flush)
 +{
 +    AVFrame *filtered_frame = NULL;
 +    int i;
 +
 +    /* Reap all buffers present in the buffer sinks */
 +    for (i = 0; i < nb_output_streams; i++) {
 +        OutputStream *ost = output_streams[i];
 +        OutputFile    *of = output_files[ost->file_index];
 +        AVFilterContext *filter;
 +        AVCodecContext *enc = ost->enc_ctx;
 +        int ret = 0;
 +
 +        if (!ost->filter)
 +            continue;
 +        filter = ost->filter->filter;
 +
 +        if (!ost->filtered_frame && !(ost->filtered_frame = av_frame_alloc())) {
 +            return AVERROR(ENOMEM);
 +        }
 +        filtered_frame = ost->filtered_frame;
 +
 +        while (1) {
 +            double float_pts = AV_NOPTS_VALUE; // this is identical to filtered_frame.pts but with higher precision
 +            ret = av_buffersink_get_frame_flags(filter, filtered_frame,
 +                                               AV_BUFFERSINK_FLAG_NO_REQUEST);
 +            if (ret < 0) {
 +                if (ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) {
 +                    av_log(NULL, AV_LOG_WARNING,
 +                           "Error in av_buffersink_get_frame_flags(): %s\n", av_err2str(ret));
 +                } else if (flush && ret == AVERROR_EOF) {
 +                    if (filter->inputs[0]->type == AVMEDIA_TYPE_VIDEO)
 +                        do_video_out(of->ctx, ost, NULL, AV_NOPTS_VALUE);
 +                }
 +                break;
 +            }
 +            if (ost->finished) {
 +                av_frame_unref(filtered_frame);
 +                continue;
 +            }
 +            if (filtered_frame->pts != AV_NOPTS_VALUE) {
 +                int64_t start_time = (of->start_time == AV_NOPTS_VALUE) ? 0 : of->start_time;
 +                AVRational tb = enc->time_base;
 +                int extra_bits = av_clip(29 - av_log2(tb.den), 0, 16);
 +
 +                tb.den <<= extra_bits;
 +                float_pts =
 +                    av_rescale_q(filtered_frame->pts, filter->inputs[0]->time_base, tb) -
 +                    av_rescale_q(start_time, AV_TIME_BASE_Q, tb);
 +                float_pts /= 1 << extra_bits;
 +                // avoid exact midoints to reduce the chance of rounding differences, this can be removed in case the fps code is changed to work with integers
 +                float_pts += FFSIGN(float_pts) * 1.0 / (1<<17);
 +
 +                filtered_frame->pts =
 +                    av_rescale_q(filtered_frame->pts, filter->inputs[0]->time_base, enc->time_base) -
 +                    av_rescale_q(start_time, AV_TIME_BASE_Q, enc->time_base);
 +            }
 +            //if (ost->source_index >= 0)
 +            //    *filtered_frame= *input_streams[ost->source_index]->decoded_frame; //for me_threshold
 +
 +            switch (filter->inputs[0]->type) {
 +            case AVMEDIA_TYPE_VIDEO:
 +                if (!ost->frame_aspect_ratio.num)
 +                    enc->sample_aspect_ratio = filtered_frame->sample_aspect_ratio;
 +
 +                if (debug_ts) {
 +                    av_log(NULL, AV_LOG_INFO, "filter -> pts:%s pts_time:%s exact:%f time_base:%d/%d\n",
 +                            av_ts2str(filtered_frame->pts), av_ts2timestr(filtered_frame->pts, &enc->time_base),
 +                            float_pts,
 +                            enc->time_base.num, enc->time_base.den);
 +                }
 +
 +                do_video_out(of->ctx, ost, filtered_frame, float_pts);
 +                break;
 +            case AVMEDIA_TYPE_AUDIO:
 +                if (!(enc->codec->capabilities & AV_CODEC_CAP_PARAM_CHANGE) &&
 +                    enc->channels != av_frame_get_channels(filtered_frame)) {
 +                    av_log(NULL, AV_LOG_ERROR,
 +                           "Audio filter graph output is not normalized and encoder does not support parameter changes\n");
 +                    break;
 +                }
 +                do_audio_out(of->ctx, ost, filtered_frame);
 +                break;
 +            default:
 +                // TODO support subtitle filters
 +                av_assert0(0);
 +            }
 +
 +            av_frame_unref(filtered_frame);
 +        }
 +    }
 +
 +    return 0;
 +}
 +
 +static void print_final_stats(int64_t total_size)
 +{
 +    uint64_t video_size = 0, audio_size = 0, extra_size = 0, other_size = 0;
 +    uint64_t subtitle_size = 0;
 +    uint64_t data_size = 0;
 +    float percent = -1.0;
 +    int i, j;
 +    int pass1_used = 1;
 +
 +    for (i = 0; i < nb_output_streams; i++) {
 +        OutputStream *ost = output_streams[i];
 +        switch (ost->enc_ctx->codec_type) {
 +            case AVMEDIA_TYPE_VIDEO: video_size += ost->data_size; break;
 +            case AVMEDIA_TYPE_AUDIO: audio_size += ost->data_size; break;
 +            case AVMEDIA_TYPE_SUBTITLE: subtitle_size += ost->data_size; break;
 +            default:                 other_size += ost->data_size; break;
 +        }
 +        extra_size += ost->enc_ctx->extradata_size;
 +        data_size  += ost->data_size;
 +        if (   (ost->enc_ctx->flags & (AV_CODEC_FLAG_PASS1 | CODEC_FLAG_PASS2))
 +            != AV_CODEC_FLAG_PASS1)
 +            pass1_used = 0;
 +    }
 +
 +    if (data_size && total_size>0 && total_size >= data_size)
 +        percent = 100.0 * (total_size - data_size) / data_size;
 +
 +    av_log(NULL, AV_LOG_INFO, "video:%1.0fkB audio:%1.0fkB subtitle:%1.0fkB other streams:%1.0fkB global headers:%1.0fkB muxing overhead: ",
 +           video_size / 1024.0,
 +           audio_size / 1024.0,
 +           subtitle_size / 1024.0,
 +           other_size / 1024.0,
 +           extra_size / 1024.0);
 +    if (percent >= 0.0)
 +        av_log(NULL, AV_LOG_INFO, "%f%%", percent);
 +    else
 +        av_log(NULL, AV_LOG_INFO, "unknown");
 +    av_log(NULL, AV_LOG_INFO, "\n");
 +
 +    /* print verbose per-stream stats */
 +    for (i = 0; i < nb_input_files; i++) {
 +        InputFile *f = input_files[i];
 +        uint64_t total_packets = 0, total_size = 0;
 +
 +        av_log(NULL, AV_LOG_VERBOSE, "Input file #%d (%s):\n",
 +               i, f->ctx->filename);
 +
 +        for (j = 0; j < f->nb_streams; j++) {
 +            InputStream *ist = input_streams[f->ist_index + j];
 +            enum AVMediaType type = ist->dec_ctx->codec_type;
 +
 +            total_size    += ist->data_size;
 +            total_packets += ist->nb_packets;
 +
 +            av_log(NULL, AV_LOG_VERBOSE, "  Input stream #%d:%d (%s): ",
 +                   i, j, media_type_string(type));
 +            av_log(NULL, AV_LOG_VERBOSE, "%"PRIu64" packets read (%"PRIu64" bytes); ",
 +                   ist->nb_packets, ist->data_size);
 +
 +            if (ist->decoding_needed) {
 +                av_log(NULL, AV_LOG_VERBOSE, "%"PRIu64" frames decoded",
 +                       ist->frames_decoded);
 +                if (type == AVMEDIA_TYPE_AUDIO)
 +                    av_log(NULL, AV_LOG_VERBOSE, " (%"PRIu64" samples)", ist->samples_decoded);
 +                av_log(NULL, AV_LOG_VERBOSE, "; ");
 +            }
 +
 +            av_log(NULL, AV_LOG_VERBOSE, "\n");
 +        }
 +
 +        av_log(NULL, AV_LOG_VERBOSE, "  Total: %"PRIu64" packets (%"PRIu64" bytes) demuxed\n",
 +               total_packets, total_size);
 +    }
 +
 +    for (i = 0; i < nb_output_files; i++) {
 +        OutputFile *of = output_files[i];
 +        uint64_t total_packets = 0, total_size = 0;
 +
 +        av_log(NULL, AV_LOG_VERBOSE, "Output file #%d (%s):\n",
 +               i, of->ctx->filename);
 +
 +        for (j = 0; j < of->ctx->nb_streams; j++) {
 +            OutputStream *ost = output_streams[of->ost_index + j];
 +            enum AVMediaType type = ost->enc_ctx->codec_type;
 +
 +            total_size    += ost->data_size;
 +            total_packets += ost->packets_written;
 +
 +            av_log(NULL, AV_LOG_VERBOSE, "  Output stream #%d:%d (%s): ",
 +                   i, j, media_type_string(type));
 +            if (ost->encoding_needed) {
 +                av_log(NULL, AV_LOG_VERBOSE, "%"PRIu64" frames encoded",
 +                       ost->frames_encoded);
 +                if (type == AVMEDIA_TYPE_AUDIO)
 +                    av_log(NULL, AV_LOG_VERBOSE, " (%"PRIu64" samples)", ost->samples_encoded);
 +                av_log(NULL, AV_LOG_VERBOSE, "; ");
 +            }
 +
 +            av_log(NULL, AV_LOG_VERBOSE, "%"PRIu64" packets muxed (%"PRIu64" bytes); ",
 +                   ost->packets_written, ost->data_size);
 +
 +            av_log(NULL, AV_LOG_VERBOSE, "\n");
 +        }
 +
 +        av_log(NULL, AV_LOG_VERBOSE, "  Total: %"PRIu64" packets (%"PRIu64" bytes) muxed\n",
 +               total_packets, total_size);
 +    }
 +    if(video_size + data_size + audio_size + subtitle_size + extra_size == 0){
 +        av_log(NULL, AV_LOG_WARNING, "Output file is empty, nothing was encoded ");
 +        if (pass1_used) {
 +            av_log(NULL, AV_LOG_WARNING, "\n");
 +        } else {
 +            av_log(NULL, AV_LOG_WARNING, "(check -ss / -t / -frames parameters if used)\n");
 +        }
 +    }
 +}
 +
 +static void print_report(int is_last_report, int64_t timer_start, int64_t cur_time)
 +{
 +    char buf[1024];
 +    AVBPrint buf_script;
 +    OutputStream *ost;
 +    AVFormatContext *oc;
 +    int64_t total_size;
 +    AVCodecContext *enc;
 +    int frame_number, vid, i;
 +    double bitrate;
 +    double speed;
 +    int64_t pts = INT64_MIN + 1;
 +    static int64_t last_time = -1;
 +    static int qp_histogram[52];
 +    int hours, mins, secs, us;
 +    int ret;
 +    float t;
 +
 +    if (!print_stats && !is_last_report && !progress_avio)
 +        return;
 +
 +    if (!is_last_report) {
 +        if (last_time == -1) {
 +            last_time = cur_time;
 +            return;
 +        }
 +        if ((cur_time - last_time) < 500000)
 +            return;
 +        last_time = cur_time;
 +    }
 +
 +    t = (cur_time-timer_start) / 1000000.0;
 +
 +
 +    oc = output_files[0]->ctx;
 +
 +    total_size = avio_size(oc->pb);
 +    if (total_size <= 0) // FIXME improve avio_size() so it works with non seekable output too
 +        total_size = avio_tell(oc->pb);
 +
 +    buf[0] = '\0';
 +    vid = 0;
 +    av_bprint_init(&buf_script, 0, 1);
 +    for (i = 0; i < nb_output_streams; i++) {
 +        float q = -1;
 +        ost = output_streams[i];
 +        enc = ost->enc_ctx;
 +        if (!ost->stream_copy)
 +            q = ost->quality / (float) FF_QP2LAMBDA;
 +
 +        if (vid && enc->codec_type == AVMEDIA_TYPE_VIDEO) {
 +            snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "q=%2.1f ", q);
 +            av_bprintf(&buf_script, "stream_%d_%d_q=%.1f\n",
 +                       ost->file_index, ost->index, q);
 +        }
 +        if (!vid && enc->codec_type == AVMEDIA_TYPE_VIDEO) {
 +            float fps;
 +
 +            frame_number = ost->frame_number;
 +            fps = t > 1 ? frame_number / t : 0;
 +            snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "frame=%5d fps=%3.*f q=%3.1f ",
 +                     frame_number, fps < 9.95, fps, q);
 +            av_bprintf(&buf_script, "frame=%d\n", frame_number);
 +            av_bprintf(&buf_script, "fps=%.1f\n", fps);
 +            av_bprintf(&buf_script, "stream_%d_%d_q=%.1f\n",
 +                       ost->file_index, ost->index, q);
 +            if (is_last_report)
 +                snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "L");
 +            if (qp_hist) {
 +                int j;
 +                int qp = lrintf(q);
 +                if (qp >= 0 && qp < FF_ARRAY_ELEMS(qp_histogram))
 +                    qp_histogram[qp]++;
 +                for (j = 0; j < 32; j++)
 +                    snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%X", av_log2(qp_histogram[j] + 1));
 +            }
 +
 +            if ((enc->flags & AV_CODEC_FLAG_PSNR) && (ost->pict_type != AV_PICTURE_TYPE_NONE || is_last_report)) {
 +                int j;
 +                double error, error_sum = 0;
 +                double scale, scale_sum = 0;
 +                double p;
 +                char type[3] = { 'Y','U','V' };
 +                snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "PSNR=");
 +                for (j = 0; j < 3; j++) {
 +                    if (is_last_report) {
 +                        error = enc->error[j];
 +                        scale = enc->width * enc->height * 255.0 * 255.0 * frame_number;
 +                    } else {
 +                        error = ost->error[j];
 +                        scale = enc->width * enc->height * 255.0 * 255.0;
 +                    }
 +                    if (j)
 +                        scale /= 4;
 +                    error_sum += error;
 +                    scale_sum += scale;
 +                    p = psnr(error / scale);
 +                    snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%c:%2.2f ", type[j], p);
 +                    av_bprintf(&buf_script, "stream_%d_%d_psnr_%c=%2.2f\n",
 +                               ost->file_index, ost->index, type[j] | 32, p);
 +                }
 +                p = psnr(error_sum / scale_sum);
 +                snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "*:%2.2f ", psnr(error_sum / scale_sum));
 +                av_bprintf(&buf_script, "stream_%d_%d_psnr_all=%2.2f\n",
 +                           ost->file_index, ost->index, p);
 +            }
 +            vid = 1;
 +        }
 +        /* compute min output value */
 +        if (av_stream_get_end_pts(ost->st) != AV_NOPTS_VALUE)
 +            pts = FFMAX(pts, av_rescale_q(av_stream_get_end_pts(ost->st),
 +                                          ost->st->time_base, AV_TIME_BASE_Q));
 +        if (is_last_report)
 +            nb_frames_drop += ost->last_dropped;
 +    }
 +
 +    secs = FFABS(pts) / AV_TIME_BASE;
 +    us = FFABS(pts) % AV_TIME_BASE;
 +    mins = secs / 60;
 +    secs %= 60;
 +    hours = mins / 60;
 +    mins %= 60;
 +
 +    bitrate = pts && total_size >= 0 ? total_size * 8 / (pts / 1000.0) : -1;
 +    speed = t != 0.0 ? (double)pts / AV_TIME_BASE / t : -1;
 +
 +    if (total_size < 0) snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
 +                                 "size=N/A time=");
 +    else                snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
 +                                 "size=%8.0fkB time=", total_size / 1024.0);
 +    if (pts < 0)
 +        snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "-");
 +    snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
 +             "%02d:%02d:%02d.%02d ", hours, mins, secs,
 +             (100 * us) / AV_TIME_BASE);
 +
 +    if (bitrate < 0) {
 +        snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),"bitrate=N/A");
 +        av_bprintf(&buf_script, "bitrate=N/A\n");
 +    }else{
 +        snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),"bitrate=%6.1fkbits/s", bitrate);
 +        av_bprintf(&buf_script, "bitrate=%6.1fkbits/s\n", bitrate);
 +    }
 +
 +    if (total_size < 0) av_bprintf(&buf_script, "total_size=N/A\n");
 +    else                av_bprintf(&buf_script, "total_size=%"PRId64"\n", total_size);
 +    av_bprintf(&buf_script, "out_time_ms=%"PRId64"\n", pts);
 +    av_bprintf(&buf_script, "out_time=%02d:%02d:%02d.%06d\n",
 +               hours, mins, secs, us);
 +
 +    if (nb_frames_dup || nb_frames_drop)
 +        snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " dup=%d drop=%d",
 +                nb_frames_dup, nb_frames_drop);
 +    av_bprintf(&buf_script, "dup_frames=%d\n", nb_frames_dup);
 +    av_bprintf(&buf_script, "drop_frames=%d\n", nb_frames_drop);
 +
 +    if (speed < 0) {
 +        snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf)," speed=N/A");
 +        av_bprintf(&buf_script, "speed=N/A\n");
 +    } else {
 +        snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf)," speed=%4.3gx", speed);
 +        av_bprintf(&buf_script, "speed=%4.3gx\n", speed);
 +    }
 +
 +    if (print_stats || is_last_report) {
 +        const char end = is_last_report ? '\n' : '\r';
 +        if (print_stats==1 && AV_LOG_INFO > av_log_get_level()) {
 +            fprintf(stderr, "%s    %c", buf, end);
 +        } else
 +            av_log(NULL, AV_LOG_INFO, "%s    %c", buf, end);
 +
 +    fflush(stderr);
 +    }
 +
 +    if (progress_avio) {
 +        av_bprintf(&buf_script, "progress=%s\n",
 +                   is_last_report ? "end" : "continue");
 +        avio_write(progress_avio, buf_script.str,
 +                   FFMIN(buf_script.len, buf_script.size - 1));
 +        avio_flush(progress_avio);
 +        av_bprint_finalize(&buf_script, NULL);
 +        if (is_last_report) {
 +            if ((ret = avio_closep(&progress_avio)) < 0)
 +                av_log(NULL, AV_LOG_ERROR,
 +                       "Error closing progress log, loss of information possible: %s\n", av_err2str(ret));
 +        }
 +    }
 +
 +    if (is_last_report)
 +        print_final_stats(total_size);
 +}
 +
 +static void flush_encoders(void)
 +{
 +    int i, ret;
 +
 +    for (i = 0; i < nb_output_streams; i++) {
 +        OutputStream   *ost = output_streams[i];
 +        AVCodecContext *enc = ost->enc_ctx;
 +        AVFormatContext *os = output_files[ost->file_index]->ctx;
 +        int stop_encoding = 0;
 +
 +        if (!ost->encoding_needed)
 +            continue;
 +
 +        if (enc->codec_type == AVMEDIA_TYPE_AUDIO && enc->frame_size <= 1)
 +            continue;
 +#if FF_API_LAVF_FMT_RAWPICTURE
 +        if (enc->codec_type == AVMEDIA_TYPE_VIDEO && (os->oformat->flags & AVFMT_RAWPICTURE) && enc->codec->id == AV_CODEC_ID_RAWVIDEO)
 +            continue;
 +#endif
 +
 +        if (enc->codec_type != AVMEDIA_TYPE_VIDEO && enc->codec_type != AVMEDIA_TYPE_AUDIO)
 +            continue;
 +
 +        avcodec_send_frame(enc, NULL);
 +
 +        for (;;) {
 +            const char *desc = NULL;
 +
 +            switch (enc->codec_type) {
 +            case AVMEDIA_TYPE_AUDIO:
 +                desc   = "audio";
 +                break;
 +            case AVMEDIA_TYPE_VIDEO:
 +                desc   = "video";
 +                break;
 +            default:
 +                av_assert0(0);
 +            }
 +
 +            if (1) {
 +                AVPacket pkt;
 +                int pkt_size;
 +                av_init_packet(&pkt);
 +                pkt.data = NULL;
 +                pkt.size = 0;
 +
 +                update_benchmark(NULL);
 +                ret = avcodec_receive_packet(enc, &pkt);
 +                update_benchmark("flush_%s %d.%d", desc, ost->file_index, ost->index);
 +                if (ret < 0 && ret != AVERROR_EOF) {
 +                    av_log(NULL, AV_LOG_FATAL, "%s encoding failed: %s\n",
 +                           desc,
 +                           av_err2str(ret));
 +                    exit_program(1);
 +                }
 +                if (ost->logfile && enc->stats_out) {
 +                    fprintf(ost->logfile, "%s", enc->stats_out);
 +                }
 +                if (ret == AVERROR_EOF) {
 +                    stop_encoding = 1;
 +                    break;
 +                }
 +                if (ost->finished & MUXER_FINISHED) {
 +                    av_packet_unref(&pkt);
 +                    continue;
 +                }
 +                av_packet_rescale_ts(&pkt, enc->time_base, ost->st->time_base);
 +                pkt_size = pkt.size;
 +                output_packet(os, &pkt, ost);
 +                if (ost->enc_ctx->codec_type == AVMEDIA_TYPE_VIDEO && vstats_filename) {
 +                    do_video_stats(ost, pkt_size);
 +                }
 +            }
 +
 +            if (stop_encoding)
 +                break;
 +        }
 +    }
 +}
 +
 +/*
 + * Check whether a packet from ist should be written into ost at this time
 + */
 +static int check_output_constraints(InputStream *ist, OutputStream *ost)
 +{
 +    OutputFile *of = output_files[ost->file_index];
 +    int ist_index  = input_files[ist->file_index]->ist_index + ist->st->index;
 +
 +    if (ost->source_index != ist_index)
 +        return 0;
 +
 +    if (ost->finished)
 +        return 0;
 +
 +    if (of->start_time != AV_NOPTS_VALUE && ist->pts < of->start_time)
 +        return 0;
 +
 +    return 1;
 +}
 +
 +static void do_streamcopy(InputStream *ist, OutputStream *ost, const AVPacket *pkt)
 +{
 +    OutputFile *of = output_files[ost->file_index];
 +    InputFile   *f = input_files [ist->file_index];
 +    int64_t start_time = (of->start_time == AV_NOPTS_VALUE) ? 0 : of->start_time;
 +    int64_t ost_tb_start_time = av_rescale_q(start_time, AV_TIME_BASE_Q, ost->st->time_base);
 +    AVPicture pict;
 +    AVPacket opkt;
 +
 +    av_init_packet(&opkt);
 +
 +    if ((!ost->frame_number && !(pkt->flags & AV_PKT_FLAG_KEY)) &&
 +        !ost->copy_initial_nonkeyframes)
 +        return;
 +
 +    if (!ost->frame_number && !ost->copy_prior_start) {
 +        int64_t comp_start = start_time;
 +        if (copy_ts && f->start_time != AV_NOPTS_VALUE)
 +            comp_start = FFMAX(start_time, f->start_time + f->ts_offset);
 +        if (pkt->pts == AV_NOPTS_VALUE ?
 +            ist->pts < comp_start :
 +            pkt->pts < av_rescale_q(comp_start, AV_TIME_BASE_Q, ist->st->time_base))
 +            return;
 +    }
 +
 +    if (of->recording_time != INT64_MAX &&
 +        ist->pts >= of->recording_time + start_time) {
 +        close_output_stream(ost);
 +        return;
 +    }
 +
 +    if (f->recording_time != INT64_MAX) {
 +        start_time = f->ctx->start_time;
 +        if (f->start_time != AV_NOPTS_VALUE && copy_ts)
 +            start_time += f->start_time;
 +        if (ist->pts >= f->recording_time + start_time) {
 +            close_output_stream(ost);
 +            return;
 +        }
 +    }
 +
 +    /* force the input stream PTS */
 +    if (ost->enc_ctx->codec_type == AVMEDIA_TYPE_VIDEO)
 +        ost->sync_opts++;
 +
 +    if (pkt->pts != AV_NOPTS_VALUE)
 +        opkt.pts = av_rescale_q(pkt->pts, ist->st->time_base, ost->st->time_base) - ost_tb_start_time;
 +    else
 +        opkt.pts = AV_NOPTS_VALUE;
 +
 +    if (pkt->dts == AV_NOPTS_VALUE)
 +        opkt.dts = av_rescale_q(ist->dts, AV_TIME_BASE_Q, ost->st->time_base);
 +    else
 +        opkt.dts = av_rescale_q(pkt->dts, ist->st->time_base, ost->st->time_base);
 +    opkt.dts -= ost_tb_start_time;
 +
 +    if (ost->st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && pkt->dts != AV_NOPTS_VALUE) {
 +        int duration = av_get_audio_frame_duration(ist->dec_ctx, pkt->size);
 +        if(!duration)
 +            duration = ist->dec_ctx->frame_size;
 +        opkt.dts = opkt.pts = av_rescale_delta(ist->st->time_base, pkt->dts,
 +                                               (AVRational){1, ist->dec_ctx->sample_rate}, duration, &ist->filter_in_rescale_delta_last,
 +                                               ost->st->time_base) - ost_tb_start_time;
 +    }
 +
 +    opkt.duration = av_rescale_q(pkt->duration, ist->st->time_base, ost->st->time_base);
 +    opkt.flags    = pkt->flags;
 +    // FIXME remove the following 2 lines they shall be replaced by the bitstream filters
 +    if (  ost->st->codecpar->codec_id != AV_CODEC_ID_H264
 +       && ost->st->codecpar->codec_id != AV_CODEC_ID_MPEG1VIDEO
 +       && ost->st->codecpar->codec_id != AV_CODEC_ID_MPEG2VIDEO
 +       && ost->st->codecpar->codec_id != AV_CODEC_ID_VC1
 +       ) {
 +        int ret = av_parser_change(ost->parser, ost->parser_avctx,
 +                             &opkt.data, &opkt.size,
 +                             pkt->data, pkt->size,
 +                             pkt->flags & AV_PKT_FLAG_KEY);
 +        if (ret < 0) {
 +            av_log(NULL, AV_LOG_FATAL, "av_parser_change failed: %s\n",
 +                   av_err2str(ret));
 +            exit_program(1);
 +        }
 +        if (ret) {
 +            opkt.buf = av_buffer_create(opkt.data, opkt.size, av_buffer_default_free, NULL, 0);
 +            if (!opkt.buf)
 +                exit_program(1);
 +        }
 +    } else {
 +        opkt.data = pkt->data;
 +        opkt.size = pkt->size;
 +    }
 +    av_copy_packet_side_data(&opkt, pkt);
 +
 +#if FF_API_LAVF_FMT_RAWPICTURE
 +    if (ost->st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
 +        ost->st->codecpar->codec_id == AV_CODEC_ID_RAWVIDEO &&
 +        (of->ctx->oformat->flags & AVFMT_RAWPICTURE)) {
 +        /* store AVPicture in AVPacket, as expected by the output format */
 +        int ret = avpicture_fill(&pict, opkt.data, ost->st->codecpar->format, ost->st->codecpar->width, ost->st->codecpar->height);
 +        if (ret < 0) {
 +            av_log(NULL, AV_LOG_FATAL, "avpicture_fill failed: %s\n",
 +                   av_err2str(ret));
 +            exit_program(1);
 +        }
 +        opkt.data = (uint8_t *)&pict;
 +        opkt.size = sizeof(AVPicture);
 +        opkt.flags |= AV_PKT_FLAG_KEY;
 +    }
 +#endif
 +
 +    output_packet(of->ctx, &opkt, ost);
 +}
 +
 +int guess_input_channel_layout(InputStream *ist)
 +{
 +    AVCodecContext *dec = ist->dec_ctx;
 +
 +    if (!dec->channel_layout) {
 +        char layout_name[256];
 +
 +        if (dec->channels > ist->guess_layout_max)
 +            return 0;
 +        dec->channel_layout = av_get_default_channel_layout(dec->channels);
 +        if (!dec->channel_layout)
 +            return 0;
 +        av_get_channel_layout_string(layout_name, sizeof(layout_name),
 +                                     dec->channels, dec->channel_layout);
 +        av_log(NULL, AV_LOG_WARNING, "Guessed Channel Layout for Input Stream "
 +               "#%d.%d : %s\n", ist->file_index, ist->st->index, layout_name);
 +    }
 +    return 1;
 +}
 +
 +static void check_decode_result(InputStream *ist, int *got_output, int ret)
 +{
 +    if (*got_output || ret<0)
 +        decode_error_stat[ret<0] ++;
 +
 +    if (ret < 0 && exit_on_error)
 +        exit_program(1);
 +
 +    if (exit_on_error && *got_output && ist) {
 +        if (av_frame_get_decode_error_flags(ist->decoded_frame) || (ist->decoded_frame->flags & AV_FRAME_FLAG_CORRUPT)) {
 +            av_log(NULL, AV_LOG_FATAL, "%s: corrupt decoded frame in stream %d\n", input_files[ist->file_index]->ctx->filename, ist->st->index);
 +            exit_program(1);
 +        }
 +    }
 +}
 +
 +// This does not quite work like avcodec_decode_audio4/avcodec_decode_video2.
 +// There is the following difference: if you got a frame, you must call
 +// it again with pkt=NULL. pkt==NULL is treated differently from pkt.size==0
 +// (pkt==NULL means get more output, pkt.size==0 is a flush/drain packet)
 +static int decode(AVCodecContext *avctx, AVFrame *frame, int *got_frame, AVPacket *pkt)
 +{
 +    int ret;
 +
 +    *got_frame = 0;
 +
 +    if (pkt) {
 +        ret = avcodec_send_packet(avctx, pkt);
 +        // In particular, we don't expect AVERROR(EAGAIN), because we read all
 +        // decoded frames with avcodec_receive_frame() until done.
 +        if (ret < 0 && ret != AVERROR_EOF)
 +            return ret;
 +    }
 +
 +    ret = avcodec_receive_frame(avctx, frame);
 +    if (ret < 0 && ret != AVERROR(EAGAIN))
 +        return ret;
 +    if (ret >= 0)
 +        *got_frame = 1;
 +
 +    return 0;
 +}
 +
 +static int decode_audio(InputStream *ist, AVPacket *pkt, int *got_output)
 +{
 +    AVFrame *decoded_frame, *f;
 +    AVCodecContext *avctx = ist->dec_ctx;
 +    int i, ret, err = 0, resample_changed;
 +    AVRational decoded_frame_tb;
 +
 +    if (!ist->decoded_frame && !(ist->decoded_frame = av_frame_alloc()))
 +        return AVERROR(ENOMEM);
 +    if (!ist->filter_frame && !(ist->filter_frame = av_frame_alloc()))
 +        return AVERROR(ENOMEM);
 +    decoded_frame = ist->decoded_frame;
 +
 +    update_benchmark(NULL);
 +    ret = decode(avctx, decoded_frame, got_output, pkt);
 +    update_benchmark("decode_audio %d.%d", ist->file_index, ist->st->index);
 +
 +    if (ret >= 0 && avctx->sample_rate <= 0) {
 +        av_log(avctx, AV_LOG_ERROR, "Sample rate %d invalid\n", avctx->sample_rate);
 +        ret = AVERROR_INVALIDDATA;
 +    }
 +
 +    if (ret != AVERROR_EOF)
 +        check_decode_result(ist, got_output, ret);
 +
 +    if (!*got_output || ret < 0)
 +        return ret;
 +
 +    ist->samples_decoded += decoded_frame->nb_samples;
 +    ist->frames_decoded++;
 +
 +#if 1
 +    /* increment next_dts to use for the case where the input stream does not
 +       have timestamps or there are multiple frames in the packet */
 +    ist->next_pts += ((int64_t)AV_TIME_BASE * decoded_frame->nb_samples) /
 +                     avctx->sample_rate;
 +    ist->next_dts += ((int64_t)AV_TIME_BASE * decoded_frame->nb_samples) /
 +                     avctx->sample_rate;
 +#endif
 +
 +    resample_changed = ist->resample_sample_fmt     != decoded_frame->format         ||
 +                       ist->resample_channels       != avctx->channels               ||
 +                       ist->resample_channel_layout != decoded_frame->channel_layout ||
 +                       ist->resample_sample_rate    != decoded_frame->sample_rate;
 +    if (resample_changed) {
 +        char layout1[64], layout2[64];
 +
 +        if (!guess_input_channel_layout(ist)) {
 +            av_log(NULL, AV_LOG_FATAL, "Unable to find default channel "
 +                   "layout for Input Stream #%d.%d\n", ist->file_index,
 +                   ist->st->index);
 +            exit_program(1);
 +        }
 +        decoded_frame->channel_layout = avctx->channel_layout;
 +
 +        av_get_channel_layout_string(layout1, sizeof(layout1), ist->resample_channels,
 +                                     ist->resample_channel_layout);
 +        av_get_channel_layout_string(layout2, sizeof(layout2), avctx->channels,
 +                                     decoded_frame->channel_layout);
 +
 +        av_log(NULL, AV_LOG_INFO,
 +               "Input stream #%d:%d frame changed from rate:%d fmt:%s ch:%d chl:%s to rate:%d fmt:%s ch:%d chl:%s\n",
 +               ist->file_index, ist->st->index,
 +               ist->resample_sample_rate,  av_get_sample_fmt_name(ist->resample_sample_fmt),
 +               ist->resample_channels, layout1,
 +               decoded_frame->sample_rate, av_get_sample_fmt_name(decoded_frame->format),
 +               avctx->channels, layout2);
 +
 +        ist->resample_sample_fmt     = decoded_frame->format;
 +        ist->resample_sample_rate    = decoded_frame->sample_rate;
 +        ist->resample_channel_layout = decoded_frame->channel_layout;
 +        ist->resample_channels       = avctx->channels;
 +
 +        for (i = 0; i < nb_filtergraphs; i++)
 +            if (ist_in_filtergraph(filtergraphs[i], ist)) {
 +                FilterGraph *fg = filtergraphs[i];
 +                if (configure_filtergraph(fg) < 0) {
 +                    av_log(NULL, AV_LOG_FATAL, "Error reinitializing filters!\n");
 +                    exit_program(1);
 +                }
 +            }
 +    }
 +
-     if (decoded_frame->pkt_pts != AV_NOPTS_VALUE) {
-         decoded_frame->pts = decoded_frame->pkt_pts;
++    if (decoded_frame->pts != AV_NOPTS_VALUE) {
 +        decoded_frame_tb   = ist->st->time_base;
 +    } else if (pkt && pkt->pts != AV_NOPTS_VALUE) {
 +        decoded_frame->pts = pkt->pts;
 +        decoded_frame_tb   = ist->st->time_base;
 +    }else {
 +        decoded_frame->pts = ist->dts;
 +        decoded_frame_tb   = AV_TIME_BASE_Q;
 +    }
 +    if (decoded_frame->pts != AV_NOPTS_VALUE)
 +        decoded_frame->pts = av_rescale_delta(decoded_frame_tb, decoded_frame->pts,
 +                                              (AVRational){1, avctx->sample_rate}, decoded_frame->nb_samples, &ist->filter_in_rescale_delta_last,
 +                                              (AVRational){1, avctx->sample_rate});
 +    ist->nb_samples = decoded_frame->nb_samples;
 +    for (i = 0; i < ist->nb_filters; i++) {
 +        if (i < ist->nb_filters - 1) {
 +            f = ist->filter_frame;
 +            err = av_frame_ref(f, decoded_frame);
 +            if (err < 0)
 +                break;
 +        } else
 +            f = decoded_frame;
 +        err = av_buffersrc_add_frame_flags(ist->filters[i]->filter, f,
 +                                     AV_BUFFERSRC_FLAG_PUSH);
 +        if (err == AVERROR_EOF)
 +            err = 0; /* ignore */
 +        if (err < 0)
 +            break;
 +    }
 +    decoded_frame->pts = AV_NOPTS_VALUE;
 +
 +    av_frame_unref(ist->filter_frame);
 +    av_frame_unref(decoded_frame);
 +    return err < 0 ? err : ret;
 +}
 +
 +static int decode_video(InputStream *ist, AVPacket *pkt, int *got_output, int eof)
 +{
 +    AVFrame *decoded_frame, *f;
 +    int i, ret = 0, err = 0, resample_changed;
 +    int64_t best_effort_timestamp;
 +    int64_t dts = AV_NOPTS_VALUE;
 +    AVRational *frame_sample_aspect;
 +    AVPacket avpkt;
 +
 +    // With fate-indeo3-2, we're getting 0-sized packets before EOF for some
 +    // reason. This seems like a semi-critical bug. Don't trigger EOF, and
 +    // skip the packet.
 +    if (!eof && pkt && pkt->size == 0)
 +        return 0;
 +
 +    if (!ist->decoded_frame && !(ist->decoded_frame = av_frame_alloc()))
 +        return AVERROR(ENOMEM);
 +    if (!ist->filter_frame && !(ist->filter_frame = av_frame_alloc()))
 +        return AVERROR(ENOMEM);
 +    decoded_frame = ist->decoded_frame;
 +    if (ist->dts != AV_NOPTS_VALUE)
 +        dts = av_rescale_q(ist->dts, AV_TIME_BASE_Q, ist->st->time_base);
 +    if (pkt) {
 +        avpkt = *pkt;
 +        avpkt.dts = dts; // ffmpeg.c probably shouldn't do this
 +    }
 +
 +    // The old code used to set dts on the drain packet, which does not work
 +    // with the new API anymore.
 +    if (eof) {
 +        void *new = av_realloc_array(ist->dts_buffer, ist->nb_dts_buffer + 1, sizeof(ist->dts_buffer[0]));
 +        if (!new)
 +            return AVERROR(ENOMEM);
 +        ist->dts_buffer = new;
 +        ist->dts_buffer[ist->nb_dts_buffer++] = dts;
 +    }
 +
 +    update_benchmark(NULL);
 +    ret = decode(ist->dec_ctx, decoded_frame, got_output, pkt ? &avpkt : NULL);
 +    update_benchmark("decode_video %d.%d", ist->file_index, ist->st->index);
 +
 +    // The following line may be required in some cases where there is no parser
 +    // or the parser does not has_b_frames correctly
 +    if (ist->st->codecpar->video_delay < ist->dec_ctx->has_b_frames) {
 +        if (ist->dec_ctx->codec_id == AV_CODEC_ID_H264) {
 +            ist->st->codecpar->video_delay = ist->dec_ctx->has_b_frames;
 +        } else
 +            av_log(ist->dec_ctx, AV_LOG_WARNING,
 +                   "video_delay is larger in decoder than demuxer %d > %d.\n"
 +                   "If you want to help, upload a sample "
 +                   "of this file to ftp://upload.ffmpeg.org/incoming/ "
 +                   "and contact the ffmpeg-devel mailing list. (ffmpeg-devel at ffmpeg.org)",
 +                   ist->dec_ctx->has_b_frames,
 +                   ist->st->codecpar->video_delay);
 +    }
 +
 +    if (ret != AVERROR_EOF)
 +        check_decode_result(ist, got_output, ret);
 +
 +    if (*got_output && ret >= 0) {
 +        if (ist->dec_ctx->width  != decoded_frame->width ||
 +            ist->dec_ctx->height != decoded_frame->height ||
 +            ist->dec_ctx->pix_fmt != decoded_frame->format) {
 +            av_log(NULL, AV_LOG_DEBUG, "Frame parameters mismatch context %d,%d,%d != %d,%d,%d\n",
 +                decoded_frame->width,
 +                decoded_frame->height,
 +                decoded_frame->format,
 +                ist->dec_ctx->width,
 +                ist->dec_ctx->height,
 +                ist->dec_ctx->pix_fmt);
 +        }
 +    }
 +
 +    if (!*got_output || ret < 0)
 +        return ret;
 +
 +    if(ist->top_field_first>=0)
 +        decoded_frame->top_field_first = ist->top_field_first;
 +
 +    ist->frames_decoded++;
 +
 +    if (ist->hwaccel_retrieve_data && decoded_frame->format == ist->hwaccel_pix_fmt) {
 +        err = ist->hwaccel_retrieve_data(ist->dec_ctx, decoded_frame);
 +        if (err < 0)
 +            goto fail;
 +    }
 +    ist->hwaccel_retrieved_pix_fmt = decoded_frame->format;
 +
 +    best_effort_timestamp= av_frame_get_best_effort_timestamp(decoded_frame);
 +
 +    if (eof && best_effort_timestamp == AV_NOPTS_VALUE && ist->nb_dts_buffer > 0) {
 +        best_effort_timestamp = ist->dts_buffer[0];
 +
 +        for (i = 0; i < ist->nb_dts_buffer - 1; i++)
 +            ist->dts_buffer[i] = ist->dts_buffer[i + 1];
 +        ist->nb_dts_buffer--;
 +    }
 +
 +    if(best_effort_timestamp != AV_NOPTS_VALUE) {
 +        int64_t ts = av_rescale_q(decoded_frame->pts = best_effort_timestamp, ist->st->time_base, AV_TIME_BASE_Q);
 +
 +        if (ts != AV_NOPTS_VALUE)
 +            ist->next_pts = ist->pts = ts;
 +    }
 +
 +    if (debug_ts) {
 +        av_log(NULL, AV_LOG_INFO, "decoder -> ist_index:%d type:video "
 +               "frame_pts:%s frame_pts_time:%s best_effort_ts:%"PRId64" best_effort_ts_time:%s keyframe:%d frame_type:%d time_base:%d/%d\n",
 +               ist->st->index, av_ts2str(decoded_frame->pts),
 +               av_ts2timestr(decoded_frame->pts, &ist->st->time_base),
 +               best_effort_timestamp,
 +               av_ts2timestr(best_effort_timestamp, &ist->st->time_base),
 +               decoded_frame->key_frame, decoded_frame->pict_type,
 +               ist->st->time_base.num, ist->st->time_base.den);
 +    }
 +
 +    if (ist->st->sample_aspect_ratio.num)
 +        decoded_frame->sample_aspect_ratio = ist->st->sample_aspect_ratio;
 +
 +    resample_changed = ist->resample_width   != decoded_frame->width  ||
 +                       ist->resample_height  != decoded_frame->height ||
 +                       ist->resample_pix_fmt != decoded_frame->format;
 +    if (resample_changed) {
 +        av_log(NULL, AV_LOG_INFO,
 +               "Input stream #%d:%d frame changed from size:%dx%d fmt:%s to size:%dx%d fmt:%s\n",
 +               ist->file_index, ist->st->index,
 +               ist->resample_width,  ist->resample_height,  av_get_pix_fmt_name(ist->resample_pix_fmt),
 +               decoded_frame->width, decoded_frame->height, av_get_pix_fmt_name(decoded_frame->format));
 +
 +        ist->resample_width   = decoded_frame->width;
 +        ist->resample_height  = decoded_frame->height;
 +        ist->resample_pix_fmt = decoded_frame->format;
 +
 +        for (i = 0; i < nb_filtergraphs; i++) {
 +            if (ist_in_filtergraph(filtergraphs[i], ist) && ist->reinit_filters &&
 +                configure_filtergraph(filtergraphs[i]) < 0) {
 +                av_log(NULL, AV_LOG_FATAL, "Error reinitializing filters!\n");
 +                exit_program(1);
 +            }
 +        }
 +    }
 +
 +    frame_sample_aspect= av_opt_ptr(avcodec_get_frame_class(), decoded_frame, "sample_aspect_ratio");
 +    for (i = 0; i < ist->nb_filters; i++) {
 +        if (!frame_sample_aspect->num)
 +            *frame_sample_aspect = ist->st->sample_aspect_ratio;
 +
 +        if (i < ist->nb_filters - 1) {
 +            f = ist->filter_frame;
 +            err = av_frame_ref(f, decoded_frame);
 +            if (err < 0)
 +                break;
 +        } else
 +            f = decoded_frame;
 +        err = av_buffersrc_add_frame_flags(ist->filters[i]->filter, f, AV_BUFFERSRC_FLAG_PUSH);
 +        if (err == AVERROR_EOF) {
 +            err = 0; /* ignore */
 +        } else if (err < 0) {
 +            av_log(NULL, AV_LOG_FATAL,
 +                   "Failed to inject frame into filter network: %s\n", av_err2str(err));
 +            exit_program(1);
 +        }
 +    }
 +
 +fail:
 +    av_frame_unref(ist->filter_frame);
 +    av_frame_unref(decoded_frame);
 +    return err < 0 ? err : ret;
 +}
 +
 +static int transcode_subtitles(InputStream *ist, AVPacket *pkt, int *got_output)
 +{
 +    AVSubtitle subtitle;
 +    int i, ret = avcodec_decode_subtitle2(ist->dec_ctx,
 +                                          &subtitle, got_output, pkt);
 +
 +    check_decode_result(NULL, got_output, ret);
 +
 +    if (ret < 0 || !*got_output) {
 +        if (!pkt->size)
 +            sub2video_flush(ist);
 +        return ret;
 +    }
 +
 +    if (ist->fix_sub_duration) {
 +        int end = 1;
 +        if (ist->prev_sub.got_output) {
 +            end = av_rescale(subtitle.pts - ist->prev_sub.subtitle.pts,
 +                             1000, AV_TIME_BASE);
 +            if (end < ist->prev_sub.subtitle.end_display_time) {
 +                av_log(ist->dec_ctx, AV_LOG_DEBUG,
 +                       "Subtitle duration reduced from %d to %d%s\n",
 +                       ist->prev_sub.subtitle.end_display_time, end,
 +                       end <= 0 ? ", dropping it" : "");
 +                ist->prev_sub.subtitle.end_display_time = end;
 +            }
 +        }
 +        FFSWAP(int,        *got_output, ist->prev_sub.got_output);
 +        FFSWAP(int,        ret,         ist->prev_sub.ret);
 +        FFSWAP(AVSubtitle, subtitle,    ist->prev_sub.subtitle);
 +        if (end <= 0)
 +            goto out;
 +    }
 +
 +    if (!*got_output)
 +        return ret;
 +
 +    sub2video_update(ist, &subtitle);
 +
 +    if (!subtitle.num_rects)
 +        goto out;
 +
 +    ist->frames_decoded++;
 +
 +    for (i = 0; i < nb_output_streams; i++) {
 +        OutputStream *ost = output_streams[i];
 +
 +        if (!check_output_constraints(ist, ost) || !ost->encoding_needed
 +            || ost->enc->type != AVMEDIA_TYPE_SUBTITLE)
 +            continue;
 +
 +        do_subtitle_out(output_files[ost->file_index]->ctx, ost, ist, &subtitle);
 +    }
 +
 +out:
 +    avsubtitle_free(&subtitle);
 +    return ret;
 +}
 +
 +static int send_filter_eof(InputStream *ist)
 +{
 +    int i, ret;
 +    for (i = 0; i < ist->nb_filters; i++) {
 +        ret = av_buffersrc_add_frame(ist->filters[i]->filter, NULL);
 +        if (ret < 0)
 +            return ret;
 +    }
 +    return 0;
 +}
 +
 +/* pkt = NULL means EOF (needed to flush decoder buffers) */
 +static int process_input_packet(InputStream *ist, const AVPacket *pkt, int no_eof)
 +{
 +    int ret = 0, i;
 +    int repeating = 0;
 +    int eof_reached = 0;
 +
 +    AVPacket avpkt;
 +    if (!ist->saw_first_ts) {
 +        ist->dts = ist->st->avg_frame_rate.num ? - ist->dec_ctx->has_b_frames * AV_TIME_BASE / av_q2d(ist->st->avg_frame_rate) : 0;
 +        ist->pts = 0;
 +        if (pkt && pkt->pts != AV_NOPTS_VALUE && !ist->decoding_needed) {
 +            ist->dts += av_rescale_q(pkt->pts, ist->st->time_base, AV_TIME_BASE_Q);
 +            ist->pts = ist->dts; //unused but better to set it to a value thats not totally wrong
 +        }
 +        ist->saw_first_ts = 1;
 +    }
 +
 +    if (ist->next_dts == AV_NOPTS_VALUE)
 +        ist->next_dts = ist->dts;
 +    if (ist->next_pts == AV_NOPTS_VALUE)
 +        ist->next_pts = ist->pts;
 +
 +    if (!pkt) {
 +        /* EOF handling */
 +        av_init_packet(&avpkt);
 +        avpkt.data = NULL;
 +        avpkt.size = 0;
 +    } else {
 +        avpkt = *pkt;
 +    }
 +
 +    if (pkt && pkt->dts != AV_NOPTS_VALUE) {
 +        ist->next_dts = ist->dts = av_rescale_q(pkt->dts, ist->st->time_base, AV_TIME_BASE_Q);
 +        if (ist->dec_ctx->codec_type != AVMEDIA_TYPE_VIDEO || !ist->decoding_needed)
 +            ist->next_pts = ist->pts = ist->dts;
 +    }
 +
 +    // while we have more to decode or while the decoder did output something on EOF
 +    while (ist->decoding_needed) {
 +        int duration = 0;
 +        int got_output = 0;
 +
 +        ist->pts = ist->next_pts;
 +        ist->dts = ist->next_dts;
 +
 +        switch (ist->dec_ctx->codec_type) {
 +        case AVMEDIA_TYPE_AUDIO:
 +            ret = decode_audio    (ist, repeating ? NULL : &avpkt, &got_output);
 +            break;
 +        case AVMEDIA_TYPE_VIDEO:
 +            ret = decode_video    (ist, repeating ? NULL : &avpkt, &got_output, !pkt);
 +            if (!repeating || !pkt || got_output) {
 +                if (pkt && pkt->duration) {
 +                    duration = av_rescale_q(pkt->duration, ist->st->time_base, AV_TIME_BASE_Q);
 +                } else if(ist->dec_ctx->framerate.num != 0 && ist->dec_ctx->framerate.den != 0) {
 +                    int ticks= av_stream_get_parser(ist->st) ? av_stream_get_parser(ist->st)->repeat_pict+1 : ist->dec_ctx->ticks_per_frame;
 +                    duration = ((int64_t)AV_TIME_BASE *
 +                                    ist->dec_ctx->framerate.den * ticks) /
 +                                    ist->dec_ctx->framerate.num / ist->dec_ctx->ticks_per_frame;
 +                }
 +
 +                if(ist->dts != AV_NOPTS_VALUE && duration) {
 +                    ist->next_dts += duration;
 +                }else
 +                    ist->next_dts = AV_NOPTS_VALUE;
 +            }
 +
 +            if (got_output)
 +                ist->next_pts += duration; //FIXME the duration is not correct in some cases
 +            break;
 +        case AVMEDIA_TYPE_SUBTITLE:
 +            if (repeating)
 +                break;
 +            ret = transcode_subtitles(ist, &avpkt, &got_output);
 +            if (!pkt && ret >= 0)
 +                ret = AVERROR_EOF;
 +            break;
 +        default:
 +            return -1;
 +        }
 +
 +        if (ret == AVERROR_EOF) {
 +            eof_reached = 1;
 +            break;
 +        }
 +
 +        if (ret < 0) {
 +            av_log(NULL, AV_LOG_ERROR, "Error while decoding stream #%d:%d: %s\n",
 +                   ist->file_index, ist->st->index, av_err2str(ret));
 +            if (exit_on_error)
 +                exit_program(1);
 +            // Decoding might not terminate if we're draining the decoder, and
 +            // the decoder keeps returning an error.
 +            // This should probably be considered a libavcodec issue.
 +            // Sample: fate-vsynth1-dnxhd-720p-hr-lb
 +            if (!pkt)
 +                eof_reached = 1;
 +            break;
 +        }
 +
 +        if (!got_output)
 +            break;
 +
 +        // During draining, we might get multiple output frames in this loop.
 +        // ffmpeg.c does not drain the filter chain on configuration changes,
 +        // which means if we send multiple frames at once to the filters, and
 +        // one of those frames changes configuration, the buffered frames will
 +        // be lost. This can upset certain FATE tests.
 +        // Decode only 1 frame per call on EOF to appease these FATE tests.
 +        // The ideal solution would be to rewrite decoding to use the new
 +        // decoding API in a better way.
 +        if (!pkt)
 +            break;
 +
 +        repeating = 1;
 +    }
 +
 +    /* after flushing, send an EOF on all the filter inputs attached to the stream */
 +    /* except when looping we need to flush but not to send an EOF */
 +    if (!pkt && ist->decoding_needed && eof_reached && !no_eof) {
 +        int ret = send_filter_eof(ist);
 +        if (ret < 0) {
 +            av_log(NULL, AV_LOG_FATAL, "Error marking filters as finished\n");
 +            exit_program(1);
 +        }
 +    }
 +
 +    /* handle stream copy */
 +    if (!ist->decoding_needed) {
 +        ist->dts = ist->next_dts;
 +        switch (ist->dec_ctx->codec_type) {
 +        case AVMEDIA_TYPE_AUDIO:
 +            ist->next_dts += ((int64_t)AV_TIME_BASE * ist->dec_ctx->frame_size) /
 +                             ist->dec_ctx->sample_rate;
 +            break;
 +        case AVMEDIA_TYPE_VIDEO:
 +            if (ist->framerate.num) {
 +                // TODO: Remove work-around for c99-to-c89 issue 7
 +                AVRational time_base_q = AV_TIME_BASE_Q;
 +                int64_t next_dts = av_rescale_q(ist->next_dts, time_base_q, av_inv_q(ist->framerate));
 +                ist->next_dts = av_rescale_q(next_dts + 1, av_inv_q(ist->framerate), time_base_q);
 +            } else if (pkt->duration) {
 +                ist->next_dts += av_rescale_q(pkt->duration, ist->st->time_base, AV_TIME_BASE_Q);
 +            } else if(ist->dec_ctx->framerate.num != 0) {
 +                int ticks= av_stream_get_parser(ist->st) ? av_stream_get_parser(ist->st)->repeat_pict + 1 : ist->dec_ctx->ticks_per_frame;
 +                ist->next_dts += ((int64_t)AV_TIME_BASE *
 +                                  ist->dec_ctx->framerate.den * ticks) /
 +                                  ist->dec_ctx->framerate.num / ist->dec_ctx->ticks_per_frame;
 +            }
 +            break;
 +        }
 +        ist->pts = ist->dts;
 +        ist->next_pts = ist->next_dts;
 +    }
 +    for (i = 0; pkt && i < nb_output_streams; i++) {
 +        OutputStream *ost = output_streams[i];
 +
 +        if (!check_output_constraints(ist, ost) || ost->encoding_needed)
 +            continue;
 +
 +        do_streamcopy(ist, ost, pkt);
 +    }
 +
 +    return !eof_reached;
 +}
 +
 +static void print_sdp(void)
 +{
 +    char sdp[16384];
 +    int i;
 +    int j;
 +    AVIOContext *sdp_pb;
 +    AVFormatContext **avc = av_malloc_array(nb_output_files, sizeof(*avc));
 +
 +    if (!avc)
 +        exit_program(1);
 +    for (i = 0, j = 0; i < nb_output_files; i++) {
 +        if (!strcmp(output_files[i]->ctx->oformat->name, "rtp")) {
 +            avc[j] = output_files[i]->ctx;
 +            j++;
 +        }
 +    }
 +
 +    if (!j)
 +        goto fail;
 +
 +    av_sdp_create(avc, j, sdp, sizeof(sdp));
 +
 +    if (!sdp_filename) {
 +        printf("SDP:\n%s\n", sdp);
 +        fflush(stdout);
 +    } else {
 +        if (avio_open2(&sdp_pb, sdp_filename, AVIO_FLAG_WRITE, &int_cb, NULL) < 0) {
 +            av_log(NULL, AV_LOG_ERROR, "Failed to open sdp file '%s'\n", sdp_filename);
 +        } else {
 +            avio_printf(sdp_pb, "SDP:\n%s", sdp);
 +            avio_closep(&sdp_pb);
 +            av_freep(&sdp_filename);
 +        }
 +    }
 +
 +fail:
 +    av_freep(&avc);
 +}
 +
 +static const HWAccel *get_hwaccel(enum AVPixelFormat pix_fmt)
 +{
 +    int i;
 +    for (i = 0; hwaccels[i].name; i++)
 +        if (hwaccels[i].pix_fmt == pix_fmt)
 +            return &hwaccels[i];
 +    return NULL;
 +}
 +
 +static enum AVPixelFormat get_format(AVCodecContext *s, const enum AVPixelFormat *pix_fmts)
 +{
 +    InputStream *ist = s->opaque;
 +    const enum AVPixelFormat *p;
 +    int ret;
 +
 +    for (p = pix_fmts; *p != -1; p++) {
 +        const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(*p);
 +        const HWAccel *hwaccel;
 +
 +        if (!(desc->flags & AV_PIX_FMT_FLAG_HWACCEL))
 +            break;
 +
 +        hwaccel = get_hwaccel(*p);
 +        if (!hwaccel ||
 +            (ist->active_hwaccel_id && ist->active_hwaccel_id != hwaccel->id) ||
 +            (ist->hwaccel_id != HWACCEL_AUTO && ist->hwaccel_id != hwaccel->id))
 +            continue;
 +
 +        ret = hwaccel->init(s);
 +        if (ret < 0) {
 +            if (ist->hwaccel_id == hwaccel->id) {
 +                av_log(NULL, AV_LOG_FATAL,
 +                       "%s hwaccel requested for input stream #%d:%d, "
 +                       "but cannot be initialized.\n", hwaccel->name,
 +                       ist->file_index, ist->st->index);
 +                return AV_PIX_FMT_NONE;
 +            }
 +            continue;
 +        }
 +
 +        if (ist->hw_frames_ctx) {
 +            s->hw_frames_ctx = av_buffer_ref(ist->hw_frames_ctx);
 +            if (!s->hw_frames_ctx)
 +                return AV_PIX_FMT_NONE;
 +        }
 +
 +        ist->active_hwaccel_id = hwaccel->id;
 +        ist->hwaccel_pix_fmt   = *p;
 +        break;
 +    }
 +
 +    return *p;
 +}
 +
 +static int get_buffer(AVCodecContext *s, AVFrame *frame, int flags)
 +{
 +    InputStream *ist = s->opaque;
 +
 +    if (ist->hwaccel_get_buffer && frame->format == ist->hwaccel_pix_fmt)
 +        return ist->hwaccel_get_buffer(s, frame, flags);
 +
 +    return avcodec_default_get_buffer2(s, frame, flags);
 +}
 +
 +static int init_input_stream(int ist_index, char *error, int error_len)
 +{
 +    int ret;
 +    InputStream *ist = input_streams[ist_index];
 +
 +    if (ist->decoding_needed) {
 +        AVCodec *codec = ist->dec;
 +        if (!codec) {
 +            snprintf(error, error_len, "Decoder (codec %s) not found for input stream #%d:%d",
 +                    avcodec_get_name(ist->dec_ctx->codec_id), ist->file_index, ist->st->index);
 +            return AVERROR(EINVAL);
 +        }
 +
 +        ist->dec_ctx->opaque                = ist;
 +        ist->dec_ctx->get_format            = get_format;
 +        ist->dec_ctx->get_buffer2           = get_buffer;
 +        ist->dec_ctx->thread_safe_callbacks = 1;
 +
 +        av_opt_set_int(ist->dec_ctx, "refcounted_frames", 1, 0);
 +        if (ist->dec_ctx->codec_id == AV_CODEC_ID_DVB_SUBTITLE &&
 +           (ist->decoding_needed & DECODING_FOR_OST)) {
 +            av_dict_set(&ist->decoder_opts, "compute_edt", "1", AV_DICT_DONT_OVERWRITE);
 +            if (ist->decoding_needed & DECODING_FOR_FILTER)
 +                av_log(NULL, AV_LOG_WARNING, "Warning using DVB subtitles for filtering and output at the same time is not fully supported, also see -compute_edt [0|1]\n");
 +        }
 +
 +        av_dict_set(&ist->decoder_opts, "sub_text_format", "ass", AV_DICT_DONT_OVERWRITE);
 +
 +        /* Useful for subtitles retiming by lavf (FIXME), skipping samples in
 +         * audio, and video decoders such as cuvid or mediacodec */
 +        av_codec_set_pkt_timebase(ist->dec_ctx, ist->st->time_base);
 +
 +        if (!av_dict_get(ist->decoder_opts, "threads", NULL, 0))
 +            av_dict_set(&ist->decoder_opts, "threads", "auto", 0);
 +        if ((ret = avcodec_open2(ist->dec_ctx, codec, &ist->decoder_opts)) < 0) {
 +            if (ret == AVERROR_EXPERIMENTAL)
 +                abort_codec_experimental(codec, 0);
 +
 +            snprintf(error, error_len,
 +                     "Error while opening decoder for input stream "
 +                     "#%d:%d : %s",
 +                     ist->file_index, ist->st->index, av_err2str(ret));
 +            return ret;
 +        }
 +        assert_avoptions(ist->decoder_opts);
 +    }
 +
 +    ist->next_pts = AV_NOPTS_VALUE;
 +    ist->next_dts = AV_NOPTS_VALUE;
 +
 +    return 0;
 +}
 +
 +static InputStream *get_input_stream(OutputStream *ost)
 +{
 +    if (ost->source_index >= 0)
 +        return input_streams[ost->source_index];
 +    return NULL;
 +}
 +
 +static int compare_int64(const void *a, const void *b)
 +{
 +    return FFDIFFSIGN(*(const int64_t *)a, *(const int64_t *)b);
 +}
 +
 +static int init_output_bsfs(OutputStream *ost)
 +{
 +    AVBSFContext *ctx;
 +    int i, ret;
 +
 +    if (!ost->nb_bitstream_filters)
 +        return 0;
 +
 +    for (i = 0; i < ost->nb_bitstream_filters; i++) {
 +        ctx = ost->bsf_ctx[i];
 +
 +        ret = avcodec_parameters_copy(ctx->par_in,
 +                                      i ? ost->bsf_ctx[i - 1]->par_out : ost->st->codecpar);
 +        if (ret < 0)
 +            return ret;
 +
 +        ctx->time_base_in = i ? ost->bsf_ctx[i - 1]->time_base_out : ost->st->time_base;
 +
 +        ret = av_bsf_init(ctx);
 +        if (ret < 0) {
 +            av_log(NULL, AV_LOG_ERROR, "Error initializing bitstream filter: %s\n",
 +                   ost->bsf_ctx[i]->filter->name);
 +            return ret;
 +        }
 +    }
 +
 +    ctx = ost->bsf_ctx[ost->nb_bitstream_filters - 1];
 +    ret = avcodec_parameters_copy(ost->st->codecpar, ctx->par_out);
 +    if (ret < 0)
 +        return ret;
 +
 +    ost->st->time_base = ctx->time_base_out;
 +
 +    return 0;
 +}
 +
 +static int init_output_stream_streamcopy(OutputStream *ost)
 +{
 +    OutputFile *of = output_files[ost->file_index];
 +    InputStream *ist = get_input_stream(ost);
 +    AVCodecParameters *par_dst = ost->st->codecpar;
 +    AVCodecParameters *par_src = ost->ref_par;
 +    AVRational sar;
 +    int i, ret;
 +    uint64_t extra_size;
 +
 +    av_assert0(ist && !ost->filter);
 +
 +    avcodec_parameters_to_context(ost->enc_ctx, ist->st->codecpar);
 +    ret = av_opt_set_dict(ost->enc_ctx, &ost->encoder_opts);
 +    if (ret < 0) {
 +        av_log(NULL, AV_LOG_FATAL,
 +               "Error setting up codec context options.\n");
 +        return ret;
 +    }
 +    avcodec_parameters_from_context(par_src, ost->enc_ctx);
 +
 +    extra_size = (uint64_t)par_src->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE;
 +
 +    if (extra_size > INT_MAX) {
 +        return AVERROR(EINVAL);
 +    }
 +
 +    /* if stream_copy is selected, no need to decode or encode */
 +    par_dst->codec_id   = par_src->codec_id;
 +    par_dst->codec_type = par_src->codec_type;
 +
 +    if (!par_dst->codec_tag) {
 +        unsigned int codec_tag;
 +        if (!of->ctx->oformat->codec_tag ||
 +            av_codec_get_id (of->ctx->oformat->codec_tag, par_src->codec_tag) == par_dst->codec_id ||
 +            !av_codec_get_tag2(of->ctx->oformat->codec_tag, par_src->codec_id, &codec_tag))
 +            par_dst->codec_tag = par_src->codec_tag;
 +    }
 +
 +    par_dst->bit_rate        = par_src->bit_rate;
 +    par_dst->field_order     = par_src->field_order;
 +    par_dst->chroma_location = par_src->chroma_location;
 +
 +    if (par_src->extradata_size) {
 +        par_dst->extradata      = av_mallocz(extra_size);
 +        if (!par_dst->extradata) {
 +            return AVERROR(ENOMEM);
 +        }
 +        memcpy(par_dst->extradata, par_src->extradata, par_src->extradata_size);
 +        par_dst->extradata_size = par_src->extradata_size;
 +    }
 +    par_dst->bits_per_coded_sample  = par_src->bits_per_coded_sample;
 +    par_dst->bits_per_raw_sample    = par_src->bits_per_raw_sample;
 +
 +    if (!ost->frame_rate.num)
 +        ost->frame_rate = ist->framerate;
 +    ost->st->avg_frame_rate = ost->frame_rate;
 +
 +    ret = avformat_transfer_internal_stream_timing_info(of->ctx->oformat, ost->st, ist->st, copy_tb);
 +    if (ret < 0)
 +        return ret;
 +
 +    // copy timebase while removing common factors
 +    ost->st->time_base = av_add_q(av_stream_get_codec_timebase(ost->st), (AVRational){0, 1});
 +
 +    if (ist->st->nb_side_data) {
 +        ost->st->side_data = av_realloc_array(NULL, ist->st->nb_side_data,
 +                                              sizeof(*ist->st->side_data));
 +        if (!ost->st->side_data)
 +            return AVERROR(ENOMEM);
 +
 +        ost->st->nb_side_data = 0;
 +        for (i = 0; i < ist->st->nb_side_data; i++) {
 +            const AVPacketSideData *sd_src = &ist->st->side_data[i];
 +            AVPacketSideData *sd_dst = &ost->st->side_data[ost->st->nb_side_data];
 +
 +            if (ost->rotate_overridden && sd_src->type == AV_PKT_DATA_DISPLAYMATRIX)
 +                continue;
 +
 +            sd_dst->data = av_malloc(sd_src->size);
 +            if (!sd_dst->data)
 +                return AVERROR(ENOMEM);
 +            memcpy(sd_dst->data, sd_src->data, sd_src->size);
 +            sd_dst->size = sd_src->size;
 +            sd_dst->type = sd_src->type;
 +            ost->st->nb_side_data++;
 +        }
 +    }
 +
 +    ost->parser = av_parser_init(par_dst->codec_id);
 +    ost->parser_avctx = avcodec_alloc_context3(NULL);
 +    if (!ost->parser_avctx)
 +        return AVERROR(ENOMEM);
 +
 +    switch (par_dst->codec_type) {
 +    case AVMEDIA_TYPE_AUDIO:
 +        if (audio_volume != 256) {
 +            av_log(NULL, AV_LOG_FATAL, "-acodec copy and -vol are incompatible (frames are not decoded)\n");
 +            exit_program(1);
 +        }
 +        par_dst->channel_layout     = par_src->channel_layout;
 +        par_dst->sample_rate        = par_src->sample_rate;
 +        par_dst->channels           = par_src->channels;
 +        par_dst->frame_size         = par_src->frame_size;
 +        par_dst->block_align        = par_src->block_align;
 +        par_dst->initial_padding    = par_src->initial_padding;
 +        par_dst->trailing_padding   = par_src->trailing_padding;
 +        par_dst->profile            = par_src->profile;
 +        if((par_dst->block_align == 1 || par_dst->block_align == 1152 || par_dst->block_align == 576) && par_dst->codec_id == AV_CODEC_ID_MP3)
 +            par_dst->block_align= 0;
 +        if(par_dst->codec_id == AV_CODEC_ID_AC3)
 +            par_dst->block_align= 0;
 +        break;
 +    case AVMEDIA_TYPE_VIDEO:
 +        par_dst->format             = par_src->format;
 +        par_dst->color_space        = par_src->color_space;
 +        par_dst->color_range        = par_src->color_range;
 +        par_dst->color_primaries    = par_src->color_primaries;
 +        par_dst->color_trc          = par_src->color_trc;
 +        par_dst->width              = par_src->width;
 +        par_dst->height             = par_src->height;
 +        par_dst->video_delay        = par_src->video_delay;
 +        par_dst->profile            = par_src->profile;
 +        if (ost->frame_aspect_ratio.num) { // overridden by the -aspect cli option
 +            sar =
 +                av_mul_q(ost->frame_aspect_ratio,
 +                         (AVRational){ par_dst->height, par_dst->width });
 +            av_log(NULL, AV_LOG_WARNING, "Overriding aspect ratio "
 +                   "with stream copy may produce invalid files\n");
 +            }
 +        else if (ist->st->sample_aspect_ratio.num)
 +            sar = ist->st->sample_aspect_ratio;
 +        else
 +            sar = par_src->sample_aspect_ratio;
 +        ost->st->sample_aspect_ratio = par_dst->sample_aspect_ratio = sar;
 +        ost->st->avg_frame_rate = ist->st->avg_frame_rate;
 +        ost->st->r_frame_rate = ist->st->r_frame_rate;
 +        break;
 +    case AVMEDIA_TYPE_SUBTITLE:
 +        par_dst->width  = par_src->width;
 +        par_dst->height = par_src->height;
 +        break;
 +    case AVMEDIA_TYPE_UNKNOWN:
 +    case AVMEDIA_TYPE_DATA:
 +    case AVMEDIA_TYPE_ATTACHMENT:
 +        break;
 +    default:
 +        abort();
 +    }
 +
 +    return 0;
 +}
 +
 +static int init_output_stream(OutputStream *ost, char *error, int error_len)
 +{
 +    int ret = 0;
 +
 +    if (ost->encoding_needed) {
 +        AVCodec      *codec = ost->enc;
 +        AVCodecContext *dec = NULL;
 +        InputStream *ist;
 +
 +        if ((ist = get_input_stream(ost)))
 +            dec = ist->dec_ctx;
 +        if (dec && dec->subtitle_header) {
 +            /* ASS code assumes this buffer is null terminated so add extra byte. */
 +            ost->enc_ctx->subtitle_header = av_mallocz(dec->subtitle_header_size + 1);
 +            if (!ost->enc_ctx->subtitle_header)
 +                return AVERROR(ENOMEM);
 +            memcpy(ost->enc_ctx->subtitle_header, dec->subtitle_header, dec->subtitle_header_size);
 +            ost->enc_ctx->subtitle_header_size = dec->subtitle_header_size;
 +        }
 +        if (!av_dict_get(ost->encoder_opts, "threads", NULL, 0))
 +            av_dict_set(&ost->encoder_opts, "threads", "auto", 0);
 +        if (ost->enc->type == AVMEDIA_TYPE_AUDIO &&
 +            !codec->defaults &&
 +            !av_dict_get(ost->encoder_opts, "b", NULL, 0) &&
 +            !av_dict_get(ost->encoder_opts, "ab", NULL, 0))
 +            av_dict_set(&ost->encoder_opts, "b", "128000", 0);
 +
 +        if (ost->filter && ost->filter->filter->inputs[0]->hw_frames_ctx) {
 +            ost->enc_ctx->hw_frames_ctx = av_buffer_ref(ost->filter->filter->inputs[0]->hw_frames_ctx);
 +            if (!ost->enc_ctx->hw_frames_ctx)
 +                return AVERROR(ENOMEM);
 +        }
 +
 +        if ((ret = avcodec_open2(ost->enc_ctx, codec, &ost->encoder_opts)) < 0) {
 +            if (ret == AVERROR_EXPERIMENTAL)
 +                abort_codec_experimental(codec, 1);
 +            snprintf(error, error_len,
 +                     "Error while opening encoder for output stream #%d:%d - "
 +                     "maybe incorrect parameters such as bit_rate, rate, width or height",
 +                    ost->file_index, ost->index);
 +            return ret;
 +        }
 +        if (ost->enc->type == AVMEDIA_TYPE_AUDIO &&
 +            !(ost->enc->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE))
 +            av_buffersink_set_frame_size(ost->filter->filter,
 +                                            ost->enc_ctx->frame_size);
 +        assert_avoptions(ost->encoder_opts);
 +        if (ost->enc_ctx->bit_rate && ost->enc_ctx->bit_rate < 1000)
 +            av_log(NULL, AV_LOG_WARNING, "The bitrate parameter is set too low."
 +                                         " It takes bits/s as argument, not kbits/s\n");
 +
 +        ret = avcodec_parameters_from_context(ost->st->codecpar, ost->enc_ctx);
 +        if (ret < 0) {
 +            av_log(NULL, AV_LOG_FATAL,
 +                   "Error initializing the output stream codec context.\n");
 +            exit_program(1);
 +        }
 +        /*
 +         * FIXME: ost->st->codec should't be needed here anymore.
 +         */
 +        ret = avcodec_copy_context(ost->st->codec, ost->enc_ctx);
 +        if (ret < 0)
 +            return ret;
 +
 +        if (ost->enc_ctx->nb_coded_side_data) {
 +            int i;
 +
 +            ost->st->side_data = av_realloc_array(NULL, ost->enc_ctx->nb_coded_side_data,
 +                                                  sizeof(*ost->st->side_data));
 +            if (!ost->st->side_data)
 +                return AVERROR(ENOMEM);
 +
 +            for (i = 0; i < ost->enc_ctx->nb_coded_side_data; i++) {
 +                const AVPacketSideData *sd_src = &ost->enc_ctx->coded_side_data[i];
 +                AVPacketSideData *sd_dst = &ost->st->side_data[i];
 +
 +                sd_dst->data = av_malloc(sd_src->size);
 +                if (!sd_dst->data)
 +                    return AVERROR(ENOMEM);
 +                memcpy(sd_dst->data, sd_src->data, sd_src->size);
 +                sd_dst->size = sd_src->size;
 +                sd_dst->type = sd_src->type;
 +                ost->st->nb_side_data++;
 +            }
 +        }
 +
 +        // copy timebase while removing common factors
 +        ost->st->time_base = av_add_q(ost->enc_ctx->time_base, (AVRational){0, 1});
 +        ost->st->codec->codec= ost->enc_ctx->codec;
 +    } else if (ost->stream_copy) {
 +        ret = init_output_stream_streamcopy(ost);
 +        if (ret < 0)
 +            return ret;
 +
 +        /*
 +         * FIXME: will the codec context used by the parser during streamcopy
 +         * This should go away with the new parser API.
 +         */
 +        ret = avcodec_parameters_to_context(ost->parser_avctx, ost->st->codecpar);
 +        if (ret < 0)
 +            return ret;
 +    }
 +
 +    /* initialize bitstream filters for the output stream
 +     * needs to be done here, because the codec id for streamcopy is not
 +     * known until now */
 +    ret = init_output_bsfs(ost);
 +    if (ret < 0)
 +        return ret;
 +
 +    return ret;
 +}
 +
 +static void parse_forced_key_frames(char *kf, OutputStream *ost,
 +                                    AVCodecContext *avctx)
 +{
 +    char *p;
 +    int n = 1, i, size, index = 0;
 +    int64_t t, *pts;
 +
 +    for (p = kf; *p; p++)
 +        if (*p == ',')
 +            n++;
 +    size = n;
 +    pts = av_malloc_array(size, sizeof(*pts));
 +    if (!pts) {
 +        av_log(NULL, AV_LOG_FATAL, "Could not allocate forced key frames array.\n");
 +        exit_program(1);
 +    }
 +
 +    p = kf;
 +    for (i = 0; i < n; i++) {
 +        char *next = strchr(p, ',');
 +
 +        if (next)
 +            *next++ = 0;
 +
 +        if (!memcmp(p, "chapters", 8)) {
 +
 +            AVFormatContext *avf = output_files[ost->file_index]->ctx;
 +            int j;
 +
 +            if (avf->nb_chapters > INT_MAX - size ||
 +                !(pts = av_realloc_f(pts, size += avf->nb_chapters - 1,
 +                                     sizeof(*pts)))) {
 +                av_log(NULL, AV_LOG_FATAL,
 +                       "Could not allocate forced key frames array.\n");
 +                exit_program(1);
 +            }
 +            t = p[8] ? parse_time_or_die("force_key_frames", p + 8, 1) : 0;
 +            t = av_rescale_q(t, AV_TIME_BASE_Q, avctx->time_base);
 +
 +            for (j = 0; j < avf->nb_chapters; j++) {
 +                AVChapter *c = avf->chapters[j];
 +                av_assert1(index < size);
 +                pts[index++] = av_rescale_q(c->start, c->time_base,
 +                                            avctx->time_base) + t;
 +            }
 +
 +        } else {
 +
 +            t = parse_time_or_die("force_key_frames", p, 1);
 +            av_assert1(index < size);
 +            pts[index++] = av_rescale_q(t, AV_TIME_BASE_Q, avctx->time_base);
 +
 +        }
 +
 +        p = next;
 +    }
 +
 +    av_assert0(index == size);
 +    qsort(pts, size, sizeof(*pts), compare_int64);
 +    ost->forced_kf_count = size;
 +    ost->forced_kf_pts   = pts;
 +}
 +
 +static void report_new_stream(int input_index, AVPacket *pkt)
 +{
 +    InputFile *file = input_files[input_index];
 +    AVStream *st = file->ctx->streams[pkt->stream_index];
 +
 +    if (pkt->stream_index < file->nb_streams_warn)
 +        return;
 +    av_log(file->ctx, AV_LOG_WARNING,
 +           "New %s stream %d:%d at pos:%"PRId64" and DTS:%ss\n",
 +           av_get_media_type_string(st->codecpar->codec_type),
 +           input_index, pkt->stream_index,
 +           pkt->pos, av_ts2timestr(pkt->dts, &st->time_base));
 +    file->nb_streams_warn = pkt->stream_index + 1;
 +}
 +
 +static void set_encoder_id(OutputFile *of, OutputStream *ost)
 +{
 +    AVDictionaryEntry *e;
 +
 +    uint8_t *encoder_string;
 +    int encoder_string_len;
 +    int format_flags = 0;
 +    int codec_flags = 0;
 +
 +    if (av_dict_get(ost->st->metadata, "encoder",  NULL, 0))
 +        return;
 +
 +    e = av_dict_get(of->opts, "fflags", NULL, 0);
 +    if (e) {
 +        const AVOption *o = av_opt_find(of->ctx, "fflags", NULL, 0, 0);
 +        if (!o)
 +            return;
 +        av_opt_eval_flags(of->ctx, o, e->value, &format_flags);
 +    }
 +    e = av_dict_get(ost->encoder_opts, "flags", NULL, 0);
 +    if (e) {
 +        const AVOption *o = av_opt_find(ost->enc_ctx, "flags", NULL, 0, 0);
 +        if (!o)
 +            return;
 +        av_opt_eval_flags(ost->enc_ctx, o, e->value, &codec_flags);
 +    }
 +
 +    encoder_string_len = sizeof(LIBAVCODEC_IDENT) + strlen(ost->enc->name) + 2;
 +    encoder_string     = av_mallocz(encoder_string_len);
 +    if (!encoder_string)
 +        exit_program(1);
 +
 +    if (!(format_flags & AVFMT_FLAG_BITEXACT) && !(codec_flags & AV_CODEC_FLAG_BITEXACT))
 +        av_strlcpy(encoder_string, LIBAVCODEC_IDENT " ", encoder_string_len);
 +    else
 +        av_strlcpy(encoder_string, "Lavc ", encoder_string_len);
 +    av_strlcat(encoder_string, ost->enc->name, encoder_string_len);
 +    av_dict_set(&ost->st->metadata, "encoder",  encoder_string,
 +                AV_DICT_DONT_STRDUP_VAL | AV_DICT_DONT_OVERWRITE);
 +}
 +
 +static int transcode_init(void)
 +{
 +    int ret = 0, i, j, k;
 +    AVFormatContext *oc;
 +    OutputStream *ost;
 +    InputStream *ist;
 +    char error[1024] = {0};
 +    int want_sdp = 1;
 +
 +    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->ost || ofilter->ost->source_index >= 0)
 +                continue;
 +            if (fg->nb_inputs != 1)
 +                continue;
 +            for (k = nb_input_streams-1; k >= 0 ; k--)
 +                if (fg->inputs[0]->ist == input_streams[k])
 +                    break;
 +            ofilter->ost->source_index = k;
 +        }
 +    }
 +
 +    /* init framerate emulation */
 +    for (i = 0; i < nb_input_files; i++) {
 +        InputFile *ifile = input_files[i];
 +        if (ifile->rate_emu)
 +            for (j = 0; j < ifile->nb_streams; j++)
 +                input_streams[j + ifile->ist_index]->start = av_gettime_relative();
 +    }
 +
 +    /* for each output stream, we compute the right encoding parameters */
 +    for (i = 0; i < nb_output_streams; i++) {
 +        ost = output_streams[i];
 +        oc  = output_files[ost->file_index]->ctx;
 +        ist = get_input_stream(ost);
 +
 +        if (ost->attachment_filename)
 +            continue;
 +
 +        if (ist) {
 +            ost->st->disposition          = ist->st->disposition;
 +        } else {
 +            for (j=0; j<oc->nb_streams; j++) {
 +                AVStream *st = oc->streams[j];
 +                if (st != ost->st && st->codecpar->codec_type == ost->st->codecpar->codec_type)
 +                    break;
 +            }
 +            if (j == oc->nb_streams)
 +                if (ost->st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO ||
 +                    ost->st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
 +                    ost->st->disposition = AV_DISPOSITION_DEFAULT;
 +        }
 +
 +        if (!ost->stream_copy) {
 +            AVCodecContext *enc_ctx = ost->enc_ctx;
 +            AVCodecContext *dec_ctx = NULL;
 +
 +            set_encoder_id(output_files[ost->file_index], ost);
 +
 +            if (ist) {
 +                dec_ctx = ist->dec_ctx;
 +
 +                enc_ctx->chroma_sample_location = dec_ctx->chroma_sample_location;
 +            }
 +
 +#if CONFIG_LIBMFX
 +            if (qsv_transcode_init(ost))
 +                exit_program(1);
 +#endif
 +
 +#if CONFIG_CUVID
 +            if (cuvid_transcode_init(ost))
 +                exit_program(1);
 +#endif
 +
 +            if ((enc_ctx->codec_type == AVMEDIA_TYPE_VIDEO ||
 +                 enc_ctx->codec_type == AVMEDIA_TYPE_AUDIO) &&
 +                 filtergraph_is_simple(ost->filter->graph)) {
 +                    FilterGraph *fg = ost->filter->graph;
 +                    if (configure_filtergraph(fg)) {
 +                        av_log(NULL, AV_LOG_FATAL, "Error opening filters!\n");
 +                        exit_program(1);
 +                    }
 +            }
 +
 +            if (enc_ctx->codec_type == AVMEDIA_TYPE_VIDEO) {
 +                if (!ost->frame_rate.num)
 +                    ost->frame_rate = av_buffersink_get_frame_rate(ost->filter->filter);
 +                if (ist && !ost->frame_rate.num)
 +                    ost->frame_rate = ist->framerate;
 +                if (ist && !ost->frame_rate.num)
 +                    ost->frame_rate = ist->st->r_frame_rate;
 +                if (ist && !ost->frame_rate.num) {
 +                    ost->frame_rate = (AVRational){25, 1};
 +                    av_log(NULL, AV_LOG_WARNING,
 +                           "No information "
 +                           "about the input framerate is available. Falling "
 +                           "back to a default value of 25fps for output stream #%d:%d. Use the -r option "
 +                           "if you want a different framerate.\n",
 +                           ost->file_index, ost->index);
 +                }
 +//                    ost->frame_rate = ist->st->avg_frame_rate.num ? ist->st->avg_frame_rate : (AVRational){25, 1};
 +                if (ost->enc && ost->enc->supported_framerates && !ost->force_fps) {
 +                    int idx = av_find_nearest_q_idx(ost->frame_rate, ost->enc->supported_framerates);
 +                    ost->frame_rate = ost->enc->supported_framerates[idx];
 +                }
 +                // reduce frame rate for mpeg4 to be within the spec limits
 +                if (enc_ctx->codec_id == AV_CODEC_ID_MPEG4) {
 +                    av_reduce(&ost->frame_rate.num, &ost->frame_rate.den,
 +                              ost->frame_rate.num, ost->frame_rate.den, 65535);
 +                }
 +            }
 +
 +            switch (enc_ctx->codec_type) {
 +            case AVMEDIA_TYPE_AUDIO:
 +                enc_ctx->sample_fmt     = ost->filter->filter->inputs[0]->format;
 +                if (dec_ctx)
 +                    enc_ctx->bits_per_raw_sample = FFMIN(dec_ctx->bits_per_raw_sample,
 +                                                         av_get_bytes_per_sample(enc_ctx->sample_fmt) << 3);
 +                enc_ctx->sample_rate    = ost->filter->filter->inputs[0]->sample_rate;
 +                enc_ctx->channel_layout = ost->filter->filter->inputs[0]->channel_layout;
 +                enc_ctx->channels       = avfilter_link_get_channels(ost->filter->filter->inputs[0]);
 +                enc_ctx->time_base      = (AVRational){ 1, enc_ctx->sample_rate };
 +                break;
 +            case AVMEDIA_TYPE_VIDEO:
 +                enc_ctx->time_base = av_inv_q(ost->frame_rate);
 +                if (!(enc_ctx->time_base.num && enc_ctx->time_base.den))
 +                    enc_ctx->time_base = ost->filter->filter->inputs[0]->time_base;
 +                if (   av_q2d(enc_ctx->time_base) < 0.001 && video_sync_method != VSYNC_PASSTHROUGH
 +                   && (video_sync_method == VSYNC_CFR || video_sync_method == VSYNC_VSCFR || (video_sync_method == VSYNC_AUTO && !(oc->oformat->flags & AVFMT_VARIABLE_FPS)))){
 +                    av_log(oc, AV_LOG_WARNING, "Frame rate very high for a muxer not efficiently supporting it.\n"
 +                                               "Please consider specifying a lower framerate, a different muxer or -vsync 2\n");
 +                }
 +                for (j = 0; j < ost->forced_kf_count; j++)
 +                    ost->forced_kf_pts[j] = av_rescale_q(ost->forced_kf_pts[j],
 +                                                         AV_TIME_BASE_Q,
 +                                                         enc_ctx->time_base);
 +
 +                enc_ctx->width  = ost->filter->filter->inputs[0]->w;
 +                enc_ctx->height = ost->filter->filter->inputs[0]->h;
 +                enc_ctx->sample_aspect_ratio = ost->st->sample_aspect_ratio =
 +                    ost->frame_aspect_ratio.num ? // overridden by the -aspect cli option
 +                    av_mul_q(ost->frame_aspect_ratio, (AVRational){ enc_ctx->height, enc_ctx->width }) :
 +                    ost->filter->filter->inputs[0]->sample_aspect_ratio;
 +                if (!strncmp(ost->enc->name, "libx264", 7) &&
 +                    enc_ctx->pix_fmt == AV_PIX_FMT_NONE &&
 +                    ost->filter->filter->inputs[0]->format != AV_PIX_FMT_YUV420P)
 +                    av_log(NULL, AV_LOG_WARNING,
 +                           "No pixel format specified, %s for H.264 encoding chosen.\n"
 +                           "Use -pix_fmt yuv420p for compatibility with outdated media players.\n",
 +                           av_get_pix_fmt_name(ost->filter->filter->inputs[0]->format));
 +                if (!strncmp(ost->enc->name, "mpeg2video", 10) &&
 +                    enc_ctx->pix_fmt == AV_PIX_FMT_NONE &&
 +                    ost->filter->filter->inputs[0]->format != AV_PIX_FMT_YUV420P)
 +                    av_log(NULL, AV_LOG_WARNING,
 +                           "No pixel format specified, %s for MPEG-2 encoding chosen.\n"
 +                           "Use -pix_fmt yuv420p for compatibility with outdated media players.\n",
 +                           av_get_pix_fmt_name(ost->filter->filter->inputs[0]->format));
 +                enc_ctx->pix_fmt = ost->filter->filter->inputs[0]->format;
 +                if (dec_ctx)
 +                    enc_ctx->bits_per_raw_sample = FFMIN(dec_ctx->bits_per_raw_sample,
 +                                                         av_pix_fmt_desc_get(enc_ctx->pix_fmt)->comp[0].depth);
 +
 +                ost->st->avg_frame_rate = ost->frame_rate;
 +
 +                if (!dec_ctx ||
 +                    enc_ctx->width   != dec_ctx->width  ||
 +                    enc_ctx->height  != dec_ctx->height ||
 +                    enc_ctx->pix_fmt != dec_ctx->pix_fmt) {
 +                    enc_ctx->bits_per_raw_sample = frame_bits_per_raw_sample;
 +                }
 +
 +                if (ost->forced_keyframes) {
 +                    if (!strncmp(ost->forced_keyframes, "expr:", 5)) {
 +                        ret = av_expr_parse(&ost->forced_keyframes_pexpr, ost->forced_keyframes+5,
 +                                            forced_keyframes_const_names, NULL, NULL, NULL, NULL, 0, NULL);
 +                        if (ret < 0) {
 +                            av_log(NULL, AV_LOG_ERROR,
 +                                   "Invalid force_key_frames expression '%s'\n", ost->forced_keyframes+5);
 +                            return ret;
 +                        }
 +                        ost->forced_keyframes_expr_const_values[FKF_N] = 0;
 +                        ost->forced_keyframes_expr_const_values[FKF_N_FORCED] = 0;
 +                        ost->forced_keyframes_expr_const_values[FKF_PREV_FORCED_N] = NAN;
 +                        ost->forced_keyframes_expr_const_values[FKF_PREV_FORCED_T] = NAN;
 +
 +                        // Don't parse the 'forced_keyframes' in case of 'keep-source-keyframes',
 +                        // parse it only for static kf timings
 +                    } else if(strncmp(ost->forced_keyframes, "source", 6)) {
 +                        parse_forced_key_frames(ost->forced_keyframes, ost, ost->enc_ctx);
 +                    }
 +                }
 +                break;
 +            case AVMEDIA_TYPE_SUBTITLE:
 +                enc_ctx->time_base = (AVRational){1, 1000};
 +                if (!enc_ctx->width) {
 +                    enc_ctx->width     = input_streams[ost->source_index]->st->codecpar->width;
 +                    enc_ctx->height    = input_streams[ost->source_index]->st->codecpar->height;
 +                }
 +                break;
 +            case AVMEDIA_TYPE_DATA:
 +                break;
 +            default:
 +                abort();
 +                break;
 +            }
 +        }
 +
 +        if (ost->disposition) {
 +            static const AVOption opts[] = {
 +                { "disposition"         , NULL, 0, AV_OPT_TYPE_FLAGS, { .i64 = 0 }, INT64_MIN, INT64_MAX, .unit = "flags" },
 +                { "default"             , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_DEFAULT           },    .unit = "flags" },
 +                { "dub"                 , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_DUB               },    .unit = "flags" },
 +                { "original"            , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_ORIGINAL          },    .unit = "flags" },
 +                { "comment"             , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_COMMENT           },    .unit = "flags" },
 +                { "lyrics"              , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_LYRICS            },    .unit = "flags" },
 +                { "karaoke"             , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_KARAOKE           },    .unit = "flags" },
 +                { "forced"              , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_FORCED            },    .unit = "flags" },
 +                { "hearing_impaired"    , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_HEARING_IMPAIRED  },    .unit = "flags" },
 +                { "visual_impaired"     , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_VISUAL_IMPAIRED   },    .unit = "flags" },
 +                { "clean_effects"       , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_CLEAN_EFFECTS     },    .unit = "flags" },
 +                { "captions"            , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_CAPTIONS          },    .unit = "flags" },
 +                { "descriptions"        , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_DESCRIPTIONS      },    .unit = "flags" },
 +                { "metadata"            , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_METADATA          },    .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;
 +
 +            ret = av_opt_eval_flags(&pclass, &opts[0], ost->disposition, &ost->st->disposition);
 +            if (ret < 0)
 +                goto dump_format;
 +        }
 +    }
 +
 +    /* init input streams */
 +    for (i = 0; i < nb_input_streams; i++)
 +        if ((ret = init_input_stream(i, error, sizeof(error))) < 0) {
 +            for (i = 0; i < nb_output_streams; i++) {
 +                ost = output_streams[i];
 +                avcodec_close(ost->enc_ctx);
 +            }
 +            goto dump_format;
 +        }
 +
 +    /* open each encoder */
 +    for (i = 0; i < nb_output_streams; i++) {
 +        ret = init_output_stream(output_streams[i], error, sizeof(error));
 +        if (ret < 0)
 +            goto dump_format;
 +    }
 +
 +    /* discard unused programs */
 +    for (i = 0; i < nb_input_files; i++) {
 +        InputFile *ifile = input_files[i];
 +        for (j = 0; j < ifile->ctx->nb_programs; j++) {
 +            AVProgram *p = ifile->ctx->programs[j];
 +            int discard  = AVDISCARD_ALL;
 +
 +            for (k = 0; k < p->nb_stream_indexes; k++)
 +                if (!input_streams[ifile->ist_index + p->stream_index[k]]->discard) {
 +                    discard = AVDISCARD_DEFAULT;
 +                    break;
 +                }
 +            p->discard = discard;
 +        }
 +    }
 +
 +    /* open files and write file headers */
 +    for (i = 0; i < nb_output_files; i++) {
 +        oc = output_files[i]->ctx;
 +        oc->interrupt_callback = int_cb;
 +        if ((ret = avformat_write_header(oc, &output_files[i]->opts)) < 0) {
 +            snprintf(error, sizeof(error),
 +                     "Could not write header for output file #%d "
 +                     "(incorrect codec parameters ?): %s",
 +                     i, av_err2str(ret));
 +            ret = AVERROR(EINVAL);
 +            goto dump_format;
 +        }
 +//         assert_avoptions(output_files[i]->opts);
 +        if (strcmp(oc->oformat->name, "rtp")) {
 +            want_sdp = 0;
 +        }
 +    }
 +
 + dump_format:
 +    /* dump the file output parameters - cannot be done before in case
 +       of stream copy */
 +    for (i = 0; i < nb_output_files; i++) {
 +        av_dump_format(output_files[i]->ctx, i, output_files[i]->ctx->filename, 1);
 +    }
 +
 +    /* dump the stream mapping */
 +    av_log(NULL, AV_LOG_INFO, "Stream mapping:\n");
 +    for (i = 0; i < nb_input_streams; i++) {
 +        ist = input_streams[i];
 +
 +        for (j = 0; j < ist->nb_filters; j++) {
 +            if (!filtergraph_is_simple(ist->filters[j]->graph)) {
 +                av_log(NULL, AV_LOG_INFO, "  Stream #%d:%d (%s) -> %s",
 +                       ist->file_index, ist->st->index, ist->dec ? ist->dec->name : "?",
 +                       ist->filters[j]->name);
 +                if (nb_filtergraphs > 1)
 +                    av_log(NULL, AV_LOG_INFO, " (graph %d)", ist->filters[j]->graph->index);
 +                av_log(NULL, AV_LOG_INFO, "\n");
 +            }
 +        }
 +    }
 +
 +    for (i = 0; i < nb_output_streams; i++) {
 +        ost = output_streams[i];
 +
 +        if (ost->attachment_filename) {
 +            /* an attached file */
 +            av_log(NULL, AV_LOG_INFO, "  File %s -> Stream #%d:%d\n",
 +                   ost->attachment_filename, ost->file_index, ost->index);
 +            continue;
 +        }
 +
 +        if (ost->filter && !filtergraph_is_simple(ost->filter->graph)) {
 +            /* output from a complex graph */
 +            av_log(NULL, AV_LOG_INFO, "  %s", ost->filter->name);
 +            if (nb_filtergraphs > 1)
 +                av_log(NULL, AV_LOG_INFO, " (graph %d)", ost->filter->graph->index);
 +
 +            av_log(NULL, AV_LOG_INFO, " -> Stream #%d:%d (%s)\n", ost->file_index,
 +                   ost->index, ost->enc ? ost->enc->name : "?");
 +            continue;
 +        }
 +
 +        av_log(NULL, AV_LOG_INFO, "  Stream #%d:%d -> #%d:%d",
 +               input_streams[ost->source_index]->file_index,
 +               input_streams[ost->source_index]->st->index,
 +               ost->file_index,
 +               ost->index);
 +        if (ost->sync_ist != input_streams[ost->source_index])
 +            av_log(NULL, AV_LOG_INFO, " [sync #%d:%d]",
 +                   ost->sync_ist->file_index,
 +                   ost->sync_ist->st->index);
 +        if (ost->stream_copy)
 +            av_log(NULL, AV_LOG_INFO, " (copy)");
 +        else {
 +            const AVCodec *in_codec    = input_streams[ost->source_index]->dec;
 +            const AVCodec *out_codec   = ost->enc;
 +            const char *decoder_name   = "?";
 +            const char *in_codec_name  = "?";
 +            const char *encoder_name   = "?";
 +            const char *out_codec_name = "?";
 +            const AVCodecDescriptor *desc;
 +
 +            if (in_codec) {
 +                decoder_name  = in_codec->name;
 +                desc = avcodec_descriptor_get(in_codec->id);
 +                if (desc)
 +                    in_codec_name = desc->name;
 +                if (!strcmp(decoder_name, in_codec_name))
 +                    decoder_name = "native";
 +            }
 +
 +            if (out_codec) {
 +                encoder_name   = out_codec->name;
 +                desc = avcodec_descriptor_get(out_codec->id);
 +                if (desc)
 +                    out_codec_name = desc->name;
 +                if (!strcmp(encoder_name, out_codec_name))
 +                    encoder_name = "native";
 +            }
 +
 +            av_log(NULL, AV_LOG_INFO, " (%s (%s) -> %s (%s))",
 +                   in_codec_name, decoder_name,
 +                   out_codec_name, encoder_name);
 +        }
 +        av_log(NULL, AV_LOG_INFO, "\n");
 +    }
 +
 +    if (ret) {
 +        av_log(NULL, AV_LOG_ERROR, "%s\n", error);
 +        return ret;
 +    }
 +
 +    if (sdp_filename || want_sdp) {
 +        print_sdp();
 +    }
 +
 +    transcode_init_done = 1;
 +
 +    return 0;
 +}
 +
 +/* Return 1 if there remain streams where more output is wanted, 0 otherwise. */
 +static int need_output(void)
 +{
 +    int i;
 +
 +    for (i = 0; i < nb_output_streams; i++) {
 +        OutputStream *ost    = output_streams[i];
 +        OutputFile *of       = output_files[ost->file_index];
 +        AVFormatContext *os  = output_files[ost->file_index]->ctx;
 +
 +        if (ost->finished ||
 +            (os->pb && avio_tell(os->pb) >= of->limit_filesize))
 +            continue;
 +        if (ost->frame_number >= ost->max_frames) {
 +            int j;
 +            for (j = 0; j < of->ctx->nb_streams; j++)
 +                close_output_stream(output_streams[of->ost_index + j]);
 +            continue;
 +        }
 +
 +        return 1;
 +    }
 +
 +    return 0;
 +}
 +
 +/**
 + * Select the output stream to process.
 + *
 + * @return  selected output stream, or NULL if none available
 + */
 +static OutputStream *choose_output(void)
 +{
 +    int i;
 +    int64_t opts_min = INT64_MAX;
 +    OutputStream *ost_min = NULL;
 +
 +    for (i = 0; i < nb_output_streams; i++) {
 +        OutputStream *ost = output_streams[i];
 +        int64_t opts = ost->st->cur_dts == AV_NOPTS_VALUE ? INT64_MIN :
 +                       av_rescale_q(ost->st->cur_dts, ost->st->time_base,
 +                                    AV_TIME_BASE_Q);
 +        if (ost->st->cur_dts == AV_NOPTS_VALUE)
 +            av_log(NULL, AV_LOG_DEBUG, "cur_dts is invalid (this is harmless if it occurs once at the start per stream)\n");
 +
 +        if (!ost->finished && opts < opts_min) {
 +            opts_min = opts;
 +            ost_min  = ost->unavailable ? NULL : ost;
 +        }
 +    }
 +    return ost_min;
 +}
 +
 +static void set_tty_echo(int on)
 +{
 +#if HAVE_TERMIOS_H
 +    struct termios tty;
 +    if (tcgetattr(0, &tty) == 0) {
 +        if (on) tty.c_lflag |= ECHO;
 +        else    tty.c_lflag &= ~ECHO;
 +        tcsetattr(0, TCSANOW, &tty);
 +    }
 +#endif
 +}
 +
 +static int check_keyboard_interaction(int64_t cur_time)
 +{
 +    int i, ret, key;
 +    static int64_t last_time;
 +    if (received_nb_signals)
 +        return AVERROR_EXIT;
 +    /* read_key() returns 0 on EOF */
 +    if(cur_time - last_time >= 100000 && !run_as_daemon){
 +        key =  read_key();
 +        last_time = cur_time;
 +    }else
 +        key = -1;
 +    if (key == 'q')
 +        return AVERROR_EXIT;
 +    if (key == '+') av_log_set_level(av_log_get_level()+10);
 +    if (key == '-') av_log_set_level(av_log_get_level()-10);
 +    if (key == 's') qp_hist     ^= 1;
 +    if (key == 'h'){
 +        if (do_hex_dump){
 +            do_hex_dump = do_pkt_dump = 0;
 +        } else if(do_pkt_dump){
 +            do_hex_dump = 1;
 +        } else
 +            do_pkt_dump = 1;
 +        av_log_set_level(AV_LOG_DEBUG);
 +    }
 +    if (key == 'c' || key == 'C'){
 +        char buf[4096], target[64], command[256], arg[256] = {0};
 +        double time;
 +        int k, n = 0;
 +        fprintf(stderr, "\nEnter command: <target>|all <time>|-1 <command>[ <argument>]\n");
 +        i = 0;
 +        set_tty_echo(1);
 +        while ((k = read_key()) != '\n' && k != '\r' && i < sizeof(buf)-1)
 +            if (k > 0)
 +                buf[i++] = k;
 +        buf[i] = 0;
 +        set_tty_echo(0);
 +        fprintf(stderr, "\n");
 +        if (k > 0 &&
 +            (n = sscanf(buf, "%63[^ ] %lf %255[^ ] %255[^\n]", target, &time, command, arg)) >= 3) {
 +            av_log(NULL, AV_LOG_DEBUG, "Processing command target:%s time:%f command:%s arg:%s",
 +                   target, time, command, arg);
 +            for (i = 0; i < nb_filtergraphs; i++) {
 +                FilterGraph *fg = filtergraphs[i];
 +                if (fg->graph) {
 +                    if (time < 0) {
 +                        ret = avfilter_graph_send_command(fg->graph, target, command, arg, buf, sizeof(buf),
 +                                                          key == 'c' ? AVFILTER_CMD_FLAG_ONE : 0);
 +                        fprintf(stderr, "Command reply for stream %d: ret:%d res:\n%s", i, ret, buf);
 +                    } else if (key == 'c') {
 +                        fprintf(stderr, "Queuing commands only on filters supporting the specific command is unsupported\n");
 +                        ret = AVERROR_PATCHWELCOME;
 +                    } else {
 +                        ret = avfilter_graph_queue_command(fg->graph, target, command, arg, 0, time);
 +                        if (ret < 0)
 +                            fprintf(stderr, "Queuing command failed with error %s\n", av_err2str(ret));
 +                    }
 +                }
 +            }
 +        } else {
 +            av_log(NULL, AV_LOG_ERROR,
 +                   "Parse error, at least 3 arguments were expected, "
 +                   "only %d given in string '%s'\n", n, buf);
 +        }
 +    }
 +    if (key == 'd' || key == 'D'){
 +        int debug=0;
 +        if(key == 'D') {
 +            debug = input_streams[0]->st->codec->debug<<1;
 +            if(!debug) debug = 1;
 +            while(debug & (FF_DEBUG_DCT_COEFF|FF_DEBUG_VIS_QP|FF_DEBUG_VIS_MB_TYPE)) //unsupported, would just crash
 +                debug += debug;
 +        }else{
 +            char buf[32];
 +            int k = 0;
 +            i = 0;
 +            set_tty_echo(1);
 +            while ((k = read_key()) != '\n' && k != '\r' && i < sizeof(buf)-1)
 +                if (k > 0)
 +                    buf[i++] = k;
 +            buf[i] = 0;
 +            set_tty_echo(0);
 +            fprintf(stderr, "\n");
 +            if (k <= 0 || sscanf(buf, "%d", &debug)!=1)
 +                fprintf(stderr,"error parsing debug value\n");
 +        }
 +        for(i=0;i<nb_input_streams;i++) {
 +            input_streams[i]->st->codec->debug = debug;
 +        }
 +        for(i=0;i<nb_output_streams;i++) {
 +            OutputStream *ost = output_streams[i];
 +            ost->enc_ctx->debug = debug;
 +        }
 +        if(debug) av_log_set_level(AV_LOG_DEBUG);
 +        fprintf(stderr,"debug=%d\n", debug);
 +    }
 +    if (key == '?'){
 +        fprintf(stderr, "key    function\n"
 +                        "?      show this help\n"
 +                        "+      increase verbosity\n"
 +                        "-      decrease verbosity\n"
 +                        "c      Send command to first matching filter supporting it\n"
 +                        "C      Send/Que command to all matching filters\n"
 +                        "D      cycle through available debug modes\n"
 +                        "h      dump packets/hex press to cycle through the 3 states\n"
 +                        "q      quit\n"
 +                        "s      Show QP histogram\n"
 +        );
 +    }
 +    return 0;
 +}
 +
 +#if HAVE_PTHREADS
 +static void *input_thread(void *arg)
 +{
 +    InputFile *f = arg;
 +    unsigned flags = f->non_blocking ? AV_THREAD_MESSAGE_NONBLOCK : 0;
 +    int ret = 0;
 +
 +    while (1) {
 +        AVPacket pkt;
 +        ret = av_read_frame(f->ctx, &pkt);
 +
 +        if (ret == AVERROR(EAGAIN)) {
 +            av_usleep(10000);
 +            continue;
 +        }
 +        if (ret < 0) {
 +            av_thread_message_queue_set_err_recv(f->in_thread_queue, ret);
 +            break;
 +        }
 +        ret = av_thread_message_queue_send(f->in_thread_queue, &pkt, flags);
 +        if (flags && ret == AVERROR(EAGAIN)) {
 +            flags = 0;
 +            ret = av_thread_message_queue_send(f->in_thread_queue, &pkt, flags);
 +            av_log(f->ctx, AV_LOG_WARNING,
 +                   "Thread message queue blocking; consider raising the "
 +                   "thread_queue_size option (current value: %d)\n",
 +                   f->thread_queue_size);
 +        }
 +        if (ret < 0) {
 +            if (ret != AVERROR_EOF)
 +                av_log(f->ctx, AV_LOG_ERROR,
 +                       "Unable to send packet to main thread: %s\n",
 +                       av_err2str(ret));
 +            av_packet_unref(&pkt);
 +            av_thread_message_queue_set_err_recv(f->in_thread_queue, ret);
 +            break;
 +        }
 +    }
 +
 +    return NULL;
 +}
 +
 +static void free_input_threads(void)
 +{
 +    int i;
 +
 +    for (i = 0; i < nb_input_files; i++) {
 +        InputFile *f = input_files[i];
 +        AVPacket pkt;
 +
 +        if (!f || !f->in_thread_queue)
 +            continue;
 +        av_thread_message_queue_set_err_send(f->in_thread_queue, AVERROR_EOF);
 +        while (av_thread_message_queue_recv(f->in_thread_queue, &pkt, 0) >= 0)
 +            av_packet_unref(&pkt);
 +
 +        pthread_join(f->thread, NULL);
 +        f->joined = 1;
 +        av_thread_message_queue_free(&f->in_thread_queue);
 +    }
 +}
 +
 +static int init_input_threads(void)
 +{
 +    int i, ret;
 +
 +    if (nb_input_files == 1)
 +        return 0;
 +
 +    for (i = 0; i < nb_input_files; i++) {
 +        InputFile *f = input_files[i];
 +
 +        if (f->ctx->pb ? !f->ctx->pb->seekable :
 +            strcmp(f->ctx->iformat->name, "lavfi"))
 +            f->non_blocking = 1;
 +        ret = av_thread_message_queue_alloc(&f->in_thread_queue,
 +                                            f->thread_queue_size, sizeof(AVPacket));
 +        if (ret < 0)
 +            return ret;
 +
 +        if ((ret = pthread_create(&f->thread, NULL, input_thread, f))) {
 +            av_log(NULL, AV_LOG_ERROR, "pthread_create failed: %s. Try to increase `ulimit -v` or decrease `ulimit -s`.\n", strerror(ret));
 +            av_thread_message_queue_free(&f->in_thread_queue);
 +            return AVERROR(ret);
 +        }
 +    }
 +    return 0;
 +}
 +
 +static int get_input_packet_mt(InputFile *f, AVPacket *pkt)
 +{
 +    return av_thread_message_queue_recv(f->in_thread_queue, pkt,
 +                                        f->non_blocking ?
 +                                        AV_THREAD_MESSAGE_NONBLOCK : 0);
 +}
 +#endif
 +
 +static int get_input_packet(InputFile *f, AVPacket *pkt)
 +{
 +    if (f->rate_emu) {
 +        int i;
 +        for (i = 0; i < f->nb_streams; i++) {
 +            InputStream *ist = input_streams[f->ist_index + i];
 +            int64_t pts = av_rescale(ist->dts, 1000000, AV_TIME_BASE);
 +            int64_t now = av_gettime_relative() - ist->start;
 +            if (pts > now)
 +                return AVERROR(EAGAIN);
 +        }
 +    }
 +
 +#if HAVE_PTHREADS
 +    if (nb_input_files > 1)
 +        return get_input_packet_mt(f, pkt);
 +#endif
 +    return av_read_frame(f->ctx, pkt);
 +}
 +
 +static int got_eagain(void)
 +{
 +    int i;
 +    for (i = 0; i < nb_output_streams; i++)
 +        if (output_streams[i]->unavailable)
 +            return 1;
 +    return 0;
 +}
 +
 +static void reset_eagain(void)
 +{
 +    int i;
 +    for (i = 0; i < nb_input_files; i++)
 +        input_files[i]->eagain = 0;
 +    for (i = 0; i < nb_output_streams; i++)
 +        output_streams[i]->unavailable = 0;
 +}
 +
 +// set duration to max(tmp, duration) in a proper time base and return duration's time_base
 +static AVRational duration_max(int64_t tmp, int64_t *duration, AVRational tmp_time_base,
 +                                AVRational time_base)
 +{
 +    int ret;
 +
 +    if (!*duration) {
 +        *duration = tmp;
 +        return tmp_time_base;
 +    }
 +
 +    ret = av_compare_ts(*duration, time_base, tmp, tmp_time_base);
 +    if (ret < 0) {
 +        *duration = tmp;
 +        return tmp_time_base;
 +    }
 +
 +    return time_base;
 +}
 +
 +static int seek_to_start(InputFile *ifile, AVFormatContext *is)
 +{
 +    InputStream *ist;
 +    AVCodecContext *avctx;
 +    int i, ret, has_audio = 0;
 +    int64_t duration = 0;
 +
 +    ret = av_seek_frame(is, -1, is->start_time, 0);
 +    if (ret < 0)
 +        return ret;
 +
 +    for (i = 0; i < ifile->nb_streams; i++) {
 +        ist   = input_streams[ifile->ist_index + i];
 +        avctx = ist->dec_ctx;
 +
 +        // flush decoders
 +        if (ist->decoding_needed) {
 +            process_input_packet(ist, NULL, 1);
 +            avcodec_flush_buffers(avctx);
 +        }
 +
 +        /* duration is the length of the last frame in a stream
 +         * when audio stream is present we don't care about
 +         * last video frame length because it's not defined exactly */
 +        if (avctx->codec_type == AVMEDIA_TYPE_AUDIO && ist->nb_samples)
 +            has_audio = 1;
 +    }
 +
 +    for (i = 0; i < ifile->nb_streams; i++) {
 +        ist   = input_streams[ifile->ist_index + i];
 +        avctx = ist->dec_ctx;
 +
 +        if (has_audio) {
 +            if (avctx->codec_type == AVMEDIA_TYPE_AUDIO && ist->nb_samples) {
 +                AVRational sample_rate = {1, avctx->sample_rate};
 +
 +                duration = av_rescale_q(ist->nb_samples, sample_rate, ist->st->time_base);
 +            } else
 +                continue;
 +        } else {
 +            if (ist->framerate.num) {
 +                duration = av_rescale_q(1, ist->framerate, ist->st->time_base);
 +            } else if (ist->st->avg_frame_rate.num) {
 +                duration = av_rescale_q(1, ist->st->avg_frame_rate, ist->st->time_base);
 +            } else duration = 1;
 +        }
 +        if (!ifile->duration)
 +            ifile->time_base = ist->st->time_base;
 +        /* the total duration of the stream, max_pts - min_pts is
 +         * the duration of the stream without the last frame */
 +        duration += ist->max_pts - ist->min_pts;
 +        ifile->time_base = duration_max(duration, &ifile->duration, ist->st->time_base,
 +                                        ifile->time_base);
 +    }
 +
 +    if (ifile->loop > 0)
 +        ifile->loop--;
 +
 +    return ret;
 +}
 +
 +/*
 + * Return
 + * - 0 -- one packet was read and processed
 + * - AVERROR(EAGAIN) -- no packets were available for selected file,
 + *   this function should be called again
 + * - AVERROR_EOF -- this function should not be called again
 + */
 +static int process_input(int file_index)
 +{
 +    InputFile *ifile = input_files[file_index];
 +    AVFormatContext *is;
 +    InputStream *ist;
 +    AVPacket pkt;
 +    int ret, i, j;
 +    int64_t duration;
 +    int64_t pkt_dts;
 +
 +    is  = ifile->ctx;
 +    ret = get_input_packet(ifile, &pkt);
 +
 +    if (ret == AVERROR(EAGAIN)) {
 +        ifile->eagain = 1;
 +        return ret;
 +    }
 +    if (ret < 0 && ifile->loop) {
 +        if ((ret = seek_to_start(ifile, is)) < 0)
 +            return ret;
 +        ret = get_input_packet(ifile, &pkt);
 +        if (ret == AVERROR(EAGAIN)) {
 +            ifile->eagain = 1;
 +            return ret;
 +        }
 +    }
 +    if (ret < 0) {
 +        if (ret != AVERROR_EOF) {
 +            print_error(is->filename, ret);
 +            if (exit_on_error)
 +                exit_program(1);
 +        }
 +
 +        for (i = 0; i < ifile->nb_streams; i++) {
 +            ist = input_streams[ifile->ist_index + i];
 +            if (ist->decoding_needed) {
 +                ret = process_input_packet(ist, NULL, 0);
 +                if (ret>0)
 +                    return 0;
 +            }
 +
 +            /* mark all outputs that don't go through lavfi as finished */
 +            for (j = 0; j < nb_output_streams; j++) {
 +                OutputStream *ost = output_streams[j];
 +
 +                if (ost->source_index == ifile->ist_index + i &&
 +                    (ost->stream_copy || ost->enc->type == AVMEDIA_TYPE_SUBTITLE))
 +                    finish_output_stream(ost);
 +            }
 +        }
 +
 +        ifile->eof_reached = 1;
 +        return AVERROR(EAGAIN);
 +    }
 +
 +    reset_eagain();
 +
 +    if (do_pkt_dump) {
 +        av_pkt_dump_log2(NULL, AV_LOG_INFO, &pkt, do_hex_dump,
 +                         is->streams[pkt.stream_index]);
 +    }
 +    /* the following test is needed in case new streams appear
 +       dynamically in stream : we ignore them */
 +    if (pkt.stream_index >= ifile->nb_streams) {
 +        report_new_stream(file_index, &pkt);
 +        goto discard_packet;
 +    }
 +
 +    ist = input_streams[ifile->ist_index + pkt.stream_index];
 +
 +    ist->data_size += pkt.size;
 +    ist->nb_packets++;
 +
 +    if (ist->discard)
 +        goto discard_packet;
 +
 +    if (exit_on_error && (pkt.flags & AV_PKT_FLAG_CORRUPT)) {
 +        av_log(NULL, AV_LOG_FATAL, "%s: corrupt input packet in stream %d\n", is->filename, pkt.stream_index);
 +        exit_program(1);
 +    }
 +
 +    if (debug_ts) {
 +        av_log(NULL, AV_LOG_INFO, "demuxer -> ist_index:%d type:%s "
 +               "next_dts:%s next_dts_time:%s next_pts:%s next_pts_time:%s pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s off:%s off_time:%s\n",
 +               ifile->ist_index + pkt.stream_index, av_get_media_type_string(ist->dec_ctx->codec_type),
 +               av_ts2str(ist->next_dts), av_ts2timestr(ist->next_dts, &AV_TIME_BASE_Q),
 +               av_ts2str(ist->next_pts), av_ts2timestr(ist->next_pts, &AV_TIME_BASE_Q),
 +               av_ts2str(pkt.pts), av_ts2timestr(pkt.pts, &ist->st->time_base),
 +               av_ts2str(pkt.dts), av_ts2timestr(pkt.dts, &ist->st->time_base),
 +               av_ts2str(input_files[ist->file_index]->ts_offset),
 +               av_ts2timestr(input_files[ist->file_index]->ts_offset, &AV_TIME_BASE_Q));
 +    }
 +
 +    if(!ist->wrap_correction_done && is->start_time != AV_NOPTS_VALUE && ist->st->pts_wrap_bits < 64){
 +        int64_t stime, stime2;
 +        // Correcting starttime based on the enabled streams
 +        // FIXME this ideally should be done before the first use of starttime but we do not know which are the enabled streams at that point.
 +        //       so we instead do it here as part of discontinuity handling
 +        if (   ist->next_dts == AV_NOPTS_VALUE
 +            && ifile->ts_offset == -is->start_time
 +            && (is->iformat->flags & AVFMT_TS_DISCONT)) {
 +            int64_t new_start_time = INT64_MAX;
 +            for (i=0; i<is->nb_streams; i++) {
 +                AVStream *st = is->streams[i];
 +                if(st->discard == AVDISCARD_ALL || st->start_time == AV_NOPTS_VALUE)
 +                    continue;
 +                new_start_time = FFMIN(new_start_time, av_rescale_q(st->start_time, st->time_base, AV_TIME_BASE_Q));
 +            }
 +            if (new_start_time > is->start_time) {
 +                av_log(is, AV_LOG_VERBOSE, "Correcting start time by %"PRId64"\n", new_start_time - is->start_time);
 +                ifile->ts_offset = -new_start_time;
 +            }
 +        }
 +
 +        stime = av_rescale_q(is->start_time, AV_TIME_BASE_Q, ist->st->time_base);
 +        stime2= stime + (1ULL<<ist->st->pts_wrap_bits);
 +        ist->wrap_correction_done = 1;
 +
 +        if(stime2 > stime && pkt.dts != AV_NOPTS_VALUE && pkt.dts > stime + (1LL<<(ist->st->pts_wrap_bits-1))) {
 +            pkt.dts -= 1ULL<<ist->st->pts_wrap_bits;
 +            ist->wrap_correction_done = 0;
 +        }
 +        if(stime2 > stime && pkt.pts != AV_NOPTS_VALUE && pkt.pts > stime + (1LL<<(ist->st->pts_wrap_bits-1))) {
 +            pkt.pts -= 1ULL<<ist->st->pts_wrap_bits;
 +            ist->wrap_correction_done = 0;
 +        }
 +    }
 +
 +    /* add the stream-global side data to the first packet */
 +    if (ist->nb_packets == 1) {
 +        if (ist->st->nb_side_data)
 +            av_packet_split_side_data(&pkt);
 +        for (i = 0; i < ist->st->nb_side_data; i++) {
 +            AVPacketSideData *src_sd = &ist->st->side_data[i];
 +            uint8_t *dst_data;
 +
 +            if (av_packet_get_side_data(&pkt, src_sd->type, NULL))
 +                continue;
 +            if (ist->autorotate && src_sd->type == AV_PKT_DATA_DISPLAYMATRIX)
 +                continue;
 +
 +            dst_data = av_packet_new_side_data(&pkt, src_sd->type, src_sd->size);
 +            if (!dst_data)
 +                exit_program(1);
 +
 +            memcpy(dst_data, src_sd->data, src_sd->size);
 +        }
 +    }
 +
 +    if (pkt.dts != AV_NOPTS_VALUE)
 +        pkt.dts += av_rescale_q(ifile->ts_offset, AV_TIME_BASE_Q, ist->st->time_base);
 +    if (pkt.pts != AV_NOPTS_VALUE)
 +        pkt.pts += av_rescale_q(ifile->ts_offset, AV_TIME_BASE_Q, ist->st->time_base);
 +
 +    if (pkt.pts != AV_NOPTS_VALUE)
 +        pkt.pts *= ist->ts_scale;
 +    if (pkt.dts != AV_NOPTS_VALUE)
 +        pkt.dts *= ist->ts_scale;
 +
 +    pkt_dts = av_rescale_q_rnd(pkt.dts, ist->st->time_base, AV_TIME_BASE_Q, AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
 +    if ((ist->dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO ||
 +         ist->dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) &&
 +        pkt_dts != AV_NOPTS_VALUE && ist->next_dts == AV_NOPTS_VALUE && !copy_ts
 +        && (is->iformat->flags & AVFMT_TS_DISCONT) && ifile->last_ts != AV_NOPTS_VALUE) {
 +        int64_t delta   = pkt_dts - ifile->last_ts;
 +        if (delta < -1LL*dts_delta_threshold*AV_TIME_BASE ||
 +            delta >  1LL*dts_delta_threshold*AV_TIME_BASE){
 +            ifile->ts_offset -= delta;
 +            av_log(NULL, AV_LOG_DEBUG,
 +                   "Inter stream timestamp discontinuity %"PRId64", new offset= %"PRId64"\n",
 +                   delta, ifile->ts_offset);
 +            pkt.dts -= av_rescale_q(delta, AV_TIME_BASE_Q, ist->st->time_base);
 +            if (pkt.pts != AV_NOPTS_VALUE)
 +                pkt.pts -= av_rescale_q(delta, AV_TIME_BASE_Q, ist->st->time_base);
 +        }
 +    }
 +
 +    duration = av_rescale_q(ifile->duration, ifile->time_base, ist->st->time_base);
 +    if (pkt.pts != AV_NOPTS_VALUE) {
 +        pkt.pts += duration;
 +        ist->max_pts = FFMAX(pkt.pts, ist->max_pts);
 +        ist->min_pts = FFMIN(pkt.pts, ist->min_pts);
 +    }
 +
 +    if (pkt.dts != AV_NOPTS_VALUE)
 +        pkt.dts += duration;
 +
 +    pkt_dts = av_rescale_q_rnd(pkt.dts, ist->st->time_base, AV_TIME_BASE_Q, AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
 +    if ((ist->dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO ||
 +         ist->dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) &&
 +         pkt_dts != AV_NOPTS_VALUE && ist->next_dts != AV_NOPTS_VALUE &&
 +        !copy_ts) {
 +        int64_t delta   = pkt_dts - ist->next_dts;
 +        if (is->iformat->flags & AVFMT_TS_DISCONT) {
 +            if (delta < -1LL*dts_delta_threshold*AV_TIME_BASE ||
 +                delta >  1LL*dts_delta_threshold*AV_TIME_BASE ||
 +                pkt_dts + AV_TIME_BASE/10 < FFMAX(ist->pts, ist->dts)) {
 +                ifile->ts_offset -= delta;
 +                av_log(NULL, AV_LOG_DEBUG,
 +                       "timestamp discontinuity %"PRId64", new offset= %"PRId64"\n",
 +                       delta, ifile->ts_offset);
 +                pkt.dts -= av_rescale_q(delta, AV_TIME_BASE_Q, ist->st->time_base);
 +                if (pkt.pts != AV_NOPTS_VALUE)
 +                    pkt.pts -= av_rescale_q(delta, AV_TIME_BASE_Q, ist->st->time_base);
 +            }
 +        } else {
 +            if ( delta < -1LL*dts_error_threshold*AV_TIME_BASE ||
 +                 delta >  1LL*dts_error_threshold*AV_TIME_BASE) {
 +                av_log(NULL, AV_LOG_WARNING, "DTS %"PRId64", next:%"PRId64" st:%d invalid dropping\n", pkt.dts, ist->next_dts, pkt.stream_index);
 +                pkt.dts = AV_NOPTS_VALUE;
 +            }
 +            if (pkt.pts != AV_NOPTS_VALUE){
 +                int64_t pkt_pts = av_rescale_q(pkt.pts, ist->st->time_base, AV_TIME_BASE_Q);
 +                delta   = pkt_pts - ist->next_dts;
 +                if ( delta < -1LL*dts_error_threshold*AV_TIME_BASE ||
 +                     delta >  1LL*dts_error_threshold*AV_TIME_BASE) {
 +                    av_log(NULL, AV_LOG_WARNING, "PTS %"PRId64", next:%"PRId64" invalid dropping st:%d\n", pkt.pts, ist->next_dts, pkt.stream_index);
 +                    pkt.pts = AV_NOPTS_VALUE;
 +                }
 +            }
 +        }
 +    }
 +
 +    if (pkt.dts != AV_NOPTS_VALUE)
 +        ifile->last_ts = av_rescale_q(pkt.dts, ist->st->time_base, AV_TIME_BASE_Q);
 +
 +    if (debug_ts) {
 +        av_log(NULL, AV_LOG_INFO, "demuxer+ffmpeg -> ist_index:%d type:%s pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s off:%s off_time:%s\n",
 +               ifile->ist_index + pkt.stream_index, av_get_media_type_string(ist->dec_ctx->codec_type),
 +               av_ts2str(pkt.pts), av_ts2timestr(pkt.pts, &ist->st->time_base),
 +               av_ts2str(pkt.dts), av_ts2timestr(pkt.dts, &ist->st->time_base),
 +               av_ts2str(input_files[ist->file_index]->ts_offset),
 +               av_ts2timestr(input_files[ist->file_index]->ts_offset, &AV_TIME_BASE_Q));
 +    }
 +
 +    sub2video_heartbeat(ist, pkt.pts);
 +
 +    process_input_packet(ist, &pkt, 0);
 +
 +discard_packet:
 +    av_packet_unref(&pkt);
 +
 +    return 0;
 +}
 +
 +/**
 + * Perform a step of transcoding for the specified filter graph.
 + *
 + * @param[in]  graph     filter graph to consider
 + * @param[out] best_ist  input stream where a frame would allow to continue
 + * @return  0 for success, <0 for error
 + */
 +static int transcode_from_filter(FilterGraph *graph, InputStream **best_ist)
 +{
 +    int i, ret;
 +    int nb_requests, nb_requests_max = 0;
 +    InputFilter *ifilter;
 +    InputStream *ist;
 +
 +    *best_ist = NULL;
 +    ret = avfilter_graph_request_oldest(graph->graph);
 +    if (ret >= 0)
 +        return reap_filters(0);
 +
 +    if (ret == AVERROR_EOF) {
 +        ret = reap_filters(1);
 +        for (i = 0; i < graph->nb_outputs; i++)
 +            close_output_stream(graph->outputs[i]->ost);
 +        return ret;
 +    }
 +    if (ret != AVERROR(EAGAIN))
 +        return ret;
 +
 +    for (i = 0; i < graph->nb_inputs; i++) {
 +        ifilter = graph->inputs[i];
 +        ist = ifilter->ist;
 +        if (input_files[ist->file_index]->eagain ||
 +            input_files[ist->file_index]->eof_reached)
 +            continue;
 +        nb_requests = av_buffersrc_get_nb_failed_requests(ifilter->filter);
 +        if (nb_requests > nb_requests_max) {
 +            nb_requests_max = nb_requests;
 +            *best_ist = ist;
 +        }
 +    }
 +
 +    if (!*best_ist)
 +        for (i = 0; i < graph->nb_outputs; i++)
 +            graph->outputs[i]->ost->unavailable = 1;
 +
 +    return 0;
 +}
 +
 +/**
 + * Run a single step of transcoding.
 + *
 + * @return  0 for success, <0 for error
 + */
 +static int transcode_step(void)
 +{
 +    OutputStream *ost;
 +    InputStream  *ist;
 +    int ret;
 +
 +    ost = choose_output();
 +    if (!ost) {
 +        if (got_eagain()) {
 +            reset_eagain();
 +            av_usleep(10000);
 +            return 0;
 +        }
 +        av_log(NULL, AV_LOG_VERBOSE, "No more inputs to read from, finishing.\n");
 +        return AVERROR_EOF;
 +    }
 +
 +    if (ost->filter) {
 +        if ((ret = transcode_from_filter(ost->filter->graph, &ist)) < 0)
 +            return ret;
 +        if (!ist)
 +            return 0;
 +    } else {
 +        av_assert0(ost->source_index >= 0);
 +        ist = input_streams[ost->source_index];
 +    }
 +
 +    ret = process_input(ist->file_index);
 +    if (ret == AVERROR(EAGAIN)) {
 +        if (input_files[ist->file_index]->eagain)
 +            ost->unavailable = 1;
 +        return 0;
 +    }
 +
 +    if (ret < 0)
 +        return ret == AVERROR_EOF ? 0 : ret;
 +
 +    return reap_filters(0);
 +}
 +
 +/*
 + * The following code is the main loop of the file converter
 + */
 +static int transcode(void)
 +{
 +    int ret, i;
 +    AVFormatContext *os;
 +    OutputStream *ost;
 +    InputStream *ist;
 +    int64_t timer_start;
 +    int64_t total_packets_written = 0;
 +
 +    ret = transcode_init();
 +    if (ret < 0)
 +        goto fail;
 +
 +    if (stdin_interaction) {
 +        av_log(NULL, AV_LOG_INFO, "Press [q] to stop, [?] for help\n");
 +    }
 +
 +    timer_start = av_gettime_relative();
 +
 +#if HAVE_PTHREADS
 +    if ((ret = init_input_threads()) < 0)
 +        goto fail;
 +#endif
 +
 +    while (!received_sigterm) {
 +        int64_t cur_time= av_gettime_relative();
 +
 +        /* if 'q' pressed, exits */
 +        if (stdin_interaction)
 +            if (check_keyboard_interaction(cur_time) < 0)
 +                break;
 +
 +        /* check if there's any stream where output is still needed */
 +        if (!need_output()) {
 +            av_log(NULL, AV_LOG_VERBOSE, "No more output streams to write to, finishing.\n");
 +            break;
 +        }
 +
 +        ret = transcode_step();
 +        if (ret < 0 && ret != AVERROR_EOF) {
 +            char errbuf[128];
 +            av_strerror(ret, errbuf, sizeof(errbuf));
 +
 +            av_log(NULL, AV_LOG_ERROR, "Error while filtering: %s\n", errbuf);
 +            break;
 +        }
 +
 +        /* dump report by using the output first video and audio streams */
 +        print_report(0, timer_start, cur_time);
 +    }
 +#if HAVE_PTHREADS
 +    free_input_threads();
 +#endif
 +
 +    /* at the end of stream, we must flush the decoder buffers */
 +    for (i = 0; i < nb_input_streams; i++) {
 +        ist = input_streams[i];
 +        if (!input_files[ist->file_index]->eof_reached && ist->decoding_needed) {
 +            process_input_packet(ist, NULL, 0);
 +        }
 +    }
 +    flush_encoders();
 +
 +    term_exit();
 +
 +    /* write the trailer if needed and close file */
 +    for (i = 0; i < nb_output_files; i++) {
 +        os = output_files[i]->ctx;
 +        if ((ret = av_write_trailer(os)) < 0) {
 +            av_log(NULL, AV_LOG_ERROR, "Error writing trailer of %s: %s", os->filename, av_err2str(ret));
 +            if (exit_on_error)
 +                exit_program(1);
 +        }
 +    }
 +
 +    /* dump report by using the first video and audio streams */
 +    print_report(1, timer_start, av_gettime_relative());
 +
 +    /* close each encoder */
 +    for (i = 0; i < nb_output_streams; i++) {
 +        ost = output_streams[i];
 +        if (ost->encoding_needed) {
 +            av_freep(&ost->enc_ctx->stats_in);
 +        }
 +        total_packets_written += ost->packets_written;
 +    }
 +
 +    if (!total_packets_written && (abort_on_flags & ABORT_ON_FLAG_EMPTY_OUTPUT)) {
 +        av_log(NULL, AV_LOG_FATAL, "Empty output\n");
 +        exit_program(1);
 +    }
 +
 +    /* close each decoder */
 +    for (i = 0; i < nb_input_streams; i++) {
 +        ist = input_streams[i];
 +        if (ist->decoding_needed) {
 +            avcodec_close(ist->dec_ctx);
 +            if (ist->hwaccel_uninit)
 +                ist->hwaccel_uninit(ist->dec_ctx);
 +        }
 +    }
 +
 +    av_buffer_unref(&hw_device_ctx);
 +
 +    /* finished ! */
 +    ret = 0;
 +
 + fail:
 +#if HAVE_PTHREADS
 +    free_input_threads();
 +#endif
 +
 +    if (output_streams) {
 +        for (i = 0; i < nb_output_streams; i++) {
 +            ost = output_streams[i];
 +            if (ost) {
 +                if (ost->logfile) {
 +                    if (fclose(ost->logfile))
 +                        av_log(NULL, AV_LOG_ERROR,
 +                               "Error closing logfile, loss of information possible: %s\n",
 +                               av_err2str(AVERROR(errno)));
 +                    ost->logfile = NULL;
 +                }
 +                av_freep(&ost->forced_kf_pts);
 +                av_freep(&ost->apad);
 +                av_freep(&ost->disposition);
 +                av_dict_free(&ost->encoder_opts);
 +                av_dict_free(&ost->sws_dict);
 +                av_dict_free(&ost->swr_opts);
 +                av_dict_free(&ost->resample_opts);
 +            }
 +        }
 +    }
 +    return ret;
 +}
 +
 +
 +static int64_t getutime(void)
 +{
 +#if HAVE_GETRUSAGE
 +    struct rusage rusage;
 +
 +    getrusage(RUSAGE_SELF, &rusage);
 +    return (rusage.ru_utime.tv_sec * 1000000LL) + rusage.ru_utime.tv_usec;
 +#elif HAVE_GETPROCESSTIMES
 +    HANDLE proc;
 +    FILETIME c, e, k, u;
 +    proc = GetCurrentProcess();
 +    GetProcessTimes(proc, &c, &e, &k, &u);
 +    return ((int64_t) u.dwHighDateTime << 32 | u.dwLowDateTime) / 10;
 +#else
 +    return av_gettime_relative();
 +#endif
 +}
 +
 +static int64_t getmaxrss(void)
 +{
 +#if HAVE_GETRUSAGE && HAVE_STRUCT_RUSAGE_RU_MAXRSS
 +    struct rusage rusage;
 +    getrusage(RUSAGE_SELF, &rusage);
 +    return (int64_t)rusage.ru_maxrss * 1024;
 +#elif HAVE_GETPROCESSMEMORYINFO
 +    HANDLE proc;
 +    PROCESS_MEMORY_COUNTERS memcounters;
 +    proc = GetCurrentProcess();
 +    memcounters.cb = sizeof(memcounters);
 +    GetProcessMemoryInfo(proc, &memcounters, sizeof(memcounters));
 +    return memcounters.PeakPagefileUsage;
 +#else
 +    return 0;
 +#endif
 +}
 +
 +static void log_callback_null(void *ptr, int level, const char *fmt, va_list vl)
 +{
 +}
 +
 +int main(int argc, char **argv)
 +{
 +    int ret;
 +    int64_t ti;
 +
 +    init_dynload();
 +
 +    register_exit(ffmpeg_cleanup);
 +
 +    setvbuf(stderr,NULL,_IONBF,0); /* win32 runtime needs this */
 +
 +    av_log_set_flags(AV_LOG_SKIP_REPEATED);
 +    parse_loglevel(argc, argv, options);
 +
 +    if(argc>1 && !strcmp(argv[1], "-d")){
 +        run_as_daemon=1;
 +        av_log_set_callback(log_callback_null);
 +        argc--;
 +        argv++;
 +    }
 +
 +    avcodec_register_all();
 +#if CONFIG_AVDEVICE
 +    avdevice_register_all();
 +#endif
 +    avfilter_register_all();
 +    av_register_all();
 +    avformat_network_init();
 +
 +    show_banner(argc, argv, options);
 +
 +    /* parse options and open all input/output files */
 +    ret = ffmpeg_parse_options(argc, argv);
 +    if (ret < 0)
 +        exit_program(1);
 +
 +    if (nb_output_files <= 0 && nb_input_files == 0) {
 +        show_usage();
 +        av_log(NULL, AV_LOG_WARNING, "Use -h to get full help or, even better, run 'man %s'\n", program_name);
 +        exit_program(1);
 +    }
 +
 +    /* file converter / grab */
 +    if (nb_output_files <= 0) {
 +        av_log(NULL, AV_LOG_FATAL, "At least one output file must be specified\n");
 +        exit_program(1);
 +    }
 +
 +//     if (nb_input_files == 0) {
 +//         av_log(NULL, AV_LOG_FATAL, "At least one input file must be specified\n");
 +//         exit_program(1);
 +//     }
 +
 +    current_time = ti = getutime();
 +    if (transcode() < 0)
 +        exit_program(1);
 +    ti = getutime() - ti;
 +    if (do_benchmark) {
 +        av_log(NULL, AV_LOG_INFO, "bench: utime=%0.3fs\n", ti / 1000000.0);
 +    }
 +    av_log(NULL, AV_LOG_DEBUG, "%"PRIu64" frames successfully decoded, %"PRIu64" decoding errors\n",
 +           decode_error_stat[0], decode_error_stat[1]);
 +    if ((decode_error_stat[0] + decode_error_stat[1]) * max_error_rate < decode_error_stat[1])
 +        exit_program(69);
 +
 +    exit_program(received_nb_signals ? 255 : main_return_code);
 +    return main_return_code;
 +}
diff --cc ffplay.c
index 6d43191,0000000..79dc768
mode 100644,000000..100644
--- a/ffplay.c
+++ b/ffplay.c
@@@ -1,3753 -1,0 +1,3749 @@@
 +/*
 + * 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 */
 +#define SDL_VOLUME_STEP (SDL_MIX_MAXVOLUME / 50)
 +
 +/* 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 */
 +    SDL_Texture *bmp;
 +    int allocated;
 +    int width;
 +    int height;
 +    int format;
 +    AVRational sar;
 +    int uploaded;
 +} 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;
 +    AVPacket pkt_temp;
 +    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;
 +
 +    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 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_ALLOC_EVENT   (SDL_USEREVENT)
 +#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 void free_picture(Frame *vp);
 +
 +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;
 +}
 +
 +static int decoder_decode_frame(Decoder *d, AVFrame *frame, AVSubtitle *sub) {
 +    int got_frame = 0;
 +
 +    do {
 +        int ret = -1;
 +
 +        if (d->queue->abort_request)
 +            return -1;
 +
 +        if (!d->packet_pending || d->queue->serial != d->pkt_serial) {
 +            AVPacket pkt;
 +            do {
 +                if (d->queue->nb_packets == 0)
 +                    SDL_CondSignal(d->empty_queue_cond);
 +                if (packet_queue_get(d->queue, &pkt, 1, &d->pkt_serial) < 0)
 +                    return -1;
 +                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;
 +                }
 +            } while (pkt.data == flush_pkt.data || d->queue->serial != d->pkt_serial);
 +            av_packet_unref(&d->pkt);
 +            d->pkt_temp = d->pkt = pkt;
 +            d->packet_pending = 1;
 +        }
 +
 +        switch (d->avctx->codec_type) {
 +            case AVMEDIA_TYPE_VIDEO:
 +                ret = avcodec_decode_video2(d->avctx, frame, &got_frame, &d->pkt_temp);
 +                if (got_frame) {
 +                    if (decoder_reorder_pts == -1) {
 +                        frame->pts = av_frame_get_best_effort_timestamp(frame);
-                     } else if (decoder_reorder_pts) {
-                         frame->pts = frame->pkt_pts;
-                     } else {
++                    } else if (!decoder_reorder_pts) {
 +                        frame->pts = frame->pkt_dts;
 +                    }
 +                }
 +                break;
 +            case AVMEDIA_TYPE_AUDIO:
 +                ret = avcodec_decode_audio4(d->avctx, frame, &got_frame, &d->pkt_temp);
 +                if (got_frame) {
 +                    AVRational tb = (AVRational){1, frame->sample_rate};
 +                    if (frame->pts != AV_NOPTS_VALUE)
-                         frame->pts = av_rescale_q(frame->pts, d->avctx->time_base, tb);
-                     else if (frame->pkt_pts != AV_NOPTS_VALUE)
-                         frame->pts = av_rescale_q(frame->pkt_pts, av_codec_get_pkt_timebase(d->avctx), tb);
++                        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;
 +            case AVMEDIA_TYPE_SUBTITLE:
 +                ret = avcodec_decode_subtitle2(d->avctx, sub, &got_frame, &d->pkt_temp);
 +                break;
 +        }
 +
 +        if (ret < 0) {
 +            d->packet_pending = 0;
 +        } else {
 +            d->pkt_temp.dts =
 +            d->pkt_temp.pts = AV_NOPTS_VALUE;
 +            if (d->pkt_temp.data) {
 +                if (d->avctx->codec_type != AVMEDIA_TYPE_AUDIO)
 +                    ret = d->pkt_temp.size;
 +                d->pkt_temp.data += ret;
 +                d->pkt_temp.size -= ret;
 +                if (d->pkt_temp.size <= 0)
 +                    d->packet_pending = 0;
 +            } else {
 +                if (!got_frame) {
 +                    d->packet_pending = 0;
 +                    d->finished = d->pkt_serial;
 +                }
 +            }
 +        }
 +    } while (!got_frame && !d->finished);
 +
 +    return got_frame;
 +}
 +
 +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);
 +        free_picture(vp);
 +    }
 +    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 void free_picture(Frame *vp)
 +{
 +     if (vp->bmp) {
 +         SDL_DestroyTexture(vp->bmp);
 +         vp->bmp = NULL;
 +     }
 +}
 +
 +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:
 +            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:
 +            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;
 +                int pitch;
 +                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 (vp->bmp) {
 +        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;
 +                        int pitch;
 +                        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) {
 +            if (upload_texture(vp->bmp, vp->frame, &is->img_convert_ctx) < 0)
 +                return;
 +            vp->uploaded = 1;
 +        }
 +
 +        SDL_RenderCopy(renderer, vp->bmp, NULL, &rect);
 +        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->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, Frame *vp)
 +{
 +    int w,h;
 +
 +    if (vp && vp->width)
 +        set_default_window_size(vp->width, vp->height, vp->sar);
 +
 +    if (screen_width) {
 +        w = screen_width;
 +        h = screen_height;
 +    } else {
 +        w = default_width;
 +        h = default_height;
 +    }
 +
 +    if (!window) {
 +        int flags = SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE;
 +        if (!window_title)
 +            window_title = input_filename;
 +        if (is_full_screen)
 +            flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
 +        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) {
 +                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, NULL);
 +
 +    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, int step)
 +{
 +    is->audio_volume = av_clip(is->audio_volume + sign * step, 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;
 +        }
 +    }
 +}
 +
 +/* allocate a picture (needs to do that in main thread to avoid
 +   potential locking problems */
 +static void alloc_picture(VideoState *is)
 +{
 +    Frame *vp;
 +    int sdl_format;
 +
 +    vp = &is->pictq.queue[is->pictq.windex];
 +
 +    video_open(is, vp);
 +
 +    if (vp->format == AV_PIX_FMT_YUV420P)
 +        sdl_format = SDL_PIXELFORMAT_YV12;
 +    else
 +        sdl_format = SDL_PIXELFORMAT_ARGB8888;
 +
 +    if (realloc_texture(&vp->bmp, sdl_format, vp->width, vp->height, SDL_BLENDMODE_NONE, 0) < 0) {
 +        /* SDL allocates a buffer smaller than requested if the video
 +         * overlay hardware is unable to support the requested size. */
 +        av_log(NULL, AV_LOG_FATAL,
 +               "Error: the video system does not support an image\n"
 +                        "size of %dx%d pixels. Try using -lowres or -vf \"scale=w:h\"\n"
 +                        "to reduce the image size.\n", vp->width, vp->height );
 +        do_exit(is);
 +    }
 +
 +    SDL_LockMutex(is->pictq.mutex);
 +    vp->allocated = 1;
 +    SDL_CondSignal(is->pictq.cond);
 +    SDL_UnlockMutex(is->pictq.mutex);
 +}
 +
 +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;
 +
 +    /* alloc or resize hardware picture buffer */
 +    if (!vp->bmp || !vp->allocated ||
 +        vp->width  != src_frame->width ||
 +        vp->height != src_frame->height ||
 +        vp->format != src_frame->format) {
 +        SDL_Event event;
 +
 +        vp->allocated = 0;
 +        vp->width = src_frame->width;
 +        vp->height = src_frame->height;
 +        vp->format = src_frame->format;
 +
 +        /* the allocation must be done in the main thread to avoid
 +           locking problems. */
 +        event.type = FF_ALLOC_EVENT;
 +        event.user.data1 = is;
 +        SDL_PushEvent(&event);
 +
 +        /* wait until the picture is allocated */
 +        SDL_LockMutex(is->pictq.mutex);
 +        while (!vp->allocated && !is->videoq.abort_request) {
 +            SDL_CondWait(is->pictq.cond, is->pictq.mutex);
 +        }
 +        /* if the queue is aborted, we have to pop the pending ALLOC event or wait for the allocation to complete */
 +        if (is->videoq.abort_request && SDL_PeepEvents(&event, 1, SDL_GETEVENT, FF_ALLOC_EVENT, FF_ALLOC_EVENT) != 1) {
 +            while (!vp->allocated && !is->abort_request) {
 +                SDL_CondWait(is->pictq.cond, is->pictq.mutex);
 +            }
 +        }
 +        SDL_UnlockMutex(is->pictq.mutex);
 +
 +        if (is->videoq.abort_request)
 +            return -1;
 +    }
 +
 +    /* if the frame is not skipped, then display it */
 +    if (vp->bmp) {
 +        vp->pts = pts;
 +        vp->duration = duration;
 +        vp->pos = pos;
 +        vp->serial = serial;
 +
 +        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, av_frame_get_channels(frame));
 +
 +                reconfigure =
 +                    cmp_audio_fmts(is->audio_filter_src.fmt, is->audio_filter_src.channels,
 +                                   frame->format, av_frame_get_channels(frame))    ||
 +                    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, av_frame_get_channels(frame), av_get_sample_fmt_name(frame->format), buf2, is->auddec.pkt_serial);
 +
 +                    is->audio_filter_src.fmt            = frame->format;
 +                    is->audio_filter_src.channels       = av_frame_get_channels(frame);
 +                    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 = is->out_audio_filter->inputs[0]->time_base;
 +#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 = av_frame_get_pkt_pos(frame);
 +                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 = filt_out->inputs[0]->frame_rate;
 +        }
 +
 +        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 = filt_out->inputs[0]->time_base;
 +#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, av_frame_get_pkt_pos(frame), 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, av_frame_get_channels(af->frame),
 +                                           af->frame->nb_samples,
 +                                           af->frame->format, 1);
 +
 +    dec_channel_layout =
 +        (af->frame->channel_layout && av_frame_get_channels(af->frame) == av_get_channel_layout_nb_channels(af->frame->channel_layout)) ?
 +        af->frame->channel_layout : av_get_default_channel_layout(av_frame_get_channels(af->frame));
 +    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), av_frame_get_channels(af->frame),
 +                    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       = av_frame_get_channels(af->frame);
 +        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
 +        {
 +            AVFilterLink *link;
 +
 +            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;
 +            link = is->out_audio_filter->inputs[0];
 +            sample_rate    = link->sample_rate;
 +            nb_channels    = avfilter_link_get_channels(link);
 +            channel_layout = link->channel_layout;
 +        }
 +#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;
 +    is->audio_volume = SDL_MIX_MAXVOLUME;
 +    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;
 +        case FF_ALLOC_EVENT:
 +            alloc_picture(event.user.data1);
 +            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[] = {
 +#include "cmdutils_common_opts.h"
 +    { "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" },
 +    { "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 bb3979c,0000000..662137c
mode 100644,000000..100644
--- a/ffprobe.c
+++ b/ffprobe.c
@@@ -1,3367 -1,0 +1,3367 @@@
 +/*
 + * 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/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"
 +
 +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_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_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, -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 } },
 +    [SECTION_ID_FRAME_SIDE_DATA] =     { SECTION_ID_FRAME_SIDE_DATA, "side_data", 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 } },
 +    [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 } },
 +    [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;
 +
 +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));
 +}
 +
 +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", "&");  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);
 +        }
 +    }
 +
 +    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,
 +                                const AVPacketSideData *side_data,
 +                                int nb_side_data,
 +                                SectionID id_data_list,
 +                                SectionID id_data)
 +{
 +    int i;
 +
 +    writer_print_section_header(w, SECTION_ID_STREAM_SIDE_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, SECTION_ID_STREAM_SIDE_DATA);
 +        print_str("side_data_type", name ? name : "unknown");
 +        print_int("side_data_size", sd->size);
 +        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));
 +        }
 +        writer_print_section_footer(w);
 +    }
 +    writer_print_section_footer(w);
 +}
 +
 +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, 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->pkt_pts);
-     print_time("pkt_pts_time",          frame->pkt_pts, &stream->time_base);
++    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", av_frame_get_best_effort_timestamp(frame));
 +    print_time("best_effort_timestamp_time", av_frame_get_best_effort_timestamp(frame), &stream->time_base);
 +    print_duration_ts  ("pkt_duration",      av_frame_get_pkt_duration(frame));
 +    print_duration_time("pkt_duration_time", av_frame_get_pkt_duration(frame), &stream->time_base);
 +    if (av_frame_get_pkt_pos (frame) != -1) print_fmt    ("pkt_pos", "%"PRId64, av_frame_get_pkt_pos(frame));
 +    else                      print_str_opt("pkt_pos", "N/A");
 +    if (av_frame_get_pkt_size(frame) != -1) print_val    ("pkt_size", av_frame_get_pkt_size(frame), 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", av_frame_get_channels(frame));
 +        if (av_frame_get_channel_layout(frame)) {
 +            av_bprint_clear(&pbuf);
 +            av_bprint_channel_layout(&pbuf, av_frame_get_channels(frame),
 +                                     av_frame_get_channel_layout(frame));
 +            print_str    ("channel_layout", pbuf.str);
 +        } else
 +            print_str_opt("channel_layout", "unknown");
 +        break;
 +    }
 +    if (do_show_frame_tags)
 +        show_tags(w, av_frame_get_metadata(frame), SECTION_ID_FRAME_TAGS);
 +    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");
 +            print_int("side_data_size", sd->size);
 +            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)
 +{
 +    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;
 +
 +    if (dec_ctx && dec_ctx->codec) {
 +        switch (par->codec_type) {
 +        case AVMEDIA_TYPE_VIDEO:
 +            ret = avcodec_decode_video2(dec_ctx, frame, &got_frame, pkt);
 +            break;
 +
 +        case AVMEDIA_TYPE_AUDIO:
 +            ret = avcodec_decode_audio4(dec_ctx, frame, &got_frame, pkt);
 +            break;
 +
 +        case AVMEDIA_TYPE_SUBTITLE:
 +            ret = avcodec_decode_subtitle2(dec_ctx, &sub, &got_frame, pkt);
 +            break;
 +        }
 +    }
 +
 +    if (ret < 0)
 +        return ret;
 +    ret = FFMIN(ret, pkt->size); /* guard against bogus return values */
 +    pkt->data += ret;
 +    pkt->size -= 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;
 +}
 +
 +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, pkt1;
 +    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) {
 +                pkt1 = pkt;
 +                while (pkt1.size && process_frame(w, ifile, frame, &pkt1) > 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) > 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 */
 +    av_get_codec_tag_string(val_str, sizeof(val_str), par->codec_tag);
 +    print_str("codec_tag_string",    val_str);
 +    print_fmt("codec_tag", "0x%04x", 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 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");
 +    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->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;
 +
 +    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);
 +
 +            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);
 +}
 +
 +/**
 + * 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[] = {
 +#include "cmdutils_common_opts.h"
 +    { "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" },
 +    { "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();
 +
 +    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);
 +
 +    /* 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);
 +    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 tests/api/api-h264-test.c
index ef3a1fe,0000000..7bdf92b
mode 100644,000000..100644
--- a/tests/api/api-h264-test.c
+++ b/tests/api/api-h264-test.c
@@@ -1,167 -1,0 +1,167 @@@
 +/*
 + * Copyright (c) 2015 Ludmila Glinskih
 + *
 + * Permission is hereby granted, free of charge, to any person obtaining a copy
 + * of this software and associated documentation files (the "Software"), to deal
 + * in the Software without restriction, including without limitation the rights
 + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 + * copies of the Software, and to permit persons to whom the Software is
 + * furnished to do so, subject to the following conditions:
 + *
 + * The above copyright notice and this permission notice shall be included in
 + * all copies or substantial portions of the Software.
 + *
 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 + * THE SOFTWARE.
 + */
 +
 +/**
 + * H264 codec test.
 + */
 +
 +#include "libavutil/adler32.h"
 +#include "libavcodec/avcodec.h"
 +#include "libavformat/avformat.h"
 +#include "libavutil/imgutils.h"
 +
 +static int video_decode_example(const char *input_filename)
 +{
 +    AVCodec *codec = NULL;
 +    AVCodecContext *ctx= NULL;
 +    AVCodecParameters *origin_par = NULL;
 +    AVFrame *fr = NULL;
 +    uint8_t *byte_buffer = NULL;
 +    AVPacket pkt;
 +    AVFormatContext *fmt_ctx = NULL;
 +    int number_of_written_bytes;
 +    int video_stream;
 +    int got_frame = 0;
 +    int byte_buffer_size;
 +    int i = 0;
 +    int result;
 +    int end_of_stream = 0;
 +
 +    result = avformat_open_input(&fmt_ctx, input_filename, NULL, NULL);
 +    if (result < 0) {
 +        av_log(NULL, AV_LOG_ERROR, "Can't open file\n");
 +        return result;
 +    }
 +
 +    result = avformat_find_stream_info(fmt_ctx, NULL);
 +    if (result < 0) {
 +        av_log(NULL, AV_LOG_ERROR, "Can't get stream info\n");
 +        return result;
 +    }
 +
 +    video_stream = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
 +    if (video_stream < 0) {
 +      av_log(NULL, AV_LOG_ERROR, "Can't find video stream in input file\n");
 +      return -1;
 +    }
 +
 +    origin_par = fmt_ctx->streams[video_stream]->codecpar;
 +
 +    codec = avcodec_find_decoder(origin_par->codec_id);
 +    if (!codec) {
 +        av_log(NULL, AV_LOG_ERROR, "Can't find decoder\n");
 +        return -1;
 +    }
 +
 +    ctx = avcodec_alloc_context3(codec);
 +    if (!ctx) {
 +        av_log(NULL, AV_LOG_ERROR, "Can't allocate decoder context\n");
 +        return AVERROR(ENOMEM);
 +    }
 +
 +    result = avcodec_parameters_to_context(ctx, origin_par);
 +    if (result) {
 +        av_log(NULL, AV_LOG_ERROR, "Can't copy decoder context\n");
 +        return result;
 +    }
 +
 +    result = avcodec_open2(ctx, codec, NULL);
 +    if (result < 0) {
 +        av_log(ctx, AV_LOG_ERROR, "Can't open decoder\n");
 +        return result;
 +    }
 +
 +    fr = av_frame_alloc();
 +    if (!fr) {
 +        av_log(NULL, AV_LOG_ERROR, "Can't allocate frame\n");
 +        return AVERROR(ENOMEM);
 +    }
 +
 +    byte_buffer_size = av_image_get_buffer_size(ctx->pix_fmt, ctx->width, ctx->height, 16);
 +    byte_buffer = av_malloc(byte_buffer_size);
 +    if (!byte_buffer) {
 +        av_log(NULL, AV_LOG_ERROR, "Can't allocate buffer\n");
 +        return AVERROR(ENOMEM);
 +    }
 +
 +    printf("#tb %d: %d/%d\n", video_stream, fmt_ctx->streams[video_stream]->time_base.num, fmt_ctx->streams[video_stream]->time_base.den);
 +    i = 0;
 +    av_init_packet(&pkt);
 +    do {
 +        if (!end_of_stream)
 +            if (av_read_frame(fmt_ctx, &pkt) < 0)
 +                end_of_stream = 1;
 +        if (end_of_stream) {
 +            pkt.data = NULL;
 +            pkt.size = 0;
 +        }
 +        if (pkt.stream_index == video_stream || end_of_stream) {
 +            got_frame = 0;
 +            if (pkt.pts == AV_NOPTS_VALUE)
 +                pkt.pts = pkt.dts = i;
 +            result = avcodec_decode_video2(ctx, fr, &got_frame, &pkt);
 +            if (result < 0) {
 +                av_log(NULL, AV_LOG_ERROR, "Error decoding frame\n");
 +                return result;
 +            }
 +            if (got_frame) {
 +                number_of_written_bytes = av_image_copy_to_buffer(byte_buffer, byte_buffer_size,
 +                                        (const uint8_t* const *)fr->data, (const int*) fr->linesize,
 +                                        ctx->pix_fmt, ctx->width, ctx->height, 1);
 +                if (number_of_written_bytes < 0) {
 +                    av_log(NULL, AV_LOG_ERROR, "Can't copy image to buffer\n");
 +                    return number_of_written_bytes;
 +                }
 +                printf("%d, %10"PRId64", %10"PRId64", %8"PRId64", %8d, 0x%08lx\n", video_stream,
-                         fr->pkt_pts, fr->pkt_dts, av_frame_get_pkt_duration(fr),
++                        fr->pts, fr->pkt_dts, av_frame_get_pkt_duration(fr),
 +                        number_of_written_bytes, av_adler32_update(0, (const uint8_t*)byte_buffer, number_of_written_bytes));
 +            }
 +            av_packet_unref(&pkt);
 +            av_init_packet(&pkt);
 +        }
 +        i++;
 +    } while (!end_of_stream || got_frame);
 +
 +    av_packet_unref(&pkt);
 +    av_frame_free(&fr);
 +    avcodec_close(ctx);
 +    avformat_close_input(&fmt_ctx);
 +    avcodec_free_context(&ctx);
 +    av_freep(&byte_buffer);
 +    return 0;
 +}
 +
 +int main(int argc, char **argv)
 +{
 +    if (argc < 2)
 +    {
 +        av_log(NULL, AV_LOG_ERROR, "Incorrect input\n");
 +        return 1;
 +    }
 +
 +    av_register_all();
 +
 +    if (video_decode_example(argv[1]) != 0)
 +        return 1;
 +
 +    return 0;
 +}
diff --cc tests/api/api-seek-test.c
index df47a5f,0000000..8117df4
mode 100644,000000..100644
--- a/tests/api/api-seek-test.c
+++ b/tests/api/api-seek-test.c
@@@ -1,279 -1,0 +1,279 @@@
 +/*
 + * Copyright (c) 2015 Ludmila Glinskih
 + *
 + * Permission is hereby granted, free of charge, to any person obtaining a copy
 + * of this software and associated documentation files (the "Software"), to deal
 + * in the Software without restriction, including without limitation the rights
 + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 + * copies of the Software, and to permit persons to whom the Software is
 + * furnished to do so, subject to the following conditions:
 + *
 + * The above copyright notice and this permission notice shall be included in
 + * all copies or substantial portions of the Software.
 + *
 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 + * THE SOFTWARE.
 + */
 +
 +/**
 + * Seek test.
 + */
 +
 +#include "libavutil/adler32.h"
 +#include "libavcodec/avcodec.h"
 +#include "libavformat/avformat.h"
 +#include "libavutil/imgutils.h"
 +
 +int64_t *pts_array;
 +int64_t *crc_array;
 +int size_of_array;
 +int number_of_elements;
 +
 +static int add_crc_to_array(int64_t crc, int64_t pts)
 +{
 +    if (size_of_array <= number_of_elements) {
 +        if (size_of_array == 0)
 +            size_of_array = 10;
 +        size_of_array *= 2;
 +        crc_array = av_realloc(crc_array, size_of_array * sizeof(int64_t));
 +        pts_array = av_realloc(pts_array, size_of_array * sizeof(int64_t));
 +        if ((crc_array == NULL) || (pts_array == NULL)) {
 +            av_log(NULL, AV_LOG_ERROR, "Can't allocate array to store crcs\n");
 +            return AVERROR(ENOMEM);
 +        }
 +    }
 +    crc_array[number_of_elements] = crc;
 +    pts_array[number_of_elements] = pts;
 +    number_of_elements++;
 +    return 0;
 +}
 +
 +static int compare_crc_in_array(int64_t crc, int64_t pts)
 +{
 +    int i;
 +    for (i = 0; i < number_of_elements; i++) {
 +        if (pts_array[i] == pts) {
 +            if (crc_array[i] == crc) {
 +                printf("Comparing 0x%08lx %"PRId64" %d is OK\n", crc, pts, i);
 +                return 0;
 +            }
 +            else {
 +                av_log(NULL, AV_LOG_ERROR, "Incorrect crc of a frame after seeking\n");
 +                return -1;
 +            }
 +        }
 +    }
 +    av_log(NULL, AV_LOG_ERROR, "Incorrect pts of a frame after seeking\n");
 +    return -1;
 +}
 +
 +static int compute_crc_of_packets(AVFormatContext *fmt_ctx, int video_stream,
 +                                AVCodecContext *ctx, AVFrame *fr, uint64_t ts_start, uint64_t ts_end, int no_seeking)
 +{
 +    int number_of_written_bytes;
 +    int got_frame = 0;
 +    int result;
 +    int end_of_stream = 0;
 +    int byte_buffer_size;
 +    uint8_t *byte_buffer;
 +    int64_t crc;
 +    AVPacket pkt;
 +
 +    byte_buffer_size = av_image_get_buffer_size(ctx->pix_fmt, ctx->width, ctx->height, 16);
 +    byte_buffer = av_malloc(byte_buffer_size);
 +    if (!byte_buffer) {
 +        av_log(NULL, AV_LOG_ERROR, "Can't allocate buffer\n");
 +        return AVERROR(ENOMEM);
 +    }
 +
 +    if (!no_seeking) {
 +        result = av_seek_frame(fmt_ctx, video_stream, ts_start, AVSEEK_FLAG_ANY);
 +        printf("Seeking to %"PRId64", computing crc for frames with pts < %"PRId64"\n", ts_start, ts_end);
 +        if (result < 0) {
 +            av_log(NULL, AV_LOG_ERROR, "Error in seeking\n");
 +            return result;
 +        }
 +        avcodec_flush_buffers(ctx);
 +    }
 +
 +    av_init_packet(&pkt);
 +    do {
 +        if (!end_of_stream)
 +            if (av_read_frame(fmt_ctx, &pkt) < 0)
 +                end_of_stream = 1;
 +        if (end_of_stream) {
 +            pkt.data = NULL;
 +            pkt.size = 0;
 +        }
 +        if (pkt.stream_index == video_stream || end_of_stream) {
 +            got_frame = 0;
 +            if ((pkt.pts == AV_NOPTS_VALUE) && (!end_of_stream)) {
 +                av_log(NULL, AV_LOG_ERROR, "Error: frames doesn't have pts values\n");
 +                return -1;
 +            }
 +            result = avcodec_decode_video2(ctx, fr, &got_frame, &pkt);
 +            if (result < 0) {
 +                av_log(NULL, AV_LOG_ERROR, "Error decoding frame\n");
 +                return result;
 +            }
 +            if (got_frame) {
 +                number_of_written_bytes = av_image_copy_to_buffer(byte_buffer, byte_buffer_size,
 +                                        (const uint8_t* const *)fr->data, (const int*) fr->linesize,
 +                                        ctx->pix_fmt, ctx->width, ctx->height, 1);
 +                if (number_of_written_bytes < 0) {
 +                    av_log(NULL, AV_LOG_ERROR, "Can't copy image to buffer\n");
 +                    return number_of_written_bytes;
 +                }
-                 if ((fr->pkt_pts > ts_end) && (!no_seeking))
++                if ((fr->pts > ts_end) && (!no_seeking))
 +                    break;
 +                crc = av_adler32_update(0, (const uint8_t*)byte_buffer, number_of_written_bytes);
-                 printf("%10"PRId64", 0x%08lx\n", fr->pkt_pts, crc);
++                printf("%10"PRId64", 0x%08lx\n", fr->pts, crc);
 +                if (no_seeking) {
-                     if (add_crc_to_array(crc, fr->pkt_pts) < 0)
++                    if (add_crc_to_array(crc, fr->pts) < 0)
 +                        return -1;
 +                }
 +                else {
-                     if (compare_crc_in_array(crc, fr->pkt_pts) < 0)
++                    if (compare_crc_in_array(crc, fr->pts) < 0)
 +                        return -1;
 +                }
 +            }
 +        }
 +        av_packet_unref(&pkt);
 +        av_init_packet(&pkt);
-     } while ((!end_of_stream || got_frame) && (no_seeking || (fr->pkt_pts + av_frame_get_pkt_duration(fr) <= ts_end)));
++    } while ((!end_of_stream || got_frame) && (no_seeking || (fr->pts + av_frame_get_pkt_duration(fr) <= ts_end)));
 +
 +    av_packet_unref(&pkt);
 +    av_freep(&byte_buffer);
 +
 +    return 0;
 +}
 +
 +static long int read_seek_range(const char *string_with_number)
 +{
 +    long int number;
 +    char *end_of_string = NULL;
 +    number = strtol(string_with_number, &end_of_string, 10);
 +    if ((strlen(string_with_number) != end_of_string - string_with_number)  || (number < 0)) {
 +        av_log(NULL, AV_LOG_ERROR, "Incorrect input ranges of seeking\n");
 +        return -1;
 +    }
 +    else if ((number == LONG_MAX) || (number == LONG_MIN)) {
 +        if (errno == ERANGE) {
 +            av_log(NULL, AV_LOG_ERROR, "Incorrect input ranges of seeking\n");
 +            return -1;
 +        }
 +    }
 +    return number;
 +}
 +
 +static int seek_test(const char *input_filename, const char *start, const char *end)
 +{
 +    AVCodec *codec = NULL;
 +    AVCodecContext *ctx= NULL;
 +    AVCodecParameters *origin_par = NULL;
 +    AVFrame *fr = NULL;
 +    AVFormatContext *fmt_ctx = NULL;
 +    int video_stream;
 +    int result;
 +    int i, j;
 +    long int start_ts, end_ts;
 +
 +    size_of_array = 0;
 +    number_of_elements = 0;
 +    crc_array = pts_array = NULL;
 +
 +    result = avformat_open_input(&fmt_ctx, input_filename, NULL, NULL);
 +    if (result < 0) {
 +        av_log(NULL, AV_LOG_ERROR, "Can't open file\n");
 +        return result;
 +    }
 +
 +    result = avformat_find_stream_info(fmt_ctx, NULL);
 +    if (result < 0) {
 +        av_log(NULL, AV_LOG_ERROR, "Can't get stream info\n");
 +        return result;
 +    }
 +
 +    start_ts = read_seek_range(start);
 +    end_ts = read_seek_range(end);
 +    if ((start_ts < 0) || (end_ts < 0))
 +        return -1;
 +
 +    //TODO: add ability to work with audio format
 +    video_stream = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
 +    if (video_stream < 0) {
 +      av_log(NULL, AV_LOG_ERROR, "Can't find video stream in input file\n");
 +      return -1;
 +    }
 +
 +    origin_par = fmt_ctx->streams[video_stream]->codecpar;
 +
 +    codec = avcodec_find_decoder(origin_par->codec_id);
 +    if (!codec) {
 +        av_log(NULL, AV_LOG_ERROR, "Can't find decoder\n");
 +        return -1;
 +    }
 +
 +    ctx = avcodec_alloc_context3(codec);
 +    if (!ctx) {
 +        av_log(NULL, AV_LOG_ERROR, "Can't allocate decoder context\n");
 +        return AVERROR(ENOMEM);
 +    }
 +
 +    result = avcodec_parameters_to_context(ctx, origin_par);
 +    if (result) {
 +        av_log(NULL, AV_LOG_ERROR, "Can't copy decoder context\n");
 +        return result;
 +    }
 +
 +    result = avcodec_open2(ctx, codec, NULL);
 +    if (result < 0) {
 +        av_log(ctx, AV_LOG_ERROR, "Can't open decoder\n");
 +        return result;
 +    }
 +
 +    fr = av_frame_alloc();
 +    if (!fr) {
 +        av_log(NULL, AV_LOG_ERROR, "Can't allocate frame\n");
 +        return AVERROR(ENOMEM);
 +    }
 +
 +    result = compute_crc_of_packets(fmt_ctx, video_stream, ctx, fr, i, j, 1);
 +    if (result != 0)
 +        return -1;
 +
 +    for (i = start_ts; i < end_ts; i += 100) {
 +        for (j = i + 100; j < end_ts; j += 100)
 +        result = compute_crc_of_packets(fmt_ctx, video_stream, ctx, fr, i, j, 0);
 +        if (result != 0)
 +            return -1;
 +    }
 +
 +    av_freep(&crc_array);
 +    av_freep(&pts_array);
 +    av_frame_free(&fr);
 +    avcodec_close(ctx);
 +    avformat_close_input(&fmt_ctx);
 +    avcodec_free_context(&ctx);
 +    return 0;
 +}
 +
 +int main(int argc, char **argv)
 +{
 +    if (argc < 4) {
 +        av_log(NULL, AV_LOG_ERROR, "Incorrect input\n");
 +        return 1;
 +    }
 +
 +    av_register_all();
 +
 +    if (seek_test(argv[1], argv[2], argv[3]) != 0)
 +        return 1;
 +
 +    return 0;
 +}



More information about the ffmpeg-cvslog mailing list