自己动手写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,网 ...
随机推荐
- [C++空间分配]new运算符、operator new、placement new的区别于联系
先科普一下: 1. new的执行过程: (1)通过operator new申请内存 (2)使用placement new调用构造函数(内置类型忽略此步) (3)返回内存指针 2. new和malloc ...
- leetcode First Missing Positive python
class Solution(object): def firstMissingPositive(self, nums): """ :type nums: List[in ...
- Linux学习之进程管理
|-进程管理 进程常用命令 |- w查看当前系统信息 |- ps进程查看命令 |- kill终止进程 |- 一个存放内存中的特殊目录/p ...
- Git 系列(三):建立你的第一个 Git 仓库
现在是时候学习怎样创建你自己的 Git 仓库了,还有怎样增加文件和完成提交. 在本系列前面的文章中,你已经学习了怎样作为一个最终用户与 Git 进行交互:你就像一个漫无目的的流浪者一样偶然发现了一个开 ...
- SharedPreferences最佳实践
转:http://blog.csdn.net/xushuaic/article/details/24513599 笔记摘要:该文章是我在Android Weekly中看到的,以前也一直用SharedP ...
- Flink资料(5) -- Job和调度
该文档翻译自Jobs and Scheduling ----------------------------------------------- 该文档简单描述了Flink是如何调度Job的,以及如 ...
- codeforces 665D Simple Subset
题目链接 给一个数列, 让你选出其中的m个数, 使得选出的数中任意两个数之和都为质数, m尽可能的大. 首先, 除了1以外的任意两个相同的数相加结果都不是质数. 然后, 不考虑1的话, 选出的数的个数 ...
- 编写一个程序实现strcpy函数的功能
#include <stdio.h> #include <string.h> #define N 5 char *mycpy(char *s1, char *s2) { //数 ...
- [问题解决] Could not update ICEauthority file /home/username/.ICEauthority
错误: Could not update ICEauthority file /home/username/.ICEauthority 发生场景: 虚拟机下的ubuntu server12.04 解决 ...
- delphi 修改代码补全的快捷键(由Ctrl+Space 改为 Ctrl + alt + Space)
delphi 的IDE快捷键与输入法切换键中突,以往的解决方法是下载一个ImeTool修改 windows 系统的快捷键 在 xp win7 都好使,但在win 10经常是修改完后,重启又失效了. 本 ...