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