RTP 发送PS流零拷贝方案

Posted qianbo_insist

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了RTP 发送PS流零拷贝方案相关的知识,希望对你有一定的参考价值。

代码依然在gitee上,逐步修改
https://gitee.com/guanzhi0319/rtp

数据达到发送RTP

数据收集到后,编码h264,或者h265,发送ps流或者h264裸流到指定服务器。

void LiveRTPUdpEncoder::OnCaptureVideoBuffer(uint8_t *data, int len, unsigned int timestamp, bool isKeyframe)

#define D(x) *(data+x)
	if (SimpleThread::IsStop()) return;

#if 0
	char buffer[32];
	sprintf_s(buffer, "QB:%02x %02x %02x %02x %02x %02x",
		D(0), D(1), D(2), D(3), D(4), D(5));
	OutputDebugStringA(buffer);
	return;
#endif
    uint8_t* odata = NULL;
    int olen = 0;
    uint8_t n = D(4) & 0x1f;
    int isIFrame = n == 0x07;
    switch (v_stream_type)
    
    case en_rtp:
        odata = data;
        olen = len;
        break;
    case en_ps_over_rtp:
        uint8_t* odata;
        int olen;
        v_ps.packagingPs(isIFrame, timestamp, data, len, &odata, &olen);
        break;
    

	//timestamp = GetTimestamp();
    
        Lock lock(this);
               if(v_stream_type == en_ps_over_rtp)
        
            uint8_t* ps = odata + RTP_UDP_HEAD_LEN;
            olen = olen - RTP_UDP_HEAD_LEN;
            v_rtp.send_video_ps(ps, olen, convertToRTPTimestamp(), isKeyframe);
            delete[]odata;

        
        else if (v_stream_type == en_rtp)
        
            v_rtp.send_video(odata, olen, convertToRTPTimestamp(), isKeyframe);
        

        if (!gVideoBegin)
            gVideoBegin = true;
    

发送RTP ps

在ps流的前面留空12字节,每次1400字节发送的时候,修改前面12字节RTP包,注意也就是修改了数据包,不复用,并且发送一定是同步的才能正确,好处是避免了数据拷贝。
避免数据拷贝的方式有两种,
1、一种是同步缓冲区,但是异步发送
2、同步发送
3、使用协程,同步发送。
这里是客户端,发送时只有一路,使用了UDP,直接同步发送就行。申请ps内存的时候为了不拷贝数据,前面留空了12字节作为RTP UDP的头部。那么在发送每隔包的时候,在发送的数据往前倒推12字节,避免拷贝,但是这样修改了缓冲区里的数据,如果想复用,每次在覆盖的时候先把12字节赋值,发送完毕,再把字节恢复。

int c_rtp::send_video_ps(uint8_t* data, int len, uint32_t timestamp, bool iskeyframe)

	//data 前面有12 字节为空,构造 ps 流时留空
	uint8_t* x = data;
	int num = (len - 1) / 1400 + 1;
	for (int i = 0; i < num; i++)
	
		uint8_t* pos = x - 12;
		memset(pos, 0, 12);
		RTP_FIXED_HEADER* rtp_hdr;  //RTP头部
		rtp_hdr = (RTP_FIXED_HEADER*)data;
		//设置RTP HEADER,
		rtp_hdr->payload = H264;  //负载类型号,
		rtp_hdr->version = 2;  //版本号,此版本固定为2
		rtp_hdr->marker = 0;   //标志位,由具体协议规定其值。
		rtp_hdr->ssrc = little2big(1111);
		rtp_hdr->timestamp = little2big(timestamp);
		if (i < num-1)
			v_sock.SendTo(pos, 12 + 1400, v_sockaddr);
		else
			v_sock.SendTo(pos, 12 + len -1400*i,v_sockaddr);
		x += 1400;
	


这里的v_sock 就是简单的同步发送socket。读者有兴趣,可以自己改为异步方式。

以上是关于RTP 发送PS流零拷贝方案的主要内容,如果未能解决你的问题,请参考以下文章

RTP 发送PS流工具(已经开源)

H264 NALU 使用PS封装 RTP发送

GB28181设备端PS流封装和发送

VLC播放RTP封装的h264,PS,TS流的SDP写法

H264关于RTP协议的实现

基于C/S架构的完整RTP/RTCP的H264/H265视频传输方案实现