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 AVPixelFormat pix_fmts[] = {
00047 AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUVJ420P,
00048 AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVJ422P,
00049 AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUVJ444P,
00050 AV_PIX_FMT_YUV411P, AV_PIX_FMT_GRAY8,
00051 AV_PIX_FMT_NV12, AV_PIX_FMT_NV21,
00052 AV_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_desc_get(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 filter_frame(AVFilterLink *inlink, AVFilterBufferRef *frame)
00121 {
00122 AVFilterContext *ctx = inlink->dst;
00123 CropDetectContext *cd = ctx->priv;
00124 int bpp = cd->max_pixsteps[0];
00125 int w, h, x, y, shrink_by;
00126
00127
00128 if (++cd->frame_nb > 0) {
00129
00130 if (cd->reset_count > 0 && cd->frame_nb > cd->reset_count) {
00131 cd->x1 = frame->video->w-1;
00132 cd->y1 = frame->video->h-1;
00133 cd->x2 = 0;
00134 cd->y2 = 0;
00135 cd->frame_nb = 1;
00136 }
00137
00138 for (y = 0; y < cd->y1; y++) {
00139 if (checkline(ctx, frame->data[0] + frame->linesize[0] * y, bpp, frame->video->w, bpp) > cd->limit) {
00140 cd->y1 = y;
00141 break;
00142 }
00143 }
00144
00145 for (y = frame->video->h-1; y > cd->y2; y--) {
00146 if (checkline(ctx, frame->data[0] + frame->linesize[0] * y, bpp, frame->video->w, bpp) > cd->limit) {
00147 cd->y2 = y;
00148 break;
00149 }
00150 }
00151
00152 for (y = 0; y < cd->x1; y++) {
00153 if (checkline(ctx, frame->data[0] + bpp*y, frame->linesize[0], frame->video->h, bpp) > cd->limit) {
00154 cd->x1 = y;
00155 break;
00156 }
00157 }
00158
00159 for (y = frame->video->w-1; y > cd->x2; y--) {
00160 if (checkline(ctx, frame->data[0] + bpp*y, frame->linesize[0], frame->video->h, bpp) > cd->limit) {
00161 cd->x2 = y;
00162 break;
00163 }
00164 }
00165
00166
00167
00168 x = (cd->x1+1) & ~1;
00169 y = (cd->y1+1) & ~1;
00170
00171 w = cd->x2 - x + 1;
00172 h = cd->y2 - y + 1;
00173
00174
00175
00176 if (cd->round <= 1)
00177 cd->round = 16;
00178 if (cd->round % 2)
00179 cd->round *= 2;
00180
00181 shrink_by = w % cd->round;
00182 w -= shrink_by;
00183 x += (shrink_by/2 + 1) & ~1;
00184
00185 shrink_by = h % cd->round;
00186 h -= shrink_by;
00187 y += (shrink_by/2 + 1) & ~1;
00188
00189 av_log(ctx, AV_LOG_INFO,
00190 "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",
00191 cd->x1, cd->x2, cd->y1, cd->y2, w, h, x, y, frame->pos, frame->pts,
00192 frame->pts == AV_NOPTS_VALUE ? -1 : frame->pts * av_q2d(inlink->time_base),
00193 w, h, x, y);
00194 }
00195
00196 return ff_filter_frame(inlink->dst->outputs[0], frame);
00197 }
00198
00199 static const AVFilterPad avfilter_vf_cropdetect_inputs[] = {
00200 {
00201 .name = "default",
00202 .type = AVMEDIA_TYPE_VIDEO,
00203 .config_props = config_input,
00204 .get_video_buffer = ff_null_get_video_buffer,
00205 .filter_frame = filter_frame,
00206 },
00207 { NULL }
00208 };
00209
00210 static const AVFilterPad avfilter_vf_cropdetect_outputs[] = {
00211 {
00212 .name = "default",
00213 .type = AVMEDIA_TYPE_VIDEO
00214 },
00215 { NULL }
00216 };
00217
00218 AVFilter avfilter_vf_cropdetect = {
00219 .name = "cropdetect",
00220 .description = NULL_IF_CONFIG_SMALL("Auto-detect crop size."),
00221
00222 .priv_size = sizeof(CropDetectContext),
00223 .init = init,
00224
00225 .query_formats = query_formats,
00226
00227 .inputs = avfilter_vf_cropdetect_inputs,
00228
00229 .outputs = avfilter_vf_cropdetect_outputs,
00230 };