UDP RTP头结构

UDP 源数据端口,目的端口各占2个字节,长度也占2个字节,checksum 校检和也占2个字节长度

Rtp头标准长度为12个字节。
其中,rtp协议版本号V占2位,填充标准P占1位,扩展标准X占1位,CSRC计数器CC占4位,这四个共占8位,一个字节。
M标记占1位,有效载荷占7位,共占用1个字节。
序列化占16位,占用2个字节。
时间戳(Timestamp)占32位,占用4个字节。
同步信源(SSRC)占32位,共占用4个字节。
特约信源(CSRC),每个特约信源占32位,可以有0到15个。

MPEG-TS包头信息 详细

Sync byte:同步字节,值为0x47;
Transport error indicator:传输错误指示位,置1时,表示传送包中至少有一个不可纠正的错误位。
Payload unit start indicator:负载单元起始指标位,表示TS包的有效净荷以PES/PSI包的第一个字节开始,举个例子,一个PES包可能由多个TS包构成,第一个TS包的负载单元起始指标位才会被置位。
Transport priority:传输优先级,表明该包比同个PID的但未置位的TS包有更高的优先级。
PID:该TS包的ID号,如果净荷是PAT包,则PID固定为0x00。
Transport scrambling control:传输加扰控制位
Adaption field control:自适应调整域控制位,置位则表明该TS包存在自适应调整字段。
Continuity counter:连续计数器,随着具有相同PID的TS包的增加而增加,达到最大时恢复为0,如果两个连续相同PID的TS包具有相同的计数,则表明这两个包是一样的,只取一个解析即可。
Payload:负载内容,可能为PAT/PMT/PES。data_byte为1B长度的数据,为负载字节。

源码解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
#pragma comment(lib, "ws2_32.lib") 

#pragma pack(1)

/*
* [memo] FFmpeg stream Command:
* ffmpeg -re -i sintel.ts -f mpegts udp://127.0.0.1:8880
* ffmpeg -re -i sintel.ts -f rtp_mpegts udp://127.0.0.1:8880
*/

typedef struct RTP_FIXED_HEADER{
/* byte 0 */
unsigned char csrc_len:4; /* expect 0 */
unsigned char extension:1; /* expect 1 */
unsigned char padding:1; /* expect 0 */
unsigned char version:2; /* expect 2 */
/* byte 1 */
unsigned char payload:7;
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;

typedef struct MPEGTS_FIXED_HEADER {
unsigned sync_byte: 8;
unsigned transport_error_indicator: 1;
unsigned payload_unit_start_indicator: 1;
unsigned transport_priority: 1;
unsigned PID: 13;
unsigned scrambling_control: 2;
unsigned adaptation_field_exist: 2;
unsigned continuity_counter: 4;
} MPEGTS_FIXED_HEADER;



int simplest_udp_parser(int port)
{
WSADATA wsaData;//https://baike.baidu.com/item/WSADATA/3031763?fr=aladdina
//这个结构被用来存储被WSAStartup函数调用后返回的Windows Sockets数据。
WORD sockVersion = MAKEWORD(2,2);//声明调用不同的Winsock版本。例如MAKEWORD(2,2)就是调用2.2版 MAKEWORD(2,2)构成一个16位无符号的整形 MAKEWORD(a,b) ((WORD) (((BYTE) (a)) | ((WORD) ((BYTE) (b))) << 8))
int cnt=0;

//FILE *myout=fopen("output_log.txt","wb+");
FILE *myout=stdout;

FILE *fp1=fopen("output_dump.ts","wb+");

if(WSAStartup(sockVersion, &wsaData) != 0){
return 0;
}

SOCKET serSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(serSocket == INVALID_SOCKET){
printf("socket error !");
return 0;
}

sockaddr_in serAddr;//sockaddr_in易于操作 与sockaddr大小一致 可强制转换为sockaddr
serAddr.sin_family = AF_INET;
serAddr.sin_port = htons(port);//网络字节顺序 变成大端
serAddr.sin_addr.S_un.S_addr = INADDR_ANY;
if(bind(serSocket, (sockaddr *)&serAddr, sizeof(serAddr)) == SOCKET_ERROR){
printf("bind error !");
closesocket(serSocket);
return 0;
}

sockaddr_in remoteAddr;
int nAddrLen = sizeof(remoteAddr);

//How to parse?
int parse_rtp=1;
int parse_mpegts=1;

printf("Listening on port %d\n",port);

char recvData[10000];
while (1){

int pktsize = recvfrom(serSocket, recvData, 10000, 0, (sockaddr *)&remoteAddr, &nAddrLen);
if (pktsize > 0){
//printf("Addr:%s\r\n",inet_ntoa(remoteAddr.sin_addr));
//printf("packet size:%d\r\n",pktsize);
//Parse RTP
//
if(parse_rtp!=0){
char payload_str[10]={0};
RTP_FIXED_HEADER rtp_header;
int rtp_header_size=sizeof(RTP_FIXED_HEADER);
//RTP Header
memcpy((void *)&rtp_header,recvData,rtp_header_size);

//RFC3551
char payload=rtp_header.payload;
switch(payload){
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
case 11:
case 12:
case 13:
case 14:
case 15:
case 16:
case 17:
case 18: sprintf(payload_str,"Audio");break;
case 31: sprintf(payload_str,"H.261");break;
case 32: sprintf(payload_str,"MPV");break;
case 33: sprintf(payload_str,"MP2T");break;
case 34: sprintf(payload_str,"H.263");break;
case 96: sprintf(payload_str,"H.264");break;
default:sprintf(payload_str,"other");break;
}

unsigned int timestamp=ntohl(rtp_header.timestamp);//将网络字节顺序 转成本机 无符号长整
unsigned int seq_no=ntohs(rtp_header.seq_no);//无符号短整

fprintf(myout,"[RTP Pkt] %5d| %5s| %10u| %5d| %5d|\n",cnt,payload_str,timestamp,seq_no,pktsize);

//RTP Data
char *rtp_data=recvData+rtp_header_size;
int rtp_data_size=pktsize-rtp_header_size;
fwrite(rtp_data,rtp_data_size,1,fp1);

//Parse MPEGTS
if(parse_mpegts!=0&&payload==33){
MPEGTS_FIXED_HEADER mpegts_header;
for(int i=0;i<rtp_data_size;i=i+188){//一段固定188
if(rtp_data[i]!=0x47)//同步字 固定0x47
break;
//MPEGTS Header
//memcpy((void *)&mpegts_header,rtp_data+i,sizeof(MPEGTS_FIXED_HEADER));
fprintf(myout," [MPEGTS Pkt]\n");
}
}

}else{
fprintf(myout,"[UDP Pkt] %5d| %5d|\n",cnt,pktsize);
fwrite(recvData,pktsize,1,fp1);
}

cnt++;
}
}
closesocket(serSocket);
WSACleanup();
fclose(fp1);

return 0;
}