summaryrefslogtreecommitdiff
path: root/src/post/deinterlace/pulldown.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/post/deinterlace/pulldown.c')
-rw-r--r--src/post/deinterlace/pulldown.c561
1 files changed, 561 insertions, 0 deletions
diff --git a/src/post/deinterlace/pulldown.c b/src/post/deinterlace/pulldown.c
new file mode 100644
index 000000000..b99c4fdd4
--- /dev/null
+++ b/src/post/deinterlace/pulldown.c
@@ -0,0 +1,561 @@
+/**
+ * Copyright (C) 2001, 2002, 2003 Billy Biggs <vektor@dumbterm.net>.
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <limits.h>
+#include <string.h>
+#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;
+
+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<<i) == predicted ) { predicted_pos = i; break; } }
+
+ /*
+ fprintf( stderr, "top: %8d bot: %8d\ttop-avg: %8d bot-avg: %8d (%d)\n", top_repeat, bot_repeat, top_repeat - avgtop, bot_repeat - avgbot, (5 + predicted_pos - reference) % 5 );
+ */
+
+ for( j = 0; j < HISTORY_SIZE; j++ ) {
+ int cur = tophistory[ j ];
+ if( cur < mintopval || mintopval < 0 ) {
+ min2topval = mintopval;
+ min2toppos = mintoppos;
+ mintopval = cur;
+ mintoppos = j;
+ } else if( cur < min2topval || min2topval < 0 ) {
+ min2topval = cur;
+ min2toppos = j;
+ }
+ }
+
+ for( j = 0; j < HISTORY_SIZE; j++ ) {
+ int cur = bothistory[ j ];
+ if( cur < minbotval || minbotval < 0 ) {
+ min2botval = minbotval;
+ min2botpos = minbotpos;
+ minbotval = cur;
+ minbotpos = j;
+ } else if( cur < min2botval || min2botval < 0 ) {
+ min2botval = cur;
+ min2botpos = j;
+ }
+ }
+
+ tophistory_diff[ histpos ] = ((mintoppos == histpos) || (min2toppos == histpos));
+ bothistory_diff[ histpos ] = ((minbotpos == histpos) || (min2botpos == histpos));
+
+ ret = 0;
+ for( i = 0; i < 5; i++ ) {
+ int valid = 1;
+ for( j = 0; j < 5; j++ ) {
+ // if( tff_top_pattern[ j ] && !tophistory_diff[ (i + j) % 5 ] && tophistory[ (i + j) % 5 ] != mintopval ) {
+ if( tff_top_pattern[ j ] && (tophistory[ (i + j) % 5 ] > 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<<i) ) return (1<<i); }
+ }
+
+ /**
+ * The predicted phase is still valid.
+ */
+ return predicted;
+}
+
+int determine_pulldown_offset_short_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 < 3; j++ ) {
+ avgtop += tophistory[ (histpos + 5 - j) % 5 ];
+ avgbot += bothistory[ (histpos + 5 - j) % 5 ];
+ }
+ avgtop /= 3;
+ avgbot /= 3;
+
+ for( i = 0; i < 5; i++ ) { if( (1<<i) == predicted ) { predicted_pos = i; break; } }
+
+ /*
+ fprintf( stderr, "top: %8d bot: %8d\ttop-avg: %8d bot-avg: %8d (%d)\n",
+ top_repeat, bot_repeat, top_repeat - avgtop, bot_repeat - avgbot,
+ (5 + predicted_pos - reference) % 5 );
+ */
+
+ for( j = 0; j < 3; j++ ) {
+ int cur = tophistory[ (histpos + 5 - j) % 5 ];
+ if( cur < mintopval || mintopval < 0 ) {
+ min2topval = mintopval;
+ min2toppos = mintoppos;
+ mintopval = cur;
+ mintoppos = j;
+ } else if( cur < min2topval || min2topval < 0 ) {
+ min2topval = cur;
+ min2toppos = j;
+ }
+ }
+
+ for( j = 0; j < 3; j++ ) {
+ int cur = bothistory[ (histpos + 5 - j) % 5 ];
+ if( cur < minbotval || minbotval < 0 ) {
+ min2botval = minbotval;
+ min2botpos = minbotpos;
+ minbotval = cur;
+ minbotpos = j;
+ } else if( cur < min2botval || min2botval < 0 ) {
+ min2botval = cur;
+ min2botpos = j;
+ }
+ }
+
+ tophistory_diff[ histpos ] = ((mintoppos == histpos) || (min2toppos == histpos));
+ bothistory_diff[ histpos ] = ((minbotpos == histpos) || (min2botpos == histpos));
+
+ ret = 0;
+ for( i = 0; i < 5; i++ ) {
+ int valid = 1;
+ for( j = 0; j < 3; j++ ) {
+ // if( tff_top_pattern[ j ] && !tophistory_diff[ (i + j) % 5 ] && tophistory[ (i + j) % 5 ] != mintopval ) {
+ // if( tff_top_pattern[ j ] && (tophistory[ (i + j) % 5 ] > 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<<i);
+ }
+
+ /*
+ 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<<i) ) return (1<<i); }
+ }
+
+ /**
+ * The predicted phase is still valid.
+ */
+ return predicted;
+}
+
+int determine_pulldown_offset_dalias( pulldown_metrics_t *old_peak,
+ pulldown_metrics_t *old_relative,
+ pulldown_metrics_t *old_mean,
+ pulldown_metrics_t *new_peak,
+ pulldown_metrics_t *new_relative,
+ pulldown_metrics_t *new_mean )
+{
+ int laced = 0;
+
+ if (old_peak->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;
+}