PS:要转载请注明出处,本人版权所有。

PS: 这个只是基于《我自己》的理解,

如果和你的原则及想法相冲突,请谅解,勿喷。

前置说明

  本文作为本人csdn blog的主站的备份。(BlogID=077)

  本文发布于 2019-01-08 17:41:05,现用MarkDown+图床做备份更新。blog原图已丢失,使用csdn所存的图进行更新。(BlogID=077)

环境说明

  无

背景


  参考前置文章:《一个简单的RTMP服务器实现 --- RTMP与H264》https://blog.csdn.net/u011728480/article/details/85770696

前置知识


  《一个简单的RTMP服务器实现 --- RTMP与H264》:https://blog.csdn.net/u011728480/article/details/85770696

  《一个简单的RTMP服务器实现 --- RTMP与FLV》:https://blog.csdn.net/u011728480/article/details/85780974

RTMP简介


  RTMP是Real Time Messaging Protocol的简写。RTMP是应用层协议,其是基于TCP实现的。

  网上有许多介绍RTMP基础知识的地方,本文不重复介绍。但是如果有人对以下概念不熟悉的,建议去随意找一篇翻译《rtmp_specification_1.0.pdf》的文章即可。

  阅读本文需要找一篇RTMP的详细知识总结,一起配合阅读。

RTMP基本知识要点


Chunk 、ChunkStream和ChunkStreamID

  Chunk是RTMP的一种应用层分包结构。

  ChunkStream是一种逻辑通道,代表的是RTMP Message分包为Chunk之后的传输的一种数据流。可从服务器到客户端,反之亦然。

  ChunkStreamID是描述ChunkStream的一个ID,消息拆包为Chunk后,可根据此ID的标识来组合Message。其取值范围为3~65599(2^16 -1 + 2 ^6 -1)。0代表2byte形式ChunkbasicHeader,1代表3byte形式ChunkbasicHeader。2代表是控制消息和命令的流。

  下图为chunkbasicheader格式图:

  下图为chunk格式图:

Message、MessageStream和MessageStreamID

  Message是RTMP协议的基本数据结构。绝大部分RTMP协议的数据发送都必须按照此结构来封装。

  MessageStream也是一种逻辑通道,它描述的是一种消息流。根据抓包结果显示,基本的消息通信为一种消息流,音视频消息通信为另外一种流。这种逻辑流在RTMP播放过程中一定要注意。

  MessageStreamID是一种表示MessageStream的ID。

  Message有多种结构,由chunkbasicheader中fmt字段决定,有如下四种格式:

  type0

  type1

  type2

  type3, 没有头结构。

Message ~ Control Message

  控制消息是一些设置属性的消息。他们的MessageTypeId是1-7.

  1. MessageTypeId==1,设置Chunk分包大小,默认为128bytes
  1. MessageTypeId==2,终止消息,如果一个消息正在被等待接收完毕(Chunk分包没有接收完毕),那么本消息用于放弃这个消息的等待。
  1. MessageTypeId==3,确认消息。本消息用于发送本客户端接收到了多少数据。
  1. MessageTypeId==4,用户控制报文信息。具体用户控制报文看后文

  用户控制报文协议中Stream Begin是实现RTMP的播放的重要的一个报文。详情参考RTMP官方文档,及其他参考资料。

  1. MessageTypeId==5,发送窗口确认大小信息。用于设置窗口大小,达到这个值后,回复确认信息。
  1. MessageTypeId==6,设置对端带宽信息。如果,携带的确认窗口信息大小和之前不一致,要回应一个确认窗口信息大小。反之,不回应。
  1. MessageTypeId==7 ,保留。
Message ~ Command Message

MessageTypeId==17或者20. 17对应的格式是AMF3,20对应的格式是AMF0.

connect, createStream, publish, play, pause等命令是非常重要的。特别注意其中的事物ID,这个ID是关键。

Message ~ Data message

MessageTypeId==18或者15. 18对应的格式是AMF0。15对应的格式是AMF3.

Metadata的发送,就要靠此类型的消息。

Message ~ Shared object message

MessageTypeId==19或者16,19对应格式是AMF0,16对应的格式是AMF3.

Message ~ Audio message

MessageTypeId==8 音频数据

Message ~ Video message

MessageTypeId==9 视频数据

Message ~ Aggregate message

MessageTypeId==22 聚集消息数据

NetConnection相关命令

  NetConnection相关的命令是用于处理RTMP连接方面的问题,当使用命令createstream后,就会创建成功一个流了,就会切换到NetStream相关命令下工作。

  本文的服务器用了的命令为connect、createstream等等。

  具体用法可看下文抓包分析,特别是这些命令的的回应,是本文的重点。

NetStream相关命令

  本文的服务器用了命令为play、pause等等。

  具体用法可看下文抓包分析,特别是这些命令的的回应,是本文的重点。

AMF0和AMF3格式

  这种格式是用来序列化相关数据的。具体参考其他文章。

RTMP 通信流程分析(理论和抓包结合)


RTMP 简单握手(此种握手为造成一个坑爹的问题,具体看文末注意事项。)

  c0和s0结构:

  c0和s0实际抓包:

  C1和S1结构:

  zero字段必须为0.

  random区域长度为1528bytes的随机数。

  C1和S1实际抓包:

  从图中可以看出,我服务器回应的随机字节基本都是0。

  C2和S2实际抓包:

  图中可以看出,S2C1。同理C2S1。

  简单握手时序图:

  实际过程中,C0和C1一起发送,服务器一起回应S0,S1,S2。当握手完毕后,就会进入下一阶段。

RTMP 连接及响应

  首先给出连接及响应时序图:

  Command Message 之 connect

  从这里可以看到,connect命令携带了rtmp流地址的属性以及相关的版本。关于这个命令的重点其实是其事物ID的值是1。

  作为connect的回应,这里一般来说有如下几个基本消息需要发送:

  • 确认窗口大小信息
  • 设置带宽信息
  • 设置Chunk分包大小

  最终,我们需要对connect命令进行回应,如果不回应或者回应错误,将不会走到下一步。

  _result命令:

  这里的一个重点是事物ID必须为1,表示对connect的回应。

  其次,携带的object必须包含connect的状态回应属性,如:NetConnection.Connect.Success和Connection succeeded.等等

  当客户端收到connect 的回应后,客户端发送createstream命令,服务端收到createstream命令后,发送createstream响应命令。

  注意画框部分的事物ID,同时也注意回应命令中,数字为1的流ID,这个值代表的是对于本次连接,MessageStreamID必须为此值。对于本连接来说,后续所有需要发送MessageStreamID的地方必须填写此值,才能够完成相关通信。

RTMP 播放及响应命令

  播放时序图:

  当创建流成功后,客户端会发送一个play命令:

  这里需要注意的是事物ID为0,且包含要播放那个视频流的属性。这里的test就是这个地址里面的rtmp://xxx.xxx.xxx.xxx/live/test

  这个时候服务器会设置相应的属性:如设置chunk大小等等。同时服务器会回应用户控制消息streambegin。

  最后服务器会回应play命令:

  这个时候,其实就是可以发送媒体数据了,但是根据抓包结果显示,还需要这个数据:

RTMP传输音视频

  在传输音视频之前,必须先传输onMetaData(参考flv一文)数据。

  然后可以传输音视频数据了,

  但是传输普通的音视频数据之前,必须传输相关的配置数据。对于h264视频来说,就是flv一文中的AVCDecoderConfigurationRecord 的videotag数据。然年即可传输普通的音视频数据。

  注意:在创建成功一个流之后,发送的媒体数据中的MessageStreamID必须为上文createstream 返回的值。

注意:如果严格按照上文实现,就可以用vlc或者plotplayer等等播放对应的RTMP流。但是,Flash网页播放器一定不能够播放(具体表现为所有都工作正常,只是页面没有画面,像没有接收到数据一样)。原因是简单握手导致的。

后记


  总结:

  1. 根据官方文档实现相关功能后,如果没有达到预期效果,别急慢慢排查。至少我是这样的,别把思路搞混了就行了。
  2. 还有一个教训就是:相信自己,要敢于怀疑别人的资料是错的。

参考文献

  • rtmp_specification_1.0.pdf

打赏、订阅、收藏、丢香蕉、硬币,请关注公众号(攻城狮的搬砖之路)

PS: 请尊重原创,不喜勿喷。

PS: 要转载请注明出处,本人版权所有。

PS: 有问题请留言,看到后我会第一时间回复。

一个简单的RTMP服务器实现 --- RTMP实现要点的更多相关文章

  1. 自己动手模拟开发一个简单的Web服务器

    开篇:每当我们将开发好的ASP.NET网站部署到IIS服务器中,在浏览器正常浏览页面时,可曾想过Web服务器是怎么工作的,其原理是什么?“纸上得来终觉浅,绝知此事要躬行”,于是我们自己模拟一个简单的W ...

  2. 一个简单的web服务器

    写在前面 新的一年了,新的开始,打算重新看一遍asp.net本质论这本书,再重新认识一下,查漏补缺,认认真真的过一遍. 一个简单的web服务器 首先需要引入命名空间: System.Net,关于网络编 ...

  3. [置顶] 在Ubuntu下实现一个简单的Web服务器

    要求: 实现一个简单的Web服务器,当服务器启动时要读取配置文件的路径.如果浏览器请求的文件是可执行的则称为CGI程序,服务器并不是将这个文件发给浏览器,而是在服务器端执行这个程序,将它的标准输出发给 ...

  4. Tomcat剖析(二):一个简单的Servlet服务器

    Tomcat剖析(二):一个简单的Servlet服务器 1. Tomcat剖析(一):一个简单的Web服务器 2. Tomcat剖析(二):一个简单的Servlet服务器 3. Tomcat剖析(三) ...

  5. Tomcat剖析(一):一个简单的Web服务器

    Tomcat剖析(一):一个简单的Web服务器 1. Tomcat剖析(一):一个简单的Web服务器 2. Tomcat剖析(二):一个简单的Servlet服务器 3. Tomcat剖析(三):连接器 ...

  6. 自己模拟的一个简单的web服务器

    首先我为大家推荐一本书:How Tomcat Works.这本书讲的很详细的,虽然实际开发中我们并不会自己去写一个tomcat,但是对于了解Tomcat是如何工作的还是很有必要的. Servlet容器 ...

  7. java实现一个简单的Web服务器

    注:本段内容来源于<JAVA 实现 简单的 HTTP服务器> 1. HTTP所有状态码 状态码 状态码英文名称 中文描述 100 Continue 继续.客户端应继续其请求 101 Swi ...

  8. 响应式编程笔记三:一个简单的HTTP服务器

    # 响应式编程笔记三:一个简单的HTTP服务器 本文我们将继续前面的学习,但将更多的注意力放在用例和编写实际能用的代码上面,而非基本的APIs学习. 我们会看到Reactive是一个有用的抽象 - 对 ...

  9. 转:【专题十二】实现一个简单的FTP服务器

    引言: 休息一个国庆节后好久没有更新文章了,主要是刚开始休息完心态还没有调整过来的, 现在差不多进入状态了, 所以继续和大家分享下网络编程的知识,在本专题中将和大家分享如何自己实现一个简单的FTP服务 ...

  10. 使用gitblit搭建一个简单的局域网服务器

    使用gitblit搭建一个简单的局域网服务器 1.使用背景 现在很多使用github管理代码,但是github需要互联网的支持,而且私有的git库需要收费.有一些项目的代码不能外泄,所以,搭建一个局域 ...

随机推荐

  1. 来了!HelloGitHub 年度热门开源项目

    年关将至,「HelloGitHub 月刊」也迎来了年终盘点时刻. 在过去的一年里,「HelloGitHub 月刊」一共分享了 520 个开源项目.我始终秉持着分享 GitHub 上有趣.入门级开源项目 ...

  2. 【AI视频教程】只需5步,AI作出鸡你太美视频

    1.视频效果 黄昏见证虔诚的信徒 2.准备工作 制作视频效果,需要准备下面3个条件: 准备stable diffusion的环境 剪辑一段[鸡你太美]原版视频 stable diffusion安装sd ...

  3. webrtc终极版(二)搭建自己的iceserver服务,并用到RTCMultiConnection的demo中

    webrtc终极版(二)搭建自己的iceserver服务,并用到RTCMultiConnection的demo中 目录 webrtc终极版(二)搭建自己的iceserver服务,并用到RTCMulti ...

  4. 19c RAC 告警日志报错 ORA 7445 [pevm_icd_call_common()+225]

    问题现象: 在一套2节点的19c RAC 环境下,节点2 alert告警 ORA 7445,且频度固定为每分钟报一次:期间有重启实例,但故障依旧: ========================== ...

  5. 全脸 苦思设计了半年的注册中心,与spring cloud 的做法 基本一致

    早知道不去自己思考设计了,害死了不少脑细胞,物理层的东西,所有设计者的思路 都基本一致: 没有必要每个微服务都要做一次安全校验,一个物理集群,一个网关: 网关校验token后,把用户信息 保存到 ht ...

  6. 【简写MyBatis】01-简单映射器

    前言 新开一个坑,为了学习一下MyBatis的源码,写代码是次要的,主要为了吸收一下其中的思想和手法. 目的 关联对象接口和映射类的问题,把 DAO 接口使用代理类,包装映射操作. 知识点 动态代理 ...

  7. Power BI 15 DAY

    业务(表结构)数据分析 1.业务理解 准确 全面 2.数据收集 了解需要用到的数据有哪些 5W2H 结构化数据 SQL.通过查询获取数据库资源 多源表结构数据 企业数据库数据 文本文件数据 Excel ...

  8. Google搜索操作符:让你秒变搜索专家

    搜索引擎对互联网的重要性不言而喻,不过,随着ChatGPT及其类似AI工具的推出,对搜索引擎带来了前所未有的挑战. 因为ChatGPT具有自然语言处理能力,能够更好地理解用户的搜索意图,提供更准确.更 ...

  9. Goland 使用[临时]

    环境变量 因为module模式的引入, 多个项目可以共用同一套External Libraries, 在File->Settings->Go中, 设置GOROOT为go安装目录, 例如 / ...

  10. Java判断是否为闰年

    题目: 判断一个输入的整数是否为闰年? 1.需要对输入的数据类型进行验证 2.支持多次输入和结束符号判断,例如输入q代表退出程序. 分析: 闰年的判断规则如下: (1)若某个年份能被4整除但不能被10 ...