自己动手写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] 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. #define TS_PACKET_SIZE 188
  7. #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. 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递增。

[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. // 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文件。

[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. 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”,等待几秒后,发现真的可以进行播放啦!

时间: 2024-08-13 02:51:05

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

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

转自:http://blog.csdn.net/baby313/article/details/7353605 本文会带领着你一步步动手实现一个简单的RTP传输服务器,旨在了解RTP流媒体传输协议以及一些关于多媒体编解码的知识. 关于RTP协议的必备知识 要动手实现一个协议,当然首先需要阅读该协议的文档.RTP协议的文档,有rfc1889.rfc1890.rfc3550,其中rfc3550是现在的版本,另外两个是过期版.这个协议可以在ietf的官网找到:http://tools.ietf.org

自己动手写web服务器一(浏览器的访问信息)

要协议一个web服务器,需要了解http协议,下面我们来看一下当浏览器请求网张的时候向web服务器发送的数据,我使用的是ubuntu 中telent展现一个下过程. 我需要一个简单的网站来演示一下,我装了一个apache.来完成成这个过程.下面为过程展示. 其实,访问一个网站就是浏览器web服务器指定端口发送信息,然后,web服务器向浏览器发送网页的内容, 1.使用telnet连接网站 2.输入请求的页面. GET  /   HTTP/1.0 或者 GET /index.html  HTTP/1

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

// 代码网上抄的 忘记链接了 抱歉哈package upload; import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.HttpURLConnec

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

上一篇文章我们介绍了如何用一个简单的UDP socket搭建一个RTP服务器.我把这份80行的代码呈现到客户面前的时候,就有人不满意了. 还有人在参考的时候会问:“楼主你的TS格式的文件是哪里来的?应该去哪里下载?”“这也太简单了吧,不就是用UDP把TS文件发过去么?就这么几十行代码也算个大作业?”“平时看的视频格式哪有TS的呀,不是avi就是mp4.rmvb什么的,根本不实用啊!” 首先回答问题1: 如果你用过强大的ffmpeg,就可以用一行简单的命令 [plain] view plainco

从头写rtsp服务器-RTSP协议的解析

rtps demo(linux是64位的,windows32位的):rtsp_demo.rar 1.首先服务器收到客户端连接请求,生产 一个RtspClientConnection对象,RtspClientConnection定义详见  从头写rtsp服务器-模块的划分 int rtsp::v_accept(netconnection * n) { netoperation::v_accept(n); printf("client accept\n\n\n"); RtspClientC

关于如何有效避免僵尸进程的一种探索。(动手写自己的http服务器)

关于如何避免僵尸进程.其实方法很多.有很多现成的解决方案.这里给出一个简单的方案.以及背后思考的过程. 设想一种最简单的http服务器: 伪代码: 当接收到一个tcp请求的时候. fork 一个子进程去做工作. 主进程依旧在监听下面要来的请求. 这个模型实际上会导致.因为fork出来的子进程没有回收,并且父进程一直存在没有结束.导致越来越多的僵尸进程出现. 都知道子进程结束的时候会发一个sigchld信号给父进程.于是改进成增加一行代码: signal(SIGCHLD,deal_signal);

自己动手写工具:百度图片批量下载器

开篇:在某些场景下,我们想要对百度图片搜出来的东东进行保存,但是一个一个得下载保存不仅耗时而且费劲,有木有一种方法能够简化我们的工作量呢,让我们在离线模式下也能爽爽地浏览大量的美图呢?于是,我们想到了使用网络抓取去帮我们去下载图片,并且保存到我们设定的文件夹中,现在我们就来看看如何来设计开发一个这样的图片批量下载器. 一.关于网络抓取与爬虫 网络蜘蛛的主要作用是从Internet上不停地下载网络资源.它的基本实现思想就是通过一个或多个入口网址来获取更多的URL,然后通过对这些URL所指向的网络资

基于RTP的h.264视频传输系统设计(一)

一.H.264 的层次介绍 H.264 定义三个层次,每个层次支持一组特定的编码功能,并且依照各个层次指定所指定的功能.基础层次(baselineprofile)支持I 帧和 P 帧[1]的帧内和帧间编码,支持自适应的可变长度的熵编码(CAVLC).主要层次(main profile)支持隔行扫描视频,B帧[2]的帧内编码,使用加权预测的帧内编码和使用上下文的算术编码(CABAV).扩展层次(extendedprofile)不支持隔行扫描视频和CABAC,但增加了码流之间高效的转化模式(SP 和

《自己动手写Docker》书摘之一: Linux Namespace

Linux Namespace 介绍 我们经常听到说Docker 是一个使用了Linux Namespace 和 Cgroups 的虚拟化工具,但是什么是Linux Namespace 它在Docker内是怎么被使用的,说到这里很多人就会迷茫,下面我们就先介绍一下Linux Namespace 以及它们是如何在容器里面使用的. 概念 Linux Namespace 是kernel 的一个功能,它可以隔离一系列系统的资源,比如PID(Process ID),User ID, Network等等.一