webrtc 和 rtp 协议

我会开辟一个空间rtp协议,包含vp9 h264 h265 aac opus 等rtp实例,包含实例代码,让看文章的人得到真实的提升

一、webrtc

rtp协议为实时传输协议 real transfer protocol
webrtc 已经被w3c(万维网联盟) 和IETF(互联网工程任务组)宣布成为正式标准,webrtc 底层使用 rtp 协议来传输音视频内容,同时可以使用websocket协议和http等标准协议来传输信令。rtp其实可以作为传输层来看,也可以看成半传输层半应用层协议。

二、rtp协议

rtp协议太重要了,如果你懂rtp协议,那基本就会把音视频传输里所有的协议摸一遍,为什么,我们来说一下:

1 sip 协议传输层使用rtp

2 rtsp 协议传输层使用rtp

3 onvif协议使用rtsp协议,最终到了rtp协议上

4 webrtc 使用srtp协议,最终又到了rtp协议上

5 国标GB28181使用sip 传输信令,最终又到了rtp协议上

6 dlna协议和miracast协议直播使用rtsp ,传输当然还是rtp

没骗你吧,所以作为一名开发者,如果懂了rtp 协议,能够徒手写出rtp协议,基本就成了你的功力了。其他协议如rtmp,xmpp,等等就都不在话下了。

rtp协议头部一般来说为12字节,

 typedef struct 
{
    /**//* byte 0 */
    unsigned char csrc_len:4;        /**//* expect 0 */
    unsigned char extension:1;        /**//* expect 1, see RTP_OP below */
    unsigned char padding:1;        /**//* expect 0 */
    unsigned char version:2;        /**//* expect 2 */
    /**//* byte 1 */
    unsigned char payload:7;        /**//* RTP_PAYLOAD_RTSP */
    unsigned char marker:1;        /**//* expect 1 */
    /**//* bytes 2, 3 */
    unsigned short seq_no;            
    /**//* bytes 4-7 */
    unsigned  long timestamp;        
    /**//* bytes 8-11 */
    unsigned long ssrc;            /**//* stream number is used here. */
} RTP_FIXED_HEADER;

rtp的头部并非固定12字节,是可以扩展的,具体就看头部的extension是否为1,先来一个rtp h264 的发送实例,发送rtp h264 并且播放。播放在下一篇。

三、show me the code

#include 
#include 
#include 
#include "RTPDefine.h"

//小端转大端
uint32_t little2big(uint32_t le) {

	return (le & 0xff) << 24
		| (le & 0xff00) << 8
		| (le & 0xff0000) >> 8
		| (le >> 24) & 0xff;
}

//大端转小端
uint32_t big2little(uint32_t be)
{
	return ((be >> 24) & 0xff)
		| ((be >> 8) & 0xFF00)
		| ((be << 8) & 0xFF0000)
		| ((be << 24));
}
//小端转大端
uint16_t little2bigs(uint16_t num)
{
	uint16_t swapped = (num >> 8) | (num << 8);
	return swapped;
}

//大端转小端
uint16_t big2littles(uint16_t be)
{
	short swapped = (be << 8) | (be >> 8);
	return swapped;
}
static int start_code_check(char *buf, int len)
{
#define D(x) *(buf+x)
	if (len < 4)
		return 0;
	uint8_t c1 = *buf;
	if ((D(0) | D(1)) == 0)
	{
		switch (D(2))
		{
		case 0:
			return D(3) == 1 ? 4 : -1;
		case 1:
			return 3;
		}
	}
	return 0;
}


 int rtp_packet_init_header(char *rtp_hd, uint8_t pt, uint8_t mark, 
	 uint16_t seq, 
	 uint32_t ts, uint32_t ssrc)
{
	if (rtp_hd == NULL)
	{
		return -1;
	}
	RTP_FIXED_HEADER *h = (RTP_FIXED_HEADER*)rtp_hd;
	h->version = 2;
	h->csrc_len = 0;
	h->extension = 0;
	h->padding = 0;
	h->payload = pt;
	h->marker = mark;
	h->seq_no = little2big(seq);
	h->timestamp = little2big(ts);
	h->ssrc = little2big(ssrc);
	
	return 0;
}

static int rtp_packet_for_h264( char *output, int out_len, char *intput, int int_len, 
	rtp_send_callback call_back, void *arg)
{
	unsigned char fu[2];

	//DBG_INFO("size %d\n",size);
	
	int len = start_code_check(intput, int_len);//start code
	
	if (int_len <=  MAX_RTP_PKT_LENGTH)
	{
		memcpy(output, &intput[len], int_len - len);
		
		out_len = int_len - len;
		if (call_back)
		{
			call_back(arg, output, out_len, 1);//send
		}
		
	}
	else
	{
        unsigned char type = intput[len] & 0x1F;
        unsigned char nri = intput[len] & 0x60;
		int_len -= len;

        fu[0] = 28;        /* FU Indicator; Type = 28 ---> FU-A */
        fu[0] |= nri;
        fu[1] = type;
        fu[1] |= 1 << 7; //start

		int offset = len + 1;
		int_len -= 1;

		int flag = 0;
		int count = (int_len - offset) / MAX_RTP_PKT_LENGTH;
		int left = (int_len -offset) % (MAX_RTP_PKT_LENGTH*count);
		if (left <= 12 && left > 0)
		{
			flag  = 1;
		}
		while (offset + MAX_RTP_PKT_LENGTH + MAX_RTP_PKT_LENGTH*flag < int_len)
		{
			memcpy(output, fu, 2);
			memcpy(&output[2], &intput[offset], MAX_RTP_PKT_LENGTH);

			out_len = MAX_RTP_PKT_LENGTH + 2 + RTP_HEADER_S_LEN;
			if (call_back)
			{
				call_back(arg, output, out_len, 0);//send
			}
			offset += MAX_RTP_PKT_LENGTH;
            fu[1] &= ~(1 << 7);
		}
		fu[1] |= 1 << 6; //end
		memcpy(output, fu, 2);
		memcpy(&output[2], &intput[offset], int_len - offset);
		out_len = int_len - offset + RTP_HEADER_S_LEN + 2;		
		if (call_back)
		{
			call_back(arg, output, out_len, 1);
		}
	}
	return 0;
}

这段代码并不包含网络,是用回调函数来发送的,这样,有利于开发人员可以掌控,而且其实更好的方式是零拷贝,是可以做到的,这一段代码没有做,有兴趣的朋友可以看我另外一篇文章
如何零拷贝
rtp协议的重要性不言而喻,udp 作为逐渐登场的重要角色越来越重要,后面还有rtcp 和 fec ,前面错误纠正,这些实在通用的底层是程序员的核心。如果你想做一个普通的编码人员,最好是掌握核心的协议部分和算法部分,如果你想做管理人员,另当别论。

未完待续

你可能感兴趣的:(操作系统,webrtc,rtp)