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"
35 
36 static 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  */
83 static 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 
118 end:
119  for (i = 0; i < argc; i++)
120  av_freep(&argv[i]);
121  return ret;
122 }
123 
124 int 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 
150 int 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 
203 int 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 
282 int 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 
304 int 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 
354 int 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 }
384 
AV_LOG_WARNING
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition: log.h:215
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
av_clip
#define av_clip
Definition: common.h:100
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
int64_t
long long int64_t
Definition: coverity.c:34
tmp
static uint8_t tmp[11]
Definition: aes_ctr.c:28
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
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
ff_expand_text
int ff_expand_text(FFExpandTextContext *expand_text, const char *text, AVBPrint *bp)
Expand text template.
Definition: textutils.c:124
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:209
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:354
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:230
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:142
time_internal.h
FFExpandTextContext::functions
const FFExpandTextFunction * functions
list of functions to use to expand sequences in the format FUNCTION_NAME{PARAMS}
Definition: textutils.h:77
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:304
isnan
#define isnan(x)
Definition: libm.h:340
textutils.h
double
double
Definition: af_crystalizer.c:132
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:592
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:282
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:150
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
ff_expand_text_function
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
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:36
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:203
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