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