Hi3559 MPP学习记录(1)


前言

最近开始接触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源码实现了很多常见且实用的功能,后续还是要继续深度学习这些源码呀。
(ง๑ •̀_•́)ง

猜你喜欢

转载自blog.csdn.net/weixin_49513223/article/details/130449188