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 A'rpi
3
 * This file is part of FFmpeg.
4
 *
5
 * FFmpeg is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 2 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * FFmpeg is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License along
16
 * with FFmpeg; if not, write to the Free Software Foundation, Inc.,
17
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18
 */
19
 
20
/**
21
 * @file
22
 * border detection filter
23
 * Ported from MPlayer libmpcodecs/vf_cropdetect.c.
24
 */
25
 
26
#include "libavutil/imgutils.h"
27
#include "libavutil/internal.h"
28
#include "libavutil/opt.h"
29
 
30
#include "avfilter.h"
31
#include "formats.h"
32
#include "internal.h"
33
#include "video.h"
34
 
35
typedef struct {
36
    const AVClass *class;
37
    int x1, y1, x2, y2;
38
    int limit;
39
    int round;
40
    int reset_count;
41
    int frame_nb;
42
    int max_pixsteps[4];
43
} CropDetectContext;
44
 
45
static int query_formats(AVFilterContext *ctx)
46
{
47
    static const enum AVPixelFormat pix_fmts[] = {
48
        AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUVJ420P,
49
        AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVJ422P,
50
        AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUVJ444P,
51
        AV_PIX_FMT_YUV411P, AV_PIX_FMT_GRAY8,
52
        AV_PIX_FMT_NV12,    AV_PIX_FMT_NV21,
53
        AV_PIX_FMT_NONE
54
    };
55
 
56
    ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
57
    return 0;
58
}
59
 
60
static int checkline(void *ctx, const unsigned char *src, int stride, int len, int bpp)
61
{
62
    int total = 0;
63
    int div = len;
64
 
65
    switch (bpp) {
66
    case 1:
67
        while (--len >= 0) {
68
            total += src[0];
69
            src += stride;
70
        }
71
        break;
72
    case 3:
73
    case 4:
74
        while (--len >= 0) {
75
            total += src[0] + src[1] + src[2];
76
            src += stride;
77
        }
78
        div *= 3;
79
        break;
80
    }
81
    total /= div;
82
 
83
    av_log(ctx, AV_LOG_DEBUG, "total:%d\n", total);
84
    return total;
85
}
86
 
87
static av_cold int init(AVFilterContext *ctx)
88
{
89
    CropDetectContext *s = ctx->priv;
90
 
91
    s->frame_nb = -2;
92
 
93
    av_log(ctx, AV_LOG_VERBOSE, "limit:%d round:%d reset_count:%d\n",
94
           s->limit, s->round, s->reset_count);
95
 
96
    return 0;
97
}
98
 
99
static int config_input(AVFilterLink *inlink)
100
{
101
    AVFilterContext *ctx = inlink->dst;
102
    CropDetectContext *s = ctx->priv;
103
 
104
    av_image_fill_max_pixsteps(s->max_pixsteps, NULL,
105
                               av_pix_fmt_desc_get(inlink->format));
106
 
107
    s->x1 = inlink->w - 1;
108
    s->y1 = inlink->h - 1;
109
    s->x2 = 0;
110
    s->y2 = 0;
111
 
112
    return 0;
113
}
114
 
115
#define SET_META(key, value) \
116
    snprintf(buf, sizeof(buf), "%d", value);  \
117
    av_dict_set(metadata, key, buf, 0)
118
 
119
static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
120
{
121
    AVFilterContext *ctx = inlink->dst;
122
    CropDetectContext *s = ctx->priv;
123
    int bpp = s->max_pixsteps[0];
124
    int w, h, x, y, shrink_by;
125
    AVDictionary **metadata;
126
    char buf[32];
127
 
128
    // ignore first 2 frames - they may be empty
129
    if (++s->frame_nb > 0) {
130
        metadata = avpriv_frame_get_metadatap(frame);
131
 
132
        // Reset the crop area every reset_count frames, if reset_count is > 0
133
        if (s->reset_count > 0 && s->frame_nb > s->reset_count) {
134
            s->x1 = frame->width  - 1;
135
            s->y1 = frame->height - 1;
136
            s->x2 = 0;
137
            s->y2 = 0;
138
            s->frame_nb = 1;
139
        }
140
 
141
        for (y = 0; y < s->y1; y++) {
142
            if (checkline(ctx, frame->data[0] + frame->linesize[0] * y, bpp, frame->width, bpp) > s->limit) {
143
                s->y1 = y;
144
                break;
145
            }
146
        }
147
 
148
        for (y = frame->height - 1; y > s->y2; y--) {
149
            if (checkline(ctx, frame->data[0] + frame->linesize[0] * y, bpp, frame->width, bpp) > s->limit) {
150
                s->y2 = y;
151
                break;
152
            }
153
        }
154
 
155
        for (y = 0; y < s->x1; y++) {
156
            if (checkline(ctx, frame->data[0] + bpp*y, frame->linesize[0], frame->height, bpp) > s->limit) {
157
                s->x1 = y;
158
                break;
159
            }
160
        }
161
 
162
        for (y = frame->width - 1; y > s->x2; y--) {
163
            if (checkline(ctx, frame->data[0] + bpp*y, frame->linesize[0], frame->height, bpp) > s->limit) {
164
                s->x2 = y;
165
                break;
166
            }
167
        }
168
 
169
        // round x and y (up), important for yuv colorspaces
170
        // make sure they stay rounded!
171
        x = (s->x1+1) & ~1;
172
        y = (s->y1+1) & ~1;
173
 
174
        w = s->x2 - x + 1;
175
        h = s->y2 - y + 1;
176
 
177
        // w and h must be divisible by 2 as well because of yuv
178
        // colorspace problems.
179
        if (s->round <= 1)
180
            s->round = 16;
181
        if (s->round % 2)
182
            s->round *= 2;
183
 
184
        shrink_by = w % s->round;
185
        w -= shrink_by;
186
        x += (shrink_by/2 + 1) & ~1;
187
 
188
        shrink_by = h % s->round;
189
        h -= shrink_by;
190
        y += (shrink_by/2 + 1) & ~1;
191
 
192
        SET_META("lavfi.cropdetect.x1", s->x1);
193
        SET_META("lavfi.cropdetect.x2", s->x2);
194
        SET_META("lavfi.cropdetect.y1", s->y1);
195
        SET_META("lavfi.cropdetect.y2", s->y2);
196
        SET_META("lavfi.cropdetect.w",  w);
197
        SET_META("lavfi.cropdetect.h",  h);
198
        SET_META("lavfi.cropdetect.x",  x);
199
        SET_META("lavfi.cropdetect.y",  y);
200
 
201
        av_log(ctx, AV_LOG_INFO,
202
               "x1:%d x2:%d y1:%d y2:%d w:%d h:%d x:%d y:%d pts:%"PRId64" t:%f crop=%d:%d:%d:%d\n",
203
               s->x1, s->x2, s->y1, s->y2, w, h, x, y, frame->pts,
204
               frame->pts == AV_NOPTS_VALUE ? -1 : frame->pts * av_q2d(inlink->time_base),
205
               w, h, x, y);
206
    }
207
 
208
    return ff_filter_frame(inlink->dst->outputs[0], frame);
209
}
210
 
211
#define OFFSET(x) offsetof(CropDetectContext, x)
212
#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
213
 
214
static const AVOption cropdetect_options[] = {
215
    { "limit", "Threshold below which the pixel is considered black", OFFSET(limit),       AV_OPT_TYPE_INT, { .i64 = 24 }, 0, 255, FLAGS },
216
    { "round", "Value by which the width/height should be divisible", OFFSET(round),       AV_OPT_TYPE_INT, { .i64 = 16 }, 0, INT_MAX, FLAGS },
217
    { "reset", "Recalculate the crop area after this many frames",    OFFSET(reset_count), AV_OPT_TYPE_INT, { .i64 = 0 },  0, INT_MAX, FLAGS },
218
    { "reset_count", "Recalculate the crop area after this many frames",OFFSET(reset_count),AV_OPT_TYPE_INT,{ .i64 = 0 },  0, INT_MAX, FLAGS },
219
    { NULL }
220
};
221
 
222
AVFILTER_DEFINE_CLASS(cropdetect);
223
 
224
static const AVFilterPad avfilter_vf_cropdetect_inputs[] = {
225
    {
226
        .name         = "default",
227
        .type         = AVMEDIA_TYPE_VIDEO,
228
        .config_props = config_input,
229
        .filter_frame = filter_frame,
230
    },
231
    { NULL }
232
};
233
 
234
static const AVFilterPad avfilter_vf_cropdetect_outputs[] = {
235
    {
236
        .name = "default",
237
        .type = AVMEDIA_TYPE_VIDEO
238
    },
239
    { NULL }
240
};
241
 
242
AVFilter avfilter_vf_cropdetect = {
243
    .name          = "cropdetect",
244
    .description   = NULL_IF_CONFIG_SMALL("Auto-detect crop size."),
245
    .priv_size     = sizeof(CropDetectContext),
246
    .priv_class    = &cropdetect_class,
247
    .init          = init,
248
    .query_formats = query_formats,
249
    .inputs        = avfilter_vf_cropdetect_inputs,
250
    .outputs       = avfilter_vf_cropdetect_outputs,
251
    .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
252
};