2 * Copyright (c) 2013 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/avstring.h"
22 #include "libavutil/imgutils.h"
23 #include "libavutil/opt.h"
24 #include "libavutil/pixdesc.h"
26 #include "drawutils.h"
45 } ExtractPlanesContext
;
47 #define OFFSET(x) offsetof(ExtractPlanesContext, x)
48 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
49 static const AVOption extractplanes_options
[] = {
50 { "planes", "set planes", OFFSET(requested_planes
), AV_OPT_TYPE_FLAGS
, {.i64
=1}, 1, 0xff, FLAGS
, "flags"},
51 { "y", "set luma plane", 0, AV_OPT_TYPE_CONST
, {.i64
=PLANE_Y
}, 0, 0, FLAGS
, "flags"},
52 { "u", "set u plane", 0, AV_OPT_TYPE_CONST
, {.i64
=PLANE_U
}, 0, 0, FLAGS
, "flags"},
53 { "v", "set v plane", 0, AV_OPT_TYPE_CONST
, {.i64
=PLANE_V
}, 0, 0, FLAGS
, "flags"},
54 { "r", "set red plane", 0, AV_OPT_TYPE_CONST
, {.i64
=PLANE_R
}, 0, 0, FLAGS
, "flags"},
55 { "g", "set green plane", 0, AV_OPT_TYPE_CONST
, {.i64
=PLANE_G
}, 0, 0, FLAGS
, "flags"},
56 { "b", "set blue plane", 0, AV_OPT_TYPE_CONST
, {.i64
=PLANE_B
}, 0, 0, FLAGS
, "flags"},
57 { "a", "set alpha plane", 0, AV_OPT_TYPE_CONST
, {.i64
=PLANE_A
}, 0, 0, FLAGS
, "flags"},
61 AVFILTER_DEFINE_CLASS(extractplanes
);
63 static int query_formats(AVFilterContext
*ctx
)
65 static const enum AVPixelFormat in_pixfmts
[] = {
69 AV_PIX_FMT_YUV420P
, AV_PIX_FMT_YUVA420P
,
70 AV_PIX_FMT_YUV422P
, AV_PIX_FMT_YUVA422P
,
71 AV_PIX_FMT_YUVJ420P
, AV_PIX_FMT_YUVJ422P
,
72 AV_PIX_FMT_YUVJ440P
, AV_PIX_FMT_YUVJ444P
,
73 AV_PIX_FMT_YUV444P
, AV_PIX_FMT_YUVA444P
,
74 AV_PIX_FMT_YUV420P16LE
, AV_PIX_FMT_YUVA420P16LE
,
75 AV_PIX_FMT_YUV420P16BE
, AV_PIX_FMT_YUVA420P16BE
,
76 AV_PIX_FMT_YUV422P16LE
, AV_PIX_FMT_YUVA422P16LE
,
77 AV_PIX_FMT_YUV422P16BE
, AV_PIX_FMT_YUVA422P16BE
,
78 AV_PIX_FMT_YUV444P16LE
, AV_PIX_FMT_YUVA444P16LE
,
79 AV_PIX_FMT_YUV444P16BE
, AV_PIX_FMT_YUVA444P16BE
,
80 AV_PIX_FMT_GRAY8
, AV_PIX_FMT_GRAY8A
,
81 AV_PIX_FMT_GRAY16LE
, AV_PIX_FMT_GRAY16BE
,
82 AV_PIX_FMT_RGB24
, AV_PIX_FMT_BGR24
,
83 AV_PIX_FMT_RGBA
, AV_PIX_FMT_BGRA
,
84 AV_PIX_FMT_ARGB
, AV_PIX_FMT_ABGR
,
85 AV_PIX_FMT_RGB48LE
, AV_PIX_FMT_BGR48LE
,
86 AV_PIX_FMT_RGB48BE
, AV_PIX_FMT_BGR48BE
,
87 AV_PIX_FMT_RGBA64LE
, AV_PIX_FMT_BGRA64LE
,
88 AV_PIX_FMT_RGBA64BE
, AV_PIX_FMT_BGRA64BE
,
89 AV_PIX_FMT_GBRP
, AV_PIX_FMT_GBRAP
,
90 AV_PIX_FMT_GBRP16LE
, AV_PIX_FMT_GBRP16BE
,
91 AV_PIX_FMT_GBRAP16LE
, AV_PIX_FMT_GBRAP16BE
,
94 static const enum AVPixelFormat out8_pixfmts
[] = { AV_PIX_FMT_GRAY8
, AV_PIX_FMT_NONE
};
95 static const enum AVPixelFormat out16le_pixfmts
[] = { AV_PIX_FMT_GRAY16LE
, AV_PIX_FMT_NONE
};
96 static const enum AVPixelFormat out16be_pixfmts
[] = { AV_PIX_FMT_GRAY16BE
, AV_PIX_FMT_NONE
};
97 const enum AVPixelFormat
*out_pixfmts
;
98 const AVPixFmtDescriptor
*desc
;
99 AVFilterFormats
*avff
;
100 int i
, depth
= 0, be
= 0;
102 if (!ctx
->inputs
[0]->in_formats
||
103 !ctx
->inputs
[0]->in_formats
->nb_formats
) {
104 return AVERROR(EAGAIN
);
107 if (!ctx
->inputs
[0]->out_formats
)
108 ff_formats_ref(ff_make_format_list(in_pixfmts
), &ctx
->inputs
[0]->out_formats
);
110 avff
= ctx
->inputs
[0]->in_formats
;
111 desc
= av_pix_fmt_desc_get(avff
->formats
[0]);
112 depth
= desc
->comp
[0].depth_minus1
;
113 be
= desc
->flags
& AV_PIX_FMT_FLAG_BE
;
114 for (i
= 1; i
< avff
->nb_formats
; i
++) {
115 desc
= av_pix_fmt_desc_get(avff
->formats
[i
]);
116 if (depth
!= desc
->comp
[0].depth_minus1
||
117 be
!= (desc
->flags
& AV_PIX_FMT_FLAG_BE
)) {
118 return AVERROR(EAGAIN
);
123 out_pixfmts
= out8_pixfmts
;
125 out_pixfmts
= out16be_pixfmts
;
127 out_pixfmts
= out16le_pixfmts
;
129 for (i
= 0; i
< ctx
->nb_outputs
; i
++)
130 ff_formats_ref(ff_make_format_list(out_pixfmts
), &ctx
->outputs
[i
]->in_formats
);
134 static int config_input(AVFilterLink
*inlink
)
136 AVFilterContext
*ctx
= inlink
->dst
;
137 ExtractPlanesContext
*e
= ctx
->priv
;
138 const AVPixFmtDescriptor
*desc
= av_pix_fmt_desc_get(inlink
->format
);
139 int plane_avail
, ret
, i
;
142 plane_avail
= ((desc
->flags
& AV_PIX_FMT_FLAG_RGB
) ? PLANE_R
|PLANE_G
|PLANE_B
:
144 ((desc
->nb_components
> 2) ? PLANE_U
|PLANE_V
: 0)) |
145 ((desc
->flags
& AV_PIX_FMT_FLAG_ALPHA
) ? PLANE_A
: 0);
146 if (e
->requested_planes
& ~plane_avail
) {
147 av_log(ctx
, AV_LOG_ERROR
, "Requested planes not available.\n");
148 return AVERROR(EINVAL
);
150 if ((ret
= av_image_fill_linesizes(e
->linesize
, inlink
->format
, inlink
->w
)) < 0)
153 e
->depth
= (desc
->comp
[0].depth_minus1
+ 1) >> 3;
154 e
->step
= av_get_padded_bits_per_pixel(desc
) >> 3;
155 e
->is_packed_rgb
= !(desc
->flags
& AV_PIX_FMT_FLAG_PLANAR
);
156 if (desc
->flags
& AV_PIX_FMT_FLAG_RGB
) {
157 ff_fill_rgba_map(rgba_map
, inlink
->format
);
158 for (i
= 0; i
< 4; i
++)
159 e
->map
[i
] = rgba_map
[e
->map
[i
]];
165 static int config_output(AVFilterLink
*outlink
)
167 AVFilterContext
*ctx
= outlink
->src
;
168 AVFilterLink
*inlink
= ctx
->inputs
[0];
169 ExtractPlanesContext
*e
= ctx
->priv
;
170 const AVPixFmtDescriptor
*desc
= av_pix_fmt_desc_get(inlink
->format
);
171 const int output
= outlink
->srcpad
- ctx
->output_pads
;
173 if (e
->map
[output
] == 1 || e
->map
[output
] == 2) {
174 outlink
->h
= FF_CEIL_RSHIFT(inlink
->h
, desc
->log2_chroma_h
);
175 outlink
->w
= FF_CEIL_RSHIFT(inlink
->w
, desc
->log2_chroma_w
);
181 static void extract_from_packed(uint8_t *dst
, int dst_linesize
,
182 const uint8_t *src
, int src_linesize
,
183 int width
, int height
,
184 int depth
, int step
, int comp
)
188 for (y
= 0; y
< height
; y
++) {
191 for (x
= 0; x
< width
; x
++)
192 dst
[x
] = src
[x
* step
+ comp
];
195 for (x
= 0; x
< width
; x
++) {
196 dst
[x
* 2 ] = src
[x
* step
+ comp
* 2 ];
197 dst
[x
* 2 + 1] = src
[x
* step
+ comp
* 2 + 1];
206 static int filter_frame(AVFilterLink
*inlink
, AVFrame
*frame
)
208 AVFilterContext
*ctx
= inlink
->dst
;
209 ExtractPlanesContext
*e
= ctx
->priv
;
210 int i
, eof
= 0, ret
= 0;
212 for (i
= 0; i
< ctx
->nb_outputs
; i
++) {
213 AVFilterLink
*outlink
= ctx
->outputs
[i
];
214 const int idx
= e
->map
[i
];
220 out
= ff_get_video_buffer(outlink
, outlink
->w
, outlink
->h
);
222 ret
= AVERROR(ENOMEM
);
225 av_frame_copy_props(out
, frame
);
227 if (e
->is_packed_rgb
) {
228 extract_from_packed(out
->data
[0], out
->linesize
[0],
229 frame
->data
[0], frame
->linesize
[0],
230 outlink
->w
, outlink
->h
,
234 av_image_copy_plane(out
->data
[0], out
->linesize
[0],
235 frame
->data
[idx
], frame
->linesize
[idx
],
236 e
->linesize
[idx
], outlink
->h
);
239 ret
= ff_filter_frame(outlink
, out
);
240 if (ret
== AVERROR_EOF
)
245 av_frame_free(&frame
);
247 if (eof
== ctx
->nb_outputs
)
249 else if (ret
== AVERROR_EOF
)
254 static av_cold
int init(AVFilterContext
*ctx
)
256 ExtractPlanesContext
*e
= ctx
->priv
;
257 int planes
= (e
->requested_planes
& 0xf) | (e
->requested_planes
>> 4);
260 for (i
= 0; i
< 4; i
++) {
262 AVFilterPad pad
= { 0 };
264 if (!(planes
& (1 << i
)))
267 name
= av_asprintf("out%d", ctx
->nb_outputs
);
269 return AVERROR(ENOMEM
);
270 e
->map
[ctx
->nb_outputs
] = i
;
272 pad
.type
= AVMEDIA_TYPE_VIDEO
;
273 pad
.config_props
= config_output
;
275 ff_insert_outpad(ctx
, ctx
->nb_outputs
, &pad
);
281 static av_cold
void uninit(AVFilterContext
*ctx
)
285 for (i
= 0; i
< ctx
->nb_outputs
; i
++)
286 av_freep(&ctx
->output_pads
[i
].name
);
289 static const AVFilterPad extractplanes_inputs
[] = {
292 .type
= AVMEDIA_TYPE_VIDEO
,
293 .filter_frame
= filter_frame
,
294 .config_props
= config_input
,
299 AVFilter ff_vf_extractplanes
= {
300 .name
= "extractplanes",
301 .description
= NULL_IF_CONFIG_SMALL("Extract planes as grayscale frames."),
302 .priv_size
= sizeof(ExtractPlanesContext
),
303 .priv_class
= &extractplanes_class
,
306 .query_formats
= query_formats
,
307 .inputs
= extractplanes_inputs
,
309 .flags
= AVFILTER_FLAG_DYNAMIC_OUTPUTS
,
312 #if CONFIG_ALPHAEXTRACT_FILTER
314 static av_cold
int init_alphaextract(AVFilterContext
*ctx
)
316 ExtractPlanesContext
*e
= ctx
->priv
;
318 e
->requested_planes
= PLANE_A
;
323 AVFilter ff_vf_alphaextract
= {
324 .name
= "alphaextract",
325 .description
= NULL_IF_CONFIG_SMALL("Extract an alpha channel as a "
326 "grayscale image component."),
327 .priv_size
= sizeof(ExtractPlanesContext
),
328 .init
= init_alphaextract
,
330 .query_formats
= query_formats
,
331 .inputs
= extractplanes_inputs
,
333 .flags
= AVFILTER_FLAG_DYNAMIC_OUTPUTS
,
335 #endif /* CONFIG_ALPHAEXTRACT_FILTER */