Imported Upstream version 6.1
[platform/upstream/ffmpeg.git] / libavfilter / vf_varblur.c
1 /*
2  * Copyright (c) 2021 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/opt.h"
23 #include "libavutil/pixdesc.h"
24 #include "avfilter.h"
25 #include "framesync.h"
26 #include "internal.h"
27 #include "video.h"
28
29 typedef struct VarBlurContext {
30     const AVClass *class;
31     FFFrameSync fs;
32
33     int min_radius;
34     int max_radius;
35     int planes;
36
37     int depth;
38     int planewidth[4];
39     int planeheight[4];
40
41     uint8_t *sat[4];
42     int sat_linesize[4];
43     int nb_planes;
44
45     void (*compute_sat)(const uint8_t *ssrc,
46                         int linesize,
47                         int w, int h,
48                         uint8_t *dstp,
49                         int dst_linesize);
50
51     int (*blur_plane)(AVFilterContext *ctx,
52                       uint8_t *ddst,
53                       int ddst_linesize,
54                       const uint8_t *rrptr,
55                       int rrptr_linesize,
56                       int w, int h,
57                       const uint8_t *pptr,
58                       int pptr_linesize,
59                       int slice_start, int slice_end);
60 } VarBlurContext;
61
62 #define OFFSET(x) offsetof(VarBlurContext, x)
63 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM
64
65 static const AVOption varblur_options[] = {
66     { "min_r",  "set min blur radius",  OFFSET(min_radius), AV_OPT_TYPE_INT,   {.i64=0},     0, 254, FLAGS },
67     { "max_r",  "set max blur radius",  OFFSET(max_radius), AV_OPT_TYPE_INT,   {.i64=8},     1, 255, FLAGS },
68     { "planes", "set planes to filter", OFFSET(planes),     AV_OPT_TYPE_INT,   {.i64=0xF},   0, 0xF, FLAGS },
69     { NULL }
70 };
71
72 FRAMESYNC_DEFINE_CLASS(varblur, VarBlurContext, fs);
73
74 static const enum AVPixelFormat pix_fmts[] = {
75     AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV440P,
76     AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P,
77     AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUV420P,
78     AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ420P,
79     AV_PIX_FMT_YUVJ411P, AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV410P,
80     AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9,
81     AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10,
82     AV_PIX_FMT_YUV420P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV440P12,
83     AV_PIX_FMT_YUV420P14, AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV444P14,
84     AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16,
85     AV_PIX_FMT_YUVA420P9, AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA444P9,
86     AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA444P10,
87     AV_PIX_FMT_YUVA422P12, AV_PIX_FMT_YUVA444P12,
88     AV_PIX_FMT_YUVA420P16, AV_PIX_FMT_YUVA422P16, AV_PIX_FMT_YUVA444P16,
89     AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP10,
90     AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRP14, AV_PIX_FMT_GBRP16,
91     AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRAP16,
92     AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY9, AV_PIX_FMT_GRAY10, AV_PIX_FMT_GRAY12, AV_PIX_FMT_GRAY14, AV_PIX_FMT_GRAY16,
93     AV_PIX_FMT_GBRPF32, AV_PIX_FMT_GBRAPF32,
94     AV_PIX_FMT_NONE
95 };
96
97 #define COMPUTE_SAT(type, stype, depth)              \
98 static void compute_sat##depth(const uint8_t *ssrc,  \
99                                int linesize,         \
100                                int w, int h,         \
101                                uint8_t *dstp,        \
102                                int dst_linesize)     \
103 {                                                    \
104     const type *src = (const type *)ssrc;            \
105     stype *dst = (stype *)dstp;                      \
106                                                      \
107     linesize /= (depth / 8);                         \
108     dst_linesize /= sizeof(stype);                   \
109     dst += dst_linesize;                             \
110                                                      \
111     for (int y = 0; y < h; y++) {                    \
112         stype sum = 0;                               \
113                                                      \
114         for (int x = 1; x < w; x++) {                \
115             sum += src[x - 1];                       \
116             dst[x] = sum + dst[x - dst_linesize];    \
117         }                                            \
118                                                      \
119         src += linesize;                             \
120         dst += dst_linesize;                         \
121     }                                                \
122 }
123
124 COMPUTE_SAT(uint8_t,  uint32_t, 8)
125 COMPUTE_SAT(uint16_t, uint64_t, 16)
126 COMPUTE_SAT(float,    double,   32)
127
128 typedef struct ThreadData {
129     AVFrame *in, *out, *radius;
130 } ThreadData;
131
132 static float lerpf(float v0, float v1, float f)
133 {
134     return v0 + (v1 - v0) * f;
135 }
136
137 #define BLUR_PLANE(type, stype, bits)                          \
138 static int blur_plane##bits(AVFilterContext *ctx,              \
139                             uint8_t *ddst,                     \
140                             int ddst_linesize,                 \
141                             const uint8_t *rrptr,              \
142                             int rrptr_linesize,                \
143                             int w, int h,                      \
144                             const uint8_t *pptr,               \
145                             int pptr_linesize,                 \
146                             int slice_start, int slice_end)    \
147 {                                                              \
148     VarBlurContext *s = ctx->priv;                             \
149     const int ddepth = (bits == 32) ? 1 : s->depth;            \
150     const int dst_linesize = ddst_linesize / (bits / 8);       \
151     const int ptr_linesize = pptr_linesize / sizeof(stype);    \
152     const int rptr_linesize = rrptr_linesize / (bits / 8);     \
153     const type *rptr = ((const type *)rrptr) + slice_start * rptr_linesize; \
154     type *dst = ((type *)ddst) + slice_start * dst_linesize;   \
155     const stype *ptr = (stype *)pptr;                          \
156     const float minr = 2.f * s->min_radius + 1.f;              \
157     const float maxr = 2.f * s->max_radius + 1.f;              \
158     const float scaler = (maxr - minr) / ((1 << ddepth) - 1);  \
159                                                                \
160     for (int y = slice_start; y < slice_end; y++) {            \
161         for (int x = 0; x < w; x++) {                          \
162             const float radiusf = minr + (FFMAX(0.f, 2 * rptr[x] + 1 - minr)) * scaler; \
163             const int radius = floorf(radiusf);                \
164             const float factor = radiusf - radius;             \
165             const int nradius = radius + 1;                    \
166             const int l = FFMIN(radius, x);                    \
167             const int r = FFMIN(radius, w - x - 1);            \
168             const int t = FFMIN(radius, y);                    \
169             const int b = FFMIN(radius, h - y - 1);            \
170             const int nl = FFMIN(nradius, x);                  \
171             const int nr = FFMIN(nradius, w - x - 1);          \
172             const int nt = FFMIN(nradius, y);                  \
173             const int nb = FFMIN(nradius, h - y - 1);          \
174             stype tl = ptr[(y - t) * ptr_linesize + x - l];    \
175             stype tr = ptr[(y - t) * ptr_linesize + x + r];    \
176             stype bl = ptr[(y + b) * ptr_linesize + x - l];    \
177             stype br = ptr[(y + b) * ptr_linesize + x + r];    \
178             stype ntl = ptr[(y - nt) * ptr_linesize + x - nl]; \
179             stype ntr = ptr[(y - nt) * ptr_linesize + x + nr]; \
180             stype nbl = ptr[(y + nb) * ptr_linesize + x - nl]; \
181             stype nbr = ptr[(y + nb) * ptr_linesize + x + nr]; \
182             stype div = (l + r) * (t + b);                     \
183             stype ndiv = (nl + nr) * (nt + nb);                \
184             stype p0 = (br + tl - bl - tr) / div;              \
185             stype n0 = (nbr + ntl - nbl - ntr) / ndiv;         \
186                                                                \
187             if (bits == 32)                                    \
188                 dst[x] = lerpf(p0, n0, factor);                \
189             else                                               \
190                 dst[x] = av_clip_uintp2_c(lrintf(              \
191                                       lerpf(p0, n0, factor)),  \
192                                       ddepth);                 \
193         }                                                      \
194                                                                \
195         rptr += rptr_linesize;                                 \
196         dst  += dst_linesize;                                  \
197     }                                                          \
198                                                                \
199     return 0;                                                  \
200 }
201
202 BLUR_PLANE(uint8_t,  uint32_t, 8)
203 BLUR_PLANE(uint16_t, uint64_t, 16)
204 BLUR_PLANE(float,    double,   32)
205
206 static int blur_planes(AVFilterContext *ctx, void *arg,
207                        int jobnr, int nb_jobs)
208 {
209     VarBlurContext *s = ctx->priv;
210     ThreadData *td = arg;
211     AVFrame *radius = td->radius;
212     AVFrame *out = td->out;
213     AVFrame *in = td->in;
214
215     for (int plane = 0; plane < s->nb_planes; plane++) {
216         const int height = s->planeheight[plane];
217         const int slice_start = (height * jobnr) / nb_jobs;
218         const int slice_end = (height * (jobnr+1)) / nb_jobs;
219         const int width = s->planewidth[plane];
220         const int linesize = in->linesize[plane];
221         const int dst_linesize = out->linesize[plane];
222         const uint8_t *rptr = radius->data[plane];
223         const int rptr_linesize = radius->linesize[plane];
224         uint8_t *ptr = s->sat[plane];
225         const int ptr_linesize = s->sat_linesize[plane];
226         const uint8_t *src = in->data[plane];
227         uint8_t *dst = out->data[plane];
228
229         if (!(s->planes & (1 << plane))) {
230             if (out != in)
231                 av_image_copy_plane(dst + slice_start * dst_linesize,
232                                     dst_linesize,
233                                     src + slice_start * linesize,
234                                     linesize,
235                                     width * ((s->depth + 7) / 8),
236                                     slice_end - slice_start);
237             continue;
238         }
239
240         s->blur_plane(ctx, dst, dst_linesize,
241                       rptr, rptr_linesize,
242                       width, height,
243                       ptr, ptr_linesize,
244                       slice_start, slice_end);
245     }
246
247     return 0;
248 }
249
250 static int blur_frame(AVFilterContext *ctx, AVFrame *in, AVFrame *radius)
251 {
252     VarBlurContext *s = ctx->priv;
253     AVFilterLink *outlink = ctx->outputs[0];
254     ThreadData td;
255     AVFrame *out;
256
257     if (av_frame_is_writable(in)) {
258         out = in;
259     } else {
260         out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
261         if (!out) {
262             av_frame_free(&in);
263             return AVERROR(ENOMEM);
264         }
265         av_frame_copy_props(out, in);
266     }
267
268     for (int plane = 0; plane < s->nb_planes; plane++) {
269         const int height = s->planeheight[plane];
270         const int width = s->planewidth[plane];
271         const int linesize = in->linesize[plane];
272         uint8_t *ptr = s->sat[plane];
273         const int ptr_linesize = s->sat_linesize[plane];
274         const uint8_t *src = in->data[plane];
275
276         if (!(s->planes & (1 << plane)))
277             continue;
278
279         s->compute_sat(src, linesize, width, height, ptr, ptr_linesize);
280     }
281
282     td.in = in;
283     td.out = out;
284     td.radius = radius;
285     ff_filter_execute(ctx, blur_planes, &td, NULL,
286                       FFMIN(s->planeheight[1], ff_filter_get_nb_threads(ctx)));
287
288     if (out != in)
289         av_frame_free(&in);
290     return ff_filter_frame(outlink, out);
291 }
292
293 static int activate(AVFilterContext *ctx)
294 {
295     VarBlurContext *s = ctx->priv;
296     return ff_framesync_activate(&s->fs);
297 }
298
299 static int varblur_frame(FFFrameSync *fs)
300 {
301     AVFilterContext *ctx = fs->parent;
302     VarBlurContext *s = ctx->priv;
303     AVFrame *in, *radius;
304     int ret;
305
306     if (s->max_radius <= s->min_radius)
307         s->max_radius = s->min_radius + 1;
308
309     ret = ff_framesync_dualinput_get(fs, &in, &radius);
310     if (ret < 0)
311         return ret;
312     if (!radius)
313         return ff_filter_frame(ctx->outputs[0], in);
314     return blur_frame(ctx, in, radius);
315 }
316
317 static int config_output(AVFilterLink *outlink)
318 {
319     AVFilterContext *ctx = outlink->src;
320     AVFilterLink *inlink = ctx->inputs[0];
321     AVFilterLink *radiuslink = ctx->inputs[1];
322     VarBlurContext *s = ctx->priv;
323     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(outlink->format);
324     int ret;
325
326     if (inlink->w != radiuslink->w || inlink->h != radiuslink->h) {
327         av_log(ctx, AV_LOG_ERROR, "First input link %s parameters "
328                "(size %dx%d) do not match the corresponding "
329                "second input link %s parameters (size %dx%d)\n",
330                ctx->input_pads[0].name, inlink->w, inlink->h,
331                ctx->input_pads[1].name, radiuslink->w, radiuslink->h);
332         return AVERROR(EINVAL);
333     }
334
335     outlink->w = inlink->w;
336     outlink->h = inlink->h;
337     outlink->time_base = inlink->time_base;
338     outlink->sample_aspect_ratio = inlink->sample_aspect_ratio;
339     outlink->frame_rate = inlink->frame_rate;
340
341     s->depth = desc->comp[0].depth;
342     s->blur_plane = s->depth <= 8 ? blur_plane8 : s->depth <= 16 ? blur_plane16 : blur_plane32;
343     s->compute_sat = s->depth <= 8 ? compute_sat8 : s->depth <= 16 ? compute_sat16 : compute_sat32;
344
345     s->planewidth[1]  = s->planewidth[2]  = AV_CEIL_RSHIFT(outlink->w, desc->log2_chroma_w);
346     s->planewidth[0]  = s->planewidth[3]  = outlink->w;
347     s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(outlink->h, desc->log2_chroma_h);
348     s->planeheight[0] = s->planeheight[3] = outlink->h;
349
350     s->nb_planes = av_pix_fmt_count_planes(outlink->format);
351
352     for (int p = 0; p < s->nb_planes; p++) {
353         s->sat_linesize[p] = (outlink->w + 1) * (4 + 4 * (s->depth > 8));
354         s->sat[p] = av_calloc(s->sat_linesize[p], outlink->h + 1);
355         if (!s->sat[p])
356             return AVERROR(ENOMEM);
357     }
358
359     s->fs.on_event = varblur_frame;
360     if ((ret = ff_framesync_init_dualinput(&s->fs, ctx)) < 0)
361         return ret;
362
363     ret = ff_framesync_configure(&s->fs);
364     outlink->time_base = s->fs.time_base;
365
366     return ret;
367 }
368
369 static av_cold void uninit(AVFilterContext *ctx)
370 {
371     VarBlurContext *s = ctx->priv;
372
373     ff_framesync_uninit(&s->fs);
374     for (int p = 0; p < 4; p++)
375         av_freep(&s->sat[p]);
376 }
377
378 static const AVFilterPad varblur_inputs[] = {
379     {
380         .name = "default",
381         .type = AVMEDIA_TYPE_VIDEO,
382     },
383     {
384         .name = "radius",
385         .type = AVMEDIA_TYPE_VIDEO,
386     },
387 };
388
389 static const AVFilterPad varblur_outputs[] = {
390     {
391         .name         = "default",
392         .type         = AVMEDIA_TYPE_VIDEO,
393         .config_props = config_output,
394     },
395 };
396
397 const AVFilter ff_vf_varblur = {
398     .name          = "varblur",
399     .description   = NULL_IF_CONFIG_SMALL("Apply Variable Blur filter."),
400     .priv_size     = sizeof(VarBlurContext),
401     .priv_class    = &varblur_class,
402     .activate      = activate,
403     .preinit       = varblur_framesync_preinit,
404     .uninit        = uninit,
405     FILTER_INPUTS(varblur_inputs),
406     FILTER_OUTPUTS(varblur_outputs),
407     FILTER_PIXFMTS_ARRAY(pix_fmts),
408     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL |
409                      AVFILTER_FLAG_SLICE_THREADS,
410     .process_command = ff_filter_process_command,
411 };