一个简单的RTMP服务器实现 --- RTMP实现要点

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011728480/article/details/86010851

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

#PS:这个只是 《 我自己 》理解,如果和你的

#原则相冲突,请谅解,勿喷

背景

参考前置文章:《一个简单的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
    在这里插入图片描述

  2. MessageTypeId==2,终止消息,如果一个消息正在被等待接收完毕(Chunk分包没有接收完毕),那么本消息用于放弃这个消息的等待。
    在这里插入图片描述

  3. MessageTypeId==3,确认消息。本消息用于发送本客户端接收到了多少数据。
    在这里插入图片描述

  4. MessageTypeId==4,用户控制报文信息。具体用户控制报文看后文
    在这里插入图片描述

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

  1. MessageTypeId==5,发送窗口确认大小信息。用于设置窗口大小,达到这个值后,回复确认信息。
    在这里插入图片描述

  2. MessageTypeId==6,设置对端带宽信息。如果,携带的确认窗口信息大小和之前不一致,要回应一个确认窗口信息大小。反之,不回应。
    在这里插入图片描述

  3. 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 还有一个教训就是:相信自己,要敢于怀疑别人的资料是错的。

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

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

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

猜你喜欢

转载自blog.csdn.net/u011728480/article/details/86010851