History

lavfi: add qrencode source and filter

Stefano Sabatini committed 2 years ago

Commit 899302b1 parent 732fb126 files · +1259 −0Browse files

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

Unified · Split

3 files
  1. libavfilter/Makefile +2 −0
  2. libavfilter/allfilters.c +2 −0
  3. libavfilter/qrencode.c +820 −0
libavfilter/Makefile+2−0
@@ -599,6 +599,8 @@ OBJS-$(CONFIG_NULLSRC_FILTER) += vsrc_testsrc.o · 598 unmodified lines
599OBJS-$(CONFIG_OPENCLSRC_FILTER) += vf_program_opencl.o opencl.o599OBJS-$(CONFIG_OPENCLSRC_FILTER) += vf_program_opencl.o opencl.o
600OBJS-$(CONFIG_PAL75BARS_FILTER) += vsrc_testsrc.o600OBJS-$(CONFIG_PAL75BARS_FILTER) += vsrc_testsrc.o
601OBJS-$(CONFIG_PAL100BARS_FILTER) += vsrc_testsrc.o601OBJS-$(CONFIG_PAL100BARS_FILTER) += vsrc_testsrc.o
602OBJS-$(CONFIG_QRENCODE_FILTER) += qrencode.o textutils.o
603OBJS-$(CONFIG_QRENCODESRC_FILTER) += qrencode.o textutils.o
602OBJS-$(CONFIG_RGBTESTSRC_FILTER) += vsrc_testsrc.o604OBJS-$(CONFIG_RGBTESTSRC_FILTER) += vsrc_testsrc.o
603OBJS-$(CONFIG_SIERPINSKI_FILTER) += vsrc_sierpinski.o605OBJS-$(CONFIG_SIERPINSKI_FILTER) += vsrc_sierpinski.o
604OBJS-$(CONFIG_SMPTEBARS_FILTER) += vsrc_testsrc.o606OBJS-$(CONFIG_SMPTEBARS_FILTER) += vsrc_testsrc.o
libavfilter/allfilters.c+2−0
@@ -411,6 +411,7 @@ extern const AVFilter ff_vf_pseudocolor; · 410 unmodified lines
411extern const AVFilter ff_vf_psnr;411extern const AVFilter ff_vf_psnr;
412extern const AVFilter ff_vf_pullup;412extern const AVFilter ff_vf_pullup;
413extern const AVFilter ff_vf_qp;413extern const AVFilter ff_vf_qp;
414extern const AVFilter ff_vf_qrencode;
414extern const AVFilter ff_vf_random;415extern const AVFilter ff_vf_random;
415extern const AVFilter ff_vf_readeia608;416extern const AVFilter ff_vf_readeia608;
416extern const AVFilter ff_vf_readvitc;417extern const AVFilter ff_vf_readvitc;
@@ -561,6 +562,7 @@ extern const AVFilter ff_vsrc_mandelbrot; · 144 unmodified lines
561extern const AVFilter ff_vsrc_mptestsrc;562extern const AVFilter ff_vsrc_mptestsrc;
562extern const AVFilter ff_vsrc_nullsrc;563extern const AVFilter ff_vsrc_nullsrc;
563extern const AVFilter ff_vsrc_openclsrc;564extern const AVFilter ff_vsrc_openclsrc;
565extern const AVFilter ff_vsrc_qrencodesrc;
564extern const AVFilter ff_vsrc_pal75bars;566extern const AVFilter ff_vsrc_pal75bars;
565extern const AVFilter ff_vsrc_pal100bars;567extern const AVFilter ff_vsrc_pal100bars;
566extern const AVFilter ff_vsrc_rgbtestsrc;568extern const AVFilter ff_vsrc_rgbtestsrc;
libavfilter/qrencode.c+820−0
@@ -0,0 +1,820 @@
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 QR encoder source and filter.
21 *
22 * A QR code (quick-response code) is a type of two-dimensional matrix
23 * barcode, invented in 1994, by Japanese company Denso Wave for
24 * labelling automobile parts.
25 *
26 * This source uses the libqrencode library to generate QR code:
27 * https://fukuchi.org/works/qrencode/
28 */
29
30//#define DEBUG
31
32#include "config_components.h"
33
34#include "libavutil/internal.h"
35#include "libavutil/imgutils.h"
36#include "libavutil/opt.h"
37#include "libavutil/lfg.h"
38#include "libavutil/random_seed.h"
39
40#include "avfilter.h"
41#include "drawutils.h"
42#include "internal.h"
43#include "formats.h"
44#include "textutils.h"
45#include "video.h"
46#include "libswscale/swscale.h"
47
48#include <qrencode.h>
49
50enum var_name {
51 VAR_dar,
52 VAR_duration,
53 VAR_hsub, VAR_vsub,
54 VAR_main_h, VAR_H,
55 VAR_main_w, VAR_W,
56 VAR_n,
57 VAR_pict_type,
58 VAR_qr_w, VAR_w,
59 VAR_rendered_padded_qr_w, VAR_Q,
60 VAR_rendered_qr_w, VAR_q,
61 VAR_sar,
62 VAR_t,
63 VAR_x,
64 VAR_y,
65 VAR_VARS_NB
66};
67
68static const char *const var_names[] = {
69 "dar",
70 "duration",
71 "hsub", "vsub",
72 "main_h", "H", ///< height of the input video
73 "main_w", "W", ///< width of the input video
74 "n", ///< number of frame
75 "pict_type",
76 "qr_w", "w", ///< width of the QR code
77 "rendered_padded_qr_w", "Q", ///< width of the rendered QR code
78 "rendered_qr_w", "q", ///< width of the rendered QR code
79 "sar",
80 "t", ///< timestamp expressed in seconds
81 "x",
82 "y",
83 NULL
84};
85
86#define V(name_) qr->var_values[VAR_##name_]
87
88enum Expansion {
89 EXPANSION_NONE,
90 EXPANSION_NORMAL
91};
92
93typedef struct QREncodeContext {
94 const AVClass *class;
95
96 char is_source;
97 char *x_expr;
98 char *y_expr;
99 AVExpr *x_pexpr, *y_pexpr;
100
101 char *rendered_qrcode_width_expr;
102 char *rendered_padded_qrcode_width_expr;
103 AVExpr *rendered_qrcode_width_pexpr, *rendered_padded_qrcode_width_pexpr;
104
105 int rendered_qrcode_width;
106 int rendered_padded_qrcode_width;
107
108 unsigned char *text;
109 char *textfile;
110 uint64_t pts;
111
112 int level;
113 char case_sensitive;
114
115 uint8_t foreground_color[4];
116 uint8_t background_color[4];
117
118 FFDrawContext draw;
119 FFDrawColor draw_foreground_color; ///< foreground color
120 FFDrawColor draw_background_color; ///< background color
121
122 /* these are only used when nothing must be encoded */
123 FFDrawContext draw0;
124 FFDrawColor draw0_background_color; ///< background color
125
126 uint8_t *qrcode_data[4];
127 int qrcode_linesize[4];
128 uint8_t *qrcode_mask_data[4];
129 int qrcode_mask_linesize[4];
130
131 /* only used for filter to contain scaled image to blend on top of input */
132 uint8_t *rendered_qrcode_data[4];
133 int rendered_qrcode_linesize[4];
134
135 int qrcode_width;
136 int padded_qrcode_width;
137
138 AVRational frame_rate;
139
140 int expansion; ///< expansion mode to use for the text
141 FFExpandTextContext expand_text; ///< expand text in case expansion is enabled
142 AVBPrint expanded_text; ///< used to contain the expanded text
143
144 double var_values[VAR_VARS_NB];
145 AVLFG lfg; ///< random generator
146 AVDictionary *metadata;
147} QREncodeContext;
148
149#define OFFSET(x) offsetof(QREncodeContext, x)
150#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
151#define TFLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM
152
153#define COMMON_OPTIONS \
154 { "qrcode_width", "set rendered QR code width expression", OFFSET(rendered_qrcode_width_expr), AV_OPT_TYPE_STRING, {.str = "64"}, 0, INT_MAX, FLAGS }, \
155 { "q", "set rendered QR code width expression", OFFSET(rendered_qrcode_width_expr), AV_OPT_TYPE_STRING, {.str = "64"}, 0, INT_MAX, FLAGS }, \
156 { "padded_qrcode_width", "set rendered padded QR code width expression", OFFSET(rendered_padded_qrcode_width_expr), AV_OPT_TYPE_STRING, {.str = "q"}, 0, INT_MAX, FLAGS }, \
157 { "Q", "set rendered padded QR code width expression", OFFSET(rendered_padded_qrcode_width_expr), AV_OPT_TYPE_STRING, {.str = "q"}, 0, INT_MAX, FLAGS }, \
158 { "case_sensitive", "generate code which is case sensitive", OFFSET(case_sensitive), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, FLAGS }, \
159 { "cs", "generate code which is case sensitive", OFFSET(case_sensitive), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, FLAGS }, \
160 \
161 { "level", "error correction level, lowest is L", OFFSET(level), AV_OPT_TYPE_INT, { .i64 = AVCOL_SPC_UNSPECIFIED }, 0, QR_ECLEVEL_H, .flags = FLAGS, "level"}, \
162 { "l", "error correction level, lowest is L", OFFSET(level), AV_OPT_TYPE_INT, { .i64 = AVCOL_SPC_UNSPECIFIED }, 0, QR_ECLEVEL_H, .flags = FLAGS, "level"}, \
163 { "L", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = QR_ECLEVEL_L }, 0, 0, FLAGS, "level" }, \
164 { "M", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = QR_ECLEVEL_M }, 0, 0, FLAGS, "level" }, \
165 { "Q", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = QR_ECLEVEL_Q }, 0, 0, FLAGS, "level" }, \
166 { "H", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = QR_ECLEVEL_H }, 0, 0, FLAGS, "level" }, \
167 \
168 {"expansion", "set the expansion mode", OFFSET(expansion), AV_OPT_TYPE_INT, {.i64=EXPANSION_NORMAL}, 0, 2, FLAGS, "expansion"}, \
169 {"none", "set no expansion", OFFSET(expansion), AV_OPT_TYPE_CONST, {.i64 = EXPANSION_NONE}, 0, 0, FLAGS, "expansion"}, \
170 {"normal", "set normal expansion", OFFSET(expansion), AV_OPT_TYPE_CONST, {.i64 = EXPANSION_NORMAL}, 0, 0, FLAGS, "expansion"}, \
171 \
172 { "foreground_color", "set QR foreground color", OFFSET(foreground_color), AV_OPT_TYPE_COLOR, {.str = "black"}, 0, 0, FLAGS }, \
173 { "fc", "set QR foreground color", OFFSET(foreground_color), AV_OPT_TYPE_COLOR, {.str = "black"}, 0, 0, FLAGS }, \
174 { "background_color", "set QR background color", OFFSET(background_color), AV_OPT_TYPE_COLOR, {.str = "white"}, 0, 0, FLAGS }, \
175 { "bc", "set QR background color", OFFSET(background_color), AV_OPT_TYPE_COLOR, {.str = "white"}, 0, 0, FLAGS }, \
176 \
177 {"text", "set text to encode", OFFSET(text), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS}, \
178 {"textfile", "set text file to encode", OFFSET(textfile), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS}, \
179
180static const char *const fun2_names[] = {
181 "rand"
182};
183
184static double drand(void *opaque, double min, double max)
185{
186 return min + (max-min) / UINT_MAX * av_lfg_get(opaque);
187}
188
189static const ff_eval_func2 fun2[] = {
190 drand,
191 NULL
192};
193
194static int func_pts(void *ctx, AVBPrint *bp, const char *function_name,
195 unsigned argc, char **argv)
196{
197 QREncodeContext *qr = ((AVFilterContext *)ctx)->priv;
198 const char *fmt;
199 const char *strftime_fmt = NULL;
200 const char *delta = NULL;
201 double t = qr->var_values[VAR_t];
202
203 // argv: pts, FMT, [DELTA, strftime_fmt]
204
205 fmt = argc >= 1 ? argv[0] : "flt";
206 if (argc >= 2) {
207 delta = argv[1];
208 }
209 if (argc >= 3) {
210 strftime_fmt = argv[2];
211 }
212
213 return ff_print_pts(ctx, bp, t, delta, fmt, strftime_fmt);
214}
215
216static int func_frame_num(void *ctx, AVBPrint *bp, const char *function_name,
217 unsigned argc, char **argv)
218{
219 QREncodeContext *qr = ((AVFilterContext *)ctx)->priv;
220
221 av_bprintf(bp, "%d", (int)V(n));
222 return 0;
223}
224
225static int func_strftime(void *ctx, AVBPrint *bp, const char *function_name,
226 unsigned argc, char **argv)
227{
228 const char *strftime_fmt = argc ? argv[0] : NULL;
229
230 return ff_print_time(ctx, bp, strftime_fmt, !strcmp(function_name, "localtime"));
231}
232
233static int func_frame_metadata(void *ctx, AVBPrint *bp, const char *function_name,
234 unsigned argc, char **argv)
235{
236 QREncodeContext *qr = ((AVFilterContext *)ctx)->priv;
237 AVDictionaryEntry *e = av_dict_get(qr->metadata, argv[0], NULL, 0);
238
239 if (e && e->value)
240 av_bprintf(bp, "%s", e->value);
241 else if (argc >= 2)
242 av_bprintf(bp, "%s", argv[1]);
243
244 return 0;
245}
246
247static int func_eval_expr(void *ctx, AVBPrint *bp, const char *function_name,
248 unsigned argc, char **argv)
249{
250 QREncodeContext *qr = ((AVFilterContext *)ctx)->priv;
251
252 return ff_print_eval_expr(ctx, bp, argv[0],
253 fun2_names, fun2,
254 var_names, qr->var_values, &qr->lfg);
255}
256
257static int func_eval_expr_formatted(void *ctx, AVBPrint *bp, const char *function_name,
258 unsigned argc, char **argv)
259{
260 QREncodeContext *qr = ((AVFilterContext *)ctx)->priv;
261 int ret;
262 int positions = -1;
263
264 /*
265 * argv[0] expression to be converted to `int`
266 * argv[1] format: 'x', 'X', 'd' or 'u'
267 * argv[2] positions printed (optional)
268 */
269
270 if (argc == 3) {
271 ret = sscanf(argv[2], "%u", &positions);
272 if (ret != 1) {
273 av_log(ctx, AV_LOG_ERROR, "expr_int_format(): Invalid number of positions"
274 " to print: '%s'\n", argv[2]);
275 return AVERROR(EINVAL);
276 }
277 }
278
279 return ff_print_formatted_eval_expr(ctx, bp, argv[0],
280 fun2_names, fun2,
281 var_names, qr->var_values,
282 &qr->lfg,
283 argv[1][0], positions);
284}
285
286static FFExpandTextFunction expand_text_functions[] = {
287 { "expr", 1, 1, func_eval_expr },
288 { "e", 1, 1, func_eval_expr },
289 { "expr_formatted", 2, 3, func_eval_expr_formatted },
290 { "ef", 2, 3, func_eval_expr_formatted },
291 { "metadata", 1, 2, func_frame_metadata },
292 { "frame_num", 0, 0, func_frame_num },
293 { "n", 0, 0, func_frame_num },
294 { "gmtime", 0, 1, func_strftime },
295 { "localtime", 0, 1, func_strftime },
296 { "pts", 0, 3, func_pts }
297};
298
299static av_cold int init(AVFilterContext *ctx)
300{
301 QREncodeContext *qr = ctx->priv;
302 int ret;
303
304 av_lfg_init(&qr->lfg, av_get_random_seed());
305
306 qr->qrcode_width = -1;
307 qr->rendered_padded_qrcode_width = -1;
308
309 if (qr->textfile) {
310 if (qr->text) {
311 av_log(ctx, AV_LOG_ERROR,
312 "Both text and text file provided. Please provide only one\n");
313 return AVERROR(EINVAL);
314 }
315 if ((ret = ff_load_textfile(ctx, (const char *)qr->textfile, &(qr->text), NULL)) < 0)
316 return ret;
317 }
318
319 qr->expand_text = (FFExpandTextContext) {
320 .log_ctx = ctx,
321 .functions = expand_text_functions,
322 .functions_nb = FF_ARRAY_ELEMS(expand_text_functions)
323 };
324
325 av_bprint_init(&qr->expanded_text, 0, AV_BPRINT_SIZE_UNLIMITED);
326
327 return 0;
328}
329
330static av_cold void uninit(AVFilterContext *ctx)
331{
332 QREncodeContext *qr = ctx->priv;
333
334 av_expr_free(qr->x_pexpr);
335 av_expr_free(qr->y_pexpr);
336
337 av_bprint_finalize(&qr->expanded_text, NULL);
338
339 av_freep(&qr->qrcode_data[0]);
340 av_freep(&qr->rendered_qrcode_data[0]);
341 av_freep(&qr->qrcode_mask_data[0]);
342}
343
344#ifdef DEBUG
345static void show_qrcode(AVFilterContext *ctx, const QRcode *qrcode)
346{
347 int i, j;
348 char *line = av_malloc(qrcode->width + 1);
349 const char *p = qrcode->data;
350
351 if (!line)
352 return;
353 for (i = 0; i < qrcode->width; i++) {
354 for (j = 0; j < qrcode->width; j++)
355 line[j] = (*p++)&1 ? '@' : ' ';
356 line[j] = 0;
357 av_log(ctx, AV_LOG_DEBUG, "%3d: %s\n", i, line);
358 }
359 av_free(line);
360}
361#endif
362
363static int draw_qrcode(AVFilterContext *ctx, AVFrame *frame)
364{
365 QREncodeContext *qr = ctx->priv;
366 struct SwsContext *sws = NULL;
367 QRcode *qrcode = NULL;
368 int i, j;
369 char qrcode_width_changed;
370 int ret;
371 int offset;
372 uint8_t *srcp;
373 uint8_t *dstp0, *dstp;
374
375 av_bprint_clear(&qr->expanded_text);
376
377 switch (qr->expansion) {
378 case EXPANSION_NONE:
379 av_bprintf(&qr->expanded_text, "%s", qr->text);
380 break;
381 case EXPANSION_NORMAL:
382 if ((ret = ff_expand_text(&qr->expand_text, qr->text, &qr->expanded_text)) < 0)
383 return ret;
384 break;
385 }
386
387 if (!qr->expanded_text.str || qr->expanded_text.str[0] == 0) {
388 if (qr->is_source) {
389 ff_fill_rectangle(&qr->draw0, &qr->draw0_background_color,
390 frame->data, frame->linesize,
391 0, 0, qr->rendered_padded_qrcode_width, qr->rendered_padded_qrcode_width);
392 }
393
394 return 0;
395 }
396
397 av_log(ctx, AV_LOG_DEBUG, "Encoding string '%s'\n", qr->expanded_text.str);
398 qrcode = QRcode_encodeString(qr->expanded_text.str, 1, qr->level, QR_MODE_8,
399 qr->case_sensitive);
400 if (!qrcode) {
401 ret = AVERROR(errno);
402 av_log(ctx, AV_LOG_ERROR,
403 "Failed to encode string with error \'%s\'\n", av_err2str(ret));
404 goto end;
405 }
406
407 av_log(ctx, AV_LOG_DEBUG,
408 "Encoded QR with width:%d version:%d\n", qrcode->width, qrcode->version);
409#ifdef DEBUG
410 show_qrcode(ctx, (const QRcode *)qrcode);
411#endif
412
413 qrcode_width_changed = qr->qrcode_width != qrcode->width;
414 qr->qrcode_width = qrcode->width;
415
416 // realloc mask if needed
417 if (qrcode_width_changed) {
418 av_freep(&qr->qrcode_mask_data[0]);
419 ret = av_image_alloc(qr->qrcode_mask_data, qr->qrcode_mask_linesize,
420 qrcode->width, qrcode->width,
421 AV_PIX_FMT_GRAY8, 16);
422 if (ret < 0) {
423 av_log(ctx, AV_LOG_ERROR,
424 "Failed to allocate image for QR code with width %d\n", qrcode->width);
425 goto end;
426 }
427 }
428
429 /* fill mask */
430 dstp0 = qr->qrcode_mask_data[0];
431 srcp = qrcode->data;
432
433 for (i = 0; i < qrcode->width; i++) {
434 dstp = dstp0;
435 for (j = 0; j < qrcode->width; j++)
436 *dstp++ = (*srcp++ & 1) ? 255 : 0;
437 dstp0 += qr->qrcode_mask_linesize[0];
438 }
439
440 if (qr->is_source) {
441 if (qrcode_width_changed) {
442 /* realloc padded image */
443
444 // compute virtual non-rendered padded size
445 // Q/q = W/w
446 qr->padded_qrcode_width =
447 ((double)qr->rendered_padded_qrcode_width / qr->rendered_qrcode_width) * qrcode->width;
448
449 av_freep(&qr->qrcode_data[0]);
450 ret = av_image_alloc(qr->qrcode_data, qr->qrcode_linesize,
451 qr->padded_qrcode_width, qr->padded_qrcode_width,
452 AV_PIX_FMT_ARGB, 16);
453 if (ret < 0) {
454 av_log(ctx, AV_LOG_ERROR,
455 "Failed to allocate image for QR code with width %d\n",
456 qr->padded_qrcode_width);
457 goto end;
458 }
459 }
460
461 /* fill padding */
462 ff_fill_rectangle(&qr->draw, &qr->draw_background_color,
463 qr->qrcode_data, qr->qrcode_linesize,
464 0, 0, qr->padded_qrcode_width, qr->padded_qrcode_width);
465
466 /* blend mask */
467 offset = (qr->padded_qrcode_width - qr->qrcode_width) / 2;
468 ff_blend_mask(&qr->draw, &qr->draw_foreground_color,
469 qr->qrcode_data, qr->qrcode_linesize,
470 qr->padded_qrcode_width, qr->padded_qrcode_width,
471 qr->qrcode_mask_data[0], qr->qrcode_mask_linesize[0], qrcode->width, qrcode->width,
472 3, 0, offset, offset);
473
474 /* scale padded QR over the frame */
475 sws = sws_alloc_context();
476 if (!sws) {
477 ret = AVERROR(ENOMEM);
478 goto end;
479 }
480
481 av_opt_set_int(sws, "srcw", qr->padded_qrcode_width, 0);
482 av_opt_set_int(sws, "srch", qr->padded_qrcode_width, 0);
483 av_opt_set_int(sws, "src_format", AV_PIX_FMT_ARGB, 0);
484 av_opt_set_int(sws, "dstw", qr->rendered_padded_qrcode_width, 0);
485 av_opt_set_int(sws, "dsth", qr->rendered_padded_qrcode_width, 0);
486 av_opt_set_int(sws, "dst_format", frame->format, 0);
487 av_opt_set_int(sws, "sws_flags", SWS_POINT, 0);
488
489 if ((ret = sws_init_context(sws, NULL, NULL)) < 0)
490 goto end;
491
492 sws_scale(sws,
493 (const uint8_t *const *)&qr->qrcode_data, qr->qrcode_linesize,
494 0, qr->padded_qrcode_width,
495 frame->data, frame->linesize);
496 } else {
497#define EVAL_EXPR(name_) \
498 av_expr_eval(qr->name_##_pexpr, qr->var_values, &qr->lfg);
499
500 V(qr_w) = V(w) = qrcode->width;
501
502 V(rendered_qr_w) = V(q) = EVAL_EXPR(rendered_qrcode_width);
503 V(rendered_padded_qr_w) = V(Q) = EVAL_EXPR(rendered_padded_qrcode_width);
504 /* It is necessary if q is expressed from Q */
505 V(rendered_qr_w) = V(q) = EVAL_EXPR(rendered_qrcode_width);
506
507 V(x) = EVAL_EXPR(x);
508 V(y) = EVAL_EXPR(y);
509 /* It is necessary if x is expressed from y */
510 V(x) = EVAL_EXPR(x);
511
512 av_log(ctx, AV_LOG_DEBUG,
513 "Rendering QR code with values n:%d w:%d q:%d Q:%d x:%d y:%d t:%f\n",
514 (int)V(n), (int)V(w), (int)V(q), (int)V(Q), (int)V(x), (int)V(y), V(t));
515
516 /* blend rectangle over the target */
517 ff_blend_rectangle(&qr->draw, &qr->draw_background_color,
518 frame->data, frame->linesize, frame->width, frame->height,
519 V(x), V(y), V(Q), V(Q));
520
521 if (V(q) != qr->rendered_qrcode_width) {
522 av_freep(&qr->rendered_qrcode_data[0]);
523 qr->rendered_qrcode_width = V(q);
524
525 ret = av_image_alloc(qr->rendered_qrcode_data, qr->rendered_qrcode_linesize,
526 qr->rendered_qrcode_width, qr->rendered_qrcode_width,
527 AV_PIX_FMT_GRAY8, 16);
528 if (ret < 0) {
529 av_log(ctx, AV_LOG_ERROR,
530 "Failed to allocate image for rendered QR code with width %d\n",
531 qr->rendered_qrcode_width);
532 goto end;
533 }
534 }
535
536 /* scale mask */
537 sws = sws_alloc_context();
538 if (!sws) {
539 ret = AVERROR(ENOMEM);
540 goto end;
541 }
542
543 av_opt_set_int(sws, "srcw", qr->qrcode_width, 0);
544 av_opt_set_int(sws, "srch", qr->qrcode_width, 0);
545 av_opt_set_int(sws, "src_format", AV_PIX_FMT_GRAY8, 0);
546 av_opt_set_int(sws, "dstw", qr->rendered_qrcode_width, 0);
547 av_opt_set_int(sws, "dsth", qr->rendered_qrcode_width, 0);
548 av_opt_set_int(sws, "dst_format", AV_PIX_FMT_GRAY8, 0);
549 av_opt_set_int(sws, "sws_flags", SWS_POINT, 0);
550
551 if ((ret = sws_init_context(sws, NULL, NULL)) < 0)
552 goto end;
553
554 sws_scale(sws,
555 (const uint8_t *const *)&qr->qrcode_mask_data, qr->qrcode_mask_linesize,
556 0, qr->qrcode_width,
557 qr->rendered_qrcode_data, qr->rendered_qrcode_linesize);
558
559 /* blend mask over the input frame */
560 offset = (V(Q) - V(q)) / 2;
561 ff_blend_mask(&qr->draw, &qr->draw_foreground_color,
562 frame->data, frame->linesize, frame->width, frame->height,
563 qr->rendered_qrcode_data[0], qr->rendered_qrcode_linesize[0],
564 qr->rendered_qrcode_width, qr->rendered_qrcode_width,
565 3, 0, V(x) + offset, V(y) + offset);
566 }
567
568end:
569 sws_freeContext(sws);
570 QRcode_free(qrcode);
571
572 return ret;
573}
574
575#if CONFIG_QRENCODESRC_FILTER
576
577static const AVOption qrencodesrc_options[] = {
578 COMMON_OPTIONS
579 { "rate", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, INT_MAX, FLAGS },
580 { "r", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, INT_MAX, FLAGS },
581 { NULL }
582};
583
584AVFILTER_DEFINE_CLASS(qrencodesrc);
585
586static int qrencodesrc_config_props(AVFilterLink *outlink)
587{
588 AVFilterContext *ctx = outlink->src;
589 QREncodeContext *qr = ctx->priv;
590 int ret;
591
592 qr->is_source = 1;
593 V(x) = V(y) = 0;
594
595#define PARSE_AND_EVAL_EXPR(var_name_, expr_name_) \
596 ret = av_expr_parse_and_eval(&qr->var_values[VAR_##var_name_], \
597 qr->expr_name_##_expr, \
598 var_names, qr->var_values, \
599 NULL, NULL, \
600 fun2_names, fun2, \
601 &qr->lfg, 0, ctx); \
602 if (ret < 0) { \
603 av_log(ctx, AV_LOG_ERROR, \
604 "Could not evaluate expression '%s'\n", \
605 qr->expr_name_##_expr); \
606 return ret; \
607 }
608
609 /* undefined for the source */
610 V(main_w) = V(W) = NAN;
611 V(main_h) = V(H) = NAN;
612 V(x) = V(y) = V(t) = V(n) = NAN;
613 V(dar) = V(sar) = 1.0;
614
615 PARSE_AND_EVAL_EXPR(rendered_qr_w, rendered_qrcode_width);
616 V(q) = V(rendered_qr_w);
617 PARSE_AND_EVAL_EXPR(rendered_padded_qr_w, rendered_padded_qrcode_width);
618 V(Q) = V(rendered_padded_qr_w);
619 PARSE_AND_EVAL_EXPR(rendered_qr_w, rendered_qrcode_width);
620 V(q) = V(rendered_qr_w);
621
622 qr->rendered_qrcode_width = V(rendered_qr_w);
623 qr->rendered_padded_qrcode_width = V(rendered_padded_qr_w);
624
625 av_log(ctx, AV_LOG_VERBOSE,
626 "q:%d Q:%d case_sensitive:%d level:%d\n",
627 (int)qr->rendered_qrcode_width, (int)qr->rendered_padded_qrcode_width,
628 qr->case_sensitive, qr->level);
629
630 if (qr->rendered_padded_qrcode_width < qr->rendered_qrcode_width) {
631 av_log(ctx, AV_LOG_ERROR,
632 "Resulting padded QR code width (%d) is lesser than the QR code width (%d)\n",
633 qr->rendered_padded_qrcode_width, qr->rendered_qrcode_width);
634 return AVERROR(EINVAL);
635 }
636
637 ff_draw_init(&qr->draw, AV_PIX_FMT_ARGB, FF_DRAW_PROCESS_ALPHA);
638 ff_draw_color(&qr->draw, &qr->draw_foreground_color, (const uint8_t *)&qr->foreground_color);
639 ff_draw_color(&qr->draw, &qr->draw_background_color, (const uint8_t *)&qr->background_color);
640
641 ff_draw_init(&qr->draw0, outlink->format, FF_DRAW_PROCESS_ALPHA);
642 ff_draw_color(&qr->draw0, &qr->draw0_background_color, (const uint8_t *)&qr->background_color);
643
644 outlink->w = qr->rendered_padded_qrcode_width;
645 outlink->h = qr->rendered_padded_qrcode_width;
646 outlink->time_base = av_inv_q(qr->frame_rate);
647 outlink->frame_rate = qr->frame_rate;
648
649 return 0;
650}
651
652static int request_frame(AVFilterLink *outlink)
653{
654 AVFilterContext *ctx = (AVFilterContext *)outlink->src;
655 QREncodeContext *qr = ctx->priv;
656 AVFrame *frame =
657 ff_get_video_buffer(outlink, qr->rendered_padded_qrcode_width, qr->rendered_padded_qrcode_width);
658 int ret;
659
660 if (!frame)
661 return AVERROR(ENOMEM);
662 frame->sample_aspect_ratio = (AVRational) {1, 1};
663 V(n) = frame->pts = qr->pts++;
664 V(t) = qr->pts * av_q2d(outlink->time_base);
665
666 if ((ret = draw_qrcode(ctx, frame)) < 0)
667 return ret;
668
669 return ff_filter_frame(outlink, frame);
670}
671
672static int qrencodesrc_query_formats(AVFilterContext *ctx)
673{
674 enum AVPixelFormat pix_fmt;
675 FFDrawContext draw;
676 AVFilterFormats *fmts = NULL;
677 int ret;
678
679 // this is needed to support both the no-draw and draw cases
680 // for the no-draw case we use FFDrawContext to write on the input picture ref
681 for (pix_fmt = 0; av_pix_fmt_desc_get(pix_fmt); pix_fmt++)
682 if (ff_draw_init(&draw, pix_fmt, 0) >= 0 &&
683 sws_isSupportedOutput(pix_fmt) &&
684 (ret = ff_add_format(&fmts, pix_fmt)) < 0)
685 return ret;
686
687 return ff_set_common_formats(ctx, fmts);
688}
689
690static const AVFilterPad qrencodesrc_outputs[] = {
691 {
692 .name = "default",
693 .type = AVMEDIA_TYPE_VIDEO,
694 .request_frame = request_frame,
695 .config_props = qrencodesrc_config_props,
696 }
697};
698
699const AVFilter ff_vsrc_qrencodesrc = {
700 .name = "qrencodesrc",
701 .description = NULL_IF_CONFIG_SMALL("Generate a QR code."),
702 .priv_size = sizeof(QREncodeContext),
703 .priv_class = &qrencodesrc_class,
704 .init = init,
705 .uninit = uninit,
706 .inputs = NULL,
707 FILTER_OUTPUTS(qrencodesrc_outputs),
708 FILTER_QUERY_FUNC(qrencodesrc_query_formats),
709};
710
711#endif // CONFIG_QRENCODESRC_FILTER
712
713#if CONFIG_QRENCODE_FILTER
714
715static const AVOption qrencode_options[] = {
716 COMMON_OPTIONS
717 {"x", "set x expression", OFFSET(x_expr), AV_OPT_TYPE_STRING, {.str="0"}, 0, 0, TFLAGS},
718 {"y", "set y expression", OFFSET(y_expr), AV_OPT_TYPE_STRING, {.str="0"}, 0, 0, TFLAGS},
719 { NULL }
720};
721
722AVFILTER_DEFINE_CLASS(qrencode);
723
724static int qrencode_config_input(AVFilterLink *inlink)
725{
726 AVFilterContext *ctx = inlink->dst;
727 QREncodeContext *qr = ctx->priv;
728 char *expr;
729 int ret;
730
731 qr->is_source = 0;
732
733 ff_draw_init(&qr->draw, inlink->format, FF_DRAW_PROCESS_ALPHA);
734
735 V(W) = V(main_w) = inlink->w;
736 V(H) = V(main_h) = inlink->h;
737 V(sar) = inlink->sample_aspect_ratio.num ? av_q2d(inlink->sample_aspect_ratio) : 1;
738 V(dar) = (double)inlink->w / inlink->h * V(sar);
739 V(hsub) = 1 << qr->draw.hsub_max;
740 V(vsub) = 1 << qr->draw.vsub_max;
741 V(t) = NAN;
742 V(x) = V(y) = NAN;
743
744 qr->x_pexpr = qr->y_pexpr = NULL;
745 qr->x_pexpr = qr->y_pexpr = NULL;
746
747#define PARSE_EXPR(name_) \
748 ret = av_expr_parse(&qr->name_##_pexpr, expr = qr->name_##_expr, var_names, \
749 NULL, NULL, fun2_names, fun2, 0, ctx); \
750 if (ret < 0) { \
751 av_log(ctx, AV_LOG_ERROR, \
752 "Could not to parse expression '%s' for '%s'\n", \
753 expr, #name_); \
754 return AVERROR(EINVAL); \
755 }
756
757 PARSE_EXPR(x);
758 PARSE_EXPR(y);
759 PARSE_EXPR(rendered_qrcode_width);
760 PARSE_EXPR(rendered_padded_qrcode_width);
761
762 ff_draw_init(&qr->draw, inlink->format, FF_DRAW_PROCESS_ALPHA);
763 ff_draw_color(&qr->draw, &qr->draw_foreground_color, (const uint8_t *)&qr->foreground_color);
764 ff_draw_color(&qr->draw, &qr->draw_background_color, (const uint8_t *)&qr->background_color);
765
766 qr->rendered_qrcode_width = -1;
767
768 return 0;
769}
770
771static int qrencode_query_formats(AVFilterContext *ctx)
772{
773 return ff_set_common_formats(ctx, ff_draw_supported_pixel_formats(0));
774}
775
776static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
777{
778 AVFilterContext *ctx = inlink->dst;
779 AVFilterLink *outlink = ctx->outputs[0];
780 QREncodeContext *qr = ctx->priv;
781 int ret;
782
783 V(n) = inlink->frame_count_out;
784 V(t) = frame->pts == AV_NOPTS_VALUE ?
785 NAN : frame->pts * av_q2d(inlink->time_base);
786 V(pict_type) = frame->pict_type;
787 V(duration) = frame->duration * av_q2d(inlink->time_base);
788
789 qr->metadata = frame->metadata;
790
791 if ((ret = draw_qrcode(ctx, frame)) < 0)
792 return ret;
793
794 return ff_filter_frame(outlink, frame);
795}
796
797static const AVFilterPad avfilter_vf_qrencode_inputs[] = {
798 {
799 .name = "default",
800 .type = AVMEDIA_TYPE_VIDEO,
801 .flags = AVFILTERPAD_FLAG_NEEDS_WRITABLE,
802 .filter_frame = filter_frame,
803 .config_props = qrencode_config_input,
804 },
805};
806
807const AVFilter ff_vf_qrencode = {
808 .name = "qrencode",
809 .description = NULL_IF_CONFIG_SMALL("Draw a QR code on top of video frames."),
810 .priv_size = sizeof(QREncodeContext),
811 .priv_class = &qrencode_class,
812 .init = init,
813 .uninit = uninit,
814 FILTER_INPUTS(avfilter_vf_qrencode_inputs),
815 FILTER_OUTPUTS(ff_video_default_filterpad),
816 FILTER_QUERY_FUNC(qrencode_query_formats),
817 .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
818};
819
820#endif // CONFIG_QRENCODE_FILTER

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