当前位置:网站首页>海思3559万能平台搭建:获取数据帧修改后编码
海思3559万能平台搭建:获取数据帧修改后编码
2022-06-30 11:58:00 【快跑bug来啦】
前言
有了这么长的铺垫和反复的啃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);
最直接的想法肯定是重新开个线程,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传入。
);
暂时定义了个专门用来传参的结构体
/* 传递给线程的结构体 */
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));
而参数 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);
}
代码是后来粘过来的,并不是开发过程中,所以。。忽略掉注释和住掉的部分看哈
/* *描述 :用于处理图像信息的线程 *参数 :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);
}
这么搞没第二下就重启报错了,
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);
}
到了此处好像第一个问题就解决了?可以捕获到图片信息了,到时候调用自己的库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);
一定要看注意看注意看注意!这里写的清清楚楚,网上好多博客说海思没有官方声明,但实际只支持什么什么格式(也可能版本不一致导致)后续因为格式问题踩了大坑
/* *描述 :线程里用于处理图像信息 *参数 :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);
}
}
}
更改下sleep时间,相当于改了帧率,就能很清楚的知道最后保存下来的文件是处理过得了,毕竟sample有两个通道,我们只嚯嚯了通道1,大小小了很多嘛
注意事项
vpss通道的设置中stVpssChnAttr.u32Depth此时不能为0,否则图像队列会一直为空
结论
合适的位置找见了,下一步就开始自己做一些简单的图像处理咯!
边栏推荐
- 「运维有小邓」用户个人资料管理
- 实现多方数据安全共享,解决普惠金融信息不对称难题
- MySQL 内置函数
- Constructor, class member, destructor call order
- Multiparty Cardinality Testing for Threshold Private Set-2021:解读
- zabbix监控TCP连接个数
- A review of quantum neural networks 2022 for generating learning tasks
- R语言ggplot2可视化:使用ggplot2可视化散点图、aes函数中的size参数指定数据点的大小(point size)
- HMS Core音频编辑服务3D音频技术,助力打造沉浸式听觉盛宴
- 网络营销之四大误解
猜你喜欢
随机推荐
200. number of islands
不同类型的变量与零究竟是如何比较
After the node is installed in the NVM, the display is not internal or external when the NPM instruction is used
安装onnx很慢,使用清华镜像
Swagger2自动生成APi文档
90.(cesium篇)cesium高度监听事件
Flutter 从零开始 006 单选开关和复选框
695. maximum island area
c# 怎样能写个sql的解析器
R语言ggplot2可视化:gganimate包基于transition_time函数创建动态散点图动画(gif)、使用labs函数为动画图添加动态时间标题(抽取frame_time信息)
Hannaiping of Qilin software: the construction of Digital China needs its own open source root community
qt msvc 安装及调试
Flutter 从零开始 007 输入框
Map集合
Shell first command result is transferred to the second command delete
WebView, Scrollview sliding conflict correction
21、wpf之绑定使用小记
治数如治水,数据治理和数据创新难在哪?
Re understand oauth2.0 protocol for joint login
Go 语言入门很简单:Go 处理 XML 文件