2 * Copyright (c) 2010 Stefano Sabatini
3 * Copyright (c) 2010 Baptiste Coudurier
4 * Copyright (c) 2007 Bobby Bingham
6 * This file is part of Libav.
8 * Libav 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.
13 * Libav 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.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with Libav; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
25 * overlay one video on top of another
30 #include "libavutil/common.h"
31 #include "libavutil/eval.h"
32 #include "libavutil/avstring.h"
33 #include "libavutil/avassert.h"
34 #include "libavutil/pixdesc.h"
35 #include "libavutil/imgutils.h"
36 #include "libavutil/mathematics.h"
40 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
57 VAR_OVERLAY_W
, VAR_OW
,
58 VAR_OVERLAY_H
, VAR_OH
,
66 int x
, y
; ///< position of overlayed picture
68 int max_plane_step
[4]; ///< steps per pixel for each plane
69 int hsub
, vsub
; ///< chroma subsampling values
71 char x_expr
[256], y_expr
[256];
74 AVFrame
*over_prev
, *over_next
;
77 static av_cold
int init(AVFilterContext
*ctx
, const char *args
)
79 OverlayContext
*over
= ctx
->priv
;
81 av_strlcpy(over
->x_expr
, "0", sizeof(over
->x_expr
));
82 av_strlcpy(over
->y_expr
, "0", sizeof(over
->y_expr
));
85 sscanf(args
, "%255[^:]:%255[^:]", over
->x_expr
, over
->y_expr
);
90 static av_cold
void uninit(AVFilterContext
*ctx
)
92 OverlayContext
*s
= ctx
->priv
;
94 av_frame_free(&s
->main
);
95 av_frame_free(&s
->over_prev
);
96 av_frame_free(&s
->over_next
);
99 static int query_formats(AVFilterContext
*ctx
)
101 const enum AVPixelFormat inout_pix_fmts
[] = { AV_PIX_FMT_YUV420P
, AV_PIX_FMT_NONE
};
102 const enum AVPixelFormat blend_pix_fmts
[] = { AV_PIX_FMT_YUVA420P
, AV_PIX_FMT_NONE
};
103 AVFilterFormats
*inout_formats
= ff_make_format_list(inout_pix_fmts
);
104 AVFilterFormats
*blend_formats
= ff_make_format_list(blend_pix_fmts
);
106 ff_formats_ref(inout_formats
, &ctx
->inputs
[MAIN
]->out_formats
);
107 ff_formats_ref(blend_formats
, &ctx
->inputs
[OVERLAY
]->out_formats
);
108 ff_formats_ref(inout_formats
, &ctx
->outputs
[MAIN
]->in_formats
);
113 static int config_input_main(AVFilterLink
*inlink
)
115 OverlayContext
*over
= inlink
->dst
->priv
;
116 const AVPixFmtDescriptor
*pix_desc
= av_pix_fmt_desc_get(inlink
->format
);
118 av_image_fill_max_pixsteps(over
->max_plane_step
, NULL
, pix_desc
);
119 over
->hsub
= pix_desc
->log2_chroma_w
;
120 over
->vsub
= pix_desc
->log2_chroma_h
;
125 static int config_input_overlay(AVFilterLink
*inlink
)
127 AVFilterContext
*ctx
= inlink
->dst
;
128 OverlayContext
*over
= inlink
->dst
->priv
;
130 double var_values
[VAR_VARS_NB
], res
;
133 /* Finish the configuration by evaluating the expressions
134 now when both inputs are configured. */
135 var_values
[VAR_E
] = M_E
;
136 var_values
[VAR_PHI
] = M_PHI
;
137 var_values
[VAR_PI
] = M_PI
;
139 var_values
[VAR_MAIN_W
] = var_values
[VAR_MW
] = ctx
->inputs
[MAIN
]->w
;
140 var_values
[VAR_MAIN_H
] = var_values
[VAR_MH
] = ctx
->inputs
[MAIN
]->h
;
141 var_values
[VAR_OVERLAY_W
] = var_values
[VAR_OW
] = ctx
->inputs
[OVERLAY
]->w
;
142 var_values
[VAR_OVERLAY_H
] = var_values
[VAR_OH
] = ctx
->inputs
[OVERLAY
]->h
;
144 if ((ret
= av_expr_parse_and_eval(&res
, (expr
= over
->x_expr
), var_names
, var_values
,
145 NULL
, NULL
, NULL
, NULL
, NULL
, 0, ctx
)) < 0)
148 if ((ret
= av_expr_parse_and_eval(&res
, (expr
= over
->y_expr
), var_names
, var_values
,
149 NULL
, NULL
, NULL
, NULL
, NULL
, 0, ctx
)))
152 /* x may depend on y */
153 if ((ret
= av_expr_parse_and_eval(&res
, (expr
= over
->x_expr
), var_names
, var_values
,
154 NULL
, NULL
, NULL
, NULL
, NULL
, 0, ctx
)) < 0)
158 av_log(ctx
, AV_LOG_VERBOSE
,
159 "main w:%d h:%d fmt:%s overlay x:%d y:%d w:%d h:%d fmt:%s\n",
160 ctx
->inputs
[MAIN
]->w
, ctx
->inputs
[MAIN
]->h
,
161 av_get_pix_fmt_name(ctx
->inputs
[MAIN
]->format
),
163 ctx
->inputs
[OVERLAY
]->w
, ctx
->inputs
[OVERLAY
]->h
,
164 av_get_pix_fmt_name(ctx
->inputs
[OVERLAY
]->format
));
166 if (over
->x
< 0 || over
->y
< 0 ||
167 over
->x
+ var_values
[VAR_OVERLAY_W
] > var_values
[VAR_MAIN_W
] ||
168 over
->y
+ var_values
[VAR_OVERLAY_H
] > var_values
[VAR_MAIN_H
]) {
169 av_log(ctx
, AV_LOG_ERROR
,
170 "Overlay area (%d,%d)<->(%d,%d) not within the main area (0,0)<->(%d,%d) or zero-sized\n",
172 (int)(over
->x
+ var_values
[VAR_OVERLAY_W
]),
173 (int)(over
->y
+ var_values
[VAR_OVERLAY_H
]),
174 (int)var_values
[VAR_MAIN_W
], (int)var_values
[VAR_MAIN_H
]);
175 return AVERROR(EINVAL
);
180 av_log(NULL
, AV_LOG_ERROR
,
181 "Error when evaluating the expression '%s'\n", expr
);
185 static int config_output(AVFilterLink
*outlink
)
187 AVFilterContext
*ctx
= outlink
->src
;
189 outlink
->w
= ctx
->inputs
[MAIN
]->w
;
190 outlink
->h
= ctx
->inputs
[MAIN
]->h
;
191 outlink
->time_base
= ctx
->inputs
[MAIN
]->time_base
;
196 static void blend_frame(AVFilterContext
*ctx
,
197 AVFrame
*dst
, AVFrame
*src
,
200 OverlayContext
*over
= ctx
->priv
;
203 int overlay_end_y
= y
+ src
->height
;
206 width
= FFMIN(dst
->width
- x
, src
->width
);
207 end_y
= FFMIN(dst
->height
, overlay_end_y
);
208 start_y
= FFMAX(y
, 0);
209 height
= end_y
- start_y
;
211 if (dst
->format
== AV_PIX_FMT_BGR24
|| dst
->format
== AV_PIX_FMT_RGB24
) {
212 uint8_t *dp
= dst
->data
[0] + x
* 3 + start_y
* dst
->linesize
[0];
213 uint8_t *sp
= src
->data
[0];
214 int b
= dst
->format
== AV_PIX_FMT_BGR24
? 2 : 0;
215 int r
= dst
->format
== AV_PIX_FMT_BGR24
? 0 : 2;
217 sp
+= -y
* src
->linesize
[0];
218 for (i
= 0; i
< height
; i
++) {
219 uint8_t *d
= dp
, *s
= sp
;
220 for (j
= 0; j
< width
; j
++) {
221 d
[r
] = (d
[r
] * (0xff - s
[3]) + s
[0] * s
[3] + 128) >> 8;
222 d
[1] = (d
[1] * (0xff - s
[3]) + s
[1] * s
[3] + 128) >> 8;
223 d
[b
] = (d
[b
] * (0xff - s
[3]) + s
[2] * s
[3] + 128) >> 8;
227 dp
+= dst
->linesize
[0];
228 sp
+= src
->linesize
[0];
231 for (i
= 0; i
< 3; i
++) {
232 int hsub
= i
? over
->hsub
: 0;
233 int vsub
= i
? over
->vsub
: 0;
234 uint8_t *dp
= dst
->data
[i
] + (x
>> hsub
) +
235 (start_y
>> vsub
) * dst
->linesize
[i
];
236 uint8_t *sp
= src
->data
[i
];
237 uint8_t *ap
= src
->data
[3];
238 int wp
= FFALIGN(width
, 1<<hsub
) >> hsub
;
239 int hp
= FFALIGN(height
, 1<<vsub
) >> vsub
;
241 sp
+= ((-y
) >> vsub
) * src
->linesize
[i
];
242 ap
+= -y
* src
->linesize
[3];
244 for (j
= 0; j
< hp
; j
++) {
245 uint8_t *d
= dp
, *s
= sp
, *a
= ap
;
246 for (k
= 0; k
< wp
; k
++) {
247 // average alpha for color components, improve quality
248 int alpha_v
, alpha_h
, alpha
;
249 if (hsub
&& vsub
&& j
+1 < hp
&& k
+1 < wp
) {
250 alpha
= (a
[0] + a
[src
->linesize
[3]] +
251 a
[1] + a
[src
->linesize
[3]+1]) >> 2;
252 } else if (hsub
|| vsub
) {
253 alpha_h
= hsub
&& k
+1 < wp
?
254 (a
[0] + a
[1]) >> 1 : a
[0];
255 alpha_v
= vsub
&& j
+1 < hp
?
256 (a
[0] + a
[src
->linesize
[3]]) >> 1 : a
[0];
257 alpha
= (alpha_v
+ alpha_h
) >> 1;
260 *d
= (*d
* (0xff - alpha
) + *s
++ * alpha
+ 128) >> 8;
264 dp
+= dst
->linesize
[i
];
265 sp
+= src
->linesize
[i
];
266 ap
+= (1 << vsub
) * src
->linesize
[3];
272 static int filter_frame_main(AVFilterLink
*inlink
, AVFrame
*frame
)
274 OverlayContext
*s
= inlink
->dst
->priv
;
276 av_assert0(!s
->main
);
282 static int filter_frame_overlay(AVFilterLink
*inlink
, AVFrame
*frame
)
284 OverlayContext
*s
= inlink
->dst
->priv
;
286 av_assert0(!s
->over_next
);
287 s
->over_next
= frame
;
292 static int output_frame(AVFilterContext
*ctx
)
294 OverlayContext
*s
= ctx
->priv
;
295 AVFilterLink
*outlink
= ctx
->outputs
[0];
296 int ret
= ff_filter_frame(outlink
, s
->main
);
302 static int handle_overlay_eof(AVFilterContext
*ctx
)
304 OverlayContext
*s
= ctx
->priv
;
306 blend_frame(ctx
, s
->main
, s
->over_prev
, s
->x
, s
->y
);
307 return output_frame(ctx
);
310 static int request_frame(AVFilterLink
*outlink
)
312 AVFilterContext
*ctx
= outlink
->src
;
313 OverlayContext
*s
= ctx
->priv
;
314 AVRational tb_main
= ctx
->inputs
[MAIN
]->time_base
;
315 AVRational tb_over
= ctx
->inputs
[OVERLAY
]->time_base
;
318 /* get a frame on the main input */
320 ret
= ff_request_frame(ctx
->inputs
[MAIN
]);
325 /* get a new frame on the overlay input, on EOF
328 ret
= ff_request_frame(ctx
->inputs
[OVERLAY
]);
329 if (ret
== AVERROR_EOF
)
330 return handle_overlay_eof(ctx
);
335 while (s
->main
->pts
!= AV_NOPTS_VALUE
&&
336 s
->over_next
->pts
!= AV_NOPTS_VALUE
&&
337 av_compare_ts(s
->over_next
->pts
, tb_over
, s
->main
->pts
, tb_main
) < 0) {
338 av_frame_free(&s
->over_prev
);
339 FFSWAP(AVFrame
*, s
->over_prev
, s
->over_next
);
341 ret
= ff_request_frame(ctx
->inputs
[OVERLAY
]);
342 if (ret
== AVERROR_EOF
)
343 return handle_overlay_eof(ctx
);
348 if (s
->main
->pts
== AV_NOPTS_VALUE
||
349 s
->over_next
->pts
== AV_NOPTS_VALUE
||
350 !av_compare_ts(s
->over_next
->pts
, tb_over
, s
->main
->pts
, tb_main
)) {
351 blend_frame(ctx
, s
->main
, s
->over_next
, s
->x
, s
->y
);
352 av_frame_free(&s
->over_prev
);
353 FFSWAP(AVFrame
*, s
->over_prev
, s
->over_next
);
354 } else if (s
->over_prev
) {
355 blend_frame(ctx
, s
->main
, s
->over_prev
, s
->x
, s
->y
);
358 return output_frame(ctx
);
361 static const AVFilterPad avfilter_vf_overlay_inputs
[] = {
364 .type
= AVMEDIA_TYPE_VIDEO
,
365 .config_props
= config_input_main
,
366 .filter_frame
= filter_frame_main
,
372 .type
= AVMEDIA_TYPE_VIDEO
,
373 .config_props
= config_input_overlay
,
374 .filter_frame
= filter_frame_overlay
,
380 static const AVFilterPad avfilter_vf_overlay_outputs
[] = {
383 .type
= AVMEDIA_TYPE_VIDEO
,
384 .config_props
= config_output
,
385 .request_frame
= request_frame
,
390 AVFilter avfilter_vf_overlay
= {
392 .description
= NULL_IF_CONFIG_SMALL("Overlay a video source on top of the input."),
397 .priv_size
= sizeof(OverlayContext
),
399 .query_formats
= query_formats
,
401 .inputs
= avfilter_vf_overlay_inputs
,
402 .outputs
= avfilter_vf_overlay_outputs
,