/** * Copyright (C) 2001, 2002, 2003 Billy Biggs . * * This program 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. * * This program 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 this program; if not, write to the Free Software Foundation, * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #if HAVE_INTTYPES_H #include #else #include #endif #include "pulldown.h" /** * scratch paper: * * A A A B B C C C D D * [T B T][B T][B T B][T B] * [1 1][2 2][3 3][4 4][5 5] * [C C] [M M][C C][C C] * D A A A B B C C C D * * Top 1 : Drop * Bot 1 : Show * Top 2 : Drop * Bot 2 : Drop * Top 3 : Merge * Bot 3 : Drop * Top 4 : Show * Bot 4 : Drop * Top 5 : Drop * Bot 5 : Show */ /* 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_SEQ_DD ) predicted_offset = PULLDOWN_SEQ_AA; /** * 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; #if 0 /* FIXME: unused */ 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; } #endif 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)); } /* printf( "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<d > 360) { if (3*old_relative->e < old_relative->o) laced=1; if ((2*old_relative->d < old_relative->s) && (old_relative->s > 600)) laced=1; } if (new_peak->d > 360) { if ((2*new_relative->t < new_relative->p) && (new_relative->p > 600)) laced=1; } if( !laced ) return PULLDOWN_ACTION_NEXT_PREV; if (new_relative->t < 2*new_relative->p) { if ((3*old_relative->e < old_relative->o) || (2*new_relative->t < new_relative->p)) { return PULLDOWN_ACTION_PREV_NEXT; } } return PULLDOWN_ACTION_PREV_NEXT; } #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, uint8_t *old, uint8_t *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; } int pulldown_source( int action, int bottom_field ) { if( action == PULLDOWN_SEQ_AB || action == PULLDOWN_SEQ_BC ) { return bottom_field; } else { return !bottom_field; } } int pulldown_drop( int action, int bottom_field ) { int ret = 1; if( action == PULLDOWN_SEQ_AA && bottom_field ) ret = 0; if( action == PULLDOWN_SEQ_BC && !bottom_field ) ret = 0; if( action == PULLDOWN_SEQ_CC && !bottom_field ) ret = 0; if( action == PULLDOWN_SEQ_DD && bottom_field ) ret = 0; return ret; }