海思mpp学习(5):通过mipi接入视频并编码成H264

1.说明

使用到的模块:VI,VPSS,VENC

2.代码说明

1.内存池申请及系统初始化
    VB_CONFIG_S stVbConf;
    memset(&stVbConf, 0, sizeof(VB_CONFIG_S));
    // 最大缓存池的个数
    // 申请两个大小不同的缓存池,用来分别存储原始图像和压缩后的图像,达到节省内存的目的
    // mpp底层可以根据需要自动申请最合适的内存池
    stVbConf.u32MaxPoolCnt = 2;
    stVbConf.astCommPool[0].u64BlkSize = COMMON_GetPicBufferSize(2592, 1520, 
                                PIXEL_FORMAT_YVU_SEMIPLANAR_420, DATA_BITWIDTH_8, COMPRESS_MODE_NONE,DEFAULT_ALIGN);
    stVbConf.astCommPool[0].u32BlkCnt    = 4;
    stVbConf.astCommPool[1].u64BlkSize = COMMON_GetPicBufferSize(1920, 1080, 
                                PIXEL_FORMAT_YVU_SEMIPLANAR_420, DATA_BITWIDTH_8, COMPRESS_MODE_NONE,DEFAULT_ALIGN);
    stVbConf.astCommPool[1].u32BlkCnt    = 5;

    // 系统初始化
    HI_S32 s32Ret = SAMPLE_COMM_SYS_Init(&stVbConf);
    if (HI_SUCCESS != s32Ret)
    {
        printf("SAMPLE_COMM_SYS_Init failed!\n");
        return -1;
    }

根据我们的需求,设置合适的内存池配置:摄像头的分辨率是2592*1520,需要输出1080P的视频,所以申请两个内存池,mpp底层使用的时候会自动选择合适的内存池

2.VI模块初始化
    SAMPLE_VI_CONFIG_S  m_vi_config;
    memset(&m_vi_config, 0, sizeof(SAMPLE_VI_CONFIG_S));

    m_vi_config.s32WorkingViNum = 1;
    m_vi_config.as32WorkingViId[0] = 0;
    // sensor
    m_vi_config.astViInfo[0].stSnsInfo.enSnsType = m_sensor_type;
    m_vi_config.astViInfo[0].stSnsInfo.MipiDev = 0;
    m_vi_config.astViInfo[0].stSnsInfo.s32BusId = 0;
    m_vi_config.astViInfo[0].stSnsInfo.s32SnsId = 0;
    // dev
    m_vi_config.astViInfo[0].stDevInfo.ViDev = 0;
    m_vi_config.astViInfo[0].stDevInfo.enWDRMode = WDR_MODE_NONE;
    // pipe
    m_vi_config.astViInfo[0].stPipeInfo.aPipe[0] = 0;
    m_vi_config.astViInfo[0].stPipeInfo.aPipe[1] = -1;
    m_vi_config.astViInfo[0].stPipeInfo.enMastPipeMode = VI_OFFLINE_VPSS_ONLINE;
    m_vi_config.astViInfo[0].stPipeInfo.bMultiPipe = HI_FALSE;
    m_vi_config.astViInfo[0].stPipeInfo.bVcNumCfged = HI_FALSE;
    // channel
    m_vi_config.astViInfo[0].stChnInfo.ViChn = 0;
    m_vi_config.astViInfo[0].stChnInfo.enDynamicRange = DYNAMIC_RANGE_SDR8;
    m_vi_config.astViInfo[0].stChnInfo.enPixFormat = PIXEL_FORMAT_YVU_SEMIPLANAR_420;
    m_vi_config.astViInfo[0].stChnInfo.enVideoFormat = VIDEO_FORMAT_LINEAR;
    m_vi_config.astViInfo[0].stChnInfo.enCompressMode = COMPRESS_MODE_NONE;

    HI_S32 s32Ret = SAMPLE_COMM_VI_StartVi(&m_vi_config);
    if (HI_SUCCESS != s32Ret)
    {
        printf("SAMPLE_COMM_VI_StartVi failed with %d!\n", s32Ret);
        return -1;
    }

配置参数的地方需要注意的几个地方:

  • stDevInfo.ViDev,stPipeInfo.aPipe,stChnInfo.ViChn这几个参数不同的芯片支持的不同,所以要参考对应的说明文档来配置
  • stPipeInfo.enMastPipeMode不同的芯片支持的也不同
  • 不同的芯片该结构体可能有些差别,需要参考相应的sample设置
3.VPSS模块初始化
    // 只使用一个group
    VPSS_GRP m_vpss_grp = 0;
    // 3516ev300使用通道0会报错
    VPSS_CHN m_vpss_chn = 1;

    HI_BOOL abChnEnable[VPSS_MAX_PHY_CHN_NUM] = {HI_FALSE,HI_FALSE,HI_FALSE}; //channel0,channel1 每个grup有4个chn
    abChnEnable[m_vpss_chn] = HI_TRUE;

    VPSS_GRP_ATTR_S stVpssGrpAttr;
    VPSS_CHN_ATTR_S stVpssChnAttr[VPSS_MAX_PHY_CHN_NUM];

    //设置vpss组属性,指接收数据时控制码率
    stVpssGrpAttr.enDynamicRange = DYNAMIC_RANGE_SDR8;
    stVpssGrpAttr.enPixelFormat  = PIXEL_FORMAT_YVU_SEMIPLANAR_420; //YVU420
    stVpssGrpAttr.u32MaxW        = m_width;
    stVpssGrpAttr.u32MaxH        = m_height;
    stVpssGrpAttr.stFrameRate.s32SrcFrameRate = -1;
    stVpssGrpAttr.stFrameRate.s32DstFrameRate = -1;
    stVpssGrpAttr.bNrEn = HI_TRUE;
    stVpssGrpAttr.stNrAttr.enNrType       = VPSS_NR_TYPE_VIDEO;
    stVpssGrpAttr.stNrAttr.enNrMotionMode = NR_MOTION_MODE_NORMAL;
    stVpssGrpAttr.stNrAttr.enCompressMode = COMPRESS_MODE_FRAME;

    for(int i=0; i<VPSS_MAX_PHY_CHN_NUM; i++)
    {
        if(abChnEnable[i] == HI_TRUE)
        {
            stVpssChnAttr[i].u32Width                     = 1920;
            stVpssChnAttr[i].u32Height                    = 1080;
            stVpssChnAttr[i].enChnMode                    = VPSS_CHN_MODE_USER;
            stVpssChnAttr[i].enCompressMode               = COMPRESS_MODE_NONE;
            stVpssChnAttr[i].enDynamicRange               = stVpssGrpAttr.enDynamicRange;
            stVpssChnAttr[i].enPixelFormat                = stVpssGrpAttr.enPixelFormat;
            stVpssChnAttr[i].stFrameRate.s32SrcFrameRate  = stVpssGrpAttr.stFrameRate.s32SrcFrameRate;  //控制channel输出码率
            stVpssChnAttr[i].stFrameRate.s32DstFrameRate  = stVpssGrpAttr.stFrameRate.s32DstFrameRate;
            stVpssChnAttr[i].u32Depth                     = 3; // 这里不能为0,否则无法从vpss中获取图像
            stVpssChnAttr[i].bMirror                      = HI_FALSE;
            stVpssChnAttr[i].bFlip                        = HI_FALSE;
            stVpssChnAttr[i].enVideoFormat                = VIDEO_FORMAT_LINEAR;
            stVpssChnAttr[i].stAspectRatio.enMode         = ASPECT_RATIO_NONE;
        }
    }

    //已初始化的组属性以及channel属性,来创建grp和chn,并启动
    HI_S32 s32Ret = SAMPLE_COMM_VPSS_Start(m_vpss_grp, abChnEnable,&stVpssGrpAttr,stVpssChnAttr);
    if(s32Ret != HI_SUCCESS)
    {
        printf("start VPSS fail for %#x!\n", s32Ret);
        return -1;
    }

其中stVpssGrpAttr.u32MaxW、stVpssGrpAttr.u32MaxH为输入图像的宽高,stVpssChnAttr[i].u32Width、stVpssChnAttr[i].u32Height为输出图像的宽高

4.VENC模块初始化
    VENC_GOP_MODE_E enGopMode = VENC_GOPMODE_NORMALP;
    VENC_GOP_ATTR_S stGopAttr;
    HI_S32 s32Ret = SAMPLE_COMM_VENC_GetGopAttr(enGopMode,&stGopAttr);
    if (HI_SUCCESS != s32Ret)
    {
        printf("Venc Get GopAttr for %#x!\n", s32Ret);
        return -1;
    }

    VENC_CHN m_venc_chn;
    s32Ret = SAMPLE_COMM_VENC_Start(m_venc_chn, PT_H264, PIC_1080P, SAMPLE_RC_CBR,0, HI_TRUE, &stGopAttr);
    if (HI_SUCCESS != s32Ret)
    {
        printf("Venc Start failed for %#x!\n", s32Ret);
        return -1;
    }
5.模块绑定
    s32Ret = SAMPLE_COMM_VI_Bind_VPSS(m_vi_config.astViInfo[0].stPipeInfo.aPipe[0], 
                                        m_vi_config.astViInfo[0].stChnInfo.ViChn, m_vpss_grp);
    if(s32Ret != HI_SUCCESS)
    {
        UnInit();
        printf("VI Bind VPSS err for %#x!\n", s32Ret);
        return -1;
    }

    s32Ret = SAMPLE_COMM_VPSS_Bind_VENC(m_vpss_grp, m_vpss_chn, m_venc_chn);
    if(s32Ret != HI_SUCCESS)
    {
        UnInit();
        printf("VPSS Bind VENC err for %#x!\n", s32Ret);
        return -1;
    }
6.获取编码后的h264数据
    HI_S32 fd = HI_MPI_VENC_GetFd(m_venc_chn);
    if(fd < 0)
    {
        printf("HI_MPI_VENC_GetFd failed with %#x!\n", m_venc_chn);
        return;
    }

    void* data = malloc(1920*1080*3/2);

    int max_fd = fd;
    fd_set read_fds;
    FD_ZERO(&read_fds);
    while (true)
    {
        FD_SET(fd, &read_fds);
        timeval timeout_val;
        timeout_val.tv_sec = 1;
        timeout_val.tv_usec = 0;
        int ret = select(max_fd+1, &read_fds, NULL, NULL, &timeout_val);
        if(ret < 0)
        {
            printf("select failed!\n");
            break;
        }
        else if(ret == 0)
        {
            printf("get venc stream time out\n");
            continue;
        }
        else
        {
            if(FD_ISSET(fd, &read_fds))
            {
                VENC_CHN_STATUS_S status;
                ret = HI_MPI_VENC_QueryStatus(m_venc_chn, &status);
                if (ret != HI_SUCCESS)
                {
                    printf("HI_MPI_VENC_QueryStatus chn[%d] failed with %#x!\n", m_venc_chn, ret);
                    break;
                }

                if(status.u32CurPacks == 0)
                {
                    printf("NOTE: Current  frame is NULL!\n");
                    continue;
                }

                VENC_STREAM_S stream;
                memset(&stream, 0, sizeof(stream));
                stream.pstPack = (VENC_PACK_S*)malloc(sizeof(VENC_PACK_S) * status.u32CurPacks);
                if (NULL == stream.pstPack)
                {
                    printf("malloc stream pack failed!\n");
                    break;
                }
                stream.u32PackCount = status.u32CurPacks;
                ret = HI_MPI_VENC_GetStream(m_venc_chn, &stream, HI_TRUE);
                if (HI_SUCCESS != ret)
                {
                    free(stream.pstPack);
                    printf("HI_MPI_VENC_GetStream failed with %#x!\n", ret);
                    break;
                }

                // 推送视频流
                int data_len = 0;
                for(int i = 0 ; i < stream.u32PackCount; i++)
                {
                    data_len += (stream.pstPack[i].u32Len-stream.pstPack[i].u32Offset);
                }

                void* tmp = data;
                for(int i = 0 ; i < stream.u32PackCount; i++)
                {
                    memcpy(tmp, stream.pstPack[i].pu8Addr+stream.pstPack[i].u32Offset, stream.pstPack[i].u32Len-stream.pstPack[i].u32Offset);
                    tmp = tmp + (stream.pstPack[i].u32Len-stream.pstPack[i].u32Offset);
                }
                m_push_steam.PushOneFrame(data, data_len);    

                ret = HI_MPI_VENC_ReleaseStream(m_venc_chn, &stream);
                if (ret != HI_SUCCESS)
                {
                    printf("HI_MPI_VENC_ReleaseStream failed!\n");
                    free(stream.pstPack);
                    break;
                }

                free(stream.pstPack);
                stream.pstPack = NULL;
            }
        }
    }

源码地址:https://github.com/key1024/mpp_test.git

  • 3
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值