我的初衷是在骁龙820的CAMERA驱动HAL层加上水印,以验证自己学习Android驱动开发的学习成果。在开始试验的初期,参考了Android视频添加时间水印一文。非常感谢作者给了我最初的方向。因为Android版本和芯片版本的原因,文中有一些地方需要补充和修订:
和一般的YUV420_NV12格式不同,NV12_UBWC的数据并非是从左到右,从上到下线性排列的。经过反复试验,终于发现它的排列规律。
NV12_UBWC是按宏块顺序排列的,以方便多核、多视频流的处理。不同于H.264的16X16像素的宏块尺寸,NV12_UBWC的宏块尺寸是8X32像素的。一个宏块有8行32列。每16个宏块有一个完整的排列顺序,之后16个开始重复前16个的顺序。可以把这16个宏块成为一个Slice。一个Slice里的宏块可以按8个分为1组,第二组的顺序和第一组完全相反。每一组的尺寸是4X2像素。16个宏块的连续顺序是:
第一组第一列 | 第一组第二列 | 第二组第一列 | 第二组第一列 |
---|---|---|---|
MB1 | MB8 | MB11 | MB14 |
MB7 | MB2 | MB13 | MB12 |
MB4 | MB5 | MB10 | MB15 |
MB6 | MB3 | MB16 | MB9 |
如果单独看每一个4X2大小的组,它的排列顺序是这样的:
偶数组第一列 | 偶数组第二列 |
---|---|
MB1 | MB8 |
MB7 | MB2 |
MB4 | MB5 |
MB6 | MB3 |
奇数组第一列 | 奇数组第二列 |
---|---|
MB3 | MB6 |
MB5 | MB4 |
MB2 | MB7 |
MB8 | MB1 |
以上两个表格的奇偶遵循C,C++的一般计数规则,从0开始计数。所以偶数组比奇数组先出现。
对于我所试验的CAMERA PREVIEW场景,其分辨率是640X480,所以整个屏幕包含有15X10个(32X64像素的)Slice,它们是按从上到下,从左到右的顺序在内存中排列的。
在一个8X32像素的宏块内部,按照4X4像素的尺寸组成2X8=16个子块。宏块内子块与子块之间从左到右,从上到下排列:
第一列 | 第二列 | 第三列 | 第四列 | 第五列 | 第六列 | 第七列 | 第八列 |
---|---|---|---|---|---|---|---|
SB1 | SB2 | SB3 | SB4 | SB5 | SB6 | SB7 | SB8 |
SB9 | SB10 | SB11 | SB12 | SB13 | SB14 | SB15 | SB16 |
每一个子块内部的像素,也是从左到右,从上到下的顺序排列
第一列 | 第二列 | 第三列 | 第四列 |
---|---|---|---|
PX1 | PX2 | PX3 | PX4 |
PX5 | PX6 | PX7 | PX8 |
PX9 | PX10 | PX11 | PX12 |
PX13 | PX14 | PX15 | PX15 |
cam_frame_len_offset_t offset;
memset(&offset, 0, sizeof(cam_frame_len_offset_t));
cam_dimension_t dim;
memset(&dim, 0, sizeof(dim));
pStream->getFrameDimension(dim);
pStream->getFrameOffset(offset);
int width = dim.width; //offset.mp[0].scanline; //stride; //dim.width;
int height = dim.height; //offset.mp[0].stride; //scanline; //dim.height;
unsigned char *_ptr;
_ptr = (unsigned char*)pFrame->buffer;
cam_format_t fmt;
mParameters.getStreamFormat(CAM_STREAM_TYPE_PREVIEW,fmt);
QCameraMemory *previewMemObj = (QCameraMemory *)pFrame->mem_info;
camera_memory_t *preview_mem = NULL;
if (previewMemObj != NULL) {
preview_mem = previewMemObj->getMemory(pFrame->buf_idx, false);
}
LOGI("getStreamFormat = %d, ptr = %u, mem = %u", fmt, _ptr, preview_mem);
LOGI("Offset.meta_len %d, offset %d, buffer %u",offset.mp[0].meta_len,offset.mp[0].offset, _ptr);
LOGI("len %u, Offset.stride %d, width %d, height %u",offset.mp[0].len, offset.mp[0].stride,width, height);
LOGI("meta_stride %d, meta_scanline %d %u",offset.mp[0].meta_stride,offset.mp[0].meta_scanline);
LOGI("frame.len %d, buf_type %d",pFrame->frame_len, pFrame->buf_type);
int imageWidth = logo_width;
int imageHeight = logo_height;
uint8_t nEvenOrder[4][2] = {1, 8, 7, 2, 4, 5, 6, 3};
uint8_t nOddOrder[4][2] = {3, 6, 5, 4, 2, 7, 8, 1};
uint8_t nBlockWidth = 64;
uint8_t nBlockHeight = 32;
uint8_t nSubBlockWidth = 32;
uint8_t nSubBlockHeight = 8;
uint8_t nImgXBlocks = imageWidth/nBlockWidth;
uint8_t nImgYBlocks = imageHeight/nBlockHeight;
uint8_t nImgXRest = imageWidth%nSubBlockWidth;
uint8_t nImgYRest = imageHeight%nSubBlockHeight;
uint8_t nInnerBlockWidth =4;
uint8_t nInnerBlockHeight = 4;
uint8_t nInnerBlockSize = nInnerBlockWidth * nInnerBlockHeight;
uint8_t nInnerBlockXNum = 8;
uint8_t nInnerBlockYNum = 2;
//My logo size is 236 * 67, so I don't want to calculate imageWidth%nBlockWidth and height rest
_ptr += offset.mp[0].offset + offset.mp[0].meta_len;
uint8_t aSrcImage[sizeof(gImage_logo)];
uint32_t nDstIndex;
uint32_t nSrcIndex;
uint32_t order;
uint32_t nBlockStride = nBlockHeight * width; // for preview, width is 640, or 10 times of block width.
memcpy(aSrcImage, gImage_logo, sizeof(gImage_logo));
for(int i = 0; i < imageWidth * imageHeight; i++)
if(aSrcImage[ i ] < 0x80)
aSrcImage[ i ] = 0;
else
aSrcImage[ i ] = 0xFF;
#if 1
for(int i = 0; i < nImgYBlocks; i++)
{
for(int j = 0; j < nImgXBlocks; j++)
{
for(int k = 0; k < 4; k++) //Y count of subblock in a macro block
for(int m = 0; m < 2; m++) //X count of subblock in a macro block
{
if(j%2)
order = j * 8 + nOddOrder[k][m] - 1;
else
order = j * 8 + nEvenOrder[k][m] - 1;
nDstIndex = i * nBlockStride + order * nSubBlockWidth * nSubBlockHeight;
nSrcIndex = i * nBlockHeight * imageWidth + k * nSubBlockHeight * imageWidth + j * nBlockWidth + m * nSubBlockWidth;
//for(int n = 0; n < nSubBlockHeight; n++)
for(int n = 0; n < nInnerBlockYNum; n++)
{
nSrcIndex += n * nInnerBlockHeight * imageWidth;
for( int p = 0; p < nInnerBlockXNum; p++) //inner block
{
//memcpy(_ptr + nDstIndex, &gImage_logo[nSrcIndex], nSubBlockWidth);
//nDstIndex += nSubBlockWidth;
//nSrcIndex += imageWidth;
uint32_t nSrcOffset = nSrcIndex + p * nInnerBlockWidth;
for( int q = 0; q < nInnerBlockHeight; q++)
{
memcpy(_ptr + nDstIndex, &aSrcImage[nSrcOffset], nInnerBlockWidth);
nDstIndex += nInnerBlockWidth;
nSrcOffset += imageWidth;
}
}
}
}
}
}
#else