Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
4349 Serge 1
/*
2
 * Copyright (c) 2002 Michael Niedermayer 
3
 * Copyright (c) 2011 Stefano Sabatini
4
 *
5
 * This file is part of FFmpeg.
6
 *
7
 * FFmpeg is free software; you can redistribute it and/or modify
8
 * it under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation; either version 2 of the License, or
10
 * (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
15
 * GNU General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License along
18
 * with FFmpeg; if not, write to the Free Software Foundation, Inc.,
19
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
 */
21
 
22
/**
23
 * @file
24
 * Apply a boxblur filter to the input video.
25
 * Ported from MPlayer libmpcodecs/vf_boxblur.c.
26
 */
27
 
28
#include "libavutil/avstring.h"
29
#include "libavutil/common.h"
30
#include "libavutil/eval.h"
31
#include "libavutil/opt.h"
32
#include "libavutil/pixdesc.h"
33
#include "avfilter.h"
34
#include "formats.h"
35
#include "internal.h"
36
#include "video.h"
37
 
38
static const char *const var_names[] = {
39
    "w",
40
    "h",
41
    "cw",
42
    "ch",
43
    "hsub",
44
    "vsub",
45
    NULL
46
};
47
 
48
enum var_name {
49
    VAR_W,
50
    VAR_H,
51
    VAR_CW,
52
    VAR_CH,
53
    VAR_HSUB,
54
    VAR_VSUB,
55
    VARS_NB
56
};
57
 
58
typedef struct {
59
    int radius;
60
    int power;
61
    char *radius_expr;
62
} FilterParam;
63
 
64
typedef struct {
65
    const AVClass *class;
66
    FilterParam luma_param;
67
    FilterParam chroma_param;
68
    FilterParam alpha_param;
69
 
70
    int hsub, vsub;
71
    int radius[4];
72
    int power[4];
73
    uint8_t *temp[2]; ///< temporary buffer used in blur_power()
74
} BoxBlurContext;
75
 
76
#define Y 0
77
#define U 1
78
#define V 2
79
#define A 3
80
 
81
static av_cold int init(AVFilterContext *ctx)
82
{
83
    BoxBlurContext *s = ctx->priv;
84
 
85
    if (!s->luma_param.radius_expr) {
86
        av_log(ctx, AV_LOG_ERROR, "Luma radius expression is not set.\n");
87
        return AVERROR(EINVAL);
88
    }
89
 
90
    /* fill missing params */
91
    if (!s->chroma_param.radius_expr) {
92
        s->chroma_param.radius_expr = av_strdup(s->luma_param.radius_expr);
93
        if (!s->chroma_param.radius_expr)
94
            return AVERROR(ENOMEM);
95
    }
96
    if (s->chroma_param.power < 0)
97
        s->chroma_param.power = s->luma_param.power;
98
 
99
    if (!s->alpha_param.radius_expr) {
100
        s->alpha_param.radius_expr = av_strdup(s->luma_param.radius_expr);
101
        if (!s->alpha_param.radius_expr)
102
            return AVERROR(ENOMEM);
103
    }
104
    if (s->alpha_param.power < 0)
105
        s->alpha_param.power = s->luma_param.power;
106
 
107
    return 0;
108
}
109
 
110
static av_cold void uninit(AVFilterContext *ctx)
111
{
112
    BoxBlurContext *s = ctx->priv;
113
 
114
    av_freep(&s->temp[0]);
115
    av_freep(&s->temp[1]);
116
}
117
 
118
static int query_formats(AVFilterContext *ctx)
119
{
120
    static const enum AVPixelFormat pix_fmts[] = {
121
        AV_PIX_FMT_YUV444P,  AV_PIX_FMT_YUV422P,  AV_PIX_FMT_YUV420P,
122
        AV_PIX_FMT_YUV411P,  AV_PIX_FMT_YUV410P,  AV_PIX_FMT_YUVA420P,
123
        AV_PIX_FMT_YUV440P,  AV_PIX_FMT_GRAY8,
124
        AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ420P,
125
        AV_PIX_FMT_YUVJ440P,
126
        AV_PIX_FMT_NONE
127
    };
128
 
129
    ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
130
    return 0;
131
}
132
 
133
static int config_input(AVFilterLink *inlink)
134
{
135
    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
136
    AVFilterContext    *ctx = inlink->dst;
137
    BoxBlurContext *s = ctx->priv;
138
    int w = inlink->w, h = inlink->h;
139
    int cw, ch;
140
    double var_values[VARS_NB], res;
141
    char *expr;
142
    int ret;
143
 
144
    if (!(s->temp[0] = av_malloc(FFMAX(w, h))) ||
145
        !(s->temp[1] = av_malloc(FFMAX(w, h))))
146
        return AVERROR(ENOMEM);
147
 
148
    s->hsub = desc->log2_chroma_w;
149
    s->vsub = desc->log2_chroma_h;
150
 
151
    var_values[VAR_W]       = inlink->w;
152
    var_values[VAR_H]       = inlink->h;
153
    var_values[VAR_CW] = cw = w>>s->hsub;
154
    var_values[VAR_CH] = ch = h>>s->vsub;
155
    var_values[VAR_HSUB]    = 1<hsub;
156
    var_values[VAR_VSUB]    = 1<vsub;
157
 
158
#define EVAL_RADIUS_EXPR(comp)                                          \
159
    expr = s->comp##_param.radius_expr;                                 \
160
    ret = av_expr_parse_and_eval(&res, expr, var_names, var_values,     \
161
                                 NULL, NULL, NULL, NULL, NULL, 0, ctx); \
162
    s->comp##_param.radius = res;                                       \
163
    if (ret < 0) {                                                      \
164
        av_log(NULL, AV_LOG_ERROR,                                      \
165
               "Error when evaluating " #comp " radius expression '%s'\n", expr); \
166
        return ret;                                                     \
167
    }
168
    EVAL_RADIUS_EXPR(luma);
169
    EVAL_RADIUS_EXPR(chroma);
170
    EVAL_RADIUS_EXPR(alpha);
171
 
172
    av_log(ctx, AV_LOG_VERBOSE,
173
           "luma_radius:%d luma_power:%d "
174
           "chroma_radius:%d chroma_power:%d "
175
           "alpha_radius:%d alpha_power:%d "
176
           "w:%d chroma_w:%d h:%d chroma_h:%d\n",
177
           s->luma_param  .radius, s->luma_param  .power,
178
           s->chroma_param.radius, s->chroma_param.power,
179
           s->alpha_param .radius, s->alpha_param .power,
180
           w, cw, h, ch);
181
 
182
#define CHECK_RADIUS_VAL(w_, h_, comp)                                  \
183
    if (s->comp##_param.radius < 0 ||                                   \
184
        2*s->comp##_param.radius > FFMIN(w_, h_)) {                     \
185
        av_log(ctx, AV_LOG_ERROR,                                       \
186
               "Invalid " #comp " radius value %d, must be >= 0 and <= %d\n", \
187
               s->comp##_param.radius, FFMIN(w_, h_)/2);                \
188
        return AVERROR(EINVAL);                                         \
189
    }
190
    CHECK_RADIUS_VAL(w,  h,  luma);
191
    CHECK_RADIUS_VAL(cw, ch, chroma);
192
    CHECK_RADIUS_VAL(w,  h,  alpha);
193
 
194
    s->radius[Y] = s->luma_param.radius;
195
    s->radius[U] = s->radius[V] = s->chroma_param.radius;
196
    s->radius[A] = s->alpha_param.radius;
197
 
198
    s->power[Y] = s->luma_param.power;
199
    s->power[U] = s->power[V] = s->chroma_param.power;
200
    s->power[A] = s->alpha_param.power;
201
 
202
    return 0;
203
}
204
 
205
static inline void blur(uint8_t *dst, int dst_step, const uint8_t *src, int src_step,
206
                        int len, int radius)
207
{
208
    /* Naive boxblur would sum source pixels from x-radius .. x+radius
209
     * for destination pixel x. That would be O(radius*width).
210
     * If you now look at what source pixels represent 2 consecutive
211
     * output pixels, then you see they are almost identical and only
212
     * differ by 2 pixels, like:
213
     * src0       111111111
214
     * dst0           1
215
     * src1        111111111
216
     * dst1            1
217
     * src0-src1  1       -1
218
     * so when you know one output pixel you can find the next by just adding
219
     * and subtracting 1 input pixel.
220
     * The following code adopts this faster variant.
221
     */
222
    const int length = radius*2 + 1;
223
    const int inv = ((1<<16) + length/2)/length;
224
    int x, sum = 0;
225
 
226
    for (x = 0; x < radius; x++)
227
        sum += src[x*src_step]<<1;
228
    sum += src[radius*src_step];
229
 
230
    for (x = 0; x <= radius; x++) {
231
        sum += src[(radius+x)*src_step] - src[(radius-x)*src_step];
232
        dst[x*dst_step] = (sum*inv + (1<<15))>>16;
233
    }
234
 
235
    for (; x < len-radius; x++) {
236
        sum += src[(radius+x)*src_step] - src[(x-radius-1)*src_step];
237
        dst[x*dst_step] = (sum*inv + (1<<15))>>16;
238
    }
239
 
240
    for (; x < len; x++) {
241
        sum += src[(2*len-radius-x-1)*src_step] - src[(x-radius-1)*src_step];
242
        dst[x*dst_step] = (sum*inv + (1<<15))>>16;
243
    }
244
}
245
 
246
static inline void blur_power(uint8_t *dst, int dst_step, const uint8_t *src, int src_step,
247
                              int len, int radius, int power, uint8_t *temp[2])
248
{
249
    uint8_t *a = temp[0], *b = temp[1];
250
 
251
    if (radius && power) {
252
        blur(a, 1, src, src_step, len, radius);
253
        for (; power > 2; power--) {
254
            uint8_t *c;
255
            blur(b, 1, a, 1, len, radius);
256
            c = a; a = b; b = c;
257
        }
258
        if (power > 1) {
259
            blur(dst, dst_step, a, 1, len, radius);
260
        } else {
261
            int i;
262
            for (i = 0; i < len; i++)
263
                dst[i*dst_step] = a[i];
264
        }
265
    } else {
266
        int i;
267
        for (i = 0; i < len; i++)
268
            dst[i*dst_step] = src[i*src_step];
269
    }
270
}
271
 
272
static void hblur(uint8_t *dst, int dst_linesize, const uint8_t *src, int src_linesize,
273
                  int w, int h, int radius, int power, uint8_t *temp[2])
274
{
275
    int y;
276
 
277
    if (radius == 0 && dst == src)
278
        return;
279
 
280
    for (y = 0; y < h; y++)
281
        blur_power(dst + y*dst_linesize, 1, src + y*src_linesize, 1,
282
                   w, radius, power, temp);
283
}
284
 
285
static void vblur(uint8_t *dst, int dst_linesize, const uint8_t *src, int src_linesize,
286
                  int w, int h, int radius, int power, uint8_t *temp[2])
287
{
288
    int x;
289
 
290
    if (radius == 0 && dst == src)
291
        return;
292
 
293
    for (x = 0; x < w; x++)
294
        blur_power(dst + x, dst_linesize, src + x, src_linesize,
295
                   h, radius, power, temp);
296
}
297
 
298
static int filter_frame(AVFilterLink *inlink, AVFrame *in)
299
{
300
    AVFilterContext *ctx = inlink->dst;
301
    BoxBlurContext *s = ctx->priv;
302
    AVFilterLink *outlink = inlink->dst->outputs[0];
303
    AVFrame *out;
304
    int plane;
305
    int cw = FF_CEIL_RSHIFT(inlink->w, s->hsub), ch = FF_CEIL_RSHIFT(in->height, s->vsub);
306
    int w[4] = { inlink->w, cw, cw, inlink->w };
307
    int h[4] = { in->height, ch, ch, in->height };
308
 
309
    out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
310
    if (!out) {
311
        av_frame_free(&in);
312
        return AVERROR(ENOMEM);
313
    }
314
    av_frame_copy_props(out, in);
315
 
316
    for (plane = 0; plane < 4 && in->data[plane] && in->linesize[plane]; plane++)
317
        hblur(out->data[plane], out->linesize[plane],
318
              in ->data[plane], in ->linesize[plane],
319
              w[plane], h[plane], s->radius[plane], s->power[plane],
320
              s->temp);
321
 
322
    for (plane = 0; plane < 4 && in->data[plane] && in->linesize[plane]; plane++)
323
        vblur(out->data[plane], out->linesize[plane],
324
              out->data[plane], out->linesize[plane],
325
              w[plane], h[plane], s->radius[plane], s->power[plane],
326
              s->temp);
327
 
328
    av_frame_free(&in);
329
 
330
    return ff_filter_frame(outlink, out);
331
}
332
 
333
#define OFFSET(x) offsetof(BoxBlurContext, x)
334
#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
335
 
336
static const AVOption boxblur_options[] = {
337
    { "luma_radius", "Radius of the luma blurring box", OFFSET(luma_param.radius_expr), AV_OPT_TYPE_STRING, {.str="2"}, .flags = FLAGS },
338
    { "lr",          "Radius of the luma blurring box", OFFSET(luma_param.radius_expr), AV_OPT_TYPE_STRING, {.str="2"}, .flags = FLAGS },
339
    { "luma_power",  "How many times should the boxblur be applied to luma",  OFFSET(luma_param.power), AV_OPT_TYPE_INT, {.i64=2}, 0, INT_MAX, .flags = FLAGS },
340
    { "lp",          "How many times should the boxblur be applied to luma",  OFFSET(luma_param.power), AV_OPT_TYPE_INT, {.i64=2}, 0, INT_MAX, .flags = FLAGS },
341
 
342
    { "chroma_radius", "Radius of the chroma blurring box", OFFSET(chroma_param.radius_expr), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
343
    { "cr",            "Radius of the chroma blurring box", OFFSET(chroma_param.radius_expr), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
344
    { "chroma_power",  "How many times should the boxblur be applied to chroma",  OFFSET(chroma_param.power), AV_OPT_TYPE_INT, {.i64=-1}, -1, INT_MAX, .flags = FLAGS },
345
    { "cp",            "How many times should the boxblur be applied to chroma",  OFFSET(chroma_param.power), AV_OPT_TYPE_INT, {.i64=-1}, -1, INT_MAX, .flags = FLAGS },
346
 
347
    { "alpha_radius", "Radius of the alpha blurring box", OFFSET(alpha_param.radius_expr), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
348
    { "ar",           "Radius of the alpha blurring box", OFFSET(alpha_param.radius_expr), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
349
    { "alpha_power",  "How many times should the boxblur be applied to alpha",  OFFSET(alpha_param.power), AV_OPT_TYPE_INT, {.i64=-1}, -1, INT_MAX, .flags = FLAGS },
350
    { "ap",           "How many times should the boxblur be applied to alpha",  OFFSET(alpha_param.power), AV_OPT_TYPE_INT, {.i64=-1}, -1, INT_MAX, .flags = FLAGS },
351
 
352
    { NULL }
353
};
354
 
355
AVFILTER_DEFINE_CLASS(boxblur);
356
 
357
static const AVFilterPad avfilter_vf_boxblur_inputs[] = {
358
    {
359
        .name         = "default",
360
        .type         = AVMEDIA_TYPE_VIDEO,
361
        .config_props = config_input,
362
        .filter_frame = filter_frame,
363
    },
364
    { NULL }
365
};
366
 
367
static const AVFilterPad avfilter_vf_boxblur_outputs[] = {
368
    {
369
        .name = "default",
370
        .type = AVMEDIA_TYPE_VIDEO,
371
    },
372
    { NULL }
373
};
374
 
375
AVFilter avfilter_vf_boxblur = {
376
    .name          = "boxblur",
377
    .description   = NULL_IF_CONFIG_SMALL("Blur the input."),
378
    .priv_size     = sizeof(BoxBlurContext),
379
    .priv_class    = &boxblur_class,
380
    .init          = init,
381
    .uninit        = uninit,
382
    .query_formats = query_formats,
383
    .inputs        = avfilter_vf_boxblur_inputs,
384
    .outputs       = avfilter_vf_boxblur_outputs,
385
    .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
386
};