RV1126+FFMPEG推流项目(5)VI和VENC模块绑定,并且开启线程采集
回顾一下这个图,前面VI和VENC已经初始化了,现在也已经到了绑定两个模块的时候了。最后才开启线程到通道里面拿数据。
接下要完成这个两个模块,
rkmedia_assignment_manage.cpp (任务管理模块),管理 RV1126 上的各个任务。主要任务是创建并管理三个线程:
- 视频编码线程 (camera_venc_thread)。
- 音频编码线程 (audio_aenc_thread)。
- 音视频合成推流线程 (push_server_thread)。
rkmedia_data_process.cpp
(数据处理模块)
-
实现了三个线程的具体功能:
-
视频编码线程:处理视频编码和数据存储。
-
音频编码线程:处理音频编码和数据存储。
-
音视频合成并推流线程:将音视频数据进行合成并推送到服务器。
-
今天的任务: //1.VI和VENC绑定,//2.开启线程采集视频
int init_rv1126_first_assignment(int protocol_type, char* network_addr)
{
//1.VI和VENC绑定
//2.开启线程采集视频
}
第一步:VI和VENC绑定,我们先来认识一个数据结构"MPP_CHN_S"和一个函数“RK_MPI_SYS_Bind()”,这个结构体也是来自rv1126手册的。
RK_MPI_SYS_Bind() :
看到这个函数 ,有两个参数,这两个参数就是这两个模块,所以先要定义出来
//绑定vi和venc
//不绑定venc就无法从vi模块拿到数据进行编码
ret = RK_MPI_SYS_Bind(&vi_channel, &venc_channel);
if(ret != 0)
{
printf("绑定失败\n");
return -1;
}
//成功
printf("绑定成功");
MPP_CHN_S:
在rv1126上面,有怎么多的模块,怎么管理的?采用一个先描述后组织的思想,先描述:MPP_CHN_S这个是在rv1126上面描述一个模块的结构体,后组织:就是用什么把他管理起来,链表还是数组,这里没有用到先不说。
int init_rv1126_first_assignment(int protocol_type, char* network_addr)
{
//1.VI和VENC绑定
//1.1定义出VI和VENC模块
MPP_CHN_S vi_channel;
MPP_CHN_S venc_channel;
//1.2从VI容器里面获取通道VI_ID
//这里就体现出来容器的作用了,我只要放在公共的容器里面,就可以从其他文件拿到,很好的解偶
RV1126_VI_CONTAINER vi_container;
get_vi_container(0, &vi_container);
vi_channel.s32ChnId = vi_container.vi_id; //获取id
vi_channel.enModId = RK_ID_VI; //设定成VI模块,enModId 是一个枚举里面还有很多模块,这里只用到了RK_ID_VI
//1.3从VI容器里面获取通道VENC_ID
RV1126_VENC_CONTAINER venc_container;
get_venc_container(0, &venc_container);
venc_channel.s32ChnId = venc_container.venc_id;//获取id
venc_channel.enModId = RK_ID_VENC;//设定成VI模块
//绑定
//2.开启线程采集视频
}
开启线程获取摄像头的编码数据:
先来认识几个参数,是和venc哪里拿数据有关的“RK_MPI_SYS_GetMediaBuffer”这个函数是从指定通道中获取VENC数据。假设获取数据是mb, mb数据有两部分组成,有效数据和数据的大小。
RK_MPI_SYS_GetMediaBuffer:
“RK_MPI_MB_GetPtr”获取到mb里面的有效数据,RK_MPI_MB_GetSize这个mb数据是多大
RK_MPI_MB_GetPtr:
RK_MPI_MB_GetSize:
rkmedia_assignment_manage.cpp :
int init_rv1126_first_assignment(int protocol_type, char* network_addr)
{
int ret;
//1.VI和VENC绑定
//1.1定义出VI和VENC模块
MPP_CHN_S vi_channel;
MPP_CHN_S venc_channel;
//1.2从VI容器里面获取通道VI_ID
//这里就体现出来容器的作用了,我只要放在公共的容器里面,就可以从其他文件拿到,很好的解偶
RV1126_VI_CONTAINER vi_container;
get_vi_container(0, &vi_container);
vi_channel.s32ChnId = vi_container.vi_id; //获取id
vi_channel.enModId = RK_ID_VI; //设定成VI模块,enModId 是一个枚举里面还有很多模块,这里只用到了RK_ID_VI
//1.3从VI容器里面获取通道VENC_ID
RV1126_VENC_CONTAINER venc_container;
get_venc_container(0, &venc_container);
venc_channel.s32ChnId = venc_container.venc_id;//获取id
venc_channel.enModId = RK_ID_VENC;//设定成VI模块
//绑定vi和venc
//不绑定venc就无法从vi模块拿到数据进行编码
ret = RK_MPI_SYS_Bind(&vi_channel, &venc_channel);
if(ret != 0)
{
printf("绑定失败\n");
return -1;
}
//成功
printf("绑定成功");
//2.创建VENC线程,获取摄像头编码数据
//VENC线程的参数
VENC_PROC_PARAM* venc_arg_params = ( VENC_PROC_PARAM*)malloc(sizeof( VENC_PROC_PARAM));
venc_arg_params->vencId = vi_channel.s32DevId; //保存这个通道号,里面要通过这个id号获取
pthread_t pid;
ret = pthread_create(&pid, NULL, camera_venc_thread, (void*)venc_arg_params);
}
rkmedia_data_process.cpp:
//视频编码线程,并把数据放到队列里面去
void * camera_venc_thread(void *args)
{
int ret;
pthread_detach(pthread_self());//线程分离,主线程不用等待回收了,自动释放资源
MEDIA_BUFFER mb = NULL; //定义一个媒体缓存区,用于接受编码器的输出数据
VENC_PROC_PARAM *venc_arg = static_cast< VENC_PROC_PARAM *>(args);//可以直接强转,但是最高根安全一点
free(args); //参数解析到了,释放传入的内存
printf("线程启动成功");
while(1) //开始获取和处理venc数据
{
//从指定RK_ID_VENC模块的venc_arg->vencId通道里面拿数据, -1就是非阻塞
mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VENC, venc_arg->vencId, -1);
if(!mb)
{
printf("获取数据失败了\n");
break;
}
//把取出的数据存储到一个一个的视频包里面,分配数据结构
video_data_packet_t *video_data_packet = (video_data_packet_t* )malloc(sizeof(video_data_packet_t));
//把数据拷贝进去视频包里面,mb数据由两部分组成,有效数据和数据的大小
memcpy(video_data_packet->buffer, RK_MPI_MB_GetPtr(mb), RK_MPI_MB_GetSize(mb) );
//把venc视频数据缓冲区数据的大小传到video_frame_size
video_data_packet->video_frame_size = RK_MPI_MB_GetSize(mb);
//把视频包入到队列里面
video_queue->putVideoPacketQueue(video_data_packet);//todo......
//释放mb,重新获取下一个包
RK_MPI_MB_ReleaseBuffer(mb);
}
//到这里就是没数据了,就要把资源释放
//解绑定
MPP_CHN_S vi_channel;
MPP_CHN_S venc_channel;
vi_channel.s32ChnId = 0; //摄像头通道是0,也可以把vi通道号的参数一起传进来
vi_channel.enModId = RK_ID_VI;
venc_channel.s32ChnId = venc_arg->vencId; //venc通道函数
venc_channel.enModId = RK_ID_VENC;
ret = RK_MPI_SYS_UnBind(&vi_channel, &venc_channel);
if(ret != 0)
{
printf("VI和VENC解绑失败\n");
}
else
{
printf("VI和VENC解绑成功\n");
}
//释放venc
ret = RK_MPI_VENC_DestroyChn(venc_arg->vencId);
if(ret)
{
printf("venc释放失败");
return 0;
}
//释放vi
ret = RK_MPI_VI_DisableChn(0,0);
if(ret)
{
printf("vI释放失败");
return 0;
}
return NULL;
}
详细的源码
原文地址:https://blog.csdn.net/weixin_49529507/article/details/145158196
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!