MT6739 Android 8.1 modifies the mtkcam of HAL layer to avoid the camera cutting and stretching the unsupported resolution

Posted by itsmeArry on Mon, 13 Jan 2020 10:22:23 +0100

MT6739 Android 8.1 modifies the mtkcam of HAL layer to avoid the camera cutting and stretching the unsupported resolution

CVBS camera is used in the project, and then mipi camera is used. NTSC and PAL cameras are used. Interlaced scanning has a special resolution. 720x240960x240 x240x240x240x2720x288960x288, when preview, the picture will be cut and stretched!

Through the log trace code, the actual preview uses 1280x720 to preview, that is, the ratio of 16:9. Therefore, in the actual test, the cameras of AHD720p and AHD1080p are displayed normally. The two cameras are progressive scanning, and the resolution is 1280x720 and 1920x1080, both of which are 16:9.
The following log shows the software setting 1280x720 resolution to preview:

The logic of the code here is to set a preview resolution, and then go to the list of resolutions supported by preview to check whether it is supported,

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" is the set preview resolution. The default is 640x480. When starting preview, the software is set to 1280x720, and then it is judged that 1280x720 is in the "preview size values" support list. This resolution is used. If not, calculate and use the closest! The correlation functions are as follows:
File: 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();	//Calculate whether the set resolution is supported here. If not, select the closest one
        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;
}

File: vendor / mediator / 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;
}

After the resolution of preview is determined to be 1280x720, in fact, the display ratio is mainly 16:9. The real cutting is to have a special thread to do this when you want to preview after getting every frame of data.

The calling relationship here is threadLoopUpdate – > enquepass2 – > getpass2info – > calCrop. Finally, it determines the cutting resolution. In the "calCrop" method, the related functions are as follows:

File: 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;

}

File: 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;
}

File: 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
                    {
                    	//Here we get the resolution of the Preview settings and the resolution of the hardware data, and then call "calCrop" to calculate the clipping.
                        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;
}

The following method is used to calculate the clipping function:
File: 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;
}

The preview resolution is 1280x720, the hardware data resolution is 960x240, and the resolution after calculation and clipping is 426x240, the same as that printed by log:

426 / 240 = 1.775, very close to 16:9!

Then modify this function to use the hardware data resolution directly instead of cutting, as follows:

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);

	//Ignore the inappropriate clipping of image scale, and directly use the hardware data resolution
	rCrop.w = rSrc.w;
	rCrop.h = rSrc.h;
    //
    rCrop.x = (rSrc.w - rCrop.w) / 2;
    rCrop.y = (rSrc.h - rCrop.h) / 2;

    return rCrop;
}

After modification, compile and run, the image can be displayed completely. See log printing:

The original resolution is used!

In this way, the image can be displayed completely. As for whether the image is deformed after being displayed, it depends on the scale of the image range when the image sensor samples itself and the scale used to display the LCD.

The NTSC standard is 720x480. Because of interlaced scanning, the height becomes 240, but the actual image range is still 720x480. The resolution horizontal direction seen in the log above is 960 because the CVBS to mipi chip increases the sampling rate, but the image range function of the sensor does not change, 720 / 480 = 1.5, which is actually displayed on the LCD, but also displayed in 1024x600 scale, 1024 / 600 = 1 . 71, so the image will be slightly deformed; the display of AHD with 16 / 9 = 1.78 will be closer to 1024 / 600 = 1.71, and the deformation will be smaller!

There's another problem above that I don't understand. NTSC is interlaced scanning, and it's in the form of even and odd frames. The data on the hardware is 960x240, but the actual even and odd frames are 960x480 when they are combined. In other words, they should be 960x480 after they are combined when preview ing! The platform has been opened Eliminating even and odd frame interlace jitter , but in fact, the preview is 960x240, but the screen display is complete. The relationship between them is still unclear for the time being. There is an opportunity to study it in detail later!

Published 11 original articles, won praise 4, visited 40000+
Private letter follow

Topics: Android