上一篇文章我们介绍了关于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文件,内容如下:

  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <sys/types.h>
  4. #include <sys/socket.h>
  5. #include <netinet/in.h>
  6. #define TS_PACKET_SIZE 188
  7. #define MTU 1500

说明:包含一些必要的头文件,并且定义了TS Packet的长度(188 bytes),MTU的限制(1500 bytes)。

  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. unsigned char pt:7;
  7. unsigned char m:1;
  8. unsigned short sequence_number;
  9. unsigned int timestamp;
  10. unsigned int ssrc;
  11. };
  12. void init_rtp_header(struct rtp_header *h){
  13. h->v = 2;
  14. h->p = 0;
  15. h->x = 0;
  16. h->cc = 0;
  17. h->m = 0;
  18. h->pt = 33;
  19. h->sequence_number = 123;
  20. h->timestamp = 123;
  21. h->ssrc = 123;
  22. }

说明:这里定义了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递增。

  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加一,还是由于本机与网络字节序不同的原因,所以显得略微复杂些。

  1. int main(){
  2. // RTP Packet we will send
  3. char buf[MTU];
  4. unsigned int count = 0;
  5. // Init RTP Header
  6. init_rtp_header((struct rtp_header*)buf);
  7. count = sizeof(struct rtp_header);
  8. // Init socket
  9. int sock = socket(AF_INET, SOCK_DGRAM, 0);
  10. struct sockaddr_in dest_addr;
  11. dest_addr.sin_family=AF_INET;
  12. dest_addr.sin_port = htons(6666);
  13. dest_addr.sin_addr.s_addr = INADDR_ANY;
  14. bzero(&(dest_addr.sin_zero),8);
  15. // Open TS file
  16. 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文件。

  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. if (count + TS_PACKET_SIZE > MTU){// We should send
  9. sequence_number_increase((struct rtp_header*)buf);
  10. sendto(sock, buf, count, 0, (const struct sockaddr*)&dest_addr, sizeof(dest_addr));
  11. count = sizeof(struct rtp_header);
  12. usleep(10000);
  13. }
  14. }
  15. 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”,等待几秒后,发现真的可以进行播放啦!

自己动手写RTP服务器——用RTP协议传输TS流的更多相关文章

  1. 自己动手写RTP服务器——关于RTP协议

    转自:http://blog.csdn.net/baby313/article/details/7353605 本文会带领着你一步步动手实现一个简单的RTP传输服务器,旨在了解RTP流媒体传输协议以及 ...

  2. 自己动手写http服务器——主程序(三)

    功能:目前只支持对资源的访问. 使用的模型:多线程加epoll,与传统的一个连接请求一个线程处理不同的是,这个模型只为那些需要服务的连接请求调用线程进行处理, 整个模型的大致流程 创建一个线程持对象, ...

  3. 自己动手写http服务器——处理http连接(二)

    关于http报文格式请看这篇文章 //http_conn.h #ifndef HTTPCONNECTION_H #define HTTPCONNECTION_H #include <unistd ...

  4. 自己动手写http服务器——线程池(一)

    创建一个线程池,每有一个连接对象就将它添加到工作队列中,线程池中的线程通过竞争来取得任务并执行它(它是通过信号量实现的). //filename threadpool.h #ifndef THREAD ...

  5. 用java pyhont通过HTTP协议传输文件流

    // 代码网上抄的 忘记链接了 抱歉哈package upload; import java.io.BufferedReader; import java.io.DataOutputStream; i ...

  6. 自己动手写RTP服务器——传输所有格式的视频

    上一篇文章我们介绍了如何用一个简单的UDP socket搭建一个RTP服务器.我把这份80行的代码呈现到客户面前的时候,就有人不满意了. 还有人在参考的时候会问:“楼主你的TS格式的文件是哪里来的?应 ...

  7. 利用html 5 websocket做个山寨版web聊天室(手写C#服务器)

    在之前的博客中提到过看到html5 的websocket后很感兴趣,终于可以摆脱长轮询(websocket之前的实现方式可以看看Developer Works上的一篇文章,有简单提到,同时也说了web ...

  8. Git服务器、http协议及XCode

    本来费了老鼻子牛劲搭好了SVN,可以通过web进行访问,也弄好了eclipse和XCode,结果几个开发的同事说要上git,悲了个催,又开始折腾git. 因为公司只有一个公网的http出口,因此开始了 ...

  9. Ubuntu搭建NFS服务器,NFS协议详细分析

    目录 1. Ubuntu搭建NFS服务器 2. NFS协议分析 2.1 实验拓扑: 2.2 在kali抓包分析 1. Ubuntu搭建NFS服务器 ​ NFS(Network FileSystem,网 ...

随机推荐

  1. Javascript 缓冲运动——逐行分析代码,让你轻松了解缓冲运动的原理

    看过上一篇关于Javascript 匀速运动文章的朋友相信对于运动已经有了初步的了解 接下来 讲一下关于缓冲运动的原理 ,我会逐行分析代码,代码简单易懂,能马上理解其中的原理,适用于初学者. #div ...

  2. how to install git 1.8 rpm

    git版本在低于1.8之前,对于私有项目会出现401的pull失败错误,只能通过升级git版本来解决 It appears that git18 is no longer available from ...

  3. javascript 动态创建表格

    <html> <head> <script> function createTable(rows,lines){ this.rows=rows; this.line ...

  4. Android 初步

    Android 四大组件: 1.Activity -------构建应用程序界面 /*2.Intent--------程序之间传输数据*/ 2.BroadcastReciever--------用来接 ...

  5. windbg命令学习2

    一.windbg查看内存命令: 当我们在调试器中分析问题时, 经常需要查看不同内存块的内容以分析产生的原因, 并且在随后验证所做出的假设是否正确. 由于各个对象的状态都是保存在内存中的, 因此内存的内 ...

  6. 30款javascript脚本插件 jquery插件大全

      Shifty Nav - a Fully Responsive JS CSS3 Mega Menu Show Demo Shifty Nav is a fully responsive CSS3 ...

  7. 无线功率 mW 和 dBm 的换算

    无线电发射机输出的射频信号,通过馈线(电缆)输送到天线,由天线以电磁波形式辐射出去.电磁波到达接收地点后,由天线接收下来(仅仅接收很小很小一部分功率),并通过馈线送到无线电接收机.因此在无线网络的工程 ...

  8. IP地址获取到为0:0:0:0:0:0:0:1

    引用 13 楼 oXiaoShe 的回复: Quote: 引用 11 楼 ahjsdzm 的回复: [Quote=引用 9 楼 huazaiyou 的回复:]最近在进行web开发时,遇到了reques ...

  9. 柯南君:看大数据时代下的IT架构(9)消息队列之RabbitMQ--案例(RPC起航)

    二.Remote procedure call (RPC)(using the Java client) 三.Client interface(客户端接口) 为了展示一个RPC服务是如何使用的,我们将 ...

  10. JSON 解析器。JSON.stringify和JSON.parse

    以前用的是JavaScript  的eval. 现在JSON 提供了JSON.stringify和JSON.parse两个函数. JSON.parse用于从一个字符串中解析出json对象. JSON. ...