LCOV - code coverage report
Current view: top level - libavfilter - vf_idet.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 150 208 72.1 %
Date: 2017-12-15 02:19:58 Functions: 11 12 91.7 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (C) 2012 Michael Niedermayer <michaelni@gmx.at>
       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             : #include <float.h> /* FLT_MAX */
      22             : 
      23             : #include "libavutil/cpu.h"
      24             : #include "libavutil/common.h"
      25             : #include "libavutil/opt.h"
      26             : #include "internal.h"
      27             : #include "vf_idet.h"
      28             : 
      29             : #define OFFSET(x) offsetof(IDETContext, x)
      30             : #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
      31             : 
      32             : static const AVOption idet_options[] = {
      33             :     { "intl_thres", "set interlacing threshold", OFFSET(interlace_threshold),   AV_OPT_TYPE_FLOAT, {.dbl = 1.04}, -1, FLT_MAX, FLAGS },
      34             :     { "prog_thres", "set progressive threshold", OFFSET(progressive_threshold), AV_OPT_TYPE_FLOAT, {.dbl = 1.5},  -1, FLT_MAX, FLAGS },
      35             :     { "rep_thres",  "set repeat threshold",      OFFSET(repeat_threshold),      AV_OPT_TYPE_FLOAT, {.dbl = 3.0},  -1, FLT_MAX, FLAGS },
      36             :     { "half_life", "half life of cumulative statistics", OFFSET(half_life),     AV_OPT_TYPE_FLOAT, {.dbl = 0.0},  -1, INT_MAX, FLAGS },
      37             :     { "analyze_interlaced_flag", "set number of frames to use to determine if the interlace flag is accurate", OFFSET(analyze_interlaced_flag), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, INT_MAX, FLAGS },
      38             :     { NULL }
      39             : };
      40             : 
      41             : AVFILTER_DEFINE_CLASS(idet);
      42             : 
      43         104 : static const char *type2str(Type type)
      44             : {
      45         104 :     switch(type) {
      46           0 :         case TFF          : return "tff";
      47           0 :         case BFF          : return "bff";
      48         102 :         case PROGRESSIVE  : return "progressive";
      49           2 :         case UNDETERMINED : return "undetermined";
      50             :     }
      51           0 :     return NULL;
      52             : }
      53             : 
      54             : #define PRECISION 1048576
      55             : 
      56         286 : static uint64_t uintpow(uint64_t b,unsigned int e)
      57             : {
      58         286 :     uint64_t r=1;
      59         286 :     while(e--) r*=b;
      60         286 :     return r;
      61             : }
      62             : 
      63         286 : static int av_dict_set_fxp(AVDictionary **pm, const char *key, uint64_t value, unsigned int digits,
      64             :                 int flags)
      65             : {
      66             :     char valuestr[44];
      67         286 :     uint64_t print_precision = uintpow(10, digits);
      68             : 
      69         286 :     value = av_rescale(value, print_precision, PRECISION);
      70             : 
      71         286 :     snprintf(valuestr, sizeof(valuestr), "%"PRId64".%0*"PRId64,
      72             :              value / print_precision, digits, value % print_precision);
      73             : 
      74         286 :     return av_dict_set(pm, key, valuestr, flags);
      75             : }
      76             : 
      77          52 : static const char *rep2str(RepeatedField repeated_field)
      78             : {
      79          52 :     switch(repeated_field) {
      80          52 :         case REPEAT_NONE    : return "neither";
      81           0 :         case REPEAT_TOP     : return "top";
      82           0 :         case REPEAT_BOTTOM  : return "bottom";
      83             :     }
      84           0 :     return NULL;
      85             : }
      86             : 
      87       44512 : int ff_idet_filter_line_c(const uint8_t *a, const uint8_t *b, const uint8_t *c, int w)
      88             : {
      89             :     int x;
      90       44512 :     int ret=0;
      91             : 
      92    15712736 :     for(x=0; x<w; x++){
      93    15668224 :         int v = (*a++ + *c++) - 2 * *b++;
      94    15668224 :         ret += FFABS(v);
      95             :     }
      96             : 
      97       44512 :     return ret;
      98             : }
      99             : 
     100           0 : int ff_idet_filter_line_c_16bit(const uint16_t *a, const uint16_t *b, const uint16_t *c, int w)
     101             : {
     102             :     int x;
     103           0 :     int ret=0;
     104             : 
     105           0 :     for(x=0; x<w; x++){
     106           0 :         int v = (*a++ + *c++) - 2 * *b++;
     107           0 :         ret += FFABS(v);
     108             :     }
     109             : 
     110           0 :     return ret;
     111             : }
     112             : 
     113          26 : static void filter(AVFilterContext *ctx)
     114             : {
     115          26 :     IDETContext *idet = ctx->priv;
     116             :     int y, i;
     117          26 :     int64_t alpha[2]={0};
     118          26 :     int64_t delta=0;
     119          26 :     int64_t gamma[2]={0};
     120             :     Type type, best_type;
     121             :     RepeatedField repeat;
     122          26 :     int match = 0;
     123          26 :     AVDictionary **metadata = &idet->cur->metadata;
     124             : 
     125          52 :     for (i = 0; i < idet->csp->nb_components; i++) {
     126          26 :         int w = idet->cur->width;
     127          26 :         int h = idet->cur->height;
     128          26 :         int refs = idet->cur->linesize[i];
     129             : 
     130          26 :         if (i && i<3) {
     131           0 :             w = AV_CEIL_RSHIFT(w, idet->csp->log2_chroma_w);
     132           0 :             h = AV_CEIL_RSHIFT(h, idet->csp->log2_chroma_h);
     133             :         }
     134             : 
     135       11154 :         for (y = 2; y < h - 2; y++) {
     136       11128 :             uint8_t *prev = &idet->prev->data[i][y*refs];
     137       11128 :             uint8_t *cur  = &idet->cur ->data[i][y*refs];
     138       11128 :             uint8_t *next = &idet->next->data[i][y*refs];
     139       11128 :             alpha[ y   &1] += idet->filter_line(cur-refs, prev, cur+refs, w);
     140       11128 :             alpha[(y^1)&1] += idet->filter_line(cur-refs, next, cur+refs, w);
     141       11128 :             delta          += idet->filter_line(cur-refs,  cur, cur+refs, w);
     142       11128 :             gamma[(y^1)&1] += idet->filter_line(cur     , prev, cur     , w);
     143             :         }
     144             :     }
     145             : 
     146          26 :     if      (alpha[0] > idet->interlace_threshold * alpha[1]){
     147           0 :         type = TFF;
     148          26 :     }else if(alpha[1] > idet->interlace_threshold * alpha[0]){
     149           0 :         type = BFF;
     150          26 :     }else if(alpha[1] > idet->progressive_threshold * delta){
     151          25 :         type = PROGRESSIVE;
     152             :     }else{
     153           1 :         type = UNDETERMINED;
     154             :     }
     155             : 
     156          26 :     if ( gamma[0] > idet->repeat_threshold * gamma[1] ){
     157           0 :         repeat = REPEAT_TOP;
     158          26 :     } else if ( gamma[1] > idet->repeat_threshold * gamma[0] ){
     159           0 :         repeat = REPEAT_BOTTOM;
     160             :     } else {
     161          26 :         repeat = REPEAT_NONE;
     162             :     }
     163             : 
     164          26 :     memmove(idet->history+1, idet->history, HIST_SIZE-1);
     165          26 :     idet->history[0] = type;
     166          26 :     best_type = UNDETERMINED;
     167         130 :     for(i=0; i<HIST_SIZE; i++){
     168         104 :         if(idet->history[i] != UNDETERMINED){
     169          95 :             if(best_type == UNDETERMINED)
     170          26 :                 best_type = idet->history[i];
     171             : 
     172          95 :             if(idet->history[i] == best_type) {
     173          95 :                 match++;
     174             :             }else{
     175           0 :                 match=0;
     176           0 :                 break;
     177             :             }
     178             :         }
     179             :     }
     180          26 :     if(idet->last_type == UNDETERMINED){
     181           1 :         if(match  ) idet->last_type = best_type;
     182             :     }else{
     183          25 :         if(match>2) idet->last_type = best_type;
     184             :     }
     185             : 
     186          26 :     if      (idet->last_type == TFF){
     187           0 :         idet->cur->top_field_first = 1;
     188           0 :         idet->cur->interlaced_frame = 1;
     189          26 :     }else if(idet->last_type == BFF){
     190           0 :         idet->cur->top_field_first = 0;
     191           0 :         idet->cur->interlaced_frame = 1;
     192          26 :     }else if(idet->last_type == PROGRESSIVE){
     193          26 :         idet->cur->interlaced_frame = 0;
     194             :     }
     195             : 
     196         104 :     for(i=0; i<3; i++)
     197          78 :         idet->repeats[i]  = av_rescale(idet->repeats [i], idet->decay_coefficient, PRECISION);
     198             : 
     199         130 :     for(i=0; i<4; i++){
     200         104 :         idet->prestat [i] = av_rescale(idet->prestat [i], idet->decay_coefficient, PRECISION);
     201         104 :         idet->poststat[i] = av_rescale(idet->poststat[i], idet->decay_coefficient, PRECISION);
     202             :     }
     203             : 
     204          26 :     idet->total_repeats [         repeat] ++;
     205          26 :     idet->repeats       [         repeat] += PRECISION;
     206             : 
     207          26 :     idet->total_prestat [           type] ++;
     208          26 :     idet->prestat       [           type] += PRECISION;
     209             : 
     210          26 :     idet->total_poststat[idet->last_type] ++;
     211          26 :     idet->poststat      [idet->last_type] += PRECISION;
     212             : 
     213          26 :     av_log(ctx, AV_LOG_DEBUG, "Repeated Field:%12s, Single frame:%12s, Multi frame:%12s\n",
     214             :            rep2str(repeat), type2str(type), type2str(idet->last_type));
     215             : 
     216          26 :     av_dict_set    (metadata, "lavfi.idet.repeated.current_frame", rep2str(repeat), 0);
     217          26 :     av_dict_set_fxp(metadata, "lavfi.idet.repeated.neither",       idet->repeats[REPEAT_NONE], 2, 0);
     218          26 :     av_dict_set_fxp(metadata, "lavfi.idet.repeated.top",           idet->repeats[REPEAT_TOP], 2, 0);
     219          26 :     av_dict_set_fxp(metadata, "lavfi.idet.repeated.bottom",        idet->repeats[REPEAT_BOTTOM], 2, 0);
     220             : 
     221          26 :     av_dict_set    (metadata, "lavfi.idet.single.current_frame",   type2str(type), 0);
     222          26 :     av_dict_set_fxp(metadata, "lavfi.idet.single.tff",             idet->prestat[TFF], 2 , 0);
     223          26 :     av_dict_set_fxp(metadata, "lavfi.idet.single.bff",             idet->prestat[BFF], 2, 0);
     224          26 :     av_dict_set_fxp(metadata, "lavfi.idet.single.progressive",     idet->prestat[PROGRESSIVE], 2, 0);
     225          26 :     av_dict_set_fxp(metadata, "lavfi.idet.single.undetermined",    idet->prestat[UNDETERMINED], 2, 0);
     226             : 
     227          26 :     av_dict_set    (metadata, "lavfi.idet.multiple.current_frame", type2str(idet->last_type), 0);
     228          26 :     av_dict_set_fxp(metadata, "lavfi.idet.multiple.tff",           idet->poststat[TFF], 2, 0);
     229          26 :     av_dict_set_fxp(metadata, "lavfi.idet.multiple.bff",           idet->poststat[BFF], 2, 0);
     230          26 :     av_dict_set_fxp(metadata, "lavfi.idet.multiple.progressive",   idet->poststat[PROGRESSIVE], 2, 0);
     231          26 :     av_dict_set_fxp(metadata, "lavfi.idet.multiple.undetermined",  idet->poststat[UNDETERMINED], 2, 0);
     232          26 : }
     233             : 
     234          27 : static int filter_frame(AVFilterLink *link, AVFrame *picref)
     235             : {
     236          27 :     AVFilterContext *ctx = link->dst;
     237          27 :     IDETContext *idet = ctx->priv;
     238             : 
     239             :     // initial frame(s) and not interlaced, just pass through for
     240             :     // the analyze_interlaced_flag mode
     241          27 :     if (idet->analyze_interlaced_flag &&
     242           0 :         !picref->interlaced_frame &&
     243           0 :         !idet->next) {
     244           0 :         return ff_filter_frame(ctx->outputs[0], picref);
     245             :     }
     246          27 :     if (idet->analyze_interlaced_flag_done) {
     247           0 :         if (picref->interlaced_frame && idet->interlaced_flag_accuracy < 0)
     248           0 :             picref->interlaced_frame = 0;
     249           0 :         return ff_filter_frame(ctx->outputs[0], picref);
     250             :     }
     251             : 
     252          27 :     av_frame_free(&idet->prev);
     253             : 
     254          27 :     if(   picref->width  != link->w
     255          27 :        || picref->height != link->h
     256          27 :        || picref->format != link->format) {
     257           0 :         link->dst->inputs[0]->format = picref->format;
     258           0 :         link->dst->inputs[0]->w      = picref->width;
     259           0 :         link->dst->inputs[0]->h      = picref->height;
     260             : 
     261           0 :         av_frame_free(&idet->cur );
     262           0 :         av_frame_free(&idet->next);
     263             :     }
     264             : 
     265          27 :     idet->prev = idet->cur;
     266          27 :     idet->cur  = idet->next;
     267          27 :     idet->next = picref;
     268             : 
     269          28 :     if (!idet->cur &&
     270           1 :         !(idet->cur = av_frame_clone(idet->next)))
     271           0 :         return AVERROR(ENOMEM);
     272             : 
     273          27 :     if (!idet->prev)
     274           1 :         return 0;
     275             : 
     276          26 :     if (!idet->csp)
     277           1 :         idet->csp = av_pix_fmt_desc_get(link->format);
     278          26 :     if (idet->csp->comp[0].depth > 8){
     279           0 :         idet->filter_line = (ff_idet_filter_func)ff_idet_filter_line_c_16bit;
     280             :         if (ARCH_X86)
     281           0 :             ff_idet_init_x86(idet, 1);
     282             :     }
     283             : 
     284          26 :     if (idet->analyze_interlaced_flag) {
     285           0 :         if (idet->cur->interlaced_frame) {
     286           0 :             idet->cur->interlaced_frame = 0;
     287           0 :             filter(ctx);
     288           0 :             if (idet->last_type == PROGRESSIVE) {
     289           0 :                 idet->interlaced_flag_accuracy --;
     290           0 :                 idet->analyze_interlaced_flag --;
     291           0 :             } else if (idet->last_type != UNDETERMINED) {
     292           0 :                 idet->interlaced_flag_accuracy ++;
     293           0 :                 idet->analyze_interlaced_flag --;
     294             :             }
     295           0 :             if (idet->analyze_interlaced_flag == 1) {
     296           0 :                 ff_filter_frame(ctx->outputs[0], av_frame_clone(idet->cur));
     297             : 
     298           0 :                 if (idet->next->interlaced_frame && idet->interlaced_flag_accuracy < 0)
     299           0 :                     idet->next->interlaced_frame = 0;
     300           0 :                 idet->analyze_interlaced_flag_done = 1;
     301           0 :                 av_log(ctx, AV_LOG_INFO, "Final flag accuracy %d\n", idet->interlaced_flag_accuracy);
     302           0 :                 return ff_filter_frame(ctx->outputs[0], av_frame_clone(idet->next));
     303             :             }
     304             :         }
     305             :     } else {
     306          26 :         filter(ctx);
     307             :     }
     308             : 
     309          26 :     return ff_filter_frame(ctx->outputs[0], av_frame_clone(idet->cur));
     310             : }
     311             : 
     312          26 : static int request_frame(AVFilterLink *link)
     313             : {
     314          26 :     AVFilterContext *ctx = link->src;
     315          26 :     IDETContext *idet = ctx->priv;
     316             :     int ret;
     317             : 
     318          26 :     if (idet->eof)
     319           0 :         return AVERROR_EOF;
     320             : 
     321          26 :     ret = ff_request_frame(link->src->inputs[0]);
     322             : 
     323          26 :     if (ret == AVERROR_EOF && idet->cur && !idet->analyze_interlaced_flag_done) {
     324           1 :         AVFrame *next = av_frame_clone(idet->next);
     325             : 
     326           1 :         if (!next)
     327           0 :             return AVERROR(ENOMEM);
     328             : 
     329           1 :         ret = filter_frame(link->src->inputs[0], next);
     330           1 :         idet->eof = 1;
     331             :     }
     332             : 
     333          26 :     return ret;
     334             : }
     335             : 
     336           1 : static av_cold void uninit(AVFilterContext *ctx)
     337             : {
     338           1 :     IDETContext *idet = ctx->priv;
     339           1 :     int level = strncmp(ctx->name, "auto-inserted", 13) ? AV_LOG_INFO : AV_LOG_DEBUG;
     340             : 
     341           1 :     av_log(ctx, level, "Repeated Fields: Neither:%6"PRId64" Top:%6"PRId64" Bottom:%6"PRId64"\n",
     342             :            idet->total_repeats[REPEAT_NONE],
     343             :            idet->total_repeats[REPEAT_TOP],
     344             :            idet->total_repeats[REPEAT_BOTTOM]
     345             :         );
     346           1 :     av_log(ctx, level, "Single frame detection: TFF:%6"PRId64" BFF:%6"PRId64" Progressive:%6"PRId64" Undetermined:%6"PRId64"\n",
     347             :            idet->total_prestat[TFF],
     348             :            idet->total_prestat[BFF],
     349             :            idet->total_prestat[PROGRESSIVE],
     350             :            idet->total_prestat[UNDETERMINED]
     351             :         );
     352           1 :     av_log(ctx, level, "Multi frame detection: TFF:%6"PRId64" BFF:%6"PRId64" Progressive:%6"PRId64" Undetermined:%6"PRId64"\n",
     353             :            idet->total_poststat[TFF],
     354             :            idet->total_poststat[BFF],
     355             :            idet->total_poststat[PROGRESSIVE],
     356             :            idet->total_poststat[UNDETERMINED]
     357             :         );
     358             : 
     359           1 :     av_frame_free(&idet->prev);
     360           1 :     av_frame_free(&idet->cur );
     361           1 :     av_frame_free(&idet->next);
     362           1 : }
     363             : 
     364           1 : static int query_formats(AVFilterContext *ctx)
     365             : {
     366             :     static const enum AVPixelFormat pix_fmts[] = {
     367             :         AV_PIX_FMT_YUV420P,
     368             :         AV_PIX_FMT_YUV422P,
     369             :         AV_PIX_FMT_YUV444P,
     370             :         AV_PIX_FMT_YUV410P,
     371             :         AV_PIX_FMT_YUV411P,
     372             :         AV_PIX_FMT_GRAY8,
     373             :         AV_PIX_FMT_YUVJ420P,
     374             :         AV_PIX_FMT_YUVJ422P,
     375             :         AV_PIX_FMT_YUVJ444P,
     376             :         AV_PIX_FMT_GRAY16,
     377             :         AV_PIX_FMT_YUV440P,
     378             :         AV_PIX_FMT_YUVJ440P,
     379             :         AV_PIX_FMT_YUV420P9,
     380             :         AV_PIX_FMT_YUV422P9,
     381             :         AV_PIX_FMT_YUV444P9,
     382             :         AV_PIX_FMT_YUV420P10,
     383             :         AV_PIX_FMT_YUV422P10,
     384             :         AV_PIX_FMT_YUV444P10,
     385             :         AV_PIX_FMT_YUV420P12,
     386             :         AV_PIX_FMT_YUV422P12,
     387             :         AV_PIX_FMT_YUV444P12,
     388             :         AV_PIX_FMT_YUV420P14,
     389             :         AV_PIX_FMT_YUV422P14,
     390             :         AV_PIX_FMT_YUV444P14,
     391             :         AV_PIX_FMT_YUV420P16,
     392             :         AV_PIX_FMT_YUV422P16,
     393             :         AV_PIX_FMT_YUV444P16,
     394             :         AV_PIX_FMT_YUVA420P,
     395             :         AV_PIX_FMT_NONE
     396             :     };
     397           1 :     AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts);
     398           1 :     if (!fmts_list)
     399           0 :         return AVERROR(ENOMEM);
     400           1 :     return ff_set_common_formats(ctx, fmts_list);
     401             : }
     402             : 
     403           1 : static av_cold int init(AVFilterContext *ctx)
     404             : {
     405           1 :     IDETContext *idet = ctx->priv;
     406             : 
     407           1 :     idet->eof = 0;
     408           1 :     idet->last_type = UNDETERMINED;
     409           1 :     memset(idet->history, UNDETERMINED, HIST_SIZE);
     410             : 
     411           1 :     if( idet->half_life > 0 )
     412           0 :         idet->decay_coefficient = lrint( PRECISION * exp2(-1.0 / idet->half_life) );
     413             :     else
     414           1 :         idet->decay_coefficient = PRECISION;
     415             : 
     416           1 :     idet->filter_line = ff_idet_filter_line_c;
     417             : 
     418             :     if (ARCH_X86)
     419           1 :         ff_idet_init_x86(idet, 0);
     420             : 
     421           1 :     return 0;
     422             : }
     423             : 
     424             : static const AVFilterPad idet_inputs[] = {
     425             :     {
     426             :         .name         = "default",
     427             :         .type         = AVMEDIA_TYPE_VIDEO,
     428             :         .filter_frame = filter_frame,
     429             :     },
     430             :     { NULL }
     431             : };
     432             : 
     433             : static const AVFilterPad idet_outputs[] = {
     434             :     {
     435             :         .name         = "default",
     436             :         .type         = AVMEDIA_TYPE_VIDEO,
     437             :         .request_frame = request_frame
     438             :     },
     439             :     { NULL }
     440             : };
     441             : 
     442             : AVFilter ff_vf_idet = {
     443             :     .name          = "idet",
     444             :     .description   = NULL_IF_CONFIG_SMALL("Interlace detect Filter."),
     445             :     .priv_size     = sizeof(IDETContext),
     446             :     .init          = init,
     447             :     .uninit        = uninit,
     448             :     .query_formats = query_formats,
     449             :     .inputs        = idet_inputs,
     450             :     .outputs       = idet_outputs,
     451             :     .priv_class    = &idet_class,
     452             : };

Generated by: LCOV version 1.13