| 1 | /* |
| 2 | * Copyright (c) 2010 Stefano Sabatini |
| 3 | * Copyright (c) 2010 Baptiste Coudurier |
| 4 | * Copyright (c) 2007 Bobby Bingham |
| 5 | * |
| 6 | * This file is part of FFmpeg. |
| 7 | * |
| 8 | * FFmpeg is free software; you can redistribute it and/or |
| 9 | * modify it under the terms of the GNU Lesser General Public |
| 10 | * License as published by the Free Software Foundation; either |
| 11 | * version 2.1 of the License, or (at your option) any later version. |
| 12 | * |
| 13 | * FFmpeg is distributed in the hope that it will be useful, |
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 16 | * Lesser General Public License for more details. |
| 17 | * |
| 18 | * You should have received a copy of the GNU Lesser General Public |
| 19 | * License along with FFmpeg; if not, write to the Free Software |
| 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| 21 | */ |
| 22 | |
| 23 | /** |
| 24 | * @file |
| 25 | * overlay one video on top of another |
| 26 | */ |
| 27 | |
| 28 | #include "avfilter.h" |
| 29 | #include "formats.h" |
| 30 | #include "libavutil/common.h" |
| 31 | #include "libavutil/eval.h" |
| 32 | #include "libavutil/avstring.h" |
| 33 | #include "libavutil/pixdesc.h" |
| 34 | #include "libavutil/imgutils.h" |
| 35 | #include "libavutil/mathematics.h" |
| 36 | #include "libavutil/opt.h" |
| 37 | #include "libavutil/timestamp.h" |
| 38 | #include "internal.h" |
| 39 | #include "dualinput.h" |
| 40 | #include "drawutils.h" |
| 41 | #include "video.h" |
| 42 | |
| 43 | static const char *const var_names[] = { |
| 44 | "main_w", "W", ///< width of the main video |
| 45 | "main_h", "H", ///< height of the main video |
| 46 | "overlay_w", "w", ///< width of the overlay video |
| 47 | "overlay_h", "h", ///< height of the overlay video |
| 48 | "hsub", |
| 49 | "vsub", |
| 50 | "x", |
| 51 | "y", |
| 52 | "n", ///< number of frame |
| 53 | "pos", ///< position in the file |
| 54 | "t", ///< timestamp expressed in seconds |
| 55 | NULL |
| 56 | }; |
| 57 | |
| 58 | enum var_name { |
| 59 | VAR_MAIN_W, VAR_MW, |
| 60 | VAR_MAIN_H, VAR_MH, |
| 61 | VAR_OVERLAY_W, VAR_OW, |
| 62 | VAR_OVERLAY_H, VAR_OH, |
| 63 | VAR_HSUB, |
| 64 | VAR_VSUB, |
| 65 | VAR_X, |
| 66 | VAR_Y, |
| 67 | VAR_N, |
| 68 | VAR_POS, |
| 69 | VAR_T, |
| 70 | VAR_VARS_NB |
| 71 | }; |
| 72 | |
| 73 | enum EOFAction { |
| 74 | EOF_ACTION_REPEAT, |
| 75 | EOF_ACTION_ENDALL, |
| 76 | EOF_ACTION_PASS |
| 77 | }; |
| 78 | |
| 79 | static const char * const eof_action_str[] = { |
| 80 | "repeat", "endall", "pass" |
| 81 | }; |
| 82 | |
| 83 | #define MAIN 0 |
| 84 | #define OVERLAY 1 |
| 85 | |
| 86 | #define R 0 |
| 87 | #define G 1 |
| 88 | #define B 2 |
| 89 | #define A 3 |
| 90 | |
| 91 | #define Y 0 |
| 92 | #define U 1 |
| 93 | #define V 2 |
| 94 | |
| 95 | typedef struct OverlayContext { |
| 96 | const AVClass *class; |
| 97 | int x, y; ///< position of overlayed picture |
| 98 | |
| 99 | int allow_packed_rgb; |
| 100 | uint8_t main_is_packed_rgb; |
| 101 | uint8_t main_rgba_map[4]; |
| 102 | uint8_t main_has_alpha; |
| 103 | uint8_t overlay_is_packed_rgb; |
| 104 | uint8_t overlay_rgba_map[4]; |
| 105 | uint8_t overlay_has_alpha; |
| 106 | enum OverlayFormat { OVERLAY_FORMAT_YUV420, OVERLAY_FORMAT_YUV422, OVERLAY_FORMAT_YUV444, OVERLAY_FORMAT_RGB, OVERLAY_FORMAT_NB} format; |
| 107 | enum EvalMode { EVAL_MODE_INIT, EVAL_MODE_FRAME, EVAL_MODE_NB } eval_mode; |
| 108 | |
| 109 | FFDualInputContext dinput; |
| 110 | |
| 111 | int main_pix_step[4]; ///< steps per pixel for each plane of the main output |
| 112 | int overlay_pix_step[4]; ///< steps per pixel for each plane of the overlay |
| 113 | int hsub, vsub; ///< chroma subsampling values |
| 114 | |
| 115 | double var_values[VAR_VARS_NB]; |
| 116 | char *x_expr, *y_expr; |
| 117 | |
| 118 | enum EOFAction eof_action; ///< action to take on EOF from source |
| 119 | |
| 120 | AVExpr *x_pexpr, *y_pexpr; |
| 121 | } OverlayContext; |
| 122 | |
| 123 | static av_cold void uninit(AVFilterContext *ctx) |
| 124 | { |
| 125 | OverlayContext *s = ctx->priv; |
| 126 | |
| 127 | ff_dualinput_uninit(&s->dinput); |
| 128 | av_expr_free(s->x_pexpr); s->x_pexpr = NULL; |
| 129 | av_expr_free(s->y_pexpr); s->y_pexpr = NULL; |
| 130 | } |
| 131 | |
| 132 | static inline int normalize_xy(double d, int chroma_sub) |
| 133 | { |
| 134 | if (isnan(d)) |
| 135 | return INT_MAX; |
| 136 | return (int)d & ~((1 << chroma_sub) - 1); |
| 137 | } |
| 138 | |
| 139 | static void eval_expr(AVFilterContext *ctx) |
| 140 | { |
| 141 | OverlayContext *s = ctx->priv; |
| 142 | |
| 143 | s->var_values[VAR_X] = av_expr_eval(s->x_pexpr, s->var_values, NULL); |
| 144 | s->var_values[VAR_Y] = av_expr_eval(s->y_pexpr, s->var_values, NULL); |
| 145 | s->var_values[VAR_X] = av_expr_eval(s->x_pexpr, s->var_values, NULL); |
| 146 | s->x = normalize_xy(s->var_values[VAR_X], s->hsub); |
| 147 | s->y = normalize_xy(s->var_values[VAR_Y], s->vsub); |
| 148 | } |
| 149 | |
| 150 | static int set_expr(AVExpr **pexpr, const char *expr, const char *option, void *log_ctx) |
| 151 | { |
| 152 | int ret; |
| 153 | AVExpr *old = NULL; |
| 154 | |
| 155 | if (*pexpr) |
| 156 | old = *pexpr; |
| 157 | ret = av_expr_parse(pexpr, expr, var_names, |
| 158 | NULL, NULL, NULL, NULL, 0, log_ctx); |
| 159 | if (ret < 0) { |
| 160 | av_log(log_ctx, AV_LOG_ERROR, |
| 161 | "Error when evaluating the expression '%s' for %s\n", |
| 162 | expr, option); |
| 163 | *pexpr = old; |
| 164 | return ret; |
| 165 | } |
| 166 | |
| 167 | av_expr_free(old); |
| 168 | return 0; |
| 169 | } |
| 170 | |
| 171 | static int process_command(AVFilterContext *ctx, const char *cmd, const char *args, |
| 172 | char *res, int res_len, int flags) |
| 173 | { |
| 174 | OverlayContext *s = ctx->priv; |
| 175 | int ret; |
| 176 | |
| 177 | if (!strcmp(cmd, "x")) |
| 178 | ret = set_expr(&s->x_pexpr, args, cmd, ctx); |
| 179 | else if (!strcmp(cmd, "y")) |
| 180 | ret = set_expr(&s->y_pexpr, args, cmd, ctx); |
| 181 | else |
| 182 | ret = AVERROR(ENOSYS); |
| 183 | |
| 184 | if (ret < 0) |
| 185 | return ret; |
| 186 | |
| 187 | if (s->eval_mode == EVAL_MODE_INIT) { |
| 188 | eval_expr(ctx); |
| 189 | av_log(ctx, AV_LOG_VERBOSE, "x:%f xi:%d y:%f yi:%d\n", |
| 190 | s->var_values[VAR_X], s->x, |
| 191 | s->var_values[VAR_Y], s->y); |
| 192 | } |
| 193 | return ret; |
| 194 | } |
| 195 | |
| 196 | static int query_formats(AVFilterContext *ctx) |
| 197 | { |
| 198 | OverlayContext *s = ctx->priv; |
| 199 | |
| 200 | /* overlay formats contains alpha, for avoiding conversion with alpha information loss */ |
| 201 | static const enum AVPixelFormat main_pix_fmts_yuv420[] = { |
| 202 | AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUVA420P, AV_PIX_FMT_NONE |
| 203 | }; |
| 204 | static const enum AVPixelFormat overlay_pix_fmts_yuv420[] = { |
| 205 | AV_PIX_FMT_YUVA420P, AV_PIX_FMT_NONE |
| 206 | }; |
| 207 | |
| 208 | static const enum AVPixelFormat main_pix_fmts_yuv422[] = { |
| 209 | AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVA422P, AV_PIX_FMT_NONE |
| 210 | }; |
| 211 | static const enum AVPixelFormat overlay_pix_fmts_yuv422[] = { |
| 212 | AV_PIX_FMT_YUVA422P, AV_PIX_FMT_NONE |
| 213 | }; |
| 214 | |
| 215 | static const enum AVPixelFormat main_pix_fmts_yuv444[] = { |
| 216 | AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUVA444P, AV_PIX_FMT_NONE |
| 217 | }; |
| 218 | static const enum AVPixelFormat overlay_pix_fmts_yuv444[] = { |
| 219 | AV_PIX_FMT_YUVA444P, AV_PIX_FMT_NONE |
| 220 | }; |
| 221 | |
| 222 | static const enum AVPixelFormat main_pix_fmts_rgb[] = { |
| 223 | AV_PIX_FMT_ARGB, AV_PIX_FMT_RGBA, |
| 224 | AV_PIX_FMT_ABGR, AV_PIX_FMT_BGRA, |
| 225 | AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24, |
| 226 | AV_PIX_FMT_NONE |
| 227 | }; |
| 228 | static const enum AVPixelFormat overlay_pix_fmts_rgb[] = { |
| 229 | AV_PIX_FMT_ARGB, AV_PIX_FMT_RGBA, |
| 230 | AV_PIX_FMT_ABGR, AV_PIX_FMT_BGRA, |
| 231 | AV_PIX_FMT_NONE |
| 232 | }; |
| 233 | |
| 234 | AVFilterFormats *main_formats; |
| 235 | AVFilterFormats *overlay_formats; |
| 236 | |
| 237 | switch (s->format) { |
| 238 | case OVERLAY_FORMAT_YUV420: |
| 239 | main_formats = ff_make_format_list(main_pix_fmts_yuv420); |
| 240 | overlay_formats = ff_make_format_list(overlay_pix_fmts_yuv420); |
| 241 | break; |
| 242 | case OVERLAY_FORMAT_YUV422: |
| 243 | main_formats = ff_make_format_list(main_pix_fmts_yuv422); |
| 244 | overlay_formats = ff_make_format_list(overlay_pix_fmts_yuv422); |
| 245 | break; |
| 246 | case OVERLAY_FORMAT_YUV444: |
| 247 | main_formats = ff_make_format_list(main_pix_fmts_yuv444); |
| 248 | overlay_formats = ff_make_format_list(overlay_pix_fmts_yuv444); |
| 249 | break; |
| 250 | case OVERLAY_FORMAT_RGB: |
| 251 | main_formats = ff_make_format_list(main_pix_fmts_rgb); |
| 252 | overlay_formats = ff_make_format_list(overlay_pix_fmts_rgb); |
| 253 | break; |
| 254 | default: |
| 255 | av_assert0(0); |
| 256 | } |
| 257 | |
| 258 | ff_formats_ref(main_formats, &ctx->inputs [MAIN ]->out_formats); |
| 259 | ff_formats_ref(overlay_formats, &ctx->inputs [OVERLAY]->out_formats); |
| 260 | ff_formats_ref(main_formats, &ctx->outputs[MAIN ]->in_formats ); |
| 261 | |
| 262 | return 0; |
| 263 | } |
| 264 | |
| 265 | static const enum AVPixelFormat alpha_pix_fmts[] = { |
| 266 | AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUVA444P, |
| 267 | AV_PIX_FMT_ARGB, AV_PIX_FMT_ABGR, AV_PIX_FMT_RGBA, |
| 268 | AV_PIX_FMT_BGRA, AV_PIX_FMT_NONE |
| 269 | }; |
| 270 | |
| 271 | static int config_input_main(AVFilterLink *inlink) |
| 272 | { |
| 273 | OverlayContext *s = inlink->dst->priv; |
| 274 | const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(inlink->format); |
| 275 | |
| 276 | av_image_fill_max_pixsteps(s->main_pix_step, NULL, pix_desc); |
| 277 | |
| 278 | s->hsub = pix_desc->log2_chroma_w; |
| 279 | s->vsub = pix_desc->log2_chroma_h; |
| 280 | |
| 281 | s->main_is_packed_rgb = |
| 282 | ff_fill_rgba_map(s->main_rgba_map, inlink->format) >= 0; |
| 283 | s->main_has_alpha = ff_fmt_is_in(inlink->format, alpha_pix_fmts); |
| 284 | return 0; |
| 285 | } |
| 286 | |
| 287 | static int config_input_overlay(AVFilterLink *inlink) |
| 288 | { |
| 289 | AVFilterContext *ctx = inlink->dst; |
| 290 | OverlayContext *s = inlink->dst->priv; |
| 291 | int ret; |
| 292 | const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(inlink->format); |
| 293 | |
| 294 | av_image_fill_max_pixsteps(s->overlay_pix_step, NULL, pix_desc); |
| 295 | |
| 296 | /* Finish the configuration by evaluating the expressions |
| 297 | now when both inputs are configured. */ |
| 298 | s->var_values[VAR_MAIN_W ] = s->var_values[VAR_MW] = ctx->inputs[MAIN ]->w; |
| 299 | s->var_values[VAR_MAIN_H ] = s->var_values[VAR_MH] = ctx->inputs[MAIN ]->h; |
| 300 | s->var_values[VAR_OVERLAY_W] = s->var_values[VAR_OW] = ctx->inputs[OVERLAY]->w; |
| 301 | s->var_values[VAR_OVERLAY_H] = s->var_values[VAR_OH] = ctx->inputs[OVERLAY]->h; |
| 302 | s->var_values[VAR_HSUB] = 1<<pix_desc->log2_chroma_w; |
| 303 | s->var_values[VAR_VSUB] = 1<<pix_desc->log2_chroma_h; |
| 304 | s->var_values[VAR_X] = NAN; |
| 305 | s->var_values[VAR_Y] = NAN; |
| 306 | s->var_values[VAR_N] = 0; |
| 307 | s->var_values[VAR_T] = NAN; |
| 308 | s->var_values[VAR_POS] = NAN; |
| 309 | |
| 310 | if ((ret = set_expr(&s->x_pexpr, s->x_expr, "x", ctx)) < 0 || |
| 311 | (ret = set_expr(&s->y_pexpr, s->y_expr, "y", ctx)) < 0) |
| 312 | return ret; |
| 313 | |
| 314 | s->overlay_is_packed_rgb = |
| 315 | ff_fill_rgba_map(s->overlay_rgba_map, inlink->format) >= 0; |
| 316 | s->overlay_has_alpha = ff_fmt_is_in(inlink->format, alpha_pix_fmts); |
| 317 | |
| 318 | if (s->eval_mode == EVAL_MODE_INIT) { |
| 319 | eval_expr(ctx); |
| 320 | av_log(ctx, AV_LOG_VERBOSE, "x:%f xi:%d y:%f yi:%d\n", |
| 321 | s->var_values[VAR_X], s->x, |
| 322 | s->var_values[VAR_Y], s->y); |
| 323 | } |
| 324 | |
| 325 | av_log(ctx, AV_LOG_VERBOSE, |
| 326 | "main w:%d h:%d fmt:%s overlay w:%d h:%d fmt:%s eof_action:%s\n", |
| 327 | ctx->inputs[MAIN]->w, ctx->inputs[MAIN]->h, |
| 328 | av_get_pix_fmt_name(ctx->inputs[MAIN]->format), |
| 329 | ctx->inputs[OVERLAY]->w, ctx->inputs[OVERLAY]->h, |
| 330 | av_get_pix_fmt_name(ctx->inputs[OVERLAY]->format), |
| 331 | eof_action_str[s->eof_action]); |
| 332 | return 0; |
| 333 | } |
| 334 | |
| 335 | static int config_output(AVFilterLink *outlink) |
| 336 | { |
| 337 | AVFilterContext *ctx = outlink->src; |
| 338 | OverlayContext *s = ctx->priv; |
| 339 | int ret; |
| 340 | |
| 341 | if ((ret = ff_dualinput_init(ctx, &s->dinput)) < 0) |
| 342 | return ret; |
| 343 | |
| 344 | outlink->w = ctx->inputs[MAIN]->w; |
| 345 | outlink->h = ctx->inputs[MAIN]->h; |
| 346 | outlink->time_base = ctx->inputs[MAIN]->time_base; |
| 347 | |
| 348 | return 0; |
| 349 | } |
| 350 | |
| 351 | // divide by 255 and round to nearest |
| 352 | // apply a fast variant: (X+127)/255 = ((X+127)*257+257)>>16 = ((X+128)*257)>>16 |
| 353 | #define FAST_DIV255(x) ((((x) + 128) * 257) >> 16) |
| 354 | |
| 355 | // calculate the unpremultiplied alpha, applying the general equation: |
| 356 | // alpha = alpha_overlay / ( (alpha_main + alpha_overlay) - (alpha_main * alpha_overlay) ) |
| 357 | // (((x) << 16) - ((x) << 9) + (x)) is a faster version of: 255 * 255 * x |
| 358 | // ((((x) + (y)) << 8) - ((x) + (y)) - (y) * (x)) is a faster version of: 255 * (x + y) |
| 359 | #define UNPREMULTIPLY_ALPHA(x, y) ((((x) << 16) - ((x) << 9) + (x)) / ((((x) + (y)) << 8) - ((x) + (y)) - (y) * (x))) |
| 360 | |
| 361 | /** |
| 362 | * Blend image in src to destination buffer dst at position (x, y). |
| 363 | */ |
| 364 | static void blend_image(AVFilterContext *ctx, |
| 365 | AVFrame *dst, const AVFrame *src, |
| 366 | int x, int y) |
| 367 | { |
| 368 | OverlayContext *s = ctx->priv; |
| 369 | int i, imax, j, jmax, k, kmax; |
| 370 | const int src_w = src->width; |
| 371 | const int src_h = src->height; |
| 372 | const int dst_w = dst->width; |
| 373 | const int dst_h = dst->height; |
| 374 | |
| 375 | if (x >= dst_w || x+src_w < 0 || |
| 376 | y >= dst_h || y+src_h < 0) |
| 377 | return; /* no intersection */ |
| 378 | |
| 379 | if (s->main_is_packed_rgb) { |
| 380 | uint8_t alpha; ///< the amount of overlay to blend on to main |
| 381 | const int dr = s->main_rgba_map[R]; |
| 382 | const int dg = s->main_rgba_map[G]; |
| 383 | const int db = s->main_rgba_map[B]; |
| 384 | const int da = s->main_rgba_map[A]; |
| 385 | const int dstep = s->main_pix_step[0]; |
| 386 | const int sr = s->overlay_rgba_map[R]; |
| 387 | const int sg = s->overlay_rgba_map[G]; |
| 388 | const int sb = s->overlay_rgba_map[B]; |
| 389 | const int sa = s->overlay_rgba_map[A]; |
| 390 | const int sstep = s->overlay_pix_step[0]; |
| 391 | const int main_has_alpha = s->main_has_alpha; |
| 392 | uint8_t *s, *sp, *d, *dp; |
| 393 | |
| 394 | i = FFMAX(-y, 0); |
| 395 | sp = src->data[0] + i * src->linesize[0]; |
| 396 | dp = dst->data[0] + (y+i) * dst->linesize[0]; |
| 397 | |
| 398 | for (imax = FFMIN(-y + dst_h, src_h); i < imax; i++) { |
| 399 | j = FFMAX(-x, 0); |
| 400 | s = sp + j * sstep; |
| 401 | d = dp + (x+j) * dstep; |
| 402 | |
| 403 | for (jmax = FFMIN(-x + dst_w, src_w); j < jmax; j++) { |
| 404 | alpha = s[sa]; |
| 405 | |
| 406 | // if the main channel has an alpha channel, alpha has to be calculated |
| 407 | // to create an un-premultiplied (straight) alpha value |
| 408 | if (main_has_alpha && alpha != 0 && alpha != 255) { |
| 409 | uint8_t alpha_d = d[da]; |
| 410 | alpha = UNPREMULTIPLY_ALPHA(alpha, alpha_d); |
| 411 | } |
| 412 | |
| 413 | switch (alpha) { |
| 414 | case 0: |
| 415 | break; |
| 416 | case 255: |
| 417 | d[dr] = s[sr]; |
| 418 | d[dg] = s[sg]; |
| 419 | d[db] = s[sb]; |
| 420 | break; |
| 421 | default: |
| 422 | // main_value = main_value * (1 - alpha) + overlay_value * alpha |
| 423 | // since alpha is in the range 0-255, the result must divided by 255 |
| 424 | d[dr] = FAST_DIV255(d[dr] * (255 - alpha) + s[sr] * alpha); |
| 425 | d[dg] = FAST_DIV255(d[dg] * (255 - alpha) + s[sg] * alpha); |
| 426 | d[db] = FAST_DIV255(d[db] * (255 - alpha) + s[sb] * alpha); |
| 427 | } |
| 428 | if (main_has_alpha) { |
| 429 | switch (alpha) { |
| 430 | case 0: |
| 431 | break; |
| 432 | case 255: |
| 433 | d[da] = s[sa]; |
| 434 | break; |
| 435 | default: |
| 436 | // apply alpha compositing: main_alpha += (1-main_alpha) * overlay_alpha |
| 437 | d[da] += FAST_DIV255((255 - d[da]) * s[sa]); |
| 438 | } |
| 439 | } |
| 440 | d += dstep; |
| 441 | s += sstep; |
| 442 | } |
| 443 | dp += dst->linesize[0]; |
| 444 | sp += src->linesize[0]; |
| 445 | } |
| 446 | } else { |
| 447 | const int main_has_alpha = s->main_has_alpha; |
| 448 | if (main_has_alpha) { |
| 449 | uint8_t alpha; ///< the amount of overlay to blend on to main |
| 450 | uint8_t *s, *sa, *d, *da; |
| 451 | |
| 452 | i = FFMAX(-y, 0); |
| 453 | sa = src->data[3] + i * src->linesize[3]; |
| 454 | da = dst->data[3] + (y+i) * dst->linesize[3]; |
| 455 | |
| 456 | for (imax = FFMIN(-y + dst_h, src_h); i < imax; i++) { |
| 457 | j = FFMAX(-x, 0); |
| 458 | s = sa + j; |
| 459 | d = da + x+j; |
| 460 | |
| 461 | for (jmax = FFMIN(-x + dst_w, src_w); j < jmax; j++) { |
| 462 | alpha = *s; |
| 463 | if (alpha != 0 && alpha != 255) { |
| 464 | uint8_t alpha_d = *d; |
| 465 | alpha = UNPREMULTIPLY_ALPHA(alpha, alpha_d); |
| 466 | } |
| 467 | switch (alpha) { |
| 468 | case 0: |
| 469 | break; |
| 470 | case 255: |
| 471 | *d = *s; |
| 472 | break; |
| 473 | default: |
| 474 | // apply alpha compositing: main_alpha += (1-main_alpha) * overlay_alpha |
| 475 | *d += FAST_DIV255((255 - *d) * *s); |
| 476 | } |
| 477 | d += 1; |
| 478 | s += 1; |
| 479 | } |
| 480 | da += dst->linesize[3]; |
| 481 | sa += src->linesize[3]; |
| 482 | } |
| 483 | } |
| 484 | for (i = 0; i < 3; i++) { |
| 485 | int hsub = i ? s->hsub : 0; |
| 486 | int vsub = i ? s->vsub : 0; |
| 487 | int src_wp = FF_CEIL_RSHIFT(src_w, hsub); |
| 488 | int src_hp = FF_CEIL_RSHIFT(src_h, vsub); |
| 489 | int dst_wp = FF_CEIL_RSHIFT(dst_w, hsub); |
| 490 | int dst_hp = FF_CEIL_RSHIFT(dst_h, vsub); |
| 491 | int yp = y>>vsub; |
| 492 | int xp = x>>hsub; |
| 493 | uint8_t *s, *sp, *d, *dp, *a, *ap; |
| 494 | |
| 495 | j = FFMAX(-yp, 0); |
| 496 | sp = src->data[i] + j * src->linesize[i]; |
| 497 | dp = dst->data[i] + (yp+j) * dst->linesize[i]; |
| 498 | ap = src->data[3] + (j<<vsub) * src->linesize[3]; |
| 499 | |
| 500 | for (jmax = FFMIN(-yp + dst_hp, src_hp); j < jmax; j++) { |
| 501 | k = FFMAX(-xp, 0); |
| 502 | d = dp + xp+k; |
| 503 | s = sp + k; |
| 504 | a = ap + (k<<hsub); |
| 505 | |
| 506 | for (kmax = FFMIN(-xp + dst_wp, src_wp); k < kmax; k++) { |
| 507 | int alpha_v, alpha_h, alpha; |
| 508 | |
| 509 | // average alpha for color components, improve quality |
| 510 | if (hsub && vsub && j+1 < src_hp && k+1 < src_wp) { |
| 511 | alpha = (a[0] + a[src->linesize[3]] + |
| 512 | a[1] + a[src->linesize[3]+1]) >> 2; |
| 513 | } else if (hsub || vsub) { |
| 514 | alpha_h = hsub && k+1 < src_wp ? |
| 515 | (a[0] + a[1]) >> 1 : a[0]; |
| 516 | alpha_v = vsub && j+1 < src_hp ? |
| 517 | (a[0] + a[src->linesize[3]]) >> 1 : a[0]; |
| 518 | alpha = (alpha_v + alpha_h) >> 1; |
| 519 | } else |
| 520 | alpha = a[0]; |
| 521 | // if the main channel has an alpha channel, alpha has to be calculated |
| 522 | // to create an un-premultiplied (straight) alpha value |
| 523 | if (main_has_alpha && alpha != 0 && alpha != 255) { |
| 524 | // average alpha for color components, improve quality |
| 525 | uint8_t alpha_d; |
| 526 | if (hsub && vsub && j+1 < src_hp && k+1 < src_wp) { |
| 527 | alpha_d = (d[0] + d[src->linesize[3]] + |
| 528 | d[1] + d[src->linesize[3]+1]) >> 2; |
| 529 | } else if (hsub || vsub) { |
| 530 | alpha_h = hsub && k+1 < src_wp ? |
| 531 | (d[0] + d[1]) >> 1 : d[0]; |
| 532 | alpha_v = vsub && j+1 < src_hp ? |
| 533 | (d[0] + d[src->linesize[3]]) >> 1 : d[0]; |
| 534 | alpha_d = (alpha_v + alpha_h) >> 1; |
| 535 | } else |
| 536 | alpha_d = d[0]; |
| 537 | alpha = UNPREMULTIPLY_ALPHA(alpha, alpha_d); |
| 538 | } |
| 539 | *d = FAST_DIV255(*d * (255 - alpha) + *s * alpha); |
| 540 | s++; |
| 541 | d++; |
| 542 | a += 1 << hsub; |
| 543 | } |
| 544 | dp += dst->linesize[i]; |
| 545 | sp += src->linesize[i]; |
| 546 | ap += (1 << vsub) * src->linesize[3]; |
| 547 | } |
| 548 | } |
| 549 | } |
| 550 | } |
| 551 | |
| 552 | static AVFrame *do_blend(AVFilterContext *ctx, AVFrame *mainpic, |
| 553 | const AVFrame *second) |
| 554 | { |
| 555 | OverlayContext *s = ctx->priv; |
| 556 | AVFilterLink *inlink = ctx->inputs[0]; |
| 557 | |
| 558 | if (s->eval_mode == EVAL_MODE_FRAME) { |
| 559 | int64_t pos = av_frame_get_pkt_pos(mainpic); |
| 560 | |
| 561 | s->var_values[VAR_N] = inlink->frame_count; |
| 562 | s->var_values[VAR_T] = mainpic->pts == AV_NOPTS_VALUE ? |
| 563 | NAN : mainpic->pts * av_q2d(inlink->time_base); |
| 564 | s->var_values[VAR_POS] = pos == -1 ? NAN : pos; |
| 565 | |
| 566 | eval_expr(ctx); |
| 567 | av_log(ctx, AV_LOG_DEBUG, "n:%f t:%f pos:%f x:%f xi:%d y:%f yi:%d\n", |
| 568 | s->var_values[VAR_N], s->var_values[VAR_T], s->var_values[VAR_POS], |
| 569 | s->var_values[VAR_X], s->x, |
| 570 | s->var_values[VAR_Y], s->y); |
| 571 | } |
| 572 | |
| 573 | blend_image(ctx, mainpic, second, s->x, s->y); |
| 574 | return mainpic; |
| 575 | } |
| 576 | |
| 577 | static int filter_frame(AVFilterLink *inlink, AVFrame *inpicref) |
| 578 | { |
| 579 | OverlayContext *s = inlink->dst->priv; |
| 580 | av_log(inlink->dst, AV_LOG_DEBUG, "Incoming frame (time:%s) from link #%d\n", av_ts2timestr(inpicref->pts, &inlink->time_base), FF_INLINK_IDX(inlink)); |
| 581 | return ff_dualinput_filter_frame(&s->dinput, inlink, inpicref); |
| 582 | } |
| 583 | |
| 584 | static int request_frame(AVFilterLink *outlink) |
| 585 | { |
| 586 | OverlayContext *s = outlink->src->priv; |
| 587 | return ff_dualinput_request_frame(&s->dinput, outlink); |
| 588 | } |
| 589 | |
| 590 | static av_cold int init(AVFilterContext *ctx) |
| 591 | { |
| 592 | OverlayContext *s = ctx->priv; |
| 593 | |
| 594 | if (s->allow_packed_rgb) { |
| 595 | av_log(ctx, AV_LOG_WARNING, |
| 596 | "The rgb option is deprecated and is overriding the format option, use format instead\n"); |
| 597 | s->format = OVERLAY_FORMAT_RGB; |
| 598 | } |
| 599 | if (!s->dinput.repeatlast || s->eof_action == EOF_ACTION_PASS) { |
| 600 | s->dinput.repeatlast = 0; |
| 601 | s->eof_action = EOF_ACTION_PASS; |
| 602 | } |
| 603 | if (s->dinput.shortest || s->eof_action == EOF_ACTION_ENDALL) { |
| 604 | s->dinput.shortest = 1; |
| 605 | s->eof_action = EOF_ACTION_ENDALL; |
| 606 | } |
| 607 | |
| 608 | s->dinput.process = do_blend; |
| 609 | return 0; |
| 610 | } |
| 611 | |
| 612 | #define OFFSET(x) offsetof(OverlayContext, x) |
| 613 | #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM |
| 614 | |
| 615 | static const AVOption overlay_options[] = { |
| 616 | { "x", "set the x expression", OFFSET(x_expr), AV_OPT_TYPE_STRING, {.str = "0"}, CHAR_MIN, CHAR_MAX, FLAGS }, |
| 617 | { "y", "set the y expression", OFFSET(y_expr), AV_OPT_TYPE_STRING, {.str = "0"}, CHAR_MIN, CHAR_MAX, FLAGS }, |
| 618 | { "eof_action", "Action to take when encountering EOF from secondary input ", |
| 619 | OFFSET(eof_action), AV_OPT_TYPE_INT, { .i64 = EOF_ACTION_REPEAT }, |
| 620 | EOF_ACTION_REPEAT, EOF_ACTION_PASS, .flags = FLAGS, "eof_action" }, |
| 621 | { "repeat", "Repeat the previous frame.", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_REPEAT }, .flags = FLAGS, "eof_action" }, |
| 622 | { "endall", "End both streams.", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_ENDALL }, .flags = FLAGS, "eof_action" }, |
| 623 | { "pass", "Pass through the main input.", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_PASS }, .flags = FLAGS, "eof_action" }, |
| 624 | { "eval", "specify when to evaluate expressions", OFFSET(eval_mode), AV_OPT_TYPE_INT, {.i64 = EVAL_MODE_FRAME}, 0, EVAL_MODE_NB-1, FLAGS, "eval" }, |
| 625 | { "init", "eval expressions once during initialization", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_INIT}, .flags = FLAGS, .unit = "eval" }, |
| 626 | { "frame", "eval expressions per-frame", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_FRAME}, .flags = FLAGS, .unit = "eval" }, |
| 627 | { "rgb", "force packed RGB in input and output (deprecated)", OFFSET(allow_packed_rgb), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS }, |
| 628 | { "shortest", "force termination when the shortest input terminates", OFFSET(dinput.shortest), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, FLAGS }, |
| 629 | { "format", "set output format", OFFSET(format), AV_OPT_TYPE_INT, {.i64=OVERLAY_FORMAT_YUV420}, 0, OVERLAY_FORMAT_NB-1, FLAGS, "format" }, |
| 630 | { "yuv420", "", 0, AV_OPT_TYPE_CONST, {.i64=OVERLAY_FORMAT_YUV420}, .flags = FLAGS, .unit = "format" }, |
| 631 | { "yuv422", "", 0, AV_OPT_TYPE_CONST, {.i64=OVERLAY_FORMAT_YUV422}, .flags = FLAGS, .unit = "format" }, |
| 632 | { "yuv444", "", 0, AV_OPT_TYPE_CONST, {.i64=OVERLAY_FORMAT_YUV444}, .flags = FLAGS, .unit = "format" }, |
| 633 | { "rgb", "", 0, AV_OPT_TYPE_CONST, {.i64=OVERLAY_FORMAT_RGB}, .flags = FLAGS, .unit = "format" }, |
| 634 | { "repeatlast", "repeat overlay of the last overlay frame", OFFSET(dinput.repeatlast), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS }, |
| 635 | { NULL } |
| 636 | }; |
| 637 | |
| 638 | AVFILTER_DEFINE_CLASS(overlay); |
| 639 | |
| 640 | static const AVFilterPad avfilter_vf_overlay_inputs[] = { |
| 641 | { |
| 642 | .name = "main", |
| 643 | .type = AVMEDIA_TYPE_VIDEO, |
| 644 | .config_props = config_input_main, |
| 645 | .filter_frame = filter_frame, |
| 646 | .needs_writable = 1, |
| 647 | }, |
| 648 | { |
| 649 | .name = "overlay", |
| 650 | .type = AVMEDIA_TYPE_VIDEO, |
| 651 | .config_props = config_input_overlay, |
| 652 | .filter_frame = filter_frame, |
| 653 | }, |
| 654 | { NULL } |
| 655 | }; |
| 656 | |
| 657 | static const AVFilterPad avfilter_vf_overlay_outputs[] = { |
| 658 | { |
| 659 | .name = "default", |
| 660 | .type = AVMEDIA_TYPE_VIDEO, |
| 661 | .config_props = config_output, |
| 662 | .request_frame = request_frame, |
| 663 | }, |
| 664 | { NULL } |
| 665 | }; |
| 666 | |
| 667 | AVFilter ff_vf_overlay = { |
| 668 | .name = "overlay", |
| 669 | .description = NULL_IF_CONFIG_SMALL("Overlay a video source on top of the input."), |
| 670 | .init = init, |
| 671 | .uninit = uninit, |
| 672 | .priv_size = sizeof(OverlayContext), |
| 673 | .priv_class = &overlay_class, |
| 674 | .query_formats = query_formats, |
| 675 | .process_command = process_command, |
| 676 | .inputs = avfilter_vf_overlay_inputs, |
| 677 | .outputs = avfilter_vf_overlay_outputs, |
| 678 | .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, |
| 679 | }; |