有了这么长的铺垫和反复的啃sample,现在开始搭建自己的平台就底气多了,倒也不至于万能平台哈哈,只是在完成配置文件的功能后,可以不用改代码重新编译,就可以实现多场景多平台多功能下的使用了
万里长征第一步,在vi暂时没有改动依靠传感器的前提下,自然就是找个合适的位置从vpss处获取图像信息了,查看vpss处有关api,首先想到的自然是HI_MPI_VPSS_SendFrame和HI_MPI_VPSS_GetChnFrame(HI_MPI_VPSS_GetGrpFrame是从group获得的原始图像,主要于高清设备解码回放,要求暂停、步进时等场景,PIP 层和普通视频层上的两个通道显示同一帧图像)
该函数的应用场景在文档里并没有详细说明,默认开发者是熟悉海思平台的开发流程的,但是很明显,对于刚上手的小白来说,一脚就踩进了文字描述的大坑(个人理解问题),用户向vpss发送数据,怎么理解呢,数据哪来,肯定是vi来的,难道这相当于使能数据发送(怎么可能这么不智能)?调来一用果然各种报错,完全没有体会到,在平台各个部分配置完成后平台就可以自行运转数据流动的功能(甭管流没溜出去总之在流),一番尝试无果后才明白选对group和chnl就可以直接get数据信息了

/*
*描述 : 用户从通道获取一帧处理完成的图像。
*参数 : 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,一劳永逸,但是会屏蔽开发中的很多漏洞,学习阶段还是不这么搞

第二种方法也类似,是在main最开始做一个资源的释放,毕竟海思自己初始化之前不也有去初始化的操作吗?
第三种还是正向解决比较好,规范写法最优,更何况这个问题相对后面重构后的坑来说还是直接多了的,毕竟,就加了一句啊,尝试释放了通道的信息后马上就好了
简单的做个处理前的判断,要是可以读到信息就进行我们的处理,连着几次读不到就歇着吧您
/*
*描述 :用于处理图像信息的线程
*参数 :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的绑定,要不然人家流转的飞起,你自娱自乐
/*
*描述 :支持用户发送原始图像进行编码
*参数 :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,否则图像队列会一直为空
合适的位置找见了,下一步就开始自己做一些简单的图像处理咯!