2 * Copyright (c) 2008 vmrsss
3 * Copyright (c) 2009 Stefano Sabatini
5 * This file is part of Libav.
7 * Libav 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.
12 * Libav 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.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with Libav; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24 * video padding filter
31 #include "libavutil/avstring.h"
32 #include "libavutil/common.h"
33 #include "libavutil/eval.h"
34 #include "libavutil/pixdesc.h"
35 #include "libavutil/colorspace.h"
36 #include "libavutil/avassert.h"
37 #include "libavutil/imgutils.h"
38 #include "libavutil/parseutils.h"
39 #include "libavutil/mathematics.h"
40 #include "drawutils.h"
42 static const char *const var_names
[] = {
74 static int query_formats(AVFilterContext
*ctx
)
76 static const enum AVPixelFormat pix_fmts
[] = {
77 AV_PIX_FMT_ARGB
, AV_PIX_FMT_RGBA
,
78 AV_PIX_FMT_ABGR
, AV_PIX_FMT_BGRA
,
79 AV_PIX_FMT_RGB24
, AV_PIX_FMT_BGR24
,
81 AV_PIX_FMT_YUV444P
, AV_PIX_FMT_YUV422P
,
82 AV_PIX_FMT_YUV420P
, AV_PIX_FMT_YUV411P
,
83 AV_PIX_FMT_YUV410P
, AV_PIX_FMT_YUV440P
,
84 AV_PIX_FMT_YUVJ444P
, AV_PIX_FMT_YUVJ422P
,
85 AV_PIX_FMT_YUVJ420P
, AV_PIX_FMT_YUVJ440P
,
91 ff_set_common_formats(ctx
, ff_make_format_list(pix_fmts
));
96 int w
, h
; ///< output dimensions, a value of 0 will result in the input size
97 int x
, y
; ///< offsets of the input area with respect to the padded area
98 int in_w
, in_h
; ///< width and height for the padded input video, which has to be aligned to the chroma values in order to avoid chroma issues
100 char w_expr
[256]; ///< width expression string
101 char h_expr
[256]; ///< height expression string
102 char x_expr
[256]; ///< width expression string
103 char y_expr
[256]; ///< height expression string
105 uint8_t color
[4]; ///< color expressed either in YUVA or RGBA colorspace for the padding area
108 int hsub
, vsub
; ///< chroma subsampling values
111 static av_cold
int init(AVFilterContext
*ctx
, const char *args
)
113 PadContext
*pad
= ctx
->priv
;
114 char color_string
[128] = "black";
116 av_strlcpy(pad
->w_expr
, "iw", sizeof(pad
->w_expr
));
117 av_strlcpy(pad
->h_expr
, "ih", sizeof(pad
->h_expr
));
118 av_strlcpy(pad
->x_expr
, "0" , sizeof(pad
->w_expr
));
119 av_strlcpy(pad
->y_expr
, "0" , sizeof(pad
->h_expr
));
122 sscanf(args
, "%255[^:]:%255[^:]:%255[^:]:%255[^:]:%255s",
123 pad
->w_expr
, pad
->h_expr
, pad
->x_expr
, pad
->y_expr
, color_string
);
125 if (av_parse_color(pad
->color
, color_string
, -1, ctx
) < 0)
126 return AVERROR(EINVAL
);
131 static av_cold
void uninit(AVFilterContext
*ctx
)
133 PadContext
*pad
= ctx
->priv
;
136 for (i
= 0; i
< 4; i
++) {
137 av_freep(&pad
->line
[i
]);
138 pad
->line_step
[i
] = 0;
142 static int config_input(AVFilterLink
*inlink
)
144 AVFilterContext
*ctx
= inlink
->dst
;
145 PadContext
*pad
= ctx
->priv
;
146 const AVPixFmtDescriptor
*pix_desc
= av_pix_fmt_desc_get(inlink
->format
);
147 uint8_t rgba_color
[4];
148 int ret
, is_packed_rgba
;
149 double var_values
[VARS_NB
], res
;
152 pad
->hsub
= pix_desc
->log2_chroma_w
;
153 pad
->vsub
= pix_desc
->log2_chroma_h
;
155 var_values
[VAR_PI
] = M_PI
;
156 var_values
[VAR_PHI
] = M_PHI
;
157 var_values
[VAR_E
] = M_E
;
158 var_values
[VAR_IN_W
] = var_values
[VAR_IW
] = inlink
->w
;
159 var_values
[VAR_IN_H
] = var_values
[VAR_IH
] = inlink
->h
;
160 var_values
[VAR_OUT_W
] = var_values
[VAR_OW
] = NAN
;
161 var_values
[VAR_OUT_H
] = var_values
[VAR_OH
] = NAN
;
162 var_values
[VAR_A
] = (double) inlink
->w
/ inlink
->h
;
163 var_values
[VAR_HSUB
] = 1<<pad
->hsub
;
164 var_values
[VAR_VSUB
] = 1<<pad
->vsub
;
166 /* evaluate width and height */
167 av_expr_parse_and_eval(&res
, (expr
= pad
->w_expr
),
168 var_names
, var_values
,
169 NULL
, NULL
, NULL
, NULL
, NULL
, 0, ctx
);
170 pad
->w
= var_values
[VAR_OUT_W
] = var_values
[VAR_OW
] = res
;
171 if ((ret
= av_expr_parse_and_eval(&res
, (expr
= pad
->h_expr
),
172 var_names
, var_values
,
173 NULL
, NULL
, NULL
, NULL
, NULL
, 0, ctx
)) < 0)
175 pad
->h
= var_values
[VAR_OUT_H
] = var_values
[VAR_OH
] = res
;
176 /* evaluate the width again, as it may depend on the evaluated output height */
177 if ((ret
= av_expr_parse_and_eval(&res
, (expr
= pad
->w_expr
),
178 var_names
, var_values
,
179 NULL
, NULL
, NULL
, NULL
, NULL
, 0, ctx
)) < 0)
181 pad
->w
= var_values
[VAR_OUT_W
] = var_values
[VAR_OW
] = res
;
183 /* evaluate x and y */
184 av_expr_parse_and_eval(&res
, (expr
= pad
->x_expr
),
185 var_names
, var_values
,
186 NULL
, NULL
, NULL
, NULL
, NULL
, 0, ctx
);
187 pad
->x
= var_values
[VAR_X
] = res
;
188 if ((ret
= av_expr_parse_and_eval(&res
, (expr
= pad
->y_expr
),
189 var_names
, var_values
,
190 NULL
, NULL
, NULL
, NULL
, NULL
, 0, ctx
)) < 0)
192 pad
->y
= var_values
[VAR_Y
] = res
;
193 /* evaluate x again, as it may depend on the evaluated y value */
194 if ((ret
= av_expr_parse_and_eval(&res
, (expr
= pad
->x_expr
),
195 var_names
, var_values
,
196 NULL
, NULL
, NULL
, NULL
, NULL
, 0, ctx
)) < 0)
198 pad
->x
= var_values
[VAR_X
] = res
;
200 /* sanity check params */
201 if (pad
->w
< 0 || pad
->h
< 0 || pad
->x
< 0 || pad
->y
< 0) {
202 av_log(ctx
, AV_LOG_ERROR
, "Negative values are not acceptable.\n");
203 return AVERROR(EINVAL
);
211 pad
->w
&= ~((1 << pad
->hsub
) - 1);
212 pad
->h
&= ~((1 << pad
->vsub
) - 1);
213 pad
->x
&= ~((1 << pad
->hsub
) - 1);
214 pad
->y
&= ~((1 << pad
->vsub
) - 1);
216 pad
->in_w
= inlink
->w
& ~((1 << pad
->hsub
) - 1);
217 pad
->in_h
= inlink
->h
& ~((1 << pad
->vsub
) - 1);
219 memcpy(rgba_color
, pad
->color
, sizeof(rgba_color
));
220 ff_fill_line_with_color(pad
->line
, pad
->line_step
, pad
->w
, pad
->color
,
221 inlink
->format
, rgba_color
, &is_packed_rgba
, NULL
);
223 av_log(ctx
, AV_LOG_VERBOSE
, "w:%d h:%d -> w:%d h:%d x:%d y:%d color:0x%02X%02X%02X%02X[%s]\n",
224 inlink
->w
, inlink
->h
, pad
->w
, pad
->h
, pad
->x
, pad
->y
,
225 pad
->color
[0], pad
->color
[1], pad
->color
[2], pad
->color
[3],
226 is_packed_rgba
? "rgba" : "yuva");
228 if (pad
->x
< 0 || pad
->y
< 0 ||
229 pad
->w
<= 0 || pad
->h
<= 0 ||
230 (unsigned)pad
->x
+ (unsigned)inlink
->w
> pad
->w
||
231 (unsigned)pad
->y
+ (unsigned)inlink
->h
> pad
->h
) {
232 av_log(ctx
, AV_LOG_ERROR
,
233 "Input area %d:%d:%d:%d not within the padded area 0:0:%d:%d or zero-sized\n",
234 pad
->x
, pad
->y
, pad
->x
+ inlink
->w
, pad
->y
+ inlink
->h
, pad
->w
, pad
->h
);
235 return AVERROR(EINVAL
);
241 av_log(NULL
, AV_LOG_ERROR
,
242 "Error when evaluating the expression '%s'\n", expr
);
247 static int config_output(AVFilterLink
*outlink
)
249 PadContext
*pad
= outlink
->src
->priv
;
256 static AVFilterBufferRef
*get_video_buffer(AVFilterLink
*inlink
, int perms
, int w
, int h
)
258 PadContext
*pad
= inlink
->dst
->priv
;
260 AVFilterBufferRef
*picref
= ff_get_video_buffer(inlink
->dst
->outputs
[0], perms
,
261 w
+ (pad
->w
- pad
->in_w
),
262 h
+ (pad
->h
- pad
->in_h
));
268 picref
->video
->w
= w
;
269 picref
->video
->h
= h
;
271 for (plane
= 0; plane
< 4 && picref
->data
[plane
]; plane
++) {
272 int hsub
= (plane
== 1 || plane
== 2) ? pad
->hsub
: 0;
273 int vsub
= (plane
== 1 || plane
== 2) ? pad
->vsub
: 0;
275 picref
->data
[plane
] += (pad
->x
>> hsub
) * pad
->line_step
[plane
] +
276 (pad
->y
>> vsub
) * picref
->linesize
[plane
];
282 static int does_clip(PadContext
*pad
, AVFilterBufferRef
*outpicref
, int plane
, int hsub
, int vsub
, int x
, int y
)
284 int64_t x_in_buf
, y_in_buf
;
286 x_in_buf
= outpicref
->data
[plane
] - outpicref
->buf
->data
[plane
]
287 + (x
>> hsub
) * pad
->line_step
[plane
]
288 + (y
>> vsub
) * outpicref
->linesize
[plane
];
290 if(x_in_buf
< 0 || x_in_buf
% pad
->line_step
[plane
])
292 x_in_buf
/= pad
->line_step
[plane
];
294 av_assert0(outpicref
->buf
->linesize
[plane
]>0); //while reference can use negative linesize the main buffer should not
296 y_in_buf
= x_in_buf
/ outpicref
->buf
->linesize
[plane
];
297 x_in_buf
%= outpicref
->buf
->linesize
[plane
];
299 if( y_in_buf
<<vsub
>= outpicref
->buf
->h
300 || x_in_buf
<<hsub
>= outpicref
->buf
->w
)
305 static int filter_frame(AVFilterLink
*inlink
, AVFilterBufferRef
*in
)
307 PadContext
*pad
= inlink
->dst
->priv
;
308 AVFilterBufferRef
*out
= avfilter_ref_buffer(in
, ~0);
309 int plane
, needs_copy
;
312 avfilter_unref_bufferp(&in
);
313 return AVERROR(ENOMEM
);
316 for (plane
= 0; plane
< 4 && out
->data
[plane
]; plane
++) {
317 int hsub
= (plane
== 1 || plane
== 2) ? pad
->hsub
: 0;
318 int vsub
= (plane
== 1 || plane
== 2) ? pad
->vsub
: 0;
320 av_assert0(out
->buf
->w
> 0 && out
->buf
->h
> 0);
322 if (out
->format
!= out
->buf
->format
) //unsupported currently
325 out
->data
[plane
] -= (pad
->x
>> hsub
) * pad
->line_step
[plane
] +
326 (pad
->y
>> vsub
) * out
->linesize
[plane
];
328 if (does_clip(pad
, out
, plane
, hsub
, vsub
, 0, 0) ||
329 does_clip(pad
, out
, plane
, hsub
, vsub
, 0, pad
->h
- 1) ||
330 does_clip(pad
, out
, plane
, hsub
, vsub
, pad
->w
- 1, 0) ||
331 does_clip(pad
, out
, plane
, hsub
, vsub
, pad
->w
- 1, pad
->h
- 1))
334 needs_copy
= plane
< 4 && out
->data
[plane
];
336 av_log(inlink
->dst
, AV_LOG_DEBUG
, "Direct padding impossible allocating new frame\n");
337 avfilter_unref_buffer(out
);
338 out
= ff_get_video_buffer(inlink
->dst
->outputs
[0], AV_PERM_WRITE
| AV_PERM_NEG_LINESIZES
,
339 FFMAX(inlink
->w
, pad
->w
),
340 FFMAX(inlink
->h
, pad
->h
));
342 avfilter_unref_bufferp(&in
);
343 return AVERROR(ENOMEM
);
346 avfilter_copy_buffer_ref_props(out
, in
);
349 out
->video
->w
= pad
->w
;
350 out
->video
->h
= pad
->h
;
354 ff_draw_rectangle(out
->data
, out
->linesize
,
355 pad
->line
, pad
->line_step
, pad
->hsub
, pad
->vsub
,
356 0, 0, pad
->w
, pad
->y
);
360 if (pad
->h
> pad
->y
+ pad
->in_h
) {
361 ff_draw_rectangle(out
->data
, out
->linesize
,
362 pad
->line
, pad
->line_step
, pad
->hsub
, pad
->vsub
,
363 0, pad
->y
+ pad
->in_h
, pad
->w
, pad
->h
- pad
->y
- pad
->in_h
);
367 ff_draw_rectangle(out
->data
, out
->linesize
, pad
->line
, pad
->line_step
,
368 pad
->hsub
, pad
->vsub
, 0, pad
->y
, pad
->x
, in
->video
->h
);
371 ff_copy_rectangle(out
->data
, out
->linesize
, in
->data
, in
->linesize
,
372 pad
->line_step
, pad
->hsub
, pad
->vsub
,
373 pad
->x
, pad
->y
, 0, in
->video
->w
, in
->video
->h
);
377 ff_draw_rectangle(out
->data
, out
->linesize
,
378 pad
->line
, pad
->line_step
, pad
->hsub
, pad
->vsub
,
379 pad
->x
+ pad
->in_w
, pad
->y
, pad
->w
- pad
->x
- pad
->in_w
,
382 avfilter_unref_bufferp(&in
);
383 return ff_filter_frame(inlink
->dst
->outputs
[0], out
);
386 static const AVFilterPad avfilter_vf_pad_inputs
[] = {
389 .type
= AVMEDIA_TYPE_VIDEO
,
390 .config_props
= config_input
,
391 .get_video_buffer
= get_video_buffer
,
392 .filter_frame
= filter_frame
,
397 static const AVFilterPad avfilter_vf_pad_outputs
[] = {
400 .type
= AVMEDIA_TYPE_VIDEO
,
401 .config_props
= config_output
,
406 AVFilter avfilter_vf_pad
= {
408 .description
= NULL_IF_CONFIG_SMALL("Pad input image to width:height[:x:y[:color]] (default x and y: 0, default color: black)."),
410 .priv_size
= sizeof(PadContext
),
413 .query_formats
= query_formats
,
415 .inputs
= avfilter_vf_pad_inputs
,
417 .outputs
= avfilter_vf_pad_outputs
,