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
291291 OBJS-$(CONFIG_DRAWBOX_FILTER) += vf_drawbox.o
292292 OBJS-$(CONFIG_DRAWGRAPH_FILTER) += f_drawgraph.o
293293 OBJS-$(CONFIG_DRAWGRID_FILTER) += vf_drawbox.o
294−OBJS-$(CONFIG_DRAWTEXT_FILTER) += vf_drawtext.o
294+OBJS-$(CONFIG_DRAWTEXT_FILTER) += vf_drawtext.o textutils.o
295295 OBJS-$(CONFIG_EDGEDETECT_FILTER) += vf_edgedetect.o edge_common.o
296296 OBJS-$(CONFIG_ELBG_FILTER) += vf_elbg.o
297297 OBJS-$(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+
34+static 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+ */
81+static 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+
116+end:
117+ for (i = 0; i < argc; i++)
118+ av_freep(&argv[i]);
119+ return ret;
120+}
121+
122+int 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+
148+int 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+
201+int 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+
280+int 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+
302+int 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+
352+int 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+ */
36+typedef 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+ */
66+typedef 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+ */
97+int 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+ */
120+int 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+ */
138+int ff_print_time(void *log_ctx, AVBPrint *bp, const char *strftime_fmt, char localtime);
139+
140+typedef 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+ */
161+int 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+ */
196+int 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+ */
209+static 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+ */
226+int 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
4747 #include "libavutil/avstring.h"
4848 #include "libavutil/bprint.h"
4949 #include "libavutil/common.h"
50−#include "libavutil/file.h"
5150 #include "libavutil/eval.h"
5251 #include "libavutil/opt.h"
5352 #include "libavutil/random_seed.h"
@@ -62,6 +61,7 @@ · 8 unmodified lines
6261 #include "drawutils.h"
6362 #include "formats.h"
6463 #include "internal.h"
64+#include "textutils.h"
6565 #include "video.h"
6666
6767 #if CONFIG_LIBFRIBIDI
@@ -253,6 +253,7 @@ typedef struct TextMetrics { · 185 unmodified lines
253253 typedef struct DrawTextContext {
254254 const AVClass *class;
255255 int exp_mode; ///< expansion mode to use for the text
256+ FFExpandTextContext expand_text; ///< expand text in case exp_mode == NORMAL
256257 int reinit; ///< tells if the filter is being reinited
257258 #if CONFIG_LIBFONTCONFIG
258259 uint8_t *font; ///< font to be used
@@ -631,40 +632,6 @@ static int load_font(AVFilterContext *ctx) · 372 unmodified lines
631632 return err;
632633 }
633634
634−static inline int is_newline(uint32_t c)
635−{
636− return c == '\n' || c == '\r' || c == '\f' || c == '\v';
637−}
638−
639−static 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−
668635 #if CONFIG_LIBFRIBIDI
669636 static int shape_text(AVFilterContext *ctx)
670637 {
@@ -885,6 +852,123 @@ static int string_to_array(const char *source, int *result, int result_size) · 214 unmodified lines
885852 return counter;
886853 }
887854
855+static 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+
863+static 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+
894+static 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+
902+static 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+
914+static 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+
921+static 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+
930+static 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+
958+static 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+
888972 static av_cold int init(AVFilterContext *ctx)
889973 {
890974 int err;
@@ -907,7 +991,7 @@ static av_cold int init(AVFilterContext *ctx) · 16 unmodified lines
907991 "Both text and text file provided. Please provide only one\n");
908992 return AVERROR(EINVAL);
909993 }
910− if ((err = load_textfile(ctx)) < 0)
994+ if ((err = ff_load_textfile(ctx, (const char *)s->textfile, &s->text, NULL)) < 0)
911995 return err;
912996 }
913997
@@ -950,6 +1034,12 @@ static av_cold int init(AVFilterContext *ctx) · 36 unmodified lines
9501034 return AVERROR(EINVAL);
9511035 }
9521036
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+
9531043 #if CONFIG_LIBFRIBIDI
9541044 if (s->text_shaping)
9551045 if ((err = shape_text(ctx)) < 0)
@@ -1160,367 +1250,6 @@ fail: · 204 unmodified lines
11601250 return ret;
11611251 }
11621252
1163−static 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−
1172−static 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−
1232−static 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−
1241−static 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−
1254−static 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−
1333−static 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−
1353−static 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−
1415−static 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−
1434−static 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−
1461−static 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−
1495−end:
1496− for (i = 0; i < argc; i++)
1497− av_freep(&argv[i]);
1498− return ret;
1499−}
1500−
1501−static 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−
15241253 static void update_color_with_alpha(DrawTextContext *s, FFDrawColor *color, const FFDrawColor incolor)
15251254 {
15261255 *color = incolor;
@@ -1688,7 +1417,7 @@ static int measure_text(AVFilterContext *ctx, TextMetrics *metrics) · 161 unmodified lines
16881417 for (i = 0, p = text; 1; i++) {
16891418 GET_UTF8(code, *p ? *p++ : 0, code = 0xfffd; goto continue_on_failed;);
16901419 continue_on_failed:
1691− if (is_newline(code) || code == 0) {
1420+ if (ff_is_newline(code) || code == 0) {
16921421 ++line_count;
16931422 if (code == 0) {
16941423 break;
@@ -1729,7 +1458,7 @@ continue_on_failed: · 34 unmodified lines
17291458 }
17301459 GET_UTF8(code, *p ? *p++ : 0, code = 0xfffd; goto continue_on_failed2;);
17311460 continue_on_failed2:
1732− if (is_newline(code) || code == 0) {
1461+ if (ff_is_newline(code) || code == 0) {
17331462 TextLine *cur_line = &s->lines[line_count];
17341463 HarfbuzzData *hb = &cur_line->hb_data;
17351464 cur_line->cluster_offset = line_offset;
@@ -1861,7 +1590,7 @@ static int draw_text(AVFilterContext *ctx, AVFrame *frame) · 125 unmodified lines
18611590 av_bprintf(bp, "%s", s->text);
18621591 break;
18631592 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)
18651594 return ret;
18661595 break;
18671596 case EXP_STRFTIME:
@@ -1883,7 +1612,7 @@ static int draw_text(AVFilterContext *ctx, AVFrame *frame) · 15 unmodified lines
18831612 if (s->fontcolor_expr[0]) {
18841613 /* If expression is set, evaluate and replace the static value */
18851614 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)
18871616 return ret;
18881617 if (!av_bprint_is_complete(&s->expanded_fontcolor))
18891618 return AVERROR(ENOMEM);
@@ -2125,7 +1854,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) · 235 unmodified lines
21251854 }
21261855
21271856 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) {
21291858 av_frame_free(&frame);
21301859 return ret;
21311860 }

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