History

lavfi: add qrencode source and filter

Stefano Sabatini committed 2 years ago

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

Unified · Split

6 files
  1. Changelog +1 −0
  2. configure +7 −0
  3. doc/filters.texi +427 −0
  4. libavfilter/Makefile +2 −0
  5. libavfilter/allfilters.c +2 −0
  6. libavfilter/qrencode.c +820 −0
Changelog+1−0
@@ -13,6 +13,7 @@ version <next>: · 12 unmodified lines
13- IAMF raw demuxer and muxer13- IAMF raw demuxer and muxer
14- D3D12VA hardware accelerated H264, HEVC, VP9, AV1, MPEG-2 and VC1 decoding14- D3D12VA hardware accelerated H264, HEVC, VP9, AV1, MPEG-2 and VC1 decoding
15- tiltandshift filter15- tiltandshift filter
16- qrencode filter and qrencodesrc source
16 17
17version 6.1:18version 6.1:
18- libaribcaption decoder19- libaribcaption decoder
configure+7−0
@@ -256,6 +256,7 @@ External library support: · 255 unmodified lines
256 --enable-libopus enable Opus de/encoding via libopus [no]256 --enable-libopus enable Opus de/encoding via libopus [no]
257 --enable-libplacebo enable libplacebo library [no]257 --enable-libplacebo enable libplacebo library [no]
258 --enable-libpulse enable Pulseaudio input via libpulse [no]258 --enable-libpulse enable Pulseaudio input via libpulse [no]
259 --enable-libqrencode enable QR encode generation via libqrencode [no]
259 --enable-librabbitmq enable RabbitMQ library [no]260 --enable-librabbitmq enable RabbitMQ library [no]
260 --enable-librav1e enable AV1 encoding via rav1e [no]261 --enable-librav1e enable AV1 encoding via rav1e [no]
261 --enable-librist enable RIST via librist [no]262 --enable-librist enable RIST via librist [no]
@@ -1881,6 +1882,7 @@ EXTERNAL_LIBRARY_LIST=" · 1619 unmodified lines
1881 libopus1882 libopus
1882 libplacebo1883 libplacebo
1883 libpulse1884 libpulse
1885 libqrencode
1884 librabbitmq1886 librabbitmq
1885 librav1e1887 librav1e
1886 librist1888 librist
@@ -3789,6 +3791,8 @@ nnedi_filter_deps="gpl" · 1902 unmodified lines
3789ocr_filter_deps="libtesseract"3791ocr_filter_deps="libtesseract"
3790ocv_filter_deps="libopencv"3792ocv_filter_deps="libopencv"
3791openclsrc_filter_deps="opencl"3793openclsrc_filter_deps="opencl"
3794qrencode_filter_deps="libqrencode"
3795qrencodesrc_filter_deps="libqrencode"
3792overlay_opencl_filter_deps="opencl"3796overlay_opencl_filter_deps="opencl"
3793overlay_qsv_filter_deps="libmfx"3797overlay_qsv_filter_deps="libmfx"
3794overlay_qsv_filter_select="qsvvpp"3798overlay_qsv_filter_select="qsvvpp"
@@ -6840,6 +6844,7 @@ enabled libopus && { · 3045 unmodified lines
6840}6844}
6841enabled libplacebo && require_pkg_config libplacebo "libplacebo >= 4.192.0" libplacebo/vulkan.h pl_vulkan_create6845enabled libplacebo && require_pkg_config libplacebo "libplacebo >= 4.192.0" libplacebo/vulkan.h pl_vulkan_create
6842enabled libpulse && require_pkg_config libpulse libpulse pulse/pulseaudio.h pa_context_new6846enabled libpulse && require_pkg_config libpulse libpulse pulse/pulseaudio.h pa_context_new
6847enabled libqrencode && require_pkg_config libqrencode libqrencode qrencode.h QRcode_encodeString
6843enabled librabbitmq && require_pkg_config librabbitmq "librabbitmq >= 0.7.1" amqp.h amqp_new_connection6848enabled librabbitmq && require_pkg_config librabbitmq "librabbitmq >= 0.7.1" amqp.h amqp_new_connection
6844enabled librav1e && require_pkg_config librav1e "rav1e >= 0.5.0" rav1e.h rav1e_context_new6849enabled librav1e && require_pkg_config librav1e "rav1e >= 0.5.0" rav1e.h rav1e_context_new
6845enabled librist && require_pkg_config librist "librist >= 0.2.7" librist/librist.h rist_receiver_create6850enabled 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" · 822 unmodified lines
7668enabled movie_filter && prepend avfilter_deps "avformat avcodec"7673enabled movie_filter && prepend avfilter_deps "avformat avcodec"
7669enabled pan_filter && prepend avfilter_deps "swresample"7674enabled pan_filter && prepend avfilter_deps "swresample"
7670enabled pp_filter && prepend avfilter_deps "postproc"7675enabled pp_filter && prepend avfilter_deps "postproc"
7676enabled qrencode_filter && prepend_avfilter_deps "swscale"
7677enabled qrencodesrc_filter && prepend_avfilter_deps "swscale"
7671enabled removelogo_filter && prepend avfilter_deps "avformat avcodec swscale"7678enabled removelogo_filter && prepend avfilter_deps "avformat avcodec swscale"
7672enabled sab_filter && prepend avfilter_deps "swscale"7679enabled sab_filter && prepend avfilter_deps "swscale"
7673enabled scale_filter && prepend avfilter_deps "swscale"7680enabled scale_filter && prepend avfilter_deps "swscale"
doc/filters.texi+427−0
@@ -20096,6 +20096,302 @@ qp=2+2*sin(PI*qp) · 20095 unmodified lines
20096@end example20096@end example
20097@end itemize20097@end itemize
20098 20098
20099@section qrencode
20100Generate a QR code using the libqrencode library (see
20101@url{https://fukuchi.org/works/qrencode/}), and overlay it on top of the current
20102frame.
20103
20104To enable the compilation of this filter, you need to configure FFmpeg with with
20105@code{--enable-libqrencode}.
20106
20107The QR code is generated from the provided text or text pattern. The
20108corresponding QR code is scaled and overlayed into the video output according to
20109the specified options.
20110
20111In case no text is specified, no QR code is overlaied.
20112
20113This filter accepts the following options:
20114
20115@table @option
20116
20117@item qrcode_width, q
20118@item padded_qrcode_width, Q
20119Specify an expression for the width of the rendered QR code, with and without
20120padding. The @var{qrcode_width} expression can reference the value set by the
20121@var{padded_qrcode_width} expression, and vice versa.
20122By default @var{padded_qrcode_width} is set to @var{qrcode_width}, meaning that
20123there is no padding.
20124
20125These expressions are evaluated for each new frame.
20126
20127See the @ref{qrencode_expressions,,qrencode Expressions} section for details.
20128
20129@item x
20130@item y
20131Specify an expression for positioning the padded QR code top-left corner. The
20132@var{x} expression can reference the value set by the @var{y} expression, and
20133vice.
20134
20135By default @var{x} and @var{y} are set set to @var{0}, meaning that the QR code
20136is placed in the top left corner of the input.
20137
20138These expressions are evaluated for each new frame.
20139
20140See the @ref{qrencode_expressions,,qrencode Expressions} section for details.
20141
20142@item case_sensitive, cs
20143Instruct libqrencode to use case sensitive encoding. This is enabled by
20144default. This can be disabled to reduce the QR encoding size.
20145
20146@item level, l
20147Specify the QR encoding error correction level. With an higher correction level,
20148the encoding size will increase but the code will be more robust to corruption.
20149Lower level is @var{L}.
20150
20151It accepts the following values:
20152@table @samp
20153@item L
20154@item M
20155@item Q
20156@item H
20157@end table
20158
20159@item expansion
20160Select how the input text is expanded. Can be either @code{none}, or
20161@code{normal} (default). See the @ref{qrencode_text_expansion,,qrencode Text expansion}
20162section below for details.
20163
20164@item text
20165@item textfile
20166Define the text to be rendered. In case neither is specified, no QR is encoded
20167(just an empty colored frame).
20168
20169In case expansion is enabled, the text is treated as a text template, using the
20170qrencode expansion mechanism. See the @ref{qrencode_text_expansion,,qrencode
20171Text expansion} section below for details.
20172
20173@item background_color, bc
20174@item foreground_color, fc
20175Set the QR code and background color. The default value of
20176@var{foreground_color} is "black", the default value of @var{background_color}
20177is "white".
20178
20179For the syntax of the color options, check the @ref{color syntax,,"Color"
20180section in the ffmpeg-utils manual,ffmpeg-utils}.
20181@end table
20182
20183@anchor{qrencode_expressions}
20184@subsection qrencode Expressions
20185
20186The expressions set by the options contain the following constants and functions.
20187
20188@table @option
20189@item dar
20190input display aspect ratio, it is the same as (@var{w} / @var{h}) * @var{sar}
20191
20192@item duration
20193the current frame's duration, in seconds
20194
20195@item hsub
20196@item vsub
20197horizontal and vertical chroma subsample values. For example for the
20198pixel format "yuv422p" @var{hsub} is 2 and @var{vsub} is 1.
20199
20200@item main_h, H
20201the input height
20202
20203@item main_w, W
20204the input width
20205
20206@item n
20207the number of input frame, starting from 0
20208
20209@item pict_type
20210a number representing the picture type
20211
20212@item qr_w, w
20213the width of the encoded QR code
20214
20215@item rendered_qr_w, q
20216@item rendered_padded_qr_w, Q
20217the width of the rendered QR code, without and without padding.
20218
20219These parameters allow the @var{q} and @var{Q} expressions to refer to each
20220other, so you can for example specify @code{q=3/4*Q}.
20221
20222@item rand(min, max)
20223return a random number included between @var{min} and @var{max}
20224
20225@item sar
20226the input sample aspect ratio
20227
20228@item t
20229timestamp expressed in seconds, NAN if the input timestamp is unknown
20230
20231@item x
20232@item y
20233the x and y offset coordinates where the text is drawn.
20234
20235These parameters allow the @var{x} and @var{y} expressions to refer to each
20236other, so you can for example specify @code{y=x/dar}.
20237@end table
20238
20239@anchor{qrencode_text_expansion}
20240@subsection qrencode Text expansion
20241
20242If @option{expansion} is set to @code{none}, the text is printed verbatim.
20243
20244If @option{expansion} is set to @code{normal} (which is the default),
20245the following expansion mechanism is used.
20246
20247The backslash character @samp{\}, followed by any character, always expands to
20248the second character.
20249
20250Sequences of the form @code{%@{...@}} are expanded. The text between the
20251braces is a function name, possibly followed by arguments separated by ':'.
20252If the arguments contain special characters or delimiters (':' or '@}'),
20253they should be escaped.
20254
20255Note that they probably must also be escaped as the value for the @option{text}
20256option in the filter argument string and as the filter argument in the
20257filtergraph description, and possibly also for the shell, that makes up to four
20258levels of escaping; using a text file with the @option{textfile} option avoids
20259these problems.
20260
20261The following functions are available:
20262
20263@table @command
20264@item n, frame_num
20265return the frame number
20266
20267@item pts
20268Return the presentation timestamp of the current frame.
20269
20270It can take up to two arguments.
20271
20272The first argument is the format of the timestamp; it defaults to @code{flt} for
20273seconds as a decimal number with microsecond accuracy; @code{hms} stands for a
20274formatted @var{[-]HH:MM:SS.mmm} timestamp with millisecond accuracy.
20275@code{gmtime} stands for the timestamp of the frame formatted as UTC time;
20276@code{localtime} stands for the timestamp of the frame formatted as local time
20277zone time. If the format is set to @code{hms24hh}, the time is formatted in 24h
20278format (00-23).
20279
20280The second argument is an offset added to the timestamp.
20281
20282If the format is set to @code{localtime} or @code{gmtime}, a third argument may
20283be supplied: a @code{strftime} C function format string. By default,
20284@var{YYYY-MM-DD HH:MM:SS} format will be used.
20285
20286@item expr, e
20287Evaluate the expression's value and output as a double.
20288
20289It must take one argument specifying the expression to be evaluated, accepting
20290the constants and functions defined in @ref{qrencode_expressions}.
20291
20292@item expr_formatted, ef
20293Evaluate the expression's value and output as a formatted string.
20294
20295The first argument is the expression to be evaluated, just as for the @var{expr} function.
20296The second argument specifies the output format. Allowed values are @samp{x},
20297@samp{X}, @samp{d} and @samp{u}. They are treated exactly as in the
20298@code{printf} function.
20299The third parameter is optional and sets the number of positions taken by the output.
20300It can be used to add padding with zeros from the left.
20301
20302@item gmtime
20303The time at which the filter is running, expressed in UTC.
20304It can accept an argument: a @code{strftime} C function format string.
20305The format string is extended to support the variable @var{%[1-6]N}
20306which prints fractions of the second with optionally specified number of digits.
20307
20308@item localtime
20309The time at which the filter is running, expressed in the local time zone.
20310It can accept an argument: a @code{strftime} C function format string.
20311The format string is extended to support the variable @var{%[1-6]N}
20312which prints fractions of the second with optionally specified number of digits.
20313
20314@item metadata
20315Frame metadata. Takes one or two arguments.
20316
20317The first argument is mandatory and specifies the metadata key.
20318
20319The second argument is optional and specifies a default value, used when the
20320metadata key is not found or empty.
20321
20322Available metadata can be identified by inspecting entries starting with TAG
20323included within each frame section printed by running @code{ffprobe
20324-show_frames}.
20325
20326String metadata generated in filters leading to the qrencode filter are also
20327available.
20328
20329@item rand(min, max)
20330return a random number included between @var{min} and @var{max}
20331@end table
20332
20333@subsection Examples
20334
20335@itemize
20336@item
20337Generate a QR code encoding the specified text with the default size, overalaid
20338in the top left corner of the input video, with the default size:
20339@example
20340qrencode=text=www.ffmpeg.org
20341@end example
20342
20343@item
20344Same as below, but select blue on pink colors:
20345@example
20346qrencode=text=www.ffmpeg.org:bc=pink@@0.5:fc=blue
20347@end example
20348
20349@item
20350Place the QR code in the bottom right corner of the input video:
20351@example
20352qrencode=text=www.ffmpeg.org:x=W-Q:y=H-Q
20353@end example
20354
20355@item
20356Generate a QR code with width of 200 pixels and padding, making the padded width
203574/3 of the QR code width:
20358@example
20359qrencode=text=www.ffmpeg.org:q=200:Q=4/3*q
20360@end example
20361
20362@item
20363Generate a QR code with padded width of 200 pixels and padding, making the QR
20364code width 3/4 of the padded width:
20365@example
20366qrencode=text=www.ffmpeg.org:Q=200:q=3/4*Q
20367@end example
20368
20369@item
20370Make the QR code a fraction of the input video width:
20371@example
20372qrencode=text=www.ffmpeg.org:q=W/5
20373@end example
20374
20375@item
20376Generate a QR code encoding the frame number:
20377@example
20378qrencode=text=%@{n@}
20379@end example
20380
20381@item
20382Generate a QR code encoding the GMT timestamp:
20383@example
20384qrencode=text=%@{gmtime@}
20385@end example
20386
20387@item
20388Generate a QR code encoding the timestamp expressed as a float:
20389@example
20390qrencode=text=%@{pts@}
20391@end example
20392
20393@end itemize
20394
20099@section random20395@section random
20100 20396
20101Flush video frames from internal cache of frames into a random order.20397Flush 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 · 8647 unmodified lines
28749@end example29045@end example
28750@end itemize29046@end itemize
28751 29047
29048@section qrencodesrc
29049
29050Generate a QR code using the libqrencode library (see
29051@url{https://fukuchi.org/works/qrencode/}).
29052
29053To enable the compilation of this source, you need to configure FFmpeg with with
29054@code{--enable-libqrencode}.
29055
29056The QR code is generated from the provided text or text pattern. The
29057corresponding QR code is scaled and put in the video output according to the
29058specified output size options.
29059
29060In case no text is specified, the QR code is not generated, but an empty colored
29061output is returned instead.
29062
29063This source accepts the following options:
29064
29065@table @option
29066
29067@item qrcode_width, q
29068@item padded_qrcode_width, Q
29069Specify an expression for the width of the rendered QR code, with and without
29070padding. The @var{qrcode_width} expression can reference the value set by the
29071@var{padded_qrcode_width} expression, and vice versa.
29072By default @var{padded_qrcode_width} is set to @var{qrcode_width}, meaning that
29073there is no padding.
29074
29075These expressions are evaluated only once, when initializing the source.
29076See the @ref{qrencode_expressions,,qrencode Expressions} section for details.
29077
29078Note that some of the constants are missing for the source (for example the
29079@var{x} or @var{t} or ¸@var{n}), since they only makes sense when evaluating the
29080expression for each frame rather than at initialization time.
29081
29082@item rate, r
29083Specify the frame rate of the sourced video, as the number of frames
29084generated per second. It has to be a string in the format
29085@var{frame_rate_num}/@var{frame_rate_den}, an integer number, a floating point
29086number or a valid video frame rate abbreviation. The default value is
29087"25".
29088
29089@item case_sensitive, cs
29090Instruct libqrencode to use case sensitive encoding. This is enabled by
29091default. This can be disabled to reduce the QR encoding size.
29092
29093@item level, l
29094Specify the QR encoding error correction level. With an higher correction level,
29095the encoding size will increase but the code will be more robust to corruption.
29096Lower level is @var{L}.
29097
29098It accepts the following values:
29099@table @samp
29100@item L
29101@item M
29102@item Q
29103@item H
29104@end table
29105
29106@item expansion
29107Select how the input text is expanded. Can be either @code{none}, or
29108@code{normal} (default). See the @ref{qrencode_text_expansion,,qrencode Text expansion}
29109section for details.
29110
29111@item text
29112@item textfile
29113Define the text to be rendered. In case neither is specified, no QR is encoded
29114(just an empty colored frame).
29115
29116In case expansion is enabled, the text is treated as a text template, using the
29117qrencode expansion mechanism. See the @ref{qrencode_text_expansion,,qrencode
29118Text expansion} section for details.
29119
29120@item background_color, bc
29121@item foreground_color, fc
29122Set the QR code and background color. The default value of
29123@var{foreground_color} is "black", the default value of @var{background_color}
29124is "white".
29125
29126For the syntax of the color options, check the @ref{color syntax,,"Color"
29127section in the ffmpeg-utils manual,ffmpeg-utils}.
29128@end table
29129
29130@subsection Examples
29131
29132@itemize
29133@item
29134Generate a QR code encoding the specified text with the default size:
29135@example
29136qrencodesrc=text=www.ffmpeg.org
29137@end example
29138
29139@item
29140Same as below, but select blue on pink colors:
29141@example
29142qrencodesrc=text=www.ffmpeg.org:bc=pink:fc=blue
29143@end example
29144
29145@item
29146Generate a QR code with width of 200 pixels and padding, making the padded width
291474/3 of the QR code width:
29148@example
29149qrencodesrc=text=www.ffmpeg.org:q=200:Q=4/3*q
29150@end example
29151
29152@item
29153Generate a QR code with padded width of 200 pixels and padding, making the QR
29154code width 3/4 of the padded width:
29155@example
29156qrencodesrc=text=www.ffmpeg.org:Q=200:q=3/4*Q
29157@end example
29158
29159@item
29160Generate a QR code encoding the frame number:
29161@example
29162qrencodesrc=text=%@{n@}
29163@end example
29164
29165@item
29166Generate a QR code encoding the GMT timestamp:
29167@example
29168qrencodesrc=text=%@{gmtime@}
29169@end example
29170
29171@item
29172Generate a QR code encoding the timestamp expressed as a float:
29173@example
29174qrencodesrc=text=%@{pts@}
29175@end example
29176
29177@end itemize
29178
28752@anchor{allrgb}29179@anchor{allrgb}
28753@anchor{allyuv}29180@anchor{allyuv}
28754@anchor{color}29181@anchor{color}
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