ffprobe.c 82.1 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 "version.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/opt.h"
Stefano Sabatini's avatar
Stefano Sabatini committed
37
#include "libavutil/pixdesc.h"
38
#include "libavutil/dict.h"
39
#include "libavutil/libm.h"
40
#include "libavutil/timecode.h"
41
#include "libavdevice/avdevice.h"
42 43 44
#include "libswscale/swscale.h"
#include "libswresample/swresample.h"
#include "libpostproc/postprocess.h"
Stefano Sabatini's avatar
Stefano Sabatini committed
45 46
#include "cmdutils.h"

47
const char program_name[] = "ffprobe";
Stefano Sabatini's avatar
Stefano Sabatini committed
48 49
const int program_birth_year = 2007;

50
static int do_bitexact = 0;
51 52 53 54
static int do_count_frames = 0;
static int do_count_packets = 0;
static int do_read_frames  = 0;
static int do_read_packets = 0;
55
static int do_show_error   = 0;
Stefano Sabatini's avatar
Stefano Sabatini committed
56
static int do_show_format  = 0;
57
static int do_show_frames  = 0;
58
static int do_show_packets = 0;
Stefano Sabatini's avatar
Stefano Sabatini committed
59
static int do_show_streams = 0;
60
static int do_show_stream_disposition = 0;
61
static int do_show_data    = 0;
62 63
static int do_show_program_version  = 0;
static int do_show_library_versions = 0;
Stefano Sabatini's avatar
Stefano Sabatini committed
64 65 66 67 68

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;
69
static int show_private_data            = 1;
Stefano Sabatini's avatar
Stefano Sabatini committed
70

71
static char *print_format;
72
static char *stream_specifier;
73

74 75
/* section structure definition */

76 77
#define SECTION_MAX_NB_CHILDREN 10

78
struct section {
79
    int id;             ///< unique id identifying a section
80 81 82 83
    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
84 85
#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.
86
    int flags;
87
    int children_ids[SECTION_MAX_NB_CHILDREN+1]; ///< list of children section IDS, terminated by -1
88
    const char *element_name; ///< name of the contained element, if provided
89 90 91
    const char *unique_name;  ///< unique section name, in case the name is ambiguous
    AVDictionary *entries_to_show;
    int show_all_entries;
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
};

typedef enum {
    SECTION_ID_NONE = -1,
    SECTION_ID_ERROR,
    SECTION_ID_FORMAT,
    SECTION_ID_FORMAT_TAGS,
    SECTION_ID_FRAME,
    SECTION_ID_FRAMES,
    SECTION_ID_FRAME_TAGS,
    SECTION_ID_LIBRARY_VERSION,
    SECTION_ID_LIBRARY_VERSIONS,
    SECTION_ID_PACKET,
    SECTION_ID_PACKETS,
    SECTION_ID_PACKETS_AND_FRAMES,
    SECTION_ID_PROGRAM_VERSION,
    SECTION_ID_ROOT,
    SECTION_ID_STREAM,
110
    SECTION_ID_STREAM_DISPOSITION,
111
    SECTION_ID_STREAMS,
112
    SECTION_ID_STREAM_TAGS,
113 114
} SectionID;

115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
static struct section sections[] = {
    [SECTION_ID_ERROR] =              { SECTION_ID_ERROR, "error", 0, { -1 } },
    [SECTION_ID_FORMAT] =             { SECTION_ID_FORMAT, "format", 0, { SECTION_ID_FORMAT_TAGS, -1 } },
    [SECTION_ID_FORMAT_TAGS] =        { SECTION_ID_FORMAT_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "format_tags" },
    [SECTION_ID_FRAMES] =             { SECTION_ID_FRAMES, "frames", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME, -1 } },
    [SECTION_ID_FRAME] =              { SECTION_ID_FRAME, "frame", 0, { SECTION_ID_FRAME_TAGS, -1 } },
    [SECTION_ID_FRAME_TAGS] =         { SECTION_ID_FRAME_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "frame_tags" },
    [SECTION_ID_LIBRARY_VERSIONS] =   { SECTION_ID_LIBRARY_VERSIONS, "library_versions", SECTION_FLAG_IS_ARRAY, { SECTION_ID_LIBRARY_VERSION, -1 } },
    [SECTION_ID_LIBRARY_VERSION] =    { SECTION_ID_LIBRARY_VERSION, "library_version", 0, { -1 } },
    [SECTION_ID_PACKETS] =            { SECTION_ID_PACKETS, "packets", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PACKET, -1} },
    [SECTION_ID_PACKETS_AND_FRAMES] = { SECTION_ID_PACKETS_AND_FRAMES, "packets_and_frames", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PACKET, -1} },
    [SECTION_ID_PACKET] =             { SECTION_ID_PACKET, "packet", 0, { -1 } },
    [SECTION_ID_PROGRAM_VERSION] =    { SECTION_ID_PROGRAM_VERSION, "program_version", 0, { -1 } },
    [SECTION_ID_ROOT] =               { SECTION_ID_ROOT, "root", SECTION_FLAG_IS_WRAPPER,
                                        { SECTION_ID_FORMAT, SECTION_ID_FRAMES, SECTION_ID_STREAMS, SECTION_ID_PACKETS,
                                          SECTION_ID_ERROR, SECTION_ID_PROGRAM_VERSION, SECTION_ID_LIBRARY_VERSIONS, -1} },
    [SECTION_ID_STREAMS] =            { SECTION_ID_STREAMS, "streams", SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM, -1 } },
    [SECTION_ID_STREAM] =             { SECTION_ID_STREAM, "stream", 0, { SECTION_ID_STREAM_DISPOSITION, SECTION_ID_STREAM_TAGS, -1 } },
    [SECTION_ID_STREAM_DISPOSITION] = { SECTION_ID_STREAM_DISPOSITION, "disposition", 0, { -1 }, .unique_name = "stream_disposition" },
    [SECTION_ID_STREAM_TAGS] =        { SECTION_ID_STREAM_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "stream_tags" },
135 136
};

137
static const OptionDef *options;
Stefano Sabatini's avatar
Stefano Sabatini committed
138 139 140

/* FFprobe context */
static const char *input_filename;
141
static AVInputFormat *iformat = NULL;
Stefano Sabatini's avatar
Stefano Sabatini committed
142

143 144
static const char *const binary_unit_prefixes [] = { "", "Ki", "Mi", "Gi", "Ti", "Pi" };
static const char *const decimal_unit_prefixes[] = { "", "K" , "M" , "G" , "T" , "P"  };
Stefano Sabatini's avatar
Stefano Sabatini committed
145

146 147 148 149
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";
150

151 152
static uint64_t *nb_streams_packets;
static uint64_t *nb_streams_frames;
153
static int *selected_streams;
Stefano Sabatini's avatar
Stefano Sabatini committed
154

155
static void exit_program(void)
156
{
157 158 159
    int i;
    for (i = 0; i < FF_ARRAY_ELEMS(sections); i++)
        av_dict_free(&(sections[i].entries_to_show));
160 161
}

162
struct unit_value {
163
    union { double d; long long int i; } val;
164 165 166 167
    const char *unit;
};

static char *value_string(char *buf, int buf_size, struct unit_value uv)
Stefano Sabatini's avatar
Stefano Sabatini committed
168
{
169
    double vald;
170
    long long int vali;
171 172 173 174 175 176
    int show_float = 0;

    if (uv.unit == unit_second_str) {
        vald = uv.val.d;
        show_float = 1;
    } else {
177
        vald = vali = uv.val.i;
178 179 180
    }

    if (uv.unit == unit_second_str && use_value_sexagesimal_format) {
Stefano Sabatini's avatar
Stefano Sabatini committed
181 182
        double secs;
        int hours, mins;
183
        secs  = vald;
Stefano Sabatini's avatar
Stefano Sabatini committed
184 185 186 187 188
        mins  = (int)secs / 60;
        secs  = secs - mins * 60;
        hours = mins / 60;
        mins %= 60;
        snprintf(buf, buf_size, "%d:%02d:%09.6f", hours, mins, secs);
189 190
    } else {
        const char *prefix_string = "";
Stefano Sabatini's avatar
Stefano Sabatini committed
191

192
        if (use_value_prefix && vald > 1) {
193 194 195
            long long int index;

            if (uv.unit == unit_byte_str && use_byte_value_binary_prefix) {
196
                index = (long long int) (log2(vald)) / 10;
197
                index = av_clip(index, 0, FF_ARRAY_ELEMS(binary_unit_prefixes) - 1);
198
                vald /= exp2(index * 10);
199 200 201 202 203 204 205
                prefix_string = binary_unit_prefixes[index];
            } else {
                index = (long long int) (log10(vald)) / 3;
                index = av_clip(index, 0, FF_ARRAY_ELEMS(decimal_unit_prefixes) - 1);
                vald /= pow(10, index * 3);
                prefix_string = decimal_unit_prefixes[index];
            }
206
        }
Stefano Sabatini's avatar
Stefano Sabatini committed
207

208
        if (show_float || (use_value_prefix && vald != (long long int)vald))
209
            snprintf(buf, buf_size, "%f", vald);
210
        else
211
            snprintf(buf, buf_size, "%lld", vali);
212
        av_strlcatf(buf, buf_size, "%s%s%s", *prefix_string || show_value_unit ? " " : "",
213
                 prefix_string, show_value_unit ? uv.unit : "");
Stefano Sabatini's avatar
Stefano Sabatini committed
214 215 216 217 218
    }

    return buf;
}

219
/* WRITERS API */
220

221
typedef struct WriterContext WriterContext;
222

223
#define WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS 1
224
#define WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER 2
225

226
typedef struct Writer {
227
    const AVClass *priv_class;      ///< private class of the writer, if any
228 229
    int priv_size;                  ///< private size for the writer context
    const char *name;
230

231
    int  (*init)  (WriterContext *wctx);
232 233
    void (*uninit)(WriterContext *wctx);

234 235
    void (*print_section_header)(WriterContext *wctx);
    void (*print_section_footer)(WriterContext *wctx);
236
    void (*print_integer)       (WriterContext *wctx, const char *, long long int);
237
    void (*print_rational)      (WriterContext *wctx, AVRational *q, char *sep);
238
    void (*print_string)        (WriterContext *wctx, const char *, const char *);
239
    int flags;                  ///< a combination or WRITER_FLAG_*
240 241
} Writer;

242 243
#define SECTION_MAX_NB_LEVELS 10

244 245 246 247 248
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
249 250 251 252 253 254 255 256 257 258 259

    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];
260 261
    AVBPrint section_pbuf[SECTION_MAX_NB_LEVELS]; ///< generic print buffer dedicated to each section,
                                                  ///  used by various writers
262

263 264 265
    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
266
};
267

268 269 270 271 272 273 274 275 276 277 278 279 280
static const char *writer_get_name(void *p)
{
    WriterContext *wctx = p;
    return wctx->writer->name;
}

static const AVClass writer_class = {
    "Writer",
    writer_get_name,
    NULL,
    LIBAVUTIL_VERSION_INT,
};

281
static void writer_close(WriterContext **wctx)
282
{
283 284
    int i;

285 286
    if (!*wctx)
        return;
287

288 289
    if ((*wctx)->writer->uninit)
        (*wctx)->writer->uninit(*wctx);
290 291
    for (i = 0; i < SECTION_MAX_NB_LEVELS; i++)
        av_bprint_finalize(&(*wctx)->section_pbuf[i], NULL);
292 293
    if ((*wctx)->writer->priv_class)
        av_opt_free((*wctx)->priv);
294 295
    av_freep(&((*wctx)->priv));
    av_freep(wctx);
296 297
}

298 299
static int writer_open(WriterContext **wctx, const Writer *writer, const char *args,
                       const struct section *sections, int nb_sections)
300
{
301
    int i, ret = 0;
302

303 304 305
    if (!(*wctx = av_malloc(sizeof(WriterContext)))) {
        ret = AVERROR(ENOMEM);
        goto fail;
306 307
    }

308 309 310
    if (!((*wctx)->priv = av_mallocz(writer->priv_size))) {
        ret = AVERROR(ENOMEM);
        goto fail;
311
    }
312

313
    (*wctx)->class = &writer_class;
314
    (*wctx)->writer = writer;
315 316 317
    (*wctx)->level = -1;
    (*wctx)->sections = sections;
    (*wctx)->nb_sections = nb_sections;
318 319 320 321 322 323 324 325 326 327

    if (writer->priv_class) {
        void *priv_ctx = (*wctx)->priv;
        *((const AVClass **)priv_ctx) = writer->priv_class;
        av_opt_set_defaults(priv_ctx);

        if (args &&
            (ret = av_set_options_string(priv_ctx, args, "=", ":")) < 0)
            goto fail;
    }
328 329 330 331

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

332
    if ((*wctx)->writer->init)
333
        ret = (*wctx)->writer->init(*wctx);
334 335 336 337 338 339 340
    if (ret < 0)
        goto fail;

    return 0;

fail:
    writer_close(wctx);
341 342 343
    return ret;
}

344
static inline void writer_print_section_header(WriterContext *wctx,
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363
                                               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;
    }

364
    if (wctx->writer->print_section_header)
365
        wctx->writer->print_section_header(wctx);
366 367
}

368
static inline void writer_print_section_footer(WriterContext *wctx)
369
{
370 371 372 373 374 375 376 377 378
    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++;
379
    }
380 381 382
    if (wctx->writer->print_section_footer)
        wctx->writer->print_section_footer(wctx);
    wctx->level--;
383
}
384

385
static inline void writer_print_integer(WriterContext *wctx,
386
                                        const char *key, long long int val)
387
{
388 389 390
    const struct section *section = wctx->section[wctx->level];

    if (section->show_all_entries || av_dict_get(section->entries_to_show, key, NULL, 0)) {
391
        wctx->writer->print_integer(wctx, key, val);
392
        wctx->nb_item[wctx->level]++;
393
    }
394 395
}

396
static inline void writer_print_string(WriterContext *wctx,
397
                                       const char *key, const char *val, int opt)
398
{
399 400
    const struct section *section = wctx->section[wctx->level];

401 402
    if (opt && !(wctx->writer->flags & WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS))
        return;
403 404

    if (section->show_all_entries || av_dict_get(section->entries_to_show, key, NULL, 0)) {
405
        wctx->writer->print_string(wctx, key, val);
406
        wctx->nb_item[wctx->level]++;
407
    }
408 409
}

410 411 412 413 414 415 416 417 418
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);
}

419
static void writer_print_time(WriterContext *wctx, const char *key,
420
                              int64_t ts, const AVRational *time_base, int is_duration)
421 422 423
{
    char buf[128];

424 425 426 427 428 429 430 431 432 433
    if ((!is_duration && ts == AV_NOPTS_VALUE) || (is_duration && ts == 0)) {
        writer_print_string(wctx, key, "N/A", 1);
    } 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);
    }
434 435
}

436
static void writer_print_ts(WriterContext *wctx, const char *key, int64_t ts, int is_duration)
437
{
438
    if ((!is_duration && ts == AV_NOPTS_VALUE) || (is_duration && ts == 0)) {
439 440
        writer_print_string(wctx, key, "N/A", 1);
    } else {
441
        writer_print_integer(wctx, key, ts);
442 443 444
    }
}

445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472
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);
}

473 474
#define MAX_REGISTERED_WRITERS_NB 64

475
static const Writer *registered_writers[MAX_REGISTERED_WRITERS_NB + 1];
476

477
static int writer_register(const Writer *writer)
478
{
479 480 481 482 483 484 485
    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;
486 487
}

488
static const Writer *writer_get_by_name(const char *name)
489 490 491 492 493 494 495 496 497
{
    int i;

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

    return NULL;
}
498

499

500
/* WRITERS */
501

502 503 504 505 506 507 508 509 510 511 512
#define DEFINE_WRITER_CLASS(name)                   \
static const char *name##_get_name(void *ctx)       \
{                                                   \
    return #name ;                                  \
}                                                   \
static const AVClass name##_class = {               \
    #name,                                          \
    name##_get_name,                                \
    name##_options                                  \
}

513 514
/* Default output */

515 516
typedef struct DefaultContext {
    const AVClass *class;
517
    int nokey;
518
    int noprint_wrappers;
519
    int nested_section[SECTION_MAX_NB_LEVELS];
520 521 522 523 524
} DefaultContext;

#define OFFSET(x) offsetof(DefaultContext, x)

static const AVOption default_options[] = {
525 526 527 528
    { "noprint_wrappers", "do not print headers and footers", OFFSET(noprint_wrappers), AV_OPT_TYPE_INT, {.i64=0}, 0, 1 },
    { "nw",               "do not print headers and footers", OFFSET(noprint_wrappers), AV_OPT_TYPE_INT, {.i64=0}, 0, 1 },
    { "nokey",          "force no key printing",     OFFSET(nokey),          AV_OPT_TYPE_INT, {.i64=0}, 0, 1 },
    { "nk",             "force no key printing",     OFFSET(nokey),          AV_OPT_TYPE_INT, {.i64=0}, 0, 1 },
529 530 531
    {NULL},
};

532
DEFINE_WRITER_CLASS(default);
533

534 535 536 537 538
/* 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++)
539
        dst[i] = av_toupper(src[i]);
540 541 542 543
    dst[i] = 0;
    return dst;
}

544
static void default_print_section_header(WriterContext *wctx)
545
{
546
    DefaultContext *def = wctx->priv;
547
    char buf[32];
548 549 550 551
    const struct section *section = wctx->section[wctx->level];
    const struct section *parent_section = wctx->level ?
        wctx->section[wctx->level-1] : NULL;

552
    av_bprint_clear(&wctx->section_pbuf[wctx->level]);
553 554 555
    if (parent_section &&
        !(parent_section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY))) {
        def->nested_section[wctx->level] = 1;
556 557
        av_bprintf(&wctx->section_pbuf[wctx->level], "%s%s:",
                   wctx->section_pbuf[wctx->level-1].str,
558 559 560 561 562
                   upcase_string(buf, sizeof(buf),
                                 av_x_if_null(section->element_name, section->name)));
    }

    if (def->noprint_wrappers || def->nested_section[wctx->level])
563
        return;
564

565 566
    if (!(section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY)))
        printf("[%s]\n", upcase_string(buf, sizeof(buf), section->name));
567 568
}

569
static void default_print_section_footer(WriterContext *wctx)
570
{
571
    DefaultContext *def = wctx->priv;
572
    const struct section *section = wctx->section[wctx->level];
573 574
    char buf[32];

575
    if (def->noprint_wrappers || def->nested_section[wctx->level])
576 577 578 579
        return;

    if (!(section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY)))
        printf("[/%s]\n", upcase_string(buf, sizeof(buf), section->name));
580 581 582 583
}

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

586
    if (!def->nokey)
587
        printf("%s%s=", wctx->section_pbuf[wctx->level].str, key);
588
    printf("%s\n", value);
589 590
}

591
static void default_print_int(WriterContext *wctx, const char *key, long long int value)
592
{
593 594 595
    DefaultContext *def = wctx->priv;

    if (!def->nokey)
596
        printf("%s%s=", wctx->section_pbuf[wctx->level].str, key);
597
    printf("%lld\n", value);
598 599
}

600
static const Writer default_writer = {
601
    .name                  = "default",
602
    .priv_size             = sizeof(DefaultContext),
603 604 605 606
    .print_section_header  = default_print_section_header,
    .print_section_footer  = default_print_section_footer,
    .print_integer         = default_print_int,
    .print_string          = default_print_str,
607
    .flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS,
608
    .priv_class            = &default_class,
609 610
};

611 612 613
/* Compact output */

/**
614
 * Apply C-language-like string escaping.
615
 */
616
static const char *c_escape_str(AVBPrint *dst, const char *src, const char sep, void *log_ctx)
617 618 619 620
{
    const char *p;

    for (p = src; *p; p++) {
621
        switch (*p) {
622 623
        case '\b': av_bprintf(dst, "%s", "\\b");  break;
        case '\f': av_bprintf(dst, "%s", "\\f");  break;
624 625 626
        case '\n': av_bprintf(dst, "%s", "\\n");  break;
        case '\r': av_bprintf(dst, "%s", "\\r");  break;
        case '\\': av_bprintf(dst, "%s", "\\\\"); break;
627 628
        default:
            if (*p == sep)
629 630
                av_bprint_chars(dst, '\\', 1);
            av_bprint_chars(dst, *p, 1);
631 632
        }
    }
633
    return dst->str;
634 635 636 637 638
}

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

644
    if (needs_quoting)
645
        av_bprint_chars(dst, '"', 1);
646

647 648
    for (; *src; src++) {
        if (*src == '"')
649
            av_bprint_chars(dst, '"', 1);
650
        av_bprint_chars(dst, *src, 1);
651
    }
652
    if (needs_quoting)
653
        av_bprint_chars(dst, '"', 1);
654
    return dst->str;
655 656
}

657
static const char *none_escape_str(AVBPrint *dst, const char *src, const char sep, void *log_ctx)
658 659 660 661 662 663 664 665 666
{
    return src;
}

typedef struct CompactContext {
    const AVClass *class;
    char *item_sep_str;
    char item_sep;
    int nokey;
667
    int print_section;
668
    char *escape_mode_str;
669
    const char * (*escape_str)(AVBPrint *dst, const char *src, const char sep, void *log_ctx);
670
    int nested_section[SECTION_MAX_NB_LEVELS];
671 672
} CompactContext;

673
#undef OFFSET
674 675 676 677 678
#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 },
679 680
    {"nokey",    "force no key printing", OFFSET(nokey),           AV_OPT_TYPE_INT,    {.i64=0},    0,        1        },
    {"nk",       "force no key printing", OFFSET(nokey),           AV_OPT_TYPE_INT,    {.i64=0},    0,        1        },
681 682
    {"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 },
683 684
    {"print_section", "print section name", OFFSET(print_section), AV_OPT_TYPE_INT,    {.i64=1},    0,        1        },
    {"p",             "print section name", OFFSET(print_section), AV_OPT_TYPE_INT,    {.i64=1},    0,        1        },
685 686 687
    {NULL},
};

688
DEFINE_WRITER_CLASS(compact);
689

690
static av_cold int compact_init(WriterContext *wctx)
691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711
{
    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;
}

712
static void compact_print_section_header(WriterContext *wctx)
713 714
{
    CompactContext *compact = wctx->priv;
715
    const struct section *section = wctx->section[wctx->level];
716 717
    const struct section *parent_section = wctx->level ?
        wctx->section[wctx->level-1] : NULL;
718

719
    av_bprint_clear(&wctx->section_pbuf[wctx->level]);
720 721 722
    if (parent_section &&
        !(parent_section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY))) {
        compact->nested_section[wctx->level] = 1;
723 724
        av_bprintf(&wctx->section_pbuf[wctx->level], "%s%s:",
                   wctx->section_pbuf[wctx->level-1].str,
725
                   (char *)av_x_if_null(section->element_name, section->name));
726
        wctx->nb_item[wctx->level] = wctx->nb_item[wctx->level-1];
727
    } else if (compact->print_section &&
728
        !(section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY)))
729
       printf("%s%c", section->name, compact->item_sep);
730 731
}

732
static void compact_print_section_footer(WriterContext *wctx)
733
{
734
    CompactContext *compact = wctx->priv;
735

736 737
    if (!compact->nested_section[wctx->level] &&
        !(wctx->section[wctx->level]->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY)))
738
        printf("\n");
739 740 741 742 743
}

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

746
    if (wctx->nb_item[wctx->level]) printf("%c", compact->item_sep);
747
    if (!compact->nokey)
748
        printf("%s%s=", wctx->section_pbuf[wctx->level].str, key);
749 750 751
    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);
752 753
}

754
static void compact_print_int(WriterContext *wctx, const char *key, long long int value)
755 756 757
{
    CompactContext *compact = wctx->priv;

758
    if (wctx->nb_item[wctx->level]) printf("%c", compact->item_sep);
759
    if (!compact->nokey)
760
        printf("%s%s=", wctx->section_pbuf[wctx->level].str, key);
761
    printf("%lld", value);
762 763
}

764
static const Writer compact_writer = {
765 766 767 768 769 770 771 772
    .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,
773
    .priv_class           = &compact_class,
774 775
};

776 777
/* CSV output */

778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793
#undef OFFSET
#define OFFSET(x) offsetof(CompactContext, x)

static const AVOption csv_options[] = {
    {"item_sep", "set item separator",    OFFSET(item_sep_str),    AV_OPT_TYPE_STRING, {.str=","},  CHAR_MIN, CHAR_MAX },
    {"s",        "set item separator",    OFFSET(item_sep_str),    AV_OPT_TYPE_STRING, {.str=","},  CHAR_MIN, CHAR_MAX },
    {"nokey",    "force no key printing", OFFSET(nokey),           AV_OPT_TYPE_INT,    {.i64=1},    0,        1        },
    {"nk",       "force no key printing", OFFSET(nokey),           AV_OPT_TYPE_INT,    {.i64=1},    0,        1        },
    {"escape",   "set escape mode",       OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="csv"}, CHAR_MIN, CHAR_MAX },
    {"e",        "set escape mode",       OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="csv"}, CHAR_MIN, CHAR_MAX },
    {"print_section", "print section name", OFFSET(print_section), AV_OPT_TYPE_INT,    {.i64=1},    0,        1        },
    {"p",             "print section name", OFFSET(print_section), AV_OPT_TYPE_INT,    {.i64=1},    0,        1        },
    {NULL},
};

DEFINE_WRITER_CLASS(csv);
794

795
static const Writer csv_writer = {
796 797
    .name                 = "csv",
    .priv_size            = sizeof(CompactContext),
798
    .init                 = compact_init,
799 800 801 802 803
    .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,
804
    .priv_class           = &csv_class,
805 806
};

807 808 809 810 811 812 813 814 815 816 817 818 819 820 821
/* 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 },
822 823
    {"hierarchical", "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_INT, {.i64=1}, 0, 1 },
    {"h",           "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_INT, {.i64=1}, 0, 1 },
824 825 826
    {NULL},
};

827
DEFINE_WRITER_CLASS(flat);
828

829
static av_cold int flat_init(WriterContext *wctx)
830 831 832 833 834 835 836 837 838
{
    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];
839

840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867
    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;
868 869
        case '`':  av_bprintf(dst, "%s", "\\`");  break;
        case '$':  av_bprintf(dst, "%s", "\\$");  break;
870 871 872 873 874 875
        default:   av_bprint_chars(dst, *p, 1);   break;
        }
    }
    return dst->str;
}

876
static void flat_print_section_header(WriterContext *wctx)
877 878
{
    FlatContext *flat = wctx->priv;
879
    AVBPrint *buf = &wctx->section_pbuf[wctx->level];
880 881 882
    const struct section *section = wctx->section[wctx->level];
    const struct section *parent_section = wctx->level ?
        wctx->section[wctx->level-1] : NULL;
883

884 885
    /* build section header */
    av_bprint_clear(buf);
886 887
    if (!parent_section)
        return;
888
    av_bprintf(buf, "%s", wctx->section_pbuf[wctx->level-1].str);
889

890 891 892
    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);
893

894 895 896 897 898
        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);
        }
899
    }
900 901 902 903
}

static void flat_print_int(WriterContext *wctx, const char *key, long long int value)
{
904
    printf("%s%s=%lld\n", wctx->section_pbuf[wctx->level].str, key, value);
905 906 907 908 909 910 911
}

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

912
    printf("%s", wctx->section_pbuf[wctx->level].str);
913 914 915 916 917 918 919 920 921 922 923 924 925 926 927
    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,
928
    .priv_class            = &flat_class,
929 930
};

931 932 933 934 935 936 937 938 939 940 941
/* INI format output */

typedef struct {
    const AVClass *class;
    int hierarchical;
} INIContext;

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

static const AVOption ini_options[] = {
942 943
    {"hierarchical", "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_INT, {.i64=1}, 0, 1 },
    {"h",           "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_INT, {.i64=1}, 0, 1 },
944 945 946
    {NULL},
};

947
DEFINE_WRITER_CLASS(ini);
948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975

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;
}

976
static void ini_print_section_header(WriterContext *wctx)
977 978
{
    INIContext *ini = wctx->priv;
979
    AVBPrint *buf = &wctx->section_pbuf[wctx->level];
980 981 982
    const struct section *section = wctx->section[wctx->level];
    const struct section *parent_section = wctx->level ?
        wctx->section[wctx->level-1] : NULL;
983

984 985
    av_bprint_clear(buf);
    if (!parent_section) {
986 987 988
        printf("# ffprobe output\n\n");
        return;
    }
989

990
    if (wctx->nb_item[wctx->level-1])
991 992
        printf("\n");

993
    av_bprintf(buf, "%s", wctx->section_pbuf[wctx->level-1].str);
994 995 996
    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);
997

998 999 1000 1001 1002
        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);
        }
1003
    }
1004

1005
    if (!(section->flags & (SECTION_FLAG_IS_ARRAY|SECTION_FLAG_IS_WRAPPER)))
1006
        printf("[%s]\n", buf->str);
1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031
}

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,
1032
    .priv_class            = &ini_class,
1033 1034
};

1035 1036 1037
/* JSON output */

typedef struct {
1038
    const AVClass *class;
1039
    int indent_level;
1040 1041
    int compact;
    const char *item_sep, *item_start_end;
1042 1043
} JSONContext;

1044 1045 1046 1047
#undef OFFSET
#define OFFSET(x) offsetof(JSONContext, x)

static const AVOption json_options[]= {
1048 1049
    { "compact", "enable compact output", OFFSET(compact), AV_OPT_TYPE_INT, {.i64=0}, 0, 1 },
    { "c",       "enable compact output", OFFSET(compact), AV_OPT_TYPE_INT, {.i64=0}, 0, 1 },
1050 1051 1052
    { NULL }
};

1053
DEFINE_WRITER_CLASS(json);
1054

1055
static av_cold int json_init(WriterContext *wctx)
1056 1057
{
    JSONContext *json = wctx->priv;
1058 1059 1060

    json->item_sep       = json->compact ? ", " : ",\n";
    json->item_start_end = json->compact ? " "  : "\n";
1061 1062 1063 1064

    return 0;
}

1065
static const char *json_escape_str(AVBPrint *dst, const char *src, void *log_ctx)
1066 1067 1068
{
    static const char json_escape[] = {'"', '\\', '\b', '\f', '\n', '\r', '\t', 0};
    static const char json_subst[]  = {'"', '\\',  'b',  'f',  'n',  'r',  't', 0};
1069 1070 1071 1072 1073
    const char *p;

    for (p = src; *p; p++) {
        char *s = strchr(json_escape, *p);
        if (s) {
1074 1075
            av_bprint_chars(dst, '\\', 1);
            av_bprint_chars(dst, json_subst[s - json_escape], 1);
1076
        } else if ((unsigned char)*p < 32) {
1077
            av_bprintf(dst, "\\u00%02x", *p & 0xff);
1078
        } else {
1079
            av_bprint_chars(dst, *p, 1);
1080 1081
        }
    }
1082
    return dst->str;
1083 1084
}

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

1087
static void json_print_section_header(WriterContext *wctx)
1088 1089
{
    JSONContext *json = wctx->priv;
1090
    AVBPrint buf;
1091 1092 1093
    const struct section *section = wctx->section[wctx->level];
    const struct section *parent_section = wctx->level ?
        wctx->section[wctx->level-1] : NULL;
1094

1095 1096 1097 1098 1099 1100 1101
    if (wctx->level && wctx->nb_item[wctx->level-1])
        printf(",\n");

    if (section->flags & SECTION_FLAG_IS_WRAPPER) {
        printf("{\n");
        json->indent_level++;
    } else {
1102
        av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
1103 1104 1105
        json_escape_str(&buf, section->name, wctx);
        JSON_INDENT();

1106
        json->indent_level++;
1107 1108
        if (section->flags & SECTION_FLAG_IS_ARRAY) {
            printf("\"%s\": [\n", buf.str);
1109
        } else if (parent_section && !(parent_section->flags & SECTION_FLAG_IS_ARRAY)) {
1110 1111 1112 1113 1114
            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 */
1115
            if (parent_section && parent_section->id == SECTION_ID_PACKETS_AND_FRAMES) {
1116 1117 1118 1119 1120 1121
                if (!json->compact)
                    JSON_INDENT();
                printf("\"type\": \"%s\"%s", section->name, json->item_sep);
            }
        }
        av_bprint_finalize(&buf, NULL);
1122
    }
1123 1124
}

1125
static void json_print_section_footer(WriterContext *wctx)
1126 1127
{
    JSONContext *json = wctx->priv;
1128
    const struct section *section = wctx->section[wctx->level];
1129

1130 1131 1132 1133
    if (wctx->level == 0) {
        json->indent_level--;
        printf("\n}\n");
    } else if (section->flags & SECTION_FLAG_IS_ARRAY) {
1134
        printf("\n");
1135
        json->indent_level--;
1136 1137
        JSON_INDENT();
        printf("]");
1138 1139 1140
    } else {
        printf("%s", json->item_start_end);
        json->indent_level--;
1141 1142
        if (!json->compact)
            JSON_INDENT();
1143
        printf("}");
1144
    }
1145 1146 1147
}

static inline void json_print_item_str(WriterContext *wctx,
1148
                                       const char *key, const char *value)
1149
{
1150
    AVBPrint buf;
1151

1152 1153
    av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
    printf("\"%s\":", json_escape_str(&buf, key,   wctx));
1154
    av_bprint_clear(&buf);
1155 1156
    printf(" \"%s\"", json_escape_str(&buf, value, wctx));
    av_bprint_finalize(&buf, NULL);
1157 1158 1159 1160
}

static void json_print_str(WriterContext *wctx, const char *key, const char *value)
{
1161 1162
    JSONContext *json = wctx->priv;

1163 1164
    if (wctx->nb_item[wctx->level])
        printf("%s", json->item_sep);
1165 1166
    if (!json->compact)
        JSON_INDENT();
1167
    json_print_item_str(wctx, key, value);
1168 1169
}

1170
static void json_print_int(WriterContext *wctx, const char *key, long long int value)
1171
{
1172
    JSONContext *json = wctx->priv;
1173
    AVBPrint buf;
1174

1175 1176
    if (wctx->nb_item[wctx->level])
        printf("%s", json->item_sep);
1177 1178
    if (!json->compact)
        JSON_INDENT();
1179 1180 1181 1182

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

1185
static const Writer json_writer = {
1186 1187
    .name                 = "json",
    .priv_size            = sizeof(JSONContext),
1188
    .init                 = json_init,
1189 1190 1191 1192
    .print_section_header = json_print_section_header,
    .print_section_footer = json_print_section_footer,
    .print_integer        = json_print_int,
    .print_string         = json_print_str,
1193
    .flags = WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER,
1194
    .priv_class           = &json_class,
1195 1196
};

1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210
/* XML output */

typedef struct {
    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[] = {
1211 1212 1213 1214
    {"fully_qualified", "specify if the output should be fully qualified", OFFSET(fully_qualified), AV_OPT_TYPE_INT, {.i64=0},  0, 1 },
    {"q",               "specify if the output should be fully qualified", OFFSET(fully_qualified), AV_OPT_TYPE_INT, {.i64=0},  0, 1 },
    {"xsd_strict",      "ensure that the output is XSD compliant",         OFFSET(xsd_strict),      AV_OPT_TYPE_INT, {.i64=0},  0, 1 },
    {"x",               "ensure that the output is XSD compliant",         OFFSET(xsd_strict),      AV_OPT_TYPE_INT, {.i64=0},  0, 1 },
1215 1216 1217
    {NULL},
};

1218
DEFINE_WRITER_CLASS(xml);
1219

1220
static av_cold int xml_init(WriterContext *wctx)
1221 1222 1223 1224 1225 1226 1227 1228 1229 1230
{
    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); \
1231
            return AVERROR(EINVAL);                                     \
1232 1233 1234 1235
        }
        CHECK_COMPLIANCE(show_private_data, "private");
        CHECK_COMPLIANCE(show_value_unit,   "unit");
        CHECK_COMPLIANCE(use_value_prefix,  "prefix");
1236 1237 1238 1239 1240 1241 1242

        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);
        }
1243 1244 1245 1246 1247
    }

    return 0;
}

1248
static const char *xml_escape_str(AVBPrint *dst, const char *src, void *log_ctx)
1249 1250 1251
{
    const char *p;

1252
    for (p = src; *p; p++) {
1253
        switch (*p) {
1254 1255 1256
        case '&' : av_bprintf(dst, "%s", "&amp;");  break;
        case '<' : av_bprintf(dst, "%s", "&lt;");   break;
        case '>' : av_bprintf(dst, "%s", "&gt;");   break;
1257
        case '"' : av_bprintf(dst, "%s", "&quot;"); break;
1258 1259
        case '\'': av_bprintf(dst, "%s", "&apos;"); break;
        default: av_bprint_chars(dst, *p, 1);
1260 1261 1262
        }
    }

1263
    return dst->str;
1264 1265
}

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

1268
static void xml_print_section_header(WriterContext *wctx)
1269 1270
{
    XMLContext *xml = wctx->priv;
1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284
    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;
1285 1286
    }

1287 1288 1289
    if (xml->within_tag) {
        xml->within_tag = 0;
        printf(">\n");
1290
    }
1291
    if (section->flags & SECTION_FLAG_HAS_VARIABLE_FIELDS) {
1292 1293
        xml->indent_level++;
    } else {
1294 1295 1296
        if (parent_section && (parent_section->flags & SECTION_FLAG_IS_WRAPPER) &&
            wctx->level && wctx->nb_item[wctx->level-1])
            printf("\n");
1297
        xml->indent_level++;
1298

1299 1300 1301 1302 1303 1304 1305
        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;
        }
    }
1306 1307
}

1308
static void xml_print_section_footer(WriterContext *wctx)
1309 1310
{
    XMLContext *xml = wctx->priv;
1311
    const struct section *section = wctx->section[wctx->level];
1312

1313 1314 1315 1316
    if (wctx->level == 0) {
        printf("</%sffprobe>\n", xml->fully_qualified ? "ffprobe:" : "");
    } else if (xml->within_tag) {
        xml->within_tag = 0;
1317
        printf("/>\n");
1318
        xml->indent_level--;
1319
    } else if (section->flags & SECTION_FLAG_HAS_VARIABLE_FIELDS) {
1320 1321 1322 1323
        xml->indent_level--;
    } else {
        XML_INDENT(); printf("</%s>\n", section->name);
        xml->indent_level--;
1324 1325 1326 1327 1328
    }
}

static void xml_print_str(WriterContext *wctx, const char *key, const char *value)
{
1329
    AVBPrint buf;
1330 1331
    XMLContext *xml = wctx->priv;
    const struct section *section = wctx->section[wctx->level];
1332

1333
    av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
1334

1335
    if (section->flags & SECTION_FLAG_HAS_VARIABLE_FIELDS) {
1336
        XML_INDENT();
1337 1338
        printf("<%s key=\"%s\"",
               section->element_name, xml_escape_str(&buf, key, wctx));
1339 1340 1341 1342 1343 1344 1345 1346
        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));
    }

1347
    av_bprint_finalize(&buf, NULL);
1348 1349 1350 1351
}

static void xml_print_int(WriterContext *wctx, const char *key, long long int value)
{
1352
    if (wctx->nb_item[wctx->level])
1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364
        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,
1365
    .flags = WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER,
1366
    .priv_class           = &xml_class,
1367 1368
};

1369 1370 1371 1372 1373 1374 1375 1376 1377
static void writer_register_all(void)
{
    static int initialized;

    if (initialized)
        return;
    initialized = 1;

    writer_register(&default_writer);
1378
    writer_register(&compact_writer);
1379
    writer_register(&csv_writer);
1380
    writer_register(&flat_writer);
1381
    writer_register(&ini_writer);
1382
    writer_register(&json_writer);
1383
    writer_register(&xml_writer);
1384 1385 1386
}

#define print_fmt(k, f, ...) do {              \
1387 1388 1389
    av_bprint_clear(&pbuf);                    \
    av_bprintf(&pbuf, f, __VA_ARGS__);         \
    writer_print_string(w, k, pbuf.str, 0);    \
1390 1391 1392
} while (0)

#define print_int(k, v)         writer_print_integer(w, k, v)
1393
#define print_q(k, v, s)        writer_print_rational(w, k, v, s)
1394 1395
#define print_str(k, v)         writer_print_string(w, k, v, 0)
#define print_str_opt(k, v)     writer_print_string(w, k, v, 1)
1396 1397 1398 1399
#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)
1400 1401 1402 1403 1404 1405 1406
#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)

1407 1408
#define print_section_header(s) writer_print_section_header(w, s)
#define print_section_footer(s) writer_print_section_footer(w, s)
1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420

static inline void show_tags(WriterContext *wctx, AVDictionary *tags, int section_id)
{
    AVDictionaryEntry *tag = NULL;

    if (!tags)
        return;
    writer_print_section_header(wctx, section_id);
    while ((tag = av_dict_get(tags, "", tag, AV_DICT_IGNORE_SUFFIX)))
        writer_print_string(wctx, tag->key, tag->value, 0);
    writer_print_section_footer(wctx);
}
1421 1422

static void show_packet(WriterContext *w, AVFormatContext *fmt_ctx, AVPacket *pkt, int packet_idx)
1423 1424 1425
{
    char val_str[128];
    AVStream *st = fmt_ctx->streams[pkt->stream_index];
1426
    AVBPrint pbuf;
1427
    const char *s;
1428

1429 1430
    av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);

1431 1432
    writer_print_section_header(w, SECTION_ID_PACKET);

1433 1434 1435
    s = av_get_media_type_string(st->codec->codec_type);
    if (s) print_str    ("codec_type", s);
    else   print_str_opt("codec_type", "unknown");
1436
    print_int("stream_index",     pkt->stream_index);
1437 1438 1439 1440
    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);
1441 1442
    print_duration_ts("duration",        pkt->duration);
    print_duration_time("duration_time", pkt->duration, &st->time_base);
1443 1444
    print_duration_ts("convergence_duration", pkt->convergence_duration);
    print_duration_time("convergence_duration_time", pkt->convergence_duration, &st->time_base);
1445
    print_val("size",             pkt->size, unit_byte_str);
1446 1447
    if (pkt->pos != -1) print_fmt    ("pos", "%"PRId64, pkt->pos);
    else                print_str_opt("pos", "N/A");
1448
    print_fmt("flags", "%c",      pkt->flags & AV_PKT_FLAG_KEY ? 'K' : '_');
1449 1450
    if (do_show_data)
        writer_print_data(w, "data", pkt->data, pkt->size);
1451
    writer_print_section_footer(w);
1452

1453
    av_bprint_finalize(&pbuf, NULL);
1454
    fflush(stdout);
1455 1456
}

1457 1458
static void show_frame(WriterContext *w, AVFrame *frame, AVStream *stream,
                       AVFormatContext *fmt_ctx)
1459
{
1460
    AVBPrint pbuf;
1461 1462
    const char *s;

1463 1464
    av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);

1465
    writer_print_section_header(w, SECTION_ID_FRAME);
1466 1467 1468 1469 1470 1471 1472 1473 1474

    s = av_get_media_type_string(stream->codec->codec_type);
    if (s) print_str    ("media_type", s);
    else   print_str_opt("media_type", "unknown");
    print_int("key_frame",              frame->key_frame);
    print_ts  ("pkt_pts",               frame->pkt_pts);
    print_time("pkt_pts_time",          frame->pkt_pts, &stream->time_base);
    print_ts  ("pkt_dts",               frame->pkt_dts);
    print_time("pkt_dts_time",          frame->pkt_dts, &stream->time_base);
1475 1476
    print_duration_ts  ("pkt_duration",      frame->pkt_duration);
    print_duration_time("pkt_duration_time", frame->pkt_duration, &stream->time_base);
1477 1478
    if (frame->pkt_pos != -1) print_fmt    ("pkt_pos", "%"PRId64, frame->pkt_pos);
    else                      print_str_opt("pkt_pos", "N/A");
1479 1480
    if (frame->pkt_size != -1) print_fmt    ("pkt_size", "%d", av_frame_get_pkt_size(frame));
    else                       print_str_opt("pkt_size", "N/A");
1481 1482

    switch (stream->codec->codec_type) {
1483 1484
        AVRational sar;

1485
    case AVMEDIA_TYPE_VIDEO:
1486 1487 1488 1489 1490
        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");
1491 1492 1493
        sar = av_guess_sample_aspect_ratio(fmt_ctx, stream, frame);
        if (sar.num) {
            print_q("sample_aspect_ratio", sar, ':');
1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504
        } 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);
        print_int("reference",              frame->reference);
        break;
1505 1506 1507 1508 1509 1510

    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);
1511 1512 1513 1514 1515 1516 1517 1518
        print_int("channels", av_frame_get_channels(frame));
        if (av_frame_get_channel_layout(frame)) {
            av_bprint_clear(&pbuf);
            av_bprint_channel_layout(&pbuf, av_frame_get_channels(frame),
                                     av_frame_get_channel_layout(frame));
            print_str    ("channel_layout", pbuf.str);
        } else
            print_str_opt("channel_layout", "unknown");
1519 1520
        break;
    }
1521
    show_tags(w, av_frame_get_metadata(frame), SECTION_ID_FRAME_TAGS);
1522

1523
    writer_print_section_footer(w);
1524

1525
    av_bprint_finalize(&pbuf, NULL);
1526 1527 1528
    fflush(stdout);
}

1529 1530 1531
static av_always_inline int process_frame(WriterContext *w,
                                          AVFormatContext *fmt_ctx,
                                          AVFrame *frame, AVPacket *pkt)
1532 1533
{
    AVCodecContext *dec_ctx = fmt_ctx->streams[pkt->stream_index]->codec;
1534
    int ret = 0, got_frame = 0;
1535

1536
    avcodec_get_frame_defaults(frame);
1537
    if (dec_ctx->codec) {
1538 1539
        switch (dec_ctx->codec_type) {
        case AVMEDIA_TYPE_VIDEO:
1540
            ret = avcodec_decode_video2(dec_ctx, frame, &got_frame, pkt);
1541
            break;
1542

1543
        case AVMEDIA_TYPE_AUDIO:
1544
            ret = avcodec_decode_audio4(dec_ctx, frame, &got_frame, pkt);
1545 1546
            break;
        }
1547
    }
1548

1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559
    if (ret < 0)
        return ret;
    ret = FFMIN(ret, pkt->size); /* guard against bogus return values */
    pkt->data += ret;
    pkt->size -= ret;
    if (got_frame) {
        nb_streams_frames[pkt->stream_index]++;
        if (do_show_frames)
            show_frame(w, frame, fmt_ctx->streams[pkt->stream_index], fmt_ctx);
    }
    return got_frame;
1560 1561
}

1562
static void read_packets(WriterContext *w, AVFormatContext *fmt_ctx)
1563
{
1564
    AVPacket pkt, pkt1;
1565
    AVFrame frame;
1566
    int i = 0;
1567 1568 1569

    av_init_packet(&pkt);

1570
    while (!av_read_frame(fmt_ctx, &pkt)) {
1571
        if (selected_streams[pkt.stream_index]) {
1572 1573 1574 1575 1576 1577 1578 1579 1580
            if (do_read_packets) {
                if (do_show_packets)
                    show_packet(w, fmt_ctx, &pkt, i++);
                nb_streams_packets[pkt.stream_index]++;
            }
            if (do_read_frames) {
                pkt1 = pkt;
                while (pkt1.size && process_frame(w, fmt_ctx, &frame, &pkt1) > 0);
            }
1581
        }
1582
        av_free_packet(&pkt);
1583 1584 1585 1586 1587 1588 1589
    }
    av_init_packet(&pkt);
    pkt.data = NULL;
    pkt.size = 0;
    //Flush remaining frames that are cached in the decoder
    for (i = 0; i < fmt_ctx->nb_streams; i++) {
        pkt.stream_index = i;
1590 1591
        if (do_read_frames)
            while (process_frame(w, fmt_ctx, &frame, &pkt) > 0);
1592
    }
1593 1594
}

1595
static void show_stream(WriterContext *w, AVFormatContext *fmt_ctx, int stream_idx)
Stefano Sabatini's avatar
Stefano Sabatini committed
1596 1597 1598
{
    AVStream *stream = fmt_ctx->streams[stream_idx];
    AVCodecContext *dec_ctx;
1599
    const AVCodec *dec;
Stefano Sabatini's avatar
Stefano Sabatini committed
1600
    char val_str[128];
1601
    const char *s;
1602
    AVRational sar, dar;
1603 1604 1605
    AVBPrint pbuf;

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

1607
    writer_print_section_header(w, SECTION_ID_STREAM);
Stefano Sabatini's avatar
Stefano Sabatini committed
1608

1609
    print_int("index", stream->index);
Stefano Sabatini's avatar
Stefano Sabatini committed
1610 1611

    if ((dec_ctx = stream->codec)) {
1612
        const char *profile = NULL;
1613 1614 1615 1616
        dec = dec_ctx->codec;
        if (dec) {
            print_str("codec_name", dec->name);
            if (!do_bitexact) {
1617 1618
                if (dec->long_name) print_str    ("codec_long_name", dec->long_name);
                else                print_str_opt("codec_long_name", "unknown");
1619
            }
Stefano Sabatini's avatar
Stefano Sabatini committed
1620
        } else {
1621
            print_str_opt("codec_name", "unknown");
1622
            if (!do_bitexact) {
1623
                print_str_opt("codec_long_name", "unknown");
1624
            }
Stefano Sabatini's avatar
Stefano Sabatini committed
1625 1626
        }

1627 1628 1629 1630 1631
        if (dec && (profile = av_get_profile_name(dec, dec_ctx->profile)))
            print_str("profile", profile);
        else
            print_str_opt("profile", "unknown");

1632 1633 1634
        s = av_get_media_type_string(dec_ctx->codec_type);
        if (s) print_str    ("codec_type", s);
        else   print_str_opt("codec_type", "unknown");
1635
        print_q("codec_time_base", dec_ctx->time_base, '/');
Stefano Sabatini's avatar
Stefano Sabatini committed
1636 1637

        /* print AVI/FourCC tag */
1638
        av_get_codec_tag_string(val_str, sizeof(val_str), dec_ctx->codec_tag);
1639 1640
        print_str("codec_tag_string",    val_str);
        print_fmt("codec_tag", "0x%04x", dec_ctx->codec_tag);
Stefano Sabatini's avatar
Stefano Sabatini committed
1641 1642

        switch (dec_ctx->codec_type) {
1643
        case AVMEDIA_TYPE_VIDEO:
1644 1645 1646
            print_int("width",        dec_ctx->width);
            print_int("height",       dec_ctx->height);
            print_int("has_b_frames", dec_ctx->has_b_frames);
1647 1648 1649 1650 1651 1652
            sar = av_guess_sample_aspect_ratio(fmt_ctx, stream, NULL);
            if (sar.den) {
                print_q("sample_aspect_ratio", sar, ':');
                av_reduce(&dar.num, &dar.den,
                          dec_ctx->width  * sar.num,
                          dec_ctx->height * sar.den,
1653
                          1024*1024);
1654
                print_q("display_aspect_ratio", dar, ':');
1655 1656 1657
            } else {
                print_str_opt("sample_aspect_ratio", "N/A");
                print_str_opt("display_aspect_ratio", "N/A");
1658
            }
1659 1660 1661
            s = av_get_pix_fmt_name(dec_ctx->pix_fmt);
            if (s) print_str    ("pix_fmt", s);
            else   print_str_opt("pix_fmt", "unknown");
1662
            print_int("level",   dec_ctx->level);
1663
            if (dec_ctx->timecode_frame_start >= 0) {
1664 1665 1666
                char tcbuf[AV_TIMECODE_STR_SIZE];
                av_timecode_make_mpeg_tc_string(tcbuf, dec_ctx->timecode_frame_start);
                print_str("timecode", tcbuf);
1667 1668 1669
            } else {
                print_str_opt("timecode", "N/A");
            }
Stefano Sabatini's avatar
Stefano Sabatini committed
1670 1671
            break;

1672
        case AVMEDIA_TYPE_AUDIO:
1673 1674 1675
            s = av_get_sample_fmt_name(dec_ctx->sample_fmt);
            if (s) print_str    ("sample_fmt", s);
            else   print_str_opt("sample_fmt", "unknown");
1676
            print_val("sample_rate",     dec_ctx->sample_rate, unit_hertz_str);
1677 1678
            print_int("channels",        dec_ctx->channels);
            print_int("bits_per_sample", av_get_bits_per_sample(dec_ctx->codec_id));
Stefano Sabatini's avatar
Stefano Sabatini committed
1679 1680 1681
            break;
        }
    } else {
1682
        print_str_opt("codec_type", "unknown");
Stefano Sabatini's avatar
Stefano Sabatini committed
1683
    }
1684
    if (dec_ctx->codec && dec_ctx->codec->priv_class && show_private_data) {
1685
        const AVOption *opt = NULL;
1686 1687 1688 1689 1690 1691 1692 1693 1694
        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
1695

1696 1697
    if (fmt_ctx->iformat->flags & AVFMT_SHOW_IDS) print_fmt    ("id", "0x%x", stream->id);
    else                                          print_str_opt("id", "N/A");
1698 1699 1700
    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,      '/');
1701 1702 1703 1704
    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);
1705 1706
    if (dec_ctx->bit_rate > 0) print_val    ("bit_rate", dec_ctx->bit_rate, unit_bit_per_second_str);
    else                       print_str_opt("bit_rate", "N/A");
1707 1708
    if (stream->nb_frames) print_fmt    ("nb_frames", "%"PRId64, stream->nb_frames);
    else                   print_str_opt("nb_frames", "N/A");
1709 1710 1711 1712
    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");
1713 1714 1715
    if (do_show_data)
        writer_print_data(w, "extradata", dec_ctx->extradata,
                                          dec_ctx->extradata_size);
1716 1717 1718 1719 1720 1721

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

1722
    if (do_show_stream_disposition) {
1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735
    writer_print_section_header(w, SECTION_ID_STREAM_DISPOSITION);
    PRINT_DISPOSITION(DEFAULT,          "default");
    PRINT_DISPOSITION(DUB,              "dub");
    PRINT_DISPOSITION(ORIGINAL,         "original");
    PRINT_DISPOSITION(COMMENT,          "comment");
    PRINT_DISPOSITION(LYRICS,           "lyrics");
    PRINT_DISPOSITION(KARAOKE,          "karaoke");
    PRINT_DISPOSITION(FORCED,           "forced");
    PRINT_DISPOSITION(HEARING_IMPAIRED, "hearing_impaired");
    PRINT_DISPOSITION(VISUAL_IMPAIRED,  "visual_impaired");
    PRINT_DISPOSITION(CLEAN_EFFECTS,    "clean_effects");
    PRINT_DISPOSITION(ATTACHED_PIC,     "attached_pic");
    writer_print_section_footer(w);
1736
    }
1737

1738
    show_tags(w, stream->metadata, SECTION_ID_STREAM_TAGS);
Stefano Sabatini's avatar
Stefano Sabatini committed
1739

1740
    writer_print_section_footer(w);
1741
    av_bprint_finalize(&pbuf, NULL);
1742
    fflush(stdout);
Stefano Sabatini's avatar
Stefano Sabatini committed
1743 1744
}

1745
static void show_streams(WriterContext *w, AVFormatContext *fmt_ctx)
1746 1747
{
    int i;
1748
    writer_print_section_header(w, SECTION_ID_STREAMS);
1749
    for (i = 0; i < fmt_ctx->nb_streams; i++)
1750 1751
        if (selected_streams[i])
            show_stream(w, fmt_ctx, i);
1752
    writer_print_section_footer(w);
1753 1754
}

1755
static void show_format(WriterContext *w, AVFormatContext *fmt_ctx)
Stefano Sabatini's avatar
Stefano Sabatini committed
1756 1757
{
    char val_str[128];
1758
    int64_t size = fmt_ctx->pb ? avio_size(fmt_ctx->pb) : -1;
Stefano Sabatini's avatar
Stefano Sabatini committed
1759

1760
    writer_print_section_header(w, SECTION_ID_FORMAT);
1761
    print_str("filename",         fmt_ctx->filename);
1762 1763
    print_int("nb_streams",       fmt_ctx->nb_streams);
    print_str("format_name",      fmt_ctx->iformat->name);
1764
    if (!do_bitexact) {
1765 1766
        if (fmt_ctx->iformat->long_name) print_str    ("format_long_name", fmt_ctx->iformat->long_name);
        else                             print_str_opt("format_long_name", "unknown");
1767
    }
1768 1769
    print_time("start_time",      fmt_ctx->start_time, &AV_TIME_BASE_Q);
    print_time("duration",        fmt_ctx->duration,   &AV_TIME_BASE_Q);
1770 1771 1772 1773
    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");
1774 1775 1776
    show_tags(w, fmt_ctx->metadata, SECTION_ID_FORMAT_TAGS);

    writer_print_section_footer(w);
1777
    fflush(stdout);
Stefano Sabatini's avatar
Stefano Sabatini committed
1778 1779
}

1780 1781 1782 1783 1784 1785 1786 1787
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));

1788
    writer_print_section_header(w, SECTION_ID_ERROR);
1789 1790
    print_int("code", err);
    print_str("string", errbuf_ptr);
1791
    writer_print_section_footer(w);
1792 1793
}

Stefano Sabatini's avatar
Stefano Sabatini committed
1794 1795 1796
static int open_input_file(AVFormatContext **fmt_ctx_ptr, const char *filename)
{
    int err, i;
1797 1798
    AVFormatContext *fmt_ctx = NULL;
    AVDictionaryEntry *t;
Stefano Sabatini's avatar
Stefano Sabatini committed
1799

1800 1801
    if ((err = avformat_open_input(&fmt_ctx, filename,
                                   iformat, &format_opts)) < 0) {
Stefano Sabatini's avatar
Stefano Sabatini committed
1802 1803 1804
        print_error(filename, err);
        return err;
    }
1805 1806 1807 1808 1809
    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;
    }

Stefano Sabatini's avatar
Stefano Sabatini committed
1810 1811

    /* fill the streams in the format context */
1812
    if ((err = avformat_find_stream_info(fmt_ctx, NULL)) < 0) {
Stefano Sabatini's avatar
Stefano Sabatini committed
1813 1814 1815 1816
        print_error(filename, err);
        return err;
    }

1817
    av_dump_format(fmt_ctx, 0, filename, 0);
Stefano Sabatini's avatar
Stefano Sabatini committed
1818 1819 1820 1821 1822 1823

    /* bind a decoder to each input stream */
    for (i = 0; i < fmt_ctx->nb_streams; i++) {
        AVStream *stream = fmt_ctx->streams[i];
        AVCodec *codec;

1824
        if (stream->codec->codec_id == AV_CODEC_ID_PROBE) {
1825 1826 1827 1828
            av_log(NULL, AV_LOG_ERROR,
                   "Failed to probe codec for input stream %d\n",
                    stream->index);
        } else if (!(codec = avcodec_find_decoder(stream->codec->codec_id))) {
1829 1830 1831
            av_log(NULL, AV_LOG_ERROR,
                    "Unsupported codec with id %d for input stream %d\n",
                    stream->codec->codec_id, stream->index);
1832
        } else if (avcodec_open2(stream->codec, codec, NULL) < 0) {
1833 1834
            av_log(NULL, AV_LOG_ERROR, "Error while opening codec for input stream %d\n",
                   stream->index);
Stefano Sabatini's avatar
Stefano Sabatini committed
1835 1836 1837 1838 1839 1840 1841
        }
    }

    *fmt_ctx_ptr = fmt_ctx;
    return 0;
}

1842 1843 1844 1845 1846 1847 1848
static void close_input_file(AVFormatContext **ctx_ptr)
{
    int i;
    AVFormatContext *fmt_ctx = *ctx_ptr;

    /* close decoder for each stream */
    for (i = 0; i < fmt_ctx->nb_streams; i++)
1849
        if (fmt_ctx->streams[i]->codec->codec_id != AV_CODEC_ID_NONE)
1850 1851 1852 1853 1854
            avcodec_close(fmt_ctx->streams[i]->codec);

    avformat_close_input(ctx_ptr);
}

1855
static int probe_file(WriterContext *wctx, const char *filename)
Stefano Sabatini's avatar
Stefano Sabatini committed
1856 1857
{
    AVFormatContext *fmt_ctx;
1858
    int ret, i;
1859
    int section_id;
Stefano Sabatini's avatar
Stefano Sabatini committed
1860

1861 1862 1863
    do_read_frames = do_show_frames || do_count_frames;
    do_read_packets = do_show_packets || do_count_packets;

1864 1865
    ret = open_input_file(&fmt_ctx, filename);
    if (ret >= 0) {
1866 1867
        nb_streams_frames  = av_calloc(fmt_ctx->nb_streams, sizeof(*nb_streams_frames));
        nb_streams_packets = av_calloc(fmt_ctx->nb_streams, sizeof(*nb_streams_packets));
1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883
        selected_streams   = av_calloc(fmt_ctx->nb_streams, sizeof(*selected_streams));

        for (i = 0; i < fmt_ctx->nb_streams; i++) {
            if (stream_specifier) {
                ret = avformat_match_stream_specifier(fmt_ctx,
                                                      fmt_ctx->streams[i],
                                                      stream_specifier);
                if (ret < 0)
                    goto end;
                else
                    selected_streams[i] = ret;
            } else {
                selected_streams[i] = 1;
            }
        }

1884
        if (do_read_frames || do_read_packets) {
1885 1886
            if (do_show_frames && do_show_packets &&
                wctx->writer->flags & WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER)
1887
                section_id = SECTION_ID_PACKETS_AND_FRAMES;
1888
            else if (do_show_packets && !do_show_frames)
1889
                section_id = SECTION_ID_PACKETS;
1890
            else // (!do_show_packets && do_show_frames)
1891
                section_id = SECTION_ID_FRAMES;
1892
            if (do_show_frames || do_show_packets)
1893
                writer_print_section_header(wctx, section_id);
1894 1895
            read_packets(wctx, fmt_ctx);
            if (do_show_frames || do_show_packets)
1896
                writer_print_section_footer(wctx);
1897
        }
1898 1899 1900 1901 1902
        if (do_show_streams)
            show_streams(wctx, fmt_ctx);
        if (do_show_format)
            show_format(wctx, fmt_ctx);

1903
    end:
1904
        close_input_file(&fmt_ctx);
1905 1906
        av_freep(&nb_streams_frames);
        av_freep(&nb_streams_packets);
1907
        av_freep(&selected_streams);
1908
    }
1909
    return ret;
Stefano Sabatini's avatar
Stefano Sabatini committed
1910 1911 1912 1913
}

static void show_usage(void)
{
1914 1915 1916
    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
1917 1918
}

1919 1920
static void ffprobe_show_program_version(WriterContext *w)
{
1921 1922
    AVBPrint pbuf;
    av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
1923

1924
    writer_print_section_header(w, SECTION_ID_PROGRAM_VERSION);
1925 1926 1927 1928 1929
    print_str("version", FFMPEG_VERSION);
    print_fmt("copyright", "Copyright (c) %d-%d the FFmpeg developers",
              program_birth_year, this_year);
    print_str("build_date", __DATE__);
    print_str("build_time", __TIME__);
1930
    print_str("compiler_ident", CC_IDENT);
1931
    print_str("configuration", FFMPEG_CONFIGURATION);
1932
    writer_print_section_footer(w);
1933

1934
    av_bprint_finalize(&pbuf, NULL);
1935 1936 1937 1938 1939 1940
}

#define SHOW_LIB_VERSION(libname, LIBNAME)                              \
    do {                                                                \
        if (CONFIG_##LIBNAME) {                                         \
            unsigned int version = libname##_version();                 \
1941
            writer_print_section_header(w, SECTION_ID_LIBRARY_VERSION); \
1942 1943 1944 1945 1946
            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);                              \
1947
            print_str("ident",   LIB##LIBNAME##_IDENT);                 \
1948
            writer_print_section_footer(w);                             \
1949 1950 1951 1952 1953
        }                                                               \
    } while (0)

static void ffprobe_show_library_versions(WriterContext *w)
{
1954
    writer_print_section_header(w, SECTION_ID_LIBRARY_VERSIONS);
1955 1956 1957 1958 1959 1960 1961 1962
    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);
1963
    writer_print_section_footer(w);
1964 1965
}

1966
static int opt_format(void *optctx, const char *opt, const char *arg)
1967 1968 1969
{
    iformat = av_find_input_format(arg);
    if (!iformat) {
1970
        av_log(NULL, AV_LOG_ERROR, "Unknown input format: %s\n", arg);
1971
        return AVERROR(EINVAL);
1972
    }
1973
    return 0;
1974 1975
}

1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 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 2039 2040 2041 2042 2043 2044 2045 2046 2047
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);
        }
2048
        av_dict_free(&entries);
2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059
        av_free(section_name);

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

    return ret;
}

2060
static int opt_show_format_entry(void *optctx, const char *opt, const char *arg)
2061
{
2062 2063 2064 2065 2066 2067 2068 2069 2070
    char *buf = av_asprintf("format=%s", arg);
    int ret;

    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;
2071 2072
}

2073
static void opt_input_file(void *optctx, const char *arg)
Stefano Sabatini's avatar
Stefano Sabatini committed
2074
{
2075
    if (input_filename) {
2076 2077 2078
        av_log(NULL, AV_LOG_ERROR,
                "Argument '%s' provided as input filename, but '%s' was already specified.\n",
                arg, input_filename);
2079 2080
        exit(1);
    }
2081 2082 2083
    if (!strcmp(arg, "-"))
        arg = "pipe:";
    input_filename = arg;
Stefano Sabatini's avatar
Stefano Sabatini committed
2084 2085
}

2086 2087 2088 2089 2090 2091
static int opt_input_file_i(void *optctx, const char *opt, const char *arg)
{
    opt_input_file(optctx, arg);
    return 0;
}

2092
void show_help_default(const char *opt, const char *arg)
Stefano Sabatini's avatar
Stefano Sabatini committed
2093
{
2094
    av_log_set_callback(log_callback_help);
Stefano Sabatini's avatar
Stefano Sabatini committed
2095
    show_usage();
2096
    show_help_options(options, "Main options:", 0, 0, 0);
Stefano Sabatini's avatar
Stefano Sabatini committed
2097
    printf("\n");
2098 2099

    show_help_children(avformat_get_class(), AV_OPT_FLAG_DECODING_PARAM);
Stefano Sabatini's avatar
Stefano Sabatini committed
2100 2101
}

2102
static int opt_pretty(void *optctx, const char *opt, const char *arg)
Stefano Sabatini's avatar
Stefano Sabatini committed
2103 2104 2105 2106 2107
{
    show_value_unit              = 1;
    use_value_prefix             = 1;
    use_byte_value_binary_prefix = 1;
    use_value_sexagesimal_format = 1;
2108
    return 0;
Stefano Sabatini's avatar
Stefano Sabatini committed
2109 2110
}

2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139
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;
}

2140 2141
static int opt_show_versions(const char *opt, const char *arg)
{
2142 2143
    mark_section_show_entries(SECTION_ID_PROGRAM_VERSION, 1, NULL);
    mark_section_show_entries(SECTION_ID_LIBRARY_VERSION, 1, NULL);
2144 2145 2146
    return 0;
}

2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161
#define DEFINE_OPT_SHOW_SECTION(section, target_section_id)             \
    static int opt_show_##section(const char *opt, const char *arg)     \
    {                                                                   \
        mark_section_show_entries(SECTION_ID_##target_section_id, 1, NULL); \
        return 0;                                                       \
    }

DEFINE_OPT_SHOW_SECTION(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(program_version,  PROGRAM_VERSION);
DEFINE_OPT_SHOW_SECTION(streams,          STREAMS);

2162
static const OptionDef real_options[] = {
Stefano Sabatini's avatar
Stefano Sabatini committed
2163
#include "cmdutils_common_opts.h"
2164 2165 2166 2167
    { "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
2168
      "use binary prefixes for byte units" },
2169
    { "sexagesimal", OPT_BOOL,  {&use_value_sexagesimal_format},
Stefano Sabatini's avatar
Stefano Sabatini committed
2170
      "use sexagesimal format HOURS:MM:SS.MICROSECONDS for time units" },
2171
    { "pretty", 0, {.func_arg = opt_pretty},
Stefano Sabatini's avatar
Stefano Sabatini committed
2172
      "prettify the format of displayed values, make it more human readable" },
2173
    { "print_format", OPT_STRING | HAS_ARG, {(void*)&print_format},
2174
      "set the output printing format (available formats are: default, compact, csv, flat, ini, json, xml)", "format" },
2175
    { "of", OPT_STRING | HAS_ARG, {(void*)&print_format}, "alias for -print_format", "format" },
2176
    { "select_streams", OPT_STRING | HAS_ARG, {(void*)&stream_specifier}, "select the specified streams", "stream_specifier" },
2177
    { "sections", OPT_EXIT, {.func_arg = opt_sections}, "print sections structure and section information, and exit" },
2178
    { "show_data",    OPT_BOOL, {(void*)&do_show_data}, "show packets data" },
2179 2180 2181
    { "show_error",   0, {(void*)&opt_show_error},  "show probing error" },
    { "show_format",  0, {(void*)&opt_show_format}, "show format/container info" },
    { "show_frames",  0, {(void*)&opt_show_frames}, "show frames info" },
2182
    { "show_format_entry", HAS_ARG, {.func_arg = opt_show_format_entry},
2183
      "show a particular entry from the format/container info", "entry" },
2184 2185 2186 2187
    { "show_entries", HAS_ARG, {.func_arg = opt_show_entries},
      "show a set of specified entries", "entry_list" },
    { "show_packets", 0, {(void*)&opt_show_packets}, "show packets info" },
    { "show_streams", 0, {(void*)&opt_show_streams}, "show streams info" },
2188 2189
    { "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" },
2190 2191
    { "show_program_version",  0, {(void*)&opt_show_program_version},  "show ffprobe version" },
    { "show_library_versions", 0, {(void*)&opt_show_library_versions}, "show library versions" },
2192
    { "show_versions",         0, {(void*)&opt_show_versions}, "show program and library versions" },
2193 2194
    { "show_private_data", OPT_BOOL, {(void*)&show_private_data}, "show private data" },
    { "private",           OPT_BOOL, {(void*)&show_private_data}, "same as show_private_data" },
2195
    { "bitexact", OPT_BOOL, {&do_bitexact}, "force bitexact output" },
2196
    { "default", HAS_ARG | OPT_AUDIO | OPT_VIDEO | OPT_EXPERT, {.func_arg = opt_default}, "generic catch all option", "" },
2197
    { "i", HAS_ARG, {.func_arg = opt_input_file_i}, "read specified file", "input_file"},
Stefano Sabatini's avatar
Stefano Sabatini committed
2198 2199 2200
    { NULL, },
};

2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217
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
2218 2219
int main(int argc, char **argv)
{
2220 2221 2222 2223
    const Writer *w;
    WriterContext *wctx;
    char *buf;
    char *w_name = NULL, *w_args = NULL;
2224
    int ret, i;
2225

2226
    av_log_set_flags(AV_LOG_SKIP_REPEATED);
2227 2228
    atexit(exit_program);

2229
    options = real_options;
2230
    parse_loglevel(argc, argv, options);
Stefano Sabatini's avatar
Stefano Sabatini committed
2231
    av_register_all();
2232
    avformat_network_init();
2233
    init_opts();
2234 2235 2236
#if CONFIG_AVDEVICE
    avdevice_register_all();
#endif
Stefano Sabatini's avatar
Stefano Sabatini committed
2237

2238
    show_banner(argc, argv, options);
2239
    parse_options(NULL, argc, argv, options, opt_input_file);
Stefano Sabatini's avatar
Stefano Sabatini committed
2240

2241 2242 2243 2244 2245 2246 2247 2248 2249 2250
    /* mark things to show, based on -show_entries */
    SET_DO_SHOW(ERROR, error);
    SET_DO_SHOW(FORMAT, format);
    SET_DO_SHOW(FRAMES, frames);
    SET_DO_SHOW(LIBRARY_VERSIONS, library_versions);
    SET_DO_SHOW(PACKETS, packets);
    SET_DO_SHOW(PROGRAM_VERSION, program_version);
    SET_DO_SHOW(STREAMS, streams);
    SET_DO_SHOW(STREAM_DISPOSITION, stream_disposition);

2251 2252 2253 2254 2255 2256 2257 2258
    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;
    }

2259 2260 2261 2262
    writer_register_all();

    if (!print_format)
        print_format = av_strdup("default");
2263 2264 2265 2266
    if (!print_format) {
        ret = AVERROR(ENOMEM);
        goto end;
    }
2267 2268 2269 2270 2271 2272 2273 2274
    w_name = av_strtok(print_format, "=", &buf);
    w_args = buf;

    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
2275 2276
    }

2277 2278 2279
    if ((ret = writer_open(&wctx, w, w_args,
                           sections, FF_ARRAY_ELEMS(sections))) >= 0) {
        writer_print_section_header(wctx, SECTION_ID_ROOT);
2280

2281 2282 2283 2284 2285 2286 2287 2288
        if (do_show_program_version)
            ffprobe_show_program_version(wctx);
        if (do_show_library_versions)
            ffprobe_show_library_versions(wctx);

        if (!input_filename &&
            ((do_show_format || do_show_streams || do_show_packets || do_show_error) ||
             (!do_show_program_version && !do_show_library_versions))) {
2289 2290 2291 2292
            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);
2293
        } else if (input_filename) {
2294
            ret = probe_file(wctx, input_filename);
2295 2296 2297
            if (ret < 0 && do_show_error)
                show_error(wctx, ret);
        }
2298

2299
        writer_print_section_footer(wctx);
2300 2301
        writer_close(&wctx);
    }
2302

2303 2304
end:
    av_freep(&print_format);
2305 2306

    uninit_opts();
2307 2308
    for (i = 0; i < FF_ARRAY_ELEMS(sections); i++)
        av_dict_free(&(sections[i].entries_to_show));
2309

2310 2311
    avformat_network_deinit();

2312
    return ret;
Stefano Sabatini's avatar
Stefano Sabatini committed
2313
}