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分辨率去预览:
MT6739 Android 8.1 修改HAL层mtkcam,避免camera对不支持的分辨率做裁剪拉伸_第1张图片
这里代码的逻辑就是,设置一个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,但是画面显示又是完整的,这之间的关系暂时还是没有搞明白,后面有机会再详细研究!

你可能感兴趣的:(技术随笔,mtk,android,驱动程序)