GCC Code Coverage Report
Directory: ../../../ffmpeg/ Exec Total Coverage
File: src/libavfilter/vf_displace.c Lines: 0 207 0.0 %
Date: 2020-08-14 10:39:37 Branches: 0 112 0.0 %

Line Branch Exec Source
1
/*
2
 * Copyright (c) 2013 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 "framesync.h"
27
#include "internal.h"
28
#include "video.h"
29
30
enum EdgeMode {
31
    EDGE_BLANK,
32
    EDGE_SMEAR,
33
    EDGE_WRAP,
34
    EDGE_MIRROR,
35
    EDGE_NB
36
};
37
38
typedef struct DisplaceContext {
39
    const AVClass *class;
40
    int width[4], height[4];
41
    enum EdgeMode edge;
42
    int nb_planes;
43
    int nb_components;
44
    int step;
45
    uint8_t blank[4];
46
    FFFrameSync fs;
47
48
    void (*displace)(struct DisplaceContext *s, const AVFrame *in,
49
                     const AVFrame *xpic, const AVFrame *ypic, AVFrame *out);
50
} DisplaceContext;
51
52
#define OFFSET(x) offsetof(DisplaceContext, x)
53
#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
54
55
static const AVOption displace_options[] = {
56
    { "edge", "set edge mode", OFFSET(edge), AV_OPT_TYPE_INT, {.i64=EDGE_SMEAR}, 0, EDGE_NB-1, FLAGS, "edge" },
57
    {   "blank",   "", 0, AV_OPT_TYPE_CONST, {.i64=EDGE_BLANK},  0, 0, FLAGS, "edge" },
58
    {   "smear",   "", 0, AV_OPT_TYPE_CONST, {.i64=EDGE_SMEAR},  0, 0, FLAGS, "edge" },
59
    {   "wrap" ,   "", 0, AV_OPT_TYPE_CONST, {.i64=EDGE_WRAP},   0, 0, FLAGS, "edge" },
60
    {   "mirror" , "", 0, AV_OPT_TYPE_CONST, {.i64=EDGE_MIRROR}, 0, 0, FLAGS, "edge" },
61
    { NULL }
62
};
63
64
AVFILTER_DEFINE_CLASS(displace);
65
66
static int query_formats(AVFilterContext *ctx)
67
{
68
    static const enum AVPixelFormat pix_fmts[] = {
69
        AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV440P,
70
        AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P,
71
        AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUV420P,
72
        AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ420P,
73
        AV_PIX_FMT_YUVJ411P, AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV410P,
74
        AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24,
75
        AV_PIX_FMT_ARGB, AV_PIX_FMT_ABGR, AV_PIX_FMT_RGBA, AV_PIX_FMT_BGRA,
76
        AV_PIX_FMT_0RGB, AV_PIX_FMT_0BGR, AV_PIX_FMT_RGB0, AV_PIX_FMT_BGR0,
77
        AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRAP,
78
        AV_PIX_FMT_GRAY8, AV_PIX_FMT_NONE
79
    };
80
81
    return ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
82
}
83
84
static void displace_planar(DisplaceContext *s, const AVFrame *in,
85
                            const AVFrame *xpic, const AVFrame *ypic,
86
                            AVFrame *out)
87
{
88
    int plane, x, y;
89
90
    for (plane = 0; plane < s->nb_planes; plane++) {
91
        const int h = s->height[plane];
92
        const int w = s->width[plane];
93
        const int dlinesize = out->linesize[plane];
94
        const int slinesize = in->linesize[plane];
95
        const int xlinesize = xpic->linesize[plane];
96
        const int ylinesize = ypic->linesize[plane];
97
        const uint8_t *src = in->data[plane];
98
        const uint8_t *ysrc = ypic->data[plane];
99
        const uint8_t *xsrc = xpic->data[plane];
100
        uint8_t *dst = out->data[plane];
101
        const uint8_t blank = s->blank[plane];
102
103
        for (y = 0; y < h; y++) {
104
            switch (s->edge) {
105
            case EDGE_BLANK:
106
                for (x = 0; x < w; x++) {
107
                    int Y = y + ysrc[x] - 128;
108
                    int X = x + xsrc[x] - 128;
109
110
                    if (Y < 0 || Y >= h || X < 0 || X >= w)
111
                        dst[x] = blank;
112
                    else
113
                        dst[x] = src[Y * slinesize + X];
114
                }
115
                break;
116
            case EDGE_SMEAR:
117
                for (x = 0; x < w; x++) {
118
                    int Y = av_clip(y + ysrc[x] - 128, 0, h - 1);
119
                    int X = av_clip(x + xsrc[x] - 128, 0, w - 1);
120
                    dst[x] = src[Y * slinesize + X];
121
                }
122
                break;
123
            case EDGE_WRAP:
124
                for (x = 0; x < w; x++) {
125
                    int Y = (y + ysrc[x] - 128) % h;
126
                    int X = (x + xsrc[x] - 128) % w;
127
128
                    if (Y < 0)
129
                        Y += h;
130
                    if (X < 0)
131
                        X += w;
132
                    dst[x] = src[Y * slinesize + X];
133
                }
134
                break;
135
            case EDGE_MIRROR:
136
                for (x = 0; x < w; x++) {
137
                    int Y = y + ysrc[x] - 128;
138
                    int X = x + xsrc[x] - 128;
139
140
                    if (Y < 0)
141
                        Y = (-Y) % h;
142
                    if (X < 0)
143
                        X = (-X) % w;
144
                    if (Y >= h)
145
                        Y = h - (Y % h) - 1;
146
                    if (X >= w)
147
                        X = w - (X % w) - 1;
148
                    dst[x] = src[Y * slinesize + X];
149
                }
150
                break;
151
            }
152
153
            ysrc += ylinesize;
154
            xsrc += xlinesize;
155
            dst  += dlinesize;
156
        }
157
    }
158
}
159
160
static void displace_packed(DisplaceContext *s, const AVFrame *in,
161
                            const AVFrame *xpic, const AVFrame *ypic,
162
                            AVFrame *out)
163
{
164
    const int step = s->step;
165
    const int h = s->height[0];
166
    const int w = s->width[0];
167
    const int dlinesize = out->linesize[0];
168
    const int slinesize = in->linesize[0];
169
    const int xlinesize = xpic->linesize[0];
170
    const int ylinesize = ypic->linesize[0];
171
    const uint8_t *src = in->data[0];
172
    const uint8_t *ysrc = ypic->data[0];
173
    const uint8_t *xsrc = xpic->data[0];
174
    const uint8_t *blank = s->blank;
175
    uint8_t *dst = out->data[0];
176
    int c, x, y;
177
178
    for (y = 0; y < h; y++) {
179
        switch (s->edge) {
180
        case EDGE_BLANK:
181
            for (x = 0; x < w; x++) {
182
                for (c = 0; c < s->nb_components; c++) {
183
                    int Y = y + (ysrc[x * step + c] - 128);
184
                    int X = x + (xsrc[x * step + c] - 128);
185
186
                    if (Y < 0 || Y >= h || X < 0 || X >= w)
187
                        dst[x * step + c] = blank[c];
188
                    else
189
                        dst[x * step + c] = src[Y * slinesize + X * step + c];
190
                }
191
            }
192
            break;
193
        case EDGE_SMEAR:
194
            for (x = 0; x < w; x++) {
195
                for (c = 0; c < s->nb_components; c++) {
196
                    int Y = av_clip(y + (ysrc[x * step + c] - 128), 0, h - 1);
197
                    int X = av_clip(x + (xsrc[x * step + c] - 128), 0, w - 1);
198
199
                    dst[x * step + c] = src[Y * slinesize + X * step + c];
200
                }
201
            }
202
            break;
203
        case EDGE_WRAP:
204
            for (x = 0; x < w; x++) {
205
                for (c = 0; c < s->nb_components; c++) {
206
                    int Y = (y + (ysrc[x * step + c] - 128)) % h;
207
                    int X = (x + (xsrc[x * step + c] - 128)) % w;
208
209
                    if (Y < 0)
210
                        Y += h;
211
                    if (X < 0)
212
                        X += w;
213
                    dst[x * step + c] = src[Y * slinesize + X * step + c];
214
                }
215
            }
216
            break;
217
        case EDGE_MIRROR:
218
            for (x = 0; x < w; x++) {
219
                for (c = 0; c < s->nb_components; c++) {
220
                    int Y = y + ysrc[x * step + c] - 128;
221
                    int X = x + xsrc[x * step + c] - 128;
222
223
                    if (Y < 0)
224
                        Y = (-Y) % h;
225
                    if (X < 0)
226
                        X = (-X) % w;
227
                    if (Y >= h)
228
                        Y = h - (Y % h) - 1;
229
                    if (X >= w)
230
                        X = w - (X % w) - 1;
231
                    dst[x * step + c] = src[Y * slinesize + X * step + c];
232
                }
233
            }
234
            break;
235
        }
236
237
        ysrc += ylinesize;
238
        xsrc += xlinesize;
239
        dst  += dlinesize;
240
    }
241
}
242
243
static int process_frame(FFFrameSync *fs)
244
{
245
    AVFilterContext *ctx = fs->parent;
246
    DisplaceContext *s = fs->opaque;
247
    AVFilterLink *outlink = ctx->outputs[0];
248
    AVFrame *out, *in, *xpic, *ypic;
249
    int ret;
250
251
    if ((ret = ff_framesync_get_frame(&s->fs, 0, &in,   0)) < 0 ||
252
        (ret = ff_framesync_get_frame(&s->fs, 1, &xpic, 0)) < 0 ||
253
        (ret = ff_framesync_get_frame(&s->fs, 2, &ypic, 0)) < 0)
254
        return ret;
255
256
    if (ctx->is_disabled) {
257
        out = av_frame_clone(in);
258
        if (!out)
259
            return AVERROR(ENOMEM);
260
    } else {
261
        out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
262
        if (!out)
263
            return AVERROR(ENOMEM);
264
        av_frame_copy_props(out, in);
265
266
        s->displace(s, in, xpic, ypic, out);
267
    }
268
    out->pts = av_rescale_q(s->fs.pts, s->fs.time_base, outlink->time_base);
269
270
    return ff_filter_frame(outlink, out);
271
}
272
273
static int config_input(AVFilterLink *inlink)
274
{
275
    AVFilterContext *ctx = inlink->dst;
276
    DisplaceContext *s = ctx->priv;
277
    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
278
    int vsub, hsub;
279
280
    s->nb_planes = av_pix_fmt_count_planes(inlink->format);
281
    s->nb_components = desc->nb_components;
282
283
    if (s->nb_planes > 1 || s->nb_components == 1)
284
        s->displace = displace_planar;
285
    else
286
        s->displace = displace_packed;
287
288
    if (!(desc->flags & AV_PIX_FMT_FLAG_RGB)) {
289
        s->blank[1] = s->blank[2] = 128;
290
        s->blank[0] = 16;
291
    }
292
293
    s->step = av_get_padded_bits_per_pixel(desc) >> 3;
294
    hsub = desc->log2_chroma_w;
295
    vsub = desc->log2_chroma_h;
296
    s->height[1] = s->height[2] = AV_CEIL_RSHIFT(inlink->h, vsub);
297
    s->height[0] = s->height[3] = inlink->h;
298
    s->width[1]  = s->width[2]  = AV_CEIL_RSHIFT(inlink->w, hsub);
299
    s->width[0]  = s->width[3]  = inlink->w;
300
301
    return 0;
302
}
303
304
static int config_output(AVFilterLink *outlink)
305
{
306
    AVFilterContext *ctx = outlink->src;
307
    DisplaceContext *s = ctx->priv;
308
    AVFilterLink *srclink = ctx->inputs[0];
309
    AVFilterLink *xlink = ctx->inputs[1];
310
    AVFilterLink *ylink = ctx->inputs[2];
311
    FFFrameSyncIn *in;
312
    int ret;
313
314
    if (srclink->format != xlink->format ||
315
        srclink->format != ylink->format) {
316
        av_log(ctx, AV_LOG_ERROR, "inputs must be of same pixel format\n");
317
        return AVERROR(EINVAL);
318
    }
319
    if (srclink->w != xlink->w ||
320
        srclink->h != xlink->h ||
321
        srclink->w != ylink->w ||
322
        srclink->h != ylink->h) {
323
        av_log(ctx, AV_LOG_ERROR, "First input link %s parameters "
324
               "(size %dx%d) do not match the corresponding "
325
               "second input link %s parameters (%dx%d) "
326
               "and/or third input link %s parameters (%dx%d)\n",
327
               ctx->input_pads[0].name, srclink->w, srclink->h,
328
               ctx->input_pads[1].name, xlink->w, xlink->h,
329
               ctx->input_pads[2].name, ylink->w, ylink->h);
330
        return AVERROR(EINVAL);
331
    }
332
333
    outlink->w = srclink->w;
334
    outlink->h = srclink->h;
335
    outlink->sample_aspect_ratio = srclink->sample_aspect_ratio;
336
    outlink->frame_rate = srclink->frame_rate;
337
338
    ret = ff_framesync_init(&s->fs, ctx, 3);
339
    if (ret < 0)
340
        return ret;
341
342
    in = s->fs.in;
343
    in[0].time_base = srclink->time_base;
344
    in[1].time_base = xlink->time_base;
345
    in[2].time_base = ylink->time_base;
346
    in[0].sync   = 2;
347
    in[0].before = EXT_STOP;
348
    in[0].after  = EXT_STOP;
349
    in[1].sync   = 1;
350
    in[1].before = EXT_NULL;
351
    in[1].after  = EXT_INFINITY;
352
    in[2].sync   = 1;
353
    in[2].before = EXT_NULL;
354
    in[2].after  = EXT_INFINITY;
355
    s->fs.opaque   = s;
356
    s->fs.on_event = process_frame;
357
358
    ret = ff_framesync_configure(&s->fs);
359
    outlink->time_base = s->fs.time_base;
360
361
    return ret;
362
}
363
364
static int activate(AVFilterContext *ctx)
365
{
366
    DisplaceContext *s = ctx->priv;
367
    return ff_framesync_activate(&s->fs);
368
}
369
370
static av_cold void uninit(AVFilterContext *ctx)
371
{
372
    DisplaceContext *s = ctx->priv;
373
374
    ff_framesync_uninit(&s->fs);
375
}
376
377
static const AVFilterPad displace_inputs[] = {
378
    {
379
        .name         = "source",
380
        .type         = AVMEDIA_TYPE_VIDEO,
381
        .config_props = config_input,
382
    },
383
    {
384
        .name         = "xmap",
385
        .type         = AVMEDIA_TYPE_VIDEO,
386
    },
387
    {
388
        .name         = "ymap",
389
        .type         = AVMEDIA_TYPE_VIDEO,
390
    },
391
    { NULL }
392
};
393
394
static const AVFilterPad displace_outputs[] = {
395
    {
396
        .name          = "default",
397
        .type          = AVMEDIA_TYPE_VIDEO,
398
        .config_props  = config_output,
399
    },
400
    { NULL }
401
};
402
403
AVFilter ff_vf_displace = {
404
    .name          = "displace",
405
    .description   = NULL_IF_CONFIG_SMALL("Displace pixels."),
406
    .priv_size     = sizeof(DisplaceContext),
407
    .uninit        = uninit,
408
    .query_formats = query_formats,
409
    .activate      = activate,
410
    .inputs        = displace_inputs,
411
    .outputs       = displace_outputs,
412
    .priv_class    = &displace_class,
413
    .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL,
414
};