• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2018 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/avstring.h"
22 #include "libavutil/eval.h"
23 #include "libavutil/imgutils.h"
24 #include "libavutil/intreadwrite.h"
25 #include "libavutil/opt.h"
26 #include "libavutil/pixdesc.h"
27 
28 #include "avfilter.h"
29 #include "formats.h"
30 #include "internal.h"
31 #include "video.h"
32 
33 typedef struct ChromaShiftContext {
34     const AVClass *class;
35     int cbh, cbv;
36     int crh, crv;
37     int rh, rv;
38     int gh, gv;
39     int bh, bv;
40     int ah, av;
41     int edge;
42 
43     int nb_planes;
44     int depth;
45     int height[4];
46     int width[4];
47     int linesize[4];
48 
49     AVFrame *in;
50 
51     int is_rgbashift;
52     int (*filter_slice[2])(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs);
53 } ChromaShiftContext;
54 
55 #define DEFINE_SMEAR(depth, type, div)                                                    \
56 static int smear_slice ## depth(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)  \
57 {                                                                                         \
58     ChromaShiftContext *s = ctx->priv;                                                    \
59     AVFrame *in = s->in;                                                                  \
60     AVFrame *out = arg;                                                                   \
61     const int sulinesize = in->linesize[1] / div;                                         \
62     const int svlinesize = in->linesize[2] / div;                                         \
63     const int ulinesize = out->linesize[1] / div;                                         \
64     const int vlinesize = out->linesize[2] / div;                                         \
65     const int cbh = s->cbh;                                                               \
66     const int cbv = s->cbv;                                                               \
67     const int crh = s->crh;                                                               \
68     const int crv = s->crv;                                                               \
69     const int h = s->height[1];                                                           \
70     const int w = s->width[1];                                                            \
71     const int slice_start = (h * jobnr) / nb_jobs;                                        \
72     const int slice_end = (h * (jobnr+1)) / nb_jobs;                                      \
73     const type *su = (const type *)in->data[1];                                           \
74     const type *sv = (const type *)in->data[2];                                           \
75     type *du = (type *)out->data[1] + slice_start * ulinesize;                            \
76     type *dv = (type *)out->data[2] + slice_start * vlinesize;                            \
77                                                                                           \
78     for (int y = slice_start; y < slice_end; y++) {                                       \
79         const int duy = av_clip(y - cbv, 0, h-1) * sulinesize;                            \
80         const int dvy = av_clip(y - crv, 0, h-1) * svlinesize;                            \
81                                                                                           \
82         for (int x = 0; x < w; x++) {                                                     \
83             du[x] = su[av_clip(x - cbh, 0, w - 1) + duy];                                 \
84             dv[x] = sv[av_clip(x - crh, 0, w - 1) + dvy];                                 \
85         }                                                                                 \
86                                                                                           \
87         du += ulinesize;                                                                  \
88         dv += vlinesize;                                                                  \
89     }                                                                                     \
90                                                                                           \
91     return 0;                                                                             \
92 }
93 
94 DEFINE_SMEAR(8, uint8_t, 1)
95 DEFINE_SMEAR(16, uint16_t, 2)
96 
97 #define DEFINE_WRAP(depth, type, div)                                                     \
98 static int wrap_slice ## depth(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)   \
99 {                                                                                         \
100     ChromaShiftContext *s = ctx->priv;                                                    \
101     AVFrame *in = s->in;                                                                  \
102     AVFrame *out = arg;                                                                   \
103     const int sulinesize = in->linesize[1] / div;                                         \
104     const int svlinesize = in->linesize[2] / div;                                         \
105     const int ulinesize = out->linesize[1] / div;                                         \
106     const int vlinesize = out->linesize[2] / div;                                         \
107     const int cbh = s->cbh;                                                               \
108     const int cbv = s->cbv;                                                               \
109     const int crh = s->crh;                                                               \
110     const int crv = s->crv;                                                               \
111     const int h = s->height[1];                                                           \
112     const int w = s->width[1];                                                            \
113     const int slice_start = (h * jobnr) / nb_jobs;                                        \
114     const int slice_end = (h * (jobnr+1)) / nb_jobs;                                      \
115     const type *su = (const type *)in->data[1];                                           \
116     const type *sv = (const type *)in->data[2];                                           \
117     type *du = (type *)out->data[1] + slice_start * ulinesize;                            \
118     type *dv = (type *)out->data[2] + slice_start * vlinesize;                            \
119                                                                                           \
120     for (int y = slice_start; y < slice_end; y++) {                                       \
121         int uy = (y - cbv) % h;                                                           \
122         int vy = (y - crv) % h;                                                           \
123                                                                                           \
124         if (uy < 0)                                                                       \
125             uy += h;                                                                      \
126         if (vy < 0)                                                                       \
127             vy += h;                                                                      \
128                                                                                           \
129         for (int x = 0; x < w; x++) {                                                     \
130             int ux = (x - cbh) % w;                                                       \
131             int vx = (x - crh) % w;                                                       \
132                                                                                           \
133             if (ux < 0)                                                                   \
134                 ux += w;                                                                  \
135             if (vx < 0)                                                                   \
136                 vx += w;                                                                  \
137                                                                                           \
138             du[x] = su[ux + uy * sulinesize];                                             \
139             dv[x] = sv[vx + vy * svlinesize];                                             \
140         }                                                                                 \
141                                                                                           \
142         du += ulinesize;                                                                  \
143         dv += vlinesize;                                                                  \
144     }                                                                                     \
145                                                                                           \
146     return 0;                                                                             \
147 }
148 
149 DEFINE_WRAP(8, uint8_t, 1)
150 DEFINE_WRAP(16, uint16_t, 2)
151 
152 #define DEFINE_RGBASMEAR(depth, type, div)                                                    \
153 static int rgbasmear_slice ## depth(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)  \
154 {                                                                                         \
155     ChromaShiftContext *s = ctx->priv;                                                    \
156     AVFrame *in = s->in;                                                                  \
157     AVFrame *out = arg;                                                                   \
158     const int srlinesize = in->linesize[2] / div;                                         \
159     const int sglinesize = in->linesize[0] / div;                                         \
160     const int sblinesize = in->linesize[1] / div;                                         \
161     const int salinesize = in->linesize[3] / div;                                         \
162     const int rlinesize = out->linesize[2] / div;                                         \
163     const int glinesize = out->linesize[0] / div;                                         \
164     const int blinesize = out->linesize[1] / div;                                         \
165     const int alinesize = out->linesize[3] / div;                                         \
166     const int rh = s->rh;                                                                 \
167     const int rv = s->rv;                                                                 \
168     const int gh = s->gh;                                                                 \
169     const int gv = s->gv;                                                                 \
170     const int bh = s->bh;                                                                 \
171     const int bv = s->bv;                                                                 \
172     const int ah = s->ah;                                                                 \
173     const int av = s->av;                                                                 \
174     const int h = s->height[1];                                                           \
175     const int w = s->width[1];                                                            \
176     const int slice_start = (h * jobnr) / nb_jobs;                                        \
177     const int slice_end = (h * (jobnr+1)) / nb_jobs;                                      \
178     const type *sr = (const type *)in->data[2];                                           \
179     const type *sg = (const type *)in->data[0];                                           \
180     const type *sb = (const type *)in->data[1];                                           \
181     const type *sa = (const type *)in->data[3];                                           \
182     type *dr = (type *)out->data[2] + slice_start * rlinesize;                            \
183     type *dg = (type *)out->data[0] + slice_start * glinesize;                            \
184     type *db = (type *)out->data[1] + slice_start * blinesize;                            \
185     type *da = (type *)out->data[3] + slice_start * alinesize;                            \
186                                                                                           \
187     for (int y = slice_start; y < slice_end; y++) {                                       \
188         const int ry = av_clip(y - rv, 0, h-1) * srlinesize;                              \
189         const int gy = av_clip(y - gv, 0, h-1) * sglinesize;                              \
190         const int by = av_clip(y - bv, 0, h-1) * sblinesize;                              \
191         int ay;                                                                           \
192                                                                                           \
193         for (int x = 0; x < w; x++) {                                                     \
194             dr[x] = sr[av_clip(x - rh, 0, w - 1) + ry];                                   \
195             dg[x] = sg[av_clip(x - gh, 0, w - 1) + gy];                                   \
196             db[x] = sb[av_clip(x - bh, 0, w - 1) + by];                                   \
197         }                                                                                 \
198                                                                                           \
199         dr += rlinesize;                                                                  \
200         dg += glinesize;                                                                  \
201         db += blinesize;                                                                  \
202                                                                                           \
203         if (s->nb_planes < 4)                                                             \
204             continue;                                                                     \
205         ay = av_clip(y - av, 0, h-1) * salinesize;                                        \
206         for (int x = 0; x < w; x++) {                                                     \
207             da[x] = sa[av_clip(x - ah, 0, w - 1) + ay];                                   \
208         }                                                                                 \
209                                                                                           \
210         da += alinesize;                                                                  \
211     }                                                                                     \
212                                                                                           \
213     return 0;                                                                             \
214 }
215 
216 DEFINE_RGBASMEAR(8, uint8_t, 1)
217 DEFINE_RGBASMEAR(16, uint16_t, 2)
218 
219 #define DEFINE_RGBAWRAP(depth, type, div)                                                     \
220 static int rgbawrap_slice ## depth(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)   \
221 {                                                                                         \
222     ChromaShiftContext *s = ctx->priv;                                                    \
223     AVFrame *in = s->in;                                                                  \
224     AVFrame *out = arg;                                                                   \
225     const int srlinesize = in->linesize[2] / div;                                         \
226     const int sglinesize = in->linesize[0] / div;                                         \
227     const int sblinesize = in->linesize[1] / div;                                         \
228     const int salinesize = in->linesize[3] / div;                                         \
229     const int rlinesize = out->linesize[2] / div;                                         \
230     const int glinesize = out->linesize[0] / div;                                         \
231     const int blinesize = out->linesize[1] / div;                                         \
232     const int alinesize = out->linesize[3] / div;                                         \
233     const int rh = s->rh;                                                                 \
234     const int rv = s->rv;                                                                 \
235     const int gh = s->gh;                                                                 \
236     const int gv = s->gv;                                                                 \
237     const int bh = s->bh;                                                                 \
238     const int bv = s->bv;                                                                 \
239     const int ah = s->ah;                                                                 \
240     const int av = s->av;                                                                 \
241     const int h = s->height[1];                                                           \
242     const int w = s->width[1];                                                            \
243     const int slice_start = (h * jobnr) / nb_jobs;                                        \
244     const int slice_end = (h * (jobnr+1)) / nb_jobs;                                      \
245     const type *sr = (const type *)in->data[2];                                           \
246     const type *sg = (const type *)in->data[0];                                           \
247     const type *sb = (const type *)in->data[1];                                           \
248     const type *sa = (const type *)in->data[3];                                           \
249     type *dr = (type *)out->data[2] + slice_start * rlinesize;                            \
250     type *dg = (type *)out->data[0] + slice_start * glinesize;                            \
251     type *db = (type *)out->data[1] + slice_start * blinesize;                            \
252     type *da = (type *)out->data[3] + slice_start * alinesize;                            \
253                                                                                           \
254     for (int y = slice_start; y < slice_end; y++) {                                       \
255         int ry = (y - rv) % h;                                                            \
256         int gy = (y - gv) % h;                                                            \
257         int by = (y - bv) % h;                                                            \
258                                                                                           \
259         if (ry < 0)                                                                       \
260             ry += h;                                                                      \
261         if (gy < 0)                                                                       \
262             gy += h;                                                                      \
263         if (by < 0)                                                                       \
264             by += h;                                                                      \
265                                                                                           \
266         for (int x = 0; x < w; x++) {                                                     \
267             int rx = (x - rh) % w;                                                        \
268             int gx = (x - gh) % w;                                                        \
269             int bx = (x - bh) % w;                                                        \
270                                                                                           \
271             if (rx < 0)                                                                   \
272                 rx += w;                                                                  \
273             if (gx < 0)                                                                   \
274                 gx += w;                                                                  \
275             if (bx < 0)                                                                   \
276                 bx += w;                                                                  \
277             dr[x] = sr[rx + ry * srlinesize];                                             \
278             dg[x] = sg[gx + gy * sglinesize];                                             \
279             db[x] = sb[bx + by * sblinesize];                                             \
280         }                                                                                 \
281                                                                                           \
282         dr += rlinesize;                                                                  \
283         dg += glinesize;                                                                  \
284         db += blinesize;                                                                  \
285                                                                                           \
286         if (s->nb_planes < 4)                                                             \
287             continue;                                                                     \
288         for (int x = 0; x < w; x++) {                                                     \
289             int ax = (x - ah) % w;                                                        \
290             int ay = (x - av) % h;                                                        \
291                                                                                           \
292             if (ax < 0)                                                                   \
293                 ax += w;                                                                  \
294             if (ay < 0)                                                                   \
295                 ay += h;                                                                  \
296             da[x] = sa[ax + ay * salinesize];                                             \
297         }                                                                                 \
298                                                                                           \
299         da += alinesize;                                                                  \
300     }                                                                                     \
301                                                                                           \
302     return 0;                                                                             \
303 }
304 
305 DEFINE_RGBAWRAP(8, uint8_t, 1)
306 DEFINE_RGBAWRAP(16, uint16_t, 2)
307 
filter_frame(AVFilterLink * inlink,AVFrame * in)308 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
309 {
310     AVFilterContext *ctx = inlink->dst;
311     AVFilterLink *outlink = ctx->outputs[0];
312     ChromaShiftContext *s = ctx->priv;
313     AVFrame *out;
314 
315     out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
316     if (!out) {
317         av_frame_free(&in);
318         return AVERROR(ENOMEM);
319     }
320     av_frame_copy_props(out, in);
321 
322     s->in = in;
323     if (!s->is_rgbashift) {
324         av_image_copy_plane(out->data[0],
325                             out->linesize[0],
326                             in->data[0], in->linesize[0],
327                             s->linesize[0], s->height[0]);
328     }
329     ff_filter_execute(ctx, s->filter_slice[s->edge], out, NULL,
330                       FFMIN3(s->height[1],
331                              s->height[2],
332                              ff_filter_get_nb_threads(ctx)));
333     s->in = NULL;
334     av_frame_free(&in);
335     return ff_filter_frame(outlink, out);
336 }
337 
config_input(AVFilterLink * inlink)338 static int config_input(AVFilterLink *inlink)
339 {
340     AVFilterContext *ctx = inlink->dst;
341     ChromaShiftContext *s = ctx->priv;
342     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
343 
344     s->is_rgbashift = !strcmp(ctx->filter->name, "rgbashift");
345     s->depth = desc->comp[0].depth;
346     s->nb_planes = desc->nb_components;
347     if (s->is_rgbashift) {
348         s->filter_slice[1] = s->depth > 8 ? rgbawrap_slice16 : rgbawrap_slice8;
349         s->filter_slice[0] = s->depth > 8 ? rgbasmear_slice16 : rgbasmear_slice8;
350     } else {
351         s->filter_slice[1] = s->depth > 8 ? wrap_slice16 : wrap_slice8;
352         s->filter_slice[0] = s->depth > 8 ? smear_slice16 : smear_slice8;
353     }
354     s->height[1] = s->height[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
355     s->height[0] = s->height[3] = inlink->h;
356     s->width[1] = s->width[2] = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w);
357     s->width[0] = s->width[3] = inlink->w;
358 
359     return av_image_fill_linesizes(s->linesize, inlink->format, inlink->w);
360 }
361 
362 #define OFFSET(x) offsetof(ChromaShiftContext, x)
363 #define VFR AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_RUNTIME_PARAM
364 
365 static const AVOption chromashift_options[] = {
366     { "cbh", "shift chroma-blue horizontally", OFFSET(cbh),  AV_OPT_TYPE_INT,   {.i64=0}, -255, 255, .flags = VFR },
367     { "cbv", "shift chroma-blue vertically",   OFFSET(cbv),  AV_OPT_TYPE_INT,   {.i64=0}, -255, 255, .flags = VFR },
368     { "crh", "shift chroma-red horizontally",  OFFSET(crh),  AV_OPT_TYPE_INT,   {.i64=0}, -255, 255, .flags = VFR },
369     { "crv", "shift chroma-red vertically",    OFFSET(crv),  AV_OPT_TYPE_INT,   {.i64=0}, -255, 255, .flags = VFR },
370     { "edge", "set edge operation",            OFFSET(edge), AV_OPT_TYPE_INT,   {.i64=0},    0,   1, .flags = VFR, "edge" },
371     { "smear",                              0,            0, AV_OPT_TYPE_CONST, {.i64=0},    0,   0, .flags = VFR, "edge" },
372     { "wrap",                               0,            0, AV_OPT_TYPE_CONST, {.i64=1},    0,   0, .flags = VFR, "edge" },
373     { NULL },
374 };
375 
376 static const AVFilterPad inputs[] = {
377     {
378         .name         = "default",
379         .type         = AVMEDIA_TYPE_VIDEO,
380         .filter_frame = filter_frame,
381         .config_props = config_input,
382     },
383 };
384 
385 static const AVFilterPad outputs[] = {
386     {
387         .name = "default",
388         .type = AVMEDIA_TYPE_VIDEO,
389     },
390 };
391 
392 static const enum AVPixelFormat yuv_pix_fmts[] = {
393     AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA420P,
394     AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P, AV_PIX_FMT_YUVJ422P,AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ411P,
395     AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV410P,
396     AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9,
397     AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUV440P10,
398     AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA444P10,
399     AV_PIX_FMT_YUV420P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV440P12,
400     AV_PIX_FMT_YUVA422P12, AV_PIX_FMT_YUVA444P12,
401     AV_PIX_FMT_YUV444P14, AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV420P14,
402     AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16,
403     AV_PIX_FMT_YUVA420P16, AV_PIX_FMT_YUVA422P16, AV_PIX_FMT_YUVA444P16,
404     AV_PIX_FMT_NONE
405 };
406 
407 AVFILTER_DEFINE_CLASS(chromashift);
408 
409 const AVFilter ff_vf_chromashift = {
410     .name          = "chromashift",
411     .description   = NULL_IF_CONFIG_SMALL("Shift chroma."),
412     .priv_size     = sizeof(ChromaShiftContext),
413     .priv_class    = &chromashift_class,
414     FILTER_OUTPUTS(outputs),
415     FILTER_INPUTS(inputs),
416     FILTER_PIXFMTS_ARRAY(yuv_pix_fmts),
417     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
418     .process_command = ff_filter_process_command,
419 };
420 
421 static const enum AVPixelFormat rgb_pix_fmts[] = {
422     AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRP9,
423     AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRP12,
424     AV_PIX_FMT_GBRP14, AV_PIX_FMT_GBRP16,
425     AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRAP16,
426     AV_PIX_FMT_NONE
427 };
428 
429 static const AVOption rgbashift_options[] = {
430     { "rh", "shift red horizontally",   OFFSET(rh),   AV_OPT_TYPE_INT,   {.i64=0}, -255, 255, .flags = VFR },
431     { "rv", "shift red vertically",     OFFSET(rv),   AV_OPT_TYPE_INT,   {.i64=0}, -255, 255, .flags = VFR },
432     { "gh", "shift green horizontally", OFFSET(gh),   AV_OPT_TYPE_INT,   {.i64=0}, -255, 255, .flags = VFR },
433     { "gv", "shift green vertically",   OFFSET(gv),   AV_OPT_TYPE_INT,   {.i64=0}, -255, 255, .flags = VFR },
434     { "bh", "shift blue horizontally",  OFFSET(bh),   AV_OPT_TYPE_INT,   {.i64=0}, -255, 255, .flags = VFR },
435     { "bv", "shift blue vertically",    OFFSET(bv),   AV_OPT_TYPE_INT,   {.i64=0}, -255, 255, .flags = VFR },
436     { "ah", "shift alpha horizontally", OFFSET(ah),   AV_OPT_TYPE_INT,   {.i64=0}, -255, 255, .flags = VFR },
437     { "av", "shift alpha vertically",   OFFSET(av),   AV_OPT_TYPE_INT,   {.i64=0}, -255, 255, .flags = VFR },
438     { "edge", "set edge operation",     OFFSET(edge), AV_OPT_TYPE_INT,   {.i64=0},    0,   1, .flags = VFR, "edge" },
439     { "smear",                          0,         0, AV_OPT_TYPE_CONST, {.i64=0},    0,   0, .flags = VFR, "edge" },
440     { "wrap",                           0,         0, AV_OPT_TYPE_CONST, {.i64=1},    0,   0, .flags = VFR, "edge" },
441     { NULL },
442 };
443 
444 AVFILTER_DEFINE_CLASS(rgbashift);
445 
446 const AVFilter ff_vf_rgbashift = {
447     .name          = "rgbashift",
448     .description   = NULL_IF_CONFIG_SMALL("Shift RGBA."),
449     .priv_size     = sizeof(ChromaShiftContext),
450     .priv_class    = &rgbashift_class,
451     FILTER_OUTPUTS(outputs),
452     FILTER_INPUTS(inputs),
453     FILTER_PIXFMTS_ARRAY(rgb_pix_fmts),
454     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
455     .process_command = ff_filter_process_command,
456 };
457