海思多媒体(MPP)开发,获取VI中的YUV数据
前言:
海思多媒体处理平台(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);
注意事项
- 此接口可以获取指定 VI 通道的视频图像信息。图像信息主要包括:图像的宽度、高度、像素格式、时间戳以及 YUV 各分量的物理地址。
- 此接口需在通道已启用后才有效。
- 支持多次获取后再释放,但建议获取和释放接口配对使用。
- 获取的物理地址信息来自 MPP 内部使用的 VideoBuffer,因此使用完之后,必须要调用 HI_MPI_VI_ReleaseFrame 接口释放其内存。
- 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,显示如下:
- 分享
- 举报
-
浏览量:5276次2020-07-31 11:54:44
-
浏览量:3259次2020-08-03 11:10:18
-
浏览量:2796次2020-08-04 20:08:51
-
浏览量:4990次2020-07-31 11:03:23
-
浏览量:2458次2020-08-04 20:15:40
-
浏览量:941次2024-01-11 15:54:09
-
浏览量:2890次2020-11-06 17:53:15
-
浏览量:3516次2020-07-30 11:57:30
-
2020-10-21 11:35:55
-
浏览量:10512次2020-08-03 14:27:03
-
浏览量:6119次2019-12-28 10:35:51
-
浏览量:1233次2024-02-22 15:52:02
-
浏览量:789次2022-12-26 08:56:18
-
浏览量:1256次2023-10-13 10:07:11
-
浏览量:5253次2020-08-11 10:30:44
-
浏览量:1461次2024-01-25 16:21:59
-
浏览量:8640次2022-10-17 20:30:19
-
2023-06-12 14:35:32
-
浏览量:9676次2022-09-27 10:22:54
-
广告/SPAM
-
恶意灌水
-
违规内容
-
文不对题
-
重复发帖
Asura
感谢您的打赏,如若您也想被打赏,可前往 发表专栏 哦~
举报类型
- 内容涉黄/赌/毒
- 内容侵权/抄袭
- 政治相关
- 涉嫌广告
- 侮辱谩骂
- 其他
详细说明