自己动手写RTP服务器——用RTP协议传输TS流-阿里云开发者社区

开发者社区> double2li> 正文

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

简介: 上一篇文章我们介绍了关于RTP协议的知识,那么我们现在就自己写一个简单的传输TS流媒体的RTP服务器吧。 预备知识 关于TS流的格式:TS流封装的具体格式请参考文档ISO/IEC 13818-1。
+关注继续查看

上一篇文章我们介绍了关于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] view plaincopy
 
 
  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] view plaincopy
 
 
  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] view plaincopy
 
 
  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] view plaincopy
 
 
  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] view plaincopy
 
 
  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”,等待几秒后,发现真的可以进行播放啦!

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
服务器已部署SSL开启https协议为什么浏览器仍然提示不安全?
客户反馈服务器已部署SSL,全站开启了https协议访问了,为什么浏览器仍然提示不安全?是证书无效吗?万维景盛工程师检查发现,客户网站虽然已经可以使用https访问了,但网站上仍然还有http协议的js,css,jpg或iframe的资源,因此导致浏览器不出现绿色安全锁。
6323 0
PostgreSQL 10.1 手册_部分 III. 服务器管理_第 28 章 监控数据库活动
第 28 章 监控数据库活动 目录 28.1. 标准 Unix 工具 28.2. 统计收集器 28.2.1. 统计收集配置 28.2.2. 查看统计信息 28.2.3. 统计函数 28.3. 查看锁 28.4. 进度报告 28.4.1. VACUUM进度报告 28.5. 动态追踪 28.5.1. 动态追踪的编译 28.5.2. 内建探针 28.5.3. 使用探针 28.5.4. 定义新探针 一个数据库管理员常常会疑惑,“系统现在正在做什么?”这一章会讨论如何搞清楚这个问题。
947 0
PostgreSQL 10.1 手册_部分 III. 服务器管理_第 28 章 监控数据库活动_28.3. 查看锁
28.3. 查看锁 监控数据库活动的另外一个有用的工具是pg_locks系统表。这样就允许数据库管理员查看在锁管理器里面未解决的锁的信息。例如,这个功能可以被用于: 查看当前所有未解决的锁、在一个特定数据库中的关系上所有的锁、在一个特定关系上所有的锁,或者由一个特定PostgreSQL会话持有的所有的锁。
949 0
手动卸载windows服务
转自博客 http://www.cnblogs.com/Sabre/archive/2009/01/19/1378259.html   使用windows命令行工具:sc。
634 0
移动网络体验的升级——手淘海量移动网络服务的探索
本文PPT来自阿里巴巴高级无线技术专家洪海(花名:孤星)于10月15日在2016年杭州云栖大会上发表的《移动网络体验的升级——手淘海量移动网络服务的探索》。
1562 0
阿里云服务器发布最新的服务等级协议SLA 为全球最高水准
单实例的可用性从99.95%提升至99.975%,多可用区多实例可用性从99.99%提升至99.995%,均为全球最高水准。
2116 0
+关注
double2li
一个在IT行业摸爬滚打的老司机
2870
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
文娱运维技术
立即下载
《SaaS模式云原生数据仓库应用场景实践》
立即下载
《看见新力量:二》电子书
立即下载