FFmpeg
Loading...
Searching...
No Matches
textutils.c
Go to the documentation of this file.
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/**
20 * @file
21 * text expansion utilities
22 */
23
24#include <fenv.h>
25#include <math.h>
26#include <string.h>
27
28#include "textutils.h"
29#include "libavutil/avutil.h"
30#include "libavutil/error.h"
31#include "libavutil/file.h"
32#include "libavutil/mem.h"
33#include "libavutil/time.h"
35
36static int ff_expand_text_function_internal(FFExpandTextContext *expand_text, AVBPrint *bp,
37 char *name, unsigned argc, char **argv)
38{
39 void *log_ctx = expand_text->log_ctx;
40 const FFExpandTextFunction *functions = expand_text->functions;
41 unsigned i;
42
43 for (i = 0; i < expand_text->functions_nb; i++) {
44 if (strcmp(name, functions[i].name))
45 continue;
46 if (argc < functions[i].argc_min) {
47 av_log(log_ctx, AV_LOG_ERROR, "%%{%s} requires at least %d arguments\n",
48 name, functions[i].argc_min);
49 return AVERROR(EINVAL);
50 }
51 if (argc > functions[i].argc_max) {
52 av_log(log_ctx, AV_LOG_ERROR, "%%{%s} requires at most %d arguments\n",
53 name, functions[i].argc_max);
54 return AVERROR(EINVAL);
55 }
56 break;
57 }
58 if (i >= expand_text->functions_nb) {
59 av_log(log_ctx, AV_LOG_ERROR, "%%{%s} is not known\n", name);
60 return AVERROR(EINVAL);
61 }
62
63 return functions[i].func(log_ctx, bp, name, argc, argv);
64}
65
66/**
67 * Expand text template pointed to by *rtext.
68 *
69 * Expand text template defined in text using the logic defined in a text
70 * expander object.
71 *
72 * This function expects the text to be in the format %{FUNCTION_NAME[:PARAMS]},
73 * where PARAMS is a sequence of strings separated by : and represents the function
74 * arguments to use for the function evaluation.
75 *
76 * @param text_expander TextExpander object used to expand the text
77 * @param bp BPrint object where the expanded text is written to
78 * @param rtext pointer to pointer to the text to expand, it is updated to point
79 * to the next part of the template to process
80 * @return negative value corresponding to an AVERROR error code in case of
81 * errors, a non-negative value otherwise
82 */
83static int ff_expand_text_function(FFExpandTextContext *expand_text, AVBPrint *bp, const char **rtext)
84{
85 void *log_ctx = expand_text->log_ctx;
86 const char *text = *rtext;
87 char *argv[16] = { NULL };
88 unsigned argc = 0, i;
89 int ret;
90
91 if (*text != '{') {
92 av_log(log_ctx, AV_LOG_ERROR, "Stray %% near '%s'\n", text);
93 return AVERROR(EINVAL);
94 }
95 text++;
96 while (1) {
97 if (!(argv[argc++] = av_get_token(&text, ":}"))) {
98 ret = AVERROR(ENOMEM);
99 goto end;
100 }
101 if (!*text) {
102 av_log(log_ctx, AV_LOG_ERROR, "Unterminated %%{} near '%s'\n", *rtext);
103 ret = AVERROR(EINVAL);
104 goto end;
105 }
106 if (argc == FF_ARRAY_ELEMS(argv))
107 av_freep(&argv[--argc]); /* error will be caught later */
108 if (*text == '}')
109 break;
110 text++;
111 }
112
113 if ((ret = ff_expand_text_function_internal(expand_text, bp, argv[0], argc - 1, argv + 1)) < 0)
114 goto end;
115 ret = 0;
116 *rtext = text + 1;
117
118end:
119 for (i = 0; i < argc; i++)
120 av_freep(&argv[i]);
121 return ret;
122}
123
124int ff_expand_text(FFExpandTextContext *expand_text, const char *text, AVBPrint *bp)
125{
126 int ret;
127
128 av_bprint_clear(bp);
129 if (!text)
130 return 0;
131
132 while (*text) {
133 if (*text == '\\' && text[1]) {
134 av_bprint_chars(bp, text[1], 1);
135 text += 2;
136 } else if (*text == '%') {
137 text++;
138 if ((ret = ff_expand_text_function(expand_text, bp, &text)) < 0)
139 return ret;
140 } else {
141 av_bprint_chars(bp, *text, 1);
142 text++;
143 }
144 }
145 if (!av_bprint_is_complete(bp))
146 return AVERROR(ENOMEM);
147 return 0;
148}
149
150int ff_print_pts(void *log_ctx, AVBPrint *bp, double pts, const char *delta,
151 const char *fmt, const char *strftime_fmt)
152{
153 int ret;
154
155 if (delta) {
156 int64_t delta_i;
157 if ((ret = av_parse_time(&delta_i, delta, 1)) < 0) {
158 av_log(log_ctx, AV_LOG_ERROR, "Invalid delta '%s'\n", delta);
159 return ret;
160 }
161 pts += (double)delta_i / AV_TIME_BASE;
162 }
163
164 if (!strcmp(fmt, "flt")) {
165 av_bprintf(bp, "%.6f", pts);
166 } else if (!strcmp(fmt, "hms") ||
167 !strcmp(fmt, "hms24hh")) {
168 if (isnan(pts)) {
169 av_bprintf(bp, " ??:??:??.???");
170 } else {
171 int64_t ms = llrint(pts * 1000);
172 char sign = ' ';
173 if (ms < 0) {
174 sign = '-';
175 ms = -ms;
176 }
177 if (!strcmp(fmt, "hms24hh")) {
178 /* wrap around 24 hours */
179 ms %= 24 * 60 * 60 * 1000;
180 }
181 av_bprintf(bp, "%c%02d:%02d:%02d.%03d", sign,
182 (int)(ms / (60 * 60 * 1000)),
183 (int)(ms / (60 * 1000)) % 60,
184 (int)(ms / 1000) % 60,
185 (int)(ms % 1000));
186 }
187 } else if (!strcmp(fmt, "localtime") ||
188 !strcmp(fmt, "gmtime")) {
189 struct tm tm;
190 time_t ms = (time_t)pts;
191 if (!strcmp(fmt, "localtime"))
192 localtime_r(&ms, &tm);
193 else
194 gmtime_r(&ms, &tm);
195 av_bprint_strftime(bp, av_x_if_null(strftime_fmt, "%Y-%m-%d %H:%M:%S"), &tm);
196 } else {
197 av_log(log_ctx, AV_LOG_ERROR, "Invalid format '%s'\n", fmt);
198 return AVERROR(EINVAL);
199 }
200 return 0;
201}
202
203int ff_print_time(void *log_ctx, AVBPrint *bp,
204 const char *strftime_fmt, char localtime)
205{
206 const char *fmt = av_x_if_null(strftime_fmt, "%Y-%m-%d %H:%M:%S");
207 const char *fmt_begin = fmt;
208 int64_t unow;
209 time_t now;
210 struct tm tm;
211 const char *begin;
212 const char *tmp;
213 int len;
214 int div;
215 AVBPrint fmt_bp;
216
218
219 unow = av_gettime();
220 now = unow / 1000000;
221 if (localtime)
222 localtime_r(&now, &tm);
223 else
224 tm = *gmtime_r(&now, &tm);
225
226 // manually parse format for %N (fractional seconds)
227 begin = fmt;
228 while ((begin = strchr(begin, '%'))) {
229 tmp = begin + 1;
230 len = 0;
231
232 // skip escaped "%%"
233 if (*tmp == '%') {
234 begin = tmp + 1;
235 continue;
236 }
237
238 // count digits between % and possible N
239 while (*tmp != '\0' && av_isdigit((int)*tmp)) {
240 len++;
241 tmp++;
242 }
243
244 // N encountered, insert time
245 if (*tmp == 'N') {
246 int num_digits = 3; // default show millisecond [1,6]
247
248 // if digit given, expect [1,6], warn & clamp otherwise
249 if (len == 1) {
250 num_digits = av_clip(*(begin + 1) - '0', 1, 6);
251 } else if (len > 1) {
252 av_log(log_ctx, AV_LOG_WARNING, "Invalid number of decimals for %%N, using default of %i\n", num_digits);
253 }
254
255 len += 2; // add % and N to get length of string part
256
257 div = pow(10, 6 - num_digits);
258
259 av_bprintf(&fmt_bp, "%.*s%0*d", (int)(begin - fmt_begin), fmt_begin, num_digits, (int)(unow % 1000000) / div);
260
261 begin += len;
262 fmt_begin = begin;
263
264 continue;
265 }
266
267 begin = tmp;
268 }
269
270 av_bprintf(&fmt_bp, "%s", fmt_begin);
271 if (!av_bprint_is_complete(&fmt_bp)) {
272 av_log(log_ctx, AV_LOG_WARNING, "Format string truncated at %u/%u.", fmt_bp.size, fmt_bp.len);
273 }
274
275 av_bprint_strftime(bp, fmt_bp.str, &tm);
276
277 av_bprint_finalize(&fmt_bp, NULL);
278
279 return 0;
280}
281
282int ff_print_eval_expr(void *log_ctx, AVBPrint *bp,
283 const char *expr,
284 const char * const *fun_names, const ff_eval_func2 *fun_values,
285 const char * const *var_names, const double *var_values,
286 void *eval_ctx)
287{
288 double res;
289 int ret;
290
291 ret = av_expr_parse_and_eval(&res, expr, var_names, var_values,
292 NULL, NULL, fun_names, fun_values,
293 eval_ctx, 0, log_ctx);
294 if (ret < 0)
295 av_log(log_ctx, AV_LOG_ERROR,
296 "Text expansion expression '%s' is not valid\n",
297 expr);
298 else
299 av_bprintf(bp, "%f", res);
300
301 return ret;
302}
303
304int ff_print_formatted_eval_expr(void *log_ctx, AVBPrint *bp,
305 const char *expr,
306 const char * const *fun_names, const ff_eval_func2 *fun_values,
307 const char * const *var_names, const double *var_values,
308 void *eval_ctx,
309 const char format, int positions)
310{
311 double res;
312 int intval;
313 int ret;
314 char fmt_str[30] = "%";
315
316 ret = av_expr_parse_and_eval(&res, expr, var_names, var_values,
317 NULL, NULL, fun_names, fun_values,
318 eval_ctx, 0, log_ctx);
319 if (ret < 0) {
320 av_log(log_ctx, AV_LOG_ERROR,
321 "Text expansion expression '%s' is not valid\n",
322 expr);
323 return ret;
324 }
325
326 if (!strchr("xXdu", format)) {
327 av_log(log_ctx, AV_LOG_ERROR, "Invalid format '%c' specified,"
328 " allowed values: 'x', 'X', 'd', 'u'\n", format);
329 return AVERROR(EINVAL);
330 }
331
332 feclearexcept(FE_ALL_EXCEPT);
333 intval = res;
334#if defined(FE_INVALID) && defined(FE_OVERFLOW) && defined(FE_UNDERFLOW)
335 if ((ret = fetestexcept(FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW))) {
336 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);
337 return AVERROR(EINVAL);
338 }
339#endif
340
341 if (positions >= 0)
342 av_strlcatf(fmt_str, sizeof(fmt_str), "0%u", positions);
343 av_strlcatf(fmt_str, sizeof(fmt_str), "%c", format);
344
345 av_log(log_ctx, AV_LOG_DEBUG, "Formatting value %f (expr '%s') with spec '%s'\n",
346 res, expr, fmt_str);
347
348 av_bprintf(bp, fmt_str, intval);
349
350 return 0;
351}
352
353
354int ff_load_textfile(void *log_ctx, const char *textfile,
355 unsigned char **text, size_t *text_size)
356{
357 int err;
358 uint8_t *textbuf;
359 uint8_t *tmp;
360 size_t textbuf_size;
361
362 if ((err = av_file_map(textfile, &textbuf, &textbuf_size, 0, log_ctx)) < 0) {
363 av_log(log_ctx, AV_LOG_ERROR,
364 "The text file '%s' could not be read or is empty\n",
365 textfile);
366 return err;
367 }
368
369 if (textbuf_size > 0 && ff_is_newline(textbuf[textbuf_size - 1]))
370 textbuf_size--;
371 if (textbuf_size > SIZE_MAX - 1 || !(tmp = av_realloc(*text, textbuf_size + 1))) {
372 av_file_unmap(textbuf, textbuf_size);
373 return AVERROR(ENOMEM);
374 }
375 *text = tmp;
376 memcpy(*text, textbuf, textbuf_size);
377 (*text)[textbuf_size] = 0;
378 if (text_size)
379 *text_size = textbuf_size;
380 av_file_unmap(textbuf, textbuf_size);
381
382 return 0;
383}
static const char *const format[]
Definition af_aiir.c:444
size_t av_strlcatf(char *dst, size_t size, const char *fmt,...)
Definition avstring.c:103
Convenience header that includes libavutil's core.
void av_bprintf(AVBPrint *buf, const char *fmt,...)
Definition bprint.c:122
void av_bprint_init(AVBPrint *buf, unsigned size_init, unsigned size_max)
Definition bprint.c:69
#define AV_BPRINT_SIZE_UNLIMITED
#define i(width, name, range_min, range_max)
Definition cbs_h264.c:63
#define av_clip
Definition common.h:100
#define NULL
Definition coverity.c:32
long long int64_t
Definition coverity.c:34
error code definitions
int av_expr_parse_and_eval(double *d, const char *s, const char *const *const_names, const double *const_values, const char *const *func1_names, double(*const *funcs1)(void *, double), const char *const *func2_names, double(*const *funcs2)(void *, double, double), void *opaque, int log_offset, void *log_ctx)
Parse and evaluate an expression.
Definition eval.c:839
Misc file utilities.
static int av_bprint_is_complete(const AVBPrint *buf)
Test if the print buffer is complete (not truncated).
Definition bprint.h:218
int av_bprint_finalize(AVBPrint *buf, char **ret_str)
Finalize a print buffer.
Definition bprint.c:235
void av_bprint_strftime(AVBPrint *buf, const char *fmt, const struct tm *tm)
Append a formatted date and time to a print buffer.
Definition bprint.c:166
void av_bprint_chars(AVBPrint *buf, char c, unsigned n)
Append char c n times to a print buffer.
Definition bprint.c:130
void av_bprint_clear(AVBPrint *buf)
Reset the string to "" but keep internal allocated data.
Definition bprint.c:227
#define AVERROR(e)
Definition error.h:45
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
Definition log.h:231
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition log.h:216
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition log.h:210
static void * av_x_if_null(const void *p, const void *x)
Return x default pointer in case p is NULL.
Definition avutil.h:311
static av_const int av_isdigit(int c)
Locale-independent conversion of ASCII isdigit.
Definition avstring.h:202
char * av_get_token(const char **buf, const char *term)
Unescape the given string until a non escaped terminating char, and return the token corresponding to...
Definition avstring.c:143
#define AV_TIME_BASE
Internal time base represented as integer.
Definition avutil.h:253
void av_file_unmap(uint8_t *bufptr, size_t size)
Unmap or free the buffer bufptr created by av_file_map().
Definition file.c:142
int av_file_map(const char *filename, uint8_t **bufptr, size_t *size, int log_offset, void *log_ctx)
Read the file with name filename, and put its content in a newly allocated buffer or map it with mmap...
Definition file.c:55
#define llrint(x)
Definition libm.h:396
#define isnan(x)
Definition libm.h:342
Memory handling functions.
static const char *const var_names[]
Definition noise.c:30
#define av_realloc(p, s)
Definition ops_static.c:54
int av_parse_time(int64_t *timeval, const char *timestr, int duration)
Parse timestr and return in *time a corresponding number of microseconds.
Definition parseutils.c:592
const char * name
Definition qsvenc.c:142
#define FF_ARRAY_ELEMS(a)
Text expander context, used to encapsulate the logic to expand a given text template.
Definition textutils.h:66
unsigned int functions_nb
number of functions
Definition textutils.h:82
void * log_ctx
log context to pass to the function, used for logging and for accessing the context for the function
Definition textutils.h:71
const FFExpandTextFunction * functions
list of functions to use to expand sequences in the format FUNCTION_NAME{PARAMS}
Definition textutils.h:77
Function used to expand a template sequence in the format %{FUNCTION_NAME[:PARAMS]}...
Definition textutils.h:36
int(* func)(void *ctx, AVBPrint *bp, const char *function_name, unsigned argc, char **args)
actual function used to perform the expansion
Definition textutils.h:51
#define av_freep(p)
#define av_log(a,...)
static uint8_t tmp[40]
Definition aes_ctr.c:52
int ff_print_pts(void *log_ctx, AVBPrint *bp, double pts, const char *delta, const char *fmt, const char *strftime_fmt)
Definition textutils.c:150
int ff_load_textfile(void *log_ctx, const char *textfile, unsigned char **text, size_t *text_size)
Definition textutils.c:354
int ff_expand_text(FFExpandTextContext *expand_text, const char *text, AVBPrint *bp)
Expand text template.
Definition textutils.c:124
int ff_print_time(void *log_ctx, AVBPrint *bp, const char *strftime_fmt, char localtime)
Definition textutils.c:203
static int ff_expand_text_function(FFExpandTextContext *expand_text, AVBPrint *bp, const char **rtext)
Expand text template pointed to by *rtext.
Definition textutils.c:83
int ff_print_formatted_eval_expr(void *log_ctx, AVBPrint *bp, const char *expr, const char *const *fun_names, const ff_eval_func2 *fun_values, const char *const *var_names, const double *var_values, void *eval_ctx, const char format, int positions)
Definition textutils.c:304
int ff_print_eval_expr(void *log_ctx, AVBPrint *bp, const char *expr, const char *const *fun_names, const ff_eval_func2 *fun_values, const char *const *var_names, const double *var_values, void *eval_ctx)
Definition textutils.c:282
static int ff_expand_text_function_internal(FFExpandTextContext *expand_text, AVBPrint *bp, char *name, unsigned argc, char **argv)
Definition textutils.c:36
text utilities
int64_t av_gettime(void)
Get the current time in microseconds.
Definition time.c:40
#define gmtime_r
#define localtime_r
static int64_t pts
static const uint16_t positions[][14][3]
float delta
int len