1/* 2 * Copyright (c) 2002 Michael Niedermayer <michaelni@gmx.at> 3 * Copyright (c) 2013 Paul B Mahol 4 * 5 * This file is part of FFmpeg. 6 * 7 * FFmpeg is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * FFmpeg is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with FFmpeg; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 */ 21 22/** 23 * @file 24 * (de)interleave fields filter 25 */ 26 27#include "libavutil/opt.h" 28#include "libavutil/imgutils.h" 29#include "libavutil/pixdesc.h" 30#include "avfilter.h" 31#include "internal.h" 32 33enum FilterMode { 34 MODE_NONE, 35 MODE_INTERLEAVE, 36 MODE_DEINTERLEAVE 37}; 38 39typedef struct { 40 const AVClass *class; 41 enum FilterMode luma_mode, chroma_mode, alpha_mode; 42 int luma_swap, chroma_swap, alpha_swap; 43 int nb_planes; 44 int linesize[4], chroma_height; 45 int has_alpha; 46} IlContext; 47 48#define OFFSET(x) offsetof(IlContext, x) 49#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM 50 51static const AVOption il_options[] = { 52 {"luma_mode", "select luma mode", OFFSET(luma_mode), AV_OPT_TYPE_INT, {.i64=MODE_NONE}, MODE_NONE, MODE_DEINTERLEAVE, FLAGS, "luma_mode"}, 53 {"l", "select luma mode", OFFSET(luma_mode), AV_OPT_TYPE_INT, {.i64=MODE_NONE}, MODE_NONE, MODE_DEINTERLEAVE, FLAGS, "luma_mode"}, 54 {"none", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_NONE}, 0, 0, FLAGS, "luma_mode"}, 55 {"interleave", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLEAVE}, 0, 0, FLAGS, "luma_mode"}, 56 {"i", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLEAVE}, 0, 0, FLAGS, "luma_mode"}, 57 {"deinterleave", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_DEINTERLEAVE}, 0, 0, FLAGS, "luma_mode"}, 58 {"d", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_DEINTERLEAVE}, 0, 0, FLAGS, "luma_mode"}, 59 {"chroma_mode", "select chroma mode", OFFSET(chroma_mode), AV_OPT_TYPE_INT, {.i64=MODE_NONE}, MODE_NONE, MODE_DEINTERLEAVE, FLAGS, "chroma_mode"}, 60 {"c", "select chroma mode", OFFSET(chroma_mode), AV_OPT_TYPE_INT, {.i64=MODE_NONE}, MODE_NONE, MODE_DEINTERLEAVE, FLAGS, "chroma_mode"}, 61 {"none", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_NONE}, 0, 0, FLAGS, "chroma_mode"}, 62 {"interleave", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLEAVE}, 0, 0, FLAGS, "chroma_mode"}, 63 {"i", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLEAVE}, 0, 0, FLAGS, "chroma_mode"}, 64 {"deinterleave", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_DEINTERLEAVE}, 0, 0, FLAGS, "chroma_mode"}, 65 {"d", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_DEINTERLEAVE}, 0, 0, FLAGS, "chroma_mode"}, 66 {"alpha_mode", "select alpha mode", OFFSET(alpha_mode), AV_OPT_TYPE_INT, {.i64=MODE_NONE}, MODE_NONE, MODE_DEINTERLEAVE, FLAGS, "alpha_mode"}, 67 {"a", "select alpha mode", OFFSET(alpha_mode), AV_OPT_TYPE_INT, {.i64=MODE_NONE}, MODE_NONE, MODE_DEINTERLEAVE, FLAGS, "alpha_mode"}, 68 {"none", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_NONE}, 0, 0, FLAGS, "alpha_mode"}, 69 {"interleave", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLEAVE}, 0, 0, FLAGS, "alpha_mode"}, 70 {"i", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLEAVE}, 0, 0, FLAGS, "alpha_mode"}, 71 {"deinterleave", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_DEINTERLEAVE}, 0, 0, FLAGS, "alpha_mode"}, 72 {"d", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_DEINTERLEAVE}, 0, 0, FLAGS, "alpha_mode"}, 73 {"luma_swap", "swap luma fields", OFFSET(luma_swap), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS}, 74 {"ls", "swap luma fields", OFFSET(luma_swap), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS}, 75 {"chroma_swap", "swap chroma fields", OFFSET(chroma_swap), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS}, 76 {"cs", "swap chroma fields", OFFSET(chroma_swap), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS}, 77 {"alpha_swap", "swap alpha fields", OFFSET(alpha_swap), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS}, 78 {"as", "swap alpha fields", OFFSET(alpha_swap), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS}, 79 {NULL} 80}; 81 82AVFILTER_DEFINE_CLASS(il); 83 84static int query_formats(AVFilterContext *ctx) 85{ 86 AVFilterFormats *formats = NULL; 87 int fmt; 88 89 for (fmt = 0; av_pix_fmt_desc_get(fmt); fmt++) { 90 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(fmt); 91 if (!(desc->flags & AV_PIX_FMT_FLAG_PAL) && !(desc->flags & AV_PIX_FMT_FLAG_HWACCEL)) 92 ff_add_format(&formats, fmt); 93 } 94 95 ff_set_common_formats(ctx, formats); 96 return 0; 97} 98 99static int config_input(AVFilterLink *inlink) 100{ 101 IlContext *il = inlink->dst->priv; 102 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); 103 int ret; 104 105 il->nb_planes = av_pix_fmt_count_planes(inlink->format); 106 107 il->has_alpha = !!(desc->flags & AV_PIX_FMT_FLAG_ALPHA); 108 if ((ret = av_image_fill_linesizes(il->linesize, inlink->format, inlink->w)) < 0) 109 return ret; 110 111 il->chroma_height = FF_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h); 112 113 return 0; 114} 115 116static void interleave(uint8_t *dst, uint8_t *src, int w, int h, 117 int dst_linesize, int src_linesize, 118 enum FilterMode mode, int swap) 119{ 120 const int a = swap; 121 const int b = 1 - a; 122 const int m = h >> 1; 123 int y; 124 125 switch (mode) { 126 case MODE_DEINTERLEAVE: 127 for (y = 0; y < m; y++) { 128 memcpy(dst + dst_linesize * y , src + src_linesize * (y * 2 + a), w); 129 memcpy(dst + dst_linesize * (y + m), src + src_linesize * (y * 2 + b), w); 130 } 131 break; 132 case MODE_NONE: 133 for (y = 0; y < m; y++) { 134 memcpy(dst + dst_linesize * y * 2 , src + src_linesize * (y * 2 + a), w); 135 memcpy(dst + dst_linesize * (y * 2 + 1), src + src_linesize * (y * 2 + b), w); 136 } 137 break; 138 case MODE_INTERLEAVE: 139 for (y = 0; y < m; y++) { 140 memcpy(dst + dst_linesize * (y * 2 + a), src + src_linesize * y , w); 141 memcpy(dst + dst_linesize * (y * 2 + b), src + src_linesize * (y + m), w); 142 } 143 break; 144 } 145} 146 147static int filter_frame(AVFilterLink *inlink, AVFrame *inpicref) 148{ 149 IlContext *il = inlink->dst->priv; 150 AVFilterLink *outlink = inlink->dst->outputs[0]; 151 AVFrame *out; 152 int comp; 153 154 out = ff_get_video_buffer(outlink, outlink->w, outlink->h); 155 if (!out) { 156 av_frame_free(&inpicref); 157 return AVERROR(ENOMEM); 158 } 159 av_frame_copy_props(out, inpicref); 160 161 interleave(out->data[0], inpicref->data[0], 162 il->linesize[0], inlink->h, 163 out->linesize[0], inpicref->linesize[0], 164 il->luma_mode, il->luma_swap); 165 166 for (comp = 1; comp < (il->nb_planes - il->has_alpha); comp++) { 167 interleave(out->data[comp], inpicref->data[comp], 168 il->linesize[comp], il->chroma_height, 169 out->linesize[comp], inpicref->linesize[comp], 170 il->chroma_mode, il->chroma_swap); 171 } 172 173 if (il->has_alpha) { 174 comp = il->nb_planes - 1; 175 interleave(out->data[comp], inpicref->data[comp], 176 il->linesize[comp], inlink->h, 177 out->linesize[comp], inpicref->linesize[comp], 178 il->alpha_mode, il->alpha_swap); 179 } 180 181 av_frame_free(&inpicref); 182 return ff_filter_frame(outlink, out); 183} 184 185static const AVFilterPad inputs[] = { 186 { 187 .name = "default", 188 .type = AVMEDIA_TYPE_VIDEO, 189 .filter_frame = filter_frame, 190 .config_props = config_input, 191 }, 192 { NULL } 193}; 194 195static const AVFilterPad outputs[] = { 196 { 197 .name = "default", 198 .type = AVMEDIA_TYPE_VIDEO, 199 }, 200 { NULL } 201}; 202 203AVFilter ff_vf_il = { 204 .name = "il", 205 .description = NULL_IF_CONFIG_SMALL("Deinterleave or interleave fields."), 206 .priv_size = sizeof(IlContext), 207 .query_formats = query_formats, 208 .inputs = inputs, 209 .outputs = outputs, 210 .priv_class = &il_class, 211 .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, 212}; 213