前言:
海思多媒体处理平台(MPP)分为:视频输入(VI),视频处理(VPSS),视频编码(VENC),视频解码(VDEC),视频输出(VO)、视频侦测分析(VDA),音频输入(AI),音频输出(AO),音频编码(AENC),音频解码(ADEC),区域管理(REGION)等模块. 这里介绍视频解码(VDEC)和视频输出模块(VO)。
VO与自己的显示设备密切相关,在使用VO之前,应该了解自己的显示设备的接口方式及时序等信息。
说明:
测试使用开发板的配置:
- Hisi:Hi3521A
- AD :NVP6134C 一个
- 摄像头:720P,1080P
- 显示:HDMI总线,1080P屏。
Hi3521A VO:
- 1个高清设备DHD0,视频层VHD0最多支持16画面。
- 支持PIP(Picture In Picture)画面叠加,PIP 视频层最多支持 1 画面
- 最大输出时序:1920x1080@60或者1600x1200@60
HDMI:
海思设备的HDMI开发,可以参考官方提供的《HDMI 开发参考.pdf》,其基本内容有:
HDMI 的音频不能单独输出,必须依赖于视频输出,且 HDMI 的时钟来源于 VO 的时钟,因此接口调用顺序上需要先使能 VO,再调用 HDMI 接口,然后配置音视频输出。此外 Hi35xx 芯片内置的 HDMI 不支持 HDCP、CEC 功能。
正确加载驱动之后,直接调用SAMPLE_COMM_VO_HdmiStart 函数就可以启动HDMI接口。
注意,在调试HDMI的时候,因为HDMI的引脚可以被复用成普通GPIO口,所以如果调试不通的时候,可以先检查一下HDMI引脚的配置。
视频解码:
这里不关联视频输入VI,直接使用一个H264文件来模拟h264视频流,
(a)数据流
- 一般的视频里面封装有视频和音频,现在主流使用的视频压缩格式有H264,H265,MGPEG等格式。我们这里只介绍H264格式。
- 海思sample里面是用一个1080P的h264文件来产生h264数据流。
- 模拟数据流主要的是要寻找到h264每一帧数据的开始位置和结束位置,然后将一帧完整的数据发送到解码器里面去。
- 可以有两种方式的去找到一帧完整的数据
(2)一次读一个字节数据
每次读取一个自己数据,找到帧的开始位置,然后再找到下一帧开始的位置,中间就是一帧完整的数据
- 优点:可以不用预设一帧数据的大小
- 缺点:读数据耗费的时间长。
(3)一次多一大包数据
先根据视频文件的分辨率设置一帧数据的最大空间大小,将这个值作为最小空间一次从文件中读取出来。再从这包数据中找出帧的开始位置和结束位置,下次读取的时候按结束位置来读取,剩下的数据包不用处理。
- 优点:读取文件速度快
- 缺点:需要预先知道一帧数据最大空间
海思sample_vdec 中使用的就是方法2,代码如下:
else if ( (pstVdecThreadParam->s32StreamMode==VIDEO_MODE_FRAME) && (pstVdecThreadParam->enType == PT_H264) )
{
bFindStart = HI_FALSE;
bFindEnd = HI_FALSE;
/**每次偏移到下一帧开始的位置**/
fseek(fpStrm, s32UsedBytes, SEEK_SET);
s32ReadLen = fread(pu8Buf, 1, pstVdecThreadParam->s32MinBufSize, fpStrm);
if (s32ReadLen == 0)
{
if (pstVdecThreadParam->bLoopSend)
{
s32UsedBytes = 0;
fseek(fpStrm, 0, SEEK_SET);
s32ReadLen = fread(pu8Buf, 1, pstVdecThreadParam->s32MinBufSize, fpStrm);
}
else
{
break;
}
}
/**查找开始位置**/
for (i=0; i<s32ReadLen-5; i++)
{
if ( pu8Buf[i] == 0 && pu8Buf[i+1] == 0 && pu8Buf[i+2] == 1 &&
( (pu8Buf[i+3]&0x1F) == 0x5 || (pu8Buf[i+3]&0x1F) == 0x1 ) &&
( (pu8Buf[i+4]&0x80) == 0x80)
)
{
bFindStart = HI_TRUE;
i += 4;
break;
}
}
/**查找结束位置,也就是下一个开始帧的标志**/
for (; i<s32ReadLen-5; i++)
{
if ( pu8Buf[i ] == 0 && pu8Buf[i+1] == 0 && pu8Buf[i+2] == 1 &&
( ((pu8Buf[i+3]&0x1F) == 0x7) || ((pu8Buf[i+3]&0x1F) == 0x8) || ((pu8Buf[i+3]&0x1F) == 0x6)
|| (((pu8Buf[i+3]&0x1F) == 0x5 || (pu8Buf[i+3]&0x1F) == 0x1) &&((pu8Buf[i+4]&0x80) == 0x80))
)
)
{
bFindEnd = HI_TRUE;
break;
}
}
if(i > 0) s32ReadLen = i;
if (bFindStart == HI_FALSE)
{
printf("SAMPLE_TEST: chn %d can not find start code!s32ReadLen %d, s32UsedBytes %d. \n",
pstVdecThreadParam->s32ChnId, s32ReadLen, s32UsedBytes);
}
else if (bFindEnd == HI_FALSE)
{
s32ReadLen = i+5;
}
}
解码显示:
解码模块基本上参考手册上的接口说明就可以实现。
这里将将4个h264文件模拟成数据流输入到解码模块, 将解码模块与输出模块绑定,在显示屏中以4分屏显示4路画面,效果如下:
在海思Hi3521A 设备上实时解码4路1080P@30fps 从画面上看不会出现卡顿问题,但是CPU占用率非常高。
本章频测工程可以从「目录与序言」提供的地址去获取
本专栏第一篇文章「目录与序言」列出了专栏的完整目录,按目录顺序阅读,有助于你的理解。