我会开辟一个空间rtp协议,包含vp9 h264 h265 aac opus 等rtp实例,包含实例代码,让看文章的人得到真实的提升
rtp协议为实时传输协议 real transfer protocol
webrtc 已经被w3c(万维网联盟) 和IETF(互联网工程任务组)宣布成为正式标准,webrtc 底层使用 rtp 协议来传输音视频内容,同时可以使用websocket协议和http等标准协议来传输信令。rtp其实可以作为传输层来看,也可以看成半传输层半应用层协议。
rtp协议太重要了,如果你懂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 并且播放。播放在下一篇。
#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 ,前面错误纠正,这些实在通用的底层是程序员的核心。如果你想做一个普通的编码人员,最好是掌握核心的协议部分和算法部分,如果你想做管理人员,另当别论。