• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2013 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 "config_components.h"
22 
23 #include "libavutil/avstring.h"
24 #include "libavutil/imgutils.h"
25 #include "libavutil/opt.h"
26 #include "libavutil/pixdesc.h"
27 
28 #include "avfilter.h"
29 #include "drawutils.h"
30 #include "filters.h"
31 #include "internal.h"
32 
33 #define PLANE_R 0x01
34 #define PLANE_G 0x02
35 #define PLANE_B 0x04
36 #define PLANE_A 0x08
37 #define PLANE_Y 0x10
38 #define PLANE_U 0x20
39 #define PLANE_V 0x40
40 
41 typedef struct ExtractPlanesContext {
42     const AVClass *class;
43     int requested_planes;
44     int map[4];
45     int linesize[4];
46     int is_packed;
47     int depth;
48     int step;
49 } ExtractPlanesContext;
50 
51 #define OFFSET(x) offsetof(ExtractPlanesContext, x)
52 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
53 static const AVOption extractplanes_options[] = {
54     { "planes", "set planes",  OFFSET(requested_planes), AV_OPT_TYPE_FLAGS, {.i64=1}, 1, 0xff, FLAGS, "flags"},
55     {      "y", "set luma plane",  0, AV_OPT_TYPE_CONST, {.i64=PLANE_Y}, 0, 0, FLAGS, "flags"},
56     {      "u", "set u plane",     0, AV_OPT_TYPE_CONST, {.i64=PLANE_U}, 0, 0, FLAGS, "flags"},
57     {      "v", "set v plane",     0, AV_OPT_TYPE_CONST, {.i64=PLANE_V}, 0, 0, FLAGS, "flags"},
58     {      "r", "set red plane",   0, AV_OPT_TYPE_CONST, {.i64=PLANE_R}, 0, 0, FLAGS, "flags"},
59     {      "g", "set green plane", 0, AV_OPT_TYPE_CONST, {.i64=PLANE_G}, 0, 0, FLAGS, "flags"},
60     {      "b", "set blue plane",  0, AV_OPT_TYPE_CONST, {.i64=PLANE_B}, 0, 0, FLAGS, "flags"},
61     {      "a", "set alpha plane", 0, AV_OPT_TYPE_CONST, {.i64=PLANE_A}, 0, 0, FLAGS, "flags"},
62     { NULL }
63 };
64 
65 AVFILTER_DEFINE_CLASS(extractplanes);
66 
67 #define EIGHTBIT_FORMATS                           \
68         AV_PIX_FMT_YUV410P,                        \
69         AV_PIX_FMT_YUV411P,                        \
70         AV_PIX_FMT_YUV440P,                        \
71         AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUVA420P,   \
72         AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVA422P,   \
73         AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ422P,  \
74         AV_PIX_FMT_YUVJ440P, AV_PIX_FMT_YUVJ444P,  \
75         AV_PIX_FMT_YUVJ411P,                       \
76         AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUVA444P,   \
77         AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY8A,       \
78         AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24,        \
79         AV_PIX_FMT_RGBA, AV_PIX_FMT_BGRA,          \
80         AV_PIX_FMT_ARGB, AV_PIX_FMT_ABGR,          \
81         AV_PIX_FMT_RGB0, AV_PIX_FMT_BGR0,          \
82         AV_PIX_FMT_0RGB, AV_PIX_FMT_0BGR,          \
83         AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRAP
84 
85 #define HIGHDEPTH_FORMATS(suf)                                 \
86         AV_PIX_FMT_YA16##suf,                                  \
87         AV_PIX_FMT_GRAY9##suf,                                 \
88         AV_PIX_FMT_GRAY10##suf,                                \
89         AV_PIX_FMT_GRAY12##suf,                                \
90         AV_PIX_FMT_GRAY14##suf,                                \
91         AV_PIX_FMT_GRAY16##suf,                                \
92         AV_PIX_FMT_YUV420P16##suf, AV_PIX_FMT_YUVA420P16##suf, \
93         AV_PIX_FMT_YUV422P16##suf, AV_PIX_FMT_YUVA422P16##suf, \
94         AV_PIX_FMT_YUV444P16##suf, AV_PIX_FMT_YUVA444P16##suf, \
95         AV_PIX_FMT_RGB48##suf, AV_PIX_FMT_BGR48##suf,          \
96         AV_PIX_FMT_RGBA64##suf, AV_PIX_FMT_BGRA64##suf,        \
97         AV_PIX_FMT_GBRP16##suf, AV_PIX_FMT_GBRAP16##suf,       \
98         AV_PIX_FMT_YUV420P10##suf,                             \
99         AV_PIX_FMT_YUV422P10##suf,                             \
100         AV_PIX_FMT_YUV444P10##suf,                             \
101         AV_PIX_FMT_YUV440P10##suf,                             \
102         AV_PIX_FMT_YUVA420P10##suf,                            \
103         AV_PIX_FMT_YUVA422P10##suf,                            \
104         AV_PIX_FMT_YUVA444P10##suf,                            \
105         AV_PIX_FMT_YUV420P12##suf,                             \
106         AV_PIX_FMT_YUV422P12##suf,                             \
107         AV_PIX_FMT_YUV444P12##suf,                             \
108         AV_PIX_FMT_YUV440P12##suf,                             \
109         AV_PIX_FMT_YUVA422P12##suf,                            \
110         AV_PIX_FMT_YUVA444P12##suf,                            \
111         AV_PIX_FMT_GBRP10##suf, AV_PIX_FMT_GBRAP10##suf,       \
112         AV_PIX_FMT_GBRP12##suf, AV_PIX_FMT_GBRAP12##suf,       \
113         AV_PIX_FMT_YUV420P9##suf,                              \
114         AV_PIX_FMT_YUV422P9##suf,                              \
115         AV_PIX_FMT_YUV444P9##suf,                              \
116         AV_PIX_FMT_YUVA420P9##suf,                             \
117         AV_PIX_FMT_YUVA422P9##suf,                             \
118         AV_PIX_FMT_YUVA444P9##suf,                             \
119         AV_PIX_FMT_GBRP9##suf,                                 \
120         AV_PIX_FMT_GBRP14##suf,                                \
121         AV_PIX_FMT_YUV420P14##suf,                             \
122         AV_PIX_FMT_YUV422P14##suf,                             \
123         AV_PIX_FMT_YUV444P14##suf
124 
125 #define FLOAT_FORMATS(suf)                                     \
126         AV_PIX_FMT_GRAYF32##suf,                               \
127         AV_PIX_FMT_GBRPF32##suf, AV_PIX_FMT_GBRAPF32##suf      \
128 
query_formats(AVFilterContext * ctx)129 static int query_formats(AVFilterContext *ctx)
130 {
131     static const enum AVPixelFormat in_pixfmts_le[] = {
132         EIGHTBIT_FORMATS,
133         HIGHDEPTH_FORMATS(LE),
134         FLOAT_FORMATS(LE),
135         AV_PIX_FMT_NONE,
136     };
137     static const enum AVPixelFormat in_pixfmts_be[] = {
138         EIGHTBIT_FORMATS,
139         HIGHDEPTH_FORMATS(BE),
140         FLOAT_FORMATS(BE),
141         AV_PIX_FMT_NONE,
142     };
143     static const enum AVPixelFormat out8_pixfmts[] = { AV_PIX_FMT_GRAY8, AV_PIX_FMT_NONE };
144     static const enum AVPixelFormat out9le_pixfmts[] = { AV_PIX_FMT_GRAY9LE, AV_PIX_FMT_NONE };
145     static const enum AVPixelFormat out9be_pixfmts[] = { AV_PIX_FMT_GRAY9BE, AV_PIX_FMT_NONE };
146     static const enum AVPixelFormat out10le_pixfmts[] = { AV_PIX_FMT_GRAY10LE, AV_PIX_FMT_NONE };
147     static const enum AVPixelFormat out10be_pixfmts[] = { AV_PIX_FMT_GRAY10BE, AV_PIX_FMT_NONE };
148     static const enum AVPixelFormat out12le_pixfmts[] = { AV_PIX_FMT_GRAY12LE, AV_PIX_FMT_NONE };
149     static const enum AVPixelFormat out12be_pixfmts[] = { AV_PIX_FMT_GRAY12BE, AV_PIX_FMT_NONE };
150     static const enum AVPixelFormat out14le_pixfmts[] = { AV_PIX_FMT_GRAY14LE, AV_PIX_FMT_NONE };
151     static const enum AVPixelFormat out14be_pixfmts[] = { AV_PIX_FMT_GRAY14BE, AV_PIX_FMT_NONE };
152     static const enum AVPixelFormat out16le_pixfmts[] = { AV_PIX_FMT_GRAY16LE, AV_PIX_FMT_NONE };
153     static const enum AVPixelFormat out16be_pixfmts[] = { AV_PIX_FMT_GRAY16BE, AV_PIX_FMT_NONE };
154     static const enum AVPixelFormat out32le_pixfmts[] = { AV_PIX_FMT_GRAYF32LE, AV_PIX_FMT_NONE };
155     static const enum AVPixelFormat out32be_pixfmts[] = { AV_PIX_FMT_GRAYF32BE, AV_PIX_FMT_NONE };
156     const enum AVPixelFormat *out_pixfmts, *in_pixfmts;
157     const AVPixFmtDescriptor *desc;
158     AVFilterFormats *avff;
159     int i, ret, depth = 0, be = 0;
160 
161     if (!ctx->inputs[0]->incfg.formats ||
162         !ctx->inputs[0]->incfg.formats->nb_formats) {
163         return AVERROR(EAGAIN);
164     }
165 
166     avff = ctx->inputs[0]->incfg.formats;
167     desc = av_pix_fmt_desc_get(avff->formats[0]);
168     depth = desc->comp[0].depth;
169     be = desc->flags & AV_PIX_FMT_FLAG_BE;
170     if (be) {
171         in_pixfmts = in_pixfmts_be;
172     } else {
173         in_pixfmts = in_pixfmts_le;
174     }
175     if (!ctx->inputs[0]->outcfg.formats)
176         if ((ret = ff_formats_ref(ff_make_format_list(in_pixfmts), &ctx->inputs[0]->outcfg.formats)) < 0)
177             return ret;
178 
179     for (i = 1; i < avff->nb_formats; i++) {
180         desc = av_pix_fmt_desc_get(avff->formats[i]);
181         if (depth != desc->comp[0].depth ||
182             be    != (desc->flags & AV_PIX_FMT_FLAG_BE)) {
183             return AVERROR(EAGAIN);
184         }
185     }
186 
187     if (depth == 8)
188         out_pixfmts = out8_pixfmts;
189     else if (!be && depth == 9)
190         out_pixfmts = out9le_pixfmts;
191     else if (be && depth == 9)
192         out_pixfmts = out9be_pixfmts;
193     else if (!be && depth == 10)
194         out_pixfmts = out10le_pixfmts;
195     else if (be && depth == 10)
196         out_pixfmts = out10be_pixfmts;
197     else if (!be && depth == 12)
198         out_pixfmts = out12le_pixfmts;
199     else if (be && depth == 12)
200         out_pixfmts = out12be_pixfmts;
201     else if (!be && depth == 14)
202         out_pixfmts = out14le_pixfmts;
203     else if (be && depth == 14)
204         out_pixfmts = out14be_pixfmts;
205     else if (be && depth == 16)
206         out_pixfmts = out16be_pixfmts;
207     else if (!be && depth == 16)
208         out_pixfmts = out16le_pixfmts;
209     else if (be && depth == 32)
210         out_pixfmts = out32be_pixfmts;
211     else
212         out_pixfmts = out32le_pixfmts;
213 
214     for (i = 0; i < ctx->nb_outputs; i++)
215         if ((ret = ff_formats_ref(ff_make_format_list(out_pixfmts), &ctx->outputs[i]->incfg.formats)) < 0)
216             return ret;
217     return 0;
218 }
219 
config_input(AVFilterLink * inlink)220 static int config_input(AVFilterLink *inlink)
221 {
222     AVFilterContext *ctx = inlink->dst;
223     ExtractPlanesContext *s = ctx->priv;
224     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
225     int plane_avail, ret, i;
226     uint8_t rgba_map[4];
227 
228     plane_avail = ((desc->flags & AV_PIX_FMT_FLAG_RGB) ? PLANE_R|PLANE_G|PLANE_B :
229                                                  PLANE_Y |
230                                 ((desc->nb_components > 2) ? PLANE_U|PLANE_V : 0)) |
231                   ((desc->flags & AV_PIX_FMT_FLAG_ALPHA) ? PLANE_A : 0);
232     if (s->requested_planes & ~plane_avail) {
233         av_log(ctx, AV_LOG_ERROR, "Requested planes not available.\n");
234         return AVERROR(EINVAL);
235     }
236     if ((ret = av_image_fill_linesizes(s->linesize, inlink->format, inlink->w)) < 0)
237         return ret;
238 
239     s->depth = desc->comp[0].depth >> 3;
240     s->step = av_get_padded_bits_per_pixel(desc) >> 3;
241     s->is_packed = !(desc->flags & AV_PIX_FMT_FLAG_PLANAR) &&
242                     (desc->nb_components > 1);
243     if (desc->flags & AV_PIX_FMT_FLAG_RGB) {
244         ff_fill_rgba_map(rgba_map, inlink->format);
245         for (i = 0; i < 4; i++)
246             s->map[i] = rgba_map[s->map[i]];
247     }
248 
249     return 0;
250 }
251 
config_output(AVFilterLink * outlink)252 static int config_output(AVFilterLink *outlink)
253 {
254     AVFilterContext *ctx = outlink->src;
255     AVFilterLink *inlink = ctx->inputs[0];
256     ExtractPlanesContext *s = ctx->priv;
257     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
258     const int output = outlink->srcpad - ctx->output_pads;
259 
260     if (s->map[output] == 1 || s->map[output] == 2) {
261         outlink->h = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
262         outlink->w = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w);
263     }
264 
265     return 0;
266 }
267 
extract_from_packed(uint8_t * dst,int dst_linesize,const uint8_t * src,int src_linesize,int width,int height,int depth,int step,int comp)268 static void extract_from_packed(uint8_t *dst, int dst_linesize,
269                                 const uint8_t *src, int src_linesize,
270                                 int width, int height,
271                                 int depth, int step, int comp)
272 {
273     int x, y;
274 
275     for (y = 0; y < height; y++) {
276         switch (depth) {
277         case 1:
278             for (x = 0; x < width; x++)
279                 dst[x] = src[x * step + comp];
280             break;
281         case 2:
282             for (x = 0; x < width; x++) {
283                 dst[x * 2    ] = src[x * step + comp * 2    ];
284                 dst[x * 2 + 1] = src[x * step + comp * 2 + 1];
285             }
286             break;
287         }
288         dst += dst_linesize;
289         src += src_linesize;
290     }
291 }
292 
extract_plane(AVFilterLink * outlink,AVFrame * frame)293 static int extract_plane(AVFilterLink *outlink, AVFrame *frame)
294 {
295     AVFilterContext *ctx = outlink->src;
296     ExtractPlanesContext *s = ctx->priv;
297     const int idx = s->map[FF_OUTLINK_IDX(outlink)];
298     AVFrame *out;
299 
300     out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
301     if (!out)
302         return AVERROR(ENOMEM);
303     av_frame_copy_props(out, frame);
304 
305     if (s->is_packed) {
306         extract_from_packed(out->data[0], out->linesize[0],
307                             frame->data[0], frame->linesize[0],
308                             outlink->w, outlink->h,
309                             s->depth,
310                             s->step, idx);
311     } else {
312         av_image_copy_plane(out->data[0], out->linesize[0],
313                             frame->data[idx], frame->linesize[idx],
314                             s->linesize[idx], outlink->h);
315     }
316 
317     return ff_filter_frame(outlink, out);
318 }
319 
activate(AVFilterContext * ctx)320 static int activate(AVFilterContext *ctx)
321 {
322     AVFilterLink *inlink = ctx->inputs[0];
323     int status, ret;
324     AVFrame *in;
325     int64_t pts;
326 
327     for (int i = 0; i < ctx->nb_outputs; i++) {
328         FF_FILTER_FORWARD_STATUS_BACK_ALL(ctx->outputs[i], ctx);
329     }
330 
331     ret = ff_inlink_consume_frame(inlink, &in);
332     if (ret < 0)
333         return ret;
334     if (ret > 0) {
335         for (int i = 0; i < ctx->nb_outputs; i++) {
336             if (ff_outlink_get_status(ctx->outputs[i]))
337                 continue;
338 
339             ret = extract_plane(ctx->outputs[i], in);
340             if (ret < 0)
341                 break;
342         }
343 
344         av_frame_free(&in);
345         if (ret < 0)
346             return ret;
347     }
348 
349     if (ff_inlink_acknowledge_status(inlink, &status, &pts)) {
350         for (int i = 0; i < ctx->nb_outputs; i++) {
351             if (ff_outlink_get_status(ctx->outputs[i]))
352                 continue;
353             ff_outlink_set_status(ctx->outputs[i], status, pts);
354         }
355         return 0;
356     }
357 
358     for (int i = 0; i < ctx->nb_outputs; i++) {
359         if (ff_outlink_get_status(ctx->outputs[i]))
360             continue;
361 
362         if (ff_outlink_frame_wanted(ctx->outputs[i])) {
363             ff_inlink_request_frame(inlink);
364             return 0;
365         }
366     }
367 
368     return FFERROR_NOT_READY;
369 }
370 
init(AVFilterContext * ctx)371 static av_cold int init(AVFilterContext *ctx)
372 {
373     ExtractPlanesContext *s = ctx->priv;
374     int planes = (s->requested_planes & 0xf) | (s->requested_planes >> 4);
375     int i, ret;
376 
377     for (i = 0; i < 4; i++) {
378         char *name;
379         AVFilterPad pad = { 0 };
380 
381         if (!(planes & (1 << i)))
382             continue;
383 
384         name = av_asprintf("out%d", ctx->nb_outputs);
385         if (!name)
386             return AVERROR(ENOMEM);
387         s->map[ctx->nb_outputs] = i;
388         pad.name = name;
389         pad.type = AVMEDIA_TYPE_VIDEO;
390         pad.config_props = config_output;
391 
392         if ((ret = ff_append_outpad_free_name(ctx, &pad)) < 0)
393             return ret;
394     }
395 
396     return 0;
397 }
398 
399 static const AVFilterPad extractplanes_inputs[] = {
400     {
401         .name         = "default",
402         .type         = AVMEDIA_TYPE_VIDEO,
403         .config_props = config_input,
404     },
405 };
406 
407 const AVFilter ff_vf_extractplanes = {
408     .name          = "extractplanes",
409     .description   = NULL_IF_CONFIG_SMALL("Extract planes as grayscale frames."),
410     .priv_size     = sizeof(ExtractPlanesContext),
411     .priv_class    = &extractplanes_class,
412     .init          = init,
413     .activate      = activate,
414     FILTER_INPUTS(extractplanes_inputs),
415     .outputs       = NULL,
416     FILTER_QUERY_FUNC(query_formats),
417     .flags         = AVFILTER_FLAG_DYNAMIC_OUTPUTS,
418 };
419 
420 #if CONFIG_ALPHAEXTRACT_FILTER
421 
init_alphaextract(AVFilterContext * ctx)422 static av_cold int init_alphaextract(AVFilterContext *ctx)
423 {
424     ExtractPlanesContext *s = ctx->priv;
425 
426     s->requested_planes = PLANE_A;
427     s->map[0] = 3;
428 
429     return 0;
430 }
431 
432 static const AVFilterPad alphaextract_outputs[] = {
433     {
434         .name         = "default",
435         .type         = AVMEDIA_TYPE_VIDEO,
436         .config_props = config_output,
437     },
438 };
439 
440 const AVFilter ff_vf_alphaextract = {
441     .name           = "alphaextract",
442     .description    = NULL_IF_CONFIG_SMALL("Extract an alpha channel as a "
443                       "grayscale image component."),
444     .priv_size      = sizeof(ExtractPlanesContext),
445     .init           = init_alphaextract,
446     .activate       = activate,
447     FILTER_INPUTS(extractplanes_inputs),
448     FILTER_OUTPUTS(alphaextract_outputs),
449     FILTER_QUERY_FUNC(query_formats),
450 };
451 #endif  /* CONFIG_ALPHAEXTRACT_FILTER */
452