自己动手写RTP服务器——用RTP协议传输TS流

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

⾃⼰动⼿写RTP服务器——⽤RTP协议传输TS流
上⼀篇⽂章我们介绍了关于RTP协议的知识,那么我们现在就⾃⼰写⼀个简单的传输TS流媒体的RTP服务器吧。

预备知识
关于TS流的格式:TS流封装的具体格式请参考⽂档ISO/IEC 13818-1。

这⾥我们只需要了解⼀些简单的信息就好。

⾸先TS流是有许多的TS Packet组成的,每个TS Packet的长度固定为188 bytes,每个packet都是以sync_byte:0x47开头。

MTU(Maximum Transmission Unit):最⼤传输单元。

是指⼀种通信协议的某⼀层上⾯所能通过的最⼤数据包⼤⼩(以字节为单位)。

最⼤传输单元这个参数通常与通信接⼝有关(⽹络接⼝卡、串⼝等)。

例如:以太⽹⽆法接收⼤于1500 字节的数据包。

参考代码
下⾯我会把⾃⼰写的简单的代码贴出来,并且⼀步步地说明。

新建main.c⽂件,内容如下:
[cpp]
1. #include <stdio.h>
2. #include <string.h>
3. #include <sys/types.h>
4. #include <sys/socket.h>
5. #include <netinet/in.h>
6.
7. #define TS_PACKET_SIZE 188
8. #define MTU 1500
说明:包含⼀些必要的头⽂件,并且定义了TS Packet的长度(188 bytes),MTU的限制(1500 bytes)。

[cpp]
1. struct rtp_header{
2. unsigned char cc:4;
3. unsigned char x:1;
4. unsigned char p:1;
5. unsigned char v:2;
6.
7. unsigned char pt:7;
8. unsigned char m:1;
9.
10. unsigned short sequence_number;
11. unsigned int timestamp;
12. unsigned int ssrc;
13. };
14.
15. void init_rtp_header(struct rtp_header *h){
16. h->v = 2;
17. h->p = 0;
18. h->x = 0;
19. h->cc = 0;
20. h->m = 0;
21. h->pt = 33;
22. h->sequence_number = 123;
23. h->timestamp = 123;
24. h->ssrc = 123;
25. }
说明:这⾥定义了RTP Header的结构体,以及初始化的⽅法。

这⾥⽤到了位域,这是实现协议的时候常常会⽤到的⽅法。

需要注意的是:
你会发现这⾥定义RTP Header的时候,上⼀篇讲到的具体顺序不同。

原因是本机和⽹络字节流的顺序相反,如果按照v p x cc的顺序来定义⼀个byte,在这个byte内部v p x cc就会按照从低位到⾼位的顺序放置;⽽在RTP流中,应该是顺序从⾼位到低位放置的。

所以每个byte我都把顺序做了倒置。

初始化RTP Header的函数的初始化值的意义请参考rfc3550。

为了实现简单,其中的sequence_number、timestamp、ssrc,都是随意填写的。

在发送包的时候需要将sequence_number递增。

[cpp]
1. void sequence_number_increase(struct rtp_header *header){
2. unsigned short sequence = ntohs(header->sequence_number);
3. sequence++;
4. header->sequence_number = htons(sequence);
5. }
说明:这个函数的⽬的就是让sequence_number加⼀,还是由于本机与⽹络字节序不同的原因,所以显得略微复杂些。

[cpp]
1. int main(){
2. // RTP Packet we will send
3. char buf[MTU];
4. unsigned int count = 0;
5.
6. // Init RTP Header
7. init_rtp_header((struct rtp_header*)buf);
8. count = sizeof(struct rtp_header);
9.
10. // Init socket
11. int sock = socket(AF_INET, SOCK_DGRAM, 0);
12. struct sockaddr_in dest_addr;
13.
14. dest_addr.sin_family=AF_INET;
15. dest_addr.sin_port = htons(6666);
16. dest_addr.sin_addr.s_addr = INADDR_ANY;
17. bzero(&(dest_addr.sin_zero),8);
18.
19. // Open TS file
20. FILE *ts_file = fopen("/home/baby/Videos/480p.ts", "r+");
说明:终于到了main函数了,main函数的开始很简单,四个部分的初始化:代表RTP Packet的buffer,RTP Header,Socket,TS流媒体⽂件。

如果你⼿头没有现成的TS⽂件,可以⽤ffmpeg转码得到⼀个ts⽂件:“ffmpeg -i video.xxx video.ts”,其中 video.xxx 表⽰输⼊的视频⽂件,video.ts 为输出的TS⽂件。

[cpp]
1. while(!feof(ts_file)){
2. int read_len = fread(buf+count, 1, TS_PACKET_SIZE, ts_file);
3. if(*(buf+count) != 0x47){
4. fprintf(stderr, "Bad sync header!\n");
5. continue;
6. }
7. count += read_len;
8.
9. if (count + TS_PACKET_SIZE > MTU){// We should send
10. sequence_number_increase((struct rtp_header*)buf);
11. sendto(sock, buf, count, 0, (const struct sockaddr*)&dest_addr, sizeof(dest_addr));
12. count = sizeof(struct rtp_header);
13. usleep(10000);
14. }
15. }
16.
17. fclose(ts_file);
说明:⼀切就绪后就可以不断的⽤UDP发送RTP Packet了。

每次从ts_file中读取188 bytes,附加到buf之后,如果buf的长度还没⽤到达MTU的限制,那么就继续添加,否则就将buf发送出去。

每次发送会将sequence_number加⼀,并且间隔10000微秒。

当然这只是个简单的例⼦,实际发送视频是要根据时间戳的。

测试
短短⼏⼗⾏代码是否就能完成⼀个RTP服务器?我们需要⽤实验来验证。

我的测试环境是Linux,⽤gcc编译通过,使⽤VLC(MPlayer 测试也可以通过了)作为接收端。

⾸先启动我们的发送端程序,然后再执⾏“vlc rtp://127.0.0.1:6666”,等待⼏秒后,发现真的可以进⾏播放啦!。

相关文档
最新文档