00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "config.h"
00023
00024 #include "libavformat/avformat.h"
00025 #include "libavcodec/avcodec.h"
00026 #include "libavutil/avstring.h"
00027 #include "libavutil/opt.h"
00028 #include "libavutil/pixdesc.h"
00029 #include "libavutil/dict.h"
00030 #include "libavdevice/avdevice.h"
00031 #include "cmdutils.h"
00032
00033 const char program_name[] = "ffprobe";
00034 const int program_birth_year = 2007;
00035
00036 static int do_show_format = 0;
00037 static int do_show_packets = 0;
00038 static int do_show_streams = 0;
00039
00040 static int show_value_unit = 0;
00041 static int use_value_prefix = 0;
00042 static int use_byte_value_binary_prefix = 0;
00043 static int use_value_sexagesimal_format = 0;
00044
00045 static char *print_format;
00046
00047 static const OptionDef options[];
00048
00049
00050 static const char *input_filename;
00051 static AVInputFormat *iformat = NULL;
00052
00053 static const char *binary_unit_prefixes [] = { "", "Ki", "Mi", "Gi", "Ti", "Pi" };
00054 static const char *decimal_unit_prefixes[] = { "", "K" , "M" , "G" , "T" , "P" };
00055
00056 static const char *unit_second_str = "s" ;
00057 static const char *unit_hertz_str = "Hz" ;
00058 static const char *unit_byte_str = "byte" ;
00059 static const char *unit_bit_per_second_str = "bit/s";
00060
00061 void av_noreturn exit_program(int ret)
00062 {
00063 exit(ret);
00064 }
00065
00066 struct unit_value {
00067 union { double d; int i; } val;
00068 const char *unit;
00069 };
00070
00071 static char *value_string(char *buf, int buf_size, struct unit_value uv)
00072 {
00073 double vald;
00074 int show_float = 0;
00075
00076 if (uv.unit == unit_second_str) {
00077 vald = uv.val.d;
00078 show_float = 1;
00079 } else {
00080 vald = uv.val.i;
00081 }
00082
00083 if (uv.unit == unit_second_str && use_value_sexagesimal_format) {
00084 double secs;
00085 int hours, mins;
00086 secs = vald;
00087 mins = (int)secs / 60;
00088 secs = secs - mins * 60;
00089 hours = mins / 60;
00090 mins %= 60;
00091 snprintf(buf, buf_size, "%d:%02d:%09.6f", hours, mins, secs);
00092 } else if (use_value_prefix) {
00093 const char *prefix_string;
00094 int index, l;
00095
00096 if (uv.unit == unit_byte_str && use_byte_value_binary_prefix) {
00097 index = (int) (log(vald)/log(2)) / 10;
00098 index = av_clip(index, 0, FF_ARRAY_ELEMS(binary_unit_prefixes) -1);
00099 vald /= pow(2, index*10);
00100 prefix_string = binary_unit_prefixes[index];
00101 } else {
00102 index = (int) (log10(vald)) / 3;
00103 index = av_clip(index, 0, FF_ARRAY_ELEMS(decimal_unit_prefixes) -1);
00104 vald /= pow(10, index*3);
00105 prefix_string = decimal_unit_prefixes[index];
00106 }
00107
00108 if (show_float || vald != (int)vald) l = snprintf(buf, buf_size, "%.3f", vald);
00109 else l = snprintf(buf, buf_size, "%d", (int)vald);
00110 snprintf(buf+l, buf_size-l, "%s%s%s", prefix_string || show_value_unit ? " " : "",
00111 prefix_string, show_value_unit ? uv.unit : "");
00112 } else {
00113 int l;
00114
00115 if (show_float) l = snprintf(buf, buf_size, "%.3f", vald);
00116 else l = snprintf(buf, buf_size, "%d", (int)vald);
00117 snprintf(buf+l, buf_size-l, "%s%s", show_value_unit ? " " : "",
00118 show_value_unit ? uv.unit : "");
00119 }
00120
00121 return buf;
00122 }
00123
00124
00125
00126 typedef struct WriterContext WriterContext;
00127
00128 #define WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS 1
00129
00130 typedef struct Writer {
00131 int priv_size;
00132 const char *name;
00133
00134 int (*init) (WriterContext *wctx, const char *args, void *opaque);
00135 void (*uninit)(WriterContext *wctx);
00136
00137 void (*print_header)(WriterContext *ctx);
00138 void (*print_footer)(WriterContext *ctx);
00139
00140 void (*print_chapter_header)(WriterContext *wctx, const char *);
00141 void (*print_chapter_footer)(WriterContext *wctx, const char *);
00142 void (*print_section_header)(WriterContext *wctx, const char *);
00143 void (*print_section_footer)(WriterContext *wctx, const char *);
00144 void (*print_integer) (WriterContext *wctx, const char *, long long int);
00145 void (*print_string) (WriterContext *wctx, const char *, const char *);
00146 void (*show_tags) (WriterContext *wctx, AVDictionary *dict);
00147 int flags;
00148 } Writer;
00149
00150 struct WriterContext {
00151 const AVClass *class;
00152 const Writer *writer;
00153 char *name;
00154 void *priv;
00155 unsigned int nb_item;
00156 unsigned int nb_section;
00157 unsigned int nb_chapter;
00158 };
00159
00160 static const char *writer_get_name(void *p)
00161 {
00162 WriterContext *wctx = p;
00163 return wctx->writer->name;
00164 }
00165
00166 static const AVClass writer_class = {
00167 "Writer",
00168 writer_get_name,
00169 NULL,
00170 LIBAVUTIL_VERSION_INT,
00171 };
00172
00173 static void writer_close(WriterContext **wctx)
00174 {
00175 if (*wctx && (*wctx)->writer->uninit)
00176 (*wctx)->writer->uninit(*wctx);
00177
00178 av_freep(&((*wctx)->priv));
00179 av_freep(wctx);
00180 }
00181
00182 static int writer_open(WriterContext **wctx, const Writer *writer,
00183 const char *args, void *opaque)
00184 {
00185 int ret = 0;
00186
00187 if (!(*wctx = av_malloc(sizeof(WriterContext)))) {
00188 ret = AVERROR(ENOMEM);
00189 goto fail;
00190 }
00191
00192 if (!((*wctx)->priv = av_mallocz(writer->priv_size))) {
00193 ret = AVERROR(ENOMEM);
00194 goto fail;
00195 }
00196
00197 (*wctx)->class = &writer_class;
00198 (*wctx)->writer = writer;
00199 if ((*wctx)->writer->init)
00200 ret = (*wctx)->writer->init(*wctx, args, opaque);
00201 if (ret < 0)
00202 goto fail;
00203
00204 return 0;
00205
00206 fail:
00207 writer_close(wctx);
00208 return ret;
00209 }
00210
00211 static inline void writer_print_header(WriterContext *wctx)
00212 {
00213 if (wctx->writer->print_header)
00214 wctx->writer->print_header(wctx);
00215 wctx->nb_chapter = 0;
00216 }
00217
00218 static inline void writer_print_footer(WriterContext *wctx)
00219 {
00220 if (wctx->writer->print_footer)
00221 wctx->writer->print_footer(wctx);
00222 }
00223
00224 static inline void writer_print_chapter_header(WriterContext *wctx,
00225 const char *header)
00226 {
00227 if (wctx->writer->print_chapter_header)
00228 wctx->writer->print_chapter_header(wctx, header);
00229 wctx->nb_section = 0;
00230 }
00231
00232 static inline void writer_print_chapter_footer(WriterContext *wctx,
00233 const char *footer)
00234 {
00235 if (wctx->writer->print_chapter_footer)
00236 wctx->writer->print_chapter_footer(wctx, footer);
00237 wctx->nb_chapter++;
00238 }
00239
00240 static inline void writer_print_section_header(WriterContext *wctx,
00241 const char *header)
00242 {
00243 if (wctx->writer->print_section_header)
00244 wctx->writer->print_section_header(wctx, header);
00245 wctx->nb_item = 0;
00246 }
00247
00248 static inline void writer_print_section_footer(WriterContext *wctx,
00249 const char *footer)
00250 {
00251 if (wctx->writer->print_section_footer)
00252 wctx->writer->print_section_footer(wctx, footer);
00253 wctx->nb_section++;
00254 }
00255
00256 static inline void writer_print_integer(WriterContext *wctx,
00257 const char *key, long long int val)
00258 {
00259 wctx->writer->print_integer(wctx, key, val);
00260 wctx->nb_item++;
00261 }
00262
00263 static inline void writer_print_string(WriterContext *wctx,
00264 const char *key, const char *val, int opt)
00265 {
00266 if (opt && !(wctx->writer->flags & WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS))
00267 return;
00268 wctx->writer->print_string(wctx, key, val);
00269 wctx->nb_item++;
00270 }
00271
00272 static void writer_print_time(WriterContext *wctx, const char *key,
00273 int64_t ts, const AVRational *time_base)
00274 {
00275 char buf[128];
00276
00277 if (ts == AV_NOPTS_VALUE) {
00278 writer_print_string(wctx, key, "N/A", 1);
00279 } else {
00280 double d = ts * av_q2d(*time_base);
00281 value_string(buf, sizeof(buf), (struct unit_value){.val.d=d, .unit=unit_second_str});
00282 writer_print_string(wctx, key, buf, 0);
00283 }
00284 }
00285
00286 static void writer_print_ts(WriterContext *wctx, const char *key, int64_t ts)
00287 {
00288 if (ts == AV_NOPTS_VALUE) {
00289 writer_print_string(wctx, key, "N/A", 1);
00290 } else {
00291 writer_print_integer(wctx, key, ts);
00292 }
00293 }
00294
00295 static inline void writer_show_tags(WriterContext *wctx, AVDictionary *dict)
00296 {
00297 wctx->writer->show_tags(wctx, dict);
00298 }
00299
00300 #define MAX_REGISTERED_WRITERS_NB 64
00301
00302 static const Writer *registered_writers[MAX_REGISTERED_WRITERS_NB + 1];
00303
00304 static int writer_register(const Writer *writer)
00305 {
00306 static int next_registered_writer_idx = 0;
00307
00308 if (next_registered_writer_idx == MAX_REGISTERED_WRITERS_NB)
00309 return AVERROR(ENOMEM);
00310
00311 registered_writers[next_registered_writer_idx++] = writer;
00312 return 0;
00313 }
00314
00315 static const Writer *writer_get_by_name(const char *name)
00316 {
00317 int i;
00318
00319 for (i = 0; registered_writers[i]; i++)
00320 if (!strcmp(registered_writers[i]->name, name))
00321 return registered_writers[i];
00322
00323 return NULL;
00324 }
00325
00326
00327
00328 struct print_buf {
00329 char *s;
00330 int len;
00331 };
00332
00333 static char *fast_asprintf(struct print_buf *pbuf, const char *fmt, ...)
00334 {
00335 va_list va;
00336 int len;
00337
00338 va_start(va, fmt);
00339 len = vsnprintf(NULL, 0, fmt, va);
00340 va_end(va);
00341 if (len < 0)
00342 goto fail;
00343
00344 if (pbuf->len < len) {
00345 char *p = av_realloc(pbuf->s, len + 1);
00346 if (!p)
00347 goto fail;
00348 pbuf->s = p;
00349 pbuf->len = len;
00350 }
00351
00352 va_start(va, fmt);
00353 len = vsnprintf(pbuf->s, len + 1, fmt, va);
00354 va_end(va);
00355 if (len < 0)
00356 goto fail;
00357 return pbuf->s;
00358
00359 fail:
00360 av_freep(&pbuf->s);
00361 pbuf->len = 0;
00362 return NULL;
00363 }
00364
00365 #define ESCAPE_INIT_BUF_SIZE 256
00366
00367 #define ESCAPE_CHECK_SIZE(src, size, max_size) \
00368 if (size > max_size) { \
00369 char buf[64]; \
00370 snprintf(buf, sizeof(buf), "%s", src); \
00371 av_log(log_ctx, AV_LOG_WARNING, \
00372 "String '%s...' with is too big\n", buf); \
00373 return "FFPROBE_TOO_BIG_STRING"; \
00374 }
00375
00376 #define ESCAPE_REALLOC_BUF(dst_size_p, dst_p, src, size) \
00377 if (*dst_size_p < size) { \
00378 char *q = av_realloc(*dst_p, size); \
00379 if (!q) { \
00380 char buf[64]; \
00381 snprintf(buf, sizeof(buf), "%s", src); \
00382 av_log(log_ctx, AV_LOG_WARNING, \
00383 "String '%s...' could not be escaped\n", buf); \
00384 return "FFPROBE_THIS_STRING_COULD_NOT_BE_ESCAPED"; \
00385 } \
00386 *dst_size_p = size; \
00387 *dst = q; \
00388 }
00389
00390
00391
00392
00393
00394 static void default_print_footer(WriterContext *wctx)
00395 {
00396 printf("\n");
00397 }
00398
00399 static void default_print_chapter_header(WriterContext *wctx, const char *chapter)
00400 {
00401 if (wctx->nb_chapter)
00402 printf("\n");
00403 }
00404
00405
00406 static inline char *upcase_string(char *dst, size_t dst_size, const char *src)
00407 {
00408 int i;
00409 for (i = 0; src[i] && i < dst_size-1; i++)
00410 dst[i] = src[i]-32;
00411 dst[i] = 0;
00412 return dst;
00413 }
00414
00415 static void default_print_section_header(WriterContext *wctx, const char *section)
00416 {
00417 char buf[32];
00418
00419 if (wctx->nb_section)
00420 printf("\n");
00421 printf("[%s]\n", upcase_string(buf, sizeof(buf), section));
00422 }
00423
00424 static void default_print_section_footer(WriterContext *wctx, const char *section)
00425 {
00426 char buf[32];
00427
00428 printf("[/%s]", upcase_string(buf, sizeof(buf), section));
00429 }
00430
00431 static void default_print_str(WriterContext *wctx, const char *key, const char *value)
00432 {
00433 printf("%s=%s\n", key, value);
00434 }
00435
00436 static void default_print_int(WriterContext *wctx, const char *key, long long int value)
00437 {
00438 printf("%s=%lld\n", key, value);
00439 }
00440
00441 static void default_show_tags(WriterContext *wctx, AVDictionary *dict)
00442 {
00443 AVDictionaryEntry *tag = NULL;
00444 while ((tag = av_dict_get(dict, "", tag, AV_DICT_IGNORE_SUFFIX))) {
00445 printf("TAG:");
00446 writer_print_string(wctx, tag->key, tag->value, 0);
00447 }
00448 }
00449
00450 static const Writer default_writer = {
00451 .name = "default",
00452 .print_footer = default_print_footer,
00453 .print_chapter_header = default_print_chapter_header,
00454 .print_section_header = default_print_section_header,
00455 .print_section_footer = default_print_section_footer,
00456 .print_integer = default_print_int,
00457 .print_string = default_print_str,
00458 .show_tags = default_show_tags,
00459 .flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS,
00460 };
00461
00462
00463
00468 static const char *c_escape_str(char **dst, size_t *dst_size,
00469 const char *src, const char sep, void *log_ctx)
00470 {
00471 const char *p;
00472 char *q;
00473 size_t size = 1;
00474
00475
00476 for (p = src; *p; p++, size++) {
00477 ESCAPE_CHECK_SIZE(src, size, SIZE_MAX-2);
00478 if (*p == '\n' || *p == '\r' || *p == '\\')
00479 size++;
00480 }
00481
00482 ESCAPE_REALLOC_BUF(dst_size, dst, src, size);
00483
00484 q = *dst;
00485 for (p = src; *p; p++) {
00486 switch (*src) {
00487 case '\n': *q++ = '\\'; *q++ = 'n'; break;
00488 case '\r': *q++ = '\\'; *q++ = 'r'; break;
00489 case '\\': *q++ = '\\'; *q++ = '\\'; break;
00490 default:
00491 if (*p == sep)
00492 *q++ = '\\';
00493 *q++ = *p;
00494 }
00495 }
00496 *q = 0;
00497 return *dst;
00498 }
00499
00503 static const char *csv_escape_str(char **dst, size_t *dst_size,
00504 const char *src, const char sep, void *log_ctx)
00505 {
00506 const char *p;
00507 char *q;
00508 size_t size = 1;
00509 int quote = 0;
00510
00511
00512 for (p = src; *p; p++, size++) {
00513 ESCAPE_CHECK_SIZE(src, size, SIZE_MAX-4);
00514 if (*p == '"' || *p == sep || *p == '\n' || *p == '\r')
00515 if (!quote) {
00516 quote = 1;
00517 size += 2;
00518 }
00519 if (*p == '"')
00520 size++;
00521 }
00522
00523 ESCAPE_REALLOC_BUF(dst_size, dst, src, size);
00524
00525 q = *dst;
00526 p = src;
00527 if (quote)
00528 *q++ = '\"';
00529 while (*p) {
00530 if (*p == '"')
00531 *q++ = '\"';
00532 *q++ = *p++;
00533 }
00534 if (quote)
00535 *q++ = '\"';
00536 *q = 0;
00537
00538 return *dst;
00539 }
00540
00541 static const char *none_escape_str(char **dst, size_t *dst_size,
00542 const char *src, const char sep, void *log_ctx)
00543 {
00544 return src;
00545 }
00546
00547 typedef struct CompactContext {
00548 const AVClass *class;
00549 char *item_sep_str;
00550 char item_sep;
00551 int nokey;
00552 char *buf;
00553 size_t buf_size;
00554 char *escape_mode_str;
00555 const char * (*escape_str)(char **dst, size_t *dst_size,
00556 const char *src, const char sep, void *log_ctx);
00557 } CompactContext;
00558
00559 #define OFFSET(x) offsetof(CompactContext, x)
00560
00561 static const AVOption compact_options[]= {
00562 {"item_sep", "set item separator", OFFSET(item_sep_str), AV_OPT_TYPE_STRING, {.str="|"}, CHAR_MIN, CHAR_MAX },
00563 {"s", "set item separator", OFFSET(item_sep_str), AV_OPT_TYPE_STRING, {.str="|"}, CHAR_MIN, CHAR_MAX },
00564 {"nokey", "force no key printing", OFFSET(nokey), AV_OPT_TYPE_INT, {.dbl=0}, 0, 1 },
00565 {"nk", "force no key printing", OFFSET(nokey), AV_OPT_TYPE_INT, {.dbl=0}, 0, 1 },
00566 {"escape", "set escape mode", OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="c"}, CHAR_MIN, CHAR_MAX },
00567 {"e", "set escape mode", OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="c"}, CHAR_MIN, CHAR_MAX },
00568 {NULL},
00569 };
00570
00571 static const char *compact_get_name(void *ctx)
00572 {
00573 return "compact";
00574 }
00575
00576 static const AVClass compact_class = {
00577 "CompactContext",
00578 compact_get_name,
00579 compact_options
00580 };
00581
00582 static av_cold int compact_init(WriterContext *wctx, const char *args, void *opaque)
00583 {
00584 CompactContext *compact = wctx->priv;
00585 int err;
00586
00587 compact->class = &compact_class;
00588 av_opt_set_defaults(compact);
00589
00590 if (args &&
00591 (err = (av_set_options_string(compact, args, "=", ":"))) < 0) {
00592 av_log(wctx, AV_LOG_ERROR, "Error parsing options string: '%s'\n", args);
00593 return err;
00594 }
00595 if (strlen(compact->item_sep_str) != 1) {
00596 av_log(wctx, AV_LOG_ERROR, "Item separator '%s' specified, but must contain a single character\n",
00597 compact->item_sep_str);
00598 return AVERROR(EINVAL);
00599 }
00600 compact->item_sep = compact->item_sep_str[0];
00601
00602 compact->buf_size = ESCAPE_INIT_BUF_SIZE;
00603 if (!(compact->buf = av_malloc(compact->buf_size)))
00604 return AVERROR(ENOMEM);
00605
00606 if (!strcmp(compact->escape_mode_str, "none")) compact->escape_str = none_escape_str;
00607 else if (!strcmp(compact->escape_mode_str, "c" )) compact->escape_str = c_escape_str;
00608 else if (!strcmp(compact->escape_mode_str, "csv" )) compact->escape_str = csv_escape_str;
00609 else {
00610 av_log(wctx, AV_LOG_ERROR, "Unknown escape mode '%s'\n", compact->escape_mode_str);
00611 return AVERROR(EINVAL);
00612 }
00613
00614 return 0;
00615 }
00616
00617 static av_cold void compact_uninit(WriterContext *wctx)
00618 {
00619 CompactContext *compact = wctx->priv;
00620
00621 av_freep(&compact->item_sep_str);
00622 av_freep(&compact->buf);
00623 av_freep(&compact->escape_mode_str);
00624 }
00625
00626 static void compact_print_section_header(WriterContext *wctx, const char *section)
00627 {
00628 CompactContext *compact = wctx->priv;
00629
00630 printf("%s%c", section, compact->item_sep);
00631 }
00632
00633 static void compact_print_section_footer(WriterContext *wctx, const char *section)
00634 {
00635 printf("\n");
00636 }
00637
00638 static void compact_print_str(WriterContext *wctx, const char *key, const char *value)
00639 {
00640 CompactContext *compact = wctx->priv;
00641
00642 if (wctx->nb_item) printf("%c", compact->item_sep);
00643 if (!compact->nokey)
00644 printf("%s=", key);
00645 printf("%s", compact->escape_str(&compact->buf, &compact->buf_size,
00646 value, compact->item_sep, wctx));
00647 }
00648
00649 static void compact_print_int(WriterContext *wctx, const char *key, long long int value)
00650 {
00651 CompactContext *compact = wctx->priv;
00652
00653 if (wctx->nb_item) printf("%c", compact->item_sep);
00654 if (!compact->nokey)
00655 printf("%s=", key);
00656 printf("%lld", value);
00657 }
00658
00659 static void compact_show_tags(WriterContext *wctx, AVDictionary *dict)
00660 {
00661 CompactContext *compact = wctx->priv;
00662 AVDictionaryEntry *tag = NULL;
00663
00664 while ((tag = av_dict_get(dict, "", tag, AV_DICT_IGNORE_SUFFIX))) {
00665 if (wctx->nb_item) printf("%c", compact->item_sep);
00666 if (!compact->nokey)
00667 printf("tag:%s=", compact->escape_str(&compact->buf, &compact->buf_size,
00668 tag->key, compact->item_sep, wctx));
00669 printf("%s", compact->escape_str(&compact->buf, &compact->buf_size,
00670 tag->value, compact->item_sep, wctx));
00671 }
00672 }
00673
00674 static const Writer compact_writer = {
00675 .name = "compact",
00676 .priv_size = sizeof(CompactContext),
00677 .init = compact_init,
00678 .uninit = compact_uninit,
00679 .print_section_header = compact_print_section_header,
00680 .print_section_footer = compact_print_section_footer,
00681 .print_integer = compact_print_int,
00682 .print_string = compact_print_str,
00683 .show_tags = compact_show_tags,
00684 .flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS,
00685 };
00686
00687
00688
00689 static av_cold int csv_init(WriterContext *wctx, const char *args, void *opaque)
00690 {
00691 return compact_init(wctx, "item_sep=,:nokey=1:escape=csv", opaque);
00692 }
00693
00694 static const Writer csv_writer = {
00695 .name = "csv",
00696 .priv_size = sizeof(CompactContext),
00697 .init = csv_init,
00698 .uninit = compact_uninit,
00699 .print_section_header = compact_print_section_header,
00700 .print_section_footer = compact_print_section_footer,
00701 .print_integer = compact_print_int,
00702 .print_string = compact_print_str,
00703 .show_tags = compact_show_tags,
00704 .flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS,
00705 };
00706
00707
00708
00709 typedef struct {
00710 int multiple_entries;
00711 char *buf;
00712 size_t buf_size;
00713 } JSONContext;
00714
00715 static av_cold int json_init(WriterContext *wctx, const char *args, void *opaque)
00716 {
00717 JSONContext *json = wctx->priv;
00718
00719 json->buf_size = ESCAPE_INIT_BUF_SIZE;
00720 if (!(json->buf = av_malloc(json->buf_size)))
00721 return AVERROR(ENOMEM);
00722
00723 return 0;
00724 }
00725
00726 static av_cold void json_uninit(WriterContext *wctx)
00727 {
00728 JSONContext *json = wctx->priv;
00729 av_freep(&json->buf);
00730 }
00731
00732 static const char *json_escape_str(char **dst, size_t *dst_size, const char *src,
00733 void *log_ctx)
00734 {
00735 static const char json_escape[] = {'"', '\\', '\b', '\f', '\n', '\r', '\t', 0};
00736 static const char json_subst[] = {'"', '\\', 'b', 'f', 'n', 'r', 't', 0};
00737 const char *p;
00738 char *q;
00739 size_t size = 1;
00740
00741
00742 for (p = src; *p; p++) {
00743 ESCAPE_CHECK_SIZE(src, size, SIZE_MAX-6);
00744 if (strchr(json_escape, *p)) size += 2;
00745 else if ((unsigned char)*p < 32) size += 6;
00746 else size += 1;
00747 }
00748 ESCAPE_REALLOC_BUF(dst_size, dst, src, size);
00749
00750 q = *dst;
00751 for (p = src; *p; p++) {
00752 char *s = strchr(json_escape, *p);
00753 if (s) {
00754 *q++ = '\\';
00755 *q++ = json_subst[s - json_escape];
00756 } else if ((unsigned char)*p < 32) {
00757 snprintf(q, 7, "\\u00%02x", *p & 0xff);
00758 q += 6;
00759 } else {
00760 *q++ = *p;
00761 }
00762 }
00763 *q = 0;
00764 return *dst;
00765 }
00766
00767 static void json_print_header(WriterContext *wctx)
00768 {
00769 printf("{");
00770 }
00771
00772 static void json_print_footer(WriterContext *wctx)
00773 {
00774 printf("\n}\n");
00775 }
00776
00777 static void json_print_chapter_header(WriterContext *wctx, const char *chapter)
00778 {
00779 JSONContext *json = wctx->priv;
00780
00781 if (wctx->nb_chapter)
00782 printf(",");
00783 json->multiple_entries = !strcmp(chapter, "packets") || !strcmp(chapter, "streams");
00784 printf("\n \"%s\":%s", json_escape_str(&json->buf, &json->buf_size, chapter, wctx),
00785 json->multiple_entries ? " [" : " ");
00786 }
00787
00788 static void json_print_chapter_footer(WriterContext *wctx, const char *chapter)
00789 {
00790 JSONContext *json = wctx->priv;
00791
00792 if (json->multiple_entries)
00793 printf("]");
00794 }
00795
00796 static void json_print_section_header(WriterContext *wctx, const char *section)
00797 {
00798 if (wctx->nb_section) printf(",");
00799 printf("{\n");
00800 }
00801
00802 static void json_print_section_footer(WriterContext *wctx, const char *section)
00803 {
00804 printf("\n }");
00805 }
00806
00807 static inline void json_print_item_str(WriterContext *wctx,
00808 const char *key, const char *value,
00809 const char *indent)
00810 {
00811 JSONContext *json = wctx->priv;
00812
00813 printf("%s\"%s\":", indent, json_escape_str(&json->buf, &json->buf_size, key, wctx));
00814 printf(" \"%s\"", json_escape_str(&json->buf, &json->buf_size, value, wctx));
00815 }
00816
00817 #define INDENT " "
00818
00819 static void json_print_str(WriterContext *wctx, const char *key, const char *value)
00820 {
00821 if (wctx->nb_item) printf(",\n");
00822 json_print_item_str(wctx, key, value, INDENT);
00823 }
00824
00825 static void json_print_int(WriterContext *wctx, const char *key, long long int value)
00826 {
00827 JSONContext *json = wctx->priv;
00828
00829 if (wctx->nb_item) printf(",\n");
00830 printf(INDENT "\"%s\": %lld",
00831 json_escape_str(&json->buf, &json->buf_size, key, wctx), value);
00832 }
00833
00834 static void json_show_tags(WriterContext *wctx, AVDictionary *dict)
00835 {
00836 AVDictionaryEntry *tag = NULL;
00837 int is_first = 1;
00838 if (!dict)
00839 return;
00840 printf(",\n" INDENT "\"tags\": {\n");
00841 while ((tag = av_dict_get(dict, "", tag, AV_DICT_IGNORE_SUFFIX))) {
00842 if (is_first) is_first = 0;
00843 else printf(",\n");
00844 json_print_item_str(wctx, tag->key, tag->value, INDENT INDENT);
00845 }
00846 printf("\n }");
00847 }
00848
00849 static const Writer json_writer = {
00850 .name = "json",
00851 .priv_size = sizeof(JSONContext),
00852 .init = json_init,
00853 .uninit = json_uninit,
00854 .print_header = json_print_header,
00855 .print_footer = json_print_footer,
00856 .print_chapter_header = json_print_chapter_header,
00857 .print_chapter_footer = json_print_chapter_footer,
00858 .print_section_header = json_print_section_header,
00859 .print_section_footer = json_print_section_footer,
00860 .print_integer = json_print_int,
00861 .print_string = json_print_str,
00862 .show_tags = json_show_tags,
00863 };
00864
00865 static void writer_register_all(void)
00866 {
00867 static int initialized;
00868
00869 if (initialized)
00870 return;
00871 initialized = 1;
00872
00873 writer_register(&default_writer);
00874 writer_register(&compact_writer);
00875 writer_register(&csv_writer);
00876 writer_register(&json_writer);
00877 }
00878
00879 #define print_fmt(k, f, ...) do { \
00880 if (fast_asprintf(&pbuf, f, __VA_ARGS__)) \
00881 writer_print_string(w, k, pbuf.s, 0); \
00882 } while (0)
00883
00884 #define print_fmt_opt(k, f, ...) do { \
00885 if (fast_asprintf(&pbuf, f, __VA_ARGS__)) \
00886 writer_print_string(w, k, pbuf.s, 1); \
00887 } while (0)
00888
00889 #define print_int(k, v) writer_print_integer(w, k, v)
00890 #define print_str(k, v) writer_print_string(w, k, v, 0)
00891 #define print_str_opt(k, v) writer_print_string(w, k, v, 1)
00892 #define print_time(k, v, tb) writer_print_time(w, k, v, tb)
00893 #define print_ts(k, v) writer_print_ts(w, k, v)
00894 #define print_val(k, v, u) writer_print_string(w, k, \
00895 value_string(val_str, sizeof(val_str), (struct unit_value){.val.i = v, .unit=u}), 0)
00896 #define print_section_header(s) writer_print_section_header(w, s)
00897 #define print_section_footer(s) writer_print_section_footer(w, s)
00898 #define show_tags(metadata) writer_show_tags(w, metadata)
00899
00900 static void show_packet(WriterContext *w, AVFormatContext *fmt_ctx, AVPacket *pkt, int packet_idx)
00901 {
00902 char val_str[128];
00903 AVStream *st = fmt_ctx->streams[pkt->stream_index];
00904 struct print_buf pbuf = {.s = NULL};
00905 const char *s;
00906
00907 print_section_header("packet");
00908 s = av_get_media_type_string(st->codec->codec_type);
00909 if (s) print_str ("codec_type", s);
00910 else print_str_opt("codec_type", "unknown");
00911 print_int("stream_index", pkt->stream_index);
00912 print_ts ("pts", pkt->pts);
00913 print_time("pts_time", pkt->pts, &st->time_base);
00914 print_ts ("dts", pkt->dts);
00915 print_time("dts_time", pkt->dts, &st->time_base);
00916 print_ts ("duration", pkt->duration);
00917 print_time("duration_time", pkt->duration, &st->time_base);
00918 print_val("size", pkt->size, unit_byte_str);
00919 if (pkt->pos != -1) print_fmt ("pos", "%"PRId64, pkt->pos);
00920 else print_str_opt("pos", "N/A");
00921 print_fmt("flags", "%c", pkt->flags & AV_PKT_FLAG_KEY ? 'K' : '_');
00922 print_section_footer("packet");
00923
00924 av_free(pbuf.s);
00925 fflush(stdout);
00926 }
00927
00928 static void show_packets(WriterContext *w, AVFormatContext *fmt_ctx)
00929 {
00930 AVPacket pkt;
00931 int i = 0;
00932
00933 av_init_packet(&pkt);
00934
00935 while (!av_read_frame(fmt_ctx, &pkt))
00936 show_packet(w, fmt_ctx, &pkt, i++);
00937 }
00938
00939 static void show_stream(WriterContext *w, AVFormatContext *fmt_ctx, int stream_idx)
00940 {
00941 AVStream *stream = fmt_ctx->streams[stream_idx];
00942 AVCodecContext *dec_ctx;
00943 AVCodec *dec;
00944 char val_str[128];
00945 const char *s;
00946 AVRational display_aspect_ratio;
00947 struct print_buf pbuf = {.s = NULL};
00948
00949 print_section_header("stream");
00950
00951 print_int("index", stream->index);
00952
00953 if ((dec_ctx = stream->codec)) {
00954 if ((dec = dec_ctx->codec)) {
00955 print_str("codec_name", dec->name);
00956 print_str("codec_long_name", dec->long_name);
00957 } else {
00958 print_str_opt("codec_name", "unknown");
00959 print_str_opt("codec_long_name", "unknown");
00960 }
00961
00962 s = av_get_media_type_string(dec_ctx->codec_type);
00963 if (s) print_str ("codec_type", s);
00964 else print_str_opt("codec_type", "unknown");
00965 print_fmt("codec_time_base", "%d/%d", dec_ctx->time_base.num, dec_ctx->time_base.den);
00966
00967
00968 av_get_codec_tag_string(val_str, sizeof(val_str), dec_ctx->codec_tag);
00969 print_str("codec_tag_string", val_str);
00970 print_fmt("codec_tag", "0x%04x", dec_ctx->codec_tag);
00971
00972 switch (dec_ctx->codec_type) {
00973 case AVMEDIA_TYPE_VIDEO:
00974 print_int("width", dec_ctx->width);
00975 print_int("height", dec_ctx->height);
00976 print_int("has_b_frames", dec_ctx->has_b_frames);
00977 if (dec_ctx->sample_aspect_ratio.num) {
00978 print_fmt("sample_aspect_ratio", "%d:%d",
00979 dec_ctx->sample_aspect_ratio.num,
00980 dec_ctx->sample_aspect_ratio.den);
00981 av_reduce(&display_aspect_ratio.num, &display_aspect_ratio.den,
00982 dec_ctx->width * dec_ctx->sample_aspect_ratio.num,
00983 dec_ctx->height * dec_ctx->sample_aspect_ratio.den,
00984 1024*1024);
00985 print_fmt("display_aspect_ratio", "%d:%d",
00986 display_aspect_ratio.num,
00987 display_aspect_ratio.den);
00988 } else {
00989 print_str_opt("sample_aspect_ratio", "N/A");
00990 print_str_opt("display_aspect_ratio", "N/A");
00991 }
00992 s = av_get_pix_fmt_name(dec_ctx->pix_fmt);
00993 if (s) print_str ("pix_fmt", s);
00994 else print_str_opt("pix_fmt", "unknown");
00995 print_int("level", dec_ctx->level);
00996 if (dec_ctx->timecode_frame_start >= 0) {
00997 uint32_t tc = dec_ctx->timecode_frame_start;
00998 print_fmt("timecode", "%02d:%02d:%02d%c%02d",
00999 tc>>19 & 0x1f,
01000 tc>>13 & 0x3f,
01001 tc>>6 & 0x3f,
01002 tc & 1<<24 ? ';' : ':',
01003 tc & 0x3f);
01004 } else {
01005 print_str_opt("timecode", "N/A");
01006 }
01007 break;
01008
01009 case AVMEDIA_TYPE_AUDIO:
01010 s = av_get_sample_fmt_name(dec_ctx->sample_fmt);
01011 if (s) print_str ("sample_fmt", s);
01012 else print_str_opt("sample_fmt", "unknown");
01013 print_val("sample_rate", dec_ctx->sample_rate, unit_hertz_str);
01014 print_int("channels", dec_ctx->channels);
01015 print_int("bits_per_sample", av_get_bits_per_sample(dec_ctx->codec_id));
01016 break;
01017 }
01018 } else {
01019 print_str_opt("codec_type", "unknown");
01020 }
01021 if (dec_ctx->codec && dec_ctx->codec->priv_class) {
01022 const AVOption *opt = NULL;
01023 while (opt = av_opt_next(dec_ctx->priv_data,opt)) {
01024 uint8_t *str;
01025 if (opt->flags) continue;
01026 if (av_opt_get(dec_ctx->priv_data, opt->name, 0, &str) >= 0) {
01027 print_str(opt->name, str);
01028 av_free(str);
01029 }
01030 }
01031 }
01032
01033 if (fmt_ctx->iformat->flags & AVFMT_SHOW_IDS) print_fmt ("id", "0x%x", stream->id);
01034 else print_str_opt("id", "N/A");
01035 print_fmt("r_frame_rate", "%d/%d", stream->r_frame_rate.num, stream->r_frame_rate.den);
01036 print_fmt("avg_frame_rate", "%d/%d", stream->avg_frame_rate.num, stream->avg_frame_rate.den);
01037 print_fmt("time_base", "%d/%d", stream->time_base.num, stream->time_base.den);
01038 print_time("start_time", stream->start_time, &stream->time_base);
01039 print_time("duration", stream->duration, &stream->time_base);
01040 if (stream->nb_frames) print_fmt ("nb_frames", "%"PRId64, stream->nb_frames);
01041 else print_str_opt("nb_frames", "N/A");
01042 show_tags(stream->metadata);
01043
01044 print_section_footer("stream");
01045 av_free(pbuf.s);
01046 fflush(stdout);
01047 }
01048
01049 static void show_streams(WriterContext *w, AVFormatContext *fmt_ctx)
01050 {
01051 int i;
01052 for (i = 0; i < fmt_ctx->nb_streams; i++)
01053 show_stream(w, fmt_ctx, i);
01054 }
01055
01056 static void show_format(WriterContext *w, AVFormatContext *fmt_ctx)
01057 {
01058 char val_str[128];
01059 int64_t size = avio_size(fmt_ctx->pb);
01060 struct print_buf pbuf = {.s = NULL};
01061
01062 print_section_header("format");
01063 print_str("filename", fmt_ctx->filename);
01064 print_int("nb_streams", fmt_ctx->nb_streams);
01065 print_str("format_name", fmt_ctx->iformat->name);
01066 print_str("format_long_name", fmt_ctx->iformat->long_name);
01067 print_time("start_time", fmt_ctx->start_time, &AV_TIME_BASE_Q);
01068 print_time("duration", fmt_ctx->duration, &AV_TIME_BASE_Q);
01069 if (size >= 0) print_val ("size", size, unit_byte_str);
01070 else print_str_opt("size", "N/A");
01071 if (fmt_ctx->bit_rate > 0) print_val ("bit_rate", fmt_ctx->bit_rate, unit_bit_per_second_str);
01072 else print_str_opt("bit_rate", "N/A");
01073 show_tags(fmt_ctx->metadata);
01074 print_section_footer("format");
01075 av_free(pbuf.s);
01076 fflush(stdout);
01077 }
01078
01079 static int open_input_file(AVFormatContext **fmt_ctx_ptr, const char *filename)
01080 {
01081 int err, i;
01082 AVFormatContext *fmt_ctx = NULL;
01083 AVDictionaryEntry *t;
01084
01085 if ((err = avformat_open_input(&fmt_ctx, filename, iformat, &format_opts)) < 0) {
01086 print_error(filename, err);
01087 return err;
01088 }
01089 if ((t = av_dict_get(format_opts, "", NULL, AV_DICT_IGNORE_SUFFIX))) {
01090 av_log(NULL, AV_LOG_ERROR, "Option %s not found.\n", t->key);
01091 return AVERROR_OPTION_NOT_FOUND;
01092 }
01093
01094
01095
01096 if ((err = avformat_find_stream_info(fmt_ctx, NULL)) < 0) {
01097 print_error(filename, err);
01098 return err;
01099 }
01100
01101 av_dump_format(fmt_ctx, 0, filename, 0);
01102
01103
01104 for (i = 0; i < fmt_ctx->nb_streams; i++) {
01105 AVStream *stream = fmt_ctx->streams[i];
01106 AVCodec *codec;
01107
01108 if (!(codec = avcodec_find_decoder(stream->codec->codec_id))) {
01109 fprintf(stderr, "Unsupported codec with id %d for input stream %d\n",
01110 stream->codec->codec_id, stream->index);
01111 } else if (avcodec_open2(stream->codec, codec, NULL) < 0) {
01112 fprintf(stderr, "Error while opening codec for input stream %d\n",
01113 stream->index);
01114 }
01115 }
01116
01117 *fmt_ctx_ptr = fmt_ctx;
01118 return 0;
01119 }
01120
01121 #define PRINT_CHAPTER(name) do { \
01122 if (do_show_ ## name) { \
01123 writer_print_chapter_header(wctx, #name); \
01124 show_ ## name (wctx, fmt_ctx); \
01125 writer_print_chapter_footer(wctx, #name); \
01126 } \
01127 } while (0)
01128
01129 static int probe_file(const char *filename)
01130 {
01131 AVFormatContext *fmt_ctx;
01132 int ret;
01133 const Writer *w;
01134 char *buf;
01135 char *w_name = NULL, *w_args = NULL;
01136 WriterContext *wctx;
01137
01138 writer_register_all();
01139
01140 if (!print_format)
01141 print_format = av_strdup("default");
01142 w_name = av_strtok(print_format, "=", &buf);
01143 w_args = buf;
01144
01145 w = writer_get_by_name(w_name);
01146 if (!w) {
01147 av_log(NULL, AV_LOG_ERROR, "Unknown output format with name '%s'\n", w_name);
01148 ret = AVERROR(EINVAL);
01149 goto end;
01150 }
01151
01152 if ((ret = writer_open(&wctx, w, w_args, NULL)) < 0)
01153 goto end;
01154 if ((ret = open_input_file(&fmt_ctx, filename)))
01155 goto end;
01156
01157 writer_print_header(wctx);
01158 PRINT_CHAPTER(packets);
01159 PRINT_CHAPTER(streams);
01160 PRINT_CHAPTER(format);
01161 writer_print_footer(wctx);
01162
01163 av_close_input_file(fmt_ctx);
01164 writer_close(&wctx);
01165
01166 end:
01167 av_freep(&print_format);
01168
01169 return ret;
01170 }
01171
01172 static void show_usage(void)
01173 {
01174 printf("Simple multimedia streams analyzer\n");
01175 printf("usage: %s [OPTIONS] [INPUT_FILE]\n", program_name);
01176 printf("\n");
01177 }
01178
01179 static int opt_format(const char *opt, const char *arg)
01180 {
01181 iformat = av_find_input_format(arg);
01182 if (!iformat) {
01183 fprintf(stderr, "Unknown input format: %s\n", arg);
01184 return AVERROR(EINVAL);
01185 }
01186 return 0;
01187 }
01188
01189 static void opt_input_file(void *optctx, const char *arg)
01190 {
01191 if (input_filename) {
01192 fprintf(stderr, "Argument '%s' provided as input filename, but '%s' was already specified.\n",
01193 arg, input_filename);
01194 exit(1);
01195 }
01196 if (!strcmp(arg, "-"))
01197 arg = "pipe:";
01198 input_filename = arg;
01199 }
01200
01201 static int opt_help(const char *opt, const char *arg)
01202 {
01203 av_log_set_callback(log_callback_help);
01204 show_usage();
01205 show_help_options(options, "Main options:\n", 0, 0);
01206 printf("\n");
01207
01208 show_help_children(avformat_get_class(), AV_OPT_FLAG_DECODING_PARAM);
01209
01210 return 0;
01211 }
01212
01213 static int opt_pretty(const char *opt, const char *arg)
01214 {
01215 show_value_unit = 1;
01216 use_value_prefix = 1;
01217 use_byte_value_binary_prefix = 1;
01218 use_value_sexagesimal_format = 1;
01219 return 0;
01220 }
01221
01222 static const OptionDef options[] = {
01223 #include "cmdutils_common_opts.h"
01224 { "f", HAS_ARG, {(void*)opt_format}, "force format", "format" },
01225 { "unit", OPT_BOOL, {(void*)&show_value_unit}, "show unit of the displayed values" },
01226 { "prefix", OPT_BOOL, {(void*)&use_value_prefix}, "use SI prefixes for the displayed values" },
01227 { "byte_binary_prefix", OPT_BOOL, {(void*)&use_byte_value_binary_prefix},
01228 "use binary prefixes for byte units" },
01229 { "sexagesimal", OPT_BOOL, {(void*)&use_value_sexagesimal_format},
01230 "use sexagesimal format HOURS:MM:SS.MICROSECONDS for time units" },
01231 { "pretty", 0, {(void*)&opt_pretty},
01232 "prettify the format of displayed values, make it more human readable" },
01233 { "print_format", OPT_STRING | HAS_ARG, {(void*)&print_format},
01234 "set the output printing format (available formats are: default, compact, csv, json)", "format" },
01235 { "show_format", OPT_BOOL, {(void*)&do_show_format} , "show format/container info" },
01236 { "show_packets", OPT_BOOL, {(void*)&do_show_packets}, "show packets info" },
01237 { "show_streams", OPT_BOOL, {(void*)&do_show_streams}, "show streams info" },
01238 { "default", HAS_ARG | OPT_AUDIO | OPT_VIDEO | OPT_EXPERT, {(void*)opt_default}, "generic catch all option", "" },
01239 { "i", HAS_ARG, {(void *)opt_input_file}, "read specified file", "input_file"},
01240 { NULL, },
01241 };
01242
01243 int main(int argc, char **argv)
01244 {
01245 int ret;
01246
01247 parse_loglevel(argc, argv, options);
01248 av_register_all();
01249 avformat_network_init();
01250 init_opts();
01251 #if CONFIG_AVDEVICE
01252 avdevice_register_all();
01253 #endif
01254
01255 show_banner(argc, argv, options);
01256 parse_options(NULL, argc, argv, options, opt_input_file);
01257
01258 if (!input_filename) {
01259 show_usage();
01260 fprintf(stderr, "You have to specify one input file.\n");
01261 fprintf(stderr, "Use -h to get full help or, even better, run 'man %s'.\n", program_name);
01262 exit(1);
01263 }
01264
01265 ret = probe_file(input_filename);
01266
01267 avformat_network_deinit();
01268
01269 return ret;
01270 }