vdr/xine-lib-vdr/src/post/deinterlace pulldown.c speedy.c tvtime.c

Darren Salt pkg-vdr-dvb-changes@lists.alioth.debian.org
Mon, 04 Apr 2005 22:20:01 +0000


Update of /cvsroot/pkg-vdr-dvb/vdr/xine-lib-vdr/src/post/deinterlace
In directory haydn:/tmp/cvs-serv1691/src/post/deinterlace

Added Files:
	pulldown.c speedy.c tvtime.c 
Log Message:
Import of VDR-patched xine-lib.

--- NEW FILE: pulldown.c ---
/**
 * 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.
 */

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <stdio.h>
#include <limits.h>
#include <string.h>

#if HAVE_INTTYPES_H
#include <inttypes.h>
#else
#include <stdint.h>
#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<<i) == predicted ) { predicted_pos = i; break; } }

    /*
    printf(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));
    }

    /*
    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<<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; } }

    /*
    printf( "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);
    }

    /*
    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<<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;
}

--- NEW FILE: speedy.c ---
/**
 * Copyright (c) 2002, 2003 Billy Biggs <vektor@dumbterm.net>.
 * Copyright (C) 2001 Matthew J. Marjanovic <maddog@mir.com>
 *
 * 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.
 */

[...2429 lines suppressed...]
        comb_factor_packed422_scanline = comb_factor_packed422_scanline_mmx;
        kill_chroma_packed422_inplace_scanline = kill_chroma_packed422_inplace_scanline_mmx;
        diff_packed422_block8x8 = diff_packed422_block8x8_mmx;
        invert_colour_packed422_inplace_scanline = invert_colour_packed422_inplace_scanline_mmx;
        vfilter_chroma_121_packed422_scanline = vfilter_chroma_121_packed422_scanline_mmx;
        vfilter_chroma_332_packed422_scanline = vfilter_chroma_332_packed422_scanline_mmx;
        speedy_memcpy = speedy_memcpy_mmx;
    } else {
        if( verbose ) {
            printf( "speedycode: No MMX or MMXEXT support detected, using C fallbacks.\n" );
        }
    }
#endif
}

uint32_t speedy_get_accel( void )
{
    return speedy_accel;
}


--- NEW FILE: tvtime.c ---
/**
 * 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.
 */

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <math.h>

#if HAVE_INTTYPES_H
#include <inttypes.h>
#else
#include <stdint.h>
#endif

#include "speedy.h"
#include "deinterlace.h"
#include "pulldown.h"
#include "tvtime.h"

/* use tvtime_t */
#define pulldown_alg this->pulldown_alg
#define curmethod this->curmethod

#define last_topdiff this->last_topdiff
#define last_botdiff this->last_botdiff

#define pdoffset this->pdoffset
#define pderror this->pderror
#define pdlastbusted this->pdlastbusted
#define filmmode this->filmmode



/**
 * This is how many frames to wait until deciding if the pulldown phase
 * has changed or if we've really found a pulldown sequence.  This is
 * currently set to about 1 second, that is, we won't go into film mode
 * until we've seen a pulldown sequence successfully for 1 second.
 */
#define PULLDOWN_ERROR_WAIT     60

/**
 * This is how many predictions have to be incorrect before we fall back to
 * video mode.  Right now, if we mess up, we jump to video mode immediately.
 */
#define PULLDOWN_ERROR_THRESHOLD 2


/**
 * Explination of the loop:
 *
 * We want to build frames so that they look like this:
 *  Top field:      Bot field:
 *     Copy            Double
 *     Interp          Copy
 *     Copy            Interp
 *     Interp          Copy
 *     Copy            --
 *     --              --
 *     --              --
 *     Copy            Interp
 *     Interp          Copy
 *     Copy            Interp
 *     Double          Copy
 *
 *  So, say a frame is n high.
 *  For the bottom field, the first scanline is blank (special case).
 *  For the top field, the final scanline is blank (special case).
 *  For the rest of the scanlines, we alternate between Copy then Interpolate.
 *
 *  To do the loop, I go 'Interp then Copy', and handle the first copy
 *  outside the loop for both top and bottom.
 *  The top field therefore handles n-2 scanlines in the loop.
 *  The bot field handles n-2 scanlines in the loop.
 *
 * What we pass to the deinterlacing routines:
 *
 * Each deinterlacing routine can require data from up to four fields.
 * The current field is being output is Field 4:
 *
 * | Field 3 | Field 2 | Field 1 | Field 0 |
 * |         |   T2    |         |   T0    |
 * |   M3    |         |    M1   |         |
 * |         |   B2    |         |   B0    |
 * |  NX3    |         |   NX1   |         |
 *
 * So, since we currently get frames not individual fields from V4L, there
 * are two possibilities for where these come from:
 *
 * CASE 1: Deinterlacing the top field:
 * | Field 4 | Field 3 | Field 2 | Field 1 | Field 0 |
 * |   T4    |         |   T2    |         |   T0    |
 * |         |   M3    |         |    M1   |         |
 * |   B4    |         |   B2    |         |   B0    |
 *  [--  secondlast --] [--  lastframe  --] [--  curframe   --]
 *
 * CASE 2: Deinterlacing the bottom field:
 * | Field 4 | Field 3 | Field 2 | Field 1 | Field 0 |
 * |   T4    |         |   T2    |         |   T0    |
 * |         |   M3    |         |    M1   |         |
 * |   B4    |         |   B2    |         |   B0    |
 * ndlast --] [--  lastframe  --] [--  curframe   --]
 *
 * So, in case 1, we need the previous 2 frames as well as the current
 * frame, and in case 2, we only need the previous frame, since the
 * current frame contains both Field 3 and Field 4.
 */
static void pulldown_merge_fields( uint8_t *output,
                                   uint8_t *topfield,
                                   uint8_t *botfield,
                                   int width,
                                   int frame_height,
                                   int fieldstride,
                                   int outstride )
{
    int i;

    for( i = 0; i < frame_height; i++ ) {
        uint8_t *curoutput = output + (i * outstride);

        if( i & 1 ) {
            blit_packed422_scanline( curoutput, botfield + ((i / 2) * fieldstride), width );
        } else {
            blit_packed422_scanline( curoutput, topfield + ((i / 2) * fieldstride), width );
        }
    }
}


static void calculate_pulldown_score_vektor( tvtime_t *this, uint8_t *curframe,
                                             uint8_t *lastframe,
                                             int instride,
                                             int frame_height,
                                             int width )
{
    int i;

    last_topdiff = 0;
    last_botdiff = 0;

    for( i = 0; i < frame_height; i++ ) {

        if( i > 40 && (i & 3) == 0 && i < frame_height - 40 ) {
            last_topdiff += diff_factor_packed422_scanline( curframe + (i*instride),
                                                            lastframe + (i*instride), width );
            last_botdiff += diff_factor_packed422_scanline( curframe + (i*instride) + instride,
                                                            lastframe + (i*instride) + instride,
                                                            width );
        }
    }
}


int tvtime_build_deinterlaced_frame( tvtime_t *this, uint8_t *output,
                                             uint8_t *curframe,
                                             uint8_t *lastframe,
                                             uint8_t *secondlastframe,
                                             int bottom_field, int second_field,
                                             int width,
                                             int frame_height,
                                             int instride,
                                             int outstride )
{
    int i;

    if( pulldown_alg != PULLDOWN_VEKTOR ) {
        /* If we leave vektor pulldown mode, lose our state. */
        filmmode = 0;
    }

    if( pulldown_alg == PULLDOWN_VEKTOR ) {
        /* Make pulldown phase decisions every top field. */
        if( !bottom_field ) {
            int predicted;

            predicted = pdoffset << 1;
            if( predicted > PULLDOWN_SEQ_DD ) predicted = PULLDOWN_SEQ_AA;

            /**
             * Old algorithm:
            pdoffset = determine_pulldown_offset_history( last_topdiff, last_botdiff, 1, &realbest );
            if( pdoffset & predicted ) { pdoffset = predicted; } else { pdoffset = realbest; }
             */

            calculate_pulldown_score_vektor( this, curframe, lastframe, instride, frame_height, width );

            pdoffset = determine_pulldown_offset_short_history_new( last_topdiff, last_botdiff, 1, predicted );
            //pdoffset = determine_pulldown_offset_history_new( last_topdiff, last_botdiff, 1, predicted );

            /* 3:2 pulldown state machine. */
            if( !pdoffset ) {
                /* No pulldown offset applies, drop out of pulldown immediately. */
                pdlastbusted = 0;
                pderror = PULLDOWN_ERROR_WAIT;
            } else if( pdoffset != predicted ) {
                if( pdlastbusted ) {
                    pdlastbusted--;
                    pdoffset = predicted;
                } else {
                    pderror = PULLDOWN_ERROR_WAIT;
                }
            } else {
                if( pderror ) {
                    pderror--;
                }

                if( !pderror ) {
                    pdlastbusted = PULLDOWN_ERROR_THRESHOLD;
                }
            }


            if( !pderror ) {
                // We're in pulldown, reverse it.
                if( !filmmode ) {
                    printf( "Film mode enabled.\n" );
                    filmmode = 1;
                }

                if( pulldown_drop( pdoffset, 0 ) )
                  return 0;

                if( pulldown_source( pdoffset, 0 ) ) {
                    pulldown_merge_fields( output, curframe, curframe + instride,
                                           width, frame_height, instride*2, outstride );
                } else {
                    pulldown_merge_fields( output, curframe, lastframe + instride,
                                           width, frame_height, instride*2, outstride );
                }

                return 1;
            } else {
                if( filmmode ) {
                    printf( "Film mode disabled.\n" );
                    filmmode = 0;
                }
            }
        } else if( !pderror ) {
            if( pulldown_drop( pdoffset, 1 ) )
                return 0;

            if( pulldown_source( pdoffset, 1 ) ) {
                pulldown_merge_fields( output, curframe, lastframe + instride,
                                       width, frame_height, instride*2, outstride );
            } else {
                pulldown_merge_fields( output, curframe, curframe + instride,
                                       width, frame_height, instride*2, outstride );
            }

            return 1;
        }
    }

    if( !curmethod->scanlinemode ) {
        deinterlace_frame_data_t data;

        data.f0 = curframe;
        data.f1 = lastframe;
        data.f2 = secondlastframe;

        curmethod->deinterlace_frame( output, outstride, &data, bottom_field, second_field,
                                      width, frame_height );

    } else {
        int loop_size;
        int scanline = 0;

        if( bottom_field ) {
            /* Advance frame pointers to the next input line. */
            curframe += instride;
            lastframe += instride;
            secondlastframe += instride;

            /* Double the top scanline a scanline. */
            blit_packed422_scanline( output, curframe, width );

            output += outstride;
            scanline++;
        }

        /* Copy a scanline. */
        blit_packed422_scanline( output, curframe, width );

        output += outstride;
        scanline++;

        /* Something is wrong here. -Billy */
        loop_size = ((frame_height - 2) / 2);
        for( i = loop_size; i; --i ) {
            deinterlace_scanline_data_t data;

            data.bottom_field = bottom_field;

            data.t0 = curframe;
            data.b0 = curframe + (instride*2);

            if( second_field ) {
                data.tt1 = (i < loop_size) ? (curframe - instride) : (curframe + instride);
                data.m1  = curframe + instride;
                data.bb1 = (i > 1) ? (curframe + (instride*3)) : (curframe + instride);
            } else {
                data.tt1 = (i < loop_size) ? (lastframe - instride) : (lastframe + instride);
                data.m1  = lastframe + instride;
                data.bb1 = (i > 1) ? (lastframe + (instride*3)) : (lastframe + instride);
            }

            data.t2 = lastframe;
            data.b2 = lastframe + (instride*2);

            if( second_field ) {
                data.tt3 = (i < loop_size) ? (lastframe - instride) : (lastframe + instride);
                data.m3  = lastframe + instride;
                data.bb3 = (i > 1) ? (lastframe + (instride*3)) : (lastframe + instride);
            } else {
                data.tt3 = (i < loop_size) ? (secondlastframe - instride) : (secondlastframe + instride);
                data.m3  = secondlastframe + instride;
                data.bb3 = (i > 1) ? (secondlastframe + (instride*3)) : (secondlastframe + instride);
            }

            curmethod->interpolate_scanline( output, &data, width );

            output += outstride;
            scanline++;

            data.tt0 = curframe;
            data.m0  = curframe + (instride*2);
            data.bb0 = (i > 1) ? (curframe + (instride*4)) : (curframe + (instride*2));

            if( second_field ) {
                data.t1 = curframe + instride;
                data.b1 = (i > 1) ? (curframe + (instride*3)) : (curframe + instride);
            } else {
                data.t1 = lastframe + instride;
                data.b1 = (i > 1) ? (lastframe + (instride*3)) : (lastframe + instride);
            }

            data.tt2 = lastframe;
            data.m2  = lastframe + (instride*2);
            data.bb2 = (i > 1) ? (lastframe + (instride*4)) : (lastframe + (instride*2));

            if( second_field ) {
                data.t2 = lastframe + instride;
                data.b2 = (i > 1) ? (lastframe + (instride*3)) : (lastframe + instride);
            } else {
                data.t2 = secondlastframe + instride;
                data.b2 = (i > 1) ? (secondlastframe + (instride*3)) : (secondlastframe + instride);
            }

            /* Copy a scanline. */
            curmethod->copy_scanline( output, &data, width );
            curframe += instride * 2;
            lastframe += instride * 2;
            secondlastframe += instride * 2;

            output += outstride;
            scanline++;
        }

        if( !bottom_field ) {
            /* Double the bottom scanline. */
            blit_packed422_scanline( output, curframe, width );

            output += outstride;
            scanline++;
        }
    }

    return 1;
}


int tvtime_build_copied_field( tvtime_t *this, uint8_t *output,
                                       uint8_t *curframe,
                                       int bottom_field,
                                       int width,
                                       int frame_height,
                                       int instride,
                                       int outstride )
{
    int scanline = 0;
    int i;

    if( bottom_field ) {
        /* Advance frame pointers to the next input line. */
        curframe += instride;
    }

    /* Copy a scanline. */
    // blit_packed422_scanline( output, curframe, width );
    quarter_blit_vertical_packed422_scanline( output, curframe + (instride*2), curframe, width );

    curframe += instride * 2;
    output += outstride;
    scanline += 2;

    for( i = ((frame_height - 2) / 2); i; --i ) {
        /* Copy/interpolate a scanline. */
        if( bottom_field ) {
            // interpolate_packed422_scanline( output, curframe, curframe - (instride*2), width );
            quarter_blit_vertical_packed422_scanline( output, curframe - (instride*2), curframe, width );
        } else {
            // blit_packed422_scanline( output, curframe, width );
            if( i > 1 ) {
                quarter_blit_vertical_packed422_scanline( output, curframe + (instride*2), curframe, width );
            } else {
                blit_packed422_scanline( output, curframe, width );
            }
        }
        curframe += instride * 2;

        output += outstride;
        scanline += 2;
    }

    return 1;
}

tvtime_t *tvtime_new_context(void)
{
  tvtime_t *this;

  this = malloc(sizeof(tvtime_t));

  pulldown_alg = PULLDOWN_NONE;

  curmethod = NULL;

  tvtime_reset_context(this);

  return this;
}

void tvtime_reset_context( tvtime_t *this )
{
  last_topdiff = 0;
  last_botdiff = 0;

  pdoffset = PULLDOWN_SEQ_AA;
  pderror = PULLDOWN_ERROR_WAIT;
  pdlastbusted = 0;
  filmmode = 0;
}