项目使用CVBS摄像头,然后转mipi,会用到NTSC和PAL制式的摄像头,隔行扫描,分辨率比较特殊,720x240,960x240,720x288,960x288,preview的时候,画面会被裁剪拉伸!
通过log追代码,实际preview的时候是以1280x720去预览的,就是16:9的比例,所以实际测试,AHD720p和AHD1080p的摄像头都是正常显示的,这两个摄像头是逐行扫描,分辨率是1280x720和1920x1080,都是16:9的比例。
下面log显示软件设置1280x720分辨率去预览:
这里代码的逻辑就是,设置一个preview分辨率,然后去preview支持的分辨率列表里查询是否支持,
preview-size=640x480;
preview-size-values=176x144,320x240,352x288,432x320,480x320,480x368,640x480,720x480,728x480,782x480,800x480,854x480,800x600,864x480,888x540,960x540,1280x720,1280x960;
“preview-size”就是设置的预览分辨率,默认是640x480,start preview的时候,软件设置为1280x720,然后判断1280x720在支持列表“preview-size-values”里的,就用这个分辨率了。如果不支持,就计算并使用一个最接近的!相关函数如下:
文件:vendor/mediatek/proprietary/hardware/mtkcam/legacy/v1/common/paramsmgr/params/ParamsManager.cpp
status_t
ParamsManager::
setParameters(String8 const& paramsIn)
{
......
// Check to see if Preview Size Changes or not. Get old preview size
if(camParams.get(CameraParameters::KEY_PREVIEW_SIZE))
{
Size oldPrvSize, tmpPrvSize, newPrvSize;
mParameters.getPreviewSize(&oldPrvSize.width, &oldPrvSize.height);
camParams.getPreviewSize(&tmpPrvSize.width, &tmpPrvSize.height);
// Update Parameter: preview size
mParameters.setPreviewSize(tmpPrvSize.width, tmpPrvSize.height);
updatePreviewSize(); //在这里去计算设置的分辨率是否支持,不支持的话选一个最接近的
mParameters.getPreviewSize(&newPrvSize.width, &newPrvSize.height);
MY_LOGD_IF(mEnableDebugLog && ( oldPrvSize.width != newPrvSize.width || oldPrvSize.height != newPrvSize.height ),
"Preview Size change: %dx%d/%dx%d -> (%dx%d)",
oldPrvSize.width, oldPrvSize.height,
tmpPrvSize.width, tmpPrvSize.height,
newPrvSize.width, newPrvSize.height
);
camParams.remove(CameraParameters::KEY_PREVIEW_SIZE);
}
......
return status;
}
文件:vendor/mediatek/proprietary/hardware/mtkcam/legacy/v1/common/paramsmgr/params/ParamsManager.update.cpp
bool
ParamsManager::
updatePreviewSize()
{
// Update preview size to mParameters.
MY_LOGD_IF(mEnableDebugLog, "+");
Vector<Size> prvSizes;
Size oriPrvSize, prvSize, candPrvSize;
int prvSizeDiff = 0;
int diffRatio = 0, diffSize = 0;
int candRatio = 0, candSize = 0;
//
mParameters.getPreviewSize(&oriPrvSize.width, &oriPrvSize.height);
mParameters.getSupportedPreviewSizes(prvSizes);
if(prvSizes.size() < 1) {
const char *previewSizeValues = mParameters.get(CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES);
MY_LOGE("Please check %s: %s", CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES, (previewSizeValues)?previewSizeValues:"NULL");
return false;
}
//
/**************************************************************************
* In normal case, preview width is bigger than preview height. (prvSizeDiff [w-h] is bigger than 0)
* If preview width <= preview height, (prvSizeDiff <= 0), switch preview w/h.
**************************************************************************/
prvSizeDiff = oriPrvSize.width - oriPrvSize.height;
prvSize.width = (prvSizeDiff > 0) ? oriPrvSize.width : oriPrvSize.height;
prvSize.height = (prvSizeDiff > 0) ? oriPrvSize.height : oriPrvSize.width;
//
/**************************************************************************
* src : original preview size (w, h)
* old : candidate preview size (wc, hc)
* new: for each preview size in supported list (w', h')
*
* | w/h - wc/hc | - | w/h - w'/h' | => | w*hc*h' - wc*h*h' | - | w*hc*h' - w'*h*hc |
*
**************************************************************************/
candPrvSize = prvSizes[0]; // default candidate preview size
candRatio = abs(prvSize.width*candPrvSize.height*prvSizes[0].height - prvSize.height*prvSizes[0].height*candPrvSize.width); // default diff of ratio
candSize = abs(prvSize.width*prvSize.height - prvSizes[0].width*prvSizes[0].height); // default diff of size
//
bool err = false;
for ( unsigned int idx = 0; idx < prvSizes.size(); ++idx )
{
diffRatio = abs(prvSize.width*candPrvSize.height*prvSizes[idx].height - prvSize.height*candPrvSize.height*prvSizes[idx].width);
candRatio = abs(prvSize.width*candPrvSize.height*prvSizes[idx].height - prvSize.height*prvSizes[idx].height*candPrvSize.width);
diffSize = abs(prvSize.width*prvSize.height - prvSizes[idx].width*prvSizes[idx].height);
//
if ( 0 == diffRatio && 0 == diffSize)
{
// prvSize is in supported preview size list.
err = true;
break;
}
//
if (diffRatio < candRatio)
{
candSize = diffSize;
candPrvSize = prvSizes[idx];
}
else if ( diffRatio == candRatio && diffSize < candSize)
{
candSize = diffSize;
candPrvSize = prvSizes[idx];
}
}
if(!err) {
/**************************************************************************
* If preview size does not in supported preview size list, choose the best preview size
* in supported preview size list. Check if original preview width is bigger than height,
* (prvSizeDiff > 0) if not, switch preview w/h back.
**************************************************************************/
if ( prvSizeDiff > 0 )
{
MY_LOGW("new prvSize(%dx%d)", candPrvSize.width, candPrvSize.height);
mParameters.setPreviewSize(candPrvSize.width, candPrvSize.height);
}
else
{
MY_LOGW("new prvSize(%dx%d)", candPrvSize.height, candPrvSize.width);
mParameters.setPreviewSize(candPrvSize.height, candPrvSize.width);
}
}
MY_LOGD_IF(mEnableDebugLog, "-");
return true;
}
确定了preview的分辨率1280x720之后,其实主要是显示的比例16:9,真正做裁剪是在拿到每一帧数据之后,要preview的时候,专门有个线程做这事情。
这里的调用关系是 threadLoopUpdate–>enquePass2–>getPass2Info–>calCrop,最后决定裁剪的分辨率大小,在“calCrop”这个方法里面,相关函数如下:
文件:vendor/mediatek/proprietary/hardware/mtkcam/legacy/platform/mt6739/core/camnode/pass2node.preview.cpp
MBOOL
PrvPass2::
threadLoopUpdate()
{
MY_LOGV("++");
MBOOL ret = MTRUE;
// featurepipe init optimization
if( doInitialization() )
{
goto lbExit;
}
#if MULTI_FRAME_ENABLE
ret = enquePass2(MTRUE);
#else
#if PASS2_CALLBACL_ENABLE
PostBufInfo postBufData;
{
Mutex::Autolock lock(mLock);
if( mlPostBufData.size() == 0 ) {
MY_LOGE("no posted buf");
return MFALSE;
}
postBufData = mlPostBufData.front();
mlPostBufData.pop_front();
}
ret = enquePass2(postBufData.data, postBufData.buf, postBufData.ext);
#else
// use p2 thread to deque
ret = dequePass2();
#endif
#endif
lbExit:
MY_LOGV("--");
return ret;
}
文件:vendor/mediatek/proprietary/hardware/mtkcam/legacy/platform/mt6739/core/camnode/pass2node.common.cpp
MBOOL
Pass2NodeImpl::
enquePass2(MBOOL const doCallback)
{
......
MUINT32 index = 0;
vector<p2data>::const_iterator pData = vP2data.begin();
while( pData != vP2data.end() )
{
......
if( pData->doCrop )
{
mpIspSyncCtrlHw->getPass2Info(
src.mBuffer,
pData->dstSize,
magicNum,
pPrivateData,
privateDataSize,
p2InCrop);
if(mbUseSelfDefCrop)
{
p2InCrop.p_fractional.x = p2InCrop.p_fractional.y = 0;
p2InCrop.p_integral = mSelfDefCrop.p;
p2InCrop.s = mSelfDefCrop.s;
MY_LOGD("p2InCrop: (%d.%d,%d.%d),(%dx%d)",
p2InCrop.p_integral.x,p2InCrop.p_fractional.x,
p2InCrop.p_integral.y,p2InCrop.p_fractional.y,
p2InCrop.s.w,p2InCrop.s.h);
}
}
else
{
......
}
......
}
......
return MTRUE;
}
文件:vendor/mediatek/proprietary/hardware/mtkcam/legacy/platform/mt6739/core/camnode/IspSyncControl.cpp
MBOOL
IspSyncControlImp::
getPass2Info(
IImageBuffer* inpImgBuf,
MSize inOutImgSize,
MUINT32& outMagicNum,
MVOID*& outpPrivateData,
MUINT32& outPrivateDataSize,
MCropRect& outInCrop)
{
......
//
for(iterMetadata = mlPass1Metadata.begin(); iterMetadata != mlPass1Metadata.end(); iterMetadata++)
{
if((*iterMetadata).pImgBuf == inpImgBuf)
{
outMagicNum = (*iterMetadata).magicNum;
outpPrivateData = (*iterMetadata).metadata.mPrivateData;
outPrivateDataSize = (*iterMetadata).metadata.mPrivateDataSize;
//
for(iterOutSize = mlPass1OutSize.begin(); iterOutSize != mlPass1OutSize.end(); iterOutSize++)
{
if((*iterOutSize).magicNum == (*iterMetadata).magicNum)
{
if((*iterMetadata).isRrzo)
{
......
}
else
{
//这里得到预览设置的分辨率和硬件数据拿到的分辨率,然后调用“calCrop”计算裁剪
NSCamHW::Rect SrcRect(0, 0, mSensorSize.w, mSensorSize.h);
NSCamHW::Rect DstRect(0, 0, inOutImgSize.w, inOutImgSize.h);
NSCamHW::Rect CropRect = MtkCamUtils::calCrop(SrcRect, DstRect, (*iterOutSize).targetZoomRatio);
//
outInCrop.p_integral.x = CropRect.x;
outInCrop.p_integral.y = CropRect.y;
outInCrop.s.w = ALIGN_UP_SIZE(CropRect.w, 2);
outInCrop.s.h = ALIGN_UP_SIZE(CropRect.h, 2);
}
//
eisEnable = (*iterMetadata).eisCrop.enable;
//
break;
}
}
//
if(iterOutSize == mlPass1OutSize.end())
{
MY_LOGE("Can't find # 0x%X",outMagicNum);
AEE_ASSERT("Can't find magic number");
}
//
break;
}
}
......
return MTRUE;
}
下面这个方法就是计算裁剪的函数:
文件:vendor/mediatek/proprietary/hardware/mtkcam/legacy/platform/mt6739/hwutils/HwMisc.cpp
NSCamHW::Rect calCrop(NSCamHW::Rect const &rSrc, NSCamHW::Rect const &rDst, uint32_t ratio)
{
NSCamHW::Rect rCrop;
// srcW/srcH < dstW/dstH
if (rSrc.w * rDst.h < rDst.w * rSrc.h) {
rCrop.w = rSrc.w;
rCrop.h = rSrc.w * rDst.h / rDst.w;
}
//srcW/srcH > dstW/dstH
else if (rSrc.w * rDst.h > rDst.w * rSrc.h) {
rCrop.w = rSrc.h * rDst.w / rDst.h;
rCrop.h = rSrc.h;
}
else {
rCrop.w = rSrc.w;
rCrop.h = rSrc.h;
}
//
rCrop.w = ROUND_TO_2X(rCrop.w * 100 / ratio);
rCrop.h = ROUND_TO_2X(rCrop.h * 100 / ratio);
//
rCrop.x = (rSrc.w - rCrop.w) / 2;
rCrop.y = (rSrc.h - rCrop.h) / 2;
return rCrop;
}
预览分辨率1280x720,硬件数据分辨率是960x240,计算裁剪之后的分辨率是426x240,和log打印出来的一样:
426/240=1.775,非常接近16:9!
那么修改这个函数,不去做裁剪,直接使用硬件数据分辨率,如下:
NSCamHW::Rect calCrop(NSCamHW::Rect const &rSrc, NSCamHW::Rect const &rDst, uint32_t ratio)
{
NSCamHW::Rect rCrop;
// srcW/srcH < dstW/dstH
if (rSrc.w * rDst.h < rDst.w * rSrc.h) {
rCrop.w = rSrc.w;
rCrop.h = rSrc.w * rDst.h / rDst.w;
}
//srcW/srcH > dstW/dstH
else if (rSrc.w * rDst.h > rDst.w * rSrc.h) {
rCrop.w = rSrc.h * rDst.w / rDst.h;
rCrop.h = rSrc.h;
}
else {
rCrop.w = rSrc.w;
rCrop.h = rSrc.h;
}
//
rCrop.w = ROUND_TO_2X(rCrop.w * 100 / ratio);
rCrop.h = ROUND_TO_2X(rCrop.h * 100 / ratio);
//忽略掉图像比例不合适的裁剪,直接使用硬件数据分辨率
rCrop.w = rSrc.w;
rCrop.h = rSrc.h;
//
rCrop.x = (rSrc.w - rCrop.w) / 2;
rCrop.y = (rSrc.h - rCrop.h) / 2;
return rCrop;
}
修改之后,编译运行,就可以完全把图像显示出来了,看log打印:
使用的是原始分辨率了!
这样就可以把图像完整显示出来了,至于显示出来之后,图像是否变形,这个就要看图像sensor本身采样的时候的图像范围的比例,以及显示到LCD的是用什么比例显示的了。
NTSC标准是720x480,由于隔行扫描,所以高度变为240,但实际看到的图像范围还是720x480的比例,上面log看到的分辨率水平方向是960是因为CVBS转mipi芯片增加了采样率,但是sensor的图像范围函数没有变,720/480=1.5,实际在LCD上显示,又是用的1024x600的比例显示的,1024/600=1.71,所以,图像稍微会有点变形;AHD的16/9=1.78的显示,就会比较接近1024/600=1.71,这个形变就会小一点!
上面还有一个问题点没搞明白,NTSC是隔行扫描,是奇偶帧格式,硬件上面数据过来是960x240,但实际奇偶帧合并起来是960x480,按说preview的时候,应该是合并之后的960x480!平台已经开启了消除奇偶帧隔行扫描抖动,但是实际看preview还是960x240,但是画面显示又是完整的,这之间的关系暂时还是没有搞明白,后面有机会再详细研究!