当前位置: 首页 > news >正文

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

转自:http://blog.csdn.net/baby313/article/details/7353605

 

本文会带领着你一步步动手实现一个简单的RTP传输服务器,旨在了解RTP流媒体传输协议以及一些关于多媒体编解码的知识。

 

关于RTP协议的必备知识

要动手实现一个协议,当然首先需要阅读该协议的文档。RTP协议的文档,有rfc1889、rfc1890、rfc3550,其中rfc3550是现在的版本,另外两个是过期版。这个协议可以在ietf的官网找到:http://tools.ietf.org/html/rfc3550

RTP packet

RTP是基于UDP协议的,RTP服务器会通过UDP协议,通常每次会发送一个RTP packet。客户端通过解析RTP packet,读取其中的数据然后进行播放了。

RTP packet的结构如下:

  1. RTP Header:RTP 包的头部
  2. contributing sources:个数为0-n个,所以可以为空。具体定义参考rfc3550
  3. RTP payload:即RTP要传输的数据

RTP Header

这是RTP流的头部,在网上搜索RTP格式,就会搜到很多文章介绍这个头部的定义。我们这里参考rfc3550的定义,在5.1节(http://tools.ietf.org/html/rfc3550#section-5.1)。

    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |V=2|P|X|  CC   |M|     PT      |       sequence number         |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                           timestamp                           |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |           synchronization source (SSRC) identifier            |
   +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
   |            contributing source (CSRC) identifiers             |
   |                             ....                              |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

每行是32 bits,由此可以直观看到每个表示部分所占的位数。简单介绍一下:

V(version):2 bits,RTP的版本,这里统一为2

P(padding):1 bit,如果置1,在packet的末尾被填充,填充有时是方便一些针对固定长度的算法的封装

X(extension):1 bit,如果置1,在RTP Header会跟着一个header extension

CC(CSRC count): 4 bits,表示头部后contributing sources的个数

M(marker): 1 bit,具体这位的定义会在一个profile里

PT(playload type): 7 bits,表示所传输的多媒体的类型,对应的编号在另一份文档rfc3551中有列出(http://tools.ietf.org/html/rfc3551)

sequence number: 16 bits,每个RTP packet的sequence number会自动加一,以便接收端检测丢包情况

timestamp: 32 bits,时间戳

SSRC: 32 bits,同步源的id,没两个同步源的id不能相同

CSRC: 上文说到,个数由CC指定,范围是0-15

 

以上的一些概念是一些要实现RTP服务器所必备的知识。介绍的非常简略,详细的定义还是要参考rfc3550原文。

动手实践

我们既然已经知道了RTP packet的结构,那么我们以前用到的RTP流是否也是这样的结构呢?如何验证呢?接下来,我们就一步步验证RTP流的结构。

我们知道RTP是基于UDP协议的,那么我们就先做一个简单的UDP接受端,看看我们可以从RTP服务器接受到什么信息。要实现这个接受端,你需要有一定的网络编程经验,至于具体到操作系统、编程环境、开发语言等都不限制。为了简单,我这里用Python给出一个小小的例子程序。

 

[python]  view plain  copy
 
  1. import socket  
  2.   
  3. # Build a socket to receive data from RTP server.  
  4. # Here we use SOCK_DGRAM, because RTP is on UDP.  
  5. sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  
  6. sock.bind(("localhost", 6666))  
  7.   
  8. for i in range(5):  
  9.         # We just get 16 bytes to analyze the RTP Header.  
  10.         buf = sock.recv(16)  
  11.   
  12.         # Output the result in octal.  
  13.         for c in buf:  
  14.                 print "%x" % ord(c),  
  15.         print  
  16.   
  17. sock.close()  


这就是接受程序啦,非常短小,而且有简单注释,这里就不解释了。

 

接受端已经做好了,那么去哪里找RTP服务器作发送端呢?你可以用一些搭建流媒体服务器的工具,我这里选用的是强大的VLC。关于VLC搭建流媒体服务器的方法,请参考我前面的文章基于移动平台的多媒体框架——用VLC搭建简单的流媒体服务器。这里需要注意几个配置的地方,一是选择Destination的时候要选择RTP而不要选择RTSP,然后地址可以填写本机ip地址或直接写localhost,端口号填写的要和接受端一致,这里是6666。配置好之后的string应该类似于:

:sout=#rtp{dst=localhost,port=6666,mux=ts} :no-sout-rtp-sap :no-sout-standard-sap :ttl=1

服务端配置完成之后,开始Stream。这时打开接受端,就会接受到一些数据,我接收到的数据开头是:

80 a1 20 43 8c cf 76 3c 93 59 d 74 47 0 44 10
80 a1 20 44 8c cf 79 4b 93 59 d 74 47 40 42 36
80 a1 20 45 8c cf 7d 36 93 59 d 74 47 0 44 1a
80 a1 20 46 8c cf 81 21 93 59 d 74 47 40 45 1a
80 a1 20 47 8c cf 85 c 93 59 d 74 47 0 45 1b
这是十六进制的表示。我们依照上面的Header的格式对其进行解读:
第一个byte 80 表示:

V(version)=2

P(padding)=0

X(extension)=0

CC(CSRC count)=0

第二个byte a1 表示:

M(marker)=1

PT(playload type)=33(对照rfc3551可以发现,33表示MP2T AV,正是我们用VLC Stream的格式类型)

后面的2bytes的sequence number我们可以直观的看出是在加一,4bytes的timestamp也是在不断递增的。再之后的93 59 d 74就是SSRC id了,由于CC为0,所以没有CCRC。再之后的几位都是RTP所要传输的数据了。

总结

对RTP协议的熟悉是实现它的基础。这里我只是做一个简单的介绍,需要详细了解,读官方的文档是必不可少的步骤。

通过写一个小程序打印出RTP流中具体的数据,并没有对实现RTP服务器有直接帮助。但是可以让你对协议本身以及编程环境更加熟悉,也方便了以后实现过程中进行调试。不论你在什么环境用什么语言实现,都强烈建议写一个这样的小程序。

转载于:https://www.cnblogs.com/x_wukong/p/5581937.html

相关文章:

  • [转]ASP.NET 成员资格 Part.1(API)
  • uint8_t / uint16_t / uint32_t /uint64_t 是什么数据类型 - 大总结,看完全明白了
  • Linux用户和权限管理
  • iOS 宏(define)与常量(const)的正确使用
  • linux系统安装python2.7
  • phoenixframework自动化测试平台webUI模块执行js代码示例
  • Spark VS Hadoop:两大大数据分析系统深度解读
  • Microphone Array Post-Filtering
  • 团队项目——工大助手界面(查询部分)
  • linux系统调优
  • last命令
  • easyui扩展tabs
  • 初心大陆-----python宝典   第三章
  • Navicat远程连接MySQL数据库
  • 【Android开发日记】Popupwindow 完美demo
  • css的样式优先级
  • gf框架之分页模块(五) - 自定义分页
  • iOS帅气加载动画、通知视图、红包助手、引导页、导航栏、朋友圈、小游戏等效果源码...
  • Map集合、散列表、红黑树介绍
  • MD5加密原理解析及OC版原理实现
  • nginx(二):进阶配置介绍--rewrite用法,压缩,https虚拟主机等
  • nodejs调试方法
  • PAT A1120
  • Python爬虫--- 1.3 BS4库的解析器
  • Travix是如何部署应用程序到Kubernetes上的
  • UMLCHINA 首席专家潘加宇鼎力推荐
  • 从 Android Sample ApiDemos 中学习 android.animation API 的用法
  • 动态规划入门(以爬楼梯为例)
  • ------- 计算机网络基础
  • 浏览器缓存机制分析
  • 浅谈web中前端模板引擎的使用
  • 区块链将重新定义世界
  • 让你的分享飞起来——极光推出社会化分享组件
  • 入门级的git使用指北
  • 深入浏览器事件循环的本质
  • 突破自己的技术思维
  • 微服务框架lagom
  • 用quicker-worker.js轻松跑一个大数据遍历
  • 在electron中实现跨域请求,无需更改服务器端设置
  • # centos7下FFmpeg环境部署记录
  • #define
  • #我与Java虚拟机的故事#连载19:等我技术变强了,我会去看你的 ​
  • $$$$GB2312-80区位编码表$$$$
  • (delphi11最新学习资料) Object Pascal 学习笔记---第5章第5节(delphi中的指针)
  • (Demo分享)利用原生JavaScript-随机数-实现做一个烟花案例
  • (八)Flask之app.route装饰器函数的参数
  • (附源码)springboot太原学院贫困生申请管理系统 毕业设计 101517
  • (淘宝无限适配)手机端rem布局详解(转载非原创)
  • (提供数据集下载)基于大语言模型LangChain与ChatGLM3-6B本地知识库调优:数据集优化、参数调整、Prompt提示词优化实战
  • (转)利用PHP的debug_backtrace函数,实现PHP文件权限管理、动态加载 【反射】...
  • ... 是什么 ?... 有什么用处?
  • .axf 转化 .bin文件 的方法
  • .NET Core工程编译事件$(TargetDir)变量为空引发的思考
  • .net快速开发框架源码分享
  • .NET中使用Redis (二)