mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2024-11-21 10:55:51 +02:00
ffprobe: use avbprint API
Simplify, increase robustness.
This commit is contained in:
parent
6101e5322f
commit
f0bb1a599c
265
ffprobe.c
265
ffprobe.c
@ -29,6 +29,7 @@
|
||||
#include "libavformat/avformat.h"
|
||||
#include "libavcodec/avcodec.h"
|
||||
#include "libavutil/avstring.h"
|
||||
#include "libavutil/bprint.h"
|
||||
#include "libavutil/opt.h"
|
||||
#include "libavutil/pixdesc.h"
|
||||
#include "libavutil/dict.h"
|
||||
@ -384,31 +385,6 @@ fail:
|
||||
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 */
|
||||
@ -487,81 +463,51 @@ static const Writer default_writer = {
|
||||
* 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)
|
||||
static const char *c_escape_str(AVBPrint *dst, 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;
|
||||
case '\n': av_bprintf(dst, "%s", "\\n"); break;
|
||||
case '\r': av_bprintf(dst, "%s", "\\r"); break;
|
||||
case '\\': av_bprintf(dst, "%s", "\\\\"); break;
|
||||
default:
|
||||
if (*p == sep)
|
||||
*q++ = '\\';
|
||||
*q++ = *p;
|
||||
av_bprint_chars(dst, '\\', 1);
|
||||
av_bprint_chars(dst, *p, 1);
|
||||
}
|
||||
}
|
||||
*q = 0;
|
||||
return *dst;
|
||||
return dst->str;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
static const char *csv_escape_str(AVBPrint *dst, 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);
|
||||
/* check if input needs quoting */
|
||||
for (p = src; *p; p++)
|
||||
if (*p == '"' || *p == sep || *p == '\n' || *p == '\r')
|
||||
if (!quote) {
|
||||
quote = 1;
|
||||
size += 2;
|
||||
}
|
||||
if (*p == '"')
|
||||
size++;
|
||||
}
|
||||
quote = 1;
|
||||
|
||||
ESCAPE_REALLOC_BUF(dst_size, dst, src, size);
|
||||
|
||||
q = *dst;
|
||||
p = src;
|
||||
if (quote)
|
||||
*q++ = '\"';
|
||||
while (*p) {
|
||||
av_bprint_chars(dst, '\"', 1);
|
||||
|
||||
for (p = src; *p; p++) {
|
||||
if (*p == '"')
|
||||
*q++ = '\"';
|
||||
*q++ = *p++;
|
||||
av_bprint_chars(dst, '\"', 1);
|
||||
av_bprint_chars(dst, *p, 1);
|
||||
}
|
||||
if (quote)
|
||||
*q++ = '\"';
|
||||
*q = 0;
|
||||
|
||||
return *dst;
|
||||
av_bprint_chars(dst, '\"', 1);
|
||||
return dst->str;
|
||||
}
|
||||
|
||||
static const char *none_escape_str(char **dst, size_t *dst_size,
|
||||
const char *src, const char sep, void *log_ctx)
|
||||
static const char *none_escape_str(AVBPrint *dst, const char *src, const char sep, void *log_ctx)
|
||||
{
|
||||
return src;
|
||||
}
|
||||
@ -571,11 +517,8 @@ typedef struct CompactContext {
|
||||
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);
|
||||
const char * (*escape_str)(AVBPrint *dst, const char *src, const char sep, void *log_ctx);
|
||||
} CompactContext;
|
||||
|
||||
#define OFFSET(x) offsetof(CompactContext, x)
|
||||
@ -621,10 +564,6 @@ static av_cold int compact_init(WriterContext *wctx, const char *args, void *opa
|
||||
}
|
||||
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;
|
||||
@ -641,7 +580,6 @@ 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);
|
||||
}
|
||||
|
||||
@ -660,12 +598,14 @@ static void compact_print_section_footer(WriterContext *wctx, const char *sectio
|
||||
static void compact_print_str(WriterContext *wctx, const char *key, const char *value)
|
||||
{
|
||||
CompactContext *compact = wctx->priv;
|
||||
AVBPrint buf;
|
||||
|
||||
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));
|
||||
av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
|
||||
printf("%s", compact->escape_str(&buf, value, compact->item_sep, wctx));
|
||||
av_bprint_finalize(&buf, NULL);
|
||||
}
|
||||
|
||||
static void compact_print_int(WriterContext *wctx, const char *key, long long int value)
|
||||
@ -682,14 +622,20 @@ static void compact_show_tags(WriterContext *wctx, AVDictionary *dict)
|
||||
{
|
||||
CompactContext *compact = wctx->priv;
|
||||
AVDictionaryEntry *tag = NULL;
|
||||
AVBPrint buf;
|
||||
|
||||
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));
|
||||
|
||||
if (!compact->nokey) {
|
||||
av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
|
||||
printf("tag:%s=", compact->escape_str(&buf, tag->key, compact->item_sep, wctx));
|
||||
av_bprint_finalize(&buf, NULL);
|
||||
}
|
||||
|
||||
av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
|
||||
printf("%s", compact->escape_str(&buf, tag->value, compact->item_sep, wctx));
|
||||
av_bprint_finalize(&buf, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
@ -731,8 +677,6 @@ static const Writer csv_writer = {
|
||||
typedef struct {
|
||||
const AVClass *class;
|
||||
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;
|
||||
@ -776,52 +720,27 @@ static av_cold int json_init(WriterContext *wctx, const char *args, void *opaque
|
||||
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_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;
|
||||
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];
|
||||
av_bprint_chars(dst, '\\', 1);
|
||||
av_bprint_chars(dst, json_subst[s - json_escape], 1);
|
||||
} else if ((unsigned char)*p < 32) {
|
||||
snprintf(q, 7, "\\u00%02x", *p & 0xff);
|
||||
q += 6;
|
||||
av_bprintf(dst, "\\u00%02x", *p & 0xff);
|
||||
} else {
|
||||
*q++ = *p;
|
||||
av_bprint_chars(dst, *p, 1);
|
||||
}
|
||||
}
|
||||
*q = 0;
|
||||
return *dst;
|
||||
return dst->str;
|
||||
}
|
||||
|
||||
static void json_print_header(WriterContext *wctx)
|
||||
@ -843,6 +762,7 @@ static void json_print_footer(WriterContext *wctx)
|
||||
static void json_print_chapter_header(WriterContext *wctx, const char *chapter)
|
||||
{
|
||||
JSONContext *json = wctx->priv;
|
||||
AVBPrint buf;
|
||||
|
||||
if (wctx->nb_chapter)
|
||||
printf(",");
|
||||
@ -852,7 +772,9 @@ static void json_print_chapter_header(WriterContext *wctx, const char *chapter)
|
||||
!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));
|
||||
av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
|
||||
printf("\"%s\": [\n", json_escape_str(&buf, chapter, wctx));
|
||||
av_bprint_finalize(&buf, NULL);
|
||||
json->print_packets_and_frames = !strcmp(chapter, "packets_and_frames");
|
||||
json->indent_level++;
|
||||
}
|
||||
@ -903,10 +825,15 @@ static void json_print_section_footer(WriterContext *wctx, const char *section)
|
||||
static inline void json_print_item_str(WriterContext *wctx,
|
||||
const char *key, const char *value)
|
||||
{
|
||||
JSONContext *json = wctx->priv;
|
||||
AVBPrint buf;
|
||||
|
||||
printf("\"%s\":", json_escape_str(&json->buf, &json->buf_size, key, wctx));
|
||||
printf(" \"%s\"", json_escape_str(&json->buf, &json->buf_size, value, wctx));
|
||||
av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
|
||||
printf("\"%s\":", json_escape_str(&buf, key, wctx));
|
||||
av_bprint_finalize(&buf, NULL);
|
||||
|
||||
av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
|
||||
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)
|
||||
@ -922,12 +849,15 @@ static void json_print_str(WriterContext *wctx, const char *key, const char *val
|
||||
static void json_print_int(WriterContext *wctx, const char *key, long long int value)
|
||||
{
|
||||
JSONContext *json = wctx->priv;
|
||||
AVBPrint buf;
|
||||
|
||||
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);
|
||||
|
||||
av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
|
||||
printf("\"%s\": %lld", json_escape_str(&buf, key, wctx), value);
|
||||
av_bprint_finalize(&buf, NULL);
|
||||
}
|
||||
|
||||
static void json_show_tags(WriterContext *wctx, AVDictionary *dict)
|
||||
@ -960,7 +890,6 @@ static const Writer json_writer = {
|
||||
.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,
|
||||
@ -982,8 +911,6 @@ typedef struct {
|
||||
int indent_level;
|
||||
int fully_qualified;
|
||||
int xsd_strict;
|
||||
char *buf;
|
||||
size_t buf_size;
|
||||
} XMLContext;
|
||||
|
||||
#undef OFFSET
|
||||
@ -1043,61 +970,25 @@ static av_cold int xml_init(WriterContext *wctx, const char *args, void *opaque)
|
||||
}
|
||||
}
|
||||
|
||||
xml->buf_size = ESCAPE_INIT_BUF_SIZE;
|
||||
if (!(xml->buf = av_malloc(xml->buf_size)))
|
||||
return AVERROR(ENOMEM);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static av_cold void xml_uninit(WriterContext *wctx)
|
||||
{
|
||||
XMLContext *xml = wctx->priv;
|
||||
av_freep(&xml->buf);
|
||||
}
|
||||
|
||||
static const char *xml_escape_str(char **dst, size_t *dst_size, const char *src,
|
||||
void *log_ctx)
|
||||
static const char *xml_escape_str(AVBPrint *dst, const char *src, 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-10);
|
||||
for (p = src; *p; p++) {
|
||||
switch (*p) {
|
||||
case '&' : size += strlen("&"); break;
|
||||
case '<' : size += strlen("<"); break;
|
||||
case '>' : size += strlen(">"); break;
|
||||
case '\"': size += strlen("""); break;
|
||||
case '\'': size += strlen("'"); break;
|
||||
default: size++;
|
||||
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);
|
||||
}
|
||||
}
|
||||
ESCAPE_REALLOC_BUF(dst_size, dst, src, size);
|
||||
|
||||
#define COPY_STR(str) { \
|
||||
const char *s = str; \
|
||||
while (*s) \
|
||||
*q++ = *s++; \
|
||||
}
|
||||
|
||||
p = src;
|
||||
q = *dst;
|
||||
while (*p) {
|
||||
switch (*p) {
|
||||
case '&' : COPY_STR("&"); break;
|
||||
case '<' : COPY_STR("<"); break;
|
||||
case '>' : COPY_STR(">"); break;
|
||||
case '\"': COPY_STR("""); break;
|
||||
case '\'': COPY_STR("'"); break;
|
||||
default: *q++ = *p;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
*q = 0;
|
||||
|
||||
return *dst;
|
||||
return dst->str;
|
||||
}
|
||||
|
||||
static void xml_print_header(WriterContext *wctx)
|
||||
@ -1172,11 +1063,13 @@ static void xml_print_section_footer(WriterContext *wctx, const char *section)
|
||||
|
||||
static void xml_print_str(WriterContext *wctx, const char *key, const char *value)
|
||||
{
|
||||
XMLContext *xml = wctx->priv;
|
||||
AVBPrint buf;
|
||||
|
||||
if (wctx->nb_item)
|
||||
printf(" ");
|
||||
printf("%s=\"%s\"", key, xml_escape_str(&xml->buf, &xml->buf_size, value, wctx));
|
||||
av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
|
||||
printf("%s=\"%s\"", key, xml_escape_str(&buf, value, wctx));
|
||||
av_bprint_finalize(&buf, NULL);
|
||||
}
|
||||
|
||||
static void xml_print_int(WriterContext *wctx, const char *key, long long int value)
|
||||
@ -1191,6 +1084,7 @@ static void xml_show_tags(WriterContext *wctx, AVDictionary *dict)
|
||||
XMLContext *xml = wctx->priv;
|
||||
AVDictionaryEntry *tag = NULL;
|
||||
int is_first = 1;
|
||||
AVBPrint buf;
|
||||
|
||||
xml->indent_level++;
|
||||
while ((tag = av_dict_get(dict, "", tag, AV_DICT_IGNORE_SUFFIX))) {
|
||||
@ -1201,10 +1095,14 @@ static void xml_show_tags(WriterContext *wctx, AVDictionary *dict)
|
||||
is_first = 0;
|
||||
}
|
||||
XML_INDENT();
|
||||
printf("<tag key=\"%s\"",
|
||||
xml_escape_str(&xml->buf, &xml->buf_size, tag->key, wctx));
|
||||
printf(" value=\"%s\"/>\n",
|
||||
xml_escape_str(&xml->buf, &xml->buf_size, tag->value, wctx));
|
||||
|
||||
av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
|
||||
printf("<tag key=\"%s\"", xml_escape_str(&buf, tag->key, wctx));
|
||||
av_bprint_finalize(&buf, NULL);
|
||||
|
||||
av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
|
||||
printf(" value=\"%s\"/>\n", xml_escape_str(&buf, tag->value, wctx));
|
||||
av_bprint_finalize(&buf, NULL);
|
||||
}
|
||||
xml->indent_level--;
|
||||
}
|
||||
@ -1213,7 +1111,6 @@ static Writer xml_writer = {
|
||||
.name = "xml",
|
||||
.priv_size = sizeof(XMLContext),
|
||||
.init = xml_init,
|
||||
.uninit = xml_uninit,
|
||||
.print_header = xml_print_header,
|
||||
.print_footer = xml_print_footer,
|
||||
.print_chapter_header = xml_print_chapter_header,
|
||||
|
Loading…
Reference in New Issue
Block a user