• 海思3559万能平台搭建:获取数据帧修改后编码


    前言

      有了这么长的铺垫和反复的啃sample,现在开始搭建自己的平台就底气多了,倒也不至于万能平台哈哈,只是在完成配置文件的功能后,可以不用改代码重新编译,就可以实现多场景多平台多功能下的使用了

    获取通道图像

      万里长征第一步,在vi暂时没有改动依靠传感器的前提下,自然就是找个合适的位置从vpss处获取图像信息了,查看vpss处有关api,首先想到的自然是HI_MPI_VPSS_SendFrame和HI_MPI_VPSS_GetChnFrame(HI_MPI_VPSS_GetGrpFrame是从group获得的原始图像,主要于高清设备解码回放,要求暂停、步进时等场景,PIP 层和普通视频层上的两个通道显示同一帧图像)

    HI_MPI_VPSS_SendFrame

      该函数的应用场景在文档里并没有详细说明,默认开发者是熟悉海思平台的开发流程的,但是很明显,对于刚上手的小白来说,一脚就踩进了文字描述的大坑(个人理解问题),用户向vpss发送数据,怎么理解呢,数据哪来,肯定是vi来的,难道这相当于使能数据发送(怎么可能这么不智能)?调来一用果然各种报错,完全没有体会到,在平台各个部分配置完成后平台就可以自行运转数据流动的功能(甭管流没溜出去总之在流),一番尝试无果后才明白选对group和chnl就可以直接get数据信息了
    在这里插入图片描述

    HI_MPI_VPSS_GetChnFrame

    /* 
     *描述  : 用户从通道获取一帧处理完成的图像。
     *参数  : VpssGrp       VPSS GROUP 号。取值范围:[0, VPSS_MAX_GRP_NUM)  输入
     *         VpssChn        VPSS 通道号。取值范围:[0, VPSS_MAX_CHN_NUM)    输入
     *         pstVideoFrame  图像信息    输出
     *         s32MilliSec     超时时间   输入
     *返回值: 成功返回0,失败参考错误码
     *注意  : GROUP 必须已创建。
               该接口适用于 VPSS 所有通道,包括物理通道与扩展通道。
               只有在 USER 模式下,并且队列深度不为 0,才能获取到图像。
               chnl设置里u32Depth不能为0.
               调用该接口获取图像,不会对后端绑定的模块有影响。如后端绑定 VO 显示,可以在显示过程中获取图像,VO 仍正常显示,不会受到影响。
               当 s32MilliSec 设为-1 时,表示阻塞模式,程序一直等待,直到获取到图像才返回。如果 s32MilliSec 等于 0 时,表示非阻塞模式。
            如果 s32MilliSec 大于 0 时,表示超时等待模式,参数的单位是毫秒,指超时时间,在此时间内如果没有获取到图像,则超时返回。
               解码回放场景,由于不允许出现丢帧,VPSS 只要有一个通道不处理新图像(通道已使能),则整个 VPSS 不处理新图像。例如说使能了通道 0 和通道 1,
            两者都不绑定后端,通道图像队列长度都设为 2,此时从通道 0 中最多获取出 2 帧已缓存的图像,因为通道 1 缓存 2 帧后未处理新图像,所以 VPSS 不会再处理新图像
     */
    HI_S32 HI_MPI_VPSS_GetChnFrame(VPSS_GRP VpssGrp, VPSS_CHN VpssChn, VIDEO_FRAME_INFO_S *pstVideoFrame, HI_S32 s32MilliSec);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

      最直接的想法肯定是重新开个线程,while1去不断get,然后处理,发送,那么第一个问题来了,pthread_create需要传那些参数呢?之前的线程经常都是干独立的事情或者最多传一个参数。最少也需要传递group和chn两个参数进来啊,虽然是两个int类型可以手动指定,但为了代码维护和规范,还是使用通用做法吧。此时venc的sample还没有重构过,直接用用全局变量太粗暴了,为了线程的安全稳定,通过结构体传参是个不错的选择(亲自写过自然觉得容易,CV多了还真的连常识都不知道)

    #include <pthread.h>
    int pthread_create(
                     pthread_t *restrict tidp,   //新创建的线程ID指向的内存单元。
                     const pthread_attr_t *restrict attr,  //线程属性,默认为NULL
                     void *(*start_rtn)(void *), //新创建的线程从start_rtn函数的地址开始运行
                     void *restrict arg //默认为NULL。若上述函数需要参数,将参数放入结构中并将地址作为arg传入。
                      );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

      暂时定义了个专门用来传参的结构体

    /* 传递给线程的结构体 */
    typedef struct video_process_para
        {
            VPSS_GRP     VpssGrp  ;
            VPSS_CHN     VpssChn  ;  
        }video_process_s;
    video_process_s stv_process_test ;
    memset(&stv_process_test,0,sizeof( video_process_s));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

      而参数 pstVideoFrame是输出,声明下就好了
      就可以开线程随意操作了

      if(yuvtestEnable)
        {
            pthread_t video_process_id;
            stv_process_test.VpssGrp = VpssGrp;
            stv_process_test.VpssChn = VpssChn[1];
            s32Ret=pthread_create(&video_process_id, NULL, &video_process_test_task,(HI_VOID*)&stv_process_test);
            if(s32Ret != 0)
            {
                SAMPLE_PRT("pthread video_process create failed\n");
                return -HI_FAILURE;
            }
            pthread_detach(video_process_id);
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

      代码是后来粘过来的,并不是开发过程中,所以。。忽略掉注释和住掉的部分看哈

    /* 
     *描述  :用于处理图像信息的线程
     *参数  :arg 为自定义结构video_process_s,VPSS_GRP和VPSS_CHN用于传参给HI_MPI_VPSS_GetChnFrame
     *返回值:无
     *注意  :HI_MPI_VPSS_GetChnFrame完必须释放,否则再次获取VB会报错        
     */ 
    HI_VOID *video_process_test_task(HI_VOID *arg)
    {
        HI_S32 cnt = 0;
        HI_S32 s32Ret;
       
        VIDEO_FRAME_INFO_S stVideoFrame;
        video_process_s* pstPara;
        pstPara = (video_process_s*)arg;
        
        sleep(1);
        memset(&stVideoFrame,0,sizeof(VIDEO_FRAME_INFO_S));
        while(cnt <= 10)
        {
            s32Ret = HI_MPI_VPSS_GetChnFrame(pstPara->VpssGrp, pstPara->VpssChn, &stVideoFrame,1000);
            if(s32Ret != HI_SUCCESS)
            {
                SAMPLE_PRT("%dVPSS_GetChnFrame err for %#x!\n", cnt,s32Ret);
                cnt++;
            }
            else 
            {
                //HI_MPI_VPSS_ReleaseChnFrame(pstPara->VpssGrp, pstPara->VpssChn, &stVideoFrame);//不释放会导致后续vb崩溃
                goto DEAL;  
            }
                  
        }
        goto EXIT;
    DEAL:
        // deal_myself_osd(arg);
        // deal_myself_yuv2rgb(arg);
        deal_myself(arg);  
    EXIT:
        pthread_exit(0);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

      这么搞没第二下就重启报错了,
    在这里插入图片描述

    VB占用解决方法

      很明显这是资源没有释放呗,思路三种:
      第一种最粗暴的就是强制销毁VB,一劳永逸,但是会屏蔽开发中的很多漏洞,学习阶段还是不这么搞
    在这里插入图片描述
      第二种方法也类似,是在main最开始做一个资源的释放,毕竟海思自己初始化之前不也有去初始化的操作吗?
      第三种还是正向解决比较好,规范写法最优,更何况这个问题相对后面重构后的坑来说还是直接多了的,毕竟,就加了一句啊,尝试释放了通道的信息后马上就好了

    HI_MPI_VPSS_ReleaseChnFrame

      简单的做个处理前的判断,要是可以读到信息就进行我们的处理,连着几次读不到就歇着吧您

    /* 
     *描述  :用于处理图像信息的线程
     *参数  :arg 为自定义结构video_process_s,VPSS_GRP和VPSS_CHN用于传参给HI_MPI_VPSS_GetChnFrame
     *返回值:无
     *注意  :HI_MPI_VPSS_GetChnFrame完必须释放,否则再次获取VB会报错        
     */ 
    HI_VOID *video_process_test_task(HI_VOID *arg)
    {
        HI_S32 cnt = 0;
        HI_S32 s32Ret;
       
        VIDEO_FRAME_INFO_S stVideoFrame;
        video_process_s* pstPara;
        pstPara = (video_process_s*)arg;
        
        sleep(1);
        memset(&stVideoFrame,0,sizeof(VIDEO_FRAME_INFO_S));
        while(cnt <= 10)
        {
            s32Ret = HI_MPI_VPSS_GetChnFrame(pstPara->VpssGrp, pstPara->VpssChn, &stVideoFrame,1000);
            
            if(s32Ret != HI_SUCCESS)
            {
                SAMPLE_PRT("%dVPSS_GetChnFrame err for %#x!\n", cnt,s32Ret);
                cnt++;
            }
            else 
            {
                HI_MPI_VPSS_ReleaseChnFrame(pstPara->VpssGrp, pstPara->VpssChn, &stVideoFrame);//不释放会导致后续vb崩溃
                goto DEAL;  
            }
                  
        }
        goto EXIT;
    DEAL:
        // deal_myself_osd(arg);
        // deal_myself_yuv2rgb(arg);
        deal_myself(arg);
        
    EXIT:
        pthread_exit(0);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

      到了此处好像第一个问题就解决了?可以捕获到图片信息了,到时候调用自己的库deal_myself处理呗?处理完流程也是自动的,想想就激动,还是不难嘛~
    在这里插入图片描述

      sample的自动智能都是建立在绑定的基础上的,自己截了数据玩了玩释放,后续谁管你啊!

    发送编码图像

      首先需要取消原来vpss和venc的绑定,要不然人家流转的飞起,你自娱自乐

    HI_MPI_VENC_SendFrame

    /* 
     *描述  :支持用户发送原始图像进行编码
     *参数  :VeChn        编码通道号。取值范围:[0, VENC_MAX_CHN_NUM)
              pstFrame     原始图像信息结构指针。
              s32MilliSec  发送图像超时时间。取值范围:[-1,+ ∞ ) -1:阻塞。 0:非阻塞。大于 0:超时时间。
     *返回值:成功返回0,失败参考错误码
     *注意  :  此接口支持用户发送图像至编码通道。
                如果 s32MilliSec 小于-1,返回 HI_ERR_VENC_ILLEGAL_PARAM。
                用户发送原始图像必须为 Semi-planar YVU 4:2:0 或 Semi-planar YVU 4:2:2 
            或PIXEL_FORMAT_YUV_400 格式。H.264/H.265 编码通道支持接收 Semi-plannarYVU 4:2:0 或 PIXEL_FORMAT_YUV_400 图像,
            JPEG/MJPEG 编码通道支持接收Semi-plannar YVU 4:2:0、Semi-plannar YVU 4:2:2 或 PIXEL_FORMAT_YUV_400图像。
                视频输入的原始图像大小必须不小于编码通道的大小。
                调用该接口发送图像,用户需要保证编码通道已创建且开启接收输入图像。
     */
    HI_S32 HI_MPI_VENC_SendFrame(VENC_CHN VeChn, const VIDEO_FRAME_INFO_S *pstFrame, HI_S32 s32MilliSec);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

      一定要看注意看注意看注意!这里写的清清楚楚,网上好多博客说海思没有官方声明,但实际只支持什么什么格式(也可能版本不一致导致)后续因为格式问题踩了大坑

    /* 
     *描述  :线程里用于处理图像信息
     *参数  :arg 为自定义结构video_process_s,VPSS_GRP和VPSS_CHN用于传参给HI_MPI_VPSS_GetChnFrame
     *返回值:无
     *注意  :HI_MPI_VPSS_GetChnFrame完必须释放,否则再次获取VB会报错
              VIDEO_FRAME_INFO_S里的虚拟地址不可以直接使用,必须通过物理地址HI_MPI_SYS_Mmap映射后才可以作为数据存放地址用
              用完同样需要HI_MPI_SYS_Munmap解映射      
     */
    HI_VOID deal_myself(HI_VOID *arg)
    {
        HI_S32 s32Ret;
        VIDEO_FRAME_INFO_S stVideoFrame;
        // VIDEO_FRAME_INFO_S* pstVideoFrame = &stVideoFrame;
        video_process_s* pstPara;
        pstPara = (video_process_s*)arg;
        while(1)
            {
            s32Ret = HI_MPI_VPSS_GetChnFrame(pstPara->VpssGrp, pstPara->VpssChn, &stVideoFrame,1000);
            // SAMPLE_PRT("deal_myself is pstPara->VpssChn is %d!\n",pstPara->VpssChn);
            // sleep(1);
            if(s32Ret != HI_SUCCESS)
            {
                // SAMPLE_PRT("VPSS_GetChnFrame err for %#x!\n",s32Ret);//while1打印太多
            }
            // printf("u32TimeRef is %d\r\n",pstVideoFrame->stVFrame.u32TimeRef);
            else
            {
                /* ................................图像处理 */
                s32Ret = HI_MPI_VENC_SendFrame(pstPara->VpssChn, &stVideoFrame,1000);
                // printf("u32TimeRef is %d\r\n",pstVideoFrame->stVFrame.u32TimeRef);
                HI_MPI_VPSS_ReleaseChnFrame(pstPara->VpssGrp, pstPara->VpssChn, &stVideoFrame);
            }
        } 
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

      更改下sleep时间,相当于改了帧率,就能很清楚的知道最后保存下来的文件是处理过得了,毕竟sample有两个通道,我们只嚯嚯了通道1,大小小了很多嘛

    注意事项

    vpss通道的设置中stVpssChnAttr.u32Depth此时不能为0,否则图像队列会一直为空

    结论

      合适的位置找见了,下一步就开始自己做一些简单的图像处理咯!

  • 相关阅读:
    产品-Axure9英文版,A页面内a1状态跳转B页面的b2状态,(条件跳转状态)
    python:talib.BBANDS 画股价-布林线图
    ChatGPT(对话AI)汇总
    es-head插件插入查询以及条件查询(五)
    C++征途 --- STL常用算法(上)
    jvm监控服务性能、以及资源使用情况
    Gradle
    JAVA ---List
    CNN反向求导推导
    Mybatis学习笔记2
  • 原文地址:https://blog.csdn.net/qq_42330920/article/details/125447928