海思多媒体(MPP)开发,获取VI中的YUV数据

海思多媒体(MPP)开发,获取VI中的YUV数据 Asura 2023-11-07 15:38:57 538

前言:

海思多媒体处理平台(MPP)分为:视频输入(VI),视频处理(VPSS),视频编码(VENC),视频解码(VDEC),视频输出(VO)、视频侦测分析(VDA),音频输入(AI),音频输出(AO),音频编码(AENC),音频解码(ADEC),区域管理(REGION)等模块. 这里介绍视频输入模块(VI)中的直接获取YUV数据。

介绍:

在正常的视频编码和视频输出的使用中,我们都是将VI通道绑定到其它的模块去,为其它的模块提供图像数据流。如果应用程序需要获取摄像头的原始YUV数据的时候,数据并不是从VO或是VENC模块去获取,海思有为这种使用场景预留接口,直接从VI获取图像原始数据。其中主要使用到这两个接口:HI_MPI_VI_SetFrameDepth HI_MPI_VI_GetFrame

HI_MPI_VI_GetFrame:

设置可获取的 VI 图像最大深度。

HI_S32 HI_MPI_VI_SetFrameDepth(VI_CHN ViChn, HI_U32 u32Depth);

此接口用于设置某一 VI 通道缓存的视频图像帧数。当用户设置缓存多帧视频图像时,用户可以获取到一定数目的连续图像数据。

注意事项:

(1)若指定 u32Depth 为 0,表示不需要系统为该 VI 通道缓存图像,故用户获取不到该VI 通道图像数据。系统默认不为 VI 通道缓存图像,即 u32Depth 默认为 0。

(2)若指定 u32Depth>0,系统将为该 VI 通道缓存 u32Depth 个图像,用户可通过接口HI_MPI_VI_GetFrame 获取该 VI 通道图像数据。具体分为以下几种情况。

  • 用户一直不获取图像。系统将自动更新最旧的图像数据,保证用户一旦开始获取,就可获取到最近最新的 u32Depth 个连续图像。
  • 用户连续获取 u32Depth 次并一直不释放。系统因获取不到 VB 而自动停止缓存新的 VI 图像,用户也不能获取新的 VI 图像。故建议用户保证获取和释放接口配对使用。
  • 用户获取/释放的速度比 VI 通道产生图像的速度慢。系统将自动更新用户仍未获取的最旧的图像数据,保证缓存的图像队列为最近的新 VI 图像。由于用户不能保证获取速度,导致获取的可能不是连续图像。

(3)系统为每个 VI 通道缓存的 u32Depth 个 VI 图像数据,占用 MPP 内部的缓存块(简称 VB)。故用户应通过接口 HI_MPI_VB_SetConf 设置足够的 VB,否则可能由于系统缓存图像占用过多 VB,从而影响 VI 的正常图像采集,导致用户获取不到 VI 图像数据。

支持动态调整 u32Depth。如用户在不需要获取该 VI 通道数据时,可设置 u32Depth 为 0,以减少 VI 通道占用内部 VB 的数量;在需要获取时,再设置 u32Depth 为合适的值,即可获取到设置时刻后的连续 VI 图像。

HI_MPI_VI_GetFrame

获取 VI 采集的图像。

HI_S32 HI_MPI_VI_GetFrame(VI_CHN ViChn, VIDEO_FRAME_INFO_S *pstFrameInfom,HI_S32 s32MilliSec);    

注意事项

  1. 此接口可以获取指定 VI 通道的视频图像信息。图像信息主要包括:图像的宽度、高度、像素格式、时间戳以及 YUV 各分量的物理地址。
  2. 此接口需在通道已启用后才有效。
  3. 支持多次获取后再释放,但建议获取和释放接口配对使用。
  4. 获取的物理地址信息来自 MPP 内部使用的 VideoBuffer,因此使用完之后,必须要调用 HI_MPI_VI_ReleaseFrame 接口释放其内存。
  5. pstFrameInfo -> stVFrame .u32PhyAddr[0]和 pstFrameInfo ->stVFrame .u32PhyAddr[1]分别指向图像的亮度分量和色度分量的物理地址。

实例介绍:

(一)初始化VI通道

要从VI模块里面获取视频数据,首先需要初始化AD外设,然后初始化VI中的通道。

/******************************************************** 
Function:    BIAO_Get_VI_Frame
Description: 初始化VI  ,然后直接获取YUV数据
Input:  none
OutPut: none
Return: 0: success,none 0:error
Others: 
Author: Caibiao Lee
Date:   2020-02-02
*********************************************************/
int BIAO_Get_VI_Frame(void)
{
    SAMPLE_VI_MODE_E enViMode = SAMPLE_VI_MODE_2_720P;
    VIDEO_NORM_E enNorm = VIDEO_ENCODING_MODE_PAL;

    HI_U32 u32ViChnCnt = 4;
    HI_S32 s32VpssGrpCnt = 4;
    
    VB_CONF_S stVbConf;   

    HI_S32 i;
    HI_S32 s32Ret = HI_SUCCESS;
    HI_U32 u32BlkSize;
    HI_CHAR ch;
    SIZE_S stSize;
    HI_U32 u32WndNum;

    /******************************************
     step  1: init variable 
    ******************************************/
    memset(&stVbConf,0,sizeof(VB_CONF_S));
    u32BlkSize = SAMPLE_COMM_SYS_CalcPicVbBlkSize(enNorm,\
                PIC_HD720, SAMPLE_PIXEL_FORMAT, SAMPLE_SYS_ALIGN_WIDTH,COMPRESS_MODE_SEG);
    stVbConf.u32MaxPoolCnt = 128;

    /* video buffer*/
    //todo: vb=15
    stVbConf.astCommPool[0].u32BlkSize = u32BlkSize;
    stVbConf.astCommPool[0].u32BlkCnt = u32ViChnCnt * 8;

    /******************************************
     step 2: mpp system init. 
    ******************************************/
    s32Ret = SAMPLE_COMM_SYS_Init(&stVbConf);
    if (HI_SUCCESS != s32Ret)
    {
        SAMPLE_PRT("system init failed with %d!\n", s32Ret);
        goto ERROR_0;
    }

    /******************************************
     step 3: start vi dev & chn
    ******************************************/
    s32Ret = SAMPLE_COMM_VI_Start(enViMode, enNorm);
    if (HI_SUCCESS != s32Ret)
    {
        SAMPLE_PRT("start vi failed!\n");
        goto ERROR_1;
    }

    BIAO_MPP_GET_VI_Frame(0);

ERROR_1: 
    SAMPLE_COMM_VI_Stop(enViMode);
    
ERROR_0:    
    SAMPLE_COMM_SYS_Exit();

}

(二)获取YUV数据

使用mpp系统接口获取YUV数据,然后将它们保存成文件:

/******************************************************** 
Function:    BIAO_MPP_GET_VI_Frame
Description: 循环获取YUV数据
Input:  s32ViChn 通道号
OutPut: none
Return: 0: success,none 0:error
Others: 
Author: Caibiao Lee
Date:   2020-02-02
*********************************************************/
void BIAO_MPP_GET_VI_Frame(int s32ViChn)
{

    int s32Ret=-1;
    unsignedint u32Depth;
    unsignedint i = 10;
    unsignedint j = 0;
    unsignedint u32Size = 0;
    VIDEO_FRAME_INFO_S stFrameInfom;
    HI_S32 s32MilliSec;
    FILE * l_Fp = NULL;
    unsignedchar *l_pUserAddr;

    s32Ret=HI_MPI_VI_GetFrameDepth(s32ViChn, &u32Depth);

    if(s32Ret!=0)
    {
        printf("%s %d HI_MPI_VI_GetFrameDepth(%d) err=%#x\n",__FUNCTION__,__LINE__,
            s32ViChn, s32Ret);
        return-1;
    }

    if(u32Depth==0)
    {
        s32Ret=HI_MPI_VI_SetFrameDepth(s32ViChn, 1);
        if(s32Ret!=0)
        {
            printf("%s %d HI_MPI_VI_SetFrameDepth(%d) err=%#x\n",
                __FUNCTION__,__LINE__, s32ViChn, s32Ret);
            return-2;
        }
    }

    i=10;
    while(i--)
    {
        s32Ret=HI_MPI_VI_GetFrame(s32ViChn, &stFrameInfom, s32MilliSec);
        if(s32Ret!=0)
        {
            printf("%s %d HI_MPI_VI_GetFrame(%d) err=%#x\n",__FUNCTION__,__LINE__,
                s32ViChn, s32Ret);
             usleep(100000);
            //return -3;
            continue;
        }

        if(i!=1)
        {
            sleep(1);
            continue;
        }
        
        printf("u32Width        = 0x%x \n",stFrameInfom.stVFrame.u32Width);
        printf("u32Height       = 0x%x \n",stFrameInfom.stVFrame.u32Height);
        printf("u32Field        = 0x%x \n",stFrameInfom.stVFrame.u32Field);
        printf("enPixelFormat   = 0x%x \n",stFrameInfom.stVFrame.enPixelFormat);
        printf("enVideoFormat   = 0x%x \n\n",stFrameInfom.stVFrame.enVideoFormat);

        for(j=0;j<3;j++)
        {
            printf("j = %d u32PhyAddr    = 0x%x \n",j,stFrameInfom.stVFrame.u32PhyAddr[j]);
            printf("j = %d pVirAddr        = 0x%x \n",j,stFrameInfom.stVFrame.pVirAddr[j]);
            printf("j = %d u32Stride       = 0x%x \n",j,stFrameInfom.stVFrame.u32Stride[j]);
            printf("j = %d u32HeaderPhyAddr= 0x%x \n",j,stFrameInfom.stVFrame.u32HeaderPhyAddr[j]);
            printf("j = %d pHeaderVirAddr  = 0x%x \n",j,stFrameInfom.stVFrame.pHeaderVirAddr[j]);
            printf("j = %d u32HeaderStride = 0x%x\n\n",j,stFrameInfom.stVFrame.u32HeaderStride[j]); 
        }

        printf("s16OffsetBottom = 0x%x \n",stFrameInfom.stVFrame.s16OffsetBottom);
        printf("s16OffsetLeft   = 0x%x \n",stFrameInfom.stVFrame.s16OffsetLeft);
        printf("s16OffsetRight  = 0x%x \n",stFrameInfom.stVFrame.s16OffsetRight);
        printf("u64pts          = 0x%x \n",stFrameInfom.stVFrame.u64pts);
        printf("u32TimeRef      = 0x%x \n",stFrameInfom.stVFrame.u32TimeRef);
        printf("u32PrivateData  = 0x%x \n",stFrameInfom.stVFrame.u32PrivateData); 
        printf("enFlashType     = 0x%x\n\n",stFrameInfom.stVFrame.stSupplement.enFlashType);

        
#if 0
        l_Fp = fopen("yuv420.yuv","w+");
        if(NULL==l_Fp)
        {
            printf("%s %d file open error \n",__FUNCTION__,__LINE__);
            break;
        }

        /**Y 分量**/
        u32Size = stFrameInfom.stVFrame.u32Stride[0]*stFrameInfom.stVFrame.u32Height;
        s32Ret = fwrite(stFrameInfom.stVFrame.u32PhyAddr[0],1,u32Size,l_Fp);
        if(s32Ret!=u32Size)
        {
            fclose(l_Fp);
            printf("%s %d fwrite file error %d \n",__FUNCTION__,__LINE__,s32Ret);
            break;
        }else
        {
            printf("%s %d write file len = %d \n",__FUNCTION__,__LINE__,s32Ret);
        }


        /**UV 分量**/
        u32Size = stFrameInfom.stVFrame.u32Stride[1]*stFrameInfom.stVFrame.u32Height;
        s32Ret = fwrite(stFrameInfom.stVFrame.u32PhyAddr[1],1,u32Size,l_Fp);
        if(s32Ret!=u32Size)
        {
            fclose(l_Fp);
            printf("%s %d fwrite file error %d \n",__FUNCTION__,__LINE__,s32Ret);
            break;
        }else
        {
            printf("%s %d write file len = %d \n",__FUNCTION__,__LINE__,s32Ret);
        }

        fclose(l_Fp);
#endif
   
       u32Size = stFrameInfom.stVFrame.u32Stride[0]*stFrameInfom.stVFrame.u32Height*3/2; 
       l_pUserAddr =(unsignedchar *)HI_MPI_SYS_Mmap(stFrameInfom.stVFrame.u32PhyAddr[0], u32Size);
       if(NULL!=l_pUserAddr)
       {
            
           l_Fp = fopen("yuv420.yuv","w+");
           if(NULL==l_Fp)
           {
               printf("%s %d file open error \n",__FUNCTION__,__LINE__);
               break;
           }
                      
           s32Ret = fwrite(l_pUserAddr,1,u32Size,l_Fp);
           if(s32Ret!=u32Size)
           {
               fclose(l_Fp);
               printf("%s %d fwrite file error %d \n",__FUNCTION__,__LINE__,s32Ret);
               break;
           }else
           {
               printf("%s %d write file len = %d \n",__FUNCTION__,__LINE__,s32Ret);
           }

           HI_MPI_SYS_Munmap(l_pUserAddr, u32Size);
           fclose(l_Fp);
       }
 
        HI_MPI_VI_ReleaseFrame(s32ViChn, &stFrameInfom);

        break;
        
        //usleep(100000);

    }

    return0;

}

这里有几个点非常重要:

(1)如果VI缓存图像深度为0,需要根据需求设置该深度值。

(2)HI_MPI_VI_GetFrame获取到图像数据的地址是物理地址,但是我们应用程序的地址需要的是虚拟地址,所以需要将物理地址转换为虚拟地址之后才能使用,否则读取不到数据。需要使用HI_MPI_SYS_Mmap 接口映射虚拟地址和HI_MPI_SYS_Munmap解除映射。

(3)stFrameInfom.stVFrame.u32PhyAddr[0] 存放的是图像的亮度分量,stFrameInfom.stVFrame.u32PhyAddr[1]存放的是色读分量。他们两个分量的地址虽然是分开给出的,但是他们的物理地址是连续分布的,所以一次可以将他们两个分量一起进行内存映射。

(4)设备刚启动的时候,有可能VI里面还没有数据,第一次获取数据可能会返回0xa010800e 错误码,表示没有数据。

(5)刚启动获取设备的时候,可能获取到的图像是空的,也就是图像全是黑的画面,从后面几帧开始就正常了。

上面代码运行的结果如下:

BIAO_MPP_GET_VI_Frame 352 HI_MPI_VI_GetFrame(0) err=0xa010800e
u32Width        = 0x500
u32Height       = 0x2d0
u32Field        = 0x4
enPixelFormat   = 0x17
enVideoFormat   = 0x0

j = 0 u32PhyAddr    = 0x92243c00
j = 0 pVirAddr        = 0x0
j = 0 u32Stride       = 0x500
j = 0 u32HeaderPhyAddr= 0x0
j = 0 pHeaderVirAddr  = 0x0
j = 0 u32HeaderStride = 0x0

j = 1 u32PhyAddr    = 0x92324c00
j = 1 pVirAddr        = 0x0
j = 1 u32Stride       = 0x500
j = 1 u32HeaderPhyAddr= 0x0
j = 1 pHeaderVirAddr  = 0x0
j = 1 u32HeaderStride = 0x0

j = 2 u32PhyAddr    = 0x0
j = 2 pVirAddr        = 0x0
j = 2 u32Stride       = 0x0
j = 2 u32HeaderPhyAddr= 0x0
j = 2 pHeaderVirAddr  = 0x0
j = 2 u32HeaderStride = 0x0

s16OffsetBottom = 0x0
s16OffsetLeft   = 0x0
s16OffsetRight  = 0x0
u64pts          = 0x1
u32TimeRef      = 0x164
u32PrivateData  = 0x0
enFlashType     = 0x0

BIAO_MPP_GET_VI_Frame 448 write file len = 1382400
program exit normally!

从上面结果可以看出:

  • (1)VI输入的图像分辨率是1280720;(0x5000x2d0)
  • (2)像素格式为0x17对应海思的PIXEL_FORMAT_E中的PIXEL_FORMAT_YUV_SEMIPLANAR_420
  • (3)图像跨距u32Stride = 0x500 ,这里要注意下,跨距有可能与图像宽度不一样,跨距表示图像宽度加上图像附加数据,在没有附件数据的时候,他与图形的宽度时一样的。

因为图像是YUV420格式的,所以Y分量的大小等于图像的大小,YU分量是图像分量的1/2。YUV420图像数据的分布如下:

易百纳社区

按照上面介绍的参数,使用pYUV工具查看我们程序生成的文件yuv420.yuv,显示如下:

易百纳社区

声明:本文内容由易百纳平台入驻作者撰写,文章观点仅代表作者本人,不代表易百纳立场。如有内容侵权或者其他问题,请联系本站进行删除。
Asura
红包 点赞 1 评论 打赏
评论
0个
内容存在敏感词
手气红包
    易百纳技术社区暂无数据
相关专栏
置顶时间设置
结束时间
删除原因
  • 广告/SPAM
  • 恶意灌水
  • 违规内容
  • 文不对题
  • 重复发帖
打赏作者
易百纳技术社区
Asura
您的支持将鼓励我继续创作!
打赏金额:
¥1易百纳技术社区
¥5易百纳技术社区
¥10易百纳技术社区
¥50易百纳技术社区
¥100易百纳技术社区
支付方式:
微信支付
支付宝支付
易百纳技术社区微信支付
易百纳技术社区
打赏成功!

感谢您的打赏,如若您也想被打赏,可前往 发表专栏 哦~

举报反馈

举报类型

  • 内容涉黄/赌/毒
  • 内容侵权/抄袭
  • 政治相关
  • 涉嫌广告
  • 侮辱谩骂
  • 其他

详细说明

审核成功

发布时间设置
发布时间:
是否关联周任务-专栏模块

审核失败

失败原因
备注
拼手气红包 红包规则
祝福语
恭喜发财,大吉大利!
红包金额
红包最小金额不能低于5元
红包数量
红包数量范围10~50个
余额支付
当前余额:
可前往问答、专栏板块获取收益 去获取
取 消 确 定

小包子的红包

恭喜发财,大吉大利

已领取20/40,共1.6元 红包规则

    易百纳技术社区