rtmp服务端实现
前言
网上好像没一篇讲的很完善的,可能和公司保密有关吧。先就最让人困惑(至少我是这样)且网上也很少找到答案的一个点讲一下id各是什么意思? (如果我哪里理解错了,希望大神指出,毕竟我也是看了好多资料及官方文档总结的)
chunk stream id (cs id) 属于Chunk Basic Header 占6bits
message type id 属于Chunk Message Header 占1byte
message stream id 属于Chunk Message Header 占4bytes
我们可以逆向推理:
接收端(一个chunk stream链接)收到的都是chunk 那么会根据chunk stream id来组合成不同的message,相同的chunk stream id当然就属于同一message咯 但是会有不同的message,比如音频、视频、命令消息等,那么会根据message type id来区分是哪一类消息,比如我们通过message type id知道了是视频消息,这时可能存在一种可能,对方同时发送了视频a和视频b(虽然这种情况很少,而且按照官方文档的原话:this defeats the benefits of the header compression),那么就会根据message stream id来进一步区分了。
下面部分是我综合了网上几个写的比较好的文章而来的,但是我没保留原文网址,如果原作者反对,我会立即删除。
rtmp介绍
RTMP 是Real-Time Messaging Protocol(实时消息传送协议)的缩写,它是Adobe Systems公司为Flash播放器和服务器之间音频、视频和数据传输开发的协议。这是一个标准的,未加密的实时消息传递协议,默认端口是1935。
rtmp协议分析
参考官方文档:rtmp_specification_1.0.pdf(不能完全相信该文档,adobe的这份文档公认的差)
在RTMP协议中信令和媒体数据都称之为Message,在网络中传输这些Message,为了区分它们肯定是要加一个Message head的,所以RTMP协议也有一个Message head,还有一个问题因为RTMP协议是基于TCP的,由于TCP的包长度是有限制的(一般来说不超过1500个字节),而RTMP的Message长度是有可能很大的,像一个视频帧的包可能会有几十甚至几千K,这个问题就必然有一个分片的问题,在RTMP协议中对应的说法就是chunk,每一个Message + head都是由一个和多个chunk组成的。
一个Message + head可以分成一个和多个chunk,为了区分这些chunk,肯定是需要一个chunk head的,具体的实现就把Message head的信息和chunk head的信息合并在一起以chunk head的形式表现。
一个完整的chunk的组成如下图所示
Chunk basic header:
该字段包含chunk的stream ID和 type 。chunk的Type决定了消息头的编码方式。该字段的长度完全依赖于stream ID,该字段是一个可变长的字段。
Chunk Msg Header:0, 3 ,7, 11
该字段包含了将要发送的消息的信息(或者是一部分,一个消息拆成多个chunk的情况下是一部分)该字段的长度由chunk basic header中的type决定。
Extend Timestamp: 0 ,4 bytes
该字段发送的时候必须是正常的时间戳设置成0xffffff时,当正常时间戳不为0xffffff时,该字段不发送。当时间戳比0xffffff小该字段不发送,当时间戳比0xffffff大时该字段必须发送,且正常时间戳设置成0xffffff。
Chunk Data
实际数据(Payload),可以是信令,也可以是媒体数据。
Chunk basic header
chunk basic head的长度为1~3个字节,具体长度主要是依赖chunk stream ID的长度,所谓chunk stream ID是flash server用来管理连接的客户端的信令交互的标识,在red5的文档中称之为channel ID,协议最大支持65597个streamID 从3~65599。ID 0,1,2为协议保留,0代表ID是64~319(第二个byte + 64);1代表chunk stream ID为64~65599((第三个byte)* 256 + 第二个byte + 64)(小端表示);2代表该消息为低层的协议(在RTMP协议中控制信令的chunk stream ID都是2)。3~63的chunk stream ID就是该byte的值。没有附加的字段来标识chunk stream streamID。在这里要指出的是虽然RTMP的chunk stream ID理论是可以达到65599,但是目前使用的chunk stream ID很少,2~7都是约定的,8是用来传输publish play等命令,其他的chunk stream ID目前好像没有使用,至少我不知道用来干嘛的。
所以目前chunk basic head的长度一般为1个字节。这一个字节由两部分组成
+++++++++++++++++++
+fmt + cs id +
+++++++++++++++++++
fmt占两个bit用来标识紧跟其后的chunk Msg Header的长度,cs id占六个bit。
两位的fmt取值为 0~3,分别代表的意义如下:
case 0:chunk Msg Header长度为11;
case 1:chunk Msg Header长度为7;
case 2:chunk Msg Header长度为3;
case 3:chunk Msg Header长度为0;
所以 只有一个字节的chunk basic header取值为 chunk basic header = (fmt << 6) | (cs id).
Chunk Msg Header
Chunk Msg Header的长度是可变的,Chunk Msg Header可变的原因是为了压缩传输的字节数,把一些相同类型的chunk的head去掉一些字节,换句话说就是四种类型的包头都可以通过一定的规则还原成11个字节,这个压缩和还原在RTMP协议中称之为复用/解复用。
那我们以11个字节的完整包头来解释Chunk Msg Header,如图所示
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+++++timestamp+message length+ message type id +message stream id+++++
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Timestamp:3bytes
对于type 0的chunk,绝对时间戳在这里表示,如果时间戳值大于等于0xffffff(16777215),该值必须是0xffffff,且时间戳扩展字段必须发送,其他情况没有要求。
message length:3bytes
Message 的长度,注意这里的长度并不是跟随chunk head其后的chunk data(Payload)的长度,而是前文提到的一条信令或者一帧视频数据或音频数据的长度。前文提到过信令或者媒体数据都称之为Message,一条 Message可以分为一条或者多条chunk。
message type id:1byte
Message的类型ID
0×01 Chunk Size changes the chunk size for packets
0×02 Unknown
0×03 Bytes Read send every x bytes read by both sides
0×04 Ping ping is a stream control message, has subtypes
0×05 Server BW the servers downstream bw
0×06 Client BW the clients upstream bw
0×07 Unknown
0×08 Audio Data packet containing audio
0×09 Video Data packet containing video data
0x0A-0x0E Unknown
0x0F FLEX_STREAM_SEND TYPE_FLEX_STREAM_SEND
0x10 FLEX_SHARED_OBJECT TYPE_FLEX_SHARED_OBJECT
0x11 FLEX_MESSAGE TYPE_FLEX_MESSAGE
0×12 Notify an invoke which does not expect a reply
0×13 Shared Object has subtypes
0×14 Invoke like remoting call, used for stream actions too.
0×16 StreamData 这是FMS3出来后新增的数据类型,这种类型数据中包含AudioData和VideoData
message stream id:4bytes
message stream id的字节序是小端序,这个字段是为了解复用而设计的,RTMP文档上说的相当的模糊,message stream ID可以使任意值,不同的消息流复用成相同的chunk stream,基于它们的ID能够解复用。于chunk stream 是相关的,这个字段是一个不透明的值没有整明白什么意思,我的理解就是用来标识和服务器连接的flash端的序号。
长度是7 bytes 的chunk head,该类型不包含stream ID,该chunk的streamID和前一个chunk的stream ID是相同的,变长的消息,例如视频流格式,在第一个新的chunk以后使用这种类型,注意其中时间戳部分是相对时间,为何上一个绝对时间之间的差值 如图所示:
++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ timestamp delta+message length+ message type id +
++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 bytes的chunk head,该类型既不包含stream ID 也不包含消息长度,这种类型用于stream ID和前一个chunk相同,且有固定长度的信息,例如音频流格式,在第一个新的chunk以后使用该类型。如图所示:
++++++++++++++++++++
+ timestamp delta+
++++++++++++++++++++
0 bytes的chunk head,这种类型的chunk从前一个chunk得到值信息,当一个单个消息拆成多个chunk时,这些chunk除了第一个以外,其他的都应该使用这种类型,
chunk的长度:
chunk 的长度初始长度固定为128个字节,但是这个值并不是不可变的,在客户端和服务端建立连接以后,客户端和服务端都可以通过发送信令的方式来通知对端修改 chunk的长度,理论上来说可以修改chunk的最长长度为65536。这里chunk的长度是指chunk的数据部分的长度,即chunk data(payload)的长度,如果一条Message的数据长度超过了chunk的长度,就必须把Message分割成多条chunk,即如果一条 视频类型Message长度为2000个byte,chunk长度为1500,则该Message将会分割成两条chunk,第一条的chunk data长度为1500,第二条的chunk data长度为500。当然这两条chunk的chunk head肯定是不同的,其中第二条chunk的chunk head就是0字节的。
rtmp负载数据格式分析
信令格式
Rtmp信令格式是amf格式(官方文档:amf3_spec_121207.pdf
)。简单介绍下:
AMF是Action Message Format的简写,它是一种二进制的数据格式, 它的设计,是为了把actionscript里面的数据(包括Object, Array, Boolean, Number等)序列化成 一段你基本看不大懂的二进制数据, 然后你可以把这段数据随意发送给其他地方的程序,比如发给远程的服务器, 在远程服务器那边, 又可以把这段数据给还原出来。以此达到一个数据传输的作用。Amf有amf0和amf3两个版本。AMF3消息流基本上都是在AMF3外面包了一层AMF0, 也就是说我们看到的所有AMF数据流都是AMF0的。
一段AMF消息流解析如下:
00 03 表示版本号为3, 其实个人觉得这个版本号用处不大,只是可以提醒你数据流中可能会遇到AMF3的数据
00 00 表示头部的个数为0, 一般情况下貌似头部个数都是0,我还没理解什么情况下要用到头部,如果头部个数为n,那么接下来应该是n个头部的数据,这里因为n=0,所以直接跳过
00 01 表示消息体的个数为1
接下来就是(消息体的正文=targetURI+responseURI+内容长度+内容)* n,我们这个例子里n=1
targetURI是一个字符串,它表示这个消息要发到哪里去,在这个例子里它 = amfService.dispatchAMF,responseURI也是一个字符串,它其实就类似于一个token的作用,因为我们同时可能调用了很多个service,那么service返回回来的数据要回调哪个处理函数呢?关键就在于这个responseURI,这个字符串会跟着数据流发出去, 然后还会回来。AMF0规定了targetURI和responseURI都是一个UTF字符串, 也就是用2个字节来表示字符串长度,后面紧接着字符串正文。
继续往下看
00 16 表示targetURI的长度是0×16,
61 6D……41 4D 46 就是targetURI的值: amfService.dispatchAMF
00 03 表示responseURI的长度是3
2F 36 34 表示responseURI的值: /64
00 00 00 4D 表示后面的内容长度为0x4D
接下来的都是内容正文了,内容正文的数据全是AMF0的的数据
AMF的数据都是一个字节表示type(文档中称之为maker),然后紧接着数据
0A 表示这个是一个标准的Array
00 00 00 02 表示数组有2个元素
02 表示第一个元素的type是字符串,
00 30 表示第一个元素的字符串的长度是0×30
32 … 33 37 表示的是字符串的值
00 表示第2个元素是一个Number
40 00 00 00 00 00 00 00 表示的就是这个Number的值
上面这个例子正好不含有AMF3的数据,说明了开头的那个版本号3其实并没有太大意义,我们现在看到的数据都是AMF0的。
媒体流格式
Rtmp媒体流格式可以看成是flv里面video tag格式里面的data的格式。(官方文档:video_file_format_spec_v10_flv_f4v.pdf)。简单介绍下:
第1个字节包含视频数据的参数信息
第1个字节的前4位的数值表示帧类型
第1个字节的后4位的数值表示视频编码ID,1 = JPEG(现已不用),2 = Sorenson H.263,3 = Screen video,4 = On2 VP6,5 = On2 VP6 with alpha channel,6 = Screen video version 2。
从第2个字节开始为视频流数据。格式为AVCVIDEOPACKET
第一个字节为AVCPacketType, 0: AVC sequence header 1: AVC NALU 2: AVC end of sequence
第2,3,4个字节为CompositionTime 一般为0即可
接下来的就是数据部分了。
rtmp服务的实现
由于我们的项目主要是放在设备上的,对项目的大小及语言有要求,因此就不能直接 一些开源的项目,现在是基于rtmpdump里面的一个rtmpsvr.c实现的rtmp服务端。Rtmpdump是一个rtmp客户端, rtmpsvr.c是rtmpdump里面附带的用于它自己测试的。
主要流程包括:
1、HandShake
需要注意的是:RTMP Specifiction 1.0文档已经过时,从大概Adobe Flash Player 10.0.32.18版本开始,HandShake已不按该文档进行,文档中MUST be all 0的字节被填上了数据。当HandShake数据是这样时,要求回应的结尾有特定的数据,否则Adobe Flash Player将关闭H264/AAC的解码功能。Rtmpdump里面有实现握手,具体实现可以参考它
2、信令交互
交互的信令如下图所示:信令的格式参考 第三节的信令格式。
3、流数据传输
开始是流相关的信令交互,然后就是流数据了,如下图所示:
流的格式参考 第三节的媒体流格式。
一些RTMP项目,有Server端也有Client端
· Red5 only contains a server-implementation (in java).
· The python project rtmpy aims to be a free
software implementation of an RTMP library, whilst Tape intends to be a full
streaming server (in Python). rtmpy is in active development.
· There is a java client implementation of RTMP, called Flazr.
As of 2nd June 2009 (just two weeks after Adobe issued the take-down notice),
Flazr also has RTMPE support.
Congratulations to Peter, and thank you to Adobe: none of this would have
remotely happened, if you hadn't brought RTMPE to people's attention.
· SWFDechas a partial and experimental implementation of RTMP.
swfdec is client-only.
· Gnashhas a partial and experimental implementations of RTMP.
Gnash has both client and server, sharing the same common source. Cygnal
is making particularly good progress, as a server: video can already be
streamed from it, with real-time video planned for Q3 2009.
· libRTMPby boxee contains an RTMP client library, and was used as
the basis for rtmpdump.
· haxeVideois a server implementation of RTMP in Haxe.
· crtmpserveris a server implementation of RTMP that has implemented
(as of 25th may 2009) the RTMPE protocol.
· Mammoth, formerly known as OpenFMS, is a server implementation
that has implemented an RTMPE-compatible algorithm known as
"H264-compatible and DH handshaking".
· RubyIzumiis an implementation of an RTMP server in Ruby.
· rtmpdump此项目是第一个突破RTMPE加密协议的,但因为破解了RTMPE所以惹恼了ADOBE,这个开源项目在SourceForge里已经没有了。但是作者的页面还保留有链接和代码。
· http://rtmpd.com/ 这个项目极其庞大,还没有一些深入的了解
flvstreamer http://savannah.nongnu.org/projects/flvstreamer/
Wowza http://www.wowza.com/lp2?network=d&ad=15567605046&keyword=media+server&matchtype=&placement=thompsonng.blogspot.com&experiment=&mobile=&gclid=CN21xYDU2rQCFaN_Qgod9RQAXw
完
2013年1月
rtmp服务端实现的更多相关文章
- Android中直播视频技术探究之---视频直播服务端环境搭建(Nginx+RTMP)
一.前言 前面介绍了Android中视频直播中的一个重要类ByteBuffer,不了解的同学可以 点击查看 到这里开始,我们开始动手开发了,因为我们后续肯定是需要直播视频功能,然后把视频推流到服务端, ...
- rtsp实时流通过rtmp推送到服务端
很多朋友都会问到rtsp如何通过rtmp协议推送到服务端,正好前段时间开发了这个功能写在这里,和大家分享下. 首先我想说的是:ffmpeg可以实现这个功能.ffmpeg支持rtsp协议,也支持rtmp ...
- 在线教学、视频会议 Webus Fox(2) 服务端开发手册
上次在<在线教学.视频会议软件 Webus Fox(1)文本.语音.视频聊天及电子白板基本用法>里介绍了软件的基本用法.本文主要介绍服务器端如何配置.开发. 1. 配置 1.1 IIS配置 ...
- 如何利用cURL和python对服务端和web端进行接口测试
工具描述 cURL是利用URL语法在命令行方式下工作的文件传输工具,是开源爱好者编写维护的免费工具,支持包括Windows.Linux.Mac等数十个操作系统,最新版本为7.27.0,但是我推荐大家使 ...
- Swift3.0服务端开发(一) 完整示例概述及Perfect环境搭建与配置(服务端+iOS端)
本篇博客算是一个开头,接下来会持续更新使用Swift3.0开发服务端相关的博客.当然,我们使用目前使用Swift开发服务端较为成熟的框架Perfect来实现.Perfect框架是加拿大一个创业团队开发 ...
- 关于如何提高Web服务端并发效率的异步编程技术
最近我研究技术的一个重点是java的多线程开发,在我早期学习java的时候,很多书上把java的多线程开发标榜为简单易用,这个简单易用是以C语言作为参照的,不过我也没有使用过C语言开发过多线程,我只知 ...
- Socket聊天程序——服务端
写在前面: 昨天在博客记录自己抽空写的一个Socket聊天程序的初始设计,那是这个程序的整体设计,为了完整性,今天把服务端的设计细化记录一下,首页贴出Socket聊天程序的服务端大体设计图,如下图: ...
- zookeeper源码分析之五服务端(集群leader)处理请求流程
leader的实现类为LeaderZooKeeperServer,它间接继承自标准ZookeeperServer.它规定了请求到达leader时需要经历的路径: PrepRequestProcesso ...
- zookeeper源码分析之四服务端(单机)处理请求流程
上文: zookeeper源码分析之一服务端启动过程 中,我们介绍了zookeeper服务器的启动过程,其中单机是ZookeeperServer启动,集群使用QuorumPeer启动,那么这次我们分析 ...
随机推荐
- 软件测试面试题-适合零基础和工作多年的re
软件测试面试题整理,可以看看:适合零基础和多年工作经验跳槽的人 有些问题会深挖,就不在整理了 详看图片:
- linux学习总结-----web前端①
<html> <head> <title></title> <meta charset='utf-8'/> ... </head> ...
- python学习总结------邮件与短信
邮件发送 - 简介: - 邮件服务器.用户名.密码 - 相关协议: - SMTP:简单邮件传输协议 - POP3:邮局通讯协议 - IMAP:交互式邮件存取协议 - SMTP协议默认端口是25 - 用 ...
- ethday04复杂的智能合约
复杂的智能合约部署和测试 server--database 客户端服务器数据库模式 以太坊dapp应用程序结构 server --- client 模式 server -- database 传统模式 ...
- HDU 4725 The Shortest Path in Nya Graph(最短路径)(2013 ACM/ICPC Asia Regional Online ―― Warmup2)
Description This is a very easy problem, your task is just calculate el camino mas corto en un grafi ...
- 在使用easyUI时,js,css样式都加载了 但是图标加载不了
可能的问题:web.xml 配置了这些 <servlet-mapping> <servlet-name>default</servlet-name> <url ...
- .Net 面试总结
今天去面试了一家公司,做电子商务类的网站的,公司的老板应该比较有能量,可以同时拿下若干项目,技术负责人给提了几个问题: 记不清顺序了 .net 构析函数的作用 泛型的主要作用及应用方面 结构与类的区别 ...
- bcc编译
bcc编译,直接在docker里编,太方便:第一次深切体会到docker的强大: 1)下载bcc源码: 2) 把源码中的Dockerfile.ubuntu重命名为Dockerfile 3)sudo d ...
- Byte数据类型—Java
字节与字符 一个英文字母(不分大小写)占一个字节的空间,一个中文汉字占两个字节,一个二进制数字序列,在计算机中作为一个数字单元,一般为8位二进制数,换算为十进制最小值为0,最大值为255. UTF-8 ...
- BZOJ3242 [Noi2013]快餐店 【环套树 + 单调队列dp】
题目链接 BZOJ3242 题解 题意很清楚,找一点使得最远点最近 如果是一棵树,就是直径中点 现在套上了一个环,我们把环单独拿出来 先求出环上每个点外向树直径更新答案,并同时求出环上每个点外向的最远 ...