1/* 2 * Copyright (c) 2002 A'rpi 3 * This file is part of Libav. 4 * 5 * Libav 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. 9 * 10 * Libav 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. 14 * 15 * You should have received a copy of the GNU General Public License along 16 * with Libav; if not, write to the Free Software Foundation, Inc., 17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 */ 19 20/** 21 * @file 22 * border detection filter 23 * Ported from MPlayer libmpcodecs/vf_cropdetect.c. 24 */ 25 26#include "libavutil/imgutils.h" 27#include "avfilter.h" 28 29typedef struct { 30 int x1, y1, x2, y2; 31 int limit; 32 int round; 33 int reset_count; 34 int frame_nb; 35 int max_pixsteps[4]; 36} CropDetectContext; 37 38static int query_formats(AVFilterContext *ctx) 39{ 40 static const enum PixelFormat pix_fmts[] = { 41 PIX_FMT_YUV420P, PIX_FMT_YUVJ420P, 42 PIX_FMT_YUV422P, PIX_FMT_YUVJ422P, 43 PIX_FMT_YUV444P, PIX_FMT_YUVJ444P, 44 PIX_FMT_YUV411P, PIX_FMT_GRAY8, 45 PIX_FMT_NV12, PIX_FMT_NV21, 46 PIX_FMT_NONE 47 }; 48 49 avfilter_set_common_formats(ctx, avfilter_make_format_list(pix_fmts)); 50 return 0; 51} 52 53static int checkline(void *ctx, const unsigned char *src, int stride, int len, int bpp) 54{ 55 int total = 0; 56 int div = len; 57 58 switch (bpp) { 59 case 1: 60 while (--len >= 0) { 61 total += src[0]; 62 src += stride; 63 } 64 break; 65 case 3: 66 case 4: 67 while (--len >= 0) { 68 total += src[0] + src[1] + src[2]; 69 src += stride; 70 } 71 div *= 3; 72 break; 73 } 74 total /= div; 75 76 av_log(ctx, AV_LOG_DEBUG, "total:%d\n", total); 77 return total; 78} 79 80static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque) 81{ 82 CropDetectContext *cd = ctx->priv; 83 84 cd->limit = 24; 85 cd->round = 0; 86 cd->reset_count = 0; 87 cd->frame_nb = -2; 88 89 if (args) 90 sscanf(args, "%d:%d:%d", &cd->limit, &cd->round, &cd->reset_count); 91 92 av_log(ctx, AV_LOG_INFO, "limit:%d round:%d reset_count:%d\n", 93 cd->limit, cd->round, cd->reset_count); 94 95 return 0; 96} 97 98static int config_input(AVFilterLink *inlink) 99{ 100 AVFilterContext *ctx = inlink->dst; 101 CropDetectContext *cd = ctx->priv; 102 103 av_image_fill_max_pixsteps(cd->max_pixsteps, NULL, 104 &av_pix_fmt_descriptors[inlink->format]); 105 106 cd->x1 = inlink->w - 1; 107 cd->y1 = inlink->h - 1; 108 cd->x2 = 0; 109 cd->y2 = 0; 110 111 return 0; 112} 113 114static void end_frame(AVFilterLink *inlink) 115{ 116 AVFilterContext *ctx = inlink->dst; 117 CropDetectContext *cd = ctx->priv; 118 AVFilterBufferRef *picref = inlink->cur_buf; 119 int bpp = cd->max_pixsteps[0]; 120 int w, h, x, y, shrink_by; 121 122 // ignore first 2 frames - they may be empty 123 if (++cd->frame_nb > 0) { 124 // Reset the crop area every reset_count frames, if reset_count is > 0 125 if (cd->reset_count > 0 && cd->frame_nb > cd->reset_count) { 126 cd->x1 = picref->video->w-1; 127 cd->y1 = picref->video->h-1; 128 cd->x2 = 0; 129 cd->y2 = 0; 130 cd->frame_nb = 1; 131 } 132 133 for (y = 0; y < cd->y1; y++) { 134 if (checkline(ctx, picref->data[0] + picref->linesize[0] * y, bpp, picref->video->w, bpp) > cd->limit) { 135 cd->y1 = y; 136 break; 137 } 138 } 139 140 for (y = picref->video->h-1; y > cd->y2; y--) { 141 if (checkline(ctx, picref->data[0] + picref->linesize[0] * y, bpp, picref->video->w, bpp) > cd->limit) { 142 cd->y2 = y; 143 break; 144 } 145 } 146 147 for (y = 0; y < cd->x1; y++) { 148 if (checkline(ctx, picref->data[0] + bpp*y, picref->linesize[0], picref->video->h, bpp) > cd->limit) { 149 cd->x1 = y; 150 break; 151 } 152 } 153 154 for (y = picref->video->w-1; y > cd->x2; y--) { 155 if (checkline(ctx, picref->data[0] + bpp*y, picref->linesize[0], picref->video->h, bpp) > cd->limit) { 156 cd->x2 = y; 157 break; 158 } 159 } 160 161 // round x and y (up), important for yuv colorspaces 162 // make sure they stay rounded! 163 x = (cd->x1+1) & ~1; 164 y = (cd->y1+1) & ~1; 165 166 w = cd->x2 - x + 1; 167 h = cd->y2 - y + 1; 168 169 // w and h must be divisible by 2 as well because of yuv 170 // colorspace problems. 171 if (cd->round <= 1) 172 cd->round = 16; 173 if (cd->round % 2) 174 cd->round *= 2; 175 176 shrink_by = w % cd->round; 177 w -= shrink_by; 178 x += (shrink_by/2 + 1) & ~1; 179 180 shrink_by = h % cd->round; 181 h -= shrink_by; 182 y += (shrink_by/2 + 1) & ~1; 183 184 av_log(ctx, AV_LOG_INFO, 185 "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", 186 cd->x1, cd->x2, cd->y1, cd->y2, w, h, x, y, picref->pos, picref->pts, 187 picref->pts == AV_NOPTS_VALUE ? -1 : picref->pts * av_q2d(inlink->time_base), 188 w, h, x, y); 189 } 190 191 avfilter_end_frame(inlink->dst->outputs[0]); 192} 193 194AVFilter avfilter_vf_cropdetect = { 195 .name = "cropdetect", 196 .description = NULL_IF_CONFIG_SMALL("Auto-detect crop size."), 197 198 .priv_size = sizeof(CropDetectContext), 199 .init = init, 200 201 .query_formats = query_formats, 202 203 .inputs = (AVFilterPad[]) {{ .name = "default", 204 .type = AVMEDIA_TYPE_VIDEO, 205 .config_props = config_input, 206 .get_video_buffer = avfilter_null_get_video_buffer, 207 .start_frame = avfilter_null_start_frame, 208 .end_frame = end_frame, }, 209 { .name = NULL}}, 210 211 .outputs = (AVFilterPad[]) {{ .name = "default", 212 .type = AVMEDIA_TYPE_VIDEO }, 213 { .name = NULL}}, 214}; 215