History

lavfi: introduce textutils

Generalize drawtext utilities to make them usable in other filters. This will be needed to introduce the QR code source and filter without duplicating functionality.

Stefano Sabatini committed 2 years ago

Commit 732fb121 parent c2ab41b4 files · +743 −403Browse files

Showing the changes to libavfilter (4 of 4 files). Show all

Unified · Split

4 files
  1. libavfilter/Makefile +1 −1
  2. libavfilter/textutils.c +382 −0
  3. libavfilter/textutils.h +229 −0
  4. libavfilter/vf_drawtext.c +131 −402
libavfilter/Makefile+1−1
@@ -291,7 +291,7 @@ OBJS-$(CONFIG_DOUBLEWEAVE_FILTER) += vf_weave.o · 290 unmodified lines
291OBJS-$(CONFIG_DRAWBOX_FILTER) += vf_drawbox.o291OBJS-$(CONFIG_DRAWBOX_FILTER) += vf_drawbox.o
292OBJS-$(CONFIG_DRAWGRAPH_FILTER) += f_drawgraph.o292OBJS-$(CONFIG_DRAWGRAPH_FILTER) += f_drawgraph.o
293OBJS-$(CONFIG_DRAWGRID_FILTER) += vf_drawbox.o293OBJS-$(CONFIG_DRAWGRID_FILTER) += vf_drawbox.o
294OBJS-$(CONFIG_DRAWTEXT_FILTER) += vf_drawtext.o294OBJS-$(CONFIG_DRAWTEXT_FILTER) += vf_drawtext.o textutils.o
295OBJS-$(CONFIG_EDGEDETECT_FILTER) += vf_edgedetect.o edge_common.o295OBJS-$(CONFIG_EDGEDETECT_FILTER) += vf_edgedetect.o edge_common.o
296OBJS-$(CONFIG_ELBG_FILTER) += vf_elbg.o296OBJS-$(CONFIG_ELBG_FILTER) += vf_elbg.o
297OBJS-$(CONFIG_ENTROPY_FILTER) += vf_entropy.o297OBJS-$(CONFIG_ENTROPY_FILTER) += vf_entropy.o
libavfilter/textutils.c+382−0
@@ -0,0 +1,382 @@
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/time.h"
33
34static int ff_expand_text_function_internal(FFExpandTextContext *expand_text, AVBPrint *bp,
35 char *name, unsigned argc, char **argv)
36{
37 void *log_ctx = expand_text->log_ctx;
38 FFExpandTextFunction *functions = expand_text->functions;
39 unsigned i;
40
41 for (i = 0; i < expand_text->functions_nb; i++) {
42 if (strcmp(name, functions[i].name))
43 continue;
44 if (argc < functions[i].argc_min) {
45 av_log(log_ctx, AV_LOG_ERROR, "%%{%s} requires at least %d arguments\n",
46 name, functions[i].argc_min);
47 return AVERROR(EINVAL);
48 }
49 if (argc > functions[i].argc_max) {
50 av_log(log_ctx, AV_LOG_ERROR, "%%{%s} requires at most %d arguments\n",
51 name, functions[i].argc_max);
52 return AVERROR(EINVAL);
53 }
54 break;
55 }
56 if (i >= expand_text->functions_nb) {
57 av_log(log_ctx, AV_LOG_ERROR, "%%{%s} is not known\n", name);
58 return AVERROR(EINVAL);
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 */
81static int ff_expand_text_function(FFExpandTextContext *expand_text, AVBPrint *bp, char **rtext)
82{
83 void *log_ctx = expand_text->log_ctx;
84 const char *text = *rtext;
85 char *argv[16] = { NULL };
86 unsigned argc = 0, i;
87 int ret;
88
89 if (*text != '{') {
90 av_log(log_ctx, AV_LOG_ERROR, "Stray %% near '%s'\n", text);
91 return AVERROR(EINVAL);
92 }
93 text++;
94 while (1) {
95 if (!(argv[argc++] = av_get_token(&text, ":}"))) {
96 ret = AVERROR(ENOMEM);
97 goto end;
98 }
99 if (!*text) {
100 av_log(log_ctx, AV_LOG_ERROR, "Unterminated %%{} near '%s'\n", *rtext);
101 ret = AVERROR(EINVAL);
102 goto end;
103 }
104 if (argc == FF_ARRAY_ELEMS(argv))
105 av_freep(&argv[--argc]); /* error will be caught later */
106 if (*text == '}')
107 break;
108 text++;
109 }
110
111 if ((ret = ff_expand_text_function_internal(expand_text, bp, argv[0], argc - 1, argv + 1)) < 0)
112 goto end;
113 ret = 0;
114 *rtext = (char *)text + 1;
115
116end:
117 for (i = 0; i < argc; i++)
118 av_freep(&argv[i]);
119 return ret;
120}
121
122int ff_expand_text(FFExpandTextContext *expand_text, char *text, AVBPrint *bp)
123{
124 int ret;
125
126 av_bprint_clear(bp);
127 if (!text)
128 return 0;
129
130 while (*text) {
131 if (*text == '\\' && text[1]) {
132 av_bprint_chars(bp, text[1], 1);
133 text += 2;
134 } else if (*text == '%') {
135 text++;
136 if ((ret = ff_expand_text_function(expand_text, bp, &text)) < 0)
137 return ret;
138 } else {
139 av_bprint_chars(bp, *text, 1);
140 text++;
141 }
142 }
143 if (!av_bprint_is_complete(bp))
144 return AVERROR(ENOMEM);
145 return 0;
146}
147
148int ff_print_pts(void *log_ctx, AVBPrint *bp, double pts, const char *delta,
149 const char *fmt, const char *strftime_fmt)
150{
151 int ret;
152
153 if (delta) {
154 int64_t delta_i;
155 if ((ret = av_parse_time(&delta_i, delta, 1)) < 0) {
156 av_log(log_ctx, AV_LOG_ERROR, "Invalid delta '%s'\n", delta);
157 return ret;
158 }
159 pts += (double)delta_i / AV_TIME_BASE;
160 }
161
162 if (!strcmp(fmt, "flt")) {
163 av_bprintf(bp, "%.6f", pts);
164 } else if (!strcmp(fmt, "hms") ||
165 !strcmp(fmt, "hms24hh")) {
166 if (isnan(pts)) {
167 av_bprintf(bp, " ??:??:??.???");
168 } else {
169 int64_t ms = llrint(pts * 1000);
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 }
179 av_bprintf(bp, "%c%02d:%02d:%02d.%03d", sign,
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"))
190 localtime_r(&ms, &tm);
191 else
192 gmtime_r(&ms, &tm);
193 av_bprint_strftime(bp, av_x_if_null(strftime_fmt, "%Y-%m-%d %H:%M:%S"), &tm);
194 } else {
195 av_log(log_ctx, AV_LOG_ERROR, "Invalid format '%s'\n", fmt);
196 return AVERROR(EINVAL);
197 }
198 return 0;
199}
200
201int ff_print_time(void *log_ctx, AVBPrint *bp,
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;
206 int64_t unow;
207 time_t now;
208 struct tm tm;
209 const char *begin;
210 const char *tmp;
211 int len;
212 int div;
213 AVBPrint fmt_bp;
214
215 av_bprint_init(&fmt_bp, 0, AV_BPRINT_SIZE_UNLIMITED);
216
217 unow = av_gettime();
218 now = unow / 1000000;
219 if (localtime)
220 localtime_r(&now, &tm);
221 else
222 tm = *gmtime_r(&now, &tm);
223
224 // manually parse format for %N (fractional seconds)
225 begin = fmt;
226 while ((begin = strchr(begin, '%'))) {
227 tmp = begin + 1;
228 len = 0;
229
230 // skip escaped "%%"
231 if (*tmp == '%') {
232 begin = tmp + 1;
233 continue;
234 }
235
236 // count digits between % and possible N
237 while (*tmp != '\0' && av_isdigit((int)*tmp)) {
238 len++;
239 tmp++;
240 }
241
242 // N encountered, insert time
243 if (*tmp == 'N') {
244 int num_digits = 3; // default show millisecond [1,6]
245
246 // if digit given, expect [1,6], warn & clamp otherwise
247 if (len == 1) {
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
259 begin += len;
260 fmt_begin = begin;
261
262 continue;
263 }
264
265 begin = tmp;
266 }
267
268 av_bprintf(&fmt_bp, "%s", fmt_begin);
269 if (!av_bprint_is_complete(&fmt_bp)) {
270 av_log(log_ctx, AV_LOG_WARNING, "Format string truncated at %u/%u.", fmt_bp.size, fmt_bp.len);
271 }
272
273 av_bprint_strftime(bp, fmt_bp.str, &tm);
274
275 av_bprint_finalize(&fmt_bp, NULL);
276
277 return 0;
278}
279
280int ff_print_eval_expr(void *log_ctx, AVBPrint *bp,
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;
287 int ret;
288
289 ret = av_expr_parse_and_eval(&res, expr, var_names, var_values,
290 NULL, NULL, fun_names, fun_values,
291 eval_ctx, 0, log_ctx);
292 if (ret < 0)
293 av_log(log_ctx, AV_LOG_ERROR,
294 "Text expansion expression '%s' is not valid\n",
295 expr);
296 else
297 av_bprintf(bp, "%f", res);
298
299 return ret;
300}
301
302int ff_print_formatted_eval_expr(void *log_ctx, AVBPrint *bp,
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,
307 const char format, int positions)
308{
309 double res;
310 int intval;
311 int ret;
312 char fmt_str[30] = "%";
313
314 ret = av_expr_parse_and_eval(&res, expr, var_names, var_values,
315 NULL, NULL, fun_names, fun_values,
316 eval_ctx, 0, log_ctx);
317 if (ret < 0) {
318 av_log(log_ctx, AV_LOG_ERROR,
319 "Text expansion expression '%s' is not valid\n",
320 expr);
321 return ret;
322 }
323
324 if (!strchr("xXdu", format)) {
325 av_log(log_ctx, AV_LOG_ERROR, "Invalid format '%c' specified,"
326 " allowed values: 'x', 'X', 'd', 'u'\n", format);
327 return AVERROR(EINVAL);
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);
335 return AVERROR(EINVAL);
336 }
337#endif
338
339 if (positions >= 0)
340 av_strlcatf(fmt_str, sizeof(fmt_str), "0%u", positions);
341 av_strlcatf(fmt_str, sizeof(fmt_str), "%c", format);
342
343 av_log(log_ctx, AV_LOG_DEBUG, "Formatting value %f (expr '%s') with spec '%s'\n",
344 res, expr, fmt_str);
345
346 av_bprintf(bp, fmt_str, intval);
347
348 return 0;
349}
350
351
352int ff_load_textfile(void *log_ctx, const char *textfile,
353 unsigned char **text, size_t *text_size)
354{
355 int err;
356 uint8_t *textbuf;
357 uint8_t *tmp;
358 size_t textbuf_size;
359
360 if ((err = av_file_map(textfile, &textbuf, &textbuf_size, 0, log_ctx)) < 0) {
361 av_log(log_ctx, AV_LOG_ERROR,
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))) {
370 av_file_unmap(textbuf, textbuf_size);
371 return AVERROR(ENOMEM);
372 }
373 *text = tmp;
374 memcpy(*text, textbuf, textbuf_size);
375 (*text)[textbuf_size] = 0;
376 if (text_size)
377 *text_size = textbuf_size;
378 av_file_unmap(textbuf, textbuf_size);
379
380 return 0;
381}
382
libavfilter/textutils.h+229−0
@@ -0,0 +1,229 @@
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 utilities
22 */
23
24#ifndef AVFILTER_TEXTUTILS_H
25#define AVFILTER_TEXTUTILS_H
26
27#include "libavutil/bprint.h"
28#include "libavutil/eval.h"
29#include "libavutil/log.h"
30#include "libavutil/parseutils.h"
31
32/**
33 * Function used to expand a template sequence in the format
34 * %{FUNCTION_NAME[:PARAMS]}, defined in the TextExpander object.
35 */
36typedef struct FFExpandTextFunction {
37 /**
38 * name of the function
39 */
40 const char *name;
41
42 /**
43 * minimum and maximum number of arguments accepted by the
44 * function in the PARAMS
45 */
46 unsigned argc_min, argc_max;
47
48 /**
49 * actual function used to perform the expansion
50 */
51 int (*func)(void *ctx, AVBPrint *bp, const char *function_name, unsigned argc, char **args);
52} FFExpandTextFunction;
53
54/**
55 * Text expander context, used to encapsulate the logic to expand a
56 * given text template.
57 *
58 * A backslash character @samp{\} in a text template, followed by any
59 * character, always expands to the second character.
60 * Sequences of the form %{FUNCTION_NAME[:PARAMS]} are expanded using a
61 * function defined in the object. The text between the braces is a
62 * function name, possibly followed by arguments separated by ':'. If
63 * the arguments contain special characters or delimiters (':' or
64 * '}'), they should be escaped.
65 */
66typedef struct FFExpandTextContext {
67 /**
68 * log context to pass to the function, used for logging and for
69 * accessing the context for the function
70 */
71 void *log_ctx;
72
73 /**
74 * list of functions to use to expand sequences in the format
75 * FUNCTION_NAME{PARAMS}
76 */
77 FFExpandTextFunction *functions;
78
79 /**
80 * number of functions
81 */
82 unsigned int functions_nb;
83} FFExpandTextContext;
84
85/**
86 * Expand text template.
87 *
88 * Expand text template defined in text using the logic defined in a text
89 * expander object.
90 *
91 * @param expand_text text expansion context used to expand the text
92 * @param text template text to expand
93 * @param bp BPrint object where the expanded text is written to
94 * @return negative value corresponding to an AVERROR error code in case of
95 * errors, a non-negative value otherwise
96 */
97int ff_expand_text(FFExpandTextContext *expand_text, char *text, AVBPrint *bp);
98
99/**
100 * Print PTS representation to an AVBPrint object.
101 *
102 * @param log_ctx pointer to av_log object
103 * @param bp AVBPrint object where the PTS textual representation is written to
104 * @param pts PTS value expressed as a double to represent
105 * @param delta delta time parsed by av_parse_time(), added to the PTS
106 * @param fmt string representing the format to use for printing, can be
107 * "flt" - use a float representation with 6 decimal digits,
108 * "hms" - use HH:MM:SS.MMM format,
109 * "hms24hh" - same as "hms" but wraps the hours in 24hh format
110 * (so that it is expressed in the range 00-23),
111 * "localtime" or "gmtime" - expand the PTS according to the
112 * @code{strftime()} function rules, using either the corresponding
113 * @code{localtime()} or @code{gmtime()} time
114 * @param strftime_fmt: @code{strftime()} format to use to represent the PTS in
115 * case the format "localtime" or "gmtime" was selected, if not specified
116 * defaults to "%Y-%m-%d %H:%M:%S"
117 * @return negative value corresponding to an AVERROR error code in case of
118 * errors, a non-negative value otherwise
119 */
120int ff_print_pts(void *log_ctx, AVBPrint *bp, double pts, const char *delta,
121 const char *fmt, const char *strftime_fmt);
122
123/**
124 * Print time representation to an AVBPrint object.
125 *
126 * @param log_ctx pointer to av_log object
127 * @param bp AVBPrint object where the time textual representation is written to
128 * @param strftime_fmt: strftime() format to use to represent the time in case
129 * if not specified defaults to "%Y-%m-%d %H:%M:%S". The format string is
130 * extended to support the %[1-6]N after %S which prints fractions of the
131 * second with optionally specified number of digits, if not specified
132 * defaults to 3.
133 * @param localtime use local time to compute the time if non-zero, otherwise
134 * use UTC
135 * @return negative value corresponding to an AVERROR error code in case of
136 * errors, a non-negative value otherwise
137 */
138int ff_print_time(void *log_ctx, AVBPrint *bp, const char *strftime_fmt, char localtime);
139
140typedef double (*ff_eval_func2)(void *, double a, double b);
141
142/**
143 * Evaluate and print expression to an AVBprint object.
144 * The output is written as a double representation.
145 *
146 * This is a wrapper around av_expr_parse_and_eval() and following the
147 * same rules.
148 *
149 * @param log_ctx pointer to av_log object
150 * @param bp AVBPrint object where the evaluated expression is written to
151 * @param expr the expression to be evaluated
152 * @param fun_names names of the ff_eval_func2 functions used to evaluate the expression
153 * @param fun_values values of the ff_eval_func2 functions used to evaluate the expression
154 * @param var_names names of the variables used in the expression
155 * @param var_values values of the variables used in the expression
156 * @param eval_ctx evaluation context to be passed to some functions
157 *
158 * @return negative value corresponding to an AVERROR error code in case of
159 * errors, a non-negative value otherwise
160 */
161int ff_print_eval_expr(void *log_ctx, AVBPrint *bp,
162 const char *expr,
163 const char * const *fun_names, const ff_eval_func2 *fun_values,
164 const char * const *var_names, const double *var_values,
165 void *eval_ctx);
166
167/**
168 * Evaluate and print expression to an AVBprint object, using the
169 * specified format.
170 *
171 * This is a wrapper around av_expr_parse_and_eval() and following the
172 * same rules.
173 *
174 * The format is specified as a printf format character, optionally
175 * preceded by the positions numbers for zero-padding.
176 *
177 * The following formats are accepted:
178 * - x: use lowercase hexadecimal representation
179 * - X: use uppercase hexadecimal representation
180 * - d: use decimal representation
181 * - u: use unsigned decimal representation
182 *
183 * @param log_ctx pointer to av_log object
184 * @param bp AVBPrint object where the evaluated expression is written to
185 * @param expr the expression to be evaluated
186 * @param fun_names names of the ff_eval_func2 functions used to evaluate the expression
187 * @param fun_values values of the ff_eval_func2 functions used to evaluate the expression
188 * @param var_names names of the variables used in the expression
189 * @param var_values values of the variables used in the expression
190 * @param eval_ctx evaluation context to be passed to some functions
191 * @param format a character representing the format, to be chosen in xXdu
192 * @param positions final size of the value representation with 0-padding
193 * @return negative value corresponding to an AVERROR error code in case of
194 * errors, a non-negative value otherwise
195 */
196int ff_print_formatted_eval_expr(void *log_ctx, AVBPrint *bp,
197 const char *expr,
198 const char * const *fun_names, const ff_eval_func2 *fun_values,
199 const char * const *var_names, const double *var_values,
200 void *eval_ctx,
201 const char format, int positions);
202
203/**
204 * Check if the character is a newline.
205 *
206 * @param c character to check
207 * @return non-negative value in case c is a newline, 0 otherwise
208 */
209static inline int ff_is_newline(uint32_t c)
210{
211 return c == '\n' || c == '\r' || c == '\f' || c == '\v';
212}
213
214/**
215 * Load text file into the buffer pointed by text.
216 *
217 * @param log_ctx pointer to av_log object
218 * @param textfile filename containing the text to load
219 * @param text pointer to the text buffer where the loaded text will be
220 * loaded
221 * @param text_size pointer to the value to set with the loaded text data,
222 * including the terminating 0 character
223 * @return negative value corresponding to an AVERROR error code in case of
224 * errors, a non-negative value otherwise
225 */
226int ff_load_textfile(void *log_ctx, const char *textfile,
227 unsigned char **text, size_t *text_size);
228
229#endif /* AVFILTER_TEXTUTILS__H */
libavfilter/vf_drawtext.c+131−402
@@ -47,7 +47,6 @@ · 46 unmodified lines
47#include "libavutil/avstring.h"47#include "libavutil/avstring.h"
48#include "libavutil/bprint.h"48#include "libavutil/bprint.h"
49#include "libavutil/common.h"49#include "libavutil/common.h"
50#include "libavutil/file.h"
51#include "libavutil/eval.h"50#include "libavutil/eval.h"
52#include "libavutil/opt.h"51#include "libavutil/opt.h"
53#include "libavutil/random_seed.h"52#include "libavutil/random_seed.h"
@@ -62,6 +61,7 @@ · 8 unmodified lines
62#include "drawutils.h"61#include "drawutils.h"
63#include "formats.h"62#include "formats.h"
64#include "internal.h"63#include "internal.h"
64#include "textutils.h"
65#include "video.h"65#include "video.h"
66 66
67#if CONFIG_LIBFRIBIDI67#if CONFIG_LIBFRIBIDI
@@ -253,6 +253,7 @@ typedef struct TextMetrics { · 185 unmodified lines
253typedef struct DrawTextContext {253typedef struct DrawTextContext {
254 const AVClass *class;254 const AVClass *class;
255 int exp_mode; ///< expansion mode to use for the text255 int exp_mode; ///< expansion mode to use for the text
256 FFExpandTextContext expand_text; ///< expand text in case exp_mode == NORMAL
256 int reinit; ///< tells if the filter is being reinited257 int reinit; ///< tells if the filter is being reinited
257#if CONFIG_LIBFONTCONFIG258#if CONFIG_LIBFONTCONFIG
258 uint8_t *font; ///< font to be used259 uint8_t *font; ///< font to be used
@@ -631,40 +632,6 @@ static int load_font(AVFilterContext *ctx) · 372 unmodified lines
631 return err;632 return err;
632}633}
633 634
634static inline int is_newline(uint32_t c)
635{
636 return c == '\n' || c == '\r' || c == '\f' || c == '\v';
637}
638
639static int load_textfile(AVFilterContext *ctx)
640{
641 DrawTextContext *s = ctx->priv;
642 int err;
643 uint8_t *textbuf;
644 uint8_t *tmp;
645 size_t textbuf_size;
646
647 if ((err = av_file_map(s->textfile, &textbuf, &textbuf_size, 0, ctx)) < 0) {
648 av_log(ctx, AV_LOG_ERROR,
649 "The text file '%s' could not be read or is empty\n",
650 s->textfile);
651 return err;
652 }
653
654 if (textbuf_size > 0 && is_newline(textbuf[textbuf_size - 1]))
655 textbuf_size--;
656 if (textbuf_size > SIZE_MAX - 1 || !(tmp = av_realloc(s->text, textbuf_size + 1))) {
657 av_file_unmap(textbuf, textbuf_size);
658 return AVERROR(ENOMEM);
659 }
660 s->text = tmp;
661 memcpy(s->text, textbuf, textbuf_size);
662 s->text[textbuf_size] = 0;
663 av_file_unmap(textbuf, textbuf_size);
664
665 return 0;
666}
667
668#if CONFIG_LIBFRIBIDI635#if CONFIG_LIBFRIBIDI
669static int shape_text(AVFilterContext *ctx)636static int shape_text(AVFilterContext *ctx)
670{637{
@@ -885,6 +852,123 @@ static int string_to_array(const char *source, int *result, int result_size) · 214 unmodified lines
885 return counter;852 return counter;
886}853}
887 854
855static int func_pict_type(void *ctx, AVBPrint *bp, const char *function_name, unsigned argc, char **argv)
856{
857 DrawTextContext *s = ((AVFilterContext *)ctx)->priv;
858
859 av_bprintf(bp, "%c", av_get_picture_type_char(s->var_values[VAR_PICT_TYPE]));
860 return 0;
861}
862
863static int func_pts(void *ctx, AVBPrint *bp, const char *function_name, unsigned argc, char **argv)
864{
865 DrawTextContext *s = ((AVFilterContext *)ctx)->priv;
866 const char *fmt;
867 const char *strftime_fmt = NULL;
868 const char *delta = NULL;
869 double pts = s->var_values[VAR_T];
870
871 // argv: pts, FMT, [DELTA, 24HH | strftime_fmt]
872
873 fmt = argc >= 1 ? argv[0] : "flt";
874 if (argc >= 2) {
875 delta = argv[1];
876 }
877 if (argc >= 3) {
878 if (!strcmp(fmt, "hms")) {
879 if (!strcmp(argv[2], "24HH")) {
880 av_log(ctx, AV_LOG_WARNING, "pts third argument 24HH is deprected, use pts:hms24hh instead\n");
881 fmt = "hms24";
882 } else {
883 av_log(ctx, AV_LOG_ERROR, "Invalid argument '%s', '24HH' was expected\n", argv[2]);
884 return AVERROR(EINVAL);
885 }
886 } else {
887 strftime_fmt = argv[2];
888 }
889 }
890
891 return ff_print_pts(ctx, bp, pts, delta, fmt, strftime_fmt);
892}
893
894static int func_frame_num(void *ctx, AVBPrint *bp, const char *function_name, unsigned argc, char **argv)
895{
896 DrawTextContext *s = ((AVFilterContext *)ctx)->priv;
897
898 av_bprintf(bp, "%d", (int)s->var_values[VAR_N]);
899 return 0;
900}
901
902static int func_metadata(void *ctx, AVBPrint *bp, const char *function_name, unsigned argc, char **argv)
903{
904 DrawTextContext *s = ((AVFilterContext *)ctx)->priv;
905 AVDictionaryEntry *e = av_dict_get(s->metadata, argv[0], NULL, 0);
906
907 if (e && e->value)
908 av_bprintf(bp, "%s", e->value);
909 else if (argc >= 2)
910 av_bprintf(bp, "%s", argv[1]);
911 return 0;
912}
913
914static int func_strftime(void *ctx, AVBPrint *bp, const char *function_name, unsigned argc, char **argv)
915{
916 const char *strftime_fmt = argc ? argv[0] : NULL;
917
918 return ff_print_time(ctx, bp, strftime_fmt, !strcmp(function_name, "localtime"));
919}
920
921static int func_eval_expr(void *ctx, AVBPrint *bp, const char *function_name, unsigned argc, char **argv)
922{
923 DrawTextContext *s = ((AVFilterContext *)ctx)->priv;
924
925 return ff_print_eval_expr(ctx, bp, argv[0],
926 fun2_names, fun2,
927 var_names, s->var_values, &s->prng);
928}
929
930static int func_eval_expr_int_format(void *ctx, AVBPrint *bp, const char *function_name, unsigned argc, char **argv)
931{
932 DrawTextContext *s = ((AVFilterContext *)ctx)->priv;
933 int ret;
934 int positions = -1;
935
936 /*
937 * argv[0] expression to be converted to `int`
938 * argv[1] format: 'x', 'X', 'd' or 'u'
939 * argv[2] positions printed (optional)
940 */
941
942 if (argc == 3) {
943 ret = sscanf(argv[2], "%u", &positions);
944 if (ret != 1) {
945 av_log(ctx, AV_LOG_ERROR, "expr_int_format(): Invalid number of positions"
946 " to print: '%s'\n", argv[2]);
947 return AVERROR(EINVAL);
948 }
949 }
950
951 return ff_print_formatted_eval_expr(ctx, bp, argv[0],
952 fun2_names, fun2,
953 var_names, s->var_values,
954 &s->prng,
955 argv[1][0], positions);
956}
957
958static FFExpandTextFunction expand_text_functions[] = {
959 { "e", 1, 1, func_eval_expr },
960 { "eif", 2, 3, func_eval_expr_int_format },
961 { "expr", 1, 1, func_eval_expr },
962 { "expr_int_format", 2, 3, func_eval_expr_int_format },
963 { "frame_num", 0, 0, func_frame_num },
964 { "gmtime", 0, 1, func_strftime },
965 { "localtime", 0, 1, func_strftime },
966 { "metadata", 1, 2, func_metadata },
967 { "n", 0, 0, func_frame_num },
968 { "pict_type", 0, 0, func_pict_type },
969 { "pts", 0, 3, func_pts }
970};
971
888static av_cold int init(AVFilterContext *ctx)972static av_cold int init(AVFilterContext *ctx)
889{973{
890 int err;974 int err;
@@ -907,7 +991,7 @@ static av_cold int init(AVFilterContext *ctx) · 16 unmodified lines
907 "Both text and text file provided. Please provide only one\n");991 "Both text and text file provided. Please provide only one\n");
908 return AVERROR(EINVAL);992 return AVERROR(EINVAL);
909 }993 }
910 if ((err = load_textfile(ctx)) < 0)994 if ((err = ff_load_textfile(ctx, (const char *)s->textfile, &s->text, NULL)) < 0)
911 return err;995 return err;
912 }996 }
913 997
@@ -950,6 +1034,12 @@ static av_cold int init(AVFilterContext *ctx) · 36 unmodified lines
950 return AVERROR(EINVAL);1034 return AVERROR(EINVAL);
951 }1035 }
952 1036
1037 s->expand_text = (FFExpandTextContext) {
1038 .log_ctx = ctx,
1039 .functions = expand_text_functions,
1040 .functions_nb = FF_ARRAY_ELEMS(expand_text_functions)
1041 };
1042
953#if CONFIG_LIBFRIBIDI1043#if CONFIG_LIBFRIBIDI
954 if (s->text_shaping)1044 if (s->text_shaping)
955 if ((err = shape_text(ctx)) < 0)1045 if ((err = shape_text(ctx)) < 0)
@@ -1160,367 +1250,6 @@ fail: · 204 unmodified lines
1160 return ret;1250 return ret;
1161}1251}
1162 1252
1163static int func_pict_type(AVFilterContext *ctx, AVBPrint *bp,
1164 char *fct, unsigned argc, char **argv, int tag)
1165{
1166 DrawTextContext *s = ctx->priv;
1167
1168 av_bprintf(bp, "%c", av_get_picture_type_char(s->var_values[VAR_PICT_TYPE]));
1169 return 0;
1170}
1171
1172static int func_pts(AVFilterContext *ctx, AVBPrint *bp,
1173 char *fct, unsigned argc, char **argv, int tag)
1174{
1175 DrawTextContext *s = ctx->priv;
1176 const char *fmt;
1177 double pts = s->var_values[VAR_T];
1178 int ret;
1179
1180 fmt = argc >= 1 ? argv[0] : "flt";
1181 if (argc >= 2) {
1182 int64_t delta;
1183 if ((ret = av_parse_time(&delta, argv[1], 1)) < 0) {
1184 av_log(ctx, AV_LOG_ERROR, "Invalid delta '%s'\n", argv[1]);
1185 return ret;
1186 }
1187 pts += (double)delta / AV_TIME_BASE;
1188 }
1189 if (!strcmp(fmt, "flt")) {
1190 av_bprintf(bp, "%.6f", pts);
1191 } else if (!strcmp(fmt, "hms")) {
1192 if (isnan(pts)) {
1193 av_bprintf(bp, " ??:??:??.???");
1194 } else {
1195 int64_t ms = llrint(pts * 1000);
1196 char sign = ' ';
1197 if (ms < 0) {
1198 sign = '-';
1199 ms = -ms;
1200 }
1201 if (argc >= 3) {
1202 if (!strcmp(argv[2], "24HH")) {
1203 ms %= 24 * 60 * 60 * 1000;
1204 } else {
1205 av_log(ctx, AV_LOG_ERROR, "Invalid argument '%s'\n", argv[2]);
1206 return AVERROR(EINVAL);
1207 }
1208 }
1209 av_bprintf(bp, "%c%02d:%02d:%02d.%03d", sign,
1210 (int)(ms / (60 * 60 * 1000)),
1211 (int)(ms / (60 * 1000)) % 60,
1212 (int)(ms / 1000) % 60,
1213 (int)(ms % 1000));
1214 }
1215 } else if (!strcmp(fmt, "localtime") ||
1216 !strcmp(fmt, "gmtime")) {
1217 struct tm tm;
1218 time_t ms = (time_t)pts;
1219 const char *timefmt = argc >= 3 ? argv[2] : "%Y-%m-%d %H:%M:%S";
1220 if (!strcmp(fmt, "localtime"))
1221 localtime_r(&ms, &tm);
1222 else
1223 gmtime_r(&ms, &tm);
1224 av_bprint_strftime(bp, timefmt, &tm);
1225 } else {
1226 av_log(ctx, AV_LOG_ERROR, "Invalid format '%s'\n", fmt);
1227 return AVERROR(EINVAL);
1228 }
1229 return 0;
1230}
1231
1232static int func_frame_num(AVFilterContext *ctx, AVBPrint *bp,
1233 char *fct, unsigned argc, char **argv, int tag)
1234{
1235 DrawTextContext *s = ctx->priv;
1236
1237 av_bprintf(bp, "%d", (int)s->var_values[VAR_N]);
1238 return 0;
1239}
1240
1241static int func_metadata(AVFilterContext *ctx, AVBPrint *bp,
1242 char *fct, unsigned argc, char **argv, int tag)
1243{
1244 DrawTextContext *s = ctx->priv;
1245 AVDictionaryEntry *e = av_dict_get(s->metadata, argv[0], NULL, 0);
1246
1247 if (e && e->value)
1248 av_bprintf(bp, "%s", e->value);
1249 else if (argc >= 2)
1250 av_bprintf(bp, "%s", argv[1]);
1251 return 0;
1252}
1253
1254static int func_strftime(AVFilterContext *ctx, AVBPrint *bp,
1255 char *fct, unsigned argc, char **argv, int tag)
1256{
1257 const char *fmt = argc ? argv[0] : "%Y-%m-%d %H:%M:%S";
1258 const char *fmt_begin = fmt;
1259 int64_t unow;
1260 time_t now;
1261 struct tm tm;
1262 const char *begin;
1263 const char *tmp;
1264 int len;
1265 int div;
1266 AVBPrint fmt_bp;
1267
1268 av_bprint_init(&fmt_bp, 0, AV_BPRINT_SIZE_UNLIMITED);
1269
1270 unow = av_gettime();
1271 now = unow / 1000000;
1272 if (tag == 'L' || tag == 'm')
1273 localtime_r(&now, &tm);
1274 else
1275 tm = *gmtime_r(&now, &tm);
1276
1277 // manually parse format for %N (fractional seconds)
1278 begin = fmt;
1279 while ((begin = strchr(begin, '%'))) {
1280 tmp = begin + 1;
1281 len = 0;
1282
1283 // skip escaped "%%"
1284 if (*tmp == '%') {
1285 begin = tmp + 1;
1286 continue;
1287 }
1288
1289 // count digits between % and possible N
1290 while (*tmp != '\0' && av_isdigit((int)*tmp)) {
1291 len++;
1292 tmp++;
1293 }
1294
1295 // N encountered, insert time
1296 if (*tmp == 'N') {
1297 int num_digits = 3; // default show millisecond [1,6]
1298
1299 // if digit given, expect [1,6], warn & clamp otherwise
1300 if (len == 1) {
1301 num_digits = av_clip(*(begin + 1) - '0', 1, 6);
1302 } else if (len > 1) {
1303 av_log(ctx, AV_LOG_WARNING, "Invalid number of decimals for %%N, using default of %i\n", num_digits);
1304 }
1305
1306 len += 2; // add % and N to get length of string part
1307
1308 div = pow(10, 6 - num_digits);
1309
1310 av_bprintf(&fmt_bp, "%.*s%0*d", (int)(begin - fmt_begin), fmt_begin, num_digits, (int)(unow % 1000000) / div);
1311
1312 begin += len;
1313 fmt_begin = begin;
1314
1315 continue;
1316 }
1317
1318 begin = tmp;
1319 }
1320
1321 av_bprintf(&fmt_bp, "%s", fmt_begin);
1322 if (!av_bprint_is_complete(&fmt_bp)) {
1323 av_log(ctx, AV_LOG_WARNING, "Format string truncated at %u/%u.", fmt_bp.size, fmt_bp.len);
1324 }
1325
1326 av_bprint_strftime(bp, fmt_bp.str, &tm);
1327
1328 av_bprint_finalize(&fmt_bp, NULL);
1329
1330 return 0;
1331}
1332
1333static int func_eval_expr(AVFilterContext *ctx, AVBPrint *bp,
1334 char *fct, unsigned argc, char **argv, int tag)
1335{
1336 DrawTextContext *s = ctx->priv;
1337 double res;
1338 int ret;
1339
1340 ret = av_expr_parse_and_eval(&res, argv[0], var_names, s->var_values,
1341 NULL, NULL, fun2_names, fun2,
1342 &s->prng, 0, ctx);
1343 if (ret < 0)
1344 av_log(ctx, AV_LOG_ERROR,
1345 "Expression '%s' for the expr text expansion function is not valid\n",
1346 argv[0]);
1347 else
1348 av_bprintf(bp, "%f", res);
1349
1350 return ret;
1351}
1352
1353static int func_eval_expr_int_format(AVFilterContext *ctx, AVBPrint *bp,
1354 char *fct, unsigned argc, char **argv, int tag)
1355{
1356 DrawTextContext *s = ctx->priv;
1357 double res;
1358 int intval;
1359 int ret;
1360 unsigned int positions = 0;
1361 char fmt_str[30] = "%";
1362
1363 /*
1364 * argv[0] expression to be converted to `int`
1365 * argv[1] format: 'x', 'X', 'd' or 'u'
1366 * argv[2] positions printed (optional)
1367 */
1368
1369 ret = av_expr_parse_and_eval(&res, argv[0], var_names, s->var_values,
1370 NULL, NULL, fun2_names, fun2,
1371 &s->prng, 0, ctx);
1372 if (ret < 0) {
1373 av_log(ctx, AV_LOG_ERROR,
1374 "Expression '%s' for the expr text expansion function is not valid\n",
1375 argv[0]);
1376 return ret;
1377 }
1378
1379 if (!strchr("xXdu", argv[1][0])) {
1380 av_log(ctx, AV_LOG_ERROR, "Invalid format '%c' specified,"
1381 " allowed values: 'x', 'X', 'd', 'u'\n", argv[1][0]);
1382 return AVERROR(EINVAL);
1383 }
1384
1385 if (argc == 3) {
1386 ret = sscanf(argv[2], "%u", &positions);
1387 if (ret != 1) {
1388 av_log(ctx, AV_LOG_ERROR, "expr_int_format(): Invalid number of positions"
1389 " to print: '%s'\n", argv[2]);
1390 return AVERROR(EINVAL);
1391 }
1392 }
1393
1394 feclearexcept(FE_ALL_EXCEPT);
1395 intval = res;
1396#if defined(FE_INVALID) && defined(FE_OVERFLOW) && defined(FE_UNDERFLOW)
1397 if ((ret = fetestexcept(FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW))) {
1398 av_log(ctx, AV_LOG_ERROR, "Conversion of floating-point result to int failed. Control register: 0x%08x. Conversion result: %d\n", ret, intval);
1399 return AVERROR(EINVAL);
1400 }
1401#endif
1402
1403 if (argc == 3)
1404 av_strlcatf(fmt_str, sizeof(fmt_str), "0%u", positions);
1405 av_strlcatf(fmt_str, sizeof(fmt_str), "%c", argv[1][0]);
1406
1407 av_log(ctx, AV_LOG_DEBUG, "Formatting value %f (expr '%s') with spec '%s'\n",
1408 res, argv[0], fmt_str);
1409
1410 av_bprintf(bp, fmt_str, intval);
1411
1412 return 0;
1413}
1414
1415static const struct drawtext_function {
1416 const char *name;
1417 unsigned argc_min, argc_max;
1418 int tag; /**< opaque argument to func */
1419 int (*func)(AVFilterContext *, AVBPrint *, char *, unsigned, char **, int);
1420} functions[] = {
1421 { "expr", 1, 1, 0, func_eval_expr },
1422 { "e", 1, 1, 0, func_eval_expr },
1423 { "expr_int_format", 2, 3, 0, func_eval_expr_int_format },
1424 { "eif", 2, 3, 0, func_eval_expr_int_format },
1425 { "pict_type", 0, 0, 0, func_pict_type },
1426 { "pts", 0, 3, 0, func_pts },
1427 { "gmtime", 0, 1, 'G', func_strftime },
1428 { "localtime", 0, 1, 'L', func_strftime },
1429 { "frame_num", 0, 0, 0, func_frame_num },
1430 { "n", 0, 0, 0, func_frame_num },
1431 { "metadata", 1, 2, 0, func_metadata },
1432};
1433
1434static int eval_function(AVFilterContext *ctx, AVBPrint *bp, char *fct,
1435 unsigned argc, char **argv)
1436{
1437 unsigned i;
1438
1439 for (i = 0; i < FF_ARRAY_ELEMS(functions); i++) {
1440 if (strcmp(fct, functions[i].name))
1441 continue;
1442 if (argc < functions[i].argc_min) {
1443 av_log(ctx, AV_LOG_ERROR, "%%{%s} requires at least %d arguments\n",
1444 fct, functions[i].argc_min);
1445 return AVERROR(EINVAL);
1446 }
1447 if (argc > functions[i].argc_max) {
1448 av_log(ctx, AV_LOG_ERROR, "%%{%s} requires at most %d arguments\n",
1449 fct, functions[i].argc_max);
1450 return AVERROR(EINVAL);
1451 }
1452 break;
1453 }
1454 if (i >= FF_ARRAY_ELEMS(functions)) {
1455 av_log(ctx, AV_LOG_ERROR, "%%{%s} is not known\n", fct);
1456 return AVERROR(EINVAL);
1457 }
1458 return functions[i].func(ctx, bp, fct, argc, argv, functions[i].tag);
1459}
1460
1461static int expand_function(AVFilterContext *ctx, AVBPrint *bp, char **rtext)
1462{
1463 const char *text = *rtext;
1464 char *argv[16] = { NULL };
1465 unsigned argc = 0, i;
1466 int ret;
1467
1468 if (*text != '{') {
1469 av_log(ctx, AV_LOG_ERROR, "Stray %% near '%s'\n", text);
1470 return AVERROR(EINVAL);
1471 }
1472 text++;
1473 while (1) {
1474 if (!(argv[argc++] = av_get_token(&text, ":}"))) {
1475 ret = AVERROR(ENOMEM);
1476 goto end;
1477 }
1478 if (!*text) {
1479 av_log(ctx, AV_LOG_ERROR, "Unterminated %%{} near '%s'\n", *rtext);
1480 ret = AVERROR(EINVAL);
1481 goto end;
1482 }
1483 if (argc == FF_ARRAY_ELEMS(argv))
1484 av_freep(&argv[--argc]); /* error will be caught later */
1485 if (*text == '}')
1486 break;
1487 text++;
1488 }
1489
1490 if ((ret = eval_function(ctx, bp, argv[0], argc - 1, argv + 1)) < 0)
1491 goto end;
1492 ret = 0;
1493 *rtext = (char *)text + 1;
1494
1495end:
1496 for (i = 0; i < argc; i++)
1497 av_freep(&argv[i]);
1498 return ret;
1499}
1500
1501static int expand_text(AVFilterContext *ctx, char *text, AVBPrint *bp)
1502{
1503 int ret;
1504
1505 av_bprint_clear(bp);
1506 while (*text) {
1507 if (*text == '\\' && text[1]) {
1508 av_bprint_chars(bp, text[1], 1);
1509 text += 2;
1510 } else if (*text == '%') {
1511 text++;
1512 if ((ret = expand_function(ctx, bp, &text)) < 0)
1513 return ret;
1514 } else {
1515 av_bprint_chars(bp, *text, 1);
1516 text++;
1517 }
1518 }
1519 if (!av_bprint_is_complete(bp))
1520 return AVERROR(ENOMEM);
1521 return 0;
1522}
1523
1524static void update_color_with_alpha(DrawTextContext *s, FFDrawColor *color, const FFDrawColor incolor)1253static void update_color_with_alpha(DrawTextContext *s, FFDrawColor *color, const FFDrawColor incolor)
1525{1254{
1526 *color = incolor;1255 *color = incolor;
@@ -1688,7 +1417,7 @@ static int measure_text(AVFilterContext *ctx, TextMetrics *metrics) · 161 unmodified lines
1688 for (i = 0, p = text; 1; i++) {1417 for (i = 0, p = text; 1; i++) {
1689 GET_UTF8(code, *p ? *p++ : 0, code = 0xfffd; goto continue_on_failed;);1418 GET_UTF8(code, *p ? *p++ : 0, code = 0xfffd; goto continue_on_failed;);
1690continue_on_failed:1419continue_on_failed:
1691 if (is_newline(code) || code == 0) {1420 if (ff_is_newline(code) || code == 0) {
1692 ++line_count;1421 ++line_count;
1693 if (code == 0) {1422 if (code == 0) {
1694 break;1423 break;
@@ -1729,7 +1458,7 @@ continue_on_failed: · 34 unmodified lines
1729 }1458 }
1730 GET_UTF8(code, *p ? *p++ : 0, code = 0xfffd; goto continue_on_failed2;);1459 GET_UTF8(code, *p ? *p++ : 0, code = 0xfffd; goto continue_on_failed2;);
1731continue_on_failed2:1460continue_on_failed2:
1732 if (is_newline(code) || code == 0) {1461 if (ff_is_newline(code) || code == 0) {
1733 TextLine *cur_line = &s->lines[line_count];1462 TextLine *cur_line = &s->lines[line_count];
1734 HarfbuzzData *hb = &cur_line->hb_data;1463 HarfbuzzData *hb = &cur_line->hb_data;
1735 cur_line->cluster_offset = line_offset;1464 cur_line->cluster_offset = line_offset;
@@ -1861,7 +1590,7 @@ static int draw_text(AVFilterContext *ctx, AVFrame *frame) · 125 unmodified lines
1861 av_bprintf(bp, "%s", s->text);1590 av_bprintf(bp, "%s", s->text);
1862 break;1591 break;
1863 case EXP_NORMAL:1592 case EXP_NORMAL:
1864 if ((ret = expand_text(ctx, s->text, &s->expanded_text)) < 0)1593 if ((ret = ff_expand_text(&s->expand_text, s->text, &s->expanded_text)) < 0)
1865 return ret;1594 return ret;
1866 break;1595 break;
1867 case EXP_STRFTIME:1596 case EXP_STRFTIME:
@@ -1883,7 +1612,7 @@ static int draw_text(AVFilterContext *ctx, AVFrame *frame) · 15 unmodified lines
1883 if (s->fontcolor_expr[0]) {1612 if (s->fontcolor_expr[0]) {
1884 /* If expression is set, evaluate and replace the static value */1613 /* If expression is set, evaluate and replace the static value */
1885 av_bprint_clear(&s->expanded_fontcolor);1614 av_bprint_clear(&s->expanded_fontcolor);
1886 if ((ret = expand_text(ctx, s->fontcolor_expr, &s->expanded_fontcolor)) < 0)1615 if ((ret = ff_expand_text(&s->expand_text, s->fontcolor_expr, &s->expanded_fontcolor)) < 0)
1887 return ret;1616 return ret;
1888 if (!av_bprint_is_complete(&s->expanded_fontcolor))1617 if (!av_bprint_is_complete(&s->expanded_fontcolor))
1889 return AVERROR(ENOMEM);1618 return AVERROR(ENOMEM);
@@ -2125,7 +1854,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) · 235 unmodified lines
2125 }1854 }
2126 1855
2127 if (s->reload && !(inlink->frame_count_out % s->reload)) {1856 if (s->reload && !(inlink->frame_count_out % s->reload)) {
2128 if ((ret = load_textfile(ctx)) < 0) {1857 if ((ret = ff_load_textfile(ctx, (const char *)s->textfile, &s->text, NULL)) < 0) {
2129 av_frame_free(&frame);1858 av_frame_free(&frame);
2130 return ret;1859 return ret;
2131 }1860 }

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t