#include #include #include #include "pulldown.h" /** * Possible pulldown offsets. */ #define PULLDOWN_OFFSET_1 (1<<0) #define PULLDOWN_OFFSET_2 (1<<1) #define PULLDOWN_OFFSET_3 (1<<2) #define PULLDOWN_OFFSET_4 (1<<3) #define PULLDOWN_OFFSET_5 (1<<4) /* Offset 1 2 3 4 5 */ /* Field Pattern [T B T][B T][B T B] [T B] */ /* Action Copy Save Merge Copy Copy */ /* Bot Top */ int tff_top_pattern[] = { 0, 1, 0, 0, 0 }; int tff_bot_pattern[] = { 0, 0, 0, 1, 0 }; /* Offset 1 2 3 4 5 */ /* Field Pattern [B T B][T B][T B T] [B T] */ /* Action Copy Save Merge Copy Copy */ /* Top Bot */ int bff_top_pattern[] = { 0, 0, 0, 1, 0 }; int bff_bot_pattern[] = { 0, 1, 0, 0, 0 }; /* Timestamp mangling */ /* From the DVD : 0 + 3003+ 6006 + 9009+ 12012 = 15015 */ /* In 24fps time: 0 + + 3754 + 7508+ 11262 = 15016 */ /** * Flag Pattern Treat as * on DVD last offset * ============================ * T B T bff 3 * B T bff 4 * B T B tff 3 * T B tff 4 */ int determine_pulldown_offset( int top_repeat, int bot_repeat, int tff, int last_offset ) { int predicted_offset; int pd_patterns = 0; int offset = -1; int exact = -1; int i; predicted_offset = last_offset << 1; if( predicted_offset > PULLDOWN_OFFSET_5 ) predicted_offset = PULLDOWN_OFFSET_1; /** * Detect our pattern. */ for( i = 0; i < 5; i++ ) { /** * Truth table: * * ref repeat, frame repeat valid * ===========+==============+======= * 0 0 -> 1 * 0 1 -> 1 * 1 0 -> 0 * 1 1 -> 1 */ if( tff ) { if( ( !tff_top_pattern[ i ] || top_repeat ) && ( !tff_bot_pattern[ i ] || bot_repeat ) ) { pd_patterns |= ( 1 << i ); offset = i; } } else { if( ( !bff_top_pattern[ i ] || top_repeat ) && ( !bff_bot_pattern[ i ] || bot_repeat ) ) { pd_patterns |= ( 1 << i ); offset = i; } if( bff_top_pattern[ i ] == top_repeat && bff_bot_pattern[ i ] == bot_repeat ) { exact = i; } } } offset = 1 << offset; /** * Check if the 3:2 pulldown pattern we previously decided on is * valid for this set. If so, we use that. */ if( pd_patterns & predicted_offset ) offset = predicted_offset; if( ( top_repeat || bot_repeat ) && exact > 0 ) offset = ( 1 << exact ); return offset; } #define HISTORY_SIZE 5 static int tophistory[ 5 ]; static int bothistory[ 5 ]; static int tophistory_diff[ 5 ]; static int bothistory_diff[ 5 ]; static int histpos = 0; static void fill_history( int tff ) { if( tff ) { tophistory[ 0 ] = INT_MAX; bothistory[ 0 ] = INT_MAX; tophistory[ 1 ] = 0; bothistory[ 1 ] = INT_MAX; tophistory[ 2 ] = INT_MAX; bothistory[ 2 ] = INT_MAX; tophistory[ 3 ] = INT_MAX; bothistory[ 3 ] = 0; tophistory[ 4 ] = INT_MAX; bothistory[ 3 ] = INT_MAX; tophistory_diff[ 0 ] = 0; bothistory_diff[ 0 ] = 0; tophistory_diff[ 1 ] = 1; bothistory_diff[ 1 ] = 0; tophistory_diff[ 2 ] = 0; bothistory_diff[ 2 ] = 0; tophistory_diff[ 3 ] = 0; bothistory_diff[ 3 ] = 1; tophistory_diff[ 4 ] = 0; bothistory_diff[ 3 ] = 0; } else { tophistory[ 0 ] = INT_MAX; bothistory[ 0 ] = INT_MAX; tophistory[ 1 ] = INT_MAX; bothistory[ 1 ] = 0; tophistory[ 2 ] = INT_MAX; bothistory[ 2 ] = INT_MAX; tophistory[ 3 ] = 0; bothistory[ 3 ] = INT_MAX; tophistory[ 4 ] = INT_MAX; bothistory[ 3 ] = INT_MAX; tophistory_diff[ 0 ] = 0; bothistory_diff[ 0 ] = 0; tophistory_diff[ 1 ] = 0; bothistory_diff[ 1 ] = 1; tophistory_diff[ 2 ] = 0; bothistory_diff[ 2 ] = 0; tophistory_diff[ 3 ] = 1; bothistory_diff[ 3 ] = 0; tophistory_diff[ 4 ] = 0; bothistory_diff[ 3 ] = 0; } histpos = 0; } int determine_pulldown_offset_history( int top_repeat, int bot_repeat, int tff, int *realbest ) { int avgbot = 0; int avgtop = 0; int best = 0; int min = -1; int minpos = 0; int minbot = 0; int j; int ret; int mintopval = -1; int mintoppos = -1; int minbotval = -1; int minbotpos = -1; tophistory[ histpos ] = top_repeat; bothistory[ histpos ] = bot_repeat; for( j = 0; j < HISTORY_SIZE; j++ ) { avgtop += tophistory[ j ]; avgbot += bothistory[ j ]; } avgtop /= 5; avgbot /= 5; for( j = 0; j < HISTORY_SIZE; j++ ) { // int cur = (tophistory[ j ] - avgtop); int cur = tophistory[ j ]; if( cur < min || min < 0 ) { min = cur; minpos = j; } if( cur < mintopval || mintopval < 0 ) { mintopval = cur; mintoppos = j; } } for( j = 0; j < HISTORY_SIZE; j++ ) { // int cur = (bothistory[ j ] - avgbot); int cur = bothistory[ j ]; if( cur < min || min < 0 ) { min = cur; minpos = j; minbot = 1; } if( cur < minbotval || minbotval < 0 ) { minbotval = cur; minbotpos = j; } } if( minbot ) { best = tff ? ( minpos + 2 ) : ( minpos + 4 ); } else { best = tff ? ( minpos + 4 ) : ( minpos + 2 ); } best = best % HISTORY_SIZE; *realbest = 1 << ( ( histpos + (2*HISTORY_SIZE) - best ) % HISTORY_SIZE ); best = (minbotpos + 2) % 5; ret = 1 << ( ( histpos + (2*HISTORY_SIZE) - best ) % HISTORY_SIZE ); best = (mintoppos + 4) % 5; ret |= 1 << ( ( histpos + (2*HISTORY_SIZE) - best ) % HISTORY_SIZE ); histpos = (histpos + 1) % HISTORY_SIZE; return ret; } static int reference = 0; int determine_pulldown_offset_history_new( int top_repeat, int bot_repeat, int tff, int predicted ) { int avgbot = 0; int avgtop = 0; int i, j; int ret; int mintopval = -1; int mintoppos = -1; int min2topval = -1; int min2toppos = -1; int minbotval = -1; int minbotpos = -1; int min2botval = -1; int min2botpos = -1; int predicted_pos = 0; tophistory[ histpos ] = top_repeat; bothistory[ histpos ] = bot_repeat; for( j = 0; j < HISTORY_SIZE; j++ ) { avgtop += tophistory[ j ]; avgbot += bothistory[ j ]; } avgtop /= 5; avgbot /= 5; for( i = 0; i < 5; i++ ) { if( (1< avgtop || !tophistory_diff[ (i + j) % 5 ]) ) { valid = 0; break; } // if( tff_bot_pattern[ j ] && !bothistory_diff[ (i + j) % 5 ] && bothistory[ (i + j) % 5 ] != minbotval ) { if( tff_bot_pattern[ j ] && (bothistory[ (i + j) % 5 ] > avgbot || !bothistory_diff[ (i + j) % 5 ]) ) { valid = 0; break; } } if( valid ) ret |= (1<<(((5-i)+histpos)%5)); } /* fprintf( stderr, "ret: %d %d %d %d %d\n", PULLDOWN_OFFSET_1 & ret, PULLDOWN_OFFSET_2 & ret, PULLDOWN_OFFSET_3 & ret, PULLDOWN_OFFSET_4 & ret, PULLDOWN_OFFSET_5 & ret ); */ histpos = (histpos + 1) % HISTORY_SIZE; reference = (reference + 1) % 5; if( !ret ) { /* No pulldown sequence is valid, return an error. */ return 0; } else if( !(predicted & ret) ) { /** * We have a valid sequence, but it doesn't match our prediction. * Return the first 'valid' sequence in the list. */ for( i = 0; i < 5; i++ ) { if( ret & (1< avgtop || !tophistory_diff[ (i + j) % 5 ]) ) { if( tff_top_pattern[ (i + 5 - j) % 5 ] && tophistory[ (histpos + 5 - j) % 5 ] > avgtop ) { // if( tff_top_pattern[ (i + 5 - j) % 5 ] && !tophistory_diff[ (histpos + 5 - j) % 5 ] && tophistory[ (histpos + 5 - j) % 5 ] != mintopval ) { valid = 0; break; } // if( tff_bot_pattern[ j ] && !bothistory_diff[ (i + j) % 5 ] && bothistory[ (i + j) % 5 ] != minbotval ) { // if( tff_bot_pattern[ j ] && (bothistory[ (i + j) % 5 ] > avgbot || !bothistory_diff[ (i + j) % 5 ]) ) { if( tff_bot_pattern[ (i + 5 - j) % 5 ] && bothistory[ (histpos + 5 - j) % 5 ] > avgbot ) { // if( tff_bot_pattern[ (i + 5 - j) % 5 ] && !bothistory_diff[ (histpos + 5 - j) % 5 ] && bothistory[ (histpos + 5 - j) % 5 ] != minbotval ) { valid = 0; break; } } if( valid ) ret |= (1< 360) { if (3*f[0].r.e < f[0].r.o) laced=1; if ((2*f[0].r.d < f[0].r.s) && (f[0].r.s > 600)) laced=1; } if (f[1].p.d > 360) { //if (4*f[1].r.o < f[1].r.e) laced=1; if ((2*f[1].r.t < f[1].r.p) && (f[1].r.p > 600)) laced=1; } if (!laced) return F_SHOW; //if (4*f[1].r.s < f[1].r.d) return F_DROP; if ((f[1].r.t < 2*f[1].r.p) || (f[1].r.t < 2*p->nl)) { if ((3*f[0].r.e < f[0].r.o) || (2*f[1].r.t < f[1].r.p)) { p->nl = f[1].r.t; p->sync = 1; return F_MERGE; } } return F_DROP; */ return 0; } #define ABS(a) (((a) < 0)?-(a):(a)) void diff_packed422_block8x8( pulldown_metrics_t *m, unsigned char *old, unsigned char *new, int os, int ns ) { int x, y, e=0, o=0, s=0, p=0, t=0; unsigned char *oldp, *newp; unsigned char old0, old1, new0, new1; m->s = m->p = m->t = 0; for (x = 8; x; x--) { oldp = old; old += 2; newp = new; new += 2; s = p = t = 0; for (y = 4; y; y--) { old0 = *(oldp); old1 = *(oldp+os); new0 = *(newp); new1 = *(newp+ns); e += ABS(new0-old0); o += ABS(new1-old1); s += new1-new0; p += old1-old0; t += old1-new0; oldp += os<<1; newp += ns<<1; } m->s += ABS(s); m->p += ABS(p); m->t += ABS(t); } m->e = e; m->o = o; m->d = e+o; } #define MAXUP(a,b) ((a) = ((a)>(b)) ? (a) : (b)) void diff_factor_packed422_frame( pulldown_metrics_t *peak, pulldown_metrics_t *rel, pulldown_metrics_t *mean, unsigned char *old, unsigned char *new, int w, int h, int os, int ns ) { int x, y; pulldown_metrics_t l; memset(peak, 0, sizeof(pulldown_metrics_t)); memset(rel, 0, sizeof(pulldown_metrics_t)); memset(mean, 0, sizeof(pulldown_metrics_t)); for (y = 0; y < h-7; y += 8) { for (x = 8; x < w-8-7; x += 8) { diff_packed422_block8x8(&l, old+x+y*os, new+x+y*ns, os, ns); mean->d += l.d; mean->e += l.e; mean->o += l.o; mean->s += l.s; mean->p += l.p; mean->t += l.t; MAXUP(peak->d, l.d); MAXUP(peak->e, l.e); MAXUP(peak->o, l.o); MAXUP(peak->s, l.s); MAXUP(peak->p, l.p); MAXUP(peak->t, l.t); MAXUP(rel->e, l.e-l.o); MAXUP(rel->o, l.o-l.e); MAXUP(rel->s, l.s-l.t); MAXUP(rel->p, l.p-l.t); MAXUP(rel->t, l.t-l.p); MAXUP(rel->d, l.t-l.s); /* hack */ } } x = (w/8-2)*(h/8); mean->d /= x; mean->e /= x; mean->o /= x; mean->s /= x; mean->p /= x; mean->t /= x; }