]> git.sesse.net Git - ffmpeg/blob - libavfilter/vf_remap.c
avutil/opt: check return value of av_bprint_finalize()
[ffmpeg] / libavfilter / vf_remap.c
1 /*
2  * Copyright (c) 2016 Floris Sluiter
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 /**
22  * @file
23  * Pixel remap filter
24  * This filter copies pixel by pixel a source frame to a target frame.
25  * It remaps the pixels to a new x,y destination based on two files ymap/xmap.
26  * Map files are passed as a parameter and are in PGM format (P2 or P5),
27  * where the values are y(rows)/x(cols) coordinates of the source_frame.
28  * The *target* frame dimension is based on mapfile dimensions: specified in the
29  * header of the mapfile and reflected in the number of datavalues.
30  * Dimensions of ymap and xmap must be equal. Datavalues must be positive or zero.
31  * Any datavalue in the ymap or xmap which value is higher
32  * then the *source* frame height or width is silently ignored, leaving a
33  * blank/chromakey pixel. This can safely be used as a feature to create overlays.
34  *
35  * Algorithm digest:
36  * Target_frame[y][x] = Source_frame[ ymap[y][x] ][ [xmap[y][x] ];
37  */
38
39 #include "libavutil/colorspace.h"
40 #include "libavutil/imgutils.h"
41 #include "libavutil/pixdesc.h"
42 #include "libavutil/opt.h"
43 #include "avfilter.h"
44 #include "drawutils.h"
45 #include "formats.h"
46 #include "framesync.h"
47 #include "internal.h"
48 #include "video.h"
49
50 typedef struct RemapContext {
51     const AVClass *class;
52     int format;
53
54     int nb_planes;
55     int nb_components;
56     int step;
57     uint8_t fill_rgba[4];
58     int fill_color[4];
59
60     FFFrameSync fs;
61
62     int (*remap_slice)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs);
63 } RemapContext;
64
65 #define OFFSET(x) offsetof(RemapContext, x)
66 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
67
68 static const AVOption remap_options[] = {
69     { "format", "set output format", OFFSET(format), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, "format" },
70         { "color",  "", 0, AV_OPT_TYPE_CONST, {.i64=0},   .flags = FLAGS, .unit = "format" },
71         { "gray",   "", 0, AV_OPT_TYPE_CONST, {.i64=1},   .flags = FLAGS, .unit = "format" },
72     { "fill", "set the color of the unmapped pixels", OFFSET(fill_rgba), AV_OPT_TYPE_COLOR, {.str="black"}, .flags = FLAGS },
73     { NULL }
74 };
75
76 AVFILTER_DEFINE_CLASS(remap);
77
78 typedef struct ThreadData {
79     AVFrame *in, *xin, *yin, *out;
80     int nb_planes;
81     int nb_components;
82     int step;
83 } ThreadData;
84
85 static int query_formats(AVFilterContext *ctx)
86 {
87     RemapContext *s = ctx->priv;
88     static const enum AVPixelFormat pix_fmts[] = {
89         AV_PIX_FMT_YUVA444P,
90         AV_PIX_FMT_YUV444P,
91         AV_PIX_FMT_YUVJ444P,
92         AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24,
93         AV_PIX_FMT_ARGB, AV_PIX_FMT_ABGR, AV_PIX_FMT_RGBA, AV_PIX_FMT_BGRA,
94         AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRAP,
95         AV_PIX_FMT_YUV444P9, AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUV444P12,
96         AV_PIX_FMT_YUV444P14, AV_PIX_FMT_YUV444P16,
97         AV_PIX_FMT_YUVA444P9, AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_YUVA444P12, AV_PIX_FMT_YUVA444P16,
98         AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRP12,
99         AV_PIX_FMT_GBRP14, AV_PIX_FMT_GBRP16,
100         AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRAP16,
101         AV_PIX_FMT_RGB48, AV_PIX_FMT_BGR48,
102         AV_PIX_FMT_RGBA64, AV_PIX_FMT_BGRA64,
103         AV_PIX_FMT_NONE
104     };
105     static const enum AVPixelFormat gray_pix_fmts[] = {
106         AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY9,
107         AV_PIX_FMT_GRAY10, AV_PIX_FMT_GRAY12,
108         AV_PIX_FMT_GRAY14, AV_PIX_FMT_GRAY16,
109         AV_PIX_FMT_NONE
110     };
111     static const enum AVPixelFormat map_fmts[] = {
112         AV_PIX_FMT_GRAY16,
113         AV_PIX_FMT_NONE
114     };
115     AVFilterFormats *pix_formats = NULL, *map_formats = NULL;
116     int ret;
117
118     if (!(pix_formats = ff_make_format_list(s->format ? gray_pix_fmts : pix_fmts)) ||
119         !(map_formats = ff_make_format_list(map_fmts))) {
120         ret = AVERROR(ENOMEM);
121         goto fail;
122     }
123     if ((ret = ff_formats_ref(pix_formats, &ctx->inputs[0]->out_formats)) < 0 ||
124         (ret = ff_formats_ref(map_formats, &ctx->inputs[1]->out_formats)) < 0 ||
125         (ret = ff_formats_ref(map_formats, &ctx->inputs[2]->out_formats)) < 0 ||
126         (ret = ff_formats_ref(pix_formats, &ctx->outputs[0]->in_formats)) < 0)
127         goto fail;
128     return 0;
129 fail:
130     if (pix_formats)
131         av_freep(&pix_formats->formats);
132     av_freep(&pix_formats);
133     if (map_formats)
134         av_freep(&map_formats->formats);
135     av_freep(&map_formats);
136     return ret;
137 }
138
139 /**
140  * remap_planar algorithm expects planes of same size
141  * pixels are copied from source to target using :
142  * Target_frame[y][x] = Source_frame[ ymap[y][x] ][ [xmap[y][x] ];
143  */
144 #define DEFINE_REMAP_PLANAR_FUNC(name, bits, div)                                           \
145 static int remap_planar##bits##_##name##_slice(AVFilterContext *ctx, void *arg,             \
146                                                int jobnr, int nb_jobs)                      \
147 {                                                                                           \
148     RemapContext *s = ctx->priv;                                                            \
149     const ThreadData *td = arg;                                                             \
150     const AVFrame *in  = td->in;                                                            \
151     const AVFrame *xin = td->xin;                                                           \
152     const AVFrame *yin = td->yin;                                                           \
153     const AVFrame *out = td->out;                                                           \
154     const int slice_start = (out->height *  jobnr   ) / nb_jobs;                            \
155     const int slice_end   = (out->height * (jobnr+1)) / nb_jobs;                            \
156     const int xlinesize = xin->linesize[0] / 2;                                             \
157     const int ylinesize = yin->linesize[0] / 2;                                             \
158     int x , y, plane;                                                                       \
159                                                                                             \
160     for (plane = 0; plane < td->nb_planes ; plane++) {                                      \
161         const int dlinesize  = out->linesize[plane] / div;                                  \
162         const uint##bits##_t *src = (const uint##bits##_t *)in->data[plane];                \
163         uint##bits##_t *dst = (uint##bits##_t *)out->data[plane] + slice_start * dlinesize; \
164         const int slinesize  = in->linesize[plane] / div;                                   \
165         const uint16_t *xmap = (const uint16_t *)xin->data[0] + slice_start * xlinesize;    \
166         const uint16_t *ymap = (const uint16_t *)yin->data[0] + slice_start * ylinesize;    \
167         const int color = s->fill_color[plane];                                             \
168                                                                                             \
169         for (y = slice_start; y < slice_end; y++) {                                         \
170             for (x = 0; x < out->width; x++) {                                              \
171                 if (ymap[x] < in->height && xmap[x] < in->width) {                          \
172                     dst[x] = src[ymap[x] * slinesize + xmap[x]];                            \
173                 } else {                                                                    \
174                     dst[x] = color;                                                         \
175                 }                                                                           \
176             }                                                                               \
177             dst  += dlinesize;                                                              \
178             xmap += xlinesize;                                                              \
179             ymap += ylinesize;                                                              \
180         }                                                                                   \
181     }                                                                                       \
182                                                                                             \
183     return 0;                                                                               \
184 }
185
186 DEFINE_REMAP_PLANAR_FUNC(nearest, 8, 1)
187 DEFINE_REMAP_PLANAR_FUNC(nearest, 16, 2)
188
189 /**
190  * remap_packed algorithm expects pixels with both padded bits (step) and
191  * number of components correctly set.
192  * pixels are copied from source to target using :
193  * Target_frame[y][x] = Source_frame[ ymap[y][x] ][ [xmap[y][x] ];
194  */
195 #define DEFINE_REMAP_PACKED_FUNC(name, bits, div)                                           \
196 static int remap_packed##bits##_##name##_slice(AVFilterContext *ctx, void *arg,             \
197                                                int jobnr, int nb_jobs)                      \
198 {                                                                                           \
199     RemapContext *s = ctx->priv;                                                            \
200     const ThreadData *td = arg;                                                             \
201     const AVFrame *in  = td->in;                                                            \
202     const AVFrame *xin = td->xin;                                                           \
203     const AVFrame *yin = td->yin;                                                           \
204     const AVFrame *out = td->out;                                                           \
205     const int slice_start = (out->height *  jobnr   ) / nb_jobs;                            \
206     const int slice_end   = (out->height * (jobnr+1)) / nb_jobs;                            \
207     const int dlinesize  = out->linesize[0] / div;                                          \
208     const int slinesize  = in->linesize[0] / div;                                           \
209     const int xlinesize  = xin->linesize[0] / 2;                                            \
210     const int ylinesize  = yin->linesize[0] / 2;                                            \
211     const uint##bits##_t *src = (const uint##bits##_t *)in->data[0];                        \
212     uint##bits##_t *dst = (uint##bits##_t *)out->data[0] + slice_start * dlinesize;         \
213     const uint16_t *xmap = (const uint16_t *)xin->data[0] + slice_start * xlinesize;        \
214     const uint16_t *ymap = (const uint16_t *)yin->data[0] + slice_start * ylinesize;        \
215     const int step       = td->step / div;                                                  \
216     int c, x, y;                                                                            \
217                                                                                             \
218     for (y = slice_start; y < slice_end; y++) {                                             \
219         for (x = 0; x < out->width; x++) {                                                  \
220             for (c = 0; c < td->nb_components; c++) {                                       \
221                 if (ymap[x] < in->height && xmap[x] < in->width) {                          \
222                     dst[x * step + c] = src[ymap[x] * slinesize + xmap[x] * step + c];      \
223                 } else {                                                                    \
224                     dst[x * step + c] = s->fill_color[c];                                   \
225                 }                                                                           \
226             }                                                                               \
227         }                                                                                   \
228         dst  += dlinesize;                                                                  \
229         xmap += xlinesize;                                                                  \
230         ymap += ylinesize;                                                                  \
231     }                                                                                       \
232                                                                                             \
233     return 0;                                                                               \
234 }
235
236 DEFINE_REMAP_PACKED_FUNC(nearest, 8, 1)
237 DEFINE_REMAP_PACKED_FUNC(nearest, 16, 2)
238
239 static int config_input(AVFilterLink *inlink)
240 {
241     AVFilterContext *ctx = inlink->dst;
242     RemapContext *s = ctx->priv;
243     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
244     int depth = desc->comp[0].depth;
245     int is_rgb = !!(desc->flags & AV_PIX_FMT_FLAG_RGB);
246     int factor = 1 << (depth - 8);
247     uint8_t rgba_map[4];
248
249     ff_fill_rgba_map(rgba_map, inlink->format);
250     s->nb_planes = av_pix_fmt_count_planes(inlink->format);
251     s->nb_components = desc->nb_components;
252
253     if (is_rgb) {
254         s->fill_color[rgba_map[0]] = s->fill_rgba[0] * factor;
255         s->fill_color[rgba_map[1]] = s->fill_rgba[1] * factor;
256         s->fill_color[rgba_map[2]] = s->fill_rgba[2] * factor;
257         s->fill_color[rgba_map[3]] = s->fill_rgba[3] * factor;
258     } else {
259         s->fill_color[0] = RGB_TO_Y_BT709(s->fill_rgba[0], s->fill_rgba[1], s->fill_rgba[2]) * factor;
260         s->fill_color[1] = RGB_TO_U_BT709(s->fill_rgba[0], s->fill_rgba[1], s->fill_rgba[2], 0) * factor;
261         s->fill_color[2] = RGB_TO_V_BT709(s->fill_rgba[0], s->fill_rgba[1], s->fill_rgba[2], 0) * factor;
262         s->fill_color[3] = s->fill_rgba[3] * factor;
263     }
264
265     if (depth == 8) {
266         if (s->nb_planes > 1 || s->nb_components == 1) {
267             s->remap_slice = remap_planar8_nearest_slice;
268         } else {
269             s->remap_slice = remap_packed8_nearest_slice;
270         }
271     } else {
272         if (s->nb_planes > 1 || s->nb_components == 1) {
273             s->remap_slice = remap_planar16_nearest_slice;
274         } else {
275             s->remap_slice = remap_packed16_nearest_slice;
276         }
277     }
278
279     s->step = av_get_padded_bits_per_pixel(desc) >> 3;
280     return 0;
281 }
282
283 static int process_frame(FFFrameSync *fs)
284 {
285     AVFilterContext *ctx = fs->parent;
286     RemapContext *s = fs->opaque;
287     AVFilterLink *outlink = ctx->outputs[0];
288     AVFrame *out, *in, *xpic, *ypic;
289     int ret;
290
291     if ((ret = ff_framesync_get_frame(&s->fs, 0, &in,   0)) < 0 ||
292         (ret = ff_framesync_get_frame(&s->fs, 1, &xpic, 0)) < 0 ||
293         (ret = ff_framesync_get_frame(&s->fs, 2, &ypic, 0)) < 0)
294         return ret;
295
296     if (ctx->is_disabled) {
297         out = av_frame_clone(in);
298         if (!out)
299             return AVERROR(ENOMEM);
300     } else {
301         ThreadData td;
302
303         out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
304         if (!out)
305             return AVERROR(ENOMEM);
306         av_frame_copy_props(out, in);
307
308         td.in  = in;
309         td.xin = xpic;
310         td.yin = ypic;
311         td.out = out;
312         td.nb_planes = s->nb_planes;
313         td.nb_components = s->nb_components;
314         td.step = s->step;
315         ctx->internal->execute(ctx, s->remap_slice, &td, NULL, FFMIN(outlink->h, ff_filter_get_nb_threads(ctx)));
316     }
317     out->pts = av_rescale_q(s->fs.pts, s->fs.time_base, outlink->time_base);
318
319     return ff_filter_frame(outlink, out);
320 }
321
322 static int config_output(AVFilterLink *outlink)
323 {
324     AVFilterContext *ctx = outlink->src;
325     RemapContext *s = ctx->priv;
326     AVFilterLink *srclink = ctx->inputs[0];
327     AVFilterLink *xlink = ctx->inputs[1];
328     AVFilterLink *ylink = ctx->inputs[2];
329     FFFrameSyncIn *in;
330     int ret;
331
332     if (xlink->w != ylink->w || xlink->h != ylink->h) {
333         av_log(ctx, AV_LOG_ERROR, "Second input link %s parameters "
334                "(size %dx%d) do not match the corresponding "
335                "third input link %s parameters (%dx%d)\n",
336                ctx->input_pads[1].name, xlink->w, xlink->h,
337                ctx->input_pads[2].name, ylink->w, ylink->h);
338         return AVERROR(EINVAL);
339     }
340
341     outlink->w = xlink->w;
342     outlink->h = xlink->h;
343     outlink->sample_aspect_ratio = srclink->sample_aspect_ratio;
344     outlink->frame_rate = srclink->frame_rate;
345
346     ret = ff_framesync_init(&s->fs, ctx, 3);
347     if (ret < 0)
348         return ret;
349
350     in = s->fs.in;
351     in[0].time_base = srclink->time_base;
352     in[1].time_base = xlink->time_base;
353     in[2].time_base = ylink->time_base;
354     in[0].sync   = 2;
355     in[0].before = EXT_STOP;
356     in[0].after  = EXT_STOP;
357     in[1].sync   = 1;
358     in[1].before = EXT_NULL;
359     in[1].after  = EXT_INFINITY;
360     in[2].sync   = 1;
361     in[2].before = EXT_NULL;
362     in[2].after  = EXT_INFINITY;
363     s->fs.opaque   = s;
364     s->fs.on_event = process_frame;
365
366     ret = ff_framesync_configure(&s->fs);
367     outlink->time_base = s->fs.time_base;
368
369     return ret;
370 }
371
372 static int activate(AVFilterContext *ctx)
373 {
374     RemapContext *s = ctx->priv;
375     return ff_framesync_activate(&s->fs);
376 }
377
378 static av_cold void uninit(AVFilterContext *ctx)
379 {
380     RemapContext *s = ctx->priv;
381
382     ff_framesync_uninit(&s->fs);
383 }
384
385 static const AVFilterPad remap_inputs[] = {
386     {
387         .name         = "source",
388         .type         = AVMEDIA_TYPE_VIDEO,
389         .config_props = config_input,
390     },
391     {
392         .name         = "xmap",
393         .type         = AVMEDIA_TYPE_VIDEO,
394     },
395     {
396         .name         = "ymap",
397         .type         = AVMEDIA_TYPE_VIDEO,
398     },
399     { NULL }
400 };
401
402 static const AVFilterPad remap_outputs[] = {
403     {
404         .name          = "default",
405         .type          = AVMEDIA_TYPE_VIDEO,
406         .config_props  = config_output,
407     },
408     { NULL }
409 };
410
411 AVFilter ff_vf_remap = {
412     .name          = "remap",
413     .description   = NULL_IF_CONFIG_SMALL("Remap pixels."),
414     .priv_size     = sizeof(RemapContext),
415     .uninit        = uninit,
416     .query_formats = query_formats,
417     .activate      = activate,
418     .inputs        = remap_inputs,
419     .outputs       = remap_outputs,
420     .priv_class    = &remap_class,
421     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
422 };