Newer
Older
/*
* 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
*/
/**
* @file
* simple media prober based on the FFmpeg libraries
*/
#include "version.h"
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
#include "libavutil/avstring.h"
#include "libavutil/dict.h"
#include "libavutil/timecode.h"
#include "libavdevice/avdevice.h"
#include "libswscale/swscale.h"
#include "libswresample/swresample.h"
#include "libpostproc/postprocess.h"
static int do_count_frames = 0;
static int do_count_packets = 0;
static int do_read_frames = 0;
static int do_read_packets = 0;
static int do_show_error = 0;
static int do_show_frames = 0;
static int do_show_program_version = 0;
static int do_show_library_versions = 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;
static int show_private_data = 1;
static const OptionDef options[];
/* FFprobe context */
static const char *input_filename;
static AVInputFormat *iformat = NULL;
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";
static uint64_t *nb_streams_packets;
static uint64_t *nb_streams_frames;
{
exit(ret);
}
struct unit_value {
union { double d; long long int i; } val;
const char *unit;
};
static char *value_string(char *buf, int buf_size, struct unit_value uv)
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) {
secs = vald;
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 {
const char *prefix_string = "";
Stefano Sabatini
committed
if (use_value_prefix && vald > 1) {
long long int index;
if (uv.unit == unit_byte_str && use_byte_value_binary_prefix) {
index = (long long int) (log(vald)/log(2)) / 10;
index = av_clip(index, 0, FF_ARRAY_ELEMS(binary_unit_prefixes) - 1);
vald /= pow(2, index * 10);
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];
}
if (show_float || (use_value_prefix && vald != (long long int)vald))
l = snprintf(buf, buf_size, "%f", vald);
else
l = snprintf(buf, buf_size, "%lld", (long long int)vald);
snprintf(buf+l, buf_size-l, "%s%s%s", *prefix_string || show_value_unit ? " " : "",
prefix_string, show_value_unit ? uv.unit : "");
/* WRITERS API */
typedef struct WriterContext WriterContext;
#define WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS 1
#define WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER 2
typedef struct Writer {
int priv_size; ///< private size for the writer context
const char *name;
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 *);
void (*print_integer) (WriterContext *wctx, const char *, long long int);
void (*print_string) (WriterContext *wctx, const char *, const char *);
void (*show_tags) (WriterContext *wctx, AVDictionary *dict);
int flags; ///< a combination or WRITER_FLAG_*
} 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
};
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,
};
static void writer_close(WriterContext **wctx)
if (!*wctx)
return;
if ((*wctx)->writer->uninit)
(*wctx)->writer->uninit(*wctx);
av_freep(&((*wctx)->priv));
av_freep(wctx);
static int writer_open(WriterContext **wctx, const Writer *writer,
const char *args, void *opaque)
int ret = 0;
if (!(*wctx = av_malloc(sizeof(WriterContext)))) {
ret = AVERROR(ENOMEM);
goto fail;
if (!((*wctx)->priv = av_mallocz(writer->priv_size))) {
ret = AVERROR(ENOMEM);
goto fail;
(*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);
static inline void writer_print_header(WriterContext *wctx)
if (wctx->writer->print_header)
wctx->writer->print_header(wctx);
wctx->nb_chapter = 0;
static inline void writer_print_footer(WriterContext *wctx)
if (wctx->writer->print_footer)
wctx->writer->print_footer(wctx);
static inline void writer_print_chapter_header(WriterContext *wctx,
Stefano Sabatini
committed
const char *chapter)
if (wctx->writer->print_chapter_header)
Stefano Sabatini
committed
wctx->writer->print_chapter_header(wctx, chapter);
wctx->nb_section = 0;
static inline void writer_print_chapter_footer(WriterContext *wctx,
Stefano Sabatini
committed
const char *chapter)
if (wctx->writer->print_chapter_footer)
Stefano Sabatini
committed
wctx->writer->print_chapter_footer(wctx, chapter);
wctx->nb_chapter++;
static inline void writer_print_section_header(WriterContext *wctx,
Stefano Sabatini
committed
const char *section)
if (wctx->writer->print_section_header)
Stefano Sabatini
committed
wctx->writer->print_section_header(wctx, section);
wctx->nb_item = 0;
static inline void writer_print_section_footer(WriterContext *wctx,
Stefano Sabatini
committed
const char *section)
{
if (wctx->writer->print_section_footer)
Stefano Sabatini
committed
wctx->writer->print_section_footer(wctx, section);
wctx->nb_section++;
}
static inline void writer_print_integer(WriterContext *wctx,
const char *key, long long int val)
wctx->writer->print_integer(wctx, key, val);
wctx->nb_item++;
static inline void writer_print_string(WriterContext *wctx,
const char *key, const char *val, int opt)
if (opt && !(wctx->writer->flags & WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS))
return;
wctx->writer->print_string(wctx, key, val);
wctx->nb_item++;
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 {
writer_print_integer(wctx, key, ts);
}
}
static inline void writer_show_tags(WriterContext *wctx, AVDictionary *dict)
wctx->writer->show_tags(wctx, dict);
#define MAX_REGISTERED_WRITERS_NB 64
static const Writer *registered_writers[MAX_REGISTERED_WRITERS_NB + 1];
static int writer_register(const Writer *writer)
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;
static const Writer *writer_get_by_name(const char *name)
{
int i;
for (i = 0; registered_writers[i]; i++)
if (!strcmp(registered_writers[i]->name, name))
return registered_writers[i];
return NULL;
}
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
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;
}
#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...' 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; \
}
/* WRITERS */
/* 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");
}
/* 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] = av_toupper(src[i]);
dst[i] = 0;
return dst;
}
static void default_print_section_header(WriterContext *wctx, const char *section)
{
char buf[32];
if (wctx->nb_section)
printf("\n");
printf("[%s]\n", upcase_string(buf, sizeof(buf), section));
}
static void default_print_section_footer(WriterContext *wctx, const char *section)
{
char buf[32];
printf("[/%s]", upcase_string(buf, sizeof(buf), section));
}
static void default_print_str(WriterContext *wctx, const char *key, const char *value)
{
printf("%s=%s\n", key, value);
}
static void default_print_int(WriterContext *wctx, const char *key, long long int value)
printf("%s=%lld\n", key, value);
}
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:");
writer_print_string(wctx, tag->key, tag->value, 0);
}
}
.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,
.show_tags = default_show_tags,
.flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS,
};
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
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
/* 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));
}
static void compact_print_int(WriterContext *wctx, const char *key, long long int value)
{
CompactContext *compact = wctx->priv;
if (wctx->nb_item) printf("%c", compact->item_sep);
if (!compact->nokey)
printf("%s=", key);
printf("%lld", value);
}
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));
}
}
.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,
/* 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);
}
.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,
};
/* JSON output */
typedef struct {
int multiple_entries; ///< tells if the given chapter requires multiple entries
char *buf;
size_t buf_size;
int print_packets_and_frames;
int indent_level;
int compact;
const char *item_sep, *item_start_end;
} JSONContext;
#undef OFFSET
#define OFFSET(x) offsetof(JSONContext, x)
static const AVOption json_options[]= {
{ "compact", "enable compact output", OFFSET(compact), AV_OPT_TYPE_INT, {.dbl=0}, 0, 1 },
{ "c", "enable compact output", OFFSET(compact), AV_OPT_TYPE_INT, {.dbl=0}, 0, 1 },
{ NULL }
};
static const char *json_get_name(void *ctx)
{
return "json";
}
static const AVClass json_class = {
"JSONContext",
json_get_name,
json_options
};
static av_cold int json_init(WriterContext *wctx, const char *args, void *opaque)
{
JSONContext *json = wctx->priv;
int err;
json->class = &json_class;
av_opt_set_defaults(json);
if (args &&
(err = (av_set_options_string(json, args, "=", ":"))) < 0) {
av_log(wctx, AV_LOG_ERROR, "Error parsing options string: '%s'\n", args);
return err;
}
json->item_sep = json->compact ? ", " : ",\n";
json->item_start_end = json->compact ? " " : "\n";
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)
{
static const char json_escape[] = {'"', '\\', '\b', '\f', '\n', '\r', '\t', 0};
static const char json_subst[] = {'"', '\\', 'b', 'f', 'n', 'r', 't', 0};
const char *p;
char *q;
size_t size = 1;
// compute the length of the escaped string
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
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;
} else {
*q++ = *p;
}
}
*q = 0;
return *dst;
}
static void json_print_header(WriterContext *wctx)
{
JSONContext *json = wctx->priv;
printf("{");
json->indent_level++;
}
static void json_print_footer(WriterContext *wctx)
{
JSONContext *json = wctx->priv;
json->indent_level--;
printf("\n}\n");
}
#define JSON_INDENT() printf("%*c", json->indent_level * 4, ' ')
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, "frames" ) ||
!strcmp(chapter, "packets_and_frames") ||
!strcmp(chapter, "streams") || !strcmp(chapter, "library_versions");
if (json->multiple_entries) {
JSON_INDENT();
printf("\"%s\": [\n", json_escape_str(&json->buf, &json->buf_size, chapter, wctx));
json->print_packets_and_frames = !strcmp(chapter, "packets_and_frames");
json->indent_level++;
}
static void json_print_chapter_footer(WriterContext *wctx, const char *chapter)
{
JSONContext *json = wctx->priv;
if (json->multiple_entries) {
json->indent_level--;
JSON_INDENT();
printf("]");
}
static void json_print_section_header(WriterContext *wctx, const char *section)
{
JSONContext *json = wctx->priv;
if (wctx->nb_section)
printf(",\n");
JSON_INDENT();
if (!json->multiple_entries)
printf("\"%s\": ", section);
printf("{%s", json->item_start_end);
json->indent_level++;
/* this is required so the parser can distinguish between packets and frames */
if (json->print_packets_and_frames) {
if (!json->compact)
JSON_INDENT();
printf("\"type\": \"%s\"%s", section, json->item_sep);
}
static void json_print_section_footer(WriterContext *wctx, const char *section)
{
JSONContext *json = wctx->priv;
printf("%s", json->item_start_end);
json->indent_level--;
if (!json->compact)
JSON_INDENT();
printf("}");
}
static inline void json_print_item_str(WriterContext *wctx,
const char *key, const char *value)
JSONContext *json = wctx->priv;
printf("\"%s\":", json_escape_str(&json->buf, &json->buf_size, key, wctx));
printf(" \"%s\"", json_escape_str(&json->buf, &json->buf_size, value, wctx));
}
static void json_print_str(WriterContext *wctx, const char *key, const char *value)
{
JSONContext *json = wctx->priv;
if (wctx->nb_item) printf("%s", json->item_sep);
if (!json->compact)
JSON_INDENT();
json_print_item_str(wctx, key, value);
}
static void json_print_int(WriterContext *wctx, const char *key, long long int value)
JSONContext *json = wctx->priv;
if (wctx->nb_item) printf("%s", json->item_sep);
if (!json->compact)
JSON_INDENT();
printf("\"%s\": %lld",
json_escape_str(&json->buf, &json->buf_size, key, wctx), value);
static void json_show_tags(WriterContext *wctx, AVDictionary *dict)
{
JSONContext *json = wctx->priv;
AVDictionaryEntry *tag = NULL;
int is_first = 1;
if (!dict)
return;
printf("%s", json->item_sep);
if (!json->compact)
JSON_INDENT();
printf("\"tags\": {%s", json->item_start_end);
json->indent_level++;
while ((tag = av_dict_get(dict, "", tag, AV_DICT_IGNORE_SUFFIX))) {
if (is_first) is_first = 0;
else printf("%s", json->item_sep);
if (!json->compact)
JSON_INDENT();
json_print_item_str(wctx, tag->key, tag->value);
json->indent_level--;
printf("%s", json->item_start_end);
if (!json->compact)
JSON_INDENT();
printf("}");
}
.name = "json",
.priv_size = sizeof(JSONContext),
.init = json_init,
.uninit = json_uninit,
.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,
.flags = WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER,
};
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* XML output */
typedef struct {
const AVClass *class;
int within_tag;
int multiple_entries; ///< tells if the given chapter requires multiple entries
int indent_level;
int fully_qualified;
int xsd_strict;
char *buf;
size_t buf_size;
} XMLContext;
#undef OFFSET
#define OFFSET(x) offsetof(XMLContext, x)
static const AVOption xml_options[] = {
{"fully_qualified", "specify if the output should be fully qualified", OFFSET(fully_qualified), AV_OPT_TYPE_INT, {.dbl=0}, 0, 1 },
{"q", "specify if the output should be fully qualified", OFFSET(fully_qualified), AV_OPT_TYPE_INT, {.dbl=0}, 0, 1 },
{"xsd_strict", "ensure that the output is XSD compliant", OFFSET(xsd_strict), AV_OPT_TYPE_INT, {.dbl=0}, 0, 1 },
{"x", "ensure that the output is XSD compliant", OFFSET(xsd_strict), AV_OPT_TYPE_INT, {.dbl=0}, 0, 1 },
{NULL},
};
static const char *xml_get_name(void *ctx)