ffmpeg / libavfilter / vf_pad.c @ d0b9b91b
History | View | Annotate | Download (11.4 KB)
1 | 3c940173 | Michael Niedermayer | /*
|
---|---|---|---|
2 | * copyright (c) 2008 vmrsss
|
||
3 | * copyright (c) 2009 Stefano Sabatini
|
||
4 | *
|
||
5 | * This file is part of FFmpeg.
|
||
6 | *
|
||
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.
|
||
11 | *
|
||
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.
|
||
16 | *
|
||
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
|
||
20 | */
|
||
21 | |||
22 | /**
|
||
23 | aeaa9630 | Stefano Sabatini | * @file
|
24 | 3c940173 | Michael Niedermayer | * video padding filter
|
25 | */
|
||
26 | |||
27 | #include "avfilter.h" |
||
28 | #include "parseutils.h" |
||
29 | #include "libavutil/pixdesc.h" |
||
30 | 2b4abbd6 | Stefano Sabatini | #include "libavutil/colorspace.h" |
31 | 3c940173 | Michael Niedermayer | |
32 | 039baa78 | Stefano Sabatini | enum { RED = 0, GREEN, BLUE, ALPHA }; |
33 | |||
34 | static int fill_line_with_color(uint8_t *line[4], int line_step[4], int w, uint8_t color[4], |
||
35 | enum PixelFormat pix_fmt, uint8_t rgba_color[4], int *is_packed_rgba) |
||
36 | { |
||
37 | uint8_t rgba_map[4] = {0}; |
||
38 | int i;
|
||
39 | const AVPixFmtDescriptor *pix_desc = &av_pix_fmt_descriptors[pix_fmt];
|
||
40 | int hsub = pix_desc->log2_chroma_w;
|
||
41 | |||
42 | *is_packed_rgba = 1;
|
||
43 | switch (pix_fmt) {
|
||
44 | case PIX_FMT_ARGB: rgba_map[ALPHA] = 0; rgba_map[RED ] = 1; rgba_map[GREEN] = 2; rgba_map[BLUE ] = 3; break; |
||
45 | case PIX_FMT_ABGR: rgba_map[ALPHA] = 0; rgba_map[BLUE ] = 1; rgba_map[GREEN] = 2; rgba_map[RED ] = 3; break; |
||
46 | case PIX_FMT_RGBA:
|
||
47 | case PIX_FMT_RGB24: rgba_map[RED ] = 0; rgba_map[GREEN] = 1; rgba_map[BLUE ] = 2; rgba_map[ALPHA] = 3; break; |
||
48 | case PIX_FMT_BGRA:
|
||
49 | case PIX_FMT_BGR24: rgba_map[BLUE ] = 0; rgba_map[GREEN] = 1; rgba_map[RED ] = 2; rgba_map[ALPHA] = 3; break; |
||
50 | default:
|
||
51 | *is_packed_rgba = 0;
|
||
52 | } |
||
53 | |||
54 | if (*is_packed_rgba) {
|
||
55 | line_step[0] = (av_get_bits_per_pixel(pix_desc))>>3; |
||
56 | for (i = 0; i < 4; i++) |
||
57 | color[rgba_map[i]] = rgba_color[i]; |
||
58 | |||
59 | line[0] = av_malloc(w * line_step[0]); |
||
60 | for (i = 0; i < w; i++) |
||
61 | memcpy(line[0] + i * line_step[0], color, line_step[0]); |
||
62 | } else {
|
||
63 | int plane;
|
||
64 | |||
65 | d0b9b91b | Måns Rullgård | color[RED ] = RGB_TO_Y_CCIR(rgba_color[0], rgba_color[1], rgba_color[2]); |
66 | color[GREEN] = RGB_TO_U_CCIR(rgba_color[0], rgba_color[1], rgba_color[2], 0); |
||
67 | color[BLUE ] = RGB_TO_V_CCIR(rgba_color[0], rgba_color[1], rgba_color[2], 0); |
||
68 | 039baa78 | Stefano Sabatini | color[ALPHA] = rgba_color[3];
|
69 | |||
70 | for (plane = 0; plane < 4; plane++) { |
||
71 | int line_size;
|
||
72 | int hsub1 = (plane == 1 || plane == 2) ? hsub : 0; |
||
73 | |||
74 | line_step[plane] = 1;
|
||
75 | line_size = (w >> hsub1) * line_step[plane]; |
||
76 | line[plane] = av_malloc(line_size); |
||
77 | memset(line[plane], color[plane], line_size); |
||
78 | } |
||
79 | } |
||
80 | |||
81 | return 0; |
||
82 | } |
||
83 | |||
84 | 1b364fd2 | Stefano Sabatini | static void draw_rectangle(AVFilterPicRef *outpic, uint8_t *line[4], int line_step[4], |
85 | int hsub, int vsub, int x, int y, int w, int h) |
||
86 | { |
||
87 | int i, plane;
|
||
88 | uint8_t *p; |
||
89 | |||
90 | for (plane = 0; plane < 4 && outpic->data[plane]; plane++) { |
||
91 | int hsub1 = plane == 1 || plane == 2 ? hsub : 0; |
||
92 | int vsub1 = plane == 1 || plane == 2 ? vsub : 0; |
||
93 | |||
94 | p = outpic->data[plane] + (y >> vsub1) * outpic->linesize[plane]; |
||
95 | for (i = 0; i < (h >> vsub1); i++) { |
||
96 | memcpy(p + (x >> hsub1) * line_step[plane], line[plane], (w >> hsub1) * line_step[plane]); |
||
97 | p += outpic->linesize[plane]; |
||
98 | } |
||
99 | } |
||
100 | } |
||
101 | |||
102 | static int query_formats(AVFilterContext *ctx) |
||
103 | { |
||
104 | static const enum PixelFormat pix_fmts[] = { |
||
105 | PIX_FMT_ARGB, PIX_FMT_RGBA, |
||
106 | PIX_FMT_ABGR, PIX_FMT_BGRA, |
||
107 | PIX_FMT_RGB24, PIX_FMT_BGR24, |
||
108 | |||
109 | PIX_FMT_YUV444P, PIX_FMT_YUV422P, |
||
110 | PIX_FMT_YUV420P, PIX_FMT_YUV411P, |
||
111 | PIX_FMT_YUV410P, PIX_FMT_YUV440P, |
||
112 | PIX_FMT_YUVJ444P, PIX_FMT_YUVJ422P, |
||
113 | PIX_FMT_YUVJ420P, PIX_FMT_YUVJ440P, |
||
114 | PIX_FMT_YUVA420P, |
||
115 | |||
116 | PIX_FMT_NONE |
||
117 | }; |
||
118 | |||
119 | avfilter_set_common_formats(ctx, avfilter_make_format_list(pix_fmts)); |
||
120 | return 0; |
||
121 | } |
||
122 | |||
123 | #if CONFIG_PAD_FILTER
|
||
124 | |||
125 | 3c940173 | Michael Niedermayer | typedef struct { |
126 | int w, h; ///< output dimensions, a value of 0 will result in the input size |
||
127 | int x, y; ///< offsets of the input area with respect to the padded area |
||
128 | 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 |
||
129 | |||
130 | uint8_t color[4]; ///< color expressed either in YUVA or RGBA colorspace for the padding area |
||
131 | uint8_t *line[4];
|
||
132 | int line_step[4]; |
||
133 | int hsub, vsub; ///< chroma subsampling values |
||
134 | } PadContext; |
||
135 | |||
136 | static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque) |
||
137 | { |
||
138 | PadContext *pad = ctx->priv; |
||
139 | char color_string[128] = "black"; |
||
140 | |||
141 | if (args)
|
||
142 | sscanf(args, "%d:%d:%d:%d:%s", &pad->w, &pad->h, &pad->x, &pad->y, color_string);
|
||
143 | |||
144 | if (av_parse_color(pad->color, color_string, ctx) < 0) |
||
145 | 622e6ce5 | Stefano Sabatini | return AVERROR(EINVAL);
|
146 | 3c940173 | Michael Niedermayer | |
147 | /* sanity check params */
|
||
148 | if (pad->w < 0 || pad->h < 0) { |
||
149 | av_log(ctx, AV_LOG_ERROR, "Negative size values are not acceptable.\n");
|
||
150 | 622e6ce5 | Stefano Sabatini | return AVERROR(EINVAL);
|
151 | 3c940173 | Michael Niedermayer | } |
152 | |||
153 | return 0; |
||
154 | } |
||
155 | |||
156 | static av_cold void uninit(AVFilterContext *ctx) |
||
157 | { |
||
158 | PadContext *pad = ctx->priv; |
||
159 | int i;
|
||
160 | |||
161 | for (i = 0; i < 4; i++) { |
||
162 | av_freep(&pad->line[i]); |
||
163 | pad->line_step[i] = 0;
|
||
164 | } |
||
165 | } |
||
166 | |||
167 | static int config_input(AVFilterLink *inlink) |
||
168 | { |
||
169 | AVFilterContext *ctx = inlink->dst; |
||
170 | PadContext *pad = ctx->priv; |
||
171 | const AVPixFmtDescriptor *pix_desc = &av_pix_fmt_descriptors[inlink->format];
|
||
172 | uint8_t rgba_color[4];
|
||
173 | 039baa78 | Stefano Sabatini | int is_packed_rgba;
|
174 | 3c940173 | Michael Niedermayer | |
175 | pad->hsub = pix_desc->log2_chroma_w; |
||
176 | pad->vsub = pix_desc->log2_chroma_h; |
||
177 | |||
178 | if (!pad->w)
|
||
179 | pad->w = inlink->w; |
||
180 | if (!pad->h)
|
||
181 | pad->h = inlink->h; |
||
182 | |||
183 | pad->w &= ~((1 << pad->hsub) - 1); |
||
184 | pad->h &= ~((1 << pad->vsub) - 1); |
||
185 | pad->x &= ~((1 << pad->hsub) - 1); |
||
186 | pad->y &= ~((1 << pad->vsub) - 1); |
||
187 | |||
188 | pad->in_w = inlink->w & ~((1 << pad->hsub) - 1); |
||
189 | pad->in_h = inlink->h & ~((1 << pad->vsub) - 1); |
||
190 | |||
191 | memcpy(rgba_color, pad->color, sizeof(rgba_color));
|
||
192 | 039baa78 | Stefano Sabatini | fill_line_with_color(pad->line, pad->line_step, pad->w, pad->color, |
193 | inlink->format, rgba_color, &is_packed_rgba); |
||
194 | 3c940173 | Michael Niedermayer | |
195 | av_log(ctx, AV_LOG_INFO, "w:%d h:%d x:%d y:%d color:0x%02X%02X%02X%02X[%s]\n",
|
||
196 | pad->w, pad->h, pad->x, pad->y, |
||
197 | pad->color[0], pad->color[1], pad->color[2], pad->color[3], |
||
198 | 039baa78 | Stefano Sabatini | is_packed_rgba ? "rgba" : "yuva"); |
199 | 3c940173 | Michael Niedermayer | |
200 | if (pad->x < 0 || pad->y < 0 || |
||
201 | pad->w <= 0 || pad->h <= 0 || |
||
202 | (unsigned)pad->x + (unsigned)inlink->w > pad->w || |
||
203 | (unsigned)pad->y + (unsigned)inlink->h > pad->h) { |
||
204 | av_log(ctx, AV_LOG_ERROR, |
||
205 | "Input area %d:%d:%d:%d not within the padded area 0:0:%d:%d or zero-sized\n",
|
||
206 | pad->x, pad->y, pad->x + inlink->w, pad->y + inlink->h, pad->w, pad->h); |
||
207 | 622e6ce5 | Stefano Sabatini | return AVERROR(EINVAL);
|
208 | 3c940173 | Michael Niedermayer | } |
209 | |||
210 | return 0; |
||
211 | } |
||
212 | |||
213 | static int config_output(AVFilterLink *outlink) |
||
214 | { |
||
215 | PadContext *pad = outlink->src->priv; |
||
216 | |||
217 | outlink->w = pad->w; |
||
218 | outlink->h = pad->h; |
||
219 | return 0; |
||
220 | } |
||
221 | |||
222 | static AVFilterPicRef *get_video_buffer(AVFilterLink *inlink, int perms, int w, int h) |
||
223 | { |
||
224 | PadContext *pad = inlink->dst->priv; |
||
225 | |||
226 | AVFilterPicRef *picref = avfilter_get_video_buffer(inlink->dst->outputs[0], perms,
|
||
227 | w + (pad->w - pad->in_w), |
||
228 | h + (pad->h - pad->in_h)); |
||
229 | f66b3905 | Måns Rullgård | int plane;
|
230 | 3c940173 | Michael Niedermayer | |
231 | f66b3905 | Måns Rullgård | for (plane = 0; plane < 4 && picref->data[plane]; plane++) { |
232 | 3c940173 | Michael Niedermayer | int hsub = (plane == 1 || plane == 2) ? pad->hsub : 0; |
233 | int vsub = (plane == 1 || plane == 2) ? pad->vsub : 0; |
||
234 | |||
235 | picref->data[plane] += (pad->x >> hsub) * pad->line_step[plane] + |
||
236 | (pad->y >> vsub) * picref->linesize[plane]; |
||
237 | } |
||
238 | |||
239 | return picref;
|
||
240 | } |
||
241 | |||
242 | static void start_frame(AVFilterLink *inlink, AVFilterPicRef *inpicref) |
||
243 | { |
||
244 | PadContext *pad = inlink->dst->priv; |
||
245 | AVFilterPicRef *outpicref = avfilter_ref_pic(inpicref, ~0);
|
||
246 | f66b3905 | Måns Rullgård | int plane;
|
247 | |||
248 | 3c940173 | Michael Niedermayer | inlink->dst->outputs[0]->outpic = outpicref;
|
249 | |||
250 | f66b3905 | Måns Rullgård | for (plane = 0; plane < 4 && outpicref->data[plane]; plane++) { |
251 | 3c940173 | Michael Niedermayer | int hsub = (plane == 1 || plane == 2) ? pad->hsub : 0; |
252 | int vsub = (plane == 1 || plane == 2) ? pad->vsub : 0; |
||
253 | |||
254 | outpicref->data[plane] -= (pad->x >> hsub) * pad->line_step[plane] + |
||
255 | (pad->y >> vsub) * outpicref->linesize[plane]; |
||
256 | } |
||
257 | |||
258 | avfilter_start_frame(inlink->dst->outputs[0], outpicref);
|
||
259 | } |
||
260 | |||
261 | static void end_frame(AVFilterLink *link) |
||
262 | { |
||
263 | avfilter_end_frame(link->dst->outputs[0]);
|
||
264 | avfilter_unref_pic(link->cur_pic); |
||
265 | } |
||
266 | |||
267 | static void draw_send_bar_slice(AVFilterLink *link, int y, int h, int slice_dir, int before_slice) |
||
268 | { |
||
269 | PadContext *pad = link->dst->priv; |
||
270 | int bar_y, bar_h = 0; |
||
271 | |||
272 | if (slice_dir * before_slice == 1 && y == pad->y) { |
||
273 | /* top bar */
|
||
274 | bar_y = 0;
|
||
275 | bar_h = pad->y; |
||
276 | } else if (slice_dir * before_slice == -1 && (y + h) == (pad->y + pad->in_h)) { |
||
277 | /* bottom bar */
|
||
278 | bar_y = pad->y + pad->in_h; |
||
279 | bar_h = pad->h - pad->in_h - pad->y; |
||
280 | } |
||
281 | |||
282 | if (bar_h) {
|
||
283 | draw_rectangle(link->dst->outputs[0]->outpic,
|
||
284 | pad->line, pad->line_step, pad->hsub, pad->vsub, |
||
285 | 0, bar_y, pad->w, bar_h);
|
||
286 | avfilter_draw_slice(link->dst->outputs[0], bar_y, bar_h, slice_dir);
|
||
287 | } |
||
288 | } |
||
289 | |||
290 | static void draw_slice(AVFilterLink *link, int y, int h, int slice_dir) |
||
291 | { |
||
292 | PadContext *pad = link->dst->priv; |
||
293 | AVFilterPicRef *outpic = link->dst->outputs[0]->outpic;
|
||
294 | |||
295 | y += pad->y; |
||
296 | |||
297 | y &= ~((1 << pad->vsub) - 1); |
||
298 | h &= ~((1 << pad->vsub) - 1); |
||
299 | |||
300 | if (!h)
|
||
301 | return;
|
||
302 | draw_send_bar_slice(link, y, h, slice_dir, 1);
|
||
303 | |||
304 | /* left border */
|
||
305 | draw_rectangle(outpic, pad->line, pad->line_step, pad->hsub, pad->vsub, |
||
306 | 0, y, pad->x, h);
|
||
307 | /* right border */
|
||
308 | draw_rectangle(outpic, pad->line, pad->line_step, pad->hsub, pad->vsub, |
||
309 | pad->x + pad->in_w, y, pad->w - pad->x - pad->in_w, h); |
||
310 | avfilter_draw_slice(link->dst->outputs[0], y, h, slice_dir);
|
||
311 | |||
312 | draw_send_bar_slice(link, y, h, slice_dir, -1);
|
||
313 | } |
||
314 | |||
315 | AVFilter avfilter_vf_pad = { |
||
316 | .name = "pad",
|
||
317 | 478b9d74 | Stefano Sabatini | .description = NULL_IF_CONFIG_SMALL("Pad input image to width:height[:x:y[:color]] (default x and y: 0, default color: black)."),
|
318 | 3c940173 | Michael Niedermayer | |
319 | .priv_size = sizeof(PadContext),
|
||
320 | .init = init, |
||
321 | .uninit = uninit, |
||
322 | .query_formats = query_formats, |
||
323 | |||
324 | .inputs = (AVFilterPad[]) {{ .name = "default",
|
||
325 | .type = AVMEDIA_TYPE_VIDEO, |
||
326 | .config_props = config_input, |
||
327 | .get_video_buffer = get_video_buffer, |
||
328 | .start_frame = start_frame, |
||
329 | .draw_slice = draw_slice, |
||
330 | .end_frame = end_frame, }, |
||
331 | { .name = NULL}},
|
||
332 | |||
333 | .outputs = (AVFilterPad[]) {{ .name = "default",
|
||
334 | .type = AVMEDIA_TYPE_VIDEO, |
||
335 | .config_props = config_output, }, |
||
336 | { .name = NULL}},
|
||
337 | }; |
||
338 | 1b364fd2 | Stefano Sabatini | |
339 | #endif /* CONFIG_PAD_FILTER */ |