LCOV - code coverage report
Current view: top level - libavfilter - vf_blackdetect.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 62 0.0 %
Date: 2017-12-16 13:57:32 Functions: 0 5 0.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2012 Stefano Sabatini
       3             :  *
       4             :  * This file is part of FFmpeg.
       5             :  *
       6             :  * FFmpeg is free software; you can redistribute it and/or
       7             :  * modify it under the terms of the GNU Lesser General Public
       8             :  * License as published by the Free Software Foundation; either
       9             :  * version 2.1 of the License, or (at your option) any later version.
      10             :  *
      11             :  * FFmpeg is distributed in the hope that it will be useful,
      12             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      14             :  * Lesser General Public License for more details.
      15             :  *
      16             :  * You should have received a copy of the GNU Lesser General Public
      17             :  * License along with FFmpeg; if not, write to the Free Software
      18             :  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
      19             :  */
      20             : 
      21             : /**
      22             :  * @file
      23             :  * Video black detector, loosely based on blackframe with extended
      24             :  * syntax and features
      25             :  */
      26             : 
      27             : #include <float.h>
      28             : #include "libavutil/opt.h"
      29             : #include "libavutil/timestamp.h"
      30             : #include "avfilter.h"
      31             : #include "internal.h"
      32             : 
      33             : typedef struct BlackDetectContext {
      34             :     const AVClass *class;
      35             :     double  black_min_duration_time; ///< minimum duration of detected black, in seconds
      36             :     int64_t black_min_duration;      ///< minimum duration of detected black, expressed in timebase units
      37             :     int64_t black_start;             ///< pts start time of the first black picture
      38             :     int64_t black_end;               ///< pts end time of the last black picture
      39             :     int64_t last_picref_pts;         ///< pts of the last input picture
      40             :     int black_started;
      41             : 
      42             :     double       picture_black_ratio_th;
      43             :     double       pixel_black_th;
      44             :     unsigned int pixel_black_th_i;
      45             : 
      46             :     unsigned int nb_black_pixels;   ///< number of black pixels counted so far
      47             : } BlackDetectContext;
      48             : 
      49             : #define OFFSET(x) offsetof(BlackDetectContext, x)
      50             : #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
      51             : 
      52             : static const AVOption blackdetect_options[] = {
      53             :     { "d",                  "set minimum detected black duration in seconds", OFFSET(black_min_duration_time), AV_OPT_TYPE_DOUBLE, {.dbl=2}, 0, DBL_MAX, FLAGS },
      54             :     { "black_min_duration", "set minimum detected black duration in seconds", OFFSET(black_min_duration_time), AV_OPT_TYPE_DOUBLE, {.dbl=2}, 0, DBL_MAX, FLAGS },
      55             :     { "picture_black_ratio_th", "set the picture black ratio threshold", OFFSET(picture_black_ratio_th), AV_OPT_TYPE_DOUBLE, {.dbl=.98}, 0, 1, FLAGS },
      56             :     { "pic_th",                 "set the picture black ratio threshold", OFFSET(picture_black_ratio_th), AV_OPT_TYPE_DOUBLE, {.dbl=.98}, 0, 1, FLAGS },
      57             :     { "pixel_black_th", "set the pixel black threshold", OFFSET(pixel_black_th), AV_OPT_TYPE_DOUBLE, {.dbl=.10}, 0, 1, FLAGS },
      58             :     { "pix_th",         "set the pixel black threshold", OFFSET(pixel_black_th), AV_OPT_TYPE_DOUBLE, {.dbl=.10}, 0, 1, FLAGS },
      59             :     { NULL }
      60             : };
      61             : 
      62             : AVFILTER_DEFINE_CLASS(blackdetect);
      63             : 
      64             : #define YUVJ_FORMATS \
      65             :     AV_PIX_FMT_YUVJ411P, AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P
      66             : 
      67             : static const enum AVPixelFormat yuvj_formats[] = {
      68             :     YUVJ_FORMATS, AV_PIX_FMT_NONE
      69             : };
      70             : 
      71           0 : static int query_formats(AVFilterContext *ctx)
      72             : {
      73             :     static const enum AVPixelFormat pix_fmts[] = {
      74             :         AV_PIX_FMT_GRAY8,
      75             :         AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUV411P,
      76             :         AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P,
      77             :         AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV444P,
      78             :         AV_PIX_FMT_NV12, AV_PIX_FMT_NV21,
      79             :         YUVJ_FORMATS,
      80             :         AV_PIX_FMT_NONE
      81             :     };
      82             : 
      83           0 :     AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts);
      84           0 :     if (!fmts_list)
      85           0 :         return AVERROR(ENOMEM);
      86           0 :     return ff_set_common_formats(ctx, fmts_list);
      87             : }
      88             : 
      89           0 : static int config_input(AVFilterLink *inlink)
      90             : {
      91           0 :     AVFilterContext *ctx = inlink->dst;
      92           0 :     BlackDetectContext *blackdetect = ctx->priv;
      93             : 
      94           0 :     blackdetect->black_min_duration =
      95           0 :         blackdetect->black_min_duration_time / av_q2d(inlink->time_base);
      96             : 
      97           0 :     blackdetect->pixel_black_th_i = ff_fmt_is_in(inlink->format, yuvj_formats) ?
      98             :         // luminance_minimum_value + pixel_black_th * luminance_range_size
      99           0 :              blackdetect->pixel_black_th *  255 :
     100           0 :         16 + blackdetect->pixel_black_th * (235 - 16);
     101             : 
     102           0 :     av_log(blackdetect, AV_LOG_VERBOSE,
     103             :            "black_min_duration:%s pixel_black_th:%f pixel_black_th_i:%d picture_black_ratio_th:%f\n",
     104           0 :            av_ts2timestr(blackdetect->black_min_duration, &inlink->time_base),
     105             :            blackdetect->pixel_black_th, blackdetect->pixel_black_th_i,
     106             :            blackdetect->picture_black_ratio_th);
     107           0 :     return 0;
     108             : }
     109             : 
     110           0 : static void check_black_end(AVFilterContext *ctx)
     111             : {
     112           0 :     BlackDetectContext *blackdetect = ctx->priv;
     113           0 :     AVFilterLink *inlink = ctx->inputs[0];
     114             : 
     115           0 :     if ((blackdetect->black_end - blackdetect->black_start) >= blackdetect->black_min_duration) {
     116           0 :         av_log(blackdetect, AV_LOG_INFO,
     117             :                "black_start:%s black_end:%s black_duration:%s\n",
     118           0 :                av_ts2timestr(blackdetect->black_start, &inlink->time_base),
     119           0 :                av_ts2timestr(blackdetect->black_end,   &inlink->time_base),
     120           0 :                av_ts2timestr(blackdetect->black_end - blackdetect->black_start, &inlink->time_base));
     121             :     }
     122           0 : }
     123             : 
     124           0 : static int request_frame(AVFilterLink *outlink)
     125             : {
     126           0 :     AVFilterContext *ctx = outlink->src;
     127           0 :     BlackDetectContext *blackdetect = ctx->priv;
     128           0 :     AVFilterLink *inlink = ctx->inputs[0];
     129           0 :     int ret = ff_request_frame(inlink);
     130             : 
     131           0 :     if (ret == AVERROR_EOF && blackdetect->black_started) {
     132             :         // FIXME: black_end should be set to last_picref_pts + last_picref_duration
     133           0 :         blackdetect->black_end = blackdetect->last_picref_pts;
     134           0 :         check_black_end(ctx);
     135             :     }
     136           0 :     return ret;
     137             : }
     138             : 
     139             : // TODO: document metadata
     140           0 : static int filter_frame(AVFilterLink *inlink, AVFrame *picref)
     141             : {
     142           0 :     AVFilterContext *ctx = inlink->dst;
     143           0 :     BlackDetectContext *blackdetect = ctx->priv;
     144           0 :     double picture_black_ratio = 0;
     145           0 :     const uint8_t *p = picref->data[0];
     146             :     int x, i;
     147             : 
     148           0 :     for (i = 0; i < inlink->h; i++) {
     149           0 :         for (x = 0; x < inlink->w; x++)
     150           0 :             blackdetect->nb_black_pixels += p[x] <= blackdetect->pixel_black_th_i;
     151           0 :         p += picref->linesize[0];
     152             :     }
     153             : 
     154           0 :     picture_black_ratio = (double)blackdetect->nb_black_pixels / (inlink->w * inlink->h);
     155             : 
     156           0 :     av_log(ctx, AV_LOG_DEBUG,
     157             :            "frame:%"PRId64" picture_black_ratio:%f pts:%s t:%s type:%c\n",
     158             :            inlink->frame_count_out, picture_black_ratio,
     159           0 :            av_ts2str(picref->pts), av_ts2timestr(picref->pts, &inlink->time_base),
     160           0 :            av_get_picture_type_char(picref->pict_type));
     161             : 
     162           0 :     if (picture_black_ratio >= blackdetect->picture_black_ratio_th) {
     163           0 :         if (!blackdetect->black_started) {
     164             :             /* black starts here */
     165           0 :             blackdetect->black_started = 1;
     166           0 :             blackdetect->black_start = picref->pts;
     167           0 :             av_dict_set(&picref->metadata, "lavfi.black_start",
     168           0 :                 av_ts2timestr(blackdetect->black_start, &inlink->time_base), 0);
     169             :         }
     170           0 :     } else if (blackdetect->black_started) {
     171             :         /* black ends here */
     172           0 :         blackdetect->black_started = 0;
     173           0 :         blackdetect->black_end = picref->pts;
     174           0 :         check_black_end(ctx);
     175           0 :         av_dict_set(&picref->metadata, "lavfi.black_end",
     176           0 :             av_ts2timestr(blackdetect->black_end, &inlink->time_base), 0);
     177             :     }
     178             : 
     179           0 :     blackdetect->last_picref_pts = picref->pts;
     180           0 :     blackdetect->nb_black_pixels = 0;
     181           0 :     return ff_filter_frame(inlink->dst->outputs[0], picref);
     182             : }
     183             : 
     184             : static const AVFilterPad blackdetect_inputs[] = {
     185             :     {
     186             :         .name          = "default",
     187             :         .type          = AVMEDIA_TYPE_VIDEO,
     188             :         .config_props  = config_input,
     189             :         .filter_frame  = filter_frame,
     190             :     },
     191             :     { NULL }
     192             : };
     193             : 
     194             : static const AVFilterPad blackdetect_outputs[] = {
     195             :     {
     196             :         .name          = "default",
     197             :         .type          = AVMEDIA_TYPE_VIDEO,
     198             :         .request_frame = request_frame,
     199             :     },
     200             :     { NULL }
     201             : };
     202             : 
     203             : AVFilter ff_vf_blackdetect = {
     204             :     .name          = "blackdetect",
     205             :     .description   = NULL_IF_CONFIG_SMALL("Detect video intervals that are (almost) black."),
     206             :     .priv_size     = sizeof(BlackDetectContext),
     207             :     .query_formats = query_formats,
     208             :     .inputs        = blackdetect_inputs,
     209             :     .outputs       = blackdetect_outputs,
     210             :     .priv_class    = &blackdetect_class,
     211             : };

Generated by: LCOV version 1.13