FFmpeg
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"
34 
35 static int ff_expand_text_function_internal(FFExpandTextContext *expand_text, AVBPrint *bp,
36  char *name, unsigned argc, char **argv)
37 {
38  void *log_ctx = expand_text->log_ctx;
39  FFExpandTextFunction *functions = expand_text->functions;
40  unsigned i;
41 
42  for (i = 0; i < expand_text->functions_nb; i++) {
43  if (strcmp(name, functions[i].name))
44  continue;
45  if (argc < functions[i].argc_min) {
46  av_log(log_ctx, AV_LOG_ERROR, "%%{%s} requires at least %d arguments\n",
47  name, functions[i].argc_min);
48  return AVERROR(EINVAL);
49  }
50  if (argc > functions[i].argc_max) {
51  av_log(log_ctx, AV_LOG_ERROR, "%%{%s} requires at most %d arguments\n",
52  name, functions[i].argc_max);
53  return AVERROR(EINVAL);
54  }
55  break;
56  }
57  if (i >= expand_text->functions_nb) {
58  av_log(log_ctx, AV_LOG_ERROR, "%%{%s} is not known\n", name);
59  return AVERROR(EINVAL);
60  }
61 
62  return functions[i].func(log_ctx, bp, name, argc, argv);
63 }
64 
65 /**
66  * Expand text template pointed to by *rtext.
67  *
68  * Expand text template defined in text using the logic defined in a text
69  * expander object.
70  *
71  * This function expects the text to be in the format %{FUNCTION_NAME[:PARAMS]},
72  * where PARAMS is a sequence of strings separated by : and represents the function
73  * arguments to use for the function evaluation.
74  *
75  * @param text_expander TextExpander object used to expand the text
76  * @param bp BPrint object where the expanded text is written to
77  * @param rtext pointer to pointer to the text to expand, it is updated to point
78  * to the next part of the template to process
79  * @return negative value corresponding to an AVERROR error code in case of
80  * errors, a non-negative value otherwise
81  */
82 static int ff_expand_text_function(FFExpandTextContext *expand_text, AVBPrint *bp, char **rtext)
83 {
84  void *log_ctx = expand_text->log_ctx;
85  const char *text = *rtext;
86  char *argv[16] = { NULL };
87  unsigned argc = 0, i;
88  int ret;
89 
90  if (*text != '{') {
91  av_log(log_ctx, AV_LOG_ERROR, "Stray %% near '%s'\n", text);
92  return AVERROR(EINVAL);
93  }
94  text++;
95  while (1) {
96  if (!(argv[argc++] = av_get_token(&text, ":}"))) {
97  ret = AVERROR(ENOMEM);
98  goto end;
99  }
100  if (!*text) {
101  av_log(log_ctx, AV_LOG_ERROR, "Unterminated %%{} near '%s'\n", *rtext);
102  ret = AVERROR(EINVAL);
103  goto end;
104  }
105  if (argc == FF_ARRAY_ELEMS(argv))
106  av_freep(&argv[--argc]); /* error will be caught later */
107  if (*text == '}')
108  break;
109  text++;
110  }
111 
112  if ((ret = ff_expand_text_function_internal(expand_text, bp, argv[0], argc - 1, argv + 1)) < 0)
113  goto end;
114  ret = 0;
115  *rtext = (char *)text + 1;
116 
117 end:
118  for (i = 0; i < argc; i++)
119  av_freep(&argv[i]);
120  return ret;
121 }
122 
123 int ff_expand_text(FFExpandTextContext *expand_text, char *text, AVBPrint *bp)
124 {
125  int ret;
126 
127  av_bprint_clear(bp);
128  if (!text)
129  return 0;
130 
131  while (*text) {
132  if (*text == '\\' && text[1]) {
133  av_bprint_chars(bp, text[1], 1);
134  text += 2;
135  } else if (*text == '%') {
136  text++;
137  if ((ret = ff_expand_text_function(expand_text, bp, &text)) < 0)
138  return ret;
139  } else {
140  av_bprint_chars(bp, *text, 1);
141  text++;
142  }
143  }
144  if (!av_bprint_is_complete(bp))
145  return AVERROR(ENOMEM);
146  return 0;
147 }
148 
149 int ff_print_pts(void *log_ctx, AVBPrint *bp, double pts, const char *delta,
150  const char *fmt, const char *strftime_fmt)
151 {
152  int ret;
153 
154  if (delta) {
155  int64_t delta_i;
156  if ((ret = av_parse_time(&delta_i, delta, 1)) < 0) {
157  av_log(log_ctx, AV_LOG_ERROR, "Invalid delta '%s'\n", delta);
158  return ret;
159  }
160  pts += (double)delta_i / AV_TIME_BASE;
161  }
162 
163  if (!strcmp(fmt, "flt")) {
164  av_bprintf(bp, "%.6f", pts);
165  } else if (!strcmp(fmt, "hms") ||
166  !strcmp(fmt, "hms24hh")) {
167  if (isnan(pts)) {
168  av_bprintf(bp, " ??:??:??.???");
169  } else {
170  int64_t ms = llrint(pts * 1000);
171  char sign = ' ';
172  if (ms < 0) {
173  sign = '-';
174  ms = -ms;
175  }
176  if (!strcmp(fmt, "hms24hh")) {
177  /* wrap around 24 hours */
178  ms %= 24 * 60 * 60 * 1000;
179  }
180  av_bprintf(bp, "%c%02d:%02d:%02d.%03d", sign,
181  (int)(ms / (60 * 60 * 1000)),
182  (int)(ms / (60 * 1000)) % 60,
183  (int)(ms / 1000) % 60,
184  (int)(ms % 1000));
185  }
186  } else if (!strcmp(fmt, "localtime") ||
187  !strcmp(fmt, "gmtime")) {
188  struct tm tm;
189  time_t ms = (time_t)pts;
190  if (!strcmp(fmt, "localtime"))
191  localtime_r(&ms, &tm);
192  else
193  gmtime_r(&ms, &tm);
194  av_bprint_strftime(bp, av_x_if_null(strftime_fmt, "%Y-%m-%d %H:%M:%S"), &tm);
195  } else {
196  av_log(log_ctx, AV_LOG_ERROR, "Invalid format '%s'\n", fmt);
197  return AVERROR(EINVAL);
198  }
199  return 0;
200 }
201 
202 int ff_print_time(void *log_ctx, AVBPrint *bp,
203  const char *strftime_fmt, char localtime)
204 {
205  const char *fmt = av_x_if_null(strftime_fmt, "%Y-%m-%d %H:%M:%S");
206  const char *fmt_begin = fmt;
207  int64_t unow;
208  time_t now;
209  struct tm tm;
210  const char *begin;
211  const char *tmp;
212  int len;
213  int div;
214  AVBPrint fmt_bp;
215 
217 
218  unow = av_gettime();
219  now = unow / 1000000;
220  if (localtime)
221  localtime_r(&now, &tm);
222  else
223  tm = *gmtime_r(&now, &tm);
224 
225  // manually parse format for %N (fractional seconds)
226  begin = fmt;
227  while ((begin = strchr(begin, '%'))) {
228  tmp = begin + 1;
229  len = 0;
230 
231  // skip escaped "%%"
232  if (*tmp == '%') {
233  begin = tmp + 1;
234  continue;
235  }
236 
237  // count digits between % and possible N
238  while (*tmp != '\0' && av_isdigit((int)*tmp)) {
239  len++;
240  tmp++;
241  }
242 
243  // N encountered, insert time
244  if (*tmp == 'N') {
245  int num_digits = 3; // default show millisecond [1,6]
246 
247  // if digit given, expect [1,6], warn & clamp otherwise
248  if (len == 1) {
249  num_digits = av_clip(*(begin + 1) - '0', 1, 6);
250  } else if (len > 1) {
251  av_log(log_ctx, AV_LOG_WARNING, "Invalid number of decimals for %%N, using default of %i\n", num_digits);
252  }
253 
254  len += 2; // add % and N to get length of string part
255 
256  div = pow(10, 6 - num_digits);
257 
258  av_bprintf(&fmt_bp, "%.*s%0*d", (int)(begin - fmt_begin), fmt_begin, num_digits, (int)(unow % 1000000) / div);
259 
260  begin += len;
261  fmt_begin = begin;
262 
263  continue;
264  }
265 
266  begin = tmp;
267  }
268 
269  av_bprintf(&fmt_bp, "%s", fmt_begin);
270  if (!av_bprint_is_complete(&fmt_bp)) {
271  av_log(log_ctx, AV_LOG_WARNING, "Format string truncated at %u/%u.", fmt_bp.size, fmt_bp.len);
272  }
273 
274  av_bprint_strftime(bp, fmt_bp.str, &tm);
275 
276  av_bprint_finalize(&fmt_bp, NULL);
277 
278  return 0;
279 }
280 
281 int ff_print_eval_expr(void *log_ctx, AVBPrint *bp,
282  const char *expr,
283  const char * const *fun_names, const ff_eval_func2 *fun_values,
284  const char * const *var_names, const double *var_values,
285  void *eval_ctx)
286 {
287  double res;
288  int ret;
289 
290  ret = av_expr_parse_and_eval(&res, expr, var_names, var_values,
291  NULL, NULL, fun_names, fun_values,
292  eval_ctx, 0, log_ctx);
293  if (ret < 0)
294  av_log(log_ctx, AV_LOG_ERROR,
295  "Text expansion expression '%s' is not valid\n",
296  expr);
297  else
298  av_bprintf(bp, "%f", res);
299 
300  return ret;
301 }
302 
303 int ff_print_formatted_eval_expr(void *log_ctx, AVBPrint *bp,
304  const char *expr,
305  const char * const *fun_names, const ff_eval_func2 *fun_values,
306  const char * const *var_names, const double *var_values,
307  void *eval_ctx,
308  const char format, int positions)
309 {
310  double res;
311  int intval;
312  int ret;
313  char fmt_str[30] = "%";
314 
315  ret = av_expr_parse_and_eval(&res, expr, var_names, var_values,
316  NULL, NULL, fun_names, fun_values,
317  eval_ctx, 0, log_ctx);
318  if (ret < 0) {
319  av_log(log_ctx, AV_LOG_ERROR,
320  "Text expansion expression '%s' is not valid\n",
321  expr);
322  return ret;
323  }
324 
325  if (!strchr("xXdu", format)) {
326  av_log(log_ctx, AV_LOG_ERROR, "Invalid format '%c' specified,"
327  " allowed values: 'x', 'X', 'd', 'u'\n", format);
328  return AVERROR(EINVAL);
329  }
330 
331  feclearexcept(FE_ALL_EXCEPT);
332  intval = res;
333 #if defined(FE_INVALID) && defined(FE_OVERFLOW) && defined(FE_UNDERFLOW)
334  if ((ret = fetestexcept(FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW))) {
335  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  return AVERROR(EINVAL);
337  }
338 #endif
339 
340  if (positions >= 0)
341  av_strlcatf(fmt_str, sizeof(fmt_str), "0%u", positions);
342  av_strlcatf(fmt_str, sizeof(fmt_str), "%c", format);
343 
344  av_log(log_ctx, AV_LOG_DEBUG, "Formatting value %f (expr '%s') with spec '%s'\n",
345  res, expr, fmt_str);
346 
347  av_bprintf(bp, fmt_str, intval);
348 
349  return 0;
350 }
351 
352 
353 int ff_load_textfile(void *log_ctx, const char *textfile,
354  unsigned char **text, size_t *text_size)
355 {
356  int err;
357  uint8_t *textbuf;
358  uint8_t *tmp;
359  size_t textbuf_size;
360 
361  if ((err = av_file_map(textfile, &textbuf, &textbuf_size, 0, log_ctx)) < 0) {
362  av_log(log_ctx, AV_LOG_ERROR,
363  "The text file '%s' could not be read or is empty\n",
364  textfile);
365  return err;
366  }
367 
368  if (textbuf_size > 0 && ff_is_newline(textbuf[textbuf_size - 1]))
369  textbuf_size--;
370  if (textbuf_size > SIZE_MAX - 1 || !(tmp = av_realloc(*text, textbuf_size + 1))) {
371  av_file_unmap(textbuf, textbuf_size);
372  return AVERROR(ENOMEM);
373  }
374  *text = tmp;
375  memcpy(*text, textbuf, textbuf_size);
376  (*text)[textbuf_size] = 0;
377  if (text_size)
378  *text_size = textbuf_size;
379  av_file_unmap(textbuf, textbuf_size);
380 
381  return 0;
382 }
383 
AV_LOG_WARNING
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition: log.h:186
AV_BPRINT_SIZE_UNLIMITED
#define AV_BPRINT_SIZE_UNLIMITED
name
it s the only field you need to keep assuming you have a context There is some magic you don t need to care about around this just let it vf default minimum maximum flags name is the option name
Definition: writing_filters.txt:88
ff_expand_text
int ff_expand_text(FFExpandTextContext *expand_text, char *text, AVBPrint *bp)
Expand text template.
Definition: textutils.c:123
av_clip
#define av_clip
Definition: common.h:99
av_bprint_is_complete
static int av_bprint_is_complete(const AVBPrint *buf)
Test if the print buffer is complete (not truncated).
Definition: bprint.h:218
AVERROR
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later. That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another. Frame references ownership and permissions
av_bprint_init
void av_bprint_init(AVBPrint *buf, unsigned size_init, unsigned size_max)
Definition: bprint.c:69
tmp
static uint8_t tmp[11]
Definition: aes_ctr.c:28
FFExpandTextContext::functions
FFExpandTextFunction * functions
list of functions to use to expand sequences in the format FUNCTION_NAME{PARAMS}
Definition: textutils.h:77
av_strlcatf
size_t av_strlcatf(char *dst, size_t size, const char *fmt,...)
Definition: avstring.c:103
FFExpandTextContext::functions_nb
unsigned int functions_nb
number of functions
Definition: textutils.h:82
positions
const static uint16_t positions[][14][3]
Definition: vf_vectorscope.c:817
av_file_map
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
ff_expand_text_function
static int ff_expand_text_function(FFExpandTextContext *expand_text, AVBPrint *bp, char **rtext)
Expand text template pointed to by *rtext.
Definition: textutils.c:82
gmtime_r
#define gmtime_r
Definition: time_internal.h:34
FFExpandTextContext::log_ctx
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
pts
static int64_t pts
Definition: transcode_aac.c:644
FFExpandTextFunction
Function used to expand a template sequence in the format %{FUNCTION_NAME[:PARAMS]},...
Definition: textutils.h:36
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:180
FF_ARRAY_ELEMS
#define FF_ARRAY_ELEMS(a)
Definition: sinewin_tablegen.c:29
ff_load_textfile
int ff_load_textfile(void *log_ctx, const char *textfile, unsigned char **text, size_t *text_size)
Definition: textutils.c:353
var_names
static const char *const var_names[]
Definition: noise.c:31
format
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample format(the sample packing is implied by the sample format) and sample rate. The lists are not just lists
AV_LOG_DEBUG
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
Definition: log.h:201
av_file_unmap
void av_file_unmap(uint8_t *bufptr, size_t size)
Unmap or free the buffer bufptr created by av_file_map().
Definition: file.c:146
NULL
#define NULL
Definition: coverity.c:32
ff_print_formatted_eval_expr
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:303
isnan
#define isnan(x)
Definition: libm.h:340
textutils.h
double
double
Definition: af_crystalizer.c:131
av_parse_time
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:589
time.h
av_bprint_strftime
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:181
error.h
av_expr_parse_and_eval
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:803
av_bprint_finalize
int av_bprint_finalize(AVBPrint *buf, char **ret_str)
Finalize a print buffer.
Definition: bprint.c:240
localtime_r
#define localtime_r
Definition: time_internal.h:46
ff_print_eval_expr
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:281
av_isdigit
static av_const int av_isdigit(int c)
Locale-independent conversion of ASCII isdigit.
Definition: avstring.h:202
FFExpandTextFunction::func
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
ff_print_pts
int ff_print_pts(void *log_ctx, AVBPrint *bp, double pts, const char *delta, const char *fmt, const char *strftime_fmt)
Definition: textutils.c:149
i
#define i(width, name, range_min, range_max)
Definition: cbs_h2645.c:256
AV_TIME_BASE
#define AV_TIME_BASE
Internal time base represented as integer.
Definition: avutil.h:254
delta
float delta
Definition: vorbis_enc_data.h:430
len
int len
Definition: vorbis_enc_data.h:426
ret
ret
Definition: filter_design.txt:187
av_bprintf
void av_bprintf(AVBPrint *buf, const char *fmt,...)
Definition: bprint.c:99
av_get_token
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
av_bprint_clear
void av_bprint_clear(AVBPrint *buf)
Reset the string to "" but keep internal allocated data.
Definition: bprint.c:232
FFExpandTextContext
in a text template, followed by any character, always expands to the second character.
Definition: textutils.h:66
file.h
av_gettime
int64_t av_gettime(void)
Get the current time in microseconds.
Definition: time.c:39
avutil.h
mem.h
llrint
#define llrint(x)
Definition: libm.h:394
av_freep
#define av_freep(p)
Definition: tableprint_vlc.h:34
ff_expand_text_function_internal
static int ff_expand_text_function_internal(FFExpandTextContext *expand_text, AVBPrint *bp, char *name, unsigned argc, char **argv)
Definition: textutils.c:35
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:27
av_bprint_chars
void av_bprint_chars(AVBPrint *buf, char c, unsigned n)
Append char c n times to a print buffer.
Definition: bprint.c:145
ff_print_time
int ff_print_time(void *log_ctx, AVBPrint *bp, const char *strftime_fmt, char localtime)
Definition: textutils.c:202
av_x_if_null
static void * av_x_if_null(const void *p, const void *x)
Return x default pointer in case p is NULL.
Definition: avutil.h:312
av_realloc
void * av_realloc(void *ptr, size_t size)
Allocate, reallocate, or free a block of memory.
Definition: mem.c:155