一、先保证多路视频码流保存到本地,并且分辨率正确
注意点:
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—单独为每个通道各自分配一个区域
最终效果: