Newer
Older
{"nk", "force no key printing", OFFSET(nokey), AV_OPT_TYPE_INT, {.i64=1}, 0, 1 },
{"escape", "set escape mode", OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="csv"}, CHAR_MIN, CHAR_MAX },
{"e", "set escape mode", OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="csv"}, CHAR_MIN, CHAR_MAX },
{"print_section", "print section name", OFFSET(print_section), AV_OPT_TYPE_INT, {.i64=1}, 0, 1 },
{"p", "print section name", OFFSET(print_section), AV_OPT_TYPE_INT, {.i64=1}, 0, 1 },
{NULL},
};
DEFINE_WRITER_CLASS(csv);
.name = "csv",
.priv_size = sizeof(CompactContext),
.print_section_header = compact_print_section_header,
.print_section_footer = compact_print_section_footer,
.print_integer = compact_print_int,
.print_string = compact_print_str,
.flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS,
/* Flat output */
typedef struct FlatContext {
const AVClass *class;
const char *sep_str;
char sep;
int hierarchical;
} FlatContext;
#undef OFFSET
#define OFFSET(x) offsetof(FlatContext, x)
static const AVOption flat_options[]= {
{"sep_char", "set separator", OFFSET(sep_str), AV_OPT_TYPE_STRING, {.str="."}, CHAR_MIN, CHAR_MAX },
{"s", "set separator", OFFSET(sep_str), AV_OPT_TYPE_STRING, {.str="."}, CHAR_MIN, CHAR_MAX },
{"hierarchical", "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_INT, {.i64=1}, 0, 1 },
{"h", "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_INT, {.i64=1}, 0, 1 },
DEFINE_WRITER_CLASS(flat);
static av_cold int flat_init(WriterContext *wctx)
{
FlatContext *flat = wctx->priv;
if (strlen(flat->sep_str) != 1) {
av_log(wctx, AV_LOG_ERROR, "Item separator '%s' specified, but must contain a single character\n",
flat->sep_str);
return AVERROR(EINVAL);
}
flat->sep = flat->sep_str[0];
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
return 0;
}
static const char *flat_escape_key_str(AVBPrint *dst, const char *src, const char sep)
{
const char *p;
for (p = src; *p; p++) {
if (!((*p >= '0' && *p <= '9') ||
(*p >= 'a' && *p <= 'z') ||
(*p >= 'A' && *p <= 'Z')))
av_bprint_chars(dst, '_', 1);
else
av_bprint_chars(dst, *p, 1);
}
return dst->str;
}
static const char *flat_escape_value_str(AVBPrint *dst, const char *src)
{
const char *p;
for (p = src; *p; p++) {
switch (*p) {
case '\n': av_bprintf(dst, "%s", "\\n"); break;
case '\r': av_bprintf(dst, "%s", "\\r"); break;
case '\\': av_bprintf(dst, "%s", "\\\\"); break;
case '"': av_bprintf(dst, "%s", "\\\""); break;
case '`': av_bprintf(dst, "%s", "\\`"); break;
case '$': av_bprintf(dst, "%s", "\\$"); break;
default: av_bprint_chars(dst, *p, 1); break;
}
}
return dst->str;
}
static void flat_print_section_header(WriterContext *wctx)
{
FlatContext *flat = wctx->priv;
AVBPrint *buf = &wctx->section_pbuf[wctx->level];
const struct section *section = wctx->section[wctx->level];
const struct section *parent_section = wctx->level ?
wctx->section[wctx->level-1] : NULL;
/* build section header */
av_bprint_clear(buf);
av_bprintf(buf, "%s", wctx->section_pbuf[wctx->level-1].str);
if (flat->hierarchical ||
!(section->flags & (SECTION_FLAG_IS_ARRAY|SECTION_FLAG_IS_WRAPPER))) {
av_bprintf(buf, "%s%s", wctx->section[wctx->level]->name, flat->sep_str);
if (parent_section->flags & SECTION_FLAG_IS_ARRAY) {
int n = parent_section->id == SECTION_ID_PACKETS_AND_FRAMES ?
wctx->nb_section_packet_frame : wctx->nb_item[wctx->level-1];
av_bprintf(buf, "%d%s", n, flat->sep_str);
}
}
static void flat_print_int(WriterContext *wctx, const char *key, long long int value)
{
printf("%s%s=%lld\n", wctx->section_pbuf[wctx->level].str, key, value);
}
static void flat_print_str(WriterContext *wctx, const char *key, const char *value)
{
FlatContext *flat = wctx->priv;
AVBPrint buf;
printf("%s", wctx->section_pbuf[wctx->level].str);
av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
printf("%s=", flat_escape_key_str(&buf, key, flat->sep));
av_bprint_clear(&buf);
printf("\"%s\"\n", flat_escape_value_str(&buf, value));
av_bprint_finalize(&buf, NULL);
}
static const Writer flat_writer = {
.name = "flat",
.priv_size = sizeof(FlatContext),
.init = flat_init,
.print_section_header = flat_print_section_header,
.print_integer = flat_print_int,
.print_string = flat_print_str,
.flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS|WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER,
Stefano Sabatini
committed
.priv_class = &flat_class,
/* INI format output */
typedef struct {
const AVClass *class;
int hierarchical;
} INIContext;
#undef OFFSET
#define OFFSET(x) offsetof(INIContext, x)
static const AVOption ini_options[] = {
{"hierarchical", "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_INT, {.i64=1}, 0, 1 },
{"h", "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_INT, {.i64=1}, 0, 1 },
DEFINE_WRITER_CLASS(ini);
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
static char *ini_escape_str(AVBPrint *dst, const char *src)
{
int i = 0;
char c = 0;
while (c = src[i++]) {
switch (c) {
case '\b': av_bprintf(dst, "%s", "\\b"); break;
case '\f': av_bprintf(dst, "%s", "\\f"); break;
case '\n': av_bprintf(dst, "%s", "\\n"); break;
case '\r': av_bprintf(dst, "%s", "\\r"); break;
case '\t': av_bprintf(dst, "%s", "\\t"); break;
case '\\':
case '#' :
case '=' :
case ':' : av_bprint_chars(dst, '\\', 1);
default:
if ((unsigned char)c < 32)
av_bprintf(dst, "\\x00%02x", c & 0xff);
else
av_bprint_chars(dst, c, 1);
break;
}
}
return dst->str;
}
static void ini_print_section_header(WriterContext *wctx)
AVBPrint *buf = &wctx->section_pbuf[wctx->level];
const struct section *section = wctx->section[wctx->level];
const struct section *parent_section = wctx->level ?
wctx->section[wctx->level-1] : NULL;
av_bprint_clear(buf);
if (!parent_section) {
printf("# ffprobe output\n\n");
return;
}
if (wctx->nb_item[wctx->level-1])
av_bprintf(buf, "%s", wctx->section_pbuf[wctx->level-1].str);
if (ini->hierarchical ||
!(section->flags & (SECTION_FLAG_IS_ARRAY|SECTION_FLAG_IS_WRAPPER))) {
av_bprintf(buf, "%s%s", buf->str[0] ? "." : "", wctx->section[wctx->level]->name);
if (parent_section->flags & SECTION_FLAG_IS_ARRAY) {
int n = parent_section->id == SECTION_ID_PACKETS_AND_FRAMES ?
wctx->nb_section_packet_frame : wctx->nb_item[wctx->level-1];
av_bprintf(buf, ".%d", n);
}
if (!(section->flags & (SECTION_FLAG_IS_ARRAY|SECTION_FLAG_IS_WRAPPER)))
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
}
static void ini_print_str(WriterContext *wctx, const char *key, const char *value)
{
AVBPrint buf;
av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
printf("%s=", ini_escape_str(&buf, key));
av_bprint_clear(&buf);
printf("%s\n", ini_escape_str(&buf, value));
av_bprint_finalize(&buf, NULL);
}
static void ini_print_int(WriterContext *wctx, const char *key, long long int value)
{
printf("%s=%lld\n", key, value);
}
static const Writer ini_writer = {
.name = "ini",
.priv_size = sizeof(INIContext),
.print_section_header = ini_print_section_header,
.print_integer = ini_print_int,
.print_string = ini_print_str,
.flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS|WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER,
Stefano Sabatini
committed
.priv_class = &ini_class,
/* JSON output */
typedef struct {
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, {.i64=0}, 0, 1 },
{ "c", "enable compact output", OFFSET(compact), AV_OPT_TYPE_INT, {.i64=0}, 0, 1 },
DEFINE_WRITER_CLASS(json);
static av_cold int json_init(WriterContext *wctx)
{
JSONContext *json = wctx->priv;
json->item_sep = json->compact ? ", " : ",\n";
json->item_start_end = json->compact ? " " : "\n";
return 0;
}
static const char *json_escape_str(AVBPrint *dst, 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;
for (p = src; *p; p++) {
char *s = strchr(json_escape, *p);
if (s) {
av_bprint_chars(dst, '\\', 1);
av_bprint_chars(dst, json_subst[s - json_escape], 1);
} else if ((unsigned char)*p < 32) {
} else {
}
}
}
#define JSON_INDENT() printf("%*c", json->indent_level * 4, ' ')
static void json_print_section_header(WriterContext *wctx)
{
JSONContext *json = wctx->priv;
const struct section *section = wctx->section[wctx->level];
const struct section *parent_section = wctx->level ?
wctx->section[wctx->level-1] : NULL;
if (wctx->level && wctx->nb_item[wctx->level-1])
printf(",\n");
if (section->flags & SECTION_FLAG_IS_WRAPPER) {
printf("{\n");
json->indent_level++;
} else {
av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
json_escape_str(&buf, section->name, wctx);
JSON_INDENT();
Stefano Sabatini
committed
json->indent_level++;
if (section->flags & SECTION_FLAG_IS_ARRAY) {
printf("\"%s\": [\n", buf.str);
} else if (parent_section && !(parent_section->flags & SECTION_FLAG_IS_ARRAY)) {
printf("\"%s\": {%s", buf.str, json->item_start_end);
} else {
printf("{%s", json->item_start_end);
/* this is required so the parser can distinguish between packets and frames */
if (parent_section && parent_section->id == SECTION_ID_PACKETS_AND_FRAMES) {
if (!json->compact)
JSON_INDENT();
printf("\"type\": \"%s\"%s", section->name, json->item_sep);
}
}
av_bprint_finalize(&buf, NULL);
}
static void json_print_section_footer(WriterContext *wctx)
{
JSONContext *json = wctx->priv;
const struct section *section = wctx->section[wctx->level];
if (wctx->level == 0) {
json->indent_level--;
printf("\n}\n");
} else if (section->flags & SECTION_FLAG_IS_ARRAY) {
json->indent_level--;
JSON_INDENT();
printf("]");
} else {
printf("%s", json->item_start_end);
json->indent_level--;
if (!json->compact)
JSON_INDENT();
}
static inline void json_print_item_str(WriterContext *wctx,
const char *key, const char *value)
av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
printf("\"%s\":", json_escape_str(&buf, key, wctx));
printf(" \"%s\"", json_escape_str(&buf, value, wctx));
av_bprint_finalize(&buf, NULL);
}
static void json_print_str(WriterContext *wctx, const char *key, const char *value)
{
JSONContext *json = wctx->priv;
if (wctx->nb_item[wctx->level])
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[wctx->level])
printf("%s", json->item_sep);
if (!json->compact)
JSON_INDENT();
av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
printf("\"%s\": %lld", json_escape_str(&buf, key, wctx), value);
av_bprint_finalize(&buf, NULL);
.name = "json",
.priv_size = sizeof(JSONContext),
.init = json_init,
.print_section_header = json_print_section_header,
.print_section_footer = json_print_section_footer,
.print_integer = json_print_int,
.print_string = json_print_str,
.flags = WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER,
Stefano Sabatini
committed
.priv_class = &json_class,
};
/* XML output */
typedef struct {
const AVClass *class;
int within_tag;
int indent_level;
int fully_qualified;
int xsd_strict;
} XMLContext;
#undef OFFSET
#define OFFSET(x) offsetof(XMLContext, x)
static const AVOption xml_options[] = {
{"fully_qualified", "specify if the output should be fully qualified", OFFSET(fully_qualified), AV_OPT_TYPE_INT, {.i64=0}, 0, 1 },
{"q", "specify if the output should be fully qualified", OFFSET(fully_qualified), AV_OPT_TYPE_INT, {.i64=0}, 0, 1 },
{"xsd_strict", "ensure that the output is XSD compliant", OFFSET(xsd_strict), AV_OPT_TYPE_INT, {.i64=0}, 0, 1 },
{"x", "ensure that the output is XSD compliant", OFFSET(xsd_strict), AV_OPT_TYPE_INT, {.i64=0}, 0, 1 },
DEFINE_WRITER_CLASS(xml);
static av_cold int xml_init(WriterContext *wctx)
{
XMLContext *xml = wctx->priv;
if (xml->xsd_strict) {
xml->fully_qualified = 1;
#define CHECK_COMPLIANCE(opt, opt_name) \
if (opt) { \
av_log(wctx, AV_LOG_ERROR, \
"XSD-compliant output selected but option '%s' was selected, XML output may be non-compliant.\n" \
"You need to disable such option with '-no%s'\n", opt_name, opt_name); \
Stefano Sabatini
committed
return AVERROR(EINVAL); \
}
CHECK_COMPLIANCE(show_private_data, "private");
CHECK_COMPLIANCE(show_value_unit, "unit");
CHECK_COMPLIANCE(use_value_prefix, "prefix");
if (do_show_frames && do_show_packets) {
av_log(wctx, AV_LOG_ERROR,
"Interleaved frames and packets are not allowed in XSD. "
"Select only one between the -show_frames and the -show_packets options.\n");
return AVERROR(EINVAL);
}
static const char *xml_escape_str(AVBPrint *dst, const char *src, void *log_ctx)
case '&' : av_bprintf(dst, "%s", "&"); break;
case '<' : av_bprintf(dst, "%s", "<"); break;
case '>' : av_bprintf(dst, "%s", ">"); break;
case '"' : av_bprintf(dst, "%s", """); break;
case '\'': av_bprintf(dst, "%s", "'"); break;
default: av_bprint_chars(dst, *p, 1);
#define XML_INDENT() printf("%*c", xml->indent_level * 4, ' ')
static void xml_print_section_header(WriterContext *wctx)
const struct section *section = wctx->section[wctx->level];
const struct section *parent_section = wctx->level ?
wctx->section[wctx->level-1] : NULL;
if (wctx->level == 0) {
const char *qual = " xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' "
"xmlns:ffprobe='http://www.ffmpeg.org/schema/ffprobe' "
"xsi:schemaLocation='http://www.ffmpeg.org/schema/ffprobe ffprobe.xsd'";
printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
printf("<%sffprobe%s>\n",
xml->fully_qualified ? "ffprobe:" : "",
xml->fully_qualified ? qual : "");
return;
if (xml->within_tag) {
xml->within_tag = 0;
printf(">\n");
if (section->flags & SECTION_FLAG_HAS_VARIABLE_FIELDS) {
xml->indent_level++;
} else {
if (parent_section && (parent_section->flags & SECTION_FLAG_IS_WRAPPER) &&
wctx->level && wctx->nb_item[wctx->level-1])
printf("\n");
xml->indent_level++;
if (section->flags & SECTION_FLAG_IS_ARRAY) {
XML_INDENT(); printf("<%s>\n", section->name);
} else {
XML_INDENT(); printf("<%s ", section->name);
xml->within_tag = 1;
}
}
static void xml_print_section_footer(WriterContext *wctx)
const struct section *section = wctx->section[wctx->level];
if (wctx->level == 0) {
printf("</%sffprobe>\n", xml->fully_qualified ? "ffprobe:" : "");
} else if (xml->within_tag) {
xml->within_tag = 0;
xml->indent_level--;
} else if (section->flags & SECTION_FLAG_HAS_VARIABLE_FIELDS) {
xml->indent_level--;
} else {
XML_INDENT(); printf("</%s>\n", section->name);
xml->indent_level--;
}
}
static void xml_print_str(WriterContext *wctx, const char *key, const char *value)
{
XMLContext *xml = wctx->priv;
const struct section *section = wctx->section[wctx->level];
av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
if (section->flags & SECTION_FLAG_HAS_VARIABLE_FIELDS) {
printf("<%s key=\"%s\"",
section->element_name, xml_escape_str(&buf, key, wctx));
av_bprint_clear(&buf);
printf(" value=\"%s\"/>\n", xml_escape_str(&buf, value, wctx));
} else {
if (wctx->nb_item[wctx->level])
printf(" ");
printf("%s=\"%s\"", key, xml_escape_str(&buf, value, wctx));
}
}
static void xml_print_int(WriterContext *wctx, const char *key, long long int value)
{
if (wctx->nb_item[wctx->level])
printf(" ");
printf("%s=\"%lld\"", key, value);
}
static Writer xml_writer = {
.name = "xml",
.priv_size = sizeof(XMLContext),
.init = xml_init,
.print_section_header = xml_print_section_header,
.print_section_footer = xml_print_section_footer,
.print_integer = xml_print_int,
.print_string = xml_print_str,
.flags = WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER,
Stefano Sabatini
committed
.priv_class = &xml_class,
static void writer_register_all(void)
{
static int initialized;
if (initialized)
return;
initialized = 1;
writer_register(&default_writer);
writer_register(&json_writer);
}
#define print_fmt(k, f, ...) do { \
av_bprint_clear(&pbuf); \
av_bprintf(&pbuf, f, __VA_ARGS__); \
writer_print_string(w, k, pbuf.str, 0); \
} while (0)
#define print_int(k, v) writer_print_integer(w, k, v)
#define print_q(k, v, s) writer_print_rational(w, k, v, s)
#define print_str(k, v) writer_print_string(w, k, v, 0)
#define print_str_opt(k, v) writer_print_string(w, k, v, PRINT_STRING_OPT)
#define print_str_validate(k, v) writer_print_string(w, k, v, PRINT_STRING_VALIDATE)
#define print_time(k, v, tb) writer_print_time(w, k, v, tb, 0)
#define print_ts(k, v) writer_print_ts(w, k, v, 0)
#define print_duration_time(k, v, tb) writer_print_time(w, k, v, tb, 1)
#define print_duration_ts(k, v) writer_print_ts(w, k, v, 1)
#define print_val(k, v, u) do { \
struct unit_value uv; \
uv.val.i = v; \
uv.unit = u; \
writer_print_string(w, k, value_string(val_str, sizeof(val_str), uv), 0); \
} while (0)
#define print_section_header(s) writer_print_section_header(w, s)
#define print_section_footer(s) writer_print_section_footer(w, s)
static inline int show_tags(WriterContext *w, AVDictionary *tags, int section_id)
{
AVDictionaryEntry *tag = NULL;
writer_print_section_header(w, section_id);
while ((tag = av_dict_get(tags, "", tag, AV_DICT_IGNORE_SUFFIX))) {
if ((ret = print_str_validate(tag->key, tag->value)) < 0)
writer_print_section_footer(w);
static void show_packet(WriterContext *w, AVFormatContext *fmt_ctx, AVPacket *pkt, int packet_idx)
{
char val_str[128];
AVStream *st = fmt_ctx->streams[pkt->stream_index];
const char *s;
av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
writer_print_section_header(w, SECTION_ID_PACKET);
s = av_get_media_type_string(st->codec->codec_type);
if (s) print_str ("codec_type", s);
else print_str_opt("codec_type", "unknown");
print_int("stream_index", pkt->stream_index);
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_duration_ts("duration", pkt->duration);
print_duration_time("duration_time", pkt->duration, &st->time_base);
print_duration_ts("convergence_duration", pkt->convergence_duration);
print_duration_time("convergence_duration_time", pkt->convergence_duration, &st->time_base);
print_val("size", pkt->size, unit_byte_str);
if (pkt->pos != -1) print_fmt ("pos", "%"PRId64, pkt->pos);
else print_str_opt("pos", "N/A");
print_fmt("flags", "%c", pkt->flags & AV_PKT_FLAG_KEY ? 'K' : '_');
if (do_show_data)
writer_print_data(w, "data", pkt->data, pkt->size);
writer_print_section_footer(w);
av_bprint_finalize(&pbuf, NULL);
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
static void show_subtitle(WriterContext *w, AVSubtitle *sub, AVStream *stream,
AVFormatContext *fmt_ctx)
{
AVBPrint pbuf;
av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
writer_print_section_header(w, SECTION_ID_SUBTITLE);
print_str ("media_type", "subtitle");
print_ts ("pts", sub->pts);
print_time("pts_time", sub->pts, &AV_TIME_BASE_Q);
print_int ("format", sub->format);
print_int ("start_display_time", sub->start_display_time);
print_int ("end_display_time", sub->end_display_time);
print_int ("num_rects", sub->num_rects);
writer_print_section_footer(w);
av_bprint_finalize(&pbuf, NULL);
fflush(stdout);
}
Stefano Sabatini
committed
static void show_frame(WriterContext *w, AVFrame *frame, AVStream *stream,
AVFormatContext *fmt_ctx)
const char *s;
av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
writer_print_section_header(w, SECTION_ID_FRAME);
s = av_get_media_type_string(stream->codec->codec_type);
if (s) print_str ("media_type", s);
else print_str_opt("media_type", "unknown");
print_int("key_frame", frame->key_frame);
print_ts ("pkt_pts", frame->pkt_pts);
print_time("pkt_pts_time", frame->pkt_pts, &stream->time_base);
print_ts ("pkt_dts", frame->pkt_dts);
print_time("pkt_dts_time", frame->pkt_dts, &stream->time_base);
print_duration_ts ("pkt_duration", av_frame_get_pkt_duration(frame));
print_duration_time("pkt_duration_time", av_frame_get_pkt_duration(frame), &stream->time_base);
if (av_frame_get_pkt_pos (frame) != -1) print_fmt ("pkt_pos", "%"PRId64, av_frame_get_pkt_pos(frame));
else print_str_opt("pkt_pos", "N/A");
if (av_frame_get_pkt_size(frame) != -1) print_fmt ("pkt_size", "%d", av_frame_get_pkt_size(frame));
else print_str_opt("pkt_size", "N/A");
switch (stream->codec->codec_type) {
Stefano Sabatini
committed
AVRational sar;
case AVMEDIA_TYPE_VIDEO:
print_int("width", frame->width);
print_int("height", frame->height);
s = av_get_pix_fmt_name(frame->format);
if (s) print_str ("pix_fmt", s);
else print_str_opt("pix_fmt", "unknown");
Stefano Sabatini
committed
sar = av_guess_sample_aspect_ratio(fmt_ctx, stream, frame);
if (sar.num) {
print_q("sample_aspect_ratio", sar, ':');
} else {
print_str_opt("sample_aspect_ratio", "N/A");
}
print_fmt("pict_type", "%c", av_get_picture_type_char(frame->pict_type));
print_int("coded_picture_number", frame->coded_picture_number);
print_int("display_picture_number", frame->display_picture_number);
print_int("interlaced_frame", frame->interlaced_frame);
print_int("top_field_first", frame->top_field_first);
print_int("repeat_pict", frame->repeat_pict);
break;
case AVMEDIA_TYPE_AUDIO:
s = av_get_sample_fmt_name(frame->format);
if (s) print_str ("sample_fmt", s);
else print_str_opt("sample_fmt", "unknown");
print_int("nb_samples", frame->nb_samples);
print_int("channels", av_frame_get_channels(frame));
if (av_frame_get_channel_layout(frame)) {
av_bprint_clear(&pbuf);
av_bprint_channel_layout(&pbuf, av_frame_get_channels(frame),
av_frame_get_channel_layout(frame));
print_str ("channel_layout", pbuf.str);
} else
print_str_opt("channel_layout", "unknown");
break;
}
show_tags(w, av_frame_get_metadata(frame), SECTION_ID_FRAME_TAGS);
writer_print_section_footer(w);
av_bprint_finalize(&pbuf, NULL);
fflush(stdout);
}
static av_always_inline int process_frame(WriterContext *w,
AVFormatContext *fmt_ctx,
AVFrame *frame, AVPacket *pkt)
{
AVCodecContext *dec_ctx = fmt_ctx->streams[pkt->stream_index]->codec;
if (dec_ctx->codec) {
switch (dec_ctx->codec_type) {
case AVMEDIA_TYPE_VIDEO:
ret = avcodec_decode_video2(dec_ctx, frame, &got_frame, pkt);
case AVMEDIA_TYPE_AUDIO:
ret = avcodec_decode_audio4(dec_ctx, frame, &got_frame, pkt);
case AVMEDIA_TYPE_SUBTITLE:
ret = avcodec_decode_subtitle2(dec_ctx, &sub, &got_frame, pkt);
break;
if (ret < 0)
return ret;
ret = FFMIN(ret, pkt->size); /* guard against bogus return values */
pkt->data += ret;
pkt->size -= ret;
if (got_frame) {
int is_sub = (dec_ctx->codec_type == AVMEDIA_TYPE_SUBTITLE);
nb_streams_frames[pkt->stream_index]++;
if (do_show_frames)
if (is_sub)
show_subtitle(w, &sub, fmt_ctx->streams[pkt->stream_index], fmt_ctx);
else
show_frame(w, frame, fmt_ctx->streams[pkt->stream_index], fmt_ctx);
if (is_sub)
avsubtitle_free(&sub);
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
static void log_read_interval(const ReadInterval *interval, void *log_ctx, int log_level)
{
av_log(log_ctx, log_level, "id:%d", interval->id);
if (interval->has_start) {
av_log(log_ctx, log_level, " start:%s%s", interval->start_is_offset ? "+" : "",
av_ts2timestr(interval->start, &AV_TIME_BASE_Q));
} else {
av_log(log_ctx, log_level, " start:N/A");
}
if (interval->has_end) {
av_log(log_ctx, log_level, " end:%s", interval->end_is_offset ? "+" : "");
if (interval->duration_frames)
av_log(log_ctx, log_level, "#%"PRId64, interval->end);
else
av_log(log_ctx, log_level, "%s", av_ts2timestr(interval->end, &AV_TIME_BASE_Q));
} else {
av_log(log_ctx, log_level, " end:N/A");
}
av_log(log_ctx, log_level, "\n");
}
static int read_interval_packets(WriterContext *w, AVFormatContext *fmt_ctx,
const ReadInterval *interval, int64_t *cur_ts)
AVPacket pkt, pkt1;
AVFrame frame;
int ret = 0, i = 0, frame_count = 0;
int64_t start = -INT64_MAX, end = interval->end;
int has_start = 0, has_end = interval->has_end && !interval->end_is_offset;
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
av_log(NULL, AV_LOG_VERBOSE, "Processing read interval ");
log_read_interval(interval, NULL, AV_LOG_VERBOSE);
if (interval->has_start) {
int64_t target;
if (interval->start_is_offset) {
if (*cur_ts == AV_NOPTS_VALUE) {
av_log(NULL, AV_LOG_ERROR,
"Could not seek to relative position since current "
"timestamp is not defined\n");
ret = AVERROR(EINVAL);
goto end;
}
target = *cur_ts + interval->start;
} else {
target = interval->start;
}
av_log(NULL, AV_LOG_VERBOSE, "Seeking to read interval start point %s\n",
av_ts2timestr(target, &AV_TIME_BASE_Q));
if ((ret = avformat_seek_file(fmt_ctx, -1, -INT64_MAX, target, INT64_MAX, 0)) < 0) {
av_log(NULL, AV_LOG_ERROR, "Could not seek to position %"PRId64": %s\n",
interval->start, av_err2str(ret));
goto end;
}
}
while (!av_read_frame(fmt_ctx, &pkt)) {
if (selected_streams[pkt.stream_index]) {
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
AVRational tb = fmt_ctx->streams[pkt.stream_index]->time_base;
if (pkt.pts != AV_NOPTS_VALUE)
*cur_ts = av_rescale_q(pkt.pts, tb, AV_TIME_BASE_Q);
if (!has_start && *cur_ts != AV_NOPTS_VALUE) {
start = *cur_ts;
has_start = 1;
}
if (has_start && !has_end && interval->end_is_offset) {
end = start + interval->end;
has_end = 1;
}
if (interval->end_is_offset && interval->duration_frames) {
if (frame_count >= interval->end)
break;
} else if (has_end && *cur_ts != AV_NOPTS_VALUE && *cur_ts >= end) {
break;
}
frame_count++;
if (do_read_packets) {
if (do_show_packets)
show_packet(w, fmt_ctx, &pkt, i++);
nb_streams_packets[pkt.stream_index]++;
}
if (do_read_frames) {
pkt1 = pkt;
while (pkt1.size && process_frame(w, fmt_ctx, &frame, &pkt1) > 0);
}
}
av_init_packet(&pkt);
pkt.data = NULL;
pkt.size = 0;
//Flush remaining frames that are cached in the decoder
for (i = 0; i < fmt_ctx->nb_streams; i++) {
pkt.stream_index = i;
if (do_read_frames)
while (process_frame(w, fmt_ctx, &frame, &pkt) > 0);
end:
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Could not read packets in interval ");
log_read_interval(interval, NULL, AV_LOG_ERROR);
}
return ret;
}
static int read_packets(WriterContext *w, AVFormatContext *fmt_ctx)
{
int i, ret = 0;
int64_t cur_ts = fmt_ctx->start_time;
if (read_intervals_nb == 0) {
ReadInterval interval = (ReadInterval) { .has_start = 0, .has_end = 0 };
ret = read_interval_packets(w, fmt_ctx, &interval, &cur_ts);
} else {
for (i = 0; i < read_intervals_nb; i++) {
ret = read_interval_packets(w, fmt_ctx, &read_intervals[i], &cur_ts);
if (ret < 0)
break;
}
}
static int show_stream(WriterContext *w, AVFormatContext *fmt_ctx, int stream_idx, int in_program)
{
AVStream *stream = fmt_ctx->streams[stream_idx];
AVCodecContext *dec_ctx;
const char *s;
Stefano Sabatini
committed
AVRational sar, dar;
av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
writer_print_section_header(w, in_program ? SECTION_ID_PROGRAM_STREAM : SECTION_ID_STREAM);
print_int("index", stream->index);
const char *profile = NULL;
dec = dec_ctx->codec;
if (dec) {
print_str("codec_name", dec->name);
if (!do_bitexact) {
if (dec->long_name) print_str ("codec_long_name", dec->long_name);
else print_str_opt("codec_long_name", "unknown");
print_str_opt("codec_name", "unknown");
print_str_opt("codec_long_name", "unknown");
if (dec && (profile = av_get_profile_name(dec, dec_ctx->profile)))
print_str("profile", profile);
else
print_str_opt("profile", "unknown");
s = av_get_media_type_string(dec_ctx->codec_type);
if (s) print_str ("codec_type", s);
else print_str_opt("codec_type", "unknown");
print_q("codec_time_base", dec_ctx->time_base, '/');
av_get_codec_tag_string(val_str, sizeof(val_str), dec_ctx->codec_tag);
print_str("codec_tag_string", val_str);
print_fmt("codec_tag", "0x%04x", dec_ctx->codec_tag);