海思3559 sample解析:venc

本文涉及的产品
阿里云百炼推荐规格 ADB PostgreSQL,4核16GB 100GB 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 海思3559 sample解析:venc

前言:

 分析完vio的例程后瞬间信心满满,自信的去找大佬请求分配任务了,“简简单单”几个步骤,照着葫芦画瓢,调调参数,对应位置具体分析具体修改,美好光明的“大路”清晰明朗啊!

e03496f513c74999bb9743defaa911c6.png

年轻人,too young too naive啊要真是这么简单,用芯片大炮打蚊子吗?

 可是看起来就是很简单啊,属性挨个配置,一绑定,巴拉巴拉

 那如果不绑定呢?需要自己加算法修改呢?怎么获取图像,怎么输出?假如要做个人脸识别呢?b63a671dcb764ee8a0e75b596b428bd8.png

有没有感觉到越简单的流程好像越不自由,很多都没法自己掌控,想自己随需求修改的更多,自然就没那么容易了

捂着被打肿的脸痛定思痛,一定要好好拆解一下具体步骤

一个sample不够那就再看个复杂一点点的!

sample_venc

 有了之前的经验,整体流程心里还是有了点x数,就不用每次都跳来跳去挨个查看了,直接理一下代码框架吧


main 主函数
  SAMPLE_VENC_4K60 视频采集函数
  SAMPLE_COMM_SYS_GetPicSize 获取w.h参数
  SAMPLE_COMM_VI_GetSensorInfo
  SAMPLE_VENC_CheckSensor
    SAMPLE_COMM_VI_GetSizeBySensor 得到sensor的相关参数,得到mmp系统所需的一些参数
    SAMPLE_COMM_SYS_GetPicSize 得到图片的尺寸
  SAMPLE_VENC_VI_Init
    SAMPLE_VENC_SYS_Init 编码系统初始化
    SAMPLE_COMM_VI_GetSizeBySensor  得到sensor的相关参数,得到mmp系统所需的一些参数
    SAMPLE_COMM_SYS_GetPicSize  得到图片的尺寸
    COMMON_GetPicBufferSize  得到缓存块的大小
    SAMPLE_COMM_SYS_Init   MMP系统初始化
      HI_MPI_SYS_Exit();去初始化 MPP 系统。
      HI_MPI_VB_Exit();去初始化 MPP 视频缓存池
      HI_MPI_VB_SetConfig设置 MPP 视频缓存池属性
      HI_MPI_VB_Init 初始化 MPP 视频缓存池
      HI_MPI_SYS_Init 初始化 MPP 系统。
      SAMPLE_COMM_VI_GetFrameRateBySensor
    HI_MPI_ISP_GetCtrlParam 获取 ISP 控制参数。
    HI_MPI_ISP_SetCtrlParam 设置 ISP 控制参数。
    SAMPLE_COMM_VI_StartVi
    SAMPLE_COMM_VI_StartMIPI 初始化mipi : 设置mipi的模式。.使能mipi时钟。mipi复位。使能sensor时钟。sensor复位。设置mipi的属性。mipi解复位。.sensor解复位
    SAMPLE_COMM_VI_SetParam获取 VI,VPSS 的工作模式。设置 VI,VPSS 工作模式
    SAMPLE_COMM_VI_CreateVi设置 VI 设备与物理 PIPE 的绑定关系。 创建vi pipe。 设置 VI 通道ch属性,启用 VI 通道ch
    SAMPLE_COMM_VI_CreateIsp  参考HiISP开发参考
  SAMPLE_VENC_VPSS_Init
    SAMPLE_COMM_VI_GetSizeBySensor  得到sensor的相关参数,得到mmp系统所需的一些参数
    SAMPLE_COMM_SYS_GetPicSize  得到图片的尺寸
    SAMPLE_COMM_VPSS_Start  创建vpss group 配置通道属性 启用通道 启用vpss group
  SAMPLE_COMM_VI_Bind_VPSS  包含数据源3个参数接收者3个参数,分别是vi和vpss,调用数据源到数据接收者绑定接口
    HI_MPI_SYS_Bind
  /     start stream venc   /
  SAMPLE_VENC_GetRcMode    获取rc模式(码率控制)
  SAMPLE_VENC_GetGopMode  gop模式选择
  SAMPLE_COMM_VENC_GetGopAttr 根据gop模式配置相关属性
   /***encode h.265 **/
  SAMPLE_COMM_VENC_Start  启动编码 stream模式
    SAMPLE_COMM_VENC_Creat  创建venc channel根据enType选择编码格式
    HI_MPI_VENC_StartRecvFrame 开启编码通道接收输入图像。允许指定接收帧数,超出指定的帧数后自动停止接收图像
  SAMPLE_COMM_VPSS_Bind_VENC  数据源到数据接收者绑定接口。
    HI_MPI_SYS_Bind  数据源到数据接收者绑定接口。
  /***encode h.264 **/
  SAMPLE_COMM_VENC_Start
    SAMPLE_COMM_VENC_Creat  创建venc channel根据enType选择编码格式
    HI_MPI_VENC_StartRecvFrame 开启编码通道接收输入图像。允许指定接收帧数,超出指定的帧数后自动停止接收图像
  SAMPLE_COMM_VPSS_Bind_VENC
    HI_MPI_SYS_Bind  数据源到数据接收者绑定接口。
  /*stream save process*/
  SAMPLE_COMM_VENC_StartGetStream  创建线程用以开启编码流
    SAMPLE_COMM_VENC_GetVencStreamProc
     step 1:   检查并准备保存文件和venc fd
      /* 确定视频流文件名,并打开文件以保存视频流*/
      /* 设置 Venc Fd. */
     step 2:  开始获取每个通道的视频流
      step 2.1 : 查询每个帧流中有多少包。
      step 2.2 :建议同时检查u32CurPacks和u32LeftStreamFramese
      step 2.3 :  malloc对应的包节点数。
      step 2.4 : 调用mpi获取一个帧流
      step 2.5 : 将框架保存到文件
      step 2.6 : 释放视频流
       step 2.7 : 释放包节点
     step 3 :关闭保存文件
  SAMPLE_COMM_VENC_StopGetStream  pthread_join线程

看起啦又清晰了不少,那么接下来就以此为基础,开始我们自己的平台搭建吧!

部分补充注释

PIC_SIZE_E enSize[2] = {PIC_3840x2160, PIC_1080P};
编码通道s32ChnNum=2;//一个是4k120一个是1080p30
VENC_CHN VencChn[2] = {0,1};
HI_U32 u32Profile[2] = {0,1};
PAYLOAD_TYPE_E enPayLoad[2] = {PT_H265, PT_H264}; //两种编码方式

一、获取编码的具体分辨率

二、获取sensor的信息

三、检查sensor

主要的工作内容是获取sensor的宽高,检查需要编码的视频的宽高是否大于sensor能够捕获的最大宽高。如果大于则说明编码分辨率设置错误。

四、VI的初始化

1.venc初始化

获取sensor的分辨率,并计算出需要的vb块大小(这里计算了两个,一个是sensor的分辨率,一个是1920 *1080的分辨率),并且缓存池的数量是2(stVbConf.u32MaxPoolCnt = 2;)

设置 MPP 视频缓存池属性,初始化VB,初始化sys:

HI_MPI_SYS_Exit();
HI_MPI_VB_Exit();
HI_MPI_VB_SetConfig(pstVbConfig);
HI_MPI_VB_Init();
HI_MPI_SYS_Init();

2.根据sensor的类型获取帧率u32FrameRate。

3.获取ISP的控制参数。

打开isp_dev设备节点,获取控制参数:

ioctl(g_as32IspFd[ViPipe], ISP_GET_CTRL_PARAM, pstIspCtrlParam);
stIspCtrlParam.u32StatIntvl = u32FrameRate/30;
ioctl(g_as32IspFd[ViPipe], ISP_SET_CTRL_PARAM, pstIspCtrlParam);

4.start mipi

.设置mipi的模式

fd = open("/dev/hi_mipi", O_RDWR);
ioctl(fd, HI_MIPI_SET_HS_MODE, LANE_DIVIDE_MODE_7);
close(fd);

.使能mipi时钟

fd = open("/dev/hi_mipi", O_RDWR);
ioctl(fd, HI_MIPI_ENABLE_MIPI_CLOCK, &devno);
close(fd);

.reset mipi

fd = open("/dev/hi_mipi", O_RDWR);
s32Ret = ioctl(fd, HI_MIPI_RESET_MIPI, &devno);
close(fd);

.使能sensor时钟

fd = open("/dev/hi_mipi", O_RDWR);
ioctl(fd, HI_MIPI_ENABLE_SENSOR_CLOCK, &SnsDev);
close(fd);

.reset sensor

fd = open("/dev/hi_mipi", O_RDWR);
s32Ret = ioctl(fd, HI_MIPI_RESET_SENSOR, &SnsDev);
close(fd);

.设置mipi的属性

fd = open("/dev/hi_mipi", O_RDWR);
ioctl(fd, HI_MIPI_SET_DEV_ATTR, &stcomboDevAttr);//raw图位宽,
close(fd);

.mipi解复位

fd = open("/dev/hi_mipi", O_RDWR);
ioctl(fd, HI_MIPI_UNRESET_MIPI, &devno);
close(fd);

.sensor解复位

open("/dev/hi_mipi", O_RDWR);
ioctl(fd, HI_MIPI_UNRESET_SENSOR, &SnsDev);
close(fd);

5.set vi vpss的工作模式【这个模式具体细节可参考hi说明文档】

HI_MPI_SYS_SetVIVPSSMode(&stVIVPSSMode);//定义 VI 各个 PIPE 和 VPSS 各个组的工作模式。

typedef enum hiVI_VPSS_MODE_E
{
VI_OFFLINE_VPSS_OFFLINE = 0,
VI_OFFLINE_VPSS_ONLINE,
VI_ONLINE_VPSS_OFFLINE,
VI_ONLINE_VPSS_ONLINE,
VI_PARALLEL_VPSS_OFFLINE,
VI_PARALLEL_VPSS_PARALLEL,
VI_VPSS_MODE_BUTT
} VI_VPSS_MODE_E;

6.创建vi和ISP

.创建VI

HI_MPI_VI_SetDevAttr(ViDev, &stViDevAttr);
HI_MPI_VI_EnableDev(ViDev);
HI_MPI_VI_SetDevBindPipe(pstViInfo->stDevInfo.ViDev, &stDevBindPipe);//vi设备和物理pipe的绑定关系
HI_MPI_VI_CreatePipe(ViPipe, &stPipeAttr);
HI_MPI_VI_SetChnAttr(ViPipe, ViChn, &stChnAttr);
HI_MPI_VI_EnableChn(ViPipe, ViChn);// vi在线VPSS在线模式、VI离线VPSS在线模式, VI并行VPSS 并行模式下,启动VI通道不生效,直接返回成功。

.创建ISP

以下的操作,每个输入工作通道都需要处理。

获得回调函数指针:sensor_register_callback

执行注册的回调函数:sensor_register_callback

sensor_register_callback

cmos_init_sensor_exp_function(&stIspRegister.stSnsExp);
    HI_MPI_ISP_SensorRegCallBack(ViPipe, &stSnsAttrInfo, &stIspRegister);
    cmos_init_ae_exp_function(&stAeRegister.stSnsExp);
    HI_MPI_AE_SensorRegCallBack(ViPipe, pstAeLib, &stSnsAttrInfo, &stAeRegister);
    cmos_init_awb_exp_function(&stAwbRegister.stSnsExp);
    HI_MPI_AWB_SensorRegCallBack(ViPipe, pstAwbLib, &stSnsAttrInfo, &stAwbRegister);

根据sensor的不同,注册不同的控制总线(iic或者ssp)

HI_MPI_AE_Register(IspDev, &stAeLib);
HI_MPI_AWB_Register(IspDev, &stAwbLib)
HI_MPI_ISP_MemInit(ViPipe);
HI_MPI_ISP_SetPubAttr(ViPipe, &stPubAttr);
HI_MPI_ISP_Init(ViPipe);
pthread_create(&g_IspPid[*pIspDev], pstAttr, SAMPLE_COMM_ISP_Thread, (HI_VOID*)pIspDev);
SAMPLE_COMM_ISP_Thread
 HI_MPI_ISP_Run(IspDev);

五、VPSS初始化

1.通过sensor的型号确定分辨率

VPSS start
HI_MPI_VPSS_CreateGrp(VpssGrp, pstVpssGrpAttr);

同一个goroup下面的4个通道都需要

HI_MPI_VPSS_SetChnAttr(VpssGrp, VpssChn, &pastVpssChnAttr[VpssChn]);
HI_MPI_VPSS_EnableChn(VpssGrp, VpssChn);
HI_MPI_VPSS_StartGrp(VpssGrp);

六、VI bind VPSS

stSrcChn.enModId   = HI_ID_VI;
stSrcChn.s32DevId  = ViPipe;
stSrcChn.s32ChnId  = ViChn;
stDestChn.enModId  = HI_ID_VPSS;
stDestChn.s32DevId = VpssGrp;
stDestChn.s32ChnId = 0;
HI_MPI_SYS_Bind(&stSrcChn, &stDestChn);

七、start stream venc

确定用户选择的rc模式和gop模式,并根据这些用户选择确定gop属性。

八、encode h.265 h.264

1.VENC creat

HI_MPI_VENC_CreateChn(VencChn, &stVencChnAttr);//参数中对h.264 h.265 jpeg等编码做了区分。

(1)主码流进行h.265编码

SAMPLE_COMM_VENC_Start(VencChn[0], enPayLoad[0],enSize[0], enRcMode,u32Profile[0],&stGopAttr);
SAMPLE_COMM_VPSS_Bind_VENC(VpssGrp, VpssChn[0],VencChn[0]);

(2)子码流进行h.264编码

SAMPLE_COMM_VENC_Start(VencChn[1], enPayLoad[1], enSize[1], enRcMode,u32Profile[1],&stGopAttr);
SAMPLE_COMM_VPSS_Bind_VENC(VpssGrp, VpssChn[1],VencChn[1]);

2.HI_MPI_VENC_StartRecvFrame(VencChn,&stRecvParam); 获取编码码流到码流空间中

开启编码通道接收输入图像, 允许指定接收帧数,超出指定的帧数后自动停止接收图像,具体流程如下

开启线程

(1) check & prepare save-file & venc-fd

(2) Start to get streams of each channel.

select(maxfd + 1, &read_fds, NULL, NULL, &TimeoutVal)

step 2.1 : query how many packs in one-frame stream.
                HI_MPI_VENC_QueryStatus(i, &stStat);
step 2.2 :suggest to check both u32CurPacks and u32LeftStreamFrames at the same time,
step 2.3 : malloc corresponding number of pack nodes.
step 2.4 : call mpi to get one-frame stream
step 2.5 : save frame to file

snprintf(aszFileName[i],32, “stream_chn%d_%d%s”, i, u32PictureCnt[i],szFilePostfix);

pFile[i] = fopen(aszFileName[i], “wb”);

#ifndef __HuaweiLite__

直接写文件

s32Ret = SAMPLE_COMM_VENC_SaveStream(pFile[i], &stStream);
   for (i = 0; i < pstStream->u32PackCount; i++)
    {
        fwrite(pstStream->pstPack[i].pu8Addr + pstStream->pstPack[i].u32Offset,
               pstStream->pstPack[i].u32Len - pstStream->pstPack[i].u32Offset, 1, pFd);
       fflush(pFd);
    }
    #else

直接传递物理地址

s32Ret = SAMPLE_COMM_VENC_SaveStream_PhyAddr(pFile[i], &stStreamBufInfo[i], &stStream);
        #endif
stSrcChn.enModId   = HI_ID_VPSS;
stSrcChn.s32DevId  = VpssGrp;
stSrcChn.s32ChnId  = VpssChn;
stDestChn.enModId  = HI_ID_VENC;
stDestChn.s32DevId = 0;
stDestChn.s32ChnId = VencChn;
HI_MPI_SYS_Bind(&stSrcChn, &stDestChn);// h.265 h.264 VpssChn和VencChn的值分别是0和1. VpssGrp = 0

九、stream save process

pthread_create(&gs_VencPid,0,SAMPLE_COMM_VENC_GetVencStreamProc, (HI_VOID*)&gs_stPara);
SAMPLE_COMM_VENC_GetVencStreamProc// get stream from each channels and save them

十、exit process

1.杀死上面创建的获取数据流的线程

pthread_join(gs_VencPid, 0);

2.VPSS 和VENC解绑定VENC停止。要分为H264 H265,参数要求同上面绑定操作一样。

3.下面两步操作需要分为H264 H265, VencChn取值分别为0,1

HI_MPI_VENC_StopRecvFrame(VencChn);//stop recv picture
HI_MPI_VENC_DestroyChn(VencChn);// Distroy Venc Channel

4.VI和VPSS解绑定

stSrcChn.enModId   = HI_ID_VI;
stSrcChn.s32DevId  = ViPipe;
stSrcChn.s32ChnId  = ViChn;
stDestChn.enModId  = HI_ID_VPSS;
stDestChn.s32DevId = VpssGrp;
stDestChn.s32ChnId = 0;
HI_MPI_SYS_UnBind(&stSrcChn, &stDestChn)

5.VPSS停止

HI_MPI_VPSS_StopGrp(VpssGrp);
HI_MPI_VPSS_DisableChn(VpssGrp, VpssChn);
HI_MPI_VPSS_DestroyGrp(VpssGrp);

6.VI stop

SAMPLE_COMM_VI_DestroyIsp(pstViConfig);
SAMPLE_COMM_VI_DestroyVi(pstViConfig);
SAMPLE_COMM_VI_StopMIPI(pstViConfig);

7.sysexit

HI_MPI_SYS_Exit();
HI_MPI_VB_Exit();
相关文章
|
传感器 缓存 编解码
海思3559 sample解析:vio
拿到开发板,编完了平台sample,自然按捺不住要去简单学习测试了。打开最直观相对也比较简单的vio例程做个到手分析和流程梳理吧
2107 0
海思3559 sample解析:vio
|
8天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
28 2
|
1月前
|
缓存 Java 程序员
Map - LinkedHashSet&Map源码解析
Map - LinkedHashSet&Map源码解析
68 0
|
1月前
|
算法 Java 容器
Map - HashSet & HashMap 源码解析
Map - HashSet & HashMap 源码解析
57 0
|
1月前
|
存储 Java C++
Collection-PriorityQueue源码解析
Collection-PriorityQueue源码解析
62 0
|
1月前
|
安全 Java 程序员
Collection-Stack&Queue源码解析
Collection-Stack&Queue源码解析
83 0
|
8天前
|
存储 安全 Linux
Golang的GMP调度模型与源码解析
【11月更文挑战第11天】GMP 调度模型是 Go 语言运行时系统的核心部分,用于高效管理和调度大量协程(goroutine)。它通过少量的操作系统线程(M)和逻辑处理器(P)来调度大量的轻量级协程(G),从而实现高性能的并发处理。GMP 模型通过本地队列和全局队列来减少锁竞争,提高调度效率。在 Go 源码中,`runtime.h` 文件定义了关键数据结构,`schedule()` 和 `findrunnable()` 函数实现了核心调度逻辑。通过深入研究 GMP 模型,可以更好地理解 Go 语言的并发机制。
|
21天前
|
消息中间件 缓存 安全
Future与FutureTask源码解析,接口阻塞问题及解决方案
【11月更文挑战第5天】在Java开发中,多线程编程是提高系统并发性能和资源利用率的重要手段。然而,多线程编程也带来了诸如线程安全、死锁、接口阻塞等一系列复杂问题。本文将深度剖析多线程优化技巧、Future与FutureTask的源码、接口阻塞问题及解决方案,并通过具体业务场景和Java代码示例进行实战演示。
39 3
|
1月前
|
存储
让星星⭐月亮告诉你,HashMap的put方法源码解析及其中两种会触发扩容的场景(足够详尽,有问题欢迎指正~)
`HashMap`的`put`方法通过调用`putVal`实现,主要涉及两个场景下的扩容操作:1. 初始化时,链表数组的初始容量设为16,阈值设为12;2. 当存储的元素个数超过阈值时,链表数组的容量和阈值均翻倍。`putVal`方法处理键值对的插入,包括链表和红黑树的转换,确保高效的数据存取。
56 5
|
1月前
|
Java Spring
Spring底层架构源码解析(三)
Spring底层架构源码解析(三)
113 5

推荐镜像

更多