自己动手写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文件,内容如下:
- #include <stdio.h>
- #include <string.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #define TS_PACKET_SIZE 188
- #define MTU 1500
说明:包含一些必要的头文件,并且定义了TS Packet的长度(188 bytes),MTU的限制(1500 bytes)。
- struct rtp_header{
- unsigned char cc:4;
- unsigned char x:1;
- unsigned char p:1;
- unsigned char v:2;
- unsigned char pt:7;
- unsigned char m:1;
- unsigned short sequence_number;
- unsigned int timestamp;
- unsigned int ssrc;
- };
- void init_rtp_header(struct rtp_header *h){
- h->v = 2;
- h->p = 0;
- h->x = 0;
- h->cc = 0;
- h->m = 0;
- h->pt = 33;
- h->sequence_number = 123;
- h->timestamp = 123;
- h->ssrc = 123;
- }
说明:这里定义了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递增。
- void sequence_number_increase(struct rtp_header *header){
- unsigned short sequence = ntohs(header->sequence_number);
- sequence++;
- header->sequence_number = htons(sequence);
- }
说明:这个函数的目的就是让sequence_number加一,还是由于本机与网络字节序不同的原因,所以显得略微复杂些。
- int main(){
- // RTP Packet we will send
- char buf[MTU];
- unsigned int count = 0;
- // Init RTP Header
- init_rtp_header((struct rtp_header*)buf);
- count = sizeof(struct rtp_header);
- // Init socket
- int sock = socket(AF_INET, SOCK_DGRAM, 0);
- struct sockaddr_in dest_addr;
- dest_addr.sin_family=AF_INET;
- dest_addr.sin_port = htons(6666);
- dest_addr.sin_addr.s_addr = INADDR_ANY;
- bzero(&(dest_addr.sin_zero),8);
- // Open TS file
- 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文件。
- while(!feof(ts_file)){
- int read_len = fread(buf+count, 1, TS_PACKET_SIZE, ts_file);
- if(*(buf+count) != 0x47){
- fprintf(stderr, "Bad sync header!\n");
- continue;
- }
- count += read_len;
- if (count + TS_PACKET_SIZE > MTU){// We should send
- sequence_number_increase((struct rtp_header*)buf);
- sendto(sock, buf, count, 0, (const struct sockaddr*)&dest_addr, sizeof(dest_addr));
- count = sizeof(struct rtp_header);
- usleep(10000);
- }
- }
- 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流的更多相关文章
- 自己动手写RTP服务器——关于RTP协议
转自:http://blog.csdn.net/baby313/article/details/7353605 本文会带领着你一步步动手实现一个简单的RTP传输服务器,旨在了解RTP流媒体传输协议以及 ...
- 自己动手写http服务器——主程序(三)
功能:目前只支持对资源的访问. 使用的模型:多线程加epoll,与传统的一个连接请求一个线程处理不同的是,这个模型只为那些需要服务的连接请求调用线程进行处理, 整个模型的大致流程 创建一个线程持对象, ...
- 自己动手写http服务器——处理http连接(二)
关于http报文格式请看这篇文章 //http_conn.h #ifndef HTTPCONNECTION_H #define HTTPCONNECTION_H #include <unistd ...
- 自己动手写http服务器——线程池(一)
创建一个线程池,每有一个连接对象就将它添加到工作队列中,线程池中的线程通过竞争来取得任务并执行它(它是通过信号量实现的). //filename threadpool.h #ifndef THREAD ...
- 用java pyhont通过HTTP协议传输文件流
// 代码网上抄的 忘记链接了 抱歉哈package upload; import java.io.BufferedReader; import java.io.DataOutputStream; i ...
- 自己动手写RTP服务器——传输所有格式的视频
上一篇文章我们介绍了如何用一个简单的UDP socket搭建一个RTP服务器.我把这份80行的代码呈现到客户面前的时候,就有人不满意了. 还有人在参考的时候会问:“楼主你的TS格式的文件是哪里来的?应 ...
- 利用html 5 websocket做个山寨版web聊天室(手写C#服务器)
在之前的博客中提到过看到html5 的websocket后很感兴趣,终于可以摆脱长轮询(websocket之前的实现方式可以看看Developer Works上的一篇文章,有简单提到,同时也说了web ...
- Git服务器、http协议及XCode
本来费了老鼻子牛劲搭好了SVN,可以通过web进行访问,也弄好了eclipse和XCode,结果几个开发的同事说要上git,悲了个催,又开始折腾git. 因为公司只有一个公网的http出口,因此开始了 ...
- Ubuntu搭建NFS服务器,NFS协议详细分析
目录 1. Ubuntu搭建NFS服务器 2. NFS协议分析 2.1 实验拓扑: 2.2 在kali抓包分析 1. Ubuntu搭建NFS服务器 NFS(Network FileSystem,网 ...
随机推荐
- BZOJ 4016: [FJOI2014]最短路径树问题( 最短路 + 点分治 )
先跑出最短路的图, 然后对于每个点按照序号从小到大访问孩子, 就可以搞出符合题目的树了. 然后就是经典的点分治做法了. 时间复杂度O(M log N + N log N) -------------- ...
- 汇编语言学习系列 for循环实现
假如汇编语言要实现如下C语言的功能,编译环境Ubuntu14.04(32位). #include<stdio.h> int fact_for(int n) { int i; ; ; i & ...
- shell脚本定时备份数据库
脚本代码: 新建文件back_db.sh #!/bin/bash TODAYTIME="`date +%Y%m%d`" DBNAME="test mysql" ...
- 关于jQuery中toggle()函数的使用
今天遇到一个有趣的例子,将它记录下来. 一个一级菜单,里边有一个二级菜单,二级菜单是通过锚点来链接页面元素的.想要实现的效果是当点击锚点时,页面链接到相应锚点,同时二级菜单隐藏,再点击一级菜单时,继续 ...
- C#两路list数组归并去重
两个相同类型已排序数据进行合并,虽然list数组中有AddRange方法,但它只是把第二个数组从第一个数组末尾插入,假如两个数组有重复数据,保存进去.还有Union方法合并去重,首先会从第一个数组进行 ...
- background:url 的使用方法
#pingfen li{ width:27px; float:left; height:28px; cursor:pointer; background:url( ; list-style:none; ...
- Android学习笔记27:网格视图GridView的使用
网格视图GridView的排列方式与矩阵类似,当屏幕上有很多元素(文字.图片或其他元素)需要按矩阵格式进行显示时,就可以使用GridView控件来实现. 本文将以一个具体的实例来说明如何使用GridV ...
- 50行实现简易HTTP服务器
话说由于一直很懒,所以博客好像也没怎么更新...今天有空就写一下吧. 最近在看node.js的时候开始对http协议感兴趣了,毕竟node一开始就是为了做web服务器而产生的.于是试着想了一下大概的思 ...
- C# Socket SSL通讯笔记
一.x.509证书 1.制作证书 先进入到vs2005的命令行状态,即:开始-->程序-->Microsoft Visual Studio 2005-->Visual Studio ...
- 常用Vxworks编程API
vxWorks编程API 一.官方的Program Guide 位于安装目录下:\docs\vxworks\guide\index.html 二.常用的库: #i nclude "taskL ...