自学内容网 自学内容网

RV1126+FFMPEG推流项目(5)VI和VENC模块绑定,并且开启线程采集

        回顾一下这个图,前面VI和VENC已经初始化了,现在也已经到了绑定两个模块的时候了。最后才开启线程到通道里面拿数据。 

 

        接下要完成这个两个模块,

rkmedia_assignment_manage.cpp (任务管理模块),管理 RV1126 上的各个任务。主要任务是创建并管理三个线程:
  1. 视频编码线程 (camera_venc_thread)。
  2. 音频编码线程 (audio_aenc_thread)。
  3. 音视频合成推流线程 (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)!