上一篇文章我们介绍了关于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. Application Loader下载安装和上传IOS app程序

    如果您安装了最新版的XCode开发环境.对于在4.2及以上版本,Developer/Applications/Utilities目录中已经有ApplicationLoader程序,无需执行以下单独安装 ...

  2. hadoop笔记之Hive的管理(远程登录方式)

    Hive的管理(三) Hive的管理(三) Hive的远程服务 远程服务启动方式 端口号10000 启动方式:hive --service hiveserver (注意:以JDBC或ODBC的程序登录 ...

  3. src 和 href 的区别

    因为理解不深,到写外部加载Javascript文件或者css文件的时候总是需要去找个例子,这样可不好.现在总结下 href 属性规定被链接文档的位置(URL). href是hyperrefresh的缩 ...

  4. Windows下载安装jmeter

    一.下载 jmeter下载地址: http://jmeter.apache.org/download_jmeter.cgi Binaries-apache-jmeter-3.0.zip 二.安装 1. ...

  5. Latex常用包笔记

    1.hyperref 标签包 \usepackage[colorlinks,linkcolor=black,anchorcolor=blue,citecolor=green]{hyperref} 2. ...

  6. 50行实现简易HTTP服务器

    话说由于一直很懒,所以博客好像也没怎么更新...今天有空就写一下吧. 最近在看node.js的时候开始对http协议感兴趣了,毕竟node一开始就是为了做web服务器而产生的.于是试着想了一下大概的思 ...

  7. Windows的历史zt

    原文地址:http://windows.microsoft.com/zh-CN/windows/history#T1=era0 1975–1981:Microsoft 起步 Microsoft 联合创 ...

  8. J2SE知识点摘记(十八)

    Java容器类类库的用途是“保存对象”,并将其划分为两个不同的概念: 1)  Collection . 一组对立的元素,通常这些元素都服从某种规则.List必须保持元素特定的顺序,而Set 不能有重复 ...

  9. Python-第一天

    1. 基础知识 1.1 编码 python默认是ascii编码,不支持中文 在脚本顶部添加 #coding=utf-8,使python支持中文 #!/usr/bin/env python    --- ...

  10. 使用Swift的代理,闭包来封装一个公用协议减少垃圾代码

    iOS开发中,如果不进行适当的封装,使用协议或者继承类来进行开发,你就会遇到传说中的ViewController(以后简称VC) Hell的问题…… 比如说,我们先声网App中为了调用接口,做简单的判 ...