MT6739 Android 8.1 修改HAL層mtkcam,避免camera對不支持的分辨率做裁剪拉伸

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,但是畫面顯示又是完整的,這之間的關係暫時還是沒有搞明白,後面有機會再詳細研究!

發佈了11 篇原創文章 · 獲贊 4 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章