前言
最近开始接触Hi3559,开启一段新的学习之旅,在这里简单记录一下学习过程以及遇到的问题
一、MPP是什么?
MPP是海思提供的媒体处理软件平台(Media Process Platform,简称 MPP),包括视频输入(VI)、视频 处理(VPSS)、视频编码(VENC)、视频解码(VDEC)、视频输出(VO)、视频拼接 (AVS)、音频输入(AI)、音频输出(AO)、音频编码(AENC)、音频解码(ADEC)、区域管理(REGION)等模块。
该平台对应用软件屏蔽了芯片相关的复杂的底层处理,并对应用软件直接提供 MPI(MPP Program Interface)接口完成相应功能。
更多关于MPP的详细介绍在官方的说明文档《HiMPP V4.0 媒体处理软件开发参考.pdf》中
- 应用程序启动 MPP 业务前,必须完成 MPP 系统初始化工作
- 应用程序退出 MPP 业务后,也要完成 MPP 系统去初始化工作,释放资源
二、系统绑定(Bind)
- 通过数据接收者绑定数据源来建立两者之间的关联关系(只允许数据接收者绑定数据源)。绑定后,数据源生成的数据将自动发送给接收者。
有点像QT的信号和槽的机制?
三、工作模式
VI 和 VPSS 各自的工作模式分为在线,离线,并行模式。
VI
- 在线模式:VI_CAP 与 VI_PROC 之间在线 数据流传输,此模式下 VI_CAP 不会写出 RAW 数据到 DDR, 而是直接把数据流送给 VI_PROC;
- 离线模式:VI_CAP 写出 RAW 数据到DDR,然后 VI_PROC 从 DDR 读取 RAW 数据进行后处理;
- 并行模式:当对接大数据量的时序,,需要 VI_CAP 与 两个 VI_PROC 处于并行模式,VI_CAP 直接把一帧数据送给两 个 VI_PROC 并行处理。
VPSS
-在线模式:VI_PROC 与 VPSS 之间的在线数据流传输,在此模式下 VI_PROC 不会写出 YUV 数据到 DDR,而 是直接把数据流送给 VPSS;
- 离线模式:VI_PROC 写出 YUV 数据到DDR,然后 VPSS 从 DDR 读取YUV 数据进行后处理;
- 并行模式:当对接大数据量的时序,需要 VI_CAP 与 两个 VI_PROC 处于并行模式, 同时两个 VPSS 也分别与 VI_PROC 处于并行模式, VI_CAP 直接把一帧数据送给两 个 VI_PROC 并行处理,再给 VPSS 并行处理。
VI_CAP模块可以接收并处理多达8个摄像头的输入信号,同时支持多种图像格式,包括YUV、RGB等。
VI_PROC模块可用于图像处理、视频编码、人脸识别等应用场景。能够增强图像的质量,使得用户能够用更高的效率处理数据。
四、代码实例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include "sample_comm.h"
/******************************************************************************
* function : to process abnormal case
******************************************************************************/
void SAMPLE_VIO_HandleSig(HI_S32 signo)
{
//当捕获到异常信号后触发的自定义函数如下,主要是对信号的重新定义以及系统退出资源的释放
if (SIGINT == signo || SIGTERM == signo)
{
SAMPLE_COMM_All_ISP_Stop();
SAMPLE_COMM_SYS_Exit();
printf("\033[0;31mprogram termination abnormally!\033[0;39m\n");
}
exit(-1);
}
HI_S32 SAMPLE_VIO_StartViVo(SAMPLE_VI_CONFIG_S* pstViConfig, SAMPLE_VO_CONFIG_S* pstVoConfig)
{
HI_S32 s32Ret;
s32Ret = SAMPLE_COMM_VI_StartVi(pstViConfig);
if (HI_SUCCESS != s32Ret)
{
SAMPLE_PRT("start vi failed!\n");
return s32Ret;
}
s32Ret = SAMPLE_COMM_VO_StartVO(pstVoConfig);
if (HI_SUCCESS != s32Ret)
{
SAMPLE_PRT("SAMPLE_VIO start VO failed with %#x!\n", s32Ret);
goto EXIT;
}
return s32Ret;
EXIT:
//VO失败了需要把VI一同停止
SAMPLE_COMM_VI_StopVi(pstViConfig);
return s32Ret;
}
HI_S32 SAMPLE_VIO_StopViVo(SAMPLE_VI_CONFIG_S* pstViConfig, SAMPLE_VO_CONFIG_S* pstVoConfig)
{
SAMPLE_COMM_VO_StopVO(pstVoConfig);
SAMPLE_COMM_VI_StopVi(pstViConfig);
return HI_SUCCESS;
}
HI_S32 SAMPLE_VIO_4K30_FreeRotation(VO_INTF_TYPE_E enVoIntfType)
{
// Embeded isp, FreeRotation.
HI_S32 s32Ret; //返回值
VI_DEV ViDev = 0;
VI_PIPE ViPipe = 0;
VI_CHN ViChn = 0;
HI_S32 s32WorkSnsId = 0;
VO_DEV VoDev = SAMPLE_VO_DEV_DHD0;
VO_CHN VoChn = 0;
SIZE_S stSize; //结构体,宽、高
VB_CONFIG_S stVbConf; //视频缓存池属性结构体,
PIC_SIZE_E enPicSize = PIC_3840x2160; //图片分辨率,也就是定义的视频的每一帧的分辨率
HI_U32 u32BlkSize;
VI_ROTATION_EX_ATTR_S stViRotationExAttr = {
0}; // VI的任意角度旋转属性
SAMPLE_VI_CONFIG_S stViConfig = {
0}; //设置VI模块属性的结构体
SAMPLE_VO_CONFIG_S stVoConfig = {
0}; //设置VO模块属性的结构体
combo_dev_t ComboDev;
DYNAMIC_RANGE_E enDynamicRange = DYNAMIC_RANGE_SDR8; //动态范围
PIXEL_FORMAT_E enPixFormat = PIXEL_FORMAT_YVU_SEMIPLANAR_420; //像素格式类型
VIDEO_FORMAT_E enVideoFormat = VIDEO_FORMAT_LINEAR; //视频格式类型
COMPRESS_MODE_E enCompressMode = COMPRESS_MODE_NONE; //视频压缩数据格式
VI_VPSS_MODE_E enMastPipeMode = VI_ONLINE_VPSS_OFFLINE; //VI PIPE 和 VPSS 组的工作模式
/************************************************
step1: Get all sensors information
*************************************************/
SAMPLE_COMM_VI_GetSensorInfo(&stViConfig);
ComboDev = SAMPLE_COMM_VI_GetComboDevBySensor(stViConfig.astViInfo[s32WorkSnsId].stSnsInfo.enSnsType, s32WorkSnsId);
stViConfig.s32WorkingViNum = 1;
stViConfig.as32WorkingViId[0] = 0;
stViConfig.astViInfo[0].stSnsInfo.MipiDev = ComboDev;
stViConfig.astViInfo[0].stSnsInfo.s32BusId = 0;
stViConfig.astViInfo[0].stDevInfo.ViDev = ViDev;
stViConfig.astViInfo[0].stDevInfo.enWDRMode = WDR_MODE_NONE;
stViConfig.astViInfo[0].stPipeInfo.enMastPipeMode = enMastPipeMode;
stViConfig.astViInfo[0].stPipeInfo.aPipe[0] = ViPipe;
stViConfig.astViInfo[0].stPipeInfo.aPipe[1] = -1;
stViConfig.astViInfo[0].stPipeInfo.aPipe[2] = -1;
stViConfig.astViInfo[0].stPipeInfo.aPipe[3] = -1;
stViConfig.astViInfo[0].stChnInfo.ViChn = ViChn;
stViConfig.astViInfo[0].stChnInfo.enPixFormat = enPixFormat;
stViConfig.astViInfo[0].stChnInfo.enDynamicRange = enDynamicRange;
stViConfig.astViInfo[0].stChnInfo.enVideoFormat = enVideoFormat;
stViConfig.astViInfo[0].stChnInfo.enCompressMode = enCompressMode;
/************************************************
step2: Get input size
*************************************************/
s32Ret = SAMPLE_COMM_VI_GetSizeBySensor(stViConfig.astViInfo[s32WorkSnsId].stSnsInfo.enSnsType, &enPicSize);
if (HI_SUCCESS != s32Ret)
{
SAMPLE_PRT("SAMPLE_COMM_VI_GetSizeBySensor failed!\n");
return s32Ret;
}
s32Ret = SAMPLE_COMM_SYS_GetPicSize(enPicSize, &stSize);
if (HI_SUCCESS != s32Ret)
{
SAMPLE_PRT("SAMPLE_COMM_SYS_GetPicSize failed!\n");
return s32Ret;
}
/************************************************
step3: Init SYS and common VB(视频缓存池处理模块)
*************************************************/
hi_memset(&stVbConf, sizeof(VB_CONFIG_S), 0, sizeof(VB_CONFIG_S));
stVbConf.u32MaxPoolCnt = 2;
u32BlkSize = COMMON_GetPicBufferSize(stSize.u32Width, stSize.u32Height, SAMPLE_PIXEL_FORMAT, DATA_BITWIDTH_10, COMPRESS_MODE_SEG, DEFAULT_ALIGN);
stVbConf.astCommPool[0].u64BlkSize = u32BlkSize;
stVbConf.astCommPool[0].u32BlkCnt = 10;
u32BlkSize = VI_GetRawBufferSize(stSize.u32Width, stSize.u32Height, PIXEL_FORMAT_RGB_BAYER_16BPP, COMPRESS_MODE_NONE, 32);
stVbConf.astCommPool[1].u64BlkSize = u32BlkSize;
stVbConf.astCommPool[1].u32BlkCnt = 4;
s32Ret = SAMPLE_COMM_SYS_Init(&stVbConf);
if (HI_SUCCESS != s32Ret)
{
SAMPLE_PRT("system init failed with %d!\n", s32Ret);
SAMPLE_COMM_SYS_Exit();
return s32Ret;
}
s32Ret = SAMPLE_COMM_VI_SetParam(&stViConfig);
if (HI_SUCCESS != s32Ret)
{
SAMPLE_COMM_SYS_Exit();
return s32Ret;
}
/************************************************
step4: Init VI and VO
*************************************************/
SAMPLE_COMM_VO_GetDefConfig(&stVoConfig);
s32Ret = SAMPLE_VIO_StartViVo(&stViConfig, &stVoConfig);
if (HI_SUCCESS != s32Ret)
{
SAMPLE_PRT("SAMPLE_VIO_StartViVo failed witfh %d\n", s32Ret);
goto EXIT;
}
/************************************************
step5: Bind VI and VO
*************************************************/
s32Ret = SAMPLE_COMM_VI_Bind_VO(ViPipe, ViChn, VoDev, VoChn);
if (HI_SUCCESS != s32Ret)
{
SAMPLE_PRT("SAMPLE_COMM_VI_Bind_VO failed with %#x!\n", s32Ret);
goto EXIT1;
}
stViRotationExAttr.bEnable = HI_TRUE;
stViRotationExAttr.stRotationEx.enViewType = ROTATION_VIEW_TYPE_ALL; //旋转模式,全模式
stViRotationExAttr.stRotationEx.s32CenterXOffset = 0; //畸变中心点相对图象中心点水平偏移
stViRotationExAttr.stRotationEx.s32CenterYOffset = 0; //畸变中心点相对图象中心点垂直偏移
stViRotationExAttr.stRotationEx.u32Angle = 90; //旋转的角度
stViRotationExAttr.stRotationEx.stDestSize.u32Width = stSize.u32Width;
stViRotationExAttr.stRotationEx.stDestSize.u32Height = stSize.u32Height;
s32Ret = HI_MPI_VI_SetChnRotationEx(ViPipe,ViChn,&stViRotationExAttr); //设置 VI 的任意角度旋转属性
if (HI_SUCCESS != s32Ret)
{
SAMPLE_PRT("HI_MPI_VI_SetChnRotationEx failed witfh %d\n", s32Ret);
goto EXIT;
}
printf("now FreeAngle Rotation on and enViewType ALL, press any key to switch enViewType TYPICAL!\n");
getchar();
// 更改模式
stViRotationExAttr.bEnable = HI_TRUE;
stViRotationExAttr.stRotationEx.enViewType = ROTATION_VIEW_TYPE_TYPICAL; //典型模式
stViRotationExAttr.stRotationEx.s32CenterXOffset = 0;
stViRotationExAttr.stRotationEx.s32CenterYOffset = 0;
stViRotationExAttr.stRotationEx.u32Angle = 90;
stViRotationExAttr.stRotationEx.stDestSize.u32Width = stSize.u32Width;
stViRotationExAttr.stRotationEx.stDestSize.u32Height = stSize.u32Height;
s32Ret = HI_MPI_VI_SetChnRotationEx(ViPipe,ViChn,&stViRotationExAttr);
if (HI_SUCCESS != s32Ret)
{
SAMPLE_PRT("HI_MPI_VI_SetChnRotationEx failed witfh %d\n", s32Ret);
goto EXIT;
}
printf("now FreeAngle Rotation on, press any key to switch enViewType INSIDE!\n");
getchar();
// 更改模式
stViRotationExAttr.bEnable = HI_TRUE;
stViRotationExAttr.stRotationEx.enViewType = ROTATION_VIEW_TYPE_INSIDE; //无黑边模式
stViRotationExAttr.stRotationEx.s32CenterXOffset = 0;
stViRotationExAttr.stRotationEx.s32CenterYOffset = 0;
stViRotationExAttr.stRotationEx.u32Angle = 90;
stViRotationExAttr.stRotationEx.stDestSize.u32Width = stSize.u32Width;
stViRotationExAttr.stRotationEx.stDestSize.u32Height = stSize.u32Height;
s32Ret = HI_MPI_VI_SetChnRotationEx(ViPipe,ViChn,&stViRotationExAttr);
if (HI_SUCCESS != s32Ret)
{
SAMPLE_PRT("HI_MPI_VI_SetChnRotationEx failed witfh %d\n", s32Ret);
goto EXIT;
}
printf("now FreeAngle Rotation on, press any key to switch FreeAngle Rotation off!\n");
getchar();
// 更改模式
stViRotationExAttr.bEnable = HI_FALSE;
stViRotationExAttr.stRotationEx.enViewType = ROTATION_VIEW_TYPE_INSIDE;
stViRotationExAttr.stRotationEx.s32CenterXOffset = 0;
stViRotationExAttr.stRotationEx.s32CenterYOffset = 0;
stViRotationExAttr.stRotationEx.u32Angle = 90;
stViRotationExAttr.stRotationEx.stDestSize.u32Width = stSize.u32Width;
stViRotationExAttr.stRotationEx.stDestSize.u32Height = stSize.u32Height;
s32Ret = HI_MPI_VI_SetChnRotationEx(ViPipe,ViChn,&stViRotationExAttr);
if (HI_SUCCESS != s32Ret)
{
SAMPLE_PRT("HI_MPI_VI_SetChnRotationEx failed witfh %d\n", s32Ret);
goto EXIT;
}
PAUSE();
SAMPLE_COMM_VI_UnBind_VO(ViPipe, ViChn, VoDev, VoChn); //解除bind
EXIT1:
SAMPLE_VIO_StopViVo(&stViConfig, &stVoConfig);
EXIT:
SAMPLE_COMM_SYS_Exit();
return s32Ret;
}
/******************************************************************************
* function : main()
* Description : main
******************************************************************************/
int main(int argc, char* argv[])
{
HI_S32 s32Ret = HI_FAILURE;
VO_INTF_TYPE_E enVoIntfType = VO_INTF_HDMI; //输出格式
signal(SIGINT, SAMPLE_VIO_HandleSig);
signal(SIGTERM, SAMPLE_VIO_HandleSig);
s32Ret = SAMPLE_VIO_4K30_FreeRotation(enVoIntfType);
if (HI_SUCCESS == s32Ret)
{
SAMPLE_PRT("sample_vio exit success!\n");
}
else
{
SAMPLE_PRT("sample_vio exit abnormally!\n");
}
return s32Ret;
}
VI_DEV,VO_DEV:VI,VO设备号
VI_PIPE :VI的PIPE编号
VI_CHN:VI的CHN编号
- 在hisi平台vi模块中,存在sensor -> dev -> pipe -> phy_chn -> ext_chn的关系
- pipe为输入图像数据管道,即dev接收的数据会传入pipe中,dev与pipe之间可以采用一对一或一对多的关系,当采用宽动态模式时,就需要一个dev对应两个pipe(长短曝光帧),然后再融合,得到宽动态图像;VI 的 PIPE 包含了 ISP 的相关处理功能,主要是对图像数据进行流水线处理,输出YUV 图像格式给通道。
- phy_chn:物理通道
- 扩展通道(ext_chn):扩展通道是物理通道的扩展,扩展通道具备缩放、裁剪、鱼眼矫正功能,它通过绑定物理通道,将物理通道输出作为自己的输入,然后输出用户设置的目标图像。
五、运行结果
成功获取相机图像在HDMI显示器上实时显示;同时通过设置使画面旋转了90度。
六、名词解释
- ISP:Image Signal Processing,图像信号处理;广义的ISP包含了JPEG和H.264/265图像压缩处理,而狭义的ISP仅包括从RAW格式变换到RGB或YUV的处理过程
- MIPI:Mobile Industry Processor Interface,移动工业处理器接口;(MIPI是由ARM和一系列手机公司成立的联盟,目的是为了将手机内部的接口如摄像头、显示屏接口、射频/基带接口等标准化,从而减少手机设计的复杂程度和增加设计灵活性,增加兼容性。)
总结
海思的代码真的难读
海思的sample源码实现了很多常见且实用的功能,后续还是要继续深度学习这些源码呀。
(ง๑ •̀_•́)ง