ffprobe.c 135 KB
Newer Older
Stefano Sabatini's avatar
Stefano Sabatini committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/*
 * Copyright (c) 2007-2010 Stefano Sabatini
 *
 * This file is part of FFmpeg.
 *
 * FFmpeg is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * FFmpeg is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with FFmpeg; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

21 22 23 24 25
/**
 * @file
 * simple media prober based on the FFmpeg libraries
 */

26
#include "config.h"
27
#include "libavutil/ffversion.h"
28

29 30
#include <string.h>

Stefano Sabatini's avatar
Stefano Sabatini committed
31 32
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
33
#include "libavutil/avassert.h"
34
#include "libavutil/avstring.h"
35
#include "libavutil/bprint.h"
36
#include "libavutil/display.h"
37
#include "libavutil/hash.h"
38
#include "libavutil/mastering_display_metadata.h"
39
#include "libavutil/opt.h"
Stefano Sabatini's avatar
Stefano Sabatini committed
40
#include "libavutil/pixdesc.h"
41
#include "libavutil/spherical.h"
42
#include "libavutil/stereo3d.h"
43
#include "libavutil/dict.h"
44
#include "libavutil/intreadwrite.h"
45
#include "libavutil/libm.h"
46
#include "libavutil/parseutils.h"
47
#include "libavutil/timecode.h"
48
#include "libavutil/timestamp.h"
49
#include "libavdevice/avdevice.h"
50 51 52
#include "libswscale/swscale.h"
#include "libswresample/swresample.h"
#include "libpostproc/postprocess.h"
Stefano Sabatini's avatar
Stefano Sabatini committed
53 54
#include "cmdutils.h"

55 56 57 58 59 60
#include "libavutil/thread.h"

#if !HAVE_THREADS
#  ifdef pthread_mutex_lock
#    undef pthread_mutex_lock
#  endif
61
#  define pthread_mutex_lock(a) do{}while(0)
62 63 64
#  ifdef pthread_mutex_unlock
#    undef pthread_mutex_unlock
#  endif
65
#  define pthread_mutex_unlock(a) do{}while(0)
66 67
#endif

68 69
typedef struct InputStream {
    AVStream *st;
70 71

    AVCodecContext *dec_ctx;
72 73
} InputStream;

74 75
typedef struct InputFile {
    AVFormatContext *fmt_ctx;
76 77 78

    InputStream *streams;
    int       nb_streams;
79 80
} InputFile;

81
const char program_name[] = "ffprobe";
Stefano Sabatini's avatar
Stefano Sabatini committed
82 83
const int program_birth_year = 2007;

84
static int do_bitexact = 0;
85 86 87 88
static int do_count_frames = 0;
static int do_count_packets = 0;
static int do_read_frames  = 0;
static int do_read_packets = 0;
89
static int do_show_chapters = 0;
90
static int do_show_error   = 0;
Stefano Sabatini's avatar
Stefano Sabatini committed
91
static int do_show_format  = 0;
92
static int do_show_frames  = 0;
93
static int do_show_packets = 0;
94
static int do_show_programs = 0;
Stefano Sabatini's avatar
Stefano Sabatini committed
95
static int do_show_streams = 0;
96
static int do_show_stream_disposition = 0;
97
static int do_show_data    = 0;
98 99
static int do_show_program_version  = 0;
static int do_show_library_versions = 0;
100
static int do_show_pixel_formats = 0;
101
static int do_show_pixel_format_flags = 0;
102
static int do_show_pixel_format_components = 0;
103
static int do_show_log = 0;
Stefano Sabatini's avatar
Stefano Sabatini committed
104

105 106 107 108 109
static int do_show_chapter_tags = 0;
static int do_show_format_tags = 0;
static int do_show_frame_tags = 0;
static int do_show_program_tags = 0;
static int do_show_stream_tags = 0;
110
static int do_show_packet_tags = 0;
111

Stefano Sabatini's avatar
Stefano Sabatini committed
112 113 114 115
static int show_value_unit              = 0;
static int use_value_prefix             = 0;
static int use_byte_value_binary_prefix = 0;
static int use_value_sexagesimal_format = 0;
116
static int show_private_data            = 1;
Stefano Sabatini's avatar
Stefano Sabatini committed
117

118
static char *print_format;
119
static char *stream_specifier;
120
static char *show_data_hash;
121

122
typedef struct ReadInterval {
123 124 125 126 127 128 129 130 131 132
    int id;             ///< identifier
    int64_t start, end; ///< start, end in second/AV_TIME_BASE units
    int has_start, has_end;
    int start_is_offset, end_is_offset;
    int duration_frames;
} ReadInterval;

static ReadInterval *read_intervals;
static int read_intervals_nb = 0;

133 134
static int find_stream_info  = 1;

135 136
/* section structure definition */

137 138
#define SECTION_MAX_NB_CHILDREN 10

139
struct section {
140
    int id;             ///< unique id identifying a section
141 142 143 144
    const char *name;

#define SECTION_FLAG_IS_WRAPPER      1 ///< the section only contains other sections, but has no data at its own level
#define SECTION_FLAG_IS_ARRAY        2 ///< the section contains an array of elements of the same type
145 146
#define SECTION_FLAG_HAS_VARIABLE_FIELDS 4 ///< the section may contain a variable number of fields with variable keys.
                                           ///  For these sections the element_name field is mandatory.
147
    int flags;
148
    int children_ids[SECTION_MAX_NB_CHILDREN+1]; ///< list of children section IDS, terminated by -1
149
    const char *element_name; ///< name of the contained element, if provided
150 151 152
    const char *unique_name;  ///< unique section name, in case the name is ambiguous
    AVDictionary *entries_to_show;
    int show_all_entries;
153 154 155 156
};

typedef enum {
    SECTION_ID_NONE = -1,
157 158 159
    SECTION_ID_CHAPTER,
    SECTION_ID_CHAPTER_TAGS,
    SECTION_ID_CHAPTERS,
160 161 162 163 164 165
    SECTION_ID_ERROR,
    SECTION_ID_FORMAT,
    SECTION_ID_FORMAT_TAGS,
    SECTION_ID_FRAME,
    SECTION_ID_FRAMES,
    SECTION_ID_FRAME_TAGS,
166 167
    SECTION_ID_FRAME_SIDE_DATA_LIST,
    SECTION_ID_FRAME_SIDE_DATA,
168 169
    SECTION_ID_FRAME_SIDE_DATA_TIMECODE_LIST,
    SECTION_ID_FRAME_SIDE_DATA_TIMECODE,
170 171
    SECTION_ID_FRAME_LOG,
    SECTION_ID_FRAME_LOGS,
172 173 174
    SECTION_ID_LIBRARY_VERSION,
    SECTION_ID_LIBRARY_VERSIONS,
    SECTION_ID_PACKET,
175
    SECTION_ID_PACKET_TAGS,
176 177
    SECTION_ID_PACKETS,
    SECTION_ID_PACKETS_AND_FRAMES,
178 179
    SECTION_ID_PACKET_SIDE_DATA_LIST,
    SECTION_ID_PACKET_SIDE_DATA,
180
    SECTION_ID_PIXEL_FORMAT,
181
    SECTION_ID_PIXEL_FORMAT_FLAGS,
182 183
    SECTION_ID_PIXEL_FORMAT_COMPONENT,
    SECTION_ID_PIXEL_FORMAT_COMPONENTS,
184
    SECTION_ID_PIXEL_FORMATS,
185 186 187 188 189 190
    SECTION_ID_PROGRAM_STREAM_DISPOSITION,
    SECTION_ID_PROGRAM_STREAM_TAGS,
    SECTION_ID_PROGRAM,
    SECTION_ID_PROGRAM_STREAMS,
    SECTION_ID_PROGRAM_STREAM,
    SECTION_ID_PROGRAM_TAGS,
191
    SECTION_ID_PROGRAM_VERSION,
192
    SECTION_ID_PROGRAMS,
193 194
    SECTION_ID_ROOT,
    SECTION_ID_STREAM,
195
    SECTION_ID_STREAM_DISPOSITION,
196
    SECTION_ID_STREAMS,
197
    SECTION_ID_STREAM_TAGS,
198 199
    SECTION_ID_STREAM_SIDE_DATA_LIST,
    SECTION_ID_STREAM_SIDE_DATA,
200
    SECTION_ID_SUBTITLE,
201 202
} SectionID;

203
static struct section sections[] = {
204 205 206
    [SECTION_ID_CHAPTERS] =           { SECTION_ID_CHAPTERS, "chapters", SECTION_FLAG_IS_ARRAY, { SECTION_ID_CHAPTER, -1 } },
    [SECTION_ID_CHAPTER] =            { SECTION_ID_CHAPTER, "chapter", 0, { SECTION_ID_CHAPTER_TAGS, -1 } },
    [SECTION_ID_CHAPTER_TAGS] =       { SECTION_ID_CHAPTER_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "chapter_tags" },
207 208 209
    [SECTION_ID_ERROR] =              { SECTION_ID_ERROR, "error", 0, { -1 } },
    [SECTION_ID_FORMAT] =             { SECTION_ID_FORMAT, "format", 0, { SECTION_ID_FORMAT_TAGS, -1 } },
    [SECTION_ID_FORMAT_TAGS] =        { SECTION_ID_FORMAT_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "format_tags" },
210
    [SECTION_ID_FRAMES] =             { SECTION_ID_FRAMES, "frames", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME, SECTION_ID_SUBTITLE, -1 } },
211
    [SECTION_ID_FRAME] =              { SECTION_ID_FRAME, "frame", 0, { SECTION_ID_FRAME_TAGS, SECTION_ID_FRAME_SIDE_DATA_LIST, SECTION_ID_FRAME_LOGS, -1 } },
212
    [SECTION_ID_FRAME_TAGS] =         { SECTION_ID_FRAME_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "frame_tags" },
213
    [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" },
214 215 216
    [SECTION_ID_FRAME_SIDE_DATA] =     { SECTION_ID_FRAME_SIDE_DATA, "side_data", 0, { SECTION_ID_FRAME_SIDE_DATA_TIMECODE_LIST, -1 } },
    [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 } },
    [SECTION_ID_FRAME_SIDE_DATA_TIMECODE] =     { SECTION_ID_FRAME_SIDE_DATA_TIMECODE, "timecode", 0, { -1 } },
217 218
    [SECTION_ID_FRAME_LOGS] =         { SECTION_ID_FRAME_LOGS, "logs", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME_LOG, -1 } },
    [SECTION_ID_FRAME_LOG] =          { SECTION_ID_FRAME_LOG, "log", 0, { -1 },  },
219 220 221 222
    [SECTION_ID_LIBRARY_VERSIONS] =   { SECTION_ID_LIBRARY_VERSIONS, "library_versions", SECTION_FLAG_IS_ARRAY, { SECTION_ID_LIBRARY_VERSION, -1 } },
    [SECTION_ID_LIBRARY_VERSION] =    { SECTION_ID_LIBRARY_VERSION, "library_version", 0, { -1 } },
    [SECTION_ID_PACKETS] =            { SECTION_ID_PACKETS, "packets", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PACKET, -1} },
    [SECTION_ID_PACKETS_AND_FRAMES] = { SECTION_ID_PACKETS_AND_FRAMES, "packets_and_frames", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PACKET, -1} },
223 224
    [SECTION_ID_PACKET] =             { SECTION_ID_PACKET, "packet", 0, { SECTION_ID_PACKET_TAGS, SECTION_ID_PACKET_SIDE_DATA_LIST, -1 } },
    [SECTION_ID_PACKET_TAGS] =        { SECTION_ID_PACKET_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "packet_tags" },
225
    [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" },
226
    [SECTION_ID_PACKET_SIDE_DATA] =     { SECTION_ID_PACKET_SIDE_DATA, "side_data", 0, { -1 } },
227
    [SECTION_ID_PIXEL_FORMATS] =      { SECTION_ID_PIXEL_FORMATS, "pixel_formats", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PIXEL_FORMAT, -1 } },
228
    [SECTION_ID_PIXEL_FORMAT] =       { SECTION_ID_PIXEL_FORMAT, "pixel_format", 0, { SECTION_ID_PIXEL_FORMAT_FLAGS, SECTION_ID_PIXEL_FORMAT_COMPONENTS, -1 } },
229
    [SECTION_ID_PIXEL_FORMAT_FLAGS] = { SECTION_ID_PIXEL_FORMAT_FLAGS, "flags", 0, { -1 }, .unique_name = "pixel_format_flags" },
230 231
    [SECTION_ID_PIXEL_FORMAT_COMPONENTS] = { SECTION_ID_PIXEL_FORMAT_COMPONENTS, "components", SECTION_FLAG_IS_ARRAY, {SECTION_ID_PIXEL_FORMAT_COMPONENT, -1 }, .unique_name = "pixel_format_components" },
    [SECTION_ID_PIXEL_FORMAT_COMPONENT]  = { SECTION_ID_PIXEL_FORMAT_COMPONENT, "component", 0, { -1 } },
232 233 234 235 236 237
    [SECTION_ID_PROGRAM_STREAM_DISPOSITION] = { SECTION_ID_PROGRAM_STREAM_DISPOSITION, "disposition", 0, { -1 }, .unique_name = "program_stream_disposition" },
    [SECTION_ID_PROGRAM_STREAM_TAGS] =        { SECTION_ID_PROGRAM_STREAM_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "program_stream_tags" },
    [SECTION_ID_PROGRAM] =                    { SECTION_ID_PROGRAM, "program", 0, { SECTION_ID_PROGRAM_TAGS, SECTION_ID_PROGRAM_STREAMS, -1 } },
    [SECTION_ID_PROGRAM_STREAMS] =            { SECTION_ID_PROGRAM_STREAMS, "streams", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PROGRAM_STREAM, -1 }, .unique_name = "program_streams" },
    [SECTION_ID_PROGRAM_STREAM] =             { SECTION_ID_PROGRAM_STREAM, "stream", 0, { SECTION_ID_PROGRAM_STREAM_DISPOSITION, SECTION_ID_PROGRAM_STREAM_TAGS, -1 }, .unique_name = "program_stream" },
    [SECTION_ID_PROGRAM_TAGS] =               { SECTION_ID_PROGRAM_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "program_tags" },
238
    [SECTION_ID_PROGRAM_VERSION] =    { SECTION_ID_PROGRAM_VERSION, "program_version", 0, { -1 } },
239
    [SECTION_ID_PROGRAMS] =                   { SECTION_ID_PROGRAMS, "programs", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PROGRAM, -1 } },
240
    [SECTION_ID_ROOT] =               { SECTION_ID_ROOT, "root", SECTION_FLAG_IS_WRAPPER,
241
                                        { SECTION_ID_CHAPTERS, SECTION_ID_FORMAT, SECTION_ID_FRAMES, SECTION_ID_PROGRAMS, SECTION_ID_STREAMS,
242 243
                                          SECTION_ID_PACKETS, SECTION_ID_ERROR, SECTION_ID_PROGRAM_VERSION, SECTION_ID_LIBRARY_VERSIONS,
                                          SECTION_ID_PIXEL_FORMATS, -1} },
244
    [SECTION_ID_STREAMS] =            { SECTION_ID_STREAMS, "streams", SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM, -1 } },
245
    [SECTION_ID_STREAM] =             { SECTION_ID_STREAM, "stream", 0, { SECTION_ID_STREAM_DISPOSITION, SECTION_ID_STREAM_TAGS, SECTION_ID_STREAM_SIDE_DATA_LIST, -1 } },
246 247
    [SECTION_ID_STREAM_DISPOSITION] = { SECTION_ID_STREAM_DISPOSITION, "disposition", 0, { -1 }, .unique_name = "stream_disposition" },
    [SECTION_ID_STREAM_TAGS] =        { SECTION_ID_STREAM_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "stream_tags" },
248
    [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" },
249
    [SECTION_ID_STREAM_SIDE_DATA] =     { SECTION_ID_STREAM_SIDE_DATA, "side_data", 0, { -1 } },
250
    [SECTION_ID_SUBTITLE] =           { SECTION_ID_SUBTITLE, "subtitle", 0, { -1 } },
251 252
};

253
static const OptionDef *options;
Stefano Sabatini's avatar
Stefano Sabatini committed
254 255 256

/* FFprobe context */
static const char *input_filename;
257
static const char *print_input_filename;
258
static AVInputFormat *iformat = NULL;
Stefano Sabatini's avatar
Stefano Sabatini committed
259

260 261
static struct AVHashContext *hash;

262 263 264 265 266 267 268 269 270 271 272 273 274
static const struct {
    double bin_val;
    double dec_val;
    const char *bin_str;
    const char *dec_str;
} si_prefixes[] = {
    { 1.0, 1.0, "", "" },
    { 1.024e3, 1e3, "Ki", "K" },
    { 1.048576e6, 1e6, "Mi", "M" },
    { 1.073741824e9, 1e9, "Gi", "G" },
    { 1.099511627776e12, 1e12, "Ti", "T" },
    { 1.125899906842624e15, 1e15, "Pi", "P" },
};
Stefano Sabatini's avatar
Stefano Sabatini committed
275

276 277 278 279
static const char unit_second_str[]         = "s"    ;
static const char unit_hertz_str[]          = "Hz"   ;
static const char unit_byte_str[]           = "byte" ;
static const char unit_bit_per_second_str[] = "bit/s";
280

281
static int nb_streams;
282 283
static uint64_t *nb_streams_packets;
static uint64_t *nb_streams_frames;
284
static int *selected_streams;
Stefano Sabatini's avatar
Stefano Sabatini committed
285

286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349
#if HAVE_THREADS
pthread_mutex_t log_mutex;
#endif
typedef struct LogBuffer {
    char *context_name;
    int log_level;
    char *log_message;
    AVClassCategory category;
    char *parent_name;
    AVClassCategory parent_category;
}LogBuffer;

static LogBuffer *log_buffer;
static int log_buffer_size;

static void log_callback(void *ptr, int level, const char *fmt, va_list vl)
{
    AVClass* avc = ptr ? *(AVClass **) ptr : NULL;
    va_list vl2;
    char line[1024];
    static int print_prefix = 1;
    void *new_log_buffer;

    va_copy(vl2, vl);
    av_log_default_callback(ptr, level, fmt, vl);
    av_log_format_line(ptr, level, fmt, vl2, line, sizeof(line), &print_prefix);
    va_end(vl2);

#if HAVE_THREADS
    pthread_mutex_lock(&log_mutex);

    new_log_buffer = av_realloc_array(log_buffer, log_buffer_size + 1, sizeof(*log_buffer));
    if (new_log_buffer) {
        char *msg;
        int i;

        log_buffer = new_log_buffer;
        memset(&log_buffer[log_buffer_size], 0, sizeof(log_buffer[log_buffer_size]));
        log_buffer[log_buffer_size].context_name= avc ? av_strdup(avc->item_name(ptr)) : NULL;
        if (avc) {
            if (avc->get_category) log_buffer[log_buffer_size].category = avc->get_category(ptr);
            else                   log_buffer[log_buffer_size].category = avc->category;
        }
        log_buffer[log_buffer_size].log_level   = level;
        msg = log_buffer[log_buffer_size].log_message = av_strdup(line);
        for (i=strlen(msg) - 1; i>=0 && msg[i] == '\n'; i--) {
            msg[i] = 0;
        }
        if (avc && avc->parent_log_context_offset) {
            AVClass** parent = *(AVClass ***) (((uint8_t *) ptr) +
                                   avc->parent_log_context_offset);
            if (parent && *parent) {
                log_buffer[log_buffer_size].parent_name = av_strdup((*parent)->item_name(parent));
                log_buffer[log_buffer_size].parent_category =
                    (*parent)->get_category ? (*parent)->get_category(parent) :(*parent)->category;
            }
        }
        log_buffer_size ++;
    }

    pthread_mutex_unlock(&log_mutex);
#endif
}

350
static void ffprobe_cleanup(int ret)
351
{
352 353 354
    int i;
    for (i = 0; i < FF_ARRAY_ELEMS(sections); i++)
        av_dict_free(&(sections[i].entries_to_show));
355 356 357 358

#if HAVE_THREADS
    pthread_mutex_destroy(&log_mutex);
#endif
359 360
}

361
struct unit_value {
362
    union { double d; long long int i; } val;
363 364 365 366
    const char *unit;
};

static char *value_string(char *buf, int buf_size, struct unit_value uv)
Stefano Sabatini's avatar
Stefano Sabatini committed
367
{
368
    double vald;
369
    long long int vali;
370 371 372 373 374 375
    int show_float = 0;

    if (uv.unit == unit_second_str) {
        vald = uv.val.d;
        show_float = 1;
    } else {
376
        vald = vali = uv.val.i;
377 378 379
    }

    if (uv.unit == unit_second_str && use_value_sexagesimal_format) {
Stefano Sabatini's avatar
Stefano Sabatini committed
380 381
        double secs;
        int hours, mins;
382
        secs  = vald;
Stefano Sabatini's avatar
Stefano Sabatini committed
383 384 385 386 387
        mins  = (int)secs / 60;
        secs  = secs - mins * 60;
        hours = mins / 60;
        mins %= 60;
        snprintf(buf, buf_size, "%d:%02d:%09.6f", hours, mins, secs);
388 389
    } else {
        const char *prefix_string = "";
Stefano Sabatini's avatar
Stefano Sabatini committed
390

391
        if (use_value_prefix && vald > 1) {
392 393 394
            long long int index;

            if (uv.unit == unit_byte_str && use_byte_value_binary_prefix) {
395
                index = (long long int) (log2(vald)) / 10;
396 397 398
                index = av_clip(index, 0, FF_ARRAY_ELEMS(si_prefixes) - 1);
                vald /= si_prefixes[index].bin_val;
                prefix_string = si_prefixes[index].bin_str;
399 400
            } else {
                index = (long long int) (log10(vald)) / 3;
401 402 403
                index = av_clip(index, 0, FF_ARRAY_ELEMS(si_prefixes) - 1);
                vald /= si_prefixes[index].dec_val;
                prefix_string = si_prefixes[index].dec_str;
404
            }
405
            vali = vald;
406
        }
Stefano Sabatini's avatar
Stefano Sabatini committed
407

408
        if (show_float || (use_value_prefix && vald != (long long int)vald))
409
            snprintf(buf, buf_size, "%f", vald);
410
        else
411
            snprintf(buf, buf_size, "%lld", vali);
412
        av_strlcatf(buf, buf_size, "%s%s%s", *prefix_string || show_value_unit ? " " : "",
413
                 prefix_string, show_value_unit ? uv.unit : "");
Stefano Sabatini's avatar
Stefano Sabatini committed
414 415 416 417 418
    }

    return buf;
}

419
/* WRITERS API */
420

421
typedef struct WriterContext WriterContext;
422

423
#define WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS 1
424
#define WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER 2
425

426 427 428 429
typedef enum {
    WRITER_STRING_VALIDATION_FAIL,
    WRITER_STRING_VALIDATION_REPLACE,
    WRITER_STRING_VALIDATION_IGNORE,
430
    WRITER_STRING_VALIDATION_NB
431 432
} StringValidation;

433
typedef struct Writer {
434
    const AVClass *priv_class;      ///< private class of the writer, if any
435 436
    int priv_size;                  ///< private size for the writer context
    const char *name;
437

438
    int  (*init)  (WriterContext *wctx);
439 440
    void (*uninit)(WriterContext *wctx);

441 442
    void (*print_section_header)(WriterContext *wctx);
    void (*print_section_footer)(WriterContext *wctx);
443
    void (*print_integer)       (WriterContext *wctx, const char *, long long int);
444
    void (*print_rational)      (WriterContext *wctx, AVRational *q, char *sep);
445
    void (*print_string)        (WriterContext *wctx, const char *, const char *);
446
    int flags;                  ///< a combination or WRITER_FLAG_*
447 448
} Writer;

449 450
#define SECTION_MAX_NB_LEVELS 10

451 452 453 454 455
struct WriterContext {
    const AVClass *class;           ///< class of the writer
    const Writer *writer;           ///< the Writer of which this is an instance
    char *name;                     ///< name of this writer instance
    void *priv;                     ///< private data for use by the filter
456 457 458 459 460 461 462 463 464 465 466

    const struct section *sections; ///< array containing all sections
    int nb_sections;                ///< number of sections

    int level;                      ///< current level, starting from 0

    /** number of the item printed in the given section, starting from 0 */
    unsigned int nb_item[SECTION_MAX_NB_LEVELS];

    /** section per each level */
    const struct section *section[SECTION_MAX_NB_LEVELS];
467 468
    AVBPrint section_pbuf[SECTION_MAX_NB_LEVELS]; ///< generic print buffer dedicated to each section,
                                                  ///  used by various writers
469

470 471 472
    unsigned int nb_section_packet; ///< number of the packet section in case we are in "packets_and_frames" section
    unsigned int nb_section_frame;  ///< number of the frame  section in case we are in "packets_and_frames" section
    unsigned int nb_section_packet_frame; ///< nb_section_packet or nb_section_frame according if is_packets_and_frames
473

474
    int string_validation;
475 476
    char *string_validation_replacement;
    unsigned int string_validation_utf8_flags;
477
};
478

479 480 481 482 483 484
static const char *writer_get_name(void *p)
{
    WriterContext *wctx = p;
    return wctx->writer->name;
}

485 486
#define OFFSET(x) offsetof(WriterContext, x)

487 488 489 490 491 492 493 494 495
static const AVOption writer_options[] = {
    { "string_validation", "set string validation mode",
      OFFSET(string_validation), AV_OPT_TYPE_INT, {.i64=WRITER_STRING_VALIDATION_REPLACE}, 0, WRITER_STRING_VALIDATION_NB-1, .unit = "sv" },
    { "sv", "set string validation mode",
      OFFSET(string_validation), AV_OPT_TYPE_INT, {.i64=WRITER_STRING_VALIDATION_REPLACE}, 0, WRITER_STRING_VALIDATION_NB-1, .unit = "sv" },
    { "ignore",  NULL, 0, AV_OPT_TYPE_CONST, {.i64 = WRITER_STRING_VALIDATION_IGNORE},  .unit = "sv" },
    { "replace", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = WRITER_STRING_VALIDATION_REPLACE}, .unit = "sv" },
    { "fail",    NULL, 0, AV_OPT_TYPE_CONST, {.i64 = WRITER_STRING_VALIDATION_FAIL},    .unit = "sv" },
    { "string_validation_replacement", "set string validation replacement string", OFFSET(string_validation_replacement), AV_OPT_TYPE_STRING, {.str=""}},
496
    { "svr", "set string validation replacement string", OFFSET(string_validation_replacement), AV_OPT_TYPE_STRING, {.str="\xEF\xBF\xBD"}},
497
    { NULL }
498 499
};

500 501 502 503 504 505 506 507
static void *writer_child_next(void *obj, void *prev)
{
    WriterContext *ctx = obj;
    if (!prev && ctx->writer && ctx->writer->priv_class && ctx->priv)
        return ctx->priv;
    return NULL;
}

508
static const AVClass writer_class = {
509 510 511 512
    .class_name = "Writer",
    .item_name  = writer_get_name,
    .option     = writer_options,
    .version    = LIBAVUTIL_VERSION_INT,
513
    .child_next = writer_child_next,
514 515
};

516
static void writer_close(WriterContext **wctx)
517
{
518 519
    int i;

520 521
    if (!*wctx)
        return;
522

523 524
    if ((*wctx)->writer->uninit)
        (*wctx)->writer->uninit(*wctx);
525 526
    for (i = 0; i < SECTION_MAX_NB_LEVELS; i++)
        av_bprint_finalize(&(*wctx)->section_pbuf[i], NULL);
527 528
    if ((*wctx)->writer->priv_class)
        av_opt_free((*wctx)->priv);
529
    av_freep(&((*wctx)->priv));
530
    av_opt_free(*wctx);
531
    av_freep(wctx);
532 533
}

534 535 536 537 538 539 540 541 542
static void bprint_bytes(AVBPrint *bp, const uint8_t *ubuf, size_t ubuf_size)
{
    int i;
    av_bprintf(bp, "0X");
    for (i = 0; i < ubuf_size; i++)
        av_bprintf(bp, "%02X", ubuf[i]);
}


543 544
static int writer_open(WriterContext **wctx, const Writer *writer, const char *args,
                       const struct section *sections, int nb_sections)
545
{
546
    int i, ret = 0;
547

548
    if (!(*wctx = av_mallocz(sizeof(WriterContext)))) {
549 550
        ret = AVERROR(ENOMEM);
        goto fail;
551 552
    }

553 554 555
    if (!((*wctx)->priv = av_mallocz(writer->priv_size))) {
        ret = AVERROR(ENOMEM);
        goto fail;
556
    }
557

558
    (*wctx)->class = &writer_class;
559
    (*wctx)->writer = writer;
560 561 562
    (*wctx)->level = -1;
    (*wctx)->sections = sections;
    (*wctx)->nb_sections = nb_sections;
563

564 565
    av_opt_set_defaults(*wctx);

566 567 568 569
    if (writer->priv_class) {
        void *priv_ctx = (*wctx)->priv;
        *((const AVClass **)priv_ctx) = writer->priv_class;
        av_opt_set_defaults(priv_ctx);
570
    }
571

572 573 574 575 576 577 578 579
    /* convert options to dictionary */
    if (args) {
        AVDictionary *opts = NULL;
        AVDictionaryEntry *opt = NULL;

        if ((ret = av_dict_parse_string(&opts, args, "=", ":", 0)) < 0) {
            av_log(*wctx, AV_LOG_ERROR, "Failed to parse option string '%s' provided to writer context\n", args);
            av_dict_free(&opts);
580
            goto fail;
581 582 583 584 585 586 587 588 589 590 591 592
        }

        while ((opt = av_dict_get(opts, "", opt, AV_DICT_IGNORE_SUFFIX))) {
            if ((ret = av_opt_set(*wctx, opt->key, opt->value, AV_OPT_SEARCH_CHILDREN)) < 0) {
                av_log(*wctx, AV_LOG_ERROR, "Failed to set option '%s' with value '%s' provided to writer context\n",
                       opt->key, opt->value);
                av_dict_free(&opts);
                goto fail;
            }
        }

        av_dict_free(&opts);
593
    }
594

595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614
    /* validate replace string */
    {
        const uint8_t *p = (*wctx)->string_validation_replacement;
        const uint8_t *endp = p + strlen(p);
        while (*p) {
            const uint8_t *p0 = p;
            int32_t code;
            ret = av_utf8_decode(&code, &p, endp, (*wctx)->string_validation_utf8_flags);
            if (ret < 0) {
                AVBPrint bp;
                av_bprint_init(&bp, 0, AV_BPRINT_SIZE_AUTOMATIC);
                bprint_bytes(&bp, p0, p-p0),
                    av_log(wctx, AV_LOG_ERROR,
                           "Invalid UTF8 sequence %s found in string validation replace '%s'\n",
                           bp.str, (*wctx)->string_validation_replacement);
                return ret;
            }
        }
    }

615 616 617
    for (i = 0; i < SECTION_MAX_NB_LEVELS; i++)
        av_bprint_init(&(*wctx)->section_pbuf[i], 1, AV_BPRINT_SIZE_UNLIMITED);

618
    if ((*wctx)->writer->init)
619
        ret = (*wctx)->writer->init(*wctx);
620 621 622 623 624 625 626
    if (ret < 0)
        goto fail;

    return 0;

fail:
    writer_close(wctx);
627 628 629
    return ret;
}

630
static inline void writer_print_section_header(WriterContext *wctx,
631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649
                                               int section_id)
{
    int parent_section_id;
    wctx->level++;
    av_assert0(wctx->level < SECTION_MAX_NB_LEVELS);
    parent_section_id = wctx->level ?
        (wctx->section[wctx->level-1])->id : SECTION_ID_NONE;

    wctx->nb_item[wctx->level] = 0;
    wctx->section[wctx->level] = &wctx->sections[section_id];

    if (section_id == SECTION_ID_PACKETS_AND_FRAMES) {
        wctx->nb_section_packet = wctx->nb_section_frame =
        wctx->nb_section_packet_frame = 0;
    } else if (parent_section_id == SECTION_ID_PACKETS_AND_FRAMES) {
        wctx->nb_section_packet_frame = section_id == SECTION_ID_PACKET ?
            wctx->nb_section_packet : wctx->nb_section_frame;
    }

650
    if (wctx->writer->print_section_header)
651
        wctx->writer->print_section_header(wctx);
652 653
}

654
static inline void writer_print_section_footer(WriterContext *wctx)
655
{
656 657 658 659 660 661 662 663 664
    int section_id = wctx->section[wctx->level]->id;
    int parent_section_id = wctx->level ?
        wctx->section[wctx->level-1]->id : SECTION_ID_NONE;

    if (parent_section_id != SECTION_ID_NONE)
        wctx->nb_item[wctx->level-1]++;
    if (parent_section_id == SECTION_ID_PACKETS_AND_FRAMES) {
        if (section_id == SECTION_ID_PACKET) wctx->nb_section_packet++;
        else                                     wctx->nb_section_frame++;
665
    }
666 667 668
    if (wctx->writer->print_section_footer)
        wctx->writer->print_section_footer(wctx);
    wctx->level--;
669
}
670

671
static inline void writer_print_integer(WriterContext *wctx,
672
                                        const char *key, long long int val)
673
{
674 675 676
    const struct section *section = wctx->section[wctx->level];

    if (section->show_all_entries || av_dict_get(section->entries_to_show, key, NULL, 0)) {
677
        wctx->writer->print_integer(wctx, key, val);
678
        wctx->nb_item[wctx->level]++;
679
    }
680 681
}

682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739
static inline int validate_string(WriterContext *wctx, char **dstp, const char *src)
{
    const uint8_t *p, *endp;
    AVBPrint dstbuf;
    int invalid_chars_nb = 0, ret = 0;

    av_bprint_init(&dstbuf, 0, AV_BPRINT_SIZE_UNLIMITED);

    endp = src + strlen(src);
    for (p = (uint8_t *)src; *p;) {
        uint32_t code;
        int invalid = 0;
        const uint8_t *p0 = p;

        if (av_utf8_decode(&code, &p, endp, wctx->string_validation_utf8_flags) < 0) {
            AVBPrint bp;
            av_bprint_init(&bp, 0, AV_BPRINT_SIZE_AUTOMATIC);
            bprint_bytes(&bp, p0, p-p0);
            av_log(wctx, AV_LOG_DEBUG,
                   "Invalid UTF-8 sequence %s found in string '%s'\n", bp.str, src);
            invalid = 1;
        }

        if (invalid) {
            invalid_chars_nb++;

            switch (wctx->string_validation) {
            case WRITER_STRING_VALIDATION_FAIL:
                av_log(wctx, AV_LOG_ERROR,
                       "Invalid UTF-8 sequence found in string '%s'\n", src);
                ret = AVERROR_INVALIDDATA;
                goto end;
                break;

            case WRITER_STRING_VALIDATION_REPLACE:
                av_bprintf(&dstbuf, "%s", wctx->string_validation_replacement);
                break;
            }
        }

        if (!invalid || wctx->string_validation == WRITER_STRING_VALIDATION_IGNORE)
            av_bprint_append_data(&dstbuf, p0, p-p0);
    }

    if (invalid_chars_nb && wctx->string_validation == WRITER_STRING_VALIDATION_REPLACE) {
        av_log(wctx, AV_LOG_WARNING,
               "%d invalid UTF-8 sequence(s) found in string '%s', replaced with '%s'\n",
               invalid_chars_nb, src, wctx->string_validation_replacement);
    }

end:
    av_bprint_finalize(&dstbuf, dstp);
    return ret;
}

#define PRINT_STRING_OPT      1
#define PRINT_STRING_VALIDATE 2

740
static inline int writer_print_string(WriterContext *wctx,
741
                                      const char *key, const char *val, int flags)
742
{
743
    const struct section *section = wctx->section[wctx->level];
744
    int ret = 0;
745

746 747
    if ((flags & PRINT_STRING_OPT)
        && !(wctx->writer->flags & WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS))
748
        return 0;
749 750

    if (section->show_all_entries || av_dict_get(section->entries_to_show, key, NULL, 0)) {
751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769
        if (flags & PRINT_STRING_VALIDATE) {
            char *key1 = NULL, *val1 = NULL;
            ret = validate_string(wctx, &key1, key);
            if (ret < 0) goto end;
            ret = validate_string(wctx, &val1, val);
            if (ret < 0) goto end;
            wctx->writer->print_string(wctx, key1, val1);
        end:
            if (ret < 0) {
                av_log(wctx, AV_LOG_ERROR,
                       "Invalid key=value string combination %s=%s in section %s\n",
                       key, val, section->unique_name);
            }
            av_free(key1);
            av_free(val1);
        } else {
            wctx->writer->print_string(wctx, key, val);
        }

770
        wctx->nb_item[wctx->level]++;
771
    }
772 773

    return ret;
774 775
}

776 777 778 779 780 781 782 783 784
static inline void writer_print_rational(WriterContext *wctx,
                                         const char *key, AVRational q, char sep)
{
    AVBPrint buf;
    av_bprint_init(&buf, 0, AV_BPRINT_SIZE_AUTOMATIC);
    av_bprintf(&buf, "%d%c%d", q.num, sep, q.den);
    writer_print_string(wctx, key, buf.str, 0);
}

785
static void writer_print_time(WriterContext *wctx, const char *key,
786
                              int64_t ts, const AVRational *time_base, int is_duration)
787 788 789
{
    char buf[128];

790
    if ((!is_duration && ts == AV_NOPTS_VALUE) || (is_duration && ts == 0)) {
791
        writer_print_string(wctx, key, "N/A", PRINT_STRING_OPT);
792 793 794 795 796 797 798 799
    } else {
        double d = ts * av_q2d(*time_base);
        struct unit_value uv;
        uv.val.d = d;
        uv.unit = unit_second_str;
        value_string(buf, sizeof(buf), uv);
        writer_print_string(wctx, key, buf, 0);
    }
800 801
}

802
static void writer_print_ts(WriterContext *wctx, const char *key, int64_t ts, int is_duration)
803
{
804
    if ((!is_duration && ts == AV_NOPTS_VALUE) || (is_duration && ts == 0)) {
805
        writer_print_string(wctx, key, "N/A", PRINT_STRING_OPT);
806
    } else {
807
        writer_print_integer(wctx, key, ts);
808 809 810
    }
}

811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838
static void writer_print_data(WriterContext *wctx, const char *name,
                              uint8_t *data, int size)
{
    AVBPrint bp;
    int offset = 0, l, i;

    av_bprint_init(&bp, 0, AV_BPRINT_SIZE_UNLIMITED);
    av_bprintf(&bp, "\n");
    while (size) {
        av_bprintf(&bp, "%08x: ", offset);
        l = FFMIN(size, 16);
        for (i = 0; i < l; i++) {
            av_bprintf(&bp, "%02x", data[i]);
            if (i & 1)
                av_bprintf(&bp, " ");
        }
        av_bprint_chars(&bp, ' ', 41 - 2 * i - i / 2);
        for (i = 0; i < l; i++)
            av_bprint_chars(&bp, data[i] - 32U < 95 ? data[i] : '.', 1);
        av_bprintf(&bp, "\n");
        offset += l;
        data   += l;
        size   -= l;
    }
    writer_print_string(wctx, name, bp.str, 0);
    av_bprint_finalize(&bp, NULL);
}

839 840 841 842 843 844 845 846 847 848 849 850 851 852 853
static void writer_print_data_hash(WriterContext *wctx, const char *name,
                                   uint8_t *data, int size)
{
    char *p, buf[AV_HASH_MAX_SIZE * 2 + 64] = { 0 };

    if (!hash)
        return;
    av_hash_init(hash);
    av_hash_update(hash, data, size);
    snprintf(buf, sizeof(buf), "%s:", av_hash_get_name(hash));
    p = buf + strlen(buf);
    av_hash_final_hex(hash, p, buf + sizeof(buf) - p);
    writer_print_string(wctx, name, buf, 0);
}

854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879
static void writer_print_integers(WriterContext *wctx, const char *name,
                                  uint8_t *data, int size, const char *format,
                                  int columns, int bytes, int offset_add)
{
    AVBPrint bp;
    int offset = 0, l, i;

    av_bprint_init(&bp, 0, AV_BPRINT_SIZE_UNLIMITED);
    av_bprintf(&bp, "\n");
    while (size) {
        av_bprintf(&bp, "%08x: ", offset);
        l = FFMIN(size, columns);
        for (i = 0; i < l; i++) {
            if      (bytes == 1) av_bprintf(&bp, format, *data);
            else if (bytes == 2) av_bprintf(&bp, format, AV_RN16(data));
            else if (bytes == 4) av_bprintf(&bp, format, AV_RN32(data));
            data += bytes;
            size --;
        }
        av_bprintf(&bp, "\n");
        offset += offset_add;
    }
    writer_print_string(wctx, name, bp.str, 0);
    av_bprint_finalize(&bp, NULL);
}

880 881
#define MAX_REGISTERED_WRITERS_NB 64

882
static const Writer *registered_writers[MAX_REGISTERED_WRITERS_NB + 1];
883

884
static int writer_register(const Writer *writer)
885
{
886 887 888 889 890 891 892
    static int next_registered_writer_idx = 0;

    if (next_registered_writer_idx == MAX_REGISTERED_WRITERS_NB)
        return AVERROR(ENOMEM);

    registered_writers[next_registered_writer_idx++] = writer;
    return 0;
893 894
}

895
static const Writer *writer_get_by_name(const char *name)
896 897 898 899 900 901 902 903 904
{
    int i;

    for (i = 0; registered_writers[i]; i++)
        if (!strcmp(registered_writers[i]->name, name))
            return registered_writers[i];

    return NULL;
}
905

906

907
/* WRITERS */
908

909 910 911 912 913 914
#define DEFINE_WRITER_CLASS(name)                   \
static const char *name##_get_name(void *ctx)       \
{                                                   \
    return #name ;                                  \
}                                                   \
static const AVClass name##_class = {               \
915 916 917
    .class_name = #name,                            \
    .item_name  = name##_get_name,                  \
    .option     = name##_options                    \
918 919
}

920 921
/* Default output */

922 923
typedef struct DefaultContext {
    const AVClass *class;
924
    int nokey;
925
    int noprint_wrappers;
926
    int nested_section[SECTION_MAX_NB_LEVELS];
927 928
} DefaultContext;

929
#undef OFFSET
930 931 932
#define OFFSET(x) offsetof(DefaultContext, x)

static const AVOption default_options[] = {
933 934 935 936
    { "noprint_wrappers", "do not print headers and footers", OFFSET(noprint_wrappers), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
    { "nw",               "do not print headers and footers", OFFSET(noprint_wrappers), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
    { "nokey",          "force no key printing",     OFFSET(nokey),          AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
    { "nk",             "force no key printing",     OFFSET(nokey),          AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
937 938 939
    {NULL},
};

940
DEFINE_WRITER_CLASS(default);
941

942 943 944 945 946
/* lame uppercasing routine, assumes the string is lower case ASCII */
static inline char *upcase_string(char *dst, size_t dst_size, const char *src)
{
    int i;
    for (i = 0; src[i] && i < dst_size-1; i++)
947
        dst[i] = av_toupper(src[i]);
948 949 950 951
    dst[i] = 0;
    return dst;
}

952
static void default_print_section_header(WriterContext *wctx)
953
{
954
    DefaultContext *def = wctx->priv;
955
    char buf[32];
956 957 958 959
    const struct section *section = wctx->section[wctx->level];
    const struct section *parent_section = wctx->level ?
        wctx->section[wctx->level-1] : NULL;

960
    av_bprint_clear(&wctx->section_pbuf[wctx->level]);
961 962 963
    if (parent_section &&
        !(parent_section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY))) {
        def->nested_section[wctx->level] = 1;
964 965
        av_bprintf(&wctx->section_pbuf[wctx->level], "%s%s:",
                   wctx->section_pbuf[wctx->level-1].str,
966 967 968 969 970
                   upcase_string(buf, sizeof(buf),
                                 av_x_if_null(section->element_name, section->name)));
    }

    if (def->noprint_wrappers || def->nested_section[wctx->level])
971
        return;
972

973 974
    if (!(section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY)))
        printf("[%s]\n", upcase_string(buf, sizeof(buf), section->name));
975 976
}

977
static void default_print_section_footer(WriterContext *wctx)
978
{
979
    DefaultContext *def = wctx->priv;
980
    const struct section *section = wctx->section[wctx->level];
981 982
    char buf[32];

983
    if (def->noprint_wrappers || def->nested_section[wctx->level])
984 985 986 987
        return;

    if (!(section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY)))
        printf("[/%s]\n", upcase_string(buf, sizeof(buf), section->name));
988 989 990 991
}

static void default_print_str(WriterContext *wctx, const char *key, const char *value)
{
992
    DefaultContext *def = wctx->priv;
993

994
    if (!def->nokey)
995
        printf("%s%s=", wctx->section_pbuf[wctx->level].str, key);
996
    printf("%s\n", value);
997 998
}

999
static void default_print_int(WriterContext *wctx, const char *key, long long int value)
1000
{
1001 1002 1003
    DefaultContext *def = wctx->priv;

    if (!def->nokey)
1004
        printf("%s%s=", wctx->section_pbuf[wctx->level].str, key);
1005
    printf("%lld\n", value);
1006 1007
}

1008
static const Writer default_writer = {
1009
    .name                  = "default",
1010
    .priv_size             = sizeof(DefaultContext),
1011 1012 1013 1014
    .print_section_header  = default_print_section_header,
    .print_section_footer  = default_print_section_footer,
    .print_integer         = default_print_int,
    .print_string          = default_print_str,
1015
    .flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS,
1016
    .priv_class            = &default_class,
1017 1018
};

1019 1020 1021
/* Compact output */

/**
1022
 * Apply C-language-like string escaping.
1023
 */
1024
static const char *c_escape_str(AVBPrint *dst, const char *src, const char sep, void *log_ctx)
1025 1026 1027 1028
{
    const char *p;

    for (p = src; *p; p++) {
1029
        switch (*p) {
1030 1031
        case '\b': av_bprintf(dst, "%s", "\\b");  break;
        case '\f': av_bprintf(dst, "%s", "\\f");  break;
1032 1033 1034
        case '\n': av_bprintf(dst, "%s", "\\n");  break;
        case '\r': av_bprintf(dst, "%s", "\\r");  break;
        case '\\': av_bprintf(dst, "%s", "\\\\"); break;
1035 1036
        default:
            if (*p == sep)
1037 1038
                av_bprint_chars(dst, '\\', 1);
            av_bprint_chars(dst, *p, 1);
1039 1040
        }
    }
1041
    return dst->str;
1042 1043 1044 1045 1046
}

/**
 * Quote fields containing special characters, check RFC4180.
 */
1047
static const char *csv_escape_str(AVBPrint *dst, const char *src, const char sep, void *log_ctx)
1048
{
1049 1050
    char meta_chars[] = { sep, '"', '\n', '\r', '\0' };
    int needs_quoting = !!src[strcspn(src, meta_chars)];
1051

1052
    if (needs_quoting)
1053
        av_bprint_chars(dst, '"', 1);
1054

1055 1056
    for (; *src; src++) {
        if (*src == '"')
1057
            av_bprint_chars(dst, '"', 1);
1058
        av_bprint_chars(dst, *src, 1);
1059
    }
1060
    if (needs_quoting)
1061
        av_bprint_chars(dst, '"', 1);
1062
    return dst->str;
1063 1064
}

1065
static const char *none_escape_str(AVBPrint *dst, const char *src, const char sep, void *log_ctx)
1066 1067 1068 1069 1070 1071 1072 1073 1074
{
    return src;
}

typedef struct CompactContext {
    const AVClass *class;
    char *item_sep_str;
    char item_sep;
    int nokey;
1075
    int print_section;
1076
    char *escape_mode_str;
1077
    const char * (*escape_str)(AVBPrint *dst, const char *src, const char sep, void *log_ctx);
1078
    int nested_section[SECTION_MAX_NB_LEVELS];
1079 1080
    int has_nested_elems[SECTION_MAX_NB_LEVELS];
    int terminate_line[SECTION_MAX_NB_LEVELS];
1081 1082
} CompactContext;

1083
#undef OFFSET
1084 1085 1086 1087 1088
#define OFFSET(x) offsetof(CompactContext, x)

static const AVOption compact_options[]= {
    {"item_sep", "set item separator",    OFFSET(item_sep_str),    AV_OPT_TYPE_STRING, {.str="|"},  CHAR_MIN, CHAR_MAX },
    {"s",        "set item separator",    OFFSET(item_sep_str),    AV_OPT_TYPE_STRING, {.str="|"},  CHAR_MIN, CHAR_MAX },
1089 1090
    {"nokey",    "force no key printing", OFFSET(nokey),           AV_OPT_TYPE_BOOL,   {.i64=0},    0,        1        },
    {"nk",       "force no key printing", OFFSET(nokey),           AV_OPT_TYPE_BOOL,   {.i64=0},    0,        1        },
1091 1092
    {"escape",   "set escape mode",       OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="c"},  CHAR_MIN, CHAR_MAX },
    {"e",        "set escape mode",       OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="c"},  CHAR_MIN, CHAR_MAX },
1093 1094
    {"print_section", "print section name", OFFSET(print_section), AV_OPT_TYPE_BOOL,   {.i64=1},    0,        1        },
    {"p",             "print section name", OFFSET(print_section), AV_OPT_TYPE_BOOL,   {.i64=1},    0,        1        },
1095 1096 1097
    {NULL},
};

1098
DEFINE_WRITER_CLASS(compact);
1099

1100
static av_cold int compact_init(WriterContext *wctx)
1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121
{
    CompactContext *compact = wctx->priv;

    if (strlen(compact->item_sep_str) != 1) {
        av_log(wctx, AV_LOG_ERROR, "Item separator '%s' specified, but must contain a single character\n",
               compact->item_sep_str);
        return AVERROR(EINVAL);
    }
    compact->item_sep = compact->item_sep_str[0];

    if      (!strcmp(compact->escape_mode_str, "none")) compact->escape_str = none_escape_str;
    else if (!strcmp(compact->escape_mode_str, "c"   )) compact->escape_str = c_escape_str;
    else if (!strcmp(compact->escape_mode_str, "csv" )) compact->escape_str = csv_escape_str;
    else {
        av_log(wctx, AV_LOG_ERROR, "Unknown escape mode '%s'\n", compact->escape_mode_str);
        return AVERROR(EINVAL);
    }

    return 0;
}

1122
static void compact_print_section_header(WriterContext *wctx)
1123 1124
{
    CompactContext *compact = wctx->priv;
1125
    const struct section *section = wctx->section[wctx->level];
1126 1127
    const struct section *parent_section = wctx->level ?
        wctx->section[wctx->level-1] : NULL;
1128 1129
    compact->terminate_line[wctx->level] = 1;
    compact->has_nested_elems[wctx->level] = 0;
1130

1131
    av_bprint_clear(&wctx->section_pbuf[wctx->level]);
1132
    if (!(section->flags & SECTION_FLAG_IS_ARRAY) && parent_section &&
1133 1134
        !(parent_section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY))) {
        compact->nested_section[wctx->level] = 1;
1135
        compact->has_nested_elems[wctx->level-1] = 1;
1136 1137
        av_bprintf(&wctx->section_pbuf[wctx->level], "%s%s:",
                   wctx->section_pbuf[wctx->level-1].str,
1138
                   (char *)av_x_if_null(section->element_name, section->name));
1139
        wctx->nb_item[wctx->level] = wctx->nb_item[wctx->level-1];
1140 1141 1142 1143 1144 1145 1146 1147 1148 1149
    } else {
        if (parent_section && compact->has_nested_elems[wctx->level-1] &&
            (section->flags & SECTION_FLAG_IS_ARRAY)) {
            compact->terminate_line[wctx->level-1] = 0;
            printf("\n");
        }
        if (compact->print_section &&
            !(section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY)))
            printf("%s%c", section->name, compact->item_sep);
    }
1150 1151
}

1152
static void compact_print_section_footer(WriterContext *wctx)
1153
{
1154
    CompactContext *compact = wctx->priv;
1155

1156
    if (!compact->nested_section[wctx->level] &&
1157
        compact->terminate_line[wctx->level] &&
1158
        !(wctx->section[wctx->level]->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY)))
1159
        printf("\n");
1160 1161 1162 1163 1164
}

static void compact_print_str(WriterContext *wctx, const char *key, const char *value)
{
    CompactContext *compact = wctx->priv;
1165
    AVBPrint buf;
1166

1167
    if (wctx->nb_item[wctx->level]) printf("%c", compact->item_sep);
1168
    if (!compact->nokey)
1169
        printf("%s%s=", wctx->section_pbuf[wctx->level].str, key);
1170 1171 1172
    av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
    printf("%s", compact->escape_str(&buf, value, compact->item_sep, wctx));
    av_bprint_finalize(&buf, NULL);
1173 1174
}

1175
static void compact_print_int(WriterContext *wctx, const char *key, long long int value)
1176 1177 1178
{
    CompactContext *compact = wctx->priv;

1179
    if (wctx->nb_item[wctx->level]) printf("%c", compact->item_sep);
1180
    if (!compact->nokey)
1181
        printf("%s%s=", wctx->section_pbuf[wctx->level].str, key);
1182
    printf("%lld", value);
1183 1184
}

1185
static const Writer compact_writer = {
1186 1187 1188 1189 1190 1191 1192 1193
    .name                 = "compact",
    .priv_size            = sizeof(CompactContext),
    .init                 = compact_init,
    .print_section_header = compact_print_section_header,
    .print_section_footer = compact_print_section_footer,
    .print_integer        = compact_print_int,
    .print_string         = compact_print_str,
    .flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS,
1194
    .priv_class           = &compact_class,
1195 1196
};

1197 1198
/* CSV output */

1199 1200 1201 1202 1203 1204
#undef OFFSET
#define OFFSET(x) offsetof(CompactContext, x)

static const AVOption csv_options[] = {
    {"item_sep", "set item separator",    OFFSET(item_sep_str),    AV_OPT_TYPE_STRING, {.str=","},  CHAR_MIN, CHAR_MAX },
    {"s",        "set item separator",    OFFSET(item_sep_str),    AV_OPT_TYPE_STRING, {.str=","},  CHAR_MIN, CHAR_MAX },
1205 1206
    {"nokey",    "force no key printing", OFFSET(nokey),           AV_OPT_TYPE_BOOL,   {.i64=1},    0,        1        },
    {"nk",       "force no key printing", OFFSET(nokey),           AV_OPT_TYPE_BOOL,   {.i64=1},    0,        1        },
1207 1208
    {"escape",   "set escape mode",       OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="csv"}, CHAR_MIN, CHAR_MAX },
    {"e",        "set escape mode",       OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="csv"}, CHAR_MIN, CHAR_MAX },
1209 1210
    {"print_section", "print section name", OFFSET(print_section), AV_OPT_TYPE_BOOL,   {.i64=1},    0,        1        },
    {"p",             "print section name", OFFSET(print_section), AV_OPT_TYPE_BOOL,   {.i64=1},    0,        1        },
1211 1212 1213 1214
    {NULL},
};

DEFINE_WRITER_CLASS(csv);
1215

1216
static const Writer csv_writer = {
1217 1218
    .name                 = "csv",
    .priv_size            = sizeof(CompactContext),
1219
    .init                 = compact_init,
1220 1221 1222 1223 1224
    .print_section_header = compact_print_section_header,
    .print_section_footer = compact_print_section_footer,
    .print_integer        = compact_print_int,
    .print_string         = compact_print_str,
    .flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS,
1225
    .priv_class           = &csv_class,
1226 1227
};

1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242
/* Flat output */

typedef struct FlatContext {
    const AVClass *class;
    const char *sep_str;
    char sep;
    int hierarchical;
} FlatContext;

#undef OFFSET
#define OFFSET(x) offsetof(FlatContext, x)

static const AVOption flat_options[]= {
    {"sep_char", "set separator",    OFFSET(sep_str),    AV_OPT_TYPE_STRING, {.str="."},  CHAR_MIN, CHAR_MAX },
    {"s",        "set separator",    OFFSET(sep_str),    AV_OPT_TYPE_STRING, {.str="."},  CHAR_MIN, CHAR_MAX },
1243 1244
    {"hierarchical", "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 },
    {"h",            "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 },
1245 1246 1247
    {NULL},
};

1248
DEFINE_WRITER_CLASS(flat);
1249

1250
static av_cold int flat_init(WriterContext *wctx)
1251 1252 1253 1254 1255 1256 1257 1258 1259
{
    FlatContext *flat = wctx->priv;

    if (strlen(flat->sep_str) != 1) {
        av_log(wctx, AV_LOG_ERROR, "Item separator '%s' specified, but must contain a single character\n",
               flat->sep_str);
        return AVERROR(EINVAL);
    }
    flat->sep = flat->sep_str[0];
1260

1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288
    return 0;
}

static const char *flat_escape_key_str(AVBPrint *dst, const char *src, const char sep)
{
    const char *p;

    for (p = src; *p; p++) {
        if (!((*p >= '0' && *p <= '9') ||
              (*p >= 'a' && *p <= 'z') ||
              (*p >= 'A' && *p <= 'Z')))
            av_bprint_chars(dst, '_', 1);
        else
            av_bprint_chars(dst, *p, 1);
    }
    return dst->str;
}

static const char *flat_escape_value_str(AVBPrint *dst, const char *src)
{
    const char *p;

    for (p = src; *p; p++) {
        switch (*p) {
        case '\n': av_bprintf(dst, "%s", "\\n");  break;
        case '\r': av_bprintf(dst, "%s", "\\r");  break;
        case '\\': av_bprintf(dst, "%s", "\\\\"); break;
        case '"':  av_bprintf(dst, "%s", "\\\""); break;
1289 1290
        case '`':  av_bprintf(dst, "%s", "\\`");  break;
        case '$':  av_bprintf(dst, "%s", "\\$");  break;
1291 1292 1293 1294 1295 1296
        default:   av_bprint_chars(dst, *p, 1);   break;
        }
    }
    return dst->str;
}

1297
static void flat_print_section_header(WriterContext *wctx)
1298 1299
{
    FlatContext *flat = wctx->priv;
1300
    AVBPrint *buf = &wctx->section_pbuf[wctx->level];
1301 1302 1303
    const struct section *section = wctx->section[wctx->level];
    const struct section *parent_section = wctx->level ?
        wctx->section[wctx->level-1] : NULL;
1304

1305 1306
    /* build section header */
    av_bprint_clear(buf);
1307 1308
    if (!parent_section)
        return;
1309
    av_bprintf(buf, "%s", wctx->section_pbuf[wctx->level-1].str);
1310

1311 1312 1313
    if (flat->hierarchical ||
        !(section->flags & (SECTION_FLAG_IS_ARRAY|SECTION_FLAG_IS_WRAPPER))) {
        av_bprintf(buf, "%s%s", wctx->section[wctx->level]->name, flat->sep_str);
1314

1315 1316 1317 1318 1319
        if (parent_section->flags & SECTION_FLAG_IS_ARRAY) {
            int n = parent_section->id == SECTION_ID_PACKETS_AND_FRAMES ?
                wctx->nb_section_packet_frame : wctx->nb_item[wctx->level-1];
            av_bprintf(buf, "%d%s", n, flat->sep_str);
        }
1320
    }
1321 1322 1323 1324
}

static void flat_print_int(WriterContext *wctx, const char *key, long long int value)
{
1325
    printf("%s%s=%lld\n", wctx->section_pbuf[wctx->level].str, key, value);
1326 1327 1328 1329 1330 1331 1332
}

static void flat_print_str(WriterContext *wctx, const char *key, const char *value)
{
    FlatContext *flat = wctx->priv;
    AVBPrint buf;

1333
    printf("%s", wctx->section_pbuf[wctx->level].str);
1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348
    av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
    printf("%s=", flat_escape_key_str(&buf, key, flat->sep));
    av_bprint_clear(&buf);
    printf("\"%s\"\n", flat_escape_value_str(&buf, value));
    av_bprint_finalize(&buf, NULL);
}

static const Writer flat_writer = {
    .name                  = "flat",
    .priv_size             = sizeof(FlatContext),
    .init                  = flat_init,
    .print_section_header  = flat_print_section_header,
    .print_integer         = flat_print_int,
    .print_string          = flat_print_str,
    .flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS|WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER,
1349
    .priv_class            = &flat_class,
1350 1351
};

1352 1353
/* INI format output */

1354
typedef struct INIContext {
1355 1356 1357 1358 1359 1360 1361 1362
    const AVClass *class;
    int hierarchical;
} INIContext;

#undef OFFSET
#define OFFSET(x) offsetof(INIContext, x)

static const AVOption ini_options[] = {
1363 1364
    {"hierarchical", "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 },
    {"h",            "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 },
1365 1366 1367
    {NULL},
};

1368
DEFINE_WRITER_CLASS(ini);
1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396

static char *ini_escape_str(AVBPrint *dst, const char *src)
{
    int i = 0;
    char c = 0;

    while (c = src[i++]) {
        switch (c) {
        case '\b': av_bprintf(dst, "%s", "\\b"); break;
        case '\f': av_bprintf(dst, "%s", "\\f"); break;
        case '\n': av_bprintf(dst, "%s", "\\n"); break;
        case '\r': av_bprintf(dst, "%s", "\\r"); break;
        case '\t': av_bprintf(dst, "%s", "\\t"); break;
        case '\\':
        case '#' :
        case '=' :
        case ':' : av_bprint_chars(dst, '\\', 1);
        default:
            if ((unsigned char)c < 32)
                av_bprintf(dst, "\\x00%02x", c & 0xff);
            else
                av_bprint_chars(dst, c, 1);
            break;
        }
    }
    return dst->str;
}

1397
static void ini_print_section_header(WriterContext *wctx)
1398 1399
{
    INIContext *ini = wctx->priv;
1400
    AVBPrint *buf = &wctx->section_pbuf[wctx->level];
1401 1402 1403
    const struct section *section = wctx->section[wctx->level];
    const struct section *parent_section = wctx->level ?
        wctx->section[wctx->level-1] : NULL;
1404

1405 1406
    av_bprint_clear(buf);
    if (!parent_section) {
1407 1408 1409
        printf("# ffprobe output\n\n");
        return;
    }
1410

1411
    if (wctx->nb_item[wctx->level-1])
1412 1413
        printf("\n");

1414
    av_bprintf(buf, "%s", wctx->section_pbuf[wctx->level-1].str);
1415 1416 1417
    if (ini->hierarchical ||
        !(section->flags & (SECTION_FLAG_IS_ARRAY|SECTION_FLAG_IS_WRAPPER))) {
        av_bprintf(buf, "%s%s", buf->str[0] ? "." : "", wctx->section[wctx->level]->name);
1418

1419 1420 1421 1422 1423
        if (parent_section->flags & SECTION_FLAG_IS_ARRAY) {
            int n = parent_section->id == SECTION_ID_PACKETS_AND_FRAMES ?
                wctx->nb_section_packet_frame : wctx->nb_item[wctx->level-1];
            av_bprintf(buf, ".%d", n);
        }
1424
    }
1425

1426
    if (!(section->flags & (SECTION_FLAG_IS_ARRAY|SECTION_FLAG_IS_WRAPPER)))
1427
        printf("[%s]\n", buf->str);
1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452
}

static void ini_print_str(WriterContext *wctx, const char *key, const char *value)
{
    AVBPrint buf;

    av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
    printf("%s=", ini_escape_str(&buf, key));
    av_bprint_clear(&buf);
    printf("%s\n", ini_escape_str(&buf, value));
    av_bprint_finalize(&buf, NULL);
}

static void ini_print_int(WriterContext *wctx, const char *key, long long int value)
{
    printf("%s=%lld\n", key, value);
}

static const Writer ini_writer = {
    .name                  = "ini",
    .priv_size             = sizeof(INIContext),
    .print_section_header  = ini_print_section_header,
    .print_integer         = ini_print_int,
    .print_string          = ini_print_str,
    .flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS|WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER,
1453
    .priv_class            = &ini_class,
1454 1455
};

1456 1457
/* JSON output */

1458
typedef struct JSONContext {
1459
    const AVClass *class;
1460
    int indent_level;
1461 1462
    int compact;
    const char *item_sep, *item_start_end;
1463 1464
} JSONContext;

1465 1466 1467 1468
#undef OFFSET
#define OFFSET(x) offsetof(JSONContext, x)

static const AVOption json_options[]= {
1469 1470
    { "compact", "enable compact output", OFFSET(compact), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
    { "c",       "enable compact output", OFFSET(compact), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
1471 1472 1473
    { NULL }
};

1474
DEFINE_WRITER_CLASS(json);
1475

1476
static av_cold int json_init(WriterContext *wctx)
1477 1478
{
    JSONContext *json = wctx->priv;
1479 1480 1481

    json->item_sep       = json->compact ? ", " : ",\n";
    json->item_start_end = json->compact ? " "  : "\n";
1482 1483 1484 1485

    return 0;
}

1486
static const char *json_escape_str(AVBPrint *dst, const char *src, void *log_ctx)
1487 1488 1489
{
    static const char json_escape[] = {'"', '\\', '\b', '\f', '\n', '\r', '\t', 0};
    static const char json_subst[]  = {'"', '\\',  'b',  'f',  'n',  'r',  't', 0};
1490 1491 1492 1493 1494
    const char *p;

    for (p = src; *p; p++) {
        char *s = strchr(json_escape, *p);
        if (s) {
1495 1496
            av_bprint_chars(dst, '\\', 1);
            av_bprint_chars(dst, json_subst[s - json_escape], 1);
1497
        } else if ((unsigned char)*p < 32) {
1498
            av_bprintf(dst, "\\u00%02x", *p & 0xff);
1499
        } else {
1500
            av_bprint_chars(dst, *p, 1);
1501 1502
        }
    }
1503
    return dst->str;
1504 1505
}

1506 1507
#define JSON_INDENT() printf("%*c", json->indent_level * 4, ' ')

1508
static void json_print_section_header(WriterContext *wctx)
1509 1510
{
    JSONContext *json = wctx->priv;
1511
    AVBPrint buf;
1512 1513 1514
    const struct section *section = wctx->section[wctx->level];
    const struct section *parent_section = wctx->level ?
        wctx->section[wctx->level-1] : NULL;
1515

1516 1517 1518 1519 1520 1521 1522
    if (wctx->level && wctx->nb_item[wctx->level-1])
        printf(",\n");

    if (section->flags & SECTION_FLAG_IS_WRAPPER) {
        printf("{\n");
        json->indent_level++;
    } else {
1523
        av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
1524 1525 1526
        json_escape_str(&buf, section->name, wctx);
        JSON_INDENT();

1527
        json->indent_level++;
1528 1529
        if (section->flags & SECTION_FLAG_IS_ARRAY) {
            printf("\"%s\": [\n", buf.str);
1530
        } else if (parent_section && !(parent_section->flags & SECTION_FLAG_IS_ARRAY)) {
1531 1532 1533 1534 1535
            printf("\"%s\": {%s", buf.str, json->item_start_end);
        } else {
            printf("{%s", json->item_start_end);

            /* this is required so the parser can distinguish between packets and frames */
1536
            if (parent_section && parent_section->id == SECTION_ID_PACKETS_AND_FRAMES) {
1537 1538
                if (!json->compact)
                    JSON_INDENT();
1539
                printf("\"type\": \"%s\"", section->name);
1540 1541 1542
            }
        }
        av_bprint_finalize(&buf, NULL);
1543
    }
1544 1545
}

1546
static void json_print_section_footer(WriterContext *wctx)
1547 1548
{
    JSONContext *json = wctx->priv;
1549
    const struct section *section = wctx->section[wctx->level];
1550

1551 1552 1553 1554
    if (wctx->level == 0) {
        json->indent_level--;
        printf("\n}\n");
    } else if (section->flags & SECTION_FLAG_IS_ARRAY) {
1555
        printf("\n");
1556
        json->indent_level--;
1557 1558
        JSON_INDENT();
        printf("]");
1559 1560 1561
    } else {
        printf("%s", json->item_start_end);
        json->indent_level--;
1562 1563
        if (!json->compact)
            JSON_INDENT();
1564
        printf("}");
1565
    }
1566 1567 1568
}

static inline void json_print_item_str(WriterContext *wctx,
1569
                                       const char *key, const char *value)
1570
{
1571
    AVBPrint buf;
1572

1573 1574
    av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
    printf("\"%s\":", json_escape_str(&buf, key,   wctx));
1575
    av_bprint_clear(&buf);
1576 1577
    printf(" \"%s\"", json_escape_str(&buf, value, wctx));
    av_bprint_finalize(&buf, NULL);
1578 1579 1580 1581
}

static void json_print_str(WriterContext *wctx, const char *key, const char *value)
{
1582
    JSONContext *json = wctx->priv;
1583 1584
    const struct section *parent_section = wctx->level ?
        wctx->section[wctx->level-1] : NULL;
1585

1586
    if (wctx->nb_item[wctx->level] || (parent_section && parent_section->id == SECTION_ID_PACKETS_AND_FRAMES))
1587
        printf("%s", json->item_sep);
1588 1589
    if (!json->compact)
        JSON_INDENT();
1590
    json_print_item_str(wctx, key, value);
1591 1592
}

1593
static void json_print_int(WriterContext *wctx, const char *key, long long int value)
1594
{
1595
    JSONContext *json = wctx->priv;
1596 1597
    const struct section *parent_section = wctx->level ?
        wctx->section[wctx->level-1] : NULL;
1598
    AVBPrint buf;
1599

1600
    if (wctx->nb_item[wctx->level] || (parent_section && parent_section->id == SECTION_ID_PACKETS_AND_FRAMES))
1601
        printf("%s", json->item_sep);
1602 1603
    if (!json->compact)
        JSON_INDENT();
1604 1605 1606 1607

    av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
    printf("\"%s\": %lld", json_escape_str(&buf, key, wctx), value);
    av_bprint_finalize(&buf, NULL);
1608
}
1609

1610
static const Writer json_writer = {
1611 1612
    .name                 = "json",
    .priv_size            = sizeof(JSONContext),
1613
    .init                 = json_init,
1614 1615 1616 1617
    .print_section_header = json_print_section_header,
    .print_section_footer = json_print_section_footer,
    .print_integer        = json_print_int,
    .print_string         = json_print_str,
1618
    .flags = WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER,
1619
    .priv_class           = &json_class,
1620 1621
};

1622 1623
/* XML output */

1624
typedef struct XMLContext {
1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635
    const AVClass *class;
    int within_tag;
    int indent_level;
    int fully_qualified;
    int xsd_strict;
} XMLContext;

#undef OFFSET
#define OFFSET(x) offsetof(XMLContext, x)

static const AVOption xml_options[] = {
1636 1637 1638 1639
    {"fully_qualified", "specify if the output should be fully qualified", OFFSET(fully_qualified), AV_OPT_TYPE_BOOL, {.i64=0},  0, 1 },
    {"q",               "specify if the output should be fully qualified", OFFSET(fully_qualified), AV_OPT_TYPE_BOOL, {.i64=0},  0, 1 },
    {"xsd_strict",      "ensure that the output is XSD compliant",         OFFSET(xsd_strict),      AV_OPT_TYPE_BOOL, {.i64=0},  0, 1 },
    {"x",               "ensure that the output is XSD compliant",         OFFSET(xsd_strict),      AV_OPT_TYPE_BOOL, {.i64=0},  0, 1 },
1640 1641 1642
    {NULL},
};

1643
DEFINE_WRITER_CLASS(xml);
1644

1645
static av_cold int xml_init(WriterContext *wctx)
1646 1647 1648 1649 1650 1651 1652 1653 1654 1655
{
    XMLContext *xml = wctx->priv;

    if (xml->xsd_strict) {
        xml->fully_qualified = 1;
#define CHECK_COMPLIANCE(opt, opt_name)                                 \
        if (opt) {                                                      \
            av_log(wctx, AV_LOG_ERROR,                                  \
                   "XSD-compliant output selected but option '%s' was selected, XML output may be non-compliant.\n" \
                   "You need to disable such option with '-no%s'\n", opt_name, opt_name); \
1656
            return AVERROR(EINVAL);                                     \
1657 1658 1659 1660
        }
        CHECK_COMPLIANCE(show_private_data, "private");
        CHECK_COMPLIANCE(show_value_unit,   "unit");
        CHECK_COMPLIANCE(use_value_prefix,  "prefix");
1661 1662 1663 1664 1665 1666 1667

        if (do_show_frames && do_show_packets) {
            av_log(wctx, AV_LOG_ERROR,
                   "Interleaved frames and packets are not allowed in XSD. "
                   "Select only one between the -show_frames and the -show_packets options.\n");
            return AVERROR(EINVAL);
        }
1668 1669 1670 1671 1672
    }

    return 0;
}

1673
static const char *xml_escape_str(AVBPrint *dst, const char *src, void *log_ctx)
1674 1675 1676
{
    const char *p;

1677
    for (p = src; *p; p++) {
1678
        switch (*p) {
1679 1680 1681
        case '&' : av_bprintf(dst, "%s", "&amp;");  break;
        case '<' : av_bprintf(dst, "%s", "&lt;");   break;
        case '>' : av_bprintf(dst, "%s", "&gt;");   break;
1682
        case '"' : av_bprintf(dst, "%s", "&quot;"); break;
1683 1684
        case '\'': av_bprintf(dst, "%s", "&apos;"); break;
        default: av_bprint_chars(dst, *p, 1);
1685 1686 1687
        }
    }

1688
    return dst->str;
1689 1690
}

1691
#define XML_INDENT() printf("%*c", xml->indent_level * 4, ' ')
1692

1693
static void xml_print_section_header(WriterContext *wctx)
1694 1695
{
    XMLContext *xml = wctx->priv;
1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709
    const struct section *section = wctx->section[wctx->level];
    const struct section *parent_section = wctx->level ?
        wctx->section[wctx->level-1] : NULL;

    if (wctx->level == 0) {
        const char *qual = " xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' "
            "xmlns:ffprobe='http://www.ffmpeg.org/schema/ffprobe' "
            "xsi:schemaLocation='http://www.ffmpeg.org/schema/ffprobe ffprobe.xsd'";

        printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
        printf("<%sffprobe%s>\n",
               xml->fully_qualified ? "ffprobe:" : "",
               xml->fully_qualified ? qual : "");
        return;
1710 1711
    }

1712 1713 1714
    if (xml->within_tag) {
        xml->within_tag = 0;
        printf(">\n");
1715
    }
1716
    if (section->flags & SECTION_FLAG_HAS_VARIABLE_FIELDS) {
1717 1718
        xml->indent_level++;
    } else {
1719 1720 1721
        if (parent_section && (parent_section->flags & SECTION_FLAG_IS_WRAPPER) &&
            wctx->level && wctx->nb_item[wctx->level-1])
            printf("\n");
1722
        xml->indent_level++;
1723

1724 1725 1726 1727 1728 1729 1730
        if (section->flags & SECTION_FLAG_IS_ARRAY) {
            XML_INDENT(); printf("<%s>\n", section->name);
        } else {
            XML_INDENT(); printf("<%s ", section->name);
            xml->within_tag = 1;
        }
    }
1731 1732
}

1733
static void xml_print_section_footer(WriterContext *wctx)
1734 1735
{
    XMLContext *xml = wctx->priv;
1736
    const struct section *section = wctx->section[wctx->level];
1737

1738 1739 1740 1741
    if (wctx->level == 0) {
        printf("</%sffprobe>\n", xml->fully_qualified ? "ffprobe:" : "");
    } else if (xml->within_tag) {
        xml->within_tag = 0;
1742
        printf("/>\n");
1743
        xml->indent_level--;
1744
    } else if (section->flags & SECTION_FLAG_HAS_VARIABLE_FIELDS) {
1745 1746 1747 1748
        xml->indent_level--;
    } else {
        XML_INDENT(); printf("</%s>\n", section->name);
        xml->indent_level--;
1749 1750 1751 1752 1753
    }
}

static void xml_print_str(WriterContext *wctx, const char *key, const char *value)
{
1754
    AVBPrint buf;
1755 1756
    XMLContext *xml = wctx->priv;
    const struct section *section = wctx->section[wctx->level];
1757

1758
    av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
1759

1760
    if (section->flags & SECTION_FLAG_HAS_VARIABLE_FIELDS) {
1761
        XML_INDENT();
1762 1763
        printf("<%s key=\"%s\"",
               section->element_name, xml_escape_str(&buf, key, wctx));
1764 1765 1766 1767 1768 1769 1770 1771
        av_bprint_clear(&buf);
        printf(" value=\"%s\"/>\n", xml_escape_str(&buf, value, wctx));
    } else {
        if (wctx->nb_item[wctx->level])
            printf(" ");
        printf("%s=\"%s\"", key, xml_escape_str(&buf, value, wctx));
    }

1772
    av_bprint_finalize(&buf, NULL);
1773 1774 1775 1776
}

static void xml_print_int(WriterContext *wctx, const char *key, long long int value)
{
1777
    if (wctx->nb_item[wctx->level])
1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789
        printf(" ");
    printf("%s=\"%lld\"", key, value);
}

static Writer xml_writer = {
    .name                 = "xml",
    .priv_size            = sizeof(XMLContext),
    .init                 = xml_init,
    .print_section_header = xml_print_section_header,
    .print_section_footer = xml_print_section_footer,
    .print_integer        = xml_print_int,
    .print_string         = xml_print_str,
1790
    .flags = WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER,
1791
    .priv_class           = &xml_class,
1792 1793
};

1794 1795 1796 1797 1798 1799 1800 1801 1802
static void writer_register_all(void)
{
    static int initialized;

    if (initialized)
        return;
    initialized = 1;

    writer_register(&default_writer);
1803
    writer_register(&compact_writer);
1804
    writer_register(&csv_writer);
1805
    writer_register(&flat_writer);
1806
    writer_register(&ini_writer);
1807
    writer_register(&json_writer);
1808
    writer_register(&xml_writer);
1809 1810 1811
}

#define print_fmt(k, f, ...) do {              \
1812 1813 1814
    av_bprint_clear(&pbuf);                    \
    av_bprintf(&pbuf, f, __VA_ARGS__);         \
    writer_print_string(w, k, pbuf.str, 0);    \
1815 1816 1817
} while (0)

#define print_int(k, v)         writer_print_integer(w, k, v)
1818
#define print_q(k, v, s)        writer_print_rational(w, k, v, s)
1819
#define print_str(k, v)         writer_print_string(w, k, v, 0)
1820 1821
#define print_str_opt(k, v)     writer_print_string(w, k, v, PRINT_STRING_OPT)
#define print_str_validate(k, v) writer_print_string(w, k, v, PRINT_STRING_VALIDATE)
1822 1823 1824 1825
#define print_time(k, v, tb)    writer_print_time(w, k, v, tb, 0)
#define print_ts(k, v)          writer_print_ts(w, k, v, 0)
#define print_duration_time(k, v, tb) writer_print_time(w, k, v, tb, 1)
#define print_duration_ts(k, v)       writer_print_ts(w, k, v, 1)
1826 1827 1828 1829 1830 1831 1832
#define print_val(k, v, u) do {                                     \
    struct unit_value uv;                                           \
    uv.val.i = v;                                                   \
    uv.unit = u;                                                    \
    writer_print_string(w, k, value_string(val_str, sizeof(val_str), uv), 0); \
} while (0)

1833 1834
#define print_section_header(s) writer_print_section_header(w, s)
#define print_section_footer(s) writer_print_section_footer(w, s)
1835

1836 1837 1838 1839 1840 1841 1842 1843
#define REALLOCZ_ARRAY_STREAM(ptr, cur_n, new_n)                        \
{                                                                       \
    ret = av_reallocp_array(&(ptr), (new_n), sizeof(*(ptr)));           \
    if (ret < 0)                                                        \
        goto end;                                                       \
    memset( (ptr) + (cur_n), 0, ((new_n) - (cur_n)) * sizeof(*(ptr)) ); \
}

1844
static inline int show_tags(WriterContext *w, AVDictionary *tags, int section_id)
1845 1846
{
    AVDictionaryEntry *tag = NULL;
1847
    int ret = 0;
1848 1849

    if (!tags)
1850
        return 0;
1851
    writer_print_section_header(w, section_id);
1852 1853

    while ((tag = av_dict_get(tags, "", tag, AV_DICT_IGNORE_SUFFIX))) {
1854
        if ((ret = print_str_validate(tag->key, tag->value)) < 0)
1855 1856
            break;
    }
1857
    writer_print_section_footer(w);
1858 1859

    return ret;
1860
}
1861

1862
static void print_pkt_side_data(WriterContext *w,
1863
                                AVCodecParameters *par,
1864 1865 1866 1867 1868 1869 1870
                                const AVPacketSideData *side_data,
                                int nb_side_data,
                                SectionID id_data_list,
                                SectionID id_data)
{
    int i;

1871
    writer_print_section_header(w, id_data_list);
1872 1873 1874 1875
    for (i = 0; i < nb_side_data; i++) {
        const AVPacketSideData *sd = &side_data[i];
        const char *name = av_packet_side_data_name(sd->type);

1876
        writer_print_section_header(w, id_data);
1877 1878 1879 1880
        print_str("side_data_type", name ? name : "unknown");
        if (sd->type == AV_PKT_DATA_DISPLAYMATRIX && sd->size >= 9*4) {
            writer_print_integers(w, "displaymatrix", sd->data, 9, " %11d", 3, 4, 1);
            print_int("rotation", av_display_rotation_get((int32_t *)sd->data));
1881 1882 1883 1884
        } else if (sd->type == AV_PKT_DATA_STEREO3D) {
            const AVStereo3D *stereo = (AVStereo3D *)sd->data;
            print_str("type", av_stereo3d_type_name(stereo->type));
            print_int("inverted", !!(stereo->flags & AV_STEREO3D_FLAG_INVERT));
1885 1886
        } else if (sd->type == AV_PKT_DATA_SPHERICAL) {
            const AVSphericalMapping *spherical = (AVSphericalMapping *)sd->data;
1887 1888
            print_str("projection", av_spherical_projection_name(spherical->projection));
            if (spherical->projection == AV_SPHERICAL_CUBEMAP) {
1889 1890 1891 1892 1893 1894 1895 1896 1897
                print_int("padding", spherical->padding);
            } else if (spherical->projection == AV_SPHERICAL_EQUIRECTANGULAR_TILE) {
                size_t l, t, r, b;
                av_spherical_tile_bounds(spherical, par->width, par->height,
                                         &l, &t, &r, &b);
                print_int("bound_left", l);
                print_int("bound_top", t);
                print_int("bound_right", r);
                print_int("bound_bottom", b);
1898
            }
1899 1900 1901 1902

            print_int("yaw", (double) spherical->yaw / (1 << 16));
            print_int("pitch", (double) spherical->pitch / (1 << 16));
            print_int("roll", (double) spherical->roll / (1 << 16));
1903 1904 1905 1906 1907
        } else if (sd->type == AV_PKT_DATA_SKIP_SAMPLES && sd->size == 10) {
            print_int("skip_samples",    AV_RL32(sd->data));
            print_int("discard_padding", AV_RL32(sd->data + 4));
            print_int("skip_reason",     AV_RL8(sd->data + 8));
            print_int("discard_reason",  AV_RL8(sd->data + 9));
1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926
        } else if (sd->type == AV_PKT_DATA_MASTERING_DISPLAY_METADATA) {
            AVMasteringDisplayMetadata *metadata = (AVMasteringDisplayMetadata *)sd->data;

            if (metadata->has_primaries) {
                print_q("red_x", metadata->display_primaries[0][0], '/');
                print_q("red_y", metadata->display_primaries[0][1], '/');
                print_q("green_x", metadata->display_primaries[1][0], '/');
                print_q("green_y", metadata->display_primaries[1][1], '/');
                print_q("blue_x", metadata->display_primaries[2][0], '/');
                print_q("blue_y", metadata->display_primaries[2][1], '/');

                print_q("white_point_x", metadata->white_point[0], '/');
                print_q("white_point_y", metadata->white_point[1], '/');
            }

            if (metadata->has_luminance) {
                print_q("min_luminance", metadata->min_luminance, '/');
                print_q("max_luminance", metadata->max_luminance, '/');
            }
1927 1928 1929 1930
        } else if (sd->type == AV_PKT_DATA_CONTENT_LIGHT_LEVEL) {
            AVContentLightMetadata *metadata = (AVContentLightMetadata *)sd->data;
            print_int("max_content", metadata->MaxCLL);
            print_int("max_average", metadata->MaxFALL);
1931 1932 1933 1934 1935 1936
        }
        writer_print_section_footer(w);
    }
    writer_print_section_footer(w);
}

1937
static void print_color_range(WriterContext *w, enum AVColorRange color_range)
1938 1939 1940
{
    const char *val = av_color_range_name(color_range);
    if (!val || color_range == AVCOL_RANGE_UNSPECIFIED) {
1941
        print_str_opt("color_range", "unknown");
1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956
    } else {
        print_str("color_range", val);
    }
}

static void print_color_space(WriterContext *w, enum AVColorSpace color_space)
{
    const char *val = av_color_space_name(color_space);
    if (!val || color_space == AVCOL_SPC_UNSPECIFIED) {
        print_str_opt("color_space", "unknown");
    } else {
        print_str("color_space", val);
    }
}

1957 1958 1959 1960 1961 1962 1963 1964 1965 1966
static void print_primaries(WriterContext *w, enum AVColorPrimaries color_primaries)
{
    const char *val = av_color_primaries_name(color_primaries);
    if (!val || color_primaries == AVCOL_PRI_UNSPECIFIED) {
        print_str_opt("color_primaries", "unknown");
    } else {
        print_str("color_primaries", val);
    }
}

1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987
static void print_color_trc(WriterContext *w, enum AVColorTransferCharacteristic color_trc)
{
    const char *val = av_color_transfer_name(color_trc);
    if (!val || color_trc == AVCOL_TRC_UNSPECIFIED) {
        print_str_opt("color_transfer", "unknown");
    } else {
        print_str("color_transfer", val);
    }
}

static void print_chroma_location(WriterContext *w, enum AVChromaLocation chroma_location)
{
    const char *val = av_chroma_location_name(chroma_location);
    if (!val || chroma_location == AVCHROMA_LOC_UNSPECIFIED) {
        print_str_opt("chroma_location", "unspecified");
    } else {
        print_str("chroma_location", val);
    }
}


1988 1989 1990 1991 1992 1993 1994 1995
static void clear_log(int need_lock)
{
    int i;

    if (need_lock)
        pthread_mutex_lock(&log_mutex);
    for (i=0; i<log_buffer_size; i++) {
        av_freep(&log_buffer[i].context_name);
1996
        av_freep(&log_buffer[i].parent_name);
1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038
        av_freep(&log_buffer[i].log_message);
    }
    log_buffer_size = 0;
    if(need_lock)
        pthread_mutex_unlock(&log_mutex);
}

static int show_log(WriterContext *w, int section_ids, int section_id, int log_level)
{
    int i;
    pthread_mutex_lock(&log_mutex);
    if (!log_buffer_size) {
        pthread_mutex_unlock(&log_mutex);
        return 0;
    }
    writer_print_section_header(w, section_ids);

    for (i=0; i<log_buffer_size; i++) {
        if (log_buffer[i].log_level <= log_level) {
            writer_print_section_header(w, section_id);
            print_str("context", log_buffer[i].context_name);
            print_int("level", log_buffer[i].log_level);
            print_int("category", log_buffer[i].category);
            if (log_buffer[i].parent_name) {
                print_str("parent_context", log_buffer[i].parent_name);
                print_int("parent_category", log_buffer[i].parent_category);
            } else {
                print_str_opt("parent_context", "N/A");
                print_str_opt("parent_category", "N/A");
            }
            print_str("message", log_buffer[i].log_message);
            writer_print_section_footer(w);
        }
    }
    clear_log(0);
    pthread_mutex_unlock(&log_mutex);

    writer_print_section_footer(w);

    return 0;
}

2039
static void show_packet(WriterContext *w, InputFile *ifile, AVPacket *pkt, int packet_idx)
2040 2041
{
    char val_str[128];
2042
    AVStream *st = ifile->streams[pkt->stream_index].st;
2043
    AVBPrint pbuf;
2044
    const char *s;
2045

2046 2047
    av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);

2048 2049
    writer_print_section_header(w, SECTION_ID_PACKET);

2050
    s = av_get_media_type_string(st->codecpar->codec_type);
2051 2052
    if (s) print_str    ("codec_type", s);
    else   print_str_opt("codec_type", "unknown");
2053
    print_int("stream_index",     pkt->stream_index);
2054 2055 2056 2057
    print_ts  ("pts",             pkt->pts);
    print_time("pts_time",        pkt->pts, &st->time_base);
    print_ts  ("dts",             pkt->dts);
    print_time("dts_time",        pkt->dts, &st->time_base);
2058 2059
    print_duration_ts("duration",        pkt->duration);
    print_duration_time("duration_time", pkt->duration, &st->time_base);
2060 2061
    print_duration_ts("convergence_duration", pkt->convergence_duration);
    print_duration_time("convergence_duration_time", pkt->convergence_duration, &st->time_base);
2062
    print_val("size",             pkt->size, unit_byte_str);
2063 2064
    if (pkt->pos != -1) print_fmt    ("pos", "%"PRId64, pkt->pos);
    else                print_str_opt("pos", "N/A");
2065 2066
    print_fmt("flags", "%c%c",      pkt->flags & AV_PKT_FLAG_KEY ? 'K' : '_',
              pkt->flags & AV_PKT_FLAG_DISCARD ? 'D' : '_');
2067 2068

    if (pkt->side_data_elems) {
2069 2070 2071 2072 2073 2074 2075 2076 2077 2078
        int size;
        const uint8_t *side_metadata;

        side_metadata = av_packet_get_side_data(pkt, AV_PKT_DATA_STRINGS_METADATA, &size);
        if (side_metadata && size && do_show_packet_tags) {
            AVDictionary *dict = NULL;
            if (av_packet_unpack_dictionary(side_metadata, size, &dict) >= 0)
                show_tags(w, dict, SECTION_ID_PACKET_TAGS);
            av_dict_free(&dict);
        }
2079

2080
        print_pkt_side_data(w, st->codecpar, pkt->side_data, pkt->side_data_elems,
2081 2082
                            SECTION_ID_PACKET_SIDE_DATA_LIST,
                            SECTION_ID_PACKET_SIDE_DATA);
2083 2084
    }

2085 2086
    if (do_show_data)
        writer_print_data(w, "data", pkt->data, pkt->size);
2087
    writer_print_data_hash(w, "data_hash", pkt->data, pkt->size);
2088
    writer_print_section_footer(w);
2089

2090
    av_bprint_finalize(&pbuf, NULL);
2091
    fflush(stdout);
2092 2093
}

2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116
static void show_subtitle(WriterContext *w, AVSubtitle *sub, AVStream *stream,
                          AVFormatContext *fmt_ctx)
{
    AVBPrint pbuf;

    av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);

    writer_print_section_header(w, SECTION_ID_SUBTITLE);

    print_str ("media_type",         "subtitle");
    print_ts  ("pts",                 sub->pts);
    print_time("pts_time",            sub->pts, &AV_TIME_BASE_Q);
    print_int ("format",              sub->format);
    print_int ("start_display_time",  sub->start_display_time);
    print_int ("end_display_time",    sub->end_display_time);
    print_int ("num_rects",           sub->num_rects);

    writer_print_section_footer(w);

    av_bprint_finalize(&pbuf, NULL);
    fflush(stdout);
}

2117 2118
static void show_frame(WriterContext *w, AVFrame *frame, AVStream *stream,
                       AVFormatContext *fmt_ctx)
2119
{
2120
    AVBPrint pbuf;
2121
    char val_str[128];
2122
    const char *s;
2123
    int i;
2124

2125 2126
    av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);

2127
    writer_print_section_header(w, SECTION_ID_FRAME);
2128

2129
    s = av_get_media_type_string(stream->codecpar->codec_type);
2130 2131
    if (s) print_str    ("media_type", s);
    else   print_str_opt("media_type", "unknown");
2132
    print_int("stream_index",           stream->index);
2133
    print_int("key_frame",              frame->key_frame);
2134 2135
    print_ts  ("pkt_pts",               frame->pts);
    print_time("pkt_pts_time",          frame->pts, &stream->time_base);
2136 2137
    print_ts  ("pkt_dts",               frame->pkt_dts);
    print_time("pkt_dts_time",          frame->pkt_dts, &stream->time_base);
2138 2139 2140 2141 2142
    print_ts  ("best_effort_timestamp", frame->best_effort_timestamp);
    print_time("best_effort_timestamp_time", frame->best_effort_timestamp, &stream->time_base);
    print_duration_ts  ("pkt_duration",      frame->pkt_duration);
    print_duration_time("pkt_duration_time", frame->pkt_duration, &stream->time_base);
    if (frame->pkt_pos != -1) print_fmt    ("pkt_pos", "%"PRId64, frame->pkt_pos);
2143
    else                      print_str_opt("pkt_pos", "N/A");
2144
    if (frame->pkt_size != -1) print_val    ("pkt_size", frame->pkt_size, unit_byte_str);
2145
    else                       print_str_opt("pkt_size", "N/A");
2146

2147
    switch (stream->codecpar->codec_type) {
2148 2149
        AVRational sar;

2150
    case AVMEDIA_TYPE_VIDEO:
2151 2152 2153 2154 2155
        print_int("width",                  frame->width);
        print_int("height",                 frame->height);
        s = av_get_pix_fmt_name(frame->format);
        if (s) print_str    ("pix_fmt", s);
        else   print_str_opt("pix_fmt", "unknown");
2156 2157 2158
        sar = av_guess_sample_aspect_ratio(fmt_ctx, stream, frame);
        if (sar.num) {
            print_q("sample_aspect_ratio", sar, ':');
2159 2160 2161 2162 2163 2164 2165 2166 2167
        } else {
            print_str_opt("sample_aspect_ratio", "N/A");
        }
        print_fmt("pict_type",              "%c", av_get_picture_type_char(frame->pict_type));
        print_int("coded_picture_number",   frame->coded_picture_number);
        print_int("display_picture_number", frame->display_picture_number);
        print_int("interlaced_frame",       frame->interlaced_frame);
        print_int("top_field_first",        frame->top_field_first);
        print_int("repeat_pict",            frame->repeat_pict);
2168

2169
        print_color_range(w, frame->color_range);
2170
        print_color_space(w, frame->colorspace);
2171
        print_primaries(w, frame->color_primaries);
2172 2173
        print_color_trc(w, frame->color_trc);
        print_chroma_location(w, frame->chroma_location);
2174
        break;
2175 2176 2177 2178 2179 2180

    case AVMEDIA_TYPE_AUDIO:
        s = av_get_sample_fmt_name(frame->format);
        if (s) print_str    ("sample_fmt", s);
        else   print_str_opt("sample_fmt", "unknown");
        print_int("nb_samples",         frame->nb_samples);
2181 2182
        print_int("channels", frame->channels);
        if (frame->channel_layout) {
2183
            av_bprint_clear(&pbuf);
2184 2185
            av_bprint_channel_layout(&pbuf, frame->channels,
                                     frame->channel_layout);
2186 2187 2188
            print_str    ("channel_layout", pbuf.str);
        } else
            print_str_opt("channel_layout", "unknown");
2189 2190
        break;
    }
2191
    if (do_show_frame_tags)
2192
        show_tags(w, frame->metadata, SECTION_ID_FRAME_TAGS);
2193 2194
    if (do_show_log)
        show_log(w, SECTION_ID_FRAME_LOGS, SECTION_ID_FRAME_LOG, do_show_log);
2195 2196 2197 2198 2199 2200 2201 2202 2203
    if (frame->nb_side_data) {
        writer_print_section_header(w, SECTION_ID_FRAME_SIDE_DATA_LIST);
        for (i = 0; i < frame->nb_side_data; i++) {
            AVFrameSideData *sd = frame->side_data[i];
            const char *name;

            writer_print_section_header(w, SECTION_ID_FRAME_SIDE_DATA);
            name = av_frame_side_data_name(sd->type);
            print_str("side_data_type", name ? name : "unknown");
2204 2205 2206
            if (sd->type == AV_FRAME_DATA_DISPLAYMATRIX && sd->size >= 9*4) {
                writer_print_integers(w, "displaymatrix", sd->data, 9, " %11d", 3, 4, 1);
                print_int("rotation", av_display_rotation_get((int32_t *)sd->data));
2207 2208 2209 2210
            } else if (sd->type == AV_FRAME_DATA_GOP_TIMECODE && sd->size >= 8) {
                char tcbuf[AV_TIMECODE_STR_SIZE];
                av_timecode_make_mpeg_tc_string(tcbuf, *(int64_t *)(sd->data));
                print_str("timecode", tcbuf);
2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222
            } else if (sd->type == AV_FRAME_DATA_S12M_TIMECODE && sd->size == 16) {
                uint32_t *tc = (uint32_t*)sd->data;
                int m = FFMIN(tc[0],3);
                writer_print_section_header(w, SECTION_ID_FRAME_SIDE_DATA_TIMECODE_LIST);
                for (int j = 1; j <= m ; j++) {
                    char tcbuf[AV_TIMECODE_STR_SIZE];
                    av_timecode_make_smpte_tc_string(tcbuf, tc[j], 0);
                    writer_print_section_header(w, SECTION_ID_FRAME_SIDE_DATA_TIMECODE);
                    print_str("value", tcbuf);
                    writer_print_section_footer(w);
                }
                writer_print_section_footer(w);
2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241
            } else if (sd->type == AV_FRAME_DATA_MASTERING_DISPLAY_METADATA) {
                AVMasteringDisplayMetadata *metadata = (AVMasteringDisplayMetadata *)sd->data;

                if (metadata->has_primaries) {
                    print_q("red_x", metadata->display_primaries[0][0], '/');
                    print_q("red_y", metadata->display_primaries[0][1], '/');
                    print_q("green_x", metadata->display_primaries[1][0], '/');
                    print_q("green_y", metadata->display_primaries[1][1], '/');
                    print_q("blue_x", metadata->display_primaries[2][0], '/');
                    print_q("blue_y", metadata->display_primaries[2][1], '/');

                    print_q("white_point_x", metadata->white_point[0], '/');
                    print_q("white_point_y", metadata->white_point[1], '/');
                }

                if (metadata->has_luminance) {
                    print_q("min_luminance", metadata->min_luminance, '/');
                    print_q("max_luminance", metadata->max_luminance, '/');
                }
2242 2243 2244 2245
            } else if (sd->type == AV_FRAME_DATA_CONTENT_LIGHT_LEVEL) {
                AVContentLightMetadata *metadata = (AVContentLightMetadata *)sd->data;
                print_int("max_content", metadata->MaxCLL);
                print_int("max_average", metadata->MaxFALL);
2246 2247 2248 2249 2250
            } else if (sd->type == AV_FRAME_DATA_ICC_PROFILE) {
                AVDictionaryEntry *tag = av_dict_get(sd->metadata, "name", NULL, AV_DICT_MATCH_CASE);
                if (tag)
                    print_str(tag->key, tag->value);
                print_int("size", sd->size);
2251
            }
2252 2253 2254 2255
            writer_print_section_footer(w);
        }
        writer_print_section_footer(w);
    }
2256

2257
    writer_print_section_footer(w);
2258

2259
    av_bprint_finalize(&pbuf, NULL);
2260 2261 2262
    fflush(stdout);
}

2263
static av_always_inline int process_frame(WriterContext *w,
2264
                                          InputFile *ifile,
wm4's avatar
wm4 committed
2265 2266
                                          AVFrame *frame, AVPacket *pkt,
                                          int *packet_new)
2267
{
2268 2269 2270
    AVFormatContext *fmt_ctx = ifile->fmt_ctx;
    AVCodecContext *dec_ctx = ifile->streams[pkt->stream_index].dec_ctx;
    AVCodecParameters *par = ifile->streams[pkt->stream_index].st->codecpar;
2271
    AVSubtitle sub;
2272
    int ret = 0, got_frame = 0;
2273

2274
    clear_log(1);
2275
    if (dec_ctx && dec_ctx->codec) {
2276
        switch (par->codec_type) {
2277 2278
        case AVMEDIA_TYPE_VIDEO:
        case AVMEDIA_TYPE_AUDIO:
wm4's avatar
wm4 committed
2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295
            if (*packet_new) {
                ret = avcodec_send_packet(dec_ctx, pkt);
                if (ret == AVERROR(EAGAIN)) {
                    ret = 0;
                } else if (ret >= 0 || ret == AVERROR_EOF) {
                    ret = 0;
                    *packet_new = 0;
                }
            }
            if (ret >= 0) {
                ret = avcodec_receive_frame(dec_ctx, frame);
                if (ret >= 0) {
                    got_frame = 1;
                } else if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
                    ret = 0;
                }
            }
2296
            break;
2297 2298

        case AVMEDIA_TYPE_SUBTITLE:
2299 2300
            if (*packet_new)
                ret = avcodec_decode_subtitle2(dec_ctx, &sub, &got_frame, pkt);
wm4's avatar
wm4 committed
2301
            *packet_new = 0;
2302
            break;
wm4's avatar
wm4 committed
2303 2304
        default:
            *packet_new = 0;
2305
        }
wm4's avatar
wm4 committed
2306 2307
    } else {
        *packet_new = 0;
2308
    }
2309

2310 2311 2312
    if (ret < 0)
        return ret;
    if (got_frame) {
2313
        int is_sub = (par->codec_type == AVMEDIA_TYPE_SUBTITLE);
2314 2315
        nb_streams_frames[pkt->stream_index]++;
        if (do_show_frames)
2316
            if (is_sub)
2317
                show_subtitle(w, &sub, ifile->streams[pkt->stream_index].st, fmt_ctx);
2318
            else
2319
                show_frame(w, frame, ifile->streams[pkt->stream_index].st, fmt_ctx);
2320 2321
        if (is_sub)
            avsubtitle_free(&sub);
2322
    }
wm4's avatar
wm4 committed
2323
    return got_frame || *packet_new;
2324 2325
}

2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349
static void log_read_interval(const ReadInterval *interval, void *log_ctx, int log_level)
{
    av_log(log_ctx, log_level, "id:%d", interval->id);

    if (interval->has_start) {
        av_log(log_ctx, log_level, " start:%s%s", interval->start_is_offset ? "+" : "",
               av_ts2timestr(interval->start, &AV_TIME_BASE_Q));
    } else {
        av_log(log_ctx, log_level, " start:N/A");
    }

    if (interval->has_end) {
        av_log(log_ctx, log_level, " end:%s", interval->end_is_offset ? "+" : "");
        if (interval->duration_frames)
            av_log(log_ctx, log_level, "#%"PRId64, interval->end);
        else
            av_log(log_ctx, log_level, "%s", av_ts2timestr(interval->end, &AV_TIME_BASE_Q));
    } else {
        av_log(log_ctx, log_level, " end:N/A");
    }

    av_log(log_ctx, log_level, "\n");
}

2350
static int read_interval_packets(WriterContext *w, InputFile *ifile,
2351
                                 const ReadInterval *interval, int64_t *cur_ts)
2352
{
2353
    AVFormatContext *fmt_ctx = ifile->fmt_ctx;
wm4's avatar
wm4 committed
2354
    AVPacket pkt;
2355
    AVFrame *frame = NULL;
2356
    int ret = 0, i = 0, frame_count = 0;
2357
    int64_t start = -INT64_MAX, end = interval->end;
2358
    int has_start = 0, has_end = interval->has_end && !interval->end_is_offset;
2359 2360 2361

    av_init_packet(&pkt);

2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388
    av_log(NULL, AV_LOG_VERBOSE, "Processing read interval ");
    log_read_interval(interval, NULL, AV_LOG_VERBOSE);

    if (interval->has_start) {
        int64_t target;
        if (interval->start_is_offset) {
            if (*cur_ts == AV_NOPTS_VALUE) {
                av_log(NULL, AV_LOG_ERROR,
                       "Could not seek to relative position since current "
                       "timestamp is not defined\n");
                ret = AVERROR(EINVAL);
                goto end;
            }
            target = *cur_ts + interval->start;
        } else {
            target = interval->start;
        }

        av_log(NULL, AV_LOG_VERBOSE, "Seeking to read interval start point %s\n",
               av_ts2timestr(target, &AV_TIME_BASE_Q));
        if ((ret = avformat_seek_file(fmt_ctx, -1, -INT64_MAX, target, INT64_MAX, 0)) < 0) {
            av_log(NULL, AV_LOG_ERROR, "Could not seek to position %"PRId64": %s\n",
                   interval->start, av_err2str(ret));
            goto end;
        }
    }

2389
    frame = av_frame_alloc();
2390 2391 2392 2393
    if (!frame) {
        ret = AVERROR(ENOMEM);
        goto end;
    }
2394
    while (!av_read_frame(fmt_ctx, &pkt)) {
2395
        if (fmt_ctx->nb_streams > nb_streams) {
2396 2397 2398
            REALLOCZ_ARRAY_STREAM(nb_streams_frames,  nb_streams, fmt_ctx->nb_streams);
            REALLOCZ_ARRAY_STREAM(nb_streams_packets, nb_streams, fmt_ctx->nb_streams);
            REALLOCZ_ARRAY_STREAM(selected_streams,   nb_streams, fmt_ctx->nb_streams);
2399
            nb_streams = fmt_ctx->nb_streams;
2400
        }
2401
        if (selected_streams[pkt.stream_index]) {
2402
            AVRational tb = ifile->streams[pkt.stream_index].st->time_base;
2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424

            if (pkt.pts != AV_NOPTS_VALUE)
                *cur_ts = av_rescale_q(pkt.pts, tb, AV_TIME_BASE_Q);

            if (!has_start && *cur_ts != AV_NOPTS_VALUE) {
                start = *cur_ts;
                has_start = 1;
            }

            if (has_start && !has_end && interval->end_is_offset) {
                end = start + interval->end;
                has_end = 1;
            }

            if (interval->end_is_offset && interval->duration_frames) {
                if (frame_count >= interval->end)
                    break;
            } else if (has_end && *cur_ts != AV_NOPTS_VALUE && *cur_ts >= end) {
                break;
            }

            frame_count++;
2425 2426
            if (do_read_packets) {
                if (do_show_packets)
2427
                    show_packet(w, ifile, &pkt, i++);
2428 2429 2430
                nb_streams_packets[pkt.stream_index]++;
            }
            if (do_read_frames) {
wm4's avatar
wm4 committed
2431 2432
                int packet_new = 1;
                while (process_frame(w, ifile, frame, &pkt, &packet_new) > 0);
2433
            }
2434
        }
2435
        av_packet_unref(&pkt);
2436
    }
2437
    av_packet_unref(&pkt);
2438 2439 2440
    //Flush remaining frames that are cached in the decoder
    for (i = 0; i < fmt_ctx->nb_streams; i++) {
        pkt.stream_index = i;
2441
        if (do_read_frames)
wm4's avatar
wm4 committed
2442
            while (process_frame(w, ifile, frame, &pkt, &(int){1}) > 0);
2443
    }
2444 2445

end:
2446
    av_frame_free(&frame);
2447 2448 2449 2450 2451 2452 2453
    if (ret < 0) {
        av_log(NULL, AV_LOG_ERROR, "Could not read packets in interval ");
        log_read_interval(interval, NULL, AV_LOG_ERROR);
    }
    return ret;
}

2454
static int read_packets(WriterContext *w, InputFile *ifile)
2455
{
2456
    AVFormatContext *fmt_ctx = ifile->fmt_ctx;
2457 2458 2459 2460 2461
    int i, ret = 0;
    int64_t cur_ts = fmt_ctx->start_time;

    if (read_intervals_nb == 0) {
        ReadInterval interval = (ReadInterval) { .has_start = 0, .has_end = 0 };
2462
        ret = read_interval_packets(w, ifile, &interval, &cur_ts);
2463 2464
    } else {
        for (i = 0; i < read_intervals_nb; i++) {
2465
            ret = read_interval_packets(w, ifile, &read_intervals[i], &cur_ts);
2466 2467 2468 2469
            if (ret < 0)
                break;
        }
    }
2470 2471

    return ret;
2472 2473
}

2474
static int show_stream(WriterContext *w, AVFormatContext *fmt_ctx, int stream_idx, InputStream *ist, int in_program)
Stefano Sabatini's avatar
Stefano Sabatini committed
2475
{
2476 2477
    AVStream *stream = ist->st;
    AVCodecParameters *par;
Stefano Sabatini's avatar
Stefano Sabatini committed
2478 2479
    AVCodecContext *dec_ctx;
    char val_str[128];
2480
    const char *s;
2481
    AVRational sar, dar;
2482
    AVBPrint pbuf;
2483
    const AVCodecDescriptor *cd;
2484
    int ret = 0;
2485
    const char *profile = NULL;
2486 2487

    av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
Stefano Sabatini's avatar
Stefano Sabatini committed
2488

2489
    writer_print_section_header(w, in_program ? SECTION_ID_PROGRAM_STREAM : SECTION_ID_STREAM);
Stefano Sabatini's avatar
Stefano Sabatini committed
2490

2491
    print_int("index", stream->index);
Stefano Sabatini's avatar
Stefano Sabatini committed
2492

2493 2494 2495
    par     = stream->codecpar;
    dec_ctx = ist->dec_ctx;
    if (cd = avcodec_descriptor_get(par->codec_id)) {
2496
        print_str("codec_name", cd->name);
2497
        if (!do_bitexact) {
2498 2499
            print_str("codec_long_name",
                      cd->long_name ? cd->long_name : "unknown");
2500
        }
2501 2502 2503 2504 2505 2506
    } else {
        print_str_opt("codec_name", "unknown");
        if (!do_bitexact) {
            print_str_opt("codec_long_name", "unknown");
        }
    }
2507

2508
    if (!do_bitexact && (profile = avcodec_profile_name(par->codec_id, par->profile)))
2509 2510
        print_str("profile", profile);
    else {
2511
        if (par->profile != FF_PROFILE_UNKNOWN) {
2512
            char profile_num[12];
2513
            snprintf(profile_num, sizeof(profile_num), "%d", par->profile);
2514 2515 2516 2517
            print_str("profile", profile_num);
        } else
            print_str_opt("profile", "unknown");
    }
Stefano Sabatini's avatar
Stefano Sabatini committed
2518

2519
    s = av_get_media_type_string(par->codec_type);
2520 2521
    if (s) print_str    ("codec_type", s);
    else   print_str_opt("codec_type", "unknown");
2522
#if FF_API_LAVF_AVCTX
2523 2524
    if (dec_ctx)
        print_q("codec_time_base", dec_ctx->time_base, '/');
2525
#endif
Stefano Sabatini's avatar
Stefano Sabatini committed
2526

2527
    /* print AVI/FourCC tag */
2528
    print_str("codec_tag_string",    av_fourcc2str(par->codec_tag));
2529
    print_fmt("codec_tag", "0x%04"PRIx32, par->codec_tag);
2530

2531
    switch (par->codec_type) {
2532
    case AVMEDIA_TYPE_VIDEO:
2533 2534
        print_int("width",        par->width);
        print_int("height",       par->height);
2535
#if FF_API_LAVF_AVCTX
2536 2537 2538 2539
        if (dec_ctx) {
            print_int("coded_width",  dec_ctx->coded_width);
            print_int("coded_height", dec_ctx->coded_height);
        }
2540
#endif
2541
        print_int("has_b_frames", par->video_delay);
2542
        sar = av_guess_sample_aspect_ratio(fmt_ctx, stream, NULL);
2543
        if (sar.num) {
2544 2545
            print_q("sample_aspect_ratio", sar, ':');
            av_reduce(&dar.num, &dar.den,
2546 2547
                      par->width  * sar.num,
                      par->height * sar.den,
2548 2549 2550 2551 2552 2553
                      1024*1024);
            print_q("display_aspect_ratio", dar, ':');
        } else {
            print_str_opt("sample_aspect_ratio", "N/A");
            print_str_opt("display_aspect_ratio", "N/A");
        }
2554
        s = av_get_pix_fmt_name(par->format);
2555 2556
        if (s) print_str    ("pix_fmt", s);
        else   print_str_opt("pix_fmt", "unknown");
2557
        print_int("level",   par->level);
2558

2559
        print_color_range(w, par->color_range);
2560 2561
        print_color_space(w, par->color_space);
        print_color_trc(w, par->color_trc);
2562
        print_primaries(w, par->color_primaries);
2563
        print_chroma_location(w, par->chroma_location);
2564

2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577
        if (par->field_order == AV_FIELD_PROGRESSIVE)
            print_str("field_order", "progressive");
        else if (par->field_order == AV_FIELD_TT)
            print_str("field_order", "tt");
        else if (par->field_order == AV_FIELD_BB)
            print_str("field_order", "bb");
        else if (par->field_order == AV_FIELD_TB)
            print_str("field_order", "tb");
        else if (par->field_order == AV_FIELD_BT)
            print_str("field_order", "bt");
        else
            print_str_opt("field_order", "unknown");

2578
#if FF_API_PRIVATE_OPT
2579
        if (dec_ctx && dec_ctx->timecode_frame_start >= 0) {
2580 2581 2582 2583 2584 2585
            char tcbuf[AV_TIMECODE_STR_SIZE];
            av_timecode_make_mpeg_tc_string(tcbuf, dec_ctx->timecode_frame_start);
            print_str("timecode", tcbuf);
        } else {
            print_str_opt("timecode", "N/A");
        }
2586
#endif
2587 2588
        if (dec_ctx)
            print_int("refs", dec_ctx->refs);
2589
        break;
2590

2591
    case AVMEDIA_TYPE_AUDIO:
2592
        s = av_get_sample_fmt_name(par->format);
2593 2594
        if (s) print_str    ("sample_fmt", s);
        else   print_str_opt("sample_fmt", "unknown");
2595 2596
        print_val("sample_rate",     par->sample_rate, unit_hertz_str);
        print_int("channels",        par->channels);
2597

2598
        if (par->channel_layout) {
2599
            av_bprint_clear(&pbuf);
2600
            av_bprint_channel_layout(&pbuf, par->channels, par->channel_layout);
2601 2602 2603
            print_str    ("channel_layout", pbuf.str);
        } else {
            print_str_opt("channel_layout", "unknown");
Stefano Sabatini's avatar
Stefano Sabatini committed
2604
        }
2605

2606
        print_int("bits_per_sample", av_get_bits_per_sample(par->codec_id));
2607 2608 2609
        break;

    case AVMEDIA_TYPE_SUBTITLE:
2610 2611
        if (par->width)
            print_int("width",       par->width);
2612 2613
        else
            print_str_opt("width",   "N/A");
2614 2615
        if (par->height)
            print_int("height",      par->height);
2616 2617 2618
        else
            print_str_opt("height",  "N/A");
        break;
Stefano Sabatini's avatar
Stefano Sabatini committed
2619
    }
2620

2621
    if (dec_ctx && dec_ctx->codec && dec_ctx->codec->priv_class && show_private_data) {
2622
        const AVOption *opt = NULL;
2623 2624 2625 2626 2627 2628 2629 2630 2631
        while (opt = av_opt_next(dec_ctx->priv_data,opt)) {
            uint8_t *str;
            if (opt->flags) continue;
            if (av_opt_get(dec_ctx->priv_data, opt->name, 0, &str) >= 0) {
                print_str(opt->name, str);
                av_free(str);
            }
        }
    }
Stefano Sabatini's avatar
Stefano Sabatini committed
2632

2633 2634
    if (fmt_ctx->iformat->flags & AVFMT_SHOW_IDS) print_fmt    ("id", "0x%x", stream->id);
    else                                          print_str_opt("id", "N/A");
2635 2636 2637
    print_q("r_frame_rate",   stream->r_frame_rate,   '/');
    print_q("avg_frame_rate", stream->avg_frame_rate, '/');
    print_q("time_base",      stream->time_base,      '/');
2638 2639 2640 2641
    print_ts  ("start_pts",   stream->start_time);
    print_time("start_time",  stream->start_time, &stream->time_base);
    print_ts  ("duration_ts", stream->duration);
    print_time("duration",    stream->duration, &stream->time_base);
2642
    if (par->bit_rate > 0)     print_val    ("bit_rate", par->bit_rate, unit_bit_per_second_str);
2643
    else                       print_str_opt("bit_rate", "N/A");
2644 2645 2646 2647 2648 2649
#if FF_API_LAVF_AVCTX
    if (stream->codec->rc_max_rate > 0) print_val ("max_bit_rate", stream->codec->rc_max_rate, unit_bit_per_second_str);
    else                                print_str_opt("max_bit_rate", "N/A");
#endif
    if (dec_ctx && dec_ctx->bits_per_raw_sample > 0) print_fmt("bits_per_raw_sample", "%d", dec_ctx->bits_per_raw_sample);
    else                                             print_str_opt("bits_per_raw_sample", "N/A");
2650 2651
    if (stream->nb_frames) print_fmt    ("nb_frames", "%"PRId64, stream->nb_frames);
    else                   print_str_opt("nb_frames", "N/A");
2652 2653 2654 2655
    if (nb_streams_frames[stream_idx])  print_fmt    ("nb_read_frames", "%"PRIu64, nb_streams_frames[stream_idx]);
    else                                print_str_opt("nb_read_frames", "N/A");
    if (nb_streams_packets[stream_idx]) print_fmt    ("nb_read_packets", "%"PRIu64, nb_streams_packets[stream_idx]);
    else                                print_str_opt("nb_read_packets", "N/A");
2656
    if (do_show_data)
2657 2658 2659 2660
        writer_print_data(w, "extradata", par->extradata,
                                          par->extradata_size);
    writer_print_data_hash(w, "extradata_hash", par->extradata,
                                                par->extradata_size);
2661 2662 2663 2664 2665 2666

    /* Print disposition information */
#define PRINT_DISPOSITION(flagname, name) do {                                \
        print_int(name, !!(stream->disposition & AV_DISPOSITION_##flagname)); \
    } while (0)

2667
    if (do_show_stream_disposition) {
2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681
        writer_print_section_header(w, in_program ? SECTION_ID_PROGRAM_STREAM_DISPOSITION : SECTION_ID_STREAM_DISPOSITION);
        PRINT_DISPOSITION(DEFAULT,          "default");
        PRINT_DISPOSITION(DUB,              "dub");
        PRINT_DISPOSITION(ORIGINAL,         "original");
        PRINT_DISPOSITION(COMMENT,          "comment");
        PRINT_DISPOSITION(LYRICS,           "lyrics");
        PRINT_DISPOSITION(KARAOKE,          "karaoke");
        PRINT_DISPOSITION(FORCED,           "forced");
        PRINT_DISPOSITION(HEARING_IMPAIRED, "hearing_impaired");
        PRINT_DISPOSITION(VISUAL_IMPAIRED,  "visual_impaired");
        PRINT_DISPOSITION(CLEAN_EFFECTS,    "clean_effects");
        PRINT_DISPOSITION(ATTACHED_PIC,     "attached_pic");
        PRINT_DISPOSITION(TIMED_THUMBNAILS, "timed_thumbnails");
        writer_print_section_footer(w);
2682
    }
2683

2684 2685
    if (do_show_stream_tags)
        ret = show_tags(w, stream->metadata, in_program ? SECTION_ID_PROGRAM_STREAM_TAGS : SECTION_ID_STREAM_TAGS);
Stefano Sabatini's avatar
Stefano Sabatini committed
2686

2687
    if (stream->nb_side_data) {
2688
        print_pkt_side_data(w, stream->codecpar, stream->side_data, stream->nb_side_data,
2689 2690
                            SECTION_ID_STREAM_SIDE_DATA_LIST,
                            SECTION_ID_STREAM_SIDE_DATA);
2691 2692
    }

2693
    writer_print_section_footer(w);
2694
    av_bprint_finalize(&pbuf, NULL);
2695
    fflush(stdout);
2696 2697

    return ret;
Stefano Sabatini's avatar
Stefano Sabatini committed
2698 2699
}

2700
static int show_streams(WriterContext *w, InputFile *ifile)
2701
{
2702
    AVFormatContext *fmt_ctx = ifile->fmt_ctx;
2703 2704
    int i, ret = 0;

2705
    writer_print_section_header(w, SECTION_ID_STREAMS);
2706
    for (i = 0; i < ifile->nb_streams; i++)
2707
        if (selected_streams[i]) {
2708
            ret = show_stream(w, fmt_ctx, i, &ifile->streams[i], 0);
2709 2710 2711
            if (ret < 0)
                break;
        }
2712
    writer_print_section_footer(w);
2713 2714

    return ret;
2715 2716
}

2717
static int show_program(WriterContext *w, InputFile *ifile, AVProgram *program)
2718
{
2719
    AVFormatContext *fmt_ctx = ifile->fmt_ctx;
2720
    int i, ret = 0;
2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731

    writer_print_section_header(w, SECTION_ID_PROGRAM);
    print_int("program_id", program->id);
    print_int("program_num", program->program_num);
    print_int("nb_streams", program->nb_stream_indexes);
    print_int("pmt_pid", program->pmt_pid);
    print_int("pcr_pid", program->pcr_pid);
    print_ts("start_pts", program->start_time);
    print_time("start_time", program->start_time, &AV_TIME_BASE_Q);
    print_ts("end_pts", program->end_time);
    print_time("end_time", program->end_time, &AV_TIME_BASE_Q);
2732 2733
    if (do_show_program_tags)
        ret = show_tags(w, program->metadata, SECTION_ID_PROGRAM_TAGS);
2734 2735
    if (ret < 0)
        goto end;
2736 2737 2738

    writer_print_section_header(w, SECTION_ID_PROGRAM_STREAMS);
    for (i = 0; i < program->nb_stream_indexes; i++) {
2739
        if (selected_streams[program->stream_index[i]]) {
2740
            ret = show_stream(w, fmt_ctx, program->stream_index[i], &ifile->streams[program->stream_index[i]], 1);
2741 2742 2743
            if (ret < 0)
                break;
        }
2744 2745 2746
    }
    writer_print_section_footer(w);

2747
end:
2748
    writer_print_section_footer(w);
2749
    return ret;
2750 2751
}

2752
static int show_programs(WriterContext *w, InputFile *ifile)
2753
{
2754
    AVFormatContext *fmt_ctx = ifile->fmt_ctx;
2755
    int i, ret = 0;
2756 2757 2758 2759 2760 2761

    writer_print_section_header(w, SECTION_ID_PROGRAMS);
    for (i = 0; i < fmt_ctx->nb_programs; i++) {
        AVProgram *program = fmt_ctx->programs[i];
        if (!program)
            continue;
2762
        ret = show_program(w, ifile, program);
2763 2764
        if (ret < 0)
            break;
2765
    }
2766
    writer_print_section_footer(w);
2767
    return ret;
2768 2769
}

2770
static int show_chapters(WriterContext *w, InputFile *ifile)
2771
{
2772
    AVFormatContext *fmt_ctx = ifile->fmt_ctx;
2773
    int i, ret = 0;
2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785

    writer_print_section_header(w, SECTION_ID_CHAPTERS);
    for (i = 0; i < fmt_ctx->nb_chapters; i++) {
        AVChapter *chapter = fmt_ctx->chapters[i];

        writer_print_section_header(w, SECTION_ID_CHAPTER);
        print_int("id", chapter->id);
        print_q  ("time_base", chapter->time_base, '/');
        print_int("start", chapter->start);
        print_time("start_time", chapter->start, &chapter->time_base);
        print_int("end", chapter->end);
        print_time("end_time", chapter->end, &chapter->time_base);
2786 2787
        if (do_show_chapter_tags)
            ret = show_tags(w, chapter->metadata, SECTION_ID_CHAPTER_TAGS);
2788 2789 2790
        writer_print_section_footer(w);
    }
    writer_print_section_footer(w);
2791 2792

    return ret;
2793 2794
}

2795
static int show_format(WriterContext *w, InputFile *ifile)
Stefano Sabatini's avatar
Stefano Sabatini committed
2796
{
2797
    AVFormatContext *fmt_ctx = ifile->fmt_ctx;
Stefano Sabatini's avatar
Stefano Sabatini committed
2798
    char val_str[128];
2799
    int64_t size = fmt_ctx->pb ? avio_size(fmt_ctx->pb) : -1;
2800
    int ret = 0;
Stefano Sabatini's avatar
Stefano Sabatini committed
2801

2802
    writer_print_section_header(w, SECTION_ID_FORMAT);
2803
    print_str_validate("filename", fmt_ctx->url);
2804
    print_int("nb_streams",       fmt_ctx->nb_streams);
2805
    print_int("nb_programs",      fmt_ctx->nb_programs);
2806
    print_str("format_name",      fmt_ctx->iformat->name);
2807
    if (!do_bitexact) {
2808 2809
        if (fmt_ctx->iformat->long_name) print_str    ("format_long_name", fmt_ctx->iformat->long_name);
        else                             print_str_opt("format_long_name", "unknown");
2810
    }
2811 2812
    print_time("start_time",      fmt_ctx->start_time, &AV_TIME_BASE_Q);
    print_time("duration",        fmt_ctx->duration,   &AV_TIME_BASE_Q);
2813 2814 2815 2816
    if (size >= 0) print_val    ("size", size, unit_byte_str);
    else           print_str_opt("size", "N/A");
    if (fmt_ctx->bit_rate > 0) print_val    ("bit_rate", fmt_ctx->bit_rate, unit_bit_per_second_str);
    else                       print_str_opt("bit_rate", "N/A");
2817
    print_int("probe_score", fmt_ctx->probe_score);
2818 2819
    if (do_show_format_tags)
        ret = show_tags(w, fmt_ctx->metadata, SECTION_ID_FORMAT_TAGS);
2820 2821

    writer_print_section_footer(w);
2822
    fflush(stdout);
2823
    return ret;
Stefano Sabatini's avatar
Stefano Sabatini committed
2824 2825
}

2826 2827 2828 2829 2830 2831 2832 2833
static void show_error(WriterContext *w, int err)
{
    char errbuf[128];
    const char *errbuf_ptr = errbuf;

    if (av_strerror(err, errbuf, sizeof(errbuf)) < 0)
        errbuf_ptr = strerror(AVUNERROR(err));

2834
    writer_print_section_header(w, SECTION_ID_ERROR);
2835 2836
    print_int("code", err);
    print_str("string", errbuf_ptr);
2837
    writer_print_section_footer(w);
2838 2839
}

2840 2841
static int open_input_file(InputFile *ifile, const char *filename,
                           const char *print_filename)
Stefano Sabatini's avatar
Stefano Sabatini committed
2842
{
2843
    int err, i;
2844 2845
    AVFormatContext *fmt_ctx = NULL;
    AVDictionaryEntry *t;
2846
    int scan_all_pmts_set = 0;
Stefano Sabatini's avatar
Stefano Sabatini committed
2847

2848 2849 2850 2851 2852 2853
    fmt_ctx = avformat_alloc_context();
    if (!fmt_ctx) {
        print_error(filename, AVERROR(ENOMEM));
        exit_program(1);
    }

2854 2855 2856 2857
    if (!av_dict_get(format_opts, "scan_all_pmts", NULL, AV_DICT_MATCH_CASE)) {
        av_dict_set(&format_opts, "scan_all_pmts", "1", AV_DICT_DONT_OVERWRITE);
        scan_all_pmts_set = 1;
    }
2858 2859
    if ((err = avformat_open_input(&fmt_ctx, filename,
                                   iformat, &format_opts)) < 0) {
Stefano Sabatini's avatar
Stefano Sabatini committed
2860 2861 2862
        print_error(filename, err);
        return err;
    }
2863 2864 2865 2866
    if (print_filename) {
        av_freep(&fmt_ctx->url);
        fmt_ctx->url = av_strdup(print_filename);
    }
2867
    ifile->fmt_ctx = fmt_ctx;
2868 2869
    if (scan_all_pmts_set)
        av_dict_set(&format_opts, "scan_all_pmts", NULL, AV_DICT_MATCH_CASE);
2870 2871 2872 2873 2874
    if ((t = av_dict_get(format_opts, "", NULL, AV_DICT_IGNORE_SUFFIX))) {
        av_log(NULL, AV_LOG_ERROR, "Option %s not found.\n", t->key);
        return AVERROR_OPTION_NOT_FOUND;
    }

2875 2876 2877
    if (find_stream_info) {
        AVDictionary **opts = setup_find_stream_info_opts(fmt_ctx, codec_opts);
        int orig_nb_streams = fmt_ctx->nb_streams;
2878

2879
        err = avformat_find_stream_info(fmt_ctx, opts);
2880

2881 2882 2883
        for (i = 0; i < orig_nb_streams; i++)
            av_dict_free(&opts[i]);
        av_freep(&opts);
Stefano Sabatini's avatar
Stefano Sabatini committed
2884

2885 2886 2887 2888
        if (err < 0) {
            print_error(filename, err);
            return err;
        }
2889
    }
2890

2891
    av_dump_format(fmt_ctx, 0, filename, 0);
Stefano Sabatini's avatar
Stefano Sabatini committed
2892

2893 2894 2895 2896 2897 2898
    ifile->streams = av_mallocz_array(fmt_ctx->nb_streams,
                                      sizeof(*ifile->streams));
    if (!ifile->streams)
        exit(1);
    ifile->nb_streams = fmt_ctx->nb_streams;

Stefano Sabatini's avatar
Stefano Sabatini committed
2899 2900
    /* bind a decoder to each input stream */
    for (i = 0; i < fmt_ctx->nb_streams; i++) {
2901
        InputStream *ist = &ifile->streams[i];
Stefano Sabatini's avatar
Stefano Sabatini committed
2902 2903 2904
        AVStream *stream = fmt_ctx->streams[i];
        AVCodec *codec;

2905 2906
        ist->st = stream;

2907
        if (stream->codecpar->codec_id == AV_CODEC_ID_PROBE) {
2908
            av_log(NULL, AV_LOG_WARNING,
2909 2910
                   "Failed to probe codec for input stream %d\n",
                    stream->index);
2911 2912 2913 2914 2915
            continue;
        }

        codec = avcodec_find_decoder(stream->codecpar->codec_id);
        if (!codec) {
2916
            av_log(NULL, AV_LOG_WARNING,
2917
                    "Unsupported codec with id %d for input stream %d\n",
2918 2919 2920 2921 2922
                    stream->codecpar->codec_id, stream->index);
            continue;
        }
        {
            AVDictionary *opts = filter_codec_opts(codec_opts, stream->codecpar->codec_id,
2923
                                                   fmt_ctx, stream, codec);
2924 2925 2926 2927 2928 2929 2930 2931 2932

            ist->dec_ctx = avcodec_alloc_context3(codec);
            if (!ist->dec_ctx)
                exit(1);

            err = avcodec_parameters_to_context(ist->dec_ctx, stream->codecpar);
            if (err < 0)
                exit(1);

2933 2934 2935 2936 2937 2938 2939
            if (do_show_log) {
                // For loging it is needed to disable at least frame threads as otherwise
                // the log information would need to be reordered and matches up to contexts and frames
                // That is in fact possible but not trivial
                av_dict_set(&codec_opts, "threads", "1", 0);
            }

2940
            ist->dec_ctx->pkt_timebase = stream->time_base;
2941
            ist->dec_ctx->framerate = stream->avg_frame_rate;
2942 2943 2944 2945
#if FF_API_LAVF_AVCTX
            ist->dec_ctx->coded_width = stream->codec->coded_width;
            ist->dec_ctx->coded_height = stream->codec->coded_height;
#endif
2946 2947

            if (avcodec_open2(ist->dec_ctx, codec, &opts) < 0) {
2948
                av_log(NULL, AV_LOG_WARNING, "Could not open codec for input stream %d\n",
2949
                       stream->index);
2950
                exit(1);
2951
            }
2952

2953 2954 2955 2956 2957
            if ((t = av_dict_get(opts, "", NULL, AV_DICT_IGNORE_SUFFIX))) {
                av_log(NULL, AV_LOG_ERROR, "Option %s for input stream %d not found\n",
                       t->key, stream->index);
                return AVERROR_OPTION_NOT_FOUND;
            }
Stefano Sabatini's avatar
Stefano Sabatini committed
2958 2959 2960
        }
    }

2961
    ifile->fmt_ctx = fmt_ctx;
Stefano Sabatini's avatar
Stefano Sabatini committed
2962 2963 2964
    return 0;
}

2965
static void close_input_file(InputFile *ifile)
2966 2967 2968 2969
{
    int i;

    /* close decoder for each stream */
2970 2971 2972
    for (i = 0; i < ifile->nb_streams; i++)
        if (ifile->streams[i].st->codecpar->codec_id != AV_CODEC_ID_NONE)
            avcodec_free_context(&ifile->streams[i].dec_ctx);
2973

2974 2975 2976
    av_freep(&ifile->streams);
    ifile->nb_streams = 0;

2977
    avformat_close_input(&ifile->fmt_ctx);
2978 2979
}

2980 2981
static int probe_file(WriterContext *wctx, const char *filename,
                      const char *print_filename)
Stefano Sabatini's avatar
Stefano Sabatini committed
2982
{
2983
    InputFile ifile = { 0 };
2984
    int ret, i;
2985
    int section_id;
Stefano Sabatini's avatar
Stefano Sabatini committed
2986

2987 2988 2989
    do_read_frames = do_show_frames || do_count_frames;
    do_read_packets = do_show_packets || do_count_packets;

2990
    ret = open_input_file(&ifile, filename, print_filename);
2991
    if (ret < 0)
2992
        goto end;
2993

2994 2995
#define CHECK_END if (ret < 0) goto end

2996 2997 2998 2999
    nb_streams = ifile.fmt_ctx->nb_streams;
    REALLOCZ_ARRAY_STREAM(nb_streams_frames,0,ifile.fmt_ctx->nb_streams);
    REALLOCZ_ARRAY_STREAM(nb_streams_packets,0,ifile.fmt_ctx->nb_streams);
    REALLOCZ_ARRAY_STREAM(selected_streams,0,ifile.fmt_ctx->nb_streams);
3000

3001
    for (i = 0; i < ifile.fmt_ctx->nb_streams; i++) {
3002
        if (stream_specifier) {
3003 3004
            ret = avformat_match_stream_specifier(ifile.fmt_ctx,
                                                  ifile.fmt_ctx->streams[i],
3005
                                                  stream_specifier);
3006
            CHECK_END;
3007 3008 3009 3010 3011
            else
                selected_streams[i] = ret;
            ret = 0;
        } else {
            selected_streams[i] = 1;
3012
        }
3013 3014
        if (!selected_streams[i])
            ifile.fmt_ctx->streams[i]->discard = AVDISCARD_ALL;
3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026
    }

    if (do_read_frames || do_read_packets) {
        if (do_show_frames && do_show_packets &&
            wctx->writer->flags & WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER)
            section_id = SECTION_ID_PACKETS_AND_FRAMES;
        else if (do_show_packets && !do_show_frames)
            section_id = SECTION_ID_PACKETS;
        else // (!do_show_packets && do_show_frames)
            section_id = SECTION_ID_FRAMES;
        if (do_show_frames || do_show_packets)
            writer_print_section_header(wctx, section_id);
3027
        ret = read_packets(wctx, &ifile);
3028 3029
        if (do_show_frames || do_show_packets)
            writer_print_section_footer(wctx);
3030 3031
        CHECK_END;
    }
3032

3033
    if (do_show_programs) {
3034
        ret = show_programs(wctx, &ifile);
3035 3036 3037 3038
        CHECK_END;
    }

    if (do_show_streams) {
3039
        ret = show_streams(wctx, &ifile);
3040 3041 3042
        CHECK_END;
    }
    if (do_show_chapters) {
3043
        ret = show_chapters(wctx, &ifile);
3044 3045 3046
        CHECK_END;
    }
    if (do_show_format) {
3047
        ret = show_format(wctx, &ifile);
3048
        CHECK_END;
3049 3050 3051
    }

end:
3052 3053
    if (ifile.fmt_ctx)
        close_input_file(&ifile);
3054 3055 3056
    av_freep(&nb_streams_frames);
    av_freep(&nb_streams_packets);
    av_freep(&selected_streams);
3057

3058
    return ret;
Stefano Sabatini's avatar
Stefano Sabatini committed
3059 3060 3061 3062
}

static void show_usage(void)
{
3063 3064 3065
    av_log(NULL, AV_LOG_INFO, "Simple multimedia streams analyzer\n");
    av_log(NULL, AV_LOG_INFO, "usage: %s [OPTIONS] [INPUT_FILE]\n", program_name);
    av_log(NULL, AV_LOG_INFO, "\n");
Stefano Sabatini's avatar
Stefano Sabatini committed
3066 3067
}

3068 3069
static void ffprobe_show_program_version(WriterContext *w)
{
3070 3071
    AVBPrint pbuf;
    av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
3072

3073
    writer_print_section_header(w, SECTION_ID_PROGRAM_VERSION);
3074 3075
    print_str("version", FFMPEG_VERSION);
    print_fmt("copyright", "Copyright (c) %d-%d the FFmpeg developers",
3076
              program_birth_year, CONFIG_THIS_YEAR);
3077
    print_str("compiler_ident", CC_IDENT);
3078
    print_str("configuration", FFMPEG_CONFIGURATION);
3079
    writer_print_section_footer(w);
3080

3081
    av_bprint_finalize(&pbuf, NULL);
3082 3083 3084 3085 3086 3087
}

#define SHOW_LIB_VERSION(libname, LIBNAME)                              \
    do {                                                                \
        if (CONFIG_##LIBNAME) {                                         \
            unsigned int version = libname##_version();                 \
3088
            writer_print_section_header(w, SECTION_ID_LIBRARY_VERSION); \
3089 3090 3091 3092 3093
            print_str("name",    "lib" #libname);                       \
            print_int("major",   LIB##LIBNAME##_VERSION_MAJOR);         \
            print_int("minor",   LIB##LIBNAME##_VERSION_MINOR);         \
            print_int("micro",   LIB##LIBNAME##_VERSION_MICRO);         \
            print_int("version", version);                              \
3094
            print_str("ident",   LIB##LIBNAME##_IDENT);                 \
3095
            writer_print_section_footer(w);                             \
3096 3097 3098 3099 3100
        }                                                               \
    } while (0)

static void ffprobe_show_library_versions(WriterContext *w)
{
3101
    writer_print_section_header(w, SECTION_ID_LIBRARY_VERSIONS);
3102 3103 3104 3105 3106 3107 3108 3109
    SHOW_LIB_VERSION(avutil,     AVUTIL);
    SHOW_LIB_VERSION(avcodec,    AVCODEC);
    SHOW_LIB_VERSION(avformat,   AVFORMAT);
    SHOW_LIB_VERSION(avdevice,   AVDEVICE);
    SHOW_LIB_VERSION(avfilter,   AVFILTER);
    SHOW_LIB_VERSION(swscale,    SWSCALE);
    SHOW_LIB_VERSION(swresample, SWRESAMPLE);
    SHOW_LIB_VERSION(postproc,   POSTPROC);
3110
    writer_print_section_footer(w);
3111 3112
}

3113 3114 3115 3116 3117
#define PRINT_PIX_FMT_FLAG(flagname, name)                                \
    do {                                                                  \
        print_int(name, !!(pixdesc->flags & AV_PIX_FMT_FLAG_##flagname)); \
    } while (0)

3118 3119 3120
static void ffprobe_show_pixel_formats(WriterContext *w)
{
    const AVPixFmtDescriptor *pixdesc = NULL;
3121
    int i, n;
3122 3123 3124 3125 3126 3127

    writer_print_section_header(w, SECTION_ID_PIXEL_FORMATS);
    while (pixdesc = av_pix_fmt_desc_next(pixdesc)) {
        writer_print_section_header(w, SECTION_ID_PIXEL_FORMAT);
        print_str("name", pixdesc->name);
        print_int("nb_components", pixdesc->nb_components);
3128 3129 3130 3131 3132 3133 3134
        if ((pixdesc->nb_components >= 3) && !(pixdesc->flags & AV_PIX_FMT_FLAG_RGB)) {
            print_int    ("log2_chroma_w", pixdesc->log2_chroma_w);
            print_int    ("log2_chroma_h", pixdesc->log2_chroma_h);
        } else {
            print_str_opt("log2_chroma_w", "N/A");
            print_str_opt("log2_chroma_h", "N/A");
        }
3135 3136 3137
        n = av_get_bits_per_pixel(pixdesc);
        if (n) print_int    ("bits_per_pixel", n);
        else   print_str_opt("bits_per_pixel", "N/A");
3138 3139 3140 3141 3142 3143 3144 3145
        if (do_show_pixel_format_flags) {
            writer_print_section_header(w, SECTION_ID_PIXEL_FORMAT_FLAGS);
            PRINT_PIX_FMT_FLAG(BE,        "big_endian");
            PRINT_PIX_FMT_FLAG(PAL,       "palette");
            PRINT_PIX_FMT_FLAG(BITSTREAM, "bitstream");
            PRINT_PIX_FMT_FLAG(HWACCEL,   "hwaccel");
            PRINT_PIX_FMT_FLAG(PLANAR,    "planar");
            PRINT_PIX_FMT_FLAG(RGB,       "rgb");
3146
#if FF_API_PSEUDOPAL
3147
            PRINT_PIX_FMT_FLAG(PSEUDOPAL, "pseudopal");
3148
#endif
3149 3150 3151
            PRINT_PIX_FMT_FLAG(ALPHA,     "alpha");
            writer_print_section_footer(w);
        }
3152 3153 3154 3155 3156
        if (do_show_pixel_format_components && (pixdesc->nb_components > 0)) {
            writer_print_section_header(w, SECTION_ID_PIXEL_FORMAT_COMPONENTS);
            for (i = 0; i < pixdesc->nb_components; i++) {
                writer_print_section_header(w, SECTION_ID_PIXEL_FORMAT_COMPONENT);
                print_int("index", i + 1);
3157
                print_int("bit_depth", pixdesc->comp[i].depth);
3158 3159 3160 3161
                writer_print_section_footer(w);
            }
            writer_print_section_footer(w);
        }
3162 3163 3164 3165 3166
        writer_print_section_footer(w);
    }
    writer_print_section_footer(w);
}

3167
static int opt_format(void *optctx, const char *opt, const char *arg)
3168 3169 3170
{
    iformat = av_find_input_format(arg);
    if (!iformat) {
3171
        av_log(NULL, AV_LOG_ERROR, "Unknown input format: %s\n", arg);
3172
        return AVERROR(EINVAL);
3173
    }
3174
    return 0;
3175 3176
}

3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248
static inline void mark_section_show_entries(SectionID section_id,
                                             int show_all_entries, AVDictionary *entries)
{
    struct section *section = &sections[section_id];

    section->show_all_entries = show_all_entries;
    if (show_all_entries) {
        SectionID *id;
        for (id = section->children_ids; *id != -1; id++)
            mark_section_show_entries(*id, show_all_entries, entries);
    } else {
        av_dict_copy(&section->entries_to_show, entries, 0);
    }
}

static int match_section(const char *section_name,
                         int show_all_entries, AVDictionary *entries)
{
    int i, ret = 0;

    for (i = 0; i < FF_ARRAY_ELEMS(sections); i++) {
        const struct section *section = &sections[i];
        if (!strcmp(section_name, section->name) ||
            (section->unique_name && !strcmp(section_name, section->unique_name))) {
            av_log(NULL, AV_LOG_DEBUG,
                   "'%s' matches section with unique name '%s'\n", section_name,
                   (char *)av_x_if_null(section->unique_name, section->name));
            ret++;
            mark_section_show_entries(section->id, show_all_entries, entries);
        }
    }
    return ret;
}

static int opt_show_entries(void *optctx, const char *opt, const char *arg)
{
    const char *p = arg;
    int ret = 0;

    while (*p) {
        AVDictionary *entries = NULL;
        char *section_name = av_get_token(&p, "=:");
        int show_all_entries = 0;

        if (!section_name) {
            av_log(NULL, AV_LOG_ERROR,
                   "Missing section name for option '%s'\n", opt);
            return AVERROR(EINVAL);
        }

        if (*p == '=') {
            p++;
            while (*p && *p != ':') {
                char *entry = av_get_token(&p, ",:");
                if (!entry)
                    break;
                av_log(NULL, AV_LOG_VERBOSE,
                       "Adding '%s' to the entries to show in section '%s'\n",
                       entry, section_name);
                av_dict_set(&entries, entry, "", AV_DICT_DONT_STRDUP_KEY);
                if (*p == ',')
                    p++;
            }
        } else {
            show_all_entries = 1;
        }

        ret = match_section(section_name, show_all_entries, entries);
        if (ret == 0) {
            av_log(NULL, AV_LOG_ERROR, "No match for section '%s'\n", section_name);
            ret = AVERROR(EINVAL);
        }
3249
        av_dict_free(&entries);
3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260
        av_free(section_name);

        if (ret <= 0)
            break;
        if (*p)
            p++;
    }

    return ret;
}

3261
static int opt_show_format_entry(void *optctx, const char *opt, const char *arg)
3262
{
3263 3264 3265
    char *buf = av_asprintf("format=%s", arg);
    int ret;

3266 3267 3268
    if (!buf)
        return AVERROR(ENOMEM);

3269 3270 3271 3272 3273 3274
    av_log(NULL, AV_LOG_WARNING,
           "Option '%s' is deprecated, use '-show_entries format=%s' instead\n",
           opt, arg);
    ret = opt_show_entries(optctx, opt, buf);
    av_free(buf);
    return ret;
3275 3276
}

3277
static void opt_input_file(void *optctx, const char *arg)
Stefano Sabatini's avatar
Stefano Sabatini committed
3278
{
3279
    if (input_filename) {
3280 3281 3282
        av_log(NULL, AV_LOG_ERROR,
                "Argument '%s' provided as input filename, but '%s' was already specified.\n",
                arg, input_filename);
3283
        exit_program(1);
3284
    }
3285 3286 3287
    if (!strcmp(arg, "-"))
        arg = "pipe:";
    input_filename = arg;
Stefano Sabatini's avatar
Stefano Sabatini committed
3288 3289
}

3290 3291 3292 3293 3294 3295
static int opt_input_file_i(void *optctx, const char *opt, const char *arg)
{
    opt_input_file(optctx, arg);
    return 0;
}

3296 3297 3298 3299 3300 3301
static int opt_print_filename(void *optctx, const char *opt, const char *arg)
{
    print_input_filename = arg;
    return 0;
}

3302
void show_help_default(const char *opt, const char *arg)
Stefano Sabatini's avatar
Stefano Sabatini committed
3303
{
3304
    av_log_set_callback(log_callback_help);
Stefano Sabatini's avatar
Stefano Sabatini committed
3305
    show_usage();
3306
    show_help_options(options, "Main options:", 0, 0, 0);
Stefano Sabatini's avatar
Stefano Sabatini committed
3307
    printf("\n");
3308 3309

    show_help_children(avformat_get_class(), AV_OPT_FLAG_DECODING_PARAM);
3310
    show_help_children(avcodec_get_class(), AV_OPT_FLAG_DECODING_PARAM);
Stefano Sabatini's avatar
Stefano Sabatini committed
3311 3312
}

3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382
/**
 * Parse interval specification, according to the format:
 * INTERVAL ::= [START|+START_OFFSET][%[END|+END_OFFSET]]
 * INTERVALS ::= INTERVAL[,INTERVALS]
*/
static int parse_read_interval(const char *interval_spec,
                               ReadInterval *interval)
{
    int ret = 0;
    char *next, *p, *spec = av_strdup(interval_spec);
    if (!spec)
        return AVERROR(ENOMEM);

    if (!*spec) {
        av_log(NULL, AV_LOG_ERROR, "Invalid empty interval specification\n");
        ret = AVERROR(EINVAL);
        goto end;
    }

    p = spec;
    next = strchr(spec, '%');
    if (next)
        *next++ = 0;

    /* parse first part */
    if (*p) {
        interval->has_start = 1;

        if (*p == '+') {
            interval->start_is_offset = 1;
            p++;
        } else {
            interval->start_is_offset = 0;
        }

        ret = av_parse_time(&interval->start, p, 1);
        if (ret < 0) {
            av_log(NULL, AV_LOG_ERROR, "Invalid interval start specification '%s'\n", p);
            goto end;
        }
    } else {
        interval->has_start = 0;
    }

    /* parse second part */
    p = next;
    if (p && *p) {
        int64_t us;
        interval->has_end = 1;

        if (*p == '+') {
            interval->end_is_offset = 1;
            p++;
        } else {
            interval->end_is_offset = 0;
        }

        if (interval->end_is_offset && *p == '#') {
            long long int lli;
            char *tail;
            interval->duration_frames = 1;
            p++;
            lli = strtoll(p, &tail, 10);
            if (*tail || lli < 0) {
                av_log(NULL, AV_LOG_ERROR,
                       "Invalid or negative value '%s' for duration number of frames\n", p);
                goto end;
            }
            interval->end = lli;
        } else {
3383
            interval->duration_frames = 0;
3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412
            ret = av_parse_time(&us, p, 1);
            if (ret < 0) {
                av_log(NULL, AV_LOG_ERROR, "Invalid interval end/duration specification '%s'\n", p);
                goto end;
            }
            interval->end = us;
        }
    } else {
        interval->has_end = 0;
    }

end:
    av_free(spec);
    return ret;
}

static int parse_read_intervals(const char *intervals_spec)
{
    int ret, n, i;
    char *p, *spec = av_strdup(intervals_spec);
    if (!spec)
        return AVERROR(ENOMEM);

    /* preparse specification, get number of intervals */
    for (n = 0, p = spec; *p; p++)
        if (*p == ',')
            n++;
    n++;

3413
    read_intervals = av_malloc_array(n, sizeof(*read_intervals));
3414 3415 3416 3417 3418 3419 3420 3421
    if (!read_intervals) {
        ret = AVERROR(ENOMEM);
        goto end;
    }
    read_intervals_nb = n;

    /* parse intervals */
    p = spec;
3422 3423 3424 3425 3426
    for (i = 0; p; i++) {
        char *next;

        av_assert0(i < read_intervals_nb);
        next = strchr(p, ',');
3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452
        if (next)
            *next++ = 0;

        read_intervals[i].id = i;
        ret = parse_read_interval(p, &read_intervals[i]);
        if (ret < 0) {
            av_log(NULL, AV_LOG_ERROR, "Error parsing read interval #%d '%s'\n",
                   i, p);
            goto end;
        }
        av_log(NULL, AV_LOG_VERBOSE, "Parsed log interval ");
        log_read_interval(&read_intervals[i], NULL, AV_LOG_VERBOSE);
        p = next;
    }
    av_assert0(i == read_intervals_nb);

end:
    av_free(spec);
    return ret;
}

static int opt_read_intervals(void *optctx, const char *opt, const char *arg)
{
    return parse_read_intervals(arg);
}

3453
static int opt_pretty(void *optctx, const char *opt, const char *arg)
Stefano Sabatini's avatar
Stefano Sabatini committed
3454 3455 3456 3457 3458
{
    show_value_unit              = 1;
    use_value_prefix             = 1;
    use_byte_value_binary_prefix = 1;
    use_value_sexagesimal_format = 1;
3459
    return 0;
Stefano Sabatini's avatar
Stefano Sabatini committed
3460 3461
}

3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490
static void print_section(SectionID id, int level)
{
    const SectionID *pid;
    const struct section *section = &sections[id];
    printf("%c%c%c",
           section->flags & SECTION_FLAG_IS_WRAPPER           ? 'W' : '.',
           section->flags & SECTION_FLAG_IS_ARRAY             ? 'A' : '.',
           section->flags & SECTION_FLAG_HAS_VARIABLE_FIELDS  ? 'V' : '.');
    printf("%*c  %s", level * 4, ' ', section->name);
    if (section->unique_name)
        printf("/%s", section->unique_name);
    printf("\n");

    for (pid = section->children_ids; *pid != -1; pid++)
        print_section(*pid, level+1);
}

static int opt_sections(void *optctx, const char *opt, const char *arg)
{
    printf("Sections:\n"
           "W.. = Section is a wrapper (contains other sections, no local entries)\n"
           ".A. = Section contains an array of elements of the same type\n"
           "..V = Section may contain a variable number of fields with variable keys\n"
           "FLAGS NAME/UNIQUE_NAME\n"
           "---\n");
    print_section(SECTION_ID_ROOT, 0);
    return 0;
}

3491
static int opt_show_versions(void *optctx, const char *opt, const char *arg)
3492
{
3493 3494
    mark_section_show_entries(SECTION_ID_PROGRAM_VERSION, 1, NULL);
    mark_section_show_entries(SECTION_ID_LIBRARY_VERSION, 1, NULL);
3495 3496 3497
    return 0;
}

3498
#define DEFINE_OPT_SHOW_SECTION(section, target_section_id)             \
3499
    static int opt_show_##section(void *optctx, const char *opt, const char *arg) \
3500 3501 3502 3503 3504
    {                                                                   \
        mark_section_show_entries(SECTION_ID_##target_section_id, 1, NULL); \
        return 0;                                                       \
    }

3505 3506 3507 3508 3509 3510 3511 3512 3513 3514
DEFINE_OPT_SHOW_SECTION(chapters,         CHAPTERS)
DEFINE_OPT_SHOW_SECTION(error,            ERROR)
DEFINE_OPT_SHOW_SECTION(format,           FORMAT)
DEFINE_OPT_SHOW_SECTION(frames,           FRAMES)
DEFINE_OPT_SHOW_SECTION(library_versions, LIBRARY_VERSIONS)
DEFINE_OPT_SHOW_SECTION(packets,          PACKETS)
DEFINE_OPT_SHOW_SECTION(pixel_formats,    PIXEL_FORMATS)
DEFINE_OPT_SHOW_SECTION(program_version,  PROGRAM_VERSION)
DEFINE_OPT_SHOW_SECTION(streams,          STREAMS)
DEFINE_OPT_SHOW_SECTION(programs,         PROGRAMS)
3515

3516
static const OptionDef real_options[] = {
3517
    CMDUTILS_COMMON_OPTIONS
3518 3519 3520 3521
    { "f", HAS_ARG, {.func_arg = opt_format}, "force format", "format" },
    { "unit", OPT_BOOL, {&show_value_unit}, "show unit of the displayed values" },
    { "prefix", OPT_BOOL, {&use_value_prefix}, "use SI prefixes for the displayed values" },
    { "byte_binary_prefix", OPT_BOOL, {&use_byte_value_binary_prefix},
Stefano Sabatini's avatar
Stefano Sabatini committed
3522
      "use binary prefixes for byte units" },
3523
    { "sexagesimal", OPT_BOOL,  {&use_value_sexagesimal_format},
Stefano Sabatini's avatar
Stefano Sabatini committed
3524
      "use sexagesimal format HOURS:MM:SS.MICROSECONDS for time units" },
3525
    { "pretty", 0, {.func_arg = opt_pretty},
Stefano Sabatini's avatar
Stefano Sabatini committed
3526
      "prettify the format of displayed values, make it more human readable" },
3527
    { "print_format", OPT_STRING | HAS_ARG, {(void*)&print_format},
3528
      "set the output printing format (available formats are: default, compact, csv, flat, ini, json, xml)", "format" },
3529
    { "of", OPT_STRING | HAS_ARG, {(void*)&print_format}, "alias for -print_format", "format" },
3530
    { "select_streams", OPT_STRING | HAS_ARG, {(void*)&stream_specifier}, "select the specified streams", "stream_specifier" },
3531
    { "sections", OPT_EXIT, {.func_arg = opt_sections}, "print sections structure and section information, and exit" },
3532
    { "show_data",    OPT_BOOL, {(void*)&do_show_data}, "show packets data" },
3533
    { "show_data_hash", OPT_STRING | HAS_ARG, {(void*)&show_data_hash}, "show packets data hash" },
3534 3535 3536
    { "show_error",   0, { .func_arg = &opt_show_error },  "show probing error" },
    { "show_format",  0, { .func_arg = &opt_show_format }, "show format/container info" },
    { "show_frames",  0, { .func_arg = &opt_show_frames }, "show frames info" },
3537
    { "show_format_entry", HAS_ARG, {.func_arg = opt_show_format_entry},
3538
      "show a particular entry from the format/container info", "entry" },
3539 3540
    { "show_entries", HAS_ARG, {.func_arg = opt_show_entries},
      "show a set of specified entries", "entry_list" },
3541 3542 3543
#if HAVE_THREADS
    { "show_log", OPT_INT|HAS_ARG, {(void*)&do_show_log}, "show log" },
#endif
3544 3545 3546 3547
    { "show_packets", 0, { .func_arg = &opt_show_packets }, "show packets info" },
    { "show_programs", 0, { .func_arg = &opt_show_programs }, "show programs info" },
    { "show_streams", 0, { .func_arg = &opt_show_streams }, "show streams info" },
    { "show_chapters", 0, { .func_arg = &opt_show_chapters }, "show chapters info" },
3548 3549
    { "count_frames", OPT_BOOL, {(void*)&do_count_frames}, "count the number of frames per stream" },
    { "count_packets", OPT_BOOL, {(void*)&do_count_packets}, "count the number of packets per stream" },
3550 3551 3552 3553
    { "show_program_version",  0, { .func_arg = &opt_show_program_version },  "show ffprobe version" },
    { "show_library_versions", 0, { .func_arg = &opt_show_library_versions }, "show library versions" },
    { "show_versions",         0, { .func_arg = &opt_show_versions }, "show program and library versions" },
    { "show_pixel_formats", 0, { .func_arg = &opt_show_pixel_formats }, "show pixel format descriptions" },
3554 3555
    { "show_private_data", OPT_BOOL, {(void*)&show_private_data}, "show private data" },
    { "private",           OPT_BOOL, {(void*)&show_private_data}, "same as show_private_data" },
3556
    { "bitexact", OPT_BOOL, {&do_bitexact}, "force bitexact output" },
3557
    { "read_intervals", HAS_ARG, {.func_arg = opt_read_intervals}, "set read intervals", "read_intervals" },
3558
    { "default", HAS_ARG | OPT_AUDIO | OPT_VIDEO | OPT_EXPERT, {.func_arg = opt_default}, "generic catch all option", "" },
3559
    { "i", HAS_ARG, {.func_arg = opt_input_file_i}, "read specified file", "input_file"},
3560
    { "print_filename", HAS_ARG, {.func_arg = opt_print_filename}, "override the printed input filename", "print_file"},
3561 3562
    { "find_stream_info", OPT_BOOL | OPT_INPUT | OPT_EXPERT, { &find_stream_info },
        "read and decode the streams to fill missing information with heuristics" },
Stefano Sabatini's avatar
Stefano Sabatini committed
3563 3564 3565
    { NULL, },
};

3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582
static inline int check_section_show_entries(int section_id)
{
    int *id;
    struct section *section = &sections[section_id];
    if (sections[section_id].show_all_entries || sections[section_id].entries_to_show)
        return 1;
    for (id = section->children_ids; *id != -1; id++)
        if (check_section_show_entries(*id))
            return 1;
    return 0;
}

#define SET_DO_SHOW(id, varname) do {                                   \
        if (check_section_show_entries(SECTION_ID_##id))                \
            do_show_##varname = 1;                                      \
    } while (0)

Stefano Sabatini's avatar
Stefano Sabatini committed
3583 3584
int main(int argc, char **argv)
{
3585 3586 3587 3588
    const Writer *w;
    WriterContext *wctx;
    char *buf;
    char *w_name = NULL, *w_args = NULL;
3589
    int ret, i;
3590

3591 3592
    init_dynload();

3593 3594 3595 3596 3597 3598
#if HAVE_THREADS
    ret = pthread_mutex_init(&log_mutex, NULL);
    if (ret != 0) {
        goto end;
    }
#endif
3599
    av_log_set_flags(AV_LOG_SKIP_REPEATED);
3600
    register_exit(ffprobe_cleanup);
3601

3602
    options = real_options;
3603
    parse_loglevel(argc, argv, options);
3604
    avformat_network_init();
3605
    init_opts();
3606 3607 3608
#if CONFIG_AVDEVICE
    avdevice_register_all();
#endif
Stefano Sabatini's avatar
Stefano Sabatini committed
3609

3610
    show_banner(argc, argv, options);
3611
    parse_options(NULL, argc, argv, options, opt_input_file);
Stefano Sabatini's avatar
Stefano Sabatini committed
3612

3613 3614 3615
    if (do_show_log)
        av_log_set_callback(log_callback);

3616
    /* mark things to show, based on -show_entries */
3617
    SET_DO_SHOW(CHAPTERS, chapters);
3618 3619 3620 3621 3622
    SET_DO_SHOW(ERROR, error);
    SET_DO_SHOW(FORMAT, format);
    SET_DO_SHOW(FRAMES, frames);
    SET_DO_SHOW(LIBRARY_VERSIONS, library_versions);
    SET_DO_SHOW(PACKETS, packets);
3623
    SET_DO_SHOW(PIXEL_FORMATS, pixel_formats);
3624
    SET_DO_SHOW(PIXEL_FORMAT_FLAGS, pixel_format_flags);
3625
    SET_DO_SHOW(PIXEL_FORMAT_COMPONENTS, pixel_format_components);
3626
    SET_DO_SHOW(PROGRAM_VERSION, program_version);
3627
    SET_DO_SHOW(PROGRAMS, programs);
3628 3629
    SET_DO_SHOW(STREAMS, streams);
    SET_DO_SHOW(STREAM_DISPOSITION, stream_disposition);
3630
    SET_DO_SHOW(PROGRAM_STREAM_DISPOSITION, stream_disposition);
3631

3632 3633 3634 3635 3636
    SET_DO_SHOW(CHAPTER_TAGS, chapter_tags);
    SET_DO_SHOW(FORMAT_TAGS, format_tags);
    SET_DO_SHOW(FRAME_TAGS, frame_tags);
    SET_DO_SHOW(PROGRAM_TAGS, program_tags);
    SET_DO_SHOW(STREAM_TAGS, stream_tags);
3637
    SET_DO_SHOW(PROGRAM_STREAM_TAGS, stream_tags);
3638
    SET_DO_SHOW(PACKET_TAGS, packet_tags);
3639

3640 3641 3642 3643 3644 3645 3646 3647
    if (do_bitexact && (do_show_program_version || do_show_library_versions)) {
        av_log(NULL, AV_LOG_ERROR,
               "-bitexact and -show_program_version or -show_library_versions "
               "options are incompatible\n");
        ret = AVERROR(EINVAL);
        goto end;
    }

3648 3649 3650 3651
    writer_register_all();

    if (!print_format)
        print_format = av_strdup("default");
3652 3653 3654 3655
    if (!print_format) {
        ret = AVERROR(ENOMEM);
        goto end;
    }
3656
    w_name = av_strtok(print_format, "=", &buf);
3657 3658 3659 3660 3661 3662
    if (!w_name) {
        av_log(NULL, AV_LOG_ERROR,
               "No name specified for the output format\n");
        ret = AVERROR(EINVAL);
        goto end;
    }
3663 3664
    w_args = buf;

3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679
    if (show_data_hash) {
        if ((ret = av_hash_alloc(&hash, show_data_hash)) < 0) {
            if (ret == AVERROR(EINVAL)) {
                const char *n;
                av_log(NULL, AV_LOG_ERROR,
                       "Unknown hash algorithm '%s'\nKnown algorithms:",
                       show_data_hash);
                for (i = 0; (n = av_hash_names(i)); i++)
                    av_log(NULL, AV_LOG_ERROR, " %s", n);
                av_log(NULL, AV_LOG_ERROR, "\n");
            }
            goto end;
        }
    }

3680 3681 3682 3683 3684
    w = writer_get_by_name(w_name);
    if (!w) {
        av_log(NULL, AV_LOG_ERROR, "Unknown output format with name '%s'\n", w_name);
        ret = AVERROR(EINVAL);
        goto end;
Stefano Sabatini's avatar
Stefano Sabatini committed
3685 3686
    }

3687 3688
    if ((ret = writer_open(&wctx, w, w_args,
                           sections, FF_ARRAY_ELEMS(sections))) >= 0) {
3689 3690 3691
        if (w == &xml_writer)
            wctx->string_validation_utf8_flags |= AV_UTF8_FLAG_EXCLUDE_XML_INVALID_CONTROL_CODES;

3692
        writer_print_section_header(wctx, SECTION_ID_ROOT);
3693

3694 3695 3696 3697
        if (do_show_program_version)
            ffprobe_show_program_version(wctx);
        if (do_show_library_versions)
            ffprobe_show_library_versions(wctx);
3698 3699
        if (do_show_pixel_formats)
            ffprobe_show_pixel_formats(wctx);
3700 3701

        if (!input_filename &&
3702
            ((do_show_format || do_show_programs || do_show_streams || do_show_chapters || do_show_packets || do_show_error) ||
3703
             (!do_show_program_version && !do_show_library_versions && !do_show_pixel_formats))) {
3704 3705 3706 3707
            show_usage();
            av_log(NULL, AV_LOG_ERROR, "You have to specify one input file.\n");
            av_log(NULL, AV_LOG_ERROR, "Use -h to get full help or, even better, run 'man %s'.\n", program_name);
            ret = AVERROR(EINVAL);
3708
        } else if (input_filename) {
3709
            ret = probe_file(wctx, input_filename, print_input_filename);
3710 3711 3712
            if (ret < 0 && do_show_error)
                show_error(wctx, ret);
        }
3713

3714
        writer_print_section_footer(wctx);
3715 3716
        writer_close(&wctx);
    }
3717

3718 3719
end:
    av_freep(&print_format);
3720
    av_freep(&read_intervals);
3721
    av_hash_freep(&hash);
3722 3723

    uninit_opts();
3724 3725
    for (i = 0; i < FF_ARRAY_ELEMS(sections); i++)
        av_dict_free(&(sections[i].entries_to_show));
3726

3727 3728
    avformat_network_deinit();

3729
    return ret < 0;
Stefano Sabatini's avatar
Stefano Sabatini committed
3730
}