ffprobe.c 40.1 KB
Newer Older
Stefano Sabatini's avatar
Stefano Sabatini committed
1
/*
2
 * ffprobe : Simple Media Prober based on the FFmpeg libraries
Stefano Sabatini's avatar
Stefano Sabatini committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 * 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
 */

22 23
#include "config.h"

Stefano Sabatini's avatar
Stefano Sabatini committed
24 25
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
26
#include "libavutil/avstring.h"
27
#include "libavutil/opt.h"
Stefano Sabatini's avatar
Stefano Sabatini committed
28
#include "libavutil/pixdesc.h"
29
#include "libavutil/dict.h"
30
#include "libavdevice/avdevice.h"
Stefano Sabatini's avatar
Stefano Sabatini committed
31 32
#include "cmdutils.h"

33
const char program_name[] = "ffprobe";
Stefano Sabatini's avatar
Stefano Sabatini committed
34 35 36
const int program_birth_year = 2007;

static int do_show_format  = 0;
37
static int do_show_packets = 0;
Stefano Sabatini's avatar
Stefano Sabatini committed
38 39 40 41 42 43 44
static int do_show_streams = 0;

static int show_value_unit              = 0;
static int use_value_prefix             = 0;
static int use_byte_value_binary_prefix = 0;
static int use_value_sexagesimal_format = 0;

45 46
static char *print_format;

Stefano Sabatini's avatar
Stefano Sabatini committed
47 48 49 50
static const OptionDef options[];

/* FFprobe context */
static const char *input_filename;
51
static AVInputFormat *iformat = NULL;
Stefano Sabatini's avatar
Stefano Sabatini committed
52 53 54 55 56 57 58 59 60

static const char *binary_unit_prefixes [] = { "", "Ki", "Mi", "Gi", "Ti", "Pi" };
static const char *decimal_unit_prefixes[] = { "", "K" , "M" , "G" , "T" , "P"  };

static const char *unit_second_str          = "s"    ;
static const char *unit_hertz_str           = "Hz"   ;
static const char *unit_byte_str            = "byte" ;
static const char *unit_bit_per_second_str  = "bit/s";

61
void av_noreturn exit_program(int ret)
62 63 64 65
{
    exit(ret);
}

66 67 68 69 70 71
struct unit_value {
    union { double d; int i; } val;
    const char *unit;
};

static char *value_string(char *buf, int buf_size, struct unit_value uv)
Stefano Sabatini's avatar
Stefano Sabatini committed
72
{
73 74 75 76 77 78 79 80 81 82 83
    double vald;
    int show_float = 0;

    if (uv.unit == unit_second_str) {
        vald = uv.val.d;
        show_float = 1;
    } else {
        vald = uv.val.i;
    }

    if (uv.unit == unit_second_str && use_value_sexagesimal_format) {
Stefano Sabatini's avatar
Stefano Sabatini committed
84 85
        double secs;
        int hours, mins;
86
        secs  = vald;
Stefano Sabatini's avatar
Stefano Sabatini committed
87 88 89 90 91 92 93
        mins  = (int)secs / 60;
        secs  = secs - mins * 60;
        hours = mins / 60;
        mins %= 60;
        snprintf(buf, buf_size, "%d:%02d:%09.6f", hours, mins, secs);
    } else if (use_value_prefix) {
        const char *prefix_string;
94
        int index, l;
Stefano Sabatini's avatar
Stefano Sabatini committed
95

96 97
        if (uv.unit == unit_byte_str && use_byte_value_binary_prefix) {
            index = (int) (log(vald)/log(2)) / 10;
Stefano Sabatini's avatar
Stefano Sabatini committed
98
            index = av_clip(index, 0, FF_ARRAY_ELEMS(binary_unit_prefixes) -1);
99
            vald /= pow(2, index*10);
Stefano Sabatini's avatar
Stefano Sabatini committed
100 101
            prefix_string = binary_unit_prefixes[index];
        } else {
102
            index = (int) (log10(vald)) / 3;
Stefano Sabatini's avatar
Stefano Sabatini committed
103
            index = av_clip(index, 0, FF_ARRAY_ELEMS(decimal_unit_prefixes) -1);
104
            vald /= pow(10, index*3);
Stefano Sabatini's avatar
Stefano Sabatini committed
105 106 107
            prefix_string = decimal_unit_prefixes[index];
        }

108 109 110 111
        if (show_float || vald != (int)vald) l = snprintf(buf, buf_size, "%.3f", vald);
        else                                 l = snprintf(buf, buf_size, "%d",   (int)vald);
        snprintf(buf+l, buf_size-l, "%s%s%s", prefix_string || show_value_unit ? " " : "",
                 prefix_string, show_value_unit ? uv.unit : "");
Stefano Sabatini's avatar
Stefano Sabatini committed
112
    } else {
113 114 115 116 117 118
        int l;

        if (show_float) l = snprintf(buf, buf_size, "%.3f", vald);
        else            l = snprintf(buf, buf_size, "%d",   (int)vald);
        snprintf(buf+l, buf_size-l, "%s%s", show_value_unit ? " " : "",
                 show_value_unit ? uv.unit : "");
Stefano Sabatini's avatar
Stefano Sabatini committed
119 120 121 122 123
    }

    return buf;
}

124
/* WRITERS API */
125

126
typedef struct WriterContext WriterContext;
127

128 129
#define WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS 1

130 131 132
typedef struct Writer {
    int priv_size;                  ///< private size for the writer context
    const char *name;
133

134 135 136 137 138 139 140 141 142 143
    int  (*init)  (WriterContext *wctx, const char *args, void *opaque);
    void (*uninit)(WriterContext *wctx);

    void (*print_header)(WriterContext *ctx);
    void (*print_footer)(WriterContext *ctx);

    void (*print_chapter_header)(WriterContext *wctx, const char *);
    void (*print_chapter_footer)(WriterContext *wctx, const char *);
    void (*print_section_header)(WriterContext *wctx, const char *);
    void (*print_section_footer)(WriterContext *wctx, const char *);
144
    void (*print_integer)       (WriterContext *wctx, const char *, long long int);
145 146
    void (*print_string)        (WriterContext *wctx, const char *, const char *);
    void (*show_tags)           (WriterContext *wctx, AVDictionary *dict);
147
    int flags;                  ///< a combination or WRITER_FLAG_*
148 149 150 151 152 153 154 155 156 157 158
} Writer;

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
    unsigned int nb_item;           ///< number of the item printed in the given section, starting at 0
    unsigned int nb_section;        ///< number of the section printed in the given section sequence, starting at 0
    unsigned int nb_chapter;        ///< number of the chapter, starting at 0
};
159

160 161 162 163 164 165 166 167 168 169 170 171 172
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,
};

173
static void writer_close(WriterContext **wctx)
174
{
175 176 177 178 179
    if (*wctx && (*wctx)->writer->uninit)
        (*wctx)->writer->uninit(*wctx);

    av_freep(&((*wctx)->priv));
    av_freep(wctx);
180 181
}

182 183
static int writer_open(WriterContext **wctx, const Writer *writer,
                       const char *args, void *opaque)
184
{
185
    int ret = 0;
186

187 188 189
    if (!(*wctx = av_malloc(sizeof(WriterContext)))) {
        ret = AVERROR(ENOMEM);
        goto fail;
190 191
    }

192 193 194
    if (!((*wctx)->priv = av_mallocz(writer->priv_size))) {
        ret = AVERROR(ENOMEM);
        goto fail;
195
    }
196

197
    (*wctx)->class = &writer_class;
198 199 200 201 202 203 204 205 206 207
    (*wctx)->writer = writer;
    if ((*wctx)->writer->init)
        ret = (*wctx)->writer->init(*wctx, args, opaque);
    if (ret < 0)
        goto fail;

    return 0;

fail:
    writer_close(wctx);
208 209 210
    return ret;
}

211
static inline void writer_print_header(WriterContext *wctx)
212
{
213 214 215
    if (wctx->writer->print_header)
        wctx->writer->print_header(wctx);
    wctx->nb_chapter = 0;
216 217
}

218
static inline void writer_print_footer(WriterContext *wctx)
219
{
220 221
    if (wctx->writer->print_footer)
        wctx->writer->print_footer(wctx);
222 223
}

224 225
static inline void writer_print_chapter_header(WriterContext *wctx,
                                               const char *header)
226
{
227 228 229
    if (wctx->writer->print_chapter_header)
        wctx->writer->print_chapter_header(wctx, header);
    wctx->nb_section = 0;
230 231
}

232 233
static inline void writer_print_chapter_footer(WriterContext *wctx,
                                               const char *footer)
234
{
235 236 237
    if (wctx->writer->print_chapter_footer)
        wctx->writer->print_chapter_footer(wctx, footer);
    wctx->nb_chapter++;
238 239
}

240 241
static inline void writer_print_section_header(WriterContext *wctx,
                                               const char *header)
242
{
243 244 245
    if (wctx->writer->print_section_header)
        wctx->writer->print_section_header(wctx, header);
    wctx->nb_item = 0;
246 247
}

248 249 250 251 252 253 254
static inline void writer_print_section_footer(WriterContext *wctx,
                                               const char *footer)
{
    if (wctx->writer->print_section_footer)
        wctx->writer->print_section_footer(wctx, footer);
    wctx->nb_section++;
}
255

256
static inline void writer_print_integer(WriterContext *wctx,
257
                                        const char *key, long long int val)
258
{
259 260
    wctx->writer->print_integer(wctx, key, val);
    wctx->nb_item++;
261 262
}

263
static inline void writer_print_string(WriterContext *wctx,
264
                                       const char *key, const char *val, int opt)
265
{
266 267
    if (opt && !(wctx->writer->flags & WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS))
        return;
268 269
    wctx->writer->print_string(wctx, key, val);
    wctx->nb_item++;
270 271
}

272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
static void writer_print_time(WriterContext *wctx, const char *key,
                              int64_t ts, const AVRational *time_base)
{
    char buf[128];

    if (ts == AV_NOPTS_VALUE) {
        writer_print_string(wctx, key, "N/A", 1);
    } else {
        double d = ts * av_q2d(*time_base);
        value_string(buf, sizeof(buf), (struct unit_value){.val.d=d, .unit=unit_second_str});
        writer_print_string(wctx, key, buf, 0);
    }
}

static void writer_print_ts(WriterContext *wctx, const char *key, int64_t ts)
{
    if (ts == AV_NOPTS_VALUE) {
        writer_print_string(wctx, key, "N/A", 1);
    } else {
291
        writer_print_integer(wctx, key, ts);
292 293 294
    }
}

295
static inline void writer_show_tags(WriterContext *wctx, AVDictionary *dict)
296
{
297
    wctx->writer->show_tags(wctx, dict);
298 299
}

300 301
#define MAX_REGISTERED_WRITERS_NB 64

302
static const Writer *registered_writers[MAX_REGISTERED_WRITERS_NB + 1];
303

304
static int writer_register(const Writer *writer)
305
{
306 307 308 309 310 311 312
    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;
313 314
}

315
static const Writer *writer_get_by_name(const char *name)
316 317 318 319 320 321 322 323 324
{
    int i;

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

    return NULL;
}
325 326 327

/* Print helpers */

328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364
struct print_buf {
    char *s;
    int len;
};

static char *fast_asprintf(struct print_buf *pbuf, const char *fmt, ...)
{
    va_list va;
    int len;

    va_start(va, fmt);
    len = vsnprintf(NULL, 0, fmt, va);
    va_end(va);
    if (len < 0)
        goto fail;

    if (pbuf->len < len) {
        char *p = av_realloc(pbuf->s, len + 1);
        if (!p)
            goto fail;
        pbuf->s   = p;
        pbuf->len = len;
    }

    va_start(va, fmt);
    len = vsnprintf(pbuf->s, len + 1, fmt, va);
    va_end(va);
    if (len < 0)
        goto fail;
    return pbuf->s;

fail:
    av_freep(&pbuf->s);
    pbuf->len = 0;
    return NULL;
}

365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389
#define ESCAPE_INIT_BUF_SIZE 256

#define ESCAPE_CHECK_SIZE(src, size, max_size)                          \
    if (size > max_size) {                                              \
        char buf[64];                                                   \
        snprintf(buf, sizeof(buf), "%s", src);                          \
        av_log(log_ctx, AV_LOG_WARNING,                                 \
               "String '%s...' with is too big\n", buf);                \
        return "FFPROBE_TOO_BIG_STRING";                                \
    }

#define ESCAPE_REALLOC_BUF(dst_size_p, dst_p, src, size)                \
    if (*dst_size_p < size) {                                           \
        char *q = av_realloc(*dst_p, size);                             \
        if (!q) {                                                       \
            char buf[64];                                               \
            snprintf(buf, sizeof(buf), "%s", src);                      \
            av_log(log_ctx, AV_LOG_WARNING,                             \
                   "String '%s...' could not be escaped\n", buf);       \
            return "FFPROBE_THIS_STRING_COULD_NOT_BE_ESCAPED";          \
        }                                                               \
        *dst_size_p = size;                                             \
        *dst = q;                                                       \
    }

390
/* WRITERS */
391

392 393 394 395 396 397 398 399 400 401 402 403 404
/* Default output */

static void default_print_footer(WriterContext *wctx)
{
    printf("\n");
}

static void default_print_chapter_header(WriterContext *wctx, const char *chapter)
{
    if (wctx->nb_chapter)
        printf("\n");
}

405 406 407 408 409 410 411 412 413 414
/* lame uppercasing routine, assumes the string is lower case ASCII */
static inline char *upcase_string(char *dst, size_t dst_size, const char *src)
{
    int i;
    for (i = 0; src[i] && i < dst_size-1; i++)
        dst[i] = src[i]-32;
    dst[i] = 0;
    return dst;
}

415 416
static void default_print_section_header(WriterContext *wctx, const char *section)
{
417 418
    char buf[32];

419 420
    if (wctx->nb_section)
        printf("\n");
421
    printf("[%s]\n", upcase_string(buf, sizeof(buf), section));
422 423 424 425
}

static void default_print_section_footer(WriterContext *wctx, const char *section)
{
426 427 428
    char buf[32];

    printf("[/%s]", upcase_string(buf, sizeof(buf), section));
429 430 431 432 433 434 435
}

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

436
static void default_print_int(WriterContext *wctx, const char *key, long long int value)
437
{
438
    printf("%s=%lld\n", key, value);
439 440 441 442 443 444 445
}

static void default_show_tags(WriterContext *wctx, AVDictionary *dict)
{
    AVDictionaryEntry *tag = NULL;
    while ((tag = av_dict_get(dict, "", tag, AV_DICT_IGNORE_SUFFIX))) {
        printf("TAG:");
446
        writer_print_string(wctx, tag->key, tag->value, 0);
447 448 449
    }
}

450
static const Writer default_writer = {
451 452 453 454 455 456 457
    .name                  = "default",
    .print_footer          = default_print_footer,
    .print_chapter_header  = default_print_chapter_header,
    .print_section_header  = default_print_section_header,
    .print_section_footer  = default_print_section_footer,
    .print_integer         = default_print_int,
    .print_string          = default_print_str,
458 459
    .show_tags             = default_show_tags,
    .flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS,
460 461
};

462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648
/* Compact output */

/**
 * Escape \n, \r, \\ and sep characters contained in s, and print the
 * resulting string.
 */
static const char *c_escape_str(char **dst, size_t *dst_size,
                                const char *src, const char sep, void *log_ctx)
{
    const char *p;
    char *q;
    size_t size = 1;

    /* precompute size */
    for (p = src; *p; p++, size++) {
        ESCAPE_CHECK_SIZE(src, size, SIZE_MAX-2);
        if (*p == '\n' || *p == '\r' || *p == '\\')
            size++;
    }

    ESCAPE_REALLOC_BUF(dst_size, dst, src, size);

    q = *dst;
    for (p = src; *p; p++) {
        switch (*src) {
        case '\n': *q++ = '\\'; *q++ = 'n';  break;
        case '\r': *q++ = '\\'; *q++ = 'r';  break;
        case '\\': *q++ = '\\'; *q++ = '\\'; break;
        default:
            if (*p == sep)
                *q++ = '\\';
            *q++ = *p;
        }
    }
    *q = 0;
    return *dst;
}

/**
 * Quote fields containing special characters, check RFC4180.
 */
static const char *csv_escape_str(char **dst, size_t *dst_size,
                                  const char *src, const char sep, void *log_ctx)
{
    const char *p;
    char *q;
    size_t size = 1;
    int quote = 0;

    /* precompute size */
    for (p = src; *p; p++, size++) {
        ESCAPE_CHECK_SIZE(src, size, SIZE_MAX-4);
        if (*p == '"' || *p == sep || *p == '\n' || *p == '\r')
            if (!quote) {
                quote = 1;
                size += 2;
            }
        if (*p == '"')
            size++;
    }

    ESCAPE_REALLOC_BUF(dst_size, dst, src, size);

    q = *dst;
    p = src;
    if (quote)
        *q++ = '\"';
    while (*p) {
        if (*p == '"')
            *q++ = '\"';
        *q++ = *p++;
    }
    if (quote)
        *q++ = '\"';
    *q = 0;

    return *dst;
}

static const char *none_escape_str(char **dst, size_t *dst_size,
                                   const char *src, const char sep, void *log_ctx)
{
    return src;
}

typedef struct CompactContext {
    const AVClass *class;
    char *item_sep_str;
    char item_sep;
    int nokey;
    char  *buf;
    size_t buf_size;
    char *escape_mode_str;
    const char * (*escape_str)(char **dst, size_t *dst_size,
                               const char *src, const char sep, void *log_ctx);
} CompactContext;

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

static const AVOption compact_options[]= {
    {"item_sep", "set item separator",    OFFSET(item_sep_str),    AV_OPT_TYPE_STRING, {.str="|"},  CHAR_MIN, CHAR_MAX },
    {"s",        "set item separator",    OFFSET(item_sep_str),    AV_OPT_TYPE_STRING, {.str="|"},  CHAR_MIN, CHAR_MAX },
    {"nokey",    "force no key printing", OFFSET(nokey),           AV_OPT_TYPE_INT,    {.dbl=0},    0,        1        },
    {"nk",       "force no key printing", OFFSET(nokey),           AV_OPT_TYPE_INT,    {.dbl=0},    0,        1        },
    {"escape",   "set escape mode",       OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="c"},  CHAR_MIN, CHAR_MAX },
    {"e",        "set escape mode",       OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="c"},  CHAR_MIN, CHAR_MAX },
    {NULL},
};

static const char *compact_get_name(void *ctx)
{
    return "compact";
}

static const AVClass compact_class = {
    "CompactContext",
    compact_get_name,
    compact_options
};

static av_cold int compact_init(WriterContext *wctx, const char *args, void *opaque)
{
    CompactContext *compact = wctx->priv;
    int err;

    compact->class = &compact_class;
    av_opt_set_defaults(compact);

    if (args &&
        (err = (av_set_options_string(compact, args, "=", ":"))) < 0) {
        av_log(wctx, AV_LOG_ERROR, "Error parsing options string: '%s'\n", args);
        return err;
    }
    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];

    compact->buf_size = ESCAPE_INIT_BUF_SIZE;
    if (!(compact->buf = av_malloc(compact->buf_size)))
        return AVERROR(ENOMEM);

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

    return 0;
}

static av_cold void compact_uninit(WriterContext *wctx)
{
    CompactContext *compact = wctx->priv;

    av_freep(&compact->item_sep_str);
    av_freep(&compact->buf);
    av_freep(&compact->escape_mode_str);
}

static void compact_print_section_header(WriterContext *wctx, const char *section)
{
    CompactContext *compact = wctx->priv;

    printf("%s%c", section, compact->item_sep);
}

static void compact_print_section_footer(WriterContext *wctx, const char *section)
{
    printf("\n");
}

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

    if (wctx->nb_item) printf("%c", compact->item_sep);
    if (!compact->nokey)
        printf("%s=", key);
    printf("%s", compact->escape_str(&compact->buf, &compact->buf_size,
                                     value, compact->item_sep, wctx));
}

649
static void compact_print_int(WriterContext *wctx, const char *key, long long int value)
650 651 652 653 654 655
{
    CompactContext *compact = wctx->priv;

    if (wctx->nb_item) printf("%c", compact->item_sep);
    if (!compact->nokey)
        printf("%s=", key);
656
    printf("%lld", value);
657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673
}

static void compact_show_tags(WriterContext *wctx, AVDictionary *dict)
{
    CompactContext *compact = wctx->priv;
    AVDictionaryEntry *tag = NULL;

    while ((tag = av_dict_get(dict, "", tag, AV_DICT_IGNORE_SUFFIX))) {
        if (wctx->nb_item) printf("%c", compact->item_sep);
        if (!compact->nokey)
            printf("tag:%s=", compact->escape_str(&compact->buf, &compact->buf_size,
                                                  tag->key, compact->item_sep, wctx));
        printf("%s", compact->escape_str(&compact->buf, &compact->buf_size,
                                         tag->value, compact->item_sep, wctx));
    }
}

674
static const Writer compact_writer = {
675 676 677 678 679 680 681 682 683 684
    .name                 = "compact",
    .priv_size            = sizeof(CompactContext),
    .init                 = compact_init,
    .uninit               = compact_uninit,
    .print_section_header = compact_print_section_header,
    .print_section_footer = compact_print_section_footer,
    .print_integer        = compact_print_int,
    .print_string         = compact_print_str,
    .show_tags            = compact_show_tags,
    .flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS,
685 686
};

687 688 689 690 691 692 693
/* CSV output */

static av_cold int csv_init(WriterContext *wctx, const char *args, void *opaque)
{
    return compact_init(wctx, "item_sep=,:nokey=1:escape=csv", opaque);
}

694
static const Writer csv_writer = {
695 696 697 698 699 700 701 702 703 704 705 706
    .name                 = "csv",
    .priv_size            = sizeof(CompactContext),
    .init                 = csv_init,
    .uninit               = compact_uninit,
    .print_section_header = compact_print_section_header,
    .print_section_footer = compact_print_section_footer,
    .print_integer        = compact_print_int,
    .print_string         = compact_print_str,
    .show_tags            = compact_show_tags,
    .flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS,
};

707 708 709 710
/* JSON output */

typedef struct {
    int multiple_entries; ///< tells if the given chapter requires multiple entries
711 712
    char *buf;
    size_t buf_size;
713 714
} JSONContext;

715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733
static av_cold int json_init(WriterContext *wctx, const char *args, void *opaque)
{
    JSONContext *json = wctx->priv;

    json->buf_size = ESCAPE_INIT_BUF_SIZE;
    if (!(json->buf = av_malloc(json->buf_size)))
        return AVERROR(ENOMEM);

    return 0;
}

static av_cold void json_uninit(WriterContext *wctx)
{
    JSONContext *json = wctx->priv;
    av_freep(&json->buf);
}

static const char *json_escape_str(char **dst, size_t *dst_size, const char *src,
                                   void *log_ctx)
734 735 736
{
    static const char json_escape[] = {'"', '\\', '\b', '\f', '\n', '\r', '\t', 0};
    static const char json_subst[]  = {'"', '\\',  'b',  'f',  'n',  'r',  't', 0};
737 738 739
    const char *p;
    char *q;
    size_t size = 1;
740 741

    // compute the length of the escaped string
742 743 744 745 746
    for (p = src; *p; p++) {
        ESCAPE_CHECK_SIZE(src, size, SIZE_MAX-6);
        if (strchr(json_escape, *p))     size += 2; // simple escape
        else if ((unsigned char)*p < 32) size += 6; // handle non-printable chars
        else                             size += 1; // char copy
747
    }
748 749 750 751 752 753 754 755 756 757 758
    ESCAPE_REALLOC_BUF(dst_size, dst, src, size);

    q = *dst;
    for (p = src; *p; p++) {
        char *s = strchr(json_escape, *p);
        if (s) {
            *q++ = '\\';
            *q++ = json_subst[s - json_escape];
        } else if ((unsigned char)*p < 32) {
            snprintf(q, 7, "\\u00%02x", *p & 0xff);
            q += 6;
759
        } else {
760
            *q++ = *p;
761 762
        }
    }
763 764
    *q = 0;
    return *dst;
765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783
}

static void json_print_header(WriterContext *wctx)
{
    printf("{");
}

static void json_print_footer(WriterContext *wctx)
{
    printf("\n}\n");
}

static void json_print_chapter_header(WriterContext *wctx, const char *chapter)
{
    JSONContext *json = wctx->priv;

    if (wctx->nb_chapter)
        printf(",");
    json->multiple_entries = !strcmp(chapter, "packets") || !strcmp(chapter, "streams");
784
    printf("\n  \"%s\":%s", json_escape_str(&json->buf, &json->buf_size, chapter, wctx),
785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810
           json->multiple_entries ? " [" : " ");
}

static void json_print_chapter_footer(WriterContext *wctx, const char *chapter)
{
    JSONContext *json = wctx->priv;

    if (json->multiple_entries)
        printf("]");
}

static void json_print_section_header(WriterContext *wctx, const char *section)
{
    if (wctx->nb_section) printf(",");
    printf("{\n");
}

static void json_print_section_footer(WriterContext *wctx, const char *section)
{
    printf("\n  }");
}

static inline void json_print_item_str(WriterContext *wctx,
                                       const char *key, const char *value,
                                       const char *indent)
{
811
    JSONContext *json = wctx->priv;
812

813 814
    printf("%s\"%s\":", indent, json_escape_str(&json->buf, &json->buf_size, key,   wctx));
    printf(" \"%s\"",           json_escape_str(&json->buf, &json->buf_size, value, wctx));
815 816 817 818 819 820 821 822 823 824
}

#define INDENT "    "

static void json_print_str(WriterContext *wctx, const char *key, const char *value)
{
    if (wctx->nb_item) printf(",\n");
    json_print_item_str(wctx, key, value, INDENT);
}

825
static void json_print_int(WriterContext *wctx, const char *key, long long int value)
826
{
827
    JSONContext *json = wctx->priv;
828

829
    if (wctx->nb_item) printf(",\n");
830
    printf(INDENT "\"%s\": %lld",
831
           json_escape_str(&json->buf, &json->buf_size, key, wctx), value);
832
}
833

834 835 836 837 838 839 840 841 842 843 844 845 846 847 848
static void json_show_tags(WriterContext *wctx, AVDictionary *dict)
{
    AVDictionaryEntry *tag = NULL;
    int is_first = 1;
    if (!dict)
        return;
    printf(",\n" INDENT "\"tags\": {\n");
    while ((tag = av_dict_get(dict, "", tag, AV_DICT_IGNORE_SUFFIX))) {
        if (is_first) is_first = 0;
        else          printf(",\n");
        json_print_item_str(wctx, tag->key, tag->value, INDENT INDENT);
    }
    printf("\n    }");
}

849
static const Writer json_writer = {
850 851
    .name                 = "json",
    .priv_size            = sizeof(JSONContext),
852 853
    .init                 = json_init,
    .uninit               = json_uninit,
854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873
    .print_header         = json_print_header,
    .print_footer         = json_print_footer,
    .print_chapter_header = json_print_chapter_header,
    .print_chapter_footer = json_print_chapter_footer,
    .print_section_header = json_print_section_header,
    .print_section_footer = json_print_section_footer,
    .print_integer        = json_print_int,
    .print_string         = json_print_str,
    .show_tags            = json_show_tags,
};

static void writer_register_all(void)
{
    static int initialized;

    if (initialized)
        return;
    initialized = 1;

    writer_register(&default_writer);
874
    writer_register(&compact_writer);
875
    writer_register(&csv_writer);
876 877 878 879 880
    writer_register(&json_writer);
}

#define print_fmt(k, f, ...) do {              \
    if (fast_asprintf(&pbuf, f, __VA_ARGS__))  \
881 882 883 884 885 886
        writer_print_string(w, k, pbuf.s, 0);  \
} while (0)

#define print_fmt_opt(k, f, ...) do {          \
    if (fast_asprintf(&pbuf, f, __VA_ARGS__))  \
        writer_print_string(w, k, pbuf.s, 1);  \
887 888 889
} while (0)

#define print_int(k, v)         writer_print_integer(w, k, v)
890 891 892 893 894
#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)
#define print_time(k, v, tb)    writer_print_time(w, k, v, tb)
#define print_ts(k, v)          writer_print_ts(w, k, v)
#define print_val(k, v, u)      writer_print_string(w, k, \
895
    value_string(val_str, sizeof(val_str), (struct unit_value){.val.i = v, .unit=u}), 0)
896 897 898 899 900
#define print_section_header(s) writer_print_section_header(w, s)
#define print_section_footer(s) writer_print_section_footer(w, s)
#define show_tags(metadata)     writer_show_tags(w, metadata)

static void show_packet(WriterContext *w, AVFormatContext *fmt_ctx, AVPacket *pkt, int packet_idx)
901 902 903
{
    char val_str[128];
    AVStream *st = fmt_ctx->streams[pkt->stream_index];
904
    struct print_buf pbuf = {.s = NULL};
905
    const char *s;
906

907
    print_section_header("packet");
908 909 910
    s = av_get_media_type_string(st->codec->codec_type);
    if (s) print_str    ("codec_type", s);
    else   print_str_opt("codec_type", "unknown");
911
    print_int("stream_index",     pkt->stream_index);
912 913 914 915 916 917
    print_ts  ("pts",             pkt->pts);
    print_time("pts_time",        pkt->pts, &st->time_base);
    print_ts  ("dts",             pkt->dts);
    print_time("dts_time",        pkt->dts, &st->time_base);
    print_ts  ("duration",        pkt->duration);
    print_time("duration_time",   pkt->duration, &st->time_base);
918
    print_val("size",             pkt->size, unit_byte_str);
919 920
    if (pkt->pos != -1) print_fmt    ("pos", "%"PRId64, pkt->pos);
    else                print_str_opt("pos", "N/A");
921
    print_fmt("flags", "%c",      pkt->flags & AV_PKT_FLAG_KEY ? 'K' : '_');
922
    print_section_footer("packet");
923

924
    av_free(pbuf.s);
925
    fflush(stdout);
926 927
}

928
static void show_packets(WriterContext *w, AVFormatContext *fmt_ctx)
929 930
{
    AVPacket pkt;
931
    int i = 0;
932 933 934 935

    av_init_packet(&pkt);

    while (!av_read_frame(fmt_ctx, &pkt))
936
        show_packet(w, fmt_ctx, &pkt, i++);
937 938
}

939
static void show_stream(WriterContext *w, AVFormatContext *fmt_ctx, int stream_idx)
Stefano Sabatini's avatar
Stefano Sabatini committed
940 941 942 943 944
{
    AVStream *stream = fmt_ctx->streams[stream_idx];
    AVCodecContext *dec_ctx;
    AVCodec *dec;
    char val_str[128];
945
    const char *s;
946
    AVRational display_aspect_ratio;
947
    struct print_buf pbuf = {.s = NULL};
Stefano Sabatini's avatar
Stefano Sabatini committed
948

949
    print_section_header("stream");
Stefano Sabatini's avatar
Stefano Sabatini committed
950

951
    print_int("index", stream->index);
Stefano Sabatini's avatar
Stefano Sabatini committed
952 953 954

    if ((dec_ctx = stream->codec)) {
        if ((dec = dec_ctx->codec)) {
955 956
            print_str("codec_name",      dec->name);
            print_str("codec_long_name", dec->long_name);
Stefano Sabatini's avatar
Stefano Sabatini committed
957
        } else {
958 959
            print_str_opt("codec_name",      "unknown");
            print_str_opt("codec_long_name", "unknown");
Stefano Sabatini's avatar
Stefano Sabatini committed
960 961
        }

962 963 964
        s = av_get_media_type_string(dec_ctx->codec_type);
        if (s) print_str    ("codec_type", s);
        else   print_str_opt("codec_type", "unknown");
965
        print_fmt("codec_time_base", "%d/%d", dec_ctx->time_base.num, dec_ctx->time_base.den);
Stefano Sabatini's avatar
Stefano Sabatini committed
966 967

        /* print AVI/FourCC tag */
968
        av_get_codec_tag_string(val_str, sizeof(val_str), dec_ctx->codec_tag);
969 970
        print_str("codec_tag_string",    val_str);
        print_fmt("codec_tag", "0x%04x", dec_ctx->codec_tag);
Stefano Sabatini's avatar
Stefano Sabatini committed
971 972

        switch (dec_ctx->codec_type) {
973
        case AVMEDIA_TYPE_VIDEO:
974 975 976
            print_int("width",        dec_ctx->width);
            print_int("height",       dec_ctx->height);
            print_int("has_b_frames", dec_ctx->has_b_frames);
977
            if (dec_ctx->sample_aspect_ratio.num) {
978 979 980
                print_fmt("sample_aspect_ratio", "%d:%d",
                          dec_ctx->sample_aspect_ratio.num,
                          dec_ctx->sample_aspect_ratio.den);
981 982 983 984
                av_reduce(&display_aspect_ratio.num, &display_aspect_ratio.den,
                          dec_ctx->width  * dec_ctx->sample_aspect_ratio.num,
                          dec_ctx->height * dec_ctx->sample_aspect_ratio.den,
                          1024*1024);
985 986 987
                print_fmt("display_aspect_ratio", "%d:%d",
                          display_aspect_ratio.num,
                          display_aspect_ratio.den);
988 989 990
            } else {
                print_str_opt("sample_aspect_ratio", "N/A");
                print_str_opt("display_aspect_ratio", "N/A");
991
            }
992 993 994
            s = av_get_pix_fmt_name(dec_ctx->pix_fmt);
            if (s) print_str    ("pix_fmt", s);
            else   print_str_opt("pix_fmt", "unknown");
995
            print_int("level",   dec_ctx->level);
996 997 998 999 1000 1001 1002 1003 1004 1005 1006
            if (dec_ctx->timecode_frame_start >= 0) {
                uint32_t tc = dec_ctx->timecode_frame_start;
                print_fmt("timecode", "%02d:%02d:%02d%c%02d",
                          tc>>19 & 0x1f,              // hours
                          tc>>13 & 0x3f,              // minutes
                          tc>>6  & 0x3f,              // seconds
                          tc     & 1<<24 ? ';' : ':', // drop
                          tc     & 0x3f);             // frames
            } else {
                print_str_opt("timecode", "N/A");
            }
Stefano Sabatini's avatar
Stefano Sabatini committed
1007 1008
            break;

1009
        case AVMEDIA_TYPE_AUDIO:
1010 1011 1012
            s = av_get_sample_fmt_name(dec_ctx->sample_fmt);
            if (s) print_str    ("sample_fmt", s);
            else   print_str_opt("sample_fmt", "unknown");
1013
            print_val("sample_rate",     dec_ctx->sample_rate, unit_hertz_str);
1014 1015
            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
1016 1017 1018
            break;
        }
    } else {
1019
        print_str_opt("codec_type", "unknown");
Stefano Sabatini's avatar
Stefano Sabatini committed
1020
    }
1021
    if (dec_ctx->codec && dec_ctx->codec->priv_class) {
1022
        const AVOption *opt = NULL;
1023 1024 1025 1026 1027 1028 1029 1030 1031
        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
1032

1033 1034
    if (fmt_ctx->iformat->flags & AVFMT_SHOW_IDS) print_fmt    ("id", "0x%x", stream->id);
    else                                          print_str_opt("id", "N/A");
1035 1036 1037
    print_fmt("r_frame_rate",   "%d/%d", stream->r_frame_rate.num,   stream->r_frame_rate.den);
    print_fmt("avg_frame_rate", "%d/%d", stream->avg_frame_rate.num, stream->avg_frame_rate.den);
    print_fmt("time_base",      "%d/%d", stream->time_base.num,      stream->time_base.den);
1038 1039
    print_time("start_time",    stream->start_time, &stream->time_base);
    print_time("duration",      stream->duration,   &stream->time_base);
1040 1041
    if (stream->nb_frames) print_fmt    ("nb_frames", "%"PRId64, stream->nb_frames);
    else                   print_str_opt("nb_frames", "N/A");
1042
    show_tags(stream->metadata);
Stefano Sabatini's avatar
Stefano Sabatini committed
1043

1044
    print_section_footer("stream");
1045
    av_free(pbuf.s);
1046
    fflush(stdout);
Stefano Sabatini's avatar
Stefano Sabatini committed
1047 1048
}

1049
static void show_streams(WriterContext *w, AVFormatContext *fmt_ctx)
1050 1051 1052 1053 1054 1055
{
    int i;
    for (i = 0; i < fmt_ctx->nb_streams; i++)
        show_stream(w, fmt_ctx, i);
}

1056
static void show_format(WriterContext *w, AVFormatContext *fmt_ctx)
Stefano Sabatini's avatar
Stefano Sabatini committed
1057 1058
{
    char val_str[128];
1059
    int64_t size = avio_size(fmt_ctx->pb);
1060
    struct print_buf pbuf = {.s = NULL};
Stefano Sabatini's avatar
Stefano Sabatini committed
1061

1062
    print_section_header("format");
1063
    print_str("filename",         fmt_ctx->filename);
1064 1065 1066
    print_int("nb_streams",       fmt_ctx->nb_streams);
    print_str("format_name",      fmt_ctx->iformat->name);
    print_str("format_long_name", fmt_ctx->iformat->long_name);
1067 1068
    print_time("start_time",      fmt_ctx->start_time, &AV_TIME_BASE_Q);
    print_time("duration",        fmt_ctx->duration,   &AV_TIME_BASE_Q);
1069 1070 1071 1072
    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");
1073
    show_tags(fmt_ctx->metadata);
1074
    print_section_footer("format");
1075
    av_free(pbuf.s);
1076
    fflush(stdout);
Stefano Sabatini's avatar
Stefano Sabatini committed
1077 1078 1079 1080 1081
}

static int open_input_file(AVFormatContext **fmt_ctx_ptr, const char *filename)
{
    int err, i;
1082 1083
    AVFormatContext *fmt_ctx = NULL;
    AVDictionaryEntry *t;
Stefano Sabatini's avatar
Stefano Sabatini committed
1084

1085
    if ((err = avformat_open_input(&fmt_ctx, filename, iformat, &format_opts)) < 0) {
Stefano Sabatini's avatar
Stefano Sabatini committed
1086 1087 1088
        print_error(filename, err);
        return err;
    }
1089 1090 1091 1092 1093
    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
1094 1095

    /* fill the streams in the format context */
1096
    if ((err = avformat_find_stream_info(fmt_ctx, NULL)) < 0) {
Stefano Sabatini's avatar
Stefano Sabatini committed
1097 1098 1099 1100
        print_error(filename, err);
        return err;
    }

1101
    av_dump_format(fmt_ctx, 0, filename, 0);
Stefano Sabatini's avatar
Stefano Sabatini committed
1102 1103 1104 1105 1106 1107 1108

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

        if (!(codec = avcodec_find_decoder(stream->codec->codec_id))) {
1109
            fprintf(stderr, "Unsupported codec with id %d for input stream %d\n",
Stefano Sabatini's avatar
Stefano Sabatini committed
1110
                    stream->codec->codec_id, stream->index);
1111
        } else if (avcodec_open2(stream->codec, codec, NULL) < 0) {
Stefano Sabatini's avatar
Stefano Sabatini committed
1112 1113 1114 1115 1116 1117 1118 1119 1120
            fprintf(stderr, "Error while opening codec for input stream %d\n",
                    stream->index);
        }
    }

    *fmt_ctx_ptr = fmt_ctx;
    return 0;
}

1121 1122 1123 1124 1125 1126
#define PRINT_CHAPTER(name) do {                                        \
    if (do_show_ ## name) {                                             \
        writer_print_chapter_header(wctx, #name);                       \
        show_ ## name (wctx, fmt_ctx);                                  \
        writer_print_chapter_footer(wctx, #name);                       \
    }                                                                   \
1127 1128
} while (0)

Stefano Sabatini's avatar
Stefano Sabatini committed
1129 1130 1131
static int probe_file(const char *filename)
{
    AVFormatContext *fmt_ctx;
1132
    int ret;
1133
    const Writer *w;
1134 1135
    char *buf;
    char *w_name = NULL, *w_args = NULL;
1136 1137 1138
    WriterContext *wctx;

    writer_register_all();
1139

1140 1141
    if (!print_format)
        print_format = av_strdup("default");
1142
    w_name = av_strtok(print_format, "=", &buf);
1143
    w_args = buf;
1144

1145
    w = writer_get_by_name(w_name);
1146
    if (!w) {
1147
        av_log(NULL, AV_LOG_ERROR, "Unknown output format with name '%s'\n", w_name);
1148 1149 1150
        ret = AVERROR(EINVAL);
        goto end;
    }
Stefano Sabatini's avatar
Stefano Sabatini committed
1151

1152 1153
    if ((ret = writer_open(&wctx, w, w_args, NULL)) < 0)
        goto end;
1154
    if ((ret = open_input_file(&fmt_ctx, filename)))
1155
        goto end;
Stefano Sabatini's avatar
Stefano Sabatini committed
1156

1157 1158 1159 1160 1161
    writer_print_header(wctx);
    PRINT_CHAPTER(packets);
    PRINT_CHAPTER(streams);
    PRINT_CHAPTER(format);
    writer_print_footer(wctx);
Stefano Sabatini's avatar
Stefano Sabatini committed
1162

1163
    avformat_close_input(&fmt_ctx);
1164 1165
    writer_close(&wctx);

1166
end:
1167
    av_freep(&print_format);
1168 1169

    return ret;
Stefano Sabatini's avatar
Stefano Sabatini committed
1170 1171 1172 1173 1174
}

static void show_usage(void)
{
    printf("Simple multimedia streams analyzer\n");
1175
    printf("usage: %s [OPTIONS] [INPUT_FILE]\n", program_name);
Stefano Sabatini's avatar
Stefano Sabatini committed
1176 1177 1178
    printf("\n");
}

1179
static int opt_format(const char *opt, const char *arg)
1180 1181 1182 1183
{
    iformat = av_find_input_format(arg);
    if (!iformat) {
        fprintf(stderr, "Unknown input format: %s\n", arg);
1184
        return AVERROR(EINVAL);
1185
    }
1186
    return 0;
1187 1188
}

1189
static void opt_input_file(void *optctx, const char *arg)
Stefano Sabatini's avatar
Stefano Sabatini committed
1190
{
1191
    if (input_filename) {
1192 1193
        fprintf(stderr, "Argument '%s' provided as input filename, but '%s' was already specified.\n",
                arg, input_filename);
1194 1195
        exit(1);
    }
1196 1197 1198
    if (!strcmp(arg, "-"))
        arg = "pipe:";
    input_filename = arg;
Stefano Sabatini's avatar
Stefano Sabatini committed
1199 1200
}

1201
static int opt_help(const char *opt, const char *arg)
Stefano Sabatini's avatar
Stefano Sabatini committed
1202
{
1203
    av_log_set_callback(log_callback_help);
Stefano Sabatini's avatar
Stefano Sabatini committed
1204 1205 1206
    show_usage();
    show_help_options(options, "Main options:\n", 0, 0);
    printf("\n");
1207 1208 1209

    show_help_children(avformat_get_class(), AV_OPT_FLAG_DECODING_PARAM);

1210
    return 0;
Stefano Sabatini's avatar
Stefano Sabatini committed
1211 1212
}

1213
static int opt_pretty(const char *opt, const char *arg)
Stefano Sabatini's avatar
Stefano Sabatini committed
1214 1215 1216 1217 1218
{
    show_value_unit              = 1;
    use_value_prefix             = 1;
    use_byte_value_binary_prefix = 1;
    use_value_sexagesimal_format = 1;
1219
    return 0;
Stefano Sabatini's avatar
Stefano Sabatini committed
1220 1221 1222 1223
}

static const OptionDef options[] = {
#include "cmdutils_common_opts.h"
1224
    { "f", HAS_ARG, {(void*)opt_format}, "force format", "format" },
1225 1226
    { "unit", OPT_BOOL, {(void*)&show_value_unit}, "show unit of the displayed values" },
    { "prefix", OPT_BOOL, {(void*)&use_value_prefix}, "use SI prefixes for the displayed values" },
Stefano Sabatini's avatar
Stefano Sabatini committed
1227 1228 1229 1230 1231 1232
    { "byte_binary_prefix", OPT_BOOL, {(void*)&use_byte_value_binary_prefix},
      "use binary prefixes for byte units" },
    { "sexagesimal", OPT_BOOL,  {(void*)&use_value_sexagesimal_format},
      "use sexagesimal format HOURS:MM:SS.MICROSECONDS for time units" },
    { "pretty", 0, {(void*)&opt_pretty},
      "prettify the format of displayed values, make it more human readable" },
1233 1234
    { "print_format", OPT_STRING | HAS_ARG, {(void*)&print_format},
      "set the output printing format (available formats are: default, compact, csv, json)", "format" },
Stefano Sabatini's avatar
Stefano Sabatini committed
1235
    { "show_format",  OPT_BOOL, {(void*)&do_show_format} , "show format/container info" },
1236
    { "show_packets", OPT_BOOL, {(void*)&do_show_packets}, "show packets info" },
1237
    { "show_streams", OPT_BOOL, {(void*)&do_show_streams}, "show streams info" },
1238
    { "default", HAS_ARG | OPT_AUDIO | OPT_VIDEO | OPT_EXPERT, {(void*)opt_default}, "generic catch all option", "" },
1239
    { "i", HAS_ARG, {(void *)opt_input_file}, "read specified file", "input_file"},
Stefano Sabatini's avatar
Stefano Sabatini committed
1240 1241 1242 1243 1244
    { NULL, },
};

int main(int argc, char **argv)
{
1245 1246
    int ret;

1247
    parse_loglevel(argc, argv, options);
Stefano Sabatini's avatar
Stefano Sabatini committed
1248
    av_register_all();
1249
    avformat_network_init();
1250
    init_opts();
1251 1252 1253
#if CONFIG_AVDEVICE
    avdevice_register_all();
#endif
Stefano Sabatini's avatar
Stefano Sabatini committed
1254 1255

    show_banner();
1256
    parse_options(NULL, argc, argv, options, opt_input_file);
Stefano Sabatini's avatar
Stefano Sabatini committed
1257 1258 1259 1260

    if (!input_filename) {
        show_usage();
        fprintf(stderr, "You have to specify one input file.\n");
1261
        fprintf(stderr, "Use -h to get full help or, even better, run 'man %s'.\n", program_name);
Stefano Sabatini's avatar
Stefano Sabatini committed
1262 1263 1264
        exit(1);
    }

1265 1266
    ret = probe_file(input_filename);

1267 1268
    avformat_network_deinit();

1269
    return ret;
Stefano Sabatini's avatar
Stefano Sabatini committed
1270
}