前言
最近学习了 RTMP,自己也简单实现了一个 RTMP 服务器,可以和 FFmpeg 交互实现推拉流。接下来计划写几篇笔记梳理一下,欢迎大家斧正~
本系列的内容主要来自 Adobe 的 《RTMP specification 1.0》,我看的这版发表于 2012.12.21,距今已经快十年啦~
我把该文档上传到网盘了,大家自取~
链接: https://pan.baidu.com/s/1nZzYPWB2sl5at-lTiYwxMA
提取码: fqan
协议特点
Real Time Messaging Protocol 有以下几个特点:
- 支持双向传输。
- 一条连接支持传输不同用途的消息,比如 (有啥用后面再讲)
- Protocol Control Message
- RTMP Message
- RTMP Command Message
- 借助 chunk 的概念,在一个链接上可以“同时”传输多个消息。
- 依赖可靠的传输层协议,比如TCP。
握手过程
类似 TCP,RTMP 在发送数据之前也有一个握手过程。整个过程如下图所示:
RTMP 的握手过程有什么用呢?大家也可以带着这个问题读下去,后面我罗列了一些网上的答案,还有自己的一点思考。
报文格式
C0 和 S0
C0 和 S0 的格式一致,仅一个字节,用于表示自身支持的协议版本,目前一般是 3。
C1 和 S1
C1 和 S1 的格式一致,如下图示,共包含四个字段。
time (4 bytes)
时间戳,可以是任意值。FFmpeg 的实现将该值设为 0。
zero (4 bytes)
协议中规定该字段必须是零值。但我在于 FFmpeg 交互时,发现该值被设为 151026690
。
看了下 libavformat/rtmpproto.c
中实现,发现 FFmpeg 将 Flash 的版本号填充至该字段了。工业界潜规则?
/**
* emulated Flash client version - 9.0.124.2 on Linux
* @{
*/
#define RTMP_CLIENT_PLATFORM "LNX"
#define RTMP_CLIENT_VER1 9
#define RTMP_CLIENT_VER2 0
#define RTMP_CLIENT_VER3 124
#define RTMP_CLIENT_VER4 2
/** @} */ //version defines
static int rtmp_handshake(URLContext *s, RTMPContext *rt)
{
AVLFG rnd;
uint8_t tosend [RTMP_HANDSHAKE_PACKET_SIZE+1] = {
3, // unencrypted data
0, 0, 0, 0, // client uptime
RTMP_CLIENT_VER1,
RTMP_CLIENT_VER2,
RTMP_CLIENT_VER3,
RTMP_CLIENT_VER4,
};
...
random bytes (1528 bytes)
random bytes 部分长 1528 字节,其值可以是任意值。
C2 和 S2
C2 和 S2 格式是一致的,如下图所示:
time (4 bytes)
对端发来的包中的时间戳,即
C2.time = S1.time
S2.time = C1.time
time2 (4 bytes)
发给对端的前一个包中的时间戳,即
C2.time2 = C1.time
S2.time2 = S1.time
random echo (1528 bytes)
值如其名。将对端发来的 random bytes 返回去,即
C2.random_echo = S1.random_bytes
S2.random_echo = C1.random_bytes
为啥要握手
在发送完 C2/S2 后,client 和 server 分别进入 Handshake Done 状态,就可以开始传输数据了。那么握手有何作用呢?总结了两点:
- 协商 client 和 server 所使用的的协议版本。
- 通过 C2/S2 中的时间戳,接收端可以粗略的估计时延和带宽,但好像没啥用。文档原文是:
Either peer can use the time and time2 fields together with the current timestamp as a quick estimate of the bandwidth and/or latency of the connection, but this is unlikely to be useful.