Unified · Split
| @@ -0,0 +1,672 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) The FFmpeg developers | ||
| 3 | * | ||
| 4 | * This file is part of FFmpeg. | ||
| 5 | * | ||
| 6 | * FFmpeg is free software; you can redistribute it and/or | ||
| 7 | * modify it under the terms of the GNU Lesser General Public | ||
| 8 | * License as published by the Free Software Foundation; either | ||
| 9 | * version 2.1 of the License, or (at your option) any later version. | ||
| 10 | * | ||
| 11 | * FFmpeg is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 14 | * Lesser General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU Lesser General Public | ||
| 17 | * License along with FFmpeg; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 19 | */ | ||
| 20 | |||
| 21 | #include <limits.h> | ||
| 22 | #include <stdarg.h> | ||
| 23 | #include <stdint.h> | ||
| 24 | #include <stdio.h> | ||
| 25 | #include <string.h> | ||
| 26 | |||
| 27 | #include "libavutil/mem.h" | ||
| 28 | #include "libavutil/avassert.h" | ||
| 29 | #include "libavutil/bprint.h" | ||
| 30 | #include "libavutil/error.h" | ||
| 31 | #include "libavutil/hash.h" | ||
| 32 | #include "libavutil/intreadwrite.h" | ||
| 33 | #include "libavutil/macros.h" | ||
| 34 | #include "libavutil/opt.h" | ||
| 35 | #include "avtextformat.h" | ||
| 36 | |||
| 37 | #define SECTION_ID_NONE -1 | ||
| 38 | |||
| 39 | #define SHOW_OPTIONAL_FIELDS_AUTO -1 | ||
| 40 | #define SHOW_OPTIONAL_FIELDS_NEVER 0 | ||
| 41 | #define SHOW_OPTIONAL_FIELDS_ALWAYS 1 | ||
| 42 | |||
| 43 | static const struct { | ||
| 44 | double bin_val; | ||
| 45 | double dec_val; | ||
| 46 | const char *bin_str; | ||
| 47 | const char *dec_str; | ||
| 48 | } si_prefixes[] = { | ||
| 49 | { 1.0, 1.0, "", "" }, | ||
| 50 | { 1.024e3, 1e3, "Ki", "K" }, | ||
| 51 | { 1.048576e6, 1e6, "Mi", "M" }, | ||
| 52 | { 1.073741824e9, 1e9, "Gi", "G" }, | ||
| 53 | { 1.099511627776e12, 1e12, "Ti", "T" }, | ||
| 54 | { 1.125899906842624e15, 1e15, "Pi", "P" }, | ||
| 55 | }; | ||
| 56 | |||
| 57 | static const char *textcontext_get_formatter_name(void *p) | ||
| 58 | { | ||
| 59 | AVTextFormatContext *tctx = p; | ||
| 60 | return tctx->formatter->name; | ||
| 61 | } | ||
| 62 | |||
| 63 | #define OFFSET(x) offsetof(AVTextFormatContext, x) | ||
| 64 | |||
| 65 | static const AVOption textcontext_options[] = { | ||
| 66 | { "string_validation", "set string validation mode", | ||
| 67 | OFFSET(string_validation), AV_OPT_TYPE_INT, {.i64=AV_TEXTFORMAT_STRING_VALIDATION_REPLACE}, 0, AV_TEXTFORMAT_STRING_VALIDATION_NB-1, .unit = "sv" }, | ||
| 68 | { "sv", "set string validation mode", | ||
| 69 | OFFSET(string_validation), AV_OPT_TYPE_INT, {.i64=AV_TEXTFORMAT_STRING_VALIDATION_REPLACE}, 0, AV_TEXTFORMAT_STRING_VALIDATION_NB-1, .unit = "sv" }, | ||
| 70 | { "ignore", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AV_TEXTFORMAT_STRING_VALIDATION_IGNORE}, .unit = "sv" }, | ||
| 71 | { "replace", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AV_TEXTFORMAT_STRING_VALIDATION_REPLACE}, .unit = "sv" }, | ||
| 72 | { "fail", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AV_TEXTFORMAT_STRING_VALIDATION_FAIL}, .unit = "sv" }, | ||
| 73 | { "string_validation_replacement", "set string validation replacement string", OFFSET(string_validation_replacement), AV_OPT_TYPE_STRING, {.str=""}}, | ||
| 74 | { "svr", "set string validation replacement string", OFFSET(string_validation_replacement), AV_OPT_TYPE_STRING, {.str="\xEF\xBF\xBD"}}, | ||
| 75 | { NULL } | ||
| 76 | }; | ||
| 77 | |||
| 78 | static void *textcontext_child_next(void *obj, void *prev) | ||
| 79 | { | ||
| 80 | AVTextFormatContext *ctx = obj; | ||
| 81 | if (!prev && ctx->formatter && ctx->formatter->priv_class && ctx->priv) | ||
| 82 | return ctx->priv; | ||
| 83 | return NULL; | ||
| 84 | } | ||
| 85 | |||
| 86 | static const AVClass textcontext_class = { | ||
| 87 | .class_name = "AVTextContext", | ||
| 88 | .item_name = textcontext_get_formatter_name, | ||
| 89 | .option = textcontext_options, | ||
| 90 | .version = LIBAVUTIL_VERSION_INT, | ||
| 91 | .child_next = textcontext_child_next, | ||
| 92 | }; | ||
| 93 | |||
| 94 | static void bprint_bytes(AVBPrint *bp, const uint8_t *ubuf, size_t ubuf_size) | ||
| 95 | { | ||
| 96 | int i; | ||
| 97 | av_bprintf(bp, "0X"); | ||
| 98 | for (i = 0; i < ubuf_size; i++) | ||
| 99 | av_bprintf(bp, "%02X", ubuf[i]); | ||
| 100 | } | ||
| 101 | |||
| 102 | int avtext_context_close(AVTextFormatContext **ptctx) | ||
| 103 | { | ||
| 104 | AVTextFormatContext *tctx = *ptctx; | ||
| 105 | int i; | ||
| 106 | int ret = 0; | ||
| 107 | |||
| 108 | if (!tctx) | ||
| 109 | return EINVAL; | ||
| 110 | |||
| 111 | av_hash_freep(&tctx->hash); | ||
| 112 | |||
| 113 | av_hash_freep(&tctx->hash); | ||
| 114 | |||
| 115 | if (tctx->formatter->uninit) | ||
| 116 | tctx->formatter->uninit(tctx); | ||
| 117 | for (i = 0; i < SECTION_MAX_NB_LEVELS; i++) | ||
| 118 | av_bprint_finalize(&tctx->section_pbuf[i], NULL); | ||
| 119 | if (tctx->formatter->priv_class) | ||
| 120 | av_opt_free(tctx->priv); | ||
| 121 | av_freep(&tctx->priv); | ||
| 122 | av_opt_free(tctx); | ||
| 123 | av_freep(ptctx); | ||
| 124 | return ret; | ||
| 125 | } | ||
| 126 | |||
| 127 | |||
| 128 | int avtext_context_open(AVTextFormatContext **ptctx, const AVTextFormatter *formatter, AVTextWriterContext *writer_context, const char *args, | ||
| 129 | const struct AVTextFormatSection *sections, int nb_sections, | ||
| 130 | int show_value_unit, | ||
| 131 | int use_value_prefix, | ||
| 132 | int use_byte_value_binary_prefix, | ||
| 133 | int use_value_sexagesimal_format, | ||
| 134 | int show_optional_fields, | ||
| 135 | char *show_data_hash) | ||
| 136 | { | ||
| 137 | AVTextFormatContext *tctx; | ||
| 138 | int i, ret = 0; | ||
| 139 | |||
| 140 | if (!(tctx = av_mallocz(sizeof(AVTextFormatContext)))) { | ||
| 141 | ret = AVERROR(ENOMEM); | ||
| 142 | goto fail; | ||
| 143 | } | ||
| 144 | |||
| 145 | if (!(tctx->priv = av_mallocz(formatter->priv_size))) { | ||
| 146 | ret = AVERROR(ENOMEM); | ||
| 147 | goto fail; | ||
| 148 | } | ||
| 149 | |||
| 150 | tctx->show_value_unit = show_value_unit; | ||
| 151 | tctx->use_value_prefix = use_value_prefix; | ||
| 152 | tctx->use_byte_value_binary_prefix = use_byte_value_binary_prefix; | ||
| 153 | tctx->use_value_sexagesimal_format = use_value_sexagesimal_format; | ||
| 154 | tctx->show_optional_fields = show_optional_fields; | ||
| 155 | |||
| 156 | if (nb_sections > SECTION_MAX_NB_SECTIONS) { | ||
| 157 | av_log(tctx, AV_LOG_ERROR, "The number of section definitions (%d) is larger than the maximum allowed (%d)\n", nb_sections, SECTION_MAX_NB_SECTIONS); | ||
| 158 | ret = AVERROR(EINVAL); | ||
| 159 | goto fail; | ||
| 160 | } | ||
| 161 | |||
| 162 | tctx->class = &textcontext_class; | ||
| 163 | tctx->formatter = formatter; | ||
| 164 | tctx->level = -1; | ||
| 165 | tctx->sections = sections; | ||
| 166 | tctx->nb_sections = nb_sections; | ||
| 167 | tctx->writer = writer_context; | ||
| 168 | |||
| 169 | av_opt_set_defaults(tctx); | ||
| 170 | |||
| 171 | if (formatter->priv_class) { | ||
| 172 | void *priv_ctx = tctx->priv; | ||
| 173 | *(const AVClass **)priv_ctx = formatter->priv_class; | ||
| 174 | av_opt_set_defaults(priv_ctx); | ||
| 175 | } | ||
| 176 | |||
| 177 | /* convert options to dictionary */ | ||
| 178 | if (args) { | ||
| 179 | AVDictionary *opts = NULL; | ||
| 180 | const AVDictionaryEntry *opt = NULL; | ||
| 181 | |||
| 182 | if ((ret = av_dict_parse_string(&opts, args, "=", ":", 0)) < 0) { | ||
| 183 | av_log(tctx, AV_LOG_ERROR, "Failed to parse option string '%s' provided to textformat context\n", args); | ||
| 184 | av_dict_free(&opts); | ||
| 185 | goto fail; | ||
| 186 | } | ||
| 187 | |||
| 188 | while ((opt = av_dict_iterate(opts, opt))) { | ||
| 189 | if ((ret = av_opt_set(tctx, opt->key, opt->value, AV_OPT_SEARCH_CHILDREN)) < 0) { | ||
| 190 | av_log(tctx, AV_LOG_ERROR, "Failed to set option '%s' with value '%s' provided to textformat context\n", | ||
| 191 | opt->key, opt->value); | ||
| 192 | av_dict_free(&opts); | ||
| 193 | goto fail; | ||
| 194 | } | ||
| 195 | } | ||
| 196 | |||
| 197 | av_dict_free(&opts); | ||
| 198 | } | ||
| 199 | |||
| 200 | if (show_data_hash) { | ||
| 201 | if ((ret = av_hash_alloc(&tctx->hash, show_data_hash)) < 0) { | ||
| 202 | if (ret == AVERROR(EINVAL)) { | ||
| 203 | const char *n; | ||
| 204 | av_log(NULL, AV_LOG_ERROR, "Unknown hash algorithm '%s'\nKnown algorithms:", show_data_hash); | ||
| 205 | for (i = 0; (n = av_hash_names(i)); i++) | ||
| 206 | av_log(NULL, AV_LOG_ERROR, " %s", n); | ||
| 207 | av_log(NULL, AV_LOG_ERROR, "\n"); | ||
| 208 | } | ||
| 209 | return ret; | ||
| 210 | } | ||
| 211 | } | ||
| 212 | |||
| 213 | /* validate replace string */ | ||
| 214 | { | ||
| 215 | const uint8_t *p = tctx->string_validation_replacement; | ||
| 216 | const uint8_t *endp = p + strlen(p); | ||
| 217 | while (*p) { | ||
| 218 | const uint8_t *p0 = p; | ||
| 219 | int32_t code; | ||
| 220 | ret = av_utf8_decode(&code, &p, endp, tctx->string_validation_utf8_flags); | ||
| 221 | if (ret < 0) { | ||
| 222 | AVBPrint bp; | ||
| 223 | av_bprint_init(&bp, 0, AV_BPRINT_SIZE_AUTOMATIC); | ||
| 224 | bprint_bytes(&bp, p0, p-p0), | ||
| 225 | av_log(tctx, AV_LOG_ERROR, | ||
| 226 | "Invalid UTF8 sequence %s found in string validation replace '%s'\n", | ||
| 227 | bp.str, tctx->string_validation_replacement); | ||
| 228 | return ret; | ||
| 229 | } | ||
| 230 | } | ||
| 231 | } | ||
| 232 | |||
| 233 | for (i = 0; i < SECTION_MAX_NB_LEVELS; i++) | ||
| 234 | av_bprint_init(&tctx->section_pbuf[i], 1, AV_BPRINT_SIZE_UNLIMITED); | ||
| 235 | |||
| 236 | if (tctx->formatter->init) | ||
| 237 | ret = tctx->formatter->init(tctx); | ||
| 238 | if (ret < 0) | ||
| 239 | goto fail; | ||
| 240 | |||
| 241 | *ptctx = tctx; | ||
| 242 | |||
| 243 | return 0; | ||
| 244 | |||
| 245 | fail: | ||
| 246 | avtext_context_close(&tctx); | ||
| 247 | return ret; | ||
| 248 | } | ||
| 249 | |||
| 250 | /* Temporary definitions during refactoring */ | ||
| 251 | static const char unit_second_str[] = "s" ; | ||
| 252 | static const char unit_hertz_str[] = "Hz" ; | ||
| 253 | static const char unit_byte_str[] = "byte" ; | ||
| 254 | static const char unit_bit_per_second_str[] = "bit/s"; | ||
| 255 | |||
| 256 | |||
| 257 | void avtext_print_section_header(AVTextFormatContext *tctx, | ||
| 258 | const void *data, | ||
| 259 | int section_id) | ||
| 260 | { | ||
| 261 | tctx->level++; | ||
| 262 | av_assert0(tctx->level < SECTION_MAX_NB_LEVELS); | ||
| 263 | |||
| 264 | tctx->nb_item[tctx->level] = 0; | ||
| 265 | memset(tctx->nb_item_type[tctx->level], 0, sizeof(tctx->nb_item_type[tctx->level])); | ||
| 266 | tctx->section[tctx->level] = &tctx->sections[section_id]; | ||
| 267 | |||
| 268 | if (tctx->formatter->print_section_header) | ||
| 269 | tctx->formatter->print_section_header(tctx, data); | ||
| 270 | } | ||
| 271 | |||
| 272 | void avtext_print_section_footer(AVTextFormatContext *tctx) | ||
| 273 | { | ||
| 274 | int section_id = tctx->section[tctx->level]->id; | ||
| 275 | int parent_section_id = tctx->level ? | ||
| 276 | tctx->section[tctx->level-1]->id : SECTION_ID_NONE; | ||
| 277 | |||
| 278 | if (parent_section_id != SECTION_ID_NONE) { | ||
| 279 | tctx->nb_item[tctx->level - 1]++; | ||
| 280 | tctx->nb_item_type[tctx->level - 1][section_id]++; | ||
| 281 | } | ||
| 282 | |||
| 283 | if (tctx->formatter->print_section_footer) | ||
| 284 | tctx->formatter->print_section_footer(tctx); | ||
| 285 | tctx->level--; | ||
| 286 | } | ||
| 287 | |||
| 288 | void avtext_print_integer(AVTextFormatContext *tctx, | ||
| 289 | const char *key, int64_t val) | ||
| 290 | { | ||
| 291 | const struct AVTextFormatSection *section = tctx->section[tctx->level]; | ||
| 292 | |||
| 293 | if (section->show_all_entries || av_dict_get(section->entries_to_show, key, NULL, 0)) { | ||
| 294 | tctx->formatter->print_integer(tctx, key, val); | ||
| 295 | tctx->nb_item[tctx->level]++; | ||
| 296 | } | ||
| 297 | } | ||
| 298 | |||
| 299 | static inline int validate_string(AVTextFormatContext *tctx, char **dstp, const char *src) | ||
| 300 | { | ||
| 301 | const uint8_t *p, *endp; | ||
| 302 | AVBPrint dstbuf; | ||
| 303 | int invalid_chars_nb = 0, ret = 0; | ||
| 304 | |||
| 305 | av_bprint_init(&dstbuf, 0, AV_BPRINT_SIZE_UNLIMITED); | ||
| 306 | |||
| 307 | endp = src + strlen(src); | ||
| 308 | for (p = src; *p;) { | ||
| 309 | uint32_t code; | ||
| 310 | int invalid = 0; | ||
| 311 | const uint8_t *p0 = p; | ||
| 312 | |||
| 313 | if (av_utf8_decode(&code, &p, endp, tctx->string_validation_utf8_flags) < 0) { | ||
| 314 | AVBPrint bp; | ||
| 315 | av_bprint_init(&bp, 0, AV_BPRINT_SIZE_AUTOMATIC); | ||
| 316 | bprint_bytes(&bp, p0, p-p0); | ||
| 317 | av_log(tctx, AV_LOG_DEBUG, | ||
| 318 | "Invalid UTF-8 sequence %s found in string '%s'\n", bp.str, src); | ||
| 319 | invalid = 1; | ||
| 320 | } | ||
| 321 | |||
| 322 | if (invalid) { | ||
| 323 | invalid_chars_nb++; | ||
| 324 | |||
| 325 | switch (tctx->string_validation) { | ||
| 326 | case AV_TEXTFORMAT_STRING_VALIDATION_FAIL: | ||
| 327 | av_log(tctx, AV_LOG_ERROR, | ||
| 328 | "Invalid UTF-8 sequence found in string '%s'\n", src); | ||
| 329 | ret = AVERROR_INVALIDDATA; | ||
| 330 | goto end; | ||
| 331 | break; | ||
| 332 | |||
| 333 | case AV_TEXTFORMAT_STRING_VALIDATION_REPLACE: | ||
| 334 | av_bprintf(&dstbuf, "%s", tctx->string_validation_replacement); | ||
| 335 | break; | ||
| 336 | } | ||
| 337 | } | ||
| 338 | |||
| 339 | if (!invalid || tctx->string_validation == AV_TEXTFORMAT_STRING_VALIDATION_IGNORE) | ||
| 340 | av_bprint_append_data(&dstbuf, p0, p-p0); | ||
| 341 | } | ||
| 342 | |||
| 343 | if (invalid_chars_nb && tctx->string_validation == AV_TEXTFORMAT_STRING_VALIDATION_REPLACE) { | ||
| 344 | av_log(tctx, AV_LOG_WARNING, | ||
| 345 | "%d invalid UTF-8 sequence(s) found in string '%s', replaced with '%s'\n", | ||
| 346 | invalid_chars_nb, src, tctx->string_validation_replacement); | ||
| 347 | } | ||
| 348 | |||
| 349 | end: | ||
| 350 | av_bprint_finalize(&dstbuf, dstp); | ||
| 351 | return ret; | ||
| 352 | } | ||
| 353 | |||
| 354 | struct unit_value { | ||
| 355 | union { double d; int64_t i; } val; | ||
| 356 | const char *unit; | ||
| 357 | }; | ||
| 358 | |||
| 359 | static char *value_string(AVTextFormatContext *tctx, char *buf, int buf_size, struct unit_value uv) | ||
| 360 | { | ||
| 361 | double vald; | ||
| 362 | int64_t vali; | ||
| 363 | int show_float = 0; | ||
| 364 | |||
| 365 | if (uv.unit == unit_second_str) { | ||
| 366 | vald = uv.val.d; | ||
| 367 | show_float = 1; | ||
| 368 | } else { | ||
| 369 | vald = vali = uv.val.i; | ||
| 370 | } | ||
| 371 | |||
| 372 | if (uv.unit == unit_second_str && tctx->use_value_sexagesimal_format) { | ||
| 373 | double secs; | ||
| 374 | int hours, mins; | ||
| 375 | secs = vald; | ||
| 376 | mins = (int)secs / 60; | ||
| 377 | secs = secs - mins * 60; | ||
| 378 | hours = mins / 60; | ||
| 379 | mins %= 60; | ||
| 380 | snprintf(buf, buf_size, "%d:%02d:%09.6f", hours, mins, secs); | ||
| 381 | } else { | ||
| 382 | const char *prefix_string = ""; | ||
| 383 | |||
| 384 | if (tctx->use_value_prefix && vald > 1) { | ||
| 385 | int64_t index; | ||
| 386 | |||
| 387 | if (uv.unit == unit_byte_str && tctx->use_byte_value_binary_prefix) { | ||
| 388 | index = (int64_t) (log2(vald)) / 10; | ||
| 389 | index = av_clip(index, 0, FF_ARRAY_ELEMS(si_prefixes) - 1); | ||
| 390 | vald /= si_prefixes[index].bin_val; | ||
| 391 | prefix_string = si_prefixes[index].bin_str; | ||
| 392 | } else { | ||
| 393 | index = (int64_t) (log10(vald)) / 3; | ||
| 394 | index = av_clip(index, 0, FF_ARRAY_ELEMS(si_prefixes) - 1); | ||
| 395 | vald /= si_prefixes[index].dec_val; | ||
| 396 | prefix_string = si_prefixes[index].dec_str; | ||
| 397 | } | ||
| 398 | vali = vald; | ||
| 399 | } | ||
| 400 | |||
| 401 | if (show_float || (tctx->use_value_prefix && vald != (int64_t)vald)) | ||
| 402 | snprintf(buf, buf_size, "%f", vald); | ||
| 403 | else | ||
| 404 | snprintf(buf, buf_size, "%"PRId64, vali); | ||
| 405 | av_strlcatf(buf, buf_size, "%s%s%s", *prefix_string || tctx->show_value_unit ? " " : "", | ||
| 406 | prefix_string, tctx->show_value_unit ? uv.unit : ""); | ||
| 407 | } | ||
| 408 | |||
| 409 | return buf; | ||
| 410 | } | ||
| 411 | |||
| 412 | |||
| 413 | void avtext_print_unit_int(AVTextFormatContext *tctx, const char *key, int value, const char *unit) | ||
| 414 | { | ||
| 415 | char val_str[128]; | ||
| 416 | struct unit_value uv; | ||
| 417 | uv.val.i = value; | ||
| 418 | uv.unit = unit; | ||
| 419 | avtext_print_string(tctx, key, value_string(tctx, val_str, sizeof(val_str), uv), 0); | ||
| 420 | } | ||
| 421 | |||
| 422 | |||
| 423 | int avtext_print_string(AVTextFormatContext *tctx, const char *key, const char *val, int flags) | ||
| 424 | { | ||
| 425 | const struct AVTextFormatSection *section = tctx->section[tctx->level]; | ||
| 426 | int ret = 0; | ||
| 427 | |||
| 428 | if (tctx->show_optional_fields == SHOW_OPTIONAL_FIELDS_NEVER || | ||
| 429 | (tctx->show_optional_fields == SHOW_OPTIONAL_FIELDS_AUTO | ||
| 430 | && (flags & AV_TEXTFORMAT_PRINT_STRING_OPTIONAL) | ||
| 431 | && !(tctx->formatter->flags & AV_TEXTFORMAT_FLAG_SUPPORTS_OPTIONAL_FIELDS))) | ||
| 432 | return 0; | ||
| 433 | |||
| 434 | if (section->show_all_entries || av_dict_get(section->entries_to_show, key, NULL, 0)) { | ||
| 435 | if (flags & AV_TEXTFORMAT_PRINT_STRING_VALIDATE) { | ||
| 436 | char *key1 = NULL, *val1 = NULL; | ||
| 437 | ret = validate_string(tctx, &key1, key); | ||
| 438 | if (ret < 0) goto end; | ||
| 439 | ret = validate_string(tctx, &val1, val); | ||
| 440 | if (ret < 0) goto end; | ||
| 441 | tctx->formatter->print_string(tctx, key1, val1); | ||
| 442 | end: | ||
| 443 | if (ret < 0) { | ||
| 444 | av_log(tctx, AV_LOG_ERROR, | ||
| 445 | "Invalid key=value string combination %s=%s in section %s\n", | ||
| 446 | key, val, section->unique_name); | ||
| 447 | } | ||
| 448 | av_free(key1); | ||
| 449 | av_free(val1); | ||
| 450 | } else { | ||
| 451 | tctx->formatter->print_string(tctx, key, val); | ||
| 452 | } | ||
| 453 | |||
| 454 | tctx->nb_item[tctx->level]++; | ||
| 455 | } | ||
| 456 | |||
| 457 | return ret; | ||
| 458 | } | ||
| 459 | |||
| 460 | void avtext_print_rational(AVTextFormatContext *tctx, | ||
| 461 | const char *key, AVRational q, char sep) | ||
| 462 | { | ||
| 463 | AVBPrint buf; | ||
| 464 | av_bprint_init(&buf, 0, AV_BPRINT_SIZE_AUTOMATIC); | ||
| 465 | av_bprintf(&buf, "%d%c%d", q.num, sep, q.den); | ||
| 466 | avtext_print_string(tctx, key, buf.str, 0); | ||
| 467 | } | ||
| 468 | |||
| 469 | void avtext_print_time(AVTextFormatContext *tctx, const char *key, | ||
| 470 | int64_t ts, const AVRational *time_base, int is_duration) | ||
| 471 | { | ||
| 472 | char buf[128]; | ||
| 473 | |||
| 474 | if ((!is_duration && ts == AV_NOPTS_VALUE) || (is_duration && ts == 0)) { | ||
| 475 | avtext_print_string(tctx, key, "N/A", AV_TEXTFORMAT_PRINT_STRING_OPTIONAL); | ||
| 476 | } else { | ||
| 477 | double d = ts * av_q2d(*time_base); | ||
| 478 | struct unit_value uv; | ||
| 479 | uv.val.d = d; | ||
| 480 | uv.unit = unit_second_str; | ||
| 481 | value_string(tctx, buf, sizeof(buf), uv); | ||
| 482 | avtext_print_string(tctx, key, buf, 0); | ||
| 483 | } | ||
| 484 | } | ||
| 485 | |||
| 486 | void avtext_print_ts(AVTextFormatContext *tctx, const char *key, int64_t ts, int is_duration) | ||
| 487 | { | ||
| 488 | if ((!is_duration && ts == AV_NOPTS_VALUE) || (is_duration && ts == 0)) { | ||
| 489 | avtext_print_string(tctx, key, "N/A", AV_TEXTFORMAT_PRINT_STRING_OPTIONAL); | ||
| 490 | } else { | ||
| 491 | avtext_print_integer(tctx, key, ts); | ||
| 492 | } | ||
| 493 | } | ||
| 494 | |||
| 495 | void avtext_print_data(AVTextFormatContext *tctx, const char *name, | ||
| 496 | const uint8_t *data, int size) | ||
| 497 | { | ||
| 498 | AVBPrint bp; | ||
| 499 | int offset = 0, l, i; | ||
| 500 | |||
| 501 | av_bprint_init(&bp, 0, AV_BPRINT_SIZE_UNLIMITED); | ||
| 502 | av_bprintf(&bp, "\n"); | ||
| 503 | while (size) { | ||
| 504 | av_bprintf(&bp, "%08x: ", offset); | ||
| 505 | l = FFMIN(size, 16); | ||
| 506 | for (i = 0; i < l; i++) { | ||
| 507 | av_bprintf(&bp, "%02x", data[i]); | ||
| 508 | if (i & 1) | ||
| 509 | av_bprintf(&bp, " "); | ||
| 510 | } | ||
| 511 | av_bprint_chars(&bp, ' ', 41 - 2 * i - i / 2); | ||
| 512 | for (i = 0; i < l; i++) | ||
| 513 | av_bprint_chars(&bp, data[i] - 32U < 95 ? data[i] : '.', 1); | ||
| 514 | av_bprintf(&bp, "\n"); | ||
| 515 | offset += l; | ||
| 516 | data += l; | ||
| 517 | size -= l; | ||
| 518 | } | ||
| 519 | avtext_print_string(tctx, name, bp.str, 0); | ||
| 520 | av_bprint_finalize(&bp, NULL); | ||
| 521 | } | ||
| 522 | |||
| 523 | void avtext_print_data_hash(AVTextFormatContext *tctx, const char *name, | ||
| 524 | const uint8_t *data, int size) | ||
| 525 | { | ||
| 526 | char *p, buf[AV_HASH_MAX_SIZE * 2 + 64] = { 0 }; | ||
| 527 | |||
| 528 | if (!tctx->hash) | ||
| 529 | return; | ||
| 530 | av_hash_init(tctx->hash); | ||
| 531 | av_hash_update(tctx->hash, data, size); | ||
| 532 | snprintf(buf, sizeof(buf), "%s:", av_hash_get_name(tctx->hash)); | ||
| 533 | p = buf + strlen(buf); | ||
| 534 | av_hash_final_hex(tctx->hash, p, buf + sizeof(buf) - p); | ||
| 535 | avtext_print_string(tctx, name, buf, 0); | ||
| 536 | } | ||
| 537 | |||
| 538 | void avtext_print_integers(AVTextFormatContext *tctx, const char *name, | ||
| 539 | uint8_t *data, int size, const char *format, | ||
| 540 | int columns, int bytes, int offset_add) | ||
| 541 | { | ||
| 542 | AVBPrint bp; | ||
| 543 | int offset = 0, l, i; | ||
| 544 | |||
| 545 | av_bprint_init(&bp, 0, AV_BPRINT_SIZE_UNLIMITED); | ||
| 546 | av_bprintf(&bp, "\n"); | ||
| 547 | while (size) { | ||
| 548 | av_bprintf(&bp, "%08x: ", offset); | ||
| 549 | l = FFMIN(size, columns); | ||
| 550 | for (i = 0; i < l; i++) { | ||
| 551 | if (bytes == 1) av_bprintf(&bp, format, *data); | ||
| 552 | else if (bytes == 2) av_bprintf(&bp, format, AV_RN16(data)); | ||
| 553 | else if (bytes == 4) av_bprintf(&bp, format, AV_RN32(data)); | ||
| 554 | data += bytes; | ||
| 555 | size --; | ||
| 556 | } | ||
| 557 | av_bprintf(&bp, "\n"); | ||
| 558 | offset += offset_add; | ||
| 559 | } | ||
| 560 | avtext_print_string(tctx, name, bp.str, 0); | ||
| 561 | av_bprint_finalize(&bp, NULL); | ||
| 562 | } | ||
| 563 | |||
| 564 | static const char *writercontext_get_writer_name(void *p) | ||
| 565 | { | ||
| 566 | AVTextWriterContext *wctx = p; | ||
| 567 | return wctx->writer->name; | ||
| 568 | } | ||
| 569 | |||
| 570 | static void *writercontext_child_next(void *obj, void *prev) | ||
| 571 | { | ||
| 572 | AVTextFormatContext *ctx = obj; | ||
| 573 | if (!prev && ctx->formatter && ctx->formatter->priv_class && ctx->priv) | ||
| 574 | return ctx->priv; | ||
| 575 | return NULL; | ||
| 576 | } | ||
| 577 | |||
| 578 | static const AVClass textwriter_class = { | ||
| 579 | .class_name = "AVTextWriterContext", | ||
| 580 | .item_name = writercontext_get_writer_name, | ||
| 581 | .version = LIBAVUTIL_VERSION_INT, | ||
| 582 | .child_next = writercontext_child_next, | ||
| 583 | }; | ||
| 584 | |||
| 585 | |||
| 586 | int avtextwriter_context_close(AVTextWriterContext **pwctx) | ||
| 587 | { | ||
| 588 | AVTextWriterContext *wctx = *pwctx; | ||
| 589 | int ret = 0; | ||
| 590 | |||
| 591 | if (!wctx) | ||
| 592 | return EINVAL; | ||
| 593 | |||
| 594 | if (wctx->writer->uninit) | ||
| 595 | wctx->writer->uninit(wctx); | ||
| 596 | if (wctx->writer->priv_class) | ||
| 597 | av_opt_free(wctx->priv); | ||
| 598 | av_freep(&wctx->priv); | ||
| 599 | av_freep(pwctx); | ||
| 600 | return ret; | ||
| 601 | } | ||
| 602 | |||
| 603 | |||
| 604 | int avtextwriter_context_open(AVTextWriterContext **pwctx, const AVTextWriter *writer) | ||
| 605 | { | ||
| 606 | AVTextWriterContext *wctx; | ||
| 607 | int ret = 0; | ||
| 608 | |||
| 609 | if (!(wctx = av_mallocz(sizeof(AVTextWriterContext)))) { | ||
| 610 | ret = AVERROR(ENOMEM); | ||
| 611 | goto fail; | ||
| 612 | } | ||
| 613 | |||
| 614 | if (!(wctx->priv = av_mallocz(writer->priv_size))) { | ||
| 615 | ret = AVERROR(ENOMEM); | ||
| 616 | goto fail; | ||
| 617 | } | ||
| 618 | |||
| 619 | if (writer->priv_class) { | ||
| 620 | void *priv_ctx = wctx->priv; | ||
| 621 | *(const AVClass **)priv_ctx = writer->priv_class; | ||
| 622 | av_opt_set_defaults(priv_ctx); | ||
| 623 | } | ||
| 624 | |||
| 625 | wctx->class = &textwriter_class; | ||
| 626 | wctx->writer = writer; | ||
| 627 | |||
| 628 | av_opt_set_defaults(wctx); | ||
| 629 | |||
| 630 | |||
| 631 | if (wctx->writer->init) | ||
| 632 | ret = wctx->writer->init(wctx); | ||
| 633 | if (ret < 0) | ||
| 634 | goto fail; | ||
| 635 | |||
| 636 | *pwctx = wctx; | ||
| 637 | |||
| 638 | return 0; | ||
| 639 | |||
| 640 | fail: | ||
| 641 | avtextwriter_context_close(&wctx); | ||
| 642 | return ret; | ||
| 643 | } | ||
| 644 | |||
| 645 | static const AVTextFormatter *registered_formatters[7+1]; | ||
| 646 | static void formatters_register_all(void) | ||
| 647 | { | ||
| 648 | static int initialized; | ||
| 649 | |||
| 650 | if (initialized) | ||
| 651 | return; | ||
| 652 | initialized = 1; | ||
| 653 | |||
| 654 | registered_formatters[0] = &avtextformatter_default; | ||
| 655 | registered_formatters[1] = &avtextformatter_compact; | ||
| 656 | registered_formatters[2] = &avtextformatter_csv; | ||
| 657 | registered_formatters[3] = &avtextformatter_flat; | ||
| 658 | registered_formatters[4] = &avtextformatter_ini; | ||
| 659 | registered_formatters[5] = &avtextformatter_json; | ||
| 660 | registered_formatters[6] = &avtextformatter_xml; | ||
| 661 | } | ||
| 662 | |||
| 663 | const AVTextFormatter *avtext_get_formatter_by_name(const char *name) | ||
| 664 | { | ||
| 665 | formatters_register_all(); | ||
| 666 | |||
| 667 | for (int i = 0; registered_formatters[i]; i++) | ||
| 668 | if (!strcmp(registered_formatters[i]->name, name)) | ||
| 669 | return registered_formatters[i]; | ||
| 670 | |||
| 671 | return NULL; | ||
| 672 | } | ||
| @@ -0,0 +1,171 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) The FFmpeg developers | ||
| 3 | * | ||
| 4 | * This file is part of FFmpeg. | ||
| 5 | * | ||
| 6 | * FFmpeg is free software; you can redistribute it and/or | ||
| 7 | * modify it under the terms of the GNU Lesser General Public | ||
| 8 | * License as published by the Free Software Foundation; either | ||
| 9 | * version 2.1 of the License, or (at your option) any later version. | ||
| 10 | * | ||
| 11 | * FFmpeg is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 14 | * Lesser General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU Lesser General Public | ||
| 17 | * License along with FFmpeg; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 19 | */ | ||
| 20 | |||
| 21 | #ifndef FFTOOLS_TEXTFORMAT_AVTEXTFORMAT_H | ||
| 22 | #define FFTOOLS_TEXTFORMAT_AVTEXTFORMAT_H | ||
| 23 | |||
| 24 | #include <stddef.h> | ||
| 25 | #include <stdint.h> | ||
| 26 | #include "libavutil/attributes.h" | ||
| 27 | #include "libavutil/dict.h" | ||
| 28 | #include "libavformat/avio.h" | ||
| 29 | #include "libavutil/bprint.h" | ||
| 30 | #include "libavutil/rational.h" | ||
| 31 | #include "libavutil/hash.h" | ||
| 32 | #include "avtextwriters.h" | ||
| 33 | |||
| 34 | #define SECTION_MAX_NB_CHILDREN 11 | ||
| 35 | |||
| 36 | |||
| 37 | typedef struct AVTextFormatSection { | ||
| 38 | int id; ///< unique id identifying a section | ||
| 39 | const char *name; | ||
| 40 | |||
| 41 | #define AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER 1 ///< the section only contains other sections, but has no data at its own level | ||
| 42 | #define AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY 2 ///< the section contains an array of elements of the same type | ||
| 43 | #define AV_TEXTFORMAT_SECTION_FLAG_HAS_VARIABLE_FIELDS 4 ///< the section may contain a variable number of fields with variable keys. | ||
| 44 | /// For these sections the element_name field is mandatory. | ||
| 45 | #define AV_TEXTFORMAT_SECTION_FLAG_HAS_TYPE 8 ///< the section contains a type to distinguish multiple nested elements | ||
| 46 | #define AV_TEXTFORMAT_SECTION_FLAG_NUMBERING_BY_TYPE 16 ///< the items in this array section should be numbered individually by type | ||
| 47 | |||
| 48 | int flags; | ||
| 49 | const int children_ids[SECTION_MAX_NB_CHILDREN+1]; ///< list of children section IDS, terminated by -1 | ||
| 50 | const char *element_name; ///< name of the contained element, if provided | ||
| 51 | const char *unique_name; ///< unique section name, in case the name is ambiguous | ||
| 52 | AVDictionary *entries_to_show; | ||
| 53 | const char *(* get_type)(const void *data); ///< function returning a type if defined, must be defined when SECTION_FLAG_HAS_TYPE is defined | ||
| 54 | int show_all_entries; | ||
| 55 | } AVTextFormatSection; | ||
| 56 | |||
| 57 | typedef struct AVTextFormatContext AVTextFormatContext; | ||
| 58 | |||
| 59 | #define AV_TEXTFORMAT_FLAG_SUPPORTS_OPTIONAL_FIELDS 1 | ||
| 60 | #define AV_TEXTFORMAT_FLAG_SUPPORTS_MIXED_ARRAY_CONTENT 2 | ||
| 61 | |||
| 62 | typedef enum { | ||
| 63 | AV_TEXTFORMAT_STRING_VALIDATION_FAIL, | ||
| 64 | AV_TEXTFORMAT_STRING_VALIDATION_REPLACE, | ||
| 65 | AV_TEXTFORMAT_STRING_VALIDATION_IGNORE, | ||
| 66 | AV_TEXTFORMAT_STRING_VALIDATION_NB | ||
| 67 | } StringValidation; | ||
| 68 | |||
| 69 | typedef struct AVTextFormatter { | ||
| 70 | const AVClass *priv_class; ///< private class of the formatter, if any | ||
| 71 | int priv_size; ///< private size for the formatter context | ||
| 72 | const char *name; | ||
| 73 | |||
| 74 | int (*init) (AVTextFormatContext *tctx); | ||
| 75 | void (*uninit)(AVTextFormatContext *tctx); | ||
| 76 | |||
| 77 | void (*print_section_header)(AVTextFormatContext *tctx, const void *data); | ||
| 78 | void (*print_section_footer)(AVTextFormatContext *tctx); | ||
| 79 | void (*print_integer) (AVTextFormatContext *tctx, const char *, int64_t); | ||
| 80 | void (*print_rational) (AVTextFormatContext *tctx, AVRational *q, char *sep); | ||
| 81 | void (*print_string) (AVTextFormatContext *tctx, const char *, const char *); | ||
| 82 | int flags; ///< a combination or AV_TEXTFORMAT__FLAG_* | ||
| 83 | } AVTextFormatter; | ||
| 84 | |||
| 85 | #define SECTION_MAX_NB_LEVELS 12 | ||
| 86 | #define SECTION_MAX_NB_SECTIONS 100 | ||
| 87 | |||
| 88 | struct AVTextFormatContext { | ||
| 89 | const AVClass *class; ///< class of the formatter | ||
| 90 | const AVTextFormatter *formatter; ///< the AVTextFormatter of which this is an instance | ||
| 91 | AVTextWriterContext *writer; ///< the AVTextWriterContext | ||
| 92 | |||
| 93 | char *name; ///< name of this formatter instance | ||
| 94 | void *priv; ///< private data for use by the filter | ||
| 95 | |||
| 96 | const struct AVTextFormatSection *sections; ///< array containing all sections | ||
| 97 | int nb_sections; ///< number of sections | ||
| 98 | |||
| 99 | int level; ///< current level, starting from 0 | ||
| 100 | |||
| 101 | /** number of the item printed in the given section, starting from 0 */ | ||
| 102 | unsigned int nb_item[SECTION_MAX_NB_LEVELS]; | ||
| 103 | unsigned int nb_item_type[SECTION_MAX_NB_LEVELS][SECTION_MAX_NB_SECTIONS]; | ||
| 104 | |||
| 105 | /** section per each level */ | ||
| 106 | const struct AVTextFormatSection *section[SECTION_MAX_NB_LEVELS]; | ||
| 107 | AVBPrint section_pbuf[SECTION_MAX_NB_LEVELS]; ///< generic print buffer dedicated to each section, | ||
| 108 | /// used by various formatters | ||
| 109 | |||
| 110 | int show_optional_fields; | ||
| 111 | int show_value_unit; | ||
| 112 | int use_value_prefix; | ||
| 113 | int use_byte_value_binary_prefix; | ||
| 114 | int use_value_sexagesimal_format; | ||
| 115 | |||
| 116 | struct AVHashContext *hash; | ||
| 117 | |||
| 118 | int string_validation; | ||
| 119 | char *string_validation_replacement; | ||
| 120 | unsigned int string_validation_utf8_flags; | ||
| 121 | }; | ||
| 122 | |||
| 123 | #define AV_TEXTFORMAT_PRINT_STRING_OPTIONAL 1 | ||
| 124 | #define AV_TEXTFORMAT_PRINT_STRING_VALIDATE 2 | ||
| 125 | |||
| 126 | int avtext_context_open(AVTextFormatContext **ptctx, const AVTextFormatter *formatter, AVTextWriterContext *writer_context, const char *args, | ||
| 127 | const struct AVTextFormatSection *sections, int nb_sections, | ||
| 128 | int show_value_unit, | ||
| 129 | int use_value_prefix, | ||
| 130 | int use_byte_value_binary_prefix, | ||
| 131 | int use_value_sexagesimal_format, | ||
| 132 | int show_optional_fields, | ||
| 133 | char *show_data_hash); | ||
| 134 | |||
| 135 | int avtext_context_close(AVTextFormatContext **tctx); | ||
| 136 | |||
| 137 | |||
| 138 | void avtext_print_section_header(AVTextFormatContext *tctx, const void *data, int section_id); | ||
| 139 | |||
| 140 | void avtext_print_section_footer(AVTextFormatContext *tctx); | ||
| 141 | |||
| 142 | void avtext_print_integer(AVTextFormatContext *tctx, const char *key, int64_t val); | ||
| 143 | |||
| 144 | int avtext_print_string(AVTextFormatContext *tctx, const char *key, const char *val, int flags); | ||
| 145 | |||
| 146 | void avtext_print_unit_int(AVTextFormatContext *tctx, const char *key, int value, const char *unit); | ||
| 147 | |||
| 148 | void avtext_print_rational(AVTextFormatContext *tctx, const char *key, AVRational q, char sep); | ||
| 149 | |||
| 150 | void avtext_print_time(AVTextFormatContext *tctx, const char *key, int64_t ts, const AVRational *time_base, int is_duration); | ||
| 151 | |||
| 152 | void avtext_print_ts(AVTextFormatContext *tctx, const char *key, int64_t ts, int is_duration); | ||
| 153 | |||
| 154 | void avtext_print_data(AVTextFormatContext *tctx, const char *name, const uint8_t *data, int size); | ||
| 155 | |||
| 156 | void avtext_print_data_hash(AVTextFormatContext *tctx, const char *name, const uint8_t *data, int size); | ||
| 157 | |||
| 158 | void avtext_print_integers(AVTextFormatContext *tctx, const char *name, uint8_t *data, int size, | ||
| 159 | const char *format, int columns, int bytes, int offset_add); | ||
| 160 | |||
| 161 | const AVTextFormatter *avtext_get_formatter_by_name(const char *name); | ||
| 162 | |||
| 163 | extern const AVTextFormatter avtextformatter_default; | ||
| 164 | extern const AVTextFormatter avtextformatter_compact; | ||
| 165 | extern const AVTextFormatter avtextformatter_csv; | ||
| 166 | extern const AVTextFormatter avtextformatter_flat; | ||
| 167 | extern const AVTextFormatter avtextformatter_ini; | ||
| 168 | extern const AVTextFormatter avtextformatter_json; | ||
| 169 | extern const AVTextFormatter avtextformatter_xml; | ||
| 170 | |||
| 171 | #endif /* FFTOOLS_TEXTFORMAT_AVTEXTFORMAT_H */ | ||
| @@ -0,0 +1,68 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) The FFmpeg developers | ||
| 3 | * | ||
| 4 | * This file is part of FFmpeg. | ||
| 5 | * | ||
| 6 | * FFmpeg is free software; you can redistribute it and/or | ||
| 7 | * modify it under the terms of the GNU Lesser General Public | ||
| 8 | * License as published by the Free Software Foundation; either | ||
| 9 | * version 2.1 of the License, or (at your option) any later version. | ||
| 10 | * | ||
| 11 | * FFmpeg is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 14 | * Lesser General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU Lesser General Public | ||
| 17 | * License along with FFmpeg; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 19 | */ | ||
| 20 | |||
| 21 | #ifndef FFTOOLS_TEXTFORMAT_AVTEXTWRITERS_H | ||
| 22 | #define FFTOOLS_TEXTFORMAT_AVTEXTWRITERS_H | ||
| 23 | |||
| 24 | #include <stddef.h> | ||
| 25 | #include <stdint.h> | ||
| 26 | #include "libavutil/attributes.h" | ||
| 27 | #include "libavutil/dict.h" | ||
| 28 | #include "libavformat/avio.h" | ||
| 29 | #include "libavutil/bprint.h" | ||
| 30 | #include "libavutil/rational.h" | ||
| 31 | #include "libavutil/hash.h" | ||
| 32 | |||
| 33 | typedef struct AVTextWriterContext AVTextWriterContext; | ||
| 34 | |||
| 35 | typedef struct AVTextWriter { | ||
| 36 | const AVClass *priv_class; ///< private class of the writer, if any | ||
| 37 | int priv_size; ///< private size for the writer private class | ||
| 38 | const char *name; | ||
| 39 | |||
| 40 | int (* init)(AVTextWriterContext *wctx); | ||
| 41 | void (* uninit)(AVTextWriterContext *wctx); | ||
| 42 | void (* writer_w8)(AVTextWriterContext *wctx, int b); | ||
| 43 | void (* writer_put_str)(AVTextWriterContext *wctx, const char *str); | ||
| 44 | void (* writer_printf)(AVTextWriterContext *wctx, const char *fmt, ...); | ||
| 45 | } AVTextWriter; | ||
| 46 | |||
| 47 | typedef struct AVTextWriterContext { | ||
| 48 | const AVClass *class; ///< class of the writer | ||
| 49 | const AVTextWriter *writer; | ||
| 50 | const char *name; | ||
| 51 | void *priv; ///< private data for use by the writer | ||
| 52 | |||
| 53 | } AVTextWriterContext; | ||
| 54 | |||
| 55 | |||
| 56 | int avtextwriter_context_open(AVTextWriterContext **pwctx, const AVTextWriter *writer); | ||
| 57 | |||
| 58 | int avtextwriter_context_close(AVTextWriterContext **pwctx); | ||
| 59 | |||
| 60 | int avtextwriter_create_stdout(AVTextWriterContext **pwctx); | ||
| 61 | |||
| 62 | int avtextwriter_create_avio(AVTextWriterContext **pwctx, AVIOContext *avio_ctx, int close_on_uninit); | ||
| 63 | |||
| 64 | int avtextwriter_create_file(AVTextWriterContext **pwctx, const char *output_filename, int close_on_uninit); | ||
| 65 | |||
| 66 | int avtextwriter_create_buffer(AVTextWriterContext **pwctx, AVBPrint *buffer); | ||
| 67 | |||
| 68 | #endif /* FFTOOLS_TEXTFORMAT_AVTEXTWRITERS_H */ | ||
| @@ -0,0 +1,282 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) The FFmpeg developers | ||
| 3 | * | ||
| 4 | * This file is part of FFmpeg. | ||
| 5 | * | ||
| 6 | * FFmpeg is free software; you can redistribute it and/or | ||
| 7 | * modify it under the terms of the GNU Lesser General Public | ||
| 8 | * License as published by the Free Software Foundation; either | ||
| 9 | * version 2.1 of the License, or (at your option) any later version. | ||
| 10 | * | ||
| 11 | * FFmpeg is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 14 | * Lesser General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU Lesser General Public | ||
| 17 | * License along with FFmpeg; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 19 | */ | ||
| 20 | |||
| 21 | #include <limits.h> | ||
| 22 | #include <stdarg.h> | ||
| 23 | #include <stdint.h> | ||
| 24 | #include <stdio.h> | ||
| 25 | #include <string.h> | ||
| 26 | |||
| 27 | #include "avtextformat.h" | ||
| 28 | #include <libavutil/mem.h> | ||
| 29 | #include <libavutil/avassert.h> | ||
| 30 | #include <libavutil/bprint.h> | ||
| 31 | #include <libavutil/error.h> | ||
| 32 | #include <libavutil/macros.h> | ||
| 33 | #include <libavutil/opt.h> | ||
| 34 | |||
| 35 | |||
| 36 | #define writer_w8(wctx_, b_) (wctx_)->writer->writer->writer_w8((wctx_)->writer, b_) | ||
| 37 | #define writer_put_str(wctx_, str_) (wctx_)->writer->writer->writer_put_str((wctx_)->writer, str_) | ||
| 38 | #define writer_printf(wctx_, fmt_, ...) (wctx_)->writer->writer->writer_printf((wctx_)->writer, fmt_, __VA_ARGS__) | ||
| 39 | |||
| 40 | |||
| 41 | #define DEFINE_FORMATTER_CLASS(name) \ | ||
| 42 | static const char *name##_get_name(void *ctx) \ | ||
| 43 | { \ | ||
| 44 | return #name ; \ | ||
| 45 | } \ | ||
| 46 | static const AVClass name##_class = { \ | ||
| 47 | .class_name = #name, \ | ||
| 48 | .item_name = name##_get_name, \ | ||
| 49 | .option = name##_options \ | ||
| 50 | } | ||
| 51 | |||
| 52 | |||
| 53 | /* Compact output */ | ||
| 54 | |||
| 55 | /** | ||
| 56 | * Apply C-language-like string escaping. | ||
| 57 | */ | ||
| 58 | static const char *c_escape_str(AVBPrint *dst, const char *src, const char sep, void *log_ctx) | ||
| 59 | { | ||
| 60 | const char *p; | ||
| 61 | |||
| 62 | for (p = src; *p; p++) { | ||
| 63 | switch (*p) { | ||
| 64 | case '\b': av_bprintf(dst, "%s", "\\b"); break; | ||
| 65 | case '\f': av_bprintf(dst, "%s", "\\f"); break; | ||
| 66 | case '\n': av_bprintf(dst, "%s", "\\n"); break; | ||
| 67 | case '\r': av_bprintf(dst, "%s", "\\r"); break; | ||
| 68 | case '\\': av_bprintf(dst, "%s", "\\\\"); break; | ||
| 69 | default: | ||
| 70 | if (*p == sep) | ||
| 71 | av_bprint_chars(dst, '\\', 1); | ||
| 72 | av_bprint_chars(dst, *p, 1); | ||
| 73 | } | ||
| 74 | } | ||
| 75 | return dst->str; | ||
| 76 | } | ||
| 77 | |||
| 78 | /** | ||
| 79 | * Quote fields containing special characters, check RFC4180. | ||
| 80 | */ | ||
| 81 | static const char *csv_escape_str(AVBPrint *dst, const char *src, const char sep, void *log_ctx) | ||
| 82 | { | ||
| 83 | char meta_chars[] = { sep, '"', '\n', '\r', '\0' }; | ||
| 84 | int needs_quoting = !!src[strcspn(src, meta_chars)]; | ||
| 85 | |||
| 86 | if (needs_quoting) | ||
| 87 | av_bprint_chars(dst, '"', 1); | ||
| 88 | |||
| 89 | for (; *src; src++) { | ||
| 90 | if (*src == '"') | ||
| 91 | av_bprint_chars(dst, '"', 1); | ||
| 92 | av_bprint_chars(dst, *src, 1); | ||
| 93 | } | ||
| 94 | if (needs_quoting) | ||
| 95 | av_bprint_chars(dst, '"', 1); | ||
| 96 | return dst->str; | ||
| 97 | } | ||
| 98 | |||
| 99 | static const char *none_escape_str(AVBPrint *dst, const char *src, const char sep, void *log_ctx) | ||
| 100 | { | ||
| 101 | return src; | ||
| 102 | } | ||
| 103 | |||
| 104 | typedef struct CompactContext { | ||
| 105 | const AVClass *class; | ||
| 106 | char *item_sep_str; | ||
| 107 | char item_sep; | ||
| 108 | int nokey; | ||
| 109 | int print_section; | ||
| 110 | char *escape_mode_str; | ||
| 111 | const char * (*escape_str)(AVBPrint *dst, const char *src, const char sep, void *log_ctx); | ||
| 112 | int nested_section[SECTION_MAX_NB_LEVELS]; | ||
| 113 | int has_nested_elems[SECTION_MAX_NB_LEVELS]; | ||
| 114 | int terminate_line[SECTION_MAX_NB_LEVELS]; | ||
| 115 | } CompactContext; | ||
| 116 | |||
| 117 | #undef OFFSET | ||
| 118 | #define OFFSET(x) offsetof(CompactContext, x) | ||
| 119 | |||
| 120 | static const AVOption compact_options[]= { | ||
| 121 | {"item_sep", "set item separator", OFFSET(item_sep_str), AV_OPT_TYPE_STRING, {.str="|"}, 0, 0 }, | ||
| 122 | {"s", "set item separator", OFFSET(item_sep_str), AV_OPT_TYPE_STRING, {.str="|"}, 0, 0 }, | ||
| 123 | {"nokey", "force no key printing", OFFSET(nokey), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 }, | ||
| 124 | {"nk", "force no key printing", OFFSET(nokey), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 }, | ||
| 125 | {"escape", "set escape mode", OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="c"}, 0, 0 }, | ||
| 126 | {"e", "set escape mode", OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="c"}, 0, 0 }, | ||
| 127 | {"print_section", "print section name", OFFSET(print_section), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 }, | ||
| 128 | {"p", "print section name", OFFSET(print_section), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 }, | ||
| 129 | {NULL}, | ||
| 130 | }; | ||
| 131 | |||
| 132 | DEFINE_FORMATTER_CLASS(compact); | ||
| 133 | |||
| 134 | static av_cold int compact_init(AVTextFormatContext *wctx) | ||
| 135 | { | ||
| 136 | CompactContext *compact = wctx->priv; | ||
| 137 | |||
| 138 | if (strlen(compact->item_sep_str) != 1) { | ||
| 139 | av_log(wctx, AV_LOG_ERROR, "Item separator '%s' specified, but must contain a single character\n", | ||
| 140 | compact->item_sep_str); | ||
| 141 | return AVERROR(EINVAL); | ||
| 142 | } | ||
| 143 | compact->item_sep = compact->item_sep_str[0]; | ||
| 144 | |||
| 145 | if (!strcmp(compact->escape_mode_str, "none")) compact->escape_str = none_escape_str; | ||
| 146 | else if (!strcmp(compact->escape_mode_str, "c" )) compact->escape_str = c_escape_str; | ||
| 147 | else if (!strcmp(compact->escape_mode_str, "csv" )) compact->escape_str = csv_escape_str; | ||
| 148 | else { | ||
| 149 | av_log(wctx, AV_LOG_ERROR, "Unknown escape mode '%s'\n", compact->escape_mode_str); | ||
| 150 | return AVERROR(EINVAL); | ||
| 151 | } | ||
| 152 | |||
| 153 | return 0; | ||
| 154 | } | ||
| 155 | |||
| 156 | static void compact_print_section_header(AVTextFormatContext *wctx, const void *data) | ||
| 157 | { | ||
| 158 | CompactContext *compact = wctx->priv; | ||
| 159 | const struct AVTextFormatSection *section = wctx->section[wctx->level]; | ||
| 160 | const struct AVTextFormatSection *parent_section = wctx->level ? | ||
| 161 | wctx->section[wctx->level-1] : NULL; | ||
| 162 | compact->terminate_line[wctx->level] = 1; | ||
| 163 | compact->has_nested_elems[wctx->level] = 0; | ||
| 164 | |||
| 165 | av_bprint_clear(&wctx->section_pbuf[wctx->level]); | ||
| 166 | if (parent_section && | ||
| 167 | (section->flags & AV_TEXTFORMAT_SECTION_FLAG_HAS_TYPE || | ||
| 168 | (!(section->flags & AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY) && | ||
| 169 | !(parent_section->flags & (AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER|AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY))))) { | ||
| 170 | |||
| 171 | /* define a prefix for elements not contained in an array or | ||
| 172 | in a wrapper, or for array elements with a type */ | ||
| 173 | const char *element_name = (char *)av_x_if_null(section->element_name, section->name); | ||
| 174 | AVBPrint *section_pbuf = &wctx->section_pbuf[wctx->level]; | ||
| 175 | |||
| 176 | compact->nested_section[wctx->level] = 1; | ||
| 177 | compact->has_nested_elems[wctx->level-1] = 1; | ||
| 178 | |||
| 179 | av_bprintf(section_pbuf, "%s%s", | ||
| 180 | wctx->section_pbuf[wctx->level-1].str, element_name); | ||
| 181 | |||
| 182 | if (section->flags & AV_TEXTFORMAT_SECTION_FLAG_HAS_TYPE) { | ||
| 183 | // add /TYPE to prefix | ||
| 184 | av_bprint_chars(section_pbuf, '/', 1); | ||
| 185 | |||
| 186 | // normalize section type, replace special characters and lower case | ||
| 187 | for (const char *p = section->get_type(data); *p; p++) { | ||
| 188 | char c = | ||
| 189 | (*p >= '0' && *p <= '9') || | ||
| 190 | (*p >= 'a' && *p <= 'z') || | ||
| 191 | (*p >= 'A' && *p <= 'Z') ? av_tolower(*p) : '_'; | ||
| 192 | av_bprint_chars(section_pbuf, c, 1); | ||
| 193 | } | ||
| 194 | } | ||
| 195 | av_bprint_chars(section_pbuf, ':', 1); | ||
| 196 | |||
| 197 | wctx->nb_item[wctx->level] = wctx->nb_item[wctx->level-1]; | ||
| 198 | } else { | ||
| 199 | if (parent_section && !(parent_section->flags & (AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER|AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY)) && | ||
| 200 | wctx->level && wctx->nb_item[wctx->level-1]) | ||
| 201 | writer_w8(wctx, compact->item_sep); | ||
| 202 | if (compact->print_section && | ||
| 203 | !(section->flags & (AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER|AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY))) | ||
| 204 | writer_printf(wctx, "%s%c", section->name, compact->item_sep); | ||
| 205 | } | ||
| 206 | } | ||
| 207 | |||
| 208 | static void compact_print_section_footer(AVTextFormatContext *wctx) | ||
| 209 | { | ||
| 210 | CompactContext *compact = wctx->priv; | ||
| 211 | |||
| 212 | if (!compact->nested_section[wctx->level] && | ||
| 213 | compact->terminate_line[wctx->level] && | ||
| 214 | !(wctx->section[wctx->level]->flags & (AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER|AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY))) | ||
| 215 | writer_w8(wctx, '\n'); | ||
| 216 | } | ||
| 217 | |||
| 218 | static void compact_print_str(AVTextFormatContext *wctx, const char *key, const char *value) | ||
| 219 | { | ||
| 220 | CompactContext *compact = wctx->priv; | ||
| 221 | AVBPrint buf; | ||
| 222 | |||
| 223 | if (wctx->nb_item[wctx->level]) writer_w8(wctx, compact->item_sep); | ||
| 224 | if (!compact->nokey) | ||
| 225 | writer_printf(wctx, "%s%s=", wctx->section_pbuf[wctx->level].str, key); | ||
| 226 | av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED); | ||
| 227 | writer_put_str(wctx, compact->escape_str(&buf, value, compact->item_sep, wctx)); | ||
| 228 | av_bprint_finalize(&buf, NULL); | ||
| 229 | } | ||
| 230 | |||
| 231 | static void compact_print_int(AVTextFormatContext *wctx, const char *key, int64_t value) | ||
| 232 | { | ||
| 233 | CompactContext *compact = wctx->priv; | ||
| 234 | |||
| 235 | if (wctx->nb_item[wctx->level]) writer_w8(wctx, compact->item_sep); | ||
| 236 | if (!compact->nokey) | ||
| 237 | writer_printf(wctx, "%s%s=", wctx->section_pbuf[wctx->level].str, key); | ||
| 238 | writer_printf(wctx, "%"PRId64, value); | ||
| 239 | } | ||
| 240 | |||
| 241 | const AVTextFormatter avtextformatter_compact = { | ||
| 242 | .name = "compact", | ||
| 243 | .priv_size = sizeof(CompactContext), | ||
| 244 | .init = compact_init, | ||
| 245 | .print_section_header = compact_print_section_header, | ||
| 246 | .print_section_footer = compact_print_section_footer, | ||
| 247 | .print_integer = compact_print_int, | ||
| 248 | .print_string = compact_print_str, | ||
| 249 | .flags = AV_TEXTFORMAT_FLAG_SUPPORTS_OPTIONAL_FIELDS, | ||
| 250 | .priv_class = &compact_class, | ||
| 251 | }; | ||
| 252 | |||
| 253 | /* CSV output */ | ||
| 254 | |||
| 255 | #undef OFFSET | ||
| 256 | #define OFFSET(x) offsetof(CompactContext, x) | ||
| 257 | |||
| 258 | static const AVOption csv_options[] = { | ||
| 259 | {"item_sep", "set item separator", OFFSET(item_sep_str), AV_OPT_TYPE_STRING, {.str=","}, 0, 0 }, | ||
| 260 | {"s", "set item separator", OFFSET(item_sep_str), AV_OPT_TYPE_STRING, {.str=","}, 0, 0 }, | ||
| 261 | {"nokey", "force no key printing", OFFSET(nokey), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 }, | ||
| 262 | {"nk", "force no key printing", OFFSET(nokey), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 }, | ||
| 263 | {"escape", "set escape mode", OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="csv"}, 0, 0 }, | ||
| 264 | {"e", "set escape mode", OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="csv"}, 0, 0 }, | ||
| 265 | {"print_section", "print section name", OFFSET(print_section), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 }, | ||
| 266 | {"p", "print section name", OFFSET(print_section), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 }, | ||
| 267 | {NULL}, | ||
| 268 | }; | ||
| 269 | |||
| 270 | DEFINE_FORMATTER_CLASS(csv); | ||
| 271 | |||
| 272 | const AVTextFormatter avtextformatter_csv = { | ||
| 273 | .name = "csv", | ||
| 274 | .priv_size = sizeof(CompactContext), | ||
| 275 | .init = compact_init, | ||
| 276 | .print_section_header = compact_print_section_header, | ||
| 277 | .print_section_footer = compact_print_section_footer, | ||
| 278 | .print_integer = compact_print_int, | ||
| 279 | .print_string = compact_print_str, | ||
| 280 | .flags = AV_TEXTFORMAT_FLAG_SUPPORTS_OPTIONAL_FIELDS, | ||
| 281 | .priv_class = &csv_class, | ||
| 282 | }; | ||
| @@ -0,0 +1,145 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) The FFmpeg developers | ||
| 3 | * | ||
| 4 | * This file is part of FFmpeg. | ||
| 5 | * | ||
| 6 | * FFmpeg is free software; you can redistribute it and/or | ||
| 7 | * modify it under the terms of the GNU Lesser General Public | ||
| 8 | * License as published by the Free Software Foundation; either | ||
| 9 | * version 2.1 of the License, or (at your option) any later version. | ||
| 10 | * | ||
| 11 | * FFmpeg is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 14 | * Lesser General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU Lesser General Public | ||
| 17 | * License along with FFmpeg; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 19 | */ | ||
| 20 | |||
| 21 | #include <limits.h> | ||
| 22 | #include <stdarg.h> | ||
| 23 | #include <stdint.h> | ||
| 24 | #include <stdio.h> | ||
| 25 | #include <string.h> | ||
| 26 | |||
| 27 | #include "avtextformat.h" | ||
| 28 | #include <libavutil/mem.h> | ||
| 29 | #include <libavutil/avassert.h> | ||
| 30 | #include <libavutil/bprint.h> | ||
| 31 | #include <libavutil/opt.h> | ||
| 32 | |||
| 33 | #define writer_w8(wctx_, b_) (wctx_)->writer->writer->writer_w8((wctx_)->writer, b_) | ||
| 34 | #define writer_put_str(wctx_, str_) (wctx_)->writer->writer->writer_put_str((wctx_)->writer, str_) | ||
| 35 | #define writer_printf(wctx_, fmt_, ...) (wctx_)->writer->writer->writer_printf((wctx_)->writer, fmt_, __VA_ARGS__) | ||
| 36 | |||
| 37 | #define DEFINE_FORMATTER_CLASS(name) \ | ||
| 38 | static const char *name##_get_name(void *ctx) \ | ||
| 39 | { \ | ||
| 40 | return #name ; \ | ||
| 41 | } \ | ||
| 42 | static const AVClass name##_class = { \ | ||
| 43 | .class_name = #name, \ | ||
| 44 | .item_name = name##_get_name, \ | ||
| 45 | .option = name##_options \ | ||
| 46 | } | ||
| 47 | |||
| 48 | /* Default output */ | ||
| 49 | |||
| 50 | typedef struct DefaultContext { | ||
| 51 | const AVClass *class; | ||
| 52 | int nokey; | ||
| 53 | int noprint_wrappers; | ||
| 54 | int nested_section[SECTION_MAX_NB_LEVELS]; | ||
| 55 | } DefaultContext; | ||
| 56 | |||
| 57 | #undef OFFSET | ||
| 58 | #define OFFSET(x) offsetof(DefaultContext, x) | ||
| 59 | |||
| 60 | static const AVOption default_options[] = { | ||
| 61 | { "noprint_wrappers", "do not print headers and footers", OFFSET(noprint_wrappers), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 }, | ||
| 62 | { "nw", "do not print headers and footers", OFFSET(noprint_wrappers), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 }, | ||
| 63 | { "nokey", "force no key printing", OFFSET(nokey), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 }, | ||
| 64 | { "nk", "force no key printing", OFFSET(nokey), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 }, | ||
| 65 | {NULL}, | ||
| 66 | }; | ||
| 67 | |||
| 68 | DEFINE_FORMATTER_CLASS(default); | ||
| 69 | |||
| 70 | /* lame uppercasing routine, assumes the string is lower case ASCII */ | ||
| 71 | static inline char *upcase_string(char *dst, size_t dst_size, const char *src) | ||
| 72 | { | ||
| 73 | int i; | ||
| 74 | for (i = 0; src[i] && i < dst_size-1; i++) | ||
| 75 | dst[i] = av_toupper(src[i]); | ||
| 76 | dst[i] = 0; | ||
| 77 | return dst; | ||
| 78 | } | ||
| 79 | |||
| 80 | static void default_print_section_header(AVTextFormatContext *wctx, const void *data) | ||
| 81 | { | ||
| 82 | DefaultContext *def = wctx->priv; | ||
| 83 | char buf[32]; | ||
| 84 | const struct AVTextFormatSection *section = wctx->section[wctx->level]; | ||
| 85 | const struct AVTextFormatSection *parent_section = wctx->level ? | ||
| 86 | wctx->section[wctx->level-1] : NULL; | ||
| 87 | |||
| 88 | av_bprint_clear(&wctx->section_pbuf[wctx->level]); | ||
| 89 | if (parent_section && | ||
| 90 | !(parent_section->flags & (AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER|AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY))) { | ||
| 91 | def->nested_section[wctx->level] = 1; | ||
| 92 | av_bprintf(&wctx->section_pbuf[wctx->level], "%s%s:", | ||
| 93 | wctx->section_pbuf[wctx->level-1].str, | ||
| 94 | upcase_string(buf, sizeof(buf), | ||
| 95 | av_x_if_null(section->element_name, section->name))); | ||
| 96 | } | ||
| 97 | |||
| 98 | if (def->noprint_wrappers || def->nested_section[wctx->level]) | ||
| 99 | return; | ||
| 100 | |||
| 101 | if (!(section->flags & (AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER|AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY))) | ||
| 102 | writer_printf(wctx, "[%s]\n", upcase_string(buf, sizeof(buf), section->name)); | ||
| 103 | } | ||
| 104 | |||
| 105 | static void default_print_section_footer(AVTextFormatContext *wctx) | ||
| 106 | { | ||
| 107 | DefaultContext *def = wctx->priv; | ||
| 108 | const struct AVTextFormatSection *section = wctx->section[wctx->level]; | ||
| 109 | char buf[32]; | ||
| 110 | |||
| 111 | if (def->noprint_wrappers || def->nested_section[wctx->level]) | ||
| 112 | return; | ||
| 113 | |||
| 114 | if (!(section->flags & (AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER|AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY))) | ||
| 115 | writer_printf(wctx, "[/%s]\n", upcase_string(buf, sizeof(buf), section->name)); | ||
| 116 | } | ||
| 117 | |||
| 118 | static void default_print_str(AVTextFormatContext *wctx, const char *key, const char *value) | ||
| 119 | { | ||
| 120 | DefaultContext *def = wctx->priv; | ||
| 121 | |||
| 122 | if (!def->nokey) | ||
| 123 | writer_printf(wctx, "%s%s=", wctx->section_pbuf[wctx->level].str, key); | ||
| 124 | writer_printf(wctx, "%s\n", value); | ||
| 125 | } | ||
| 126 | |||
| 127 | static void default_print_int(AVTextFormatContext *wctx, const char *key, int64_t value) | ||
| 128 | { | ||
| 129 | DefaultContext *def = wctx->priv; | ||
| 130 | |||
| 131 | if (!def->nokey) | ||
| 132 | writer_printf(wctx, "%s%s=", wctx->section_pbuf[wctx->level].str, key); | ||
| 133 | writer_printf(wctx, "%"PRId64"\n", value); | ||
| 134 | } | ||
| 135 | |||
| 136 | const AVTextFormatter avtextformatter_default = { | ||
| 137 | .name = "default", | ||
| 138 | .priv_size = sizeof(DefaultContext), | ||
| 139 | .print_section_header = default_print_section_header, | ||
| 140 | .print_section_footer = default_print_section_footer, | ||
| 141 | .print_integer = default_print_int, | ||
| 142 | .print_string = default_print_str, | ||
| 143 | .flags = AV_TEXTFORMAT_FLAG_SUPPORTS_OPTIONAL_FIELDS, | ||
| 144 | .priv_class = &default_class, | ||
| 145 | }; | ||
| @@ -0,0 +1,174 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) The FFmpeg developers | ||
| 3 | * | ||
| 4 | * This file is part of FFmpeg. | ||
| 5 | * | ||
| 6 | * FFmpeg is free software; you can redistribute it and/or | ||
| 7 | * modify it under the terms of the GNU Lesser General Public | ||
| 8 | * License as published by the Free Software Foundation; either | ||
| 9 | * version 2.1 of the License, or (at your option) any later version. | ||
| 10 | * | ||
| 11 | * FFmpeg is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 14 | * Lesser General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU Lesser General Public | ||
| 17 | * License along with FFmpeg; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 19 | */ | ||
| 20 | |||
| 21 | #include <limits.h> | ||
| 22 | #include <stdarg.h> | ||
| 23 | #include <stdint.h> | ||
| 24 | #include <stdio.h> | ||
| 25 | #include <string.h> | ||
| 26 | |||
| 27 | #include "avtextformat.h" | ||
| 28 | #include <libavutil/mem.h> | ||
| 29 | #include <libavutil/avassert.h> | ||
| 30 | #include <libavutil/bprint.h> | ||
| 31 | #include <libavutil/error.h> | ||
| 32 | #include <libavutil/macros.h> | ||
| 33 | #include <libavutil/opt.h> | ||
| 34 | |||
| 35 | #define writer_w8(wctx_, b_) (wctx_)->writer->writer->writer_w8((wctx_)->writer, b_) | ||
| 36 | #define writer_put_str(wctx_, str_) (wctx_)->writer->writer->writer_put_str((wctx_)->writer, str_) | ||
| 37 | #define writer_printf(wctx_, fmt_, ...) (wctx_)->writer->writer->writer_printf((wctx_)->writer, fmt_, __VA_ARGS__) | ||
| 38 | |||
| 39 | #define DEFINE_FORMATTER_CLASS(name) \ | ||
| 40 | static const char *name##_get_name(void *ctx) \ | ||
| 41 | { \ | ||
| 42 | return #name ; \ | ||
| 43 | } \ | ||
| 44 | static const AVClass name##_class = { \ | ||
| 45 | .class_name = #name, \ | ||
| 46 | .item_name = name##_get_name, \ | ||
| 47 | .option = name##_options \ | ||
| 48 | } | ||
| 49 | |||
| 50 | |||
| 51 | /* Flat output */ | ||
| 52 | |||
| 53 | typedef struct FlatContext { | ||
| 54 | const AVClass *class; | ||
| 55 | const char *sep_str; | ||
| 56 | char sep; | ||
| 57 | int hierarchical; | ||
| 58 | } FlatContext; | ||
| 59 | |||
| 60 | #undef OFFSET | ||
| 61 | #define OFFSET(x) offsetof(FlatContext, x) | ||
| 62 | |||
| 63 | static const AVOption flat_options[]= { | ||
| 64 | {"sep_char", "set separator", OFFSET(sep_str), AV_OPT_TYPE_STRING, {.str="."}, 0, 0 }, | ||
| 65 | {"s", "set separator", OFFSET(sep_str), AV_OPT_TYPE_STRING, {.str="."}, 0, 0 }, | ||
| 66 | {"hierarchical", "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 }, | ||
| 67 | {"h", "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 }, | ||
| 68 | {NULL}, | ||
| 69 | }; | ||
| 70 | |||
| 71 | DEFINE_FORMATTER_CLASS(flat); | ||
| 72 | |||
| 73 | static av_cold int flat_init(AVTextFormatContext *wctx) | ||
| 74 | { | ||
| 75 | FlatContext *flat = wctx->priv; | ||
| 76 | |||
| 77 | if (strlen(flat->sep_str) != 1) { | ||
| 78 | av_log(wctx, AV_LOG_ERROR, "Item separator '%s' specified, but must contain a single character\n", | ||
| 79 | flat->sep_str); | ||
| 80 | return AVERROR(EINVAL); | ||
| 81 | } | ||
| 82 | flat->sep = flat->sep_str[0]; | ||
| 83 | |||
| 84 | return 0; | ||
| 85 | } | ||
| 86 | |||
| 87 | static const char *flat_escape_key_str(AVBPrint *dst, const char *src, const char sep) | ||
| 88 | { | ||
| 89 | const char *p; | ||
| 90 | |||
| 91 | for (p = src; *p; p++) { | ||
| 92 | if (!((*p >= '0' && *p <= '9') || | ||
| 93 | (*p >= 'a' && *p <= 'z') || | ||
| 94 | (*p >= 'A' && *p <= 'Z'))) | ||
| 95 | av_bprint_chars(dst, '_', 1); | ||
| 96 | else | ||
| 97 | av_bprint_chars(dst, *p, 1); | ||
| 98 | } | ||
| 99 | return dst->str; | ||
| 100 | } | ||
| 101 | |||
| 102 | static const char *flat_escape_value_str(AVBPrint *dst, const char *src) | ||
| 103 | { | ||
| 104 | const char *p; | ||
| 105 | |||
| 106 | for (p = src; *p; p++) { | ||
| 107 | switch (*p) { | ||
| 108 | case '\n': av_bprintf(dst, "%s", "\\n"); break; | ||
| 109 | case '\r': av_bprintf(dst, "%s", "\\r"); break; | ||
| 110 | case '\\': av_bprintf(dst, "%s", "\\\\"); break; | ||
| 111 | case '"': av_bprintf(dst, "%s", "\\\""); break; | ||
| 112 | case '`': av_bprintf(dst, "%s", "\\`"); break; | ||
| 113 | case '$': av_bprintf(dst, "%s", "\\$"); break; | ||
| 114 | default: av_bprint_chars(dst, *p, 1); break; | ||
| 115 | } | ||
| 116 | } | ||
| 117 | return dst->str; | ||
| 118 | } | ||
| 119 | |||
| 120 | static void flat_print_section_header(AVTextFormatContext *wctx, const void *data) | ||
| 121 | { | ||
| 122 | FlatContext *flat = wctx->priv; | ||
| 123 | AVBPrint *buf = &wctx->section_pbuf[wctx->level]; | ||
| 124 | const struct AVTextFormatSection *section = wctx->section[wctx->level]; | ||
| 125 | const struct AVTextFormatSection *parent_section = wctx->level ? | ||
| 126 | wctx->section[wctx->level-1] : NULL; | ||
| 127 | |||
| 128 | /* build section header */ | ||
| 129 | av_bprint_clear(buf); | ||
| 130 | if (!parent_section) | ||
| 131 | return; | ||
| 132 | av_bprintf(buf, "%s", wctx->section_pbuf[wctx->level-1].str); | ||
| 133 | |||
| 134 | if (flat->hierarchical || | ||
| 135 | !(section->flags & (AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY|AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER))) { | ||
| 136 | av_bprintf(buf, "%s%s", wctx->section[wctx->level]->name, flat->sep_str); | ||
| 137 | |||
| 138 | if (parent_section->flags & AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY) { | ||
| 139 | int n = parent_section->flags & AV_TEXTFORMAT_SECTION_FLAG_NUMBERING_BY_TYPE ? | ||
| 140 | wctx->nb_item_type[wctx->level-1][section->id] : | ||
| 141 | wctx->nb_item[wctx->level-1]; | ||
| 142 | av_bprintf(buf, "%d%s", n, flat->sep_str); | ||
| 143 | } | ||
| 144 | } | ||
| 145 | } | ||
| 146 | |||
| 147 | static void flat_print_int(AVTextFormatContext *wctx, const char *key, int64_t value) | ||
| 148 | { | ||
| 149 | writer_printf(wctx, "%s%s=%"PRId64"\n", wctx->section_pbuf[wctx->level].str, key, value); | ||
| 150 | } | ||
| 151 | |||
| 152 | static void flat_print_str(AVTextFormatContext *wctx, const char *key, const char *value) | ||
| 153 | { | ||
| 154 | FlatContext *flat = wctx->priv; | ||
| 155 | AVBPrint buf; | ||
| 156 | |||
| 157 | writer_put_str(wctx, wctx->section_pbuf[wctx->level].str); | ||
| 158 | av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED); | ||
| 159 | writer_printf(wctx, "%s=", flat_escape_key_str(&buf, key, flat->sep)); | ||
| 160 | av_bprint_clear(&buf); | ||
| 161 | writer_printf(wctx, "\"%s\"\n", flat_escape_value_str(&buf, value)); | ||
| 162 | av_bprint_finalize(&buf, NULL); | ||
| 163 | } | ||
| 164 | |||
| 165 | const AVTextFormatter avtextformatter_flat = { | ||
| 166 | .name = "flat", | ||
| 167 | .priv_size = sizeof(FlatContext), | ||
| 168 | .init = flat_init, | ||
| 169 | .print_section_header = flat_print_section_header, | ||
| 170 | .print_integer = flat_print_int, | ||
| 171 | .print_string = flat_print_str, | ||
| 172 | .flags = AV_TEXTFORMAT_FLAG_SUPPORTS_OPTIONAL_FIELDS|AV_TEXTFORMAT_FLAG_SUPPORTS_MIXED_ARRAY_CONTENT, | ||
| 173 | .priv_class = &flat_class, | ||
| 174 | }; | ||
| @@ -0,0 +1,160 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) The FFmpeg developers | ||
| 3 | * | ||
| 4 | * This file is part of FFmpeg. | ||
| 5 | * | ||
| 6 | * FFmpeg is free software; you can redistribute it and/or | ||
| 7 | * modify it under the terms of the GNU Lesser General Public | ||
| 8 | * License as published by the Free Software Foundation; either | ||
| 9 | * version 2.1 of the License, or (at your option) any later version. | ||
| 10 | * | ||
| 11 | * FFmpeg is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 14 | * Lesser General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU Lesser General Public | ||
| 17 | * License along with FFmpeg; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 19 | */ | ||
| 20 | |||
| 21 | #include <limits.h> | ||
| 22 | #include <stdarg.h> | ||
| 23 | #include <stdint.h> | ||
| 24 | #include <stdio.h> | ||
| 25 | #include <string.h> | ||
| 26 | |||
| 27 | #include "avtextformat.h" | ||
| 28 | #include <libavutil/mem.h> | ||
| 29 | #include <libavutil/avassert.h> | ||
| 30 | #include <libavutil/bprint.h> | ||
| 31 | #include <libavutil/opt.h> | ||
| 32 | |||
| 33 | #define writer_w8(wctx_, b_) (wctx_)->writer->writer->writer_w8((wctx_)->writer, b_) | ||
| 34 | #define writer_put_str(wctx_, str_) (wctx_)->writer->writer->writer_put_str((wctx_)->writer, str_) | ||
| 35 | #define writer_printf(wctx_, fmt_, ...) (wctx_)->writer->writer->writer_printf((wctx_)->writer, fmt_, __VA_ARGS__) | ||
| 36 | |||
| 37 | #define DEFINE_FORMATTER_CLASS(name) \ | ||
| 38 | static const char *name##_get_name(void *ctx) \ | ||
| 39 | { \ | ||
| 40 | return #name ; \ | ||
| 41 | } \ | ||
| 42 | static const AVClass name##_class = { \ | ||
| 43 | .class_name = #name, \ | ||
| 44 | .item_name = name##_get_name, \ | ||
| 45 | .option = name##_options \ | ||
| 46 | } | ||
| 47 | |||
| 48 | /* Default output */ | ||
| 49 | |||
| 50 | typedef struct DefaultContext { | ||
| 51 | const AVClass *class; | ||
| 52 | int nokey; | ||
| 53 | int noprint_wrappers; | ||
| 54 | int nested_section[SECTION_MAX_NB_LEVELS]; | ||
| 55 | } DefaultContext; | ||
| 56 | |||
| 57 | /* INI format output */ | ||
| 58 | |||
| 59 | typedef struct INIContext { | ||
| 60 | const AVClass *class; | ||
| 61 | int hierarchical; | ||
| 62 | } INIContext; | ||
| 63 | |||
| 64 | #undef OFFSET | ||
| 65 | #define OFFSET(x) offsetof(INIContext, x) | ||
| 66 | |||
| 67 | static const AVOption ini_options[] = { | ||
| 68 | {"hierarchical", "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 }, | ||
| 69 | {"h", "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 }, | ||
| 70 | {NULL}, | ||
| 71 | }; | ||
| 72 | |||
| 73 | DEFINE_FORMATTER_CLASS(ini); | ||
| 74 | |||
| 75 | static char *ini_escape_str(AVBPrint *dst, const char *src) | ||
| 76 | { | ||
| 77 | int i = 0; | ||
| 78 | char c = 0; | ||
| 79 | |||
| 80 | while (c = src[i++]) { | ||
| 81 | switch (c) { | ||
| 82 | case '\b': av_bprintf(dst, "%s", "\\b"); break; | ||
| 83 | case '\f': av_bprintf(dst, "%s", "\\f"); break; | ||
| 84 | case '\n': av_bprintf(dst, "%s", "\\n"); break; | ||
| 85 | case '\r': av_bprintf(dst, "%s", "\\r"); break; | ||
| 86 | case '\t': av_bprintf(dst, "%s", "\\t"); break; | ||
| 87 | case '\\': | ||
| 88 | case '#' : | ||
| 89 | case '=' : | ||
| 90 | case ':' : av_bprint_chars(dst, '\\', 1); | ||
| 91 | default: | ||
| 92 | if ((unsigned char)c < 32) | ||
| 93 | av_bprintf(dst, "\\x00%02x", c & 0xff); | ||
| 94 | else | ||
| 95 | av_bprint_chars(dst, c, 1); | ||
| 96 | break; | ||
| 97 | } | ||
| 98 | } | ||
| 99 | return dst->str; | ||
| 100 | } | ||
| 101 | |||
| 102 | static void ini_print_section_header(AVTextFormatContext *wctx, const void *data) | ||
| 103 | { | ||
| 104 | INIContext *ini = wctx->priv; | ||
| 105 | AVBPrint *buf = &wctx->section_pbuf[wctx->level]; | ||
| 106 | const struct AVTextFormatSection *section = wctx->section[wctx->level]; | ||
| 107 | const struct AVTextFormatSection *parent_section = wctx->level ? | ||
| 108 | wctx->section[wctx->level-1] : NULL; | ||
| 109 | |||
| 110 | av_bprint_clear(buf); | ||
| 111 | if (!parent_section) { | ||
| 112 | writer_put_str(wctx, "# ffprobe output\n\n"); | ||
| 113 | return; | ||
| 114 | } | ||
| 115 | |||
| 116 | if (wctx->nb_item[wctx->level-1]) | ||
| 117 | writer_w8(wctx, '\n'); | ||
| 118 | |||
| 119 | av_bprintf(buf, "%s", wctx->section_pbuf[wctx->level-1].str); | ||
| 120 | if (ini->hierarchical || | ||
| 121 | !(section->flags & (AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY|AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER))) { | ||
| 122 | av_bprintf(buf, "%s%s", buf->str[0] ? "." : "", wctx->section[wctx->level]->name); | ||
| 123 | |||
| 124 | if (parent_section->flags & AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY) { | ||
| 125 | int n = parent_section->flags & AV_TEXTFORMAT_SECTION_FLAG_NUMBERING_BY_TYPE ? | ||
| 126 | wctx->nb_item_type[wctx->level-1][section->id] : | ||
| 127 | wctx->nb_item[wctx->level-1]; | ||
| 128 | av_bprintf(buf, ".%d", n); | ||
| 129 | } | ||
| 130 | } | ||
| 131 | |||
| 132 | if (!(section->flags & (AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY|AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER))) | ||
| 133 | writer_printf(wctx, "[%s]\n", buf->str); | ||
| 134 | } | ||
| 135 | |||
| 136 | static void ini_print_str(AVTextFormatContext *wctx, const char *key, const char *value) | ||
| 137 | { | ||
| 138 | AVBPrint buf; | ||
| 139 | |||
| 140 | av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED); | ||
| 141 | writer_printf(wctx, "%s=", ini_escape_str(&buf, key)); | ||
| 142 | av_bprint_clear(&buf); | ||
| 143 | writer_printf(wctx, "%s\n", ini_escape_str(&buf, value)); | ||
| 144 | av_bprint_finalize(&buf, NULL); | ||
| 145 | } | ||
| 146 | |||
| 147 | static void ini_print_int(AVTextFormatContext *wctx, const char *key, int64_t value) | ||
| 148 | { | ||
| 149 | writer_printf(wctx, "%s=%"PRId64"\n", key, value); | ||
| 150 | } | ||
| 151 | |||
| 152 | const AVTextFormatter avtextformatter_ini = { | ||
| 153 | .name = "ini", | ||
| 154 | .priv_size = sizeof(INIContext), | ||
| 155 | .print_section_header = ini_print_section_header, | ||
| 156 | .print_integer = ini_print_int, | ||
| 157 | .print_string = ini_print_str, | ||
| 158 | .flags = AV_TEXTFORMAT_FLAG_SUPPORTS_OPTIONAL_FIELDS|AV_TEXTFORMAT_FLAG_SUPPORTS_MIXED_ARRAY_CONTENT, | ||
| 159 | .priv_class = &ini_class, | ||
| 160 | }; | ||
| @@ -0,0 +1,215 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) The FFmpeg developers | ||
| 3 | * | ||
| 4 | * This file is part of FFmpeg. | ||
| 5 | * | ||
| 6 | * FFmpeg is free software; you can redistribute it and/or | ||
| 7 | * modify it under the terms of the GNU Lesser General Public | ||
| 8 | * License as published by the Free Software Foundation; either | ||
| 9 | * version 2.1 of the License, or (at your option) any later version. | ||
| 10 | * | ||
| 11 | * FFmpeg is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 14 | * Lesser General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU Lesser General Public | ||
| 17 | * License along with FFmpeg; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 19 | */ | ||
| 20 | |||
| 21 | #include <limits.h> | ||
| 22 | #include <stdarg.h> | ||
| 23 | #include <stdint.h> | ||
| 24 | #include <stdio.h> | ||
| 25 | #include <string.h> | ||
| 26 | |||
| 27 | #include "avtextformat.h" | ||
| 28 | #include <libavutil/mem.h> | ||
| 29 | #include <libavutil/avassert.h> | ||
| 30 | #include <libavutil/bprint.h> | ||
| 31 | #include <libavutil/opt.h> | ||
| 32 | |||
| 33 | #define writer_w8(wctx_, b_) (wctx_)->writer->writer->writer_w8((wctx_)->writer, b_) | ||
| 34 | #define writer_put_str(wctx_, str_) (wctx_)->writer->writer->writer_put_str((wctx_)->writer, str_) | ||
| 35 | #define writer_printf(wctx_, fmt_, ...) (wctx_)->writer->writer->writer_printf((wctx_)->writer, fmt_, __VA_ARGS__) | ||
| 36 | |||
| 37 | #define DEFINE_FORMATTER_CLASS(name) \ | ||
| 38 | static const char *name##_get_name(void *ctx) \ | ||
| 39 | { \ | ||
| 40 | return #name ; \ | ||
| 41 | } \ | ||
| 42 | static const AVClass name##_class = { \ | ||
| 43 | .class_name = #name, \ | ||
| 44 | .item_name = name##_get_name, \ | ||
| 45 | .option = name##_options \ | ||
| 46 | } | ||
| 47 | |||
| 48 | |||
| 49 | /* JSON output */ | ||
| 50 | |||
| 51 | typedef struct JSONContext { | ||
| 52 | const AVClass *class; | ||
| 53 | int indent_level; | ||
| 54 | int compact; | ||
| 55 | const char *item_sep, *item_start_end; | ||
| 56 | } JSONContext; | ||
| 57 | |||
| 58 | #undef OFFSET | ||
| 59 | #define OFFSET(x) offsetof(JSONContext, x) | ||
| 60 | |||
| 61 | static const AVOption json_options[]= { | ||
| 62 | { "compact", "enable compact output", OFFSET(compact), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 }, | ||
| 63 | { "c", "enable compact output", OFFSET(compact), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 }, | ||
| 64 | { NULL } | ||
| 65 | }; | ||
| 66 | |||
| 67 | DEFINE_FORMATTER_CLASS(json); | ||
| 68 | |||
| 69 | static av_cold int json_init(AVTextFormatContext *wctx) | ||
| 70 | { | ||
| 71 | JSONContext *json = wctx->priv; | ||
| 72 | |||
| 73 | json->item_sep = json->compact ? ", " : ",\n"; | ||
| 74 | json->item_start_end = json->compact ? " " : "\n"; | ||
| 75 | |||
| 76 | return 0; | ||
| 77 | } | ||
| 78 | |||
| 79 | static const char *json_escape_str(AVBPrint *dst, const char *src, void *log_ctx) | ||
| 80 | { | ||
| 81 | static const char json_escape[] = {'"', '\\', '\b', '\f', '\n', '\r', '\t', 0}; | ||
| 82 | static const char json_subst[] = {'"', '\\', 'b', 'f', 'n', 'r', 't', 0}; | ||
| 83 | const char *p; | ||
| 84 | |||
| 85 | for (p = src; *p; p++) { | ||
| 86 | char *s = strchr(json_escape, *p); | ||
| 87 | if (s) { | ||
| 88 | av_bprint_chars(dst, '\\', 1); | ||
| 89 | av_bprint_chars(dst, json_subst[s - json_escape], 1); | ||
| 90 | } else if ((unsigned char)*p < 32) { | ||
| 91 | av_bprintf(dst, "\\u00%02x", *p & 0xff); | ||
| 92 | } else { | ||
| 93 | av_bprint_chars(dst, *p, 1); | ||
| 94 | } | ||
| 95 | } | ||
| 96 | return dst->str; | ||
| 97 | } | ||
| 98 | |||
| 99 | #define JSON_INDENT() writer_printf(wctx, "%*c", json->indent_level * 4, ' ') | ||
| 100 | |||
| 101 | static void json_print_section_header(AVTextFormatContext *wctx, const void *data) | ||
| 102 | { | ||
| 103 | JSONContext *json = wctx->priv; | ||
| 104 | AVBPrint buf; | ||
| 105 | const struct AVTextFormatSection *section = wctx->section[wctx->level]; | ||
| 106 | const struct AVTextFormatSection *parent_section = wctx->level ? | ||
| 107 | wctx->section[wctx->level-1] : NULL; | ||
| 108 | |||
| 109 | if (wctx->level && wctx->nb_item[wctx->level-1]) | ||
| 110 | writer_put_str(wctx, ",\n"); | ||
| 111 | |||
| 112 | if (section->flags & AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER) { | ||
| 113 | writer_put_str(wctx, "{\n"); | ||
| 114 | json->indent_level++; | ||
| 115 | } else { | ||
| 116 | av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED); | ||
| 117 | json_escape_str(&buf, section->name, wctx); | ||
| 118 | JSON_INDENT(); | ||
| 119 | |||
| 120 | json->indent_level++; | ||
| 121 | if (section->flags & AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY) { | ||
| 122 | writer_printf(wctx, "\"%s\": [\n", buf.str); | ||
| 123 | } else if (parent_section && !(parent_section->flags & AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY)) { | ||
| 124 | writer_printf(wctx, "\"%s\": {%s", buf.str, json->item_start_end); | ||
| 125 | } else { | ||
| 126 | writer_printf(wctx, "{%s", json->item_start_end); | ||
| 127 | |||
| 128 | /* this is required so the parser can distinguish between packets and frames */ | ||
| 129 | if (parent_section && parent_section->flags & AV_TEXTFORMAT_SECTION_FLAG_NUMBERING_BY_TYPE) { | ||
| 130 | if (!json->compact) | ||
| 131 | JSON_INDENT(); | ||
| 132 | writer_printf(wctx, "\"type\": \"%s\"", section->name); | ||
| 133 | wctx->nb_item[wctx->level]++; | ||
| 134 | } | ||
| 135 | } | ||
| 136 | av_bprint_finalize(&buf, NULL); | ||
| 137 | } | ||
| 138 | } | ||
| 139 | |||
| 140 | static void json_print_section_footer(AVTextFormatContext *wctx) | ||
| 141 | { | ||
| 142 | JSONContext *json = wctx->priv; | ||
| 143 | const struct AVTextFormatSection *section = wctx->section[wctx->level]; | ||
| 144 | |||
| 145 | if (wctx->level == 0) { | ||
| 146 | json->indent_level--; | ||
| 147 | writer_put_str(wctx, "\n}\n"); | ||
| 148 | } else if (section->flags & AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY) { | ||
| 149 | writer_w8(wctx, '\n'); | ||
| 150 | json->indent_level--; | ||
| 151 | JSON_INDENT(); | ||
| 152 | writer_w8(wctx, ']'); | ||
| 153 | } else { | ||
| 154 | writer_put_str(wctx, json->item_start_end); | ||
| 155 | json->indent_level--; | ||
| 156 | if (!json->compact) | ||
| 157 | JSON_INDENT(); | ||
| 158 | writer_w8(wctx, '}'); | ||
| 159 | } | ||
| 160 | } | ||
| 161 | |||
| 162 | static inline void json_print_item_str(AVTextFormatContext *wctx, | ||
| 163 | const char *key, const char *value) | ||
| 164 | { | ||
| 165 | AVBPrint buf; | ||
| 166 | |||
| 167 | av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED); | ||
| 168 | writer_printf(wctx, "\"%s\":", json_escape_str(&buf, key, wctx)); | ||
| 169 | av_bprint_clear(&buf); | ||
| 170 | writer_printf(wctx, " \"%s\"", json_escape_str(&buf, value, wctx)); | ||
| 171 | av_bprint_finalize(&buf, NULL); | ||
| 172 | } | ||
| 173 | |||
| 174 | static void json_print_str(AVTextFormatContext *wctx, const char *key, const char *value) | ||
| 175 | { | ||
| 176 | JSONContext *json = wctx->priv; | ||
| 177 | const struct AVTextFormatSection *parent_section = wctx->level ? | ||
| 178 | wctx->section[wctx->level-1] : NULL; | ||
| 179 | |||
| 180 | if (wctx->nb_item[wctx->level] || (parent_section && parent_section->flags & AV_TEXTFORMAT_SECTION_FLAG_NUMBERING_BY_TYPE)) | ||
| 181 | writer_put_str(wctx, json->item_sep); | ||
| 182 | if (!json->compact) | ||
| 183 | JSON_INDENT(); | ||
| 184 | json_print_item_str(wctx, key, value); | ||
| 185 | } | ||
| 186 | |||
| 187 | static void json_print_int(AVTextFormatContext *wctx, const char *key, int64_t value) | ||
| 188 | { | ||
| 189 | JSONContext *json = wctx->priv; | ||
| 190 | const struct AVTextFormatSection *parent_section = wctx->level ? | ||
| 191 | wctx->section[wctx->level-1] : NULL; | ||
| 192 | AVBPrint buf; | ||
| 193 | |||
| 194 | if (wctx->nb_item[wctx->level] || (parent_section && parent_section->flags & AV_TEXTFORMAT_SECTION_FLAG_NUMBERING_BY_TYPE)) | ||
| 195 | writer_put_str(wctx, json->item_sep); | ||
| 196 | if (!json->compact) | ||
| 197 | JSON_INDENT(); | ||
| 198 | |||
| 199 | av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED); | ||
| 200 | writer_printf(wctx, "\"%s\": %"PRId64, json_escape_str(&buf, key, wctx), value); | ||
| 201 | av_bprint_finalize(&buf, NULL); | ||
| 202 | } | ||
| 203 | |||
| 204 | const AVTextFormatter avtextformatter_json = { | ||
| 205 | .name = "json", | ||
| 206 | .priv_size = sizeof(JSONContext), | ||
| 207 | .init = json_init, | ||
| 208 | .print_section_header = json_print_section_header, | ||
| 209 | .print_section_footer = json_print_section_footer, | ||
| 210 | .print_integer = json_print_int, | ||
| 211 | .print_string = json_print_str, | ||
| 212 | .flags = AV_TEXTFORMAT_FLAG_SUPPORTS_MIXED_ARRAY_CONTENT, | ||
| 213 | .priv_class = &json_class, | ||
| 214 | }; | ||
| 215 | |||
| @@ -0,0 +1,221 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) The FFmpeg developers | ||
| 3 | * | ||
| 4 | * This file is part of FFmpeg. | ||
| 5 | * | ||
| 6 | * FFmpeg is free software; you can redistribute it and/or | ||
| 7 | * modify it under the terms of the GNU Lesser General Public | ||
| 8 | * License as published by the Free Software Foundation; either | ||
| 9 | * version 2.1 of the License, or (at your option) any later version. | ||
| 10 | * | ||
| 11 | * FFmpeg is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 14 | * Lesser General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU Lesser General Public | ||
| 17 | * License along with FFmpeg; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 19 | */ | ||
| 20 | |||
| 21 | #include <limits.h> | ||
| 22 | #include <stdarg.h> | ||
| 23 | #include <stdint.h> | ||
| 24 | #include <stdio.h> | ||
| 25 | #include <string.h> | ||
| 26 | |||
| 27 | #include "avtextformat.h" | ||
| 28 | #include <libavutil/mem.h> | ||
| 29 | #include <libavutil/avassert.h> | ||
| 30 | #include <libavutil/bprint.h> | ||
| 31 | #include <libavutil/error.h> | ||
| 32 | #include <libavutil/macros.h> | ||
| 33 | #include <libavutil/opt.h> | ||
| 34 | |||
| 35 | #define writer_w8(wctx_, b_) (wctx_)->writer->writer->writer_w8((wctx_)->writer, b_) | ||
| 36 | #define writer_put_str(wctx_, str_) (wctx_)->writer->writer->writer_put_str((wctx_)->writer, str_) | ||
| 37 | #define writer_printf(wctx_, fmt_, ...) (wctx_)->writer->writer->writer_printf((wctx_)->writer, fmt_, __VA_ARGS__) | ||
| 38 | |||
| 39 | #define DEFINE_FORMATTER_CLASS(name) \ | ||
| 40 | static const char *name##_get_name(void *ctx) \ | ||
| 41 | { \ | ||
| 42 | return #name ; \ | ||
| 43 | } \ | ||
| 44 | static const AVClass name##_class = { \ | ||
| 45 | .class_name = #name, \ | ||
| 46 | .item_name = name##_get_name, \ | ||
| 47 | .option = name##_options \ | ||
| 48 | } | ||
| 49 | |||
| 50 | /* XML output */ | ||
| 51 | |||
| 52 | typedef struct XMLContext { | ||
| 53 | const AVClass *class; | ||
| 54 | int within_tag; | ||
| 55 | int indent_level; | ||
| 56 | int fully_qualified; | ||
| 57 | int xsd_strict; | ||
| 58 | } XMLContext; | ||
| 59 | |||
| 60 | #undef OFFSET | ||
| 61 | #define OFFSET(x) offsetof(XMLContext, x) | ||
| 62 | |||
| 63 | static const AVOption xml_options[] = { | ||
| 64 | {"fully_qualified", "specify if the output should be fully qualified", OFFSET(fully_qualified), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 }, | ||
| 65 | {"q", "specify if the output should be fully qualified", OFFSET(fully_qualified), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 }, | ||
| 66 | {"xsd_strict", "ensure that the output is XSD compliant", OFFSET(xsd_strict), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 }, | ||
| 67 | {"x", "ensure that the output is XSD compliant", OFFSET(xsd_strict), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 }, | ||
| 68 | {NULL}, | ||
| 69 | }; | ||
| 70 | |||
| 71 | DEFINE_FORMATTER_CLASS(xml); | ||
| 72 | |||
| 73 | static av_cold int xml_init(AVTextFormatContext *wctx) | ||
| 74 | { | ||
| 75 | XMLContext *xml = wctx->priv; | ||
| 76 | |||
| 77 | if (xml->xsd_strict) { | ||
| 78 | xml->fully_qualified = 1; | ||
| 79 | #define CHECK_COMPLIANCE(opt, opt_name) \ | ||
| 80 | if (opt) { \ | ||
| 81 | av_log(wctx, AV_LOG_ERROR, \ | ||
| 82 | "XSD-compliant output selected but option '%s' was selected, XML output may be non-compliant.\n" \ | ||
| 83 | "You need to disable such option with '-no%s'\n", opt_name, opt_name); \ | ||
| 84 | return AVERROR(EINVAL); \ | ||
| 85 | } | ||
| 86 | ////CHECK_COMPLIANCE(show_private_data, "private"); | ||
| 87 | CHECK_COMPLIANCE(wctx->show_value_unit, "unit"); | ||
| 88 | CHECK_COMPLIANCE(wctx->use_value_prefix, "prefix"); | ||
| 89 | } | ||
| 90 | |||
| 91 | return 0; | ||
| 92 | } | ||
| 93 | |||
| 94 | #define XML_INDENT() writer_printf(wctx, "%*c", xml->indent_level * 4, ' ') | ||
| 95 | |||
| 96 | static void xml_print_section_header(AVTextFormatContext *wctx, const void *data) | ||
| 97 | { | ||
| 98 | XMLContext *xml = wctx->priv; | ||
| 99 | const struct AVTextFormatSection *section = wctx->section[wctx->level]; | ||
| 100 | const struct AVTextFormatSection *parent_section = wctx->level ? | ||
| 101 | wctx->section[wctx->level-1] : NULL; | ||
| 102 | |||
| 103 | if (wctx->level == 0) { | ||
| 104 | const char *qual = " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " | ||
| 105 | "xmlns:ffprobe=\"http://www.ffmpeg.org/schema/ffprobe\" " | ||
| 106 | "xsi:schemaLocation=\"http://www.ffmpeg.org/schema/ffprobe ffprobe.xsd\""; | ||
| 107 | |||
| 108 | writer_put_str(wctx, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); | ||
| 109 | writer_printf(wctx, "<%sffprobe%s>\n", | ||
| 110 | xml->fully_qualified ? "ffprobe:" : "", | ||
| 111 | xml->fully_qualified ? qual : ""); | ||
| 112 | return; | ||
| 113 | } | ||
| 114 | |||
| 115 | if (xml->within_tag) { | ||
| 116 | xml->within_tag = 0; | ||
| 117 | writer_put_str(wctx, ">\n"); | ||
| 118 | } | ||
| 119 | |||
| 120 | if (parent_section && (parent_section->flags & AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER) && | ||
| 121 | wctx->level && wctx->nb_item[wctx->level-1]) | ||
| 122 | writer_w8(wctx, '\n'); | ||
| 123 | xml->indent_level++; | ||
| 124 | |||
| 125 | if (section->flags & (AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY|AV_TEXTFORMAT_SECTION_FLAG_HAS_VARIABLE_FIELDS)) { | ||
| 126 | XML_INDENT(); writer_printf(wctx, "<%s", section->name); | ||
| 127 | |||
| 128 | if (section->flags & AV_TEXTFORMAT_SECTION_FLAG_HAS_TYPE) { | ||
| 129 | AVBPrint buf; | ||
| 130 | av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED); | ||
| 131 | av_bprint_escape(&buf, section->get_type(data), NULL, | ||
| 132 | AV_ESCAPE_MODE_XML, AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES); | ||
| 133 | writer_printf(wctx, " type=\"%s\"", buf.str); | ||
| 134 | } | ||
| 135 | writer_printf(wctx, ">\n", section->name); | ||
| 136 | } else { | ||
| 137 | XML_INDENT(); writer_printf(wctx, "<%s ", section->name); | ||
| 138 | xml->within_tag = 1; | ||
| 139 | } | ||
| 140 | } | ||
| 141 | |||
| 142 | static void xml_print_section_footer(AVTextFormatContext *wctx) | ||
| 143 | { | ||
| 144 | XMLContext *xml = wctx->priv; | ||
| 145 | const struct AVTextFormatSection *section = wctx->section[wctx->level]; | ||
| 146 | |||
| 147 | if (wctx->level == 0) { | ||
| 148 | writer_printf(wctx, "</%sffprobe>\n", xml->fully_qualified ? "ffprobe:" : ""); | ||
| 149 | } else if (xml->within_tag) { | ||
| 150 | xml->within_tag = 0; | ||
| 151 | writer_put_str(wctx, "/>\n"); | ||
| 152 | xml->indent_level--; | ||
| 153 | } else { | ||
| 154 | XML_INDENT(); writer_printf(wctx, "</%s>\n", section->name); | ||
| 155 | xml->indent_level--; | ||
| 156 | } | ||
| 157 | } | ||
| 158 | |||
| 159 | static void xml_print_value(AVTextFormatContext *wctx, const char *key, | ||
| 160 | const char *str, int64_t num, const int is_int) | ||
| 161 | { | ||
| 162 | AVBPrint buf; | ||
| 163 | XMLContext *xml = wctx->priv; | ||
| 164 | const struct AVTextFormatSection *section = wctx->section[wctx->level]; | ||
| 165 | |||
| 166 | av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED); | ||
| 167 | |||
| 168 | if (section->flags & AV_TEXTFORMAT_SECTION_FLAG_HAS_VARIABLE_FIELDS) { | ||
| 169 | xml->indent_level++; | ||
| 170 | XML_INDENT(); | ||
| 171 | av_bprint_escape(&buf, key, NULL, | ||
| 172 | AV_ESCAPE_MODE_XML, AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES); | ||
| 173 | writer_printf(wctx, "<%s key=\"%s\"", | ||
| 174 | section->element_name, buf.str); | ||
| 175 | av_bprint_clear(&buf); | ||
| 176 | |||
| 177 | if (is_int) { | ||
| 178 | writer_printf(wctx, " value=\"%"PRId64"\"/>\n", num); | ||
| 179 | } else { | ||
| 180 | av_bprint_escape(&buf, str, NULL, | ||
| 181 | AV_ESCAPE_MODE_XML, AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES); | ||
| 182 | writer_printf(wctx, " value=\"%s\"/>\n", buf.str); | ||
| 183 | } | ||
| 184 | xml->indent_level--; | ||
| 185 | } else { | ||
| 186 | if (wctx->nb_item[wctx->level]) | ||
| 187 | writer_w8(wctx, ' '); | ||
| 188 | |||
| 189 | if (is_int) { | ||
| 190 | writer_printf(wctx, "%s=\"%"PRId64"\"", key, num); | ||
| 191 | } else { | ||
| 192 | av_bprint_escape(&buf, str, NULL, | ||
| 193 | AV_ESCAPE_MODE_XML, AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES); | ||
| 194 | writer_printf(wctx, "%s=\"%s\"", key, buf.str); | ||
| 195 | } | ||
| 196 | } | ||
| 197 | |||
| 198 | av_bprint_finalize(&buf, NULL); | ||
| 199 | } | ||
| 200 | |||
| 201 | static inline void xml_print_str(AVTextFormatContext *wctx, const char *key, const char *value) { | ||
| 202 | xml_print_value(wctx, key, value, 0, 0); | ||
| 203 | } | ||
| 204 | |||
| 205 | static void xml_print_int(AVTextFormatContext *wctx, const char *key, int64_t value) | ||
| 206 | { | ||
| 207 | xml_print_value(wctx, key, NULL, value, 1); | ||
| 208 | } | ||
| 209 | |||
| 210 | const AVTextFormatter avtextformatter_xml = { | ||
| 211 | .name = "xml", | ||
| 212 | .priv_size = sizeof(XMLContext), | ||
| 213 | .init = xml_init, | ||
| 214 | .print_section_header = xml_print_section_header, | ||
| 215 | .print_section_footer = xml_print_section_footer, | ||
| 216 | .print_integer = xml_print_int, | ||
| 217 | .print_string = xml_print_str, | ||
| 218 | .flags = AV_TEXTFORMAT_FLAG_SUPPORTS_MIXED_ARRAY_CONTENT, | ||
| 219 | .priv_class = &xml_class, | ||
| 220 | }; | ||
| 221 | |||
| @@ -0,0 +1,129 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) The FFmpeg developers | ||
| 3 | * | ||
| 4 | * This file is part of FFmpeg. | ||
| 5 | * | ||
| 6 | * FFmpeg is free software; you can redistribute it and/or | ||
| 7 | * modify it under the terms of the GNU Lesser General Public | ||
| 8 | * License as published by the Free Software Foundation; either | ||
| 9 | * version 2.1 of the License, or (at your option) any later version. | ||
| 10 | * | ||
| 11 | * FFmpeg is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 14 | * Lesser General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU Lesser General Public | ||
| 17 | * License along with FFmpeg; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 19 | */ | ||
| 20 | |||
| 21 | #include <limits.h> | ||
| 22 | #include <stdarg.h> | ||
| 23 | |||
| 24 | #include "avtextwriters.h" | ||
| 25 | #include "libavutil/opt.h" | ||
| 26 | |||
| 27 | /* AVIO Writer */ | ||
| 28 | |||
| 29 | # define WRITER_NAME "aviowriter" | ||
| 30 | |||
| 31 | typedef struct IOWriterContext { | ||
| 32 | const AVClass *class; | ||
| 33 | AVIOContext *avio_context; | ||
| 34 | int close_on_uninit; | ||
| 35 | } IOWriterContext; | ||
| 36 | |||
| 37 | static const char *iowriter_get_name(void *ctx) | ||
| 38 | { | ||
| 39 | return WRITER_NAME; | ||
| 40 | } | ||
| 41 | |||
| 42 | static const AVClass iowriter_class = { | ||
| 43 | .class_name = WRITER_NAME, | ||
| 44 | .item_name = iowriter_get_name, | ||
| 45 | }; | ||
| 46 | |||
| 47 | static av_cold void iowriter_uninit(AVTextWriterContext *wctx) | ||
| 48 | { | ||
| 49 | IOWriterContext *ctx = wctx->priv; | ||
| 50 | |||
| 51 | if (ctx->close_on_uninit && ctx->avio_context) { | ||
| 52 | avio_flush(ctx->avio_context); | ||
| 53 | avio_close(ctx->avio_context); | ||
| 54 | } | ||
| 55 | } | ||
| 56 | |||
| 57 | static void io_w8(AVTextWriterContext *wctx, int b) | ||
| 58 | { | ||
| 59 | IOWriterContext *ctx = wctx->priv; | ||
| 60 | avio_w8(ctx->avio_context, b); | ||
| 61 | } | ||
| 62 | |||
| 63 | static void io_put_str(AVTextWriterContext *wctx, const char *str) | ||
| 64 | { | ||
| 65 | IOWriterContext *ctx = wctx->priv; | ||
| 66 | avio_write(ctx->avio_context, str, strlen(str)); | ||
| 67 | } | ||
| 68 | |||
| 69 | static void io_printf(AVTextWriterContext *wctx, const char *fmt, ...) | ||
| 70 | { | ||
| 71 | IOWriterContext *ctx = wctx->priv; | ||
| 72 | va_list ap; | ||
| 73 | |||
| 74 | va_start(ap, fmt); | ||
| 75 | avio_vprintf(ctx->avio_context, fmt, ap); | ||
| 76 | va_end(ap); | ||
| 77 | } | ||
| 78 | |||
| 79 | |||
| 80 | const AVTextWriter avtextwriter_avio = { | ||
| 81 | .name = WRITER_NAME, | ||
| 82 | .priv_size = sizeof(IOWriterContext), | ||
| 83 | .uninit = iowriter_uninit, | ||
| 84 | .priv_class = &iowriter_class, | ||
| 85 | .writer_put_str = io_put_str, | ||
| 86 | .writer_printf = io_printf, | ||
| 87 | .writer_w8 = io_w8 | ||
| 88 | }; | ||
| 89 | |||
| 90 | int avtextwriter_create_file(AVTextWriterContext **pwctx, const char *output_filename, int close_on_uninit) | ||
| 91 | { | ||
| 92 | IOWriterContext *ctx; | ||
| 93 | int ret; | ||
| 94 | |||
| 95 | |||
| 96 | ret = avtextwriter_context_open(pwctx, &avtextwriter_avio); | ||
| 97 | if (ret < 0) | ||
| 98 | return ret; | ||
| 99 | |||
| 100 | ctx = (*pwctx)->priv; | ||
| 101 | |||
| 102 | if ((ret = avio_open(&ctx->avio_context, output_filename, AVIO_FLAG_WRITE)) < 0) { | ||
| 103 | av_log(ctx, AV_LOG_ERROR, | ||
| 104 | "Failed to open output '%s' with error: %s\n", output_filename, av_err2str(ret)); | ||
| 105 | avtextwriter_context_close(pwctx); | ||
| 106 | return ret; | ||
| 107 | } | ||
| 108 | |||
| 109 | ctx->close_on_uninit = close_on_uninit; | ||
| 110 | |||
| 111 | return ret; | ||
| 112 | } | ||
| 113 | |||
| 114 | |||
| 115 | int avtextwriter_create_avio(AVTextWriterContext **pwctx, AVIOContext *avio_ctx, int close_on_uninit) | ||
| 116 | { | ||
| 117 | IOWriterContext *ctx; | ||
| 118 | int ret; | ||
| 119 | |||
| 120 | ret = avtextwriter_context_open(pwctx, &avtextwriter_avio); | ||
| 121 | if (ret < 0) | ||
| 122 | return ret; | ||
| 123 | |||
| 124 | ctx = (*pwctx)->priv; | ||
| 125 | ctx->avio_context = avio_ctx; | ||
| 126 | ctx->close_on_uninit = close_on_uninit; | ||
| 127 | |||
| 128 | return ret; | ||
| 129 | } | ||
| @@ -0,0 +1,92 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) The FFmpeg developers | ||
| 3 | * | ||
| 4 | * This file is part of FFmpeg. | ||
| 5 | * | ||
| 6 | * FFmpeg is free software; you can redistribute it and/or | ||
| 7 | * modify it under the terms of the GNU Lesser General Public | ||
| 8 | * License as published by the Free Software Foundation; either | ||
| 9 | * version 2.1 of the License, or (at your option) any later version. | ||
| 10 | * | ||
| 11 | * FFmpeg is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 14 | * Lesser General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU Lesser General Public | ||
| 17 | * License along with FFmpeg; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 19 | */ | ||
| 20 | |||
| 21 | #include <limits.h> | ||
| 22 | #include <stdarg.h> | ||
| 23 | |||
| 24 | #include "avtextwriters.h" | ||
| 25 | #include "libavutil/opt.h" | ||
| 26 | #include "libavutil/bprint.h" | ||
| 27 | |||
| 28 | /* Buffer Writer */ | ||
| 29 | |||
| 30 | # define WRITER_NAME "bufferwriter" | ||
| 31 | |||
| 32 | typedef struct BufferWriterContext { | ||
| 33 | const AVClass *class; | ||
| 34 | AVBPrint *buffer; | ||
| 35 | } BufferWriterContext; | ||
| 36 | |||
| 37 | static const char *bufferwriter_get_name(void *ctx) | ||
| 38 | { | ||
| 39 | return WRITER_NAME; | ||
| 40 | } | ||
| 41 | |||
| 42 | static const AVClass bufferwriter_class = { | ||
| 43 | .class_name = WRITER_NAME, | ||
| 44 | .item_name = bufferwriter_get_name, | ||
| 45 | }; | ||
| 46 | |||
| 47 | static void buffer_w8(AVTextWriterContext *wctx, int b) | ||
| 48 | { | ||
| 49 | BufferWriterContext *ctx = wctx->priv; | ||
| 50 | av_bprintf(ctx->buffer, "%c", b); | ||
| 51 | } | ||
| 52 | |||
| 53 | static void buffer_put_str(AVTextWriterContext *wctx, const char *str) | ||
| 54 | { | ||
| 55 | BufferWriterContext *ctx = wctx->priv; | ||
| 56 | av_bprintf(ctx->buffer, "%s", str); | ||
| 57 | } | ||
| 58 | |||
| 59 | static void buffer_printf(AVTextWriterContext *wctx, const char *fmt, ...) | ||
| 60 | { | ||
| 61 | BufferWriterContext *ctx = wctx->priv; | ||
| 62 | |||
| 63 | va_list vargs; | ||
| 64 | va_start(vargs, fmt); | ||
| 65 | av_vbprintf(ctx->buffer, fmt, vargs); | ||
| 66 | va_end(vargs); | ||
| 67 | } | ||
| 68 | |||
| 69 | |||
| 70 | const AVTextWriter avtextwriter_buffer = { | ||
| 71 | .name = WRITER_NAME, | ||
| 72 | .priv_size = sizeof(BufferWriterContext), | ||
| 73 | .priv_class = &bufferwriter_class, | ||
| 74 | .writer_put_str = buffer_put_str, | ||
| 75 | .writer_printf = buffer_printf, | ||
| 76 | .writer_w8 = buffer_w8 | ||
| 77 | }; | ||
| 78 | |||
| 79 | int avtextwriter_create_buffer(AVTextWriterContext **pwctx, AVBPrint *buffer) | ||
| 80 | { | ||
| 81 | BufferWriterContext *ctx; | ||
| 82 | int ret; | ||
| 83 | |||
| 84 | ret = avtextwriter_context_open(pwctx, &avtextwriter_buffer); | ||
| 85 | if (ret < 0) | ||
| 86 | return ret; | ||
| 87 | |||
| 88 | ctx = (*pwctx)->priv; | ||
| 89 | ctx->buffer = buffer; | ||
| 90 | |||
| 91 | return ret; | ||
| 92 | } | ||
| @@ -0,0 +1,82 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) The FFmpeg developers | ||
| 3 | * | ||
| 4 | * This file is part of FFmpeg. | ||
| 5 | * | ||
| 6 | * FFmpeg is free software; you can redistribute it and/or | ||
| 7 | * modify it under the terms of the GNU Lesser General Public | ||
| 8 | * License as published by the Free Software Foundation; either | ||
| 9 | * version 2.1 of the License, or (at your option) any later version. | ||
| 10 | * | ||
| 11 | * FFmpeg is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 14 | * Lesser General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU Lesser General Public | ||
| 17 | * License along with FFmpeg; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 19 | */ | ||
| 20 | |||
| 21 | #include <stdarg.h> | ||
| 22 | #include <stdio.h> | ||
| 23 | #include <string.h> | ||
| 24 | |||
| 25 | #include "avtextwriters.h" | ||
| 26 | #include "libavutil/opt.h" | ||
| 27 | |||
| 28 | /* STDOUT Writer */ | ||
| 29 | |||
| 30 | # define WRITER_NAME "stdoutwriter" | ||
| 31 | |||
| 32 | typedef struct StdOutWriterContext { | ||
| 33 | const AVClass *class; | ||
| 34 | } StdOutWriterContext; | ||
| 35 | |||
| 36 | static const char *stdoutwriter_get_name(void *ctx) | ||
| 37 | { | ||
| 38 | return WRITER_NAME; | ||
| 39 | } | ||
| 40 | |||
| 41 | static const AVClass stdoutwriter_class = { | ||
| 42 | .class_name = WRITER_NAME, | ||
| 43 | .item_name = stdoutwriter_get_name, | ||
| 44 | }; | ||
| 45 | |||
| 46 | static inline void stdout_w8(AVTextWriterContext *wctx, int b) | ||
| 47 | { | ||
| 48 | printf("%c", b); | ||
| 49 | } | ||
| 50 | |||
| 51 | static inline void stdout_put_str(AVTextWriterContext *wctx, const char *str) | ||
| 52 | { | ||
| 53 | printf("%s", str); | ||
| 54 | } | ||
| 55 | |||
| 56 | static inline void stdout_printf(AVTextWriterContext *wctx, const char *fmt, ...) | ||
| 57 | { | ||
| 58 | va_list ap; | ||
| 59 | |||
| 60 | va_start(ap, fmt); | ||
| 61 | vprintf(fmt, ap); | ||
| 62 | va_end(ap); | ||
| 63 | } | ||
| 64 | |||
| 65 | |||
| 66 | static const AVTextWriter avtextwriter_stdout = { | ||
| 67 | .name = WRITER_NAME, | ||
| 68 | .priv_size = sizeof(StdOutWriterContext), | ||
| 69 | .priv_class = &stdoutwriter_class, | ||
| 70 | .writer_put_str = stdout_put_str, | ||
| 71 | .writer_printf = stdout_printf, | ||
| 72 | .writer_w8 = stdout_w8 | ||
| 73 | }; | ||
| 74 | |||
| 75 | int avtextwriter_create_stdout(AVTextWriterContext **pwctx) | ||
| 76 | { | ||
| 77 | int ret; | ||
| 78 | |||
| 79 | ret = avtextwriter_context_open(pwctx, &avtextwriter_stdout); | ||
| 80 | |||
| 81 | return ret; | ||
| 82 | } | ||