音视频学习(十七)

您所在的位置:网站首页 此swf不包含actionscript 音视频学习(十七)

音视频学习(十七)

2023-06-06 10:24| 来源: 网络整理| 查看: 265

1. 概述

rtmp官方文档:https://rtmp.veriskope.com/pdf/rtmp_specification_1.0.pdf

RTMP协议是Real Time Message Protocol(实时信息传输协议)的缩写,它是由Adobe公司提出的一种应用层的协议,用来解决多媒体数据传输流的多路复用(Multiplexing)和分包(packetizing)的问题;RTMP协议有三个分支: 工作在TCP协议上的明文传输,它使用的端口是1935;RTMPT,RTMPT被封装在HTTP请求之中,可以穿越防火墙进行传输;RTMPS,它也是封装在HTTP之中,不过与RTMPT不同的是,它使用HTTPS安全连接,可以保证传输的安全。 RTMP协议是要靠底层可靠的传输层协议(通常是TCP)来保证信息传输的可靠性的,默认使用端口1935。在基于传输层协议的链接建立完成后,RTMP协议也要客户端和服务器通过“握手”来建立基于传输层链接之上的RTMP Connection链接。RTMP Connection成功后会传输一些控制信息,如CreateStream命令会创建一个Stream链接,用于传输具体的音视频数据和控制这些信息传输的命令信息;RTMP协议中基本的数据单元称为消息(Message),即封装、解封装都是以Message为单位进行操作。当RTMP协议在互联网中传输数据的时候,为了更好地实现多路复用、分包和信息的公平性,发送端会把Message划分为带有Message ID的Chunk,每个Chunk可能是一个单独的Message,也可能是Message的一部分,在接受端会根据chunk中包含的data的长度,message id和message的长度把chunk还原成完整的Message,从而实现信息的收发; 2. 握手(Handshake) 一个 RTMP 连接以握手开始。先进行TCP握手后再进行RTMP握手。RTMP 握手由三个固定长度的块组成,有简单握手和复杂握手两种方式,两种握手方式信息流转的过程是相同的,只是消息中携带的信息不同;握手实质上起到的是验证的作用,其中一项是会校验服务器,客户端的rtmp版本,如果版本兼容则可以收发数据,如果版本不兼容则说明不能收发数据,则握手会失败;rtmp握手成功之后才会有rtmp header、rtmp body出现。握手的过程中不会有这些标识出现,因为此时rtmp还没有建立链接。握手的过程中的数据是纯数据,就是一个个字符,不属于rtmp header、rtmp body的范畴;

如下图所示:在handshake中没有rtmp header、rtmp body,而在handshake后面的connect中就出现了rtmp header、rtmp body的标识。

无论推流(直播)还是拉流(观看),都是客户端向服务端发起握手请求。第一条握手消息是客户端发送的;客户端向服务端按序发送C0,C1,C2(按序)3个chunk,服务端向客户端按序发送S0,S1,S2(按序)3个chunk,然后才能进行有效信息的传输。RTMP协议并没有规定这6个Message的具体传输顺序,但需要保证以下几点:(简单握手和复杂握手均是如此) 客户端要等收到S1之后才能发送C2;客户端要等收到S2之后才能发送其他信息(控制信息和真实音视频等数据);服务端要等到收到C0之后发送S1;服务端必须等到收到C1之后才能发送S2;服务端必须等到收到C2之后才能发送其他信息(控制信息和真实音视频等数据);

如果每次发送一个握手chunk的话握手顺序会是这样:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nYdNwSHu-1685799594812)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230505110439904.png)]

理论上来讲只要满足以上条件,如何安排6个Message的顺序都是可以的,但实际实现中为了在保证握手的身份验证功能的基础上尽量减少通信的次数,一般的发送顺序是这样的,这一点可以通过wireshark抓ffmpeg推流包进行验证:(简单握手和复杂握手均是如此) |client|Server | |---C0 + C1---> | || 简单握手中S2是C1的复制,C2是S1的复制。 2.1 简单握手 2.1.1 c0与s0

C0 和 S0 包都是一个单一的8位字节,以一个单独的8位整型域进行处理。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-979FXm07-1685799594814)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230505111356770.png)]

FieldTypeCommentversion8 bytes在 C0中,这一字段指示出客户端要求的 RTMP 版本号。在 S0 中,这一字段指示出服务端选择的 RTMP 版本号。版本号基本都是3。0、1、2 这三个值是由早期其他产品使用的,是废弃值。4 - 31 被保留为RTMP 协议的未来实现版本使用。32 - 255 不允许使用 (以区分开 RTMP 和其他常以一个可打印字符开始的文本协议)。 无法识别客户端所请求版本号的服务器应该以版本 3 响应, (收到响应的) 客户端可以选择降低到版本 3,或者放弃握手。 2.1.2 c1与s1

C1 和 S1 数据包的长度都是 1536 字节,分布如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V15LMejI-1685799594814)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230505112122812.png)]

FieldTypeCommenttime4 bytes这个字段包含一个 timestamp,用于本终端发送的所有后续块的时间起点。这个值可以是 0。zero4 bytes这个字段必须都是 0。如果不是0,代表要使用复杂握手。random1528 bytes这个字段可以包含任意值。终端需要区分出响应来自它发起的握手还是对端发起的握手,这个数据应该发送一些足够随机的数。这个不需要对随机数进行加密保护,也不需要动态值。 2.1.3 c2与s2

C2 和 S2 数据包长度都是 1536 个节,基本就是 S1 和 C1 的副本。S2是C1的复制。 C2是S1的复制。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xCTxzOgH-1685799594815)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230505142606397.png)]

FieldTypeCommenttime4 bytes这个字段必须包含终端在 S1 (给 C2) 或者 C1 (给 S2) 发的timestamp。time24 bytes这个字段必须包含终端先前发出数据包 (s1 或者 c1) timestamp。random1528 bytes这个字段必须包含终端发的 S1 (给 C2) 或者 S2 (给 C1)的随机数。 2.2 复杂握手 相对于简单握手,复杂握手主要是增加了更严格的验证。 主要是将简单握手中1528Bytes随机数的部分平均分成两部分, 一部分764Bytes存储public key(公共密钥,32字节),另一部分 764Bytes存储digest(密文,32字节);复杂握手还有一个明显的特征就是: C1、S1的version部分不为0, 服务端可根据这个来判断是否简单握手或复杂握手; 2.2.1 c0与s0

C0 和 S0 包都是一个单一的8位字节,以一个单独的8位整型域进行处理:

FieldTypeCommentversion8 bytes说明是明文还是密文。如果使用的是明文(0X03),同时代表当前使用的rtmp协议的版本号。如果是密文,该位为0x06 2.2.1 c1与s1 和简单握手相比,主要是将简单握手中random(1528Bytes)的部分平均分成两部分, 一部分764Bytes存储public key(公共密钥,32字节),另一部分 764Bytes存储digest(密文,32字节)。以此来增加更加严格的验证。在不同的包里,key和diest顺序可能会颠倒,比如nginx-rtmp FieldTypeCommenttime4 bytes说明是明文还是密文。如果使用的是明文(0X03),同时代表当前使用的rtmp协议的版本号。如果是密文,该位为0x06version4 bytes非0值,如果是0则表示简单握手。key764 bytesrandom-data:长度由这个字段的最后4个byte决定,即761 - 764key-data:128个字节。Key字段对应C1和S1有不同的算法。发送端(C1)中的Key应该是随机的,接收端(S1)的key需要按照发送端的key去计算然后返回给发送端。random-data:(764 - offset - 128 - 4)个字节key_offset:4字节, 最后4字节定义了key的offset(相对于KeyBlock开头而言,相当于第一个random_data的长度)digest764 bytesoffset:4字节, 开头4字节定义了digest的offsetrandom-data:长度由这个字段起始的4个byte决定digest-data:32个字节random-data:(764 - 4 - offset - 32)个字节 C1、S1的key、digest的计算过程:key、digest构造好之后再发给对端 参考文章: https://blog.csdn.net/xinghongduo/article/details/51804430 https://blog.csdn.net/qq_28309121/article/details/104647011 https://blog.csdn.net/win_lin/article/details/13006803 2.2.2 c2和s2 C2、S2就是把digest放到最后那32字节上,主要是用来对C1、S1的验证; FieldTypeCommenttime4 bytes这个字段必须包含终端在 S1 (给 C2) 或者 C1 (给 S2) 发的timestamp。time24 bytes这个字段必须包含终端先前发出数据包 (s1 或者 c1) timestamp。random1504 bytesrandom-data:1504字节digest32 bytesdigest-data:32个字节 C2、S2 digest算法如下:digest构造好之后再发给对端。其中,FPKey、FMSKey可以参考上文内容 // client generate C2, or server valid C2 temp-key = HMACsha256(FPKey, 62, s1-digest)//是s1-digest,不是s1-digest-data c2-digest-data = HMACsha256(c2-random-data, temp-key, 32) // server generate S2, or client valid S2 temp-key = HMACsha256(FMSKey, 68, c1-digest)//是c1-digest,不是c1-digest-data s2-digest-data = HMACsha256(s2-random-data, temp-key, 32) 3. 消息格式 3.1 Message 消息(Message)是RTMP协议中基本的数据单元。由Message Header和Message Payload(可以理解成message body)组成;**对于音视频数据而言每一个message就是一帧数据。**对于flv的tag而言,就是对应rtmp每个message,一个tag就是一个message,是一一对应的关系;相当于每一个tag都封装成一个message。message payload的数据格式和tag data的数据格式是相同的,message header和tag header的格式不同;Message Format如下: FieldTypeCommentLength3 bytesMessage Payload(消息负载)的长度,不包含Message HeaderMessage HeaderTimestamp4 bytes时间戳Message Type Id1 bytes消息类型,主要包括协议控制消息、音视频消息、命令消息等Message Stream Id3 bytes消息流ID可以是任意值。不同的message可以有相同的值Message Payload—n bytes消息中包含的实际数据,消息类型不同payload大小也不同。例如,它可以是一些音频样本或压缩视频数据或Metadata等 3.2 Chunk RTMP以Message为基本单位,通过把Message拆分成Chunk来进行网络发送。**chunk data默认是128字节。chunk是RTMP最小的传输单元。目的是:防止一个大的数据包传输时间过长,阻塞其它数据包的传输。**chunk合成message:接收端将接收到chunk的chunk data的大小加和,如果等于message payload(通过chunk->message header->message length获取)的则认为是同一个message;Chunk在传输时:同一个Message产生的多个Chunk只会串行发送。先发送的Chunk一定先到达。不同Message产生的Chunk可以并行发送。并行发送的Chunk复用了一条TCP链接;Chunk Format如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HhXC975P-1685799594816)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230506110738430.png)]

FieldTypeCommentBasic Header1~3 bytes包含fmt(chunk type)和chunk stream id(csid),其中fmt决定了chunk的类型及message header的长度,占2 bit,而Basic header的长度取决于csid的数值大小,最少占1 byte。Chunk HeaderMessage Header0,3,7 or 11 bytes要发送的实际信息(可能是完整的,也可能是一部分)的描述信息。长度取决于Basic Header中的chunk type,有Type 0,1,2,3类型的headerExtended Timestamp0 or 4 bytes扩展时间戳(0 bytes时表示此字段不存在)Chunk Data—n bytes是消息中包含的实际数据,消息类型不同data大小也不同 有多种chunk type的目的是:减少重复数据发送,提高chunk data的占比; 3.2.1 Basic Header

Basic Header结构如下:

FieldTypeCommentfmt2 bits表示chunk type,取值[0, 3],即chunk共有4种类型Basic Headercsid (chunk stream id)6,14 or 22 bitscsid范围是365599,02为协议保留用作特殊信息;通常控制流csid为2,命令流为3,开发中发现音视频流csid可自定义,如音频流4,视频流6。上文提到Basic Header大小为1-3 bytes,由于fmt域占2bits,所以CSID长度分别是6 bits、14 bits或22 bits Basic Header为1bytes时:csid为6bits,取值在[3~63];

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YzjvSXRn-1685799594817)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230506122251294.png)]

Basic Header为2bytes时:第一个字节除了fmt外,其余6位表示数字0,csid范围是[64~319],即最大为(2^8 - 1) + 64 = 319

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zVvwMGiM-1685799594818)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230506122405480.png)]

Basic Header为3bytes时:第一个字节除了fmt外,其余6位表示数字1,csid范围是[64~65599],最大值为 (2^16 - 1) + 64 = 65599

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VpzPjBez-1685799594819)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230506122900009.png)]

3.2.2 Message Header

Message Header的格式和长度取决于Basic Header的chunk type,即fmt,fmt取值[0-3],所以共有4种不同的chunk格式,目的是减少重复数据发送,提高 chunk data的占比。同时也有4种不同的Message Header。

Message Header结构如下:

chunk type = 0(fmt = 0):Message Header共11字节,此类型必须在块流开始时使用,当流时间戳向后(例如,回退播放)时也要使用此格式;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RJZk9SOn-1685799594820)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230506123140944.png)]

FieldTypeCommenttimestamp3 bytes时间戳,如果值大于等于16777215(0xFFFFFF),该字段必须等于16777215,然后转存到4字节的Extended Timstamp字段中。接收端判断是0xFFFFFF后会Extended Timstamp解析时间戳message length3 bytes指的是message拆分之前message payload的长度(不包含header),而且如果被拆分成chunk,此字段填充拆分前message的body长度,而不是chunk的长度message type id1 byte消息类型,如8代表audio数据,9代表video,其它值可参考后文的消息类型message stream id4 bytes表示该Chunk所在的流的ID chunk type = 1(fmt = 1):Message Header共7字节,和前一个chunk共用message stream id(msid),因此省去了message stream id的4字节,表示此Chunk和上一次发的Chunk所在的流相同(不是相同的message),如果在发送端和对端有一个流连接的时候尽量采用这种格式;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hHsoatp7-1685799594821)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230506123539097.png)]

FieldTypeCommenttimestamp3 bytes时间戳,如果值大于等于16777215(0xFFFFFF),该字段必须等于16777215,然后转存到4字节的Extended Timstamp字段中。接收端判断是0xFFFFFF后会Extended Timstamp解析时间戳message length3 bytes指的是message拆分之前message payload的长度(不包含header),而且如果被拆分成chunk,此字段填充拆分前message的body长度,而不是chunk的长度message type id1 byte消息类型,如8代表audio数据,9代表video,其它值可参考后文的消息类型 chunk type = 2(fmt = 2):Message Header共3字节,相对于 chunk type = 1 格式又省去了message length的3个字节和message type id的1个字节,**表示此 chunk和上一次发送的 chunk 的message length、message type id都相同。**余下的这三个字节表示 timestamp delta,使用同type=1;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QPeKj872-1685799594822)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230509185607142.png)]

FieldTypeCommenttimestamp3 bytes和上一个chunk的时间差。如果值大于等于16777215(0xFFFFFF),该字段必须等于16777215,然后转存到4字节的Extended Timstamp字段中。接收端判断是0xFFFFFF后会去Extended Timstamp解析时间戳 chunk type = 3(fmt = 3):Message Header共0字节,即此时chunk没有Message header。 当它在type = 0的chunk后面时,表示和前一个chunk的时间戳是相同的,也就是一个Message拆分成多个chunk时,后一个chunk和前一个chunk同属一个Message自然也就可以不用传Message header。当它跟在type = 1或type = 2的chunk后面时,表示和前一个时间戳的差相同。如第一个chunk是type = 0,timestamp = 0,第二个chunk是type = 2,timestamp delta = 20,表示时间戳为0+20=20,第三个chunk是type = 3,则timestamp delta = 20,表示时间戳为20+20=40。

chunk->message header 和 message->message header 的关系

chunk type = 0(fmt = 0)时,如果没有chunk->extended timestamp则chunk->message header的内容和message->message header完全相同。如果存在chunk->extended timestamp则chunk->message header->timestamp和message->message header->timestamp的内容不相同,但是其余字段的内容是完全相同的。chunk type = 1(fmt = 1)时,chunk->message header除了timestamp delta的字段,其余字段内容都和message->message header相同。chunk type = 2(fmt = 2)时,chunk->message header没有字段和message->message header相同。chunk type = 3(fmt = 3)时,chunk->message header没有字段和message->message header相同。 4. 消息类型

rtmp协议中有多种消息,用于数据传输和命令控制等操作,所有的消息都是封装成message,然后通过chunk来传输。

4.1 协议控制消息(Protocol Control Messages)

在RTMP的chunk流会用一些特殊的值来代表协议的控制消息,属于RTMP chunk流协议层的消息,它们的Message Stream ID必须为0(代表控制流信息),CSID必须为2,Message Type ID可以为1,2,3,5,6,具体代表的消息会在下面依次说明。控制消息的接受端会忽略掉chunk中的时间戳,收到后立即生效。

4.1.1 设置块大小(Set Chunk Size ,Message Type ID = 1) RTMP消息需要以chunk size为单位封装成chunk包发送,因此接收端需要根据chunk size才能正确解包,所以双端都要记录对端的封包单位chunk size,默认128 bytes。通信过程中可发送此消息通知对端更新其记录的本端的chunk size。比如client想发送131 bytes的音频数据(此时chunk size为128 bytes,不更新chunk size的话需要拆成两个chunk),此时client可通知对端,这边client的chunk size更新为131 bytes,之后发送一个data为131 bytes的chunk即可,server端收到Set Chunk Size之后更新chunk size即可正确解析之后到来的chunk。双端的chunk size各自独立维护,可以不同。例如client可以发送131 bytes的chunk,server也按照131 bytes解析,server发送128 bytes的chunk,client也按照128 bytes解析。message payload如下所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UmkA50LT-1685799594823)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230509192036386.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ySMERV1S-1685799594824)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230509192259486.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YdHy7VbP-1685799594825)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230509192433886.png)]

4.1.2 中断消息(Abort Message,Message Type ID = 2) 发送数据过程中,发送端可发送Abort消息通知接收端丢弃当前未接收完的Message及忽略之后的消息。先前已收到的chunk将被全部被抛弃,接收端根据Abort消息中的chunk stream id(csid)可丢弃对应chunk流中之后的所有数据。比如在发送端需要关闭时,发送此消息通知对端之后的数据可以不用处理了;message payload如下所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BNbSeGOu-1685799594826)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230509192831273.png)]

FieldTypeCommentMessage Payloadchunk stream id32 bits此字段保存要丢弃其当前消息的块流ID 4.1.3 应答消息(Acknowledgement,Message Type ID = 3) 当收到对端消息字节数等于接收窗口大小时,接收端要回复一个应答消息(相当于ack)告知对端可以继续发送数据,发送方在收到应答消息之前不会再继续发送消息;message payload如下所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZtayywWr-1685799594826)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230513162701702.png)]

FieldTypeCommentMessage Payloadchunk stream id32 bits截止目前接收到的数据总和,以字节为单位 4.1.4 应答窗口大小(Window Acknowledgement Size,Message Type ID = 5) 规定接收端接收多少数据后需要发送一个应答消息。发送端可以发送此消息通知对端更新窗口大小,一般在音视频数据之前发送。并且双端的window size共同维护,保持相同。message payload如下所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6gH2cpOk-1685799594827)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230513163317648.png)]

FieldTypeCommentMessage Payloadacknowledgement window size32 bits接收端接收多少数据后需要发送一个应答消息 4.1.5 设置流带宽(Set Peer Bandwidth,Message Type ID = 6) 客户端或服务器发送此消息以限制其对等端的输出带宽;message payload如下所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1sqzjCH0-1685799594828)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230513163640334.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZqeTHwXy-1685799594828)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230513163724702.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pxRxZAhS-1685799594829)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230513163747001.png)]

4.2 命令消息(Command Message) 命令消息(Command Messages)是用于 C-S 进行直接交互应答的一类消息。一般情况下,命令消息的发送对端,是需要对端进行应答信号反馈的。需要AMF编码,AMF0编码时Message Type ID = 20,使用AMF3编码时Message Type ID = 17,CSID通常为3;命令类型的消息包含命令名称、事务ID和相关参数。如client端发送connect命令时需要包含要连接的应用名称作为参数,然后server端回复消息时带上收到的transaction ID表示对此条消息的回应。回复命令有_result,_error,或者其他如verifyClient,contactExternalServer的方法名;发送命令消息的对象有两种分别是NetConnection和NetStream:NetConnection:表示双端的上层连接,服务器和客户端之间进行网络连接的一种高级表示形式。NetStream:表示流信息的传输通道如音频流、视频流,以及控制流信息的状态,如Play播放流,Pause暂停; 4.2.1 网络连接命令(NetConnection Commands) 表示双端的上层连接,服务器和客户端之间进行网络连接的一种高级表示形式。网络连接允许使用:连接(connect)、调用(call)、创建流(createStream),每一种消息都有应答消息。应答消息中的“Transaction ID”表明对哪个请求的应答。 4.2.1.1 连接(connect)

客户端向服务器发送connect命令,以请求连接到服务器应用程序实例(Application Instance)。不同的Application Instance可根据功能进行区分,**比如直播可以用live表示,点播可以用vod表示,**测试环境可以用test表示,用户可自定义。例如:rtmp://192.127.0.1/test/16,可以表示test环境,16是对流的描述,用户可自定义。ip后面的内容可以用来标识流的内容 ;

消息的结构如下所示:

请求消息:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e1ItX16K-1685799594830)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230513164530293.png)]

Optional User Arguments(额外的用户参数) 包含如下内容:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KwJMP0Km-1685799594830)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230603155506965.png)]

audioCodecs包含如下内容:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J010kfIx-1685799594831)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230603155536012.png)]

videoCodecs包含如下内容:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b6b8vYTI-1685799594832)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230603155604918.png)]

videoFunction包含如下内容:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IUp9DpFf-1685799594832)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230603155633192.png)]

应答消息

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zekYjt79-1685799594833)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230603155802356.png)]

4.2.1.2 调用(call)

NetConnection对象的调用方法在接收端运行远程过程调用(RPC)。被调用的RPC名称作为参数传递给调用命令。

消息的结构如下所示:

请求消息

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NOKeYRCF-1685799594834)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230603160209630.png)]

应答消息

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jyap7U1a-1685799594834)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230603160236682.png)]

4.2.1.3 创建流(createStream)

客户端将此命令发送到服务器,以创建消息通信的逻辑通道。音频、视频和元数据的发布通过使用createStream命令创建的流通道执行。NetConnection是默认的通信信道,其流ID为0。协议和一些命令消息(包括createStream)使用默认的通信通道。

消息的结构如下所示:

请求消息

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ihhgNSu6-1685799594836)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230603160428531.png)]

应答消息

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JNykhag9-1685799594837)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230603160456717.png)]

4.2.2 网络流命令(NetStream)

1)网络流命令是在数据信道建立完毕后(先有NetConnection,才有NetStream命令),用来对音视频数据流进行直接控制的命令类型。这些命令作用于当前信道对应数据的操控行为,都是客户端向服务端发送的命令,一个NetConnection对象可以有多个NetStream,进而支持多种数据。常见的命令如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KdkwtA1W-1685799594838)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230603160641015.png)]

2)网络流命令的请求命令所对应的应答命令,被统一命名为 “onStatus” 以描述数据所处的状态发生变更,因此具有统一的格式(注意:不是所有的消息都有应答,而是有应答消息的都是如下格式)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LHks34EJ-1685799594839)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230603160714100.png)]

因此,我们通过处理 NetStream 的应答指令的 ‘level’ 和 ’code’ 字段,就可以知道相应的请求指令是否有成功执行了。进而判断当前状态,并确认是否需要执行后续的操作流程。

4.2.2.1 播放(play)

客户端发送此命令让服务器播放流。也可以多次使用此命令创建一个播放列表。如果你想创建一个在不同直播或录制流之间切换的动态播放列表,多次调用play并将reset字段设置为false。相反,如果你想立即播放指定的流,将reset字段设置为true。

消息的结构如下所示:

请求消息(客户端发送给服务器):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RMFxF9rP-1685799594841)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230603160915405.png)]

4.2.2.2 播放2(play2)

play命令不同的是,play2命令可以将当前正在播放的流切换到同样数据但不同码率的流上,服务端会维护多种比特率的文件来供客户端使用play2命令来切换。

消息的结构如下所示:

请求消息(客户端发送给服务器):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5IcW0NPi-1685799594842)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230603161052392.png)]

4.2.2.3 删除流(deleteStream)

NetStream在NetStream对象被销毁时发送deleteStream命令,用于客户端告知服务端本地的某个流对象已被删除,不需要再传输此路流。

消息的结构如下所示:

请求消息(客户端发送给服务器):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3reRCd4K-1685799594843)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230603161209493.png)]

应答消息(应答格式参考“网络流命令(NetStream)–> 2”): 4.2.2.4 接收音频(receiveAudio)

客户端通知服务端是否要发送音频。

消息的结构如下所示:

请求消息(客户端发送给服务器):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R7GYhHRD-1685799594844)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230603161345393.png)]

应答消息(应答格式参考“网络流命令(NetStream)–> 2”):

如果,Bool Flag = false 这个消息只用来通知服务器,没有统一应答返回。 如果,Bool Flag = true则应答消息’code’为 NetStream.Seek.Notify 或 NetStream.Play.Start

4.2.2.5 接收视频(receiveVideo)

客户端通知服务端是否要发送视频。

消息的结构如下所示:

请求消息(客户端发送给服务器):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cXRRDPqi-1685799594846)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230603161545041.png)]

应答消息(应答格式参考“网络流命令(NetStream)–> 2”):

如果,Bool Flag = false 这个消息只用来通知服务器,没有统一应答返回。 如果,Bool Flag = true则应答消息’code’为 NetStream.Seek.Notify 或 NetStream.Play.Start

4.2.2.6 发布(publish)

客户端发送此消息,将命名流发布到服务器。其他客户端可以使用此流名来播放流,接收发布的音频,视频,以及其它数据消息。

消息的结构如下所示:

请求消息(客户端发送给服务器):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TGTSiYmW-1685799594847)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230603161738570.png)]

应答消息(应答格式参考“网络流命令(NetStream)–> 2”):

publish 的返回状态 NetStream.Publish.Start,这个消息不止由 onStatus 统一应答携带,也会由 onFCPublish 返回。

4.2.2.7 定位(seek)

定位到视频或音频的某个位置,以毫秒为单位。

消息的结构如下所示:

请求消息(客户端发送给服务器):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dN0dP391-1685799594848)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230603161901212.png)]

应答消息(应答格式参考“网络流命令(NetStream)–> 2”):

当定位成功,服务器发送NetStream.Seek.Notify的状态消息。失败的时候,它返回一个_error的消息

4.2.2.8 暂停(pause)

客户端发送pause命令以告诉服务器暂停或者开始播放。

消息的结构如下所示:

请求消息(客户端发送给服务器):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FCE9jL0o-1685799594850)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230603162008821.png)]

应答消息

pause 成功后,会有 ’code’ 为 NetStream.Pause.Notify 消息返回。 unpause 成功后,会有 ’code’ 为 NetStream.Unpause.Notify 消息返回。

4.3 数据消息(Data Message)

传递一些元数据(MetaData,比如视频名,分辨率等等)或者用户自定义的一些消息。当信息使用AMF0编码时,Message Type ID=18,AMF3编码时Message Type ID=15。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QUc7L1qm-1685799594851)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230603153811106.png)]

4.4 共享消息(Shared Object Message)

1)共享对象是在多个客户端、实例等之间同步的Flash对象,Flash对象是由键值对组成的集合。每条消息可以包含多个事件。当信息使用AMF0编码时,Message Type ID=19,AMF3编码时Message Type ID=16。

2)消息结构如下所示:

每一个Event Type代表一个事件,每条消息可以包含多个事件。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yhBdo5cN-1685799594852)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230603154112957.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7xwrkK3Y-1685799594853)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230603154246277.png)]

4.5 音/视频信息(Audio/Video Message)

1)每一个message就是一帧数据。对于flv的tag而言,就是对应rtmp每个message,一个tag就是一个message,是一一对应的关系;相当于每一个tag都封装成一个message。

2)RTMP 块流使用Message Type ID=8 作为音频数据,flv的tag header->tag type也用8来表示音频。通常音频流的csid是4(也可以自定义),音频流的每一个chunk的csid都是相同的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EA0u8fC8-1685799594854)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230603154617868.png)]

3)RTMP 块流使用Message Type ID=9 作为视频数据,flv的tag header->tag type也用9来表示音频。通常视频流的csid是6(也可以自定义),视频流的每一个chunk的csid都是相同的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ymkGsdBI-1685799594855)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230603154645263.png)]

4.6 聚合消息(Aggregate Message)

1)聚合消息是包含一系列RTMP子消息的单个消息,Message Type ID=22。

2)聚合消息的消息流ID覆盖聚合内部的子消息的消息流程ID;聚合消息和第一个子消息的时间戳之间的差异是用于将子消息的时间戳记重新规范化为流时间尺度的偏移量。偏移量被添加到每个子消息的时间戳,以达到标准化的流时间。第一个子消息的时间戳应与聚合消息的时间戳记相同,因此偏移量应为零;返回指针包含前一条消息的大小,包括其标头,它被包括以匹配FLV文件的格式,并用于反向搜索,类似于flv的previous tag header。

3)使用聚合消息有几个性能优势:

​ 3.1)区块流最多可以在一个区块内发送一条完整的消息。因此,增加块大小并使用聚合消息可以减少发送的块数。

​ 3.2)子消息可以连续存储在内存中。当进行系统调用以在网络上发送数据时,效率更高。

4、消息格式如下所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UYMojBsg-1685799594856)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230603154751490.png)]

4.7 用户控制消息(User Control Message Event)

1)RTMP 流中的用户控制消息在接收时立即生效,消息中的时间戳被忽略。该信息在 chunk 流中发送时,它们的Message Stream ID必须为0(代表控制流信息),CSID必须为2,Message Type ID必须为4。和前面提到的协议控制信息(Protocol Control Message)不同,这是在RTMP协议层的,而不是在RTMP chunk流协议层的,这个很容易弄混。

2)消息格式如下所示:

前 2 个字节数据用来标识事件类型,事件类型后面是事件数据。事件数据字段的大小是不固定的。因为消息是通过RTMP chunk发送的,所以最大块大小应足够大,以允许这些消息在单个块中。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YqzCW2ay-1685799594857)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230603154854630.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dK73DkOm-1685799594857)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230603154927357.png)]

Event Type = StreamBegin

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-q3mKUgIp-1685799594858)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230603154950683.png)]

Event Type = SetBufferLength

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qbGAY276-1685799594859)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230603155011972.png)]

5. 数据传输过程 5.1 发送/接收过程概述 Chunk在传输时:同一个Message产生的多个Chunk只会串行发送,先发送的Chunk一定先到达。不同Message产生的Chunk可以并行发送。并行发送的Chunk复用了一条TCP链接从下图中可以看出,发送端同时发送了3个chunk 发送端、服务端

img

(1)发送端

把数据封装成消息(Message):协议控制消息、用户控制消息、音视频数据消息等。 把消息分割成消息块(Chunk,网络中实际传输的内容)。 将分割后的消息块(Chunk)通过TCP协议发送出去。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qdKELlvN-1685799594859)(C:\Users\guanzedong\AppData\Roaming\Typora\typora-user-images\image-20230513165620858.png)]

(2)接收端

在通过TCP协议收到数据后,现将消息块重新组合成消息(Message)。 通过对消息进行解封装处理就可以恢复出数据。

img

RTMP消息优先级 在RTMP中,消息(Message)主要分为两大类:**控制消息(协议控制消息、用户控制消息)和数据消息(音视频消息)**但是通路只有一条(RTMP是单通路),到底谁先走呢,谁后走呢?在实际传输过程中是对消息分优先级,优先级高的先行。优先级低的不能阻塞 优先级高的;总结起来就是协议先行,数据次之; 协议控制消息(Protocol Control Messages)和用户控制消息(User Control Messages)应该包含消息它们的Message Stream ID必须为0(代表控制流信息),CSID必须为2。 数据消息(音频信息、音频消息)比控制信息的优先级低。 另外,一般情况下,音频消息比视频数据优先级高。

img

5.2 Chunk传输过程示例 5.2.1 Audio Example

一个audio的消息流:Message Type ID = 8 表示音频,每个message有相同的message stream ID,message type ID,message length,以及增量相同的timestamp。

img

audio message比较小,基本不会对message进行拆分,一个message对应一个chunk,实际发送的情况为:

第1个Message的chunk的chunk type为0,因为前面没有可参考的chunk,此时chunk的长度为Basic Header(1 byte)+ Message Header(11 bytes)+ Payload(32 bytes)= 44 bytes。第2个Message的chunk可与前一个共用length、type id和msid,但是不可省略timestamp delta字段,所以chunk type为2,同理可得chunk长度为36 bytes。第3个Message的chunk可以共用前一个的timestamp delta字段,所以chunk type为3,chunk长度为33 bytes。第4个Message的chunk可以共用前一个的timestamp delta字段,所以chunk type为3,chunk长度为33 bytes。

img

5.2.2 Video Example

一个video的消息流:Message Type ID = 9 表示视频,

img

Payload length为307 > 128字节(chunk data默认是128字节),所以需要将Message拆分成多个chunks,首个chunk使用Type 0;

之后的chunk由于是同一个Message拆分而来,以上字段都可共享,所以直接使用Type 3

需要注意的是,第一个chunk的length需要传入整个message的负载长度即307

img

5.3 RTMP推拉流过程 5.3.1 推流过程

客户端发送握手请求,和服务器完成握手;

客户端发送命令消息中的“连接”(connect)到服务器,请求与一个服务应用实例建立连接;

服务器接收到连接命令消息后,发送确认窗口大小(Window Acknowledgement Size)到客户端,同时连接到连接命令中提到的应用程序;

服务器发送设置带宽协议(Set Peer Bandwidth)消息到客户端;

客户端处理设置带宽协议控制消息后,发送确认窗口大小(Window Acknowledgement Size)到服务器端;

服务器发送用户控制消息中的“流开始”(StreamBegin)消息到客户端,通知客户端流成功创建,可用于通信;

服务端发送connect的“应答消息”(_result),通知客户端连接的状态;

客户端发送网络连接命令的“创建流”(createStream)消息到服务端,以创建消息通信的逻辑通道。音频、视频和元数据的发布通过使用createStream命令创建的流通道执行。服务端发送createStream的“应答消息”(_result);

客户端发送网络流命令的“发布”(publish)到服务端,将命名流发布到服务器。其它客户端可以使用此流名来播放流,接收发布的音频,视频,以及其他数据消息;

客户端发送命令消息或音视频数据至服务端;

img

img

注意: 上图中在看发送的数据的同时也要关注发送端和接收端的ip,弄清楚是客户端还是服务器发送的消息;在推流的时候往往能看到 FCPublish、releasestream 消息,这两种消息官方文档中没有描述,在srs代码中只是接收了消息并给出了应答,但是在代码内部并没有处理这两种消息 5.3.2 拉流过程

客户端发送握手请求,和服务器完成握手;

客户端发送命令消息中的“连接”(connect)到服务器,请求与一个服务应用实例建立连接;

客户端发送网络连接命令的“创建流”(createStream)消息到服务端,以创建消息通信的逻辑通道。音频、视频和元数据的发布通过使用createStream命令创建的流通道执行。服务端发送createStream的“应答消息”(_result);

客户端发送网络流命令中的“播放”(play)到服务端;

服务端发送协议控制消息中的“设置块大小”(Set Chunk Size)到客户端设置chunk大小;

服务器发送另一个协议控制消息(用户控制),指定事件“StreamIsRecorded”和该消息中的流ID。消息在前2字节中携带事件类型,在后4字节中携带流ID;

服务端发送用户控制消息中的“流开始”(StreamBegin)消息到客户端,通知客户端流成功创建,可用于通信;

如果客户端发送的播放命令成功,则服务器发送onStatus命令消息NetStream.Play.Start和NetStream.Play.Reset。仅当客户端发送的播放命令设置了重置标志时,服务器才会发送NetStream.Play.Reset。如果未找到要播放的流,服务器将发送onStatus消息NetStream.Play.StreamNotFound;

服务端发送音视频数据到客户端;

img

img

客户端发送网络流命令中的“播放2”(play2)到服务端服务端发送不同码率的音视频数据到客户端

img

6. 总结

message分成chunk的目的:防止一个大的数据包传输时间过长,阻塞其它数据包的传输;

chunk分成4种形式的目的:减少重复数据发送,提高chunk data的占比;

消息优先级:总结起来就是协议先行,数据次之。详见“五、数据传输过程-> 5.1->细节说明->3、RTMP消息优先级”;

协议控制消息(Protocol Control Messages):中断消息只能是数据发送端给接收端(如果是推流就是客户端发给服务端,如果是拉流则是服务端发给客户端),其它消息都可以双端发送;

命令消息(Command Message)

先有网络连接命令再有网络流命令,也就是说先建立连接,才能传输数据;所有命令消息都需要应答,网络连接中不同的命令应答格式不同,网络流命令的应答命令格式相同;除了调用(call)命令可以双端发送外,其余命令消息都是客户端向服务端发送,服务端向客户端发送应答消息; 7. RTMP使用场景及优缺点

使用场景:用于流式传输到 Flash 播放器,广泛应用于直播。

RTMP的优点

低延迟:RTMP使用独占的 1935 端口,无需缓冲,可以实现低延迟;适应性强:所有 RTMP 服务器都可以录制直播媒体流,同时还允许观众跳过部分广播并在直播开始后加入直播流;灵活性:RTMP 支持整合文本、视频和音频,支持 MP3 和 AAC 音频流,也支持MP4、FLV 和 F4V 视频;

RTMP的缺点

HTML5 不支持:标准HTML5 播放器不支持 RTMP 流;容易受到带宽问题的影响:RTMP 流经常会出现低带宽问题,造成视频中断;HTTP 不兼容:无法通过 HTTP 流式传输 RTMP,必须需要实现一个特殊的服务器,并使用第三方内容交付网络或使用流媒体视频平台; 8. QA

1、rtmp为什么会低延时?

一个message就是帧,相当于一帧数据产生后立刻发送过来,所以延时较低。相比于HLS,一个切片生成后才会发送,延时就会很大。

2、为什么会有AMF0和AMF3?有什么区别?

img

AMF从Flash MX时代的AMF0发展到现在的AMF3。AMF3用作Flash Playe 9的ActionScript 3.0的默认序列化格式,而AMF0则用作旧版的ActionScript 1.0和2.0的序列化格式。 在网络传输数据方面,AMF3比AMF0更有效率。AMF3能将int和uint对象作为整数(integer)传输,并且能序列化ActionScript 3.0才支持的数据类型, 比如ByteArray,XML和Iexternalizable。

3、接收端如何知道哪些chunk属于同一个message?

通过srs代码(SrsProtocol::read_message_payload)得知:接收端将接收到chunk的chunk data的大小加和,如果等于message payload(通过chunk->message header->message length获取)的则认为是同一个message。

4、每个chunk的csid是否相同?

csid是chunk的一个标识,csid和chunk的类型有关,详见“3.2.1 Basic Header”,所以同一类型的不同chunk的csid是相同的。csid和chunk是否属于同一个message没关系。

5、chunk的哪些字段继承至message ?

可以参考“3.2.2 Message Header->3、chunk->message header 和 message->message header 的关系”

6、message拆分成chunk传输是防止低优先级的数据传输时间长阻塞优先级高的数据传输,哪些是高优先级数据,哪些是低优先级数据?

消息优先级:总结起来就是协议先行,数据次之。

7、rtmp和flv的关系

rtmp是数据传输协议,flv是音视频的封装格式。rtmp和flv在音视频数据的封装上是相同的,即message payload的数据形式和flv tag data的数据形式是相同的。

音频

img

视频

img



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3