+ ScrollContext *s = ctx->priv;
+ ThreadData *td = arg;
+ AVFrame *in = td->in;
+ AVFrame *out = td->out;
+
+ for (int p = 0; p < s->nb_planes; p++) {
+ const uint8_t *src = in->data[p];
+ const int h = s->planeheight[p];
+ const int w = s->planewidth[p] * s->bytes;
+ const int slice_start = (h * jobnr) / nb_jobs;
+ const int slice_end = (h * (jobnr+1)) / nb_jobs;
+ uint8_t *dst = out->data[p] + slice_start * out->linesize[p];
+
+ for (int y = slice_start; y < slice_end; y++) {
+ int yy = (y + s->pos_v[p]) % h;
+ const uint8_t *ssrc = src + yy * in->linesize[p];
+
+ if (s->pos_h[p] < w)
+ memcpy(dst, ssrc + s->pos_h[p], w - s->pos_h[p]);
+ if (s->pos_h[p] > 0)
+ memcpy(dst + w - s->pos_h[p], ssrc, s->pos_h[p]);
+
+ dst += out->linesize[p];
+ }
+ }
+
+ return 0;
+}
+
+static void scroll(AVFilterContext *ctx, AVFrame *in, AVFrame *out)
+{
+ ScrollContext *s = ctx->priv;
+ ThreadData td;