| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * Copyright (c) 2015 Arwa Arif <arwaarif1994@gmail.com> | ||
| 3 | * Copyright (c) 2017 Paul B Mahol | ||
| 4 | * | ||
| 5 | * This file is part of FFmpeg. | ||
| 6 | * | ||
| 7 | * FFmpeg is free software; you can redistribute it and/or modify it | ||
| 8 | * under the terms of the GNU Lesser General Public License as published | ||
| 9 | * by the Free Software Foundation; either version 2.1 of the License, | ||
| 10 | * 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 | * FFT domain filtering. | ||
| 25 | */ | ||
| 26 | |||
| 27 | #include "filters.h" | ||
| 28 | #include "video.h" | ||
| 29 | #include "libavutil/common.h" | ||
| 30 | #include "libavutil/cpu.h" | ||
| 31 | #include "libavutil/mem.h" | ||
| 32 | #include "libavutil/opt.h" | ||
| 33 | #include "libavutil/pixdesc.h" | ||
| 34 | #include "libavutil/tx.h" | ||
| 35 | #include "libavutil/eval.h" | ||
| 36 | |||
| 37 | #define MAX_THREADS 32 | ||
| 38 | #define MAX_PLANES 4 | ||
| 39 | |||
| 40 | enum EvalMode { | ||
| 41 | EVAL_MODE_INIT, | ||
| 42 | EVAL_MODE_FRAME, | ||
| 43 | EVAL_MODE_NB | ||
| 44 | }; | ||
| 45 | |||
| 46 | typedef struct FFTFILTContext { | ||
| 47 | const AVClass *class; | ||
| 48 | |||
| 49 | int eval_mode; | ||
| 50 | int depth; | ||
| 51 | int nb_planes; | ||
| 52 | int nb_threads; | ||
| 53 | int planewidth[MAX_PLANES]; | ||
| 54 | int planeheight[MAX_PLANES]; | ||
| 55 | |||
| 56 | AVTXContext *hrdft[MAX_THREADS][MAX_PLANES]; | ||
| 57 | AVTXContext *vrdft[MAX_THREADS][MAX_PLANES]; | ||
| 58 | AVTXContext *ihrdft[MAX_THREADS][MAX_PLANES]; | ||
| 59 | AVTXContext *ivrdft[MAX_THREADS][MAX_PLANES]; | ||
| 60 | |||
| 61 | av_tx_fn htx_fn, ihtx_fn; | ||
| 62 | av_tx_fn vtx_fn, ivtx_fn; | ||
| 63 | |||
| 64 | int rdft_hbits[MAX_PLANES]; | ||
| 65 | int rdft_vbits[MAX_PLANES]; | ||
| 66 | size_t rdft_hstride[MAX_PLANES]; | ||
| 67 | size_t rdft_vstride[MAX_PLANES]; | ||
| 68 | size_t rdft_hlen[MAX_PLANES]; | ||
| 69 | size_t rdft_vlen[MAX_PLANES]; | ||
| 70 | float *rdft_hdata_in[MAX_PLANES]; | ||
| 71 | float *rdft_vdata_in[MAX_PLANES]; | ||
| 72 | float *rdft_hdata_out[MAX_PLANES]; | ||
| 73 | float *rdft_vdata_out[MAX_PLANES]; | ||
| 74 | |||
| 75 | int dc[MAX_PLANES]; | ||
| 76 | char *weight_str[MAX_PLANES]; | ||
| 77 | AVExpr *weight_expr[MAX_PLANES]; | ||
| 78 | double *weight[MAX_PLANES]; | ||
| 79 | |||
| 80 | int (*rdft_horizontal)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs); | ||
| 81 | int (*irdft_horizontal)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs); | ||
| 82 | } FFTFILTContext; | ||
| 83 | |||
| 84 | static const char *const var_names[] = { "X", "Y", "W", "H", "N", "WS", "HS", NULL }; | ||
| 85 | enum { VAR_X, VAR_Y, VAR_W, VAR_H, VAR_N, VAR_WS, VAR_HS, VAR_VARS_NB }; | ||
| 86 | |||
| 87 | enum { Y = 0, U, V }; | ||
| 88 | |||
| 89 | #define OFFSET(x) offsetof(FFTFILTContext, x) | ||
| 90 | #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM | ||
| 91 | |||
| 92 | static const AVOption fftfilt_options[] = { | ||
| 93 | { "dc_Y", "adjust gain in Y plane", OFFSET(dc[Y]), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1000, FLAGS }, | ||
| 94 | { "dc_U", "adjust gain in U plane", OFFSET(dc[U]), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1000, FLAGS }, | ||
| 95 | { "dc_V", "adjust gain in V plane", OFFSET(dc[V]), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1000, FLAGS }, | ||
| 96 | { "weight_Y", "set luminance expression in Y plane", OFFSET(weight_str[Y]), AV_OPT_TYPE_STRING, {.str = "1"}, 0, 0, FLAGS }, | ||
| 97 | { "weight_U", "set chrominance expression in U plane", OFFSET(weight_str[U]), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS }, | ||
| 98 | { "weight_V", "set chrominance expression in V plane", OFFSET(weight_str[V]), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS }, | ||
| 99 | { "eval", "specify when to evaluate expressions", OFFSET(eval_mode), AV_OPT_TYPE_INT, {.i64 = EVAL_MODE_INIT}, 0, EVAL_MODE_NB-1, FLAGS, .unit = "eval" }, | ||
| 100 | { "init", "eval expressions once during initialization", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_INIT}, .flags = FLAGS, .unit = "eval" }, | ||
| 101 | { "frame", "eval expressions per-frame", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_FRAME}, .flags = FLAGS, .unit = "eval" }, | ||
| 102 | {NULL}, | ||
| 103 | }; | ||
| 104 | |||
| 105 | AVFILTER_DEFINE_CLASS(fftfilt); | ||
| 106 | |||
| 107 | ✗ | static inline double lum(void *priv, double x, double y, int plane) | |
| 108 | { | ||
| 109 | ✗ | FFTFILTContext *s = priv; | |
| 110 | ✗ | return s->rdft_vdata_out[plane][(int)x * s->rdft_vstride[plane] + (int)y]; | |
| 111 | } | ||
| 112 | |||
| 113 | ✗ | static double weight_Y(void *priv, double x, double y) { return lum(priv, x, y, Y); } | |
| 114 | ✗ | static double weight_U(void *priv, double x, double y) { return lum(priv, x, y, U); } | |
| 115 | ✗ | static double weight_V(void *priv, double x, double y) { return lum(priv, x, y, V); } | |
| 116 | |||
| 117 | ✗ | static void copy_rev(float *dest, int w, int w2) | |
| 118 | { | ||
| 119 | int i; | ||
| 120 | |||
| 121 | ✗ | for (i = w; i < w + (w2-w)/2; i++) | |
| 122 | ✗ | dest[i] = dest[2*w - i - 1]; | |
| 123 | |||
| 124 | ✗ | for (; i < w2; i++) | |
| 125 | ✗ | dest[i] = dest[w2 - i]; | |
| 126 | ✗ | } | |
| 127 | |||
| 128 | ✗ | static int rdft_horizontal8(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) | |
| 129 | { | ||
| 130 | ✗ | FFTFILTContext *s = ctx->priv; | |
| 131 | ✗ | AVFrame *in = arg; | |
| 132 | |||
| 133 | ✗ | for (int plane = 0; plane < s->nb_planes; plane++) { | |
| 134 | ✗ | const int w = s->planewidth[plane]; | |
| 135 | ✗ | const int h = s->planeheight[plane]; | |
| 136 | ✗ | const int slice_start = (h * jobnr) / nb_jobs; | |
| 137 | ✗ | const int slice_end = (h * (jobnr+1)) / nb_jobs; | |
| 138 | |||
| 139 | ✗ | for (int i = slice_start; i < slice_end; i++) { | |
| 140 | ✗ | const uint8_t *src = in->data[plane] + i * in->linesize[plane]; | |
| 141 | ✗ | float *hdata_in = s->rdft_hdata_in[plane] + i * s->rdft_hstride[plane]; | |
| 142 | |||
| 143 | ✗ | for (int j = 0; j < w; j++) | |
| 144 | ✗ | hdata_in[j] = src[j]; | |
| 145 | |||
| 146 | ✗ | copy_rev(s->rdft_hdata_in[plane] + i * s->rdft_hstride[plane], w, s->rdft_hlen[plane]); | |
| 147 | } | ||
| 148 | |||
| 149 | ✗ | for (int i = slice_start; i < slice_end; i++) | |
| 150 | ✗ | s->htx_fn(s->hrdft[jobnr][plane], | |
| 151 | ✗ | s->rdft_hdata_out[plane] + i * s->rdft_hstride[plane], | |
| 152 | ✗ | s->rdft_hdata_in[plane] + i * s->rdft_hstride[plane], | |
| 153 | sizeof(float)); | ||
| 154 | } | ||
| 155 | |||
| 156 | ✗ | return 0; | |
| 157 | } | ||
| 158 | |||
| 159 | ✗ | static int rdft_horizontal16(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) | |
| 160 | { | ||
| 161 | ✗ | FFTFILTContext *s = ctx->priv; | |
| 162 | ✗ | AVFrame *in = arg; | |
| 163 | |||
| 164 | ✗ | for (int plane = 0; plane < s->nb_planes; plane++) { | |
| 165 | ✗ | const int w = s->planewidth[plane]; | |
| 166 | ✗ | const int h = s->planeheight[plane]; | |
| 167 | ✗ | const int slice_start = (h * jobnr) / nb_jobs; | |
| 168 | ✗ | const int slice_end = (h * (jobnr+1)) / nb_jobs; | |
| 169 | |||
| 170 | ✗ | for (int i = slice_start; i < slice_end; i++) { | |
| 171 | ✗ | const uint16_t *src = (const uint16_t *)(in->data[plane] + i * in->linesize[plane]); | |
| 172 | ✗ | float *hdata_in = s->rdft_hdata_in[plane] + i * s->rdft_hstride[plane]; | |
| 173 | |||
| 174 | ✗ | for (int j = 0; j < w; j++) | |
| 175 | ✗ | hdata_in[j] = src[j]; | |
| 176 | |||
| 177 | ✗ | copy_rev(s->rdft_hdata_in[plane] + i * s->rdft_hstride[plane], w, s->rdft_hlen[plane]); | |
| 178 | } | ||
| 179 | |||
| 180 | ✗ | for (int i = slice_start; i < slice_end; i++) | |
| 181 | ✗ | s->htx_fn(s->hrdft[jobnr][plane], | |
| 182 | ✗ | s->rdft_hdata_out[plane] + i * s->rdft_hstride[plane], | |
| 183 | ✗ | s->rdft_hdata_in[plane] + i * s->rdft_hstride[plane], | |
| 184 | sizeof(float)); | ||
| 185 | } | ||
| 186 | |||
| 187 | ✗ | return 0; | |
| 188 | } | ||
| 189 | |||
| 190 | ✗ | static int irdft_horizontal8(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) | |
| 191 | { | ||
| 192 | ✗ | FFTFILTContext *s = ctx->priv; | |
| 193 | ✗ | AVFrame *out = arg; | |
| 194 | |||
| 195 | ✗ | for (int plane = 0; plane < s->nb_planes; plane++) { | |
| 196 | ✗ | const int w = s->planewidth[plane]; | |
| 197 | ✗ | const int h = s->planeheight[plane]; | |
| 198 | ✗ | const int slice_start = (h * jobnr) / nb_jobs; | |
| 199 | ✗ | const int slice_end = (h * (jobnr+1)) / nb_jobs; | |
| 200 | |||
| 201 | ✗ | for (int i = slice_start; i < slice_end; i++) | |
| 202 | ✗ | s->ihtx_fn(s->ihrdft[jobnr][plane], | |
| 203 | ✗ | s->rdft_hdata_out[plane] + i * s->rdft_hstride[plane], | |
| 204 | ✗ | s->rdft_hdata_in[plane] + i * s->rdft_hstride[plane], | |
| 205 | sizeof(AVComplexFloat)); | ||
| 206 | |||
| 207 | ✗ | for (int i = slice_start; i < slice_end; i++) { | |
| 208 | ✗ | const float scale = 1.f / (s->rdft_hlen[plane] * s->rdft_vlen[plane]); | |
| 209 | ✗ | const float *src = s->rdft_hdata_out[plane] + i * s->rdft_hstride[plane]; | |
| 210 | ✗ | uint8_t *dst = out->data[plane] + i * out->linesize[plane]; | |
| 211 | |||
| 212 | ✗ | for (int j = 0; j < w; j++) | |
| 213 | ✗ | dst[j] = av_clip_uint8(lrintf(src[j] * scale)); | |
| 214 | } | ||
| 215 | } | ||
| 216 | |||
| 217 | ✗ | return 0; | |
| 218 | } | ||
| 219 | |||
| 220 | ✗ | static int irdft_horizontal16(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) | |
| 221 | { | ||
| 222 | ✗ | FFTFILTContext *s = ctx->priv; | |
| 223 | ✗ | AVFrame *out = arg; | |
| 224 | |||
| 225 | ✗ | for (int plane = 0; plane < s->nb_planes; plane++) { | |
| 226 | ✗ | int max = (1 << s->depth) - 1; | |
| 227 | ✗ | const int w = s->planewidth[plane]; | |
| 228 | ✗ | const int h = s->planeheight[plane]; | |
| 229 | ✗ | const int slice_start = (h * jobnr) / nb_jobs; | |
| 230 | ✗ | const int slice_end = (h * (jobnr+1)) / nb_jobs; | |
| 231 | |||
| 232 | ✗ | for (int i = slice_start; i < slice_end; i++) | |
| 233 | ✗ | s->ihtx_fn(s->ihrdft[jobnr][plane], | |
| 234 | ✗ | s->rdft_hdata_out[plane] + i * s->rdft_hstride[plane], | |
| 235 | ✗ | s->rdft_hdata_in[plane] + i * s->rdft_hstride[plane], | |
| 236 | sizeof(AVComplexFloat)); | ||
| 237 | |||
| 238 | ✗ | for (int i = slice_start; i < slice_end; i++) { | |
| 239 | ✗ | const float scale = 1.f / (s->rdft_hlen[plane] * s->rdft_vlen[plane]); | |
| 240 | ✗ | const float *src = s->rdft_hdata_out[plane] + i * s->rdft_hstride[plane]; | |
| 241 | ✗ | uint16_t *dst = (uint16_t *)(out->data[plane] + i * out->linesize[plane]); | |
| 242 | |||
| 243 | ✗ | for (int j = 0; j < w; j++) | |
| 244 | ✗ | dst[j] = av_clip(lrintf(src[j] * scale), 0, max); | |
| 245 | } | ||
| 246 | } | ||
| 247 | |||
| 248 | ✗ | return 0; | |
| 249 | } | ||
| 250 | |||
| 251 | ✗ | static av_cold int initialize(AVFilterContext *ctx) | |
| 252 | { | ||
| 253 | ✗ | FFTFILTContext *s = ctx->priv; | |
| 254 | ✗ | int ret = 0, plane; | |
| 255 | |||
| 256 | ✗ | if (!s->dc[U] && !s->dc[V]) { | |
| 257 | ✗ | s->dc[U] = s->dc[Y]; | |
| 258 | ✗ | s->dc[V] = s->dc[Y]; | |
| 259 | } else { | ||
| 260 | ✗ | if (!s->dc[U]) s->dc[U] = s->dc[V]; | |
| 261 | ✗ | if (!s->dc[V]) s->dc[V] = s->dc[U]; | |
| 262 | } | ||
| 263 | |||
| 264 | ✗ | if (!s->weight_str[U] && !s->weight_str[V]) { | |
| 265 | ✗ | s->weight_str[U] = av_strdup(s->weight_str[Y]); | |
| 266 | ✗ | s->weight_str[V] = av_strdup(s->weight_str[Y]); | |
| 267 | } else { | ||
| 268 | ✗ | if (!s->weight_str[U]) s->weight_str[U] = av_strdup(s->weight_str[V]); | |
| 269 | ✗ | if (!s->weight_str[V]) s->weight_str[V] = av_strdup(s->weight_str[U]); | |
| 270 | } | ||
| 271 | |||
| 272 | ✗ | for (plane = 0; plane < 3; plane++) { | |
| 273 | static double (*p[])(void *, double, double) = { weight_Y, weight_U, weight_V }; | ||
| 274 | ✗ | const char *const func2_names[] = {"weight_Y", "weight_U", "weight_V", NULL }; | |
| 275 | ✗ | double (*func2[])(void *, double, double) = { weight_Y, weight_U, weight_V, p[plane], NULL }; | |
| 276 | |||
| 277 | ✗ | ret = av_expr_parse(&s->weight_expr[plane], s->weight_str[plane], var_names, | |
| 278 | NULL, NULL, func2_names, func2, 0, ctx); | ||
| 279 | ✗ | if (ret < 0) | |
| 280 | ✗ | break; | |
| 281 | } | ||
| 282 | ✗ | return ret; | |
| 283 | } | ||
| 284 | |||
| 285 | ✗ | static void do_eval(FFTFILTContext *s, AVFilterLink *inlink, int plane) | |
| 286 | { | ||
| 287 | ✗ | FilterLink *l = ff_filter_link(inlink); | |
| 288 | double values[VAR_VARS_NB]; | ||
| 289 | int i, j; | ||
| 290 | |||
| 291 | ✗ | values[VAR_N] = l->frame_count_out; | |
| 292 | ✗ | values[VAR_W] = s->planewidth[plane]; | |
| 293 | ✗ | values[VAR_H] = s->planeheight[plane]; | |
| 294 | ✗ | values[VAR_WS] = s->rdft_hlen[plane]; | |
| 295 | ✗ | values[VAR_HS] = s->rdft_vlen[plane]; | |
| 296 | |||
| 297 | ✗ | for (i = 0; i < s->rdft_hlen[plane]; i++) { | |
| 298 | ✗ | values[VAR_X] = i; | |
| 299 | ✗ | for (j = 0; j < s->rdft_vlen[plane]; j++) { | |
| 300 | ✗ | values[VAR_Y] = j; | |
| 301 | ✗ | s->weight[plane][i * s->rdft_vlen[plane] + j] = | |
| 302 | ✗ | av_expr_eval(s->weight_expr[plane], values, s); | |
| 303 | } | ||
| 304 | } | ||
| 305 | ✗ | } | |
| 306 | |||
| 307 | ✗ | static int config_props(AVFilterLink *inlink) | |
| 308 | { | ||
| 309 | ✗ | FFTFILTContext *s = inlink->dst->priv; | |
| 310 | const AVPixFmtDescriptor *desc; | ||
| 311 | int ret, i, plane; | ||
| 312 | |||
| 313 | ✗ | desc = av_pix_fmt_desc_get(inlink->format); | |
| 314 | ✗ | s->depth = desc->comp[0].depth; | |
| 315 | ✗ | s->planewidth[1] = s->planewidth[2] = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w); | |
| 316 | ✗ | s->planewidth[0] = s->planewidth[3] = inlink->w; | |
| 317 | ✗ | s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h); | |
| 318 | ✗ | s->planeheight[0] = s->planeheight[3] = inlink->h; | |
| 319 | |||
| 320 | ✗ | s->nb_planes = av_pix_fmt_count_planes(inlink->format); | |
| 321 | ✗ | s->nb_threads = FFMIN(32, ff_filter_get_nb_threads(inlink->dst)); | |
| 322 | |||
| 323 | ✗ | for (i = 0; i < desc->nb_components; i++) { | |
| 324 | ✗ | int w = s->planewidth[i]; | |
| 325 | ✗ | int h = s->planeheight[i]; | |
| 326 | |||
| 327 | /* RDFT - Array initialization for Horizontal pass*/ | ||
| 328 | ✗ | s->rdft_hlen[i] = 1 << (32 - ff_clz(w)); | |
| 329 | ✗ | s->rdft_hstride[i] = FFALIGN(s->rdft_hlen[i] + 2, av_cpu_max_align()); | |
| 330 | ✗ | s->rdft_hbits[i] = av_log2(s->rdft_hlen[i]); | |
| 331 | ✗ | if (!(s->rdft_hdata_in[i] = av_calloc(h, s->rdft_hstride[i] * sizeof(float)))) | |
| 332 | ✗ | return AVERROR(ENOMEM); | |
| 333 | |||
| 334 | ✗ | if (!(s->rdft_hdata_out[i] = av_calloc(h, s->rdft_hstride[i] * sizeof(float)))) | |
| 335 | ✗ | return AVERROR(ENOMEM); | |
| 336 | |||
| 337 | ✗ | for (int j = 0; j < s->nb_threads; j++) { | |
| 338 | ✗ | float scale = 1.f, iscale = 1.f; | |
| 339 | |||
| 340 | ✗ | ret = av_tx_init(&s->hrdft[j][i], &s->htx_fn, AV_TX_FLOAT_RDFT, | |
| 341 | ✗ | 0, 1 << s->rdft_hbits[i], &scale, 0); | |
| 342 | ✗ | if (ret < 0) | |
| 343 | ✗ | return ret; | |
| 344 | ✗ | ret = av_tx_init(&s->ihrdft[j][i], &s->ihtx_fn, AV_TX_FLOAT_RDFT, | |
| 345 | ✗ | 1, 1 << s->rdft_hbits[i], &iscale, 0); | |
| 346 | ✗ | if (ret < 0) | |
| 347 | ✗ | return ret; | |
| 348 | } | ||
| 349 | |||
| 350 | /* RDFT - Array initialization for Vertical pass*/ | ||
| 351 | ✗ | s->rdft_vlen[i] = 1 << (32 - ff_clz(h)); | |
| 352 | ✗ | s->rdft_vstride[i] = FFALIGN(s->rdft_vlen[i] + 2, av_cpu_max_align()); | |
| 353 | ✗ | s->rdft_vbits[i] = av_log2(s->rdft_vlen[i]); | |
| 354 | ✗ | if (!(s->rdft_vdata_in[i] = av_calloc(s->rdft_hstride[i], s->rdft_vstride[i] * sizeof(float)))) | |
| 355 | ✗ | return AVERROR(ENOMEM); | |
| 356 | |||
| 357 | ✗ | if (!(s->rdft_vdata_out[i] = av_calloc(s->rdft_hstride[i], s->rdft_vstride[i] * sizeof(float)))) | |
| 358 | ✗ | return AVERROR(ENOMEM); | |
| 359 | |||
| 360 | ✗ | for (int j = 0; j < s->nb_threads; j++) { | |
| 361 | ✗ | float scale = 1.f, iscale = 1.f; | |
| 362 | |||
| 363 | ✗ | ret = av_tx_init(&s->vrdft[j][i], &s->vtx_fn, AV_TX_FLOAT_RDFT, | |
| 364 | ✗ | 0, 1 << s->rdft_vbits[i], &scale, 0); | |
| 365 | ✗ | if (ret < 0) | |
| 366 | ✗ | return ret; | |
| 367 | ✗ | ret = av_tx_init(&s->ivrdft[j][i], &s->ivtx_fn, AV_TX_FLOAT_RDFT, | |
| 368 | ✗ | 1, 1 << s->rdft_vbits[i], &iscale, 0); | |
| 369 | ✗ | if (ret < 0) | |
| 370 | ✗ | return ret; | |
| 371 | } | ||
| 372 | } | ||
| 373 | |||
| 374 | /*Luminance value - Array initialization*/ | ||
| 375 | ✗ | for (plane = 0; plane < 3; plane++) { | |
| 376 | ✗ | if(!(s->weight[plane] = av_calloc(s->rdft_hlen[plane], s->rdft_vlen[plane] * sizeof(double)))) | |
| 377 | ✗ | return AVERROR(ENOMEM); | |
| 378 | |||
| 379 | ✗ | if (s->eval_mode == EVAL_MODE_INIT) | |
| 380 | ✗ | do_eval(s, inlink, plane); | |
| 381 | } | ||
| 382 | |||
| 383 | ✗ | if (s->depth <= 8) { | |
| 384 | ✗ | s->rdft_horizontal = rdft_horizontal8; | |
| 385 | ✗ | s->irdft_horizontal = irdft_horizontal8; | |
| 386 | } else { | ||
| 387 | ✗ | s->rdft_horizontal = rdft_horizontal16; | |
| 388 | ✗ | s->irdft_horizontal = irdft_horizontal16; | |
| 389 | } | ||
| 390 | ✗ | return 0; | |
| 391 | } | ||
| 392 | |||
| 393 | ✗ | static int multiply_data(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) | |
| 394 | { | ||
| 395 | ✗ | FFTFILTContext *s = ctx->priv; | |
| 396 | |||
| 397 | ✗ | for (int plane = 0; plane < s->nb_planes; plane++) { | |
| 398 | ✗ | const int height = s->rdft_hlen[plane]; | |
| 399 | ✗ | const int slice_start = (height * jobnr) / nb_jobs; | |
| 400 | ✗ | const int slice_end = (height * (jobnr+1)) / nb_jobs; | |
| 401 | /*Change user defined parameters*/ | ||
| 402 | ✗ | for (int i = slice_start; i < slice_end; i++) { | |
| 403 | ✗ | const double *weight = s->weight[plane] + i * s->rdft_vlen[plane]; | |
| 404 | ✗ | float *vdata = s->rdft_vdata_out[plane] + i * s->rdft_vstride[plane]; | |
| 405 | |||
| 406 | ✗ | for (int j = 0; j < s->rdft_vlen[plane]; j++) | |
| 407 | ✗ | vdata[j] *= weight[j]; | |
| 408 | } | ||
| 409 | } | ||
| 410 | |||
| 411 | ✗ | return 0; | |
| 412 | } | ||
| 413 | |||
| 414 | ✗ | static int copy_vertical(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) | |
| 415 | { | ||
| 416 | ✗ | FFTFILTContext *s = ctx->priv; | |
| 417 | |||
| 418 | ✗ | for (int plane = 0; plane < s->nb_planes; plane++) { | |
| 419 | ✗ | const int hlen = s->rdft_hlen[plane]; | |
| 420 | ✗ | const int vlen = s->rdft_vlen[plane]; | |
| 421 | ✗ | const int hstride = s->rdft_hstride[plane]; | |
| 422 | ✗ | const int vstride = s->rdft_vstride[plane]; | |
| 423 | ✗ | const int slice_start = (hlen * jobnr) / nb_jobs; | |
| 424 | ✗ | const int slice_end = (hlen * (jobnr+1)) / nb_jobs; | |
| 425 | ✗ | const int h = s->planeheight[plane]; | |
| 426 | ✗ | float *hdata = s->rdft_hdata_out[plane]; | |
| 427 | ✗ | float *vdata = s->rdft_vdata_in[plane]; | |
| 428 | |||
| 429 | ✗ | for (int i = slice_start; i < slice_end; i++) { | |
| 430 | ✗ | for (int j = 0; j < h; j++) | |
| 431 | ✗ | vdata[i * vstride + j] = hdata[j * hstride + i]; | |
| 432 | ✗ | copy_rev(vdata + i * vstride, h, vlen); | |
| 433 | } | ||
| 434 | } | ||
| 435 | |||
| 436 | ✗ | return 0; | |
| 437 | } | ||
| 438 | |||
| 439 | ✗ | static int rdft_vertical(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) | |
| 440 | { | ||
| 441 | ✗ | FFTFILTContext *s = ctx->priv; | |
| 442 | |||
| 443 | ✗ | for (int plane = 0; plane < s->nb_planes; plane++) { | |
| 444 | ✗ | const int height = s->rdft_hlen[plane]; | |
| 445 | ✗ | const int slice_start = (height * jobnr) / nb_jobs; | |
| 446 | ✗ | const int slice_end = (height * (jobnr+1)) / nb_jobs; | |
| 447 | |||
| 448 | ✗ | for (int i = slice_start; i < slice_end; i++) | |
| 449 | ✗ | s->vtx_fn(s->vrdft[jobnr][plane], | |
| 450 | ✗ | s->rdft_vdata_out[plane] + i * s->rdft_vstride[plane], | |
| 451 | ✗ | s->rdft_vdata_in[plane] + i * s->rdft_vstride[plane], | |
| 452 | sizeof(float)); | ||
| 453 | } | ||
| 454 | |||
| 455 | ✗ | return 0; | |
| 456 | } | ||
| 457 | |||
| 458 | ✗ | static int irdft_vertical(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) | |
| 459 | { | ||
| 460 | ✗ | FFTFILTContext *s = ctx->priv; | |
| 461 | |||
| 462 | ✗ | for (int plane = 0; plane < s->nb_planes; plane++) { | |
| 463 | ✗ | const int height = s->rdft_hlen[plane]; | |
| 464 | ✗ | const int slice_start = (height * jobnr) / nb_jobs; | |
| 465 | ✗ | const int slice_end = (height * (jobnr+1)) / nb_jobs; | |
| 466 | |||
| 467 | ✗ | for (int i = slice_start; i < slice_end; i++) | |
| 468 | ✗ | s->ivtx_fn(s->ivrdft[jobnr][plane], | |
| 469 | ✗ | s->rdft_vdata_in[plane] + i * s->rdft_vstride[plane], | |
| 470 | ✗ | s->rdft_vdata_out[plane] + i * s->rdft_vstride[plane], | |
| 471 | sizeof(AVComplexFloat)); | ||
| 472 | } | ||
| 473 | |||
| 474 | ✗ | return 0; | |
| 475 | } | ||
| 476 | |||
| 477 | ✗ | static int copy_horizontal(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) | |
| 478 | { | ||
| 479 | ✗ | FFTFILTContext *s = ctx->priv; | |
| 480 | |||
| 481 | ✗ | for (int plane = 0; plane < s->nb_planes; plane++) { | |
| 482 | ✗ | const int hlen = s->rdft_hlen[plane]; | |
| 483 | ✗ | const int hstride = s->rdft_hstride[plane]; | |
| 484 | ✗ | const int vstride = s->rdft_vstride[plane]; | |
| 485 | ✗ | const int slice_start = (hlen * jobnr) / nb_jobs; | |
| 486 | ✗ | const int slice_end = (hlen * (jobnr+1)) / nb_jobs; | |
| 487 | ✗ | const int h = s->planeheight[plane]; | |
| 488 | ✗ | float *hdata = s->rdft_hdata_in[plane]; | |
| 489 | ✗ | float *vdata = s->rdft_vdata_in[plane]; | |
| 490 | |||
| 491 | ✗ | for (int i = slice_start; i < slice_end; i++) | |
| 492 | ✗ | for (int j = 0; j < h; j++) | |
| 493 | ✗ | hdata[j * hstride + i] = vdata[i * vstride + j]; | |
| 494 | } | ||
| 495 | |||
| 496 | ✗ | return 0; | |
| 497 | } | ||
| 498 | |||
| 499 | ✗ | static int filter_frame(AVFilterLink *inlink, AVFrame *in) | |
| 500 | { | ||
| 501 | ✗ | AVFilterContext *ctx = inlink->dst; | |
| 502 | ✗ | AVFilterLink *outlink = inlink->dst->outputs[0]; | |
| 503 | ✗ | FFTFILTContext *s = ctx->priv; | |
| 504 | AVFrame *out; | ||
| 505 | |||
| 506 | ✗ | out = ff_get_video_buffer(outlink, inlink->w, inlink->h); | |
| 507 | ✗ | if (!out) { | |
| 508 | ✗ | av_frame_free(&in); | |
| 509 | ✗ | return AVERROR(ENOMEM); | |
| 510 | } | ||
| 511 | |||
| 512 | ✗ | av_frame_copy_props(out, in); | |
| 513 | |||
| 514 | ✗ | ff_filter_execute(ctx, s->rdft_horizontal, in, NULL, | |
| 515 | ✗ | FFMIN(s->planeheight[1], s->nb_threads)); | |
| 516 | |||
| 517 | ✗ | ff_filter_execute(ctx, copy_vertical, NULL, NULL, | |
| 518 | ✗ | FFMIN(s->planeheight[1], s->nb_threads)); | |
| 519 | |||
| 520 | ✗ | ff_filter_execute(ctx, rdft_vertical, NULL, NULL, | |
| 521 | ✗ | FFMIN(s->planeheight[1], s->nb_threads)); | |
| 522 | |||
| 523 | ✗ | for (int plane = 0; plane < s->nb_planes; plane++) { | |
| 524 | ✗ | if (s->eval_mode == EVAL_MODE_FRAME) | |
| 525 | ✗ | do_eval(s, inlink, plane); | |
| 526 | } | ||
| 527 | |||
| 528 | ✗ | ff_filter_execute(ctx, multiply_data, NULL, NULL, | |
| 529 | ✗ | FFMIN(s->planeheight[1], s->nb_threads)); | |
| 530 | |||
| 531 | ✗ | for (int plane = 0; plane < s->nb_planes; plane++) | |
| 532 | ✗ | s->rdft_vdata_out[plane][0] += s->rdft_hlen[plane] * s->rdft_vlen[plane] * s->dc[plane] * (1 << (s->depth - 8)); | |
| 533 | |||
| 534 | ✗ | ff_filter_execute(ctx, irdft_vertical, NULL, NULL, | |
| 535 | ✗ | FFMIN(s->planeheight[1], s->nb_threads)); | |
| 536 | |||
| 537 | ✗ | ff_filter_execute(ctx, copy_horizontal, NULL, NULL, | |
| 538 | ✗ | FFMIN(s->planeheight[1], s->nb_threads)); | |
| 539 | |||
| 540 | ✗ | ff_filter_execute(ctx, s->irdft_horizontal, out, NULL, | |
| 541 | ✗ | FFMIN(s->planeheight[1], s->nb_threads)); | |
| 542 | |||
| 543 | ✗ | av_frame_free(&in); | |
| 544 | ✗ | return ff_filter_frame(outlink, out); | |
| 545 | } | ||
| 546 | |||
| 547 | ✗ | static av_cold void uninit(AVFilterContext *ctx) | |
| 548 | { | ||
| 549 | ✗ | FFTFILTContext *s = ctx->priv; | |
| 550 | |||
| 551 | ✗ | for (int i = 0; i < MAX_PLANES; i++) { | |
| 552 | ✗ | av_freep(&s->rdft_hdata_in[i]); | |
| 553 | ✗ | av_freep(&s->rdft_vdata_in[i]); | |
| 554 | ✗ | av_freep(&s->rdft_hdata_out[i]); | |
| 555 | ✗ | av_freep(&s->rdft_vdata_out[i]); | |
| 556 | ✗ | av_expr_free(s->weight_expr[i]); | |
| 557 | ✗ | av_freep(&s->weight[i]); | |
| 558 | ✗ | for (int j = 0; j < s->nb_threads; j++) { | |
| 559 | ✗ | av_tx_uninit(&s->hrdft[j][i]); | |
| 560 | ✗ | av_tx_uninit(&s->ihrdft[j][i]); | |
| 561 | ✗ | av_tx_uninit(&s->vrdft[j][i]); | |
| 562 | ✗ | av_tx_uninit(&s->ivrdft[j][i]); | |
| 563 | } | ||
| 564 | } | ||
| 565 | ✗ | } | |
| 566 | |||
| 567 | static const enum AVPixelFormat pixel_fmts_fftfilt[] = { | ||
| 568 | AV_PIX_FMT_GRAY8, | ||
| 569 | AV_PIX_FMT_GRAY9, AV_PIX_FMT_GRAY10, AV_PIX_FMT_GRAY12, | ||
| 570 | AV_PIX_FMT_GRAY14, AV_PIX_FMT_GRAY16, | ||
| 571 | AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUVJ444P, | ||
| 572 | AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUVJ420P, | ||
| 573 | AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVJ422P, | ||
| 574 | AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV420P10, | ||
| 575 | AV_PIX_FMT_YUV420P12, AV_PIX_FMT_YUV420P14, | ||
| 576 | AV_PIX_FMT_YUV420P16, | ||
| 577 | AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV422P10, | ||
| 578 | AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV422P14, | ||
| 579 | AV_PIX_FMT_YUV422P16, | ||
| 580 | AV_PIX_FMT_YUV444P9, AV_PIX_FMT_YUV444P10, | ||
| 581 | AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV444P14, | ||
| 582 | AV_PIX_FMT_YUV444P16, | ||
| 583 | AV_PIX_FMT_NONE | ||
| 584 | }; | ||
| 585 | |||
| 586 | static const AVFilterPad fftfilt_inputs[] = { | ||
| 587 | { | ||
| 588 | .name = "default", | ||
| 589 | .type = AVMEDIA_TYPE_VIDEO, | ||
| 590 | .config_props = config_props, | ||
| 591 | .filter_frame = filter_frame, | ||
| 592 | }, | ||
| 593 | }; | ||
| 594 | |||
| 595 | const FFFilter ff_vf_fftfilt = { | ||
| 596 | .p.name = "fftfilt", | ||
| 597 | .p.description = NULL_IF_CONFIG_SMALL("Apply arbitrary expressions to pixels in frequency domain."), | ||
| 598 | .p.priv_class = &fftfilt_class, | ||
| 599 | .p.flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, | ||
| 600 | .priv_size = sizeof(FFTFILTContext), | ||
| 601 | FILTER_INPUTS(fftfilt_inputs), | ||
| 602 | FILTER_OUTPUTS(ff_video_default_filterpad), | ||
| 603 | FILTER_PIXFMTS_ARRAY(pixel_fmts_fftfilt), | ||
| 604 | .init = initialize, | ||
| 605 | .uninit = uninit, | ||
| 606 | }; | ||
| 607 |