9.多路码流RTSP传输并对每路视频图像叠加OSD信息

一、先保证多路视频码流保存到本地,并且分辨率正确

注意点:
1.VPSS分两路通道
2.VENC分两路通道

if(s32ChnNum>=1)
{
    s32Ret = SAMPLE_COMM_SYS_GetPicSize(gs_enNorm, enSize[0], &stSize);
        if (HI_SUCCESS != s32Ret)
        {
        SAMPLE_PRT("SAMPLE_COMM_SYS_GetPicSize failed!\n");
        goto END_VENC_720P_CLASSIC_1;
        }

        VpssGrp = 0;
    stVpssGrpAttr.u32MaxW = stSize.u32Width;
    stVpssGrpAttr.u32MaxH = stSize.u32Height;
    stVpssGrpAttr.bIeEn = HI_FALSE;
    stVpssGrpAttr.bNrEn = HI_TRUE;
    stVpssGrpAttr.bHistEn = HI_FALSE;
    stVpssGrpAttr.bDciEn = HI_FALSE;
    stVpssGrpAttr.enDieMode = VPSS_DIE_MODE_NODIE;
    stVpssGrpAttr.enPixFmt = PIXEL_FORMAT_YUV_SEMIPLANAR_420;

    s32Ret = SAMPLE_COMM_VPSS_StartGroup(VpssGrp, &stVpssGrpAttr);
        if (HI_SUCCESS != s32Ret)
        {
        SAMPLE_PRT("Start Vpss failed!\n");
        goto END_VENC_720P_CLASSIC_2;
        }

    s32Ret = SAMPLE_COMM_VI_BindVpss(stViConfig.enViMode);
        if (HI_SUCCESS != s32Ret)
        {
        SAMPLE_PRT("Vi bind Vpss failed!\n");
        goto END_VENC_720P_CLASSIC_3;
        }

        VpssChn = 0;
    stVpssChnMode.enChnMode      = VPSS_CHN_MODE_USER;
    stVpssChnMode.bDouble        = HI_FALSE;
    stVpssChnMode.enPixelFormat  = PIXEL_FORMAT_YUV_SEMIPLANAR_420;
    stVpssChnMode.u32Width       = stSize.u32Width;
    stVpssChnMode.u32Height      = stSize.u32Height;
    stVpssChnMode.enCompressMode = COMPRESS_MODE_SEG;
        memset(&stVpssChnAttr, 0, sizeof(stVpssChnAttr));
    stVpssChnAttr.s32SrcFrameRate = -1;
    stVpssChnAttr.s32DstFrameRate = -1;
    enRcMode = SAMPLE_RC_CBR;
    s32Ret = SAMPLE_COMM_VPSS_EnableChn(VpssGrp, VpssChn, &stVpssChnAttr, &stVpssChnMode, HI_NULL);
        if (HI_SUCCESS != s32Ret)
        {
        SAMPLE_PRT("Enable vpss chn failed!\n");
        goto END_VENC_720P_CLASSIC_4;
        }
}

//--------Modify point-----------
#ifdef Modity_point

        if(s32ChnNum>=2)
        {
        s32Ret = SAMPLE_COMM_SYS_GetPicSize(gs_enNorm, enSize[1], &stSize);
            if (HI_SUCCESS != s32Ret)
            {
        SAMPLE_PRT("SAMPLE_COMM_SYS_GetPicSize failed!\n");
        goto END_VENC_720P_CLASSIC_1;
            }
            VpssChn = 1;
            stVpssChnMode.enChnMode      = VPSS_CHN_MODE_USER;
            stVpssChnMode.bDouble        = HI_FALSE;
            stVpssChnMode.enPixelFormat  = PIXEL_FORMAT_YUV_SEMIPLANAR_420;
            stVpssChnMode.u32Width       = stSize.u32Width;
            stVpssChnMode.u32Height      = stSize.u32Height;
            stVpssChnMode.enCompressMode = COMPRESS_MODE_SEG;
            memset(&stVpssChnAttr, 0, sizeof(stVpssChnAttr));
            stVpssChnAttr.s32SrcFrameRate = -1;
            stVpssChnAttr.s32DstFrameRate = -1;
            enRcMode = SAMPLE_RC_CBR;
            s32Ret = SAMPLE_COMM_VPSS_EnableChn(VpssGrp, VpssChn, &stVpssChnAttr, &stVpssChnMode, HI_NULL);
            if (HI_SUCCESS != s32Ret)
        {
        SAMPLE_PRT("Enable vpss chn failed!\n");
        goto END_VENC_720P_CLASSIC_4;
            }
}
#endif

二、将每个视频码流加入各自的环形缓冲区

比如将720P的码流加入缓冲区0,VGA的码流加入缓冲区1
注意点:
1.不能共用一个,如果共用一个会导致数据混乱
2.只有VLC连接过来并且服务器进入了PLAY状态,才开始将视频码流加入环形缓冲区

HI_S32 saveStream(int chnnum,VENC_STREAM_S *pstStream)
{
    HI_S32 i,lens=0;

//---------Moidty point-----------
        //for(;j<MAX_RTSP_CLIENT;)//have atleast a connect//MAX_RTSP_CLIENT = 2
        //{
        //只有播放器连接过来我才开始将编码数据加入环形缓冲区
        if(g_rtspClients[chnnum].status == RTSP_SENDING)//可发送状态
        {
            for (i = 0; i < pstStream->u32PackCount; i++)//u32PackCount:码流包个数
            {
                RTPbuf_s *p = (RTPbuf_s *)malloc(sizeof(RTPbuf_s));    //生成一个链表节点
                INIT_LIST_HEAD(&(p->list));    //并且指针都指向自己

                //填充结构体
                lens = pstStream->pstPack[i].u32Len-pstStream->pstPack[i].u32Offset;
                p->buf = (char *)malloc(lens);
                p->len = lens;
                memcpy(p->buf,pstStream->pstPack[i].pu8Addr+pstStream->pstPack[i].u32Offset,lens);
//---------Moidty point-----------
#ifdef Modity_point

                if(chnnum==0)
                    list_add_tail(&(p->list),&RTPbuf_head);//节点加入环形链表
                    
                if(chnnum==1)
                    list_add_tail(&(p->list),&RTPbuf_head2);//节点加入环形链表2
        
#endif
                count[chnnum]++;            //链表节点的个数
                //count++;            //链表节点的个数
                //printf("count = %d\n",count);
            }
          }
//    }

    return HI_SUCCESS;
}

三、监听线程会等待播放器客户端连接,每当有一个链接就会创建一个线程去对接,在此线程函数中进行完OPTHIONS、Describe、Setup的响应后, 会生成一个udp套接字专门用于发送视频数据,然后创建各自的发送线程来使用各自的udp套接字进行发送

int PlayAnswer(char *cseq, int sock,int SessionId,char* urlPre,char* recvbuf,int index)
{
    if (sock != 0)
    {
        char buf[1024];
        memset(buf,0,1024);
        char *pTemp = buf;
        char*localip;
        localip = GetLocalIP(sock);
        pTemp += sprintf(pTemp,"RTSP/1.0 200 OK\r\nCSeq: %s\r\n%sRange: npt=0.000-\r\nSession: %d\r\nRTP-Info: url=rtsp://%s/%s;seq=0\r\n\r\n",
            cseq,dateHeader(),SessionId,localip,urlPre);

        free(localip);
        
        int reg = send(sock, buf,strlen(buf),0);
        if(reg <= 0)
        {
            return FALSE;
        }
        else
        {
            printf(">>>>>%s",buf);
            udpfd[index] = socket(AF_INET,SOCK_DGRAM,0);//为每个链接创建一个UDP套接字,专门用来发送
            struct sockaddr_in server;
            server.sin_family=AF_INET;
               server.sin_port=htons(g_rtspClients[index].rtpport[index]);          
               server.sin_addr.s_addr=inet_addr(g_rtspClients[index].IP);
            connect(udpfd[index],(struct sockaddr *)&server,sizeof(server));
                    printf("udp up\n");
            pthread_create(&gs_RtpPid[index], 0, vdRTPSendThread, (void *)&index);//为每个链接创建一个发送线程
        }
        return TRUE;
    }
    return FALSE;
}

注意点:保证最后需要几路码流就生成几个udp套接字和几个发送线程,每路码流对应一个udp套接字和一个发送线程

四、由于所有发送线程共用一个发送函数vdRTPSendThread,所以要此函数中对各个线程进行区分,执行不同的函数体(可以通过线程传参来实现),在发送函数中判断环形缓冲区是否为空,不为空则一次一次地调用发送函数发出去;为空则阻塞

HI_VOID* vdRTPSendThread(HI_VOID *p)
{
        HI_S32 index = *((HI_S32*)p);//线程传参,用于区分不同的发送线程
        while(1)    //每5ms判断当前是否有数据要发送
        {
                if(index == 0){
#if 1

        //printf("kongkongkong");
            if(!list_empty(&RTPbuf_head))    //链表为空,则表明当前没有数据要发送
            {
                //printf("feikong-hhhhhhhhhhhhhhhh");
            //有执行
            RTPbuf_s *p = get_first_item(&RTPbuf_head,RTPbuf_s,list);
            //有发
            VENC_Sent(p->buf,p->len,0);
            list_del(&(p->list));
            free(p->buf);
            free(p);
            p = NULL;
            count[0]--;
            //printf("count = %d\n",count);        
            }
                }
#endif

#ifdef Modity_point    
            else if(index == 1){
         
            if(!list_empty(&RTPbuf_head2))    //链表为空,则表明当前没有数据要发送
            {  
            
            RTPbuf_s *p = get_first_item(&RTPbuf_head2,RTPbuf_s,list);
            VENC_Sent(p->buf,p->len,1);
            list_del(&(p->list));
            free(p->buf);
            free(p);
            p = NULL;
            count[1]--;
            //printf("count = %d\n",count);
        
            }
                  }

#endif
        usleep(5000);                //延时5ms
    }
}

五、发送函数(对RTP协议的封包发送)只管发一次RTP包出去

注意点:因为有多个环形缓冲区需要发送,所以设计时要考虑根据udp套接字的不同而发送

六、多路视频图像如何为每路叠加OSD—单独为每个通道各自分配一个区域

最终效果:
![在这里插入图片描述](https://img-blog.csdnimg.cn/636cc65ef88b4be2a81e98acfcbff15d.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
同时录制多路RTSP流可以通过以下步骤实现。首先,需要使用RTSP协议来获取多路流的数据。RTSP协议定义了一对多应用程序如何通过IP网络传输多媒体数据。可以使用基于RTSP/RTP的流媒体传输技术来实现这一目标。\[1\] 其次,需要使用流媒体播放器来对遵循RTSP标准协议的码流进行实时播放和录制。播放器的核心包括网络库和播放库。网络库负责对码流的获取和链路的管理,可以基于开源库如Live555进行开发。播放库则负责对实时码流进行解码播放和控制,可以基于ffmpeg和DirectX等技术进行开发。\[2\] 在录制多路RTSP流时,可以通过比较音频和视频的时间戳(pts)来对视频的显示速度进行调整。如果当前的视频的pts比音频pts大于最小偏差值,说明视频快了,可以放慢视频的显示速度。反之,如果差距太大,大于最大偏差值时,可以采用丢帧方式来加快视频的显示速度。这样可以保持多路流的同步和稳定性。\[3\] 综上所述,同时录制多路RTSP流可以通过使用RTSP协议获取流数据,结合流媒体播放器进行实时播放和录制,并通过比较时间戳来调整视频的显示速度来实现。 #### 引用[.reference_title] - *1* *2* *3* [多路RTSP播放器直播与点播技术实现 | 学步园](https://blog.csdn.net/weixin_30229479/article/details/113551518)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值