Files
MAX_CARLINK_A270S/MXC_A27-PCB4.5-CANUI/lib/hx170dec/src/refbuffer.c

1316 lines
40 KiB
C

/*------------------------------------------------------------------------------
-- --
-- This software is confidential and proprietary and may be used --
-- only as expressly authorized by a licensing agreement from --
-- --
-- Hantro Products Oy. --
-- --
-- (C) COPYRIGHT 2006 HANTRO PRODUCTS OY --
-- ALL RIGHTS RESERVED --
-- --
-- The entire notice above must be reproduced --
-- on all copies and should not be removed. --
-- --
--------------------------------------------------------------------------------
--
-- Description : Reference buffer control functions.
--
------------------------------------------------------------------------------
--
-- Version control information, please leave untouched.
--
-- $RCSfile: refbuffer.c,v $
-- $Revision: 1.49 $
-- $Date: 2010/11/10 08:01:28 $
--
------------------------------------------------------------------------------*/
#include "basetype.h"
#include "regdrv.h"
#include "deccfg.h"
#include "refbuffer.h"
#include <stdio.h>
#define DEC_X170_MODE_H264 (0)
#define DEC_X170_MODE_MPEG4 (1)
#define DEC_X170_MODE_H263 (2)
#define DEC_X170_MODE_VC1 (4)
#define DEC_X170_MODE_MPEG2 (5)
#define DEC_X170_MODE_MPEG1 (6)
#define DEC_X170_MODE_VP6 (7)
#define DEC_X170_MODE_RV (8)
#define DEC_X170_MODE_VP7 (9)
#define DEC_X170_MODE_VP8 (10)
#define DEC_X170_MODE_AVS (11)
#define THR_ADJ_MAX (8)
#define THR_ADJ_MIN (1)
#define MAX_DIRECT_MVS (3000)
#ifndef DEC_X170_REFBU_ADJUST_VALUE
#define DEC_X170_REFBU_ADJUST_VALUE 130
#endif
/* Read direct MV from output memory; indexes i0..i2 used to
* handle both big- and little-endian modes. */
#define DIR_MV_VER(p,i0,i1,i2) \
((((u32)(p[(i0)])) << 3) | (((u32)(p[(i1)])) >> 5) | (((u32)(p[(i2)] & 0x3) ) << 11 ))
#define DIR_MV_BE_VER(p) \
((((u32)(p[1])) << 3) | (((u32)(p[0])) >> 5) | (((u32)(p[2] & 0x3) ) << 11 ))
#define DIR_MV_LE_VER(p) \
((((u32)(p[2])) << 3) | (((u32)(p[3])) >> 5) | (((u32)(p[1] & 0x3) ) << 11 ))
#define SIGN_EXTEND(value, bits) (value) = (((value)<<(32-bits))>>(32-bits))
/* Distribution ranges and zero point */
#define VER_DISTR_MIN (-256)
#define VER_DISTR_MAX (255)
#define VER_DISTR_RANGE (512)
#define VER_DISTR_ZERO_POINT (256)
#define VER_MV_RANGE (16)
#define HOR_CALC_WIDTH (32)
/* macro to get absolute value */
#define ABS(a) (((a) < 0) ? -(a) : (a))
static const memAccess_t memStatsPerFormat[] = {
{ 307, 36, 150 }, /* H.264 (upped 20%) */
{ 236, 29, 112 }, /* MPEG-4 */
{ 228, 25, 92 }, /* H.263 (based on MPEG-2) */
{ 0, 0, 0 }, /* JPEG */
{ 240, 29, 112 }, /* VC-1 */
{ 302, 25, 92 }, /* MPEG-2 */
{ 228, 25, 92 }, /* MPEG-1 */
{ 240, 29, 112 }, /* AVS (based on VC-1) */
{ 240, 29, 112 }, /* VP6 (based on VC-1) */
{ 240, 29, 112 }, /* RVx (based on VC-1) */
{ 240, 29, 112 }, /* VP7 (based on VC-1) */
{ 240, 29, 112 }, /* VP8 (based on VC-1) */
{ 240, 29, 112 } /* AVS (based on VC-1) */
};
static const i32 mbDataPerFormat[][2] = {
{ 734, 880 }, /* H.264 (upped 20%) */
{ 464, 535 }, /* MPEG-4 */
{ 435, 486 }, /* H.263 (same as MPEG-2 used) */
{ 0, 0 }, /* JPEG */
{ 533, 644 }, /* VC-1 */
{ 435, 486 }, /* MPEG-2 */
{ 435, 486 }, /* MPEG-1 */
{ 533, 486 }, /* AVS (based now on VC-1) */
{ 533, 486 }, /* VP6 (based now on VC-1) */
{ 533, 486 }, /* RVx (based on VC-1) */
{ 533, 486 }, /* VP7 (based now on VC-1) */
{ 533, 486 }, /* VP8 (based now on VC-1) */
{ 533, 486 } /* AVS (based now on VC-1) */
};
static u32 GetSettings( refBuffer_t *pRefbu, i32 *pX, i32 *pY, u32 isBpic,
u32 isFieldPic );
static void IntraFrame( refBuffer_t *pRefbu );
/*------------------------------------------------------------------------------
Function name : UpdateMemModel
Description : Update memory model for reference buffer
Return type :
Argument :
------------------------------------------------------------------------------*/
void UpdateMemModel( refBuffer_t *pRefbu )
{
/* Variables */
i32 widthInMbs;
i32 heightInMbs;
i32 latency;
i32 nonseq;
i32 seq;
i32 busWidth;
i32 tmp, tmp2;
/* Code */
#define DIV_CEIL(a,b) (((a)+(b)-1)/(b))
widthInMbs = pRefbu->picWidthInMbs;
heightInMbs = pRefbu->picHeightInMbs;
busWidth = pRefbu->busWidthInBits;
tmp = busWidth >> 2; /* initially buffered mbs per row */
tmp2 = busWidth >> 3; /* n:o of mbs buffered at refresh */
tmp = ( 1 + DIV_CEIL(widthInMbs - tmp, tmp2 ) ); /* latencies per mb row */
tmp2 = 24 * heightInMbs; /* Number of rows to buffer in total */
/* Latencies per frame */
latency = 2 * tmp * heightInMbs;
nonseq = tmp2 * tmp;
seq = ( DIV_CEIL( 16*widthInMbs, busWidth>>3 ) ) * tmp2 -
nonseq;
pRefbu->numCyclesForBuffering =
latency * pRefbu->currMemModel.latency +
nonseq * ( 1 + pRefbu->currMemModel.nonseq ) +
seq * ( 1 + pRefbu->currMemModel.seq );
pRefbu->bufferPenalty =
pRefbu->memAccessStats.nonseq +
pRefbu->memAccessStats.seq;
if( busWidth == 32 )
pRefbu->bufferPenalty >>= 1;
pRefbu->avgCyclesPerMb =
( ( pRefbu->memAccessStats.latency * pRefbu->currMemModel.latency ) / 100 ) +
pRefbu->memAccessStats.nonseq * ( 1 + pRefbu->currMemModel.nonseq ) +
pRefbu->memAccessStats.seq * ( 1 + pRefbu->currMemModel.seq );
#ifdef REFBUFFER_TRACE
printf("***** ref buffer mem model trace *****\n");
printf("latency = %7d clk\n", pRefbu->currMemModel.latency );
printf("sequential = %7d clk\n", pRefbu->currMemModel.nonseq );
printf("non-sequential = %7d clk\n", pRefbu->currMemModel.seq );
printf("latency (mb) = %7d\n", pRefbu->memAccessStats.latency );
printf("sequential (mb) = %7d\n", pRefbu->memAccessStats.nonseq );
printf("non-sequential (mb) = %7d\n", pRefbu->memAccessStats.seq );
printf("bus-width = %7d\n", busWidth );
printf("buffering cycles = %7d\n", pRefbu->numCyclesForBuffering );
printf("buffer penalty = %7d\n", pRefbu->bufferPenalty );
printf("avg cycles per mb = %7d\n", pRefbu->avgCyclesPerMb );
printf("***** ref buffer mem model trace *****\n");
#endif /* REFBUFFER_TRACE */
#undef DIV_CEIL
}
/*------------------------------------------------------------------------------
Function name : IntraFrame
Description : Clear history buffers on intra frames
Return type :
Argument :
------------------------------------------------------------------------------*/
void IntraFrame( refBuffer_t *pRefbu )
{
pRefbu->oy[0] = pRefbu->oy[1] = pRefbu->oy[2] = 0;
pRefbu->numIntraBlk[0] =
pRefbu->numIntraBlk[1] = pRefbu->numIntraBlk[2] = (-1);
pRefbu->coverage[0] = /*4 * tmp;*/
pRefbu->coverage[1] =
pRefbu->coverage[2] = (-1); /* initial value */
}
/*------------------------------------------------------------------------------
Function name : RefbuGetHitThreshold
Description :
Return type :
Argument :
------------------------------------------------------------------------------*/
i32 RefbuGetHitThreshold( refBuffer_t *pRefbu )
{
i32 requiredHitsClk = 0;
i32 requiredHitsData = 0;
i32 divisor;
i32 tmp;
#ifdef REFBUFFER_TRACE
i32 predMiss;
printf("***** ref buffer threshold trace *****\n");
#endif /* REFBUFFER_TRACE */
divisor = pRefbu->avgCyclesPerMb - pRefbu->bufferPenalty;
if( divisor > 0 )
requiredHitsClk = ( 4 * pRefbu->numCyclesForBuffering ) / divisor;
divisor = pRefbu->mbWeight;
#ifdef REFBUFFER_TRACE
printf("mb weight = %7d\n", divisor );
#endif /* REFBUFFER_TRACE */
if( divisor > 0)
{
divisor = ( divisor * pRefbu->dataExcessMaxPct ) / 100;
#ifdef REFBUFFER_TRACE
predMiss = 4 * pRefbu->frmSizeInMbs - pRefbu->predIntraBlk -
pRefbu->predCoverage;
printf("predicted misses = %7d\n", predMiss );
printf("data excess %% = %7d\n", pRefbu->dataExcessMaxPct );
printf("divisor = %7d\n", divisor );
#endif /* REFBUFFER_TRACE */
/*tmp = (( DATA_EXCESS_MAX_PCT - 100 ) * pRefbu->mbWeight * predMiss ) / 400;*/
tmp = 0;
requiredHitsData = ( 4 * pRefbu->totalDataForBuffering - tmp);
requiredHitsData /= divisor;
}
if(pRefbu->picHeightInMbs)
{
requiredHitsClk /= pRefbu->picHeightInMbs;
requiredHitsData /= pRefbu->picHeightInMbs;
}
#ifdef REFBUFFER_TRACE
printf("target (clk) = %7d\n", requiredHitsClk );
printf("target (data) = %7d\n", requiredHitsData );
printf("***** ref buffer threshold trace *****\n");
#endif /* REFBUFFER_TRACE */
return requiredHitsClk > requiredHitsData ?
requiredHitsClk : requiredHitsData;
}
/*------------------------------------------------------------------------------
Function name : InitMemAccess
Description :
Return type :
Argument :
------------------------------------------------------------------------------*/
void InitMemAccess( refBuffer_t *pRefbu, u32 decMode, u32 busWidth )
{
/* Initialize stream format memory model */
pRefbu->memAccessStats = memStatsPerFormat[decMode];
pRefbu->memAccessStatsFlag = 0;
if( busWidth == 64 )
{
pRefbu->memAccessStats.seq >>= 1;
pRefbu->mbWeight = mbDataPerFormat[decMode][1];
}
else
{
pRefbu->mbWeight = mbDataPerFormat[decMode][0];
}
}
/*------------------------------------------------------------------------------
Function name : RefbuInit
Description :
Return type :
Argument :
------------------------------------------------------------------------------*/
void RefbuInit( refBuffer_t *pRefbu, u32 decMode, u32 picWidthInMbs,
u32 picHeightInMbs, u32 supportFlags )
{
/* Variables */
u32 tmp;
u32 i;
/* Code */
/* Ignore init's if image size doesn't change. For example H264 SW may
* call here when moving to RLC mode. */
if( pRefbu->picWidthInMbs == picWidthInMbs &&
pRefbu->picHeightInMbs == picHeightInMbs )
{
return;
}
#ifdef REFBUFFER_TRACE
printf("***** ref buffer initialized to %dx%d *****\n",
picWidthInMbs, picHeightInMbs);
#endif /* REFBUFFER_TRACE */
pRefbu->decMode = decMode;
pRefbu->picWidthInMbs = picWidthInMbs;
pRefbu->picHeightInMbs = picHeightInMbs;
tmp = picWidthInMbs * picHeightInMbs;
pRefbu->frmSizeInMbs = tmp;
pRefbu->totalDataForBuffering
= pRefbu->frmSizeInMbs*384;
tmp = picWidthInMbs * ((picHeightInMbs + 1) / 2);
pRefbu->fldSizeInMbs = tmp;
pRefbu->offsetSupport = (supportFlags & REFBU_SUPPORT_OFFSET) ? 1 : 0;
pRefbu->interlacedSupport = (supportFlags & REFBU_SUPPORT_INTERLACED) ? 1 : 0;
pRefbu->doubleSupport = (supportFlags & REFBU_SUPPORT_DOUBLE) ? 1 : 0;
pRefbu->thrAdj = THR_ADJ_MAX;
pRefbu->dataExcessMaxPct = DEC_X170_REFBU_ADJUST_VALUE;
pRefbu->predCoverage = pRefbu->predIntraBlk = 0;
IntraFrame( pRefbu );
if( decMode == DEC_X170_MODE_H264 )
{
pRefbu->mvsPerMb = 16;
pRefbu->filterSize = 3;
}
else if ( decMode == DEC_X170_MODE_VC1 )
{
pRefbu->mvsPerMb = 2;
pRefbu->filterSize = 2;
}
else
{
pRefbu->mvsPerMb = 1;
pRefbu->filterSize = 1;
}
/* Initialize buffer memory model */
pRefbu->busWidthInBits = DEC_X170_REFBU_WIDTH;
pRefbu->currMemModel.latency = DEC_X170_REFBU_LATENCY;
pRefbu->currMemModel.nonseq = DEC_X170_REFBU_NONSEQ;
pRefbu->currMemModel.seq = DEC_X170_REFBU_SEQ;
pRefbu->prevLatency = -1;
pRefbu->numCyclesForBuffering = 0;
for ( i = 0 ; i < 3 ; ++i )
{
pRefbu->fldHitsP[i][0] =
pRefbu->fldHitsP[i][1] =
pRefbu->fldHitsB[i][0] =
pRefbu->fldHitsB[i][1] = -1;
}
pRefbu->fldCnt = 0;
/* Initialize stream format memory model */
InitMemAccess( pRefbu, decMode, DEC_X170_REFBU_WIDTH );
pRefbu->decModeMbWeights[0] = mbDataPerFormat[decMode][0];
pRefbu->decModeMbWeights[1] = mbDataPerFormat[decMode][1];
}
/*------------------------------------------------------------------------------
Function name : GetSettings
Description : Determine whether or not to enable buffer, and calculate
buffer offset.
Return type : u32
Argument :
------------------------------------------------------------------------------*/
u32 GetSettings( refBuffer_t *pRefbu, i32 *pX, i32 *pY, u32 isBpic,
u32 isFieldPic)
{
/* Variables */
i32 tmp;
u32 enable = HANTRO_TRUE;
u32 frmSizeInMbs;
i32 cov;
i32 sign;
i32 numCyclesForBuffering;
/* Code */
frmSizeInMbs = pRefbu->frmSizeInMbs;
*pX = 0;
*pY = 0;
/* Disable automatically for pictures less than 16MB wide */
if( pRefbu->picWidthInMbs <= 16 )
{
return HANTRO_FALSE;
}
numCyclesForBuffering = pRefbu->numCyclesForBuffering;
if(isFieldPic)
numCyclesForBuffering /= 2;
if( pRefbu->prevUsedDouble )
{
cov = pRefbu->coverage[0];
tmp = pRefbu->avgCyclesPerMb * cov / 4;
/* Check whether it is viable to enable buffering */
tmp = (2*numCyclesForBuffering < tmp);
if( !tmp )
{
pRefbu->thrAdj -= 2;
if ( pRefbu->thrAdj < THR_ADJ_MIN )
pRefbu->thrAdj = THR_ADJ_MIN;
}
else
{
pRefbu->thrAdj++;
if ( pRefbu->thrAdj > THR_ADJ_MAX )
pRefbu->thrAdj = THR_ADJ_MAX;
}
}
if( !isBpic )
{
if( pRefbu->coverage[1] != -1 )
{
cov = (5*pRefbu->coverage[0] - 1*pRefbu->coverage[1])/4;
if( pRefbu->coverage[2] != -1 )
{
cov = cov + ( pRefbu->coverage[0] + pRefbu->coverage[1] + pRefbu->coverage[2] ) / 3;
cov /= 2;
}
}
else if ( pRefbu->coverage[0] != -1 )
{
cov = pRefbu->coverage[0];
}
else
{
cov = 4*frmSizeInMbs;
}
}
else
{
cov = pRefbu->coverage[0];
if( cov == -1 )
{
cov = 4*frmSizeInMbs;
}
/* MPEG-4 B-frames have no intra coding possibility, so extrapolate
* hit rate to match it */
else if( pRefbu->predIntraBlk < 4*frmSizeInMbs &&
pRefbu->decMode == DEC_X170_MODE_MPEG4 )
{
cov *= (128*4*frmSizeInMbs) / (4*frmSizeInMbs-pRefbu->predIntraBlk) ;
cov /= 128;
}
/* Assume that other formats have less intra MBs in B pictures */
else
{
cov *= (128*4*frmSizeInMbs) / (4*frmSizeInMbs-pRefbu->predIntraBlk/2) ;
cov /= 128;
}
}
if( cov < 0 ) cov = 0;
pRefbu->predCoverage = cov;
/* Check whether it is viable to enable buffering */
/* 1.criteria = cycles */
tmp = pRefbu->avgCyclesPerMb * cov / 4;
numCyclesForBuffering += pRefbu->bufferPenalty * cov / 4;
enable = (numCyclesForBuffering < tmp);
/* 2.criteria = data */
/*
tmp = ( DATA_EXCESS_MAX_PCT * cov ) / 400;
tmp = tmp * pRefbu->mbWeight;
enable = enable && (pRefbu->totalDataForBuffering < tmp);
*/
#ifdef REFBUFFER_TRACE
printf("***** ref buffer algorithm trace *****\n");
printf("predicted coverage = %7d\n", cov );
printf("bus width in calc = %7d\n", pRefbu->busWidthInBits );
printf("coverage history = %7d%7d%7d\n",
pRefbu->coverage[0],
pRefbu->coverage[1],
pRefbu->coverage[2] );
printf("enable = %d (%8d<%8d)\n", enable, numCyclesForBuffering, tmp );
#endif /* REFBUFFER_TRACE */
/* If still enabled, calculate offsets */
if( enable )
{
/* Round to nearest 16 multiple */
tmp = (pRefbu->oy[0] + pRefbu->oy[1] + 1)/2;
sign = ( tmp < 0 );
if( pRefbu->prevWasField )
tmp /= 2;
tmp = ABS(tmp);
tmp = tmp & ~15;
if( sign ) tmp = -tmp;
*pY = tmp;
}
#ifdef REFBUFFER_TRACE
printf("offset_x = %7d\n", *pX );
printf("offset_y = %7d (%d %d)\n", *pY, pRefbu->oy[0], pRefbu->oy[1] );
printf("***** ref buffer algorithm trace *****\n");
#endif /* REFBUFFER_TRACE */
return enable;
}
/*------------------------------------------------------------------------------
Function name : BuildDistribution
Description :
Return type :
Argument :
------------------------------------------------------------------------------*/
void BuildDistribution( u32 *pDistrVer,
u32 *pMv, i32 frmSizeInMbs,
u32 mvsPerMb,
u32 bigEndian,
i32 *minY, i32 *maxY )
{
/* Variables */
i32 mb;
i32 ver;
u8 * pMvTmp;
u32 mvs;
u32 skipMv = mvsPerMb*4;
u32 multiplier = 4;
u32 div = 2;
/* Code */
mvs = frmSizeInMbs;
/* Try to keep total n:o of mvs checked under MAX_DIRECT_MVS */
if( mvs > MAX_DIRECT_MVS )
{
while( mvs/div > MAX_DIRECT_MVS )
div++;
mvs /= div;
skipMv *= div;
multiplier *= div;
}
pMvTmp = (u8*)pMv;
if( bigEndian )
{
for( mb = 0 ; mb < mvs ; ++mb )
{
{
ver = DIR_MV_BE_VER(pMvTmp);
SIGN_EXTEND(ver, 13);
/* Cut fraction and saturate */
/*lint -save -e702 */
ver >>= 2;
/*lint -restore */
if( ver >= VER_DISTR_MIN && ver <= VER_DISTR_MAX )
{
pDistrVer[ver] += multiplier;
if( ver < *minY ) *minY = ver;
if( ver > *maxY ) *maxY = ver;
}
}
pMvTmp += skipMv; /* Skip all other blocks for macroblock */
}
}
else
{
for( mb = 0 ; mb < mvs ; ++mb )
{
{
ver = DIR_MV_LE_VER(pMvTmp);
SIGN_EXTEND(ver, 13);
/* Cut fraction and saturate */
/*lint -save -e702 */
ver >>= 2;
/*lint -restore */
if( ver >= VER_DISTR_MIN && ver <= VER_DISTR_MAX )
{
pDistrVer[ver] += multiplier;
if( ver < *minY ) *minY = ver;
if( ver > *maxY ) *maxY = ver;
}
}
pMvTmp += skipMv; /* Skip all other blocks for macroblock */
}
}
}
/*------------------------------------------------------------------------------
Function name : DirectMvStatistics
Description :
Return type :
Argument :
------------------------------------------------------------------------------*/
void DirectMvStatistics( refBuffer_t *pRefbu, u32 *pMv, i32 numIntraBlk,
u32 bigEndian )
{
/* Variables */
i32 * pTmp;
i32 frmSizeInMbs;
i32 i;
i32 oy = 0;
i32 best = 0;
i32 sum;
i32 mvsPerMb;
i32 minY = VER_DISTR_MAX, maxY = VER_DISTR_MIN;
/* Mv distributions per component*/
u32 distrVer[VER_DISTR_RANGE] = { 0 };
u32 *pDistrVer = distrVer + VER_DISTR_ZERO_POINT;
/* Code */
mvsPerMb = pRefbu->mvsPerMb;
if( pRefbu->prevWasField )
frmSizeInMbs = pRefbu->fldSizeInMbs;
else
frmSizeInMbs = pRefbu->frmSizeInMbs;
if( numIntraBlk < 4*frmSizeInMbs )
{
BuildDistribution( pDistrVer,
pMv,
frmSizeInMbs,
mvsPerMb,
bigEndian,
&minY, &maxY );
/* Fix Intra occurences */
pDistrVer[0] -= numIntraBlk;
#if 0 /* Use median for MV calculation */
/* Find median */
{
tmp = (frmSizeInMbs - numIntraMbs) / 2;
sum = 0;
i = VER_DISTR_MIN;
for( i = VER_DISTR_MIN ; sum < tmp ; i++ )
sum += pDistrVer[i];
oy = i-1;
/* Calculate coverage percent */
best = 0;
i = MAX( VER_DISTR_MIN, oy-VER_MV_RANGE );
limit = MIN( VER_DISTR_MAX, oy+VER_MV_RANGE );
for( ; i < limit ; ++i )
best += pDistrVer[i];
}
else
#endif
{
i32 y;
i32 penalty;
/* Initial sum */
sum = 0;
minY += VER_DISTR_ZERO_POINT;
maxY += VER_DISTR_ZERO_POINT;
for( i = 0 ; i < 2*VER_MV_RANGE ; ++i )
{
sum += distrVer[i];
}
best = 0;
/* Other sums */
maxY -= 2*VER_MV_RANGE;
for( i = 0 ; i < VER_DISTR_RANGE-2*VER_MV_RANGE-1 ; ++i )
{
sum -= distrVer[i];
sum += distrVer[2*VER_MV_RANGE+i];
y = VER_DISTR_MIN+VER_MV_RANGE+i+1;
if ( ABS(y) > 8 )
{
penalty = ABS(y)-8;
penalty = (frmSizeInMbs*penalty)/16;
}
else
{
penalty = 0;
}
/*if( (ABS(y) & 15) == 0 )*/
{
if( sum - penalty > best )
{
best = sum - penalty;
oy = y;
}
else if ( sum - penalty == best )
{
if( ABS(y) < ABS(oy) ) oy = y;
}
}
}
}
if(pRefbu->prevWasField)
best *= 2;
pRefbu->coverage[0] = best;
/* Store updated offsets */
pTmp = pRefbu->oy;
pTmp[2] = pTmp[1];
pTmp[1] = pTmp[0];
pTmp[0] = oy;
}
else
{
pTmp = pRefbu->oy;
pTmp[2] = pTmp[1];
pTmp[1] = pTmp[0];
pTmp[0] = 0;
}
}
/*------------------------------------------------------------------------------
Function name : RefbuMvStatisticsB
Description :
Return type :
Argument :
------------------------------------------------------------------------------*/
void RefbuMvStatisticsB( refBuffer_t *pRefbu, u32 *regBase )
{
/* Variables */
i32 topFldCnt;
i32 botFldCnt;
/* Code */
topFldCnt = GetDecRegister( regBase, HWIF_REFBU_TOP_SUM );
botFldCnt = GetDecRegister( regBase, HWIF_REFBU_BOT_SUM );
if( pRefbu->fldCnt >= 2 &&
GetDecRegister( regBase, HWIF_FIELDPIC_FLAG_E ) &&
(topFldCnt || botFldCnt))
{
pRefbu->fldHitsB[2][0] = pRefbu->fldHitsB[1][0]; pRefbu->fldHitsB[2][1] = pRefbu->fldHitsB[1][1];
pRefbu->fldHitsB[1][0] = pRefbu->fldHitsB[0][0]; pRefbu->fldHitsB[1][1] = pRefbu->fldHitsB[0][1];
if( GetDecRegister( regBase, HWIF_PIC_TOPFIELD_E ) ) {
pRefbu->fldHitsB[0][0] = topFldCnt; pRefbu->fldHitsB[0][1] = botFldCnt;
} else {
pRefbu->fldHitsB[0][0] = botFldCnt; pRefbu->fldHitsB[0][1] = topFldCnt;
}
}
if( GetDecRegister( regBase, HWIF_FIELDPIC_FLAG_E ) )
pRefbu->fldCnt++;
}
/*------------------------------------------------------------------------------
Function name : RefbuMvStatistics
Description :
Return type :
Argument :
------------------------------------------------------------------------------*/
void RefbuMvStatistics( refBuffer_t *pRefbu, u32 *regBase,
u32 *pMv, u32 directMvsAvailable, u32 isIntraPicture )
{
/* Variables */
i32 * pTmp;
i32 tmp;
i32 numIntraBlk;
i32 topFldCnt;
i32 botFldCnt;
u32 bigEndian;
/* Code */
if( isIntraPicture )
{
/*IntraFrame( pRefbu );*/
return; /* Clear buffers etc. ? */
}
if(pRefbu->prevWasField && !pRefbu->interlacedSupport)
return;
#ifdef ASIC_TRACE_SUPPORT
/* big endian */
bigEndian = 1;
#else
/* Determine HW endianness; this affects how we read motion vector
* map from HW. Map endianness is same as decoder output endianness */
if( GetDecRegister( regBase, HWIF_DEC_OUT_ENDIAN ) == 0 )
{
/* big endian */
bigEndian = 1;
}
else
{
/* little endian */
bigEndian = 0;
}
#endif
if(!pRefbu->offsetSupport || 1) /* Note: offset always disabled for now,
* so always disallow direct mv data */
{
directMvsAvailable = 0;
}
numIntraBlk = GetDecRegister( regBase, HWIF_REFBU_INTRA_SUM );
topFldCnt = GetDecRegister( regBase, HWIF_REFBU_TOP_SUM );
botFldCnt = GetDecRegister( regBase, HWIF_REFBU_BOT_SUM );
#ifdef REFBUFFER_TRACE
printf("***** ref buffer mv statistics trace *****\n");
printf("intra blocks = %7d\n", numIntraBlk );
printf("top fields = %7d\n", topFldCnt );
printf("bottom fields = %7d\n", botFldCnt );
#endif /* REFBUFFER_TRACE */
if( pRefbu->fldCnt > 0 &&
GetDecRegister( regBase, HWIF_FIELDPIC_FLAG_E ) &&
(topFldCnt || botFldCnt))
{
pRefbu->fldHitsP[2][0] = pRefbu->fldHitsP[1][0]; pRefbu->fldHitsP[2][1] = pRefbu->fldHitsP[1][1];
pRefbu->fldHitsP[1][0] = pRefbu->fldHitsP[0][0]; pRefbu->fldHitsP[1][1] = pRefbu->fldHitsP[0][1];
if( GetDecRegister( regBase, HWIF_PIC_TOPFIELD_E ) ) {
pRefbu->fldHitsP[0][0] = topFldCnt; pRefbu->fldHitsP[0][1] = botFldCnt;
} else {
pRefbu->fldHitsP[0][0] = botFldCnt; pRefbu->fldHitsP[0][1] = topFldCnt;
}
}
if( GetDecRegister( regBase, HWIF_FIELDPIC_FLAG_E ) )
pRefbu->fldCnt++;
pRefbu->coverage[2] = pRefbu->coverage[1];
pRefbu->coverage[1] = pRefbu->coverage[0];
if(directMvsAvailable)
{
DirectMvStatistics( pRefbu, pMv, numIntraBlk, bigEndian );
}
else if(pRefbu->offsetSupport)
{
i32 interMvs;
i32 sum;
sum = GetDecRegister( regBase, HWIF_REFBU_Y_MV_SUM );
SIGN_EXTEND( sum, 22 );
interMvs = (4*pRefbu->frmSizeInMbs - numIntraBlk)/4;
if( pRefbu->prevWasField )
interMvs *= 2;
if( interMvs == 0 )
interMvs = 1;
/* Require at least 50% mvs present to calculate reliable avg offset */
if( interMvs * 50 >= pRefbu->frmSizeInMbs )
{
/* Store updated offsets */
pTmp = pRefbu->oy;
pTmp[2] = pTmp[1];
pTmp[1] = pTmp[0];
pTmp[0] = sum/interMvs;
}
}
/* Read buffer hits from previous frame. If number of hits < threshold
* for the frame, then we know that HW switched buffering off. */
tmp = GetDecRegister( regBase, HWIF_REFBU_HIT_SUM );
pRefbu->prevFrameHitSum = tmp;
if ( tmp >= pRefbu->checkpoint && pRefbu->checkpoint )
{
if(pRefbu->prevWasField)
tmp *= 2;
pRefbu->coverage[0] = tmp;
#ifdef REFBUFFER_TRACE
printf("actual coverage = %7d\n", tmp );
#endif /* REFBUFFER_TRACE */
}
else if(!directMvsAvailable)
{
/* Buffering was disabled for previous frame, no direct mv
* data available either, so assume we got a bit more hits than
* the frame before that */
if( pRefbu->coverage[1] != -1 )
{
pRefbu->coverage[0] = ( 4 *
pRefbu->picWidthInMbs *
pRefbu->picHeightInMbs + 5 * pRefbu->coverage[1] ) / 6;
}
else
pRefbu->coverage[0] = pRefbu->frmSizeInMbs * 4;
#ifdef REFBUFFER_TRACE
printf("calculated coverage = %7d\n", pRefbu->coverage[0] );
#endif /* REFBUFFER_TRACE */
}
else
{
#ifdef REFBUFFER_TRACE
printf("estimated coverage = %7d\n", pRefbu->coverage[0] );
#endif /* REFBUFFER_TRACE */
}
/* Store intra counts for rate prediction */
pTmp = pRefbu->numIntraBlk;
pTmp[2] = pTmp[1];
pTmp[1] = pTmp[0];
pTmp[0] = numIntraBlk;
/* Predict number of intra mbs for next frame */
if(pTmp[2] != -1) tmp = (pTmp[0] + pTmp[1] + pTmp[2])/3;
else if( pTmp[1] != -1 ) tmp = (pTmp[0] + pTmp[1])/2;
else tmp = pTmp[0];
pRefbu->predIntraBlk = MIN( pTmp[0], tmp );
#ifdef REFBUFFER_TRACE
printf("predicted intra blk = %7d\n", pRefbu->predIntraBlk );
printf("***** ref buffer mv statistics trace *****\n");
#endif /* REFBUFFER_TRACE */
}
/*------------------------------------------------------------------------------
Function name : DecideParityMode
Description : Setup reference buffer for next frame.
Return type :
Argument : pRefbu Reference buffer descriptor
isBframe Is frame B-coded
------------------------------------------------------------------------------*/
u32 DecideParityMode( refBuffer_t *pRefbu, u32 isBframe )
{
/* Variables */
i32 same, opp;
/* Code */
if( pRefbu->decMode != DEC_X170_MODE_H264 )
{
/* Don't use parity mode for other formats than H264
* for now */
return 0;
}
/* Read history */
if( isBframe )
{
same = pRefbu->fldHitsB[0][0];
/*
if( pRefbu->fldHitsB[1][0] >= 0 )
same += pRefbu->fldHitsB[1][0];
if( pRefbu->fldHitsB[2][0] >= 0 )
same += pRefbu->fldHitsB[2][0];
*/
opp = pRefbu->fldHitsB[0][1];
/*
if( pRefbu->fldHitsB[1][1] >= 0 )
opp += pRefbu->fldHitsB[1][1];
if( pRefbu->fldHitsB[2][0] >= 0 )
opp += pRefbu->fldHitsB[2][1];*/
}
else
{
same = pRefbu->fldHitsP[0][0];
/*
if( pRefbu->fldHitsP[1][0] >= 0 )
same += pRefbu->fldHitsP[1][0];
if( pRefbu->fldHitsP[2][0] >= 0 )
same += pRefbu->fldHitsP[2][0];
*/
opp = pRefbu->fldHitsP[0][1];
/*
if( pRefbu->fldHitsP[1][1] >= 0 )
opp += pRefbu->fldHitsP[1][1];
if( pRefbu->fldHitsP[2][0] >= 0 )
opp += pRefbu->fldHitsP[2][1];
*/
}
/* If not enough data yet, bail out */
if( same == -1 || opp == -1 )
return 0;
if( opp*2 <= same )
return 1;
return 0;
}
/*------------------------------------------------------------------------------
Function name : RefbuSetup
Description : Setup reference buffer for next frame.
Return type :
Argument : pRefbu Reference buffer descriptor
regBase Pointer to SW/HW control registers
isInterlacedField
isIntraFrame Is frame intra-coded (or IDR for H.264)
isBframe Is frame B-coded
refPicId pic_id for reference picture, if
applicable. For H.264 pic_id for
nearest reference picture of L0.
------------------------------------------------------------------------------*/
void RefbuSetup( refBuffer_t *pRefbu, u32 *regBase,
refbuMode_e mode,
u32 isIntraFrame,
u32 isBframe,
u32 refPicId0, u32 refPicId1, u32 flags )
{
/* Variables */
i32 ox, oy;
i32 enable;
i32 tmp;
i32 thr2 = 0;
u32 featureDisable = 0;
u32 useAdaptiveMode = 0;
u32 useDoubleMode = 0;
u32 disableCheckpoint = 0;
u32 multipleRefFrames = 1;
u32 multipleRefFields = 1;
u32 forceAdaptiveSingle = 1;
u32 singleRefField = 0;
u32 pic0 = 0, pic1 = 0;
/* Code */
SetDecRegister(regBase, HWIF_REFBU_THR, 0 );
SetDecRegister(regBase, HWIF_REFBU2_THR, 0 );
SetDecRegister(regBase, HWIF_REFBU_PICID, 0 );
SetDecRegister(regBase, HWIF_REFBU_Y_OFFSET, 0 );
multipleRefFrames = ( flags & REFBU_MULTIPLE_REF_FRAMES ) ? 1 : 0;
disableCheckpoint = ( flags & REFBU_DISABLE_CHECKPOINT ) ? 1 : 0;
forceAdaptiveSingle = ( flags & REFBU_FORCE_ADAPTIVE_SINGLE ) ? 1 : 0;
pRefbu->prevWasField = (mode == REFBU_FIELD && !isBframe);
/* Check supported features */
if(mode != REFBU_FRAME && !pRefbu->interlacedSupport)
featureDisable = 1;
if(!isIntraFrame && !featureDisable)
{
if(pRefbu->prevLatency != pRefbu->currMemModel.latency)
{
UpdateMemModel( pRefbu );
pRefbu->prevLatency = pRefbu->currMemModel.latency;
}
enable = GetSettings( pRefbu, &ox, &oy,
isBframe, mode == REFBU_FIELD);
tmp = RefbuGetHitThreshold( pRefbu );
pRefbu->checkpoint = tmp;
if( mode == REFBU_FIELD )
{
tmp = DecideParityMode( pRefbu, isBframe );
SetDecRegister( regBase, HWIF_REFBU_FPARMOD_E, tmp );
if( !tmp )
{
pRefbu->thrAdj = 1;
}
}
else
{
pRefbu->thrAdj = 1;
}
SetDecRegister(regBase, HWIF_REFBU_E, enable );
if( enable )
{
/* Figure out which features to enable */
if( pRefbu->doubleSupport )
{
if( !isBframe ) /* P field/frame */
{
if( mode == REFBU_FIELD )
{
if( singleRefField )
{
/* Buffer only reference field given in refPicId0 */
}
else if (multipleRefFields )
{
/* Let's not try to guess */
useDoubleMode = 1;
useAdaptiveMode = 1;
pRefbu->checkpoint /= pRefbu->thrAdj;
thr2 = pRefbu->checkpoint ;
}
else
{
/* Buffer both reference fields explicitly */
useDoubleMode = 1;
pRefbu->checkpoint /= pRefbu->thrAdj;
thr2 = pRefbu->checkpoint;
}
}
else if (forceAdaptiveSingle)
{
useAdaptiveMode = 1;
useDoubleMode = 0;
}
else if( multipleRefFrames )
{
useAdaptiveMode = 1;
useDoubleMode = 1;
pRefbu->checkpoint /= pRefbu->thrAdj;
thr2 = pRefbu->checkpoint;
}
else
{
/* Content to buffer just one ref pic */
}
}
else /* B field/frame */
{
if( mode == REFBU_FIELD )
{
/* Let's not try to guess */
useAdaptiveMode = 1;
useDoubleMode = 1;
pRefbu->checkpoint /= pRefbu->thrAdj;
/*pRefbu->checkpoint /= 2;*/
thr2 = pRefbu->checkpoint;
}
else if (!multipleRefFrames )
{
/* Buffer forward and backward pictures as given in
* refPicId0 and refPicId1 */
useDoubleMode = 1;
pRefbu->checkpoint /= pRefbu->thrAdj;
thr2 = pRefbu->checkpoint;
}
else
{
/* Let's not try to guess */
useDoubleMode = 1;
useAdaptiveMode = 1;
pRefbu->checkpoint /= pRefbu->thrAdj;
thr2 = pRefbu->checkpoint;
}
}
}
else /* Just single buffering supported */
{
if( !isBframe ) /* P field/frame */
{
if( mode == REFBU_FIELD )
{
useAdaptiveMode = 1;
}
else if (forceAdaptiveSingle)
{
useAdaptiveMode = 1;
}
else if (multipleRefFrames)
{
/*useAdaptiveMode = 1;*/
}
else
{
}
}
else /* B field/frame */
{
useAdaptiveMode = 1;
}
}
if(!useAdaptiveMode)
{
pic0 = refPicId0;
if( useDoubleMode )
{
pic1 = refPicId1;
}
}
SetDecRegister(regBase, HWIF_REFBU_EVAL_E, useAdaptiveMode );
/* Calculate amount of hits required for first mb row */
if ( mode == REFBU_MBAFF )
{
pRefbu->checkpoint *= 2;
thr2 *= 2;
}
if( useDoubleMode )
{
oy = 0; /* Disable offset */
}
/* Limit offset */
{
i32 limit, height;
height = pRefbu->picHeightInMbs;
if( mode == REFBU_FIELD )
height /= 2; /* adjust to field height */
if( mode == REFBU_MBAFF )
limit = 64; /* 4 macroblock rows */
else
limit = 48; /* 3 macroblock rows */
if( (i32)(oy+limit) > (i32)(height*16) )
oy = height*16-limit;
if( (i32)((-oy)+limit) > (i32)(height*16) )
oy = -(height*16-limit);
}
/* Disable offset just to make sure */
if(!pRefbu->offsetSupport || 1) /* NOTE: always disabled for now */
oy = 0;
if(!disableCheckpoint)
SetDecRegister(regBase, HWIF_REFBU_THR, pRefbu->checkpoint );
else
SetDecRegister(regBase, HWIF_REFBU_THR, 0 );
SetDecRegister(regBase, HWIF_REFBU_PICID, pic0 );
SetDecRegister(regBase, HWIF_REFBU_Y_OFFSET, oy );
if(pRefbu->doubleSupport)
{
/* Very much TODO */
SetDecRegister(regBase, HWIF_REFBU2_BUF_E, useDoubleMode );
SetDecRegister(regBase, HWIF_REFBU2_THR, thr2 );
SetDecRegister(regBase, HWIF_REFBU2_PICID, pic1 );
pRefbu->prevUsedDouble = useDoubleMode;
}
}
pRefbu->prevWasField = (mode == REFBU_FIELD && !isBframe);
}
else
{
pRefbu->checkpoint = 0;
SetDecRegister(regBase, HWIF_REFBU_E, HANTRO_FALSE );
}
if(pRefbu->testFunction)
{
pRefbu->testFunction(pRefbu, regBase, isIntraFrame, mode );
}
}
/*------------------------------------------------------------------------------
Function name : RefbuGetVpxCoveragePrediction
Description : Return coverage and intra block prediction for VPx
"intelligent" ref buffer control. Prediction algorithm
to be finalized
Return type :
Argument :
------------------------------------------------------------------------------*/
u32 RefbuVpxGetPrevFrameStats( refBuffer_t *pRefbu)
{
i32 cov, tmp;
tmp = pRefbu->prevFrameHitSum;
if ( tmp >= pRefbu->checkpoint && pRefbu->checkpoint )
cov = tmp/4;
else
cov = 0;
return cov;
}