2 * Copyright (c) 2019 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/common.h"
22 #include "libavutil/opt.h"
23 #include "libavutil/pixdesc.h"
28 typedef struct ScrollContext
{
31 float h_speed
, v_speed
;
35 int pos_h
[4], pos_v
[4];
37 const AVPixFmtDescriptor
*desc
;
45 static const enum AVPixelFormat pix_fmts
[] = {
46 AV_PIX_FMT_YUVA444P
, AV_PIX_FMT_YUV444P
, AV_PIX_FMT_YUV440P
,
47 AV_PIX_FMT_YUVJ444P
, AV_PIX_FMT_YUVJ440P
,
48 AV_PIX_FMT_YUVA422P
, AV_PIX_FMT_YUV422P
, AV_PIX_FMT_YUVA420P
, AV_PIX_FMT_YUV420P
,
49 AV_PIX_FMT_YUVJ422P
, AV_PIX_FMT_YUVJ420P
,
50 AV_PIX_FMT_YUVJ411P
, AV_PIX_FMT_YUV411P
, AV_PIX_FMT_YUV410P
,
51 AV_PIX_FMT_YUV420P9
, AV_PIX_FMT_YUV422P9
, AV_PIX_FMT_YUV444P9
,
52 AV_PIX_FMT_YUV420P10
, AV_PIX_FMT_YUV422P10
, AV_PIX_FMT_YUV444P10
,
53 AV_PIX_FMT_YUV420P12
, AV_PIX_FMT_YUV422P12
, AV_PIX_FMT_YUV444P12
, AV_PIX_FMT_YUV440P12
,
54 AV_PIX_FMT_YUV420P14
, AV_PIX_FMT_YUV422P14
, AV_PIX_FMT_YUV444P14
,
55 AV_PIX_FMT_YUV420P16
, AV_PIX_FMT_YUV422P16
, AV_PIX_FMT_YUV444P16
,
56 AV_PIX_FMT_YUVA420P9
, AV_PIX_FMT_YUVA422P9
, AV_PIX_FMT_YUVA444P9
,
57 AV_PIX_FMT_YUVA422P12
, AV_PIX_FMT_YUVA444P12
,
58 AV_PIX_FMT_YUVA420P10
, AV_PIX_FMT_YUVA422P10
, AV_PIX_FMT_YUVA444P10
,
59 AV_PIX_FMT_YUVA420P16
, AV_PIX_FMT_YUVA422P16
, AV_PIX_FMT_YUVA444P16
,
60 AV_PIX_FMT_GBRP
, AV_PIX_FMT_GBRP9
, AV_PIX_FMT_GBRP10
,
61 AV_PIX_FMT_GBRP12
, AV_PIX_FMT_GBRP14
, AV_PIX_FMT_GBRP16
,
62 AV_PIX_FMT_GBRAP
, AV_PIX_FMT_GBRAP10
, AV_PIX_FMT_GBRAP12
, AV_PIX_FMT_GBRAP16
,
63 AV_PIX_FMT_GRAY8
, AV_PIX_FMT_GRAY9
, AV_PIX_FMT_GRAY10
, AV_PIX_FMT_GRAY12
, AV_PIX_FMT_GRAY14
, AV_PIX_FMT_GRAY16
,
67 typedef struct ThreadData
{
71 static int scroll_slice(AVFilterContext
*ctx
, void *arg
, int jobnr
,
74 ScrollContext
*s
= ctx
->priv
;
77 AVFrame
*out
= td
->out
;
79 for (int p
= 0; p
< s
->nb_planes
; p
++) {
80 const uint8_t *src
= in
->data
[p
];
81 const int h
= s
->planeheight
[p
];
82 const int w
= s
->planewidth
[p
] * s
->bytes
;
83 const int slice_start
= (h
* jobnr
) / nb_jobs
;
84 const int slice_end
= (h
* (jobnr
+1)) / nb_jobs
;
85 uint8_t *dst
= out
->data
[p
] + slice_start
* out
->linesize
[p
];
87 for (int y
= slice_start
; y
< slice_end
; y
++) {
88 int yy
= (y
+ s
->pos_v
[p
]) % h
;
89 const uint8_t *ssrc
= src
+ yy
* in
->linesize
[p
];
92 memcpy(dst
, ssrc
+ s
->pos_h
[p
], w
- s
->pos_h
[p
]);
94 memcpy(dst
+ w
- s
->pos_h
[p
], ssrc
, s
->pos_h
[p
]);
96 dst
+= out
->linesize
[p
];
103 static void scroll(AVFilterContext
*ctx
, AVFrame
*in
, AVFrame
*out
)
105 ScrollContext
*s
= ctx
->priv
;
109 s
->h_pos
= fmodf(s
->h_pos
, in
->width
);
110 s
->v_pos
= fmodf(s
->v_pos
, in
->height
);
120 s
->pos_v
[1] = s
->pos_v
[2] = AV_CEIL_RSHIFT(v_pos
, s
->desc
->log2_chroma_h
);
121 s
->pos_v
[0] = s
->pos_v
[3] = v_pos
;
122 s
->pos_h
[1] = s
->pos_h
[2] = AV_CEIL_RSHIFT(h_pos
, s
->desc
->log2_chroma_w
) * s
->bytes
;
123 s
->pos_h
[0] = s
->pos_h
[3] = h_pos
* s
->bytes
;
125 td
.in
= in
; td
.out
= out
;
126 ff_filter_execute(ctx
, scroll_slice
, &td
, NULL
,
127 FFMIN(out
->height
, ff_filter_get_nb_threads(ctx
)));
129 s
->h_pos
+= s
->h_speed
* in
->width
;
130 s
->v_pos
+= s
->v_speed
* in
->height
;
133 static int filter_frame(AVFilterLink
*inlink
, AVFrame
*in
)
135 AVFilterContext
*ctx
= inlink
->dst
;
136 AVFilterLink
*outlink
= ctx
->outputs
[0];
139 out
= ff_get_video_buffer(outlink
, outlink
->w
, outlink
->h
);
142 return AVERROR(ENOMEM
);
144 av_frame_copy_props(out
, in
);
146 scroll(ctx
, in
, out
);
149 return ff_filter_frame(outlink
, out
);
152 static int config_input(AVFilterLink
*inlink
)
154 AVFilterContext
*ctx
= inlink
->dst
;
155 ScrollContext
*s
= ctx
->priv
;
157 s
->desc
= av_pix_fmt_desc_get(inlink
->format
);
158 s
->nb_planes
= s
->desc
->nb_components
;
159 s
->bytes
= (s
->desc
->comp
[0].depth
+ 7) >> 3;
161 s
->planeheight
[1] = s
->planeheight
[2] = AV_CEIL_RSHIFT(inlink
->h
, s
->desc
->log2_chroma_h
);
162 s
->planeheight
[0] = s
->planeheight
[3] = inlink
->h
;
163 s
->planewidth
[1] = s
->planewidth
[2] = AV_CEIL_RSHIFT(inlink
->w
, s
->desc
->log2_chroma_w
);
164 s
->planewidth
[0] = s
->planewidth
[3] = inlink
->w
;
166 s
->h_pos
= (1.f
- s
->h_ipos
) * inlink
->w
;
167 s
->v_pos
= (1.f
- s
->v_ipos
) * inlink
->h
;
172 #define OFFSET(x) offsetof(ScrollContext, x)
173 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
174 #define VFT AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM
176 static const AVOption scroll_options
[] = {
177 { "horizontal", "set the horizontal scrolling speed", OFFSET(h_speed
), AV_OPT_TYPE_FLOAT
, {.dbl
=0.}, -1., 1., VFT
},
178 { "h", "set the horizontal scrolling speed", OFFSET(h_speed
), AV_OPT_TYPE_FLOAT
, {.dbl
=0.}, -1., 1., VFT
},
179 { "vertical", "set the vertical scrolling speed", OFFSET(v_speed
), AV_OPT_TYPE_FLOAT
, {.dbl
=0.}, -1., 1., VFT
},
180 { "v", "set the vertical scrolling speed", OFFSET(v_speed
), AV_OPT_TYPE_FLOAT
, {.dbl
=0.}, -1., 1., VFT
},
181 { "hpos", "set initial horizontal position", OFFSET(h_ipos
), AV_OPT_TYPE_FLOAT
, {.dbl
=0.}, 0, 1., FLAGS
},
182 { "vpos", "set initial vertical position", OFFSET(v_ipos
), AV_OPT_TYPE_FLOAT
, {.dbl
=0.}, 0, 1., FLAGS
},
186 AVFILTER_DEFINE_CLASS(scroll
);
188 static const AVFilterPad scroll_inputs
[] = {
191 .type
= AVMEDIA_TYPE_VIDEO
,
192 .config_props
= config_input
,
193 .filter_frame
= filter_frame
,
197 const FFFilter ff_vf_scroll
= {
199 .p
.description
= NULL_IF_CONFIG_SMALL("Scroll input video."),
200 .p
.priv_class
= &scroll_class
,
201 .p
.flags
= AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC
| AVFILTER_FLAG_SLICE_THREADS
,
202 .priv_size
= sizeof(ScrollContext
),
203 FILTER_INPUTS(scroll_inputs
),
204 FILTER_OUTPUTS(ff_video_default_filterpad
),
205 FILTER_PIXFMTS_ARRAY(pix_fmts
),
206 .process_command
= ff_filter_process_command
,