| Line | Branch | Exec | Source |
|---|---|---|---|
| 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/eval.h" | ||
| 24 | #include "libavutil/mem.h" | ||
| 25 | #include "libavutil/opt.h" | ||
| 26 | #include "libavutil/pixfmt.h" | ||
| 27 | #include "avfilter.h" | ||
| 28 | #include "filters.h" | ||
| 29 | #include "framesync.h" | ||
| 30 | #include "vf_blend_init.h" | ||
| 31 | #include "video.h" | ||
| 32 | #include "blend.h" | ||
| 33 | |||
| 34 | #define TOP 0 | ||
| 35 | #define BOTTOM 1 | ||
| 36 | |||
| 37 | typedef struct BlendContext { | ||
| 38 | const AVClass *class; | ||
| 39 | FFFrameSync fs; | ||
| 40 | int hsub, vsub; ///< chroma subsampling values | ||
| 41 | int nb_planes; | ||
| 42 | char *all_expr; | ||
| 43 | /* enum BlendMode */ | ||
| 44 | int all_mode; | ||
| 45 | double all_opacity; | ||
| 46 | |||
| 47 | int depth; | ||
| 48 | FilterParams params[4]; | ||
| 49 | int tblend; | ||
| 50 | AVFrame *prev_frame; /* only used with tblend */ | ||
| 51 | int nb_threads; | ||
| 52 | } BlendContext; | ||
| 53 | |||
| 54 | static const char *const var_names[] = { "X", "Y", "W", "H", "SW", "SH", "T", "N", "A", "B", "TOP", "BOTTOM", NULL }; | ||
| 55 | enum { VAR_X, VAR_Y, VAR_W, VAR_H, VAR_SW, VAR_SH, VAR_T, VAR_N, VAR_A, VAR_B, VAR_TOP, VAR_BOTTOM, VAR_VARS_NB }; | ||
| 56 | |||
| 57 | typedef struct ThreadData { | ||
| 58 | const AVFrame *top, *bottom; | ||
| 59 | AVFrame *dst; | ||
| 60 | AVFilterLink *inlink; | ||
| 61 | int plane; | ||
| 62 | int w, h; | ||
| 63 | FilterParams *param; | ||
| 64 | } ThreadData; | ||
| 65 | |||
| 66 | #define OFFSET(x) offsetof(BlendContext, x) | ||
| 67 | #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM | ||
| 68 | |||
| 69 | static const AVOption blend_options[] = { | ||
| 70 | { "c0_mode", "set component #0 blend mode", OFFSET(params[0].mode), AV_OPT_TYPE_INT, {.i64=0}, 0, BLEND_NB-1, FLAGS, .unit = "mode" }, | ||
| 71 | { "c1_mode", "set component #1 blend mode", OFFSET(params[1].mode), AV_OPT_TYPE_INT, {.i64=0}, 0, BLEND_NB-1, FLAGS, .unit = "mode" }, | ||
| 72 | { "c2_mode", "set component #2 blend mode", OFFSET(params[2].mode), AV_OPT_TYPE_INT, {.i64=0}, 0, BLEND_NB-1, FLAGS, .unit = "mode" }, | ||
| 73 | { "c3_mode", "set component #3 blend mode", OFFSET(params[3].mode), AV_OPT_TYPE_INT, {.i64=0}, 0, BLEND_NB-1, FLAGS, .unit = "mode" }, | ||
| 74 | { "all_mode", "set blend mode for all components", OFFSET(all_mode), AV_OPT_TYPE_INT, {.i64=-1},-1, BLEND_NB-1, FLAGS, .unit = "mode" }, | ||
| 75 | { "addition", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_ADDITION}, 0, 0, FLAGS, .unit = "mode" }, | ||
| 76 | { "addition128","", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_GRAINMERGE}, 0, 0, FLAGS, .unit = "mode" }, | ||
| 77 | { "grainmerge", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_GRAINMERGE}, 0, 0, FLAGS, .unit = "mode" }, | ||
| 78 | { "and", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_AND}, 0, 0, FLAGS, .unit = "mode" }, | ||
| 79 | { "average", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_AVERAGE}, 0, 0, FLAGS, .unit = "mode" }, | ||
| 80 | { "burn", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_BURN}, 0, 0, FLAGS, .unit = "mode" }, | ||
| 81 | { "darken", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_DARKEN}, 0, 0, FLAGS, .unit = "mode" }, | ||
| 82 | { "difference", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_DIFFERENCE}, 0, 0, FLAGS, .unit = "mode" }, | ||
| 83 | { "difference128", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_GRAINEXTRACT}, 0, 0, FLAGS, .unit = "mode" }, | ||
| 84 | { "grainextract", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_GRAINEXTRACT}, 0, 0, FLAGS, .unit = "mode" }, | ||
| 85 | { "divide", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_DIVIDE}, 0, 0, FLAGS, .unit = "mode" }, | ||
| 86 | { "dodge", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_DODGE}, 0, 0, FLAGS, .unit = "mode" }, | ||
| 87 | { "exclusion", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_EXCLUSION}, 0, 0, FLAGS, .unit = "mode" }, | ||
| 88 | { "extremity", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_EXTREMITY}, 0, 0, FLAGS, .unit = "mode" }, | ||
| 89 | { "freeze", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_FREEZE}, 0, 0, FLAGS, .unit = "mode" }, | ||
| 90 | { "glow", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_GLOW}, 0, 0, FLAGS, .unit = "mode" }, | ||
| 91 | { "hardlight", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_HARDLIGHT}, 0, 0, FLAGS, .unit = "mode" }, | ||
| 92 | { "hardmix", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_HARDMIX}, 0, 0, FLAGS, .unit = "mode" }, | ||
| 93 | { "heat", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_HEAT}, 0, 0, FLAGS, .unit = "mode" }, | ||
| 94 | { "lighten", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_LIGHTEN}, 0, 0, FLAGS, .unit = "mode" }, | ||
| 95 | { "linearlight","", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_LINEARLIGHT},0, 0, FLAGS, .unit = "mode" }, | ||
| 96 | { "multiply", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_MULTIPLY}, 0, 0, FLAGS, .unit = "mode" }, | ||
| 97 | { "multiply128","", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_MULTIPLY128},0, 0, FLAGS, .unit = "mode" }, | ||
| 98 | { "negation", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_NEGATION}, 0, 0, FLAGS, .unit = "mode" }, | ||
| 99 | { "normal", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_NORMAL}, 0, 0, FLAGS, .unit = "mode" }, | ||
| 100 | { "or", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_OR}, 0, 0, FLAGS, .unit = "mode" }, | ||
| 101 | { "overlay", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_OVERLAY}, 0, 0, FLAGS, .unit = "mode" }, | ||
| 102 | { "phoenix", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_PHOENIX}, 0, 0, FLAGS, .unit = "mode" }, | ||
| 103 | { "pinlight", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_PINLIGHT}, 0, 0, FLAGS, .unit = "mode" }, | ||
| 104 | { "reflect", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_REFLECT}, 0, 0, FLAGS, .unit = "mode" }, | ||
| 105 | { "screen", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_SCREEN}, 0, 0, FLAGS, .unit = "mode" }, | ||
| 106 | { "softlight", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_SOFTLIGHT}, 0, 0, FLAGS, .unit = "mode" }, | ||
| 107 | { "subtract", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_SUBTRACT}, 0, 0, FLAGS, .unit = "mode" }, | ||
| 108 | { "vividlight", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_VIVIDLIGHT}, 0, 0, FLAGS, .unit = "mode" }, | ||
| 109 | { "xor", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_XOR}, 0, 0, FLAGS, .unit = "mode" }, | ||
| 110 | { "softdifference","", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_SOFTDIFFERENCE}, 0, 0, FLAGS, .unit = "mode" }, | ||
| 111 | { "geometric", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_GEOMETRIC}, 0, 0, FLAGS, .unit = "mode" }, | ||
| 112 | { "harmonic", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_HARMONIC}, 0, 0, FLAGS, .unit = "mode" }, | ||
| 113 | { "bleach", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_BLEACH}, 0, 0, FLAGS, .unit = "mode" }, | ||
| 114 | { "stain", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_STAIN}, 0, 0, FLAGS, .unit = "mode" }, | ||
| 115 | { "interpolate","", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_INTERPOLATE},0, 0, FLAGS, .unit = "mode" }, | ||
| 116 | { "hardoverlay","", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_HARDOVERLAY},0, 0, FLAGS, .unit = "mode" }, | ||
| 117 | { "c0_expr", "set color component #0 expression", OFFSET(params[0].expr_str), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS }, | ||
| 118 | { "c1_expr", "set color component #1 expression", OFFSET(params[1].expr_str), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS }, | ||
| 119 | { "c2_expr", "set color component #2 expression", OFFSET(params[2].expr_str), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS }, | ||
| 120 | { "c3_expr", "set color component #3 expression", OFFSET(params[3].expr_str), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS }, | ||
| 121 | { "all_expr", "set expression for all color components", OFFSET(all_expr), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS }, | ||
| 122 | { "c0_opacity", "set color component #0 opacity", OFFSET(params[0].opacity), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS }, | ||
| 123 | { "c1_opacity", "set color component #1 opacity", OFFSET(params[1].opacity), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS }, | ||
| 124 | { "c2_opacity", "set color component #2 opacity", OFFSET(params[2].opacity), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS }, | ||
| 125 | { "c3_opacity", "set color component #3 opacity", OFFSET(params[3].opacity), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS }, | ||
| 126 | { "all_opacity", "set opacity for all color components", OFFSET(all_opacity), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS }, | ||
| 127 | { NULL } | ||
| 128 | }; | ||
| 129 | |||
| 130 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
|
12 | FRAMESYNC_DEFINE_CLASS(blend, BlendContext, fs); |
| 131 | |||
| 132 | #define DEFINE_BLEND_EXPR(type, name, div, clip) \ | ||
| 133 | static void blend_expr_## name(const uint8_t *_top, ptrdiff_t top_linesize, \ | ||
| 134 | const uint8_t *_bottom, ptrdiff_t bottom_linesize, \ | ||
| 135 | uint8_t *_dst, ptrdiff_t dst_linesize, \ | ||
| 136 | ptrdiff_t width, ptrdiff_t height, \ | ||
| 137 | FilterParams *param, SliceParams *sliceparam) \ | ||
| 138 | { \ | ||
| 139 | const type *top = (const type*)_top; \ | ||
| 140 | const type *bottom = (const type*)_bottom; \ | ||
| 141 | double *values = sliceparam->values; \ | ||
| 142 | int starty = sliceparam->starty; \ | ||
| 143 | type *dst = (type*)_dst; \ | ||
| 144 | AVExpr *e = sliceparam->e; \ | ||
| 145 | int y, x; \ | ||
| 146 | dst_linesize /= div; \ | ||
| 147 | top_linesize /= div; \ | ||
| 148 | bottom_linesize /= div; \ | ||
| 149 | \ | ||
| 150 | for (y = 0; y < height; y++) { \ | ||
| 151 | values[VAR_Y] = y + starty; \ | ||
| 152 | for (x = 0; x < width; x++) { \ | ||
| 153 | values[VAR_X] = x; \ | ||
| 154 | values[VAR_TOP] = values[VAR_A] = top[x]; \ | ||
| 155 | values[VAR_BOTTOM] = values[VAR_B] = bottom[x]; \ | ||
| 156 | double value = av_expr_eval(e, values, NULL); \ | ||
| 157 | dst[x] = clip ? av_clipd(value, 0, param->max_value) : value; \ | ||
| 158 | } \ | ||
| 159 | dst += dst_linesize; \ | ||
| 160 | top += top_linesize; \ | ||
| 161 | bottom += bottom_linesize; \ | ||
| 162 | } \ | ||
| 163 | } | ||
| 164 | |||
| 165 | ✗ | DEFINE_BLEND_EXPR(uint8_t, 8bit, 1, 1) | |
| 166 |
4/4✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 1 times.
|
3 | DEFINE_BLEND_EXPR(uint16_t, 16bit, 2, 1) |
| 167 | ✗ | DEFINE_BLEND_EXPR(float, 32bit, 4, 0) | |
| 168 | |||
| 169 | 1324 | static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) | |
| 170 | { | ||
| 171 | 1324 | ThreadData *td = arg; | |
| 172 | 1324 | int slice_start = ff_slice_pos(td->h, jobnr, nb_jobs); | |
| 173 | 1324 | int slice_end = ff_slice_pos(td->h, jobnr + 1, nb_jobs); | |
| 174 | 1324 | int height = slice_end - slice_start; | |
| 175 | 1324 | const uint8_t *top = td->top->data[td->plane]; | |
| 176 | 1324 | const uint8_t *bottom = td->bottom->data[td->plane]; | |
| 177 | 1324 | uint8_t *dst = td->dst->data[td->plane]; | |
| 178 | 1324 | FilterLink *inl = ff_filter_link(td->inlink); | |
| 179 | double values[VAR_VARS_NB]; | ||
| 180 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1323 times.
|
1324 | SliceParams sliceparam = {.values = &values[0], .starty = slice_start, .e = td->param->e ? td->param->e[jobnr] : NULL}; |
| 181 | |||
| 182 | 1324 | values[VAR_N] = inl->frame_count_out; | |
| 183 |
1/2✓ Branch 0 taken 1324 times.
✗ Branch 1 not taken.
|
1324 | values[VAR_T] = td->dst->pts == AV_NOPTS_VALUE ? NAN : td->dst->pts * av_q2d(td->inlink->time_base); |
| 184 | 1324 | values[VAR_W] = td->w; | |
| 185 | 1324 | values[VAR_H] = td->h; | |
| 186 | 1324 | values[VAR_SW] = td->w / (double)td->dst->width; | |
| 187 | 1324 | values[VAR_SH] = td->h / (double)td->dst->height; | |
| 188 | |||
| 189 | 1324 | td->param->blend(top + slice_start * td->top->linesize[td->plane], | |
| 190 | 1324 | td->top->linesize[td->plane], | |
| 191 | 1324 | bottom + slice_start * td->bottom->linesize[td->plane], | |
| 192 | 1324 | td->bottom->linesize[td->plane], | |
| 193 | 1324 | dst + slice_start * td->dst->linesize[td->plane], | |
| 194 | 1324 | td->dst->linesize[td->plane], | |
| 195 | 1324 | td->w, height, td->param, &sliceparam); | |
| 196 | 1324 | return 0; | |
| 197 | } | ||
| 198 | |||
| 199 | 50 | static AVFrame *blend_frame(AVFilterContext *ctx, AVFrame *top_buf, | |
| 200 | const AVFrame *bottom_buf) | ||
| 201 | { | ||
| 202 | 50 | BlendContext *s = ctx->priv; | |
| 203 | 50 | AVFilterLink *inlink = ctx->inputs[0]; | |
| 204 | 50 | AVFilterLink *outlink = ctx->outputs[0]; | |
| 205 | AVFrame *dst_buf; | ||
| 206 | int plane; | ||
| 207 | |||
| 208 | 50 | dst_buf = ff_get_video_buffer(outlink, outlink->w, outlink->h); | |
| 209 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 50 times.
|
50 | if (!dst_buf) |
| 210 | ✗ | return top_buf; | |
| 211 | |||
| 212 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 50 times.
|
50 | if (av_frame_copy_props(dst_buf, top_buf) < 0) { |
| 213 | ✗ | av_frame_free(&dst_buf); | |
| 214 | ✗ | return top_buf; | |
| 215 | } | ||
| 216 | |||
| 217 |
2/2✓ Branch 0 taken 148 times.
✓ Branch 1 taken 50 times.
|
198 | for (plane = 0; plane < s->nb_planes; plane++) { |
| 218 |
4/4✓ Branch 0 taken 99 times.
✓ Branch 1 taken 49 times.
✓ Branch 2 taken 49 times.
✓ Branch 3 taken 50 times.
|
148 | int hsub = plane == 1 || plane == 2 ? s->hsub : 0; |
| 219 |
4/4✓ Branch 0 taken 99 times.
✓ Branch 1 taken 49 times.
✓ Branch 2 taken 49 times.
✓ Branch 3 taken 50 times.
|
148 | int vsub = plane == 1 || plane == 2 ? s->vsub : 0; |
| 220 | 148 | int outw = AV_CEIL_RSHIFT(dst_buf->width, hsub); | |
| 221 | 148 | int outh = AV_CEIL_RSHIFT(dst_buf->height, vsub); | |
| 222 | 148 | FilterParams *param = &s->params[plane]; | |
| 223 | 148 | ThreadData td = { .top = top_buf, .bottom = bottom_buf, .dst = dst_buf, | |
| 224 | .w = outw, .h = outh, .param = param, .plane = plane, | ||
| 225 | .inlink = inlink }; | ||
| 226 | |||
| 227 | 148 | ff_filter_execute(ctx, filter_slice, &td, NULL, | |
| 228 | 148 | FFMIN(outh, s->nb_threads)); | |
| 229 | } | ||
| 230 | |||
| 231 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 49 times.
|
50 | if (!s->tblend) |
| 232 | 1 | av_frame_free(&top_buf); | |
| 233 | |||
| 234 | 50 | return dst_buf; | |
| 235 | } | ||
| 236 | |||
| 237 | 1 | static int blend_frame_for_dualinput(FFFrameSync *fs) | |
| 238 | { | ||
| 239 | 1 | AVFilterContext *ctx = fs->parent; | |
| 240 | AVFrame *top_buf, *bottom_buf, *dst_buf; | ||
| 241 | int ret; | ||
| 242 | |||
| 243 | 1 | ret = ff_framesync_dualinput_get(fs, &top_buf, &bottom_buf); | |
| 244 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (ret < 0) |
| 245 | ✗ | return ret; | |
| 246 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (!bottom_buf) |
| 247 | ✗ | return ff_filter_frame(ctx->outputs[0], top_buf); | |
| 248 | 1 | dst_buf = blend_frame(ctx, top_buf, bottom_buf); | |
| 249 | 1 | return ff_filter_frame(ctx->outputs[0], dst_buf); | |
| 250 | } | ||
| 251 | |||
| 252 | 4 | static av_cold int init(AVFilterContext *ctx) | |
| 253 | { | ||
| 254 | 4 | BlendContext *s = ctx->priv; | |
| 255 | |||
| 256 | 4 | s->tblend = !strcmp(ctx->filter->name, "tblend"); | |
| 257 | 4 | s->nb_threads = ff_filter_get_nb_threads(ctx); | |
| 258 | |||
| 259 | 4 | s->fs.on_event = blend_frame_for_dualinput; | |
| 260 | 4 | return 0; | |
| 261 | } | ||
| 262 | |||
| 263 | static const enum AVPixelFormat pix_fmts[] = { | ||
| 264 | AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA420P, | ||
| 265 | AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P, AV_PIX_FMT_YUVJ422P,AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ411P, | ||
| 266 | AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV410P, | ||
| 267 | AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRAP, AV_PIX_FMT_GRAY8, | ||
| 268 | AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9, | ||
| 269 | AV_PIX_FMT_YUVA420P9, AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA444P9, AV_PIX_FMT_GBRP9, AV_PIX_FMT_GRAY9, | ||
| 270 | AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUV440P10, | ||
| 271 | AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA444P10, | ||
| 272 | AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GRAY10, | ||
| 273 | AV_PIX_FMT_YUV420P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV440P12, | ||
| 274 | AV_PIX_FMT_YUVA422P12, AV_PIX_FMT_YUVA444P12, | ||
| 275 | AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GRAY12, | ||
| 276 | AV_PIX_FMT_YUV420P14, AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV444P14, AV_PIX_FMT_GBRP14, | ||
| 277 | AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16, | ||
| 278 | AV_PIX_FMT_YUVA420P16, AV_PIX_FMT_YUVA422P16, AV_PIX_FMT_YUVA444P16, | ||
| 279 | AV_PIX_FMT_GBRP16, AV_PIX_FMT_GBRAP16, AV_PIX_FMT_GRAY16, | ||
| 280 | AV_PIX_FMT_GBRPF32, AV_PIX_FMT_GBRAPF32, AV_PIX_FMT_GRAYF32, | ||
| 281 | AV_PIX_FMT_NONE | ||
| 282 | }; | ||
| 283 | |||
| 284 | 4 | static av_cold void uninit(AVFilterContext *ctx) | |
| 285 | { | ||
| 286 | 4 | BlendContext *s = ctx->priv; | |
| 287 | int i; | ||
| 288 | |||
| 289 | 4 | ff_framesync_uninit(&s->fs); | |
| 290 | 4 | av_frame_free(&s->prev_frame); | |
| 291 | |||
| 292 |
2/2✓ Branch 0 taken 16 times.
✓ Branch 1 taken 4 times.
|
20 | for (i = 0; i < FF_ARRAY_ELEMS(s->params); i++) { |
| 293 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 15 times.
|
16 | if (s->params[i].e) { |
| 294 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 1 times.
|
10 | for (int j = 0; j < s->nb_threads; j++) |
| 295 | 9 | av_expr_free(s->params[i].e[j]); | |
| 296 | 1 | av_freep(&s->params[i].e); | |
| 297 | } | ||
| 298 | } | ||
| 299 | |||
| 300 | 4 | } | |
| 301 | |||
| 302 | 2 | static int config_params(AVFilterContext *ctx) | |
| 303 | { | ||
| 304 | 2 | BlendContext *s = ctx->priv; | |
| 305 | int ret; | ||
| 306 | |||
| 307 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 2 times.
|
10 | for (int plane = 0; plane < FF_ARRAY_ELEMS(s->params); plane++) { |
| 308 | 8 | FilterParams *param = &s->params[plane]; | |
| 309 | |||
| 310 | 8 | param->max_value = UINT32_MAX >> (32 - s->depth); | |
| 311 | |||
| 312 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
|
8 | if (s->all_mode >= 0) |
| 313 | 4 | param->mode = s->all_mode; | |
| 314 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
|
8 | if (s->all_opacity < 1) |
| 315 | ✗ | param->opacity = s->all_opacity; | |
| 316 | |||
| 317 | 8 | ff_blend_init(param, s->depth); | |
| 318 | |||
| 319 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
8 | if (s->all_expr && !param->expr_str) { |
| 320 | ✗ | param->expr_str = av_strdup(s->all_expr); | |
| 321 | ✗ | if (!param->expr_str) | |
| 322 | ✗ | return AVERROR(ENOMEM); | |
| 323 | } | ||
| 324 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 7 times.
|
8 | if (param->expr_str) { |
| 325 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (!param->e) { |
| 326 | 1 | param->e = av_calloc(s->nb_threads, sizeof(*param->e)); | |
| 327 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (!param->e) |
| 328 | ✗ | return AVERROR(ENOMEM); | |
| 329 | } | ||
| 330 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 1 times.
|
10 | for (int i = 0; i < s->nb_threads; i++) { |
| 331 | 9 | av_expr_free(param->e[i]); | |
| 332 | 9 | param->e[i] = NULL; | |
| 333 | 9 | ret = av_expr_parse(¶m->e[i], param->expr_str, var_names, | |
| 334 | NULL, NULL, NULL, NULL, 0, ctx); | ||
| 335 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
|
9 | if (ret < 0) |
| 336 | ✗ | return ret; | |
| 337 | } | ||
| 338 |
2/4✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
|
1 | param->blend = s->depth > 8 ? s->depth > 16 ? blend_expr_32bit : blend_expr_16bit : blend_expr_8bit; |
| 339 | } | ||
| 340 | } | ||
| 341 | |||
| 342 | 2 | return 0; | |
| 343 | } | ||
| 344 | |||
| 345 | 2 | static int config_output(AVFilterLink *outlink) | |
| 346 | { | ||
| 347 | 2 | FilterLink *outl = ff_filter_link(outlink); | |
| 348 | 2 | AVFilterContext *ctx = outlink->src; | |
| 349 | 2 | AVFilterLink *toplink = ctx->inputs[TOP]; | |
| 350 | 2 | FilterLink *tl = ff_filter_link(toplink); | |
| 351 | 2 | BlendContext *s = ctx->priv; | |
| 352 | 2 | const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(toplink->format); | |
| 353 | int ret; | ||
| 354 | |||
| 355 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
|
2 | if (!s->tblend) { |
| 356 | 1 | AVFilterLink *bottomlink = ctx->inputs[BOTTOM]; | |
| 357 | |||
| 358 |
2/4✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
|
1 | if (toplink->w != bottomlink->w || toplink->h != bottomlink->h) { |
| 359 | ✗ | av_log(ctx, AV_LOG_ERROR, "First input link %s parameters " | |
| 360 | "(size %dx%d) do not match the corresponding " | ||
| 361 | "second input link %s parameters (size %dx%d)\n", | ||
| 362 | ✗ | ctx->input_pads[TOP].name, toplink->w, toplink->h, | |
| 363 | ✗ | ctx->input_pads[BOTTOM].name, bottomlink->w, bottomlink->h); | |
| 364 | ✗ | return AVERROR(EINVAL); | |
| 365 | } | ||
| 366 | } | ||
| 367 | |||
| 368 | 2 | outlink->w = toplink->w; | |
| 369 | 2 | outlink->h = toplink->h; | |
| 370 | 2 | outlink->time_base = toplink->time_base; | |
| 371 | 2 | outlink->sample_aspect_ratio = toplink->sample_aspect_ratio; | |
| 372 | 2 | outl->frame_rate = tl->frame_rate; | |
| 373 | |||
| 374 | 2 | s->hsub = pix_desc->log2_chroma_w; | |
| 375 | 2 | s->vsub = pix_desc->log2_chroma_h; | |
| 376 | |||
| 377 | 2 | s->depth = pix_desc->comp[0].depth; | |
| 378 | 2 | s->nb_planes = av_pix_fmt_count_planes(toplink->format); | |
| 379 | |||
| 380 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
|
2 | if (!s->tblend) |
| 381 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
|
1 | if ((ret = ff_framesync_init_dualinput(&s->fs, ctx)) < 0) |
| 382 | ✗ | return ret; | |
| 383 | |||
| 384 | 2 | ret = config_params(ctx); | |
| 385 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if (ret < 0) |
| 386 | ✗ | return ret; | |
| 387 | |||
| 388 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
|
2 | if (s->tblend) |
| 389 | 1 | return 0; | |
| 390 | |||
| 391 | 1 | ret = ff_framesync_configure(&s->fs); | |
| 392 | 1 | outlink->time_base = s->fs.time_base; | |
| 393 | |||
| 394 | 1 | return ret; | |
| 395 | } | ||
| 396 | |||
| 397 | ✗ | static int process_command(AVFilterContext *ctx, const char *cmd, const char *args, | |
| 398 | char *res, int res_len, int flags) | ||
| 399 | { | ||
| 400 | int ret; | ||
| 401 | |||
| 402 | ✗ | ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags); | |
| 403 | ✗ | if (ret < 0) | |
| 404 | ✗ | return ret; | |
| 405 | |||
| 406 | ✗ | return config_params(ctx); | |
| 407 | } | ||
| 408 | |||
| 409 | static const AVFilterPad blend_outputs[] = { | ||
| 410 | { | ||
| 411 | .name = "default", | ||
| 412 | .type = AVMEDIA_TYPE_VIDEO, | ||
| 413 | .config_props = config_output, | ||
| 414 | }, | ||
| 415 | }; | ||
| 416 | |||
| 417 | #if CONFIG_BLEND_FILTER | ||
| 418 | |||
| 419 | 4 | static int activate(AVFilterContext *ctx) | |
| 420 | { | ||
| 421 | 4 | BlendContext *s = ctx->priv; | |
| 422 | 4 | return ff_framesync_activate(&s->fs); | |
| 423 | } | ||
| 424 | |||
| 425 | static const AVFilterPad blend_inputs[] = { | ||
| 426 | { | ||
| 427 | .name = "top", | ||
| 428 | .type = AVMEDIA_TYPE_VIDEO, | ||
| 429 | },{ | ||
| 430 | .name = "bottom", | ||
| 431 | .type = AVMEDIA_TYPE_VIDEO, | ||
| 432 | }, | ||
| 433 | }; | ||
| 434 | |||
| 435 | const FFFilter ff_vf_blend = { | ||
| 436 | .p.name = "blend", | ||
| 437 | .p.description = NULL_IF_CONFIG_SMALL("Blend two video frames into each other."), | ||
| 438 | .p.priv_class = &blend_class, | ||
| 439 | .p.flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS, | ||
| 440 | .preinit = blend_framesync_preinit, | ||
| 441 | .init = init, | ||
| 442 | .uninit = uninit, | ||
| 443 | .priv_size = sizeof(BlendContext), | ||
| 444 | .activate = activate, | ||
| 445 | FILTER_INPUTS(blend_inputs), | ||
| 446 | FILTER_OUTPUTS(blend_outputs), | ||
| 447 | FILTER_PIXFMTS_ARRAY(pix_fmts), | ||
| 448 | .process_command = process_command, | ||
| 449 | }; | ||
| 450 | |||
| 451 | #endif | ||
| 452 | |||
| 453 | #if CONFIG_TBLEND_FILTER | ||
| 454 | |||
| 455 | 50 | static int tblend_filter_frame(AVFilterLink *inlink, AVFrame *frame) | |
| 456 | { | ||
| 457 | 50 | AVFilterContext *ctx = inlink->dst; | |
| 458 | 50 | BlendContext *s = ctx->priv; | |
| 459 | 50 | AVFilterLink *outlink = ctx->outputs[0]; | |
| 460 | |||
| 461 |
2/2✓ Branch 0 taken 49 times.
✓ Branch 1 taken 1 times.
|
50 | if (s->prev_frame) { |
| 462 | AVFrame *out; | ||
| 463 | |||
| 464 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 49 times.
|
49 | if (ctx->is_disabled) |
| 465 | ✗ | out = av_frame_clone(frame); | |
| 466 | else | ||
| 467 | 49 | out = blend_frame(ctx, frame, s->prev_frame); | |
| 468 | 49 | av_frame_free(&s->prev_frame); | |
| 469 | 49 | s->prev_frame = frame; | |
| 470 | 49 | return ff_filter_frame(outlink, out); | |
| 471 | } | ||
| 472 | 1 | s->prev_frame = frame; | |
| 473 | 1 | return 0; | |
| 474 | } | ||
| 475 | |||
| 476 | AVFILTER_DEFINE_CLASS_EXT(tblend, "tblend", blend_options); | ||
| 477 | |||
| 478 | static const AVFilterPad tblend_inputs[] = { | ||
| 479 | { | ||
| 480 | .name = "default", | ||
| 481 | .type = AVMEDIA_TYPE_VIDEO, | ||
| 482 | .filter_frame = tblend_filter_frame, | ||
| 483 | }, | ||
| 484 | }; | ||
| 485 | |||
| 486 | const FFFilter ff_vf_tblend = { | ||
| 487 | .p.name = "tblend", | ||
| 488 | .p.description = NULL_IF_CONFIG_SMALL("Blend successive frames."), | ||
| 489 | .p.priv_class = &tblend_class, | ||
| 490 | .p.flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS, | ||
| 491 | .priv_size = sizeof(BlendContext), | ||
| 492 | .init = init, | ||
| 493 | .uninit = uninit, | ||
| 494 | FILTER_INPUTS(tblend_inputs), | ||
| 495 | FILTER_OUTPUTS(blend_outputs), | ||
| 496 | FILTER_PIXFMTS_ARRAY(pix_fmts), | ||
| 497 | .process_command = process_command, | ||
| 498 | }; | ||
| 499 | |||
| 500 | #endif | ||
| 501 |