/*
* Copyright (c) 2010 Ixonos Plc.
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - Initial contribution
*
* Contributors:
* Ixonos Plc
*
* Description:
* Advanced Intra Coding functions (MPEG-4).
*
*/
#include "h263dConfig.h"
#include "vdcaic.h"
#include "errcodes.h"
#include "debug.h"
#include "zigzag.h"
/*
*
* aicIntraDCSwitch
*
* Parameters:
* intraDCVLCThr VOP-header field for QP dependent switching between
* MPEG-4 IntraDC coding and IntraAC VLC coding of the
* Intra DC coefficients
* QP quantization parameter
*
* Function:
* This function decides based on the input parameters if the Intra DC
* should be decoded as MPEG-4 IntraDC (switched=0) or switched to
* IntraAC VLC (switched=1)
*
* Returns:
* switched value
*
*
*
*/
u_char aicIntraDCSwitch(int intraDCVLCThr, int QP) {
if ( intraDCVLCThr == 0 ) {
return 0;
}
else if ( intraDCVLCThr == 7 ) {
return 1;
}
else if ( QP >= intraDCVLCThr*2+11 ) {
return 1;
}
else {
return 0;
}
}
/*
*
* aicDCScaler
*
* Parameters:
* QP quantization parameter
* type type of Block "1" Luminance "2" Chrominance
*
* Function:
* Calculation of DC quantization scale according
* to the incoming QP and block type
*
* Returns:
* dcScaler value
*
*
*
*/
int aicDCScaler (int QP, int type) {
int dcScaler;
if (type == 1) {
if (QP > 0 && QP < 5) {
dcScaler = 8;
}
else if (QP > 4 && QP < 9) {
dcScaler = 2 * QP;
}
else if (QP > 8 && QP < 25) {
dcScaler = QP + 8;
}
else {
dcScaler = 2 * QP - 16;
}
}
else {
if (QP > 0 && QP < 5) {
dcScaler = 8;
}
else if (QP > 4 && QP < 25) {
dcScaler = (QP + 13) / 2;
}
else {
dcScaler = QP - 6;
}
}
return dcScaler;
}
/**
* Small routine to compensate the QP value differences between the
* predictor and current block AC coefficients
*
**/
static int compensateQPDiff(int val, int QP) {
if (val<0) {
return (val-(QP>>1))/QP;
}
else {
return (val+(QP>>1))/QP;
}
}
/**
* Small routine to fill default prediction values into a dcStore entry
*
*/
static void resetPredRow(int pred[])
{
memset (pred, 0, 7 * sizeof(int));
}
/*
*
* aicStart
*
* Parameters:
* aicData aicData_t structure
* instance pointer to vdcInstance_t structure
*
* Function:
* This function initialises dcStore and qpStore buffers.
* One should call aicStart in the beginning of each VOP.
*
* Returns:
* Nothing
*
* Error codes:
*
*
*/
void aicStart(aicData_t *aicData, int numMBsInMBLine, int16 *error)
{
int i,j, numStoreUnits;
int initXpos[6] = {-1, 0, -1, 0, -1, -1};
int initYpos[6] = {-1, -1, 0, 0, -1, -1};
int initXtab[6] = {1, 0, 3, 2, 4, 5};
int initYtab[6] = {2, 3, 0, 1, 4, 5};
int initZtab[6] = {3, 2, 1, 0, 4, 5};
numStoreUnits = numMBsInMBLine*2;
if (!aicData->qpStore || !aicData->dcStore) {
aicData->qpStore = (int16 *) calloc(numStoreUnits, sizeof(int16));
if (!aicData->qpStore)
{
*error = ERR_VDC_MEMORY_ALLOC;
return;
}
/* allocate space for 3D matrix to keep track of prediction values
for DC/AC prediction */
aicData->dcStore = (int ***)calloc(numStoreUnits, sizeof(int **));
if (!aicData->dcStore)
{
*error = ERR_VDC_MEMORY_ALLOC;
return;
}
for (i = 0; i < numStoreUnits; i++)
{
aicData->dcStore[i] = (int **)calloc(6, sizeof(int *));
if (!aicData->dcStore[i])
{
*error = ERR_VDC_MEMORY_ALLOC;
return;
}
for (j = 0; j < 6; j++)
{
aicData->dcStore[i][j] = (int *)calloc(15, sizeof(int));
if ( !(aicData->dcStore[i][j]) )
{
*error = ERR_VDC_MEMORY_ALLOC;
return;
}
}
}
aicData->numMBsInMBLine = numMBsInMBLine;
for (i= 0; i < 6; i++)
{
aicData->Xpos[i] = initXpos[i];
aicData->Ypos[i] = initYpos[i];
aicData->Xtab[i] = initXtab[i];
aicData->Ytab[i] = initYtab[i];
aicData->Ztab[i] = initZtab[i];
}
/* 1 << (instance->bits_per_pixel - 1) */
aicData->midGrey = 1 << 7;
aicData->ACpred_flag = 1;
} else {
memset(aicData->qpStore, 0, numStoreUnits * sizeof(int16));
}
}
/*
* Clip the reconstructed coefficient when it is stored for prediction
*
*/
#define aicClip(rec) \
((rec < -2048) ? -2048 : ((rec > 2047) ? 2047 : rec))
/*
*
* aicBlockUpdate
*
* Parameters:
* aicData aicData_t structure
* currMBNum Current Macroblocks Number
*
* Function:
* This function fills up the dcStore and qpStore of current MB.
*
* Returns:
* Nothing
*
* Error codes:
* None
*
*
*/
void aicBlockUpdate (aicData_t *aicData, int currMBNum, int blockNum, int *block,
int pquant, int DC_coeff)
{
int n, currDCStoreIndex;
assert(currMBNum >= 0);
currDCStoreIndex = ((currMBNum / aicData->numMBsInMBLine) % 2) * aicData->numMBsInMBLine +
(currMBNum % aicData->numMBsInMBLine);
if (block != NULL) {
aicData->dcStore[currDCStoreIndex][blockNum][0] = aicClip(DC_coeff);
block[0] = aicClip(block[0]);
for (n = 1; n < 8; n++)
{
aicData->dcStore[currDCStoreIndex][blockNum][n] = block[zigzag[n]] = aicClip(block[zigzag[n]]);
aicData->dcStore[currDCStoreIndex][blockNum][n+7] = block[zigzag[n<<3 /**8*/]] = aicClip(block[zigzag[n<<3 /**8*/]]);
}
}
if (blockNum == 0)
aicData->qpStore[currDCStoreIndex]= (int16) pquant;
}
/*
*
* aicIsBlockValid
*
* Parameters:
* aicData aicData_t structure
* currMBNum Current Macroblocks Number
*
* Function:
* This function checks if the current MB has a valid entry in the
* dcStore and qpStore (needed in predictor validation of I-MBs in a P-VOP).
*
* Returns:
* 1 if MB has a valid entry
* 0 if MB doesn't have a valid entry
*
* Error codes:
* None
*
*
*/
int aicIsBlockValid (aicData_t *aicData, int currMBNum)
{
int currDCStoreIndex;
assert(currMBNum >= 0);
currDCStoreIndex = ((currMBNum / aicData->numMBsInMBLine) % 2) * aicData->numMBsInMBLine +
(currMBNum % aicData->numMBsInMBLine);
if (aicData->qpStore[currDCStoreIndex] > 0 && aicData->qpStore[currDCStoreIndex] < 32)
return 1;
else
return 0;
}
/*
*
* aicFree
*
* Parameters:
* aicData aicData_t structure
* numOfMBs Number of Macroblocks in VOP
*
* Function:
* This function frees the dynamic memory allocated by aicStart.
* aicFree should be called at least when exiting the main program.
* Alternatively it can be called whenever the playing a video has
* ended.
*
* Returns:
* Nothing
*
* Error codes:
* None
*
*
*/
void aicFree(aicData_t *aicData)
{
int i,j;
/* Free allocated memory for 3D matrix */
if (aicData->dcStore) {
for (i = 0; i < (aicData->numMBsInMBLine*2); i++)
{
for (j = 0; j < 6; j++)
free((char *)aicData->dcStore[i][j]);
free((char *)aicData->dcStore[i]);
}
free((char *)aicData->dcStore);
}
/* Free allocated memory for qpStore matrix */
if (aicData->qpStore) {
free((char *)aicData->qpStore);
}
}
/*
*
* aicDCACrecon
*
* Parameters:
* aicData aicData_t structure
* QP QP of the current MB
* fTopMBMissing flag indicating if the block above the current block
* is outside of the current Video Packet or not a valid
* Intra block (in case of P-VOP decoding)
* fLeftMBMissing flag indicating if the block left to the current block
* is outside of the current Video Packet or not a valid
* Intra block (in case of P-VOP decoding)
* fBBlockOut flag indicating if the top-left neighbour block of the
* current block is outside of the current Video Packet
* or not a valid Intra block (in case of P-VOP decoding)
* blockNum number of the current block in the MB
* (0..3 luminance, 4..5 chrominance)
* qBlock block of coefficients
* currMBNum number of the current macroblocks in the VOP
*
* Function:
* This function reconstructs the DC and AC (first column or first row or none)
* coefficients of the current block by selecting the predictor from the
* neighbouring blocks (or default values), and adding these predictor
* values to qBlock. Its output is the quantized coefficient matrix in
* normal zigzag order.
*
* Returns:
* Nothing
*
* Error codes:
* None
*
*
*/
void aicDCACrecon(aicData_t *aicData, int QP, u_char fTopMBMissing, u_char fLeftMBMissing,
u_char fBBlockOut, int blockNum, int *qBlock, int currMBNum)
{
int m, n, tempDCScaler;
int xCoordMB, yCoordMB, mbWidth, currDCStoreIndex;
int blockA = 0, blockB = 0, blockC = 0, fBlockAExist = 0, fBlockCExist = 0;
int gradHor, gradVer, predDC;
int fVertical;
int predA[7], predC[7];
int pcoeff[64];
assert(currMBNum >= 0);
assert(qBlock != NULL);
xCoordMB = currMBNum % aicData->numMBsInMBLine;
yCoordMB = currMBNum / aicData->numMBsInMBLine;
currDCStoreIndex = (yCoordMB % 2)*aicData->numMBsInMBLine + xCoordMB;
mbWidth = aicData->numMBsInMBLine * ((currDCStoreIndex < aicData->numMBsInMBLine) ? -1 : 1);
/* Find the direction of prediction and the DC prediction */
switch ( blockNum ) {
case 0 :
case 4 :
case 5 :
/* Y0, U, and V blocks */
/* Prediction blocks A (left), B (above-left), and C (above) */
if ( ( yCoordMB == 0 && xCoordMB == 0) || (fTopMBMissing && fLeftMBMissing) || (xCoordMB == 0 && fTopMBMissing) || (yCoordMB == 0 && fLeftMBMissing) ) {
/* top-left edge of VOP or VP */
blockA = aicData->midGrey<<3 /**8*/;
blockB = aicData->midGrey<<3 /**8*/;
blockC = aicData->midGrey<<3 /**8*/;
fBlockAExist = fBlockCExist = 0;
}
else if ( yCoordMB == 0 || fTopMBMissing ) {
/* top row of VOP or VP, or MB on top not valid */
blockA = aicData->dcStore[currDCStoreIndex+aicData->Xpos[blockNum]][aicData->Xtab[blockNum]][0];
fBlockAExist = 1;
if ( yCoordMB == 0 || fBBlockOut ) {
/* B MB is out of this VP */
blockB = aicData->midGrey<<3 /**8*/;
}
else {
blockB = aicData->dcStore[currDCStoreIndex+(aicData->Ypos[blockNum])*mbWidth+aicData->Xpos[blockNum]][aicData->Ztab[blockNum]][0];
}
blockC = aicData->midGrey<<3 /**8*/;
fBlockCExist = 0;
}
else if ( xCoordMB == 0 || fLeftMBMissing ) {
/* left edge of VOP or VP, or MB on left not valid */
blockA = aicData->midGrey<<3 /**8*/;
fBlockAExist = 0;
if ( xCoordMB == 0 || fBBlockOut ) {
/* B MB is out of this VP */
blockB = aicData->midGrey<<3 /**8*/;
}
else {
blockB = aicData->dcStore[currDCStoreIndex+(aicData->Ypos[blockNum])*mbWidth+aicData->Xpos[blockNum]][aicData->Ztab[blockNum]][0];
}
blockC = aicData->dcStore[currDCStoreIndex+(aicData->Ypos[blockNum])*mbWidth][aicData->Ytab[blockNum]][0];
fBlockCExist = 1;
}
else {
/* Something else */
blockA = aicData->dcStore[currDCStoreIndex+aicData->Xpos[blockNum]][aicData->Xtab[blockNum]][0];
if ( fBBlockOut ) {
/* B MB is out of this VP */
blockB = aicData->midGrey<<3 /**8*/;
}
else {
blockB = aicData->dcStore[currDCStoreIndex+(aicData->Ypos[blockNum])*mbWidth+aicData->Xpos[blockNum]][aicData->Ztab[blockNum]][0];
}
blockC = aicData->dcStore[currDCStoreIndex+(aicData->Ypos[blockNum])*mbWidth][aicData->Ytab[blockNum]][0];
fBlockAExist = fBlockCExist = 1;
}
break;
case 1 :
/* Y1 block */
/* Prediction block A (left) always available */
blockA = aicData->dcStore[currDCStoreIndex+aicData->Xpos[blockNum]][aicData->Xtab[blockNum]][0];
fBlockAExist = 1;
/* Prediction blocks B (above-left) and C (above) */
if ( yCoordMB == 0 || fTopMBMissing ) {
/* top row of VOP or VP */
blockB = aicData->midGrey<<3 /**8*/;
blockC = aicData->midGrey<<3 /**8*/;
fBlockCExist = 0;
}
else {
blockB = aicData->dcStore[currDCStoreIndex+(aicData->Ypos[blockNum])*mbWidth+aicData->Xpos[blockNum]][aicData->Ztab[blockNum]][0];
blockC = aicData->dcStore[currDCStoreIndex+(aicData->Ypos[blockNum])*mbWidth][aicData->Ytab[blockNum]][0];
fBlockCExist = 1;
}
break;
case 2 :
/* Y2 block */
/* Prediction blocks A (left) and B (above-left) */
if ( xCoordMB == 0 || fLeftMBMissing ) {
/* left edge or first MB in VP */
blockA = aicData->midGrey<<3 /**8*/;
blockB = aicData->midGrey<<3 /**8*/;
fBlockAExist = 0;
}
else {
blockA = aicData->dcStore[currDCStoreIndex+aicData->Xpos[blockNum]][aicData->Xtab[blockNum]][0];
blockB = aicData->dcStore[currDCStoreIndex+(aicData->Ypos[blockNum])*mbWidth+aicData->Xpos[blockNum]][aicData->Ztab[blockNum]][0];
fBlockAExist = 1;
}
/* Prediction block C (above) always available */
blockC = aicData->dcStore[currDCStoreIndex+(aicData->Ypos[blockNum])*mbWidth][aicData->Ytab[blockNum]][0];
fBlockCExist = 1;
break;
case 3 :
/* Y3 block */
/* Prediction block A (left) always available */
blockA = aicData->dcStore[currDCStoreIndex+aicData->Xpos[blockNum]][aicData->Xtab[blockNum]][0];
/* Prediction block B (above-left) always available */
blockB = aicData->dcStore[currDCStoreIndex+(aicData->Ypos[blockNum])*mbWidth+aicData->Xpos[blockNum]][aicData->Ztab[blockNum]][0];
/* Prediction block C (above) always available */
blockC = aicData->dcStore[currDCStoreIndex+(aicData->Ypos[blockNum])*mbWidth][aicData->Ytab[blockNum]][0];
fBlockAExist = fBlockCExist = 1;
break;
}
gradHor = blockB - blockC;
gradVer = blockA - blockB;
if ((abs(gradVer)) < (abs(gradHor))) {
/* Vertical prediction (from C) */
predDC = blockC;
fVertical = 1;
}
else {
/* Horizontal prediction (from A) */
predDC = blockA;
fVertical = 0;
}
/* Now reconstruct the DC coefficient */
tempDCScaler = aicDCScaler(QP,(blockNum<4)?1:2);
qBlock[0] = (u_int8) (qBlock[0] + (predDC + tempDCScaler/2) / tempDCScaler);
/* Do AC prediction if required */
if (aicData->ACpred_flag == 1) {
/* Do inverse zigzag-scanning */
if (fVertical) {
for (m = 0; m < 64; m++) {
pcoeff[m] = qBlock[zigzag_h[m]];
}
}
else { /* horizontal prediction */
for (m = 0; m < 64; m++) {
pcoeff[m] = qBlock[zigzag_v[m]];
}
}
/* AC predictions */
if ( !fVertical && fBlockAExist ) {
/* prediction from A */
for (m = 8; m < 15; m++) {
predA[m-8] = compensateQPDiff(((aicData->dcStore[currDCStoreIndex+aicData->Xpos[blockNum]][aicData->Xtab[blockNum]][m]) * 2 * (aicData->qpStore[currDCStoreIndex+aicData->Xpos[blockNum]])), (QP<<1));
}
}
else if ( fVertical && fBlockCExist ) {
/* prediction from C */
for (m = 1; m < 8; m++) {
predC[m-1] = compensateQPDiff(((aicData->dcStore[currDCStoreIndex+(aicData->Ypos[blockNum])*mbWidth][aicData->Ytab[blockNum]][m]) * 2 * (aicData->qpStore[currDCStoreIndex+(aicData->Ypos[blockNum])*mbWidth])), (QP<<1));
}
}
else {
/* Prediction not possible */
if ( fVertical ) {
resetPredRow(predC);
}
else {
resetPredRow(predA);
}
}
/* AC coefficients reconstruction*/
if (fVertical) { /* Vertical, top row of block C */
for (m = 0; m < 7; m++) {
qBlock[zigzag[(m+1)*8]] = pcoeff[(m+1)*8];
}
for (m = 1; m < 8; m++) {
qBlock[zigzag[m]] = pcoeff[m] + predC[m-1];
}
}
else { /* Horizontal, left column of block A */
for (m = 0; m < 7; m++) {
qBlock[zigzag[(m+1)*8]] = pcoeff[(m+1)*8] + predA[m];
}
for (m = 1; m < 8; m++) {
qBlock[zigzag[m]] = pcoeff[m];
}
}
/* Copy the rest of the coefficients back to qBlock */
for (m = 1; m < 8; m++) {
for (n = 1; n < 8; n++) {
qBlock[zigzag[m*8+n]] = pcoeff[m*8+n];
}
}
}
}
// End of file