00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00026 #include <stdio.h>
00027
00028 #include "libavutil/imgutils.h"
00029 #include "libavutil/internal.h"
00030 #include "avfilter.h"
00031 #include "formats.h"
00032 #include "internal.h"
00033 #include "video.h"
00034
00035 typedef struct {
00036 int x1, y1, x2, y2;
00037 int limit;
00038 int round;
00039 int reset_count;
00040 int frame_nb;
00041 int max_pixsteps[4];
00042 } CropDetectContext;
00043
00044 static int query_formats(AVFilterContext *ctx)
00045 {
00046 static const enum PixelFormat pix_fmts[] = {
00047 PIX_FMT_YUV420P, PIX_FMT_YUVJ420P,
00048 PIX_FMT_YUV422P, PIX_FMT_YUVJ422P,
00049 PIX_FMT_YUV444P, PIX_FMT_YUVJ444P,
00050 PIX_FMT_YUV411P, PIX_FMT_GRAY8,
00051 PIX_FMT_NV12, PIX_FMT_NV21,
00052 PIX_FMT_NONE
00053 };
00054
00055 ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
00056 return 0;
00057 }
00058
00059 static int checkline(void *ctx, const unsigned char *src, int stride, int len, int bpp)
00060 {
00061 int total = 0;
00062 int div = len;
00063
00064 switch (bpp) {
00065 case 1:
00066 while (--len >= 0) {
00067 total += src[0];
00068 src += stride;
00069 }
00070 break;
00071 case 3:
00072 case 4:
00073 while (--len >= 0) {
00074 total += src[0] + src[1] + src[2];
00075 src += stride;
00076 }
00077 div *= 3;
00078 break;
00079 }
00080 total /= div;
00081
00082 av_log(ctx, AV_LOG_DEBUG, "total:%d\n", total);
00083 return total;
00084 }
00085
00086 static av_cold int init(AVFilterContext *ctx, const char *args)
00087 {
00088 CropDetectContext *cd = ctx->priv;
00089
00090 cd->limit = 24;
00091 cd->round = 0;
00092 cd->reset_count = 0;
00093 cd->frame_nb = -2;
00094
00095 if (args)
00096 sscanf(args, "%d:%d:%d", &cd->limit, &cd->round, &cd->reset_count);
00097
00098 av_log(ctx, AV_LOG_VERBOSE, "limit:%d round:%d reset_count:%d\n",
00099 cd->limit, cd->round, cd->reset_count);
00100
00101 return 0;
00102 }
00103
00104 static int config_input(AVFilterLink *inlink)
00105 {
00106 AVFilterContext *ctx = inlink->dst;
00107 CropDetectContext *cd = ctx->priv;
00108
00109 av_image_fill_max_pixsteps(cd->max_pixsteps, NULL,
00110 &av_pix_fmt_descriptors[inlink->format]);
00111
00112 cd->x1 = inlink->w - 1;
00113 cd->y1 = inlink->h - 1;
00114 cd->x2 = 0;
00115 cd->y2 = 0;
00116
00117 return 0;
00118 }
00119
00120 static int end_frame(AVFilterLink *inlink)
00121 {
00122 AVFilterContext *ctx = inlink->dst;
00123 CropDetectContext *cd = ctx->priv;
00124 AVFilterBufferRef *picref = inlink->cur_buf;
00125 int bpp = cd->max_pixsteps[0];
00126 int w, h, x, y, shrink_by;
00127
00128
00129 if (++cd->frame_nb > 0) {
00130
00131 if (cd->reset_count > 0 && cd->frame_nb > cd->reset_count) {
00132 cd->x1 = picref->video->w-1;
00133 cd->y1 = picref->video->h-1;
00134 cd->x2 = 0;
00135 cd->y2 = 0;
00136 cd->frame_nb = 1;
00137 }
00138
00139 for (y = 0; y < cd->y1; y++) {
00140 if (checkline(ctx, picref->data[0] + picref->linesize[0] * y, bpp, picref->video->w, bpp) > cd->limit) {
00141 cd->y1 = y;
00142 break;
00143 }
00144 }
00145
00146 for (y = picref->video->h-1; y > cd->y2; y--) {
00147 if (checkline(ctx, picref->data[0] + picref->linesize[0] * y, bpp, picref->video->w, bpp) > cd->limit) {
00148 cd->y2 = y;
00149 break;
00150 }
00151 }
00152
00153 for (y = 0; y < cd->x1; y++) {
00154 if (checkline(ctx, picref->data[0] + bpp*y, picref->linesize[0], picref->video->h, bpp) > cd->limit) {
00155 cd->x1 = y;
00156 break;
00157 }
00158 }
00159
00160 for (y = picref->video->w-1; y > cd->x2; y--) {
00161 if (checkline(ctx, picref->data[0] + bpp*y, picref->linesize[0], picref->video->h, bpp) > cd->limit) {
00162 cd->x2 = y;
00163 break;
00164 }
00165 }
00166
00167
00168
00169 x = (cd->x1+1) & ~1;
00170 y = (cd->y1+1) & ~1;
00171
00172 w = cd->x2 - x + 1;
00173 h = cd->y2 - y + 1;
00174
00175
00176
00177 if (cd->round <= 1)
00178 cd->round = 16;
00179 if (cd->round % 2)
00180 cd->round *= 2;
00181
00182 shrink_by = w % cd->round;
00183 w -= shrink_by;
00184 x += (shrink_by/2 + 1) & ~1;
00185
00186 shrink_by = h % cd->round;
00187 h -= shrink_by;
00188 y += (shrink_by/2 + 1) & ~1;
00189
00190 av_log(ctx, AV_LOG_INFO,
00191 "x1:%d x2:%d y1:%d y2:%d w:%d h:%d x:%d y:%d pos:%"PRId64" pts:%"PRId64" t:%f crop=%d:%d:%d:%d\n",
00192 cd->x1, cd->x2, cd->y1, cd->y2, w, h, x, y, picref->pos, picref->pts,
00193 picref->pts == AV_NOPTS_VALUE ? -1 : picref->pts * av_q2d(inlink->time_base),
00194 w, h, x, y);
00195 }
00196
00197 return ff_end_frame(inlink->dst->outputs[0]);
00198 }
00199
00200 AVFilter avfilter_vf_cropdetect = {
00201 .name = "cropdetect",
00202 .description = NULL_IF_CONFIG_SMALL("Auto-detect crop size."),
00203
00204 .priv_size = sizeof(CropDetectContext),
00205 .init = init,
00206
00207 .query_formats = query_formats,
00208
00209 .inputs = (const AVFilterPad[]) {{ .name = "default",
00210 .type = AVMEDIA_TYPE_VIDEO,
00211 .config_props = config_input,
00212 .get_video_buffer = ff_null_get_video_buffer,
00213 .start_frame = ff_null_start_frame,
00214 .end_frame = end_frame, },
00215 { .name = NULL}},
00216
00217 .outputs = (const AVFilterPad[]) {{ .name = "default",
00218 .type = AVMEDIA_TYPE_VIDEO },
00219 { .name = NULL}},
00220 };