]> git.sesse.net Git - ffmpeg/blob - libavfilter/vf_fillborders.c
avfilter: Constify all AVFilters
[ffmpeg] / libavfilter / vf_fillborders.c
1 /*
2  * Copyright (c) 2017 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/avassert.h"
22 #include "libavutil/colorspace.h"
23 #include "libavutil/common.h"
24 #include "libavutil/opt.h"
25 #include "libavutil/pixdesc.h"
26 #include "avfilter.h"
27 #include "drawutils.h"
28 #include "formats.h"
29 #include "internal.h"
30 #include "video.h"
31
32 enum { Y, U, V, A };
33 enum { R, G, B };
34
35 enum FillMode { FM_SMEAR, FM_MIRROR, FM_FIXED, FM_REFLECT, FM_WRAP, FM_FADE, FM_NB_MODES };
36
37 typedef struct Borders {
38     int left, right, top, bottom;
39 } Borders;
40
41 typedef struct FillBordersContext {
42     const AVClass *class;
43     int left, right, top, bottom;
44     int mode;
45
46     int nb_planes;
47     int depth;
48     Borders borders[4];
49     int planewidth[4];
50     int planeheight[4];
51     uint8_t fill[4];
52     uint8_t yuv_color[4];
53     uint8_t rgba_color[4];
54
55     void (*fillborders)(struct FillBordersContext *s, AVFrame *frame);
56 } FillBordersContext;
57
58 static int query_formats(AVFilterContext *ctx)
59 {
60     static const enum AVPixelFormat pix_fmts[] = {
61         AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV440P,
62         AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P,
63         AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUV420P,
64         AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ420P,
65         AV_PIX_FMT_YUVJ411P, AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV410P,
66         AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9,
67         AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10,
68         AV_PIX_FMT_YUV420P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV440P12,
69         AV_PIX_FMT_YUV420P14, AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV444P14,
70         AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16,
71         AV_PIX_FMT_YUVA420P9, AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA444P9,
72         AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA444P10,
73         AV_PIX_FMT_YUVA422P12, AV_PIX_FMT_YUVA444P12,
74         AV_PIX_FMT_YUVA420P16, AV_PIX_FMT_YUVA422P16, AV_PIX_FMT_YUVA444P16,
75         AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP10,
76         AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRP14, AV_PIX_FMT_GBRP16,
77         AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRAP16,
78         AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY9, AV_PIX_FMT_GRAY10, AV_PIX_FMT_GRAY12, AV_PIX_FMT_GRAY14, AV_PIX_FMT_GRAY16,
79         AV_PIX_FMT_NONE
80     };
81     AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts);
82     if (!fmts_list)
83         return AVERROR(ENOMEM);
84     return ff_set_common_formats(ctx, fmts_list);
85 }
86
87 static void smear_borders8(FillBordersContext *s, AVFrame *frame)
88 {
89     int p, y;
90
91     for (p = 0; p < s->nb_planes; p++) {
92         uint8_t *ptr = frame->data[p];
93         int linesize = frame->linesize[p];
94
95         for (y = s->borders[p].top; y < s->planeheight[p] - s->borders[p].bottom; y++) {
96             memset(ptr + y * linesize,
97                    *(ptr + y * linesize + s->borders[p].left),
98                    s->borders[p].left);
99             memset(ptr + y * linesize + s->planewidth[p] - s->borders[p].right,
100                    *(ptr + y * linesize + s->planewidth[p] - s->borders[p].right - 1),
101                    s->borders[p].right);
102         }
103
104         for (y = 0; y < s->borders[p].top; y++) {
105             memcpy(ptr + y * linesize,
106                    ptr + s->borders[p].top * linesize, s->planewidth[p]);
107         }
108
109         for (y = s->planeheight[p] - s->borders[p].bottom; y < s->planeheight[p]; y++) {
110             memcpy(ptr + y * linesize,
111                    ptr + (s->planeheight[p] - s->borders[p].bottom - 1) * linesize,
112                    s->planewidth[p]);
113         }
114     }
115 }
116
117 static void smear_borders16(FillBordersContext *s, AVFrame *frame)
118 {
119     int p, y, x;
120
121     for (p = 0; p < s->nb_planes; p++) {
122         uint16_t *ptr = (uint16_t *)frame->data[p];
123         int linesize = frame->linesize[p] / 2;
124
125         for (y = s->borders[p].top; y < s->planeheight[p] - s->borders[p].bottom; y++) {
126             for (x = 0; x < s->borders[p].left; x++) {
127                 ptr[y * linesize + x] =  *(ptr + y * linesize + s->borders[p].left);
128             }
129
130             for (x = 0; x < s->borders[p].right; x++) {
131                 ptr[y * linesize + s->planewidth[p] - s->borders[p].right + x] =
132                    *(ptr + y * linesize + s->planewidth[p] - s->borders[p].right - 1);
133             }
134         }
135
136         for (y = 0; y < s->borders[p].top; y++) {
137             memcpy(ptr + y * linesize,
138                    ptr + s->borders[p].top * linesize, s->planewidth[p] * 2);
139         }
140
141         for (y = s->planeheight[p] - s->borders[p].bottom; y < s->planeheight[p]; y++) {
142             memcpy(ptr + y * linesize,
143                    ptr + (s->planeheight[p] - s->borders[p].bottom - 1) * linesize,
144                    s->planewidth[p] * 2);
145         }
146     }
147 }
148
149 static void mirror_borders8(FillBordersContext *s, AVFrame *frame)
150 {
151     int p, y, x;
152
153     for (p = 0; p < s->nb_planes; p++) {
154         uint8_t *ptr = frame->data[p];
155         int linesize = frame->linesize[p];
156
157         for (y = s->borders[p].top; y < s->planeheight[p] - s->borders[p].bottom; y++) {
158             for (x = 0; x < s->borders[p].left; x++) {
159                 ptr[y * linesize + x] = ptr[y * linesize + s->borders[p].left * 2 - 1 - x];
160             }
161
162             for (x = 0; x < s->borders[p].right; x++) {
163                 ptr[y * linesize + s->planewidth[p] - s->borders[p].right + x] =
164                     ptr[y * linesize + s->planewidth[p] - s->borders[p].right - 1 - x];
165             }
166         }
167
168         for (y = 0; y < s->borders[p].top; y++) {
169             memcpy(ptr + y * linesize,
170                    ptr + (s->borders[p].top * 2 - 1 - y) * linesize,
171                    s->planewidth[p]);
172         }
173
174         for (y = 0; y < s->borders[p].bottom; y++) {
175             memcpy(ptr + (s->planeheight[p] - s->borders[p].bottom + y) * linesize,
176                    ptr + (s->planeheight[p] - s->borders[p].bottom - 1 - y) * linesize,
177                    s->planewidth[p]);
178         }
179     }
180 }
181
182 static void mirror_borders16(FillBordersContext *s, AVFrame *frame)
183 {
184     int p, y, x;
185
186     for (p = 0; p < s->nb_planes; p++) {
187         uint16_t *ptr = (uint16_t *)frame->data[p];
188         int linesize = frame->linesize[p] / 2;
189
190         for (y = s->borders[p].top; y < s->planeheight[p] - s->borders[p].bottom; y++) {
191             for (x = 0; x < s->borders[p].left; x++) {
192                 ptr[y * linesize + x] = ptr[y * linesize + s->borders[p].left * 2 - 1 - x];
193             }
194
195             for (x = 0; x < s->borders[p].right; x++) {
196                 ptr[y * linesize + s->planewidth[p] - s->borders[p].right + x] =
197                     ptr[y * linesize + s->planewidth[p] - s->borders[p].right - 1 - x];
198             }
199         }
200
201         for (y = 0; y < s->borders[p].top; y++) {
202             memcpy(ptr + y * linesize,
203                    ptr + (s->borders[p].top * 2 - 1 - y) * linesize,
204                    s->planewidth[p] * 2);
205         }
206
207         for (y = 0; y < s->borders[p].bottom; y++) {
208             memcpy(ptr + (s->planeheight[p] - s->borders[p].bottom + y) * linesize,
209                    ptr + (s->planeheight[p] - s->borders[p].bottom - 1 - y) * linesize,
210                    s->planewidth[p] * 2);
211         }
212     }
213 }
214
215 static void fixed_borders8(FillBordersContext *s, AVFrame *frame)
216 {
217     int p, y;
218
219     for (p = 0; p < s->nb_planes; p++) {
220         uint8_t *ptr = frame->data[p];
221         uint8_t fill = s->fill[p];
222         int linesize = frame->linesize[p];
223
224         for (y = s->borders[p].top; y < s->planeheight[p] - s->borders[p].bottom; y++) {
225             memset(ptr + y * linesize, fill, s->borders[p].left);
226             memset(ptr + y * linesize + s->planewidth[p] - s->borders[p].right, fill,
227                    s->borders[p].right);
228         }
229
230         for (y = 0; y < s->borders[p].top; y++) {
231             memset(ptr + y * linesize, fill, s->planewidth[p]);
232         }
233
234         for (y = s->planeheight[p] - s->borders[p].bottom; y < s->planeheight[p]; y++) {
235             memset(ptr + y * linesize, fill, s->planewidth[p]);
236         }
237     }
238 }
239
240 static void fixed_borders16(FillBordersContext *s, AVFrame *frame)
241 {
242     int p, y, x;
243
244     for (p = 0; p < s->nb_planes; p++) {
245         uint16_t *ptr = (uint16_t *)frame->data[p];
246         uint16_t fill = s->fill[p] << (s->depth - 8);
247         int linesize = frame->linesize[p] / 2;
248
249         for (y = s->borders[p].top; y < s->planeheight[p] - s->borders[p].bottom; y++) {
250             for (x = 0; x < s->borders[p].left; x++) {
251                 ptr[y * linesize + x] = fill;
252             }
253
254             for (x = 0; x < s->borders[p].right; x++) {
255                 ptr[y * linesize + s->planewidth[p] - s->borders[p].right + x] = fill;
256             }
257         }
258
259         for (y = 0; y < s->borders[p].top; y++) {
260             for (x = 0; x < s->planewidth[p]; x++) {
261                 ptr[y * linesize + x] = fill;
262             }
263         }
264
265         for (y = s->planeheight[p] - s->borders[p].bottom; y < s->planeheight[p]; y++) {
266             for (x = 0; x < s->planewidth[p]; x++) {
267                 ptr[y * linesize + x] = fill;
268             }
269         }
270     }
271 }
272
273 static void reflect_borders8(FillBordersContext *s, AVFrame *frame)
274 {
275     int p, y, x;
276
277     for (p = 0; p < s->nb_planes; p++) {
278         uint8_t *ptr = frame->data[p];
279         int linesize = frame->linesize[p];
280
281         for (y = s->borders[p].top; y < s->planeheight[p] - s->borders[p].bottom; y++) {
282             for (x = 0; x < s->borders[p].left; x++) {
283                 ptr[y * linesize + x] = ptr[y * linesize + s->borders[p].left * 2 - x];
284             }
285
286             for (x = 0; x < s->borders[p].right; x++) {
287                 ptr[y * linesize + s->planewidth[p] - s->borders[p].right + x] =
288                     ptr[y * linesize + s->planewidth[p] - s->borders[p].right - 2 - x];
289             }
290         }
291
292         for (y = 0; y < s->borders[p].top; y++) {
293             memcpy(ptr + y * linesize,
294                    ptr + (s->borders[p].top * 2 - y) * linesize,
295                    s->planewidth[p]);
296         }
297
298         for (y = 0; y < s->borders[p].bottom; y++) {
299             memcpy(ptr + (s->planeheight[p] - s->borders[p].bottom + y) * linesize,
300                    ptr + (s->planeheight[p] - s->borders[p].bottom - 2 - y) * linesize,
301                    s->planewidth[p]);
302         }
303     }
304 }
305
306 static void reflect_borders16(FillBordersContext *s, AVFrame *frame)
307 {
308     int p, y, x;
309
310     for (p = 0; p < s->nb_planes; p++) {
311         uint16_t *ptr = (uint16_t *)frame->data[p];
312         int linesize = frame->linesize[p] / 2;
313
314         for (y = s->borders[p].top; y < s->planeheight[p] - s->borders[p].bottom; y++) {
315             for (x = 0; x < s->borders[p].left; x++) {
316                 ptr[y * linesize + x] = ptr[y * linesize + s->borders[p].left * 2 - x];
317             }
318
319             for (x = 0; x < s->borders[p].right; x++) {
320                 ptr[y * linesize + s->planewidth[p] - s->borders[p].right + x] =
321                     ptr[y * linesize + s->planewidth[p] - s->borders[p].right - 2 - x];
322             }
323         }
324
325         for (y = 0; y < s->borders[p].top; y++) {
326             memcpy(ptr + y * linesize,
327                    ptr + (s->borders[p].top * 2 - y) * linesize,
328                    s->planewidth[p] * 2);
329         }
330
331         for (y = 0; y < s->borders[p].bottom; y++) {
332             memcpy(ptr + (s->planeheight[p] - s->borders[p].bottom + y) * linesize,
333                    ptr + (s->planeheight[p] - s->borders[p].bottom - 2 - y) * linesize,
334                    s->planewidth[p] * 2);
335         }
336     }
337 }
338
339 static void wrap_borders8(FillBordersContext *s, AVFrame *frame)
340 {
341     int p, y, x;
342
343     for (p = 0; p < s->nb_planes; p++) {
344         uint8_t *ptr = frame->data[p];
345         int linesize = frame->linesize[p];
346
347         for (y = s->borders[p].top; y < s->planeheight[p] - s->borders[p].bottom; y++) {
348             for (x = 0; x < s->borders[p].left; x++) {
349                 ptr[y * linesize + x] = ptr[y * linesize + s->planewidth[p] - s->borders[p].right - s->borders[p].left + x];
350             }
351
352             for (x = 0; x < s->borders[p].right; x++) {
353                 ptr[y * linesize + s->planewidth[p] - s->borders[p].right + x] =
354                     ptr[y * linesize + s->borders[p].left + x];
355             }
356         }
357
358         for (y = 0; y < s->borders[p].top; y++) {
359             memcpy(ptr + y * linesize,
360                    ptr + (s->planeheight[p] - s->borders[p].bottom - s->borders[p].top + y) * linesize,
361                    s->planewidth[p]);
362         }
363
364         for (y = 0; y < s->borders[p].bottom; y++) {
365             memcpy(ptr + (s->planeheight[p] - s->borders[p].bottom + y) * linesize,
366                    ptr + (s->borders[p].top + y) * linesize,
367                    s->planewidth[p]);
368         }
369     }
370 }
371
372 static void wrap_borders16(FillBordersContext *s, AVFrame *frame)
373 {
374     int p, y, x;
375
376     for (p = 0; p < s->nb_planes; p++) {
377         uint16_t *ptr = (uint16_t *)frame->data[p];
378         int linesize = frame->linesize[p] / 2;
379
380         for (y = s->borders[p].top; y < s->planeheight[p] - s->borders[p].bottom; y++) {
381             for (x = 0; x < s->borders[p].left; x++) {
382                 ptr[y * linesize + x] = ptr[y * linesize + s->planewidth[p] - s->borders[p].right - s->borders[p].left + x];
383             }
384
385             for (x = 0; x < s->borders[p].right; x++) {
386                 ptr[y * linesize + s->planewidth[p] - s->borders[p].right + x] =
387                     ptr[y * linesize + s->borders[p].left + x];
388             }
389         }
390
391         for (y = 0; y < s->borders[p].top; y++) {
392             memcpy(ptr + y * linesize,
393                    ptr + (s->planeheight[p] - s->borders[p].bottom - s->borders[p].top + y) * linesize,
394                    s->planewidth[p] * 2);
395         }
396
397         for (y = 0; y < s->borders[p].bottom; y++) {
398             memcpy(ptr + (s->planeheight[p] - s->borders[p].bottom + y) * linesize,
399                    ptr + (s->borders[p].top + y) * linesize,
400                    s->planewidth[p] * 2);
401         }
402     }
403 }
404
405 static int lerp8(int fill, int src, int pos, int size)
406 {
407     return av_clip_uint8(((fill * 256 * pos / size) + (src * 256 * (size - pos) / size)) >> 8);
408 }
409
410 static int lerp16(int fill, int src, int pos, int size, int depth)
411 {
412     return av_clip_uintp2_c(((fill * (1LL << depth) * pos / size) + (src * (1LL << depth) * (size - pos) / size)) >> depth, depth);
413 }
414
415 static void fade_borders8(FillBordersContext *s, AVFrame *frame)
416 {
417     int p, y, x;
418
419     for (p = 0; p < s->nb_planes; p++) {
420         uint8_t *ptr = frame->data[p];
421         const uint8_t fill = s->fill[p];
422         const int linesize = frame->linesize[p];
423         const int start_left = s->borders[p].left;
424         const int start_right = s->planewidth[p] - s->borders[p].right;
425         const int start_top = s->borders[p].top;
426         const int start_bottom = s->planeheight[p] - s->borders[p].bottom;
427
428         for (y = 0; y < start_top; y++) {
429             for (x = 0; x < s->planewidth[p]; x++) {
430                 int src = ptr[y * linesize + x];
431                 ptr[y * linesize + x] = lerp8(fill, src, start_top - y, start_top);
432             }
433         }
434
435         for (y = start_bottom; y < s->planeheight[p]; y++) {
436             for (x = 0; x < s->planewidth[p]; x++) {
437                 int src = ptr[y * linesize + x];
438                 ptr[y * linesize + x] = lerp8(fill, src, y - start_bottom, s->borders[p].bottom);
439             }
440         }
441
442         for (y = 0; y < s->planeheight[p]; y++) {
443             for (x = 0; x < start_left; x++) {
444                 int src = ptr[y * linesize + x];
445                 ptr[y * linesize + x] = lerp8(fill, src, start_left - x, start_left);
446             }
447
448             for (x = 0; x < s->borders[p].right; x++) {
449                 int src = ptr[y * linesize + start_right + x];
450                 ptr[y * linesize + start_right + x] = lerp8(fill, src, x, s->borders[p].right);
451             }
452         }
453     }
454 }
455
456 static void fade_borders16(FillBordersContext *s, AVFrame *frame)
457 {
458     const int depth = s->depth;
459     int p, y, x;
460
461     for (p = 0; p < s->nb_planes; p++) {
462         uint16_t *ptr = (uint16_t *)frame->data[p];
463         const uint16_t fill = s->fill[p] << (depth - 8);
464         const int linesize = frame->linesize[p] / 2;
465         const int start_left = s->borders[p].left;
466         const int start_right = s->planewidth[p] - s->borders[p].right;
467         const int start_top = s->borders[p].top;
468         const int start_bottom = s->planeheight[p] - s->borders[p].bottom;
469
470         for (y = 0; y < start_top; y++) {
471             for (x = 0; x < s->planewidth[p]; x++) {
472                 int src = ptr[y * linesize + x];
473                 ptr[y * linesize + x] = lerp16(fill, src, start_top - y, start_top, depth);
474             }
475         }
476
477         for (y = start_bottom; y < s->planeheight[p]; y++) {
478             for (x = 0; x < s->planewidth[p]; x++) {
479                 int src = ptr[y * linesize + x];
480                 ptr[y * linesize + x] = lerp16(fill, src, y - start_bottom, s->borders[p].bottom, depth);
481             }
482         }
483
484         for (y = 0; y < s->planeheight[p]; y++) {
485             for (x = 0; x < start_left; x++) {
486                 int src = ptr[y * linesize + x];
487                 ptr[y * linesize + x] = lerp16(fill, src, start_left - x, start_left, depth);
488             }
489
490             for (x = 0; x < s->borders[p].right; x++) {
491                 int src = ptr[y * linesize + start_right + x];
492                 ptr[y * linesize + start_right + x] = lerp16(fill, src, x, s->borders[p].right, depth);
493             }
494         }
495     }
496 }
497
498 static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
499 {
500     FillBordersContext *s = inlink->dst->priv;
501
502     s->fillborders(s, frame);
503
504     return ff_filter_frame(inlink->dst->outputs[0], frame);
505 }
506
507 static int config_input(AVFilterLink *inlink)
508 {
509     AVFilterContext *ctx = inlink->dst;
510     FillBordersContext *s = ctx->priv;
511     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
512
513     s->nb_planes = desc->nb_components;
514     s->depth = desc->comp[0].depth;
515
516     s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
517     s->planeheight[0] = s->planeheight[3] = inlink->h;
518     s->planewidth[1]  = s->planewidth[2]  = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w);
519     s->planewidth[0]  = s->planewidth[3]  = inlink->w;
520
521     if (inlink->w < s->left + s->right ||
522         inlink->w <= s->left ||
523         inlink->w <= s->right ||
524         inlink->h < s->top + s->bottom ||
525         inlink->h <= s->top ||
526         inlink->h <= s->bottom ||
527         inlink->w < s->left * 2 ||
528         inlink->w < s->right * 2 ||
529         inlink->h < s->top * 2 ||
530         inlink->h < s->bottom * 2) {
531         av_log(ctx, AV_LOG_ERROR, "Borders are bigger than input frame size.\n");
532         return AVERROR(EINVAL);
533     }
534
535     s->borders[0].left   = s->borders[3].left = s->left;
536     s->borders[0].right  = s->borders[3].right = s->right;
537     s->borders[0].top    = s->borders[3].top = s->top;
538     s->borders[0].bottom = s->borders[3].bottom = s->bottom;
539
540     s->borders[1].left   = s->left >> desc->log2_chroma_w;
541     s->borders[1].right  = s->right >> desc->log2_chroma_w;
542     s->borders[1].top    = s->top >> desc->log2_chroma_h;
543     s->borders[1].bottom = s->bottom >> desc->log2_chroma_h;
544
545     s->borders[2].left   = s->left >> desc->log2_chroma_w;
546     s->borders[2].right  = s->right >> desc->log2_chroma_w;
547     s->borders[2].top    = s->top >> desc->log2_chroma_h;
548     s->borders[2].bottom = s->bottom >> desc->log2_chroma_h;
549
550     switch (s->mode) {
551     case FM_SMEAR:  s->fillborders = s->depth <= 8 ? smear_borders8  : smear_borders16;  break;
552     case FM_MIRROR: s->fillborders = s->depth <= 8 ? mirror_borders8 : mirror_borders16; break;
553     case FM_FIXED:  s->fillborders = s->depth <= 8 ? fixed_borders8  : fixed_borders16;  break;
554     case FM_REFLECT:s->fillborders = s->depth <= 8 ? reflect_borders8: reflect_borders16;break;
555     case FM_WRAP:   s->fillborders = s->depth <= 8 ? wrap_borders8   : wrap_borders16;   break;
556     case FM_FADE:   s->fillborders = s->depth <= 8 ? fade_borders8   : fade_borders16;   break;
557     default: av_assert0(0);
558     }
559
560     s->yuv_color[Y] = RGB_TO_Y_CCIR(s->rgba_color[R], s->rgba_color[G], s->rgba_color[B]);
561     s->yuv_color[U] = RGB_TO_U_CCIR(s->rgba_color[R], s->rgba_color[G], s->rgba_color[B], 0);
562     s->yuv_color[V] = RGB_TO_V_CCIR(s->rgba_color[R], s->rgba_color[G], s->rgba_color[B], 0);
563     s->yuv_color[A] = s->rgba_color[A];
564
565     if (desc->flags & AV_PIX_FMT_FLAG_RGB) {
566         uint8_t rgba_map[4];
567         int i;
568
569         ff_fill_rgba_map(rgba_map, inlink->format);
570         for (i = 0; i < 4; i++)
571             s->fill[rgba_map[i]] = s->rgba_color[i];
572     } else {
573         memcpy(s->fill, s->yuv_color, sizeof(s->yuv_color));
574     }
575
576     return 0;
577 }
578
579 static int process_command(AVFilterContext *ctx, const char *cmd, const char *args,
580                            char *res, int res_len, int flags)
581 {
582     int ret;
583
584     ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags);
585     if (ret < 0)
586         return ret;
587
588     return config_input(ctx->inputs[0]);
589 }
590
591 #define OFFSET(x) offsetof(FillBordersContext, x)
592 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM
593
594 static const AVOption fillborders_options[] = {
595     { "left",   "set the left fill border",   OFFSET(left),   AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX,    FLAGS },
596     { "right",  "set the right fill border",  OFFSET(right),  AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX,    FLAGS },
597     { "top",    "set the top fill border",    OFFSET(top),    AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX,    FLAGS },
598     { "bottom", "set the bottom fill border", OFFSET(bottom), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX,    FLAGS },
599     { "mode",   "set the fill borders mode",  OFFSET(mode),   AV_OPT_TYPE_INT, {.i64=FM_SMEAR}, 0, FM_NB_MODES-1, FLAGS, "mode" },
600         { "smear",  NULL, 0, AV_OPT_TYPE_CONST, {.i64=FM_SMEAR},  0, 0, FLAGS, "mode" },
601         { "mirror", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FM_MIRROR}, 0, 0, FLAGS, "mode" },
602         { "fixed",  NULL, 0, AV_OPT_TYPE_CONST, {.i64=FM_FIXED},  0, 0, FLAGS, "mode" },
603         { "reflect",NULL, 0, AV_OPT_TYPE_CONST, {.i64=FM_REFLECT},0, 0, FLAGS, "mode" },
604         { "wrap",   NULL, 0, AV_OPT_TYPE_CONST, {.i64=FM_WRAP},   0, 0, FLAGS, "mode" },
605         { "fade",   NULL, 0, AV_OPT_TYPE_CONST, {.i64=FM_FADE},   0, 0, FLAGS, "mode" },
606     { "color",  "set the color for the fixed/fade mode", OFFSET(rgba_color), AV_OPT_TYPE_COLOR, {.str = "black"}, .flags = FLAGS },
607     { NULL }
608 };
609
610 AVFILTER_DEFINE_CLASS(fillborders);
611
612 static const AVFilterPad fillborders_inputs[] = {
613     {
614         .name           = "default",
615         .type           = AVMEDIA_TYPE_VIDEO,
616         .config_props   = config_input,
617         .filter_frame   = filter_frame,
618         .needs_writable = 1,
619     },
620     { NULL }
621 };
622
623 static const AVFilterPad fillborders_outputs[] = {
624     {
625         .name = "default",
626         .type = AVMEDIA_TYPE_VIDEO,
627     },
628     { NULL }
629 };
630
631 const AVFilter ff_vf_fillborders = {
632     .name          = "fillborders",
633     .description   = NULL_IF_CONFIG_SMALL("Fill borders of the input video."),
634     .priv_size     = sizeof(FillBordersContext),
635     .priv_class    = &fillborders_class,
636     .query_formats = query_formats,
637     .inputs        = fillborders_inputs,
638     .outputs       = fillborders_outputs,
639     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
640     .process_command = process_command,
641 };