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 "libavutil/avstring.h" 22#include "libavutil/imgutils.h" 23#include "libavutil/opt.h" 24#include "libavutil/pixdesc.h" 25#include "avfilter.h" 26#include "drawutils.h" 27#include "internal.h" 28 29#define PLANE_R 0x01 30#define PLANE_G 0x02 31#define PLANE_B 0x04 32#define PLANE_A 0x08 33#define PLANE_Y 0x10 34#define PLANE_U 0x20 35#define PLANE_V 0x40 36 37typedef struct { 38 const AVClass *class; 39 int requested_planes; 40 int map[4]; 41 int linesize[4]; 42 int is_packed_rgb; 43 int depth; 44 int step; 45} ExtractPlanesContext; 46 47#define OFFSET(x) offsetof(ExtractPlanesContext, x) 48#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM 49static 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"}, 58 { NULL } 59}; 60 61AVFILTER_DEFINE_CLASS(extractplanes); 62 63static int query_formats(AVFilterContext *ctx) 64{ 65 static const enum AVPixelFormat in_pixfmts[] = { 66 AV_PIX_FMT_YUV410P, 67 AV_PIX_FMT_YUV411P, 68 AV_PIX_FMT_YUV440P, 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, 92 AV_PIX_FMT_NONE, 93 }; 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; 101 102 if (!ctx->inputs[0]->in_formats || 103 !ctx->inputs[0]->in_formats->nb_formats) { 104 return AVERROR(EAGAIN); 105 } 106 107 if (!ctx->inputs[0]->out_formats) 108 ff_formats_ref(ff_make_format_list(in_pixfmts), &ctx->inputs[0]->out_formats); 109 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); 119 } 120 } 121 122 if (depth == 7) 123 out_pixfmts = out8_pixfmts; 124 else if (be) 125 out_pixfmts = out16be_pixfmts; 126 else 127 out_pixfmts = out16le_pixfmts; 128 129 for (i = 0; i < ctx->nb_outputs; i++) 130 ff_formats_ref(ff_make_format_list(out_pixfmts), &ctx->outputs[i]->in_formats); 131 return 0; 132} 133 134static int config_input(AVFilterLink *inlink) 135{ 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; 140 uint8_t rgba_map[4]; 141 142 plane_avail = ((desc->flags & AV_PIX_FMT_FLAG_RGB) ? PLANE_R|PLANE_G|PLANE_B : 143 PLANE_Y | 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); 149 } 150 if ((ret = av_image_fill_linesizes(e->linesize, inlink->format, inlink->w)) < 0) 151 return ret; 152 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]]; 160 } 161 162 return 0; 163} 164 165static int config_output(AVFilterLink *outlink) 166{ 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; 172 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); 176 } 177 178 return 0; 179} 180 181static 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) 185{ 186 int x, y; 187 188 for (y = 0; y < height; y++) { 189 switch (depth) { 190 case 1: 191 for (x = 0; x < width; x++) 192 dst[x] = src[x * step + comp]; 193 break; 194 case 2: 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]; 198 } 199 break; 200 } 201 dst += dst_linesize; 202 src += src_linesize; 203 } 204} 205 206static int filter_frame(AVFilterLink *inlink, AVFrame *frame) 207{ 208 AVFilterContext *ctx = inlink->dst; 209 ExtractPlanesContext *e = ctx->priv; 210 int i, eof = 0, ret = 0; 211 212 for (i = 0; i < ctx->nb_outputs; i++) { 213 AVFilterLink *outlink = ctx->outputs[i]; 214 const int idx = e->map[i]; 215 AVFrame *out; 216 217 if (outlink->closed) 218 continue; 219 220 out = ff_get_video_buffer(outlink, outlink->w, outlink->h); 221 if (!out) { 222 ret = AVERROR(ENOMEM); 223 break; 224 } 225 av_frame_copy_props(out, frame); 226 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, 231 e->depth, 232 e->step, idx); 233 } else { 234 av_image_copy_plane(out->data[0], out->linesize[0], 235 frame->data[idx], frame->linesize[idx], 236 e->linesize[idx], outlink->h); 237 } 238 239 ret = ff_filter_frame(outlink, out); 240 if (ret == AVERROR_EOF) 241 eof++; 242 else if (ret < 0) 243 break; 244 } 245 av_frame_free(&frame); 246 247 if (eof == ctx->nb_outputs) 248 ret = AVERROR_EOF; 249 else if (ret == AVERROR_EOF) 250 ret = 0; 251 return ret; 252} 253 254static av_cold int init(AVFilterContext *ctx) 255{ 256 ExtractPlanesContext *e = ctx->priv; 257 int planes = (e->requested_planes & 0xf) | (e->requested_planes >> 4); 258 int i; 259 260 for (i = 0; i < 4; i++) { 261 char *name; 262 AVFilterPad pad = { 0 }; 263 264 if (!(planes & (1 << i))) 265 continue; 266 267 name = av_asprintf("out%d", ctx->nb_outputs); 268 if (!name) 269 return AVERROR(ENOMEM); 270 e->map[ctx->nb_outputs] = i; 271 pad.name = name; 272 pad.type = AVMEDIA_TYPE_VIDEO; 273 pad.config_props = config_output; 274 275 ff_insert_outpad(ctx, ctx->nb_outputs, &pad); 276 } 277 278 return 0; 279} 280 281static av_cold void uninit(AVFilterContext *ctx) 282{ 283 int i; 284 285 for (i = 0; i < ctx->nb_outputs; i++) 286 av_freep(&ctx->output_pads[i].name); 287} 288 289static const AVFilterPad extractplanes_inputs[] = { 290 { 291 .name = "default", 292 .type = AVMEDIA_TYPE_VIDEO, 293 .filter_frame = filter_frame, 294 .config_props = config_input, 295 }, 296 { NULL } 297}; 298 299AVFilter 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, 304 .init = init, 305 .uninit = uninit, 306 .query_formats = query_formats, 307 .inputs = extractplanes_inputs, 308 .outputs = NULL, 309 .flags = AVFILTER_FLAG_DYNAMIC_OUTPUTS, 310}; 311 312#if CONFIG_ALPHAEXTRACT_FILTER 313 314static av_cold int init_alphaextract(AVFilterContext *ctx) 315{ 316 ExtractPlanesContext *e = ctx->priv; 317 318 e->requested_planes = PLANE_A; 319 320 return init(ctx); 321} 322 323AVFilter 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, 329 .uninit = uninit, 330 .query_formats = query_formats, 331 .inputs = extractplanes_inputs, 332 .outputs = NULL, 333 .flags = AVFILTER_FLAG_DYNAMIC_OUTPUTS, 334}; 335#endif /* CONFIG_ALPHAEXTRACT_FILTER */ 336