ffmpeg-cvslog
Threads by month
- ----- 2026 -----
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2006 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2005 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
January 2024
- 2 participants
- 329 discussions
ffmpeg | branch: master | Stefano Sabatini <stefasab(a)gmail.com> | Wed Dec 27 20:12:51 2023 +0100| [933c7ff0b96f72e71155d91e66f726e0beb4e824] | committer: Stefano Sabatini
lavfi/setpts: use macro to access variables array
> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=933c7ff0b96f72e71…
---
libavfilter/setpts.c | 94 +++++++++++++++++++++++++---------------------------
1 file changed, 45 insertions(+), 49 deletions(-)
diff --git a/libavfilter/setpts.c b/libavfilter/setpts.c
index d057fc91c4..88a8d6af86 100644
--- a/libavfilter/setpts.c
+++ b/libavfilter/setpts.c
@@ -103,6 +103,9 @@ typedef struct SetPTSContext {
enum AVMediaType type;
} SetPTSContext;
+#define V(name_) \
+ setpts->var_values[VAR_##name_]
+
static av_cold int init(AVFilterContext *ctx)
{
SetPTSContext *setpts = ctx->priv;
@@ -114,15 +117,15 @@ static av_cold int init(AVFilterContext *ctx)
return ret;
}
- setpts->var_values[VAR_N] = 0.0;
- setpts->var_values[VAR_S] = 0.0;
- setpts->var_values[VAR_PREV_INPTS] = NAN;
- setpts->var_values[VAR_PREV_INT] = NAN;
- setpts->var_values[VAR_PREV_OUTPTS] = NAN;
- setpts->var_values[VAR_PREV_OUTT] = NAN;
- setpts->var_values[VAR_STARTPTS] = NAN;
- setpts->var_values[VAR_STARTT] = NAN;
- setpts->var_values[VAR_T_CHANGE] = NAN;
+ V(N) = 0.0;
+ V(S) = 0.0;
+ V(PREV_INPTS) = NAN;
+ V(PREV_INT) = NAN;
+ V(PREV_OUTPTS) = NAN;
+ V(PREV_OUTT) = NAN;
+ V(STARTPTS) = NAN;
+ V(STARTT) = NAN;
+ V(T_CHANGE) = NAN;
return 0;
}
@@ -132,22 +135,18 @@ static int config_input(AVFilterLink *inlink)
SetPTSContext *setpts = ctx->priv;
setpts->type = inlink->type;
- setpts->var_values[VAR_TB] = av_q2d(inlink->time_base);
- setpts->var_values[VAR_RTCSTART] = av_gettime();
+ V(TB) = av_q2d(inlink->time_base);
+ V(RTCSTART) = av_gettime();
- setpts->var_values[VAR_SR] =
- setpts->var_values[VAR_SAMPLE_RATE] =
+ V(SR) = V(SAMPLE_RATE) =
setpts->type == AVMEDIA_TYPE_AUDIO ? inlink->sample_rate : NAN;
- setpts->var_values[VAR_FRAME_RATE] =
- setpts->var_values[VAR_FR] = inlink->frame_rate.num &&
- inlink->frame_rate.den ?
- av_q2d(inlink->frame_rate) : NAN;
+ V(FRAME_RATE) = V(FR) =
+ inlink->frame_rate.num && inlink->frame_rate.den ?
+ av_q2d(inlink->frame_rate) : NAN;
av_log(inlink->src, AV_LOG_VERBOSE, "TB:%f FRAME_RATE:%f SAMPLE_RATE:%f\n",
- setpts->var_values[VAR_TB],
- setpts->var_values[VAR_FRAME_RATE],
- setpts->var_values[VAR_SAMPLE_RATE]);
+ V(TB), V(FRAME_RATE), V(SAMPLE_RATE));
return 0;
}
@@ -162,28 +161,28 @@ static inline char *double2int64str(char *buf, double v)
static double eval_pts(SetPTSContext *setpts, AVFilterLink *inlink, AVFrame *frame, int64_t pts)
{
- if (isnan(setpts->var_values[VAR_STARTPTS])) {
- setpts->var_values[VAR_STARTPTS] = TS2D(pts);
- setpts->var_values[VAR_STARTT ] = TS2T(pts, inlink->time_base);
+ if (isnan(V(STARTPTS))) {
+ V(STARTPTS) = TS2D(pts);
+ V(STARTT ) = TS2T(pts, inlink->time_base);
}
- if (isnan(setpts->var_values[VAR_T_CHANGE])) {
- setpts->var_values[VAR_T_CHANGE] = TS2T(pts, inlink->time_base);
+ if (isnan(V(T_CHANGE))) {
+ V(T_CHANGE) = TS2T(pts, inlink->time_base);
}
- setpts->var_values[VAR_PTS ] = TS2D(pts);
- setpts->var_values[VAR_T ] = TS2T(pts, inlink->time_base);
+ V(PTS ) = TS2D(pts);
+ V(T ) = TS2T(pts, inlink->time_base);
#if FF_API_FRAME_PKT
FF_DISABLE_DEPRECATION_WARNINGS
- setpts->var_values[VAR_POS ] = !frame || frame->pkt_pos == -1 ? NAN : frame->pkt_pos;
+ V(POS ) = !frame || frame->pkt_pos == -1 ? NAN : frame->pkt_pos;
FF_ENABLE_DEPRECATION_WARNINGS
#endif
- setpts->var_values[VAR_RTCTIME ] = av_gettime();
+ V(RTCTIME ) = av_gettime();
if (frame) {
if (inlink->type == AVMEDIA_TYPE_VIDEO) {
- setpts->var_values[VAR_INTERLACED] = !!(frame->flags & AV_FRAME_FLAG_INTERLACED);
+ V(INTERLACED) = !!(frame->flags & AV_FRAME_FLAG_INTERLACED);
} else if (inlink->type == AVMEDIA_TYPE_AUDIO) {
- setpts->var_values[VAR_S] = frame->nb_samples;
- setpts->var_values[VAR_NB_SAMPLES] = frame->nb_samples;
+ V(S) = frame->nb_samples;
+ V(NB_SAMPLES) = frame->nb_samples;
}
}
@@ -202,34 +201,32 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
av_log(inlink->dst, AV_LOG_TRACE,
"N:%"PRId64" PTS:%s T:%f",
- (int64_t)setpts->var_values[VAR_N],
- d2istr(setpts->var_values[VAR_PTS]),
- setpts->var_values[VAR_T]);
+ (int64_t)V(N), d2istr(V(PTS)), V(T));
switch (inlink->type) {
case AVMEDIA_TYPE_VIDEO:
av_log(inlink->dst, AV_LOG_TRACE, " INTERLACED:%"PRId64,
- (int64_t)setpts->var_values[VAR_INTERLACED]);
+ (int64_t)V(INTERLACED));
break;
case AVMEDIA_TYPE_AUDIO:
av_log(inlink->dst, AV_LOG_TRACE, " NB_SAMPLES:%"PRId64" NB_CONSUMED_SAMPLES:%"PRId64,
- (int64_t)setpts->var_values[VAR_NB_SAMPLES],
- (int64_t)setpts->var_values[VAR_NB_CONSUMED_SAMPLES]);
+ (int64_t)V(NB_SAMPLES),
+ (int64_t)V(NB_CONSUMED_SAMPLES));
break;
}
av_log(inlink->dst, AV_LOG_TRACE, " -> PTS:%s T:%f\n", d2istr(d), TS2T(d, inlink->time_base));
if (inlink->type == AVMEDIA_TYPE_VIDEO) {
- setpts->var_values[VAR_N] += 1.0;
+ V(N) += 1.0;
} else {
- setpts->var_values[VAR_N] += frame->nb_samples;
+ V(N) += frame->nb_samples;
}
- setpts->var_values[VAR_PREV_INPTS ] = TS2D(in_pts);
- setpts->var_values[VAR_PREV_INT ] = TS2T(in_pts, inlink->time_base);
- setpts->var_values[VAR_PREV_OUTPTS] = TS2D(frame->pts);
- setpts->var_values[VAR_PREV_OUTT] = TS2T(frame->pts, inlink->time_base);
+ V(PREV_INPTS ) = TS2D(in_pts);
+ V(PREV_INT ) = TS2T(in_pts, inlink->time_base);
+ V(PREV_OUTPTS) = TS2D(frame->pts);
+ V(PREV_OUTT) = TS2T(frame->pts, inlink->time_base);
if (setpts->type == AVMEDIA_TYPE_AUDIO) {
- setpts->var_values[VAR_NB_CONSUMED_SAMPLES] += frame->nb_samples;
+ V(NB_CONSUMED_SAMPLES) += frame->nb_samples;
}
return ff_filter_frame(inlink->dst->outputs[0], frame);
}
@@ -256,9 +253,7 @@ static int activate(AVFilterContext *ctx)
double d = eval_pts(setpts, inlink, NULL, pts);
av_log(ctx, AV_LOG_TRACE, "N:EOF PTS:%s T:%f -> PTS:%s T:%f\n",
- d2istr(setpts->var_values[VAR_PTS]),
- setpts->var_values[VAR_T],
- d2istr(d), TS2T(d, inlink->time_base));
+ d2istr(V(PTS)), V(T), d2istr(d), TS2T(d, inlink->time_base));
ff_outlink_set_status(outlink, status, D2TS(d));
return 0;
}
@@ -296,7 +291,7 @@ static int process_command(AVFilterContext *ctx, const char *cmd, const char *ar
} else {
av_expr_free(setpts->expr);
setpts->expr = new_expr;
- setpts->var_values[VAR_T_CHANGE] = NAN;
+ V(T_CHANGE) = NAN;
}
} else {
ret = AVERROR(EINVAL);
@@ -304,6 +299,7 @@ static int process_command(AVFilterContext *ctx, const char *cmd, const char *ar
return ret;
}
+#undef V
#define OFFSET(x) offsetof(SetPTSContext, x)
#define V AV_OPT_FLAG_VIDEO_PARAM
1
0
ffmpeg | branch: master | Stefano Sabatini <stefasab(a)gmail.com> | Tue Dec 26 16:19:10 2023 +0100| [030e1401451200566a5303f35cbe1456e31dd81e] | committer: Stefano Sabatini
lavfi: add quirc filter
> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=030e1401451200566…
---
Changelog | 1 +
configure | 4 ++
doc/filters.texi | 28 ++++++++
libavfilter/Makefile | 1 +
libavfilter/allfilters.c | 1 +
libavfilter/vf_quirc.c | 183 +++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 218 insertions(+)
diff --git a/Changelog b/Changelog
index b483bb9c69..424bfc11af 100644
--- a/Changelog
+++ b/Changelog
@@ -14,6 +14,7 @@ version <next>:
- D3D12VA hardware accelerated H264, HEVC, VP9, AV1, MPEG-2 and VC1 decoding
- tiltandshift filter
- qrencode filter and qrencodesrc source
+- quirc filter
version 6.1:
- libaribcaption decoder
diff --git a/configure b/configure
index b1853204ef..dc400db176 100755
--- a/configure
+++ b/configure
@@ -257,6 +257,7 @@ External library support:
--enable-libplacebo enable libplacebo library [no]
--enable-libpulse enable Pulseaudio input via libpulse [no]
--enable-libqrencode enable QR encode generation via libqrencode [no]
+ --enable-libquirc enable QR decoding via libquirc [no]
--enable-librabbitmq enable RabbitMQ library [no]
--enable-librav1e enable AV1 encoding via rav1e [no]
--enable-librist enable RIST via librist [no]
@@ -1883,6 +1884,7 @@ EXTERNAL_LIBRARY_LIST="
libplacebo
libpulse
libqrencode
+ libquirc
librabbitmq
librav1e
librist
@@ -3793,6 +3795,7 @@ ocv_filter_deps="libopencv"
openclsrc_filter_deps="opencl"
qrencode_filter_deps="libqrencode"
qrencodesrc_filter_deps="libqrencode"
+quirc_filter_deps="libquirc"
overlay_opencl_filter_deps="opencl"
overlay_qsv_filter_deps="libmfx"
overlay_qsv_filter_select="qsvvpp"
@@ -6845,6 +6848,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 libquirc && require libquirc quirc.h quirc_decode -lquirc
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
diff --git a/doc/filters.texi b/doc/filters.texi
index 9b120ce0a1..f54359d533 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -20392,6 +20392,34 @@ qrencode=text=%@{pts@}
@end itemize
+@section quirc
+
+Identify and decode a QR code using the libquirc library (see
+@url{https://github.com/dlbeer/quirc/}), and print the identified QR codes
+positions and payload as metadata.
+
+To enable the compilation of this filter, you need to configure FFmpeg with with
+@code{--enable-libquirc}.
+
+For each found QR code in the input video, some metadata entries are added with
+the prefix @var{lavfi.quirc.N}, where @var{N} is the index, starting from 0,
+associated to the QR code.
+
+A description of each metadata value follows:
+
+@table @option
+@item lavfi.quirc.count
+the number of found QR codes, it is not set in case none was found
+
+@item lavfi.quirc.N.corner.M.x
+@item lavfi.quirc.N.coreer.M.y
+the x/y positions of the four corners of the square containing the QR code,
+where @var{M} is the index of the corner starting from 0
+
+@item lavfi.quirc.N.payload
+the payload of the QR code
+@end table
+
@section random
Flush video frames from internal cache of frames into a random order.
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 31371ceb1a..f65fb9a5a7 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -437,6 +437,7 @@ OBJS-$(CONFIG_PSEUDOCOLOR_FILTER) += vf_pseudocolor.o
OBJS-$(CONFIG_PSNR_FILTER) += vf_psnr.o framesync.o
OBJS-$(CONFIG_PULLUP_FILTER) += vf_pullup.o
OBJS-$(CONFIG_QP_FILTER) += vf_qp.o
+OBJS-$(CONFIG_QUIRC_FILTER) += vf_quirc.o
OBJS-$(CONFIG_RANDOM_FILTER) += vf_random.o
OBJS-$(CONFIG_READEIA608_FILTER) += vf_readeia608.o
OBJS-$(CONFIG_READVITC_FILTER) += vf_readvitc.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 20feb37967..b8570dbab2 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -412,6 +412,7 @@ 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_quirc;
extern const AVFilter ff_vf_random;
extern const AVFilter ff_vf_readeia608;
extern const AVFilter ff_vf_readvitc;
diff --git a/libavfilter/vf_quirc.c b/libavfilter/vf_quirc.c
new file mode 100644
index 0000000000..62eb29b7ce
--- /dev/null
+++ b/libavfilter/vf_quirc.c
@@ -0,0 +1,183 @@
+/*
+ * 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 decoder video filter
+ *
+ * Use libquirc library to decode the content of QR codes, and put the decoded
+ * content to metadata. See:
+ * https://github.com/dlbeer/quirc
+ */
+
+#include "libavutil/imgutils.h"
+#include "libavutil/opt.h"
+#include "avfilter.h"
+#include "formats.h"
+#include "video.h"
+#include <quirc.h>
+
+typedef struct QuircContext {
+ const AVClass *class;
+
+ struct quirc *quirc;
+} QuircContext;
+
+static av_cold int init(AVFilterContext *ctx)
+{
+ QuircContext *quirc = ctx->priv;
+
+ quirc->quirc = quirc_new();
+ if (!quirc->quirc) {
+ return AVERROR(ENOMEM);
+ }
+
+ return 0;
+}
+
+static av_cold void uninit(AVFilterContext *ctx)
+{
+ QuircContext *quirc = ctx->priv;
+
+ quirc_destroy(quirc->quirc);
+}
+
+static int config_input(AVFilterLink *inlink)
+{
+ AVFilterContext *ctx = inlink->dst;
+ QuircContext *quirc = ctx->priv;
+ int err;
+
+ err = quirc_resize(quirc->quirc, inlink->w, inlink->h);
+ if (err == -1) {
+ return AVERROR(ENOMEM);
+ }
+
+ return 0;
+}
+
+static int query_formats(AVFilterContext *ctx)
+{
+ static const enum AVPixelFormat pix_fmts[] = {
+ AV_PIX_FMT_GRAY8,
+ AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUV411P,
+ AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P,
+ AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV444P,
+ AV_PIX_FMT_NV12, AV_PIX_FMT_NV21,
+ AV_PIX_FMT_YUVJ411P, AV_PIX_FMT_YUVJ420P,
+ AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ444P,
+ AV_PIX_FMT_YUVJ440P,
+ AV_PIX_FMT_NONE
+ };
+
+ return ff_set_common_formats_from_list(ctx, pix_fmts);
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+ AVFilterContext *ctx = inlink->dst;
+ AVFilterLink *outlink = ctx->outputs[0];
+ QuircContext *quirc = ctx->priv;
+ int codes_count;
+ uint8_t *image;
+
+ /* copy input image to quirc buffer */
+ image = quirc_begin(quirc->quirc, NULL, NULL);
+ av_image_copy_plane(image, inlink->w,
+ frame->data[0], frame->linesize[0], inlink->w, inlink->h);
+
+ quirc_end(quirc->quirc);
+
+ codes_count = quirc_count(quirc->quirc);
+ av_log(ctx, AV_LOG_VERBOSE,
+ "Found count %d codes in image #%ld\n", codes_count, inlink->frame_count_out);
+
+ if (codes_count) {
+ int i, j;
+ AVDictionary **metadata = &frame->metadata;
+
+ av_dict_set_int(metadata, "lavfi.quirc.count", codes_count, 0);
+
+ for (i = 0; i < codes_count; i++) {
+ struct quirc_code code;
+ struct quirc_data data;
+ quirc_decode_error_t err;
+ char metadata_key[64];
+
+ quirc_extract(quirc->quirc, i, &code);
+
+ err = quirc_decode(&code, &data);
+ if (err) {
+ av_log(ctx, AV_LOG_WARNING,
+ "Failed to decode image: %s\n", quirc_strerror(err));
+ continue;
+ }
+
+ for (j = 0; j < 4; j++) {
+ struct quirc_point corner = code.corners[j];
+
+#define SET_CORNER_METADATA(key_, value_) \
+ snprintf(metadata_key, sizeof(metadata_key)-1, \
+ "lavfi.quirc.%d.corner.%d." #key_, i, j); \
+ av_dict_set_int(metadata, metadata_key, value_, 0)
+
+ SET_CORNER_METADATA(x, corner.x);
+ SET_CORNER_METADATA(y, corner.y);
+ }
+
+ snprintf(metadata_key, sizeof(metadata_key)-1, "lavfi.quirc.%d.payload", i); \
+ av_dict_set(metadata, metadata_key, data.payload, 0);
+
+ av_log(ctx, AV_LOG_INFO,
+ "Found QR code at position %d,%d - %d,%d with payload: %s\n",
+ code.corners[0].x, code.corners[0].y,
+ code.corners[3].x, code.corners[3].y, data.payload);
+ }
+ }
+
+ return ff_filter_frame(outlink, frame);
+}
+
+static const AVClass quirc_class = {
+ .class_name = "quirc",
+ .version = LIBAVUTIL_VERSION_INT,
+ .category = AV_CLASS_CATEGORY_FILTER
+};
+
+static const AVFilterPad inputs[] = {
+ {
+ .name = "default",
+ .type = AVMEDIA_TYPE_VIDEO,
+ .filter_frame = filter_frame,
+ .config_props = config_input
+ },
+};
+
+const AVFilter ff_vf_quirc = {
+ .name = "quirc",
+ .description = NULL_IF_CONFIG_SMALL("Decode and show QR codes content."),
+ .priv_size = sizeof(QuircContext),
+ .priv_class = &quirc_class,
+ .init = init,
+ .uninit = uninit,
+ FILTER_INPUTS(inputs),
+ FILTER_OUTPUTS(ff_video_default_filterpad),
+ FILTER_QUERY_FUNC(query_formats),
+ .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC |
+ AVFILTER_FLAG_METADATA_ONLY,
+};
+
1
0
ffmpeg | branch: master | Stefano Sabatini <stefasab(a)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=899302bb5f6ac0484…
---
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
+@section qrencode
+Generate a QR code using the libqrencode library (see
+@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
+@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:
+
+@table @option
+
+@item qrcode_width, q
+@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
+@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.
+
+@item x
+@item y
+Specify an expression for positioning the padded QR code top-left corner. The
+@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.
+
+@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.
+
+@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:
+@table @samp
+@item L
+@item M
+@item Q
+@item H
+@end table
+
+@item expansion
+Select how the input text is expanded. Can be either @code{none}, or
+@code{normal} (default). See the @ref{qrencode_text_expansion,,qrencode Text expansion}
+section below for details.
+
+@item text
+@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.
+
+@item background_color, bc
+@item foreground_color, fc
+Set the QR code and background color. The default value of
+@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}.
+@end table
+
+@anchor{qrencode_expressions}
+@subsection qrencode Expressions
+
+The expressions set by the options contain the following constants and functions.
+
+@table @option
+@item dar
+input display aspect ratio, it is the same as (@var{w} / @var{h}) * @var{sar}
+
+@item duration
+the current frame's duration, in seconds
+
+@item hsub
+@item vsub
+horizontal and vertical chroma subsample values. For example for the
+pixel format "yuv422p" @var{hsub} is 2 and @var{vsub} is 1.
+
+@item main_h, H
+the input height
+
+@item main_w, W
+the input width
+
+@item n
+the number of input frame, starting from 0
+
+@item pict_type
+a number representing the picture type
+
+@item qr_w, w
+the width of the encoded QR code
+
+@item rendered_qr_w, q
+@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}.
+
+@item rand(min, max)
+return a random number included between @var{min} and @var{max}
+
+@item sar
+the input sample aspect ratio
+
+@item t
+timestamp expressed in seconds, NAN if the input timestamp is unknown
+
+@item x
+@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}.
+@end table
+
+@anchor{qrencode_text_expansion}
+@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:
+
+@table @command
+@item n, frame_num
+return the frame number
+
+@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.
+@code{gmtime} stands for the timestamp of the frame formatted as UTC time;
+@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,
+@var{YYYY-MM-DD HH:MM:SS} format will be used.
+
+@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}.
+
+@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},
+@samp{X}, @samp{d} and @samp{u}. They are treated exactly as in the
+@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.
+
+@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.
+
+@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.
+
+@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.
+
+@item rand(min, max)
+return a random number included between @var{min} and @var{max}
+@end table
+
+@subsection Examples
+
+@itemize
+@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:
+@example
+qrencode=text=www.ffmpeg.org
+@end example
+
+@item
+Same as below, but select blue on pink colors:
+@example
+qrencode=text=www.ffmpeg.org:bc=pink@@0.5:fc=blue
+@end example
+
+@item
+Place the QR code in the bottom right corner of the input video:
+@example
+qrencode=text=www.ffmpeg.org:x=W-Q:y=H-Q
+@end example
+
+@item
+Generate a QR code with width of 200 pixels and padding, making the padded width
+4/3 of the QR code width:
+@example
+qrencode=text=www.ffmpeg.org:q=200:Q=4/3*q
+@end example
+
+@item
+Generate a QR code with padded width of 200 pixels and padding, making the QR
+code width 3/4 of the padded width:
+@example
+qrencode=text=www.ffmpeg.org:Q=200:q=3/4*Q
+@end example
+
+@item
+Make the QR code a fraction of the input video width:
+@example
+qrencode=text=www.ffmpeg.org:q=W/5
+@end example
+
+@item
+Generate a QR code encoding the frame number:
+@example
+qrencode=text=%@{n@}
+@end example
+
+@item
+Generate a QR code encoding the GMT timestamp:
+@example
+qrencode=text=%@{gmtime@}
+@end example
+
+@item
+Generate a QR code encoding the timestamp expressed as a float:
+@example
+qrencode=text=%@{pts@}
+@end example
+
+@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
+@section qrencodesrc
+
+Generate a QR code using the libqrencode library (see
+@url{https://fukuchi.org/works/qrencode/}).
+
+To enable the compilation of this source, you need to configure FFmpeg with with
+@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:
+
+@table @option
+
+@item qrcode_width, q
+@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
+@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
+@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.
+
+@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
+@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".
+
+@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.
+
+@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:
+@table @samp
+@item L
+@item M
+@item Q
+@item H
+@end table
+
+@item expansion
+Select how the input text is expanded. Can be either @code{none}, or
+@code{normal} (default). See the @ref{qrencode_text_expansion,,qrencode Text expansion}
+section for details.
+
+@item text
+@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.
+
+@item background_color, bc
+@item foreground_color, fc
+Set the QR code and background color. The default value of
+@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}.
+@end table
+
+@subsection Examples
+
+@itemize
+@item
+Generate a QR code encoding the specified text with the default size:
+@example
+qrencodesrc=text=www.ffmpeg.org
+@end example
+
+@item
+Same as below, but select blue on pink colors:
+@example
+qrencodesrc=text=www.ffmpeg.org:bc=pink:fc=blue
+@end example
+
+@item
+Generate a QR code with width of 200 pixels and padding, making the padded width
+4/3 of the QR code width:
+@example
+qrencodesrc=text=www.ffmpeg.org:q=200:Q=4/3*q
+@end example
+
+@item
+Generate a QR code with padded width of 200 pixels and padding, making the QR
+code width 3/4 of the padded width:
+@example
+qrencodesrc=text=www.ffmpeg.org:Q=200:q=3/4*Q
+@end example
+
+@item
+Generate a QR code encoding the frame number:
+@example
+qrencodesrc=text=%@{n@}
+@end example
+
+@item
+Generate a QR code encoding the GMT timestamp:
+@example
+qrencodesrc=text=%@{gmtime@}
+@end example
+
+@item
+Generate a QR code encoding the timestamp expressed as a float:
+@example
+qrencodesrc=text=%@{pts@}
+@end example
+
+@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
1
0
ffmpeg | branch: master | Stefano Sabatini <stefasab(a)gmail.com> | Mon Nov 20 01:13:17 2023 +0100| [732fb122e66cf4d0d9cec2eed00e088ea6a3b97d] | committer: Stefano Sabatini
lavfi: introduce textutils
Generalize drawtext utilities to make them usable in other filters.
This will be needed to introduce the QR code source and filter without
duplicating functionality.
> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=732fb122e66cf4d0d…
---
libavfilter/Makefile | 2 +-
libavfilter/textutils.c | 382 +++++++++++++++++++++++++++++++++
libavfilter/textutils.h | 229 ++++++++++++++++++++
libavfilter/vf_drawtext.c | 533 ++++++++++++----------------------------------
4 files changed, 743 insertions(+), 403 deletions(-)
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index afc7bc1566..badaa43859 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -291,7 +291,7 @@ OBJS-$(CONFIG_DOUBLEWEAVE_FILTER) += vf_weave.o
OBJS-$(CONFIG_DRAWBOX_FILTER) += vf_drawbox.o
OBJS-$(CONFIG_DRAWGRAPH_FILTER) += f_drawgraph.o
OBJS-$(CONFIG_DRAWGRID_FILTER) += vf_drawbox.o
-OBJS-$(CONFIG_DRAWTEXT_FILTER) += vf_drawtext.o
+OBJS-$(CONFIG_DRAWTEXT_FILTER) += vf_drawtext.o textutils.o
OBJS-$(CONFIG_EDGEDETECT_FILTER) += vf_edgedetect.o edge_common.o
OBJS-$(CONFIG_ELBG_FILTER) += vf_elbg.o
OBJS-$(CONFIG_ENTROPY_FILTER) += vf_entropy.o
diff --git a/libavfilter/textutils.c b/libavfilter/textutils.c
new file mode 100644
index 0000000000..ef658d04a2
--- /dev/null
+++ b/libavfilter/textutils.c
@@ -0,0 +1,382 @@
+/*
+ * 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
+ * text expansion utilities
+ */
+
+#include <fenv.h>
+#include <math.h>
+#include <string.h>
+
+#include "textutils.h"
+#include "libavutil/avutil.h"
+#include "libavutil/error.h"
+#include "libavutil/file.h"
+#include "libavutil/time.h"
+
+static int ff_expand_text_function_internal(FFExpandTextContext *expand_text, AVBPrint *bp,
+ char *name, unsigned argc, char **argv)
+{
+ void *log_ctx = expand_text->log_ctx;
+ FFExpandTextFunction *functions = expand_text->functions;
+ unsigned i;
+
+ for (i = 0; i < expand_text->functions_nb; i++) {
+ if (strcmp(name, functions[i].name))
+ continue;
+ if (argc < functions[i].argc_min) {
+ av_log(log_ctx, AV_LOG_ERROR, "%%{%s} requires at least %d arguments\n",
+ name, functions[i].argc_min);
+ return AVERROR(EINVAL);
+ }
+ if (argc > functions[i].argc_max) {
+ av_log(log_ctx, AV_LOG_ERROR, "%%{%s} requires at most %d arguments\n",
+ name, functions[i].argc_max);
+ return AVERROR(EINVAL);
+ }
+ break;
+ }
+ if (i >= expand_text->functions_nb) {
+ av_log(log_ctx, AV_LOG_ERROR, "%%{%s} is not known\n", name);
+ return AVERROR(EINVAL);
+ }
+
+ return functions[i].func(log_ctx, bp, name, argc, argv);
+}
+
+/**
+ * Expand text template pointed to by *rtext.
+ *
+ * Expand text template defined in text using the logic defined in a text
+ * expander object.
+ *
+ * This function expects the text to be in the format %{FUNCTION_NAME[:PARAMS]},
+ * where PARAMS is a sequence of strings separated by : and represents the function
+ * arguments to use for the function evaluation.
+ *
+ * @param text_expander TextExpander object used to expand the text
+ * @param bp BPrint object where the expanded text is written to
+ * @param rtext pointer to pointer to the text to expand, it is updated to point
+ * to the next part of the template to process
+ * @return negative value corresponding to an AVERROR error code in case of
+ * errors, a non-negative value otherwise
+ */
+static int ff_expand_text_function(FFExpandTextContext *expand_text, AVBPrint *bp, char **rtext)
+{
+ void *log_ctx = expand_text->log_ctx;
+ const char *text = *rtext;
+ char *argv[16] = { NULL };
+ unsigned argc = 0, i;
+ int ret;
+
+ if (*text != '{') {
+ av_log(log_ctx, AV_LOG_ERROR, "Stray %% near '%s'\n", text);
+ return AVERROR(EINVAL);
+ }
+ text++;
+ while (1) {
+ if (!(argv[argc++] = av_get_token(&text, ":}"))) {
+ ret = AVERROR(ENOMEM);
+ goto end;
+ }
+ if (!*text) {
+ av_log(log_ctx, AV_LOG_ERROR, "Unterminated %%{} near '%s'\n", *rtext);
+ ret = AVERROR(EINVAL);
+ goto end;
+ }
+ if (argc == FF_ARRAY_ELEMS(argv))
+ av_freep(&argv[--argc]); /* error will be caught later */
+ if (*text == '}')
+ break;
+ text++;
+ }
+
+ if ((ret = ff_expand_text_function_internal(expand_text, bp, argv[0], argc - 1, argv + 1)) < 0)
+ goto end;
+ ret = 0;
+ *rtext = (char *)text + 1;
+
+end:
+ for (i = 0; i < argc; i++)
+ av_freep(&argv[i]);
+ return ret;
+}
+
+int ff_expand_text(FFExpandTextContext *expand_text, char *text, AVBPrint *bp)
+{
+ int ret;
+
+ av_bprint_clear(bp);
+ if (!text)
+ return 0;
+
+ while (*text) {
+ if (*text == '\\' && text[1]) {
+ av_bprint_chars(bp, text[1], 1);
+ text += 2;
+ } else if (*text == '%') {
+ text++;
+ if ((ret = ff_expand_text_function(expand_text, bp, &text)) < 0)
+ return ret;
+ } else {
+ av_bprint_chars(bp, *text, 1);
+ text++;
+ }
+ }
+ if (!av_bprint_is_complete(bp))
+ return AVERROR(ENOMEM);
+ return 0;
+}
+
+int ff_print_pts(void *log_ctx, AVBPrint *bp, double pts, const char *delta,
+ const char *fmt, const char *strftime_fmt)
+{
+ int ret;
+
+ if (delta) {
+ int64_t delta_i;
+ if ((ret = av_parse_time(&delta_i, delta, 1)) < 0) {
+ av_log(log_ctx, AV_LOG_ERROR, "Invalid delta '%s'\n", delta);
+ return ret;
+ }
+ pts += (double)delta_i / AV_TIME_BASE;
+ }
+
+ if (!strcmp(fmt, "flt")) {
+ av_bprintf(bp, "%.6f", pts);
+ } else if (!strcmp(fmt, "hms") ||
+ !strcmp(fmt, "hms24hh")) {
+ if (isnan(pts)) {
+ av_bprintf(bp, " ??:??:??.???");
+ } else {
+ int64_t ms = llrint(pts * 1000);
+ char sign = ' ';
+ if (ms < 0) {
+ sign = '-';
+ ms = -ms;
+ }
+ if (!strcmp(fmt, "hms24hh")) {
+ /* wrap around 24 hours */
+ ms %= 24 * 60 * 60 * 1000;
+ }
+ av_bprintf(bp, "%c%02d:%02d:%02d.%03d", sign,
+ (int)(ms / (60 * 60 * 1000)),
+ (int)(ms / (60 * 1000)) % 60,
+ (int)(ms / 1000) % 60,
+ (int)(ms % 1000));
+ }
+ } else if (!strcmp(fmt, "localtime") ||
+ !strcmp(fmt, "gmtime")) {
+ struct tm tm;
+ time_t ms = (time_t)pts;
+ if (!strcmp(fmt, "localtime"))
+ localtime_r(&ms, &tm);
+ else
+ gmtime_r(&ms, &tm);
+ av_bprint_strftime(bp, av_x_if_null(strftime_fmt, "%Y-%m-%d %H:%M:%S"), &tm);
+ } else {
+ av_log(log_ctx, AV_LOG_ERROR, "Invalid format '%s'\n", fmt);
+ return AVERROR(EINVAL);
+ }
+ return 0;
+}
+
+int ff_print_time(void *log_ctx, AVBPrint *bp,
+ const char *strftime_fmt, char localtime)
+{
+ const char *fmt = av_x_if_null(strftime_fmt, "%Y-%m-%d %H:%M:%S");
+ const char *fmt_begin = fmt;
+ int64_t unow;
+ time_t now;
+ struct tm tm;
+ const char *begin;
+ const char *tmp;
+ int len;
+ int div;
+ AVBPrint fmt_bp;
+
+ av_bprint_init(&fmt_bp, 0, AV_BPRINT_SIZE_UNLIMITED);
+
+ unow = av_gettime();
+ now = unow / 1000000;
+ if (localtime)
+ localtime_r(&now, &tm);
+ else
+ tm = *gmtime_r(&now, &tm);
+
+ // manually parse format for %N (fractional seconds)
+ begin = fmt;
+ while ((begin = strchr(begin, '%'))) {
+ tmp = begin + 1;
+ len = 0;
+
+ // skip escaped "%%"
+ if (*tmp == '%') {
+ begin = tmp + 1;
+ continue;
+ }
+
+ // count digits between % and possible N
+ while (*tmp != '\0' && av_isdigit((int)*tmp)) {
+ len++;
+ tmp++;
+ }
+
+ // N encountered, insert time
+ if (*tmp == 'N') {
+ int num_digits = 3; // default show millisecond [1,6]
+
+ // if digit given, expect [1,6], warn & clamp otherwise
+ if (len == 1) {
+ num_digits = av_clip(*(begin + 1) - '0', 1, 6);
+ } else if (len > 1) {
+ av_log(log_ctx, AV_LOG_WARNING, "Invalid number of decimals for %%N, using default of %i\n", num_digits);
+ }
+
+ len += 2; // add % and N to get length of string part
+
+ div = pow(10, 6 - num_digits);
+
+ av_bprintf(&fmt_bp, "%.*s%0*d", (int)(begin - fmt_begin), fmt_begin, num_digits, (int)(unow % 1000000) / div);
+
+ begin += len;
+ fmt_begin = begin;
+
+ continue;
+ }
+
+ begin = tmp;
+ }
+
+ av_bprintf(&fmt_bp, "%s", fmt_begin);
+ if (!av_bprint_is_complete(&fmt_bp)) {
+ av_log(log_ctx, AV_LOG_WARNING, "Format string truncated at %u/%u.", fmt_bp.size, fmt_bp.len);
+ }
+
+ av_bprint_strftime(bp, fmt_bp.str, &tm);
+
+ av_bprint_finalize(&fmt_bp, NULL);
+
+ return 0;
+}
+
+int ff_print_eval_expr(void *log_ctx, AVBPrint *bp,
+ const char *expr,
+ const char * const *fun_names, const ff_eval_func2 *fun_values,
+ const char * const *var_names, const double *var_values,
+ void *eval_ctx)
+{
+ double res;
+ int ret;
+
+ ret = av_expr_parse_and_eval(&res, expr, var_names, var_values,
+ NULL, NULL, fun_names, fun_values,
+ eval_ctx, 0, log_ctx);
+ if (ret < 0)
+ av_log(log_ctx, AV_LOG_ERROR,
+ "Text expansion expression '%s' is not valid\n",
+ expr);
+ else
+ av_bprintf(bp, "%f", res);
+
+ return ret;
+}
+
+int ff_print_formatted_eval_expr(void *log_ctx, AVBPrint *bp,
+ const char *expr,
+ const char * const *fun_names, const ff_eval_func2 *fun_values,
+ const char * const *var_names, const double *var_values,
+ void *eval_ctx,
+ const char format, int positions)
+{
+ double res;
+ int intval;
+ int ret;
+ char fmt_str[30] = "%";
+
+ ret = av_expr_parse_and_eval(&res, expr, var_names, var_values,
+ NULL, NULL, fun_names, fun_values,
+ eval_ctx, 0, log_ctx);
+ if (ret < 0) {
+ av_log(log_ctx, AV_LOG_ERROR,
+ "Text expansion expression '%s' is not valid\n",
+ expr);
+ return ret;
+ }
+
+ if (!strchr("xXdu", format)) {
+ av_log(log_ctx, AV_LOG_ERROR, "Invalid format '%c' specified,"
+ " allowed values: 'x', 'X', 'd', 'u'\n", format);
+ return AVERROR(EINVAL);
+ }
+
+ feclearexcept(FE_ALL_EXCEPT);
+ intval = res;
+#if defined(FE_INVALID) && defined(FE_OVERFLOW) && defined(FE_UNDERFLOW)
+ if ((ret = fetestexcept(FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW))) {
+ av_log(log_ctx, AV_LOG_ERROR, "Conversion of floating-point result to int failed. Control register: 0x%08x. Conversion result: %d\n", ret, intval);
+ return AVERROR(EINVAL);
+ }
+#endif
+
+ if (positions >= 0)
+ av_strlcatf(fmt_str, sizeof(fmt_str), "0%u", positions);
+ av_strlcatf(fmt_str, sizeof(fmt_str), "%c", format);
+
+ av_log(log_ctx, AV_LOG_DEBUG, "Formatting value %f (expr '%s') with spec '%s'\n",
+ res, expr, fmt_str);
+
+ av_bprintf(bp, fmt_str, intval);
+
+ return 0;
+}
+
+
+int ff_load_textfile(void *log_ctx, const char *textfile,
+ unsigned char **text, size_t *text_size)
+{
+ int err;
+ uint8_t *textbuf;
+ uint8_t *tmp;
+ size_t textbuf_size;
+
+ if ((err = av_file_map(textfile, &textbuf, &textbuf_size, 0, log_ctx)) < 0) {
+ av_log(log_ctx, AV_LOG_ERROR,
+ "The text file '%s' could not be read or is empty\n",
+ textfile);
+ return err;
+ }
+
+ if (textbuf_size > 0 && ff_is_newline(textbuf[textbuf_size - 1]))
+ textbuf_size--;
+ if (textbuf_size > SIZE_MAX - 1 || !(tmp = av_realloc(*text, textbuf_size + 1))) {
+ av_file_unmap(textbuf, textbuf_size);
+ return AVERROR(ENOMEM);
+ }
+ *text = tmp;
+ memcpy(*text, textbuf, textbuf_size);
+ (*text)[textbuf_size] = 0;
+ if (text_size)
+ *text_size = textbuf_size;
+ av_file_unmap(textbuf, textbuf_size);
+
+ return 0;
+}
+
diff --git a/libavfilter/textutils.h b/libavfilter/textutils.h
new file mode 100644
index 0000000000..7fa856c681
--- /dev/null
+++ b/libavfilter/textutils.h
@@ -0,0 +1,229 @@
+/*
+ * 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
+ * text utilities
+ */
+
+#ifndef AVFILTER_TEXTUTILS_H
+#define AVFILTER_TEXTUTILS_H
+
+#include "libavutil/bprint.h"
+#include "libavutil/eval.h"
+#include "libavutil/log.h"
+#include "libavutil/parseutils.h"
+
+/**
+ * Function used to expand a template sequence in the format
+ * %{FUNCTION_NAME[:PARAMS]}, defined in the TextExpander object.
+ */
+typedef struct FFExpandTextFunction {
+ /**
+ * name of the function
+ */
+ const char *name;
+
+ /**
+ * minimum and maximum number of arguments accepted by the
+ * function in the PARAMS
+ */
+ unsigned argc_min, argc_max;
+
+ /**
+ * actual function used to perform the expansion
+ */
+ int (*func)(void *ctx, AVBPrint *bp, const char *function_name, unsigned argc, char **args);
+} FFExpandTextFunction;
+
+/**
+ * Text expander context, used to encapsulate the logic to expand a
+ * given text template.
+ *
+ * A backslash character @samp{\} in a text template, followed by any
+ * character, always expands to the second character.
+ * Sequences of the form %{FUNCTION_NAME[:PARAMS]} are expanded using a
+ * function defined in the object. 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.
+ */
+typedef struct FFExpandTextContext {
+ /**
+ * log context to pass to the function, used for logging and for
+ * accessing the context for the function
+ */
+ void *log_ctx;
+
+ /**
+ * list of functions to use to expand sequences in the format
+ * FUNCTION_NAME{PARAMS}
+ */
+ FFExpandTextFunction *functions;
+
+ /**
+ * number of functions
+ */
+ unsigned int functions_nb;
+} FFExpandTextContext;
+
+/**
+ * Expand text template.
+ *
+ * Expand text template defined in text using the logic defined in a text
+ * expander object.
+ *
+ * @param expand_text text expansion context used to expand the text
+ * @param text template text to expand
+ * @param bp BPrint object where the expanded text is written to
+ * @return negative value corresponding to an AVERROR error code in case of
+ * errors, a non-negative value otherwise
+ */
+int ff_expand_text(FFExpandTextContext *expand_text, char *text, AVBPrint *bp);
+
+/**
+ * Print PTS representation to an AVBPrint object.
+ *
+ * @param log_ctx pointer to av_log object
+ * @param bp AVBPrint object where the PTS textual representation is written to
+ * @param pts PTS value expressed as a double to represent
+ * @param delta delta time parsed by av_parse_time(), added to the PTS
+ * @param fmt string representing the format to use for printing, can be
+ * "flt" - use a float representation with 6 decimal digits,
+ * "hms" - use HH:MM:SS.MMM format,
+ * "hms24hh" - same as "hms" but wraps the hours in 24hh format
+ * (so that it is expressed in the range 00-23),
+ * "localtime" or "gmtime" - expand the PTS according to the
+ * @code{strftime()} function rules, using either the corresponding
+ * @code{localtime()} or @code{gmtime()} time
+ * @param strftime_fmt: @code{strftime()} format to use to represent the PTS in
+ * case the format "localtime" or "gmtime" was selected, if not specified
+ * defaults to "%Y-%m-%d %H:%M:%S"
+ * @return negative value corresponding to an AVERROR error code in case of
+ * errors, a non-negative value otherwise
+ */
+int ff_print_pts(void *log_ctx, AVBPrint *bp, double pts, const char *delta,
+ const char *fmt, const char *strftime_fmt);
+
+/**
+ * Print time representation to an AVBPrint object.
+ *
+ * @param log_ctx pointer to av_log object
+ * @param bp AVBPrint object where the time textual representation is written to
+ * @param strftime_fmt: strftime() format to use to represent the time in case
+ * if not specified defaults to "%Y-%m-%d %H:%M:%S". The format string is
+ * extended to support the %[1-6]N after %S which prints fractions of the
+ * second with optionally specified number of digits, if not specified
+ * defaults to 3.
+ * @param localtime use local time to compute the time if non-zero, otherwise
+ * use UTC
+ * @return negative value corresponding to an AVERROR error code in case of
+ * errors, a non-negative value otherwise
+ */
+int ff_print_time(void *log_ctx, AVBPrint *bp, const char *strftime_fmt, char localtime);
+
+typedef double (*ff_eval_func2)(void *, double a, double b);
+
+/**
+ * Evaluate and print expression to an AVBprint object.
+ * The output is written as a double representation.
+ *
+ * This is a wrapper around av_expr_parse_and_eval() and following the
+ * same rules.
+ *
+ * @param log_ctx pointer to av_log object
+ * @param bp AVBPrint object where the evaluated expression is written to
+ * @param expr the expression to be evaluated
+ * @param fun_names names of the ff_eval_func2 functions used to evaluate the expression
+ * @param fun_values values of the ff_eval_func2 functions used to evaluate the expression
+ * @param var_names names of the variables used in the expression
+ * @param var_values values of the variables used in the expression
+ * @param eval_ctx evaluation context to be passed to some functions
+ *
+ * @return negative value corresponding to an AVERROR error code in case of
+ * errors, a non-negative value otherwise
+ */
+int ff_print_eval_expr(void *log_ctx, AVBPrint *bp,
+ const char *expr,
+ const char * const *fun_names, const ff_eval_func2 *fun_values,
+ const char * const *var_names, const double *var_values,
+ void *eval_ctx);
+
+/**
+ * Evaluate and print expression to an AVBprint object, using the
+ * specified format.
+ *
+ * This is a wrapper around av_expr_parse_and_eval() and following the
+ * same rules.
+ *
+ * The format is specified as a printf format character, optionally
+ * preceded by the positions numbers for zero-padding.
+ *
+ * The following formats are accepted:
+ * - x: use lowercase hexadecimal representation
+ * - X: use uppercase hexadecimal representation
+ * - d: use decimal representation
+ * - u: use unsigned decimal representation
+ *
+ * @param log_ctx pointer to av_log object
+ * @param bp AVBPrint object where the evaluated expression is written to
+ * @param expr the expression to be evaluated
+ * @param fun_names names of the ff_eval_func2 functions used to evaluate the expression
+ * @param fun_values values of the ff_eval_func2 functions used to evaluate the expression
+ * @param var_names names of the variables used in the expression
+ * @param var_values values of the variables used in the expression
+ * @param eval_ctx evaluation context to be passed to some functions
+ * @param format a character representing the format, to be chosen in xXdu
+ * @param positions final size of the value representation with 0-padding
+ * @return negative value corresponding to an AVERROR error code in case of
+ * errors, a non-negative value otherwise
+ */
+int ff_print_formatted_eval_expr(void *log_ctx, AVBPrint *bp,
+ const char *expr,
+ const char * const *fun_names, const ff_eval_func2 *fun_values,
+ const char * const *var_names, const double *var_values,
+ void *eval_ctx,
+ const char format, int positions);
+
+/**
+ * Check if the character is a newline.
+ *
+ * @param c character to check
+ * @return non-negative value in case c is a newline, 0 otherwise
+ */
+static inline int ff_is_newline(uint32_t c)
+{
+ return c == '\n' || c == '\r' || c == '\f' || c == '\v';
+}
+
+/**
+ * Load text file into the buffer pointed by text.
+ *
+ * @param log_ctx pointer to av_log object
+ * @param textfile filename containing the text to load
+ * @param text pointer to the text buffer where the loaded text will be
+ * loaded
+ * @param text_size pointer to the value to set with the loaded text data,
+ * including the terminating 0 character
+ * @return negative value corresponding to an AVERROR error code in case of
+ * errors, a non-negative value otherwise
+ */
+int ff_load_textfile(void *log_ctx, const char *textfile,
+ unsigned char **text, size_t *text_size);
+
+#endif /* AVFILTER_TEXTUTILS__H */
diff --git a/libavfilter/vf_drawtext.c b/libavfilter/vf_drawtext.c
index c5477cbff1..c1ea5b90b3 100644
--- a/libavfilter/vf_drawtext.c
+++ b/libavfilter/vf_drawtext.c
@@ -47,7 +47,6 @@
#include "libavutil/avstring.h"
#include "libavutil/bprint.h"
#include "libavutil/common.h"
-#include "libavutil/file.h"
#include "libavutil/eval.h"
#include "libavutil/opt.h"
#include "libavutil/random_seed.h"
@@ -62,6 +61,7 @@
#include "drawutils.h"
#include "formats.h"
#include "internal.h"
+#include "textutils.h"
#include "video.h"
#if CONFIG_LIBFRIBIDI
@@ -253,6 +253,7 @@ typedef struct TextMetrics {
typedef struct DrawTextContext {
const AVClass *class;
int exp_mode; ///< expansion mode to use for the text
+ FFExpandTextContext expand_text; ///< expand text in case exp_mode == NORMAL
int reinit; ///< tells if the filter is being reinited
#if CONFIG_LIBFONTCONFIG
uint8_t *font; ///< font to be used
@@ -631,40 +632,6 @@ static int load_font(AVFilterContext *ctx)
return err;
}
-static inline int is_newline(uint32_t c)
-{
- return c == '\n' || c == '\r' || c == '\f' || c == '\v';
-}
-
-static int load_textfile(AVFilterContext *ctx)
-{
- DrawTextContext *s = ctx->priv;
- int err;
- uint8_t *textbuf;
- uint8_t *tmp;
- size_t textbuf_size;
-
- if ((err = av_file_map(s->textfile, &textbuf, &textbuf_size, 0, ctx)) < 0) {
- av_log(ctx, AV_LOG_ERROR,
- "The text file '%s' could not be read or is empty\n",
- s->textfile);
- return err;
- }
-
- if (textbuf_size > 0 && is_newline(textbuf[textbuf_size - 1]))
- textbuf_size--;
- if (textbuf_size > SIZE_MAX - 1 || !(tmp = av_realloc(s->text, textbuf_size + 1))) {
- av_file_unmap(textbuf, textbuf_size);
- return AVERROR(ENOMEM);
- }
- s->text = tmp;
- memcpy(s->text, textbuf, textbuf_size);
- s->text[textbuf_size] = 0;
- av_file_unmap(textbuf, textbuf_size);
-
- return 0;
-}
-
#if CONFIG_LIBFRIBIDI
static int shape_text(AVFilterContext *ctx)
{
@@ -885,6 +852,123 @@ static int string_to_array(const char *source, int *result, int result_size)
return counter;
}
+static int func_pict_type(void *ctx, AVBPrint *bp, const char *function_name, unsigned argc, char **argv)
+{
+ DrawTextContext *s = ((AVFilterContext *)ctx)->priv;
+
+ av_bprintf(bp, "%c", av_get_picture_type_char(s->var_values[VAR_PICT_TYPE]));
+ return 0;
+}
+
+static int func_pts(void *ctx, AVBPrint *bp, const char *function_name, unsigned argc, char **argv)
+{
+ DrawTextContext *s = ((AVFilterContext *)ctx)->priv;
+ const char *fmt;
+ const char *strftime_fmt = NULL;
+ const char *delta = NULL;
+ double pts = s->var_values[VAR_T];
+
+ // argv: pts, FMT, [DELTA, 24HH | strftime_fmt]
+
+ fmt = argc >= 1 ? argv[0] : "flt";
+ if (argc >= 2) {
+ delta = argv[1];
+ }
+ if (argc >= 3) {
+ if (!strcmp(fmt, "hms")) {
+ if (!strcmp(argv[2], "24HH")) {
+ av_log(ctx, AV_LOG_WARNING, "pts third argument 24HH is deprected, use pts:hms24hh instead\n");
+ fmt = "hms24";
+ } else {
+ av_log(ctx, AV_LOG_ERROR, "Invalid argument '%s', '24HH' was expected\n", argv[2]);
+ return AVERROR(EINVAL);
+ }
+ } else {
+ strftime_fmt = argv[2];
+ }
+ }
+
+ return ff_print_pts(ctx, bp, pts, delta, fmt, strftime_fmt);
+}
+
+static int func_frame_num(void *ctx, AVBPrint *bp, const char *function_name, unsigned argc, char **argv)
+{
+ DrawTextContext *s = ((AVFilterContext *)ctx)->priv;
+
+ av_bprintf(bp, "%d", (int)s->var_values[VAR_N]);
+ return 0;
+}
+
+static int func_metadata(void *ctx, AVBPrint *bp, const char *function_name, unsigned argc, char **argv)
+{
+ DrawTextContext *s = ((AVFilterContext *)ctx)->priv;
+ AVDictionaryEntry *e = av_dict_get(s->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_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_eval_expr(void *ctx, AVBPrint *bp, const char *function_name, unsigned argc, char **argv)
+{
+ DrawTextContext *s = ((AVFilterContext *)ctx)->priv;
+
+ return ff_print_eval_expr(ctx, bp, argv[0],
+ fun2_names, fun2,
+ var_names, s->var_values, &s->prng);
+}
+
+static int func_eval_expr_int_format(void *ctx, AVBPrint *bp, const char *function_name, unsigned argc, char **argv)
+{
+ DrawTextContext *s = ((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, s->var_values,
+ &s->prng,
+ argv[1][0], positions);
+}
+
+static FFExpandTextFunction expand_text_functions[] = {
+ { "e", 1, 1, func_eval_expr },
+ { "eif", 2, 3, func_eval_expr_int_format },
+ { "expr", 1, 1, func_eval_expr },
+ { "expr_int_format", 2, 3, func_eval_expr_int_format },
+ { "frame_num", 0, 0, func_frame_num },
+ { "gmtime", 0, 1, func_strftime },
+ { "localtime", 0, 1, func_strftime },
+ { "metadata", 1, 2, func_metadata },
+ { "n", 0, 0, func_frame_num },
+ { "pict_type", 0, 0, func_pict_type },
+ { "pts", 0, 3, func_pts }
+};
+
static av_cold int init(AVFilterContext *ctx)
{
int err;
@@ -907,7 +991,7 @@ static av_cold int init(AVFilterContext *ctx)
"Both text and text file provided. Please provide only one\n");
return AVERROR(EINVAL);
}
- if ((err = load_textfile(ctx)) < 0)
+ if ((err = ff_load_textfile(ctx, (const char *)s->textfile, &s->text, NULL)) < 0)
return err;
}
@@ -950,6 +1034,12 @@ static av_cold int init(AVFilterContext *ctx)
return AVERROR(EINVAL);
}
+ s->expand_text = (FFExpandTextContext) {
+ .log_ctx = ctx,
+ .functions = expand_text_functions,
+ .functions_nb = FF_ARRAY_ELEMS(expand_text_functions)
+ };
+
#if CONFIG_LIBFRIBIDI
if (s->text_shaping)
if ((err = shape_text(ctx)) < 0)
@@ -1160,367 +1250,6 @@ fail:
return ret;
}
-static int func_pict_type(AVFilterContext *ctx, AVBPrint *bp,
- char *fct, unsigned argc, char **argv, int tag)
-{
- DrawTextContext *s = ctx->priv;
-
- av_bprintf(bp, "%c", av_get_picture_type_char(s->var_values[VAR_PICT_TYPE]));
- return 0;
-}
-
-static int func_pts(AVFilterContext *ctx, AVBPrint *bp,
- char *fct, unsigned argc, char **argv, int tag)
-{
- DrawTextContext *s = ctx->priv;
- const char *fmt;
- double pts = s->var_values[VAR_T];
- int ret;
-
- fmt = argc >= 1 ? argv[0] : "flt";
- if (argc >= 2) {
- int64_t delta;
- if ((ret = av_parse_time(&delta, argv[1], 1)) < 0) {
- av_log(ctx, AV_LOG_ERROR, "Invalid delta '%s'\n", argv[1]);
- return ret;
- }
- pts += (double)delta / AV_TIME_BASE;
- }
- if (!strcmp(fmt, "flt")) {
- av_bprintf(bp, "%.6f", pts);
- } else if (!strcmp(fmt, "hms")) {
- if (isnan(pts)) {
- av_bprintf(bp, " ??:??:??.???");
- } else {
- int64_t ms = llrint(pts * 1000);
- char sign = ' ';
- if (ms < 0) {
- sign = '-';
- ms = -ms;
- }
- if (argc >= 3) {
- if (!strcmp(argv[2], "24HH")) {
- ms %= 24 * 60 * 60 * 1000;
- } else {
- av_log(ctx, AV_LOG_ERROR, "Invalid argument '%s'\n", argv[2]);
- return AVERROR(EINVAL);
- }
- }
- av_bprintf(bp, "%c%02d:%02d:%02d.%03d", sign,
- (int)(ms / (60 * 60 * 1000)),
- (int)(ms / (60 * 1000)) % 60,
- (int)(ms / 1000) % 60,
- (int)(ms % 1000));
- }
- } else if (!strcmp(fmt, "localtime") ||
- !strcmp(fmt, "gmtime")) {
- struct tm tm;
- time_t ms = (time_t)pts;
- const char *timefmt = argc >= 3 ? argv[2] : "%Y-%m-%d %H:%M:%S";
- if (!strcmp(fmt, "localtime"))
- localtime_r(&ms, &tm);
- else
- gmtime_r(&ms, &tm);
- av_bprint_strftime(bp, timefmt, &tm);
- } else {
- av_log(ctx, AV_LOG_ERROR, "Invalid format '%s'\n", fmt);
- return AVERROR(EINVAL);
- }
- return 0;
-}
-
-static int func_frame_num(AVFilterContext *ctx, AVBPrint *bp,
- char *fct, unsigned argc, char **argv, int tag)
-{
- DrawTextContext *s = ctx->priv;
-
- av_bprintf(bp, "%d", (int)s->var_values[VAR_N]);
- return 0;
-}
-
-static int func_metadata(AVFilterContext *ctx, AVBPrint *bp,
- char *fct, unsigned argc, char **argv, int tag)
-{
- DrawTextContext *s = ctx->priv;
- AVDictionaryEntry *e = av_dict_get(s->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_strftime(AVFilterContext *ctx, AVBPrint *bp,
- char *fct, unsigned argc, char **argv, int tag)
-{
- const char *fmt = argc ? argv[0] : "%Y-%m-%d %H:%M:%S";
- const char *fmt_begin = fmt;
- int64_t unow;
- time_t now;
- struct tm tm;
- const char *begin;
- const char *tmp;
- int len;
- int div;
- AVBPrint fmt_bp;
-
- av_bprint_init(&fmt_bp, 0, AV_BPRINT_SIZE_UNLIMITED);
-
- unow = av_gettime();
- now = unow / 1000000;
- if (tag == 'L' || tag == 'm')
- localtime_r(&now, &tm);
- else
- tm = *gmtime_r(&now, &tm);
-
- // manually parse format for %N (fractional seconds)
- begin = fmt;
- while ((begin = strchr(begin, '%'))) {
- tmp = begin + 1;
- len = 0;
-
- // skip escaped "%%"
- if (*tmp == '%') {
- begin = tmp + 1;
- continue;
- }
-
- // count digits between % and possible N
- while (*tmp != '\0' && av_isdigit((int)*tmp)) {
- len++;
- tmp++;
- }
-
- // N encountered, insert time
- if (*tmp == 'N') {
- int num_digits = 3; // default show millisecond [1,6]
-
- // if digit given, expect [1,6], warn & clamp otherwise
- if (len == 1) {
- num_digits = av_clip(*(begin + 1) - '0', 1, 6);
- } else if (len > 1) {
- av_log(ctx, AV_LOG_WARNING, "Invalid number of decimals for %%N, using default of %i\n", num_digits);
- }
-
- len += 2; // add % and N to get length of string part
-
- div = pow(10, 6 - num_digits);
-
- av_bprintf(&fmt_bp, "%.*s%0*d", (int)(begin - fmt_begin), fmt_begin, num_digits, (int)(unow % 1000000) / div);
-
- begin += len;
- fmt_begin = begin;
-
- continue;
- }
-
- begin = tmp;
- }
-
- av_bprintf(&fmt_bp, "%s", fmt_begin);
- if (!av_bprint_is_complete(&fmt_bp)) {
- av_log(ctx, AV_LOG_WARNING, "Format string truncated at %u/%u.", fmt_bp.size, fmt_bp.len);
- }
-
- av_bprint_strftime(bp, fmt_bp.str, &tm);
-
- av_bprint_finalize(&fmt_bp, NULL);
-
- return 0;
-}
-
-static int func_eval_expr(AVFilterContext *ctx, AVBPrint *bp,
- char *fct, unsigned argc, char **argv, int tag)
-{
- DrawTextContext *s = ctx->priv;
- double res;
- int ret;
-
- ret = av_expr_parse_and_eval(&res, argv[0], var_names, s->var_values,
- NULL, NULL, fun2_names, fun2,
- &s->prng, 0, ctx);
- if (ret < 0)
- av_log(ctx, AV_LOG_ERROR,
- "Expression '%s' for the expr text expansion function is not valid\n",
- argv[0]);
- else
- av_bprintf(bp, "%f", res);
-
- return ret;
-}
-
-static int func_eval_expr_int_format(AVFilterContext *ctx, AVBPrint *bp,
- char *fct, unsigned argc, char **argv, int tag)
-{
- DrawTextContext *s = ctx->priv;
- double res;
- int intval;
- int ret;
- unsigned int positions = 0;
- char fmt_str[30] = "%";
-
- /*
- * argv[0] expression to be converted to `int`
- * argv[1] format: 'x', 'X', 'd' or 'u'
- * argv[2] positions printed (optional)
- */
-
- ret = av_expr_parse_and_eval(&res, argv[0], var_names, s->var_values,
- NULL, NULL, fun2_names, fun2,
- &s->prng, 0, ctx);
- if (ret < 0) {
- av_log(ctx, AV_LOG_ERROR,
- "Expression '%s' for the expr text expansion function is not valid\n",
- argv[0]);
- return ret;
- }
-
- if (!strchr("xXdu", argv[1][0])) {
- av_log(ctx, AV_LOG_ERROR, "Invalid format '%c' specified,"
- " allowed values: 'x', 'X', 'd', 'u'\n", argv[1][0]);
- return AVERROR(EINVAL);
- }
-
- 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);
- }
- }
-
- feclearexcept(FE_ALL_EXCEPT);
- intval = res;
-#if defined(FE_INVALID) && defined(FE_OVERFLOW) && defined(FE_UNDERFLOW)
- if ((ret = fetestexcept(FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW))) {
- av_log(ctx, AV_LOG_ERROR, "Conversion of floating-point result to int failed. Control register: 0x%08x. Conversion result: %d\n", ret, intval);
- return AVERROR(EINVAL);
- }
-#endif
-
- if (argc == 3)
- av_strlcatf(fmt_str, sizeof(fmt_str), "0%u", positions);
- av_strlcatf(fmt_str, sizeof(fmt_str), "%c", argv[1][0]);
-
- av_log(ctx, AV_LOG_DEBUG, "Formatting value %f (expr '%s') with spec '%s'\n",
- res, argv[0], fmt_str);
-
- av_bprintf(bp, fmt_str, intval);
-
- return 0;
-}
-
-static const struct drawtext_function {
- const char *name;
- unsigned argc_min, argc_max;
- int tag; /**< opaque argument to func */
- int (*func)(AVFilterContext *, AVBPrint *, char *, unsigned, char **, int);
-} functions[] = {
- { "expr", 1, 1, 0, func_eval_expr },
- { "e", 1, 1, 0, func_eval_expr },
- { "expr_int_format", 2, 3, 0, func_eval_expr_int_format },
- { "eif", 2, 3, 0, func_eval_expr_int_format },
- { "pict_type", 0, 0, 0, func_pict_type },
- { "pts", 0, 3, 0, func_pts },
- { "gmtime", 0, 1, 'G', func_strftime },
- { "localtime", 0, 1, 'L', func_strftime },
- { "frame_num", 0, 0, 0, func_frame_num },
- { "n", 0, 0, 0, func_frame_num },
- { "metadata", 1, 2, 0, func_metadata },
-};
-
-static int eval_function(AVFilterContext *ctx, AVBPrint *bp, char *fct,
- unsigned argc, char **argv)
-{
- unsigned i;
-
- for (i = 0; i < FF_ARRAY_ELEMS(functions); i++) {
- if (strcmp(fct, functions[i].name))
- continue;
- if (argc < functions[i].argc_min) {
- av_log(ctx, AV_LOG_ERROR, "%%{%s} requires at least %d arguments\n",
- fct, functions[i].argc_min);
- return AVERROR(EINVAL);
- }
- if (argc > functions[i].argc_max) {
- av_log(ctx, AV_LOG_ERROR, "%%{%s} requires at most %d arguments\n",
- fct, functions[i].argc_max);
- return AVERROR(EINVAL);
- }
- break;
- }
- if (i >= FF_ARRAY_ELEMS(functions)) {
- av_log(ctx, AV_LOG_ERROR, "%%{%s} is not known\n", fct);
- return AVERROR(EINVAL);
- }
- return functions[i].func(ctx, bp, fct, argc, argv, functions[i].tag);
-}
-
-static int expand_function(AVFilterContext *ctx, AVBPrint *bp, char **rtext)
-{
- const char *text = *rtext;
- char *argv[16] = { NULL };
- unsigned argc = 0, i;
- int ret;
-
- if (*text != '{') {
- av_log(ctx, AV_LOG_ERROR, "Stray %% near '%s'\n", text);
- return AVERROR(EINVAL);
- }
- text++;
- while (1) {
- if (!(argv[argc++] = av_get_token(&text, ":}"))) {
- ret = AVERROR(ENOMEM);
- goto end;
- }
- if (!*text) {
- av_log(ctx, AV_LOG_ERROR, "Unterminated %%{} near '%s'\n", *rtext);
- ret = AVERROR(EINVAL);
- goto end;
- }
- if (argc == FF_ARRAY_ELEMS(argv))
- av_freep(&argv[--argc]); /* error will be caught later */
- if (*text == '}')
- break;
- text++;
- }
-
- if ((ret = eval_function(ctx, bp, argv[0], argc - 1, argv + 1)) < 0)
- goto end;
- ret = 0;
- *rtext = (char *)text + 1;
-
-end:
- for (i = 0; i < argc; i++)
- av_freep(&argv[i]);
- return ret;
-}
-
-static int expand_text(AVFilterContext *ctx, char *text, AVBPrint *bp)
-{
- int ret;
-
- av_bprint_clear(bp);
- while (*text) {
- if (*text == '\\' && text[1]) {
- av_bprint_chars(bp, text[1], 1);
- text += 2;
- } else if (*text == '%') {
- text++;
- if ((ret = expand_function(ctx, bp, &text)) < 0)
- return ret;
- } else {
- av_bprint_chars(bp, *text, 1);
- text++;
- }
- }
- if (!av_bprint_is_complete(bp))
- return AVERROR(ENOMEM);
- return 0;
-}
-
static void update_color_with_alpha(DrawTextContext *s, FFDrawColor *color, const FFDrawColor incolor)
{
*color = incolor;
@@ -1688,7 +1417,7 @@ static int measure_text(AVFilterContext *ctx, TextMetrics *metrics)
for (i = 0, p = text; 1; i++) {
GET_UTF8(code, *p ? *p++ : 0, code = 0xfffd; goto continue_on_failed;);
continue_on_failed:
- if (is_newline(code) || code == 0) {
+ if (ff_is_newline(code) || code == 0) {
++line_count;
if (code == 0) {
break;
@@ -1729,7 +1458,7 @@ continue_on_failed:
}
GET_UTF8(code, *p ? *p++ : 0, code = 0xfffd; goto continue_on_failed2;);
continue_on_failed2:
- if (is_newline(code) || code == 0) {
+ if (ff_is_newline(code) || code == 0) {
TextLine *cur_line = &s->lines[line_count];
HarfbuzzData *hb = &cur_line->hb_data;
cur_line->cluster_offset = line_offset;
@@ -1861,7 +1590,7 @@ static int draw_text(AVFilterContext *ctx, AVFrame *frame)
av_bprintf(bp, "%s", s->text);
break;
case EXP_NORMAL:
- if ((ret = expand_text(ctx, s->text, &s->expanded_text)) < 0)
+ if ((ret = ff_expand_text(&s->expand_text, s->text, &s->expanded_text)) < 0)
return ret;
break;
case EXP_STRFTIME:
@@ -1883,7 +1612,7 @@ static int draw_text(AVFilterContext *ctx, AVFrame *frame)
if (s->fontcolor_expr[0]) {
/* If expression is set, evaluate and replace the static value */
av_bprint_clear(&s->expanded_fontcolor);
- if ((ret = expand_text(ctx, s->fontcolor_expr, &s->expanded_fontcolor)) < 0)
+ if ((ret = ff_expand_text(&s->expand_text, s->fontcolor_expr, &s->expanded_fontcolor)) < 0)
return ret;
if (!av_bprint_is_complete(&s->expanded_fontcolor))
return AVERROR(ENOMEM);
@@ -2125,7 +1854,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
}
if (s->reload && !(inlink->frame_count_out % s->reload)) {
- if ((ret = load_textfile(ctx)) < 0) {
+ if ((ret = ff_load_textfile(ctx, (const char *)s->textfile, &s->text, NULL)) < 0) {
av_frame_free(&frame);
return ret;
}
1
0
lavfi/overlay: factorize definition of planar and packed blending functions
by Stefano Sabatini 02 Jan '24
by Stefano Sabatini 02 Jan '24
02 Jan '24
ffmpeg | branch: master | Stefano Sabatini <stefasab(a)gmail.com> | Sat Dec 16 15:24:02 2023 +0100| [c2ab41b7e0b9bc700f6a4814f629ac52962e0025] | committer: Stefano Sabatini
lavfi/overlay: factorize definition of planar and packed blending functions
> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=c2ab41b7e0b9bc700…
---
libavfilter/vf_overlay.c | 260 ++++++++++-------------------------------------
1 file changed, 53 insertions(+), 207 deletions(-)
diff --git a/libavfilter/vf_overlay.c b/libavfilter/vf_overlay.c
index fa39abb23a..fb645f2b58 100644
--- a/libavfilter/vf_overlay.c
+++ b/libavfilter/vf_overlay.c
@@ -690,213 +690,59 @@ static av_always_inline void blend_slice_planar_rgb(AVFilterContext *ctx,
alpha_composite_8_8bits(src, dst, src_w, src_h, dst_w, dst_h, x, y, jobnr, nb_jobs);
}
-static int blend_slice_yuv420(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
-{
- OverlayContext *s = ctx->priv;
- ThreadData *td = arg;
- blend_slice_yuv_8_8bits(ctx, td->dst, td->src, 1, 1, 0, s->x, s->y, 1, jobnr, nb_jobs);
- return 0;
-}
-
-static int blend_slice_yuva420(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
-{
- OverlayContext *s = ctx->priv;
- ThreadData *td = arg;
- blend_slice_yuv_8_8bits(ctx, td->dst, td->src, 1, 1, 1, s->x, s->y, 1, jobnr, nb_jobs);
- return 0;
-}
-
-static int blend_slice_yuv420p10(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
-{
- OverlayContext *s = ctx->priv;
- ThreadData *td = arg;
- blend_slice_yuv_16_10bits(ctx, td->dst, td->src, 1, 1, 0, s->x, s->y, 1, jobnr, nb_jobs);
- return 0;
-}
-
-static int blend_slice_yuva420p10(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
-{
- OverlayContext *s = ctx->priv;
- ThreadData *td = arg;
- blend_slice_yuv_16_10bits(ctx, td->dst, td->src, 1, 1, 1, s->x, s->y, 1, jobnr, nb_jobs);
- return 0;
-}
-
-static int blend_slice_yuv422p10(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
-{
- OverlayContext *s = ctx->priv;
- ThreadData *td = arg;
- blend_slice_yuv_16_10bits(ctx, td->dst, td->src, 1, 0, 0, s->x, s->y, 1, jobnr, nb_jobs);
- return 0;
-}
-
-static int blend_slice_yuva422p10(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
-{
- OverlayContext *s = ctx->priv;
- ThreadData *td = arg;
- blend_slice_yuv_16_10bits(ctx, td->dst, td->src, 1, 0, 1, s->x, s->y, 1, jobnr, nb_jobs);
- return 0;
-}
-
-static int blend_slice_yuv422(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
-{
- OverlayContext *s = ctx->priv;
- ThreadData *td = arg;
- blend_slice_yuv_8_8bits(ctx, td->dst, td->src, 1, 0, 0, s->x, s->y, 1, jobnr, nb_jobs);
- return 0;
-}
-
-static int blend_slice_yuva422(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
-{
- OverlayContext *s = ctx->priv;
- ThreadData *td = arg;
- blend_slice_yuv_8_8bits(ctx, td->dst, td->src, 1, 0, 1, s->x, s->y, 1, jobnr, nb_jobs);
- return 0;
-}
-
-static int blend_slice_yuv444(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
-{
- OverlayContext *s = ctx->priv;
- ThreadData *td = arg;
- blend_slice_yuv_8_8bits(ctx, td->dst, td->src, 0, 0, 0, s->x, s->y, 1, jobnr, nb_jobs);
- return 0;
-}
-
-static int blend_slice_yuva444(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
-{
- OverlayContext *s = ctx->priv;
- ThreadData *td = arg;
- blend_slice_yuv_8_8bits(ctx, td->dst, td->src, 0, 0, 1, s->x, s->y, 1, jobnr, nb_jobs);
- return 0;
-}
-
-static int blend_slice_yuv444p10(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
-{
- OverlayContext *s = ctx->priv;
- ThreadData *td = arg;
- blend_slice_yuv_16_10bits(ctx, td->dst, td->src, 0, 0, 0, s->x, s->y, 1, jobnr, nb_jobs);
- return 0;
-}
-
-static int blend_slice_yuva444p10(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
-{
- OverlayContext *s = ctx->priv;
- ThreadData *td = arg;
- blend_slice_yuv_16_10bits(ctx, td->dst, td->src, 0, 0, 1, s->x, s->y, 1, jobnr, nb_jobs);
- return 0;
-}
-
-static int blend_slice_gbrp(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
-{
- OverlayContext *s = ctx->priv;
- ThreadData *td = arg;
- blend_slice_planar_rgb(ctx, td->dst, td->src, 0, 0, 0, s->x, s->y, 1, jobnr, nb_jobs);
- return 0;
-}
-
-static int blend_slice_gbrap(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
-{
- OverlayContext *s = ctx->priv;
- ThreadData *td = arg;
- blend_slice_planar_rgb(ctx, td->dst, td->src, 0, 0, 1, s->x, s->y, 1, jobnr, nb_jobs);
- return 0;
-}
-
-static int blend_slice_yuv420_pm(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
-{
- OverlayContext *s = ctx->priv;
- ThreadData *td = arg;
- blend_slice_yuv_8_8bits(ctx, td->dst, td->src, 1, 1, 0, s->x, s->y, 0, jobnr, nb_jobs);
- return 0;
-}
-
-static int blend_slice_yuva420_pm(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
-{
- OverlayContext *s = ctx->priv;
- ThreadData *td = arg;
- blend_slice_yuv_8_8bits(ctx, td->dst, td->src, 1, 1, 1, s->x, s->y, 0, jobnr, nb_jobs);
- return 0;
-}
-
-static int blend_slice_yuv422_pm(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
-{
- OverlayContext *s = ctx->priv;
- ThreadData *td = arg;
- blend_slice_yuv_8_8bits(ctx, td->dst, td->src, 1, 0, 0, s->x, s->y, 0, jobnr, nb_jobs);
- return 0;
-}
-
-static int blend_slice_yuva422_pm(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
-{
- OverlayContext *s = ctx->priv;
- ThreadData *td = arg;
- blend_slice_yuv_8_8bits(ctx, td->dst, td->src, 1, 0, 1, s->x, s->y, 0, jobnr, nb_jobs);
- return 0;
-}
-
-static int blend_slice_yuv444_pm(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
-{
- OverlayContext *s = ctx->priv;
- ThreadData *td = arg;
- blend_slice_yuv_8_8bits(ctx, td->dst, td->src, 0, 0, 0, s->x, s->y, 0, jobnr, nb_jobs);
- return 0;
-}
-
-static int blend_slice_yuva444_pm(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
-{
- OverlayContext *s = ctx->priv;
- ThreadData *td = arg;
- blend_slice_yuv_8_8bits(ctx, td->dst, td->src, 0, 0, 1, s->x, s->y, 0, jobnr, nb_jobs);
- return 0;
-}
-
-static int blend_slice_gbrp_pm(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
-{
- OverlayContext *s = ctx->priv;
- ThreadData *td = arg;
- blend_slice_planar_rgb(ctx, td->dst, td->src, 0, 0, 0, s->x, s->y, 0, jobnr, nb_jobs);
- return 0;
-}
-
-static int blend_slice_gbrap_pm(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
-{
- OverlayContext *s = ctx->priv;
- ThreadData *td = arg;
- blend_slice_planar_rgb(ctx, td->dst, td->src, 0, 0, 1, s->x, s->y, 0, jobnr, nb_jobs);
- return 0;
-}
-
-static int blend_slice_rgb(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
-{
- OverlayContext *s = ctx->priv;
- ThreadData *td = arg;
- blend_slice_packed_rgb(ctx, td->dst, td->src, 0, s->x, s->y, 1, jobnr, nb_jobs);
- return 0;
-}
-
-static int blend_slice_rgba(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
-{
- OverlayContext *s = ctx->priv;
- ThreadData *td = arg;
- blend_slice_packed_rgb(ctx, td->dst, td->src, 1, s->x, s->y, 1, jobnr, nb_jobs);
- return 0;
-}
-
-static int blend_slice_rgb_pm(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
-{
- OverlayContext *s = ctx->priv;
- ThreadData *td = arg;
- blend_slice_packed_rgb(ctx, td->dst, td->src, 0, s->x, s->y, 0, jobnr, nb_jobs);
- return 0;
-}
-
-static int blend_slice_rgba_pm(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
-{
- OverlayContext *s = ctx->priv;
- ThreadData *td = arg;
- blend_slice_packed_rgb(ctx, td->dst, td->src, 1, s->x, s->y, 0, jobnr, nb_jobs);
- return 0;
-}
+#define DEFINE_BLEND_SLICE_PLANAR_FMT(format_, blend_slice_fn_suffix_, hsub_, vsub_, main_has_alpha_, direct_) \
+static int blend_slice_##format_(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) \
+{ \
+ OverlayContext *s = ctx->priv; \
+ ThreadData *td = arg; \
+ blend_slice_##blend_slice_fn_suffix_(ctx, td->dst, td->src, \
+ hsub_, vsub_, main_has_alpha_, \
+ s->x, s->y, direct_, \
+ jobnr, nb_jobs); \
+ return 0; \
+}
+
+// FMT FN H V A D
+DEFINE_BLEND_SLICE_PLANAR_FMT(yuv420, yuv_8_8bits, 1, 1, 0, 1);
+DEFINE_BLEND_SLICE_PLANAR_FMT(yuva420, yuv_8_8bits, 1, 1, 1, 1);
+DEFINE_BLEND_SLICE_PLANAR_FMT(yuv420p10, yuv_16_10bits, 1, 1, 0, 1);
+DEFINE_BLEND_SLICE_PLANAR_FMT(yuva420p10, yuv_16_10bits, 1, 1, 1, 1);
+DEFINE_BLEND_SLICE_PLANAR_FMT(yuv422p10, yuv_16_10bits, 1, 0, 0, 1);
+DEFINE_BLEND_SLICE_PLANAR_FMT(yuva422p10, yuv_16_10bits, 1, 0, 1, 1);
+DEFINE_BLEND_SLICE_PLANAR_FMT(yuv422, yuv_8_8bits, 1, 0, 0, 1);
+DEFINE_BLEND_SLICE_PLANAR_FMT(yuva422, yuv_8_8bits, 1, 0, 1, 1);
+DEFINE_BLEND_SLICE_PLANAR_FMT(yuv444, yuv_8_8bits, 0, 0, 0, 1);
+DEFINE_BLEND_SLICE_PLANAR_FMT(yuva444, yuv_8_8bits, 0, 0, 1, 1);
+DEFINE_BLEND_SLICE_PLANAR_FMT(yuv444p10, yuv_16_10bits, 0, 0, 0, 1);
+DEFINE_BLEND_SLICE_PLANAR_FMT(yuva444p10, yuv_16_10bits, 0, 0, 1, 1);
+DEFINE_BLEND_SLICE_PLANAR_FMT(gbrp, planar_rgb, 0, 0, 0, 1);
+DEFINE_BLEND_SLICE_PLANAR_FMT(gbrap, planar_rgb, 0, 0, 1, 1);
+DEFINE_BLEND_SLICE_PLANAR_FMT(yuv420_pm, yuv_8_8bits, 1, 1, 0, 0);
+DEFINE_BLEND_SLICE_PLANAR_FMT(yuva420_pm, yuv_8_8bits, 1, 1, 1, 0);
+DEFINE_BLEND_SLICE_PLANAR_FMT(yuv422_pm, yuv_8_8bits, 1, 0, 0, 0);
+DEFINE_BLEND_SLICE_PLANAR_FMT(yuva422_pm, yuv_8_8bits, 1, 0, 1, 0);
+DEFINE_BLEND_SLICE_PLANAR_FMT(yuv444_pm, yuv_8_8bits, 0, 0, 0, 0);
+DEFINE_BLEND_SLICE_PLANAR_FMT(yuva444_pm, yuv_8_8bits, 0, 0, 1, 0);
+DEFINE_BLEND_SLICE_PLANAR_FMT(gbrp_pm, planar_rgb, 0, 0, 0, 0);
+DEFINE_BLEND_SLICE_PLANAR_FMT(gbrap_pm, planar_rgb, 0, 0, 1, 0);
+
+#define DEFINE_BLEND_SLICE_PACKED_FMT(format_, blend_slice_fn_suffix_, main_has_alpha_, direct_) \
+static int blend_slice_##format_(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) \
+{ \
+ OverlayContext *s = ctx->priv; \
+ ThreadData *td = arg; \
+ blend_slice_packed_##blend_slice_fn_suffix_(ctx, td->dst, td->src, \
+ main_has_alpha_, \
+ s->x, s->y, direct_, \
+ jobnr, nb_jobs); \
+ return 0; \
+}
+
+// FMT FN A D
+DEFINE_BLEND_SLICE_PACKED_FMT(rgb, rgb, 0, 1);
+DEFINE_BLEND_SLICE_PACKED_FMT(rgba, rgb, 1, 1);
+DEFINE_BLEND_SLICE_PACKED_FMT(rgb_pm, rgb, 0, 0);
+DEFINE_BLEND_SLICE_PACKED_FMT(rgba_pm, rgb, 1, 0);
static int config_input_main(AVFilterLink *inlink)
{
1
0
avcodec/d3d12va_mpeg2: change the type for the ID3D12Resource_Map input data argument
by James Almer 01 Jan '24
by James Almer 01 Jan '24
01 Jan '24
ffmpeg | branch: master | James Almer <jamrial(a)gmail.com> | Sun Dec 31 17:21:49 2023 -0300| [85b8d59ec763a7f3352cbae38ec3b75af9585cdb] | committer: James Almer
avcodec/d3d12va_mpeg2: change the type for the ID3D12Resource_Map input data argument
Fixes -Wincompatible-pointer-types warnings.
Reviewed-by: Wu Jianhua <toqsxw(a)outlook.com>
Signed-off-by: James Almer <jamrial(a)gmail.com>
> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=85b8d59ec763a7f33…
---
libavcodec/d3d12va_mpeg2.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libavcodec/d3d12va_mpeg2.c b/libavcodec/d3d12va_mpeg2.c
index a2ae8bf948..b964f8d2ff 100644
--- a/libavcodec/d3d12va_mpeg2.c
+++ b/libavcodec/d3d12va_mpeg2.c
@@ -98,7 +98,7 @@ static int update_input_arguments(AVCodecContext *avctx, D3D12_VIDEO_DECODE_INPU
const unsigned mb_count = s->mb_width * (s->mb_height >> is_field);
int i;
- uint8_t *mapped_data = NULL;
+ void *mapped_data = NULL;
D3D12_VIDEO_DECODE_FRAME_ARGUMENT *args = &input_args->FrameArguments[input_args->NumFrameArguments++];
D3D12_RANGE range = {
1
0
ffmpeg | branch: master | James Almer <jamrial(a)gmail.com> | Sun Dec 31 17:14:01 2023 -0300| [e9722735fad451db9efa47adfcc98af9d171c00b] | committer: James Almer
avcodec/d3d12va_mpeg2: remove unused variables
Reviewed-by: Wu Jianhua <toqsxw(a)outlook.com>
Signed-off-by: James Almer <jamrial(a)gmail.com>
> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=e9722735fad451db9…
---
libavcodec/d3d12va_mpeg2.c | 8 --------
1 file changed, 8 deletions(-)
diff --git a/libavcodec/d3d12va_mpeg2.c b/libavcodec/d3d12va_mpeg2.c
index 91bf3f8b75..a2ae8bf948 100644
--- a/libavcodec/d3d12va_mpeg2.c
+++ b/libavcodec/d3d12va_mpeg2.c
@@ -49,7 +49,6 @@ static int d3d12va_mpeg2_start_frame(AVCodecContext *avctx, av_unused const uint
const MpegEncContext *s = avctx->priv_data;
D3D12VADecodeContext *ctx = D3D12VA_DECODE_CONTEXT(avctx);
D3D12DecodePictureContext *ctx_pic = s->current_picture_ptr->hwaccel_picture_private;
- DXVA_QmatrixData *qm = &ctx_pic->qm;
if (!ctx)
return -1;
@@ -76,8 +75,6 @@ static int d3d12va_mpeg2_decode_slice(AVCodecContext *avctx, const uint8_t *buff
const MpegEncContext *s = avctx->priv_data;
D3D12DecodePictureContext *ctx_pic = s->current_picture_ptr->hwaccel_picture_private;
- int is_field = s->picture_structure != PICT_FRAME;
-
if (ctx_pic->slice_count >= MAX_SLICES) {
return AVERROR(ERANGE);
}
@@ -94,9 +91,6 @@ static int d3d12va_mpeg2_decode_slice(AVCodecContext *avctx, const uint8_t *buff
static int update_input_arguments(AVCodecContext *avctx, D3D12_VIDEO_DECODE_INPUT_STREAM_ARGUMENTS *input_args, ID3D12Resource *buffer)
{
- D3D12VADecodeContext *ctx = D3D12VA_DECODE_CONTEXT(avctx);
- AVHWFramesContext *frames_ctx = D3D12VA_FRAMES_CONTEXT(avctx);
- AVD3D12VAFramesContext *frames_hwctx = frames_ctx->hwctx;
const MpegEncContext *s = avctx->priv_data;
D3D12DecodePictureContext *ctx_pic = s->current_picture_ptr->hwaccel_picture_private;
@@ -147,7 +141,6 @@ static int d3d12va_mpeg2_end_frame(AVCodecContext *avctx)
{
int ret;
MpegEncContext *s = avctx->priv_data;
- D3D12VADecodeContext *ctx = D3D12VA_DECODE_CONTEXT(avctx);
D3D12DecodePictureContext *ctx_pic = s->current_picture_ptr->hwaccel_picture_private;
if (ctx_pic->slice_count <= 0 || ctx_pic->bitstream_size <= 0)
@@ -163,7 +156,6 @@ static int d3d12va_mpeg2_end_frame(AVCodecContext *avctx)
static int d3d12va_mpeg2_decode_init(AVCodecContext *avctx)
{
- const MpegEncContext *s = avctx->priv_data;
D3D12VADecodeContext *ctx = D3D12VA_DECODE_CONTEXT(avctx);
ctx->cfg.DecodeProfile = D3D12_VIDEO_DECODE_PROFILE_MPEG2;
1
0
ffmpeg | branch: master | Gyan Doshi <ffmpeg(a)gyani.pro> | Sun Dec 31 13:02:20 2023 +0530| [e1c1dc8347f13104bc21e4100fcf4d4dddf5e5d8] | committer: Gyan Doshi
configure: correct lensfun header function check
The function lf_db_new was deprecated in lensfun 09dcd3e7ad in 2017
in favour of lf_db_create. The AVfilter was adjusted in 8b78eb312d
but the configure check wasn't changed.
lensfun removed lf_db_new declaration in lf 35c0017593 so configure
fails to find lensfun.
> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=e1c1dc8347f13104b…
---
configure | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/configure b/configure
index 65b4659b0a..037c08afff 100755
--- a/configure
+++ b/configure
@@ -6776,7 +6776,7 @@ enabled libjxl && require_pkg_config libjxl "libjxl >= 0.7.0" jxl/dec
require_pkg_config libjxl_threads "libjxl_threads >= 0.7.0" jxl/thread_parallel_runner.h JxlThreadParallelRunner
enabled libklvanc && require libklvanc libklvanc/vanc.h klvanc_context_create -lklvanc
enabled libkvazaar && require_pkg_config libkvazaar "kvazaar >= 2.0.0" kvazaar.h kvz_api_get
-enabled liblensfun && require_pkg_config liblensfun lensfun lensfun.h lf_db_new
+enabled liblensfun && require_pkg_config liblensfun lensfun lensfun.h lf_db_create
if enabled libmfx && enabled libvpl; then
die "ERROR: can not use libmfx and libvpl together"
1
0
ffmpeg | branch: master | Lynne <dev(a)lynne.ee> | Mon Jan 1 00:00:00 2024 +0000| [b95ee2ec5f84054de8bf6db9fe1b1119d569f269] | committer: Lynne
configure: update copyright year
> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=b95ee2ec5f84054de…
---
configure | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/configure b/configure
index cd66e42850..65b4659b0a 100755
--- a/configure
+++ b/configure
@@ -8045,7 +8045,7 @@ cat > $TMPH <<EOF
#define FFMPEG_CONFIG_H
#define FFMPEG_CONFIGURATION "$(c_escape $FFMPEG_CONFIGURATION)"
#define FFMPEG_LICENSE "$(c_escape $license)"
-#define CONFIG_THIS_YEAR 2023
+#define CONFIG_THIS_YEAR 2024
#define FFMPEG_DATADIR "$(eval c_escape $datadir)"
#define AVCONV_DATADIR "$(eval c_escape $datadir)"
#define CC_IDENT "$(c_escape ${cc_ident:-Unknown compiler})"
1
0