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
33
35 char *
name,
unsigned argc,
char **argv)
36 {
37 void *log_ctx = expand_text->
log_ctx;
40
43 continue;
44 if (argc < functions[
i].argc_min) {
46 name, functions[
i].argc_min);
48 }
49 if (argc > functions[
i].argc_max) {
51 name, functions[
i].argc_max);
53 }
54 break;
55 }
59 }
60
61 return functions[
i].
func(log_ctx, bp,
name, argc, argv);
62 }
63
64 /**
65 * Expand text template pointed to by *rtext.
66 *
67 * Expand text template defined in text using the logic defined in a text
68 * expander object.
69 *
70 * This function expects the text to be in the format %{FUNCTION_NAME[:PARAMS]},
71 * where PARAMS is a sequence of strings separated by : and represents the function
72 * arguments to use for the function evaluation.
73 *
74 * @param text_expander TextExpander object used to expand the text
75 * @param bp BPrint object where the expanded text is written to
76 * @param rtext pointer to pointer to the text to expand, it is updated to point
77 * to the next part of the template to process
78 * @return negative value corresponding to an AVERROR error code in case of
79 * errors, a non-negative value otherwise
80 */
82 {
83 void *log_ctx = expand_text->
log_ctx;
84 const char *text = *rtext;
85 char *argv[16] = {
NULL };
88
89 if (*text != '{') {
92 }
93 text++;
94 while (1) {
97 goto end;
98 }
99 if (!*text) {
102 goto end;
103 }
105 av_freep(&argv[--argc]);
/* error will be caught later */
106 if (*text == '}')
107 break;
108 text++;
109 }
110
112 goto end;
114 *rtext = (char *)text + 1;
115
116 end:
117 for (
i = 0;
i < argc;
i++)
120 }
121
123 {
125
127 if (!text)
128 return 0;
129
130 while (*text) {
131 if (*text == '\\' && text[1]) {
133 text += 2;
134 } else if (*text == '%') {
135 text++;
138 } else {
140 text++;
141 }
142 }
145 return 0;
146 }
147
149 const char *fmt, const char *strftime_fmt)
150 {
152
158 }
160 }
161
162 if (!strcmp(fmt, "flt")) {
164 } else if (!strcmp(fmt, "hms") ||
165 !strcmp(fmt, "hms24hh")) {
168 } else {
170 char sign = ' ';
171 if (ms < 0) {
172 sign = '-';
173 ms = -ms;
174 }
175 if (!strcmp(fmt, "hms24hh")) {
176 /* wrap around 24 hours */
177 ms %= 24 * 60 * 60 * 1000;
178 }
180 (int)(ms / (60 * 60 * 1000)),
181 (int)(ms / (60 * 1000)) % 60,
182 (int)(ms / 1000) % 60,
183 (int)(ms % 1000));
184 }
185 } else if (!strcmp(fmt, "localtime") ||
186 !strcmp(fmt, "gmtime")) {
187 struct tm tm;
188 time_t ms = (time_t)
pts;
189 if (!strcmp(fmt, "localtime"))
191 else
194 } else {
197 }
198 return 0;
199 }
200
202 const char *strftime_fmt, char localtime)
203 {
204 const char *fmt =
av_x_if_null(strftime_fmt,
"%Y-%m-%d %H:%M:%S");
205 const char *fmt_begin = fmt;
207 time_t now;
208 struct tm tm;
209 const char *begin;
212 int div;
213 AVBPrint fmt_bp;
214
216
218 now = unow / 1000000;
219 if (localtime)
221 else
223
224 // manually parse format for %N (fractional seconds)
225 begin = fmt;
226 while ((begin = strchr(begin, '%'))) {
229
230 // skip escaped "%%"
233 continue;
234 }
235
236 // count digits between % and possible N
240 }
241
242 // N encountered, insert time
244 int num_digits = 3; // default show millisecond [1,6]
245
246 // if digit given, expect [1,6], warn & clamp otherwise
248 num_digits =
av_clip(*(begin + 1) -
'0', 1, 6);
249 }
else if (
len > 1) {
250 av_log(log_ctx,
AV_LOG_WARNING,
"Invalid number of decimals for %%N, using default of %i\n", num_digits);
251 }
252
253 len += 2;
// add % and N to get length of string part
254
255 div = pow(10, 6 - num_digits);
256
257 av_bprintf(&fmt_bp,
"%.*s%0*d", (
int)(begin - fmt_begin), fmt_begin, num_digits, (
int)(unow % 1000000) / div);
258
260 fmt_begin = begin;
261
262 continue;
263 }
264
266 }
267
271 }
272
274
276
277 return 0;
278 }
279
281 const char *expr,
282 const char * const *fun_names, const ff_eval_func2 *fun_values,
283 const char *
const *
var_names,
const double *var_values,
284 void *eval_ctx)
285 {
286 double res;
288
291 eval_ctx, 0, log_ctx);
294 "Text expansion expression '%s' is not valid\n",
295 expr);
296 else
298
300 }
301
303 const char *expr,
304 const char * const *fun_names, const ff_eval_func2 *fun_values,
305 const char *
const *
var_names,
const double *var_values,
306 void *eval_ctx,
308 {
309 double res;
310 int intval;
312 char fmt_str[30] = "%";
313
316 eval_ctx, 0, log_ctx);
319 "Text expansion expression '%s' is not valid\n",
320 expr);
322 }
323
324 if (!strchr(
"xXdu",
format)) {
326 " allowed values: 'x', 'X', 'd', 'u'\n",
format);
328 }
329
330 feclearexcept(FE_ALL_EXCEPT);
331 intval = res;
332 #if defined(FE_INVALID) && defined(FE_OVERFLOW) && defined(FE_UNDERFLOW)
333 if ((
ret = fetestexcept(FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW))) {
334 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);
336 }
337 #endif
338
342
344 res, expr, fmt_str);
345
347
348 return 0;
349 }
350
351
353 unsigned char **text, size_t *text_size)
354 {
355 int err;
356 uint8_t *textbuf;
358 size_t textbuf_size;
359
360 if ((err =
av_file_map(textfile, &textbuf, &textbuf_size, 0, log_ctx)) < 0) {
362 "The text file '%s' could not be read or is empty\n",
363 textfile);
364 return err;
365 }
366
367 if (textbuf_size > 0 && ff_is_newline(textbuf[textbuf_size - 1]))
368 textbuf_size--;
369 if (textbuf_size > SIZE_MAX - 1 || !(
tmp =
av_realloc(*text, textbuf_size + 1))) {
372 }
374 memcpy(*text, textbuf, textbuf_size);
375 (*text)[textbuf_size] = 0;
376 if (text_size)
377 *text_size = textbuf_size;
379
380 return 0;
381 }
382