summaryrefslogtreecommitdiffstats
path: root/debian/transcode/transcode-1.1.7/tools/tcyait.c
diff options
context:
space:
mode:
Diffstat (limited to 'debian/transcode/transcode-1.1.7/tools/tcyait.c')
-rw-r--r--debian/transcode/transcode-1.1.7/tools/tcyait.c1969
1 files changed, 1969 insertions, 0 deletions
diff --git a/debian/transcode/transcode-1.1.7/tools/tcyait.c b/debian/transcode/transcode-1.1.7/tools/tcyait.c
new file mode 100644
index 00000000..84882fa6
--- /dev/null
+++ b/debian/transcode/transcode-1.1.7/tools/tcyait.c
@@ -0,0 +1,1969 @@
+/*
+ * tcyait.c
+ *
+ * Copyright (C) Allan Snider - February 2007
+ *
+ * This file is part of transcode, a video stream processing tool
+ *
+ * transcode is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * transcode is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Make; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+/*
+ * tcyait:
+ * Yet Another Inverse Telecine filter.
+ *
+ * Usage:
+ * tcyait [-dnlotbwmEO] [arg...]
+ * -d Print debug information to stdout
+ * -n Do not drop frames, always de-interlace
+ * -k No forced keep frames
+ * -l log Input yait log file name
+ * -o ops Output yait frame ops file name
+ * -t thresh Interlace detection threshold (>1)
+ * -b blend forced blend threshold (>thresh)
+ * -w size Drop frame look ahead window (0-20)
+ * -m mode Transcode blend method (0-5)
+ * -E thresh Even pattern threhold [thresh]
+ * -O thresh Odd pattern threhold [thresh]
+ * -N noise minimum normalized delta, else considered noise
+ *
+ * By default, reads "yait.log" and produces "yait.ops".
+ *
+ * Description:
+ *
+ * Read a yait log file (generated via -J yait=log), and analyze it to
+ * produce a yait frame operations file. The frame operations file contains
+ * commands to the yait filter to drop, copy or save rows (to de-interlace),
+ * or blend frames. This will convert from NTSC 29.97 to 23.976 fps. The file
+ * generated is used as input for another transcode pass (-J yait=ops).
+ */
+
+
+#define YAIT_VERSION "v0.2"
+
+#define TRUE 1
+#define FALSE 0
+
+/* program defaults */
+#define Y_LOG_FN "yait.log" /* log file read */
+#define Y_OPS_FN "yait.ops" /* frame operation file written */
+#define Y_THRESH 1.2 /* even/odd ratio to detect interlace */
+#define Y_DROPWIN_SIZE 15 /* drop frame look ahead window */
+#define Y_DEINT_MODE 3 /* default transcode de-interlace mode */
+
+/* limits */
+#define Y_DEINT_MIN 0
+#define Y_DEINT_MAX 5
+#define Y_DROPWIN_MIN 0
+#define Y_DROPWIN_MAX 60
+
+#define Y_MTHRESH 1.02 /* minimum ratio allowing de-interlace */
+#define Y_NOISE 0.003 /* normalized delta too weak, noise */
+#define Y_SCENE_CHANGE 0.15 /* normalized delta > 15% of max delta */
+
+/* force de-interlace */
+#define Y_FBLEND 1.6 /* if ratio is > this */
+#define Y_FWEIGHT 0.01 /* if over this weight */
+
+/* frame operation flags */
+#define Y_OP_ODD 0x10
+#define Y_OP_EVEN 0x20
+#define Y_OP_PAT 0x30
+
+#define Y_OP_NOP 0x0
+#define Y_OP_SAVE 0x1
+#define Y_OP_COPY 0x2
+#define Y_OP_DROP 0x4
+#define Y_OP_DEINT 0x8
+
+/* group flags */
+#define Y_HAS_DROP 1
+#define Y_BANK_DROP 2
+#define Y_WITHDRAW_DROP 3
+#define Y_BORROW_DROP 4
+#define Y_RETURN_DROP 5
+#define Y_FORCE_DEINT 6
+#define Y_FORCE_DROP 7
+#define Y_FORCE_KEEP 8
+
+/* frame information */
+typedef struct fi_t Fi;
+struct fi_t
+ {
+ double r; /* even/odd delta ratio, filtered */
+ double ro; /* ratio, original value */
+ double w; /* statistical strength */
+ double nd; /* normalized delta */
+ int fn; /* frame number */
+ int ed; /* even row delta */
+ int od; /* odd row delta */
+ int gi; /* group array index */
+ int ip; /* telecine pattern */
+ int op; /* frame operation, nop, save/copy row */
+ int drop; /* frame is to be dropped */
+ int gf; /* group flag */
+ Fi *next;
+ };
+
+
+/*
+ * globals:
+ */
+
+char *Prog; /* argv[0] */
+char *LogFn; /* log file name, default "yait.log" */
+char *OpsFn; /* ops file name, default "yait.ops" */
+double Thresh; /* row delta ratio interlace detection threshold */
+double EThresh; /* even interlace detection threshold */
+double OThresh; /* odd interlace detection threshold */
+double Blend; /* force frame blending over this threshold */
+double Noise; /* minimum normalized delta */
+int DropWin; /* drop frame look ahead window */
+int DeintMode; /* transcode de-interlace mode, (1-5) */
+int DebugFi; /* dump debug frame info */
+int NoDrops; /* force de-interlace everywhere (non vfr) */
+int NoKeeps; /* Don't force keep frames (allows a/v sync drift) */
+
+FILE *LogFp; /* log file */
+FILE *OpsFp; /* ops file */
+
+Fi *Fl; /* frame list */
+Fi **Fa; /* frame array */
+Fi **Ga; /* group array */
+int *Da; /* drop count array */
+int Nf; /* number of frames */
+int Ng; /* number of elements in Ga */
+int Nd; /* number of elements in Da */
+int Md; /* max delta */
+
+int Stat_nd; /* total number of dropped frames */
+int Stat_nb; /* total number of blended frames */
+int Stat_no; /* total number of odd interlaced pairs */
+int Stat_ne; /* total number of even interlaced pairs */
+int Stat_fd; /* total number of forced drops */
+int Stat_fk; /* total number of forced keeps */
+
+
+/*
+ * protos:
+ */
+
+static void yait_parse_args( int, char** );
+static void yait_chkac( int* );
+static void yait_usage( void );
+static void yait_read_log( void );
+static double yait_calc_ratio( int, int );
+static void yait_find_ip( void );
+static void yait_chk_ip( int );
+static void yait_chk_pairs( int );
+static void yait_chk_tuplets( int );
+static int yait_find_odd( double, int, double*, int );
+static int yait_find_even( double, int, double*, int );
+static int yait_ffmin( int, int );
+static int yait_ffmax( int, int );
+static int yait_m5( int );
+static void yait_mark_grp( int, int, double );
+static void yait_find_drops( void );
+static int yait_cnt_drops( int );
+static int yait_extra_drop( int );
+static int yait_missing_drop( int );
+static void yait_keep_frame( int );
+static int yait_get_hdrop( int, int* );
+static void yait_ivtc_keep( int );
+static void yait_ivtc_grps( void );
+static int yait_scan_bk( int );
+static int yait_scan_fw( int );
+static void yait_drop_frame( int );
+static int yait_ivtc_grp( int, int, int );
+static double yait_tst_ip( int, int );
+static void yait_deint( void );
+static void yait_write_ops( void );
+static char *yait_write_op( Fi* );
+static void yait_fini( void );
+
+static void yait_debug_fi( void );
+static char *yait_op( int op );
+static char *yait_drop( Fi *f );
+static char *yait_grp( int flg );
+
+
+/*
+ * main:
+ */
+
+int
+main( int argc, char **argv )
+ {
+ /* parse args */
+ yait_parse_args( argc, argv );
+
+ LogFp = fopen( LogFn, "r" );
+ if( !LogFp )
+ {
+ perror( "fopen" );
+ fprintf( stderr, "Cannot open YAIT delta log file (%s)\n", LogFn );
+ exit( 1 );
+ }
+
+ OpsFp = fopen( OpsFn, "w" );
+ if( !OpsFp )
+ {
+ perror( "fopen" );
+ fprintf( stderr, "Cannot create YAIT frame ops file (%s)\n", OpsFn );
+ exit( 1 );
+ }
+
+ /* read the log */
+ yait_read_log();
+
+ /* find interleave patterns */
+ yait_find_ip();
+
+ /* find drop frames */
+ yait_find_drops();
+
+ /* complete groups missing an interleave pattern */
+ yait_ivtc_grps();
+
+ /* let transcode de-interlace frames we missed */
+ yait_deint();
+
+ /* print frame ops file */
+ yait_write_ops();
+
+ if( DebugFi )
+ yait_debug_fi();
+
+ /* graceful exit */
+ yait_fini();
+
+ return( 0 );
+ }
+
+
+/*
+ * yait_parse_args:
+ */
+
+static void
+yait_parse_args( int argc, char **argv )
+ {
+ int opt;
+ char *p;
+
+ LogFn = Y_LOG_FN;
+ OpsFn = Y_OPS_FN;
+ Thresh = Y_THRESH;
+ EThresh = 0;
+ OThresh = 0;
+ Blend = Y_FBLEND;
+ Noise = Y_NOISE;
+ DeintMode = Y_DEINT_MODE;
+ DropWin = Y_DROPWIN_SIZE;
+
+ --argc;
+ Prog = *argv++;
+ while( (p = *argv) )
+ {
+ if( *p++ != '-' )
+ break;
+ while( (opt = *p++) )
+ switch( opt )
+ {
+ case 'd':
+ DebugFi = TRUE;
+ break;
+
+ case 'n':
+ NoDrops = TRUE;
+ break;
+
+ case 'k':
+ NoKeeps = TRUE;
+ break;
+
+ case 'l':
+ yait_chkac( &argc );
+ LogFn = *++argv;
+ break;
+
+ case 'o':
+ yait_chkac( &argc );
+ OpsFn = *++argv;
+ break;
+
+ case 't':
+ yait_chkac( &argc );
+ Thresh = atof( *++argv );
+ break;
+
+ case 'E':
+ yait_chkac( &argc );
+ EThresh = atof( *++argv );
+ break;
+
+ case 'O':
+ yait_chkac( &argc );
+ OThresh = atof( *++argv );
+ break;
+
+ case 'b':
+ yait_chkac( &argc );
+ Blend = atof( *++argv );
+ break;
+
+ case 'N':
+ yait_chkac( &argc );
+ Noise = atof( *++argv );
+ break;
+
+ case 'w':
+ yait_chkac( &argc );
+ DropWin = atoi( *++argv );
+ break;
+
+ case 'm':
+ yait_chkac( &argc );
+ DeintMode = atoi( *++argv );
+ break;
+
+ default:
+ yait_usage();
+ break;
+ }
+ --argc;
+ argv++;
+ }
+
+ if( Thresh <= 1 )
+ {
+ printf( "Invalid threshold specified (%g).\n\n", Thresh );
+ yait_usage();
+ }
+
+ if( Blend <= Thresh )
+ {
+ printf( "Invalid blend threshold specified (%g).\n\n", Blend );
+ yait_usage();
+ }
+
+ if( DropWin<Y_DROPWIN_MIN || DropWin>Y_DROPWIN_MAX )
+ {
+ printf( "Invalid drop window size specified (%d).\n\n", DropWin );
+ yait_usage();
+ }
+
+ if( DeintMode<Y_DEINT_MIN || DeintMode>Y_DEINT_MAX )
+ {
+ printf( "Invalid de-interlace method specified (%d).\n\n", DeintMode );
+ yait_usage();
+ }
+
+ if( !EThresh )
+ EThresh = Thresh;
+ if( !OThresh )
+ OThresh = Thresh;
+
+ if( argc )
+ yait_usage();
+ }
+
+
+/*
+ * yait_chkac:
+ */
+
+static void
+yait_chkac( int *ac )
+ {
+ if( *ac < 1 )
+ yait_usage();
+ --*ac;
+ }
+
+
+/*
+ * yait_usage:
+ */
+
+static void
+yait_usage( void )
+ {
+ printf( "Usage: %s [-dnklotbwmEON] [arg...] \n", Prog );
+ printf( "\t-d\t\tPrint debug information to stdout.\n" );
+ printf( "\t-n\t\tDo not drop frames, always de-interlace.\n" );
+ printf( "\t-k\t\tNo forced keep frames.\n" );
+ printf( "\t-l log\t\tInput yait log file name [%s].\n", Y_LOG_FN );
+ printf( "\t-o ops\t\tOutput yait frame ops file name [%s].\n", Y_OPS_FN );
+ printf( "\t-t thresh\tInterlace detection threshold (>1) [%g].\n", Y_THRESH );
+ printf( "\t-b blend\tforced blend threshold (>thresh) [%g].\n", Y_FBLEND );
+ printf( "\t-w size\t\tDrop frame look ahead window (0-20) [%d].\n", Y_DROPWIN_SIZE );
+ printf( "\t-m mode\t\tTranscode blend method (0-5) [%d].\n", Y_DEINT_MODE );
+ printf( "\t-E thresh\tEven pattern threshold [%g].\n", Y_THRESH );
+ printf( "\t-O thresh\tOdd pattern threshold [%g].\n", Y_THRESH );
+ printf( "\t-N noise\tMinimum normalized delta, else noise [%g].\n", Y_NOISE );
+ printf( "\n" );
+
+ exit( 1 );
+ }
+
+
+/*
+ * yait_read_log:
+ */
+
+static void
+yait_read_log( void )
+ {
+ Fi **fa, *pf, *f;
+ int fn, ed, od;
+ int s, n;
+
+ s = 0;
+ pf = NULL;
+ for( Nf=0; ; Nf++ )
+ {
+ n = fscanf( LogFp, "%d: e: %d, o: %d\n", &fn, &ed, &od );
+ if( n != 3 )
+ break;
+
+ /* starting frame number */
+ if( !Nf )
+ s = fn;
+
+ if( (fn-s) != Nf )
+ {
+ printf( "Broken log file, line %d\n", Nf );
+ exit( 1 );
+ }
+
+ f = (Fi*) malloc( sizeof(Fi) );
+ if( !f )
+ {
+ perror( "malloc" );
+ exit( 1 );
+ }
+
+ memset( (void*) f, 0, sizeof(Fi) );
+ if( !Fl )
+ Fl = f;
+ if( pf )
+ pf->next = f;
+ pf = f;
+
+ f->r = yait_calc_ratio( ed, od );
+ f->ro = f->r;
+ f->fn = fn;
+ f->ed = ed;
+ f->od = od;
+ f->ip = -1;
+ }
+
+ if( !Fl )
+ {
+ fprintf( stderr, "Invalid log file.\n" );
+ exit( 1 );
+ }
+
+ /* number of 5 frame groups */
+ Nd = Nf / 5;
+
+ Fa = (Fi**) malloc( (Nf+1) * sizeof(Fi*) );
+ Ga = (Fi**) malloc( (Nf+1) * sizeof(Fi*) );
+ Da = (int*) malloc( Nd * sizeof(int) );
+ if( !Fa || !Ga || !Da )
+ {
+ perror( "malloc" );
+ exit( 1 );
+ }
+
+ fa = Fa;
+ for( f=Fl; f; f=f->next )
+ *fa++ = f;
+ *fa = NULL;
+ }
+
+
+/*
+ * yait_calc_ratio:
+ * Compute a ratio between even/odd row deltas. A high ratio indicates an
+ * interlace present. Use the sign of the ratio to indicate even row (<0), or odd
+ * row (>0) correlation.
+ *
+ * If the magnitude of the ratio is > 1.1, this is usually enough to
+ * indicate interlacing. A value around 1.0 indicates no row correlation at
+ * all.
+ *
+ * Assigning the ratios in this manner result in the following patterns
+ * present for interlaced material. Assume 0 for fabs(r)<thresh, else +/- 1:
+ *
+ * An odd interlace pattern (for a five frame group) would appear as:
+ *
+ * frame: 1 2 3 4 5
+ * even: a a b c d
+ * odd: a b c c d
+ *
+ * ratio: 0 -1 0 1 0
+ *
+ * If we detect this pattern, we assign the following frame operations:
+ *
+ * frame: 1 2 3 4 5
+ * even: a a b c d
+ * odd: a b c c d
+ *
+ * ratio: 0 -1 0 1 0
+ * op: osd oc
+ *
+ * osd = save odd rows and drop the frame
+ * oc = copy in saved odd rows
+ *
+ * This results with:
+ *
+ * frame: 1 |2| 3 4 5
+ * even: a |a| b c d
+ * odd: a |b|--> b c d
+ * drop
+ *
+ * For even interlace patterns, the signs are reversed, or simply:
+ *
+ * ratio: 0 1 0 -1 0
+ * esd ec
+ *
+ * The entire approach of this tool depends on these specific ratio patterns
+ * to be present, and should be for 2:3 pulldown. Lots of complications arise
+ * around still and abrupt scene changes. Again, it might be useful for the
+ * filter to produce a combing co-efficient as well as the delta information.
+ *
+ * Side note:
+ * For yuv, deltas based only on luminance yeilded much stronger
+ * interlace patterns, however, I suppose there are (rare) cases where
+ * chroma could be the only indicator, so chroma is included in the
+ * delta calculation, even though it results in weaker ratios.
+ */
+
+static double
+yait_calc_ratio( int ed, int od )
+ {
+ double r;
+
+ r = 1;
+
+ /* compute ratio, >1 odd, <-1 even */
+ if( !ed && !od )
+ /* duplicate frame */
+ r = 0;
+
+ if( ed && !od )
+ r = 100;
+
+ if( !ed && od )
+ r = -100;
+
+ if( ed && od )
+ {
+ r = (double) ed / (double) od;
+
+ if( r < 1 )
+ r = -1.0 / r;
+
+ if( r > 100 )
+ r = 100;
+ if( r < -100 )
+ r = -100;
+ }
+
+ return( r );
+ }
+
+
+/*
+ * yait_find_ip:
+ * - Mark isolated duplicate frames to be hard dropped.
+ * - Create the group array which is used to processes interleave
+ * patterns without duplicate frames present.
+ * - Find the maximum frame delta value. This is used to normalize
+ * frame deltas to filter out weak frames (noise which may cause
+ * erroneous interleave patterns to be detected).
+ * - Detect local interleave patterns.
+ */
+
+static void
+yait_find_ip( void )
+ {
+ Fi *f;
+ double w;
+ int m, p, i;
+
+ /* mark obvious drop frames */
+ if( !NoDrops )
+ for( i=1; i<Nf-1; i++ )
+ {
+ f = Fa[i];
+ if( f->r )
+ continue;
+
+ if( !Fa[i-1]->r && !Fa[i+1]->r )
+ continue;
+
+ f->drop = TRUE;
+ }
+
+ /* create group array, ommiting drops */
+ Ng = 0;
+ for( i=0; i<Nf; i++ )
+ {
+ f = Fa[i];
+ if( f->drop )
+ continue;
+
+ f->gi = Ng;
+ Ga[Ng++] = f;
+ }
+ Ga[Ng] = NULL;
+
+ /* find max row delta */
+ m = 0;
+ for( i=0; i<Nf; i++ )
+ {
+ f = Fa[i];
+ if( f->ed > m )
+ m = f->ed;
+ if( f->od > m )
+ m = f->od;
+ }
+
+ Md = m;
+ if( !Md )
+ {
+ fprintf( stderr, "All empty frames?\n" );
+ exit( 1 );
+ }
+
+ /* compute normalized row deltas and */
+ /* filter out weak r values (noise) */
+ for( i=0; i<Ng; i++ )
+ {
+ f = Ga[i];
+ f->nd = (f->ed + f->od) / (double) Md;
+ if( f->nd < Noise )
+ f->r = 1;
+ }
+
+ /* adjust for incomplete interleave patterns */
+ /* (indexing Fa[0,..,i+6]) */
+ for( i=0; i<Ng-6; i++ )
+ yait_chk_ip( i );
+
+ /* find interleave patterns */
+ for( i=0; i<Ng; i++ )
+ {
+ f = Ga[i];
+ if( f->op & Y_OP_COPY )
+ {
+ /* finish this group before looking for another pattern */
+ i++;
+ continue;
+ }
+
+ p = yait_find_odd( OThresh, i, &w, 4 );
+ if( p != -1 )
+ {
+ yait_mark_grp( p, i, w );
+ continue;
+ }
+
+ p = yait_find_even( EThresh, i, &w, 4 );
+ if( p != -1 )
+ yait_mark_grp( p+10, i, w );
+ }
+ }
+
+
+/*
+ * yait_chk_ip:
+ * Two cases to look for. An isolated pair of high r's, and an
+ * isolated tuplet of high r's. These can be caused by interlacing over
+ * still and abrupt scene changes.
+ */
+
+static void
+yait_chk_ip( int n )
+ {
+ if( !NoDrops )
+ yait_chk_pairs( n );
+
+ yait_chk_tuplets( n );
+ }
+
+
+/*
+ * yait_chk_pairs:
+ * Look for patterns of the type:
+ * i: 0 1 2 3 4 5
+ * odd: 0 0 -1 1 0 0
+ * even: 0 0 1 -1 0 0
+ *
+ * If detected, force the drop of the (single) interlaced frame.
+ * De-interlacing would just incur a redundant copy operation.
+ */
+
+static void
+yait_chk_pairs( int n )
+ {
+ Fi *fa[6];
+ double ra[6];
+ int i;
+
+ for( i=0; i<6; i++ )
+ {
+ fa[i] = Ga[n+i];
+ ra[i] = fabs( fa[i]->r );
+ }
+
+ for( i=2; i<4; i++ )
+ if( ra[i] < Thresh )
+ return;
+
+ /* adjacent frames to the tuplet must be <thresh */
+ if( ra[1]>Thresh || ra[4]>Thresh )
+ return;
+
+ /* we only need one edge frame to be <thresh */
+ if( ra[0]>Thresh && ra[5]>Thresh )
+ return;
+
+ if( fa[2]->r>0 && fa[3]->r>0 )
+ return;
+
+ if( fa[2]->r<0 && fa[3]->r<0 )
+ return;
+
+ /* two isolated high r values of opposite sign */
+ /* drop the interlaced frame, erase the pattern */
+ fa[2]->r = 1;
+ fa[3]->r = 1;
+
+ fa[2]->drop = TRUE;
+ }
+
+
+/*
+ * yait_chk_tuplets:
+ * Look for patterns of the type:
+ * i: 0 1 2 3 4 5 6
+ * odd: 0 0 -1 +/-2 1 0 0
+ * even: 0 0 1 +/-2 -1 0 0
+ *
+ * and complete to:
+ *
+ * odd: 0 0 -1 0 1 0 0
+ * even: 0 0 1 0 -1 0 0
+ */
+
+static void
+yait_chk_tuplets( int n )
+ {
+ Fi *fa[7];
+ double ra[7];
+ int i;
+
+ for( i=0; i<7; i++ )
+ {
+ fa[i] = Ga[n+i];
+ ra[i] = fabs( fa[i]->r );
+ }
+
+ for( i=2; i<5; i++ )
+ if( ra[i] < Thresh )
+ return;
+
+ /* adjacent frames to the tuplet must be <thresh */
+ if( ra[1]>Thresh || ra[5]>Thresh )
+ return;
+
+ /* we only need one edge frame to be <thresh */
+ if( ra[0]>Thresh && ra[6]>Thresh )
+ return;
+
+ if( fa[2]->r>0 && fa[4]->r>0 )
+ return;
+
+ if( fa[2]->r<0 && fa[4]->r<0 )
+ return;
+
+ /* isolated tuplet of high r values of opposite sign */
+ if( ra[3]>ra[2] || ra[3]>ra[4] )
+ fa[3]->r = 1;
+ }
+
+
+/*
+ * yait_find_odd:
+ */
+
+static int
+yait_find_odd( double thresh, int n, double *w, int win )
+ {
+ double re, ro;
+ int me, mo;
+ int p;
+
+ /* find max even/odd correlations */
+ /* (r<0 - even, r>0 - odd) */
+ me = yait_ffmin( n, win );
+ mo = yait_ffmax( n, win );
+
+ p = -1;
+ if( yait_m5(mo-2) == yait_m5(me) )
+ {
+ re = fabs( Ga[me]->r );
+ ro = fabs( Ga[mo]->r );
+ if( re>thresh && ro>thresh )
+ {
+ p = yait_m5( mo - 4 );
+ if( w )
+ *w = re + ro;
+ }
+ }
+
+ return( p );
+ }
+
+
+/*
+ * yait_find_even:
+ */
+
+static int
+yait_find_even( double thresh, int n, double *w, int win )
+ {
+ double re, ro;
+ int me, mo;
+ int p;
+
+ me = yait_ffmin( n, win );
+ mo = yait_ffmax( n, win );
+
+ p = -1;
+ if( yait_m5(me-2) == yait_m5(mo) )
+ {
+ re = fabs( Ga[me]->r );
+ ro = fabs( Ga[mo]->r );
+ if( re>thresh && ro>thresh )
+ {
+ p = yait_m5( me - 4 );
+ if( w )
+ *w = re + ro;
+ }
+ }
+
+ return( p );
+ }
+
+
+/*
+ * yait_ffmin:
+ */
+
+static int
+yait_ffmin( int n, int w )
+ {
+ Fi *f;
+ int m, i;
+ double r;
+
+ r = 0;
+ m = 0;
+ for( i=n; i<n+w; i++ )
+ {
+ if( i < 0 )
+ break;
+
+ f = Ga[i];
+ if( !f )
+ break;
+
+ if( f->r < r )
+ {
+ r = f->r;
+ m = i;
+ }
+ }
+
+ return( m );
+ }
+
+
+/*
+ * yait_ffmax:
+ */
+
+static int
+yait_ffmax( int n, int w )
+ {
+ Fi *f;
+ int m, i;
+ double r;
+
+ r = 0;
+ m = 0;
+ for( i=n; i<n+w; i++ )
+ {
+ if( i < 0 )
+ break;
+
+ f = Ga[i];
+ if( !f )
+ break;
+
+ if( f->r > r )
+ {
+ r = f->r;
+ m = i;
+ }
+ }
+
+ return( m );
+ }
+
+
+/*
+ * yait_m5:
+ */
+
+static int
+yait_m5( int n )
+ {
+ while( n < 0 )
+ n += 5;
+ return( n % 5 );
+ }
+
+
+/*
+ * yait_mark_grp:
+ * Try to catch the situation where a progressive frame is missing
+ * between interlace groups. This will cause an erroneous (opposite) ip
+ * pattern to be detected. The first sequence shown below is a normal (odd)
+ * telecine pattern. The second shows what happens when a progressive frame
+ * is missing. We want to reject the even pattern detected. Therefore, if
+ * we find an identical pattern at n+4 we keep it. If not, we reject if an
+ * opposite pattern follows at n+2 of greater weight.
+ *
+ * n: 0 1 2 3 4 0 1 2 3 4
+ * r: 0 -1 0 1 0 0 -1 0 1 0
+ * odd odd
+ *
+ * n: 0 1 2 3 4 1 2 3 4
+ * r: 0 -1 0 1 0 -1 0 1 0
+ * odd even odd
+ */
+
+static void
+yait_mark_grp( int p, int n, double w )
+ {
+ Fi *f;
+ double nw;
+ int np, t, i;
+
+ if( n%5 != (p+2)%5 )
+ return;
+
+ /* only overwrite an existing pattern if weight is greater */
+ f = Ga[n];
+ if( w <= f->w )
+ return;
+
+ /* check for same pattern at n+4 */
+ if( p < 10 )
+ np = yait_find_odd( OThresh, n+4, NULL, 5 );
+ else
+ np = yait_find_even( EThresh, n+4, NULL, 5 );
+ if( np < 0 )
+ {
+ /* no pattern at n+4, reject if opposite ip at n+2 */
+ if( p < 10 )
+ np = yait_find_even( EThresh, n+2, &nw, 5 );
+ else
+ np = yait_find_odd( OThresh, n+2, &nw, 5 );
+
+ if( np>=0 && nw>w )
+ return;
+ }
+
+ /* erase previous pattern */
+ if( n > 1 )
+ {
+ Ga[n-1]->op = 0;
+ Ga[n-2]->op = 0;
+ }
+
+ /* this frame and next are interlaced */
+ t = (p < 10) ? Y_OP_ODD : Y_OP_EVEN;
+ f->op = t | Y_OP_SAVE | Y_OP_DROP;
+ f = Ga[n+1];
+ f->op = t | Y_OP_COPY;
+
+ /* assume 1 progressive on either side of the tuplet */
+ for( i=n-1; i<n+4; i++ )
+ {
+ if( i<0 || i>=Ng )
+ continue;
+
+ f = Ga[i];
+ f->ip = p;
+ f->w = w;
+ }
+ }
+
+
+/*
+ * yait_find_drops:
+ * For every group of 5 frames, make sure we drop a frame. Allow up to a
+ * DropWin (default 15) group lookahead to make up for extra or missing drops. (The
+ * duplicated frames generated by --hard_fps can be quite early or late in the sequence).
+ * If a group requires a drop, but none exists, mark the group as requiring de-interlacing.
+ * Finally, consequetive marked groups inherit surrounding interleave patterns.
+ *
+ * Each group will receive one of the following flags:
+ *
+ * Y_HAS_DROP - group has a single drop frame
+ * Y_BANK_DROP - extra drop, can be used forward
+ * Y_WITHDRAW_DROP - missing drop, use banked drop from behind
+ * Y_RETURN_DROP - extra drop, can be used behind
+ * Y_BORROW_DROP - missing drop, use future extra drop
+ * Y_FORCE_DEINT - force de-interlacing, (produces a drop)
+ * Y_FORCE_DROP - missing drop, no extras and no interleave found
+ * Y_FORCE_KEEP - extra drop, no consumer so have to keep it
+ *
+ * For any flags other than FORCE, no action is required. Eeach group already has
+ * an available frame to drop, whether a marked duplicate, or a locally detected
+ * interleave pattern (which produces a drop).
+ *
+ * For Y_FORCE_DEINT, assemble consecutive groups of this type and try to inherit
+ * adjacent interleave patterns. If no pattern is available, mark them as
+ * Y_FORCE_DROP.
+ */
+
+static void
+yait_find_drops( void )
+ {
+ Fi *f;
+ int d, l;
+
+ /* populate drop counts */
+ for( d=0; d<Nd; d++ )
+ Da[d] = yait_cnt_drops( d*5 );
+
+ /* balance drop counts restricted by window size */
+ for( d=0; d<Nd; d++ )
+ {
+ f = Fa[d*5];
+
+ /* this is what we want to see */
+ if( Da[d] == 1 )
+ {
+ if( !f->gf )
+ f->gf = Y_HAS_DROP;
+ continue;
+ }
+
+ /* group is missing a drop? */
+ if( !Da[d] )
+ {
+ /* look ahead for an extra drop */
+ l = yait_extra_drop( d );
+ if( l )
+ {
+ /* found one */
+ Da[d]++;
+ f->gf = Y_BORROW_DROP;
+
+ --Da[l];
+ Fa[l*5]->gf = Y_RETURN_DROP;
+ continue;
+ }
+
+ /* no extra drops exist, mark for de-interlacing */
+ f->gf = Y_FORCE_DEINT;
+ continue;
+ }
+
+ /* we have too many drops */
+ while( Da[d] > 1 )
+ {
+ --Da[d];
+
+ /* look ahead for a missing drop */
+ l = yait_missing_drop( d );
+ if( l )
+ {
+ /* found one */
+ f->gf = Y_BANK_DROP;
+
+ Da[l]++;
+ Fa[l*5]->gf = Y_WITHDRAW_DROP;
+ continue;
+ }
+
+ /* we have to keep a drop */
+ if( !NoKeeps )
+ {
+ f->gf = Y_FORCE_KEEP;
+ yait_keep_frame( d*5 );
+
+ Stat_fk++;
+ }
+ }
+ }
+ }
+
+
+/*
+ * yait_cnt_drops:
+ */
+
+static int
+yait_cnt_drops( int n )
+ {
+ Fi *f;
+ int d, i;
+
+ d = 0;
+ for( i=n; i<n+5 && i<Nf; i++ )
+ {
+ f = Fa[i];
+ if( f->drop || f->op&Y_OP_DROP )
+ d++;
+ }
+
+ return( d );
+ }
+
+
+/*
+ * yait_extra_drop:
+ * Scan DropWin groups ahead for an extra drop.
+ */
+
+static int
+yait_extra_drop( int d )
+ {
+ int l, w;
+
+ for( w=0; w<DropWin; w++ )
+ {
+ l = d + w + 1;
+ if( l >= Nd )
+ return( 0 );
+
+ if( Da[l] > 1 )
+ return( l );
+ }
+
+ return( 0 );
+ }
+
+
+/*
+ * yait_missing_drop:
+ * Scan DropWin groups ahead for a missing drop.
+ */
+
+static int
+yait_missing_drop( int d )
+ {
+ int l, w;
+
+ for( w=0; w<DropWin; w++ )
+ {
+ l = d + w + 1;
+ if( l >= Nd )
+ return( 0 );
+
+ if( !Da[l] )
+ return( l );
+ }
+
+ return( 0 );
+ }
+
+
+/*
+ * yait_keep_frame:
+ * Multiple drops exist. Pick the best frame to keep. This can be difficult,
+ * as we do not want to keep a duplicate of an interlaced frame. First, try to find
+ * a hard dropped frame which does not follow an interlace. If one can be found, then
+ * simply negate the drop flag. If we are duplicating an interlace, alter the frame
+ * operations for the group to produce a non-interlaced duplicate.
+ */
+
+static void
+yait_keep_frame( int n )
+ {
+ Fi *f;
+ int da[6], bd, d, i;
+
+ d = yait_get_hdrop( n, da );
+
+ if( !d )
+ {
+ /* no hard drop frames were found, so ... */
+ /* two interlace drops exist, keep one, but blend it */
+ for( i=n; i<n+5 && i<Nf; i++ )
+ {
+ f = Fa[i];
+ if( f->op & Y_OP_DROP )
+ {
+ f->op &= ~Y_OP_DROP;
+ f->op |= Y_OP_DEINT;
+ return;
+ }
+ }
+
+ /* sanity check */
+ f = Fa[n];
+ fprintf( stderr, "No drop frame can be found, frame: %d\n", f->fn );
+ exit( 1 );
+ }
+
+ /* try to use a drop frame that isn't an interlace duplicate */
+ bd = -1;
+ for( i=0; i<5; i++ )
+ {
+ d = da[i];
+ if( !d )
+ /* can't access before Fa[0] */
+ continue;
+
+ if( d < 0 )
+ /* end of drop list */
+ break;
+
+ f = Fa[d-1];
+ if( f->drop )
+ /* two dups in a row */
+ f = Fa[d-2];
+
+ if( !f->op )
+ {
+ /* good */
+ f = Fa[d];
+ f->drop = FALSE;
+ return;
+ }
+
+ if( f->op & Y_OP_COPY )
+ bd = d;
+ }
+
+ /* keeping a duplicate of an interlace, try to use one which duplicates the */
+ /* second of an interlace pair, as that is cleaner to deal with */
+ /* bd (best drop) was set earlier in the loop if such a frame was found */
+ if( bd < 0 )
+ bd = da[0];
+
+ yait_ivtc_keep( bd );
+ }
+
+
+/*
+ * yait_get_hdrop:
+ * Populate an index array of the hard dropped frames, and return
+ * the count of how many were found.
+ */
+
+static int
+yait_get_hdrop( int n, int *da )
+ {
+ Fi *f;
+ int d, i;
+
+ d = 0;
+ for( i=n; i<n+5 && i<Nf; i++ )
+ {
+ f = Fa[i];
+ if( f->drop )
+ {
+ *da++ = i;
+ d++;
+ }
+ }
+ *da = -1;
+
+ return( d );
+ }
+
+
+/*
+ * yait_ivtc_keep
+ * Depending upon the position of the DROP in the pattern, alter the
+ * frame ops to generate a non-interlaced frame, and keep it.
+ *
+ * Case 1:
+ * If the duplicated frame is the second of the interlaced pair, then
+ * simply repeat the row copy operation and keep the frame.
+ *
+ * Original (odd pattern):
+ * sd c
+ * even: 2 2 3 3 4
+ * odd: 2 3 4 4 4
+ * drop DROP
+ *
+ * yeilds (bad keep frame):
+ * even: 2 3 3 4
+ * odd: 2 3 4 4
+ * KEEP
+ * Revised:
+ * sd c c
+ * even: 2 2 3 3 4
+ * odd: 2 3 4 4 4
+ * drop DROP
+ * yeilds:
+ * even: 2 3 3 4
+ * odd: 2 3 3 4
+ * KEEP
+ *
+ * Case 2:
+ * If the duplicated frame copies the first of the interlaced pair, more
+ * work must be done:
+ *
+ * Original (odd pattern):
+ * sd c
+ * even: 2 2 2 3 4
+ * odd: 2 3 3 4 4
+ * drop DROP
+ *
+ * yeilds (bad keep frame):
+ * even: 2 2 3 4
+ * odd: 2 3 3 4
+ * KEEP
+ * Revised:
+ * s c sd c
+ * even: 2 2 2 3 4
+ * odd: 2 3 3 4 4
+ * drop
+ * yeilds:
+ * even: 2 2 3 4
+ * odd: 2 2 3 4
+ * (keep)
+ */
+
+static void
+yait_ivtc_keep( int d )
+ {
+ Fi *fd, *fp;
+ int t;
+
+ fd = Fa[d];
+ fp = Fa[d-1];
+ if( fp->drop )
+ fp = Fa[d-2];
+
+ if( fp->op & Y_OP_COPY )
+ {
+ /* case 1 */
+ fd->op = fp->op;
+ fd->drop = FALSE;
+ return;
+ }
+
+ /* case 2 */
+ if( d < 2 )
+ {
+ /* can't access before Fa[0] */
+ /* (unlikely we would see this the first two frames of a film) */
+ fd->drop = FALSE;
+ return;
+ }
+
+ fd->op = fp->op;
+ fd->drop = FALSE;
+
+ t = fp->op & Y_OP_PAT;
+ fp->op = t | Y_OP_COPY;
+ fp = Fa[d-2];
+ fp->op = t | Y_OP_SAVE;
+ }
+
+
+/*
+ * yait_ivtc_grps:
+ * For each group missing an interleave pattern, scan backward and forward
+ * for an adjacent pattern. Consider hard dropped frames as barriers. If two
+ * different patterns exist, test the pattern against the original r values to find
+ * the best match. For consecutive (forced) interleave groups, use the previously
+ * found pattern values, until the forward scan value is used, which is then
+ * propagated to the rest of the sequence. (This avoids an O(n^2) search).
+ *
+ * If no pattern can be found, force a drop of a frame in the group.
+ *
+ * TODO:
+ * I should really be detecting scene changes as well, and consider them
+ * barriers.
+ */
+
+static void
+yait_ivtc_grps( void )
+ {
+ Fi *f;
+ int pb, pf, fg;
+ int p, n;
+
+ /* process by groups of 5 */
+ fg = TRUE;
+ pb = -1;
+ pf = -1;
+ for( n=0; n<Nf; n+=5 )
+ {
+ f = Fa[n];
+ if( f->gf != Y_FORCE_DEINT )
+ {
+ fg = TRUE;
+ continue;
+ }
+
+ if( fg )
+ {
+ /* this is the first group of a sequence, scan */
+ fg = FALSE;
+ pb = yait_scan_bk( n );
+ pf = yait_scan_fw( n );
+ }
+
+ if( pb<0 && pf<0 )
+ {
+ /* no pattern exists */
+ f->gf = Y_FORCE_DROP;
+ yait_drop_frame( n );
+ continue;
+ }
+
+ /* deinterlace the group with one of the given patterns */
+ /* if the pattern used is forward, keep it from now on */
+ p = yait_ivtc_grp( n, pb, pf );
+ if( p < 0 )
+ {
+ /* no pattern will match */
+ f->gf = Y_FORCE_DROP;
+ yait_drop_frame( n );
+ continue;
+ }
+
+ if( p == pf )
+ pb = -1;
+ }
+ }
+
+
+/*
+ * yait_scan_bk:
+ */
+
+static int
+yait_scan_bk( int n )
+ {
+ Fi *f;
+ int i;
+
+ for( i=n-1; i>=0; --i )
+ {
+ f = Fa[i];
+ if( !f )
+ return( -1 );
+
+ if( f->drop )
+ return( -1 );
+
+ if( f->ip != -1 )
+ return( f->ip );
+ }
+
+ return( -1 );
+ }
+
+
+/*
+ * yait_scan_fw:
+ */
+
+static int
+yait_scan_fw( int n )
+ {
+ Fi *f;
+ int i;
+
+ for( i=n+5; i<Nf; i++ )
+ {
+ f = Fa[i];
+
+ if( f->drop )
+ return( -1 );
+
+ if( f->ip != -1 )
+ return( f->ip );
+ }
+
+ return( -1 );
+ }
+
+
+/*
+ * yait_drop_frame:
+ * Choose a frame to drop. We want the frame with the highest fabs(r) value,
+ * as it is likely an interlaced frame. Do not use a frame which follows an assigned
+ * ip pattern, (it is the trailing element of a tuplet). If no r values exceed the
+ * threshold, choose the frame with the minimum delta.
+ *
+ * Frame: 0 1 2 3 4 | 5 6 7 8 9
+ * Ratio: 0 0 0 -1 0 | 1 0 0 0 0
+ * Op: sd c |
+ * group boundary
+ *
+ * In the above example, the first frame of the second group (5) may have the highest
+ * ratio value, but is the worst choice because it is part of the detected pattern and
+ * is a unique progressive frame.
+ */
+
+static void
+yait_drop_frame( int n )
+ {
+ Fi *f;
+ double mr, r;
+ int md, d;
+ int fr, fd;
+ int i;
+
+ mr = 0;
+ md = 0;
+ fr = n;
+ fd = n;
+
+ for( i=n; i<n+5 && i<Nf-1; i++ )
+ {
+ if( !i )
+ /* can't access before Fa[0] */
+ continue;
+
+ if( Fa[i-1]->drop || Fa[i+1]->drop )
+ /* avoid two consequetive drops */
+ continue;
+
+ if( Fa[i-1]->op & Y_OP_PAT )
+ /* trailing tuplet element */
+ continue;
+
+ f = Fa[i];
+
+ r = fabs( f->ro );
+ if( r > mr )
+ {
+ mr = r;
+ fr = i;
+ }
+
+ d = f->ed + f->od;
+ if( !md || d<md )
+ {
+ md = d;
+ fd = i;
+ }
+ }
+
+ Fa[ (mr>Thresh)?fr:fd ]->drop = TRUE;
+ Stat_fd++;
+ }
+
+
+/*
+ * yait_ivtc_grp:
+ * We need to de-interlace this group. Given are two potential patterns.
+ * If both are valid, test both and keep the one with the best r value matches.
+ * For the pattern used, mark the group, set the frame ops accordingly, and return
+ * it as the function value.
+ */
+
+static int
+yait_ivtc_grp( int n, int p1, int p2 )
+ {
+ Fi *f;
+ double thresh, m1, m2;
+ int p, t, i;
+
+ m1 = (p1 < 0) ? -1 : yait_tst_ip(n,p1);
+ m2 = (p2 < 0) ? -1 : yait_tst_ip(n,p2);
+
+ /* yait_tst_ip() returns the sum of two ratios */
+ /* we want both ratios > Y_MTHRESH */
+ thresh = Y_MTHRESH * 2;
+ if( !NoDrops && m1<thresh && m2<thresh )
+ /* neither pattern matches, force a drop instead */
+ return( -1 );
+
+ p = (m1 > m2) ? p1 : p2;
+
+ /* sanity check */
+ if( p < 0 )
+ {
+ f = Fa[n];
+ fprintf( stderr, "Impossible interlace pattern computed (%d), frame: %d\n",
+ p, f->fn );
+ exit( 1 );
+ }
+
+ /* we have a pattern, mark group */
+ for( i=n; i<n+5 && i<Nf; i++ )
+ {
+ f = Fa[i];
+ if( f->drop )
+ {
+ fprintf( stderr,
+ "De-interlace, horribly confused now, frame: %d.\n", f->fn );
+ exit( 1 );
+ }
+ f->ip = p;
+ }
+
+ f = Fa[n];
+ n = f->gi;
+
+ /* sanity check */
+ if( Ga[n] != f )
+ {
+ fprintf( stderr, "Lost our frame in the group array, frame: %d\n", f->fn );
+ exit( 1 );
+ }
+
+ t = (p < 10) ? Y_OP_ODD : Y_OP_EVEN;
+ for( i=n; i<n+5 && i<Ng-1; i++ )
+ {
+ if( i%5 == (p+2)%5 )
+ {
+ f = Ga[i];
+ f->op = t | Y_OP_SAVE | Y_OP_DROP;
+
+ /* don't overwrite an existing frame drop */
+ f = Ga[i+1];
+ if( !(f->op&Y_OP_DROP) )
+ f->op = t | Y_OP_COPY;
+
+ break;
+ }
+ }
+
+ return( p );
+ }
+
+
+/*
+ * yait_tst_ip:
+ */
+
+static double
+yait_tst_ip( int n, int p )
+ {
+ double rs, r;
+ int s, i;
+
+ s = (p < 10) ? 1 : -1;
+ rs = 0;
+
+ n = Fa[n]->gi;
+ for( i=n; i<n+5 && i<Ng-2; i++ )
+ {
+ if( i%5 != (p+2)%5 )
+ continue;
+
+ /* strong pattern would have r[i]<-thresh and r[i+2]>thresh */
+ r = s * Ga[i]->ro;
+ if( r < 0 )
+ rs += fabs( r );
+
+ r = s * Ga[i+2]->ro;
+ if( r > 0 )
+ rs += r;
+
+ break;
+ }
+
+ return( rs );
+ }
+
+
+/*
+ * yait_deint:
+ * For non 3/2 telecine patterns, we may have let interlaced frames
+ * through. Tell transcode to de-interlace (blend) these. This is the case for
+ * any frame having a high ratio with no interlace pattern detected.
+ *
+ * TODO:
+ * This was an afterthought. Perhaps we can avoid a 32detect pass on
+ * the video by performing this, although it is difficult to detect out of
+ * pattern interlace frames solely on row delta information. Perhaps we should
+ * have built 32detect into the log generation, and added an extra flag field if
+ * we thought the frame was interlaced. This also would help when trying to
+ * assign ambiguous ip patterns.
+ */
+
+static void
+yait_deint( void )
+ {
+ Fi *fp, *fn, *f;
+ int i;
+
+ for( i=1; i<Ng-1; i++ )
+ {
+ fp = Ga[i-1];
+ f = Ga[i];
+ fn = Ga[i+1];
+
+ if( f->op&Y_OP_PAT || f->drop )
+ /* already being de-interlaced or dropped */
+ continue;
+
+ if( fp->op & Y_OP_PAT )
+ /* trailing element of a tuplet */
+ continue;
+
+ if( fabs(f->r) < Blend )
+ /* it isn't interlaced (we think) */
+ continue;
+
+ if( f->nd < Y_FWEIGHT )
+ /* delta is too weak, interlace is likely not visible */
+ continue;
+
+ if( fp->nd>Y_SCENE_CHANGE || f->nd>Y_SCENE_CHANGE )
+ /* can't make a decision over scene changes */
+ continue;
+
+ /* this frame is interlaced with no operation assigned */
+ f->op = Y_OP_DEINT;
+
+ /* if the next frame ratio < thresh, it is similar, unless */
+ /* a scene change, therefore interlaced as well */
+ if( fabs(fn->r)<Thresh && fn->nd<Y_SCENE_CHANGE )
+ if( !(fn->op&Y_OP_PAT) && !fn->drop )
+ fn->op = Y_OP_DEINT;
+
+ /* if the next frame(s) are duplicates of this, mark them */
+ /* for blending as well, as the may eventually be force kept */
+ while( f->next && !f->next->gi )
+ {
+ f = f->next;
+ f->op = Y_OP_DEINT;
+ }
+ }
+ }
+
+
+/*
+ * yait_write_ops:
+ */
+
+static void
+yait_write_ops( void )
+ {
+ Fi *f;
+
+ for( f=Fl; f; f=f->next )
+ fprintf( OpsFp, "%d: %s\n", f->fn, yait_write_op(f) );
+ }
+
+
+/*
+ * yait_write_op:
+ */
+
+static char*
+yait_write_op( Fi *f )
+ {
+ static char buf[10];
+ char *p;
+ int op;
+
+ p = buf;
+ if( f->drop )
+ {
+ *p++ = 'd';
+ *p = 0;
+ Stat_nd++;
+ return( buf );
+ }
+
+ op = f->op;
+ if( op & Y_OP_ODD )
+ *p++ = 'o';
+ if( op & Y_OP_EVEN )
+ *p++ = 'e';
+ if( op & Y_OP_SAVE )
+ *p++ = 's';
+ if( op & Y_OP_COPY )
+ *p++ = 'c';
+ if( op & Y_OP_DROP )
+ {
+ *p++ = 'd';
+ Stat_nd++;
+ if( op & Y_OP_ODD )
+ Stat_no++;
+ else
+ Stat_ne++;
+ }
+ if( op & Y_OP_DEINT )
+ {
+ *p++ = '0' + DeintMode;
+ Stat_nb++;
+ }
+ *p = 0;
+
+ return( buf );
+ }
+
+
+/*
+ * yait_fini:
+ * Free up allocations.
+ */
+
+static void
+yait_fini( void )
+ {
+ int i;
+
+ for( i=0; i<Nf; i++ )
+ free( Fa[i] );
+
+ free( Fa );
+ free( Ga );
+ free( Da );
+ }
+
+
+/*
+ * Output debug information to stdout
+ */
+
+static void
+yait_debug_fi( void )
+ {
+ Fi *f;
+ int i;
+
+ printf( "Options:\n" );
+ printf( "\tLog file: %s\n", LogFn );
+ printf( "\tOps file: %s\n", OpsFn );
+ printf( "\tEven Threshold: %g\n", EThresh );
+ printf( "\tOdd Threshold: %g\n", OThresh );
+ printf( "\tBlend threshold: %g\n", Blend );
+ printf( "\tDrop window size: %d\n", DropWin );
+ printf( "\tDe-interlace mode: %d\n\n", DeintMode );
+
+ printf( "Stats:\n" );
+ printf( "\tTotal number of frames: %d\n", Nf );
+ printf( "\tNumber of frames divided by 5: %d\n", Nf/5 );
+ printf( "\tTotal dropped frames: %d\n", Stat_nd );
+ printf( "\tTotal blended frames: %d\n", Stat_nb );
+ printf( "\tTotal odd interlaced pairs: %d\n", Stat_no );
+ printf( "\tTotal even interlaced pairs: %d\n", Stat_ne );
+ printf( "\tNumber of forced frame drops: %d\n", Stat_fd );
+ printf( "\tNumber of forced frame keeps: %d\n\n", Stat_fk );
+ printf( "\tMax row delta: %d\n\n", Md );
+
+ i = 0;
+ for( f=Fl; f; f=f->next, i++ )
+ {
+ if( i && !(i%5) )
+ printf( "\n" );
+
+ printf( "Frame %6d: e: %8d, o: %8d, r: %7.3f, ro: %7.3f, w: %8.4f, "
+ "ip: %2d, gi: %6d, op: %-4s d: %s %s\n",
+ f->fn, f->ed, f->od, f->r, f->ro, f->w, f->ip, f->gi,
+ yait_op(f->op), yait_drop(f), yait_grp(f->gf) );
+ }
+ }
+
+static char*
+yait_op( int op )
+ {
+ static char buf[10];
+ char *p;
+
+ p = buf;
+ *p = 0;
+ if( !op )
+ return( buf );
+
+ if( op & Y_OP_ODD )
+ *p++ = 'o';
+ if( op & Y_OP_EVEN )
+ *p++ = 'e';
+ if( op & Y_OP_SAVE )
+ *p++ = 's';
+ if( op & Y_OP_COPY )
+ *p++ = 'c';
+ if( op & Y_OP_DROP )
+ *p++ = 'd';
+ if( op & Y_OP_DEINT )
+ *p++ = '0' + DeintMode;
+ *p = 0;
+
+ return( buf );
+ }
+
+static char*
+yait_drop( Fi *f )
+ {
+ if( f->drop )
+ return( "DROP" );
+
+ if( f->op&Y_OP_ODD && f->op&Y_OP_DROP )
+ return( "odd " );
+
+ if( f->op&Y_OP_EVEN && f->op&Y_OP_DROP )
+ return( "even" );
+
+ return( " " );
+ }
+
+static char*
+yait_grp( int flg )
+ {
+ switch( flg )
+ {
+ case Y_HAS_DROP:
+ return( "has drop" );
+ case Y_BANK_DROP:
+ return( "bank" );
+ case Y_WITHDRAW_DROP:
+ return( "withdraw" );
+ case Y_BORROW_DROP:
+ return( "borrow" );
+ case Y_RETURN_DROP:
+ return( "return" );
+ case Y_FORCE_DEINT:
+ return( "force deint" );
+ case Y_FORCE_DROP:
+ return( "force drop" );
+ case Y_FORCE_KEEP:
+ return( "force keep" );
+ }
+ return( "" );
+ }