2 * copyright (c) 2008 vmrsss
3 * copyright (c) 2009 Stefano Sabatini
5 * This file is part of FFmpeg.
7 * FFmpeg 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 * 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.
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
23 * @file libavfilter/vf_pad.c
24 * video padding filter
28 #include "parseutils.h"
29 #include "libavutil/pixdesc.h"
30 #include "libavcodec/colorspace.h"
33 int w
, h
; ///< output dimensions, a value of 0 will result in the input size
34 int x
, y
; ///< offsets of the input area with respect to the padded area
35 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
37 uint8_t color
[4]; ///< color expressed either in YUVA or RGBA colorspace for the padding area
40 int hsub
, vsub
; ///< chroma subsampling values
43 static av_cold
int init(AVFilterContext
*ctx
, const char *args
, void *opaque
)
45 PadContext
*pad
= ctx
->priv
;
46 char color_string
[128] = "black";
49 sscanf(args
, "%d:%d:%d:%d:%s", &pad
->w
, &pad
->h
, &pad
->x
, &pad
->y
, color_string
);
51 if (av_parse_color(pad
->color
, color_string
, ctx
) < 0)
54 /* sanity check params */
55 if (pad
->w
< 0 || pad
->h
< 0) {
56 av_log(ctx
, AV_LOG_ERROR
, "Negative size values are not acceptable.\n");
63 static av_cold
void uninit(AVFilterContext
*ctx
)
65 PadContext
*pad
= ctx
->priv
;
68 for (i
= 0; i
< 4; i
++) {
69 av_freep(&pad
->line
[i
]);
70 pad
->line_step
[i
] = 0;
74 static int query_formats(AVFilterContext
*ctx
)
76 static const enum PixelFormat pix_fmts
[] = {
77 PIX_FMT_ARGB
, PIX_FMT_RGBA
,
78 PIX_FMT_ABGR
, PIX_FMT_BGRA
,
79 PIX_FMT_RGB24
, PIX_FMT_BGR24
,
81 PIX_FMT_YUV444P
, PIX_FMT_YUV422P
,
82 PIX_FMT_YUV420P
, PIX_FMT_YUV411P
,
83 PIX_FMT_YUV410P
, PIX_FMT_YUV440P
,
84 PIX_FMT_YUVJ444P
, PIX_FMT_YUVJ422P
,
85 PIX_FMT_YUVJ420P
, PIX_FMT_YUVJ440P
,
91 avfilter_set_common_formats(ctx
, avfilter_make_format_list(pix_fmts
));
95 enum { RED
= 0, GREEN
, BLUE
, ALPHA
};
97 static int config_input(AVFilterLink
*inlink
)
99 AVFilterContext
*ctx
= inlink
->dst
;
100 PadContext
*pad
= ctx
->priv
;
101 const AVPixFmtDescriptor
*pix_desc
= &av_pix_fmt_descriptors
[inlink
->format
];
102 uint8_t rgba_color
[4];
104 int i
, is_packed_rgb
= 1;
106 switch (inlink
->format
) {
108 rgba_map
[ALPHA
] = 0; rgba_map
[RED
] = 1; rgba_map
[GREEN
] = 2; rgba_map
[BLUE
] = 3;
111 rgba_map
[ALPHA
] = 0; rgba_map
[BLUE
] = 1; rgba_map
[GREEN
] = 2; rgba_map
[RED
] = 3;
115 rgba_map
[RED
] = 0; rgba_map
[GREEN
] = 1; rgba_map
[BLUE
] = 2; rgba_map
[ALPHA
] = 3;
119 rgba_map
[BLUE
] = 0; rgba_map
[GREEN
] = 1; rgba_map
[RED
] = 2; rgba_map
[ALPHA
] = 3;
125 pad
->hsub
= pix_desc
->log2_chroma_w
;
126 pad
->vsub
= pix_desc
->log2_chroma_h
;
133 pad
->w
&= ~((1 << pad
->hsub
) - 1);
134 pad
->h
&= ~((1 << pad
->vsub
) - 1);
135 pad
->x
&= ~((1 << pad
->hsub
) - 1);
136 pad
->y
&= ~((1 << pad
->vsub
) - 1);
138 pad
->in_w
= inlink
->w
& ~((1 << pad
->hsub
) - 1);
139 pad
->in_h
= inlink
->h
& ~((1 << pad
->vsub
) - 1);
141 memcpy(rgba_color
, pad
->color
, sizeof(rgba_color
));
143 pad
->line_step
[0] = (av_get_bits_per_pixel(&av_pix_fmt_descriptors
[inlink
->format
]))>>3;
144 for (i
= 0; i
< 4; i
++)
145 pad
->color
[rgba_map
[i
]] = rgba_color
[i
];
147 pad
->line
[0] = av_malloc(pad
->w
* pad
->line_step
[0]);
148 for (i
= 0; i
< pad
->w
; i
++)
149 memcpy(pad
->line
[0] + i
* pad
->line_step
[0], pad
->color
, pad
->line_step
[0]);
153 pad
->color
[0] = RGB_TO_Y_CCIR(rgba_color
[0], rgba_color
[1], rgba_color
[2]);
154 pad
->color
[1] = RGB_TO_U_CCIR(rgba_color
[0], rgba_color
[1], rgba_color
[2], 0);
155 pad
->color
[2] = RGB_TO_V_CCIR(rgba_color
[0], rgba_color
[1], rgba_color
[2], 0);
156 pad
->color
[3] = rgba_color
[3];
158 for (plane
= 0; plane
< 4; plane
++) {
160 int hsub
= (plane
== 1 || plane
== 2) ? pad
->hsub
: 0;
162 pad
->line_step
[plane
] = 1;
163 line_size
= (pad
->w
>> hsub
) * pad
->line_step
[plane
];
164 pad
->line
[plane
] = av_malloc(line_size
);
165 memset(pad
->line
[plane
], pad
->color
[plane
], line_size
);
169 av_log(ctx
, AV_LOG_INFO
, "w:%d h:%d x:%d y:%d color:0x%02X%02X%02X%02X[%s]\n",
170 pad
->w
, pad
->h
, pad
->x
, pad
->y
,
171 pad
->color
[0], pad
->color
[1], pad
->color
[2], pad
->color
[3],
172 is_packed_rgb ?
"rgba" : "yuva");
174 if (pad
->x
< 0 || pad
->y
< 0 ||
175 pad
->w
<= 0 || pad
->h
<= 0 ||
176 (unsigned)pad
->x
+ (unsigned)inlink
->w
> pad
->w
||
177 (unsigned)pad
->y
+ (unsigned)inlink
->h
> pad
->h
) {
178 av_log(ctx
, AV_LOG_ERROR
,
179 "Input area %d:%d:%d:%d not within the padded area 0:0:%d:%d or zero-sized\n",
180 pad
->x
, pad
->y
, pad
->x
+ inlink
->w
, pad
->y
+ inlink
->h
, pad
->w
, pad
->h
);
187 static int config_output(AVFilterLink
*outlink
)
189 PadContext
*pad
= outlink
->src
->priv
;
196 static AVFilterPicRef
*get_video_buffer(AVFilterLink
*inlink
, int perms
, int w
, int h
)
198 PadContext
*pad
= inlink
->dst
->priv
;
200 AVFilterPicRef
*picref
= avfilter_get_video_buffer(inlink
->dst
->outputs
[0], perms
,
201 w
+ (pad
->w
- pad
->in_w
),
202 h
+ (pad
->h
- pad
->in_h
));
205 for (plane
= 0; plane
< 4 && picref
->data
[plane
]; plane
++) {
206 int hsub
= (plane
== 1 || plane
== 2) ? pad
->hsub
: 0;
207 int vsub
= (plane
== 1 || plane
== 2) ? pad
->vsub
: 0;
209 picref
->data
[plane
] += (pad
->x
>> hsub
) * pad
->line_step
[plane
] +
210 (pad
->y
>> vsub
) * picref
->linesize
[plane
];
216 static void start_frame(AVFilterLink
*inlink
, AVFilterPicRef
*inpicref
)
218 PadContext
*pad
= inlink
->dst
->priv
;
219 AVFilterPicRef
*outpicref
= avfilter_ref_pic(inpicref
, ~0);
222 inlink
->dst
->outputs
[0]->outpic
= outpicref
;
224 for (plane
= 0; plane
< 4 && outpicref
->data
[plane
]; plane
++) {
225 int hsub
= (plane
== 1 || plane
== 2) ? pad
->hsub
: 0;
226 int vsub
= (plane
== 1 || plane
== 2) ? pad
->vsub
: 0;
228 outpicref
->data
[plane
] -= (pad
->x
>> hsub
) * pad
->line_step
[plane
] +
229 (pad
->y
>> vsub
) * outpicref
->linesize
[plane
];
232 avfilter_start_frame(inlink
->dst
->outputs
[0], outpicref
);
235 static void end_frame(AVFilterLink
*link
)
237 avfilter_end_frame(link
->dst
->outputs
[0]);
238 avfilter_unref_pic(link
->cur_pic
);
241 static void draw_rectangle(AVFilterPicRef
*outpic
, uint8_t *line
[4], int line_step
[4],
242 int hsub
, int vsub
, int x
, int y
, int w
, int h
)
247 for (plane
= 0; plane
< 4 && outpic
->data
[plane
]; plane
++) {
248 int hsub1
= plane
== 1 || plane
== 2 ? hsub
: 0;
249 int vsub1
= plane
== 1 || plane
== 2 ? vsub
: 0;
251 p
= outpic
->data
[plane
] + (y
>> vsub1
) * outpic
->linesize
[plane
];
252 for (i
= 0; i
< (h
>> vsub1
); i
++) {
253 memcpy(p
+ (x
>> hsub1
) * line_step
[plane
], line
[plane
], (w
>> hsub1
) * line_step
[plane
]);
254 p
+= outpic
->linesize
[plane
];
259 static void draw_send_bar_slice(AVFilterLink
*link
, int y
, int h
, int slice_dir
, int before_slice
)
261 PadContext
*pad
= link
->dst
->priv
;
262 int bar_y
, bar_h
= 0;
264 if (slice_dir
* before_slice
== 1 && y
== pad
->y
) {
268 } else if (slice_dir
* before_slice
== -1 && (y
+ h
) == (pad
->y
+ pad
->in_h
)) {
270 bar_y
= pad
->y
+ pad
->in_h
;
271 bar_h
= pad
->h
- pad
->in_h
- pad
->y
;
275 draw_rectangle(link
->dst
->outputs
[0]->outpic
,
276 pad
->line
, pad
->line_step
, pad
->hsub
, pad
->vsub
,
277 0, bar_y
, pad
->w
, bar_h
);
278 avfilter_draw_slice(link
->dst
->outputs
[0], bar_y
, bar_h
, slice_dir
);
282 static void draw_slice(AVFilterLink
*link
, int y
, int h
, int slice_dir
)
284 PadContext
*pad
= link
->dst
->priv
;
285 AVFilterPicRef
*outpic
= link
->dst
->outputs
[0]->outpic
;
289 y
&= ~((1 << pad
->vsub
) - 1);
290 h
&= ~((1 << pad
->vsub
) - 1);
294 draw_send_bar_slice(link
, y
, h
, slice_dir
, 1);
297 draw_rectangle(outpic
, pad
->line
, pad
->line_step
, pad
->hsub
, pad
->vsub
,
300 draw_rectangle(outpic
, pad
->line
, pad
->line_step
, pad
->hsub
, pad
->vsub
,
301 pad
->x
+ pad
->in_w
, y
, pad
->w
- pad
->x
- pad
->in_w
, h
);
302 avfilter_draw_slice(link
->dst
->outputs
[0], y
, h
, slice_dir
);
304 draw_send_bar_slice(link
, y
, h
, slice_dir
, -1);
307 AVFilter avfilter_vf_pad
= {
309 .description
= "Add pads to the input image.",
311 .priv_size
= sizeof(PadContext
),
314 .query_formats
= query_formats
,
316 .inputs
= (AVFilterPad
[]) {{ .name
= "default",
317 .type
= AVMEDIA_TYPE_VIDEO
,
318 .config_props
= config_input
,
319 .get_video_buffer
= get_video_buffer
,
320 .start_frame
= start_frame
,
321 .draw_slice
= draw_slice
,
322 .end_frame
= end_frame
, },
325 .outputs
= (AVFilterPad
[]) {{ .name
= "default",
326 .type
= AVMEDIA_TYPE_VIDEO
,
327 .config_props
= config_output
, },