ffprobe.c 74.6 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 AVDictionary *fmt_entries_to_show = NULL;
59
static int do_show_packets = 0;
Stefano Sabatini's avatar
Stefano Sabatini committed
60
static int do_show_streams = 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 72
static char *print_format;

73 74 75 76 77 78 79 80 81
/* section structure definition */

struct section {
    int id;             ///< unique id indentifying a section
    const char *name;

#define SECTION_FLAG_IS_WRAPPER      1 ///< the section only contains other sections, but has no data at its own level
#define SECTION_FLAG_IS_ARRAY        2 ///< the section contains an array of elements of the same type
    int flags;
82
    const char *element_name; ///< name of the contained element, if provided
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
};

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,
    SECTION_ID_STREAMS,
    SECTION_ID_STREAM_TAGS
} SectionID;

static const struct section sections[] = {
106 107
    [SECTION_ID_ERROR] =              { SECTION_ID_ERROR,              "error" },
    [SECTION_ID_FORMAT] =             { SECTION_ID_FORMAT,             "format" },
108
    [SECTION_ID_FORMAT_TAGS] =        { SECTION_ID_FORMAT_TAGS,        "tags", .element_name = "tag" },
109 110
    [SECTION_ID_FRAME] =              { SECTION_ID_FRAME,              "frame" },
    [SECTION_ID_FRAMES] =             { SECTION_ID_FRAMES,             "frames", SECTION_FLAG_IS_ARRAY },
111
    [SECTION_ID_FRAME_TAGS] =         { SECTION_ID_FRAME_TAGS,         "tags", .element_name = "tag" },
112 113 114 115 116 117 118 119 120
    [SECTION_ID_LIBRARY_VERSION] =    { SECTION_ID_LIBRARY_VERSION,    "library_version" },
    [SECTION_ID_LIBRARY_VERSIONS] =   { SECTION_ID_LIBRARY_VERSIONS,   "library_versions", SECTION_FLAG_IS_ARRAY },
    [SECTION_ID_PACKET] =             { SECTION_ID_PACKET,             "packet" },
    [SECTION_ID_PACKETS] =            { SECTION_ID_PACKETS,            "packets", SECTION_FLAG_IS_ARRAY },
    [SECTION_ID_PACKETS_AND_FRAMES] = { SECTION_ID_PACKETS_AND_FRAMES, "packets_and_frames", SECTION_FLAG_IS_ARRAY },
    [SECTION_ID_PROGRAM_VERSION] =    { SECTION_ID_PROGRAM_VERSION,    "program_version" },
    [SECTION_ID_ROOT] =               { SECTION_ID_ROOT,               "root", SECTION_FLAG_IS_WRAPPER },
    [SECTION_ID_STREAM] =             { SECTION_ID_STREAM,             "stream" },
    [SECTION_ID_STREAMS] =            { SECTION_ID_STREAMS,            "streams", SECTION_FLAG_IS_ARRAY },
121
    [SECTION_ID_STREAM_TAGS] =        { SECTION_ID_STREAM_TAGS,        "tags", .element_name = "tag" },
122 123
};

124
static const OptionDef *options;
Stefano Sabatini's avatar
Stefano Sabatini committed
125 126 127

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

130 131
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
132

133 134 135 136
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";
137 138
static uint64_t *nb_streams_packets;
static uint64_t *nb_streams_frames;
Stefano Sabatini's avatar
Stefano Sabatini committed
139

140
void av_noreturn exit_program(int ret)
141
{
142
    av_dict_free(&fmt_entries_to_show);
143 144 145
    exit(ret);
}

146
struct unit_value {
147
    union { double d; long long int i; } val;
148 149 150 151
    const char *unit;
};

static char *value_string(char *buf, int buf_size, struct unit_value uv)
Stefano Sabatini's avatar
Stefano Sabatini committed
152
{
153
    double vald;
154
    long long int vali;
155 156 157 158 159 160
    int show_float = 0;

    if (uv.unit == unit_second_str) {
        vald = uv.val.d;
        show_float = 1;
    } else {
161
        vald = vali = uv.val.i;
162 163 164
    }

    if (uv.unit == unit_second_str && use_value_sexagesimal_format) {
Stefano Sabatini's avatar
Stefano Sabatini committed
165 166
        double secs;
        int hours, mins;
167
        secs  = vald;
Stefano Sabatini's avatar
Stefano Sabatini committed
168 169 170 171 172
        mins  = (int)secs / 60;
        secs  = secs - mins * 60;
        hours = mins / 60;
        mins %= 60;
        snprintf(buf, buf_size, "%d:%02d:%09.6f", hours, mins, secs);
173 174
    } else {
        const char *prefix_string = "";
Stefano Sabatini's avatar
Stefano Sabatini committed
175

176
        if (use_value_prefix && vald > 1) {
177 178 179
            long long int index;

            if (uv.unit == unit_byte_str && use_byte_value_binary_prefix) {
180
                index = (long long int) (log2(vald)) / 10;
181
                index = av_clip(index, 0, FF_ARRAY_ELEMS(binary_unit_prefixes) - 1);
182
                vald /= exp2(index * 10);
183 184 185 186 187 188 189
                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];
            }
190
        }
Stefano Sabatini's avatar
Stefano Sabatini committed
191

192
        if (show_float || (use_value_prefix && vald != (long long int)vald))
193
            snprintf(buf, buf_size, "%f", vald);
194
        else
195
            snprintf(buf, buf_size, "%lld", vali);
196
        av_strlcatf(buf, buf_size, "%s%s%s", *prefix_string || show_value_unit ? " " : "",
197
                 prefix_string, show_value_unit ? uv.unit : "");
Stefano Sabatini's avatar
Stefano Sabatini committed
198 199 200 201 202
    }

    return buf;
}

203
/* WRITERS API */
204

205
typedef struct WriterContext WriterContext;
206

207
#define WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS 1
208
#define WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER 2
209

210
typedef struct Writer {
211
    const AVClass *priv_class;      ///< private class of the writer, if any
212 213
    int priv_size;                  ///< private size for the writer context
    const char *name;
214

215
    int  (*init)  (WriterContext *wctx);
216 217
    void (*uninit)(WriterContext *wctx);

218 219
    void (*print_section_header)(WriterContext *wctx);
    void (*print_section_footer)(WriterContext *wctx);
220
    void (*print_integer)       (WriterContext *wctx, const char *, long long int);
221
    void (*print_rational)      (WriterContext *wctx, AVRational *q, char *sep);
222
    void (*print_string)        (WriterContext *wctx, const char *, const char *);
223
    int flags;                  ///< a combination or WRITER_FLAG_*
224 225
} Writer;

226 227
#define SECTION_MAX_NB_LEVELS 10

228 229 230 231 232
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
233 234 235 236 237 238 239 240 241 242 243 244

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

245 246 247
    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
248
};
249

250 251 252 253 254 255 256 257 258 259 260 261 262
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,
};

263
static void writer_close(WriterContext **wctx)
264
{
265 266
    if (!*wctx)
        return;
267

268 269
    if ((*wctx)->writer->uninit)
        (*wctx)->writer->uninit(*wctx);
270 271
    if ((*wctx)->writer->priv_class)
        av_opt_free((*wctx)->priv);
272 273
    av_freep(&((*wctx)->priv));
    av_freep(wctx);
274 275
}

276 277
static int writer_open(WriterContext **wctx, const Writer *writer, const char *args,
                       const struct section *sections, int nb_sections)
278
{
279
    int ret = 0;
280

281 282 283
    if (!(*wctx = av_malloc(sizeof(WriterContext)))) {
        ret = AVERROR(ENOMEM);
        goto fail;
284 285
    }

286 287 288
    if (!((*wctx)->priv = av_mallocz(writer->priv_size))) {
        ret = AVERROR(ENOMEM);
        goto fail;
289
    }
290

291
    (*wctx)->class = &writer_class;
292
    (*wctx)->writer = writer;
293 294 295
    (*wctx)->level = -1;
    (*wctx)->sections = sections;
    (*wctx)->nb_sections = nb_sections;
296 297 298 299 300 301 302 303 304 305

    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;
    }
306
    if ((*wctx)->writer->init)
307
        ret = (*wctx)->writer->init(*wctx);
308 309 310 311 312 313 314
    if (ret < 0)
        goto fail;

    return 0;

fail:
    writer_close(wctx);
315 316 317
    return ret;
}

318
static inline void writer_print_section_header(WriterContext *wctx,
319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
                                               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;
    }

338
    if (wctx->writer->print_section_header)
339
        wctx->writer->print_section_header(wctx);
340 341
}

342
static inline void writer_print_section_footer(WriterContext *wctx)
343
{
344 345 346 347 348 349 350 351 352
    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++;
353
    }
354 355 356
    if (wctx->writer->print_section_footer)
        wctx->writer->print_section_footer(wctx);
    wctx->level--;
357
}
358

359
static inline void writer_print_integer(WriterContext *wctx,
360
                                        const char *key, long long int val)
361
{
362 363 364
    if ((wctx->section[wctx->level]->id != SECTION_ID_FORMAT
         && wctx->section[wctx->level]->id != SECTION_ID_FORMAT_TAGS) ||
        !fmt_entries_to_show || av_dict_get(fmt_entries_to_show, key, NULL, 0)) {
365
        wctx->writer->print_integer(wctx, key, val);
366
        wctx->nb_item[wctx->level]++;
367
    }
368 369
}

370
static inline void writer_print_string(WriterContext *wctx,
371
                                       const char *key, const char *val, int opt)
372
{
373 374
    if (opt && !(wctx->writer->flags & WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS))
        return;
375 376 377
    if ((wctx->section[wctx->level]->id != SECTION_ID_FORMAT
         && wctx->section[wctx->level]->id != SECTION_ID_FORMAT_TAGS) ||
        !fmt_entries_to_show || av_dict_get(fmt_entries_to_show, key, NULL, 0)) {
378
        wctx->writer->print_string(wctx, key, val);
379
        wctx->nb_item[wctx->level]++;
380
    }
381 382
}

383 384 385 386 387 388 389 390 391
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);
}

392
static void writer_print_time(WriterContext *wctx, const char *key,
393
                              int64_t ts, const AVRational *time_base, int is_duration)
394 395 396
{
    char buf[128];

397 398 399 400 401 402 403 404 405 406
    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);
    }
407 408
}

409
static void writer_print_ts(WriterContext *wctx, const char *key, int64_t ts, int is_duration)
410
{
411
    if ((!is_duration && ts == AV_NOPTS_VALUE) || (is_duration && ts == 0)) {
412 413
        writer_print_string(wctx, key, "N/A", 1);
    } else {
414
        writer_print_integer(wctx, key, ts);
415 416 417
    }
}

418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445
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);
}

446 447
#define MAX_REGISTERED_WRITERS_NB 64

448
static const Writer *registered_writers[MAX_REGISTERED_WRITERS_NB + 1];
449

450
static int writer_register(const Writer *writer)
451
{
452 453 454 455 456 457 458
    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;
459 460
}

461
static const Writer *writer_get_by_name(const char *name)
462 463 464 465 466 467 468 469 470
{
    int i;

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

    return NULL;
}
471

472

473
/* WRITERS */
474

475 476 477 478 479 480 481 482 483 484 485
#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                                  \
}

486 487
/* Default output */

488 489
typedef struct DefaultContext {
    const AVClass *class;
490
    int nokey;
491
    int noprint_wrappers;
492 493
    int nested_section[SECTION_MAX_NB_LEVELS];
    AVBPrint prefix[SECTION_MAX_NB_LEVELS];
494 495 496 497 498
} DefaultContext;

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

static const AVOption default_options[] = {
499 500 501 502
    { "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 },
503 504 505
    {NULL},
};

506
DEFINE_WRITER_CLASS(default);
507

508 509 510 511 512
/* 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++)
513
        dst[i] = av_toupper(src[i]);
514 515 516 517
    dst[i] = 0;
    return dst;
}

518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536
static int default_init(WriterContext *wctx)
{
    DefaultContext *def = wctx->priv;
    int i;

    for (i = 0; i < SECTION_MAX_NB_LEVELS; i++)
        av_bprint_init(&def->prefix[i], 1, AV_BPRINT_SIZE_UNLIMITED);
    return 0;
}

static void default_uninit(WriterContext *wctx)
{
    DefaultContext *def = wctx->priv;
    int i;

    for (i = 0; i < SECTION_MAX_NB_LEVELS; i++)
        av_bprint_finalize(&def->prefix[i], NULL);
}

537
static void default_print_section_header(WriterContext *wctx)
538
{
539
    DefaultContext *def = wctx->priv;
540
    char buf[32];
541 542 543 544
    const struct section *section = wctx->section[wctx->level];
    const struct section *parent_section = wctx->level ?
        wctx->section[wctx->level-1] : NULL;

545 546 547 548 549 550 551 552 553 554
    av_bprint_clear(&def->prefix[wctx->level]);
    if (parent_section &&
        !(parent_section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY))) {
        def->nested_section[wctx->level] = 1;
        av_bprintf(&def->prefix[wctx->level], "%s%s:", def->prefix[wctx->level-1].str,
                   upcase_string(buf, sizeof(buf),
                                 av_x_if_null(section->element_name, section->name)));
    }

    if (def->noprint_wrappers || def->nested_section[wctx->level])
555
        return;
556

557 558
    if (!(section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY)))
        printf("[%s]\n", upcase_string(buf, sizeof(buf), section->name));
559 560
}

561
static void default_print_section_footer(WriterContext *wctx)
562
{
563
    DefaultContext *def = wctx->priv;
564
    const struct section *section = wctx->section[wctx->level];
565 566
    char buf[32];

567
    if (def->noprint_wrappers || def->nested_section[wctx->level])
568 569 570 571
        return;

    if (!(section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY)))
        printf("[/%s]\n", upcase_string(buf, sizeof(buf), section->name));
572 573 574 575
}

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

578
    if (!def->nokey)
579
        printf("%s%s=", def->prefix[wctx->level].str, key);
580
    printf("%s\n", value);
581 582
}

583
static void default_print_int(WriterContext *wctx, const char *key, long long int value)
584
{
585 586 587
    DefaultContext *def = wctx->priv;

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

592
static const Writer default_writer = {
593
    .name                  = "default",
594
    .priv_size             = sizeof(DefaultContext),
595 596
    .init                  = default_init,
    .uninit                = default_uninit,
597 598 599 600
    .print_section_header  = default_print_section_header,
    .print_section_footer  = default_print_section_footer,
    .print_integer         = default_print_int,
    .print_string          = default_print_str,
601
    .flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS,
602
    .priv_class            = &default_class,
603 604
};

605 606 607
/* Compact output */

/**
608
 * Apply C-language-like string escaping.
609
 */
610
static const char *c_escape_str(AVBPrint *dst, const char *src, const char sep, void *log_ctx)
611 612 613 614
{
    const char *p;

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

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

638
    if (needs_quoting)
639 640
        av_bprint_chars(dst, '\"', 1);

641 642
    for (; *src; src++) {
        if (*src == '"')
643
            av_bprint_chars(dst, '\"', 1);
644
        av_bprint_chars(dst, *src, 1);
645
    }
646
    if (needs_quoting)
647 648
        av_bprint_chars(dst, '\"', 1);
    return dst->str;
649 650
}

651
static const char *none_escape_str(AVBPrint *dst, const char *src, const char sep, void *log_ctx)
652 653 654 655 656 657 658 659 660
{
    return src;
}

typedef struct CompactContext {
    const AVClass *class;
    char *item_sep_str;
    char item_sep;
    int nokey;
661
    int print_section;
662
    char *escape_mode_str;
663
    const char * (*escape_str)(AVBPrint *dst, const char *src, const char sep, void *log_ctx);
664 665
    int nested_section[SECTION_MAX_NB_LEVELS];
    AVBPrint prefix[SECTION_MAX_NB_LEVELS];
666 667
} CompactContext;

668
#undef OFFSET
669 670 671 672 673
#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 },
674 675
    {"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        },
676 677
    {"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 },
678 679
    {"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        },
680 681 682
    {NULL},
};

683
DEFINE_WRITER_CLASS(compact);
684

685
static av_cold int compact_init(WriterContext *wctx)
686 687
{
    CompactContext *compact = wctx->priv;
688
    int i;
689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704

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

705 706
    for (i = 0; i < SECTION_MAX_NB_LEVELS; i++)
        av_bprint_init(&compact->prefix[i], 1, AV_BPRINT_SIZE_UNLIMITED);
707 708 709
    return 0;
}

710 711 712 713 714 715 716 717 718
static void compact_uninit(WriterContext *wctx)
{
    CompactContext *compact = wctx->priv;
    int i;

    for (i = 0; i < SECTION_MAX_NB_LEVELS; i++)
        av_bprint_finalize(&compact->prefix[i], NULL);
}

719
static void compact_print_section_header(WriterContext *wctx)
720 721
{
    CompactContext *compact = wctx->priv;
722
    const struct section *section = wctx->section[wctx->level];
723 724
    const struct section *parent_section = wctx->level ?
        wctx->section[wctx->level-1] : NULL;
725

726 727 728 729 730 731 732
    av_bprint_clear(&compact->prefix[wctx->level]);
    if (parent_section &&
        !(parent_section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY))) {
        compact->nested_section[wctx->level] = 1;
        av_bprintf(&compact->prefix[wctx->level], "%s%s:",
                   compact->prefix[wctx->level-1].str,
                   (char *)av_x_if_null(section->element_name, section->name));
733
        wctx->nb_item[wctx->level] = wctx->nb_item[wctx->level-1];
734
    } else if (compact->print_section &&
735
        !(section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY)))
736
       printf("%s%c", section->name, compact->item_sep);
737 738
}

739
static void compact_print_section_footer(WriterContext *wctx)
740
{
741
    CompactContext *compact = wctx->priv;
742

743 744
    if (!compact->nested_section[wctx->level] &&
        !(wctx->section[wctx->level]->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY)))
745
        printf("\n");
746 747 748 749 750
}

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

753
    if (wctx->nb_item[wctx->level]) printf("%c", compact->item_sep);
754
    if (!compact->nokey)
755
        printf("%s%s=", compact->prefix[wctx->level].str, key);
756 757 758
    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);
759 760
}

761
static void compact_print_int(WriterContext *wctx, const char *key, long long int value)
762 763 764
{
    CompactContext *compact = wctx->priv;

765
    if (wctx->nb_item[wctx->level]) printf("%c", compact->item_sep);
766
    if (!compact->nokey)
767
        printf("%s%s=", compact->prefix[wctx->level].str, key);
768
    printf("%lld", value);
769 770
}

771
static const Writer compact_writer = {
772 773 774
    .name                 = "compact",
    .priv_size            = sizeof(CompactContext),
    .init                 = compact_init,
775
    .uninit               = compact_uninit,
776 777 778 779 780
    .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,
781
    .priv_class           = &compact_class,
782 783
};

784 785
/* CSV output */

786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801
#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);
802

803
static const Writer csv_writer = {
804 805
    .name                 = "csv",
    .priv_size            = sizeof(CompactContext),
806
    .init                 = compact_init,
807 808 809 810 811
    .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,
812
    .priv_class           = &csv_class,
813 814
};

815 816 817 818
/* Flat output */

typedef struct FlatContext {
    const AVClass *class;
819
    AVBPrint section_header[SECTION_MAX_NB_LEVELS];
820 821 822 823 824 825 826 827 828 829 830
    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 },
831 832
    {"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 },
833 834 835
    {NULL},
};

836
DEFINE_WRITER_CLASS(flat);
837

838
static av_cold int flat_init(WriterContext *wctx)
839 840
{
    FlatContext *flat = wctx->priv;
841
    int i;
842 843 844 845 846 847 848

    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];
849 850 851

    for (i = 0; i < SECTION_MAX_NB_LEVELS; i++)
        av_bprint_init(&flat->section_header[i], 1, AV_BPRINT_SIZE_UNLIMITED);
852 853 854
    return 0;
}

855 856 857 858 859 860 861 862 863
static void flat_uninit(WriterContext *wctx)
{
    FlatContext *flat = wctx->priv;
    int i;

    for (i = 0; i < SECTION_MAX_NB_LEVELS; i++)
        av_bprint_finalize(&flat->section_header[i], NULL);
}

864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888
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;
889 890
        case '`':  av_bprintf(dst, "%s", "\\`");  break;
        case '$':  av_bprintf(dst, "%s", "\\$");  break;
891 892 893 894 895 896
        default:   av_bprint_chars(dst, *p, 1);   break;
        }
    }
    return dst->str;
}

897
static void flat_print_section_header(WriterContext *wctx)
898 899
{
    FlatContext *flat = wctx->priv;
900
    AVBPrint *buf = &flat->section_header[wctx->level];
901 902 903
    const struct section *section = wctx->section[wctx->level];
    const struct section *parent_section = wctx->level ?
        wctx->section[wctx->level-1] : NULL;
904

905 906
    /* build section header */
    av_bprint_clear(buf);
907 908 909
    if (!parent_section)
        return;
    av_bprintf(buf, "%s", flat->section_header[wctx->level-1].str);
910

911 912 913
    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);
914

915 916 917 918 919
        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);
        }
920
    }
921 922 923 924
}

static void flat_print_int(WriterContext *wctx, const char *key, long long int value)
{
925 926
    FlatContext *flat = wctx->priv;
    printf("%s%s=%lld\n", flat->section_header[wctx->level].str, key, value);
927 928 929 930 931 932 933
}

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

934
    printf("%s", flat->section_header[wctx->level].str);
935 936 937 938 939 940 941 942 943 944 945
    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,
946
    .uninit                = flat_uninit,
947 948 949 950
    .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,
951
    .priv_class            = &flat_class,
952 953
};

954 955 956 957 958
/* INI format output */

typedef struct {
    const AVClass *class;
    int hierarchical;
959
    AVBPrint section_header[SECTION_MAX_NB_LEVELS];
960 961 962 963 964 965
} INIContext;

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

static const AVOption ini_options[] = {
966 967
    {"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 },
968 969 970
    {NULL},
};

971
DEFINE_WRITER_CLASS(ini);
972

973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991
static int ini_init(WriterContext *wctx)
{
    INIContext *ini = wctx->priv;
    int i;

    for (i = 0; i < SECTION_MAX_NB_LEVELS; i++)
        av_bprint_init(&ini->section_header[i], 1, AV_BPRINT_SIZE_UNLIMITED);
    return 0;
}

static void ini_uninit(WriterContext *wctx)
{
    INIContext *ini = wctx->priv;
    int i;

    for (i = 0; i < SECTION_MAX_NB_LEVELS; i++)
        av_bprint_finalize(&ini->section_header[i], NULL);
}

992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018
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;
}

1019
static void ini_print_section_header(WriterContext *wctx)
1020 1021
{
    INIContext *ini = wctx->priv;
1022
    AVBPrint *buf = &ini->section_header[wctx->level];
1023 1024 1025
    const struct section *section = wctx->section[wctx->level];
    const struct section *parent_section = wctx->level ?
        wctx->section[wctx->level-1] : NULL;
1026

1027 1028
    av_bprint_clear(buf);
    if (!parent_section) {
1029 1030 1031
        printf("# ffprobe output\n\n");
        return;
    }
1032

1033
    if (wctx->nb_item[wctx->level-1])
1034 1035
        printf("\n");

1036 1037 1038 1039
    av_bprintf(buf, "%s", ini->section_header[wctx->level-1].str);
    if (ini->hierarchical ||
        !(section->flags & (SECTION_FLAG_IS_ARRAY|SECTION_FLAG_IS_WRAPPER))) {
        av_bprintf(buf, "%s%s", buf->str[0] ? "." : "", wctx->section[wctx->level]->name);
1040

1041 1042 1043 1044 1045
        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);
        }
1046
    }
1047

1048
    if (!(section->flags & (SECTION_FLAG_IS_ARRAY|SECTION_FLAG_IS_WRAPPER)))
1049
        printf("[%s]\n", buf->str);
1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070
}

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),
1071 1072
    .init                  = ini_init,
    .uninit                = ini_uninit,
1073 1074 1075 1076
    .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,
1077
    .priv_class            = &ini_class,
1078 1079
};

1080 1081 1082
/* JSON output */

typedef struct {
1083
    const AVClass *class;
1084
    int indent_level;
1085 1086
    int compact;
    const char *item_sep, *item_start_end;
1087 1088
} JSONContext;

1089 1090 1091 1092
#undef OFFSET
#define OFFSET(x) offsetof(JSONContext, x)

static const AVOption json_options[]= {
1093 1094
    { "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 },
1095 1096 1097
    { NULL }
};

1098
DEFINE_WRITER_CLASS(json);
1099

1100
static av_cold int json_init(WriterContext *wctx)
1101 1102
{
    JSONContext *json = wctx->priv;
1103 1104 1105

    json->item_sep       = json->compact ? ", " : ",\n";
    json->item_start_end = json->compact ? " "  : "\n";
1106 1107 1108 1109

    return 0;
}

1110
static const char *json_escape_str(AVBPrint *dst, const char *src, void *log_ctx)
1111 1112 1113
{
    static const char json_escape[] = {'"', '\\', '\b', '\f', '\n', '\r', '\t', 0};
    static const char json_subst[]  = {'"', '\\',  'b',  'f',  'n',  'r',  't', 0};
1114 1115 1116 1117 1118
    const char *p;

    for (p = src; *p; p++) {
        char *s = strchr(json_escape, *p);
        if (s) {
1119 1120
            av_bprint_chars(dst, '\\', 1);
            av_bprint_chars(dst, json_subst[s - json_escape], 1);
1121
        } else if ((unsigned char)*p < 32) {
1122
            av_bprintf(dst, "\\u00%02x", *p & 0xff);
1123
        } else {
1124
            av_bprint_chars(dst, *p, 1);
1125 1126
        }
    }
1127
    return dst->str;
1128 1129
}

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

1132
static void json_print_section_header(WriterContext *wctx)
1133 1134
{
    JSONContext *json = wctx->priv;
1135
    AVBPrint buf;
1136 1137 1138
    const struct section *section = wctx->section[wctx->level];
    const struct section *parent_section = wctx->level ?
        wctx->section[wctx->level-1] : NULL;
1139

1140 1141 1142 1143 1144 1145 1146
    if (wctx->level && wctx->nb_item[wctx->level-1])
        printf(",\n");

    if (section->flags & SECTION_FLAG_IS_WRAPPER) {
        printf("{\n");
        json->indent_level++;
    } else {
1147
        av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
1148 1149 1150
        json_escape_str(&buf, section->name, wctx);
        JSON_INDENT();

1151
        json->indent_level++;
1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166
        if (section->flags & SECTION_FLAG_IS_ARRAY) {
            printf("\"%s\": [\n", buf.str);
        } else if (!(parent_section->flags & SECTION_FLAG_IS_ARRAY)) {
            printf("\"%s\": {%s", buf.str, json->item_start_end);
        } else {
            printf("{%s", json->item_start_end);

            /* this is required so the parser can distinguish between packets and frames */
            if (parent_section->id == SECTION_ID_PACKETS_AND_FRAMES) {
                if (!json->compact)
                    JSON_INDENT();
                printf("\"type\": \"%s\"%s", section->name, json->item_sep);
            }
        }
        av_bprint_finalize(&buf, NULL);
1167
    }
1168 1169
}

1170
static void json_print_section_footer(WriterContext *wctx)
1171 1172
{
    JSONContext *json = wctx->priv;
1173
    const struct section *section = wctx->section[wctx->level];
1174

1175 1176 1177 1178
    if (wctx->level == 0) {
        json->indent_level--;
        printf("\n}\n");
    } else if (section->flags & SECTION_FLAG_IS_ARRAY) {
1179
        printf("\n");
1180
        json->indent_level--;
1181 1182
        JSON_INDENT();
        printf("]");
1183 1184 1185
    } else {
        printf("%s", json->item_start_end);
        json->indent_level--;
1186 1187
        if (!json->compact)
            JSON_INDENT();
1188
        printf("}");
1189
    }
1190 1191 1192
}

static inline void json_print_item_str(WriterContext *wctx,
1193
                                       const char *key, const char *value)
1194
{
1195
    AVBPrint buf;
1196

1197 1198
    av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
    printf("\"%s\":", json_escape_str(&buf, key,   wctx));
1199
    av_bprint_clear(&buf);
1200 1201
    printf(" \"%s\"", json_escape_str(&buf, value, wctx));
    av_bprint_finalize(&buf, NULL);
1202 1203 1204 1205
}

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

1208 1209
    if (wctx->nb_item[wctx->level])
        printf("%s", json->item_sep);
1210 1211
    if (!json->compact)
        JSON_INDENT();
1212
    json_print_item_str(wctx, key, value);
1213 1214
}

1215
static void json_print_int(WriterContext *wctx, const char *key, long long int value)
1216
{
1217
    JSONContext *json = wctx->priv;
1218
    AVBPrint buf;
1219

1220 1221
    if (wctx->nb_item[wctx->level])
        printf("%s", json->item_sep);
1222 1223
    if (!json->compact)
        JSON_INDENT();
1224 1225 1226 1227

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

1230
static const Writer json_writer = {
1231 1232
    .name                 = "json",
    .priv_size            = sizeof(JSONContext),
1233
    .init                 = json_init,
1234 1235 1236 1237
    .print_section_header = json_print_section_header,
    .print_section_footer = json_print_section_footer,
    .print_integer        = json_print_int,
    .print_string         = json_print_str,
1238
    .flags = WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER,
1239
    .priv_class           = &json_class,
1240 1241
};

1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255
/* 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[] = {
1256 1257 1258 1259
    {"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 },
1260 1261 1262
    {NULL},
};

1263
DEFINE_WRITER_CLASS(xml);
1264

1265
static av_cold int xml_init(WriterContext *wctx)
1266 1267 1268 1269 1270 1271 1272 1273 1274 1275
{
    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); \
1276
            return AVERROR(EINVAL);                                     \
1277 1278 1279 1280
        }
        CHECK_COMPLIANCE(show_private_data, "private");
        CHECK_COMPLIANCE(show_value_unit,   "unit");
        CHECK_COMPLIANCE(use_value_prefix,  "prefix");
1281 1282 1283 1284 1285 1286 1287

        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);
        }
1288 1289 1290 1291 1292
    }

    return 0;
}

1293
static const char *xml_escape_str(AVBPrint *dst, const char *src, void *log_ctx)
1294 1295 1296
{
    const char *p;

1297
    for (p = src; *p; p++) {
1298
        switch (*p) {
1299 1300 1301 1302 1303 1304
        case '&' : av_bprintf(dst, "%s", "&amp;");  break;
        case '<' : av_bprintf(dst, "%s", "&lt;");   break;
        case '>' : av_bprintf(dst, "%s", "&gt;");   break;
        case '\"': av_bprintf(dst, "%s", "&quot;"); break;
        case '\'': av_bprintf(dst, "%s", "&apos;"); break;
        default: av_bprint_chars(dst, *p, 1);
1305 1306 1307
        }
    }

1308
    return dst->str;
1309 1310
}

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

1313
static void xml_print_section_header(WriterContext *wctx)
1314 1315
{
    XMLContext *xml = wctx->priv;
1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329
    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;
1330 1331
    }

1332 1333 1334
    if (xml->within_tag) {
        xml->within_tag = 0;
        printf(">\n");
1335
    }
1336 1337 1338 1339 1340 1341
    if (!strcmp(section->name, "tags")) {
        xml->indent_level++;
    } else {
        if (!(parent_section->flags & SECTION_FLAG_IS_ARRAY) &&
            (wctx->level && wctx->nb_item[wctx->level-1]))
        printf("\n");
1342

1343
        xml->indent_level++;
1344

1345 1346 1347 1348 1349 1350 1351
        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;
        }
    }
1352 1353
}

1354
static void xml_print_section_footer(WriterContext *wctx)
1355 1356
{
    XMLContext *xml = wctx->priv;
1357
    const struct section *section = wctx->section[wctx->level];
1358

1359 1360 1361 1362
    if (wctx->level == 0) {
        printf("</%sffprobe>\n", xml->fully_qualified ? "ffprobe:" : "");
    } else if (xml->within_tag) {
        xml->within_tag = 0;
1363
        printf("/>\n");
1364 1365 1366 1367 1368 1369
        xml->indent_level--;
    } else if (!strcmp(section->name, "tags")) {
        xml->indent_level--;
    } else {
        XML_INDENT(); printf("</%s>\n", section->name);
        xml->indent_level--;
1370 1371 1372 1373 1374
    }
}

static void xml_print_str(WriterContext *wctx, const char *key, const char *value)
{
1375
    AVBPrint buf;
1376 1377
    XMLContext *xml = wctx->priv;
    const struct section *section = wctx->section[wctx->level];
1378

1379
    av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391

    if (!strcmp(section->name, "tags")) {
        XML_INDENT();
        printf("<tag key=\"%s\"", xml_escape_str(&buf, key, wctx));
        av_bprint_clear(&buf);
        printf(" value=\"%s\"/>\n", xml_escape_str(&buf, value, wctx));
    } else {
        if (wctx->nb_item[wctx->level])
            printf(" ");
        printf("%s=\"%s\"", key, xml_escape_str(&buf, value, wctx));
    }

1392
    av_bprint_finalize(&buf, NULL);
1393 1394 1395 1396
}

static void xml_print_int(WriterContext *wctx, const char *key, long long int value)
{
1397
    if (wctx->nb_item[wctx->level])
1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409
        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,
1410
    .flags = WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER,
1411
    .priv_class           = &xml_class,
1412 1413
};

1414 1415 1416 1417 1418 1419 1420 1421 1422
static void writer_register_all(void)
{
    static int initialized;

    if (initialized)
        return;
    initialized = 1;

    writer_register(&default_writer);
1423
    writer_register(&compact_writer);
1424
    writer_register(&csv_writer);
1425
    writer_register(&flat_writer);
1426
    writer_register(&ini_writer);
1427
    writer_register(&json_writer);
1428
    writer_register(&xml_writer);
1429 1430 1431
}

#define print_fmt(k, f, ...) do {              \
1432 1433 1434
    av_bprint_clear(&pbuf);                    \
    av_bprintf(&pbuf, f, __VA_ARGS__);         \
    writer_print_string(w, k, pbuf.str, 0);    \
1435 1436 1437
} while (0)

#define print_int(k, v)         writer_print_integer(w, k, v)
1438
#define print_q(k, v, s)        writer_print_rational(w, k, v, s)
1439 1440
#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)
1441 1442 1443 1444
#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)
1445 1446 1447 1448 1449 1450 1451
#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)

1452 1453
#define print_section_header(s) writer_print_section_header(w, s)
#define print_section_footer(s) writer_print_section_footer(w, s)
1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465

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);
}
1466 1467

static void show_packet(WriterContext *w, AVFormatContext *fmt_ctx, AVPacket *pkt, int packet_idx)
1468 1469 1470
{
    char val_str[128];
    AVStream *st = fmt_ctx->streams[pkt->stream_index];
1471
    AVBPrint pbuf;
1472
    const char *s;
1473

1474 1475
    av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);

1476 1477
    writer_print_section_header(w, SECTION_ID_PACKET);

1478 1479 1480
    s = av_get_media_type_string(st->codec->codec_type);
    if (s) print_str    ("codec_type", s);
    else   print_str_opt("codec_type", "unknown");
1481
    print_int("stream_index",     pkt->stream_index);
1482 1483 1484 1485
    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);
1486 1487
    print_duration_ts("duration",        pkt->duration);
    print_duration_time("duration_time", pkt->duration, &st->time_base);
1488 1489
    print_duration_ts("convergence_duration", pkt->convergence_duration);
    print_duration_time("convergence_duration_time", pkt->convergence_duration, &st->time_base);
1490
    print_val("size",             pkt->size, unit_byte_str);
1491 1492
    if (pkt->pos != -1) print_fmt    ("pos", "%"PRId64, pkt->pos);
    else                print_str_opt("pos", "N/A");
1493
    print_fmt("flags", "%c",      pkt->flags & AV_PKT_FLAG_KEY ? 'K' : '_');
1494 1495
    if (do_show_data)
        writer_print_data(w, "data", pkt->data, pkt->size);
1496
    writer_print_section_footer(w);
1497

1498
    av_bprint_finalize(&pbuf, NULL);
1499
    fflush(stdout);
1500 1501
}

1502 1503
static void show_frame(WriterContext *w, AVFrame *frame, AVStream *stream,
                       AVFormatContext *fmt_ctx)
1504
{
1505
    AVBPrint pbuf;
1506 1507
    const char *s;

1508 1509
    av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);

1510
    writer_print_section_header(w, SECTION_ID_FRAME);
1511 1512 1513 1514 1515 1516 1517 1518 1519

    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);
1520 1521
    print_duration_ts  ("pkt_duration",      frame->pkt_duration);
    print_duration_time("pkt_duration_time", frame->pkt_duration, &stream->time_base);
1522 1523 1524 1525
    if (frame->pkt_pos != -1) print_fmt    ("pkt_pos", "%"PRId64, frame->pkt_pos);
    else                      print_str_opt("pkt_pos", "N/A");

    switch (stream->codec->codec_type) {
1526 1527
        AVRational sar;

1528
    case AVMEDIA_TYPE_VIDEO:
1529 1530 1531 1532 1533
        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");
1534 1535 1536
        sar = av_guess_sample_aspect_ratio(fmt_ctx, stream, frame);
        if (sar.num) {
            print_q("sample_aspect_ratio", sar, ':');
1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547
        } 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;
1548 1549 1550 1551 1552 1553

    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);
1554 1555 1556 1557 1558 1559 1560 1561
        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");
1562 1563
        break;
    }
1564
    show_tags(w, av_frame_get_metadata(frame), SECTION_ID_FRAME_TAGS);
1565

1566
    writer_print_section_footer(w);
1567

1568
    av_bprint_finalize(&pbuf, NULL);
1569 1570 1571
    fflush(stdout);
}

1572 1573 1574
static av_always_inline int process_frame(WriterContext *w,
                                          AVFormatContext *fmt_ctx,
                                          AVFrame *frame, AVPacket *pkt)
1575 1576
{
    AVCodecContext *dec_ctx = fmt_ctx->streams[pkt->stream_index]->codec;
1577
    int ret = 0, got_frame = 0;
1578

1579
    avcodec_get_frame_defaults(frame);
1580
    if (dec_ctx->codec) {
1581 1582
        switch (dec_ctx->codec_type) {
        case AVMEDIA_TYPE_VIDEO:
1583
            ret = avcodec_decode_video2(dec_ctx, frame, &got_frame, pkt);
1584
            break;
1585

1586
        case AVMEDIA_TYPE_AUDIO:
1587
            ret = avcodec_decode_audio4(dec_ctx, frame, &got_frame, pkt);
1588 1589
            break;
        }
1590
    }
1591

1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602
    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;
1603 1604
}

1605
static void read_packets(WriterContext *w, AVFormatContext *fmt_ctx)
1606
{
1607
    AVPacket pkt, pkt1;
1608
    AVFrame frame;
1609
    int i = 0;
1610 1611 1612

    av_init_packet(&pkt);

1613
    while (!av_read_frame(fmt_ctx, &pkt)) {
1614 1615 1616 1617 1618 1619
        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) {
1620
            pkt1 = pkt;
1621
            while (pkt1.size && process_frame(w, fmt_ctx, &frame, &pkt1) > 0);
1622
        }
1623
        av_free_packet(&pkt);
1624 1625 1626 1627 1628 1629 1630
    }
    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;
1631 1632
        if (do_read_frames)
            while (process_frame(w, fmt_ctx, &frame, &pkt) > 0);
1633
    }
1634 1635
}

1636
static void show_stream(WriterContext *w, AVFormatContext *fmt_ctx, int stream_idx)
Stefano Sabatini's avatar
Stefano Sabatini committed
1637 1638 1639
{
    AVStream *stream = fmt_ctx->streams[stream_idx];
    AVCodecContext *dec_ctx;
1640
    const AVCodec *dec;
Stefano Sabatini's avatar
Stefano Sabatini committed
1641
    char val_str[128];
1642
    const char *s;
1643
    AVRational sar, dar;
1644 1645 1646
    AVBPrint pbuf;

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

1648
    writer_print_section_header(w, SECTION_ID_STREAM);
Stefano Sabatini's avatar
Stefano Sabatini committed
1649

1650
    print_int("index", stream->index);
Stefano Sabatini's avatar
Stefano Sabatini committed
1651 1652

    if ((dec_ctx = stream->codec)) {
1653
        const char *profile = NULL;
1654 1655 1656 1657
        dec = dec_ctx->codec;
        if (dec) {
            print_str("codec_name", dec->name);
            if (!do_bitexact) {
1658 1659
                if (dec->long_name) print_str    ("codec_long_name", dec->long_name);
                else                print_str_opt("codec_long_name", "unknown");
1660
            }
Stefano Sabatini's avatar
Stefano Sabatini committed
1661
        } else {
1662
            print_str_opt("codec_name", "unknown");
1663
            if (!do_bitexact) {
1664
                print_str_opt("codec_long_name", "unknown");
1665
            }
Stefano Sabatini's avatar
Stefano Sabatini committed
1666 1667
        }

1668 1669 1670 1671 1672
        if (dec && (profile = av_get_profile_name(dec, dec_ctx->profile)))
            print_str("profile", profile);
        else
            print_str_opt("profile", "unknown");

1673 1674 1675
        s = av_get_media_type_string(dec_ctx->codec_type);
        if (s) print_str    ("codec_type", s);
        else   print_str_opt("codec_type", "unknown");
1676
        print_q("codec_time_base", dec_ctx->time_base, '/');
Stefano Sabatini's avatar
Stefano Sabatini committed
1677 1678

        /* print AVI/FourCC tag */
1679
        av_get_codec_tag_string(val_str, sizeof(val_str), dec_ctx->codec_tag);
1680 1681
        print_str("codec_tag_string",    val_str);
        print_fmt("codec_tag", "0x%04x", dec_ctx->codec_tag);
Stefano Sabatini's avatar
Stefano Sabatini committed
1682

1683 1684 1685 1686
        /* Print useful disposition */
        print_int("default", !!(stream->disposition & AV_DISPOSITION_DEFAULT));
        print_int("forced", !!(stream->disposition & AV_DISPOSITION_FORCED));

Stefano Sabatini's avatar
Stefano Sabatini committed
1687
        switch (dec_ctx->codec_type) {
1688
        case AVMEDIA_TYPE_VIDEO:
1689 1690 1691
            print_int("width",        dec_ctx->width);
            print_int("height",       dec_ctx->height);
            print_int("has_b_frames", dec_ctx->has_b_frames);
1692 1693 1694 1695 1696 1697
            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,
1698
                          1024*1024);
1699
                print_q("display_aspect_ratio", dar, ':');
1700 1701 1702
            } else {
                print_str_opt("sample_aspect_ratio", "N/A");
                print_str_opt("display_aspect_ratio", "N/A");
1703
            }
1704 1705 1706
            s = av_get_pix_fmt_name(dec_ctx->pix_fmt);
            if (s) print_str    ("pix_fmt", s);
            else   print_str_opt("pix_fmt", "unknown");
1707
            print_int("level",   dec_ctx->level);
1708
            if (dec_ctx->timecode_frame_start >= 0) {
1709 1710 1711
                char tcbuf[AV_TIMECODE_STR_SIZE];
                av_timecode_make_mpeg_tc_string(tcbuf, dec_ctx->timecode_frame_start);
                print_str("timecode", tcbuf);
1712 1713 1714
            } else {
                print_str_opt("timecode", "N/A");
            }
1715 1716
            print_int("attached_pic",
                      !!(stream->disposition & AV_DISPOSITION_ATTACHED_PIC));
Stefano Sabatini's avatar
Stefano Sabatini committed
1717 1718
            break;

1719
        case AVMEDIA_TYPE_AUDIO:
1720 1721 1722
            s = av_get_sample_fmt_name(dec_ctx->sample_fmt);
            if (s) print_str    ("sample_fmt", s);
            else   print_str_opt("sample_fmt", "unknown");
1723
            print_val("sample_rate",     dec_ctx->sample_rate, unit_hertz_str);
1724 1725
            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
1726 1727 1728
            break;
        }
    } else {
1729
        print_str_opt("codec_type", "unknown");
Stefano Sabatini's avatar
Stefano Sabatini committed
1730
    }
1731
    if (dec_ctx->codec && dec_ctx->codec->priv_class && show_private_data) {
1732
        const AVOption *opt = NULL;
1733 1734 1735 1736 1737 1738 1739 1740 1741
        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
1742

1743 1744
    if (fmt_ctx->iformat->flags & AVFMT_SHOW_IDS) print_fmt    ("id", "0x%x", stream->id);
    else                                          print_str_opt("id", "N/A");
1745 1746 1747
    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,      '/');
1748 1749 1750 1751
    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);
1752 1753
    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");
1754 1755
    if (stream->nb_frames) print_fmt    ("nb_frames", "%"PRId64, stream->nb_frames);
    else                   print_str_opt("nb_frames", "N/A");
1756 1757 1758 1759
    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");
1760 1761 1762
    if (do_show_data)
        writer_print_data(w, "extradata", dec_ctx->extradata,
                                          dec_ctx->extradata_size);
1763
    show_tags(w, stream->metadata, SECTION_ID_STREAM_TAGS);
Stefano Sabatini's avatar
Stefano Sabatini committed
1764

1765
    writer_print_section_footer(w);
1766
    av_bprint_finalize(&pbuf, NULL);
1767
    fflush(stdout);
Stefano Sabatini's avatar
Stefano Sabatini committed
1768 1769
}

1770
static void show_streams(WriterContext *w, AVFormatContext *fmt_ctx)
1771 1772
{
    int i;
1773
    writer_print_section_header(w, SECTION_ID_STREAMS);
1774 1775
    for (i = 0; i < fmt_ctx->nb_streams; i++)
        show_stream(w, fmt_ctx, i);
1776
    writer_print_section_footer(w);
1777 1778
}

1779
static void show_format(WriterContext *w, AVFormatContext *fmt_ctx)
Stefano Sabatini's avatar
Stefano Sabatini committed
1780 1781
{
    char val_str[128];
1782
    int64_t size = fmt_ctx->pb ? avio_size(fmt_ctx->pb) : -1;
Stefano Sabatini's avatar
Stefano Sabatini committed
1783

1784
    writer_print_section_header(w, SECTION_ID_FORMAT);
1785
    print_str("filename",         fmt_ctx->filename);
1786 1787
    print_int("nb_streams",       fmt_ctx->nb_streams);
    print_str("format_name",      fmt_ctx->iformat->name);
1788
    if (!do_bitexact) {
1789 1790
        if (fmt_ctx->iformat->long_name) print_str    ("format_long_name", fmt_ctx->iformat->long_name);
        else                             print_str_opt("format_long_name", "unknown");
1791
    }
1792 1793
    print_time("start_time",      fmt_ctx->start_time, &AV_TIME_BASE_Q);
    print_time("duration",        fmt_ctx->duration,   &AV_TIME_BASE_Q);
1794 1795 1796 1797
    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");
1798 1799 1800
    show_tags(w, fmt_ctx->metadata, SECTION_ID_FORMAT_TAGS);

    writer_print_section_footer(w);
1801
    fflush(stdout);
Stefano Sabatini's avatar
Stefano Sabatini committed
1802 1803
}

1804 1805 1806 1807 1808 1809 1810 1811
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));

1812
    writer_print_section_header(w, SECTION_ID_ERROR);
1813 1814
    print_int("code", err);
    print_str("string", errbuf_ptr);
1815
    writer_print_section_footer(w);
1816 1817
}

Stefano Sabatini's avatar
Stefano Sabatini committed
1818 1819 1820
static int open_input_file(AVFormatContext **fmt_ctx_ptr, const char *filename)
{
    int err, i;
1821 1822
    AVFormatContext *fmt_ctx = NULL;
    AVDictionaryEntry *t;
Stefano Sabatini's avatar
Stefano Sabatini committed
1823

1824 1825
    if ((err = avformat_open_input(&fmt_ctx, filename,
                                   iformat, &format_opts)) < 0) {
Stefano Sabatini's avatar
Stefano Sabatini committed
1826 1827 1828
        print_error(filename, err);
        return err;
    }
1829 1830 1831 1832 1833
    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
1834 1835

    /* fill the streams in the format context */
1836
    if ((err = avformat_find_stream_info(fmt_ctx, NULL)) < 0) {
Stefano Sabatini's avatar
Stefano Sabatini committed
1837 1838 1839 1840
        print_error(filename, err);
        return err;
    }

1841
    av_dump_format(fmt_ctx, 0, filename, 0);
Stefano Sabatini's avatar
Stefano Sabatini committed
1842 1843 1844 1845 1846 1847

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

1848
        if (stream->codec->codec_id == AV_CODEC_ID_PROBE) {
1849 1850 1851 1852
            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))) {
1853 1854 1855
            av_log(NULL, AV_LOG_ERROR,
                    "Unsupported codec with id %d for input stream %d\n",
                    stream->codec->codec_id, stream->index);
1856
        } else if (avcodec_open2(stream->codec, codec, NULL) < 0) {
1857 1858
            av_log(NULL, AV_LOG_ERROR, "Error while opening codec for input stream %d\n",
                   stream->index);
Stefano Sabatini's avatar
Stefano Sabatini committed
1859 1860 1861 1862 1863 1864 1865
        }
    }

    *fmt_ctx_ptr = fmt_ctx;
    return 0;
}

1866 1867 1868 1869 1870 1871 1872
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++)
1873
        if (fmt_ctx->streams[i]->codec->codec_id != AV_CODEC_ID_NONE)
1874 1875 1876 1877 1878
            avcodec_close(fmt_ctx->streams[i]->codec);

    avformat_close_input(ctx_ptr);
}

1879
static int probe_file(WriterContext *wctx, const char *filename)
Stefano Sabatini's avatar
Stefano Sabatini committed
1880 1881
{
    AVFormatContext *fmt_ctx;
1882
    int ret;
1883
    int section_id;
Stefano Sabatini's avatar
Stefano Sabatini committed
1884

1885 1886 1887
    do_read_frames = do_show_frames || do_count_frames;
    do_read_packets = do_show_packets || do_count_packets;

1888 1889
    ret = open_input_file(&fmt_ctx, filename);
    if (ret >= 0) {
1890 1891
        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));
1892
        if (do_read_frames || do_read_packets) {
1893 1894
            if (do_show_frames && do_show_packets &&
                wctx->writer->flags & WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER)
1895
                section_id = SECTION_ID_PACKETS_AND_FRAMES;
1896
            else if (do_show_packets && !do_show_frames)
1897
                section_id = SECTION_ID_PACKETS;
1898
            else // (!do_show_packets && do_show_frames)
1899
                section_id = SECTION_ID_FRAMES;
1900
            if (do_show_frames || do_show_packets)
1901
                writer_print_section_header(wctx, section_id);
1902 1903
            read_packets(wctx, fmt_ctx);
            if (do_show_frames || do_show_packets)
1904
                writer_print_section_footer(wctx);
1905
        }
1906 1907 1908 1909 1910
        if (do_show_streams)
            show_streams(wctx, fmt_ctx);
        if (do_show_format)
            show_format(wctx, fmt_ctx);

1911
        close_input_file(&fmt_ctx);
1912 1913
        av_freep(&nb_streams_frames);
        av_freep(&nb_streams_packets);
1914
    }
1915
    return ret;
Stefano Sabatini's avatar
Stefano Sabatini committed
1916 1917 1918 1919
}

static void show_usage(void)
{
1920 1921 1922
    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
1923 1924
}

1925 1926
static void ffprobe_show_program_version(WriterContext *w)
{
1927 1928
    AVBPrint pbuf;
    av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
1929

1930
    writer_print_section_header(w, SECTION_ID_PROGRAM_VERSION);
1931 1932 1933 1934 1935
    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__);
1936
    print_str("compiler_ident", CC_IDENT);
1937
    print_str("configuration", FFMPEG_CONFIGURATION);
1938
    writer_print_section_footer(w);
1939

1940
    av_bprint_finalize(&pbuf, NULL);
1941 1942 1943 1944 1945 1946
}

#define SHOW_LIB_VERSION(libname, LIBNAME)                              \
    do {                                                                \
        if (CONFIG_##LIBNAME) {                                         \
            unsigned int version = libname##_version();                 \
1947
            writer_print_section_header(w, SECTION_ID_LIBRARY_VERSION); \
1948 1949 1950 1951 1952
            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);                              \
1953
            print_str("ident",   LIB##LIBNAME##_IDENT);                 \
1954
            writer_print_section_footer(w);                             \
1955 1956 1957 1958 1959
        }                                                               \
    } while (0)

static void ffprobe_show_library_versions(WriterContext *w)
{
1960
    writer_print_section_header(w, SECTION_ID_LIBRARY_VERSIONS);
1961 1962 1963 1964 1965 1966 1967 1968
    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);
1969
    writer_print_section_footer(w);
1970 1971
}

1972
static int opt_format(void *optctx, const char *opt, const char *arg)
1973 1974 1975
{
    iformat = av_find_input_format(arg);
    if (!iformat) {
1976
        av_log(NULL, AV_LOG_ERROR, "Unknown input format: %s\n", arg);
1977
        return AVERROR(EINVAL);
1978
    }
1979
    return 0;
1980 1981
}

1982
static int opt_show_format_entry(void *optctx, const char *opt, const char *arg)
1983 1984 1985 1986 1987 1988
{
    do_show_format = 1;
    av_dict_set(&fmt_entries_to_show, arg, "", 0);
    return 0;
}

1989
static void opt_input_file(void *optctx, const char *arg)
Stefano Sabatini's avatar
Stefano Sabatini committed
1990
{
1991
    if (input_filename) {
1992 1993 1994
        av_log(NULL, AV_LOG_ERROR,
                "Argument '%s' provided as input filename, but '%s' was already specified.\n",
                arg, input_filename);
1995 1996
        exit(1);
    }
1997 1998 1999
    if (!strcmp(arg, "-"))
        arg = "pipe:";
    input_filename = arg;
Stefano Sabatini's avatar
Stefano Sabatini committed
2000 2001
}

2002 2003 2004 2005 2006 2007
static int opt_input_file_i(void *optctx, const char *opt, const char *arg)
{
    opt_input_file(optctx, arg);
    return 0;
}

2008
void show_help_default(const char *opt, const char *arg)
Stefano Sabatini's avatar
Stefano Sabatini committed
2009
{
2010
    av_log_set_callback(log_callback_help);
Stefano Sabatini's avatar
Stefano Sabatini committed
2011
    show_usage();
2012
    show_help_options(options, "Main options:", 0, 0, 0);
Stefano Sabatini's avatar
Stefano Sabatini committed
2013
    printf("\n");
2014 2015

    show_help_children(avformat_get_class(), AV_OPT_FLAG_DECODING_PARAM);
Stefano Sabatini's avatar
Stefano Sabatini committed
2016 2017
}

2018
static int opt_pretty(void *optctx, const char *opt, const char *arg)
Stefano Sabatini's avatar
Stefano Sabatini committed
2019 2020 2021 2022 2023
{
    show_value_unit              = 1;
    use_value_prefix             = 1;
    use_byte_value_binary_prefix = 1;
    use_value_sexagesimal_format = 1;
2024
    return 0;
Stefano Sabatini's avatar
Stefano Sabatini committed
2025 2026
}

2027 2028 2029 2030 2031 2032 2033
static int opt_show_versions(const char *opt, const char *arg)
{
    do_show_program_version  = 1;
    do_show_library_versions = 1;
    return 0;
}

2034
static const OptionDef real_options[] = {
Stefano Sabatini's avatar
Stefano Sabatini committed
2035
#include "cmdutils_common_opts.h"
2036 2037 2038 2039
    { "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
2040
      "use binary prefixes for byte units" },
2041
    { "sexagesimal", OPT_BOOL,  {&use_value_sexagesimal_format},
Stefano Sabatini's avatar
Stefano Sabatini committed
2042
      "use sexagesimal format HOURS:MM:SS.MICROSECONDS for time units" },
2043
    { "pretty", 0, {.func_arg = opt_pretty},
Stefano Sabatini's avatar
Stefano Sabatini committed
2044
      "prettify the format of displayed values, make it more human readable" },
2045
    { "print_format", OPT_STRING | HAS_ARG, {(void*)&print_format},
2046
      "set the output printing format (available formats are: default, compact, csv, flat, ini, json, xml)", "format" },
2047
    { "of", OPT_STRING | HAS_ARG, {(void*)&print_format}, "alias for -print_format", "format" },
2048
    { "show_data",    OPT_BOOL, {(void*)&do_show_data}, "show packets data" },
2049
    { "show_error",   OPT_BOOL, {(void*)&do_show_error} ,  "show probing error" },
2050
    { "show_format",  OPT_BOOL, {&do_show_format} , "show format/container info" },
2051
    { "show_frames",  OPT_BOOL, {(void*)&do_show_frames} , "show frames info" },
2052
    { "show_format_entry", HAS_ARG, {.func_arg = opt_show_format_entry},
2053
      "show a particular entry from the format/container info", "entry" },
2054 2055
    { "show_packets", OPT_BOOL, {&do_show_packets}, "show packets info" },
    { "show_streams", OPT_BOOL, {&do_show_streams}, "show streams info" },
2056 2057
    { "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" },
2058 2059 2060
    { "show_program_version",  OPT_BOOL, {(void*)&do_show_program_version},  "show ffprobe version" },
    { "show_library_versions", OPT_BOOL, {(void*)&do_show_library_versions}, "show library versions" },
    { "show_versions",         0, {(void*)&opt_show_versions}, "show program and library versions" },
2061 2062
    { "show_private_data", OPT_BOOL, {(void*)&show_private_data}, "show private data" },
    { "private",           OPT_BOOL, {(void*)&show_private_data}, "same as show_private_data" },
2063
    { "bitexact", OPT_BOOL, {&do_bitexact}, "force bitexact output" },
2064
    { "default", HAS_ARG | OPT_AUDIO | OPT_VIDEO | OPT_EXPERT, {.func_arg = opt_default}, "generic catch all option", "" },
2065
    { "i", HAS_ARG, {.func_arg = opt_input_file_i}, "read specified file", "input_file"},
Stefano Sabatini's avatar
Stefano Sabatini committed
2066 2067 2068 2069 2070
    { NULL, },
};

int main(int argc, char **argv)
{
2071 2072 2073 2074
    const Writer *w;
    WriterContext *wctx;
    char *buf;
    char *w_name = NULL, *w_args = NULL;
2075 2076
    int ret;

2077
    av_log_set_flags(AV_LOG_SKIP_REPEATED);
2078
    options = real_options;
2079
    parse_loglevel(argc, argv, options);
Stefano Sabatini's avatar
Stefano Sabatini committed
2080
    av_register_all();
2081
    avformat_network_init();
2082
    init_opts();
2083 2084 2085
#if CONFIG_AVDEVICE
    avdevice_register_all();
#endif
Stefano Sabatini's avatar
Stefano Sabatini committed
2086

2087
    show_banner(argc, argv, options);
2088
    parse_options(NULL, argc, argv, options, opt_input_file);
Stefano Sabatini's avatar
Stefano Sabatini committed
2089

2090 2091 2092 2093 2094 2095 2096 2097
    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;
    }

2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109
    writer_register_all();

    if (!print_format)
        print_format = av_strdup("default");
    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
2110 2111
    }

2112 2113 2114
    if ((ret = writer_open(&wctx, w, w_args,
                           sections, FF_ARRAY_ELEMS(sections))) >= 0) {
        writer_print_section_header(wctx, SECTION_ID_ROOT);
2115

2116 2117 2118 2119 2120 2121 2122 2123
        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))) {
2124 2125 2126 2127
            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);
2128
        } else if (input_filename) {
2129
            ret = probe_file(wctx, input_filename);
2130 2131 2132
            if (ret < 0 && do_show_error)
                show_error(wctx, ret);
        }
2133

2134
        writer_print_section_footer(wctx);
2135 2136
        writer_close(&wctx);
    }
2137

2138 2139
end:
    av_freep(&print_format);
2140 2141 2142 2143

    uninit_opts();
    av_dict_free(&fmt_entries_to_show);

2144 2145
    avformat_network_deinit();

2146
    return ret;
Stefano Sabatini's avatar
Stefano Sabatini committed
2147
}