• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/common.h"
22 #include "libavutil/internal.h"
23 #include "libavutil/imgutils.h"
24 #include "libavutil/opt.h"
25 
26 #include "avfilter.h"
27 #include "internal.h"
28 #include "video.h"
29 
30 enum PixelizeModes {
31     PIXELIZE_AVG,
32     PIXELIZE_MIN,
33     PIXELIZE_MAX,
34     PIXELIZE_MODES
35 };
36 
37 typedef struct PixelizeContext {
38     const AVClass *class;
39 
40     int block_w[4], block_h[4];
41     int mode;
42 
43     int depth;
44     int planes;
45     int nb_planes;
46     int linesize[4];
47     int planewidth[4];
48     int planeheight[4];
49 
50     int log2_chroma_w;
51     int log2_chroma_h;
52 
53     int (*pixelize[PIXELIZE_MODES])(const uint8_t *src, uint8_t *dst,
54                                     ptrdiff_t src_linesize,
55                                     ptrdiff_t dst_linesize,
56                                     int w, int h);
57 } PixelizeContext;
58 
59 static const enum AVPixelFormat pix_fmts[] = {
60     AV_PIX_FMT_GRAY8,
61     AV_PIX_FMT_GRAY9,
62     AV_PIX_FMT_GRAY10,
63     AV_PIX_FMT_GRAY12,
64     AV_PIX_FMT_GRAY14,
65     AV_PIX_FMT_GRAY16,
66     AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUV411P,
67     AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P,
68     AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV444P,
69     AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ422P,
70     AV_PIX_FMT_YUVJ440P, AV_PIX_FMT_YUVJ444P,
71     AV_PIX_FMT_YUVJ411P,
72     AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9,
73     AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10,
74     AV_PIX_FMT_YUV440P10,
75     AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV420P12,
76     AV_PIX_FMT_YUV440P12,
77     AV_PIX_FMT_YUV444P14, AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV420P14,
78     AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16,
79     AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP10,
80     AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRP14, AV_PIX_FMT_GBRP16,
81     AV_PIX_FMT_YUVA420P,  AV_PIX_FMT_YUVA422P,   AV_PIX_FMT_YUVA444P,
82     AV_PIX_FMT_YUVA444P9, AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_YUVA444P12, AV_PIX_FMT_YUVA444P16,
83     AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA422P12, AV_PIX_FMT_YUVA422P16,
84     AV_PIX_FMT_YUVA420P9, AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA420P16,
85     AV_PIX_FMT_GBRAP,     AV_PIX_FMT_GBRAP10,    AV_PIX_FMT_GBRAP12,    AV_PIX_FMT_GBRAP16,
86     AV_PIX_FMT_NONE
87 };
88 
89 typedef struct ThreadData {
90     AVFrame *in, *out;
91 } ThreadData;
92 
93 #define PIXELIZE_AVG(name, type, stype) \
94 static int pixelize_avg##name(const uint8_t *ssrc, uint8_t *ddst, \
95                               ptrdiff_t src_linesize, ptrdiff_t dst_linesize, \
96                               int w, int h)       \
97 {                                                 \
98     const type *src = (const type *)ssrc;         \
99     type *dst = (type *)ddst;                     \
100     stype sum = 0;                                \
101     type fill;                                    \
102     for (int y = 0; y < h; y++) {                 \
103         for (int x = 0; x < w; x++)               \
104             sum += src[x];                        \
105                                                   \
106         src += src_linesize / sizeof(type);       \
107     }                                             \
108                                                   \
109     fill = sum / (w * h);                         \
110                                                   \
111     for (int y = 0; y < h; y++) {                 \
112         for (int x = 0; x < w; x++)               \
113             dst[x] = fill;                        \
114                                                   \
115         dst += dst_linesize / sizeof(type);       \
116     }                                             \
117                                                   \
118     return 0;                                     \
119 }
120 
121 #define PIXELIZE_MIN(name, type, stype) \
122 static int pixelize_min##name(const uint8_t *ssrc, uint8_t *ddst, \
123                               ptrdiff_t src_linesize, ptrdiff_t dst_linesize, \
124                               int w, int h)       \
125 {                                                 \
126     const type *src = (const type *)ssrc;         \
127     type *dst = (type *)ddst;                     \
128     type fill = src[0];                           \
129                                                   \
130     for (int y = 0; y < h; y++) {                 \
131         for (int x = 0; x < w; x++)               \
132             fill = FFMIN(src[x], fill);           \
133                                                   \
134         src += src_linesize / sizeof(type);       \
135     }                                             \
136                                                   \
137     for (int y = 0; y < h; y++) {                 \
138         for (int x = 0; x < w; x++)               \
139             dst[x] = fill;                        \
140                                                   \
141         dst += dst_linesize / sizeof(type);       \
142     }                                             \
143                                                   \
144     return 0;                                     \
145 }
146 
147 #define PIXELIZE_MAX(name, type, stype) \
148 static int pixelize_max##name(const uint8_t *ssrc, uint8_t *ddst, \
149                               ptrdiff_t src_linesize, ptrdiff_t dst_linesize, \
150                               int w, int h)       \
151 {                                                 \
152     const type *src = (const type *)ssrc;         \
153     type *dst = (type *)ddst;                     \
154     type fill = src[0];                           \
155                                                   \
156     for (int y = 0; y < h; y++) {                 \
157         for (int x = 0; x < w; x++)               \
158             fill = FFMAX(src[x], fill);           \
159                                                   \
160         src += src_linesize / sizeof(type);       \
161     }                                             \
162                                                   \
163     for (int y = 0; y < h; y++) {                 \
164         for (int x = 0; x < w; x++)               \
165             dst[x] = fill;                        \
166                                                   \
167         dst += dst_linesize / sizeof(type);       \
168     }                                             \
169                                                   \
170     return 0;                                     \
171 }
172 
173 PIXELIZE_AVG(8,  uint8_t,  unsigned)
174 PIXELIZE_AVG(16, uint16_t, uint64_t)
175 PIXELIZE_MIN(8,  uint8_t,  unsigned)
176 PIXELIZE_MIN(16, uint16_t, uint64_t)
177 PIXELIZE_MAX(8,  uint8_t,  unsigned)
178 PIXELIZE_MAX(16, uint16_t, uint64_t)
179 
pixelize_slice(AVFilterContext * ctx,void * arg,int jobnr,int nb_jobs)180 static int pixelize_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
181 {
182     PixelizeContext *s = ctx->priv;
183     const int mode = s->mode;
184     ThreadData *td = arg;
185     AVFrame *out = td->out;
186     AVFrame *in = td->in;
187 
188     for (int p = 0; p < s->nb_planes; p++) {
189         const int wh = s->planeheight[p];
190         const int h = (s->planeheight[p] + s->block_h[p] - 1) / s->block_h[p];
191         const int w = (s->planewidth[p] + s->block_w[p] - 1) / s->block_w[p];
192         const int wslice_start = (wh * jobnr) / nb_jobs;
193         const int wslice_end = (wh * (jobnr+1)) / nb_jobs;
194         const int slice_start = (h * jobnr) / nb_jobs;
195         const int slice_end = (h * (jobnr+1)) / nb_jobs;
196         const ptrdiff_t out_linesize = out->linesize[p];
197         const ptrdiff_t in_linesize = in->linesize[p];
198         const uint8_t *src = in->data[p];
199         uint8_t *dst = out->data[p];
200 
201         if (!((1 << p) & s->planes)) {
202             av_image_copy_plane(dst + wslice_start * out_linesize,
203                                 out_linesize,
204                                 src + wslice_start * in_linesize,
205                                 in_linesize,
206                                 s->linesize[p], wslice_end - wslice_start);
207             continue;
208         }
209 
210         for (int y = slice_start; y < slice_end; y++) {
211             const int block_h = FFMIN(s->block_h[p], s->planeheight[p] - y * s->block_h[p]);
212             for (int x = 0; x < w; x++) {
213                 const int block_w = FFMIN(s->block_w[p], s->planewidth[p] - x * s->block_w[p]);
214 
215                 s->pixelize[mode](src + s->block_h[p] * y * in_linesize  +
216                                   x * s->block_w[p] * (1 + (s->depth > 8)),
217                                   dst + s->block_h[p] * y * out_linesize +
218                                   x * s->block_w[p] * (1 + (s->depth > 8)),
219                                   in_linesize, out_linesize, block_w, block_h);
220             }
221         }
222     }
223 
224     return 0;
225 }
226 
config_output(AVFilterLink * outlink)227 static int config_output(AVFilterLink *outlink)
228 {
229     AVFilterContext *ctx = outlink->src;
230     PixelizeContext *s = ctx->priv;
231     AVFilterLink *inlink = ctx->inputs[0];
232     const AVPixFmtDescriptor *desc;
233     int ret;
234 
235     desc = av_pix_fmt_desc_get(outlink->format);
236     if (!desc)
237         return AVERROR_BUG;
238     s->nb_planes = av_pix_fmt_count_planes(outlink->format);
239     s->depth = desc->comp[0].depth;
240 
241     if ((ret = av_image_fill_linesizes(s->linesize, inlink->format, inlink->w)) < 0)
242         return ret;
243 
244     s->planewidth[1] = s->planewidth[2] = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w);
245     s->planewidth[0] = s->planewidth[3] = inlink->w;
246 
247     s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
248     s->planeheight[0] = s->planeheight[3] = inlink->h;
249 
250     s->log2_chroma_w = desc->log2_chroma_w;
251     s->log2_chroma_h = desc->log2_chroma_h;
252 
253     s->pixelize[PIXELIZE_AVG] = s->depth <= 8 ? pixelize_avg8 : pixelize_avg16;
254     s->pixelize[PIXELIZE_MIN] = s->depth <= 8 ? pixelize_min8 : pixelize_min16;
255     s->pixelize[PIXELIZE_MAX] = s->depth <= 8 ? pixelize_max8 : pixelize_max16;
256 
257     return 0;
258 }
259 
filter_frame(AVFilterLink * inlink,AVFrame * in)260 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
261 {
262     AVFilterContext *ctx = inlink->dst;
263     AVFilterLink *outlink = ctx->outputs[0];
264     PixelizeContext *s = ctx->priv;
265     ThreadData td;
266     AVFrame *out;
267     int ret;
268 
269     s->block_w[1] = s->block_w[2] = FFMAX(1, s->block_w[0] >> s->log2_chroma_w);
270     s->block_w[3] = s->block_w[0] = s->block_w[1] << s->log2_chroma_w;
271 
272     s->block_h[1] = s->block_h[2] = FFMAX(1, s->block_h[0] >> s->log2_chroma_h);
273     s->block_h[3] = s->block_h[0] = s->block_h[1] << s->log2_chroma_h;
274 
275     if (av_frame_is_writable(in)) {
276         out = in;
277     } else {
278         out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
279         if (!out) {
280             ret = AVERROR(ENOMEM);
281             goto fail;
282         }
283 
284         ret = av_frame_copy_props(out, in);
285         if (ret < 0) {
286             av_frame_free(&out);
287             goto fail;
288         }
289     }
290 
291     td.out = out;
292     td.in = in;
293     ff_filter_execute(ctx, pixelize_slice, &td, NULL,
294                       FFMIN((s->planeheight[1] + s->block_h[1] - 1) / s->block_h[1],
295                             ff_filter_get_nb_threads(ctx)));
296 
297     if (out != in)
298         av_frame_free(&in);
299     return ff_filter_frame(outlink, out);
300 fail:
301     av_frame_free(&in);
302     return ret;
303 }
304 
305 #define OFFSET(x) offsetof(PixelizeContext, x)
306 #define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_RUNTIME_PARAM)
307 
308 static const AVOption pixelize_options[] = {
309     { "width",  "set block width",  OFFSET(block_w[0]), AV_OPT_TYPE_INT, {.i64=16}, 1, 1024, FLAGS },
310     { "w",      "set block width",  OFFSET(block_w[0]), AV_OPT_TYPE_INT, {.i64=16}, 1, 1024, FLAGS },
311     { "height", "set block height", OFFSET(block_h[0]), AV_OPT_TYPE_INT, {.i64=16}, 1, 1024, FLAGS },
312     { "h",      "set block height", OFFSET(block_h[0]), AV_OPT_TYPE_INT, {.i64=16}, 1, 1024, FLAGS },
313     { "mode",  "set the pixelize mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, PIXELIZE_MODES-1, FLAGS, "mode" },
314     { "m",     "set the pixelize mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, PIXELIZE_MODES-1, FLAGS, "mode" },
315     {  "avg",    "average",  0,  AV_OPT_TYPE_CONST, {.i64=PIXELIZE_AVG}, 0,  0, FLAGS, "mode" },
316     {  "min",    "minimum",  0,  AV_OPT_TYPE_CONST, {.i64=PIXELIZE_MIN}, 0,  0, FLAGS, "mode" },
317     {  "max",    "maximum",  0,  AV_OPT_TYPE_CONST, {.i64=PIXELIZE_MAX}, 0,  0, FLAGS, "mode" },
318     { "planes", "set what planes to filter", OFFSET(planes), AV_OPT_TYPE_FLAGS, {.i64=15}, 0, 15, FLAGS },
319     { "p",      "set what planes to filter", OFFSET(planes), AV_OPT_TYPE_FLAGS, {.i64=15}, 0, 15, FLAGS },
320     { NULL },
321 };
322 
323 AVFILTER_DEFINE_CLASS(pixelize);
324 
325 static const AVFilterPad pixelize_inputs[] = {
326     {
327         .name         = "default",
328         .type         = AVMEDIA_TYPE_VIDEO,
329         .filter_frame = filter_frame,
330     },
331 };
332 
333 static const AVFilterPad pixelize_outputs[] = {
334     {
335         .name          = "default",
336         .type          = AVMEDIA_TYPE_VIDEO,
337         .config_props  = config_output,
338     },
339 };
340 
341 const AVFilter ff_vf_pixelize = {
342     .name          = "pixelize",
343     .description   = NULL_IF_CONFIG_SMALL("Pixelize video."),
344     .priv_size     = sizeof(PixelizeContext),
345     .priv_class    = &pixelize_class,
346     FILTER_INPUTS(pixelize_inputs),
347     FILTER_OUTPUTS(pixelize_outputs),
348     FILTER_PIXFMTS_ARRAY(pix_fmts),
349     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
350     .process_command = ff_filter_process_command,
351 };
352