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