blob: 7a262ed8326b54b86e344bb3141bfe9fc99416d5 [file] [log] [blame]
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*------------------------------------------------------------------------------
Table of contents
1. Include headers
2. External compiler flags
3. Module defines
4. Local function prototypes
5. Functions
h264bsdConceal
ConcealMb
Transform
------------------------------------------------------------------------------*/
/*------------------------------------------------------------------------------
1. Include headers
------------------------------------------------------------------------------*/
#include "h264bsd_conceal.h"
#include "h264bsd_util.h"
#include "h264bsd_reconstruct.h"
#include "h264bsd_dpb.h"
/*------------------------------------------------------------------------------
2. External compiler flags
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
3. Module defines
------------------------------------------------------------------------------*/
/*lint -e702 disable lint warning on right shift of signed quantity */
/*------------------------------------------------------------------------------
4. Local function prototypes
------------------------------------------------------------------------------*/
static u32 ConcealMb(mbStorage_t *pMb, image_t *currImage, u32 row, u32 col,
u32 sliceType, u8 *data);
static void Transform(i32 *data);
/*------------------------------------------------------------------------------
Function name: h264bsdConceal
Functional description:
Perform error concealment for a picture. Two types of concealment
is performed based on sliceType:
1) copy from previous picture for P-slices.
2) concealment from neighbour pixels for I-slices
I-type concealment is based on ideas presented by Jarno Tulkki.
The concealment algorithm determines frequency domain coefficients
from the neighbour pixels, applies integer transform (the same
transform used in the residual processing) and uses the results as
pixel values for concealed macroblocks. Transform produces 4x4
array and one pixel value has to be used for 4x4 luma blocks and
2x2 chroma blocks.
Similar concealment is performed for whole picture (the choise
of the type is based on last successfully decoded slice header of
the picture but it is handled by the calling function). It is
acknowledged that this may result in wrong type of concealment
when a picture contains both types of slices. However,
determination of slice type macroblock-by-macroblock cannot
be done due to the fact that it is impossible to know to which
slice each corrupted (not successfully decoded) macroblock
belongs.
The error concealment is started by searching the first propoerly
decoded macroblock and concealing the row containing the macroblock
in question. After that all macroblocks above the row in question
are concealed. Finally concealment of rows below is performed.
The order of concealment for 4x4 picture where macroblock 9 is the
first properly decoded one is as follows (properly decoded
macroblocks marked with 'x', numbers indicating the order of
concealment):
4 6 8 10
3 5 7 9
1 x x 2
11 12 13 14
If all macroblocks of the picture are lost, the concealment is
copy of previous picture for P-type and setting the image to
constant gray (pixel value 128) for I-type.
Concealment sets quantization parameter of the concealed
macroblocks to value 40 and macroblock type to intra to enable
deblocking filter to smooth the edges of the concealed areas.
Inputs:
pStorage pointer to storage structure
currImage pointer to current image structure
sliceType type of the slice
Outputs:
currImage concealed macroblocks will be written here
Returns:
HANTRO_OK
------------------------------------------------------------------------------*/
u32 h264bsdConceal(storage_t *pStorage, image_t *currImage, u32 sliceType)
{
/* Variables */
u32 i, j;
u32 row, col;
u32 width, height;
u8 *refData;
mbStorage_t *mb;
/* Code */
ASSERT(pStorage);
ASSERT(currImage);
DEBUG(("Concealing %s slice\n", IS_I_SLICE(sliceType) ?
"intra" : "inter"));
width = currImage->width;
height = currImage->height;
refData = NULL;
/* use reference picture with smallest available index */
if (IS_P_SLICE(sliceType) || (pStorage->intraConcealmentFlag != 0))
{
i = 0;
do
{
refData = h264bsdGetRefPicData(pStorage->dpb, i);
i++;
if (i >= 16)
break;
} while (refData == NULL);
}
i = row = col = 0;
/* find first properly decoded macroblock -> start point for concealment */
while (i < pStorage->picSizeInMbs && !pStorage->mb[i].decoded)
{
i++;
col++;
if (col == width)
{
row++;
col = 0;
}
}
/* whole picture lost -> copy previous or set grey */
if (i == pStorage->picSizeInMbs)
{
if ( (IS_I_SLICE(sliceType) && (pStorage->intraConcealmentFlag == 0)) ||
refData == NULL)
H264SwDecMemset(currImage->data, 128, width*height*384);
else
H264SwDecMemcpy(currImage->data, refData, width*height*384);
pStorage->numConcealedMbs = pStorage->picSizeInMbs;
/* no filtering if whole picture concealed */
for (i = 0; i < pStorage->picSizeInMbs; i++)
pStorage->mb[i].disableDeblockingFilterIdc = 1;
return(HANTRO_OK);
}
/* start from the row containing the first correct macroblock, conceal the
* row in question, all rows above that row and then continue downwards */
mb = pStorage->mb + row * width;
for (j = col; j--;)
{
ConcealMb(mb+j, currImage, row, j, sliceType, refData);
mb[j].decoded = 1;
pStorage->numConcealedMbs++;
}
for (j = col + 1; j < width; j++)
{
if (!mb[j].decoded)
{
ConcealMb(mb+j, currImage, row, j, sliceType, refData);
mb[j].decoded = 1;
pStorage->numConcealedMbs++;
}
}
/* if previous row(s) could not be concealed -> conceal them now */
if (row)
{
for (j = 0; j < width; j++)
{
i = row - 1;
mb = pStorage->mb + i*width + j;
do
{
ConcealMb(mb, currImage, i, j, sliceType, refData);
mb->decoded = 1;
pStorage->numConcealedMbs++;
mb -= width;
} while(i--);
}
}
/* process rows below the one containing the first correct macroblock */
for (i = row + 1; i < height; i++)
{
mb = pStorage->mb + i * width;
for (j = 0; j < width; j++)
{
if (!mb[j].decoded)
{
ConcealMb(mb+j, currImage, i, j, sliceType, refData);
mb[j].decoded = 1;
pStorage->numConcealedMbs++;
}
}
}
return(HANTRO_OK);
}
/*------------------------------------------------------------------------------
Function name: ConcealMb
Functional description:
Perform error concealment for one macroblock, location of the
macroblock in the picture indicated by row and col
------------------------------------------------------------------------------*/
u32 ConcealMb(mbStorage_t *pMb, image_t *currImage, u32 row, u32 col,
u32 sliceType, u8 *refData)
{
/* Variables */
u32 i, j, comp;
u32 hor, ver;
u32 mbNum;
u32 width, height;
u8 *mbPos;
u8 data[384];
u8 *pData;
i32 tmp;
i32 firstPhase[16];
i32 *pTmp;
/* neighbours above, below, left and right */
i32 a[4] = { 0,0,0,0 }, b[4], l[4] = { 0,0,0,0 }, r[4];
u32 A, B, L, R;
#ifdef H264DEC_OMXDL
u8 fillBuff[32*21 + 15 + 32];
u8 *pFill;
#endif
/* Code */
ASSERT(pMb);
ASSERT(!pMb->decoded);
ASSERT(currImage);
ASSERT(col < currImage->width);
ASSERT(row < currImage->height);
#ifdef H264DEC_OMXDL
pFill = ALIGN(fillBuff, 16);
#endif
width = currImage->width;
height = currImage->height;
mbNum = row * width + col;
h264bsdSetCurrImageMbPointers(currImage, mbNum);
mbPos = currImage->data + row * 16 * width * 16 + col * 16;
A = B = L = R = HANTRO_FALSE;
/* set qpY to 40 to enable some filtering in deblocking (stetson value) */
pMb->qpY = 40;
pMb->disableDeblockingFilterIdc = 0;
/* mbType set to intra to perform filtering despite the values of other
* boundary strength determination fields */
pMb->mbType = I_4x4;
pMb->filterOffsetA = 0;
pMb->filterOffsetB = 0;
pMb->chromaQpIndexOffset = 0;
if (IS_I_SLICE(sliceType))
H264SwDecMemset(data, 0, sizeof(data));
else
{
mv_t mv = {0,0};
image_t refImage;
refImage.width = width;
refImage.height = height;
refImage.data = refData;
if (refImage.data)
{
#ifndef H264DEC_OMXDL
h264bsdPredictSamples(data, &mv, &refImage, col*16, row*16,
0, 0, 16, 16);
#else
h264bsdPredictSamples(data, &mv, &refImage,
((row*16) + ((col*16)<<16)),
0x00001010, pFill);
#endif
h264bsdWriteMacroblock(currImage, data);
return(HANTRO_OK);
}
else
H264SwDecMemset(data, 0, sizeof(data));
}
H264SwDecMemset(firstPhase, 0, sizeof(firstPhase));
/* counter for number of neighbours used */
j = 0;
hor = ver = 0;
if (row && (pMb-width)->decoded)
{
A = HANTRO_TRUE;
pData = mbPos - width*16;
a[0] = *pData++; a[0] += *pData++; a[0] += *pData++; a[0] += *pData++;
a[1] = *pData++; a[1] += *pData++; a[1] += *pData++; a[1] += *pData++;
a[2] = *pData++; a[2] += *pData++; a[2] += *pData++; a[2] += *pData++;
a[3] = *pData++; a[3] += *pData++; a[3] += *pData++; a[3] += *pData++;
j++;
hor++;
firstPhase[0] += a[0] + a[1] + a[2] + a[3];
firstPhase[1] += a[0] + a[1] - a[2] - a[3];
}
if ((row != height - 1) && (pMb+width)->decoded)
{
B = HANTRO_TRUE;
pData = mbPos + 16*width*16;
b[0] = *pData++; b[0] += *pData++; b[0] += *pData++; b[0] += *pData++;
b[1] = *pData++; b[1] += *pData++; b[1] += *pData++; b[1] += *pData++;
b[2] = *pData++; b[2] += *pData++; b[2] += *pData++; b[2] += *pData++;
b[3] = *pData++; b[3] += *pData++; b[3] += *pData++; b[3] += *pData++;
j++;
hor++;
firstPhase[0] += b[0] + b[1] + b[2] + b[3];
firstPhase[1] += b[0] + b[1] - b[2] - b[3];
}
if (col && (pMb-1)->decoded)
{
L = HANTRO_TRUE;
pData = mbPos - 1;
l[0] = pData[0]; l[0] += pData[16*width];
l[0] += pData[32*width]; l[0] += pData[48*width];
pData += 64*width;
l[1] = pData[0]; l[1] += pData[16*width];
l[1] += pData[32*width]; l[1] += pData[48*width];
pData += 64*width;
l[2] = pData[0]; l[2] += pData[16*width];
l[2] += pData[32*width]; l[2] += pData[48*width];
pData += 64*width;
l[3] = pData[0]; l[3] += pData[16*width];
l[3] += pData[32*width]; l[3] += pData[48*width];
j++;
ver++;
firstPhase[0] += l[0] + l[1] + l[2] + l[3];
firstPhase[4] += l[0] + l[1] - l[2] - l[3];
}
if ((col != width - 1) && (pMb+1)->decoded)
{
R = HANTRO_TRUE;
pData = mbPos + 16;
r[0] = pData[0]; r[0] += pData[16*width];
r[0] += pData[32*width]; r[0] += pData[48*width];
pData += 64*width;
r[1] = pData[0]; r[1] += pData[16*width];
r[1] += pData[32*width]; r[1] += pData[48*width];
pData += 64*width;
r[2] = pData[0]; r[2] += pData[16*width];
r[2] += pData[32*width]; r[2] += pData[48*width];
pData += 64*width;
r[3] = pData[0]; r[3] += pData[16*width];
r[3] += pData[32*width]; r[3] += pData[48*width];
j++;
ver++;
firstPhase[0] += r[0] + r[1] + r[2] + r[3];
firstPhase[4] += r[0] + r[1] - r[2] - r[3];
}
/* at least one properly decoded neighbour available */
ASSERT(j);
/*lint -esym(644,l,r,a,b) variable initialized above */
if (!hor && L && R)
firstPhase[1] = (l[0]+l[1]+l[2]+l[3]-r[0]-r[1]-r[2]-r[3]) >> 5;
else if (hor)
firstPhase[1] >>= (3+hor);
if (!ver && A && B)
firstPhase[4] = (a[0]+a[1]+a[2]+a[3]-b[0]-b[1]-b[2]-b[3]) >> 5;
else if (ver)
firstPhase[4] >>= (3+ver);
switch (j)
{
case 1:
firstPhase[0] >>= 4;
break;
case 2:
firstPhase[0] >>= 5;
break;
case 3:
/* approximate (firstPhase[0]*4/3)>>6 */
firstPhase[0] = (21 * firstPhase[0]) >> 10;
break;
default: /* 4 */
firstPhase[0] >>= 6;
break;
}
Transform(firstPhase);
for (i = 0, pData = data, pTmp = firstPhase; i < 256;)
{
tmp = pTmp[(i & 0xF)>>2];
/*lint -e734 CLIP1 macro results in value that fits into 8 bits */
*pData++ = CLIP1(tmp);
/*lint +e734 */
i++;
if (!(i & 0x3F))
pTmp += 4;
}
/* chroma components */
mbPos = currImage->data + width * height * 256 +
row * 8 * width * 8 + col * 8;
for (comp = 0; comp < 2; comp++)
{
H264SwDecMemset(firstPhase, 0, sizeof(firstPhase));
/* counter for number of neighbours used */
j = 0;
hor = ver = 0;
if (A)
{
pData = mbPos - width*8;
a[0] = *pData++; a[0] += *pData++;
a[1] = *pData++; a[1] += *pData++;
a[2] = *pData++; a[2] += *pData++;
a[3] = *pData++; a[3] += *pData++;
j++;
hor++;
firstPhase[0] += a[0] + a[1] + a[2] + a[3];
firstPhase[1] += a[0] + a[1] - a[2] - a[3];
}
if (B)
{
pData = mbPos + 8*width*8;
b[0] = *pData++; b[0] += *pData++;
b[1] = *pData++; b[1] += *pData++;
b[2] = *pData++; b[2] += *pData++;
b[3] = *pData++; b[3] += *pData++;
j++;
hor++;
firstPhase[0] += b[0] + b[1] + b[2] + b[3];
firstPhase[1] += b[0] + b[1] - b[2] - b[3];
}
if (L)
{
pData = mbPos - 1;
l[0] = pData[0]; l[0] += pData[8*width];
pData += 16*width;
l[1] = pData[0]; l[1] += pData[8*width];
pData += 16*width;
l[2] = pData[0]; l[2] += pData[8*width];
pData += 16*width;
l[3] = pData[0]; l[3] += pData[8*width];
j++;
ver++;
firstPhase[0] += l[0] + l[1] + l[2] + l[3];
firstPhase[4] += l[0] + l[1] - l[2] - l[3];
}
if (R)
{
pData = mbPos + 8;
r[0] = pData[0]; r[0] += pData[8*width];
pData += 16*width;
r[1] = pData[0]; r[1] += pData[8*width];
pData += 16*width;
r[2] = pData[0]; r[2] += pData[8*width];
pData += 16*width;
r[3] = pData[0]; r[3] += pData[8*width];
j++;
ver++;
firstPhase[0] += r[0] + r[1] + r[2] + r[3];
firstPhase[4] += r[0] + r[1] - r[2] - r[3];
}
if (!hor && L && R)
firstPhase[1] = (l[0]+l[1]+l[2]+l[3]-r[0]-r[1]-r[2]-r[3]) >> 4;
else if (hor)
firstPhase[1] >>= (2+hor);
if (!ver && A && B)
firstPhase[4] = (a[0]+a[1]+a[2]+a[3]-b[0]-b[1]-b[2]-b[3]) >> 4;
else if (ver)
firstPhase[4] >>= (2+ver);
switch (j)
{
case 1:
firstPhase[0] >>= 3;
break;
case 2:
firstPhase[0] >>= 4;
break;
case 3:
/* approximate (firstPhase[0]*4/3)>>5 */
firstPhase[0] = (21 * firstPhase[0]) >> 9;
break;
default: /* 4 */
firstPhase[0] >>= 5;
break;
}
Transform(firstPhase);
pData = data + 256 + comp*64;
for (i = 0, pTmp = firstPhase; i < 64;)
{
tmp = pTmp[(i & 0x7)>>1];
/*lint -e734 CLIP1 macro results in value that fits into 8 bits */
*pData++ = CLIP1(tmp);
/*lint +e734 */
i++;
if (!(i & 0xF))
pTmp += 4;
}
/* increment pointers for cr */
mbPos += width * height * 64;
}
h264bsdWriteMacroblock(currImage, data);
return(HANTRO_OK);
}
/*------------------------------------------------------------------------------
Function name: Transform
Functional description:
Simplified transform, assuming that only dc component and lowest
horizontal and lowest vertical component may be non-zero
------------------------------------------------------------------------------*/
void Transform(i32 *data)
{
u32 col;
i32 tmp0, tmp1;
if (!data[1] && !data[4])
{
data[1] = data[2] = data[3] = data[4] = data[5] =
data[6] = data[7] = data[8] = data[9] = data[10] =
data[11] = data[12] = data[13] = data[14] = data[15] = data[0];
return;
}
/* first horizontal transform for rows 0 and 1 */
tmp0 = data[0];
tmp1 = data[1];
data[0] = tmp0 + tmp1;
data[1] = tmp0 + (tmp1>>1);
data[2] = tmp0 - (tmp1>>1);
data[3] = tmp0 - tmp1;
tmp0 = data[4];
data[5] = tmp0;
data[6] = tmp0;
data[7] = tmp0;
/* then vertical transform */
for (col = 4; col--; data++)
{
tmp0 = data[0];
tmp1 = data[4];
data[0] = tmp0 + tmp1;
data[4] = tmp0 + (tmp1>>1);
data[8] = tmp0 - (tmp1>>1);
data[12] = tmp0 - tmp1;
}
}
/*lint +e702 */