2 * Copyright (c) 2013 Paul B Mahol
4 * This file is part of FFmpeg.
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.
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.
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
21 #include "libavutil/pixdesc.h"
22 #include "libavutil/opt.h"
25 #include "framesync.h"
36 typedef struct DisplaceContext
{
38 int width
[4], height
[4];
46 int (*displace_slice
)(AVFilterContext
*ctx
, void *arg
, int jobnr
, int nb_jobs
);
49 #define OFFSET(x) offsetof(DisplaceContext, x)
50 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM
52 static const AVOption displace_options
[] = {
53 { "edge", "set edge mode", OFFSET(edge
), AV_OPT_TYPE_INT
, {.i64
=EDGE_SMEAR
}, 0, EDGE_NB
-1, FLAGS
, .unit
= "edge" },
54 { "blank", "", 0, AV_OPT_TYPE_CONST
, {.i64
=EDGE_BLANK
}, 0, 0, FLAGS
, .unit
= "edge" },
55 { "smear", "", 0, AV_OPT_TYPE_CONST
, {.i64
=EDGE_SMEAR
}, 0, 0, FLAGS
, .unit
= "edge" },
56 { "wrap" , "", 0, AV_OPT_TYPE_CONST
, {.i64
=EDGE_WRAP
}, 0, 0, FLAGS
, .unit
= "edge" },
57 { "mirror" , "", 0, AV_OPT_TYPE_CONST
, {.i64
=EDGE_MIRROR
}, 0, 0, FLAGS
, .unit
= "edge" },
61 AVFILTER_DEFINE_CLASS(displace
);
63 static const enum AVPixelFormat pix_fmts
[] = {
64 AV_PIX_FMT_YUVA444P
, AV_PIX_FMT_YUV444P
, AV_PIX_FMT_YUV440P
,
65 AV_PIX_FMT_YUVJ444P
, AV_PIX_FMT_YUVJ440P
,
66 AV_PIX_FMT_YUVA422P
, AV_PIX_FMT_YUV422P
, AV_PIX_FMT_YUVA420P
, AV_PIX_FMT_YUV420P
,
67 AV_PIX_FMT_YUVJ422P
, AV_PIX_FMT_YUVJ420P
,
68 AV_PIX_FMT_YUVJ411P
, AV_PIX_FMT_YUV411P
, AV_PIX_FMT_YUV410P
,
69 AV_PIX_FMT_RGB24
, AV_PIX_FMT_BGR24
,
70 AV_PIX_FMT_ARGB
, AV_PIX_FMT_ABGR
, AV_PIX_FMT_RGBA
, AV_PIX_FMT_BGRA
,
71 AV_PIX_FMT_0RGB
, AV_PIX_FMT_0BGR
, AV_PIX_FMT_RGB0
, AV_PIX_FMT_BGR0
,
72 AV_PIX_FMT_GBRP
, AV_PIX_FMT_GBRAP
,
73 AV_PIX_FMT_GRAY8
, AV_PIX_FMT_NONE
76 typedef struct ThreadData
{
77 AVFrame
*in
, *xin
, *yin
, *out
;
80 static int displace_planar(AVFilterContext
*ctx
, void *arg
, int jobnr
, int nb_jobs
)
82 DisplaceContext
*s
= ctx
->priv
;
83 const ThreadData
*td
= arg
;
84 const AVFrame
*in
= td
->in
;
85 const AVFrame
*xin
= td
->xin
;
86 const AVFrame
*yin
= td
->yin
;
87 const AVFrame
*out
= td
->out
;
89 for (int plane
= 0; plane
< s
->nb_planes
; plane
++) {
90 const int h
= s
->height
[plane
];
91 const int w
= s
->width
[plane
];
92 const int slice_start
= (h
* jobnr
) / nb_jobs
;
93 const int slice_end
= (h
* (jobnr
+1)) / nb_jobs
;
94 const int dlinesize
= out
->linesize
[plane
];
95 const int slinesize
= in
->linesize
[plane
];
96 const int xlinesize
= xin
->linesize
[plane
];
97 const int ylinesize
= yin
->linesize
[plane
];
98 const uint8_t *src
= in
->data
[plane
];
99 const uint8_t *ysrc
= yin
->data
[plane
] + slice_start
* ylinesize
;
100 const uint8_t *xsrc
= xin
->data
[plane
] + slice_start
* xlinesize
;
101 uint8_t *dst
= out
->data
[plane
] + slice_start
* dlinesize
;
102 const uint8_t blank
= s
->blank
[plane
];
104 for (int y
= slice_start
; y
< slice_end
; y
++) {
107 for (int x
= 0; x
< w
; x
++) {
108 int Y
= y
+ ysrc
[x
] - 128;
109 int X
= x
+ xsrc
[x
] - 128;
111 if (Y
< 0 || Y
>= h
|| X
< 0 || X
>= w
)
114 dst
[x
] = src
[Y
* slinesize
+ X
];
118 for (int x
= 0; x
< w
; x
++) {
119 int Y
= av_clip(y
+ ysrc
[x
] - 128, 0, h
- 1);
120 int X
= av_clip(x
+ xsrc
[x
] - 128, 0, w
- 1);
121 dst
[x
] = src
[Y
* slinesize
+ X
];
125 for (int x
= 0; x
< w
; x
++) {
126 int Y
= (y
+ ysrc
[x
] - 128) % h
;
127 int X
= (x
+ xsrc
[x
] - 128) % w
;
133 dst
[x
] = src
[Y
* slinesize
+ X
];
137 for (int x
= 0; x
< w
; x
++) {
138 int Y
= y
+ ysrc
[x
] - 128;
139 int X
= x
+ xsrc
[x
] - 128;
149 dst
[x
] = src
[Y
* slinesize
+ X
];
162 static int displace_packed(AVFilterContext
*ctx
, void *arg
, int jobnr
, int nb_jobs
)
164 DisplaceContext
*s
= ctx
->priv
;
165 const ThreadData
*td
= arg
;
166 const AVFrame
*in
= td
->in
;
167 const AVFrame
*xin
= td
->xin
;
168 const AVFrame
*yin
= td
->yin
;
169 const AVFrame
*out
= td
->out
;
170 const int step
= s
->step
;
171 const int h
= s
->height
[0];
172 const int w
= s
->width
[0];
173 const int slice_start
= (h
* jobnr
) / nb_jobs
;
174 const int slice_end
= (h
* (jobnr
+1)) / nb_jobs
;
175 const int dlinesize
= out
->linesize
[0];
176 const int slinesize
= in
->linesize
[0];
177 const int xlinesize
= xin
->linesize
[0];
178 const int ylinesize
= yin
->linesize
[0];
179 const uint8_t *src
= in
->data
[0];
180 const uint8_t *ysrc
= yin
->data
[0] + slice_start
* ylinesize
;
181 const uint8_t *xsrc
= xin
->data
[0] + slice_start
* xlinesize
;
182 uint8_t *dst
= out
->data
[0] + slice_start
* dlinesize
;
183 const uint8_t *blank
= s
->blank
;
185 for (int y
= slice_start
; y
< slice_end
; y
++) {
188 for (int x
= 0; x
< w
; x
++) {
189 for (int c
= 0; c
< s
->nb_components
; c
++) {
190 int Y
= y
+ (ysrc
[x
* step
+ c
] - 128);
191 int X
= x
+ (xsrc
[x
* step
+ c
] - 128);
193 if (Y
< 0 || Y
>= h
|| X
< 0 || X
>= w
)
194 dst
[x
* step
+ c
] = blank
[c
];
196 dst
[x
* step
+ c
] = src
[Y
* slinesize
+ X
* step
+ c
];
201 for (int x
= 0; x
< w
; x
++) {
202 for (int c
= 0; c
< s
->nb_components
; c
++) {
203 int Y
= av_clip(y
+ (ysrc
[x
* step
+ c
] - 128), 0, h
- 1);
204 int X
= av_clip(x
+ (xsrc
[x
* step
+ c
] - 128), 0, w
- 1);
206 dst
[x
* step
+ c
] = src
[Y
* slinesize
+ X
* step
+ c
];
211 for (int x
= 0; x
< w
; x
++) {
212 for (int c
= 0; c
< s
->nb_components
; c
++) {
213 int Y
= (y
+ (ysrc
[x
* step
+ c
] - 128)) % h
;
214 int X
= (x
+ (xsrc
[x
* step
+ c
] - 128)) % w
;
220 dst
[x
* step
+ c
] = src
[Y
* slinesize
+ X
* step
+ c
];
225 for (int x
= 0; x
< w
; x
++) {
226 for (int c
= 0; c
< s
->nb_components
; c
++) {
227 int Y
= y
+ ysrc
[x
* step
+ c
] - 128;
228 int X
= x
+ xsrc
[x
* step
+ c
] - 128;
238 dst
[x
* step
+ c
] = src
[Y
* slinesize
+ X
* step
+ c
];
251 static int process_frame(FFFrameSync
*fs
)
253 AVFilterContext
*ctx
= fs
->parent
;
254 DisplaceContext
*s
= fs
->opaque
;
255 AVFilterLink
*outlink
= ctx
->outputs
[0];
256 AVFrame
*out
, *in
, *xin
, *yin
;
259 if ((ret
= ff_framesync_get_frame(&s
->fs
, 0, &in
, 0)) < 0 ||
260 (ret
= ff_framesync_get_frame(&s
->fs
, 1, &xin
, 0)) < 0 ||
261 (ret
= ff_framesync_get_frame(&s
->fs
, 2, &yin
, 0)) < 0)
264 if (ctx
->is_disabled
) {
265 out
= av_frame_clone(in
);
267 return AVERROR(ENOMEM
);
271 out
= ff_get_video_buffer(outlink
, outlink
->w
, outlink
->h
);
273 return AVERROR(ENOMEM
);
274 av_frame_copy_props(out
, in
);
280 ff_filter_execute(ctx
, s
->displace_slice
, &td
, NULL
,
281 FFMIN(outlink
->h
, ff_filter_get_nb_threads(ctx
)));
283 out
->pts
= av_rescale_q(s
->fs
.pts
, s
->fs
.time_base
, outlink
->time_base
);
285 return ff_filter_frame(outlink
, out
);
288 static int config_input(AVFilterLink
*inlink
)
290 AVFilterContext
*ctx
= inlink
->dst
;
291 DisplaceContext
*s
= ctx
->priv
;
292 const AVPixFmtDescriptor
*desc
= av_pix_fmt_desc_get(inlink
->format
);
295 s
->nb_planes
= av_pix_fmt_count_planes(inlink
->format
);
296 s
->nb_components
= desc
->nb_components
;
298 if (s
->nb_planes
> 1 || s
->nb_components
== 1)
299 s
->displace_slice
= displace_planar
;
301 s
->displace_slice
= displace_packed
;
303 if (!(desc
->flags
& AV_PIX_FMT_FLAG_RGB
)) {
304 s
->blank
[1] = s
->blank
[2] = 128;
308 s
->step
= av_get_padded_bits_per_pixel(desc
) >> 3;
309 hsub
= desc
->log2_chroma_w
;
310 vsub
= desc
->log2_chroma_h
;
311 s
->height
[1] = s
->height
[2] = AV_CEIL_RSHIFT(inlink
->h
, vsub
);
312 s
->height
[0] = s
->height
[3] = inlink
->h
;
313 s
->width
[1] = s
->width
[2] = AV_CEIL_RSHIFT(inlink
->w
, hsub
);
314 s
->width
[0] = s
->width
[3] = inlink
->w
;
319 static int config_output(AVFilterLink
*outlink
)
321 FilterLink
*outl
= ff_filter_link(outlink
);
322 AVFilterContext
*ctx
= outlink
->src
;
323 DisplaceContext
*s
= ctx
->priv
;
324 AVFilterLink
*srclink
= ctx
->inputs
[0];
325 FilterLink
*sl
= ff_filter_link(srclink
);
326 AVFilterLink
*xlink
= ctx
->inputs
[1];
327 AVFilterLink
*ylink
= ctx
->inputs
[2];
331 if (srclink
->w
!= xlink
->w
||
332 srclink
->h
!= xlink
->h
||
333 srclink
->w
!= ylink
->w
||
334 srclink
->h
!= ylink
->h
) {
335 av_log(ctx
, AV_LOG_ERROR
, "First input link %s parameters "
336 "(size %dx%d) do not match the corresponding "
337 "second input link %s parameters (%dx%d) "
338 "and/or third input link %s parameters (%dx%d)\n",
339 ctx
->input_pads
[0].name
, srclink
->w
, srclink
->h
,
340 ctx
->input_pads
[1].name
, xlink
->w
, xlink
->h
,
341 ctx
->input_pads
[2].name
, ylink
->w
, ylink
->h
);
342 return AVERROR(EINVAL
);
345 outlink
->w
= srclink
->w
;
346 outlink
->h
= srclink
->h
;
347 outlink
->sample_aspect_ratio
= srclink
->sample_aspect_ratio
;
348 outl
->frame_rate
= sl
->frame_rate
;
350 ret
= ff_framesync_init(&s
->fs
, ctx
, 3);
355 in
[0].time_base
= srclink
->time_base
;
356 in
[1].time_base
= xlink
->time_base
;
357 in
[2].time_base
= ylink
->time_base
;
359 in
[0].before
= EXT_STOP
;
360 in
[0].after
= EXT_STOP
;
362 in
[1].before
= EXT_NULL
;
363 in
[1].after
= EXT_INFINITY
;
365 in
[2].before
= EXT_NULL
;
366 in
[2].after
= EXT_INFINITY
;
368 s
->fs
.on_event
= process_frame
;
370 ret
= ff_framesync_configure(&s
->fs
);
371 outlink
->time_base
= s
->fs
.time_base
;
376 static int activate(AVFilterContext
*ctx
)
378 DisplaceContext
*s
= ctx
->priv
;
379 return ff_framesync_activate(&s
->fs
);
382 static av_cold
void uninit(AVFilterContext
*ctx
)
384 DisplaceContext
*s
= ctx
->priv
;
386 ff_framesync_uninit(&s
->fs
);
389 static const AVFilterPad displace_inputs
[] = {
392 .type
= AVMEDIA_TYPE_VIDEO
,
393 .config_props
= config_input
,
397 .type
= AVMEDIA_TYPE_VIDEO
,
401 .type
= AVMEDIA_TYPE_VIDEO
,
405 static const AVFilterPad displace_outputs
[] = {
408 .type
= AVMEDIA_TYPE_VIDEO
,
409 .config_props
= config_output
,
413 const FFFilter ff_vf_displace
= {
414 .p
.name
= "displace",
415 .p
.description
= NULL_IF_CONFIG_SMALL("Displace pixels."),
416 .p
.priv_class
= &displace_class
,
417 .p
.flags
= AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL
|
418 AVFILTER_FLAG_SLICE_THREADS
,
419 .priv_size
= sizeof(DisplaceContext
),
421 .activate
= activate
,
422 FILTER_INPUTS(displace_inputs
),
423 FILTER_OUTPUTS(displace_outputs
),
424 FILTER_PIXFMTS_ARRAY(pix_fmts
),
425 .process_command
= ff_filter_process_command
,