Ticket #10737: ffprobe_in_ffmpeg_6.1.patch

File ffprobe_in_ffmpeg_6.1.patch, 168.6 KB (added by bubbleguuum, 3 years ago)
  • ffmpeg-6.1/fftools/ffmpeg.c

    diff -ruN tmp/ffmpeg-6.1/fftools/ffmpeg.c ffmpeg-6.1/fftools/ffmpeg.c
    old new  
    101101#include "ffmpeg.h"
    102102#include "sync_queue.h"
    103103
     104#include "ffprobe_in_ffmpeg.h"
     105
    104106const char program_name[] = "ffmpeg";
    105107const int program_birth_year = 2000;
    106108
     
    12911293
    12921294int main(int argc, char **argv)
    12931295{
    1294     int ret, err_rate_exceeded;
     1296    int i, ret, err_rate_exceeded;
    12951297    BenchmarkTimeStamps ti;
    12961298
     1299    if(argc > 1 && strcmp(argv[1], "--ffprobe")==0) {
     1300
     1301      for(int i = 2 ; i < argc ; i++) {
     1302        argv[i-1] = argv[i];
     1303      }
     1304
     1305      argv[0] = "./ffprobe";
     1306      argc--;
     1307    }
     1308   
     1309    if(strcmp(argv[0], "./ffprobe")==0 || strcmp(argv[0], "ffprobe")==0) {
     1310      return main_ffprobe(argc, argv);
     1311    }
     1312
    12971313    init_dynload();
    12981314
    12991315    setvbuf(stderr,NULL,_IONBF,0); /* win32 runtime needs this */
  • ffmpeg-6.1/fftools/ffprobe_in_ffmpeg.c

    diff -ruN tmp/ffmpeg-6.1/fftools/ffprobe_in_ffmpeg.c ffmpeg-6.1/fftools/ffprobe_in_ffmpeg.c
    old new  
     1/*
     2 * Copyright (c) 2007-2010 Stefano Sabatini
     3 *
     4 * This file is part of FFmpeg.
     5 *
     6 * FFmpeg is free software; you can redistribute it and/or
     7 * modify it under the terms of the GNU Lesser General Public
     8 * License as published by the Free Software Foundation; either
     9 * version 2.1 of the License, or (at your option) any later version.
     10 *
     11 * FFmpeg is distributed in the hope that it will be useful,
     12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14 * Lesser General Public License for more details.
     15 *
     16 * You should have received a copy of the GNU Lesser General Public
     17 * License along with FFmpeg; if not, write to the Free Software
     18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
     19 */
     20
     21/**
     22 * @file
     23 * simple media prober based on the FFmpeg libraries
     24 */
     25
     26#include "config.h"
     27#include "libavutil/ffversion.h"
     28
     29#include <string.h>
     30#include <math.h>
     31
     32#include "libavformat/avformat.h"
     33#include "libavformat/version.h"
     34#include "libavcodec/avcodec.h"
     35#include "libavcodec/version.h"
     36#include "libavutil/ambient_viewing_environment.h"
     37#include "libavutil/avassert.h"
     38#include "libavutil/avstring.h"
     39#include "libavutil/bprint.h"
     40#include "libavutil/channel_layout.h"
     41#include "libavutil/display.h"
     42#include "libavutil/hash.h"
     43#include "libavutil/hdr_dynamic_metadata.h"
     44#include "libavutil/mastering_display_metadata.h"
     45#include "libavutil/hdr_dynamic_vivid_metadata.h"
     46#include "libavutil/dovi_meta.h"
     47#include "libavutil/opt.h"
     48#include "libavutil/pixdesc.h"
     49#include "libavutil/spherical.h"
     50#include "libavutil/stereo3d.h"
     51#include "libavutil/dict.h"
     52#include "libavutil/intreadwrite.h"
     53#include "libavutil/libm.h"
     54#include "libavutil/parseutils.h"
     55#include "libavutil/timecode.h"
     56#include "libavutil/timestamp.h"
     57#include "libavdevice/avdevice.h"
     58#include "libavdevice/version.h"
     59#include "libswscale/swscale.h"
     60#include "libswscale/version.h"
     61#include "libswresample/swresample.h"
     62#include "libswresample/version.h"
     63#include "libpostproc/postprocess.h"
     64#include "libpostproc/version.h"
     65#include "libavfilter/version.h"
     66#include "cmdutils.h"
     67#include "opt_common.h"
     68
     69#include "libavutil/thread.h"
     70
     71#include "ffprobe_in_ffmpeg.h"
     72
     73#if !HAVE_THREADS
     74#  ifdef pthread_mutex_lock
     75#    undef pthread_mutex_lock
     76#  endif
     77#  define pthread_mutex_lock(a) do{}while(0)
     78#  ifdef pthread_mutex_unlock
     79#    undef pthread_mutex_unlock
     80#  endif
     81#  define pthread_mutex_unlock(a) do{}while(0)
     82#endif
     83
     84// attached as opaque_ref to packets/frames
     85typedef struct FrameData {
     86    int64_t pkt_pos;
     87    int     pkt_size;
     88} FrameData;
     89
     90typedef struct InputStream {
     91    AVStream *st;
     92
     93    AVCodecContext *dec_ctx;
     94} InputStream;
     95
     96typedef struct InputFile {
     97    AVFormatContext *fmt_ctx;
     98
     99    InputStream *streams;
     100    int       nb_streams;
     101} InputFile;
     102
     103static int do_bitexact = 0;
     104static int do_count_frames = 0;
     105static int do_count_packets = 0;
     106static int do_read_frames  = 0;
     107static int do_read_packets = 0;
     108static int do_show_chapters = 0;
     109static int do_show_error   = 0;
     110static int do_show_format  = 0;
     111static int do_show_frames  = 0;
     112static int do_show_packets = 0;
     113static int do_show_programs = 0;
     114static int do_show_streams = 0;
     115static int do_show_stream_disposition = 0;
     116static int do_show_data    = 0;
     117static int do_show_program_version  = 0;
     118static int do_show_library_versions = 0;
     119static int do_show_pixel_formats = 0;
     120static int do_show_pixel_format_flags = 0;
     121static int do_show_pixel_format_components = 0;
     122static int do_show_log = 0;
     123
     124static int do_show_chapter_tags = 0;
     125static int do_show_format_tags = 0;
     126static int do_show_frame_tags = 0;
     127static int do_show_program_tags = 0;
     128static int do_show_stream_tags = 0;
     129static int do_show_packet_tags = 0;
     130
     131static int show_value_unit              = 0;
     132static int use_value_prefix             = 0;
     133static int use_byte_value_binary_prefix = 0;
     134static int use_value_sexagesimal_format = 0;
     135static int show_private_data            = 1;
     136
     137#define SHOW_OPTIONAL_FIELDS_AUTO       -1
     138#define SHOW_OPTIONAL_FIELDS_NEVER       0
     139#define SHOW_OPTIONAL_FIELDS_ALWAYS      1
     140static int show_optional_fields = SHOW_OPTIONAL_FIELDS_AUTO;
     141
     142static char *output_format;
     143static char *stream_specifier;
     144static char *show_data_hash;
     145
     146typedef struct ReadInterval {
     147    int id;             ///< identifier
     148    int64_t start, end; ///< start, end in second/AV_TIME_BASE units
     149    int has_start, has_end;
     150    int start_is_offset, end_is_offset;
     151    int duration_frames;
     152} ReadInterval;
     153
     154static ReadInterval *read_intervals;
     155static int read_intervals_nb = 0;
     156
     157static int find_stream_info  = 1;
     158
     159/* section structure definition */
     160
     161#define SECTION_MAX_NB_CHILDREN 10
     162
     163typedef enum {
     164    SECTION_ID_NONE = -1,
     165    SECTION_ID_CHAPTER,
     166    SECTION_ID_CHAPTER_TAGS,
     167    SECTION_ID_CHAPTERS,
     168    SECTION_ID_ERROR,
     169    SECTION_ID_FORMAT,
     170    SECTION_ID_FORMAT_TAGS,
     171    SECTION_ID_FRAME,
     172    SECTION_ID_FRAMES,
     173    SECTION_ID_FRAME_TAGS,
     174    SECTION_ID_FRAME_SIDE_DATA_LIST,
     175    SECTION_ID_FRAME_SIDE_DATA,
     176    SECTION_ID_FRAME_SIDE_DATA_TIMECODE_LIST,
     177    SECTION_ID_FRAME_SIDE_DATA_TIMECODE,
     178    SECTION_ID_FRAME_SIDE_DATA_COMPONENT_LIST,
     179    SECTION_ID_FRAME_SIDE_DATA_COMPONENT,
     180    SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST,
     181    SECTION_ID_FRAME_SIDE_DATA_PIECE,
     182    SECTION_ID_FRAME_LOG,
     183    SECTION_ID_FRAME_LOGS,
     184    SECTION_ID_LIBRARY_VERSION,
     185    SECTION_ID_LIBRARY_VERSIONS,
     186    SECTION_ID_PACKET,
     187    SECTION_ID_PACKET_TAGS,
     188    SECTION_ID_PACKETS,
     189    SECTION_ID_PACKETS_AND_FRAMES,
     190    SECTION_ID_PACKET_SIDE_DATA_LIST,
     191    SECTION_ID_PACKET_SIDE_DATA,
     192    SECTION_ID_PIXEL_FORMAT,
     193    SECTION_ID_PIXEL_FORMAT_FLAGS,
     194    SECTION_ID_PIXEL_FORMAT_COMPONENT,
     195    SECTION_ID_PIXEL_FORMAT_COMPONENTS,
     196    SECTION_ID_PIXEL_FORMATS,
     197    SECTION_ID_PROGRAM_STREAM_DISPOSITION,
     198    SECTION_ID_PROGRAM_STREAM_TAGS,
     199    SECTION_ID_PROGRAM,
     200    SECTION_ID_PROGRAM_STREAMS,
     201    SECTION_ID_PROGRAM_STREAM,
     202    SECTION_ID_PROGRAM_TAGS,
     203    SECTION_ID_PROGRAM_VERSION,
     204    SECTION_ID_PROGRAMS,
     205    SECTION_ID_ROOT,
     206    SECTION_ID_STREAM,
     207    SECTION_ID_STREAM_DISPOSITION,
     208    SECTION_ID_STREAMS,
     209    SECTION_ID_STREAM_TAGS,
     210    SECTION_ID_STREAM_SIDE_DATA_LIST,
     211    SECTION_ID_STREAM_SIDE_DATA,
     212    SECTION_ID_SUBTITLE,
     213} SectionID;
     214
     215struct section {
     216    int id;             ///< unique id identifying a section
     217    const char *name;
     218
     219#define SECTION_FLAG_IS_WRAPPER      1 ///< the section only contains other sections, but has no data at its own level
     220#define SECTION_FLAG_IS_ARRAY        2 ///< the section contains an array of elements of the same type
     221#define SECTION_FLAG_HAS_VARIABLE_FIELDS 4 ///< the section may contain a variable number of fields with variable keys.
     222                                           ///  For these sections the element_name field is mandatory.
     223#define SECTION_FLAG_HAS_TYPE        8 ///< the section contains a type to distinguish multiple nested elements
     224
     225    int flags;
     226    const SectionID children_ids[SECTION_MAX_NB_CHILDREN+1]; ///< list of children section IDS, terminated by -1
     227    const char *element_name; ///< name of the contained element, if provided
     228    const char *unique_name;  ///< unique section name, in case the name is ambiguous
     229    AVDictionary *entries_to_show;
     230    const char *(* get_type)(void *data); ///< function returning a type if defined, must be defined when SECTION_FLAG_HAS_TYPE is defined
     231    int show_all_entries;
     232};
     233
     234static const char *get_packet_side_data_type(void *data) {
     235    const AVPacketSideData *sd = (const AVPacketSideData *)data;
     236    return av_x_if_null(av_packet_side_data_name(sd->type), "unknown");
     237}
     238
     239static const char *get_frame_side_data_type(void *data) {
     240    const AVFrameSideData *sd = (const AVFrameSideData *)data;
     241    return av_x_if_null(av_frame_side_data_name(sd->type), "unknown");
     242}
     243
     244static struct section sections[] = {
     245    [SECTION_ID_CHAPTERS] =           { SECTION_ID_CHAPTERS, "chapters", SECTION_FLAG_IS_ARRAY, { SECTION_ID_CHAPTER, -1 } },
     246    [SECTION_ID_CHAPTER] =            { SECTION_ID_CHAPTER, "chapter", 0, { SECTION_ID_CHAPTER_TAGS, -1 } },
     247    [SECTION_ID_CHAPTER_TAGS] =       { SECTION_ID_CHAPTER_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "chapter_tags" },
     248    [SECTION_ID_ERROR] =              { SECTION_ID_ERROR, "error", 0, { -1 } },
     249    [SECTION_ID_FORMAT] =             { SECTION_ID_FORMAT, "format", 0, { SECTION_ID_FORMAT_TAGS, -1 } },
     250    [SECTION_ID_FORMAT_TAGS] =        { SECTION_ID_FORMAT_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "format_tags" },
     251    [SECTION_ID_FRAMES] =             { SECTION_ID_FRAMES, "frames", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME, SECTION_ID_SUBTITLE, -1 } },
     252    [SECTION_ID_FRAME] =              { SECTION_ID_FRAME, "frame", 0, { SECTION_ID_FRAME_TAGS, SECTION_ID_FRAME_SIDE_DATA_LIST, SECTION_ID_FRAME_LOGS, -1 } },
     253    [SECTION_ID_FRAME_TAGS] =         { SECTION_ID_FRAME_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "frame_tags" },
     254    [SECTION_ID_FRAME_SIDE_DATA_LIST] ={ SECTION_ID_FRAME_SIDE_DATA_LIST, "side_data_list", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME_SIDE_DATA, -1 }, .element_name = "side_data", .unique_name = "frame_side_data_list" },
     255    [SECTION_ID_FRAME_SIDE_DATA] =     { SECTION_ID_FRAME_SIDE_DATA, "side_data", SECTION_FLAG_HAS_VARIABLE_FIELDS|SECTION_FLAG_HAS_TYPE, { SECTION_ID_FRAME_SIDE_DATA_TIMECODE_LIST, SECTION_ID_FRAME_SIDE_DATA_COMPONENT_LIST, -1 }, .unique_name = "frame_side_data", .element_name = "side_datum", .get_type = get_frame_side_data_type },
     256    [SECTION_ID_FRAME_SIDE_DATA_TIMECODE_LIST] =  { SECTION_ID_FRAME_SIDE_DATA_TIMECODE_LIST, "timecodes", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME_SIDE_DATA_TIMECODE, -1 } },
     257    [SECTION_ID_FRAME_SIDE_DATA_TIMECODE] =       { SECTION_ID_FRAME_SIDE_DATA_TIMECODE, "timecode", 0, { -1 } },
     258    [SECTION_ID_FRAME_SIDE_DATA_COMPONENT_LIST] = { SECTION_ID_FRAME_SIDE_DATA_COMPONENT_LIST, "components", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME_SIDE_DATA_COMPONENT, -1 } },
     259    [SECTION_ID_FRAME_SIDE_DATA_COMPONENT] =      { SECTION_ID_FRAME_SIDE_DATA_COMPONENT, "component", 0, { SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST, -1 } },
     260    [SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST] =   { SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST, "pieces", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME_SIDE_DATA_PIECE, -1 } },
     261    [SECTION_ID_FRAME_SIDE_DATA_PIECE] =        { SECTION_ID_FRAME_SIDE_DATA_PIECE, "piece", 0, { -1 } },
     262    [SECTION_ID_FRAME_LOGS] =         { SECTION_ID_FRAME_LOGS, "logs", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME_LOG, -1 } },
     263    [SECTION_ID_FRAME_LOG] =          { SECTION_ID_FRAME_LOG, "log", 0, { -1 },  },
     264    [SECTION_ID_LIBRARY_VERSIONS] =   { SECTION_ID_LIBRARY_VERSIONS, "library_versions", SECTION_FLAG_IS_ARRAY, { SECTION_ID_LIBRARY_VERSION, -1 } },
     265    [SECTION_ID_LIBRARY_VERSION] =    { SECTION_ID_LIBRARY_VERSION, "library_version", 0, { -1 } },
     266    [SECTION_ID_PACKETS] =            { SECTION_ID_PACKETS, "packets", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PACKET, -1} },
     267    [SECTION_ID_PACKETS_AND_FRAMES] = { SECTION_ID_PACKETS_AND_FRAMES, "packets_and_frames", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PACKET, -1} },
     268    [SECTION_ID_PACKET] =             { SECTION_ID_PACKET, "packet", 0, { SECTION_ID_PACKET_TAGS, SECTION_ID_PACKET_SIDE_DATA_LIST, -1 } },
     269    [SECTION_ID_PACKET_TAGS] =        { SECTION_ID_PACKET_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "packet_tags" },
     270    [SECTION_ID_PACKET_SIDE_DATA_LIST] ={ SECTION_ID_PACKET_SIDE_DATA_LIST, "side_data_list", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PACKET_SIDE_DATA, -1 }, .element_name = "side_data", .unique_name = "packet_side_data_list" },
     271    [SECTION_ID_PACKET_SIDE_DATA] =     { SECTION_ID_PACKET_SIDE_DATA, "side_data", SECTION_FLAG_HAS_VARIABLE_FIELDS|SECTION_FLAG_HAS_TYPE, { -1 }, .unique_name = "packet_side_data", .element_name = "side_datum", .get_type = get_packet_side_data_type },
     272    [SECTION_ID_PIXEL_FORMATS] =      { SECTION_ID_PIXEL_FORMATS, "pixel_formats", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PIXEL_FORMAT, -1 } },
     273    [SECTION_ID_PIXEL_FORMAT] =       { SECTION_ID_PIXEL_FORMAT, "pixel_format", 0, { SECTION_ID_PIXEL_FORMAT_FLAGS, SECTION_ID_PIXEL_FORMAT_COMPONENTS, -1 } },
     274    [SECTION_ID_PIXEL_FORMAT_FLAGS] = { SECTION_ID_PIXEL_FORMAT_FLAGS, "flags", 0, { -1 }, .unique_name = "pixel_format_flags" },
     275    [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" },
     276    [SECTION_ID_PIXEL_FORMAT_COMPONENT]  = { SECTION_ID_PIXEL_FORMAT_COMPONENT, "component", 0, { -1 } },
     277    [SECTION_ID_PROGRAM_STREAM_DISPOSITION] = { SECTION_ID_PROGRAM_STREAM_DISPOSITION, "disposition", 0, { -1 }, .unique_name = "program_stream_disposition" },
     278    [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" },
     279    [SECTION_ID_PROGRAM] =                    { SECTION_ID_PROGRAM, "program", 0, { SECTION_ID_PROGRAM_TAGS, SECTION_ID_PROGRAM_STREAMS, -1 } },
     280    [SECTION_ID_PROGRAM_STREAMS] =            { SECTION_ID_PROGRAM_STREAMS, "streams", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PROGRAM_STREAM, -1 }, .unique_name = "program_streams" },
     281    [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" },
     282    [SECTION_ID_PROGRAM_TAGS] =               { SECTION_ID_PROGRAM_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "program_tags" },
     283    [SECTION_ID_PROGRAM_VERSION] =    { SECTION_ID_PROGRAM_VERSION, "program_version", 0, { -1 } },
     284    [SECTION_ID_PROGRAMS] =                   { SECTION_ID_PROGRAMS, "programs", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PROGRAM, -1 } },
     285    [SECTION_ID_ROOT] =               { SECTION_ID_ROOT, "root", SECTION_FLAG_IS_WRAPPER,
     286                                        { SECTION_ID_CHAPTERS, SECTION_ID_FORMAT, SECTION_ID_FRAMES, SECTION_ID_PROGRAMS, SECTION_ID_STREAMS,
     287                                          SECTION_ID_PACKETS, SECTION_ID_ERROR, SECTION_ID_PROGRAM_VERSION, SECTION_ID_LIBRARY_VERSIONS,
     288                                          SECTION_ID_PIXEL_FORMATS, -1} },
     289    [SECTION_ID_STREAMS] =            { SECTION_ID_STREAMS, "streams", SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM, -1 } },
     290    [SECTION_ID_STREAM] =             { SECTION_ID_STREAM, "stream", 0, { SECTION_ID_STREAM_DISPOSITION, SECTION_ID_STREAM_TAGS, SECTION_ID_STREAM_SIDE_DATA_LIST, -1 } },
     291    [SECTION_ID_STREAM_DISPOSITION] = { SECTION_ID_STREAM_DISPOSITION, "disposition", 0, { -1 }, .unique_name = "stream_disposition" },
     292    [SECTION_ID_STREAM_TAGS] =        { SECTION_ID_STREAM_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "stream_tags" },
     293    [SECTION_ID_STREAM_SIDE_DATA_LIST] ={ SECTION_ID_STREAM_SIDE_DATA_LIST, "side_data_list", SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM_SIDE_DATA, -1 }, .element_name = "side_data", .unique_name = "stream_side_data_list" },
     294    [SECTION_ID_STREAM_SIDE_DATA] =     { SECTION_ID_STREAM_SIDE_DATA, "side_data", SECTION_FLAG_HAS_TYPE|SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .unique_name = "stream_side_data", .element_name = "side_datum", .get_type = get_packet_side_data_type },
     295    [SECTION_ID_SUBTITLE] =           { SECTION_ID_SUBTITLE, "subtitle", 0, { -1 } },
     296};
     297
     298static const OptionDef *options;
     299
     300/* FFprobe context */
     301static const char *input_filename;
     302static const char *print_input_filename;
     303static const AVInputFormat *iformat = NULL;
     304static const char *output_filename = NULL;
     305
     306static struct AVHashContext *hash;
     307
     308static const struct {
     309    double bin_val;
     310    double dec_val;
     311    const char *bin_str;
     312    const char *dec_str;
     313} si_prefixes[] = {
     314    { 1.0, 1.0, "", "" },
     315    { 1.024e3, 1e3, "Ki", "K" },
     316    { 1.048576e6, 1e6, "Mi", "M" },
     317    { 1.073741824e9, 1e9, "Gi", "G" },
     318    { 1.099511627776e12, 1e12, "Ti", "T" },
     319    { 1.125899906842624e15, 1e15, "Pi", "P" },
     320};
     321
     322static const char unit_second_str[]         = "s"    ;
     323static const char unit_hertz_str[]          = "Hz"   ;
     324static const char unit_byte_str[]           = "byte" ;
     325static const char unit_bit_per_second_str[] = "bit/s";
     326
     327static int nb_streams;
     328static uint64_t *nb_streams_packets;
     329static uint64_t *nb_streams_frames;
     330static int *selected_streams;
     331
     332#if HAVE_THREADS
     333pthread_mutex_t log_mutex;
     334#endif
     335typedef struct LogBuffer {
     336    char *context_name;
     337    int log_level;
     338    char *log_message;
     339    AVClassCategory category;
     340    char *parent_name;
     341    AVClassCategory parent_category;
     342}LogBuffer;
     343
     344static LogBuffer *log_buffer;
     345static int log_buffer_size;
     346
     347static void log_callback(void *ptr, int level, const char *fmt, va_list vl)
     348{
     349    AVClass* avc = ptr ? *(AVClass **) ptr : NULL;
     350    va_list vl2;
     351    char line[1024];
     352    static int print_prefix = 1;
     353    void *new_log_buffer;
     354
     355    va_copy(vl2, vl);
     356    av_log_default_callback(ptr, level, fmt, vl);
     357    av_log_format_line(ptr, level, fmt, vl2, line, sizeof(line), &print_prefix);
     358    va_end(vl2);
     359
     360#if HAVE_THREADS
     361    pthread_mutex_lock(&log_mutex);
     362
     363    new_log_buffer = av_realloc_array(log_buffer, log_buffer_size + 1, sizeof(*log_buffer));
     364    if (new_log_buffer) {
     365        char *msg;
     366        int i;
     367
     368        log_buffer = new_log_buffer;
     369        memset(&log_buffer[log_buffer_size], 0, sizeof(log_buffer[log_buffer_size]));
     370        log_buffer[log_buffer_size].context_name= avc ? av_strdup(avc->item_name(ptr)) : NULL;
     371        if (avc) {
     372            if (avc->get_category) log_buffer[log_buffer_size].category = avc->get_category(ptr);
     373            else                   log_buffer[log_buffer_size].category = avc->category;
     374        }
     375        log_buffer[log_buffer_size].log_level   = level;
     376        msg = log_buffer[log_buffer_size].log_message = av_strdup(line);
     377        for (i=strlen(msg) - 1; i>=0 && msg[i] == '\n'; i--) {
     378            msg[i] = 0;
     379        }
     380        if (avc && avc->parent_log_context_offset) {
     381            AVClass** parent = *(AVClass ***) (((uint8_t *) ptr) +
     382                                   avc->parent_log_context_offset);
     383            if (parent && *parent) {
     384                log_buffer[log_buffer_size].parent_name = av_strdup((*parent)->item_name(parent));
     385                log_buffer[log_buffer_size].parent_category =
     386                    (*parent)->get_category ? (*parent)->get_category(parent) :(*parent)->category;
     387            }
     388        }
     389        log_buffer_size ++;
     390    }
     391
     392    pthread_mutex_unlock(&log_mutex);
     393#endif
     394}
     395
     396struct unit_value {
     397    union { double d; long long int i; } val;
     398    const char *unit;
     399};
     400
     401static char *value_string(char *buf, int buf_size, struct unit_value uv)
     402{
     403    double vald;
     404    long long int vali;
     405    int show_float = 0;
     406
     407    if (uv.unit == unit_second_str) {
     408        vald = uv.val.d;
     409        show_float = 1;
     410    } else {
     411        vald = vali = uv.val.i;
     412    }
     413
     414    if (uv.unit == unit_second_str && use_value_sexagesimal_format) {
     415        double secs;
     416        int hours, mins;
     417        secs  = vald;
     418        mins  = (int)secs / 60;
     419        secs  = secs - mins * 60;
     420        hours = mins / 60;
     421        mins %= 60;
     422        snprintf(buf, buf_size, "%d:%02d:%09.6f", hours, mins, secs);
     423    } else {
     424        const char *prefix_string = "";
     425
     426        if (use_value_prefix && vald > 1) {
     427            long long int index;
     428
     429            if (uv.unit == unit_byte_str && use_byte_value_binary_prefix) {
     430                index = (long long int) (log2(vald)) / 10;
     431                index = av_clip(index, 0, FF_ARRAY_ELEMS(si_prefixes) - 1);
     432                vald /= si_prefixes[index].bin_val;
     433                prefix_string = si_prefixes[index].bin_str;
     434            } else {
     435                index = (long long int) (log10(vald)) / 3;
     436                index = av_clip(index, 0, FF_ARRAY_ELEMS(si_prefixes) - 1);
     437                vald /= si_prefixes[index].dec_val;
     438                prefix_string = si_prefixes[index].dec_str;
     439            }
     440            vali = vald;
     441        }
     442
     443        if (show_float || (use_value_prefix && vald != (long long int)vald))
     444            snprintf(buf, buf_size, "%f", vald);
     445        else
     446            snprintf(buf, buf_size, "%lld", vali);
     447        av_strlcatf(buf, buf_size, "%s%s%s", *prefix_string || show_value_unit ? " " : "",
     448                 prefix_string, show_value_unit ? uv.unit : "");
     449    }
     450
     451    return buf;
     452}
     453
     454/* WRITERS API */
     455
     456typedef struct WriterContext WriterContext;
     457
     458#define WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS 1
     459#define WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER 2
     460
     461typedef enum {
     462    WRITER_STRING_VALIDATION_FAIL,
     463    WRITER_STRING_VALIDATION_REPLACE,
     464    WRITER_STRING_VALIDATION_IGNORE,
     465    WRITER_STRING_VALIDATION_NB
     466} StringValidation;
     467
     468typedef struct Writer {
     469    const AVClass *priv_class;      ///< private class of the writer, if any
     470    int priv_size;                  ///< private size for the writer context
     471    const char *name;
     472
     473    int  (*init)  (WriterContext *wctx);
     474    void (*uninit)(WriterContext *wctx);
     475
     476    void (*print_section_header)(WriterContext *wctx, void *data);
     477    void (*print_section_footer)(WriterContext *wctx);
     478    void (*print_integer)       (WriterContext *wctx, const char *, long long int);
     479    void (*print_rational)      (WriterContext *wctx, AVRational *q, char *sep);
     480    void (*print_string)        (WriterContext *wctx, const char *, const char *);
     481    int flags;                  ///< a combination or WRITER_FLAG_*
     482} Writer;
     483
     484#define SECTION_MAX_NB_LEVELS 10
     485
     486struct WriterContext {
     487    const AVClass *class;           ///< class of the writer
     488    const Writer *writer;           ///< the Writer of which this is an instance
     489    AVIOContext *avio;              ///< the I/O context used to write
     490
     491    void (* writer_w8)(WriterContext *wctx, int b);
     492    void (* writer_put_str)(WriterContext *wctx, const char *str);
     493    void (* writer_printf)(WriterContext *wctx, const char *fmt, ...);
     494
     495    char *name;                     ///< name of this writer instance
     496    void *priv;                     ///< private data for use by the filter
     497
     498    const struct section *sections; ///< array containing all sections
     499    int nb_sections;                ///< number of sections
     500
     501    int level;                      ///< current level, starting from 0
     502
     503    /** number of the item printed in the given section, starting from 0 */
     504    unsigned int nb_item[SECTION_MAX_NB_LEVELS];
     505
     506    /** section per each level */
     507    const struct section *section[SECTION_MAX_NB_LEVELS];
     508    AVBPrint section_pbuf[SECTION_MAX_NB_LEVELS]; ///< generic print buffer dedicated to each section,
     509                                                  ///  used by various writers
     510
     511    unsigned int nb_section_packet; ///< number of the packet section in case we are in "packets_and_frames" section
     512    unsigned int nb_section_frame;  ///< number of the frame  section in case we are in "packets_and_frames" section
     513    unsigned int nb_section_packet_frame; ///< nb_section_packet or nb_section_frame according if is_packets_and_frames
     514
     515    int string_validation;
     516    char *string_validation_replacement;
     517    unsigned int string_validation_utf8_flags;
     518};
     519
     520static const char *writer_get_name(void *p)
     521{
     522    WriterContext *wctx = p;
     523    return wctx->writer->name;
     524}
     525
     526#define OFFSET(x) offsetof(WriterContext, x)
     527
     528static const AVOption writer_options[] = {
     529    { "string_validation", "set string validation mode",
     530      OFFSET(string_validation), AV_OPT_TYPE_INT, {.i64=WRITER_STRING_VALIDATION_REPLACE}, 0, WRITER_STRING_VALIDATION_NB-1, .unit = "sv" },
     531    { "sv", "set string validation mode",
     532      OFFSET(string_validation), AV_OPT_TYPE_INT, {.i64=WRITER_STRING_VALIDATION_REPLACE}, 0, WRITER_STRING_VALIDATION_NB-1, .unit = "sv" },
     533    { "ignore",  NULL, 0, AV_OPT_TYPE_CONST, {.i64 = WRITER_STRING_VALIDATION_IGNORE},  .unit = "sv" },
     534    { "replace", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = WRITER_STRING_VALIDATION_REPLACE}, .unit = "sv" },
     535    { "fail",    NULL, 0, AV_OPT_TYPE_CONST, {.i64 = WRITER_STRING_VALIDATION_FAIL},    .unit = "sv" },
     536    { "string_validation_replacement", "set string validation replacement string", OFFSET(string_validation_replacement), AV_OPT_TYPE_STRING, {.str=""}},
     537    { "svr", "set string validation replacement string", OFFSET(string_validation_replacement), AV_OPT_TYPE_STRING, {.str="\xEF\xBF\xBD"}},
     538    { NULL }
     539};
     540
     541static void *writer_child_next(void *obj, void *prev)
     542{
     543    WriterContext *ctx = obj;
     544    if (!prev && ctx->writer && ctx->writer->priv_class && ctx->priv)
     545        return ctx->priv;
     546    return NULL;
     547}
     548
     549static const AVClass writer_class = {
     550    .class_name = "Writer",
     551    .item_name  = writer_get_name,
     552    .option     = writer_options,
     553    .version    = LIBAVUTIL_VERSION_INT,
     554    .child_next = writer_child_next,
     555};
     556
     557static int writer_close(WriterContext **wctx)
     558{
     559    int i;
     560    int ret = 0;
     561
     562    if (!*wctx)
     563        return -1;
     564
     565    if ((*wctx)->writer->uninit)
     566        (*wctx)->writer->uninit(*wctx);
     567    for (i = 0; i < SECTION_MAX_NB_LEVELS; i++)
     568        av_bprint_finalize(&(*wctx)->section_pbuf[i], NULL);
     569    if ((*wctx)->writer->priv_class)
     570        av_opt_free((*wctx)->priv);
     571    av_freep(&((*wctx)->priv));
     572    av_opt_free(*wctx);
     573    if ((*wctx)->avio) {
     574        avio_flush((*wctx)->avio);
     575        ret = avio_close((*wctx)->avio);
     576    }
     577    av_freep(wctx);
     578    return ret;
     579}
     580
     581static void bprint_bytes(AVBPrint *bp, const uint8_t *ubuf, size_t ubuf_size)
     582{
     583    int i;
     584    av_bprintf(bp, "0X");
     585    for (i = 0; i < ubuf_size; i++)
     586        av_bprintf(bp, "%02X", ubuf[i]);
     587}
     588
     589static inline void writer_w8_avio(WriterContext *wctx, int b)
     590{
     591    avio_w8(wctx->avio, b);
     592}
     593
     594static inline void writer_put_str_avio(WriterContext *wctx, const char *str)
     595{
     596    avio_write(wctx->avio, str, strlen(str));
     597}
     598
     599static inline void writer_printf_avio(WriterContext *wctx, const char *fmt, ...)
     600{
     601    va_list ap;
     602
     603    va_start(ap, fmt);
     604    avio_vprintf(wctx->avio, fmt, ap);
     605    va_end(ap);
     606}
     607
     608static inline void writer_w8_printf(WriterContext *wctx, int b)
     609{
     610    printf("%c", b);
     611}
     612
     613static inline void writer_put_str_printf(WriterContext *wctx, const char *str)
     614{
     615    printf("%s", str);
     616}
     617
     618static inline void writer_printf_printf(WriterContext *wctx, const char *fmt, ...)
     619{
     620    va_list ap;
     621
     622    va_start(ap, fmt);
     623    vprintf(fmt, ap);
     624    va_end(ap);
     625}
     626
     627static int writer_open(WriterContext **wctx, const Writer *writer, const char *args,
     628                       const struct section *sections, int nb_sections, const char *output)
     629{
     630    int i, ret = 0;
     631
     632    if (!(*wctx = av_mallocz(sizeof(WriterContext)))) {
     633        ret = AVERROR(ENOMEM);
     634        goto fail;
     635    }
     636
     637    if (!((*wctx)->priv = av_mallocz(writer->priv_size))) {
     638        ret = AVERROR(ENOMEM);
     639        goto fail;
     640    }
     641
     642    (*wctx)->class = &writer_class;
     643    (*wctx)->writer = writer;
     644    (*wctx)->level = -1;
     645    (*wctx)->sections = sections;
     646    (*wctx)->nb_sections = nb_sections;
     647
     648    av_opt_set_defaults(*wctx);
     649
     650    if (writer->priv_class) {
     651        void *priv_ctx = (*wctx)->priv;
     652        *((const AVClass **)priv_ctx) = writer->priv_class;
     653        av_opt_set_defaults(priv_ctx);
     654    }
     655
     656    /* convert options to dictionary */
     657    if (args) {
     658        AVDictionary *opts = NULL;
     659        const AVDictionaryEntry *opt = NULL;
     660
     661        if ((ret = av_dict_parse_string(&opts, args, "=", ":", 0)) < 0) {
     662            av_log(*wctx, AV_LOG_ERROR, "Failed to parse option string '%s' provided to writer context\n", args);
     663            av_dict_free(&opts);
     664            goto fail;
     665        }
     666
     667        while ((opt = av_dict_iterate(opts, opt))) {
     668            if ((ret = av_opt_set(*wctx, opt->key, opt->value, AV_OPT_SEARCH_CHILDREN)) < 0) {
     669                av_log(*wctx, AV_LOG_ERROR, "Failed to set option '%s' with value '%s' provided to writer context\n",
     670                       opt->key, opt->value);
     671                av_dict_free(&opts);
     672                goto fail;
     673            }
     674        }
     675
     676        av_dict_free(&opts);
     677    }
     678
     679    /* validate replace string */
     680    {
     681        const uint8_t *p = (*wctx)->string_validation_replacement;
     682        const uint8_t *endp = p + strlen(p);
     683        while (*p) {
     684            const uint8_t *p0 = p;
     685            int32_t code;
     686            ret = av_utf8_decode(&code, &p, endp, (*wctx)->string_validation_utf8_flags);
     687            if (ret < 0) {
     688                AVBPrint bp;
     689                av_bprint_init(&bp, 0, AV_BPRINT_SIZE_AUTOMATIC);
     690                bprint_bytes(&bp, p0, p-p0),
     691                    av_log(wctx, AV_LOG_ERROR,
     692                           "Invalid UTF8 sequence %s found in string validation replace '%s'\n",
     693                           bp.str, (*wctx)->string_validation_replacement);
     694                return ret;
     695            }
     696        }
     697    }
     698
     699    if (!output_filename) {
     700        (*wctx)->writer_w8 = writer_w8_printf;
     701        (*wctx)->writer_put_str = writer_put_str_printf;
     702        (*wctx)->writer_printf = writer_printf_printf;
     703    } else {
     704        if ((ret = avio_open(&(*wctx)->avio, output, AVIO_FLAG_WRITE)) < 0) {
     705            av_log(*wctx, AV_LOG_ERROR,
     706                   "Failed to open output '%s' with error: %s\n", output, av_err2str(ret));
     707            goto fail;
     708        }
     709        (*wctx)->writer_w8 = writer_w8_avio;
     710        (*wctx)->writer_put_str = writer_put_str_avio;
     711        (*wctx)->writer_printf = writer_printf_avio;
     712    }
     713
     714    for (i = 0; i < SECTION_MAX_NB_LEVELS; i++)
     715        av_bprint_init(&(*wctx)->section_pbuf[i], 1, AV_BPRINT_SIZE_UNLIMITED);
     716
     717    if ((*wctx)->writer->init)
     718        ret = (*wctx)->writer->init(*wctx);
     719    if (ret < 0)
     720        goto fail;
     721
     722    return 0;
     723
     724fail:
     725    writer_close(wctx);
     726    return ret;
     727}
     728
     729static inline void writer_print_section_header(WriterContext *wctx,
     730                                               void *data,
     731                                               int section_id)
     732{
     733    int parent_section_id;
     734    wctx->level++;
     735    av_assert0(wctx->level < SECTION_MAX_NB_LEVELS);
     736    parent_section_id = wctx->level ?
     737        (wctx->section[wctx->level-1])->id : SECTION_ID_NONE;
     738
     739    wctx->nb_item[wctx->level] = 0;
     740    wctx->section[wctx->level] = &wctx->sections[section_id];
     741
     742    if (section_id == SECTION_ID_PACKETS_AND_FRAMES) {
     743        wctx->nb_section_packet = wctx->nb_section_frame =
     744        wctx->nb_section_packet_frame = 0;
     745    } else if (parent_section_id == SECTION_ID_PACKETS_AND_FRAMES) {
     746        wctx->nb_section_packet_frame = section_id == SECTION_ID_PACKET ?
     747            wctx->nb_section_packet : wctx->nb_section_frame;
     748    }
     749
     750    if (wctx->writer->print_section_header)
     751        wctx->writer->print_section_header(wctx, data);
     752}
     753
     754static inline void writer_print_section_footer(WriterContext *wctx)
     755{
     756    int section_id = wctx->section[wctx->level]->id;
     757    int parent_section_id = wctx->level ?
     758        wctx->section[wctx->level-1]->id : SECTION_ID_NONE;
     759
     760    if (parent_section_id != SECTION_ID_NONE)
     761        wctx->nb_item[wctx->level-1]++;
     762    if (parent_section_id == SECTION_ID_PACKETS_AND_FRAMES) {
     763        if (section_id == SECTION_ID_PACKET) wctx->nb_section_packet++;
     764        else                                     wctx->nb_section_frame++;
     765    }
     766    if (wctx->writer->print_section_footer)
     767        wctx->writer->print_section_footer(wctx);
     768    wctx->level--;
     769}
     770
     771static inline void writer_print_integer(WriterContext *wctx,
     772                                        const char *key, long long int val)
     773{
     774    const struct section *section = wctx->section[wctx->level];
     775
     776    if (section->show_all_entries || av_dict_get(section->entries_to_show, key, NULL, 0)) {
     777        wctx->writer->print_integer(wctx, key, val);
     778        wctx->nb_item[wctx->level]++;
     779    }
     780}
     781
     782static inline int validate_string(WriterContext *wctx, char **dstp, const char *src)
     783{
     784    const uint8_t *p, *endp;
     785    AVBPrint dstbuf;
     786    int invalid_chars_nb = 0, ret = 0;
     787
     788    av_bprint_init(&dstbuf, 0, AV_BPRINT_SIZE_UNLIMITED);
     789
     790    endp = src + strlen(src);
     791    for (p = (uint8_t *)src; *p;) {
     792        uint32_t code;
     793        int invalid = 0;
     794        const uint8_t *p0 = p;
     795
     796        if (av_utf8_decode(&code, &p, endp, wctx->string_validation_utf8_flags) < 0) {
     797            AVBPrint bp;
     798            av_bprint_init(&bp, 0, AV_BPRINT_SIZE_AUTOMATIC);
     799            bprint_bytes(&bp, p0, p-p0);
     800            av_log(wctx, AV_LOG_DEBUG,
     801                   "Invalid UTF-8 sequence %s found in string '%s'\n", bp.str, src);
     802            invalid = 1;
     803        }
     804
     805        if (invalid) {
     806            invalid_chars_nb++;
     807
     808            switch (wctx->string_validation) {
     809            case WRITER_STRING_VALIDATION_FAIL:
     810                av_log(wctx, AV_LOG_ERROR,
     811                       "Invalid UTF-8 sequence found in string '%s'\n", src);
     812                ret = AVERROR_INVALIDDATA;
     813                goto end;
     814                break;
     815
     816            case WRITER_STRING_VALIDATION_REPLACE:
     817                av_bprintf(&dstbuf, "%s", wctx->string_validation_replacement);
     818                break;
     819            }
     820        }
     821
     822        if (!invalid || wctx->string_validation == WRITER_STRING_VALIDATION_IGNORE)
     823            av_bprint_append_data(&dstbuf, p0, p-p0);
     824    }
     825
     826    if (invalid_chars_nb && wctx->string_validation == WRITER_STRING_VALIDATION_REPLACE) {
     827        av_log(wctx, AV_LOG_WARNING,
     828               "%d invalid UTF-8 sequence(s) found in string '%s', replaced with '%s'\n",
     829               invalid_chars_nb, src, wctx->string_validation_replacement);
     830    }
     831
     832end:
     833    av_bprint_finalize(&dstbuf, dstp);
     834    return ret;
     835}
     836
     837#define PRINT_STRING_OPT      1
     838#define PRINT_STRING_VALIDATE 2
     839
     840static inline int writer_print_string(WriterContext *wctx,
     841                                      const char *key, const char *val, int flags)
     842{
     843    const struct section *section = wctx->section[wctx->level];
     844    int ret = 0;
     845
     846    if (show_optional_fields == SHOW_OPTIONAL_FIELDS_NEVER ||
     847        (show_optional_fields == SHOW_OPTIONAL_FIELDS_AUTO
     848        && (flags & PRINT_STRING_OPT)
     849        && !(wctx->writer->flags & WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS)))
     850        return 0;
     851
     852    if (section->show_all_entries || av_dict_get(section->entries_to_show, key, NULL, 0)) {
     853        if (flags & PRINT_STRING_VALIDATE) {
     854            char *key1 = NULL, *val1 = NULL;
     855            ret = validate_string(wctx, &key1, key);
     856            if (ret < 0) goto end;
     857            ret = validate_string(wctx, &val1, val);
     858            if (ret < 0) goto end;
     859            wctx->writer->print_string(wctx, key1, val1);
     860        end:
     861            if (ret < 0) {
     862                av_log(wctx, AV_LOG_ERROR,
     863                       "Invalid key=value string combination %s=%s in section %s\n",
     864                       key, val, section->unique_name);
     865            }
     866            av_free(key1);
     867            av_free(val1);
     868        } else {
     869            wctx->writer->print_string(wctx, key, val);
     870        }
     871
     872        wctx->nb_item[wctx->level]++;
     873    }
     874
     875    return ret;
     876}
     877
     878static inline void writer_print_rational(WriterContext *wctx,
     879                                         const char *key, AVRational q, char sep)
     880{
     881    AVBPrint buf;
     882    av_bprint_init(&buf, 0, AV_BPRINT_SIZE_AUTOMATIC);
     883    av_bprintf(&buf, "%d%c%d", q.num, sep, q.den);
     884    writer_print_string(wctx, key, buf.str, 0);
     885}
     886
     887static void writer_print_time(WriterContext *wctx, const char *key,
     888                              int64_t ts, const AVRational *time_base, int is_duration)
     889{
     890    char buf[128];
     891
     892    if ((!is_duration && ts == AV_NOPTS_VALUE) || (is_duration && ts == 0)) {
     893        writer_print_string(wctx, key, "N/A", PRINT_STRING_OPT);
     894    } else {
     895        double d = ts * av_q2d(*time_base);
     896        struct unit_value uv;
     897        uv.val.d = d;
     898        uv.unit = unit_second_str;
     899        value_string(buf, sizeof(buf), uv);
     900        writer_print_string(wctx, key, buf, 0);
     901    }
     902}
     903
     904static void writer_print_ts(WriterContext *wctx, const char *key, int64_t ts, int is_duration)
     905{
     906    if ((!is_duration && ts == AV_NOPTS_VALUE) || (is_duration && ts == 0)) {
     907        writer_print_string(wctx, key, "N/A", PRINT_STRING_OPT);
     908    } else {
     909        writer_print_integer(wctx, key, ts);
     910    }
     911}
     912
     913static void writer_print_data(WriterContext *wctx, const char *name,
     914                              const uint8_t *data, int size)
     915{
     916    AVBPrint bp;
     917    int offset = 0, l, i;
     918
     919    av_bprint_init(&bp, 0, AV_BPRINT_SIZE_UNLIMITED);
     920    av_bprintf(&bp, "\n");
     921    while (size) {
     922        av_bprintf(&bp, "%08x: ", offset);
     923        l = FFMIN(size, 16);
     924        for (i = 0; i < l; i++) {
     925            av_bprintf(&bp, "%02x", data[i]);
     926            if (i & 1)
     927                av_bprintf(&bp, " ");
     928        }
     929        av_bprint_chars(&bp, ' ', 41 - 2 * i - i / 2);
     930        for (i = 0; i < l; i++)
     931            av_bprint_chars(&bp, data[i] - 32U < 95 ? data[i] : '.', 1);
     932        av_bprintf(&bp, "\n");
     933        offset += l;
     934        data   += l;
     935        size   -= l;
     936    }
     937    writer_print_string(wctx, name, bp.str, 0);
     938    av_bprint_finalize(&bp, NULL);
     939}
     940
     941static void writer_print_data_hash(WriterContext *wctx, const char *name,
     942                                   const uint8_t *data, int size)
     943{
     944    char *p, buf[AV_HASH_MAX_SIZE * 2 + 64] = { 0 };
     945
     946    if (!hash)
     947        return;
     948    av_hash_init(hash);
     949    av_hash_update(hash, data, size);
     950    snprintf(buf, sizeof(buf), "%s:", av_hash_get_name(hash));
     951    p = buf + strlen(buf);
     952    av_hash_final_hex(hash, p, buf + sizeof(buf) - p);
     953    writer_print_string(wctx, name, buf, 0);
     954}
     955
     956static void writer_print_integers(WriterContext *wctx, const char *name,
     957                                  uint8_t *data, int size, const char *format,
     958                                  int columns, int bytes, int offset_add)
     959{
     960    AVBPrint bp;
     961    int offset = 0, l, i;
     962
     963    av_bprint_init(&bp, 0, AV_BPRINT_SIZE_UNLIMITED);
     964    av_bprintf(&bp, "\n");
     965    while (size) {
     966        av_bprintf(&bp, "%08x: ", offset);
     967        l = FFMIN(size, columns);
     968        for (i = 0; i < l; i++) {
     969            if      (bytes == 1) av_bprintf(&bp, format, *data);
     970            else if (bytes == 2) av_bprintf(&bp, format, AV_RN16(data));
     971            else if (bytes == 4) av_bprintf(&bp, format, AV_RN32(data));
     972            data += bytes;
     973            size --;
     974        }
     975        av_bprintf(&bp, "\n");
     976        offset += offset_add;
     977    }
     978    writer_print_string(wctx, name, bp.str, 0);
     979    av_bprint_finalize(&bp, NULL);
     980}
     981
     982#define writer_w8(wctx_, b_) (wctx_)->writer_w8(wctx_, b_)
     983#define writer_put_str(wctx_, str_) (wctx_)->writer_put_str(wctx_, str_)
     984#define writer_printf(wctx_, fmt_, ...) (wctx_)->writer_printf(wctx_, fmt_, __VA_ARGS__)
     985
     986#define MAX_REGISTERED_WRITERS_NB 64
     987
     988static const Writer *registered_writers[MAX_REGISTERED_WRITERS_NB + 1];
     989
     990static int writer_register(const Writer *writer)
     991{
     992    static int next_registered_writer_idx = 0;
     993
     994    if (next_registered_writer_idx == MAX_REGISTERED_WRITERS_NB)
     995        return AVERROR(ENOMEM);
     996
     997    registered_writers[next_registered_writer_idx++] = writer;
     998    return 0;
     999}
     1000
     1001static const Writer *writer_get_by_name(const char *name)
     1002{
     1003    int i;
     1004
     1005    for (i = 0; registered_writers[i]; i++)
     1006        if (!strcmp(registered_writers[i]->name, name))
     1007            return registered_writers[i];
     1008
     1009    return NULL;
     1010}
     1011
     1012
     1013/* WRITERS */
     1014
     1015#define DEFINE_WRITER_CLASS(name)                   \
     1016static const char *name##_get_name(void *ctx)       \
     1017{                                                   \
     1018    return #name ;                                  \
     1019}                                                   \
     1020static const AVClass name##_class = {               \
     1021    .class_name = #name,                            \
     1022    .item_name  = name##_get_name,                  \
     1023    .option     = name##_options                    \
     1024}
     1025
     1026/* Default output */
     1027
     1028typedef struct DefaultContext {
     1029    const AVClass *class;
     1030    int nokey;
     1031    int noprint_wrappers;
     1032    int nested_section[SECTION_MAX_NB_LEVELS];
     1033} DefaultContext;
     1034
     1035#undef OFFSET
     1036#define OFFSET(x) offsetof(DefaultContext, x)
     1037
     1038static const AVOption default_options[] = {
     1039    { "noprint_wrappers", "do not print headers and footers", OFFSET(noprint_wrappers), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
     1040    { "nw",               "do not print headers and footers", OFFSET(noprint_wrappers), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
     1041    { "nokey",          "force no key printing",     OFFSET(nokey),          AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
     1042    { "nk",             "force no key printing",     OFFSET(nokey),          AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
     1043    {NULL},
     1044};
     1045
     1046DEFINE_WRITER_CLASS(default);
     1047
     1048/* lame uppercasing routine, assumes the string is lower case ASCII */
     1049static inline char *upcase_string(char *dst, size_t dst_size, const char *src)
     1050{
     1051    int i;
     1052    for (i = 0; src[i] && i < dst_size-1; i++)
     1053        dst[i] = av_toupper(src[i]);
     1054    dst[i] = 0;
     1055    return dst;
     1056}
     1057
     1058static void default_print_section_header(WriterContext *wctx, void *data)
     1059{
     1060    DefaultContext *def = wctx->priv;
     1061    char buf[32];
     1062    const struct section *section = wctx->section[wctx->level];
     1063    const struct section *parent_section = wctx->level ?
     1064        wctx->section[wctx->level-1] : NULL;
     1065
     1066    av_bprint_clear(&wctx->section_pbuf[wctx->level]);
     1067    if (parent_section &&
     1068        !(parent_section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY))) {
     1069        def->nested_section[wctx->level] = 1;
     1070        av_bprintf(&wctx->section_pbuf[wctx->level], "%s%s:",
     1071                   wctx->section_pbuf[wctx->level-1].str,
     1072                   upcase_string(buf, sizeof(buf),
     1073                                 av_x_if_null(section->element_name, section->name)));
     1074    }
     1075
     1076    if (def->noprint_wrappers || def->nested_section[wctx->level])
     1077        return;
     1078
     1079    if (!(section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY)))
     1080        writer_printf(wctx, "[%s]\n", upcase_string(buf, sizeof(buf), section->name));
     1081}
     1082
     1083static void default_print_section_footer(WriterContext *wctx)
     1084{
     1085    DefaultContext *def = wctx->priv;
     1086    const struct section *section = wctx->section[wctx->level];
     1087    char buf[32];
     1088
     1089    if (def->noprint_wrappers || def->nested_section[wctx->level])
     1090        return;
     1091
     1092    if (!(section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY)))
     1093        writer_printf(wctx, "[/%s]\n", upcase_string(buf, sizeof(buf), section->name));
     1094}
     1095
     1096static void default_print_str(WriterContext *wctx, const char *key, const char *value)
     1097{
     1098    DefaultContext *def = wctx->priv;
     1099
     1100    if (!def->nokey)
     1101        writer_printf(wctx, "%s%s=", wctx->section_pbuf[wctx->level].str, key);
     1102    writer_printf(wctx, "%s\n", value);
     1103}
     1104
     1105static void default_print_int(WriterContext *wctx, const char *key, long long int value)
     1106{
     1107    DefaultContext *def = wctx->priv;
     1108
     1109    if (!def->nokey)
     1110        writer_printf(wctx, "%s%s=", wctx->section_pbuf[wctx->level].str, key);
     1111    writer_printf(wctx, "%lld\n", value);
     1112}
     1113
     1114static const Writer default_writer = {
     1115    .name                  = "default",
     1116    .priv_size             = sizeof(DefaultContext),
     1117    .print_section_header  = default_print_section_header,
     1118    .print_section_footer  = default_print_section_footer,
     1119    .print_integer         = default_print_int,
     1120    .print_string          = default_print_str,
     1121    .flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS,
     1122    .priv_class            = &default_class,
     1123};
     1124
     1125/* Compact output */
     1126
     1127/**
     1128 * Apply C-language-like string escaping.
     1129 */
     1130static const char *c_escape_str(AVBPrint *dst, const char *src, const char sep, void *log_ctx)
     1131{
     1132    const char *p;
     1133
     1134    for (p = src; *p; p++) {
     1135        switch (*p) {
     1136        case '\b': av_bprintf(dst, "%s", "\\b");  break;
     1137        case '\f': av_bprintf(dst, "%s", "\\f");  break;
     1138        case '\n': av_bprintf(dst, "%s", "\\n");  break;
     1139        case '\r': av_bprintf(dst, "%s", "\\r");  break;
     1140        case '\\': av_bprintf(dst, "%s", "\\\\"); break;
     1141        default:
     1142            if (*p == sep)
     1143                av_bprint_chars(dst, '\\', 1);
     1144            av_bprint_chars(dst, *p, 1);
     1145        }
     1146    }
     1147    return dst->str;
     1148}
     1149
     1150/**
     1151 * Quote fields containing special characters, check RFC4180.
     1152 */
     1153static const char *csv_escape_str(AVBPrint *dst, const char *src, const char sep, void *log_ctx)
     1154{
     1155    char meta_chars[] = { sep, '"', '\n', '\r', '\0' };
     1156    int needs_quoting = !!src[strcspn(src, meta_chars)];
     1157
     1158    if (needs_quoting)
     1159        av_bprint_chars(dst, '"', 1);
     1160
     1161    for (; *src; src++) {
     1162        if (*src == '"')
     1163            av_bprint_chars(dst, '"', 1);
     1164        av_bprint_chars(dst, *src, 1);
     1165    }
     1166    if (needs_quoting)
     1167        av_bprint_chars(dst, '"', 1);
     1168    return dst->str;
     1169}
     1170
     1171static const char *none_escape_str(AVBPrint *dst, const char *src, const char sep, void *log_ctx)
     1172{
     1173    return src;
     1174}
     1175
     1176typedef struct CompactContext {
     1177    const AVClass *class;
     1178    char *item_sep_str;
     1179    char item_sep;
     1180    int nokey;
     1181    int print_section;
     1182    char *escape_mode_str;
     1183    const char * (*escape_str)(AVBPrint *dst, const char *src, const char sep, void *log_ctx);
     1184    int nested_section[SECTION_MAX_NB_LEVELS];
     1185    int has_nested_elems[SECTION_MAX_NB_LEVELS];
     1186    int terminate_line[SECTION_MAX_NB_LEVELS];
     1187} CompactContext;
     1188
     1189#undef OFFSET
     1190#define OFFSET(x) offsetof(CompactContext, x)
     1191
     1192static const AVOption compact_options[]= {
     1193    {"item_sep", "set item separator",    OFFSET(item_sep_str),    AV_OPT_TYPE_STRING, {.str="|"},  0, 0 },
     1194    {"s",        "set item separator",    OFFSET(item_sep_str),    AV_OPT_TYPE_STRING, {.str="|"},  0, 0 },
     1195    {"nokey",    "force no key printing", OFFSET(nokey),           AV_OPT_TYPE_BOOL,   {.i64=0},    0,        1        },
     1196    {"nk",       "force no key printing", OFFSET(nokey),           AV_OPT_TYPE_BOOL,   {.i64=0},    0,        1        },
     1197    {"escape",   "set escape mode",       OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="c"},  0, 0 },
     1198    {"e",        "set escape mode",       OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="c"},  0, 0 },
     1199    {"print_section", "print section name", OFFSET(print_section), AV_OPT_TYPE_BOOL,   {.i64=1},    0,        1        },
     1200    {"p",             "print section name", OFFSET(print_section), AV_OPT_TYPE_BOOL,   {.i64=1},    0,        1        },
     1201    {NULL},
     1202};
     1203
     1204DEFINE_WRITER_CLASS(compact);
     1205
     1206static av_cold int compact_init(WriterContext *wctx)
     1207{
     1208    CompactContext *compact = wctx->priv;
     1209
     1210    if (strlen(compact->item_sep_str) != 1) {
     1211        av_log(wctx, AV_LOG_ERROR, "Item separator '%s' specified, but must contain a single character\n",
     1212               compact->item_sep_str);
     1213        return AVERROR(EINVAL);
     1214    }
     1215    compact->item_sep = compact->item_sep_str[0];
     1216
     1217    if      (!strcmp(compact->escape_mode_str, "none")) compact->escape_str = none_escape_str;
     1218    else if (!strcmp(compact->escape_mode_str, "c"   )) compact->escape_str = c_escape_str;
     1219    else if (!strcmp(compact->escape_mode_str, "csv" )) compact->escape_str = csv_escape_str;
     1220    else {
     1221        av_log(wctx, AV_LOG_ERROR, "Unknown escape mode '%s'\n", compact->escape_mode_str);
     1222        return AVERROR(EINVAL);
     1223    }
     1224
     1225    return 0;
     1226}
     1227
     1228static void compact_print_section_header(WriterContext *wctx, void *data)
     1229{
     1230    CompactContext *compact = wctx->priv;
     1231    const struct section *section = wctx->section[wctx->level];
     1232    const struct section *parent_section = wctx->level ?
     1233        wctx->section[wctx->level-1] : NULL;
     1234    compact->terminate_line[wctx->level] = 1;
     1235    compact->has_nested_elems[wctx->level] = 0;
     1236
     1237    av_bprint_clear(&wctx->section_pbuf[wctx->level]);
     1238    if (parent_section &&
     1239        (section->flags & SECTION_FLAG_HAS_TYPE ||
     1240         (!(section->flags & SECTION_FLAG_IS_ARRAY) &&
     1241          !(parent_section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY))))) {
     1242
     1243        /* define a prefix for elements not contained in an array or
     1244           in a wrapper, or for array elements with a type */
     1245        const char *element_name = (char *)av_x_if_null(section->element_name, section->name);
     1246        AVBPrint *section_pbuf = &wctx->section_pbuf[wctx->level];
     1247
     1248        compact->nested_section[wctx->level] = 1;
     1249        compact->has_nested_elems[wctx->level-1] = 1;
     1250
     1251        av_bprintf(section_pbuf, "%s%s",
     1252                   wctx->section_pbuf[wctx->level-1].str, element_name);
     1253
     1254        if (section->flags & SECTION_FLAG_HAS_TYPE) {
     1255            // add /TYPE to prefix
     1256            av_bprint_chars(section_pbuf, '/', 1);
     1257
     1258            // normalize section type, replace special characters and lower case
     1259            for (const char *p = section->get_type(data); *p; p++) {
     1260                char c =
     1261                    (*p >= '0' && *p <= '9') ||
     1262                    (*p >= 'a' && *p <= 'z') ||
     1263                    (*p >= 'A' && *p <= 'Z') ? av_tolower(*p) : '_';
     1264                av_bprint_chars(section_pbuf, c, 1);
     1265            }
     1266        }
     1267        av_bprint_chars(section_pbuf, ':', 1);
     1268
     1269        wctx->nb_item[wctx->level] = wctx->nb_item[wctx->level-1];
     1270    } else {
     1271        if (parent_section && !(parent_section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY)) &&
     1272            wctx->level && wctx->nb_item[wctx->level-1])
     1273            writer_w8(wctx, compact->item_sep);
     1274        if (compact->print_section &&
     1275            !(section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY)))
     1276            writer_printf(wctx, "%s%c", section->name, compact->item_sep);
     1277    }
     1278}
     1279
     1280static void compact_print_section_footer(WriterContext *wctx)
     1281{
     1282    CompactContext *compact = wctx->priv;
     1283
     1284    if (!compact->nested_section[wctx->level] &&
     1285        compact->terminate_line[wctx->level] &&
     1286        !(wctx->section[wctx->level]->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY)))
     1287        writer_w8(wctx, '\n');
     1288}
     1289
     1290static void compact_print_str(WriterContext *wctx, const char *key, const char *value)
     1291{
     1292    CompactContext *compact = wctx->priv;
     1293    AVBPrint buf;
     1294
     1295    if (wctx->nb_item[wctx->level]) writer_w8(wctx, compact->item_sep);
     1296    if (!compact->nokey)
     1297        writer_printf(wctx, "%s%s=", wctx->section_pbuf[wctx->level].str, key);
     1298    av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
     1299    writer_put_str(wctx, compact->escape_str(&buf, value, compact->item_sep, wctx));
     1300    av_bprint_finalize(&buf, NULL);
     1301}
     1302
     1303static void compact_print_int(WriterContext *wctx, const char *key, long long int value)
     1304{
     1305    CompactContext *compact = wctx->priv;
     1306
     1307    if (wctx->nb_item[wctx->level]) writer_w8(wctx, compact->item_sep);
     1308    if (!compact->nokey)
     1309        writer_printf(wctx, "%s%s=", wctx->section_pbuf[wctx->level].str, key);
     1310    writer_printf(wctx, "%lld", value);
     1311}
     1312
     1313static const Writer compact_writer = {
     1314    .name                 = "compact",
     1315    .priv_size            = sizeof(CompactContext),
     1316    .init                 = compact_init,
     1317    .print_section_header = compact_print_section_header,
     1318    .print_section_footer = compact_print_section_footer,
     1319    .print_integer        = compact_print_int,
     1320    .print_string         = compact_print_str,
     1321    .flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS,
     1322    .priv_class           = &compact_class,
     1323};
     1324
     1325/* CSV output */
     1326
     1327#undef OFFSET
     1328#define OFFSET(x) offsetof(CompactContext, x)
     1329
     1330static const AVOption csv_options[] = {
     1331    {"item_sep", "set item separator",    OFFSET(item_sep_str),    AV_OPT_TYPE_STRING, {.str=","},  0, 0 },
     1332    {"s",        "set item separator",    OFFSET(item_sep_str),    AV_OPT_TYPE_STRING, {.str=","},  0, 0 },
     1333    {"nokey",    "force no key printing", OFFSET(nokey),           AV_OPT_TYPE_BOOL,   {.i64=1},    0,        1        },
     1334    {"nk",       "force no key printing", OFFSET(nokey),           AV_OPT_TYPE_BOOL,   {.i64=1},    0,        1        },
     1335    {"escape",   "set escape mode",       OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="csv"}, 0, 0 },
     1336    {"e",        "set escape mode",       OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="csv"}, 0, 0 },
     1337    {"print_section", "print section name", OFFSET(print_section), AV_OPT_TYPE_BOOL,   {.i64=1},    0,        1        },
     1338    {"p",             "print section name", OFFSET(print_section), AV_OPT_TYPE_BOOL,   {.i64=1},    0,        1        },
     1339    {NULL},
     1340};
     1341
     1342DEFINE_WRITER_CLASS(csv);
     1343
     1344static const Writer csv_writer = {
     1345    .name                 = "csv",
     1346    .priv_size            = sizeof(CompactContext),
     1347    .init                 = compact_init,
     1348    .print_section_header = compact_print_section_header,
     1349    .print_section_footer = compact_print_section_footer,
     1350    .print_integer        = compact_print_int,
     1351    .print_string         = compact_print_str,
     1352    .flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS,
     1353    .priv_class           = &csv_class,
     1354};
     1355
     1356/* Flat output */
     1357
     1358typedef struct FlatContext {
     1359    const AVClass *class;
     1360    const char *sep_str;
     1361    char sep;
     1362    int hierarchical;
     1363} FlatContext;
     1364
     1365#undef OFFSET
     1366#define OFFSET(x) offsetof(FlatContext, x)
     1367
     1368static const AVOption flat_options[]= {
     1369    {"sep_char", "set separator",    OFFSET(sep_str),    AV_OPT_TYPE_STRING, {.str="."},  0, 0 },
     1370    {"s",        "set separator",    OFFSET(sep_str),    AV_OPT_TYPE_STRING, {.str="."},  0, 0 },
     1371    {"hierarchical", "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 },
     1372    {"h",            "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 },
     1373    {NULL},
     1374};
     1375
     1376DEFINE_WRITER_CLASS(flat);
     1377
     1378static av_cold int flat_init(WriterContext *wctx)
     1379{
     1380    FlatContext *flat = wctx->priv;
     1381
     1382    if (strlen(flat->sep_str) != 1) {
     1383        av_log(wctx, AV_LOG_ERROR, "Item separator '%s' specified, but must contain a single character\n",
     1384               flat->sep_str);
     1385        return AVERROR(EINVAL);
     1386    }
     1387    flat->sep = flat->sep_str[0];
     1388
     1389    return 0;
     1390}
     1391
     1392static const char *flat_escape_key_str(AVBPrint *dst, const char *src, const char sep)
     1393{
     1394    const char *p;
     1395
     1396    for (p = src; *p; p++) {
     1397        if (!((*p >= '0' && *p <= '9') ||
     1398              (*p >= 'a' && *p <= 'z') ||
     1399              (*p >= 'A' && *p <= 'Z')))
     1400            av_bprint_chars(dst, '_', 1);
     1401        else
     1402            av_bprint_chars(dst, *p, 1);
     1403    }
     1404    return dst->str;
     1405}
     1406
     1407static const char *flat_escape_value_str(AVBPrint *dst, const char *src)
     1408{
     1409    const char *p;
     1410
     1411    for (p = src; *p; p++) {
     1412        switch (*p) {
     1413        case '\n': av_bprintf(dst, "%s", "\\n");  break;
     1414        case '\r': av_bprintf(dst, "%s", "\\r");  break;
     1415        case '\\': av_bprintf(dst, "%s", "\\\\"); break;
     1416        case '"':  av_bprintf(dst, "%s", "\\\""); break;
     1417        case '`':  av_bprintf(dst, "%s", "\\`");  break;
     1418        case '$':  av_bprintf(dst, "%s", "\\$");  break;
     1419        default:   av_bprint_chars(dst, *p, 1);   break;
     1420        }
     1421    }
     1422    return dst->str;
     1423}
     1424
     1425static void flat_print_section_header(WriterContext *wctx, void *data)
     1426{
     1427    FlatContext *flat = wctx->priv;
     1428    AVBPrint *buf = &wctx->section_pbuf[wctx->level];
     1429    const struct section *section = wctx->section[wctx->level];
     1430    const struct section *parent_section = wctx->level ?
     1431        wctx->section[wctx->level-1] : NULL;
     1432
     1433    /* build section header */
     1434    av_bprint_clear(buf);
     1435    if (!parent_section)
     1436        return;
     1437    av_bprintf(buf, "%s", wctx->section_pbuf[wctx->level-1].str);
     1438
     1439    if (flat->hierarchical ||
     1440        !(section->flags & (SECTION_FLAG_IS_ARRAY|SECTION_FLAG_IS_WRAPPER))) {
     1441        av_bprintf(buf, "%s%s", wctx->section[wctx->level]->name, flat->sep_str);
     1442
     1443        if (parent_section->flags & SECTION_FLAG_IS_ARRAY) {
     1444            int n = parent_section->id == SECTION_ID_PACKETS_AND_FRAMES ?
     1445                wctx->nb_section_packet_frame : wctx->nb_item[wctx->level-1];
     1446            av_bprintf(buf, "%d%s", n, flat->sep_str);
     1447        }
     1448    }
     1449}
     1450
     1451static void flat_print_int(WriterContext *wctx, const char *key, long long int value)
     1452{
     1453    writer_printf(wctx, "%s%s=%lld\n", wctx->section_pbuf[wctx->level].str, key, value);
     1454}
     1455
     1456static void flat_print_str(WriterContext *wctx, const char *key, const char *value)
     1457{
     1458    FlatContext *flat = wctx->priv;
     1459    AVBPrint buf;
     1460
     1461    writer_put_str(wctx, wctx->section_pbuf[wctx->level].str);
     1462    av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
     1463    writer_printf(wctx, "%s=", flat_escape_key_str(&buf, key, flat->sep));
     1464    av_bprint_clear(&buf);
     1465    writer_printf(wctx, "\"%s\"\n", flat_escape_value_str(&buf, value));
     1466    av_bprint_finalize(&buf, NULL);
     1467}
     1468
     1469static const Writer flat_writer = {
     1470    .name                  = "flat",
     1471    .priv_size             = sizeof(FlatContext),
     1472    .init                  = flat_init,
     1473    .print_section_header  = flat_print_section_header,
     1474    .print_integer         = flat_print_int,
     1475    .print_string          = flat_print_str,
     1476    .flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS|WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER,
     1477    .priv_class            = &flat_class,
     1478};
     1479
     1480/* INI format output */
     1481
     1482typedef struct INIContext {
     1483    const AVClass *class;
     1484    int hierarchical;
     1485} INIContext;
     1486
     1487#undef OFFSET
     1488#define OFFSET(x) offsetof(INIContext, x)
     1489
     1490static const AVOption ini_options[] = {
     1491    {"hierarchical", "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 },
     1492    {"h",            "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 },
     1493    {NULL},
     1494};
     1495
     1496DEFINE_WRITER_CLASS(ini);
     1497
     1498static char *ini_escape_str(AVBPrint *dst, const char *src)
     1499{
     1500    int i = 0;
     1501    char c = 0;
     1502
     1503    while (c = src[i++]) {
     1504        switch (c) {
     1505        case '\b': av_bprintf(dst, "%s", "\\b"); break;
     1506        case '\f': av_bprintf(dst, "%s", "\\f"); break;
     1507        case '\n': av_bprintf(dst, "%s", "\\n"); break;
     1508        case '\r': av_bprintf(dst, "%s", "\\r"); break;
     1509        case '\t': av_bprintf(dst, "%s", "\\t"); break;
     1510        case '\\':
     1511        case '#' :
     1512        case '=' :
     1513        case ':' : av_bprint_chars(dst, '\\', 1);
     1514        default:
     1515            if ((unsigned char)c < 32)
     1516                av_bprintf(dst, "\\x00%02x", c & 0xff);
     1517            else
     1518                av_bprint_chars(dst, c, 1);
     1519            break;
     1520        }
     1521    }
     1522    return dst->str;
     1523}
     1524
     1525static void ini_print_section_header(WriterContext *wctx, void *data)
     1526{
     1527    INIContext *ini = wctx->priv;
     1528    AVBPrint *buf = &wctx->section_pbuf[wctx->level];
     1529    const struct section *section = wctx->section[wctx->level];
     1530    const struct section *parent_section = wctx->level ?
     1531        wctx->section[wctx->level-1] : NULL;
     1532
     1533    av_bprint_clear(buf);
     1534    if (!parent_section) {
     1535        writer_put_str(wctx, "# ffprobe output\n\n");
     1536        return;
     1537    }
     1538
     1539    if (wctx->nb_item[wctx->level-1])
     1540        writer_w8(wctx, '\n');
     1541
     1542    av_bprintf(buf, "%s", wctx->section_pbuf[wctx->level-1].str);
     1543    if (ini->hierarchical ||
     1544        !(section->flags & (SECTION_FLAG_IS_ARRAY|SECTION_FLAG_IS_WRAPPER))) {
     1545        av_bprintf(buf, "%s%s", buf->str[0] ? "." : "", wctx->section[wctx->level]->name);
     1546
     1547        if (parent_section->flags & SECTION_FLAG_IS_ARRAY) {
     1548            int n = parent_section->id == SECTION_ID_PACKETS_AND_FRAMES ?
     1549                wctx->nb_section_packet_frame : wctx->nb_item[wctx->level-1];
     1550            av_bprintf(buf, ".%d", n);
     1551        }
     1552    }
     1553
     1554    if (!(section->flags & (SECTION_FLAG_IS_ARRAY|SECTION_FLAG_IS_WRAPPER)))
     1555        writer_printf(wctx, "[%s]\n", buf->str);
     1556}
     1557
     1558static void ini_print_str(WriterContext *wctx, const char *key, const char *value)
     1559{
     1560    AVBPrint buf;
     1561
     1562    av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
     1563    writer_printf(wctx, "%s=", ini_escape_str(&buf, key));
     1564    av_bprint_clear(&buf);
     1565    writer_printf(wctx, "%s\n", ini_escape_str(&buf, value));
     1566    av_bprint_finalize(&buf, NULL);
     1567}
     1568
     1569static void ini_print_int(WriterContext *wctx, const char *key, long long int value)
     1570{
     1571    writer_printf(wctx, "%s=%lld\n", key, value);
     1572}
     1573
     1574static const Writer ini_writer = {
     1575    .name                  = "ini",
     1576    .priv_size             = sizeof(INIContext),
     1577    .print_section_header  = ini_print_section_header,
     1578    .print_integer         = ini_print_int,
     1579    .print_string          = ini_print_str,
     1580    .flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS|WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER,
     1581    .priv_class            = &ini_class,
     1582};
     1583
     1584/* JSON output */
     1585
     1586typedef struct JSONContext {
     1587    const AVClass *class;
     1588    int indent_level;
     1589    int compact;
     1590    const char *item_sep, *item_start_end;
     1591} JSONContext;
     1592
     1593#undef OFFSET
     1594#define OFFSET(x) offsetof(JSONContext, x)
     1595
     1596static const AVOption json_options[]= {
     1597    { "compact", "enable compact output", OFFSET(compact), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
     1598    { "c",       "enable compact output", OFFSET(compact), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
     1599    { NULL }
     1600};
     1601
     1602DEFINE_WRITER_CLASS(json);
     1603
     1604static av_cold int json_init(WriterContext *wctx)
     1605{
     1606    JSONContext *json = wctx->priv;
     1607
     1608    json->item_sep       = json->compact ? ", " : ",\n";
     1609    json->item_start_end = json->compact ? " "  : "\n";
     1610
     1611    return 0;
     1612}
     1613
     1614static const char *json_escape_str(AVBPrint *dst, const char *src, void *log_ctx)
     1615{
     1616    static const char json_escape[] = {'"', '\\', '\b', '\f', '\n', '\r', '\t', 0};
     1617    static const char json_subst[]  = {'"', '\\',  'b',  'f',  'n',  'r',  't', 0};
     1618    const char *p;
     1619
     1620    for (p = src; *p; p++) {
     1621        char *s = strchr(json_escape, *p);
     1622        if (s) {
     1623            av_bprint_chars(dst, '\\', 1);
     1624            av_bprint_chars(dst, json_subst[s - json_escape], 1);
     1625        } else if ((unsigned char)*p < 32) {
     1626            av_bprintf(dst, "\\u00%02x", *p & 0xff);
     1627        } else {
     1628            av_bprint_chars(dst, *p, 1);
     1629        }
     1630    }
     1631    return dst->str;
     1632}
     1633
     1634#define JSON_INDENT() writer_printf(wctx, "%*c", json->indent_level * 4, ' ')
     1635
     1636static void json_print_section_header(WriterContext *wctx, void *data)
     1637{
     1638    JSONContext *json = wctx->priv;
     1639    AVBPrint buf;
     1640    const struct section *section = wctx->section[wctx->level];
     1641    const struct section *parent_section = wctx->level ?
     1642        wctx->section[wctx->level-1] : NULL;
     1643
     1644    if (wctx->level && wctx->nb_item[wctx->level-1])
     1645        writer_put_str(wctx, ",\n");
     1646
     1647    if (section->flags & SECTION_FLAG_IS_WRAPPER) {
     1648        writer_put_str(wctx, "{\n");
     1649        json->indent_level++;
     1650    } else {
     1651        av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
     1652        json_escape_str(&buf, section->name, wctx);
     1653        JSON_INDENT();
     1654
     1655        json->indent_level++;
     1656        if (section->flags & SECTION_FLAG_IS_ARRAY) {
     1657            writer_printf(wctx, "\"%s\": [\n", buf.str);
     1658        } else if (parent_section && !(parent_section->flags & SECTION_FLAG_IS_ARRAY)) {
     1659            writer_printf(wctx, "\"%s\": {%s", buf.str, json->item_start_end);
     1660        } else {
     1661            writer_printf(wctx, "{%s", json->item_start_end);
     1662
     1663            /* this is required so the parser can distinguish between packets and frames */
     1664            if (parent_section && parent_section->id == SECTION_ID_PACKETS_AND_FRAMES) {
     1665                if (!json->compact)
     1666                    JSON_INDENT();
     1667                writer_printf(wctx, "\"type\": \"%s\"", section->name);
     1668                wctx->nb_item[wctx->level]++;
     1669            }
     1670        }
     1671        av_bprint_finalize(&buf, NULL);
     1672    }
     1673}
     1674
     1675static void json_print_section_footer(WriterContext *wctx)
     1676{
     1677    JSONContext *json = wctx->priv;
     1678    const struct section *section = wctx->section[wctx->level];
     1679
     1680    if (wctx->level == 0) {
     1681        json->indent_level--;
     1682        writer_put_str(wctx, "\n}\n");
     1683    } else if (section->flags & SECTION_FLAG_IS_ARRAY) {
     1684        writer_w8(wctx, '\n');
     1685        json->indent_level--;
     1686        JSON_INDENT();
     1687        writer_w8(wctx, ']');
     1688    } else {
     1689        writer_put_str(wctx, json->item_start_end);
     1690        json->indent_level--;
     1691        if (!json->compact)
     1692            JSON_INDENT();
     1693        writer_w8(wctx, '}');
     1694    }
     1695}
     1696
     1697static inline void json_print_item_str(WriterContext *wctx,
     1698                                       const char *key, const char *value)
     1699{
     1700    AVBPrint buf;
     1701
     1702    av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
     1703    writer_printf(wctx, "\"%s\":", json_escape_str(&buf, key,   wctx));
     1704    av_bprint_clear(&buf);
     1705    writer_printf(wctx, " \"%s\"", json_escape_str(&buf, value, wctx));
     1706    av_bprint_finalize(&buf, NULL);
     1707}
     1708
     1709static void json_print_str(WriterContext *wctx, const char *key, const char *value)
     1710{
     1711    JSONContext *json = wctx->priv;
     1712    const struct section *parent_section = wctx->level ?
     1713        wctx->section[wctx->level-1] : NULL;
     1714
     1715    if (wctx->nb_item[wctx->level] || (parent_section && parent_section->id == SECTION_ID_PACKETS_AND_FRAMES))
     1716        writer_put_str(wctx, json->item_sep);
     1717    if (!json->compact)
     1718        JSON_INDENT();
     1719    json_print_item_str(wctx, key, value);
     1720}
     1721
     1722static void json_print_int(WriterContext *wctx, const char *key, long long int value)
     1723{
     1724    JSONContext *json = wctx->priv;
     1725    const struct section *parent_section = wctx->level ?
     1726        wctx->section[wctx->level-1] : NULL;
     1727    AVBPrint buf;
     1728
     1729    if (wctx->nb_item[wctx->level] || (parent_section && parent_section->id == SECTION_ID_PACKETS_AND_FRAMES))
     1730        writer_put_str(wctx, json->item_sep);
     1731    if (!json->compact)
     1732        JSON_INDENT();
     1733
     1734    av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
     1735    writer_printf(wctx, "\"%s\": %lld", json_escape_str(&buf, key, wctx), value);
     1736    av_bprint_finalize(&buf, NULL);
     1737}
     1738
     1739static const Writer json_writer = {
     1740    .name                 = "json",
     1741    .priv_size            = sizeof(JSONContext),
     1742    .init                 = json_init,
     1743    .print_section_header = json_print_section_header,
     1744    .print_section_footer = json_print_section_footer,
     1745    .print_integer        = json_print_int,
     1746    .print_string         = json_print_str,
     1747    .flags = WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER,
     1748    .priv_class           = &json_class,
     1749};
     1750
     1751/* XML output */
     1752
     1753typedef struct XMLContext {
     1754    const AVClass *class;
     1755    int within_tag;
     1756    int indent_level;
     1757    int fully_qualified;
     1758    int xsd_strict;
     1759} XMLContext;
     1760
     1761#undef OFFSET
     1762#define OFFSET(x) offsetof(XMLContext, x)
     1763
     1764static const AVOption xml_options[] = {
     1765    {"fully_qualified", "specify if the output should be fully qualified", OFFSET(fully_qualified), AV_OPT_TYPE_BOOL, {.i64=0},  0, 1 },
     1766    {"q",               "specify if the output should be fully qualified", OFFSET(fully_qualified), AV_OPT_TYPE_BOOL, {.i64=0},  0, 1 },
     1767    {"xsd_strict",      "ensure that the output is XSD compliant",         OFFSET(xsd_strict),      AV_OPT_TYPE_BOOL, {.i64=0},  0, 1 },
     1768    {"x",               "ensure that the output is XSD compliant",         OFFSET(xsd_strict),      AV_OPT_TYPE_BOOL, {.i64=0},  0, 1 },
     1769    {NULL},
     1770};
     1771
     1772DEFINE_WRITER_CLASS(xml);
     1773
     1774static av_cold int xml_init(WriterContext *wctx)
     1775{
     1776    XMLContext *xml = wctx->priv;
     1777
     1778    if (xml->xsd_strict) {
     1779        xml->fully_qualified = 1;
     1780#define CHECK_COMPLIANCE(opt, opt_name)                                 \
     1781        if (opt) {                                                      \
     1782            av_log(wctx, AV_LOG_ERROR,                                  \
     1783                   "XSD-compliant output selected but option '%s' was selected, XML output may be non-compliant.\n" \
     1784                   "You need to disable such option with '-no%s'\n", opt_name, opt_name); \
     1785            return AVERROR(EINVAL);                                     \
     1786        }
     1787        CHECK_COMPLIANCE(show_private_data, "private");
     1788        CHECK_COMPLIANCE(show_value_unit,   "unit");
     1789        CHECK_COMPLIANCE(use_value_prefix,  "prefix");
     1790    }
     1791
     1792    return 0;
     1793}
     1794
     1795#define XML_INDENT() writer_printf(wctx, "%*c", xml->indent_level * 4, ' ')
     1796
     1797static void xml_print_section_header(WriterContext *wctx, void *data)
     1798{
     1799    XMLContext *xml = wctx->priv;
     1800    const struct section *section = wctx->section[wctx->level];
     1801    const struct section *parent_section = wctx->level ?
     1802        wctx->section[wctx->level-1] : NULL;
     1803
     1804    if (wctx->level == 0) {
     1805        const char *qual = " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
     1806            "xmlns:ffprobe=\"http://www.ffmpeg.org/schema/ffprobe\" "
     1807            "xsi:schemaLocation=\"http://www.ffmpeg.org/schema/ffprobe ffprobe.xsd\"";
     1808
     1809        writer_put_str(wctx, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
     1810        writer_printf(wctx, "<%sffprobe%s>\n",
     1811               xml->fully_qualified ? "ffprobe:" : "",
     1812               xml->fully_qualified ? qual : "");
     1813        return;
     1814    }
     1815
     1816    if (xml->within_tag) {
     1817        xml->within_tag = 0;
     1818        writer_put_str(wctx, ">\n");
     1819    }
     1820
     1821    if (parent_section && (parent_section->flags & SECTION_FLAG_IS_WRAPPER) &&
     1822        wctx->level && wctx->nb_item[wctx->level-1])
     1823        writer_w8(wctx, '\n');
     1824    xml->indent_level++;
     1825
     1826    if (section->flags & (SECTION_FLAG_IS_ARRAY|SECTION_FLAG_HAS_VARIABLE_FIELDS)) {
     1827        XML_INDENT(); writer_printf(wctx, "<%s", section->name);
     1828
     1829        if (section->flags & SECTION_FLAG_HAS_TYPE) {
     1830            AVBPrint buf;
     1831            av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
     1832            av_bprint_escape(&buf, section->get_type(data), NULL,
     1833                             AV_ESCAPE_MODE_XML, AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES);
     1834            writer_printf(wctx, " type=\"%s\"", buf.str);
     1835        }
     1836        writer_printf(wctx, ">\n", section->name);
     1837    } else {
     1838        XML_INDENT(); writer_printf(wctx, "<%s ", section->name);
     1839        xml->within_tag = 1;
     1840    }
     1841}
     1842
     1843static void xml_print_section_footer(WriterContext *wctx)
     1844{
     1845    XMLContext *xml = wctx->priv;
     1846    const struct section *section = wctx->section[wctx->level];
     1847
     1848    if (wctx->level == 0) {
     1849        writer_printf(wctx, "</%sffprobe>\n", xml->fully_qualified ? "ffprobe:" : "");
     1850    } else if (xml->within_tag) {
     1851        xml->within_tag = 0;
     1852        writer_put_str(wctx, "/>\n");
     1853        xml->indent_level--;
     1854    } else {
     1855        XML_INDENT(); writer_printf(wctx, "</%s>\n", section->name);
     1856        xml->indent_level--;
     1857    }
     1858}
     1859
     1860static void xml_print_value(WriterContext *wctx, const char *key, const void *value, const int is_int)
     1861{
     1862    AVBPrint buf;
     1863    XMLContext *xml = wctx->priv;
     1864    const struct section *section = wctx->section[wctx->level];
     1865
     1866    av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
     1867
     1868    if (section->flags & SECTION_FLAG_HAS_VARIABLE_FIELDS) {
     1869        xml->indent_level++;
     1870        XML_INDENT();
     1871        av_bprint_escape(&buf, key, NULL,
     1872                         AV_ESCAPE_MODE_XML, AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES);
     1873        writer_printf(wctx, "<%s key=\"%s\"",
     1874                      section->element_name, buf.str);
     1875        av_bprint_clear(&buf);
     1876
     1877        if (is_int) {
     1878            writer_printf(wctx, " value=\"%lld\"/>\n", *(long long int *)value);
     1879        } else {
     1880            av_bprint_escape(&buf, (const char *)value, NULL,
     1881                             AV_ESCAPE_MODE_XML, AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES);
     1882            writer_printf(wctx, " value=\"%s\"/>\n", buf.str);
     1883        }
     1884        xml->indent_level--;
     1885    } else {
     1886        if (wctx->nb_item[wctx->level])
     1887            writer_w8(wctx, ' ');
     1888
     1889        if (is_int) {
     1890            writer_printf(wctx, "%s=\"%lld\"", key, *(long long int *)value);
     1891        } else {
     1892            av_bprint_escape(&buf, (const char *)value, NULL,
     1893                             AV_ESCAPE_MODE_XML, AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES);
     1894            writer_printf(wctx, "%s=\"%s\"", key, buf.str);
     1895        }
     1896    }
     1897
     1898    av_bprint_finalize(&buf, NULL);
     1899}
     1900
     1901static inline void xml_print_str(WriterContext *wctx, const char *key, const char *value) {
     1902    xml_print_value(wctx, key, (const void *)value, 0);
     1903}
     1904
     1905static inline void xml_print_int(WriterContext *wctx, const char *key, long long int value) {
     1906    xml_print_value(wctx, key, (const void *)&value, 1);
     1907}
     1908
     1909static Writer xml_writer = {
     1910    .name                 = "xml",
     1911    .priv_size            = sizeof(XMLContext),
     1912    .init                 = xml_init,
     1913    .print_section_header = xml_print_section_header,
     1914    .print_section_footer = xml_print_section_footer,
     1915    .print_integer        = xml_print_int,
     1916    .print_string         = xml_print_str,
     1917    .flags = WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER,
     1918    .priv_class           = &xml_class,
     1919};
     1920
     1921static void writer_register_all(void)
     1922{
     1923    static int initialized;
     1924
     1925    if (initialized)
     1926        return;
     1927    initialized = 1;
     1928
     1929    writer_register(&default_writer);
     1930    writer_register(&compact_writer);
     1931    writer_register(&csv_writer);
     1932    writer_register(&flat_writer);
     1933    writer_register(&ini_writer);
     1934    writer_register(&json_writer);
     1935    writer_register(&xml_writer);
     1936}
     1937
     1938#define print_fmt(k, f, ...) do {              \
     1939    av_bprint_clear(&pbuf);                    \
     1940    av_bprintf(&pbuf, f, __VA_ARGS__);         \
     1941    writer_print_string(w, k, pbuf.str, 0);    \
     1942} while (0)
     1943
     1944#define print_list_fmt(k, f, n, m, ...) do {    \
     1945    av_bprint_clear(&pbuf);                     \
     1946    for (int idx = 0; idx < n; idx++) {         \
     1947        for (int idx2 = 0; idx2 < m; idx2++) {  \
     1948            if (idx > 0 || idx2 > 0)            \
     1949                av_bprint_chars(&pbuf, ' ', 1); \
     1950            av_bprintf(&pbuf, f, __VA_ARGS__);  \
     1951        }                                       \
     1952    }                                           \
     1953    writer_print_string(w, k, pbuf.str, 0);     \
     1954} while (0)
     1955
     1956#define print_int(k, v)         writer_print_integer(w, k, v)
     1957#define print_q(k, v, s)        writer_print_rational(w, k, v, s)
     1958#define print_str(k, v)         writer_print_string(w, k, v, 0)
     1959#define print_str_opt(k, v)     writer_print_string(w, k, v, PRINT_STRING_OPT)
     1960#define print_str_validate(k, v) writer_print_string(w, k, v, PRINT_STRING_VALIDATE)
     1961#define print_time(k, v, tb)    writer_print_time(w, k, v, tb, 0)
     1962#define print_ts(k, v)          writer_print_ts(w, k, v, 0)
     1963#define print_duration_time(k, v, tb) writer_print_time(w, k, v, tb, 1)
     1964#define print_duration_ts(k, v)       writer_print_ts(w, k, v, 1)
     1965#define print_val(k, v, u) do {                                     \
     1966    struct unit_value uv;                                           \
     1967    uv.val.i = v;                                                   \
     1968    uv.unit = u;                                                    \
     1969    writer_print_string(w, k, value_string(val_str, sizeof(val_str), uv), 0); \
     1970} while (0)
     1971
     1972#define print_section_header(s) writer_print_section_header(w, NULL, s)
     1973#define print_section_header_data(s, d) writer_print_section_header(w, d, s)
     1974#define print_section_footer(s) writer_print_section_footer(w, s)
     1975
     1976#define REALLOCZ_ARRAY_STREAM(ptr, cur_n, new_n)                        \
     1977{                                                                       \
     1978    ret = av_reallocp_array(&(ptr), (new_n), sizeof(*(ptr)));           \
     1979    if (ret < 0)                                                        \
     1980        goto end;                                                       \
     1981    memset( (ptr) + (cur_n), 0, ((new_n) - (cur_n)) * sizeof(*(ptr)) ); \
     1982}
     1983
     1984static inline int show_tags(WriterContext *w, AVDictionary *tags, int section_id)
     1985{
     1986    const AVDictionaryEntry *tag = NULL;
     1987    int ret = 0;
     1988
     1989    if (!tags)
     1990        return 0;
     1991    writer_print_section_header(w, NULL, section_id);
     1992
     1993    while ((tag = av_dict_iterate(tags, tag))) {
     1994        if ((ret = print_str_validate(tag->key, tag->value)) < 0)
     1995            break;
     1996    }
     1997    writer_print_section_footer(w);
     1998
     1999    return ret;
     2000}
     2001
     2002static void print_dovi_metadata(WriterContext *w, const AVDOVIMetadata *dovi)
     2003{
     2004    if (!dovi)
     2005        return;
     2006
     2007    {
     2008        const AVDOVIRpuDataHeader *hdr     = av_dovi_get_header(dovi);
     2009        const AVDOVIDataMapping   *mapping = av_dovi_get_mapping(dovi);
     2010        const AVDOVIColorMetadata *color   = av_dovi_get_color(dovi);
     2011        AVBPrint pbuf;
     2012
     2013        av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
     2014
     2015        // header
     2016        print_int("rpu_type",        hdr->rpu_type);
     2017        print_int("rpu_format",      hdr->rpu_format);
     2018        print_int("vdr_rpu_profile", hdr->vdr_rpu_profile);
     2019        print_int("vdr_rpu_level",   hdr->vdr_rpu_level);
     2020        print_int("chroma_resampling_explicit_filter_flag",
     2021                  hdr->chroma_resampling_explicit_filter_flag);
     2022        print_int("coef_data_type",           hdr->coef_data_type);
     2023        print_int("coef_log2_denom",          hdr->coef_log2_denom);
     2024        print_int("vdr_rpu_normalized_idc",   hdr->vdr_rpu_normalized_idc);
     2025        print_int("bl_video_full_range_flag", hdr->bl_video_full_range_flag);
     2026        print_int("bl_bit_depth",             hdr->bl_bit_depth);
     2027        print_int("el_bit_depth",             hdr->el_bit_depth);
     2028        print_int("vdr_bit_depth",            hdr->vdr_bit_depth);
     2029        print_int("spatial_resampling_filter_flag",
     2030                  hdr->spatial_resampling_filter_flag);
     2031        print_int("el_spatial_resampling_filter_flag",
     2032                  hdr->el_spatial_resampling_filter_flag);
     2033        print_int("disable_residual_flag",     hdr->disable_residual_flag);
     2034
     2035        // data mapping values
     2036        print_int("vdr_rpu_id",                mapping->vdr_rpu_id);
     2037        print_int("mapping_color_space",       mapping->mapping_color_space);
     2038        print_int("mapping_chroma_format_idc",
     2039                  mapping->mapping_chroma_format_idc);
     2040
     2041        print_int("nlq_method_idc",            mapping->nlq_method_idc);
     2042        switch (mapping->nlq_method_idc) {
     2043        case AV_DOVI_NLQ_NONE:
     2044            print_str("nlq_method_idc_name", "none");
     2045            break;
     2046        case AV_DOVI_NLQ_LINEAR_DZ:
     2047            print_str("nlq_method_idc_name", "linear_dz");
     2048            break;
     2049        default:
     2050            print_str("nlq_method_idc_name", "unknown");
     2051            break;
     2052        }
     2053
     2054        print_int("num_x_partitions",          mapping->num_x_partitions);
     2055        print_int("num_y_partitions",          mapping->num_y_partitions);
     2056
     2057        writer_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_COMPONENT_LIST);
     2058
     2059        for (int c = 0; c < 3; c++) {
     2060            const AVDOVIReshapingCurve *curve = &mapping->curves[c];
     2061            writer_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_COMPONENT);
     2062
     2063            print_list_fmt("pivots", "%"PRIu16, curve->num_pivots, 1, curve->pivots[idx]);
     2064
     2065            writer_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST);
     2066            for (int i = 0; i < curve->num_pivots - 1; i++) {
     2067
     2068                writer_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_PIECE);
     2069                print_int("mapping_idc", curve->mapping_idc[i]);
     2070                switch (curve->mapping_idc[i]) {
     2071                case AV_DOVI_MAPPING_POLYNOMIAL:
     2072                    print_str("mapping_idc_name",   "polynomial");
     2073                    print_int("poly_order",         curve->poly_order[i]);
     2074                    print_list_fmt("poly_coef", "%"PRIi64,
     2075                                   curve->poly_order[i] + 1, 1,
     2076                                   curve->poly_coef[i][idx]);
     2077                    break;
     2078                case AV_DOVI_MAPPING_MMR:
     2079                    print_str("mapping_idc_name",   "mmr");
     2080                    print_int("mmr_order",          curve->mmr_order[i]);
     2081                    print_int("mmr_constant",       curve->mmr_constant[i]);
     2082                    print_list_fmt("mmr_coef", "%"PRIi64,
     2083                                   curve->mmr_order[i], 7,
     2084                                   curve->mmr_coef[i][idx][idx2]);
     2085                    break;
     2086                default:
     2087                    print_str("mapping_idc_name",   "unknown");
     2088                    break;
     2089                }
     2090
     2091                // SECTION_ID_FRAME_SIDE_DATA_PIECE
     2092                writer_print_section_footer(w);
     2093            }
     2094
     2095            // SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST
     2096            writer_print_section_footer(w);
     2097
     2098            if (mapping->nlq_method_idc != AV_DOVI_NLQ_NONE) {
     2099                const AVDOVINLQParams *nlq  = &mapping->nlq[c];
     2100                print_int("nlq_offset", nlq->nlq_offset);
     2101                print_int("vdr_in_max", nlq->vdr_in_max);
     2102
     2103                switch (mapping->nlq_method_idc) {
     2104                case AV_DOVI_NLQ_LINEAR_DZ:
     2105                    print_int("linear_deadzone_slope",      nlq->linear_deadzone_slope);
     2106                    print_int("linear_deadzone_threshold",  nlq->linear_deadzone_threshold);
     2107                    break;
     2108                }
     2109            }
     2110
     2111            // SECTION_ID_FRAME_SIDE_DATA_COMPONENT
     2112            writer_print_section_footer(w);
     2113        }
     2114
     2115        // SECTION_ID_FRAME_SIDE_DATA_COMPONENT_LIST
     2116        writer_print_section_footer(w);
     2117
     2118        // color metadata
     2119        print_int("dm_metadata_id",         color->dm_metadata_id);
     2120        print_int("scene_refresh_flag",     color->scene_refresh_flag);
     2121        print_list_fmt("ycc_to_rgb_matrix", "%d/%d",
     2122                       FF_ARRAY_ELEMS(color->ycc_to_rgb_matrix), 1,
     2123                       color->ycc_to_rgb_matrix[idx].num,
     2124                       color->ycc_to_rgb_matrix[idx].den);
     2125        print_list_fmt("ycc_to_rgb_offset", "%d/%d",
     2126                       FF_ARRAY_ELEMS(color->ycc_to_rgb_offset), 1,
     2127                       color->ycc_to_rgb_offset[idx].num,
     2128                       color->ycc_to_rgb_offset[idx].den);
     2129        print_list_fmt("rgb_to_lms_matrix", "%d/%d",
     2130                       FF_ARRAY_ELEMS(color->rgb_to_lms_matrix), 1,
     2131                       color->rgb_to_lms_matrix[idx].num,
     2132                       color->rgb_to_lms_matrix[idx].den);
     2133        print_int("signal_eotf",            color->signal_eotf);
     2134        print_int("signal_eotf_param0",     color->signal_eotf_param0);
     2135        print_int("signal_eotf_param1",     color->signal_eotf_param1);
     2136        print_int("signal_eotf_param2",     color->signal_eotf_param2);
     2137        print_int("signal_bit_depth",       color->signal_bit_depth);
     2138        print_int("signal_color_space",     color->signal_color_space);
     2139        print_int("signal_chroma_format",   color->signal_chroma_format);
     2140        print_int("signal_full_range_flag", color->signal_full_range_flag);
     2141        print_int("source_min_pq",          color->source_min_pq);
     2142        print_int("source_max_pq",          color->source_max_pq);
     2143        print_int("source_diagonal",        color->source_diagonal);
     2144
     2145        av_bprint_finalize(&pbuf, NULL);
     2146    }
     2147}
     2148
     2149static void print_dynamic_hdr10_plus(WriterContext *w, const AVDynamicHDRPlus *metadata)
     2150{
     2151    if (!metadata)
     2152        return;
     2153    print_int("application version", metadata->application_version);
     2154    print_int("num_windows", metadata->num_windows);
     2155    for (int n = 1; n < metadata->num_windows; n++) {
     2156        const AVHDRPlusColorTransformParams *params = &metadata->params[n];
     2157        print_q("window_upper_left_corner_x",
     2158                params->window_upper_left_corner_x,'/');
     2159        print_q("window_upper_left_corner_y",
     2160                params->window_upper_left_corner_y,'/');
     2161        print_q("window_lower_right_corner_x",
     2162                params->window_lower_right_corner_x,'/');
     2163        print_q("window_lower_right_corner_y",
     2164                params->window_lower_right_corner_y,'/');
     2165        print_q("window_upper_left_corner_x",
     2166                params->window_upper_left_corner_x,'/');
     2167        print_q("window_upper_left_corner_y",
     2168                params->window_upper_left_corner_y,'/');
     2169        print_int("center_of_ellipse_x",
     2170                  params->center_of_ellipse_x ) ;
     2171        print_int("center_of_ellipse_y",
     2172                  params->center_of_ellipse_y );
     2173        print_int("rotation_angle",
     2174                  params->rotation_angle);
     2175        print_int("semimajor_axis_internal_ellipse",
     2176                  params->semimajor_axis_internal_ellipse);
     2177        print_int("semimajor_axis_external_ellipse",
     2178                  params->semimajor_axis_external_ellipse);
     2179        print_int("semiminor_axis_external_ellipse",
     2180                  params->semiminor_axis_external_ellipse);
     2181        print_int("overlap_process_option",
     2182                  params->overlap_process_option);
     2183    }
     2184    print_q("targeted_system_display_maximum_luminance",
     2185            metadata->targeted_system_display_maximum_luminance,'/');
     2186    if (metadata->targeted_system_display_actual_peak_luminance_flag) {
     2187        print_int("num_rows_targeted_system_display_actual_peak_luminance",
     2188                  metadata->num_rows_targeted_system_display_actual_peak_luminance);
     2189        print_int("num_cols_targeted_system_display_actual_peak_luminance",
     2190                  metadata->num_cols_targeted_system_display_actual_peak_luminance);
     2191        for (int i = 0; i < metadata->num_rows_targeted_system_display_actual_peak_luminance; i++) {
     2192            for (int j = 0; j < metadata->num_cols_targeted_system_display_actual_peak_luminance; j++) {
     2193                print_q("targeted_system_display_actual_peak_luminance",
     2194                        metadata->targeted_system_display_actual_peak_luminance[i][j],'/');
     2195            }
     2196        }
     2197    }
     2198    for (int n = 0; n < metadata->num_windows; n++) {
     2199        const AVHDRPlusColorTransformParams *params = &metadata->params[n];
     2200        for (int i = 0; i < 3; i++) {
     2201            print_q("maxscl",params->maxscl[i],'/');
     2202        }
     2203        print_q("average_maxrgb",
     2204                params->average_maxrgb,'/');
     2205        print_int("num_distribution_maxrgb_percentiles",
     2206                  params->num_distribution_maxrgb_percentiles);
     2207        for (int i = 0; i < params->num_distribution_maxrgb_percentiles; i++) {
     2208            print_int("distribution_maxrgb_percentage",
     2209                      params->distribution_maxrgb[i].percentage);
     2210            print_q("distribution_maxrgb_percentile",
     2211                    params->distribution_maxrgb[i].percentile,'/');
     2212        }
     2213        print_q("fraction_bright_pixels",
     2214                params->fraction_bright_pixels,'/');
     2215    }
     2216    if (metadata->mastering_display_actual_peak_luminance_flag) {
     2217        print_int("num_rows_mastering_display_actual_peak_luminance",
     2218                  metadata->num_rows_mastering_display_actual_peak_luminance);
     2219        print_int("num_cols_mastering_display_actual_peak_luminance",
     2220                  metadata->num_cols_mastering_display_actual_peak_luminance);
     2221        for (int i = 0; i < metadata->num_rows_mastering_display_actual_peak_luminance; i++) {
     2222            for (int j = 0; j <  metadata->num_cols_mastering_display_actual_peak_luminance; j++) {
     2223                print_q("mastering_display_actual_peak_luminance",
     2224                        metadata->mastering_display_actual_peak_luminance[i][j],'/');
     2225            }
     2226        }
     2227    }
     2228
     2229    for (int n = 0; n < metadata->num_windows; n++) {
     2230        const AVHDRPlusColorTransformParams *params = &metadata->params[n];
     2231        if (params->tone_mapping_flag) {
     2232            print_q("knee_point_x", params->knee_point_x,'/');
     2233            print_q("knee_point_y", params->knee_point_y,'/');
     2234            print_int("num_bezier_curve_anchors",
     2235                      params->num_bezier_curve_anchors );
     2236            for (int i = 0; i < params->num_bezier_curve_anchors; i++) {
     2237                print_q("bezier_curve_anchors",
     2238                        params->bezier_curve_anchors[i],'/');
     2239            }
     2240        }
     2241        if (params->color_saturation_mapping_flag) {
     2242            print_q("color_saturation_weight",
     2243                    params->color_saturation_weight,'/');
     2244        }
     2245    }
     2246}
     2247
     2248static void print_dynamic_hdr_vivid(WriterContext *w, const AVDynamicHDRVivid *metadata)
     2249{
     2250    if (!metadata)
     2251        return;
     2252    print_int("system_start_code", metadata->system_start_code);
     2253    print_int("num_windows", metadata->num_windows);
     2254
     2255    for (int n = 0; n < metadata->num_windows; n++) {
     2256        const AVHDRVividColorTransformParams *params = &metadata->params[n];
     2257
     2258        print_q("minimum_maxrgb", params->minimum_maxrgb, '/');
     2259        print_q("average_maxrgb", params->average_maxrgb, '/');
     2260        print_q("variance_maxrgb", params->variance_maxrgb, '/');
     2261        print_q("maximum_maxrgb", params->maximum_maxrgb, '/');
     2262    }
     2263
     2264    for (int n = 0; n < metadata->num_windows; n++) {
     2265        const AVHDRVividColorTransformParams *params = &metadata->params[n];
     2266
     2267        print_int("tone_mapping_mode_flag", params->tone_mapping_mode_flag);
     2268        if (params->tone_mapping_mode_flag) {
     2269            print_int("tone_mapping_param_num", params->tone_mapping_param_num);
     2270            for (int i = 0; i < params->tone_mapping_param_num; i++) {
     2271                const AVHDRVividColorToneMappingParams *tm_params = &params->tm_params[i];
     2272
     2273                print_q("targeted_system_display_maximum_luminance",
     2274                        tm_params->targeted_system_display_maximum_luminance, '/');
     2275                print_int("base_enable_flag", tm_params->base_enable_flag);
     2276                if (tm_params->base_enable_flag) {
     2277                    print_q("base_param_m_p", tm_params->base_param_m_p, '/');
     2278                    print_q("base_param_m_m", tm_params->base_param_m_m, '/');
     2279                    print_q("base_param_m_a", tm_params->base_param_m_a, '/');
     2280                    print_q("base_param_m_b", tm_params->base_param_m_b, '/');
     2281                    print_q("base_param_m_n", tm_params->base_param_m_n, '/');
     2282
     2283                    print_int("base_param_k1", tm_params->base_param_k1);
     2284                    print_int("base_param_k2", tm_params->base_param_k2);
     2285                    print_int("base_param_k3", tm_params->base_param_k3);
     2286                    print_int("base_param_Delta_enable_mode",
     2287                              tm_params->base_param_Delta_enable_mode);
     2288                    print_q("base_param_Delta", tm_params->base_param_Delta, '/');
     2289                }
     2290                print_int("3Spline_enable_flag", tm_params->three_Spline_enable_flag);
     2291                if (tm_params->three_Spline_enable_flag) {
     2292                    print_int("3Spline_num", tm_params->three_Spline_num);
     2293
     2294                    for (int j = 0; j < tm_params->three_Spline_num; j++) {
     2295                        const AVHDRVivid3SplineParams *three_spline = &tm_params->three_spline[j];
     2296                        print_int("3Spline_TH_mode", three_spline->th_mode);
     2297                        if (three_spline->th_mode == 0 || three_spline->th_mode == 2)
     2298                            print_q("3Spline_TH_enable_MB", three_spline->th_enable_mb, '/');
     2299                        print_q("3Spline_TH_enable", three_spline->th_enable, '/');
     2300                        print_q("3Spline_TH_Delta1", three_spline->th_delta1, '/');
     2301                        print_q("3Spline_TH_Delta2", three_spline->th_delta2, '/');
     2302                        print_q("3Spline_enable_Strength", three_spline->enable_strength, '/');
     2303                    }
     2304                }
     2305            }
     2306        }
     2307
     2308        print_int("color_saturation_mapping_flag", params->color_saturation_mapping_flag);
     2309        if (params->color_saturation_mapping_flag) {
     2310            print_int("color_saturation_num", params->color_saturation_num);
     2311            for (int i = 0; i < params->color_saturation_num; i++) {
     2312                print_q("color_saturation_gain", params->color_saturation_gain[i], '/');
     2313            }
     2314        }
     2315    }
     2316}
     2317
     2318static void print_ambient_viewing_environment(WriterContext *w,
     2319                                              const AVAmbientViewingEnvironment *env)
     2320{
     2321    if (!env)
     2322        return;
     2323
     2324    print_q("ambient_illuminance", env->ambient_illuminance, '/');
     2325    print_q("ambient_light_x",     env->ambient_light_x,     '/');
     2326    print_q("ambient_light_y",     env->ambient_light_y,     '/');
     2327}
     2328
     2329static void print_pkt_side_data(WriterContext *w,
     2330                                AVCodecParameters *par,
     2331                                const AVPacketSideData *sd,
     2332                                SectionID id_data)
     2333{
     2334        const char *name = av_packet_side_data_name(sd->type);
     2335
     2336        writer_print_section_header(w, (void *)sd, id_data);
     2337        print_str("side_data_type", name ? name : "unknown");
     2338        if (sd->type == AV_PKT_DATA_DISPLAYMATRIX && sd->size >= 9*4) {
     2339            double rotation = av_display_rotation_get((int32_t *)sd->data);
     2340            if (isnan(rotation))
     2341                rotation = 0;
     2342            writer_print_integers(w, "displaymatrix", sd->data, 9, " %11d", 3, 4, 1);
     2343            print_int("rotation", rotation);
     2344        } else if (sd->type == AV_PKT_DATA_STEREO3D) {
     2345            const AVStereo3D *stereo = (AVStereo3D *)sd->data;
     2346            print_str("type", av_stereo3d_type_name(stereo->type));
     2347            print_int("inverted", !!(stereo->flags & AV_STEREO3D_FLAG_INVERT));
     2348        } else if (sd->type == AV_PKT_DATA_SPHERICAL) {
     2349            const AVSphericalMapping *spherical = (AVSphericalMapping *)sd->data;
     2350            print_str("projection", av_spherical_projection_name(spherical->projection));
     2351            if (spherical->projection == AV_SPHERICAL_CUBEMAP) {
     2352                print_int("padding", spherical->padding);
     2353            } else if (spherical->projection == AV_SPHERICAL_EQUIRECTANGULAR_TILE) {
     2354                size_t l, t, r, b;
     2355                av_spherical_tile_bounds(spherical, par->width, par->height,
     2356                                         &l, &t, &r, &b);
     2357                print_int("bound_left", l);
     2358                print_int("bound_top", t);
     2359                print_int("bound_right", r);
     2360                print_int("bound_bottom", b);
     2361            }
     2362
     2363            print_int("yaw", (double) spherical->yaw / (1 << 16));
     2364            print_int("pitch", (double) spherical->pitch / (1 << 16));
     2365            print_int("roll", (double) spherical->roll / (1 << 16));
     2366        } else if (sd->type == AV_PKT_DATA_SKIP_SAMPLES && sd->size == 10) {
     2367            print_int("skip_samples",    AV_RL32(sd->data));
     2368            print_int("discard_padding", AV_RL32(sd->data + 4));
     2369            print_int("skip_reason",     AV_RL8(sd->data + 8));
     2370            print_int("discard_reason",  AV_RL8(sd->data + 9));
     2371        } else if (sd->type == AV_PKT_DATA_MASTERING_DISPLAY_METADATA) {
     2372            AVMasteringDisplayMetadata *metadata = (AVMasteringDisplayMetadata *)sd->data;
     2373
     2374            if (metadata->has_primaries) {
     2375                print_q("red_x", metadata->display_primaries[0][0], '/');
     2376                print_q("red_y", metadata->display_primaries[0][1], '/');
     2377                print_q("green_x", metadata->display_primaries[1][0], '/');
     2378                print_q("green_y", metadata->display_primaries[1][1], '/');
     2379                print_q("blue_x", metadata->display_primaries[2][0], '/');
     2380                print_q("blue_y", metadata->display_primaries[2][1], '/');
     2381
     2382                print_q("white_point_x", metadata->white_point[0], '/');
     2383                print_q("white_point_y", metadata->white_point[1], '/');
     2384            }
     2385
     2386            if (metadata->has_luminance) {
     2387                print_q("min_luminance", metadata->min_luminance, '/');
     2388                print_q("max_luminance", metadata->max_luminance, '/');
     2389            }
     2390        } else if (sd->type == AV_PKT_DATA_CONTENT_LIGHT_LEVEL) {
     2391            AVContentLightMetadata *metadata = (AVContentLightMetadata *)sd->data;
     2392            print_int("max_content", metadata->MaxCLL);
     2393            print_int("max_average", metadata->MaxFALL);
     2394        } else if (sd->type == AV_PKT_DATA_DYNAMIC_HDR10_PLUS) {
     2395            AVDynamicHDRPlus *metadata = (AVDynamicHDRPlus *)sd->data;
     2396            print_dynamic_hdr10_plus(w, metadata);
     2397        } else if (sd->type == AV_PKT_DATA_DOVI_CONF) {
     2398            AVDOVIDecoderConfigurationRecord *dovi = (AVDOVIDecoderConfigurationRecord *)sd->data;
     2399            print_int("dv_version_major", dovi->dv_version_major);
     2400            print_int("dv_version_minor", dovi->dv_version_minor);
     2401            print_int("dv_profile", dovi->dv_profile);
     2402            print_int("dv_level", dovi->dv_level);
     2403            print_int("rpu_present_flag", dovi->rpu_present_flag);
     2404            print_int("el_present_flag", dovi->el_present_flag);
     2405            print_int("bl_present_flag", dovi->bl_present_flag);
     2406            print_int("dv_bl_signal_compatibility_id", dovi->dv_bl_signal_compatibility_id);
     2407        } else if (sd->type == AV_PKT_DATA_AUDIO_SERVICE_TYPE) {
     2408            enum AVAudioServiceType *t = (enum AVAudioServiceType *)sd->data;
     2409            print_int("service_type", *t);
     2410        } else if (sd->type == AV_PKT_DATA_MPEGTS_STREAM_ID) {
     2411            print_int("id", *sd->data);
     2412        } else if (sd->type == AV_PKT_DATA_CPB_PROPERTIES) {
     2413            const AVCPBProperties *prop = (AVCPBProperties *)sd->data;
     2414            print_int("max_bitrate", prop->max_bitrate);
     2415            print_int("min_bitrate", prop->min_bitrate);
     2416            print_int("avg_bitrate", prop->avg_bitrate);
     2417            print_int("buffer_size", prop->buffer_size);
     2418            print_int("vbv_delay",   prop->vbv_delay);
     2419        } else if (sd->type == AV_PKT_DATA_WEBVTT_IDENTIFIER ||
     2420                   sd->type == AV_PKT_DATA_WEBVTT_SETTINGS) {
     2421            if (do_show_data)
     2422                writer_print_data(w, "data", sd->data, sd->size);
     2423            writer_print_data_hash(w, "data_hash", sd->data, sd->size);
     2424        } else if (sd->type == AV_PKT_DATA_AFD && sd->size > 0) {
     2425            print_int("active_format", *sd->data);
     2426        }
     2427}
     2428
     2429static void print_private_data(WriterContext *w, void *priv_data)
     2430{
     2431    const AVOption *opt = NULL;
     2432    while (opt = av_opt_next(priv_data, opt)) {
     2433        uint8_t *str;
     2434        if (!(opt->flags & AV_OPT_FLAG_EXPORT)) continue;
     2435        if (av_opt_get(priv_data, opt->name, 0, &str) >= 0) {
     2436            print_str(opt->name, str);
     2437            av_free(str);
     2438        }
     2439    }
     2440}
     2441
     2442static void print_color_range(WriterContext *w, enum AVColorRange color_range)
     2443{
     2444    const char *val = av_color_range_name(color_range);
     2445    if (!val || color_range == AVCOL_RANGE_UNSPECIFIED) {
     2446        print_str_opt("color_range", "unknown");
     2447    } else {
     2448        print_str("color_range", val);
     2449    }
     2450}
     2451
     2452static void print_color_space(WriterContext *w, enum AVColorSpace color_space)
     2453{
     2454    const char *val = av_color_space_name(color_space);
     2455    if (!val || color_space == AVCOL_SPC_UNSPECIFIED) {
     2456        print_str_opt("color_space", "unknown");
     2457    } else {
     2458        print_str("color_space", val);
     2459    }
     2460}
     2461
     2462static void print_primaries(WriterContext *w, enum AVColorPrimaries color_primaries)
     2463{
     2464    const char *val = av_color_primaries_name(color_primaries);
     2465    if (!val || color_primaries == AVCOL_PRI_UNSPECIFIED) {
     2466        print_str_opt("color_primaries", "unknown");
     2467    } else {
     2468        print_str("color_primaries", val);
     2469    }
     2470}
     2471
     2472static void print_color_trc(WriterContext *w, enum AVColorTransferCharacteristic color_trc)
     2473{
     2474    const char *val = av_color_transfer_name(color_trc);
     2475    if (!val || color_trc == AVCOL_TRC_UNSPECIFIED) {
     2476        print_str_opt("color_transfer", "unknown");
     2477    } else {
     2478        print_str("color_transfer", val);
     2479    }
     2480}
     2481
     2482static void print_chroma_location(WriterContext *w, enum AVChromaLocation chroma_location)
     2483{
     2484    const char *val = av_chroma_location_name(chroma_location);
     2485    if (!val || chroma_location == AVCHROMA_LOC_UNSPECIFIED) {
     2486        print_str_opt("chroma_location", "unspecified");
     2487    } else {
     2488        print_str("chroma_location", val);
     2489    }
     2490}
     2491
     2492static void clear_log(int need_lock)
     2493{
     2494    int i;
     2495
     2496    if (need_lock)
     2497        pthread_mutex_lock(&log_mutex);
     2498    for (i=0; i<log_buffer_size; i++) {
     2499        av_freep(&log_buffer[i].context_name);
     2500        av_freep(&log_buffer[i].parent_name);
     2501        av_freep(&log_buffer[i].log_message);
     2502    }
     2503    log_buffer_size = 0;
     2504    if(need_lock)
     2505        pthread_mutex_unlock(&log_mutex);
     2506}
     2507
     2508static int show_log(WriterContext *w, int section_ids, int section_id, int log_level)
     2509{
     2510    int i;
     2511    pthread_mutex_lock(&log_mutex);
     2512    if (!log_buffer_size) {
     2513        pthread_mutex_unlock(&log_mutex);
     2514        return 0;
     2515    }
     2516    writer_print_section_header(w, NULL, section_ids);
     2517
     2518    for (i=0; i<log_buffer_size; i++) {
     2519        if (log_buffer[i].log_level <= log_level) {
     2520            writer_print_section_header(w, NULL, section_id);
     2521            print_str("context", log_buffer[i].context_name);
     2522            print_int("level", log_buffer[i].log_level);
     2523            print_int("category", log_buffer[i].category);
     2524            if (log_buffer[i].parent_name) {
     2525                print_str("parent_context", log_buffer[i].parent_name);
     2526                print_int("parent_category", log_buffer[i].parent_category);
     2527            } else {
     2528                print_str_opt("parent_context", "N/A");
     2529                print_str_opt("parent_category", "N/A");
     2530            }
     2531            print_str("message", log_buffer[i].log_message);
     2532            writer_print_section_footer(w);
     2533        }
     2534    }
     2535    clear_log(0);
     2536    pthread_mutex_unlock(&log_mutex);
     2537
     2538    writer_print_section_footer(w);
     2539
     2540    return 0;
     2541}
     2542
     2543static void show_packet(WriterContext *w, InputFile *ifile, AVPacket *pkt, int packet_idx)
     2544{
     2545    char val_str[128];
     2546    AVStream *st = ifile->streams[pkt->stream_index].st;
     2547    AVBPrint pbuf;
     2548    const char *s;
     2549
     2550    av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
     2551
     2552    writer_print_section_header(w, NULL, SECTION_ID_PACKET);
     2553
     2554    s = av_get_media_type_string(st->codecpar->codec_type);
     2555    if (s) print_str    ("codec_type", s);
     2556    else   print_str_opt("codec_type", "unknown");
     2557    print_int("stream_index",     pkt->stream_index);
     2558    print_ts  ("pts",             pkt->pts);
     2559    print_time("pts_time",        pkt->pts, &st->time_base);
     2560    print_ts  ("dts",             pkt->dts);
     2561    print_time("dts_time",        pkt->dts, &st->time_base);
     2562    print_duration_ts("duration",        pkt->duration);
     2563    print_duration_time("duration_time", pkt->duration, &st->time_base);
     2564    print_val("size",             pkt->size, unit_byte_str);
     2565    if (pkt->pos != -1) print_fmt    ("pos", "%"PRId64, pkt->pos);
     2566    else                print_str_opt("pos", "N/A");
     2567    print_fmt("flags", "%c%c%c",      pkt->flags & AV_PKT_FLAG_KEY ? 'K' : '_',
     2568              pkt->flags & AV_PKT_FLAG_DISCARD ? 'D' : '_',
     2569              pkt->flags & AV_PKT_FLAG_CORRUPT ? 'C' : '_');
     2570    if (do_show_data)
     2571        writer_print_data(w, "data", pkt->data, pkt->size);
     2572    writer_print_data_hash(w, "data_hash", pkt->data, pkt->size);
     2573
     2574    if (pkt->side_data_elems) {
     2575        size_t size;
     2576        const uint8_t *side_metadata;
     2577
     2578        side_metadata = av_packet_get_side_data(pkt, AV_PKT_DATA_STRINGS_METADATA, &size);
     2579        if (side_metadata && size && do_show_packet_tags) {
     2580            AVDictionary *dict = NULL;
     2581            if (av_packet_unpack_dictionary(side_metadata, size, &dict) >= 0)
     2582                show_tags(w, dict, SECTION_ID_PACKET_TAGS);
     2583            av_dict_free(&dict);
     2584        }
     2585
     2586        writer_print_section_header(w, NULL, SECTION_ID_PACKET_SIDE_DATA_LIST);
     2587        for (int i = 0; i < pkt->side_data_elems; i++) {
     2588            print_pkt_side_data(w, st->codecpar, &pkt->side_data[i],
     2589                                SECTION_ID_PACKET_SIDE_DATA);
     2590            writer_print_section_footer(w);
     2591        }
     2592        writer_print_section_footer(w);
     2593    }
     2594
     2595    writer_print_section_footer(w);
     2596
     2597    av_bprint_finalize(&pbuf, NULL);
     2598    fflush(stdout);
     2599}
     2600
     2601static void show_subtitle(WriterContext *w, AVSubtitle *sub, AVStream *stream,
     2602                          AVFormatContext *fmt_ctx)
     2603{
     2604    AVBPrint pbuf;
     2605
     2606    av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
     2607
     2608    writer_print_section_header(w, NULL, SECTION_ID_SUBTITLE);
     2609
     2610    print_str ("media_type",         "subtitle");
     2611    print_ts  ("pts",                 sub->pts);
     2612    print_time("pts_time",            sub->pts, &AV_TIME_BASE_Q);
     2613    print_int ("format",              sub->format);
     2614    print_int ("start_display_time",  sub->start_display_time);
     2615    print_int ("end_display_time",    sub->end_display_time);
     2616    print_int ("num_rects",           sub->num_rects);
     2617
     2618    writer_print_section_footer(w);
     2619
     2620    av_bprint_finalize(&pbuf, NULL);
     2621    fflush(stdout);
     2622}
     2623
     2624static void print_frame_side_data(WriterContext *w,
     2625                                  const AVFrame *frame,
     2626                                  const AVStream *stream)
     2627{
     2628    writer_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_LIST);
     2629
     2630    for (int i = 0; i < frame->nb_side_data; i++) {
     2631        const AVFrameSideData *sd = frame->side_data[i];
     2632        const char *name;
     2633
     2634        writer_print_section_header(w, (void *)sd, SECTION_ID_FRAME_SIDE_DATA);
     2635        name = av_frame_side_data_name(sd->type);
     2636        print_str("side_data_type", name ? name : "unknown");
     2637        if (sd->type == AV_FRAME_DATA_DISPLAYMATRIX && sd->size >= 9*4) {
     2638            double rotation = av_display_rotation_get((int32_t *)sd->data);
     2639            if (isnan(rotation))
     2640                rotation = 0;
     2641            writer_print_integers(w, "displaymatrix", sd->data, 9, " %11d", 3, 4, 1);
     2642            print_int("rotation", rotation);
     2643        } else if (sd->type == AV_FRAME_DATA_AFD && sd->size > 0) {
     2644            print_int("active_format", *sd->data);
     2645        } else if (sd->type == AV_FRAME_DATA_GOP_TIMECODE && sd->size >= 8) {
     2646            char tcbuf[AV_TIMECODE_STR_SIZE];
     2647            av_timecode_make_mpeg_tc_string(tcbuf, *(int64_t *)(sd->data));
     2648            print_str("timecode", tcbuf);
     2649        } else if (sd->type == AV_FRAME_DATA_S12M_TIMECODE && sd->size == 16) {
     2650            uint32_t *tc = (uint32_t*)sd->data;
     2651            int m = FFMIN(tc[0],3);
     2652            writer_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_TIMECODE_LIST);
     2653            for (int j = 1; j <= m ; j++) {
     2654                char tcbuf[AV_TIMECODE_STR_SIZE];
     2655                av_timecode_make_smpte_tc_string2(tcbuf, stream->avg_frame_rate, tc[j], 0, 0);
     2656                writer_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_TIMECODE);
     2657                print_str("value", tcbuf);
     2658                writer_print_section_footer(w);
     2659            }
     2660            writer_print_section_footer(w);
     2661        } else if (sd->type == AV_FRAME_DATA_MASTERING_DISPLAY_METADATA) {
     2662            AVMasteringDisplayMetadata *metadata = (AVMasteringDisplayMetadata *)sd->data;
     2663
     2664            if (metadata->has_primaries) {
     2665                print_q("red_x", metadata->display_primaries[0][0], '/');
     2666                print_q("red_y", metadata->display_primaries[0][1], '/');
     2667                print_q("green_x", metadata->display_primaries[1][0], '/');
     2668                print_q("green_y", metadata->display_primaries[1][1], '/');
     2669                print_q("blue_x", metadata->display_primaries[2][0], '/');
     2670                print_q("blue_y", metadata->display_primaries[2][1], '/');
     2671
     2672                print_q("white_point_x", metadata->white_point[0], '/');
     2673                print_q("white_point_y", metadata->white_point[1], '/');
     2674            }
     2675
     2676            if (metadata->has_luminance) {
     2677                print_q("min_luminance", metadata->min_luminance, '/');
     2678                print_q("max_luminance", metadata->max_luminance, '/');
     2679            }
     2680        } else if (sd->type == AV_FRAME_DATA_DYNAMIC_HDR_PLUS) {
     2681            AVDynamicHDRPlus *metadata = (AVDynamicHDRPlus *)sd->data;
     2682            print_dynamic_hdr10_plus(w, metadata);
     2683        } else if (sd->type == AV_FRAME_DATA_CONTENT_LIGHT_LEVEL) {
     2684            AVContentLightMetadata *metadata = (AVContentLightMetadata *)sd->data;
     2685            print_int("max_content", metadata->MaxCLL);
     2686            print_int("max_average", metadata->MaxFALL);
     2687        } else if (sd->type == AV_FRAME_DATA_ICC_PROFILE) {
     2688            const AVDictionaryEntry *tag = av_dict_get(sd->metadata, "name", NULL, AV_DICT_MATCH_CASE);
     2689            if (tag)
     2690                print_str(tag->key, tag->value);
     2691            print_int("size", sd->size);
     2692        } else if (sd->type == AV_FRAME_DATA_DOVI_METADATA) {
     2693            print_dovi_metadata(w, (const AVDOVIMetadata *)sd->data);
     2694        } else if (sd->type == AV_FRAME_DATA_DYNAMIC_HDR_VIVID) {
     2695            AVDynamicHDRVivid *metadata = (AVDynamicHDRVivid *)sd->data;
     2696            print_dynamic_hdr_vivid(w, metadata);
     2697        } else if (sd->type == AV_FRAME_DATA_AMBIENT_VIEWING_ENVIRONMENT) {
     2698            print_ambient_viewing_environment(w, (const AVAmbientViewingEnvironment *)sd->data);
     2699        }
     2700        writer_print_section_footer(w);
     2701    }
     2702    writer_print_section_footer(w);
     2703}
     2704
     2705static void show_frame(WriterContext *w, AVFrame *frame, AVStream *stream,
     2706                       AVFormatContext *fmt_ctx)
     2707{
     2708    FrameData *fd = frame->opaque_ref ? (FrameData*)frame->opaque_ref->data : NULL;
     2709    AVBPrint pbuf;
     2710    char val_str[128];
     2711    const char *s;
     2712
     2713    av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
     2714
     2715    writer_print_section_header(w, NULL, SECTION_ID_FRAME);
     2716
     2717    s = av_get_media_type_string(stream->codecpar->codec_type);
     2718    if (s) print_str    ("media_type", s);
     2719    else   print_str_opt("media_type", "unknown");
     2720    print_int("stream_index",           stream->index);
     2721    print_int("key_frame",           !!(frame->flags & AV_FRAME_FLAG_KEY));
     2722    print_ts  ("pts",                   frame->pts);
     2723    print_time("pts_time",              frame->pts, &stream->time_base);
     2724    print_ts  ("pkt_dts",               frame->pkt_dts);
     2725    print_time("pkt_dts_time",          frame->pkt_dts, &stream->time_base);
     2726    print_ts  ("best_effort_timestamp", frame->best_effort_timestamp);
     2727    print_time("best_effort_timestamp_time", frame->best_effort_timestamp, &stream->time_base);
     2728#if LIBAVUTIL_VERSION_MAJOR < 59
     2729    AV_NOWARN_DEPRECATED(
     2730    print_duration_ts  ("pkt_duration",      frame->pkt_duration);
     2731    print_duration_time("pkt_duration_time", frame->pkt_duration, &stream->time_base);
     2732    )
     2733#endif
     2734    print_duration_ts  ("duration",          frame->duration);
     2735    print_duration_time("duration_time",     frame->duration, &stream->time_base);
     2736    if (fd && fd->pkt_pos != -1)  print_fmt    ("pkt_pos", "%"PRId64, fd->pkt_pos);
     2737    else                          print_str_opt("pkt_pos", "N/A");
     2738    if (fd && fd->pkt_size != -1) print_val    ("pkt_size", fd->pkt_size, unit_byte_str);
     2739    else                          print_str_opt("pkt_size", "N/A");
     2740
     2741    switch (stream->codecpar->codec_type) {
     2742        AVRational sar;
     2743
     2744    case AVMEDIA_TYPE_VIDEO:
     2745        print_int("width",                  frame->width);
     2746        print_int("height",                 frame->height);
     2747        print_int("crop_top",               frame->crop_top);
     2748        print_int("crop_bottom",            frame->crop_bottom);
     2749        print_int("crop_left",              frame->crop_left);
     2750        print_int("crop_right",             frame->crop_right);
     2751        s = av_get_pix_fmt_name(frame->format);
     2752        if (s) print_str    ("pix_fmt", s);
     2753        else   print_str_opt("pix_fmt", "unknown");
     2754        sar = av_guess_sample_aspect_ratio(fmt_ctx, stream, frame);
     2755        if (sar.num) {
     2756            print_q("sample_aspect_ratio", sar, ':');
     2757        } else {
     2758            print_str_opt("sample_aspect_ratio", "N/A");
     2759        }
     2760        print_fmt("pict_type",              "%c", av_get_picture_type_char(frame->pict_type));
     2761#if LIBAVUTIL_VERSION_MAJOR < 59
     2762    AV_NOWARN_DEPRECATED(
     2763        print_int("coded_picture_number",   frame->coded_picture_number);
     2764        print_int("display_picture_number", frame->display_picture_number);
     2765    )
     2766#endif
     2767        print_int("interlaced_frame",       !!(frame->flags & AV_FRAME_FLAG_INTERLACED));
     2768        print_int("top_field_first",        !!(frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST));
     2769        print_int("repeat_pict",            frame->repeat_pict);
     2770
     2771        print_color_range(w, frame->color_range);
     2772        print_color_space(w, frame->colorspace);
     2773        print_primaries(w, frame->color_primaries);
     2774        print_color_trc(w, frame->color_trc);
     2775        print_chroma_location(w, frame->chroma_location);
     2776        break;
     2777
     2778    case AVMEDIA_TYPE_AUDIO:
     2779        s = av_get_sample_fmt_name(frame->format);
     2780        if (s) print_str    ("sample_fmt", s);
     2781        else   print_str_opt("sample_fmt", "unknown");
     2782        print_int("nb_samples",         frame->nb_samples);
     2783        print_int("channels", frame->ch_layout.nb_channels);
     2784        if (frame->ch_layout.order != AV_CHANNEL_ORDER_UNSPEC) {
     2785            av_channel_layout_describe(&frame->ch_layout, val_str, sizeof(val_str));
     2786            print_str    ("channel_layout", val_str);
     2787        } else
     2788            print_str_opt("channel_layout", "unknown");
     2789        break;
     2790    }
     2791    if (do_show_frame_tags)
     2792        show_tags(w, frame->metadata, SECTION_ID_FRAME_TAGS);
     2793    if (do_show_log)
     2794        show_log(w, SECTION_ID_FRAME_LOGS, SECTION_ID_FRAME_LOG, do_show_log);
     2795    if (frame->nb_side_data)
     2796        print_frame_side_data(w, frame, stream);
     2797
     2798    writer_print_section_footer(w);
     2799
     2800    av_bprint_finalize(&pbuf, NULL);
     2801    fflush(stdout);
     2802}
     2803
     2804static av_always_inline int process_frame(WriterContext *w,
     2805                                          InputFile *ifile,
     2806                                          AVFrame *frame, const AVPacket *pkt,
     2807                                          int *packet_new)
     2808{
     2809    AVFormatContext *fmt_ctx = ifile->fmt_ctx;
     2810    AVCodecContext *dec_ctx = ifile->streams[pkt->stream_index].dec_ctx;
     2811    AVCodecParameters *par = ifile->streams[pkt->stream_index].st->codecpar;
     2812    AVSubtitle sub;
     2813    int ret = 0, got_frame = 0;
     2814
     2815    clear_log(1);
     2816    if (dec_ctx) {
     2817        switch (par->codec_type) {
     2818        case AVMEDIA_TYPE_VIDEO:
     2819        case AVMEDIA_TYPE_AUDIO:
     2820            if (*packet_new) {
     2821                ret = avcodec_send_packet(dec_ctx, pkt);
     2822                if (ret == AVERROR(EAGAIN)) {
     2823                    ret = 0;
     2824                } else if (ret >= 0 || ret == AVERROR_EOF) {
     2825                    ret = 0;
     2826                    *packet_new = 0;
     2827                }
     2828            }
     2829            if (ret >= 0) {
     2830                ret = avcodec_receive_frame(dec_ctx, frame);
     2831                if (ret >= 0) {
     2832                    got_frame = 1;
     2833                } else if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
     2834                    ret = 0;
     2835                }
     2836            }
     2837            break;
     2838
     2839        case AVMEDIA_TYPE_SUBTITLE:
     2840            if (*packet_new)
     2841                ret = avcodec_decode_subtitle2(dec_ctx, &sub, &got_frame, pkt);
     2842            *packet_new = 0;
     2843            break;
     2844        default:
     2845            *packet_new = 0;
     2846        }
     2847    } else {
     2848        *packet_new = 0;
     2849    }
     2850
     2851    if (ret < 0)
     2852        return ret;
     2853    if (got_frame) {
     2854        int is_sub = (par->codec_type == AVMEDIA_TYPE_SUBTITLE);
     2855        nb_streams_frames[pkt->stream_index]++;
     2856        if (do_show_frames)
     2857            if (is_sub)
     2858                show_subtitle(w, &sub, ifile->streams[pkt->stream_index].st, fmt_ctx);
     2859            else
     2860                show_frame(w, frame, ifile->streams[pkt->stream_index].st, fmt_ctx);
     2861        if (is_sub)
     2862            avsubtitle_free(&sub);
     2863    }
     2864    return got_frame || *packet_new;
     2865}
     2866
     2867static void log_read_interval(const ReadInterval *interval, void *log_ctx, int log_level)
     2868{
     2869    av_log(log_ctx, log_level, "id:%d", interval->id);
     2870
     2871    if (interval->has_start) {
     2872        av_log(log_ctx, log_level, " start:%s%s", interval->start_is_offset ? "+" : "",
     2873               av_ts2timestr(interval->start, &AV_TIME_BASE_Q));
     2874    } else {
     2875        av_log(log_ctx, log_level, " start:N/A");
     2876    }
     2877
     2878    if (interval->has_end) {
     2879        av_log(log_ctx, log_level, " end:%s", interval->end_is_offset ? "+" : "");
     2880        if (interval->duration_frames)
     2881            av_log(log_ctx, log_level, "#%"PRId64, interval->end);
     2882        else
     2883            av_log(log_ctx, log_level, "%s", av_ts2timestr(interval->end, &AV_TIME_BASE_Q));
     2884    } else {
     2885        av_log(log_ctx, log_level, " end:N/A");
     2886    }
     2887
     2888    av_log(log_ctx, log_level, "\n");
     2889}
     2890
     2891static int read_interval_packets(WriterContext *w, InputFile *ifile,
     2892                                 const ReadInterval *interval, int64_t *cur_ts)
     2893{
     2894    AVFormatContext *fmt_ctx = ifile->fmt_ctx;
     2895    AVPacket *pkt = NULL;
     2896    AVFrame *frame = NULL;
     2897    int ret = 0, i = 0, frame_count = 0;
     2898    int64_t start = -INT64_MAX, end = interval->end;
     2899    int has_start = 0, has_end = interval->has_end && !interval->end_is_offset;
     2900
     2901    av_log(NULL, AV_LOG_VERBOSE, "Processing read interval ");
     2902    log_read_interval(interval, NULL, AV_LOG_VERBOSE);
     2903
     2904    if (interval->has_start) {
     2905        int64_t target;
     2906        if (interval->start_is_offset) {
     2907            if (*cur_ts == AV_NOPTS_VALUE) {
     2908                av_log(NULL, AV_LOG_ERROR,
     2909                       "Could not seek to relative position since current "
     2910                       "timestamp is not defined\n");
     2911                ret = AVERROR(EINVAL);
     2912                goto end;
     2913            }
     2914            target = *cur_ts + interval->start;
     2915        } else {
     2916            target = interval->start;
     2917        }
     2918
     2919        av_log(NULL, AV_LOG_VERBOSE, "Seeking to read interval start point %s\n",
     2920               av_ts2timestr(target, &AV_TIME_BASE_Q));
     2921        if ((ret = avformat_seek_file(fmt_ctx, -1, -INT64_MAX, target, INT64_MAX, 0)) < 0) {
     2922            av_log(NULL, AV_LOG_ERROR, "Could not seek to position %"PRId64": %s\n",
     2923                   interval->start, av_err2str(ret));
     2924            goto end;
     2925        }
     2926    }
     2927
     2928    frame = av_frame_alloc();
     2929    if (!frame) {
     2930        ret = AVERROR(ENOMEM);
     2931        goto end;
     2932    }
     2933    pkt = av_packet_alloc();
     2934    if (!pkt) {
     2935        ret = AVERROR(ENOMEM);
     2936        goto end;
     2937    }
     2938    while (!av_read_frame(fmt_ctx, pkt)) {
     2939        if (fmt_ctx->nb_streams > nb_streams) {
     2940            REALLOCZ_ARRAY_STREAM(nb_streams_frames,  nb_streams, fmt_ctx->nb_streams);
     2941            REALLOCZ_ARRAY_STREAM(nb_streams_packets, nb_streams, fmt_ctx->nb_streams);
     2942            REALLOCZ_ARRAY_STREAM(selected_streams,   nb_streams, fmt_ctx->nb_streams);
     2943            nb_streams = fmt_ctx->nb_streams;
     2944        }
     2945        if (selected_streams[pkt->stream_index]) {
     2946            AVRational tb = ifile->streams[pkt->stream_index].st->time_base;
     2947            int64_t pts = pkt->pts != AV_NOPTS_VALUE ? pkt->pts : pkt->dts;
     2948
     2949            if (pts != AV_NOPTS_VALUE)
     2950                *cur_ts = av_rescale_q(pts, tb, AV_TIME_BASE_Q);
     2951
     2952            if (!has_start && *cur_ts != AV_NOPTS_VALUE) {
     2953                start = *cur_ts;
     2954                has_start = 1;
     2955            }
     2956
     2957            if (has_start && !has_end && interval->end_is_offset) {
     2958                end = start + interval->end;
     2959                has_end = 1;
     2960            }
     2961
     2962            if (interval->end_is_offset && interval->duration_frames) {
     2963                if (frame_count >= interval->end)
     2964                    break;
     2965            } else if (has_end && *cur_ts != AV_NOPTS_VALUE && *cur_ts >= end) {
     2966                break;
     2967            }
     2968
     2969            frame_count++;
     2970            if (do_read_packets) {
     2971                if (do_show_packets)
     2972                    show_packet(w, ifile, pkt, i++);
     2973                nb_streams_packets[pkt->stream_index]++;
     2974            }
     2975            if (do_read_frames) {
     2976                int packet_new = 1;
     2977                FrameData *fd;
     2978
     2979                pkt->opaque_ref = av_buffer_allocz(sizeof(*fd));
     2980                if (!pkt->opaque_ref) {
     2981                    ret = AVERROR(ENOMEM);
     2982                    goto end;
     2983                }
     2984                fd = (FrameData*)pkt->opaque_ref->data;
     2985                fd->pkt_pos  = pkt->pos;
     2986                fd->pkt_size = pkt->size;
     2987
     2988                while (process_frame(w, ifile, frame, pkt, &packet_new) > 0);
     2989            }
     2990        }
     2991        av_packet_unref(pkt);
     2992    }
     2993    av_packet_unref(pkt);
     2994    //Flush remaining frames that are cached in the decoder
     2995    for (i = 0; i < ifile->nb_streams; i++) {
     2996        pkt->stream_index = i;
     2997        if (do_read_frames) {
     2998            while (process_frame(w, ifile, frame, pkt, &(int){1}) > 0);
     2999            if (ifile->streams[i].dec_ctx)
     3000                avcodec_flush_buffers(ifile->streams[i].dec_ctx);
     3001        }
     3002    }
     3003
     3004end:
     3005    av_frame_free(&frame);
     3006    av_packet_free(&pkt);
     3007    if (ret < 0) {
     3008        av_log(NULL, AV_LOG_ERROR, "Could not read packets in interval ");
     3009        log_read_interval(interval, NULL, AV_LOG_ERROR);
     3010    }
     3011    return ret;
     3012}
     3013
     3014static int read_packets(WriterContext *w, InputFile *ifile)
     3015{
     3016    AVFormatContext *fmt_ctx = ifile->fmt_ctx;
     3017    int i, ret = 0;
     3018    int64_t cur_ts = fmt_ctx->start_time;
     3019
     3020    if (read_intervals_nb == 0) {
     3021        ReadInterval interval = (ReadInterval) { .has_start = 0, .has_end = 0 };
     3022        ret = read_interval_packets(w, ifile, &interval, &cur_ts);
     3023    } else {
     3024        for (i = 0; i < read_intervals_nb; i++) {
     3025            ret = read_interval_packets(w, ifile, &read_intervals[i], &cur_ts);
     3026            if (ret < 0)
     3027                break;
     3028        }
     3029    }
     3030
     3031    return ret;
     3032}
     3033
     3034static int show_stream(WriterContext *w, AVFormatContext *fmt_ctx, int stream_idx, InputStream *ist, int in_program)
     3035{
     3036    AVStream *stream = ist->st;
     3037    AVCodecParameters *par;
     3038    AVCodecContext *dec_ctx;
     3039    char val_str[128];
     3040    const char *s;
     3041    AVRational sar, dar;
     3042    AVBPrint pbuf;
     3043    const AVCodecDescriptor *cd;
     3044    int ret = 0;
     3045    const char *profile = NULL;
     3046
     3047    av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
     3048
     3049    writer_print_section_header(w, NULL, in_program ? SECTION_ID_PROGRAM_STREAM : SECTION_ID_STREAM);
     3050
     3051    print_int("index", stream->index);
     3052
     3053    par     = stream->codecpar;
     3054    dec_ctx = ist->dec_ctx;
     3055    if (cd = avcodec_descriptor_get(par->codec_id)) {
     3056        print_str("codec_name", cd->name);
     3057        if (!do_bitexact) {
     3058            print_str("codec_long_name",
     3059                      cd->long_name ? cd->long_name : "unknown");
     3060        }
     3061    } else {
     3062        print_str_opt("codec_name", "unknown");
     3063        if (!do_bitexact) {
     3064            print_str_opt("codec_long_name", "unknown");
     3065        }
     3066    }
     3067
     3068    if (!do_bitexact && (profile = avcodec_profile_name(par->codec_id, par->profile)))
     3069        print_str("profile", profile);
     3070    else {
     3071        if (par->profile != AV_PROFILE_UNKNOWN) {
     3072            char profile_num[12];
     3073            snprintf(profile_num, sizeof(profile_num), "%d", par->profile);
     3074            print_str("profile", profile_num);
     3075        } else
     3076            print_str_opt("profile", "unknown");
     3077    }
     3078
     3079    s = av_get_media_type_string(par->codec_type);
     3080    if (s) print_str    ("codec_type", s);
     3081    else   print_str_opt("codec_type", "unknown");
     3082
     3083    /* print AVI/FourCC tag */
     3084    print_str("codec_tag_string",    av_fourcc2str(par->codec_tag));
     3085    print_fmt("codec_tag", "0x%04"PRIx32, par->codec_tag);
     3086
     3087    switch (par->codec_type) {
     3088    case AVMEDIA_TYPE_VIDEO:
     3089        print_int("width",        par->width);
     3090        print_int("height",       par->height);
     3091        if (dec_ctx) {
     3092            print_int("coded_width",  dec_ctx->coded_width);
     3093            print_int("coded_height", dec_ctx->coded_height);
     3094            print_int("closed_captions", !!(dec_ctx->properties & FF_CODEC_PROPERTY_CLOSED_CAPTIONS));
     3095            print_int("film_grain", !!(dec_ctx->properties & FF_CODEC_PROPERTY_FILM_GRAIN));
     3096        }
     3097        print_int("has_b_frames", par->video_delay);
     3098        sar = av_guess_sample_aspect_ratio(fmt_ctx, stream, NULL);
     3099        if (sar.num) {
     3100            print_q("sample_aspect_ratio", sar, ':');
     3101            av_reduce(&dar.num, &dar.den,
     3102                      par->width  * sar.num,
     3103                      par->height * sar.den,
     3104                      1024*1024);
     3105            print_q("display_aspect_ratio", dar, ':');
     3106        } else {
     3107            print_str_opt("sample_aspect_ratio", "N/A");
     3108            print_str_opt("display_aspect_ratio", "N/A");
     3109        }
     3110        s = av_get_pix_fmt_name(par->format);
     3111        if (s) print_str    ("pix_fmt", s);
     3112        else   print_str_opt("pix_fmt", "unknown");
     3113        print_int("level",   par->level);
     3114
     3115        print_color_range(w, par->color_range);
     3116        print_color_space(w, par->color_space);
     3117        print_color_trc(w, par->color_trc);
     3118        print_primaries(w, par->color_primaries);
     3119        print_chroma_location(w, par->chroma_location);
     3120
     3121        if (par->field_order == AV_FIELD_PROGRESSIVE)
     3122            print_str("field_order", "progressive");
     3123        else if (par->field_order == AV_FIELD_TT)
     3124            print_str("field_order", "tt");
     3125        else if (par->field_order == AV_FIELD_BB)
     3126            print_str("field_order", "bb");
     3127        else if (par->field_order == AV_FIELD_TB)
     3128            print_str("field_order", "tb");
     3129        else if (par->field_order == AV_FIELD_BT)
     3130            print_str("field_order", "bt");
     3131        else
     3132            print_str_opt("field_order", "unknown");
     3133
     3134        if (dec_ctx)
     3135            print_int("refs", dec_ctx->refs);
     3136        break;
     3137
     3138    case AVMEDIA_TYPE_AUDIO:
     3139        s = av_get_sample_fmt_name(par->format);
     3140        if (s) print_str    ("sample_fmt", s);
     3141        else   print_str_opt("sample_fmt", "unknown");
     3142        print_val("sample_rate",     par->sample_rate, unit_hertz_str);
     3143        print_int("channels",        par->ch_layout.nb_channels);
     3144
     3145        if (par->ch_layout.order != AV_CHANNEL_ORDER_UNSPEC) {
     3146            av_channel_layout_describe(&par->ch_layout, val_str, sizeof(val_str));
     3147            print_str    ("channel_layout", val_str);
     3148        } else {
     3149            print_str_opt("channel_layout", "unknown");
     3150        }
     3151
     3152        print_int("bits_per_sample", av_get_bits_per_sample(par->codec_id));
     3153
     3154        print_int("initial_padding", par->initial_padding);
     3155        break;
     3156
     3157    case AVMEDIA_TYPE_SUBTITLE:
     3158        if (par->width)
     3159            print_int("width",       par->width);
     3160        else
     3161            print_str_opt("width",   "N/A");
     3162        if (par->height)
     3163            print_int("height",      par->height);
     3164        else
     3165            print_str_opt("height",  "N/A");
     3166        break;
     3167    }
     3168
     3169    if (show_private_data) {
     3170        if (dec_ctx && dec_ctx->codec->priv_class)
     3171            print_private_data(w, dec_ctx->priv_data);
     3172        if (fmt_ctx->iformat->priv_class)
     3173            print_private_data(w, fmt_ctx->priv_data);
     3174    }
     3175
     3176    if (fmt_ctx->iformat->flags & AVFMT_SHOW_IDS) print_fmt    ("id", "0x%x", stream->id);
     3177    else                                          print_str_opt("id", "N/A");
     3178    print_q("r_frame_rate",   stream->r_frame_rate,   '/');
     3179    print_q("avg_frame_rate", stream->avg_frame_rate, '/');
     3180    print_q("time_base",      stream->time_base,      '/');
     3181    print_ts  ("start_pts",   stream->start_time);
     3182    print_time("start_time",  stream->start_time, &stream->time_base);
     3183    print_ts  ("duration_ts", stream->duration);
     3184    print_time("duration",    stream->duration, &stream->time_base);
     3185    if (par->bit_rate > 0)     print_val    ("bit_rate", par->bit_rate, unit_bit_per_second_str);
     3186    else                       print_str_opt("bit_rate", "N/A");
     3187    if (dec_ctx && dec_ctx->rc_max_rate > 0)
     3188        print_val ("max_bit_rate", dec_ctx->rc_max_rate, unit_bit_per_second_str);
     3189    else
     3190        print_str_opt("max_bit_rate", "N/A");
     3191    if (dec_ctx && dec_ctx->bits_per_raw_sample > 0) print_fmt("bits_per_raw_sample", "%d", dec_ctx->bits_per_raw_sample);
     3192    else                                             print_str_opt("bits_per_raw_sample", "N/A");
     3193    if (stream->nb_frames) print_fmt    ("nb_frames", "%"PRId64, stream->nb_frames);
     3194    else                   print_str_opt("nb_frames", "N/A");
     3195    if (nb_streams_frames[stream_idx])  print_fmt    ("nb_read_frames", "%"PRIu64, nb_streams_frames[stream_idx]);
     3196    else                                print_str_opt("nb_read_frames", "N/A");
     3197    if (nb_streams_packets[stream_idx]) print_fmt    ("nb_read_packets", "%"PRIu64, nb_streams_packets[stream_idx]);
     3198    else                                print_str_opt("nb_read_packets", "N/A");
     3199    if (do_show_data)
     3200        writer_print_data(w, "extradata", par->extradata,
     3201                                          par->extradata_size);
     3202
     3203    if (par->extradata_size > 0) {
     3204        print_int("extradata_size", par->extradata_size);
     3205        writer_print_data_hash(w, "extradata_hash", par->extradata,
     3206                                                    par->extradata_size);
     3207    }
     3208
     3209    /* Print disposition information */
     3210#define PRINT_DISPOSITION(flagname, name) do {                                \
     3211        print_int(name, !!(stream->disposition & AV_DISPOSITION_##flagname)); \
     3212    } while (0)
     3213
     3214    if (do_show_stream_disposition) {
     3215        writer_print_section_header(w, NULL, in_program ? SECTION_ID_PROGRAM_STREAM_DISPOSITION : SECTION_ID_STREAM_DISPOSITION);
     3216        PRINT_DISPOSITION(DEFAULT,          "default");
     3217        PRINT_DISPOSITION(DUB,              "dub");
     3218        PRINT_DISPOSITION(ORIGINAL,         "original");
     3219        PRINT_DISPOSITION(COMMENT,          "comment");
     3220        PRINT_DISPOSITION(LYRICS,           "lyrics");
     3221        PRINT_DISPOSITION(KARAOKE,          "karaoke");
     3222        PRINT_DISPOSITION(FORCED,           "forced");
     3223        PRINT_DISPOSITION(HEARING_IMPAIRED, "hearing_impaired");
     3224        PRINT_DISPOSITION(VISUAL_IMPAIRED,  "visual_impaired");
     3225        PRINT_DISPOSITION(CLEAN_EFFECTS,    "clean_effects");
     3226        PRINT_DISPOSITION(ATTACHED_PIC,     "attached_pic");
     3227        PRINT_DISPOSITION(TIMED_THUMBNAILS, "timed_thumbnails");
     3228        PRINT_DISPOSITION(NON_DIEGETIC,     "non_diegetic");
     3229        PRINT_DISPOSITION(CAPTIONS,         "captions");
     3230        PRINT_DISPOSITION(DESCRIPTIONS,     "descriptions");
     3231        PRINT_DISPOSITION(METADATA,         "metadata");
     3232        PRINT_DISPOSITION(DEPENDENT,        "dependent");
     3233        PRINT_DISPOSITION(STILL_IMAGE,      "still_image");
     3234        writer_print_section_footer(w);
     3235    }
     3236
     3237    if (do_show_stream_tags)
     3238        ret = show_tags(w, stream->metadata, in_program ? SECTION_ID_PROGRAM_STREAM_TAGS : SECTION_ID_STREAM_TAGS);
     3239
     3240    if (stream->codecpar->nb_coded_side_data) {
     3241        writer_print_section_header(w, NULL, SECTION_ID_STREAM_SIDE_DATA_LIST);
     3242        for (int i = 0; i < stream->codecpar->nb_coded_side_data; i++) {
     3243            print_pkt_side_data(w, stream->codecpar, &stream->codecpar->coded_side_data[i],
     3244                                SECTION_ID_STREAM_SIDE_DATA);
     3245            writer_print_section_footer(w);
     3246        }
     3247        writer_print_section_footer(w);
     3248    }
     3249
     3250    writer_print_section_footer(w);
     3251    av_bprint_finalize(&pbuf, NULL);
     3252    fflush(stdout);
     3253
     3254    return ret;
     3255}
     3256
     3257static int show_streams(WriterContext *w, InputFile *ifile)
     3258{
     3259    AVFormatContext *fmt_ctx = ifile->fmt_ctx;
     3260    int i, ret = 0;
     3261
     3262    writer_print_section_header(w, NULL, SECTION_ID_STREAMS);
     3263    for (i = 0; i < ifile->nb_streams; i++)
     3264        if (selected_streams[i]) {
     3265            ret = show_stream(w, fmt_ctx, i, &ifile->streams[i], 0);
     3266            if (ret < 0)
     3267                break;
     3268        }
     3269    writer_print_section_footer(w);
     3270
     3271    return ret;
     3272}
     3273
     3274static int show_program(WriterContext *w, InputFile *ifile, AVProgram *program)
     3275{
     3276    AVFormatContext *fmt_ctx = ifile->fmt_ctx;
     3277    int i, ret = 0;
     3278
     3279    writer_print_section_header(w, NULL, SECTION_ID_PROGRAM);
     3280    print_int("program_id", program->id);
     3281    print_int("program_num", program->program_num);
     3282    print_int("nb_streams", program->nb_stream_indexes);
     3283    print_int("pmt_pid", program->pmt_pid);
     3284    print_int("pcr_pid", program->pcr_pid);
     3285    if (do_show_program_tags)
     3286        ret = show_tags(w, program->metadata, SECTION_ID_PROGRAM_TAGS);
     3287    if (ret < 0)
     3288        goto end;
     3289
     3290    writer_print_section_header(w, NULL, SECTION_ID_PROGRAM_STREAMS);
     3291    for (i = 0; i < program->nb_stream_indexes; i++) {
     3292        if (selected_streams[program->stream_index[i]]) {
     3293            ret = show_stream(w, fmt_ctx, program->stream_index[i], &ifile->streams[program->stream_index[i]], 1);
     3294            if (ret < 0)
     3295                break;
     3296        }
     3297    }
     3298    writer_print_section_footer(w);
     3299
     3300end:
     3301    writer_print_section_footer(w);
     3302    return ret;
     3303}
     3304
     3305static int show_programs(WriterContext *w, InputFile *ifile)
     3306{
     3307    AVFormatContext *fmt_ctx = ifile->fmt_ctx;
     3308    int i, ret = 0;
     3309
     3310    writer_print_section_header(w, NULL, SECTION_ID_PROGRAMS);
     3311    for (i = 0; i < fmt_ctx->nb_programs; i++) {
     3312        AVProgram *program = fmt_ctx->programs[i];
     3313        if (!program)
     3314            continue;
     3315        ret = show_program(w, ifile, program);
     3316        if (ret < 0)
     3317            break;
     3318    }
     3319    writer_print_section_footer(w);
     3320    return ret;
     3321}
     3322
     3323static int show_chapters(WriterContext *w, InputFile *ifile)
     3324{
     3325    AVFormatContext *fmt_ctx = ifile->fmt_ctx;
     3326    int i, ret = 0;
     3327
     3328    writer_print_section_header(w, NULL, SECTION_ID_CHAPTERS);
     3329    for (i = 0; i < fmt_ctx->nb_chapters; i++) {
     3330        AVChapter *chapter = fmt_ctx->chapters[i];
     3331
     3332        writer_print_section_header(w, NULL, SECTION_ID_CHAPTER);
     3333        print_int("id", chapter->id);
     3334        print_q  ("time_base", chapter->time_base, '/');
     3335        print_int("start", chapter->start);
     3336        print_time("start_time", chapter->start, &chapter->time_base);
     3337        print_int("end", chapter->end);
     3338        print_time("end_time", chapter->end, &chapter->time_base);
     3339        if (do_show_chapter_tags)
     3340            ret = show_tags(w, chapter->metadata, SECTION_ID_CHAPTER_TAGS);
     3341        writer_print_section_footer(w);
     3342    }
     3343    writer_print_section_footer(w);
     3344
     3345    return ret;
     3346}
     3347
     3348static int show_format(WriterContext *w, InputFile *ifile)
     3349{
     3350    AVFormatContext *fmt_ctx = ifile->fmt_ctx;
     3351    char val_str[128];
     3352    int64_t size = fmt_ctx->pb ? avio_size(fmt_ctx->pb) : -1;
     3353    int ret = 0;
     3354
     3355    writer_print_section_header(w, NULL, SECTION_ID_FORMAT);
     3356    print_str_validate("filename", fmt_ctx->url);
     3357    print_int("nb_streams",       fmt_ctx->nb_streams);
     3358    print_int("nb_programs",      fmt_ctx->nb_programs);
     3359    print_str("format_name",      fmt_ctx->iformat->name);
     3360    if (!do_bitexact) {
     3361        if (fmt_ctx->iformat->long_name) print_str    ("format_long_name", fmt_ctx->iformat->long_name);
     3362        else                             print_str_opt("format_long_name", "unknown");
     3363    }
     3364    print_time("start_time",      fmt_ctx->start_time, &AV_TIME_BASE_Q);
     3365    print_time("duration",        fmt_ctx->duration,   &AV_TIME_BASE_Q);
     3366    if (size >= 0) print_val    ("size", size, unit_byte_str);
     3367    else           print_str_opt("size", "N/A");
     3368    if (fmt_ctx->bit_rate > 0) print_val    ("bit_rate", fmt_ctx->bit_rate, unit_bit_per_second_str);
     3369    else                       print_str_opt("bit_rate", "N/A");
     3370    print_int("probe_score", fmt_ctx->probe_score);
     3371    if (do_show_format_tags)
     3372        ret = show_tags(w, fmt_ctx->metadata, SECTION_ID_FORMAT_TAGS);
     3373
     3374    writer_print_section_footer(w);
     3375    fflush(stdout);
     3376    return ret;
     3377}
     3378
     3379static void show_error(WriterContext *w, int err)
     3380{
     3381    writer_print_section_header(w, NULL, SECTION_ID_ERROR);
     3382    print_int("code", err);
     3383    print_str("string", av_err2str(err));
     3384    writer_print_section_footer(w);
     3385}
     3386
     3387static int open_input_file(InputFile *ifile, const char *filename,
     3388                           const char *print_filename)
     3389{
     3390    int err, i;
     3391    AVFormatContext *fmt_ctx = NULL;
     3392    const AVDictionaryEntry *t = NULL;
     3393    int scan_all_pmts_set = 0;
     3394
     3395    fmt_ctx = avformat_alloc_context();
     3396    if (!fmt_ctx)
     3397        return AVERROR(ENOMEM);
     3398
     3399    if (!av_dict_get(format_opts, "scan_all_pmts", NULL, AV_DICT_MATCH_CASE)) {
     3400        av_dict_set(&format_opts, "scan_all_pmts", "1", AV_DICT_DONT_OVERWRITE);
     3401        scan_all_pmts_set = 1;
     3402    }
     3403    if ((err = avformat_open_input(&fmt_ctx, filename,
     3404                                   iformat, &format_opts)) < 0) {
     3405        print_error(filename, err);
     3406        return err;
     3407    }
     3408    if (print_filename) {
     3409        av_freep(&fmt_ctx->url);
     3410        fmt_ctx->url = av_strdup(print_filename);
     3411    }
     3412    ifile->fmt_ctx = fmt_ctx;
     3413    if (scan_all_pmts_set)
     3414        av_dict_set(&format_opts, "scan_all_pmts", NULL, AV_DICT_MATCH_CASE);
     3415    while ((t = av_dict_iterate(format_opts, t)))
     3416        av_log(NULL, AV_LOG_WARNING, "Option %s skipped - not known to demuxer.\n", t->key);
     3417
     3418    if (find_stream_info) {
     3419        AVDictionary **opts;
     3420        int orig_nb_streams = fmt_ctx->nb_streams;
     3421
     3422        err = setup_find_stream_info_opts(fmt_ctx, codec_opts, &opts);
     3423        if (err < 0)
     3424            return err;
     3425
     3426        err = avformat_find_stream_info(fmt_ctx, opts);
     3427
     3428        for (i = 0; i < orig_nb_streams; i++)
     3429            av_dict_free(&opts[i]);
     3430        av_freep(&opts);
     3431
     3432        if (err < 0) {
     3433            print_error(filename, err);
     3434            return err;
     3435        }
     3436    }
     3437
     3438    av_dump_format(fmt_ctx, 0, filename, 0);
     3439
     3440    ifile->streams = av_calloc(fmt_ctx->nb_streams, sizeof(*ifile->streams));
     3441    if (!ifile->streams)
     3442        exit(1);
     3443    ifile->nb_streams = fmt_ctx->nb_streams;
     3444
     3445    /* bind a decoder to each input stream */
     3446    for (i = 0; i < fmt_ctx->nb_streams; i++) {
     3447        InputStream *ist = &ifile->streams[i];
     3448        AVStream *stream = fmt_ctx->streams[i];
     3449        const AVCodec *codec;
     3450
     3451        ist->st = stream;
     3452
     3453        if (stream->codecpar->codec_id == AV_CODEC_ID_PROBE) {
     3454            av_log(NULL, AV_LOG_WARNING,
     3455                   "Failed to probe codec for input stream %d\n",
     3456                    stream->index);
     3457            continue;
     3458        }
     3459
     3460        codec = avcodec_find_decoder(stream->codecpar->codec_id);
     3461        if (!codec) {
     3462            av_log(NULL, AV_LOG_WARNING,
     3463                    "Unsupported codec with id %d for input stream %d\n",
     3464                    stream->codecpar->codec_id, stream->index);
     3465            continue;
     3466        }
     3467        {
     3468            AVDictionary *opts;
     3469
     3470            err = filter_codec_opts(codec_opts, stream->codecpar->codec_id,
     3471                                    fmt_ctx, stream, codec, &opts);
     3472            if (err < 0)
     3473                exit(1);
     3474
     3475            ist->dec_ctx = avcodec_alloc_context3(codec);
     3476            if (!ist->dec_ctx)
     3477                exit(1);
     3478
     3479            err = avcodec_parameters_to_context(ist->dec_ctx, stream->codecpar);
     3480            if (err < 0)
     3481                exit(1);
     3482
     3483            if (do_show_log) {
     3484                // For loging it is needed to disable at least frame threads as otherwise
     3485                // the log information would need to be reordered and matches up to contexts and frames
     3486                // That is in fact possible but not trivial
     3487                av_dict_set(&codec_opts, "threads", "1", 0);
     3488            }
     3489
     3490            av_dict_set(&opts, "flags", "+copy_opaque", AV_DICT_MULTIKEY);
     3491
     3492            ist->dec_ctx->pkt_timebase = stream->time_base;
     3493
     3494            if (avcodec_open2(ist->dec_ctx, codec, &opts) < 0) {
     3495                av_log(NULL, AV_LOG_WARNING, "Could not open codec for input stream %d\n",
     3496                       stream->index);
     3497                exit(1);
     3498            }
     3499
     3500            if ((t = av_dict_get(opts, "", NULL, AV_DICT_IGNORE_SUFFIX))) {
     3501                av_log(NULL, AV_LOG_ERROR, "Option %s for input stream %d not found\n",
     3502                       t->key, stream->index);
     3503                return AVERROR_OPTION_NOT_FOUND;
     3504            }
     3505        }
     3506    }
     3507
     3508    ifile->fmt_ctx = fmt_ctx;
     3509    return 0;
     3510}
     3511
     3512static void close_input_file(InputFile *ifile)
     3513{
     3514    int i;
     3515
     3516    /* close decoder for each stream */
     3517    for (i = 0; i < ifile->nb_streams; i++)
     3518        avcodec_free_context(&ifile->streams[i].dec_ctx);
     3519
     3520    av_freep(&ifile->streams);
     3521    ifile->nb_streams = 0;
     3522
     3523    avformat_close_input(&ifile->fmt_ctx);
     3524}
     3525
     3526static int probe_file(WriterContext *wctx, const char *filename,
     3527                      const char *print_filename)
     3528{
     3529    InputFile ifile = { 0 };
     3530    int ret, i;
     3531    int section_id;
     3532
     3533    do_read_frames = do_show_frames || do_count_frames;
     3534    do_read_packets = do_show_packets || do_count_packets;
     3535
     3536    ret = open_input_file(&ifile, filename, print_filename);
     3537    if (ret < 0)
     3538        goto end;
     3539
     3540#define CHECK_END if (ret < 0) goto end
     3541
     3542    nb_streams = ifile.fmt_ctx->nb_streams;
     3543    REALLOCZ_ARRAY_STREAM(nb_streams_frames,0,ifile.fmt_ctx->nb_streams);
     3544    REALLOCZ_ARRAY_STREAM(nb_streams_packets,0,ifile.fmt_ctx->nb_streams);
     3545    REALLOCZ_ARRAY_STREAM(selected_streams,0,ifile.fmt_ctx->nb_streams);
     3546
     3547    for (i = 0; i < ifile.fmt_ctx->nb_streams; i++) {
     3548        if (stream_specifier) {
     3549            ret = avformat_match_stream_specifier(ifile.fmt_ctx,
     3550                                                  ifile.fmt_ctx->streams[i],
     3551                                                  stream_specifier);
     3552            CHECK_END;
     3553            else
     3554                selected_streams[i] = ret;
     3555            ret = 0;
     3556        } else {
     3557            selected_streams[i] = 1;
     3558        }
     3559        if (!selected_streams[i])
     3560            ifile.fmt_ctx->streams[i]->discard = AVDISCARD_ALL;
     3561    }
     3562
     3563    if (do_read_frames || do_read_packets) {
     3564        if (do_show_frames && do_show_packets &&
     3565            wctx->writer->flags & WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER)
     3566            section_id = SECTION_ID_PACKETS_AND_FRAMES;
     3567        else if (do_show_packets && !do_show_frames)
     3568            section_id = SECTION_ID_PACKETS;
     3569        else // (!do_show_packets && do_show_frames)
     3570            section_id = SECTION_ID_FRAMES;
     3571        if (do_show_frames || do_show_packets)
     3572            writer_print_section_header(wctx, NULL, section_id);
     3573        ret = read_packets(wctx, &ifile);
     3574        if (do_show_frames || do_show_packets)
     3575            writer_print_section_footer(wctx);
     3576        CHECK_END;
     3577    }
     3578
     3579    if (do_show_programs) {
     3580        ret = show_programs(wctx, &ifile);
     3581        CHECK_END;
     3582    }
     3583
     3584    if (do_show_streams) {
     3585        ret = show_streams(wctx, &ifile);
     3586        CHECK_END;
     3587    }
     3588    if (do_show_chapters) {
     3589        ret = show_chapters(wctx, &ifile);
     3590        CHECK_END;
     3591    }
     3592    if (do_show_format) {
     3593        ret = show_format(wctx, &ifile);
     3594        CHECK_END;
     3595    }
     3596
     3597end:
     3598    if (ifile.fmt_ctx)
     3599        close_input_file(&ifile);
     3600    av_freep(&nb_streams_frames);
     3601    av_freep(&nb_streams_packets);
     3602    av_freep(&selected_streams);
     3603
     3604    return ret;
     3605}
     3606
     3607static void show_usage(void)
     3608{
     3609    av_log(NULL, AV_LOG_INFO, "Simple multimedia streams analyzer\n");
     3610    av_log(NULL, AV_LOG_INFO, "usage: %s [OPTIONS] INPUT_FILE\n", program_name);
     3611    av_log(NULL, AV_LOG_INFO, "\n");
     3612}
     3613
     3614static void ffprobe_show_program_version(WriterContext *w)
     3615{
     3616    AVBPrint pbuf;
     3617    av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
     3618
     3619    writer_print_section_header(w, NULL, SECTION_ID_PROGRAM_VERSION);
     3620    print_str("version", FFMPEG_VERSION);
     3621    print_fmt("copyright", "Copyright (c) %d-%d the FFmpeg developers",
     3622              program_birth_year, CONFIG_THIS_YEAR);
     3623    print_str("compiler_ident", CC_IDENT);
     3624    print_str("configuration", FFMPEG_CONFIGURATION);
     3625    writer_print_section_footer(w);
     3626
     3627    av_bprint_finalize(&pbuf, NULL);
     3628}
     3629
     3630#define SHOW_LIB_VERSION(libname, LIBNAME)                              \
     3631    do {                                                                \
     3632        if (CONFIG_##LIBNAME) {                                         \
     3633            unsigned int version = libname##_version();                 \
     3634            writer_print_section_header(w, NULL, SECTION_ID_LIBRARY_VERSION); \
     3635            print_str("name",    "lib" #libname);                       \
     3636            print_int("major",   LIB##LIBNAME##_VERSION_MAJOR);         \
     3637            print_int("minor",   LIB##LIBNAME##_VERSION_MINOR);         \
     3638            print_int("micro",   LIB##LIBNAME##_VERSION_MICRO);         \
     3639            print_int("version", version);                              \
     3640            print_str("ident",   LIB##LIBNAME##_IDENT);                 \
     3641            writer_print_section_footer(w);                             \
     3642        }                                                               \
     3643    } while (0)
     3644
     3645static void ffprobe_show_library_versions(WriterContext *w)
     3646{
     3647    writer_print_section_header(w, NULL, SECTION_ID_LIBRARY_VERSIONS);
     3648    SHOW_LIB_VERSION(avutil,     AVUTIL);
     3649    SHOW_LIB_VERSION(avcodec,    AVCODEC);
     3650    SHOW_LIB_VERSION(avformat,   AVFORMAT);
     3651    SHOW_LIB_VERSION(avdevice,   AVDEVICE);
     3652    SHOW_LIB_VERSION(avfilter,   AVFILTER);
     3653    SHOW_LIB_VERSION(swscale,    SWSCALE);
     3654    SHOW_LIB_VERSION(swresample, SWRESAMPLE);
     3655    SHOW_LIB_VERSION(postproc,   POSTPROC);
     3656    writer_print_section_footer(w);
     3657}
     3658
     3659#define PRINT_PIX_FMT_FLAG(flagname, name)                                \
     3660    do {                                                                  \
     3661        print_int(name, !!(pixdesc->flags & AV_PIX_FMT_FLAG_##flagname)); \
     3662    } while (0)
     3663
     3664static void ffprobe_show_pixel_formats(WriterContext *w)
     3665{
     3666    const AVPixFmtDescriptor *pixdesc = NULL;
     3667    int i, n;
     3668
     3669    writer_print_section_header(w, NULL, SECTION_ID_PIXEL_FORMATS);
     3670    while (pixdesc = av_pix_fmt_desc_next(pixdesc)) {
     3671        writer_print_section_header(w, NULL, SECTION_ID_PIXEL_FORMAT);
     3672        print_str("name", pixdesc->name);
     3673        print_int("nb_components", pixdesc->nb_components);
     3674        if ((pixdesc->nb_components >= 3) && !(pixdesc->flags & AV_PIX_FMT_FLAG_RGB)) {
     3675            print_int    ("log2_chroma_w", pixdesc->log2_chroma_w);
     3676            print_int    ("log2_chroma_h", pixdesc->log2_chroma_h);
     3677        } else {
     3678            print_str_opt("log2_chroma_w", "N/A");
     3679            print_str_opt("log2_chroma_h", "N/A");
     3680        }
     3681        n = av_get_bits_per_pixel(pixdesc);
     3682        if (n) print_int    ("bits_per_pixel", n);
     3683        else   print_str_opt("bits_per_pixel", "N/A");
     3684        if (do_show_pixel_format_flags) {
     3685            writer_print_section_header(w, NULL, SECTION_ID_PIXEL_FORMAT_FLAGS);
     3686            PRINT_PIX_FMT_FLAG(BE,        "big_endian");
     3687            PRINT_PIX_FMT_FLAG(PAL,       "palette");
     3688            PRINT_PIX_FMT_FLAG(BITSTREAM, "bitstream");
     3689            PRINT_PIX_FMT_FLAG(HWACCEL,   "hwaccel");
     3690            PRINT_PIX_FMT_FLAG(PLANAR,    "planar");
     3691            PRINT_PIX_FMT_FLAG(RGB,       "rgb");
     3692            PRINT_PIX_FMT_FLAG(ALPHA,     "alpha");
     3693            writer_print_section_footer(w);
     3694        }
     3695        if (do_show_pixel_format_components && (pixdesc->nb_components > 0)) {
     3696            writer_print_section_header(w, NULL, SECTION_ID_PIXEL_FORMAT_COMPONENTS);
     3697            for (i = 0; i < pixdesc->nb_components; i++) {
     3698                writer_print_section_header(w, NULL, SECTION_ID_PIXEL_FORMAT_COMPONENT);
     3699                print_int("index", i + 1);
     3700                print_int("bit_depth", pixdesc->comp[i].depth);
     3701                writer_print_section_footer(w);
     3702            }
     3703            writer_print_section_footer(w);
     3704        }
     3705        writer_print_section_footer(w);
     3706    }
     3707    writer_print_section_footer(w);
     3708}
     3709
     3710static int opt_show_optional_fields(void *optctx, const char *opt, const char *arg)
     3711{
     3712    if      (!av_strcasecmp(arg, "always")) show_optional_fields = SHOW_OPTIONAL_FIELDS_ALWAYS;
     3713    else if (!av_strcasecmp(arg, "never"))  show_optional_fields = SHOW_OPTIONAL_FIELDS_NEVER;
     3714    else if (!av_strcasecmp(arg, "auto"))   show_optional_fields = SHOW_OPTIONAL_FIELDS_AUTO;
     3715
     3716    if (show_optional_fields == SHOW_OPTIONAL_FIELDS_AUTO && av_strcasecmp(arg, "auto")) {
     3717        double num;
     3718        int ret = parse_number("show_optional_fields", arg, OPT_INT,
     3719                               SHOW_OPTIONAL_FIELDS_AUTO, SHOW_OPTIONAL_FIELDS_ALWAYS, &num);
     3720        if (ret < 0)
     3721            return ret;
     3722        show_optional_fields = num;
     3723    }
     3724    return 0;
     3725}
     3726
     3727static int opt_format(void *optctx, const char *opt, const char *arg)
     3728{
     3729    iformat = av_find_input_format(arg);
     3730    if (!iformat) {
     3731        av_log(NULL, AV_LOG_ERROR, "Unknown input format: %s\n", arg);
     3732        return AVERROR(EINVAL);
     3733    }
     3734    return 0;
     3735}
     3736
     3737static inline void mark_section_show_entries(SectionID section_id,
     3738                                             int show_all_entries, AVDictionary *entries)
     3739{
     3740    struct section *section = &sections[section_id];
     3741
     3742    section->show_all_entries = show_all_entries;
     3743    if (show_all_entries) {
     3744        for (const SectionID *id = section->children_ids; *id != -1; id++)
     3745            mark_section_show_entries(*id, show_all_entries, entries);
     3746    } else {
     3747        av_dict_copy(&section->entries_to_show, entries, 0);
     3748    }
     3749}
     3750
     3751static int match_section(const char *section_name,
     3752                         int show_all_entries, AVDictionary *entries)
     3753{
     3754    int i, ret = 0;
     3755
     3756    for (i = 0; i < FF_ARRAY_ELEMS(sections); i++) {
     3757        const struct section *section = &sections[i];
     3758        if (!strcmp(section_name, section->name) ||
     3759            (section->unique_name && !strcmp(section_name, section->unique_name))) {
     3760            av_log(NULL, AV_LOG_DEBUG,
     3761                   "'%s' matches section with unique name '%s'\n", section_name,
     3762                   (char *)av_x_if_null(section->unique_name, section->name));
     3763            ret++;
     3764            mark_section_show_entries(section->id, show_all_entries, entries);
     3765        }
     3766    }
     3767    return ret;
     3768}
     3769
     3770static int opt_show_entries(void *optctx, const char *opt, const char *arg)
     3771{
     3772    const char *p = arg;
     3773    int ret = 0;
     3774
     3775    while (*p) {
     3776        AVDictionary *entries = NULL;
     3777        char *section_name = av_get_token(&p, "=:");
     3778        int show_all_entries = 0;
     3779
     3780        if (!section_name) {
     3781            av_log(NULL, AV_LOG_ERROR,
     3782                   "Missing section name for option '%s'\n", opt);
     3783            return AVERROR(EINVAL);
     3784        }
     3785
     3786        if (*p == '=') {
     3787            p++;
     3788            while (*p && *p != ':') {
     3789                char *entry = av_get_token(&p, ",:");
     3790                if (!entry)
     3791                    break;
     3792                av_log(NULL, AV_LOG_VERBOSE,
     3793                       "Adding '%s' to the entries to show in section '%s'\n",
     3794                       entry, section_name);
     3795                av_dict_set(&entries, entry, "", AV_DICT_DONT_STRDUP_KEY);
     3796                if (*p == ',')
     3797                    p++;
     3798            }
     3799        } else {
     3800            show_all_entries = 1;
     3801        }
     3802
     3803        ret = match_section(section_name, show_all_entries, entries);
     3804        if (ret == 0) {
     3805            av_log(NULL, AV_LOG_ERROR, "No match for section '%s'\n", section_name);
     3806            ret = AVERROR(EINVAL);
     3807        }
     3808        av_dict_free(&entries);
     3809        av_free(section_name);
     3810
     3811        if (ret <= 0)
     3812            break;
     3813        if (*p)
     3814            p++;
     3815    }
     3816
     3817    return ret;
     3818}
     3819
     3820static int opt_input_file(void *optctx, const char *arg)
     3821{
     3822    if (input_filename) {
     3823        av_log(NULL, AV_LOG_ERROR,
     3824                "Argument '%s' provided as input filename, but '%s' was already specified.\n",
     3825                arg, input_filename);
     3826        return AVERROR(EINVAL);
     3827    }
     3828    if (!strcmp(arg, "-"))
     3829        arg = "fd:";
     3830    input_filename = arg;
     3831
     3832    return 0;
     3833}
     3834
     3835static int opt_input_file_i(void *optctx, const char *opt, const char *arg)
     3836{
     3837    opt_input_file(optctx, arg);
     3838    return 0;
     3839}
     3840
     3841static int opt_output_file_o(void *optctx, const char *opt, const char *arg)
     3842{
     3843    if (output_filename) {
     3844        av_log(NULL, AV_LOG_ERROR,
     3845                "Argument '%s' provided as output filename, but '%s' was already specified.\n",
     3846                arg, output_filename);
     3847        return AVERROR(EINVAL);
     3848    }
     3849    if (!strcmp(arg, "-"))
     3850        arg = "fd:";
     3851    output_filename = arg;
     3852
     3853    return 0;
     3854}
     3855
     3856static int opt_print_filename(void *optctx, const char *opt, const char *arg)
     3857{
     3858    print_input_filename = arg;
     3859    return 0;
     3860}
     3861
     3862/**
     3863 * Parse interval specification, according to the format:
     3864 * INTERVAL ::= [START|+START_OFFSET][%[END|+END_OFFSET]]
     3865 * INTERVALS ::= INTERVAL[,INTERVALS]
     3866*/
     3867static int parse_read_interval(const char *interval_spec,
     3868                               ReadInterval *interval)
     3869{
     3870    int ret = 0;
     3871    char *next, *p, *spec = av_strdup(interval_spec);
     3872    if (!spec)
     3873        return AVERROR(ENOMEM);
     3874
     3875    if (!*spec) {
     3876        av_log(NULL, AV_LOG_ERROR, "Invalid empty interval specification\n");
     3877        ret = AVERROR(EINVAL);
     3878        goto end;
     3879    }
     3880
     3881    p = spec;
     3882    next = strchr(spec, '%');
     3883    if (next)
     3884        *next++ = 0;
     3885
     3886    /* parse first part */
     3887    if (*p) {
     3888        interval->has_start = 1;
     3889
     3890        if (*p == '+') {
     3891            interval->start_is_offset = 1;
     3892            p++;
     3893        } else {
     3894            interval->start_is_offset = 0;
     3895        }
     3896
     3897        ret = av_parse_time(&interval->start, p, 1);
     3898        if (ret < 0) {
     3899            av_log(NULL, AV_LOG_ERROR, "Invalid interval start specification '%s'\n", p);
     3900            goto end;
     3901        }
     3902    } else {
     3903        interval->has_start = 0;
     3904    }
     3905
     3906    /* parse second part */
     3907    p = next;
     3908    if (p && *p) {
     3909        int64_t us;
     3910        interval->has_end = 1;
     3911
     3912        if (*p == '+') {
     3913            interval->end_is_offset = 1;
     3914            p++;
     3915        } else {
     3916            interval->end_is_offset = 0;
     3917        }
     3918
     3919        if (interval->end_is_offset && *p == '#') {
     3920            long long int lli;
     3921            char *tail;
     3922            interval->duration_frames = 1;
     3923            p++;
     3924            lli = strtoll(p, &tail, 10);
     3925            if (*tail || lli < 0) {
     3926                av_log(NULL, AV_LOG_ERROR,
     3927                       "Invalid or negative value '%s' for duration number of frames\n", p);
     3928                goto end;
     3929            }
     3930            interval->end = lli;
     3931        } else {
     3932            interval->duration_frames = 0;
     3933            ret = av_parse_time(&us, p, 1);
     3934            if (ret < 0) {
     3935                av_log(NULL, AV_LOG_ERROR, "Invalid interval end/duration specification '%s'\n", p);
     3936                goto end;
     3937            }
     3938            interval->end = us;
     3939        }
     3940    } else {
     3941        interval->has_end = 0;
     3942    }
     3943
     3944end:
     3945    av_free(spec);
     3946    return ret;
     3947}
     3948
     3949static int parse_read_intervals(const char *intervals_spec)
     3950{
     3951    int ret, n, i;
     3952    char *p, *spec = av_strdup(intervals_spec);
     3953    if (!spec)
     3954        return AVERROR(ENOMEM);
     3955
     3956    /* preparse specification, get number of intervals */
     3957    for (n = 0, p = spec; *p; p++)
     3958        if (*p == ',')
     3959            n++;
     3960    n++;
     3961
     3962    read_intervals = av_malloc_array(n, sizeof(*read_intervals));
     3963    if (!read_intervals) {
     3964        ret = AVERROR(ENOMEM);
     3965        goto end;
     3966    }
     3967    read_intervals_nb = n;
     3968
     3969    /* parse intervals */
     3970    p = spec;
     3971    for (i = 0; p; i++) {
     3972        char *next;
     3973
     3974        av_assert0(i < read_intervals_nb);
     3975        next = strchr(p, ',');
     3976        if (next)
     3977            *next++ = 0;
     3978
     3979        read_intervals[i].id = i;
     3980        ret = parse_read_interval(p, &read_intervals[i]);
     3981        if (ret < 0) {
     3982            av_log(NULL, AV_LOG_ERROR, "Error parsing read interval #%d '%s'\n",
     3983                   i, p);
     3984            goto end;
     3985        }
     3986        av_log(NULL, AV_LOG_VERBOSE, "Parsed log interval ");
     3987        log_read_interval(&read_intervals[i], NULL, AV_LOG_VERBOSE);
     3988        p = next;
     3989    }
     3990    av_assert0(i == read_intervals_nb);
     3991
     3992end:
     3993    av_free(spec);
     3994    return ret;
     3995}
     3996
     3997static int opt_read_intervals(void *optctx, const char *opt, const char *arg)
     3998{
     3999    return parse_read_intervals(arg);
     4000}
     4001
     4002static int opt_pretty(void *optctx, const char *opt, const char *arg)
     4003{
     4004    show_value_unit              = 1;
     4005    use_value_prefix             = 1;
     4006    use_byte_value_binary_prefix = 1;
     4007    use_value_sexagesimal_format = 1;
     4008    return 0;
     4009}
     4010
     4011static void print_section(SectionID id, int level)
     4012{
     4013    const SectionID *pid;
     4014    const struct section *section = &sections[id];
     4015    printf("%c%c%c%c",
     4016           section->flags & SECTION_FLAG_IS_WRAPPER           ? 'W' : '.',
     4017           section->flags & SECTION_FLAG_IS_ARRAY             ? 'A' : '.',
     4018           section->flags & SECTION_FLAG_HAS_VARIABLE_FIELDS  ? 'V' : '.',
     4019           section->flags & SECTION_FLAG_HAS_TYPE             ? 'T' : '.');
     4020    printf("%*c  %s", level * 4, ' ', section->name);
     4021    if (section->unique_name)
     4022        printf("/%s", section->unique_name);
     4023    printf("\n");
     4024
     4025    for (pid = section->children_ids; *pid != -1; pid++)
     4026        print_section(*pid, level+1);
     4027}
     4028
     4029static int opt_sections(void *optctx, const char *opt, const char *arg)
     4030{
     4031    printf("Sections:\n"
     4032           "W... = Section is a wrapper (contains other sections, no local entries)\n"
     4033           ".A.. = Section contains an array of elements of the same type\n"
     4034           "..V. = Section may contain a variable number of fields with variable keys\n"
     4035           "...T = Section contain a unique type\n"
     4036           "FLAGS NAME/UNIQUE_NAME\n"
     4037           "----\n");
     4038    print_section(SECTION_ID_ROOT, 0);
     4039    return 0;
     4040}
     4041
     4042static int opt_show_versions(void *optctx, const char *opt, const char *arg)
     4043{
     4044    mark_section_show_entries(SECTION_ID_PROGRAM_VERSION, 1, NULL);
     4045    mark_section_show_entries(SECTION_ID_LIBRARY_VERSION, 1, NULL);
     4046    return 0;
     4047}
     4048
     4049#define DEFINE_OPT_SHOW_SECTION(section, target_section_id)             \
     4050    static int opt_show_##section(void *optctx, const char *opt, const char *arg) \
     4051    {                                                                   \
     4052        mark_section_show_entries(SECTION_ID_##target_section_id, 1, NULL); \
     4053        return 0;                                                       \
     4054    }
     4055
     4056DEFINE_OPT_SHOW_SECTION(chapters,         CHAPTERS)
     4057DEFINE_OPT_SHOW_SECTION(error,            ERROR)
     4058DEFINE_OPT_SHOW_SECTION(format,           FORMAT)
     4059DEFINE_OPT_SHOW_SECTION(frames,           FRAMES)
     4060DEFINE_OPT_SHOW_SECTION(library_versions, LIBRARY_VERSIONS)
     4061DEFINE_OPT_SHOW_SECTION(packets,          PACKETS)
     4062DEFINE_OPT_SHOW_SECTION(pixel_formats,    PIXEL_FORMATS)
     4063DEFINE_OPT_SHOW_SECTION(program_version,  PROGRAM_VERSION)
     4064DEFINE_OPT_SHOW_SECTION(streams,          STREAMS)
     4065DEFINE_OPT_SHOW_SECTION(programs,         PROGRAMS)
     4066
     4067static const OptionDef real_options[] = {
     4068    CMDUTILS_COMMON_OPTIONS
     4069    { "f", HAS_ARG, {.func_arg = opt_format}, "force format", "format" },
     4070    { "unit", OPT_BOOL, {&show_value_unit}, "show unit of the displayed values" },
     4071    { "prefix", OPT_BOOL, {&use_value_prefix}, "use SI prefixes for the displayed values" },
     4072    { "byte_binary_prefix", OPT_BOOL, {&use_byte_value_binary_prefix},
     4073      "use binary prefixes for byte units" },
     4074    { "sexagesimal", OPT_BOOL,  {&use_value_sexagesimal_format},
     4075      "use sexagesimal format HOURS:MM:SS.MICROSECONDS for time units" },
     4076    { "pretty", 0, {.func_arg = opt_pretty},
     4077      "prettify the format of displayed values, make it more human readable" },
     4078    { "output_format", OPT_STRING | HAS_ARG, { &output_format },
     4079      "set the output printing format (available formats are: default, compact, csv, flat, ini, json, xml)", "format" },
     4080    { "print_format", OPT_STRING | HAS_ARG, { &output_format }, "alias for -output_format (deprecated)" },
     4081    { "of", OPT_STRING | HAS_ARG, { &output_format }, "alias for -output_format", "format" },
     4082    { "select_streams", OPT_STRING | HAS_ARG, { &stream_specifier }, "select the specified streams", "stream_specifier" },
     4083    { "sections", OPT_EXIT, {.func_arg = opt_sections}, "print sections structure and section information, and exit" },
     4084    { "show_data",    OPT_BOOL, { &do_show_data }, "show packets data" },
     4085    { "show_data_hash", OPT_STRING | HAS_ARG, { &show_data_hash }, "show packets data hash" },
     4086    { "show_error",   0, { .func_arg = &opt_show_error },  "show probing error" },
     4087    { "show_format",  0, { .func_arg = &opt_show_format }, "show format/container info" },
     4088    { "show_frames",  0, { .func_arg = &opt_show_frames }, "show frames info" },
     4089    { "show_entries", HAS_ARG, {.func_arg = opt_show_entries},
     4090      "show a set of specified entries", "entry_list" },
     4091#if HAVE_THREADS
     4092    { "show_log", OPT_INT|HAS_ARG, { &do_show_log }, "show log" },
     4093#endif
     4094    { "show_packets", 0, { .func_arg = &opt_show_packets }, "show packets info" },
     4095    { "show_programs", 0, { .func_arg = &opt_show_programs }, "show programs info" },
     4096    { "show_streams", 0, { .func_arg = &opt_show_streams }, "show streams info" },
     4097    { "show_chapters", 0, { .func_arg = &opt_show_chapters }, "show chapters info" },
     4098    { "count_frames", OPT_BOOL, { &do_count_frames }, "count the number of frames per stream" },
     4099    { "count_packets", OPT_BOOL, { &do_count_packets }, "count the number of packets per stream" },
     4100    { "show_program_version",  0, { .func_arg = &opt_show_program_version },  "show ffprobe version" },
     4101    { "show_library_versions", 0, { .func_arg = &opt_show_library_versions }, "show library versions" },
     4102    { "show_versions",         0, { .func_arg = &opt_show_versions }, "show program and library versions" },
     4103    { "show_pixel_formats", 0, { .func_arg = &opt_show_pixel_formats }, "show pixel format descriptions" },
     4104    { "show_optional_fields", HAS_ARG, { .func_arg = &opt_show_optional_fields }, "show optional fields" },
     4105    { "show_private_data", OPT_BOOL, { &show_private_data }, "show private data" },
     4106    { "private",           OPT_BOOL, { &show_private_data }, "same as show_private_data" },
     4107    { "bitexact", OPT_BOOL, {&do_bitexact}, "force bitexact output" },
     4108    { "read_intervals", HAS_ARG, {.func_arg = opt_read_intervals}, "set read intervals", "read_intervals" },
     4109    { "i", HAS_ARG, {.func_arg = opt_input_file_i}, "read specified file", "input_file"},
     4110    { "o", HAS_ARG, {.func_arg = opt_output_file_o}, "write to specified output", "output_file"},
     4111    { "print_filename", HAS_ARG, {.func_arg = opt_print_filename}, "override the printed input filename", "print_file"},
     4112    { "find_stream_info", OPT_BOOL | OPT_INPUT | OPT_EXPERT, { &find_stream_info },
     4113        "read and decode the streams to fill missing information with heuristics" },
     4114    { NULL, },
     4115};
     4116
     4117static inline int check_section_show_entries(int section_id)
     4118{
     4119    struct section *section = &sections[section_id];
     4120    if (sections[section_id].show_all_entries || sections[section_id].entries_to_show)
     4121        return 1;
     4122    for (const SectionID *id = section->children_ids; *id != -1; id++)
     4123        if (check_section_show_entries(*id))
     4124            return 1;
     4125    return 0;
     4126}
     4127
     4128#define SET_DO_SHOW(id, varname) do {                                   \
     4129        if (check_section_show_entries(SECTION_ID_##id))                \
     4130            do_show_##varname = 1;                                      \
     4131    } while (0)
     4132
     4133int main_ffprobe(int argc, char **argv)
     4134{
     4135    const Writer *w;
     4136    WriterContext *wctx;
     4137    char *buf;
     4138    char *w_name = NULL, *w_args = NULL;
     4139    int ret, input_ret, i;
     4140
     4141    init_dynload();
     4142
     4143#if HAVE_THREADS
     4144    ret = pthread_mutex_init(&log_mutex, NULL);
     4145    if (ret != 0) {
     4146        goto end;
     4147    }
     4148#endif
     4149    av_log_set_flags(AV_LOG_SKIP_REPEATED);
     4150
     4151    options = real_options;
     4152    parse_loglevel(argc, argv, options);
     4153    avformat_network_init();
     4154#if CONFIG_AVDEVICE
     4155    avdevice_register_all();
     4156#endif
     4157
     4158    show_banner(argc, argv, options);
     4159    ret = parse_options(NULL, argc, argv, options, opt_input_file);
     4160    if (ret < 0) {
     4161        ret = (ret == AVERROR_EXIT) ? 0 : ret;
     4162        goto end;
     4163    }
     4164
     4165    if (do_show_log)
     4166        av_log_set_callback(log_callback);
     4167
     4168    /* mark things to show, based on -show_entries */
     4169    SET_DO_SHOW(CHAPTERS, chapters);
     4170    SET_DO_SHOW(ERROR, error);
     4171    SET_DO_SHOW(FORMAT, format);
     4172    SET_DO_SHOW(FRAMES, frames);
     4173    SET_DO_SHOW(LIBRARY_VERSIONS, library_versions);
     4174    SET_DO_SHOW(PACKETS, packets);
     4175    SET_DO_SHOW(PIXEL_FORMATS, pixel_formats);
     4176    SET_DO_SHOW(PIXEL_FORMAT_FLAGS, pixel_format_flags);
     4177    SET_DO_SHOW(PIXEL_FORMAT_COMPONENTS, pixel_format_components);
     4178    SET_DO_SHOW(PROGRAM_VERSION, program_version);
     4179    SET_DO_SHOW(PROGRAMS, programs);
     4180    SET_DO_SHOW(STREAMS, streams);
     4181    SET_DO_SHOW(STREAM_DISPOSITION, stream_disposition);
     4182    SET_DO_SHOW(PROGRAM_STREAM_DISPOSITION, stream_disposition);
     4183
     4184    SET_DO_SHOW(CHAPTER_TAGS, chapter_tags);
     4185    SET_DO_SHOW(FORMAT_TAGS, format_tags);
     4186    SET_DO_SHOW(FRAME_TAGS, frame_tags);
     4187    SET_DO_SHOW(PROGRAM_TAGS, program_tags);
     4188    SET_DO_SHOW(STREAM_TAGS, stream_tags);
     4189    SET_DO_SHOW(PROGRAM_STREAM_TAGS, stream_tags);
     4190    SET_DO_SHOW(PACKET_TAGS, packet_tags);
     4191
     4192    if (do_bitexact && (do_show_program_version || do_show_library_versions)) {
     4193        av_log(NULL, AV_LOG_ERROR,
     4194               "-bitexact and -show_program_version or -show_library_versions "
     4195               "options are incompatible\n");
     4196        ret = AVERROR(EINVAL);
     4197        goto end;
     4198    }
     4199
     4200    writer_register_all();
     4201
     4202    if (!output_format)
     4203        output_format = av_strdup("default");
     4204    if (!output_format) {
     4205        ret = AVERROR(ENOMEM);
     4206        goto end;
     4207    }
     4208    w_name = av_strtok(output_format, "=", &buf);
     4209    if (!w_name) {
     4210        av_log(NULL, AV_LOG_ERROR,
     4211               "No name specified for the output format\n");
     4212        ret = AVERROR(EINVAL);
     4213        goto end;
     4214    }
     4215    w_args = buf;
     4216
     4217    if (show_data_hash) {
     4218        if ((ret = av_hash_alloc(&hash, show_data_hash)) < 0) {
     4219            if (ret == AVERROR(EINVAL)) {
     4220                const char *n;
     4221                av_log(NULL, AV_LOG_ERROR,
     4222                       "Unknown hash algorithm '%s'\nKnown algorithms:",
     4223                       show_data_hash);
     4224                for (i = 0; (n = av_hash_names(i)); i++)
     4225                    av_log(NULL, AV_LOG_ERROR, " %s", n);
     4226                av_log(NULL, AV_LOG_ERROR, "\n");
     4227            }
     4228            goto end;
     4229        }
     4230    }
     4231
     4232    w = writer_get_by_name(w_name);
     4233    if (!w) {
     4234        av_log(NULL, AV_LOG_ERROR, "Unknown output format with name '%s'\n", w_name);
     4235        ret = AVERROR(EINVAL);
     4236        goto end;
     4237    }
     4238
     4239    if ((ret = writer_open(&wctx, w, w_args,
     4240                           sections, FF_ARRAY_ELEMS(sections), output_filename)) >= 0) {
     4241        if (w == &xml_writer)
     4242            wctx->string_validation_utf8_flags |= AV_UTF8_FLAG_EXCLUDE_XML_INVALID_CONTROL_CODES;
     4243
     4244        writer_print_section_header(wctx, NULL, SECTION_ID_ROOT);
     4245
     4246        if (do_show_program_version)
     4247            ffprobe_show_program_version(wctx);
     4248        if (do_show_library_versions)
     4249            ffprobe_show_library_versions(wctx);
     4250        if (do_show_pixel_formats)
     4251            ffprobe_show_pixel_formats(wctx);
     4252
     4253        if (!input_filename &&
     4254            ((do_show_format || do_show_programs || do_show_streams || do_show_chapters || do_show_packets || do_show_error) ||
     4255             (!do_show_program_version && !do_show_library_versions && !do_show_pixel_formats))) {
     4256            show_usage();
     4257            av_log(NULL, AV_LOG_ERROR, "You have to specify one input file.\n");
     4258            av_log(NULL, AV_LOG_ERROR, "Use -h to get full help or, even better, run 'man %s'.\n", program_name);
     4259            ret = AVERROR(EINVAL);
     4260        } else if (input_filename) {
     4261            ret = probe_file(wctx, input_filename, print_input_filename);
     4262            if (ret < 0 && do_show_error)
     4263                show_error(wctx, ret);
     4264        }
     4265
     4266        input_ret = ret;
     4267
     4268        writer_print_section_footer(wctx);
     4269        ret = writer_close(&wctx);
     4270        if (ret < 0)
     4271            av_log(NULL, AV_LOG_ERROR, "Writing output failed: %s\n", av_err2str(ret));
     4272
     4273        ret = FFMIN(ret, input_ret);
     4274    }
     4275
     4276end:
     4277    av_freep(&output_format);
     4278    av_freep(&read_intervals);
     4279    av_hash_freep(&hash);
     4280
     4281    uninit_opts();
     4282    for (i = 0; i < FF_ARRAY_ELEMS(sections); i++)
     4283        av_dict_free(&(sections[i].entries_to_show));
     4284
     4285    avformat_network_deinit();
     4286
     4287#if HAVE_THREADS
     4288    pthread_mutex_destroy(&log_mutex);
     4289#endif
     4290
     4291    return ret < 0;
     4292}
  • ffmpeg-6.1/fftools/ffprobe_in_ffmpeg.h

    diff -ruN tmp/ffmpeg-6.1/fftools/ffprobe_in_ffmpeg.h ffmpeg-6.1/fftools/ffprobe_in_ffmpeg.h
    old new  
     1#ifndef FFPROBE_IN_FFMPEG_H
     2#define FFPROBE_IN_FFMPEG_H
     3
     4int main_ffprobe(int argc, char **argv);
     5
     6#endif
  • ffmpeg-6.1/fftools/Makefile

    diff -ruN tmp/ffmpeg-6.1/fftools/Makefile ffmpeg-6.1/fftools/Makefile
    old new  
    2121    fftools/objpool.o           \
    2222    fftools/sync_queue.o        \
    2323    fftools/thread_queue.o      \
     24    fftools/ffprobe_in_ffmpeg.o \
    2425
    2526define DOFFTOOL
    2627OBJS-$(1) += fftools/cmdutils.o fftools/opt_common.o fftools/$(1).o $(OBJS-$(1)-yes)