1/* 2 * Copyright (c) 2010 Stefano Sabatini 3 * Copyright (c) 2006 Ivo van Poorten 4 * Copyright (c) 2006 Julian Hall 5 * Copyright (c) 2002-2003 Brian J. Murrell 6 * 7 * This file is part of Libav. 8 * 9 * Libav is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 2 of the License, or 12 * (at your option) any later version. 13 * 14 * Libav is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License along 20 * with Libav; if not, write to the Free Software Foundation, Inc., 21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 22 */ 23 24/** 25 * @file 26 * Search for black frames to detect scene transitions. 27 * Ported from MPlayer libmpcodecs/vf_blackframe.c. 28 */ 29 30#include "avfilter.h" 31 32typedef struct { 33 unsigned int bamount; ///< black amount 34 unsigned int bthresh; ///< black threshold 35 unsigned int frame; ///< frame number 36 unsigned int nblack; ///< number of black pixels counted so far 37} BlackFrameContext; 38 39static int query_formats(AVFilterContext *ctx) 40{ 41 static const enum PixelFormat pix_fmts[] = { 42 PIX_FMT_YUV410P, PIX_FMT_YUV420P, PIX_FMT_GRAY8, PIX_FMT_NV12, 43 PIX_FMT_NV21, PIX_FMT_YUV444P, PIX_FMT_YUV422P, PIX_FMT_YUV411P, 44 PIX_FMT_NONE 45 }; 46 47 avfilter_set_common_formats(ctx, avfilter_make_format_list(pix_fmts)); 48 return 0; 49} 50 51static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque) 52{ 53 BlackFrameContext *blackframe = ctx->priv; 54 55 blackframe->bamount = 98; 56 blackframe->bthresh = 32; 57 blackframe->nblack = 0; 58 blackframe->frame = 0; 59 60 if (args) 61 sscanf(args, "%u:%u", &blackframe->bamount, &blackframe->bthresh); 62 63 av_log(ctx, AV_LOG_INFO, "bamount:%u bthresh:%u\n", 64 blackframe->bamount, blackframe->bthresh); 65 66 if (blackframe->bamount > 100 || blackframe->bthresh > 255) { 67 av_log(ctx, AV_LOG_ERROR, "Too big value for bamount (max is 100) or bthresh (max is 255)\n"); 68 return AVERROR(EINVAL); 69 } 70 71 return 0; 72} 73 74static void draw_slice(AVFilterLink *inlink, int y, int h, int slice_dir) 75{ 76 AVFilterContext *ctx = inlink->dst; 77 BlackFrameContext *blackframe = ctx->priv; 78 AVFilterBufferRef *picref = inlink->cur_buf; 79 int x, i; 80 uint8_t *p = picref->data[0] + y * picref->linesize[0]; 81 82 for (i = 0; i < h; i++) { 83 for (x = 0; x < inlink->w; x++) 84 blackframe->nblack += p[x] < blackframe->bthresh; 85 p += picref->linesize[0]; 86 } 87 88 avfilter_draw_slice(ctx->outputs[0], y, h, slice_dir); 89} 90 91static void end_frame(AVFilterLink *inlink) 92{ 93 AVFilterContext *ctx = inlink->dst; 94 BlackFrameContext *blackframe = ctx->priv; 95 AVFilterBufferRef *picref = inlink->cur_buf; 96 int pblack = 0; 97 98 pblack = blackframe->nblack * 100 / (inlink->w * inlink->h); 99 if (pblack >= blackframe->bamount) 100 av_log(ctx, AV_LOG_INFO, "frame:%u pblack:%u pos:%"PRId64" pts:%"PRId64" t:%f\n", 101 blackframe->frame, pblack, picref->pos, picref->pts, 102 picref->pts == AV_NOPTS_VALUE ? -1 : picref->pts * av_q2d(inlink->time_base)); 103 104 blackframe->frame++; 105 blackframe->nblack = 0; 106 avfilter_end_frame(inlink->dst->outputs[0]); 107} 108 109AVFilter avfilter_vf_blackframe = { 110 .name = "blackframe", 111 .description = NULL_IF_CONFIG_SMALL("Detect frames that are (almost) black."), 112 113 .priv_size = sizeof(BlackFrameContext), 114 .init = init, 115 116 .query_formats = query_formats, 117 118 .inputs = (AVFilterPad[]) {{ .name = "default", 119 .type = AVMEDIA_TYPE_VIDEO, 120 .draw_slice = draw_slice, 121 .get_video_buffer = avfilter_null_get_video_buffer, 122 .start_frame = avfilter_null_start_frame, 123 .end_frame = end_frame, }, 124 { .name = NULL}}, 125 126 .outputs = (AVFilterPad[]) {{ .name = "default", 127 .type = AVMEDIA_TYPE_VIDEO }, 128 { .name = NULL}}, 129}; 130