[FFmpeg-cvslog] lavfi: add qrencode source and filter

Stefano Sabatini git at videolan.org
Tue Jan 2 23:05:48 EET 2024


ffmpeg | branch: master | Stefano Sabatini <stefasab at gmail.com> | Tue Nov 28 23:58:15 2023 +0100| [899302bb5f6ac0484fedc2865ee3beca021eba85] | committer: Stefano Sabatini

lavfi: add qrencode source and filter

> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=899302bb5f6ac0484fedc2865ee3beca021eba85
---

 Changelog                |   1 +
 configure                |   7 +
 doc/filters.texi         | 427 ++++++++++++++++++++++++
 libavfilter/Makefile     |   2 +
 libavfilter/allfilters.c |   2 +
 libavfilter/qrencode.c   | 820 +++++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 1259 insertions(+)

diff --git a/Changelog b/Changelog
index a638c03250..b483bb9c69 100644
--- a/Changelog
+++ b/Changelog
@@ -13,6 +13,7 @@ version <next>:
 - IAMF raw demuxer and muxer
 - D3D12VA hardware accelerated H264, HEVC, VP9, AV1, MPEG-2 and VC1 decoding
 - tiltandshift filter
+- qrencode filter and qrencodesrc source
 
 version 6.1:
 - libaribcaption decoder
diff --git a/configure b/configure
index 037c08afff..b1853204ef 100755
--- a/configure
+++ b/configure
@@ -256,6 +256,7 @@ External library support:
   --enable-libopus         enable Opus de/encoding via libopus [no]
   --enable-libplacebo      enable libplacebo library [no]
   --enable-libpulse        enable Pulseaudio input via libpulse [no]
+  --enable-libqrencode     enable QR encode generation via libqrencode [no]
   --enable-librabbitmq     enable RabbitMQ library [no]
   --enable-librav1e        enable AV1 encoding via rav1e [no]
   --enable-librist         enable RIST via librist [no]
@@ -1881,6 +1882,7 @@ EXTERNAL_LIBRARY_LIST="
     libopus
     libplacebo
     libpulse
+    libqrencode
     librabbitmq
     librav1e
     librist
@@ -3789,6 +3791,8 @@ nnedi_filter_deps="gpl"
 ocr_filter_deps="libtesseract"
 ocv_filter_deps="libopencv"
 openclsrc_filter_deps="opencl"
+qrencode_filter_deps="libqrencode"
+qrencodesrc_filter_deps="libqrencode"
 overlay_opencl_filter_deps="opencl"
 overlay_qsv_filter_deps="libmfx"
 overlay_qsv_filter_select="qsvvpp"
@@ -6840,6 +6844,7 @@ enabled libopus           && {
 }
 enabled libplacebo        && require_pkg_config libplacebo "libplacebo >= 4.192.0" libplacebo/vulkan.h pl_vulkan_create
 enabled libpulse          && require_pkg_config libpulse libpulse pulse/pulseaudio.h pa_context_new
+enabled libqrencode       && require_pkg_config libqrencode libqrencode qrencode.h QRcode_encodeString
 enabled librabbitmq       && require_pkg_config librabbitmq "librabbitmq >= 0.7.1" amqp.h amqp_new_connection
 enabled librav1e          && require_pkg_config librav1e "rav1e >= 0.5.0" rav1e.h rav1e_context_new
 enabled librist           && require_pkg_config librist "librist >= 0.2.7" librist/librist.h rist_receiver_create
@@ -7668,6 +7673,8 @@ enabled mcdeint_filter      && prepend avfilter_deps "avcodec"
 enabled movie_filter    && prepend avfilter_deps "avformat avcodec"
 enabled pan_filter          && prepend avfilter_deps "swresample"
 enabled pp_filter           && prepend avfilter_deps "postproc"
+enabled qrencode_filter     && prepend_avfilter_deps "swscale"
+enabled qrencodesrc_filter  && prepend_avfilter_deps "swscale"
 enabled removelogo_filter   && prepend avfilter_deps "avformat avcodec swscale"
 enabled sab_filter          && prepend avfilter_deps "swscale"
 enabled scale_filter    && prepend avfilter_deps "swscale"
diff --git a/doc/filters.texi b/doc/filters.texi
index a63a635ec8..9b120ce0a1 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -20096,6 +20096,302 @@ qp=2+2*sin(PI*qp)
 @end example
 @end itemize
 
+ at section qrencode
+Generate a QR code using the libqrencode library (see
+ at url{https://fukuchi.org/works/qrencode/}), and overlay it on top of the current
+frame.
+
+To enable the compilation of this filter, you need to configure FFmpeg with with
+ at code{--enable-libqrencode}.
+
+The QR code is generated from the provided text or text pattern. The
+corresponding QR code is scaled and overlayed into the video output according to
+the specified options.
+
+In case no text is specified, no QR code is overlaied.
+
+This filter accepts the following options:
+
+ at table @option
+
+ at item qrcode_width, q
+ at item padded_qrcode_width, Q
+Specify an expression for the width of the rendered QR code, with and without
+padding. The @var{qrcode_width} expression can reference the value set by the
+ at var{padded_qrcode_width} expression, and vice versa.
+By default @var{padded_qrcode_width} is set to @var{qrcode_width}, meaning that
+there is no padding.
+
+These expressions are evaluated for each new frame.
+
+See the @ref{qrencode_expressions,,qrencode Expressions} section for details.
+
+ at item x
+ at item y
+Specify an expression for positioning the padded QR code top-left corner.  The
+ at var{x} expression can reference the value set by the @var{y} expression, and
+vice.
+
+By default @var{x} and @var{y} are set set to @var{0}, meaning that the QR code
+is placed in the top left corner of the input.
+
+These expressions are evaluated for each new frame.
+
+See the @ref{qrencode_expressions,,qrencode Expressions} section for details.
+
+ at item case_sensitive, cs
+Instruct libqrencode to use case sensitive encoding. This is enabled by
+default. This can be disabled to reduce the QR encoding size.
+
+ at item level, l
+Specify the QR encoding error correction level. With an higher correction level,
+the encoding size will increase but the code will be more robust to corruption.
+Lower level is @var{L}.
+
+It accepts the following values:
+ at table @samp
+ at item L
+ at item M
+ at item Q
+ at item H
+ at end table
+
+ at item expansion
+Select how the input text is expanded. Can be either @code{none}, or
+ at code{normal} (default). See the @ref{qrencode_text_expansion,,qrencode Text expansion}
+section below for details.
+
+ at item text
+ at item textfile
+Define the text to be rendered. In case neither is specified, no QR is encoded
+(just an empty colored frame).
+
+In case expansion is enabled, the text is treated as a text template, using the
+qrencode expansion mechanism. See the @ref{qrencode_text_expansion,,qrencode
+Text expansion} section below for details.
+
+ at item background_color, bc
+ at item foreground_color, fc
+Set the QR code and background color. The default value of
+ at var{foreground_color} is "black", the default value of @var{background_color}
+is "white".
+
+For the syntax of the color options, check the @ref{color syntax,,"Color"
+section in the ffmpeg-utils manual,ffmpeg-utils}.
+ at end table
+
+ at anchor{qrencode_expressions}
+ at subsection qrencode Expressions
+
+The expressions set by the options contain the following constants and functions.
+
+ at table @option
+ at item dar
+input display aspect ratio, it is the same as (@var{w} / @var{h}) * @var{sar}
+
+ at item duration
+the current frame's duration, in seconds
+
+ at item hsub
+ at item vsub
+horizontal and vertical chroma subsample values. For example for the
+pixel format "yuv422p" @var{hsub} is 2 and @var{vsub} is 1.
+
+ at item main_h, H
+the input height
+
+ at item main_w, W
+the input width
+
+ at item n
+the number of input frame, starting from 0
+
+ at item pict_type
+a number representing the picture type
+
+ at item qr_w, w
+the width of the encoded QR code
+
+ at item rendered_qr_w, q
+ at item rendered_padded_qr_w, Q
+the width of the rendered QR code, without and without padding.
+
+These parameters allow the @var{q} and @var{Q} expressions to refer to each
+other, so you can for example specify @code{q=3/4*Q}.
+
+ at item rand(min, max)
+return a random number included between @var{min} and @var{max}
+
+ at item sar
+the input sample aspect ratio
+
+ at item t
+timestamp expressed in seconds, NAN if the input timestamp is unknown
+
+ at item x
+ at item y
+the x and y offset coordinates where the text is drawn.
+
+These parameters allow the @var{x} and @var{y} expressions to refer to each
+other, so you can for example specify @code{y=x/dar}.
+ at end table
+
+ at anchor{qrencode_text_expansion}
+ at subsection qrencode Text expansion
+
+If @option{expansion} is set to @code{none}, the text is printed verbatim.
+
+If @option{expansion} is set to @code{normal} (which is the default),
+the following expansion mechanism is used.
+
+The backslash character @samp{\}, followed by any character, always expands to
+the second character.
+
+Sequences of the form @code{%@{...@}} are expanded. The text between the
+braces is a function name, possibly followed by arguments separated by ':'.
+If the arguments contain special characters or delimiters (':' or '@}'),
+they should be escaped.
+
+Note that they probably must also be escaped as the value for the @option{text}
+option in the filter argument string and as the filter argument in the
+filtergraph description, and possibly also for the shell, that makes up to four
+levels of escaping; using a text file with the @option{textfile} option avoids
+these problems.
+
+The following functions are available:
+
+ at table @command
+ at item n, frame_num
+return the frame number
+
+ at item pts
+Return the presentation timestamp of the current frame.
+
+It can take up to two arguments.
+
+The first argument is the format of the timestamp; it defaults to @code{flt} for
+seconds as a decimal number with microsecond accuracy; @code{hms} stands for a
+formatted @var{[-]HH:MM:SS.mmm} timestamp with millisecond accuracy.
+ at code{gmtime} stands for the timestamp of the frame formatted as UTC time;
+ at code{localtime} stands for the timestamp of the frame formatted as local time
+zone time. If the format is set to @code{hms24hh}, the time is formatted in 24h
+format (00-23).
+
+The second argument is an offset added to the timestamp.
+
+If the format is set to @code{localtime} or @code{gmtime}, a third argument may
+be supplied: a @code{strftime} C function format string. By default,
+ at var{YYYY-MM-DD HH:MM:SS} format will be used.
+
+ at item expr, e
+Evaluate the expression's value and output as a double.
+
+It must take one argument specifying the expression to be evaluated, accepting
+the constants and functions defined in @ref{qrencode_expressions}.
+
+ at item expr_formatted, ef
+Evaluate the expression's value and output as a formatted string.
+
+The first argument is the expression to be evaluated, just as for the @var{expr} function.
+The second argument specifies the output format. Allowed values are @samp{x},
+ at samp{X}, @samp{d} and @samp{u}. They are treated exactly as in the
+ at code{printf} function.
+The third parameter is optional and sets the number of positions taken by the output.
+It can be used to add padding with zeros from the left.
+
+ at item gmtime
+The time at which the filter is running, expressed in UTC.
+It can accept an argument: a @code{strftime} C function format string.
+The format string is extended to support the variable @var{%[1-6]N}
+which prints fractions of the second with optionally specified number of digits.
+
+ at item localtime
+The time at which the filter is running, expressed in the local time zone.
+It can accept an argument: a @code{strftime} C function format string.
+The format string is extended to support the variable @var{%[1-6]N}
+which prints fractions of the second with optionally specified number of digits.
+
+ at item metadata
+Frame metadata. Takes one or two arguments.
+
+The first argument is mandatory and specifies the metadata key.
+
+The second argument is optional and specifies a default value, used when the
+metadata key is not found or empty.
+
+Available metadata can be identified by inspecting entries starting with TAG
+included within each frame section printed by running @code{ffprobe
+-show_frames}.
+
+String metadata generated in filters leading to the qrencode filter are also
+available.
+
+ at item rand(min, max)
+return a random number included between @var{min} and @var{max}
+ at end table
+
+ at subsection Examples
+
+ at itemize
+ at item
+Generate a QR code encoding the specified text with the default size, overalaid
+in the top left corner of the input video, with the default size:
+ at example
+qrencode=text=www.ffmpeg.org
+ at end example
+
+ at item
+Same as below, but select blue on pink colors:
+ at example
+qrencode=text=www.ffmpeg.org:bc=pink@@0.5:fc=blue
+ at end example
+
+ at item
+Place the QR code in the bottom right corner of the input video:
+ at example
+qrencode=text=www.ffmpeg.org:x=W-Q:y=H-Q
+ at end example
+
+ at item
+Generate a QR code with width of 200 pixels and padding, making the padded width
+4/3 of the QR code width:
+ at example
+qrencode=text=www.ffmpeg.org:q=200:Q=4/3*q
+ at end example
+
+ at item
+Generate a QR code with padded width of 200 pixels and padding, making the QR
+code width 3/4 of the padded width:
+ at example
+qrencode=text=www.ffmpeg.org:Q=200:q=3/4*Q
+ at end example
+
+ at item
+Make the QR code a fraction of the input video width:
+ at example
+qrencode=text=www.ffmpeg.org:q=W/5
+ at end example
+
+ at item
+Generate a QR code encoding the frame number:
+ at example
+qrencode=text=%@{n@}
+ at end example
+
+ at item
+Generate a QR code encoding the GMT timestamp:
+ at example
+qrencode=text=%@{gmtime@}
+ at end example
+
+ at item
+Generate a QR code encoding the timestamp expressed as a float:
+ at example
+qrencode=text=%@{pts@}
+ at end example
+
+ at end itemize
+
 @section random
 
 Flush video frames from internal cache of frames into a random order.
@@ -28749,6 +29045,137 @@ ffplay -f lavfi life=s=300x200:mold=10:r=60:ratio=0.1:death_color=#C83232:life_c
 @end example
 @end itemize
 
+ at section qrencodesrc
+
+Generate a QR code using the libqrencode library (see
+ at url{https://fukuchi.org/works/qrencode/}).
+
+To enable the compilation of this source, you need to configure FFmpeg with with
+ at code{--enable-libqrencode}.
+
+The QR code is generated from the provided text or text pattern. The
+corresponding QR code is scaled and put in the video output according to the
+specified output size options.
+
+In case no text is specified, the QR code is not generated, but an empty colored
+output is returned instead.
+
+This source accepts the following options:
+
+ at table @option
+
+ at item qrcode_width, q
+ at item padded_qrcode_width, Q
+Specify an expression for the width of the rendered QR code, with and without
+padding. The @var{qrcode_width} expression can reference the value set by the
+ at var{padded_qrcode_width} expression, and vice versa.
+By default @var{padded_qrcode_width} is set to @var{qrcode_width}, meaning that
+there is no padding.
+
+These expressions are evaluated only once, when initializing the source.
+See the @ref{qrencode_expressions,,qrencode Expressions} section for details.
+
+Note that some of the constants are missing for the source (for example the
+ at var{x} or @var{t} or ¸@var{n}), since they only makes sense when evaluating the
+expression for each frame rather than at initialization time.
+
+ at item rate, r
+Specify the frame rate of the sourced video, as the number of frames
+generated per second. It has to be a string in the format
+ at var{frame_rate_num}/@var{frame_rate_den}, an integer number, a floating point
+number or a valid video frame rate abbreviation. The default value is
+"25".
+
+ at item case_sensitive, cs
+Instruct libqrencode to use case sensitive encoding. This is enabled by
+default. This can be disabled to reduce the QR encoding size.
+
+ at item level, l
+Specify the QR encoding error correction level. With an higher correction level,
+the encoding size will increase but the code will be more robust to corruption.
+Lower level is @var{L}.
+
+It accepts the following values:
+ at table @samp
+ at item L
+ at item M
+ at item Q
+ at item H
+ at end table
+
+ at item expansion
+Select how the input text is expanded. Can be either @code{none}, or
+ at code{normal} (default). See the @ref{qrencode_text_expansion,,qrencode Text expansion}
+section for details.
+
+ at item text
+ at item textfile
+Define the text to be rendered. In case neither is specified, no QR is encoded
+(just an empty colored frame).
+
+In case expansion is enabled, the text is treated as a text template, using the
+qrencode expansion mechanism. See the @ref{qrencode_text_expansion,,qrencode
+Text expansion} section for details.
+
+ at item background_color, bc
+ at item foreground_color, fc
+Set the QR code and background color. The default value of
+ at var{foreground_color} is "black", the default value of @var{background_color}
+is "white".
+
+For the syntax of the color options, check the @ref{color syntax,,"Color"
+section in the ffmpeg-utils manual,ffmpeg-utils}.
+ at end table
+
+ at subsection Examples
+
+ at itemize
+ at item
+Generate a QR code encoding the specified text with the default size:
+ at example
+qrencodesrc=text=www.ffmpeg.org
+ at end example
+
+ at item
+Same as below, but select blue on pink colors:
+ at example
+qrencodesrc=text=www.ffmpeg.org:bc=pink:fc=blue
+ at end example
+
+ at item
+Generate a QR code with width of 200 pixels and padding, making the padded width
+4/3 of the QR code width:
+ at example
+qrencodesrc=text=www.ffmpeg.org:q=200:Q=4/3*q
+ at end example
+
+ at item
+Generate a QR code with padded width of 200 pixels and padding, making the QR
+code width 3/4 of the padded width:
+ at example
+qrencodesrc=text=www.ffmpeg.org:Q=200:q=3/4*Q
+ at end example
+
+ at item
+Generate a QR code encoding the frame number:
+ at example
+qrencodesrc=text=%@{n@}
+ at end example
+
+ at item
+Generate a QR code encoding the GMT timestamp:
+ at example
+qrencodesrc=text=%@{gmtime@}
+ at end example
+
+ at item
+Generate a QR code encoding the timestamp expressed as a float:
+ at example
+qrencodesrc=text=%@{pts@}
+ at end example
+
+ at end itemize
+
 @anchor{allrgb}
 @anchor{allyuv}
 @anchor{color}
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index badaa43859..31371ceb1a 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -599,6 +599,8 @@ OBJS-$(CONFIG_NULLSRC_FILTER)                += vsrc_testsrc.o
 OBJS-$(CONFIG_OPENCLSRC_FILTER)              += vf_program_opencl.o opencl.o
 OBJS-$(CONFIG_PAL75BARS_FILTER)              += vsrc_testsrc.o
 OBJS-$(CONFIG_PAL100BARS_FILTER)             += vsrc_testsrc.o
+OBJS-$(CONFIG_QRENCODE_FILTER)               += qrencode.o textutils.o
+OBJS-$(CONFIG_QRENCODESRC_FILTER)            += qrencode.o textutils.o
 OBJS-$(CONFIG_RGBTESTSRC_FILTER)             += vsrc_testsrc.o
 OBJS-$(CONFIG_SIERPINSKI_FILTER)             += vsrc_sierpinski.o
 OBJS-$(CONFIG_SMPTEBARS_FILTER)              += vsrc_testsrc.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 135794ba36..20feb37967 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -411,6 +411,7 @@ extern const AVFilter ff_vf_pseudocolor;
 extern const AVFilter ff_vf_psnr;
 extern const AVFilter ff_vf_pullup;
 extern const AVFilter ff_vf_qp;
+extern const AVFilter ff_vf_qrencode;
 extern const AVFilter ff_vf_random;
 extern const AVFilter ff_vf_readeia608;
 extern const AVFilter ff_vf_readvitc;
@@ -561,6 +562,7 @@ extern const AVFilter ff_vsrc_mandelbrot;
 extern const AVFilter ff_vsrc_mptestsrc;
 extern const AVFilter ff_vsrc_nullsrc;
 extern const AVFilter ff_vsrc_openclsrc;
+extern const AVFilter ff_vsrc_qrencodesrc;
 extern const AVFilter ff_vsrc_pal75bars;
 extern const AVFilter ff_vsrc_pal100bars;
 extern const AVFilter ff_vsrc_rgbtestsrc;
diff --git a/libavfilter/qrencode.c b/libavfilter/qrencode.c
new file mode 100644
index 0000000000..09af8dfb4e
--- /dev/null
+++ b/libavfilter/qrencode.c
@@ -0,0 +1,820 @@
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file QR encoder source and filter.
+ *
+ * A QR code (quick-response code) is a type of two-dimensional matrix
+ * barcode, invented in 1994, by Japanese company Denso Wave for
+ * labelling automobile parts.
+ *
+ * This source uses the libqrencode library to generate QR code:
+ * https://fukuchi.org/works/qrencode/
+ */
+
+//#define DEBUG
+
+#include "config_components.h"
+
+#include "libavutil/internal.h"
+#include "libavutil/imgutils.h"
+#include "libavutil/opt.h"
+#include "libavutil/lfg.h"
+#include "libavutil/random_seed.h"
+
+#include "avfilter.h"
+#include "drawutils.h"
+#include "internal.h"
+#include "formats.h"
+#include "textutils.h"
+#include "video.h"
+#include "libswscale/swscale.h"
+
+#include <qrencode.h>
+
+enum var_name {
+    VAR_dar,
+    VAR_duration,
+    VAR_hsub, VAR_vsub,
+    VAR_main_h, VAR_H,
+    VAR_main_w, VAR_W,
+    VAR_n,
+    VAR_pict_type,
+    VAR_qr_w, VAR_w,
+    VAR_rendered_padded_qr_w, VAR_Q,
+    VAR_rendered_qr_w, VAR_q,
+    VAR_sar,
+    VAR_t,
+    VAR_x,
+    VAR_y,
+    VAR_VARS_NB
+};
+
+static const char *const var_names[] = {
+    "dar",
+    "duration",
+    "hsub", "vsub",
+    "main_h", "H",               ///< height of the input video
+    "main_w", "W",               ///< width of the input video
+    "n",                         ///< number of frame
+    "pict_type",
+    "qr_w", "w",                 ///< width of the QR code
+    "rendered_padded_qr_w", "Q", ///< width of the rendered QR code
+    "rendered_qr_w", "q",        ///< width of the rendered QR code
+    "sar",
+    "t",                         ///< timestamp expressed in seconds
+    "x",
+    "y",
+    NULL
+};
+
+#define V(name_) qr->var_values[VAR_##name_]
+
+enum Expansion {
+    EXPANSION_NONE,
+    EXPANSION_NORMAL
+};
+
+typedef struct QREncodeContext {
+    const AVClass *class;
+
+    char is_source;
+    char *x_expr;
+    char *y_expr;
+    AVExpr *x_pexpr, *y_pexpr;
+
+    char *rendered_qrcode_width_expr;
+    char *rendered_padded_qrcode_width_expr;
+    AVExpr *rendered_qrcode_width_pexpr, *rendered_padded_qrcode_width_pexpr;
+
+    int rendered_qrcode_width;
+    int rendered_padded_qrcode_width;
+
+    unsigned char *text;
+    char *textfile;
+    uint64_t pts;
+
+    int level;
+    char case_sensitive;
+
+    uint8_t foreground_color[4];
+    uint8_t background_color[4];
+
+    FFDrawContext draw;
+    FFDrawColor draw_foreground_color;   ///< foreground color
+    FFDrawColor draw_background_color;   ///< background color
+
+    /* these are only used when nothing must be encoded */
+    FFDrawContext draw0;
+    FFDrawColor draw0_background_color;   ///< background color
+
+    uint8_t *qrcode_data[4];
+    int qrcode_linesize[4];
+    uint8_t *qrcode_mask_data[4];
+    int qrcode_mask_linesize[4];
+
+    /* only used for filter to contain scaled image to blend on top of input */
+    uint8_t *rendered_qrcode_data[4];
+    int rendered_qrcode_linesize[4];
+
+    int qrcode_width;
+    int padded_qrcode_width;
+
+    AVRational frame_rate;
+
+    int expansion;                    ///< expansion mode to use for the text
+    FFExpandTextContext expand_text;  ///< expand text in case expansion is enabled
+    AVBPrint expanded_text;           ///< used to contain the expanded text
+
+    double var_values[VAR_VARS_NB];
+    AVLFG  lfg;                       ///< random generator
+    AVDictionary *metadata;
+} QREncodeContext;
+
+#define OFFSET(x) offsetof(QREncodeContext, x)
+#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
+#define TFLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM
+
+#define COMMON_OPTIONS                                                  \
+    { "qrcode_width", "set rendered QR code width expression", OFFSET(rendered_qrcode_width_expr), AV_OPT_TYPE_STRING, {.str = "64"}, 0, INT_MAX, FLAGS }, \
+    { "q",            "set rendered QR code width expression", OFFSET(rendered_qrcode_width_expr), AV_OPT_TYPE_STRING, {.str = "64"}, 0, INT_MAX, FLAGS }, \
+    { "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 }, \
+    { "Q",                   "set rendered padded QR code width expression", OFFSET(rendered_padded_qrcode_width_expr), AV_OPT_TYPE_STRING, {.str = "q"}, 0, INT_MAX, FLAGS }, \
+    { "case_sensitive", "generate code which is case sensitive", OFFSET(case_sensitive), AV_OPT_TYPE_BOOL,   {.i64 = 1},      0,    1, FLAGS }, \
+    { "cs",             "generate code which is case sensitive", OFFSET(case_sensitive), AV_OPT_TYPE_BOOL,   {.i64 = 1},      0,    1, FLAGS }, \
+                                                                        \
+    { "level", "error correction level, lowest is L", OFFSET(level), AV_OPT_TYPE_INT, { .i64 = AVCOL_SPC_UNSPECIFIED }, 0, QR_ECLEVEL_H, .flags = FLAGS, "level"}, \
+    { "l",     "error correction level, lowest is L", OFFSET(level), AV_OPT_TYPE_INT, { .i64 = AVCOL_SPC_UNSPECIFIED }, 0, QR_ECLEVEL_H, .flags = FLAGS, "level"}, \
+    { "L",     NULL, 0, AV_OPT_TYPE_CONST, { .i64 = QR_ECLEVEL_L }, 0, 0, FLAGS, "level" }, \
+    { "M",     NULL, 0, AV_OPT_TYPE_CONST, { .i64 = QR_ECLEVEL_M }, 0, 0, FLAGS, "level" }, \
+    { "Q",     NULL, 0, AV_OPT_TYPE_CONST, { .i64 = QR_ECLEVEL_Q }, 0, 0, FLAGS, "level" }, \
+    { "H",     NULL, 0, AV_OPT_TYPE_CONST, { .i64 = QR_ECLEVEL_H }, 0, 0, FLAGS, "level" }, \
+                                                                        \
+    {"expansion", "set the expansion mode", OFFSET(expansion), AV_OPT_TYPE_INT, {.i64=EXPANSION_NORMAL}, 0, 2, FLAGS, "expansion"}, \
+    {"none",     "set no expansion",     OFFSET(expansion), AV_OPT_TYPE_CONST, {.i64 = EXPANSION_NONE},     0, 0, FLAGS, "expansion"}, \
+    {"normal",   "set normal expansion", OFFSET(expansion), AV_OPT_TYPE_CONST, {.i64 = EXPANSION_NORMAL},   0, 0, FLAGS, "expansion"}, \
+                                                                        \
+    { "foreground_color", "set QR foreground color", OFFSET(foreground_color), AV_OPT_TYPE_COLOR, {.str = "black"}, 0, 0, FLAGS }, \
+    { "fc",               "set QR foreground color", OFFSET(foreground_color), AV_OPT_TYPE_COLOR, {.str = "black"}, 0, 0, FLAGS }, \
+    { "background_color", "set QR background color", OFFSET(background_color), AV_OPT_TYPE_COLOR, {.str = "white"}, 0, 0, FLAGS }, \
+    { "bc",               "set QR background color", OFFSET(background_color), AV_OPT_TYPE_COLOR, {.str = "white"}, 0, 0, FLAGS }, \
+                                                                        \
+    {"text",     "set text to encode", OFFSET(text), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS}, \
+    {"textfile", "set text file to encode", OFFSET(textfile), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS}, \
+
+static const char *const fun2_names[] = {
+    "rand"
+};
+
+static double drand(void *opaque, double min, double max)
+{
+    return min + (max-min) / UINT_MAX * av_lfg_get(opaque);
+}
+
+static const ff_eval_func2 fun2[] = {
+    drand,
+    NULL
+};
+
+static int func_pts(void *ctx, AVBPrint *bp, const char *function_name,
+                    unsigned argc, char **argv)
+{
+    QREncodeContext *qr = ((AVFilterContext *)ctx)->priv;
+    const char *fmt;
+    const char *strftime_fmt = NULL;
+    const char *delta = NULL;
+    double t = qr->var_values[VAR_t];
+
+    // argv: pts, FMT, [DELTA, strftime_fmt]
+
+    fmt = argc >= 1 ? argv[0] : "flt";
+    if (argc >= 2) {
+        delta = argv[1];
+    }
+    if (argc >= 3) {
+        strftime_fmt = argv[2];
+    }
+
+    return ff_print_pts(ctx, bp, t, delta, fmt, strftime_fmt);
+}
+
+static int func_frame_num(void *ctx, AVBPrint *bp, const char *function_name,
+                          unsigned argc, char **argv)
+{
+    QREncodeContext *qr = ((AVFilterContext *)ctx)->priv;
+
+    av_bprintf(bp, "%d", (int)V(n));
+    return 0;
+}
+
+static int func_strftime(void *ctx, AVBPrint *bp, const char *function_name,
+                         unsigned argc, char **argv)
+{
+    const char *strftime_fmt = argc ? argv[0] : NULL;
+
+    return ff_print_time(ctx, bp, strftime_fmt, !strcmp(function_name, "localtime"));
+}
+
+static int func_frame_metadata(void *ctx, AVBPrint *bp, const char *function_name,
+                               unsigned argc, char **argv)
+{
+    QREncodeContext *qr = ((AVFilterContext *)ctx)->priv;
+    AVDictionaryEntry *e = av_dict_get(qr->metadata, argv[0], NULL, 0);
+
+    if (e && e->value)
+        av_bprintf(bp, "%s", e->value);
+    else if (argc >= 2)
+        av_bprintf(bp, "%s", argv[1]);
+
+    return 0;
+}
+
+static int func_eval_expr(void *ctx, AVBPrint *bp, const char *function_name,
+                          unsigned argc, char **argv)
+{
+    QREncodeContext *qr = ((AVFilterContext *)ctx)->priv;
+
+    return ff_print_eval_expr(ctx, bp, argv[0],
+                              fun2_names, fun2,
+                              var_names, qr->var_values, &qr->lfg);
+}
+
+static int func_eval_expr_formatted(void *ctx, AVBPrint *bp, const char *function_name,
+                                    unsigned argc, char **argv)
+{
+    QREncodeContext *qr = ((AVFilterContext *)ctx)->priv;
+    int ret;
+    int positions = -1;
+
+    /*
+     * argv[0] expression to be converted to `int`
+     * argv[1] format: 'x', 'X', 'd' or 'u'
+     * argv[2] positions printed (optional)
+     */
+
+    if (argc == 3) {
+        ret = sscanf(argv[2], "%u", &positions);
+        if (ret != 1) {
+            av_log(ctx, AV_LOG_ERROR, "expr_int_format(): Invalid number of positions"
+                    " to print: '%s'\n", argv[2]);
+            return AVERROR(EINVAL);
+        }
+    }
+
+    return ff_print_formatted_eval_expr(ctx, bp, argv[0],
+                                        fun2_names, fun2,
+                                        var_names, qr->var_values,
+                                        &qr->lfg,
+                                        argv[1][0], positions);
+}
+
+static FFExpandTextFunction expand_text_functions[] = {
+    { "expr",            1, 1, func_eval_expr },
+    { "e",               1, 1, func_eval_expr },
+    { "expr_formatted",  2, 3, func_eval_expr_formatted },
+    { "ef",              2, 3, func_eval_expr_formatted },
+    { "metadata",        1, 2, func_frame_metadata },
+    { "frame_num",       0, 0, func_frame_num },
+    { "n",               0, 0, func_frame_num },
+    { "gmtime",          0, 1, func_strftime },
+    { "localtime",       0, 1, func_strftime },
+    { "pts",             0, 3, func_pts }
+};
+
+static av_cold int init(AVFilterContext *ctx)
+{
+    QREncodeContext *qr = ctx->priv;
+    int ret;
+
+    av_lfg_init(&qr->lfg, av_get_random_seed());
+
+    qr->qrcode_width = -1;
+    qr->rendered_padded_qrcode_width = -1;
+
+    if (qr->textfile) {
+        if (qr->text) {
+            av_log(ctx, AV_LOG_ERROR,
+                   "Both text and text file provided. Please provide only one\n");
+            return AVERROR(EINVAL);
+        }
+        if ((ret = ff_load_textfile(ctx, (const char *)qr->textfile, &(qr->text), NULL)) < 0)
+            return ret;
+    }
+
+    qr->expand_text = (FFExpandTextContext) {
+        .log_ctx = ctx,
+        .functions = expand_text_functions,
+        .functions_nb = FF_ARRAY_ELEMS(expand_text_functions)
+    };
+
+    av_bprint_init(&qr->expanded_text, 0, AV_BPRINT_SIZE_UNLIMITED);
+
+    return 0;
+}
+
+static av_cold void uninit(AVFilterContext *ctx)
+{
+    QREncodeContext *qr = ctx->priv;
+
+    av_expr_free(qr->x_pexpr);
+    av_expr_free(qr->y_pexpr);
+
+    av_bprint_finalize(&qr->expanded_text, NULL);
+
+    av_freep(&qr->qrcode_data[0]);
+    av_freep(&qr->rendered_qrcode_data[0]);
+    av_freep(&qr->qrcode_mask_data[0]);
+}
+
+#ifdef DEBUG
+static void show_qrcode(AVFilterContext *ctx, const QRcode *qrcode)
+{
+    int i, j;
+    char *line = av_malloc(qrcode->width + 1);
+    const char *p = qrcode->data;
+
+    if (!line)
+        return;
+    for (i = 0; i < qrcode->width; i++) {
+        for (j = 0; j < qrcode->width; j++)
+            line[j] = (*p++)&1 ? '@' : ' ';
+        line[j] = 0;
+        av_log(ctx, AV_LOG_DEBUG, "%3d: %s\n", i, line);
+    }
+    av_free(line);
+}
+#endif
+
+static int draw_qrcode(AVFilterContext *ctx, AVFrame *frame)
+{
+    QREncodeContext *qr = ctx->priv;
+    struct SwsContext *sws = NULL;
+    QRcode *qrcode = NULL;
+    int i, j;
+    char qrcode_width_changed;
+    int ret;
+    int offset;
+    uint8_t *srcp;
+    uint8_t *dstp0, *dstp;
+
+    av_bprint_clear(&qr->expanded_text);
+
+    switch (qr->expansion) {
+    case EXPANSION_NONE:
+        av_bprintf(&qr->expanded_text, "%s", qr->text);
+        break;
+    case EXPANSION_NORMAL:
+        if ((ret = ff_expand_text(&qr->expand_text, qr->text, &qr->expanded_text)) < 0)
+            return ret;
+        break;
+    }
+
+    if (!qr->expanded_text.str || qr->expanded_text.str[0] == 0) {
+        if (qr->is_source) {
+            ff_fill_rectangle(&qr->draw0, &qr->draw0_background_color,
+                              frame->data, frame->linesize,
+                              0, 0, qr->rendered_padded_qrcode_width, qr->rendered_padded_qrcode_width);
+        }
+
+        return 0;
+    }
+
+    av_log(ctx, AV_LOG_DEBUG, "Encoding string '%s'\n", qr->expanded_text.str);
+    qrcode = QRcode_encodeString(qr->expanded_text.str, 1, qr->level, QR_MODE_8,
+                                 qr->case_sensitive);
+    if (!qrcode) {
+        ret = AVERROR(errno);
+        av_log(ctx, AV_LOG_ERROR,
+               "Failed to encode string with error \'%s\'\n", av_err2str(ret));
+        goto end;
+    }
+
+    av_log(ctx, AV_LOG_DEBUG,
+           "Encoded QR with width:%d version:%d\n", qrcode->width, qrcode->version);
+#ifdef DEBUG
+    show_qrcode(ctx, (const QRcode *)qrcode);
+#endif
+
+    qrcode_width_changed = qr->qrcode_width != qrcode->width;
+    qr->qrcode_width = qrcode->width;
+
+    // realloc mask if needed
+    if (qrcode_width_changed) {
+        av_freep(&qr->qrcode_mask_data[0]);
+        ret = av_image_alloc(qr->qrcode_mask_data, qr->qrcode_mask_linesize,
+                             qrcode->width, qrcode->width,
+                             AV_PIX_FMT_GRAY8, 16);
+        if (ret < 0) {
+            av_log(ctx, AV_LOG_ERROR,
+                   "Failed to allocate image for QR code with width %d\n", qrcode->width);
+            goto end;
+        }
+    }
+
+    /* fill mask */
+    dstp0 = qr->qrcode_mask_data[0];
+    srcp = qrcode->data;
+
+    for (i = 0; i < qrcode->width; i++) {
+        dstp = dstp0;
+        for (j = 0; j < qrcode->width; j++)
+            *dstp++ = (*srcp++ & 1) ? 255 : 0;
+        dstp0 += qr->qrcode_mask_linesize[0];
+    }
+
+    if (qr->is_source) {
+        if (qrcode_width_changed) {
+            /* realloc padded image */
+
+            // compute virtual non-rendered padded size
+            // Q/q = W/w
+            qr->padded_qrcode_width =
+                ((double)qr->rendered_padded_qrcode_width / qr->rendered_qrcode_width) * qrcode->width;
+
+            av_freep(&qr->qrcode_data[0]);
+            ret = av_image_alloc(qr->qrcode_data, qr->qrcode_linesize,
+                                 qr->padded_qrcode_width, qr->padded_qrcode_width,
+                                 AV_PIX_FMT_ARGB, 16);
+            if (ret < 0) {
+                av_log(ctx, AV_LOG_ERROR,
+                       "Failed to allocate image for QR code with width %d\n",
+                       qr->padded_qrcode_width);
+                goto end;
+            }
+        }
+
+        /* fill padding */
+        ff_fill_rectangle(&qr->draw, &qr->draw_background_color,
+                          qr->qrcode_data, qr->qrcode_linesize,
+                          0, 0, qr->padded_qrcode_width, qr->padded_qrcode_width);
+
+        /* blend mask */
+        offset = (qr->padded_qrcode_width - qr->qrcode_width) / 2;
+        ff_blend_mask(&qr->draw, &qr->draw_foreground_color,
+                      qr->qrcode_data, qr->qrcode_linesize,
+                      qr->padded_qrcode_width, qr->padded_qrcode_width,
+                      qr->qrcode_mask_data[0], qr->qrcode_mask_linesize[0], qrcode->width, qrcode->width,
+                      3, 0, offset, offset);
+
+        /* scale padded QR over the frame */
+        sws = sws_alloc_context();
+        if (!sws) {
+            ret = AVERROR(ENOMEM);
+            goto end;
+        }
+
+        av_opt_set_int(sws, "srcw", qr->padded_qrcode_width, 0);
+        av_opt_set_int(sws, "srch", qr->padded_qrcode_width, 0);
+        av_opt_set_int(sws, "src_format", AV_PIX_FMT_ARGB, 0);
+        av_opt_set_int(sws, "dstw", qr->rendered_padded_qrcode_width, 0);
+        av_opt_set_int(sws, "dsth", qr->rendered_padded_qrcode_width, 0);
+        av_opt_set_int(sws, "dst_format", frame->format, 0);
+        av_opt_set_int(sws, "sws_flags", SWS_POINT, 0);
+
+        if ((ret = sws_init_context(sws, NULL, NULL)) < 0)
+            goto end;
+
+        sws_scale(sws,
+                  (const uint8_t *const *)&qr->qrcode_data, qr->qrcode_linesize,
+                  0, qr->padded_qrcode_width,
+                  frame->data, frame->linesize);
+    } else {
+#define EVAL_EXPR(name_)                                        \
+        av_expr_eval(qr->name_##_pexpr, qr->var_values, &qr->lfg);
+
+        V(qr_w) = V(w) = qrcode->width;
+
+        V(rendered_qr_w) = V(q) = EVAL_EXPR(rendered_qrcode_width);
+        V(rendered_padded_qr_w) = V(Q) = EVAL_EXPR(rendered_padded_qrcode_width);
+        /* It is necessary if q is expressed from Q */
+        V(rendered_qr_w) = V(q) = EVAL_EXPR(rendered_qrcode_width);
+
+        V(x) = EVAL_EXPR(x);
+        V(y) = EVAL_EXPR(y);
+        /* It is necessary if x is expressed from y */
+        V(x) = EVAL_EXPR(x);
+
+        av_log(ctx, AV_LOG_DEBUG,
+               "Rendering QR code with values n:%d w:%d q:%d Q:%d x:%d y:%d t:%f\n",
+               (int)V(n), (int)V(w), (int)V(q), (int)V(Q), (int)V(x), (int)V(y), V(t));
+
+        /* blend rectangle over the target */
+        ff_blend_rectangle(&qr->draw,  &qr->draw_background_color,
+                           frame->data, frame->linesize, frame->width, frame->height,
+                           V(x), V(y), V(Q), V(Q));
+
+        if (V(q) != qr->rendered_qrcode_width) {
+            av_freep(&qr->rendered_qrcode_data[0]);
+            qr->rendered_qrcode_width = V(q);
+
+            ret = av_image_alloc(qr->rendered_qrcode_data, qr->rendered_qrcode_linesize,
+                                 qr->rendered_qrcode_width, qr->rendered_qrcode_width,
+                                 AV_PIX_FMT_GRAY8, 16);
+            if (ret < 0) {
+                av_log(ctx, AV_LOG_ERROR,
+                       "Failed to allocate image for rendered QR code with width %d\n",
+                       qr->rendered_qrcode_width);
+                goto end;
+            }
+        }
+
+        /* scale mask */
+        sws = sws_alloc_context();
+        if (!sws) {
+            ret = AVERROR(ENOMEM);
+            goto end;
+        }
+
+        av_opt_set_int(sws, "srcw", qr->qrcode_width, 0);
+        av_opt_set_int(sws, "srch", qr->qrcode_width, 0);
+        av_opt_set_int(sws, "src_format", AV_PIX_FMT_GRAY8, 0);
+        av_opt_set_int(sws, "dstw", qr->rendered_qrcode_width, 0);
+        av_opt_set_int(sws, "dsth", qr->rendered_qrcode_width, 0);
+        av_opt_set_int(sws, "dst_format", AV_PIX_FMT_GRAY8, 0);
+        av_opt_set_int(sws, "sws_flags", SWS_POINT, 0);
+
+        if ((ret = sws_init_context(sws, NULL, NULL)) < 0)
+            goto end;
+
+        sws_scale(sws,
+                  (const uint8_t *const *)&qr->qrcode_mask_data, qr->qrcode_mask_linesize,
+                  0, qr->qrcode_width,
+                  qr->rendered_qrcode_data, qr->rendered_qrcode_linesize);
+
+        /* blend mask over the input frame */
+        offset = (V(Q) - V(q)) / 2;
+        ff_blend_mask(&qr->draw, &qr->draw_foreground_color,
+                      frame->data, frame->linesize, frame->width, frame->height,
+                      qr->rendered_qrcode_data[0], qr->rendered_qrcode_linesize[0],
+                      qr->rendered_qrcode_width, qr->rendered_qrcode_width,
+                      3, 0, V(x) + offset, V(y) + offset);
+    }
+
+end:
+    sws_freeContext(sws);
+    QRcode_free(qrcode);
+
+    return ret;
+}
+
+#if CONFIG_QRENCODESRC_FILTER
+
+static const AVOption qrencodesrc_options[] = {
+    COMMON_OPTIONS
+    { "rate",     "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, INT_MAX, FLAGS },
+    { "r",        "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, INT_MAX, FLAGS },
+    { NULL }
+};
+
+AVFILTER_DEFINE_CLASS(qrencodesrc);
+
+static int qrencodesrc_config_props(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = outlink->src;
+    QREncodeContext *qr = ctx->priv;
+    int ret;
+
+    qr->is_source = 1;
+    V(x) = V(y) = 0;
+
+#define PARSE_AND_EVAL_EXPR(var_name_, expr_name_)                      \
+    ret = av_expr_parse_and_eval(&qr->var_values[VAR_##var_name_],      \
+                                 qr->expr_name_##_expr,                 \
+                                 var_names, qr->var_values,             \
+                                 NULL, NULL,                            \
+                                 fun2_names, fun2,                      \
+                                 &qr->lfg, 0, ctx);                     \
+    if (ret < 0) {                                                      \
+        av_log(ctx, AV_LOG_ERROR,                                       \
+               "Could not evaluate expression '%s'\n",                  \
+               qr->expr_name_##_expr);                                  \
+        return ret;                                                     \
+    }
+
+    /* undefined for the source */
+    V(main_w) = V(W) = NAN;
+    V(main_h) = V(H) = NAN;
+    V(x) = V(y) = V(t) = V(n) = NAN;
+    V(dar) = V(sar) = 1.0;
+
+    PARSE_AND_EVAL_EXPR(rendered_qr_w, rendered_qrcode_width);
+    V(q) = V(rendered_qr_w);
+    PARSE_AND_EVAL_EXPR(rendered_padded_qr_w, rendered_padded_qrcode_width);
+    V(Q) = V(rendered_padded_qr_w);
+    PARSE_AND_EVAL_EXPR(rendered_qr_w, rendered_qrcode_width);
+    V(q) = V(rendered_qr_w);
+
+    qr->rendered_qrcode_width = V(rendered_qr_w);
+    qr->rendered_padded_qrcode_width = V(rendered_padded_qr_w);
+
+    av_log(ctx, AV_LOG_VERBOSE,
+           "q:%d Q:%d case_sensitive:%d level:%d\n",
+           (int)qr->rendered_qrcode_width, (int)qr->rendered_padded_qrcode_width,
+           qr->case_sensitive, qr->level);
+
+    if (qr->rendered_padded_qrcode_width < qr->rendered_qrcode_width) {
+        av_log(ctx, AV_LOG_ERROR,
+               "Resulting padded QR code width (%d) is lesser than the QR code width (%d)\n",
+               qr->rendered_padded_qrcode_width, qr->rendered_qrcode_width);
+        return AVERROR(EINVAL);
+    }
+
+    ff_draw_init(&qr->draw, AV_PIX_FMT_ARGB, FF_DRAW_PROCESS_ALPHA);
+    ff_draw_color(&qr->draw, &qr->draw_foreground_color, (const uint8_t *)&qr->foreground_color);
+    ff_draw_color(&qr->draw, &qr->draw_background_color, (const uint8_t *)&qr->background_color);
+
+    ff_draw_init(&qr->draw0, outlink->format, FF_DRAW_PROCESS_ALPHA);
+    ff_draw_color(&qr->draw0, &qr->draw0_background_color, (const uint8_t *)&qr->background_color);
+
+    outlink->w = qr->rendered_padded_qrcode_width;
+    outlink->h = qr->rendered_padded_qrcode_width;
+    outlink->time_base = av_inv_q(qr->frame_rate);
+    outlink->frame_rate = qr->frame_rate;
+
+    return 0;
+}
+
+static int request_frame(AVFilterLink *outlink)
+{
+    AVFilterContext *ctx = (AVFilterContext *)outlink->src;
+    QREncodeContext *qr = ctx->priv;
+    AVFrame *frame =
+        ff_get_video_buffer(outlink, qr->rendered_padded_qrcode_width, qr->rendered_padded_qrcode_width);
+    int ret;
+
+    if (!frame)
+        return AVERROR(ENOMEM);
+    frame->sample_aspect_ratio = (AVRational) {1, 1};
+    V(n) = frame->pts = qr->pts++;
+    V(t) = qr->pts * av_q2d(outlink->time_base);
+
+    if ((ret = draw_qrcode(ctx, frame)) < 0)
+        return ret;
+
+    return ff_filter_frame(outlink, frame);
+}
+
+static int qrencodesrc_query_formats(AVFilterContext *ctx)
+{
+    enum AVPixelFormat pix_fmt;
+    FFDrawContext draw;
+    AVFilterFormats *fmts = NULL;
+    int ret;
+
+    // this is needed to support both the no-draw and draw cases
+    // for the no-draw case we use FFDrawContext to write on the input picture ref
+    for (pix_fmt = 0; av_pix_fmt_desc_get(pix_fmt); pix_fmt++)
+        if (ff_draw_init(&draw, pix_fmt, 0) >= 0 &&
+            sws_isSupportedOutput(pix_fmt) &&
+            (ret = ff_add_format(&fmts, pix_fmt)) < 0)
+            return ret;
+
+    return ff_set_common_formats(ctx, fmts);
+}
+
+static const AVFilterPad qrencodesrc_outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_VIDEO,
+        .request_frame = request_frame,
+        .config_props  = qrencodesrc_config_props,
+    }
+};
+
+const AVFilter ff_vsrc_qrencodesrc = {
+    .name          = "qrencodesrc",
+    .description   = NULL_IF_CONFIG_SMALL("Generate a QR code."),
+    .priv_size     = sizeof(QREncodeContext),
+    .priv_class    = &qrencodesrc_class,
+    .init          = init,
+    .uninit        = uninit,
+    .inputs        = NULL,
+    FILTER_OUTPUTS(qrencodesrc_outputs),
+    FILTER_QUERY_FUNC(qrencodesrc_query_formats),
+};
+
+#endif // CONFIG_QRENCODESRC_FILTER
+
+#if CONFIG_QRENCODE_FILTER
+
+static const AVOption qrencode_options[] = {
+    COMMON_OPTIONS
+    {"x",              "set x expression",      OFFSET(x_expr),             AV_OPT_TYPE_STRING, {.str="0"},   0, 0, TFLAGS},
+    {"y",              "set y expression",      OFFSET(y_expr),             AV_OPT_TYPE_STRING, {.str="0"},   0, 0, TFLAGS},
+    { NULL }
+};
+
+AVFILTER_DEFINE_CLASS(qrencode);
+
+static int qrencode_config_input(AVFilterLink *inlink)
+{
+    AVFilterContext *ctx = inlink->dst;
+    QREncodeContext *qr = ctx->priv;
+    char *expr;
+    int ret;
+
+    qr->is_source = 0;
+
+    ff_draw_init(&qr->draw, inlink->format, FF_DRAW_PROCESS_ALPHA);
+
+    V(W) = V(main_w) = inlink->w;
+    V(H) = V(main_h) = inlink->h;
+    V(sar)  = inlink->sample_aspect_ratio.num ? av_q2d(inlink->sample_aspect_ratio) : 1;
+    V(dar)  = (double)inlink->w / inlink->h * V(sar);
+    V(hsub) = 1 << qr->draw.hsub_max;
+    V(vsub) = 1 << qr->draw.vsub_max;
+    V(t) = NAN;
+    V(x) = V(y) = NAN;
+
+    qr->x_pexpr = qr->y_pexpr = NULL;
+    qr->x_pexpr = qr->y_pexpr = NULL;
+
+#define PARSE_EXPR(name_)                                               \
+    ret = av_expr_parse(&qr->name_##_pexpr, expr = qr->name_##_expr, var_names, \
+                        NULL, NULL, fun2_names, fun2, 0, ctx);          \
+    if (ret < 0) {                                                      \
+        av_log(ctx, AV_LOG_ERROR,                                       \
+               "Could not to parse expression '%s' for '%s'\n",         \
+               expr, #name_);                                           \
+        return AVERROR(EINVAL);                                         \
+    }
+
+    PARSE_EXPR(x);
+    PARSE_EXPR(y);
+    PARSE_EXPR(rendered_qrcode_width);
+    PARSE_EXPR(rendered_padded_qrcode_width);
+
+    ff_draw_init(&qr->draw, inlink->format, FF_DRAW_PROCESS_ALPHA);
+    ff_draw_color(&qr->draw, &qr->draw_foreground_color, (const uint8_t *)&qr->foreground_color);
+    ff_draw_color(&qr->draw, &qr->draw_background_color, (const uint8_t *)&qr->background_color);
+
+    qr->rendered_qrcode_width = -1;
+
+    return 0;
+}
+
+static int qrencode_query_formats(AVFilterContext *ctx)
+{
+    return ff_set_common_formats(ctx, ff_draw_supported_pixel_formats(0));
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFilterContext *ctx = inlink->dst;
+    AVFilterLink *outlink = ctx->outputs[0];
+    QREncodeContext *qr = ctx->priv;
+    int ret;
+
+    V(n) = inlink->frame_count_out;
+    V(t) = frame->pts == AV_NOPTS_VALUE ?
+        NAN : frame->pts * av_q2d(inlink->time_base);
+    V(pict_type) = frame->pict_type;
+    V(duration) = frame->duration * av_q2d(inlink->time_base);
+
+    qr->metadata = frame->metadata;
+
+    if ((ret = draw_qrcode(ctx, frame)) < 0)
+        return ret;
+
+    return ff_filter_frame(outlink, frame);
+}
+
+static const AVFilterPad avfilter_vf_qrencode_inputs[] = {
+    {
+        .name           = "default",
+        .type           = AVMEDIA_TYPE_VIDEO,
+        .flags          = AVFILTERPAD_FLAG_NEEDS_WRITABLE,
+        .filter_frame   = filter_frame,
+        .config_props   = qrencode_config_input,
+    },
+};
+
+const AVFilter ff_vf_qrencode = {
+    .name          = "qrencode",
+    .description   = NULL_IF_CONFIG_SMALL("Draw a QR code on top of video frames."),
+    .priv_size     = sizeof(QREncodeContext),
+    .priv_class    = &qrencode_class,
+    .init          = init,
+    .uninit        = uninit,
+    FILTER_INPUTS(avfilter_vf_qrencode_inputs),
+    FILTER_OUTPUTS(ff_video_default_filterpad),
+    FILTER_QUERY_FUNC(qrencode_query_formats),
+    .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+};
+
+#endif // CONFIG_QRENCODE_FILTER



More information about the ffmpeg-cvslog mailing list