00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00028 #include "libavutil/avstring.h"
00029 #include "libavutil/eval.h"
00030 #include "libavutil/pixdesc.h"
00031 #include "avfilter.h"
00032
00033 static const char *var_names[] = {
00034 "w",
00035 "h",
00036 "cw",
00037 "ch",
00038 "hsub",
00039 "vsub",
00040 NULL
00041 };
00042
00043 enum var_name {
00044 VAR_W,
00045 VAR_H,
00046 VAR_CW,
00047 VAR_CH,
00048 VAR_HSUB,
00049 VAR_VSUB,
00050 VARS_NB
00051 };
00052
00053 typedef struct {
00054 int radius;
00055 int power;
00056 } FilterParam;
00057
00058 typedef struct {
00059 FilterParam luma_param;
00060 FilterParam chroma_param;
00061 FilterParam alpha_param;
00062 char luma_radius_expr [256];
00063 char chroma_radius_expr[256];
00064 char alpha_radius_expr [256];
00065
00066 int hsub, vsub;
00067 int radius[4];
00068 int power[4];
00069 uint8_t *temp[2];
00070 } BoxBlurContext;
00071
00072 #define Y 0
00073 #define U 1
00074 #define V 2
00075 #define A 3
00076
00077 static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
00078 {
00079 BoxBlurContext *boxblur = ctx->priv;
00080 int e;
00081
00082 if (!args) {
00083 av_log(ctx, AV_LOG_ERROR,
00084 "Filter expects 2 or 4 or 6 arguments, none provided\n");
00085 return AVERROR(EINVAL);
00086 }
00087
00088 e = sscanf(args, "%255[^:]:%d:%255[^:]:%d:%255[^:]:%d",
00089 boxblur->luma_radius_expr, &boxblur->luma_param .power,
00090 boxblur->chroma_radius_expr, &boxblur->chroma_param.power,
00091 boxblur->alpha_radius_expr, &boxblur->alpha_param .power);
00092
00093 if (e != 2 && e != 4 && e != 6) {
00094 av_log(ctx, AV_LOG_ERROR,
00095 "Filter expects 2 or 4 or 6 params, provided %d\n", e);
00096 return AVERROR(EINVAL);
00097 }
00098
00099 if (e < 4) {
00100 boxblur->chroma_param.power = boxblur->luma_param.power;
00101 av_strlcpy(boxblur->chroma_radius_expr, boxblur->luma_radius_expr,
00102 sizeof(boxblur->chroma_radius_expr));
00103 }
00104 if (e < 6) {
00105 boxblur->alpha_param.power = boxblur->luma_param.power;
00106 av_strlcpy(boxblur->alpha_radius_expr, boxblur->luma_radius_expr,
00107 sizeof(boxblur->alpha_radius_expr));
00108 }
00109
00110 return 0;
00111 }
00112
00113 static av_cold void uninit(AVFilterContext *ctx)
00114 {
00115 BoxBlurContext *boxblur = ctx->priv;
00116
00117 av_freep(&boxblur->temp[0]);
00118 av_freep(&boxblur->temp[1]);
00119 }
00120
00121 static int query_formats(AVFilterContext *ctx)
00122 {
00123 enum PixelFormat pix_fmts[] = {
00124 PIX_FMT_YUV444P, PIX_FMT_YUV422P, PIX_FMT_YUV420P,
00125 PIX_FMT_YUV411P, PIX_FMT_YUV410P, PIX_FMT_YUVA420P,
00126 PIX_FMT_YUV440P, PIX_FMT_GRAY8,
00127 PIX_FMT_YUVJ444P, PIX_FMT_YUVJ422P, PIX_FMT_YUVJ420P,
00128 PIX_FMT_YUVJ440P,
00129 PIX_FMT_NONE
00130 };
00131
00132 avfilter_set_common_formats(ctx, avfilter_make_format_list(pix_fmts));
00133 return 0;
00134 }
00135
00136 static int config_input(AVFilterLink *inlink)
00137 {
00138 const AVPixFmtDescriptor *desc = &av_pix_fmt_descriptors[inlink->format];
00139 AVFilterContext *ctx = inlink->dst;
00140 BoxBlurContext *boxblur = ctx->priv;
00141 int w = inlink->w, h = inlink->h;
00142 int cw, ch;
00143 double var_values[VARS_NB], res;
00144 char *expr;
00145 int ret;
00146
00147 av_freep(&boxblur->temp[0]);
00148 av_freep(&boxblur->temp[1]);
00149 if (!(boxblur->temp[0] = av_malloc(FFMAX(w, h))))
00150 return AVERROR(ENOMEM);
00151 if (!(boxblur->temp[1] = av_malloc(FFMAX(w, h)))) {
00152 av_freep(&boxblur->temp[0]);
00153 return AVERROR(ENOMEM);
00154 }
00155
00156 boxblur->hsub = desc->log2_chroma_w;
00157 boxblur->vsub = desc->log2_chroma_h;
00158
00159 var_values[VAR_W] = inlink->w;
00160 var_values[VAR_H] = inlink->h;
00161 var_values[VAR_CW] = cw = w>>boxblur->hsub;
00162 var_values[VAR_CH] = ch = h>>boxblur->vsub;
00163 var_values[VAR_HSUB] = 1<<boxblur->hsub;
00164 var_values[VAR_VSUB] = 1<<boxblur->vsub;
00165
00166 #define EVAL_RADIUS_EXPR(comp) \
00167 expr = boxblur->comp##_radius_expr; \
00168 ret = av_expr_parse_and_eval(&res, expr, var_names, var_values, \
00169 NULL, NULL, NULL, NULL, NULL, 0, ctx); \
00170 boxblur->comp##_param.radius = res; \
00171 if (ret < 0) { \
00172 av_log(NULL, AV_LOG_ERROR, \
00173 "Error when evaluating " #comp " radius expression '%s'\n", expr); \
00174 return ret; \
00175 }
00176 EVAL_RADIUS_EXPR(luma);
00177 EVAL_RADIUS_EXPR(chroma);
00178 EVAL_RADIUS_EXPR(alpha);
00179
00180 av_log(ctx, AV_LOG_DEBUG,
00181 "luma_radius:%d luma_power:%d "
00182 "chroma_radius:%d chroma_power:%d "
00183 "alpha_radius:%d alpha_power:%d "
00184 "w:%d chroma_w:%d h:%d chroma_h:%d\n",
00185 boxblur->luma_param .radius, boxblur->luma_param .power,
00186 boxblur->chroma_param.radius, boxblur->chroma_param.power,
00187 boxblur->alpha_param .radius, boxblur->alpha_param .power,
00188 w, cw, h, ch);
00189
00190 #define CHECK_RADIUS_VAL(w_, h_, comp) \
00191 if (boxblur->comp##_param.radius < 0 || \
00192 2*boxblur->comp##_param.radius > FFMIN(w_, h_)) { \
00193 av_log(ctx, AV_LOG_ERROR, \
00194 "Invalid " #comp " radius value %d, must be >= 0 and <= %d\n", \
00195 boxblur->comp##_param.radius, FFMIN(w_, h_)/2); \
00196 return AVERROR(EINVAL); \
00197 }
00198 CHECK_RADIUS_VAL(w, h, luma);
00199 CHECK_RADIUS_VAL(cw, ch, chroma);
00200 CHECK_RADIUS_VAL(w, h, alpha);
00201
00202 boxblur->radius[Y] = boxblur->luma_param.radius;
00203 boxblur->radius[U] = boxblur->radius[V] = boxblur->chroma_param.radius;
00204 boxblur->radius[A] = boxblur->alpha_param.radius;
00205
00206 boxblur->power[Y] = boxblur->luma_param.power;
00207 boxblur->power[U] = boxblur->power[V] = boxblur->chroma_param.power;
00208 boxblur->power[A] = boxblur->alpha_param.power;
00209
00210 return 0;
00211 }
00212
00213 static inline void blur(uint8_t *dst, int dst_step, const uint8_t *src, int src_step,
00214 int len, int radius)
00215 {
00216
00217
00218
00219
00220
00221
00222
00223
00224
00225
00226
00227
00228
00229
00230 const int length = radius*2 + 1;
00231 const int inv = ((1<<16) + length/2)/length;
00232 int x, sum = 0;
00233
00234 for (x = 0; x < radius; x++)
00235 sum += src[x*src_step]<<1;
00236 sum += src[radius*src_step];
00237
00238 for (x = 0; x <= radius; x++) {
00239 sum += src[(radius+x)*src_step] - src[(radius-x)*src_step];
00240 dst[x*dst_step] = (sum*inv + (1<<15))>>16;
00241 }
00242
00243 for (; x < len-radius; x++) {
00244 sum += src[(radius+x)*src_step] - src[(x-radius-1)*src_step];
00245 dst[x*dst_step] = (sum*inv + (1<<15))>>16;
00246 }
00247
00248 for (; x < len; x++) {
00249 sum += src[(2*len-radius-x-1)*src_step] - src[(x-radius-1)*src_step];
00250 dst[x*dst_step] = (sum*inv + (1<<15))>>16;
00251 }
00252 }
00253
00254 static inline void blur_power(uint8_t *dst, int dst_step, const uint8_t *src, int src_step,
00255 int len, int radius, int power, uint8_t *temp[2])
00256 {
00257 uint8_t *a = temp[0], *b = temp[1];
00258
00259 if (radius && power) {
00260 blur(a, 1, src, src_step, len, radius);
00261 for (; power > 2; power--) {
00262 uint8_t *c;
00263 blur(b, 1, a, 1, len, radius);
00264 c = a; a = b; b = c;
00265 }
00266 if (power > 1) {
00267 blur(dst, dst_step, a, 1, len, radius);
00268 } else {
00269 int i;
00270 for (i = 0; i < len; i++)
00271 dst[i*dst_step] = a[i];
00272 }
00273 } else {
00274 int i;
00275 for (i = 0; i < len; i++)
00276 dst[i*dst_step] = src[i*src_step];
00277 }
00278 }
00279
00280 static void hblur(uint8_t *dst, int dst_linesize, const uint8_t *src, int src_linesize,
00281 int w, int h, int radius, int power, uint8_t *temp[2])
00282 {
00283 int y;
00284
00285 if (radius == 0 && dst == src)
00286 return;
00287
00288 for (y = 0; y < h; y++)
00289 blur_power(dst + y*dst_linesize, 1, src + y*src_linesize, 1,
00290 w, radius, power, temp);
00291 }
00292
00293 static void vblur(uint8_t *dst, int dst_linesize, const uint8_t *src, int src_linesize,
00294 int w, int h, int radius, int power, uint8_t *temp[2])
00295 {
00296 int x;
00297
00298 if (radius == 0 && dst == src)
00299 return;
00300
00301 for (x = 0; x < w; x++)
00302 blur_power(dst + x, dst_linesize, src + x, src_linesize,
00303 h, radius, power, temp);
00304 }
00305
00306 static void draw_slice(AVFilterLink *inlink, int y0, int h0, int slice_dir)
00307 {
00308 AVFilterContext *ctx = inlink->dst;
00309 BoxBlurContext *boxblur = ctx->priv;
00310 AVFilterLink *outlink = inlink->dst->outputs[0];
00311 AVFilterBufferRef *inpicref = inlink ->cur_buf;
00312 AVFilterBufferRef *outpicref = outlink->out_buf;
00313 int plane;
00314 int cw = inlink->w >> boxblur->hsub, ch = h0 >> boxblur->vsub;
00315 int w[4] = { inlink->w, cw, cw, inlink->w };
00316 int h[4] = { h0, ch, ch, h0 };
00317
00318 for (plane = 0; inpicref->data[plane] && plane < 4; plane++)
00319 hblur(outpicref->data[plane], outpicref->linesize[plane],
00320 inpicref ->data[plane], inpicref ->linesize[plane],
00321 w[plane], h[plane], boxblur->radius[plane], boxblur->power[plane],
00322 boxblur->temp);
00323
00324 for (plane = 0; inpicref->data[plane] && plane < 4; plane++)
00325 vblur(outpicref->data[plane], outpicref->linesize[plane],
00326 outpicref->data[plane], outpicref->linesize[plane],
00327 w[plane], h[plane], boxblur->radius[plane], boxblur->power[plane],
00328 boxblur->temp);
00329
00330 avfilter_draw_slice(outlink, y0, h0, slice_dir);
00331 }
00332
00333 AVFilter avfilter_vf_boxblur = {
00334 .name = "boxblur",
00335 .description = NULL_IF_CONFIG_SMALL("Blur the input."),
00336 .priv_size = sizeof(BoxBlurContext),
00337 .init = init,
00338 .uninit = uninit,
00339 .query_formats = query_formats,
00340
00341 .inputs = (AVFilterPad[]) {{ .name = "default",
00342 .type = AVMEDIA_TYPE_VIDEO,
00343 .config_props = config_input,
00344 .draw_slice = draw_slice,
00345 .min_perms = AV_PERM_READ },
00346 { .name = NULL}},
00347 .outputs = (AVFilterPad[]) {{ .name = "default",
00348 .type = AVMEDIA_TYPE_VIDEO, },
00349 { .name = NULL}},
00350 };