textutils.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. /*
  2. * This file is part of FFmpeg.
  3. *
  4. * FFmpeg is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU Lesser General Public
  6. * License as published by the Free Software Foundation; either
  7. * version 2.1 of the License, or (at your option) any later version.
  8. *
  9. * FFmpeg is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. * Lesser General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU Lesser General Public
  15. * License along with FFmpeg; if not, write to the Free Software
  16. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  17. */
  18. /**
  19. * @file
  20. * text expansion utilities
  21. */
  22. #include <fenv.h>
  23. #include <math.h>
  24. #include <string.h>
  25. #include "textutils.h"
  26. #include "libavutil/avutil.h"
  27. #include "libavutil/error.h"
  28. #include "libavutil/file.h"
  29. #include "libavutil/mem.h"
  30. #include "libavutil/time.h"
  31. #include "libavutil/time_internal.h"
  32. static int ff_expand_text_function_internal(FFExpandTextContext *expand_text, AVBPrint *bp,
  33. char *name, unsigned argc, char **argv)
  34. {
  35. void *log_ctx = expand_text->log_ctx;
  36. const FFExpandTextFunction *functions = expand_text->functions;
  37. unsigned i;
  38. for (i = 0; i < expand_text->functions_nb; i++) {
  39. if (strcmp(name, functions[i].name))
  40. continue;
  41. if (argc < functions[i].argc_min) {
  42. av_log(log_ctx, AV_LOG_ERROR, "%%{%s} requires at least %d arguments\n",
  43. name, functions[i].argc_min);
  44. return AVERROR(EINVAL);
  45. }
  46. if (argc > functions[i].argc_max) {
  47. av_log(log_ctx, AV_LOG_ERROR, "%%{%s} requires at most %d arguments\n",
  48. name, functions[i].argc_max);
  49. return AVERROR(EINVAL);
  50. }
  51. break;
  52. }
  53. if (i >= expand_text->functions_nb) {
  54. av_log(log_ctx, AV_LOG_ERROR, "%%{%s} is not known\n", name);
  55. return AVERROR(EINVAL);
  56. }
  57. return functions[i].func(log_ctx, bp, name, argc, argv);
  58. }
  59. /**
  60. * Expand text template pointed to by *rtext.
  61. *
  62. * Expand text template defined in text using the logic defined in a text
  63. * expander object.
  64. *
  65. * This function expects the text to be in the format %{FUNCTION_NAME[:PARAMS]},
  66. * where PARAMS is a sequence of strings separated by : and represents the function
  67. * arguments to use for the function evaluation.
  68. *
  69. * @param text_expander TextExpander object used to expand the text
  70. * @param bp BPrint object where the expanded text is written to
  71. * @param rtext pointer to pointer to the text to expand, it is updated to point
  72. * to the next part of the template to process
  73. * @return negative value corresponding to an AVERROR error code in case of
  74. * errors, a non-negative value otherwise
  75. */
  76. static int ff_expand_text_function(FFExpandTextContext *expand_text, AVBPrint *bp, const char **rtext)
  77. {
  78. void *log_ctx = expand_text->log_ctx;
  79. const char *text = *rtext;
  80. char *argv[16] = { NULL };
  81. unsigned argc = 0, i;
  82. int ret;
  83. if (*text != '{') {
  84. av_log(log_ctx, AV_LOG_ERROR, "Stray %% near '%s'\n", text);
  85. return AVERROR(EINVAL);
  86. }
  87. text++;
  88. while (1) {
  89. if (!(argv[argc++] = av_get_token(&text, ":}"))) {
  90. ret = AVERROR(ENOMEM);
  91. goto end;
  92. }
  93. if (!*text) {
  94. av_log(log_ctx, AV_LOG_ERROR, "Unterminated %%{} near '%s'\n", *rtext);
  95. ret = AVERROR(EINVAL);
  96. goto end;
  97. }
  98. if (argc == FF_ARRAY_ELEMS(argv))
  99. av_freep(&argv[--argc]); /* error will be caught later */
  100. if (*text == '}')
  101. break;
  102. text++;
  103. }
  104. if ((ret = ff_expand_text_function_internal(expand_text, bp, argv[0], argc - 1, argv + 1)) < 0)
  105. goto end;
  106. ret = 0;
  107. *rtext = text + 1;
  108. end:
  109. for (i = 0; i < argc; i++)
  110. av_freep(&argv[i]);
  111. return ret;
  112. }
  113. int ff_expand_text(FFExpandTextContext *expand_text, const char *text, AVBPrint *bp)
  114. {
  115. int ret;
  116. av_bprint_clear(bp);
  117. if (!text)
  118. return 0;
  119. while (*text) {
  120. if (*text == '\\' && text[1]) {
  121. av_bprint_chars(bp, text[1], 1);
  122. text += 2;
  123. } else if (*text == '%') {
  124. text++;
  125. if ((ret = ff_expand_text_function(expand_text, bp, &text)) < 0)
  126. return ret;
  127. } else {
  128. av_bprint_chars(bp, *text, 1);
  129. text++;
  130. }
  131. }
  132. if (!av_bprint_is_complete(bp))
  133. return AVERROR(ENOMEM);
  134. return 0;
  135. }
  136. int ff_print_pts(void *log_ctx, AVBPrint *bp, double pts, const char *delta,
  137. const char *fmt, const char *strftime_fmt)
  138. {
  139. int ret;
  140. if (delta) {
  141. int64_t delta_i;
  142. if ((ret = av_parse_time(&delta_i, delta, 1)) < 0) {
  143. av_log(log_ctx, AV_LOG_ERROR, "Invalid delta '%s'\n", delta);
  144. return ret;
  145. }
  146. pts += (double)delta_i / AV_TIME_BASE;
  147. }
  148. if (!strcmp(fmt, "flt")) {
  149. av_bprintf(bp, "%.6f", pts);
  150. } else if (!strcmp(fmt, "hms") ||
  151. !strcmp(fmt, "hms24hh")) {
  152. if (isnan(pts)) {
  153. av_bprintf(bp, " ??:??:??.???");
  154. } else {
  155. int64_t ms = llrint(pts * 1000);
  156. char sign = ' ';
  157. if (ms < 0) {
  158. sign = '-';
  159. ms = -ms;
  160. }
  161. if (!strcmp(fmt, "hms24hh")) {
  162. /* wrap around 24 hours */
  163. ms %= 24 * 60 * 60 * 1000;
  164. }
  165. av_bprintf(bp, "%c%02d:%02d:%02d.%03d", sign,
  166. (int)(ms / (60 * 60 * 1000)),
  167. (int)(ms / (60 * 1000)) % 60,
  168. (int)(ms / 1000) % 60,
  169. (int)(ms % 1000));
  170. }
  171. } else if (!strcmp(fmt, "localtime") ||
  172. !strcmp(fmt, "gmtime")) {
  173. struct tm tm;
  174. time_t ms = (time_t)pts;
  175. if (!strcmp(fmt, "localtime"))
  176. localtime_r(&ms, &tm);
  177. else
  178. gmtime_r(&ms, &tm);
  179. av_bprint_strftime(bp, av_x_if_null(strftime_fmt, "%Y-%m-%d %H:%M:%S"), &tm);
  180. } else {
  181. av_log(log_ctx, AV_LOG_ERROR, "Invalid format '%s'\n", fmt);
  182. return AVERROR(EINVAL);
  183. }
  184. return 0;
  185. }
  186. int ff_print_time(void *log_ctx, AVBPrint *bp,
  187. const char *strftime_fmt, char localtime)
  188. {
  189. const char *fmt = av_x_if_null(strftime_fmt, "%Y-%m-%d %H:%M:%S");
  190. const char *fmt_begin = fmt;
  191. int64_t unow;
  192. time_t now;
  193. struct tm tm;
  194. const char *begin;
  195. const char *tmp;
  196. int len;
  197. int div;
  198. AVBPrint fmt_bp;
  199. av_bprint_init(&fmt_bp, 0, AV_BPRINT_SIZE_UNLIMITED);
  200. unow = av_gettime();
  201. now = unow / 1000000;
  202. if (localtime)
  203. localtime_r(&now, &tm);
  204. else
  205. tm = *gmtime_r(&now, &tm);
  206. // manually parse format for %N (fractional seconds)
  207. begin = fmt;
  208. while ((begin = strchr(begin, '%'))) {
  209. tmp = begin + 1;
  210. len = 0;
  211. // skip escaped "%%"
  212. if (*tmp == '%') {
  213. begin = tmp + 1;
  214. continue;
  215. }
  216. // count digits between % and possible N
  217. while (*tmp != '\0' && av_isdigit((int)*tmp)) {
  218. len++;
  219. tmp++;
  220. }
  221. // N encountered, insert time
  222. if (*tmp == 'N') {
  223. int num_digits = 3; // default show millisecond [1,6]
  224. // if digit given, expect [1,6], warn & clamp otherwise
  225. if (len == 1) {
  226. num_digits = av_clip(*(begin + 1) - '0', 1, 6);
  227. } else if (len > 1) {
  228. av_log(log_ctx, AV_LOG_WARNING, "Invalid number of decimals for %%N, using default of %i\n", num_digits);
  229. }
  230. len += 2; // add % and N to get length of string part
  231. div = pow(10, 6 - num_digits);
  232. av_bprintf(&fmt_bp, "%.*s%0*d", (int)(begin - fmt_begin), fmt_begin, num_digits, (int)(unow % 1000000) / div);
  233. begin += len;
  234. fmt_begin = begin;
  235. continue;
  236. }
  237. begin = tmp;
  238. }
  239. av_bprintf(&fmt_bp, "%s", fmt_begin);
  240. if (!av_bprint_is_complete(&fmt_bp)) {
  241. av_log(log_ctx, AV_LOG_WARNING, "Format string truncated at %u/%u.", fmt_bp.size, fmt_bp.len);
  242. }
  243. av_bprint_strftime(bp, fmt_bp.str, &tm);
  244. av_bprint_finalize(&fmt_bp, NULL);
  245. return 0;
  246. }
  247. int ff_print_eval_expr(void *log_ctx, AVBPrint *bp,
  248. const char *expr,
  249. const char * const *fun_names, const ff_eval_func2 *fun_values,
  250. const char * const *var_names, const double *var_values,
  251. void *eval_ctx)
  252. {
  253. double res;
  254. int ret;
  255. ret = av_expr_parse_and_eval(&res, expr, var_names, var_values,
  256. NULL, NULL, fun_names, fun_values,
  257. eval_ctx, 0, log_ctx);
  258. if (ret < 0)
  259. av_log(log_ctx, AV_LOG_ERROR,
  260. "Text expansion expression '%s' is not valid\n",
  261. expr);
  262. else
  263. av_bprintf(bp, "%f", res);
  264. return ret;
  265. }
  266. int ff_print_formatted_eval_expr(void *log_ctx, AVBPrint *bp,
  267. const char *expr,
  268. const char * const *fun_names, const ff_eval_func2 *fun_values,
  269. const char * const *var_names, const double *var_values,
  270. void *eval_ctx,
  271. const char format, int positions)
  272. {
  273. double res;
  274. int intval;
  275. int ret;
  276. char fmt_str[30] = "%";
  277. ret = av_expr_parse_and_eval(&res, expr, var_names, var_values,
  278. NULL, NULL, fun_names, fun_values,
  279. eval_ctx, 0, log_ctx);
  280. if (ret < 0) {
  281. av_log(log_ctx, AV_LOG_ERROR,
  282. "Text expansion expression '%s' is not valid\n",
  283. expr);
  284. return ret;
  285. }
  286. if (!strchr("xXdu", format)) {
  287. av_log(log_ctx, AV_LOG_ERROR, "Invalid format '%c' specified,"
  288. " allowed values: 'x', 'X', 'd', 'u'\n", format);
  289. return AVERROR(EINVAL);
  290. }
  291. feclearexcept(FE_ALL_EXCEPT);
  292. intval = res;
  293. #if defined(FE_INVALID) && defined(FE_OVERFLOW) && defined(FE_UNDERFLOW)
  294. if ((ret = fetestexcept(FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW))) {
  295. av_log(log_ctx, AV_LOG_ERROR, "Conversion of floating-point result to int failed. Control register: 0x%08x. Conversion result: %d\n", ret, intval);
  296. return AVERROR(EINVAL);
  297. }
  298. #endif
  299. if (positions >= 0)
  300. av_strlcatf(fmt_str, sizeof(fmt_str), "0%u", positions);
  301. av_strlcatf(fmt_str, sizeof(fmt_str), "%c", format);
  302. av_log(log_ctx, AV_LOG_DEBUG, "Formatting value %f (expr '%s') with spec '%s'\n",
  303. res, expr, fmt_str);
  304. av_bprintf(bp, fmt_str, intval);
  305. return 0;
  306. }
  307. int ff_load_textfile(void *log_ctx, const char *textfile,
  308. unsigned char **text, size_t *text_size)
  309. {
  310. int err;
  311. uint8_t *textbuf;
  312. uint8_t *tmp;
  313. size_t textbuf_size;
  314. if ((err = av_file_map(textfile, &textbuf, &textbuf_size, 0, log_ctx)) < 0) {
  315. av_log(log_ctx, AV_LOG_ERROR,
  316. "The text file '%s' could not be read or is empty\n",
  317. textfile);
  318. return err;
  319. }
  320. if (textbuf_size > 0 && ff_is_newline(textbuf[textbuf_size - 1]))
  321. textbuf_size--;
  322. if (textbuf_size > SIZE_MAX - 1 || !(tmp = av_realloc(*text, textbuf_size + 1))) {
  323. av_file_unmap(textbuf, textbuf_size);
  324. return AVERROR(ENOMEM);
  325. }
  326. *text = tmp;
  327. memcpy(*text, textbuf, textbuf_size);
  328. (*text)[textbuf_size] = 0;
  329. if (text_size)
  330. *text_size = textbuf_size;
  331. av_file_unmap(textbuf, textbuf_size);
  332. return 0;
  333. }