00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00027 #include <float.h>
00028 #include "libavutil/opt.h"
00029 #include "libavutil/timestamp.h"
00030 #include "avfilter.h"
00031 #include "internal.h"
00032
00033 typedef struct {
00034 const AVClass *class;
00035 double black_min_duration_time;
00036 int64_t black_min_duration;
00037 int64_t black_start;
00038 int64_t black_end;
00039 int black_started;
00040
00041 double picture_black_ratio_th;
00042 double pixel_black_th;
00043 unsigned int pixel_black_th_i;
00044
00045 unsigned int frame_count;
00046 unsigned int nb_black_pixels;
00047 } BlackDetectContext;
00048
00049 #define OFFSET(x) offsetof(BlackDetectContext, x)
00050 static const AVOption blackdetect_options[] = {
00051 { "d", "set minimum detected black duration in seconds", OFFSET(black_min_duration_time), AV_OPT_TYPE_DOUBLE, {.dbl=2}, 0, DBL_MAX},
00052 { "black_min_duration", "set minimum detected black duration in seconds", OFFSET(black_min_duration_time), AV_OPT_TYPE_DOUBLE, {.dbl=2}, 0, DBL_MAX},
00053 { "picture_black_ratio_th", "set the picture black ratio threshold", OFFSET(picture_black_ratio_th), AV_OPT_TYPE_DOUBLE, {.dbl=.98}, 0, 1},
00054 { "pic_th", "set the picture black ratio threshold", OFFSET(picture_black_ratio_th), AV_OPT_TYPE_DOUBLE, {.dbl=.98}, 0, 1},
00055 { "pixel_black_th", "set the pixel black threshold", OFFSET(pixel_black_th), AV_OPT_TYPE_DOUBLE, {.dbl=.10}, 0, 1},
00056 { "pix_th", "set the pixel black threshold", OFFSET(pixel_black_th), AV_OPT_TYPE_DOUBLE, {.dbl=.10}, 0, 1},
00057 { NULL },
00058 };
00059
00060 static const char *blackdetect_get_name(void *ctx)
00061 {
00062 return "blackdetect";
00063 }
00064
00065 static const AVClass blackdetect_class = {
00066 .class_name = "BlackDetectContext",
00067 .item_name = blackdetect_get_name,
00068 .option = blackdetect_options,
00069 };
00070
00071 #define YUVJ_FORMATS \
00072 PIX_FMT_YUVJ420P, PIX_FMT_YUVJ422P, PIX_FMT_YUVJ444P, PIX_FMT_YUVJ440P
00073
00074 static enum PixelFormat yuvj_formats[] = {
00075 YUVJ_FORMATS, PIX_FMT_NONE
00076 };
00077
00078 static int query_formats(AVFilterContext *ctx)
00079 {
00080 static const enum PixelFormat pix_fmts[] = {
00081 PIX_FMT_YUV410P, PIX_FMT_YUV420P, PIX_FMT_GRAY8, PIX_FMT_NV12,
00082 PIX_FMT_NV21, PIX_FMT_YUV444P, PIX_FMT_YUV422P, PIX_FMT_YUV411P,
00083 YUVJ_FORMATS,
00084 PIX_FMT_NONE
00085 };
00086
00087 avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));
00088 return 0;
00089 }
00090
00091 static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
00092 {
00093 int ret;
00094 BlackDetectContext *blackdetect = ctx->priv;
00095
00096 blackdetect->class = &blackdetect_class;
00097 av_opt_set_defaults(blackdetect);
00098
00099 if ((ret = av_set_options_string(blackdetect, args, "=", ":")) < 0) {
00100 av_log(ctx, AV_LOG_ERROR, "Error parsing options string: '%s'\n", args);
00101 return ret;
00102 }
00103
00104 return 0;
00105 }
00106
00107 static int config_input(AVFilterLink *inlink)
00108 {
00109 AVFilterContext *ctx = inlink->dst;
00110 BlackDetectContext *blackdetect = ctx->priv;
00111
00112 blackdetect->black_min_duration =
00113 blackdetect->black_min_duration_time / av_q2d(inlink->time_base);
00114
00115 blackdetect->pixel_black_th_i = ff_fmt_is_in(inlink->format, yuvj_formats) ?
00116
00117 blackdetect->pixel_black_th * 255 :
00118 16 + blackdetect->pixel_black_th * (235 - 16);
00119
00120 av_log(blackdetect, AV_LOG_INFO,
00121 "black_min_duration:%s pixel_black_th:%f pixel_black_th_i:%d picture_black_ratio_th:%f\n",
00122 av_ts2timestr(blackdetect->black_min_duration, &inlink->time_base),
00123 blackdetect->pixel_black_th, blackdetect->pixel_black_th_i,
00124 blackdetect->picture_black_ratio_th);
00125 return 0;
00126 }
00127
00128 static void draw_slice(AVFilterLink *inlink, int y, int h, int slice_dir)
00129 {
00130 AVFilterContext *ctx = inlink->dst;
00131 BlackDetectContext *blackdetect = ctx->priv;
00132 AVFilterBufferRef *picref = inlink->cur_buf;
00133 int x, i;
00134 const uint8_t *p = picref->data[0] + y * picref->linesize[0];
00135
00136 for (i = 0; i < h; i++) {
00137 for (x = 0; x < inlink->w; x++)
00138 blackdetect->nb_black_pixels += p[x] <= blackdetect->pixel_black_th_i;
00139 p += picref->linesize[0];
00140 }
00141
00142 avfilter_draw_slice(ctx->outputs[0], y, h, slice_dir);
00143 }
00144
00145 static void end_frame(AVFilterLink *inlink)
00146 {
00147 AVFilterContext *ctx = inlink->dst;
00148 BlackDetectContext *blackdetect = ctx->priv;
00149 AVFilterBufferRef *picref = inlink->cur_buf;
00150 double picture_black_ratio = 0;
00151
00152 picture_black_ratio = (double)blackdetect->nb_black_pixels / (inlink->w * inlink->h);
00153
00154 av_log(ctx, AV_LOG_DEBUG,
00155 "frame:%u picture_black_ratio:%f pos:%"PRId64" pts:%s t:%s type:%c\n",
00156 blackdetect->frame_count, picture_black_ratio,
00157 picref->pos, av_ts2str(picref->pts),
00158 av_ts2timestr(blackdetect->black_start, &inlink->time_base),
00159 av_get_picture_type_char(picref->video->pict_type));
00160
00161 if (picture_black_ratio >= blackdetect->picture_black_ratio_th) {
00162 if (!blackdetect->black_started) {
00163
00164 blackdetect->black_started = 1;
00165 blackdetect->black_start = picref->pts;
00166 }
00167 } else if (blackdetect->black_started) {
00168
00169 blackdetect->black_started = 0;
00170 blackdetect->black_end = picref->pts;
00171
00172 if ((blackdetect->black_end - blackdetect->black_start) >= blackdetect->black_min_duration) {
00173 av_log(blackdetect, AV_LOG_INFO,
00174 "black_start:%s black_end:%s black_duration:%s\n",
00175 av_ts2timestr(blackdetect->black_start, &inlink->time_base),
00176 av_ts2timestr(blackdetect->black_end, &inlink->time_base),
00177 av_ts2timestr(blackdetect->black_end - blackdetect->black_start, &inlink->time_base));
00178 }
00179 }
00180
00181 blackdetect->frame_count++;
00182 blackdetect->nb_black_pixels = 0;
00183 avfilter_unref_buffer(picref);
00184 avfilter_end_frame(inlink->dst->outputs[0]);
00185 }
00186
00187 AVFilter avfilter_vf_blackdetect = {
00188 .name = "blackdetect",
00189 .description = NULL_IF_CONFIG_SMALL("Detect video intervals that are (almost) black."),
00190 .priv_size = sizeof(BlackDetectContext),
00191 .init = init,
00192 .query_formats = query_formats,
00193
00194 .inputs = (const AVFilterPad[]) {
00195 { .name = "default",
00196 .type = AVMEDIA_TYPE_VIDEO,
00197 .config_props = config_input,
00198 .draw_slice = draw_slice,
00199 .get_video_buffer = avfilter_null_get_video_buffer,
00200 .start_frame = ff_null_start_frame_keep_ref,
00201 .end_frame = end_frame, },
00202 { .name = NULL }
00203 },
00204
00205 .outputs = (const AVFilterPad[]) {
00206 { .name = "default",
00207 .type = AVMEDIA_TYPE_VIDEO },
00208 { .name = NULL }
00209 },
00210 };