rtmp协议分析

<div id="article_content" class="article_content tracking-ad" data-mod="popu_307" data-dsm="post">
<div style="font-family:-webkit-standard;font-size:14px;"><strong><span style="font-size: 18px;"></span></strong></div><div style="font-family:-webkit-standard;font-size:14px;"><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;">标准规范学习:</span></div><div style="margin: 0px; padding: 0px;"><hr style="margin: 0px; padding: 0px;"></div><div style="margin: 0px; padding: 0px;">rtmp消息结构,包括几个部分:</div><div style="margin: 0px; padding: 0px;">时戳:4 &nbsp;byte,单位毫秒。超过最大值后会翻转。</div><div style="margin: 0px; padding: 0px;">长度:消息负载的长度。</div><div style="margin: 0px; padding: 0px;">类型ID:Type Id 一部分ID范围用于rtmp的控制信令。还有一部分可以供上层使用,rtmp只是透传。这样可以方便的在rtmp上进行扩展。</div><div style="margin: 0px; padding: 0px;">消息流ID:Message Stream ID,用于区分不同流的消息。</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;"><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;">两个ID的区别:</span></span></div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;">Message&nbsp;stream:传输消息的逻辑通道。</span></div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;">Message&nbsp;stream&nbsp;ID:每个消息都有一个流id,用于指明属于哪个流。</span></div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;">&nbsp;</span></div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;">Chunk:是更底层的一个概念。Message有可能过大,需要分割成一个个碎片,chunk就是一个消息的部分碎片。</span></div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;">Chunk&nbsp;stream:传输chunk的逻辑通道。<br style="margin: 0px; padding: 0px;"></span></div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;">Chunk&nbsp;stream&nbsp;ID:用于标示chunk属于哪个逻辑通道。</span></div></div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px; line-height: 1.8; font-family: Courier;"><span style="margin: 0px; padding: 0px;">message stream和chunk stream应该属于不同的层次,message属于应用层次消息,chunk属于更底层rtmp协议层次。</span></span></div><div style="margin: 0px; padding: 0px;">一个chunk stream 上能够跑多个message stream。message stream id在一个chunk stream下要不相等。</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;">消息会切分成一个个小的块进行传输。</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;">rtmp的时戳单位是毫秒。</div><div style="margin: 0px; padding: 0px;">连接握手流程图:</div><div style="margin: 0px; padding: 0px;"><img src="cid:e45334cce498e4d39c906023603bc490" alt="" data-en-img-selected="true" style="margin: 0px; padding: 0px; border: 0px; max-width: 900px;"></div><div style="margin: 0px; padding: 0px;">握手过程:服务器和客户端各自发送三个包:c0,c1,c2,s0,s1,s2,服务器必须在收到c0后才发送s0和s1,也可以等到c1才发送;服务端必须收到c1才能够发送s2,客户端必须收到s2才能够发送c2</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;">c0和s0就是一个字节:</span>表示协议版本号。现在是03。</div><div style="margin: 0px; padding: 0px;">01 234567</div><div style="margin: 0px; padding: 0px;">+-+-+-+-+-+-+-+-+</div><div style="margin: 0px; padding: 0px;">| &nbsp; &nbsp;version &nbsp;&nbsp; |</div><div style="margin: 0px; padding: 0px;">+-+-+-+-+-+-+-+-+</div><div style="margin: 0px; padding: 0px;">&nbsp; C0&nbsp;and&nbsp;S0&nbsp;bits</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;">c1和s1长度1536,</span></div><div style="margin: 0px; padding: 0px;"><img src="cid:ce637b0f0ea258178c272e67f242fe2e" alt="" data-en-img-selected="true" style="margin: 0px; padding: 0px; border: 0px; max-width: 900px;"></div><div style="margin: 0px; padding: 0px;">抓包发现,zero字段不一定为0。</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;">c2和s2的长度也是1536</span></div><div style="margin: 0px; padding: 0px;"><img src="cid:9c5c9f38b8bbbe2523da48e9042e446b" alt="" data-en-img-selected="true" style="margin: 0px; padding: 0px; border: 0px; max-width: 900px;"></div><div style="margin: 0px; padding: 0px;">通过vlc和nginx rtmp抓的包发现和规范不对应。安装规范,time和time2应该是为了计算带宽和延迟。</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;">交互过程:</span></div><div style="margin: 0px; padding: 0px;"><img src="cid:8967244e142889691b983504f2684539" alt="" data-en-img-selected="true" style="margin: 0px; padding: 0px; border: 0px; max-width: 900px;"></div><div style="margin: 0px; padding: 0px;">一般客户端一起发生c0和c1,然后服务器直接发送s0,1,2。客户端收到后发送c2,握手完成。三次交互就可以了。</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;">块流:</span></div><div style="margin: 0px; padding: 0px;"><hr style="margin: 0px; padding: 0px;"></div><div style="margin: 0px; padding: 0px;">一个连接可以穿输多个块流。每个块流有不同的id来区分。</div><div style="margin: 0px; padding: 0px;">块传输运行将大的消息包切割为小的消息包。防止大的数据比如视频阻塞连接,导致音频和信令也无法传递。</div><div style="margin: 0px; padding: 0px;">小的消息也可以成本更加低的传输,包头会压缩.</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;">块流大小可以配置。有一个消息:set chunk size可以协商大小。大的块可以降低cpu 负载,但是可能会阻塞重要消息;小的块不利于传输,特别是在低带宽的情况下。</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;">块允许 层协议将大的消息 解 更小的消息,例如,防 体积大的但优先级小的消 息 (比如视频) 阻碍体积较小但优先级高的消息 (比如音频或者控制命 )<span style="margin: 0px; padding: 0px;">——传送过程,音频和控制的优先级较高。</span></div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;">块的大小是&nbsp; 配置的 它&nbsp; 使用一个设置块大小的控制消息 行设置 (参考 5.4.1) 更大的块大小&nbsp; 降&nbsp; CPU 开销,但在&nbsp; 宽 接时因 它的大量的写入也会延 &nbsp; 他内容的传递 更小的块不利于高比特率的流化 所 块的大小设置 决于 体情况。<span style="margin: 0px; padding: 0px;">——这是使用tcp必须要考虑的问题。块过大会影响音频和控制的发送。过小会影响效率和比特率。</span></div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;">块类型:</span></div><div style="margin: 0px; padding: 0px;"><img src="cid:d346a62e810bc87d5f683f6abf67b129" alt="" data-en-img-selected="true" style="margin: 0px; padding: 0px; border: 0px; max-width: 900px;"></div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;">块基本头:</span></div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;"><hr style="margin: 0px; padding: 0px;"></div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div title="Page 12" style="margin: 0px; padding: 0px;"><img src="cid:b328f278072fd6b8af647a2b119a0ac2" alt="page12image7536" data-en-img-selected="true" style="margin: 0px; padding: 0px; border: 0px; max-width: 900px;" width="180.720000" height="135.720000"></div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;">第一个字节都是这样。其中fmt用来指示Message Header。</div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;">cs id:</span></div><div style="margin: 0px; padding: 0px;">cs id是chunk stream id的缩写。范围是3——65599。可变字节,后面可以跟一个字节,也可以跟两个字节。跟几个字节是有第一个字节的后面6位的值决定的。这一点中文翻译写的不完整。</div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px; line-height: 1.8; font-family: TimesNewRoman;">如果最后6位值为0,则表示后面只有一个字节。id范围是64——319,其实就是0——255,最大和最小加64。</span></div><div title="Page 13" style="margin: 0px; padding: 0px;"><img src="cid:78f28d1a3dd5432bc5282980e7818822" alt="page13image384" data-en-img-selected="true" style="margin: 0px; padding: 0px; border: 0px; max-width: 900px;" width="258.720000" height="114.720000"></div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px; line-height: 1.8; font-family: TimesNewRoman;">如果最后6位值为1,则表示后面只有两个字节。</span><span style="margin: 0px; padding: 0px; line-height: 1.8; font-family: TimesNewRoman;">id范围是</span>64 - 65599,其实就是0——65535,<span style="margin: 0px; padding: 0px; line-height: 1.8; font-family: TimesNewRoman;">最大和最小加64。j</span></div><div style="margin: 0px; padding: 0px;">计算方法比较特殊:(第三 个 节) * 256 + 第二 个 节 + 64</div><div title="Page 13" style="margin: 0px; padding: 0px;"><img src="cid:6a6f37efd4b181c1faeef924541b8276" alt="page13image7080" data-en-img-selected="true" style="margin: 0px; padding: 0px; border: 0px; max-width: 900px;" width="364.560000" height="91.560000"></div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;">块流id应该是区分每一个流,比如,一个视频通话,包括视频和音频两个流。这里的意思应该是一个连接可以传递多个流,通过块流id进行区分。</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;">块消息头</span></div><div style="margin: 0px; padding: 0px;"><hr style="margin: 0px; padding: 0px;"></div><div style="margin: 0px; padding: 0px;">块消息头格式由块基本头的fmt字段决定。共四种。</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;">fmt:</span></div><div style="margin: 0px; padding: 0px;">四种类型:0 1 2 3</div><div style="margin: 0px; padding: 0px;">包头必须尽可能的压缩。</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;">type 0:</span>用在流开始,或者时戳重置的时候。message header消息头共11byte。</div><div style="margin: 0px; padding: 0px;"><img src="cid:cfac2926fe905f01d5673a8cb7d82afa" alt="" data-en-img-selected="true" style="margin: 0px; padding: 0px; border: 0px; max-width: 900px;"></div><div style="margin: 0px; padding: 0px;">三字节时戳:表示范围为0——16777215(0xffffff),如果大于等于16777215,则表示还有一个字节的扩展时戳。chunk header的第三部分就是extend timestamp。这一本部分是否存在由三个字节的值决定。<span style="margin: 0px; padding: 0px;">注意,这个是绝对时戳,而后面的几种类型trunk都是时戳的增量;区分消息截止是根据消息的长度来进行的。那么输入处理主动丢弃包的情况?</span></div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;">type 1</span>:message header有7byte,比type 0少了4个字节的message stream id,使用上一个块的id。大小可变的消息(比如视频的每一帧数据)往往会在第一帧(第一帧的第一个chunk需要用type0)后面的帧的第一个chunk中使用type1。为什么?因为可变数据,必须有长度信息记录。</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;"><img src="cid:b24b3c6391472b18c09f7ea0abac4472" alt="" data-en-img-selected="true" style="margin: 0px; padding: 0px; border: 0px; max-width: 900px;"></div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;">type2</span>:只有三个字节。没有message stream id和message 长度。全部使用同一个chunk stream的上一个chunk的。适用于长度不变的步伐传输。</div><div style="margin: 0px; padding: 0px;"><img src="cid:38a115a03c9044e3ac8c81e13335f3de" alt="" data-en-img-selected="true" style="margin: 0px; padding: 0px; border: 0px; max-width: 900px;"></div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;">type 3</span>:0个字节的message header。时戳,长度,消息流id全部使用同一个chunk stream的上一个chunk。当一个消息切分后划分到多个chunk的时候,除了带个chunk,所有的其他chunk应该使用type3。——如何判断包结束?根据消息的长度,第一个chunk中的长度应该是一个消息完成的长度。</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;">四种格式的使用:</span></div><div style="margin: 0px; padding: 0px;">第一个消息的第一个chunk 使用type0,携带全部的信息;后面的chunk使用type3;</div><div style="margin: 0px; padding: 0px;">第二个消息,如果是视频消息,长度可变,则第一个chunk使用type1,值丢弃message stream id即可。第二个消息的其他chunk使用type3。</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;">如果是音频,且长度相同,则第一个消息用type0+type3,第二个消息用type2+type3。</div><div style="margin: 0px; padding: 0px;">如果音频消息,长度,消息间时戳增量(delta)都相同,则第一个消息可以用type0+type3,后面的所有消息全部用type3即可。这样的话也要求第一个消息的时戳必须和消息间的时戳增量相同。</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;">注意:除type0外,其他的全部是时戳的增量。而不是时戳绝对值。</div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;">从上面来看,音频和视频的chunk id应该是不一样的。否则无法区分。因为有些块是没有message stream id,只有chunk stream id的。</span></div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;">公共字段定义:</span></div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;">1、timestamp delta:表示的是上一个chunk 和当前chunk的时戳差。不是消息的时间差。</span>如果大于等于16777215,则表示还有一个字节的扩展时戳。chunk header的第三部分就是extend timestamp。</div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;">2、消息长度:消息的长度。</span>他和chunk的payload的大小是不同的。chunk size length是所有chunk的大小再加最后一个大小。——消息长度是消息实际的大小。。</div><div style="margin: 0px; padding: 0px;">3、message&nbsp;type&nbsp;id:标示消息类型,比如音频,视频,控制等。</div><div style="margin: 0px; padding: 0px;">4、message&nbsp;stream&nbsp;id:以小端序存储。</div><div style="margin: 0px; padding: 0px;">Extended&nbsp;Timestamp:当时戳大于</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;">play2:可以实现比特率的切换。</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;">message length,chunk size,tcp分包和消息的切割</span></div><div style="margin: 0px; padding: 0px;"><hr style="margin: 0px; padding: 0px;"></div><div style="margin: 0px; padding: 0px;">终于弄明白其中的原理了。本来这几个概念和方法在规范中描述不是很清楚,结合抓包终于弄清楚 了。</div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;">message length:及时消息本身的大小。这个消息要在chunk中进行传输。</span></div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;">chunk size:chunk的大小。chunk的大小默认是128。可以通过消息设置chunk size 的最大值。每个chunk的大小最大不能够超过max chunk size。</span></div><div style="margin: 0px; padding: 0px;">明白上面两点后进行分析:</div><div style="margin: 0px; padding: 0px;">1、如果message length小于等于max chunk size,则一个chunk中就包含着一个message。</div><div style="margin: 0px; padding: 0px;">2、如果message length大于max chunk size,则需要将这个message切分为多个chunk。前面几个chunk size必须是max&nbsp;</div><div style="margin: 0px; padding: 0px;">size,最后一个就是剩余的大小。</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;">tcp分包过程:</div><div style="margin: 0px; padding: 0px;">1、收到chunk,明确chunk stream id,对一个chunk stream的数据进行处理。</div><div style="margin: 0px; padding: 0px;">2、判断fmt类型,确定消息长度。</div><div style="margin: 0px; padding: 0px;">3、如果长度小于等于max chunk size,则这个chunk body的大小就是message length。</div><div style="margin: 0px; padding: 0px;">4、处理完这个chunk,跳过chunk header 和chunk body,就是下一个chunk。</div><div style="margin: 0px; padding: 0px;">5、如果长度大于max chunk size,则chunk body的大小就是max chunk size。处理这一部分数据,然后跳过max chunk size找到下一个chunk的头。有消息长度减去max chunk size计算剩余的数据长度,判断剩余长度是否大于max chunk size,如果大于,则表明这个chunk 大小还是max chunk size。以此类推,直到最后一个长度小于等于max chunk size,那么这个长度就是这个chunk的实际长度,并且是这个消息的最后一个消息切片。然后把所有的有效切片拼接起来,就是完整的消息。后面收到的将是另外一个消息。</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;">协议控制消息:</span></div><div style="margin: 0px; padding: 0px;"><hr style="margin: 0px; padding: 0px;"></div><div style="margin: 0px; padding: 0px;">RTMP使用1,2,3,4,5,6的message type id作为控制消息。控制协议的message stream id必须是0, chunk stream id必须是2(2的作用。0,1用来表示字节个数,2是信令,3-6xxxx是实际的。)。控制信令一收到就生效——就是没有协商过程。。。——时戳可以忽略。</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;">set chunk size (1):</span>设置chunk的最大size</div><div style="margin: 0px; padding: 0px;"><hr style="margin: 0px; padding: 0px;"></div><div style="margin: 0px; padding: 0px;">message type id 为1。共四个字节:</div><div style="margin: 0px; padding: 0px;"><img src="cid:62eec3fcc543f5233a903b29a25f3bf8" alt="" data-en-img-selected="true" style="margin: 0px; padding: 0px; border: 0px; max-width: 900px;"></div><div style="margin: 0px; padding: 0px;">第一位必须为0。</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;">默认值是128,服务端和客户端都可以设置chunk size。注意,一个交互中chunk size是相互独立的,也就是,客户端可以设置他发送过去的chunk size,服务端也可以设置他发送过去的chunk size。两个是没有影响的。</div><div style="margin: 0px; padding: 0px;">chunk size最小建议128,必须大于等于1。</div><div style="margin: 0px; padding: 0px;">chunk size的范围是1到0x7fffffff(2147483647),但是因为message header中message length只有三个字节,所以chunk size的最大值是16777215&nbsp; 如果大于此,就去他。</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;">Abort&nbsp;Message (2)</span></div><div style="margin: 0px; padding: 0px;"><hr style="margin: 0px; padding: 0px;"></div><div style="margin: 0px; padding: 0px;">message type id为2,四个字节,携带的内容是chunk stream id。用于通知对端,如果这个chunk stream还有消息正在等待接收未到达的消息内容,则停止,并丢弃原先接受的内容。可以用在带宽有限是优先发送音频和信令,丢弃视频。</div><div style="margin: 0px; padding: 0px;"><img src="cid:352caac880adc03ac8852df9710f7ec9" alt="" data-en-img-selected="true" style="margin: 0px; padding: 0px; border: 0px; max-width: 900px;"></div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;">Acknowledgement&nbsp;(3)</span>&nbsp;Window&nbsp;Acknowledgement&nbsp;Size&nbsp;(5)</div><div style="margin: 0px; padding: 0px;"><hr style="margin: 0px; padding: 0px;"></div><div style="margin: 0px; padding: 0px;">Window&nbsp;Acknowledgement&nbsp;Size用于设置窗口确认大小,Acknowledgement是窗口确认消息。</div><div style="margin: 0px; padding: 0px;">会话开始时,双方都要先对端发送Window&nbsp;Acknowledgement&nbsp;Size,用于指明期望获得确认的大小。当一端收到内容大小超过Window&nbsp;Acknowledgement&nbsp;Size,就要像对方发送Acknowledgement。</div><div style="margin: 0px; padding: 0px;">1、会话开始计算收到byte个数的时间点是收到Window&nbsp;Acknowledgement&nbsp;Size消息开始。</div><div style="margin: 0px; padding: 0px;">2、byte size不包括tcp包头,应该是chunk的大小,即从tcp 的recv函数中获得的内容大小。</div><div style="margin: 0px; padding: 0px;">3、双方都要向对方发送Window&nbsp;Acknowledgement&nbsp;Size和Acknowledgement。</div><div style="margin: 0px; padding: 0px;">4、发送端发送完Window&nbsp;Acknowledgement&nbsp;Size消息后,没有收到Acknowledgement是不再发送进一步的消息的——这样会容易引起错误,导致再也发送不出消息了。</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;"><img src="cid:fe493652feb012a28ccab9b6de980381" alt="" data-en-img-selected="true" style="margin: 0px; padding: 0px; border: 0px; max-width: 900px;"></div><div style="margin: 0px; padding: 0px;"><img src="cid:ffb01fe272bd938ad34bc94118c51fde" alt="" data-en-img-selected="true" style="margin: 0px; padding: 0px; border: 0px; max-width: 900px;"></div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;">Set&nbsp;Peer&nbsp;Bandwidth&nbsp;(6)</span></div><div style="margin: 0px; padding: 0px;"><hr style="margin: 0px; padding: 0px;"></div><div style="margin: 0px; padding: 0px;"><img src="cid:83cad6b8e5490fbefa229ee8b5e5c5c4" alt="" data-en-img-selected="true" style="margin: 0px; padding: 0px; border: 0px; max-width: 900px;"></div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;">设置对端输出带宽。对端是通过设置Window&nbsp;Acknowledgement&nbsp;Size来实现流量控制的。超过Window&nbsp;Acknowledgement&nbsp;Size后未确认发送端将不再发送消息。所以对端收到set peer bandwidth后,如果之前发送的Window&nbsp;Acknowledgement&nbsp;Size和这里写的的Window&nbsp;Acknowledgement&nbsp;Size不一样,一般会发送一个Window&nbsp;Acknowledgement&nbsp;Size。</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;">message format</span></div><div style="margin: 0px; padding: 0px;"><hr style="margin: 0px; padding: 0px;"></div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;">上面的协议控制消息的type id是1,2,3,5,6(这些是chunk stream的控制),没有4。4是另外一个类型:</div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;">User&nbsp;Control&nbsp;Messages&nbsp;(4)</span>:用户控制协议,是RTMP stream的控制协议。<span style="margin: 0px; padding: 0px;">同样,chunk stream id要设置为2,message stream id要设置为0。</span></div><div style="margin: 0px; padding: 0px;"><img src="cid:38fad03cc7aa20def789058167fd5976" alt="" data-en-img-selected="true" style="margin: 0px; padding: 0px; border: 0px; max-width: 900px;"></div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;">消息体格式:两个字节的event type,后面跟可变长度的event data,</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;">RTMP&nbsp;Command&nbsp;Messages</span></div><div style="margin: 0px; padding: 0px;"><hr style="margin: 0px; padding: 0px;"></div><div style="margin: 0px; padding: 0px;">客户端和服务器之间可以发送的消息包括:音频消息,视频消息,数据消息,命令消息。命令消息采用AMF编码。</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;">command message type 20,17</span>表示是命令消息。20表示使用AMF0编码,17表示使用AMF3编码。</div><div style="margin: 0px; padding: 0px;">命令消息包括connect,&nbsp;createStream,&nbsp;publish,&nbsp;play,&nbsp;pause ,响应消息使用onstatus,result。</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;">Data&nbsp;Message&nbsp;(18,&nbsp;15)</span>:数据消息,用于传输Metadata或用户数据。18表示AMF0,15是AMF3。</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;">Shared&nbsp;Object&nbsp;Message&nbsp;</span>(19,&nbsp;16):19表示AMF0,16是AMF3。</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;">Audio&nbsp;Message&nbsp;(8):音频消息</div><div style="margin: 0px; padding: 0px;">Video&nbsp;Message&nbsp;(9):视频消息</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;">Aggregate&nbsp;Message&nbsp;(22):一个消息中包含多个消息。</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;">Types&nbsp;of&nbsp;Commands( 20,17)</span></div><div style="margin: 0px; padding: 0px;"><hr style="margin: 0px; padding: 0px;"></div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;">netConnetion 命令:</span></div><div style="margin: 0px; padding: 0px;">connect :请求连接到服务器的应用实例。</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;">call:请求一个远程调用。</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;">creatStream:创建一个逻辑通道,用于客户端来发送音频,视频,描述数据。netConnection是默认通道,message stream id是0,creatStream使用0作为message stream id。</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;">NetStream&nbsp;Commands:</span></div><div style="margin: 0px; padding: 0px;">多个Netstream可以共用一个netconnection。但是stream的id是在什么时候确定的???——<span style="margin: 0px; padding: 0px;">疑问</span></div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;">play2:切换到不同的比特率的流上,而不用更改播放时间。这是一个有用的功能。这个消息是在paly之后再发送。</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;">deleteStream:删除流。</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;">receiveAudio:是否接受音频。如果为false,服务器不用回复。如果为true,服务器回复两个状态:</div><div style="margin: 0px; padding: 0px;">NetStream.Seek.Notify&nbsp;and&nbsp;&nbsp; NetStream.Play.Start</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;">receiveVideo:同样。</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;">publish:发布一个流到server。需要onStatus响应。</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;">seek:偏移。毫秒为单位。</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;">pause:暂停。</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px; line-height: 1.8; font-family: Courier;">example:</span></div><div style="margin: 0px; padding: 0px;">首先,connect是连接的服务器端的应用(nginx rtmp有配置多个应用。)</div><div style="margin: 0px; padding: 0px;"><img src="cid:386053c5f81aa2dfda0e894d8815a1c5" alt="" data-en-img-selected="true" style="margin: 0px; padding: 0px; border: 0px; max-width: 900px;"></div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;">视频裸数据是如何打包进rtmp包的?</span></div><div style="margin: 0px; padding: 0px;"><hr style="margin: 0px; padding: 0px;"></div><div style="margin: 0px; padding: 0px;">ffmpeg在flv和h264文件将的转换是有问题的。</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;">视频性格的数据包格式和flv的格式非常的像,基本上就是flv格式的变种。参考flv文件格式官方协议详解。</div><div style="margin: 0px; padding: 0px;">视频相关包包括:onMetaData,AVCDecoderConfigurationRecord,h264数据。和flv文件中的区别是flv文件用tag头,rtmp中相关信息是放在rtmp头中。至于内部内容是一样的。</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;"><span style="margin: 0px; padding: 0px;">AMF0编码格式</span></div><div style="margin: 0px; padding: 0px;"><hr style="margin: 0px; padding: 0px;"></div><div style="margin: 0px; padding: 0px;"><ol start="1" style="margin: 0px; padding: 0px 0px 0px 40px;"><li style="margin: 0px; padding: 0px; list-style: decimal;">*AMF数据类型:&nbsp;</li><li style="margin: 0px; padding: 0px; list-style: decimal;">&nbsp;*Type&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Byte&nbsp;code&nbsp;</li><li style="margin: 0px; padding: 0px; list-style: decimal;">&nbsp;*Number&nbsp;&nbsp;&nbsp;&nbsp;0x00&nbsp;</li><li style="margin: 0px; padding: 0px; list-style: decimal;">&nbsp;*Boolean&nbsp;&nbsp;&nbsp;0x01&nbsp;</li><li style="margin: 0px; padding: 0px; list-style: decimal;">&nbsp;*String&nbsp;&nbsp;&nbsp;&nbsp;0x02&nbsp;</li><li style="margin: 0px; padding: 0px; list-style: decimal;">&nbsp;*Object&nbsp;&nbsp;&nbsp;&nbsp;0x03&nbsp;</li><li style="margin: 0px; padding: 0px; list-style: decimal;">&nbsp;*MovieClip&nbsp;0x04&nbsp;</li><li style="margin: 0px; padding: 0px; list-style: decimal;">&nbsp;*Null&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0x05&nbsp;</li><li style="margin: 0px; padding: 0px; list-style: decimal;">&nbsp;*Undefined&nbsp;0x06&nbsp;</li><li style="margin: 0px; padding: 0px; list-style: decimal;">&nbsp;*Reference&nbsp;0x07&nbsp;</li><li style="margin: 0px; padding: 0px; list-style: decimal;">&nbsp;*MixedArray&nbsp;&nbsp;&nbsp;&nbsp;0x08&nbsp;</li><li style="margin: 0px; padding: 0px; list-style: decimal;">&nbsp;*EndOfObject&nbsp;&nbsp;&nbsp;0x09&nbsp;</li><li style="margin: 0px; padding: 0px; list-style: decimal;">&nbsp;*Array&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0x0a&nbsp;</li><li style="margin: 0px; padding: 0px; list-style: decimal;">&nbsp;*Date&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0x0b&nbsp;</li><li style="margin: 0px; padding: 0px; list-style: decimal;">&nbsp;*LongString&nbsp;&nbsp;&nbsp;&nbsp;0x0c&nbsp;</li><li style="margin: 0px; padding: 0px; list-style: decimal;">&nbsp;*Unsupported&nbsp;&nbsp;&nbsp;0x0d&nbsp;</li><li style="margin: 0px; padding: 0px; list-style: decimal;">&nbsp;*Recordset&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0x0e&nbsp;</li><li style="margin: 0px; padding: 0px; list-style: decimal;">&nbsp;*XML&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0x0f&nbsp;</li><li style="margin: 0px; padding: 0px; list-style: decimal;">&nbsp;*TypedObject&nbsp;(Class&nbsp;instance)&nbsp;&nbsp;0x10&nbsp;</li><li style="margin: 0px; padding: 0px; list-style: decimal;">&nbsp;*AMF3&nbsp;data&nbsp;0×11&nbsp;</li></ol></div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;">amf编码:对个数据的合集,数据的边界通过不同的类型,以及不同类型的长度来区分。</div><div style="margin: 0px; padding: 0px;">第一个字节是数据类型,然后根据数据类型确定数据的长度。其中,string是可变长度,两个字节长度值。</div><div style="margin: 0px; padding: 0px;">如果是object,则object内部是个字典,key是字符串,value是数据,这里还要对数据根据类型对数据解析,同上。</div><div style="margin: 0px; padding: 0px;">&nbsp;</div><div style="margin: 0px; padding: 0px;"><span style="color: rgb(51, 51, 51); font-family: verdana, Arial, Helvetica, sans-serif; font-size: 14px;">&nbsp;</span><p style="margin: 10px auto; padding-top: 0px; padding-bottom: 0px; color: rgb(51, 51, 51); font-family: verdana, Arial, Helvetica, sans-serif; font-size: 14px;"><img src="https://img-blog.csdn.net/20160429110147270?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" style="margin: 0px; padding: 0px; border: 0px; max-width: 900px;"></p><p style="margin: 10px auto; padding-top: 0px; padding-bottom: 0px; color: rgb(51, 51, 51); font-family: verdana, Arial, Helvetica, sans-serif; font-size: 14px;">我 的<a target="_blank" class="replace_word" title="微信开发知识库" href="http://lib.csdn.net/base/5" style="margin: 0px; padding: 0px; text-decoration: none; color: rgb(223, 52, 52); border-bottom: 1px dotted rgb(51, 51, 51);">微信</a>公众号</p><div><br></div></div><br></div><div style="font-family:-webkit-standard;font-size:14px;"></div>   
</div>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值