2 * Copyright (c) 2002 A'rpi
3 * This file is part of FFmpeg.
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.
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.
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.
22 * border detection filter
23 * Ported from MPlayer libmpcodecs/vf_cropdetect.c.
26 #include "libavutil/imgutils.h"
27 #include "libavutil/internal.h"
28 #include "libavutil/opt.h"
35 typedef struct CropDetectContext
{
45 static int query_formats(AVFilterContext
*ctx
)
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
,
56 ff_set_common_formats(ctx
, ff_make_format_list(pix_fmts
));
60 static int checkline(void *ctx
, const unsigned char *src
, int stride
, int len
, int bpp
)
75 total
+= src
[0] + src
[1] + src
[2];
83 av_log(ctx
, AV_LOG_DEBUG
, "total:%d\n", total
);
87 static av_cold
int init(AVFilterContext
*ctx
)
89 CropDetectContext
*s
= ctx
->priv
;
93 av_log(ctx
, AV_LOG_VERBOSE
, "limit:%d round:%d reset_count:%d\n",
94 s
->limit
, s
->round
, s
->reset_count
);
99 static int config_input(AVFilterLink
*inlink
)
101 AVFilterContext
*ctx
= inlink
->dst
;
102 CropDetectContext
*s
= ctx
->priv
;
104 av_image_fill_max_pixsteps(s
->max_pixsteps
, NULL
,
105 av_pix_fmt_desc_get(inlink
->format
));
107 s
->x1
= inlink
->w
- 1;
108 s
->y1
= inlink
->h
- 1;
115 #define SET_META(key, value) \
116 av_dict_set_int(metadata, key, value, 0)
118 static int filter_frame(AVFilterLink
*inlink
, AVFrame
*frame
)
120 AVFilterContext
*ctx
= inlink
->dst
;
121 CropDetectContext
*s
= ctx
->priv
;
122 int bpp
= s
->max_pixsteps
[0];
123 int w
, h
, x
, y
, shrink_by
;
124 AVDictionary
**metadata
;
126 // ignore first 2 frames - they may be empty
127 if (++s
->frame_nb
> 0) {
128 metadata
= avpriv_frame_get_metadatap(frame
);
130 // Reset the crop area every reset_count frames, if reset_count is > 0
131 if (s
->reset_count
> 0 && s
->frame_nb
> s
->reset_count
) {
132 s
->x1
= frame
->width
- 1;
133 s
->y1
= frame
->height
- 1;
139 for (y
= 0; y
< s
->y1
; y
++) {
140 if (checkline(ctx
, frame
->data
[0] + frame
->linesize
[0] * y
, bpp
, frame
->width
, bpp
) > s
->limit
) {
146 for (y
= frame
->height
- 1; y
> FFMAX(s
->y2
, s
->y1
); y
--) {
147 if (checkline(ctx
, frame
->data
[0] + frame
->linesize
[0] * y
, bpp
, frame
->width
, bpp
) > s
->limit
) {
153 for (y
= 0; y
< s
->x1
; y
++) {
154 if (checkline(ctx
, frame
->data
[0] + bpp
*y
, frame
->linesize
[0], frame
->height
, bpp
) > s
->limit
) {
160 for (y
= frame
->width
- 1; y
> FFMAX(s
->x2
, s
->x1
); y
--) {
161 if (checkline(ctx
, frame
->data
[0] + bpp
*y
, frame
->linesize
[0], frame
->height
, bpp
) > s
->limit
) {
167 // round x and y (up), important for yuv colorspaces
168 // make sure they stay rounded!
175 // w and h must be divisible by 2 as well because of yuv
176 // colorspace problems.
182 shrink_by
= w
% s
->round
;
184 x
+= (shrink_by
/2 + 1) & ~1;
186 shrink_by
= h
% s
->round
;
188 y
+= (shrink_by
/2 + 1) & ~1;
190 SET_META("lavfi.cropdetect.x1", s
->x1
);
191 SET_META("lavfi.cropdetect.x2", s
->x2
);
192 SET_META("lavfi.cropdetect.y1", s
->y1
);
193 SET_META("lavfi.cropdetect.y2", s
->y2
);
194 SET_META("lavfi.cropdetect.w", w
);
195 SET_META("lavfi.cropdetect.h", h
);
196 SET_META("lavfi.cropdetect.x", x
);
197 SET_META("lavfi.cropdetect.y", y
);
199 av_log(ctx
, AV_LOG_INFO
,
200 "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",
201 s
->x1
, s
->x2
, s
->y1
, s
->y2
, w
, h
, x
, y
, frame
->pts
,
202 frame
->pts
== AV_NOPTS_VALUE
? -1 : frame
->pts
* av_q2d(inlink
->time_base
),
206 return ff_filter_frame(inlink
->dst
->outputs
[0], frame
);
209 #define OFFSET(x) offsetof(CropDetectContext, x)
210 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
212 static const AVOption cropdetect_options
[] = {
213 { "limit", "Threshold below which the pixel is considered black", OFFSET(limit
), AV_OPT_TYPE_INT
, { .i64
= 24 }, 0, 255, FLAGS
},
214 { "round", "Value by which the width/height should be divisible", OFFSET(round
), AV_OPT_TYPE_INT
, { .i64
= 16 }, 0, INT_MAX
, FLAGS
},
215 { "reset", "Recalculate the crop area after this many frames", OFFSET(reset_count
), AV_OPT_TYPE_INT
, { .i64
= 0 }, 0, INT_MAX
, FLAGS
},
216 { "reset_count", "Recalculate the crop area after this many frames",OFFSET(reset_count
),AV_OPT_TYPE_INT
,{ .i64
= 0 }, 0, INT_MAX
, FLAGS
},
220 AVFILTER_DEFINE_CLASS(cropdetect
);
222 static const AVFilterPad avfilter_vf_cropdetect_inputs
[] = {
225 .type
= AVMEDIA_TYPE_VIDEO
,
226 .config_props
= config_input
,
227 .filter_frame
= filter_frame
,
232 static const AVFilterPad avfilter_vf_cropdetect_outputs
[] = {
235 .type
= AVMEDIA_TYPE_VIDEO
240 AVFilter ff_vf_cropdetect
= {
241 .name
= "cropdetect",
242 .description
= NULL_IF_CONFIG_SMALL("Auto-detect crop size."),
243 .priv_size
= sizeof(CropDetectContext
),
244 .priv_class
= &cropdetect_class
,
246 .query_formats
= query_formats
,
247 .inputs
= avfilter_vf_cropdetect_inputs
,
248 .outputs
= avfilter_vf_cropdetect_outputs
,
249 .flags
= AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC
,