MT6739 Android 8.1 修改HAL層mtkcam,避免camera對不支持的分辨率做裁剪拉伸
項目使用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,但是畫面顯示又是完整的,這之間的關係暫時還是沒有搞明白,後面有機會再詳細研究!