History

avfilter: add multiply video filter

Paul B Mahol committed 4 years ago

Commit 767f66e1 parent a6f136a6 files · +280 −1Browse files

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

Unified · Split

4 files
  1. libavfilter/Makefile +1 −0
  2. libavfilter/allfilters.c +1 −0
  3. libavfilter/version.h +1 −1
  4. libavfilter/vf_multiply.c +253 −0
libavfilter/Makefile+1−0
@@ -366,6 +366,7 @@ OBJS-$(CONFIG_MIX_FILTER) += vf_mix.o framesync.o · 365 unmodified lines
366366 OBJS-$(CONFIG_MONOCHROME_FILTER) += vf_monochrome.o
367367 OBJS-$(CONFIG_MORPHO_FILTER) += vf_morpho.o
368368 OBJS-$(CONFIG_MPDECIMATE_FILTER) += vf_mpdecimate.o
369+OBJS-$(CONFIG_MULTIPLY_FILTER) += vf_multiply.o
369370 OBJS-$(CONFIG_NEGATE_FILTER) += vf_negate.o
370371 OBJS-$(CONFIG_NLMEANS_FILTER) += vf_nlmeans.o
371372 OBJS-$(CONFIG_NLMEANS_OPENCL_FILTER) += vf_nlmeans_opencl.o opencl.o opencl/nlmeans.o
libavfilter/allfilters.c+1−0
@@ -350,6 +350,7 @@ extern const AVFilter ff_vf_monochrome; · 349 unmodified lines
350350 extern const AVFilter ff_vf_morpho;
351351 extern const AVFilter ff_vf_mpdecimate;
352352 extern const AVFilter ff_vf_msad;
353+extern const AVFilter ff_vf_multiply;
353354 extern const AVFilter ff_vf_negate;
354355 extern const AVFilter ff_vf_nlmeans;
355356 extern const AVFilter ff_vf_nlmeans_opencl;
libavfilter/version.h+1−1
@@ -31,7 +31,7 @@ · 30 unmodified lines
3131
3232 #include "version_major.h"
3333
34−#define LIBAVFILTER_VERSION_MINOR 37
34+#define LIBAVFILTER_VERSION_MINOR 38
3535 #define LIBAVFILTER_VERSION_MICRO 100
3636
3737
libavfilter/vf_multiply.c+253−0
@@ -0,0 +1,253 @@
1+/*
2+ * Copyright (c) 2022 Paul B Mahol
3+ *
4+ * This file is part of FFmpeg.
5+ *
6+ * FFmpeg is free software; you can redistribute it and/or
7+ * modify it under the terms of the GNU Lesser General Public
8+ * License as published by the Free Software Foundation; either
9+ * version 2.1 of the License, or (at your option) any later version.
10+ *
11+ * FFmpeg is distributed in the hope that it will be useful,
12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+ * Lesser General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU Lesser General Public
17+ * License along with FFmpeg; if not, write to the Free Software
18+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19+ */
20+
21+#include "libavutil/imgutils.h"
22+#include "libavutil/pixdesc.h"
23+#include "libavutil/opt.h"
24+#include "avfilter.h"
25+#include "formats.h"
26+#include "internal.h"
27+#include "video.h"
28+#include "framesync.h"
29+
30+typedef struct MultiplyContext {
31+ const AVClass *class;
32+
33+ float offset;
34+ float scale;
35+ int planes;
36+
37+ int linesize[4];
38+ int nb_planes;
39+
40+ FFFrameSync fs;
41+} MultiplyContext;
42+
43+#define OFFSET(x) offsetof(MultiplyContext, x)
44+#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM
45+
46+typedef struct ThreadData {
47+ AVFrame *src, *ref, *dst;
48+} ThreadData;
49+
50+static const AVOption multiply_options[] = {
51+ { "scale", "set scale", OFFSET(scale), AV_OPT_TYPE_FLOAT, {.dbl=1}, 0., 9., FLAGS },
52+ { "offset", "set offset", OFFSET(offset), AV_OPT_TYPE_FLOAT, {.dbl=0.5}, -1., 1., FLAGS },
53+ { "planes", "set planes", OFFSET(planes), AV_OPT_TYPE_FLAGS, {.i64=0xF}, 0., 0xF, FLAGS },
54+ { NULL }
55+};
56+
57+static const enum AVPixelFormat pix_fmts[] = {
58+ AV_PIX_FMT_GBRPF32, AV_PIX_FMT_GBRAPF32,
59+ AV_PIX_FMT_NONE
60+};
61+
62+static int config_input(AVFilterLink *inlink)
63+{
64+ AVFilterContext *ctx = inlink->dst;
65+ MultiplyContext *s = ctx->priv;
66+ int ret;
67+
68+ s->nb_planes = av_pix_fmt_count_planes(inlink->format);
69+ if ((ret = av_image_fill_linesizes(s->linesize, inlink->format, inlink->w)) < 0)
70+ return ret;
71+
72+ return 0;
73+}
74+
75+static void multiply(const uint8_t *ssrc, const uint8_t *rref, uint8_t *ddst,
76+ float scale, float offset, int w)
77+{
78+ const float *src = (const float *)ssrc;
79+ const float *ref = (const float *)rref;
80+ float *dst = (float *)ddst;
81+
82+ for (int x = 0; x < w; x++) {
83+ const float factor = (ref[x] + offset) * scale;
84+
85+ dst[x] = src[x] * factor;
86+ }
87+}
88+
89+static int multiply_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
90+{
91+ MultiplyContext *s = ctx->priv;
92+ const float offset = s->offset;
93+ const float scale = s->scale;
94+ ThreadData *td = arg;
95+
96+ for (int p = 0; p < s->nb_planes; p++) {
97+ const ptrdiff_t src_linesize = td->src->linesize[p];
98+ const ptrdiff_t ref_linesize = td->ref->linesize[p];
99+ const ptrdiff_t dst_linesize = td->dst->linesize[p];
100+ const int w = td->src->width;
101+ const int h = td->src->height;
102+ const int slice_start = (h * jobnr) / nb_jobs;
103+ const int slice_end = (h * (jobnr+1)) / nb_jobs;
104+ const uint8_t *src = td->src->data[p] + slice_start * src_linesize;
105+ const uint8_t *ref = td->ref->data[p] + slice_start * ref_linesize;
106+ uint8_t *dst = td->dst->data[p] + slice_start * dst_linesize;
107+
108+ if (!((1 << p) & s->planes)) {
109+ av_image_copy_plane(dst, dst_linesize, ref, ref_linesize,
110+ s->linesize[p], slice_end - slice_start);
111+ continue;
112+ }
113+
114+ for (int y = slice_start; y < slice_end; y++) {
115+ multiply(src, ref, dst, scale, offset, w);
116+
117+ dst += dst_linesize;
118+ src += src_linesize;
119+ ref += ref_linesize;
120+ }
121+ }
122+
123+ return 0;
124+}
125+
126+static int process_frame(FFFrameSync *fs)
127+{
128+ AVFilterContext *ctx = fs->parent;
129+ MultiplyContext *s = fs->opaque;
130+ AVFilterLink *outlink = ctx->outputs[0];
131+ AVFrame *out, *src, *ref;
132+ int ret;
133+
134+ if ((ret = ff_framesync_get_frame(&s->fs, 0, &src, 0)) < 0 ||
135+ (ret = ff_framesync_get_frame(&s->fs, 1, &ref, 0)) < 0)
136+ return ret;
137+
138+ if (ctx->is_disabled) {
139+ out = av_frame_clone(src);
140+ if (!out)
141+ return AVERROR(ENOMEM);
142+ } else {
143+ ThreadData td;
144+
145+ out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
146+ if (!out)
147+ return AVERROR(ENOMEM);
148+ av_frame_copy_props(out, src);
149+
150+ td.src = src;
151+ td.ref = ref;
152+ td.dst = out;
153+
154+ ff_filter_execute(ctx, multiply_slice, &td, NULL,
155+ FFMIN(outlink->h, ff_filter_get_nb_threads(ctx)));
156+ }
157+ out->pts = av_rescale_q(s->fs.pts, s->fs.time_base, outlink->time_base);
158+
159+ return ff_filter_frame(outlink, out);
160+}
161+
162+static int config_output(AVFilterLink *outlink)
163+{
164+ AVFilterContext *ctx = outlink->src;
165+ MultiplyContext *s = ctx->priv;
166+ AVFilterLink *source = ctx->inputs[0];
167+ AVFilterLink *ref = ctx->inputs[1];
168+ FFFrameSyncIn *in;
169+ int ret;
170+
171+ if (source->w != ref->w || source->h != ref->h) {
172+ av_log(ctx, AV_LOG_ERROR, "First input link %s parameters "
173+ "(size %dx%d) do not match the corresponding "
174+ "second input link %s parameters (%dx%d)\n",
175+ ctx->input_pads[0].name, source->w, source->h,
176+ ctx->input_pads[1].name, ref->w, ref->h);
177+ return AVERROR(EINVAL);
178+ }
179+
180+ outlink->w = source->w;
181+ outlink->h = source->h;
182+ outlink->sample_aspect_ratio = source->sample_aspect_ratio;
183+ outlink->frame_rate = source->frame_rate;
184+
185+ if ((ret = ff_framesync_init(&s->fs, ctx, 2)) < 0)
186+ return ret;
187+
188+ in = s->fs.in;
189+ in[0].time_base = source->time_base;
190+ in[1].time_base = ref->time_base;
191+ in[0].sync = 1;
192+ in[0].before = EXT_STOP;
193+ in[0].after = EXT_INFINITY;
194+ in[1].sync = 1;
195+ in[1].before = EXT_STOP;
196+ in[1].after = EXT_INFINITY;
197+ s->fs.opaque = s;
198+ s->fs.on_event = process_frame;
199+
200+ ret = ff_framesync_configure(&s->fs);
201+ outlink->time_base = s->fs.time_base;
202+
203+ return ret;
204+}
205+
206+static int activate(AVFilterContext *ctx)
207+{
208+ MultiplyContext *s = ctx->priv;
209+ return ff_framesync_activate(&s->fs);
210+}
211+
212+static av_cold void uninit(AVFilterContext *ctx)
213+{
214+ MultiplyContext *s = ctx->priv;
215+
216+ ff_framesync_uninit(&s->fs);
217+}
218+
219+static const AVFilterPad multiply_inputs[] = {
220+ {
221+ .name = "source",
222+ .type = AVMEDIA_TYPE_VIDEO,
223+ .config_props = config_input,
224+ },
225+ {
226+ .name = "factor",
227+ .type = AVMEDIA_TYPE_VIDEO,
228+ },
229+};
230+
231+static const AVFilterPad multiply_outputs[] = {
232+ {
233+ .name = "default",
234+ .type = AVMEDIA_TYPE_VIDEO,
235+ .config_props = config_output,
236+ },
237+};
238+
239+AVFILTER_DEFINE_CLASS(multiply);
240+
241+const AVFilter ff_vf_multiply = {
242+ .name = "multiply",
243+ .description = NULL_IF_CONFIG_SMALL("Multiply first video stream with second video stream."),
244+ .priv_class = &multiply_class,
245+ .priv_size = sizeof(MultiplyContext),
246+ .uninit = uninit,
247+ .activate = activate,
248+ FILTER_INPUTS(multiply_inputs),
249+ FILTER_OUTPUTS(multiply_outputs),
250+ FILTER_PIXFMTS_ARRAY(pix_fmts),
251+ .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS,
252+ .process_command = ff_filter_process_command,
253+};

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t