00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00027 #include "avfilter.h"
00028 #include "libavutil/avstring.h"
00029 #include "libavutil/eval.h"
00030 #include "libavutil/pixdesc.h"
00031 #include "libavutil/colorspace.h"
00032 #include "libavutil/avassert.h"
00033 #include "libavutil/imgutils.h"
00034 #include "libavutil/parseutils.h"
00035 #include "libavutil/mathematics.h"
00036 #include "drawutils.h"
00037
00038 static const char *const var_names[] = {
00039 "in_w", "iw",
00040 "in_h", "ih",
00041 "out_w", "ow",
00042 "out_h", "oh",
00043 "x",
00044 "y",
00045 "a",
00046 "sar",
00047 "dar",
00048 "hsub",
00049 "vsub",
00050 NULL
00051 };
00052
00053 enum var_name {
00054 VAR_IN_W, VAR_IW,
00055 VAR_IN_H, VAR_IH,
00056 VAR_OUT_W, VAR_OW,
00057 VAR_OUT_H, VAR_OH,
00058 VAR_X,
00059 VAR_Y,
00060 VAR_A,
00061 VAR_SAR,
00062 VAR_DAR,
00063 VAR_HSUB,
00064 VAR_VSUB,
00065 VARS_NB
00066 };
00067
00068 static int query_formats(AVFilterContext *ctx)
00069 {
00070 avfilter_set_common_pixel_formats(ctx, ff_draw_supported_pixel_formats(0));
00071 return 0;
00072 }
00073
00074 typedef struct {
00075 int w, h;
00076 int x, y;
00077 int in_w, in_h;
00078
00079 char w_expr[256];
00080 char h_expr[256];
00081 char x_expr[256];
00082 char y_expr[256];
00083
00084 uint8_t rgba_color[4];
00085 FFDrawContext draw;
00086 FFDrawColor color;
00087 int needs_copy;
00088 } PadContext;
00089
00090 static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
00091 {
00092 PadContext *pad = ctx->priv;
00093 char color_string[128] = "black";
00094
00095 av_strlcpy(pad->w_expr, "iw", sizeof(pad->w_expr));
00096 av_strlcpy(pad->h_expr, "ih", sizeof(pad->h_expr));
00097 av_strlcpy(pad->x_expr, "0" , sizeof(pad->w_expr));
00098 av_strlcpy(pad->y_expr, "0" , sizeof(pad->h_expr));
00099
00100 if (args)
00101 sscanf(args, "%255[^:]:%255[^:]:%255[^:]:%255[^:]:%127s",
00102 pad->w_expr, pad->h_expr, pad->x_expr, pad->y_expr, color_string);
00103
00104 if (av_parse_color(pad->rgba_color, color_string, -1, ctx) < 0)
00105 return AVERROR(EINVAL);
00106
00107 return 0;
00108 }
00109
00110 static int config_input(AVFilterLink *inlink)
00111 {
00112 AVFilterContext *ctx = inlink->dst;
00113 PadContext *pad = ctx->priv;
00114 int ret;
00115 double var_values[VARS_NB], res;
00116 char *expr;
00117
00118 ff_draw_init(&pad->draw, inlink->format, 0);
00119 ff_draw_color(&pad->draw, &pad->color, pad->rgba_color);
00120
00121 var_values[VAR_IN_W] = var_values[VAR_IW] = inlink->w;
00122 var_values[VAR_IN_H] = var_values[VAR_IH] = inlink->h;
00123 var_values[VAR_OUT_W] = var_values[VAR_OW] = NAN;
00124 var_values[VAR_OUT_H] = var_values[VAR_OH] = NAN;
00125 var_values[VAR_A] = (float) inlink->w / inlink->h;
00126 var_values[VAR_SAR] = inlink->sample_aspect_ratio.num ?
00127 (float) inlink->sample_aspect_ratio.num / inlink->sample_aspect_ratio.den : 1;
00128 var_values[VAR_DAR] = var_values[VAR_A] * var_values[VAR_SAR];
00129 var_values[VAR_HSUB] = 1 << pad->draw.hsub_max;
00130 var_values[VAR_VSUB] = 1 << pad->draw.vsub_max;
00131
00132
00133 av_expr_parse_and_eval(&res, (expr = pad->w_expr),
00134 var_names, var_values,
00135 NULL, NULL, NULL, NULL, NULL, 0, ctx);
00136 pad->w = var_values[VAR_OUT_W] = var_values[VAR_OW] = res;
00137 if ((ret = av_expr_parse_and_eval(&res, (expr = pad->h_expr),
00138 var_names, var_values,
00139 NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0)
00140 goto eval_fail;
00141 pad->h = var_values[VAR_OUT_H] = var_values[VAR_OH] = res;
00142
00143 if ((ret = av_expr_parse_and_eval(&res, (expr = pad->w_expr),
00144 var_names, var_values,
00145 NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0)
00146 goto eval_fail;
00147 pad->w = var_values[VAR_OUT_W] = var_values[VAR_OW] = res;
00148
00149
00150 av_expr_parse_and_eval(&res, (expr = pad->x_expr),
00151 var_names, var_values,
00152 NULL, NULL, NULL, NULL, NULL, 0, ctx);
00153 pad->x = var_values[VAR_X] = res;
00154 if ((ret = av_expr_parse_and_eval(&res, (expr = pad->y_expr),
00155 var_names, var_values,
00156 NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0)
00157 goto eval_fail;
00158 pad->y = var_values[VAR_Y] = res;
00159
00160 if ((ret = av_expr_parse_and_eval(&res, (expr = pad->x_expr),
00161 var_names, var_values,
00162 NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0)
00163 goto eval_fail;
00164 pad->x = var_values[VAR_X] = res;
00165
00166
00167 if (pad->w < 0 || pad->h < 0 || pad->x < 0 || pad->y < 0) {
00168 av_log(ctx, AV_LOG_ERROR, "Negative values are not acceptable.\n");
00169 return AVERROR(EINVAL);
00170 }
00171
00172 if (!pad->w)
00173 pad->w = inlink->w;
00174 if (!pad->h)
00175 pad->h = inlink->h;
00176
00177 pad->w = ff_draw_round_to_sub(&pad->draw, 0, -1, pad->w);
00178 pad->h = ff_draw_round_to_sub(&pad->draw, 1, -1, pad->h);
00179 pad->x = ff_draw_round_to_sub(&pad->draw, 0, -1, pad->x);
00180 pad->y = ff_draw_round_to_sub(&pad->draw, 1, -1, pad->y);
00181 pad->in_w = ff_draw_round_to_sub(&pad->draw, 0, -1, inlink->w);
00182 pad->in_h = ff_draw_round_to_sub(&pad->draw, 1, -1, inlink->h);
00183
00184 av_log(ctx, AV_LOG_INFO, "w:%d h:%d -> w:%d h:%d x:%d y:%d color:0x%02X%02X%02X%02X\n",
00185 inlink->w, inlink->h, pad->w, pad->h, pad->x, pad->y,
00186 pad->rgba_color[0], pad->rgba_color[1], pad->rgba_color[2], pad->rgba_color[3]);
00187
00188 if (pad->x < 0 || pad->y < 0 ||
00189 pad->w <= 0 || pad->h <= 0 ||
00190 (unsigned)pad->x + (unsigned)inlink->w > pad->w ||
00191 (unsigned)pad->y + (unsigned)inlink->h > pad->h) {
00192 av_log(ctx, AV_LOG_ERROR,
00193 "Input area %d:%d:%d:%d not within the padded area 0:0:%d:%d or zero-sized\n",
00194 pad->x, pad->y, pad->x + inlink->w, pad->y + inlink->h, pad->w, pad->h);
00195 return AVERROR(EINVAL);
00196 }
00197
00198 return 0;
00199
00200 eval_fail:
00201 av_log(NULL, AV_LOG_ERROR,
00202 "Error when evaluating the expression '%s'\n", expr);
00203 return ret;
00204
00205 }
00206
00207 static int config_output(AVFilterLink *outlink)
00208 {
00209 PadContext *pad = outlink->src->priv;
00210
00211 outlink->w = pad->w;
00212 outlink->h = pad->h;
00213 return 0;
00214 }
00215
00216 static AVFilterBufferRef *get_video_buffer(AVFilterLink *inlink, int perms, int w, int h)
00217 {
00218 PadContext *pad = inlink->dst->priv;
00219 int align = (perms&AV_PERM_ALIGN) ? AVFILTER_ALIGN : 1;
00220
00221 AVFilterBufferRef *picref = avfilter_get_video_buffer(inlink->dst->outputs[0], perms,
00222 w + (pad->w - pad->in_w) + 4*align,
00223 h + (pad->h - pad->in_h));
00224 int plane;
00225
00226 picref->video->w = w;
00227 picref->video->h = h;
00228
00229 for (plane = 0; plane < 4 && picref->data[plane]; plane++)
00230 picref->data[plane] += FFALIGN(pad->x >> pad->draw.hsub[plane], align) * pad->draw.pixelstep[plane] +
00231 (pad->y >> pad->draw.vsub[plane]) * picref->linesize[plane];
00232
00233 return picref;
00234 }
00235
00236 static int does_clip(PadContext *pad, AVFilterBufferRef *outpicref, int plane, int hsub, int vsub, int x, int y)
00237 {
00238 int64_t x_in_buf, y_in_buf;
00239
00240 x_in_buf = outpicref->data[plane] - outpicref->buf->data[plane]
00241 + (x >> hsub) * pad->draw.pixelstep[plane]
00242 + (y >> vsub) * outpicref->linesize[plane];
00243
00244 if(x_in_buf < 0 || x_in_buf % pad->draw.pixelstep[plane])
00245 return 1;
00246 x_in_buf /= pad->draw.pixelstep[plane];
00247
00248 av_assert0(outpicref->buf->linesize[plane]>0);
00249
00250 y_in_buf = x_in_buf / outpicref->buf->linesize[plane];
00251 x_in_buf %= outpicref->buf->linesize[plane];
00252
00253 if( y_in_buf<<vsub >= outpicref->buf->h
00254 || x_in_buf<<hsub >= outpicref->buf->w)
00255 return 1;
00256 return 0;
00257 }
00258
00259 static void start_frame(AVFilterLink *inlink, AVFilterBufferRef *inpicref)
00260 {
00261 PadContext *pad = inlink->dst->priv;
00262 AVFilterBufferRef *outpicref = avfilter_ref_buffer(inpicref, ~0);
00263 int plane;
00264
00265 for (plane = 0; plane < 4 && outpicref->data[plane] && pad->draw.pixelstep[plane]; plane++) {
00266 int hsub = pad->draw.hsub[plane];
00267 int vsub = pad->draw.vsub[plane];
00268
00269 av_assert0(outpicref->buf->w>0 && outpicref->buf->h>0);
00270
00271 if(outpicref->format != outpicref->buf->format)
00272 break;
00273
00274 outpicref->data[plane] -= (pad->x >> hsub) * pad->draw.pixelstep[plane]
00275 + (pad->y >> vsub) * outpicref->linesize[plane];
00276
00277 if( does_clip(pad, outpicref, plane, hsub, vsub, 0, 0)
00278 || does_clip(pad, outpicref, plane, hsub, vsub, 0, pad->h-1)
00279 || does_clip(pad, outpicref, plane, hsub, vsub, pad->w-1, 0)
00280 || does_clip(pad, outpicref, plane, hsub, vsub, pad->w-1, pad->h-1)
00281 )
00282 break;
00283 }
00284 pad->needs_copy= plane < 4 && outpicref->data[plane];
00285 if(pad->needs_copy){
00286 av_log(inlink->dst, AV_LOG_DEBUG, "Direct padding impossible allocating new frame\n");
00287 avfilter_unref_buffer(outpicref);
00288 outpicref = avfilter_get_video_buffer(inlink->dst->outputs[0], AV_PERM_WRITE | AV_PERM_NEG_LINESIZES,
00289 FFMAX(inlink->w, pad->w),
00290 FFMAX(inlink->h, pad->h));
00291 avfilter_copy_buffer_ref_props(outpicref, inpicref);
00292 }
00293
00294 inlink->dst->outputs[0]->out_buf = outpicref;
00295
00296 outpicref->video->w = pad->w;
00297 outpicref->video->h = pad->h;
00298
00299 avfilter_start_frame(inlink->dst->outputs[0], avfilter_ref_buffer(outpicref, ~0));
00300 }
00301
00302 static void draw_send_bar_slice(AVFilterLink *link, int y, int h, int slice_dir, int before_slice)
00303 {
00304 PadContext *pad = link->dst->priv;
00305 int bar_y, bar_h = 0;
00306
00307 if (slice_dir * before_slice == 1 && y == pad->y) {
00308
00309 bar_y = 0;
00310 bar_h = pad->y;
00311 } else if (slice_dir * before_slice == -1 && (y + h) == (pad->y + pad->in_h)) {
00312
00313 bar_y = pad->y + pad->in_h;
00314 bar_h = pad->h - pad->in_h - pad->y;
00315 }
00316
00317 if (bar_h) {
00318 ff_fill_rectangle(&pad->draw, &pad->color,
00319 link->dst->outputs[0]->out_buf->data,
00320 link->dst->outputs[0]->out_buf->linesize,
00321 0, bar_y, pad->w, bar_h);
00322 avfilter_draw_slice(link->dst->outputs[0], bar_y, bar_h, slice_dir);
00323 }
00324 }
00325
00326 static void draw_slice(AVFilterLink *link, int y, int h, int slice_dir)
00327 {
00328 PadContext *pad = link->dst->priv;
00329 AVFilterBufferRef *outpic = link->dst->outputs[0]->out_buf;
00330 AVFilterBufferRef *inpic = link->cur_buf;
00331
00332 y += pad->y;
00333
00334 y = ff_draw_round_to_sub(&pad->draw, 1, -1, y);
00335 h = ff_draw_round_to_sub(&pad->draw, 1, -1, h);
00336
00337 if (!h)
00338 return;
00339 draw_send_bar_slice(link, y, h, slice_dir, 1);
00340
00341
00342 ff_fill_rectangle(&pad->draw, &pad->color, outpic->data, outpic->linesize,
00343 0, y, pad->x, h);
00344
00345 if(pad->needs_copy){
00346 ff_copy_rectangle2(&pad->draw,
00347 outpic->data, outpic->linesize,
00348 inpic ->data, inpic ->linesize,
00349 pad->x, y, 0, y - pad->y, inpic->video->w, h);
00350 }
00351
00352
00353 ff_fill_rectangle(&pad->draw, &pad->color, outpic->data, outpic->linesize,
00354 pad->x + pad->in_w, y, pad->w - pad->x - pad->in_w, h);
00355 avfilter_draw_slice(link->dst->outputs[0], y, h, slice_dir);
00356
00357 draw_send_bar_slice(link, y, h, slice_dir, -1);
00358 }
00359
00360 AVFilter avfilter_vf_pad = {
00361 .name = "pad",
00362 .description = NULL_IF_CONFIG_SMALL("Pad input image to width:height[:x:y[:color]] (default x and y: 0, default color: black)."),
00363
00364 .priv_size = sizeof(PadContext),
00365 .init = init,
00366 .query_formats = query_formats,
00367
00368 .inputs = (const AVFilterPad[]) {{ .name = "default",
00369 .type = AVMEDIA_TYPE_VIDEO,
00370 .config_props = config_input,
00371 .get_video_buffer = get_video_buffer,
00372 .start_frame = start_frame,
00373 .draw_slice = draw_slice, },
00374 { .name = NULL}},
00375
00376 .outputs = (const AVFilterPad[]) {{ .name = "default",
00377 .type = AVMEDIA_TYPE_VIDEO,
00378 .config_props = config_output, },
00379 { .name = NULL}},
00380 };