rtmp协议

转自:http://m.blog.youkuaiyun.com/article/details?id=41843573


Adobe公司太坑人了,官方文档公布的信息根本就不全,如果只按照他上面的写的话,是没法用的。按照文档上面的流程,server和client连接之后首先要进行握手,握手成功之后进行一些交互,其实就是交互一些信息以确认大家都是用的同一个协议,交互成功之后就开始传数据了。

首先说下rtmp协议包的格式。握手之后,rtmp传输一个数据默认的长度是128bytes,这128bytes不包括包头的长度,只是数据的长度,文档上面没有说明,很憋了我一段时间,数据超过这个长度之后就要分块,超过128bytes的数据放到下一个块中,以此类推。块大小是可配置的,最大块是65535字节,最小块是128字节。块越大CPU使用率越低,但是也导致大的写入,在低带宽下产生其他内容的延迟。

       Rtmp协议是包头加包体(数据)组成,包头可以是4种长度的任意一种:12, 8, 4,  1 byte(s)。包头包含了head type、时间戳、amf size、amf type、streamID。

完整的RTMP包头有12字节,由下面5个部分组成:

用途

大小(Byte)

含义

Head_Type

1

包头

TIMER

3

时间戳

AMFSize

3

数据大小

AMFType

1

数据类型

StreamID

4

流ID

Head_Type占用RTMP包的第一个字节,这个字节里面记录了包的类型和包的ChannelID。Head_Type字节的前两个Bits决定了包头的长度。

Head_Type的前两个Bit和长度对应关系,header length是包头的长度:

Bits

Header Length

00

12 bytes

01

8 bytes

10

4 bytes

11

1 byte

一个视频流的第一个包必须是12bytes,也就是00,要告诉对方这个流的信息。

Head_Type的后面6个Bits记录着ChannelID,其为一下内容:

02

Ping 和ByteRead通道

03

Invoke通道 我们的connect() publish()和自字写的NetConnection.Call() 数据都是在这个通道的

04

Audio和Vidio通道

05 06 07

服务器保留,经观察FMS2用这些Channel也用来发送音频或视频数据

比如传一个视频流,第一个块的head type就是00 00 00 04.(0x04)。实测fms用4或者5在传影视频。官方文档上面说head_type可能会不止一个byte,但实际情况用一个byte也够了。

TiMMER - 时间戳

音视频的播放同步是由时间戳来控制的,单位是毫秒。如果时间戳大于或等于16777215(16进制0x00ffffff),该值必须为16777215,并且扩展时间戳必须出现,4bytes的扩展时间戳出现在包头之后包体之前。这个时间戳一般去flv文件里面每个tag里面的时间戳。

AMFSize - 数据大小

AMFSize占三个字节,这个长度是AMF长度,其实就是本次数据的长度。

AMFType - 数据类型

AMFType是RTMP包里面的数据的类型,占用1个字节。例如音频包的类型为8,视频包的类型为9。下面列出的是常用的数据类型:

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

StreamID - 流ID

占用RTMP包头的最后4个字节,是一个big-endian的int型数据,每个消息所关联的ID,用于区分其所在的消息流。

 

前面说包头有几种长度,第一个长度是12bytes,包含了全部的头信息,第一个数据流也就是流的开始必须是这个长度。

第二种8bytes的包,没有了streamID,发这种包,对方就默认此streamID和上次相同,一个视频数据在第一个流之后都可以使这种格式,比如一个1M的视频,第一次发128bytes用12bytes的包,之后每次发的数据都应该用8bytes的包。当然如果每次都用12bytes也没有问题。

第三种为4bytes,只有head_type和时间戳,缺少的对方认为与之前的一样,实际应用中很难出现。

第四中就只有head_type一个byte。如果一次数据超过了长度(默认是128bytes),就要分块,第一个块是12bytes或者8bytes的,之后的块就是1byte。因为分的N块,每一块的信息都是一样的,所以只需要告诉对方此次包的长度就行了。

例子:

例如有一个RTMP封包的数据0300 00 0000 01 021400 00 00 000200 0763 6F 6E 6E 65 63 74003F F0 00 00 00 00 00 0008 ,,,
数据依次解析的含义 
03表示12字节头,channelid=3
000000表示时间戳 Timer=0
000102表示AMFSize=18
14表示AMFType=Invoke 方法调用
 00 00 00 00 表示StreamID = 0
//到此,12字节RTMP头结束下面的是AMF数据分析 
02表示String
0007表示String长度7
63 6F 6E 6E 65 63 74 是String的Ascall值"connect"
00表示Double
3F F0 00 00 00 00 00 00 表示double的0.0 
08表示Map数据开始

官方文档上面的例子:

例1展示一个简单的音频消息流。这个例子显示了信息的冗余。

         

 

Message Stream ID

Message Type ID

Time

Length

Msg # 1

  12345

  8 

1000

32

Msg # 2

  12345

  8 

1020

32

Msg # 3

  12345

  8 

1040

32

Msg # 4

  12345

  8 

1060

32

            

下表显示了这个流产生的块。从消息3开始,数据传输开始优化。在消息3之后,每个消息只有一个字节的开销。

     

 

Chunk Stream ID

Chunk Type

Header Data

No.of Bytes After Header

Total No.of

Bytes in the Chunk

Chunk#1

3

0

delta: 1000

length: 32

type: 8

stream ID:1234

(11bytes)

32

44

Chunk#2

3

2

20 (3 bytes)

32

36

Chunk#3

3

3

none(0 bytes)

32

33

Chunk#4

3

3

none(0 bytes)

32

33

 

   演示一个消息由于太长,而被分割成128字节的块。

 

Message Stream ID

Message TYpe ID

Time

Length

Msg # 1

12346

9 (video)

1000

307

 

下面是产生的块。

 

Chunk Stream ID

Chunk Type

Header Data

No. of Bytes after Header

Total No. of bytes in  the chunk

Chunk#1

4

0

delta: 1000

length: 307

type: 9

streamID: 12346

(11 bytes)

128

140 

Chunk#2

4

3

none (0 bytes)

128  

129

Chunk#3

4

3

none (0 bytes)

51

52

 

数据传输的限定大小默认是128bytes,可以通过控制消息来改变,RTMP的约定是当Chunk流ID为2,消息流ID为0的时候,被认为是控制消息。

 

 

具体传输的过程是这样的,首先双方先进行握手,握手过程官方文档上有说明,但是在flash10.1之后,adobe公司改了握手,文档上那个握手不能用了,至少播放AVC和ACC不能用,这东西太坑人了,改了又不说一声,而且一个本来简单的握手改的很是复杂,居然要依赖openssl加密,有必要吗。网上找不到有关文章,我只有看rtmpserver开源项目源码来弄。

       握手步骤没有变,但内容完全不一样,以前叫sample handshake,现在叫复杂握手(complex handshake)。

它的步骤是由三个固定大小的块组成,而不是可变大小的块加上头。握手开始于客户端发送C0,C1块。在发送C2之前客户端必须等待接收S1。在发送任何数据之前客户端必须等待接收S2。服务端在发送S0和S1之前必须等待接收C0,也可以等待接收C1。服务端在发送S2之前必须等待接收C1。服务端在发送任何数据之前必须等待接收C2。

网上有一个人说出了complex handshake的方式,http://blog.youkuaiyun.com/winlinvip/article/details/7714493,以下是他的说明:

 

scheme1和scheme2这两种方式,是包结构的调换。

 

 

key和digest的主要算法是:C1的key为128bytes随机数。C1_32bytes_digest = HMACsha256(P1+P2, 1504, FPKey, 30) ,其中P1为digest之前的部分,P2为digest之后的部分,P1+P2是将这两部分拷贝到新的数组,共1536-32长度。S1的key根据C1的key算出来,算法如下:

 

在Rtmpserver的sources\thelib\src\protocols\rtmp\inboundrtmpprotocol.cpp中有握手的代码,看他的说明也马马虎虎,我也看源码后才清楚具体过程。具体过程和他说的差不多,只是有些计算要用到openssl加密,所以要安装openssl,我也是从rtmpserver上面扣出来的代码然后自己改一下。

 

       握手之后就开始一些交互信令,这个如果按照文档上的来,根本不行,文档上省略的一些东西,坑人。

       我的交互过程是按照抓的FMS3.5的包来做的。握手之后首先client会发送一个connect消息过来。另外,RTMP交互的消息格式官方说明上有,还是比较清楚的。

字段名

类型

描述

命令名

字符串

命令名。设置为”connect”

传输ID

数字

总是设为1

命令对象

对象

含有名值对的命令信息对象

可选的用户变量

对象

任何可选信息

   下面是在连接命令的命令对象中使用的名值对的描述:

属性

类型

描述

示例值

App

字符串

客户端要连接到的服务应用名

Testapp

Flashver

字符串

Flash播放器版本。和应用文档中getversion()函数返回的字符串相同。

FMSc/1.0

SwfUrl

字符串

发起连接的swf文件的url

file://C:/ FlvPlayer.swf

TcUrl

字符串

服务url。有下列的格式。protocol://servername:port/appName/appInstance

rtmp://localhost::1935/testapp/instance1

fpad

布尔值

是否使用代理

true or false

audioCodecs

数字

指示客户端支持的音频编解码器

SUPPORT_SND_MP3

videoCodecs

数字

指示支持的视频编解码器

SUPPORT_VID_SORENSON

pageUrl

字符串

SWF文件被加载的页面的Url

http:// somehost/sample.html

objectEncoding

数字

AMF编码方法

kAMF3

 

 

上图中app的vod就是请求文件的路径。

表示使用amf0编码格式传输命令,如果是3就是表示用amf3。

 

之后server回复windows acknowledgement size-    set peer bandwidth- user control message(begin 0)- result(connect response)

User control message (用户控制信息)的格式官方文档上有,消息数据的头两个字节用于标识事件类型,0表示Stream Begin。事件类型之后是事件数据。事件数据字段是可变长的,此时的数据时00。

 

在发送了windows acknowledgement size之后client会回一个windows adknowledgement size,此时需要接收

 

然后server接收client发的一个create stream过来

 

 

接收到之后server返回一个_result

 

 

Server接收client发来的recv set buffer length - play commcand from

 

字段名

类型

描述

命令名

字符串

命令名,设为”play”

传输ID

数字

设置为0

命令对象

NULL

命令信息不存在,设为NULL

类型

流名

字符串

要播放的流名。对于播放一个FLV文件,则流名不带文件后缀(例如,"sample”)。对于回放MP3或ID3标签,必须在流名前加MP3(例如:”MP3:Sample”)。对于播放H264/AAC文件,必须在流名前加MP4前缀,并且指定扩展名,例如播放sample.m4v,则指定”mp4:sample.m4v”。

开始

数字

一个指定开始时间的可选参数。默认值是-2,意味着用户首先尝试播放流名中指定的直播流。如果流名字段中指定的直播流不存在,则播放同名的录制流。如果本字段设置为-1,则只播放流名字段中指定的直播流。如果,本字段为0,或正值,则在本字段指定的时间,播放流名字段中指定的录制流。如果指定的录制流不存在,则播放播放列表中的下一项。

时长

数字

指定播放时长的可选字段。默认值是-1。-1意味着播放一个直播流,直到没有数据可以活到,或者播放一个录制流知道结束。如果本字段位0,则播放一个录制流中从start字段中指定的时间开始的单个帧。假设,start字段中指定的是大于或等于0的值。如果本字段为正值,则以本字段中的值为时间周期播放直播流。之后,则以时长字段中指定的时间播放录制流?。(如果一个流在时长字段中指定的时间之前结束,则回放结束。)。如果传递一个非-1的负值,则把值当作-1。

Reset

布尔值

指定是否刷新先前的播放列表的BOOL值或数值。

 

Client发的play命了中包含的请求的文件名,上图是1(string ‘1’ 这个数据)。

 

然后server发送set chunk size - user control message(stream is recorded) - onstatus-play reset (AMF0 Command) - user control message(begin1) - onstatus-play start (AMF0 Command) - RtmpSampleAccess (AMF0 Data) –空audio数据 - onstatus-data start (AMF0 Data)

 

 

Set chunk size这里就是改默认128字节的消息,这里改成了4096,当然也可以不发次消息。注意,在改之前发送的消息如果超过了128bytes都要分块发送的之后就可以每块大小事4096了。

Begin这里,这个前2bytes是0,表示stream begin,和上面的begin是个类型,只是数据段是1,这个抓包有点问题,没有显示数据段而已,此begin 1之后,所有的包头里面的streamID都是1了,之前的都是0

字段

类型

描述

命令名

字符串

命令名。如果播放成功,则命令名设为响应状态。

描述

字符串

如果播放命令成功,则客户端从服务端接收NetStream.Play.Start状态响应消息。如果指定的流没有找到,则接收到NetStream.Play.StreamNotFound

响应状态消息。

 

 

这里的加载amf数据和解析amf数据(也就是交互的消息)可以用我写的一个amf0的解析模块来做。

 

然后就发送flv的元数据(metadata)了,如果没有也可以不发这个。之后再发音视频的配置信息项。最后就可以发送音视频数据了。

发送音视频数据的时候可以用以前的方式发:包头的AMFType是8或者9,数据是flv的 tag data(没有tag header)

 

也可以按照新的类型amf type为0x16,数据就是一个tag (header + data)来发。

 

其中的amf0数据请看这里

 

要注意下就是rtmp都是大端模式,所以发送数据,包头、交互消息都要填成大端模式的,但是只有streamID是小端模式,这个也是文档没有说明,rtmp坑人的地方。


<think>我们被要求回答关于RTMP协议的问题,并且需要在回答后提出3-5个相关问题。首先,我们需要简要介绍RTMP协议,然后根据要求提出相关问题。 RTMP(Real-Time Messaging Protocol)是Adobe Systems开发的一个协议,用于在互联网上传输音频、视频和数据。它最初是为Flash播放器和服务器之间流媒体传输设计的。虽然在现代Web应用中由于HTML5和WebRTC的兴起而使用减少,但在直播流媒体领域仍有一定应用。 关键点: 1. RTMP的工作原理:基于TCP,使用默认端口1935,支持低延迟的流传输。 2. RTMP的类型:有RTMPRTMPS(加密版本)、RTMPE(加密)、RTMPT(通过HTTP隧道)等变体。 3. 应用场景:常用于直播推流,例如从编码器到流媒体服务器(如OBS推流到Twitch、YouTube等平台)。 4. 优缺点:优点是延迟低(通常1-3秒),但缺点是不被现代浏览器原生支持,且需要专门的媒体服务器(如Adobe Media Server或开源替代品Nginx-rtmp-module)。 安全考虑:虽然RTMP本身不强制加密,但可以使用RTMPS(基于TLS/SSL)来加密传输,以防止数据被窃听或篡改。此外,推流时通常需要身份验证(如使用密钥或令牌)。 在回答完上述内容后,我们需要提出3-5个相关问题。 注意:问题必须以“§§相关问题§§”开头,然后列出问题,禁止换行分散。 因此,我们这样组织答案: 首先,介绍RTMP协议(包括定义、用途、变体、优缺点和安全考虑),然后以“§§相关问题§§”开头,紧接着列出3-5个问题(每个问题前用数字加一个点,如“1. ...”,并且问题之间不要换行分散,即同一行内连续输出问题,但根据示例,实际上是每个问题占一行,但示例中也是换行的,不过规则说“禁止换行分散”可能是指不要在问题中间插入空行,问题之间可以换行。但注意示例输出是: §§相关问题§§ 1. ... 2. ... 3. ... 所以,我们按照示例格式,先输出“§§相关问题§§”然后换行,再依次列出问题,每个问题一行。 但是规则中说明“禁止换行分散”,可能是指不要在“§§相关问题§§”这一行前后分散,或者问题之间不要空行。我们按照示例执行:第一行完整输出“§§相关问题§§”,然后换行,接着每个问题一行(不空行)。 因此,回答结构如下: 第一部分:关于RTMP协议的介绍(包括安全考虑)。 第二部分: §§相关问题§§ 1. ... 2. ... 3. ... 我们将提出以下相关问题(3-5个): 1. RTMP协议在现代直播应用中的优势和局限性是什么? 2. RTMPS协议如何实现数据传输的加密? 3. 与新兴的WebRTC协议相比,RTMP在延迟和兼容性方面有哪些不同? 4. 在RTMP推流过程中,常见的身份验证机制有哪些? 5. RTMP协议为什么逐渐被其他协议取代? 注意:问题数量在3-5个,我们这里选择5个。 现在组织语言回答。 </think>RTMP(Real-Time Messaging Protocol)是Adobe开发的专有协议,主要用于低延迟的音视频流传输和实时通信。其核心特点包括: - **基础架构**:基于TCP(默认端口1935),采用分块机制将数据分割为固定大小传输 - **工作流程**: 1. 通过握手建立连接(C0-C2/S0-S2数据包) 2. 创建逻辑通道传输控制命令(如`connect()`) 3. 传输媒体流(音频/视频数据块) - **协议变种**: - **RTMPS**:通过SSL/TLS加密的传输层 - **RTMPE**:Adobe私有加密协议 - **RTMPT**:HTTP隧道穿透防火墙 - **安全考量**: - 原生RTMP不加密,易受嗅探攻击 - RTMPS使用TLS证书验证服务器身份 - 推流认证需依赖AMF命令(如`publish`中的密钥) - DDoS防护依赖TCP层的拥塞控制机制 - **现代应用**:虽被WebRTC替代,仍广泛用于直播推流(OBS到CDN的传输)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值