RTMP 协议

介绍

RTMP(Real Time Messaging Protocol)协议是 Adobe 推出的实时消息协议,通过可靠的流式传输协议(例如 TCP)提供双向消息多路复用服务,旨在在一对通信对等点(a pair of communicating peers)之间传输携带相关时间信息的视频、音频和数据消息的并行流。

定义

字节序,对齐方式和时间格式

字节序 : 所有整数字段按网络字节序进行传输,地址低位(整型起始地址)存储整型高位,这种字节序通常被称为大端序。

对齐方式 :除非另有说明,否则 RTMP 中的所有数据都是字节对齐的。

时间格式 : RTMP 中的时间戳以毫秒的整数形式给出,相对于一个未指定的纪元(epoch)。通常,每个流的开始时间戳未 0;不过,如果两个端点(endpoint)在纪元上达成一致,就可以从指定的纪元开始。

由于时间戳的长度是32位的,因此时间戳会每49天17时2分47.296秒滚动一次。因为流可以连续运行,可能连续运行很多年,所以RTMP应用程序在处理时间戳时应使用序列号算法[ RFC1982 ],并且应该能够处理包装。

RTMP Chunk Stream

RTMP Chunk Stream 是一个逻辑通信通道,为更高级的多媒体流协议(message)提供多路复用和打包服务。RTMP connection 可以复用一路或多路 chunk stream,不同的 message stream 可以被复用到同一个 chunk stream 中,然后通过不同的 message stream id 被解复用成不同的 message stream。message stream id 在 chunk header 中占用4个字节。

Chunk

RTMP 基于 TCP 传输,当 RTMP 发送大消息时,大的 message (例如 video key frame)将占用信道导致小的 audio message 或 control message 得不到传输。RTMP 通过 chunking 解决这一问题,保证传输的公平性。Chunking 将高级协议中的大 message 分解为小 chunk 。

Chunk Format

每一个 chunk 包含 chunk header 和 chunk data,chunk header 被划分为三部分:Basic header,message header 和 extended timestamp 。

+--------------+----------------+--------------------+--------------+
| Basic Header | Message Header | Extended Timestamp |  Chunk Data  |
+--------------+----------------+--------------------+--------------+
|                                                    |
|<------------------- Chunk Header ----------------->|
chunk field

Chunk Basic Header

chunk basic header 包含 chunk type(2bits)和 chunk stream ID,长度取决于 chunk stream ID,其可变长度范围为 1, 2, 3 bytes 。

1 bytes

 0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
|fmt|   cs id   |
+-+-+-+-+-+-+-+-+

1 bytes 长度的 chunk basic header 的 cs id 只表示 2-63,保留 0 和 1 分别用于指示 2 bytes 和 3 bytes 的 chunk basic header 。

2 bytes

 0                   1
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|fmt|0 0 0 0 0 0|   cs id - 64  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

其中,cs id 表示 64-319
cs id = 第二个字节表示的值 + 64。

3 bytes

 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|fmt|0 0 0 0 0 1|          cs id - 64           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

其中,cs id 表示 64-65599
cs id = 第三个字节表示的值 * 256 + 第二个字节表示的值 + 64。

fmt
fmt (2 bits) : 标识四种 chunk message header 格式:

  • 00 表示 TYPE 0
  • 01 表示 TYPE 1
  • 10 表示 TYPE 2
  • 11 表示 TYPE 3

chunk message header

TYPE0
TYPE 0 包含 message header 的完整信息,chunk stream 必须以 TYPE 0 开始,否则关于 message header 的信息将不完整。

0                   1                   2                   3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                 timestamp                     |message length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|      message length (cont)    |message type id| msg stream id |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|            message stream id (cont)           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

timestamp : 消息的绝对时间戳。如果时间戳大于等于16777215 (十六进制0xFFFFFF),则该字段为16777215。0xFFFFFF 时表明存在 Extended Timestamp 字段来编码完整的32位时间戳。

TYPE1
当使用 TYPE1 时 表明 message 和当前 chunk stream 的上一个 TYPE0 的 message stream id 相同。

0                   1                   2                   3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                timestamp delta                |message length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|     message length (cont)     |message type id|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

timestamp delta : 消息的增量时间戳。基于相同 chunk stream id 的最新时间戳进行计算。

TYPE2
当使用 TYPE2 时表明 message 和当前 chunk stream 的上一个 message 的 message length,message type id,message stream id 相同。
使用场景:当发送相同 message length,message type id,message stream id 的 messages 时使用。

0                   1                   2
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|               timestamp delta                 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

TYPE3
TYPE 3 的 chunk 没有 message header 。TYPE 3 的 chunks 从相同的 chunk stream id 的前一个块中获取信息。

TYPE 3 有两张使用场景:

  1. 当一个 message 被分割成许多 chunks 时,初了第一个 chunk 之外的其余 chunk 都应该使用这种类型。接收端根据第一个 chunk 的 message length 等待后续的 chunk 并组合成一个完成的 message 。
  2. 由 message stream id,message length,message type,timestamp delta 相同的 messages 组成的 message stream,在第一个 message 使用 TYPE0 指示 message stream id,message length,message type 和 timestamp,第二个 message 使用 TYPE2 指示 timestamp delta 后,剩下的 message 可以使用 TYPE3 。

header fields

header fields

Message

Message Format

RTMP message 拥有两部分:header 和 payload 。

Message Header

0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Message Type  |                Payload length                 |
|    (1 byte)   |                   (3 bytes)                   |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                           Timestamp                           |
|                           (4 bytes)                           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                 Stream ID                     |
|                 (3 bytes)                     |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Message Type: 表示消息类型。
Length: 表示负载长度,网络字节序。
Timestamp: 表示消息的时间戳,网络字节序。
Message Stream Id: 表示消息流ID,定义消息流的唯一性,网络字节序。

Message Payload

Message 中的数据部分,例如:音频数据或视频数据。Payload 不在 RTMP 的解释范围,详见:FLV,FLV Audio Tag,FLV Video Tag 等。

Message Type

Message Type

Protocal Control Message

Protocal Control Message 的 message stream ID 必须等于 0 (0表示控制协议),并且 chunk stream ID 必须是 2 。协议控制消息在收到后需要即刻处理,其时间戳可以忽略。

Set Chunk Size

Set Chunk Size Message 用于通知对端:新的 maximum chunk size, 默认的 maximum chunk size 为 128 字节,maximum chunk size 至少为 128 字节。每个方向的 maximum chunk size 是独立维护的(即 A发往B 和 B发往A 的maximum chunk size 是独立维护的)。

格式:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0|                     chunk size (31 bits)                    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

其中:

  • 0 : 第 0 位必须为 0 。
  • chunk size : 表示新的 maximum chunk size ,单位为字节。chunk size 的有效大小为 1 到 2147483647 (0x7FFFFFFF),但是设置大于 16777215 (0xFFFFFF) 的 maximum chunk size 均等价于 16777215 ,因为 message size 是小于等于 16777215 (0xFFFFFF) 的。

Abort Message

Abort Message 用于通知对端:如果正在等待 chunks 完成一个 message,则当收到 Abort Message 时请结束等待 Abort Message 指定的 chunk stream id 的chunks,并且丢弃 chunk stream id 上收到的部分 message 的 chunks。

格式:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                       chunk stream id (32 bits)               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

其中:

  • chunk stream id : 通知对端中断 chunk stream id 上的当前 message 。

Acknowledgement

Acknowledgement message 指定 sequence number 表示目前为止接收到的字节数。
当 client 或 server 在接收到的字节数等于 window size 后,必须向对端发送一个 acknowledgement 。window size 是发送端在没有接收到接收端 acknowledgement 的情况下发送的最大字节数,因此接收端必须给发送端回 acknowledgement,否则发送端将不继续发送数据。
格式:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                      sequence number (4 bytes)                |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

其中:

  • sequence number : 接收端迄今为止收到的字节数 。

Window Acknowledgement Size

Window Acknowledgement Size Message 用于通知对端现在的 Window Acknowledgement Size,对端在每接收到 window size 个字节时需要发送一个 Acknowledgement message(从一个新的 session 或上一个 Acknowledgement message 开始统计)。

格式:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|            Acknowledgement Window size (4 bytes)              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Set Peer Bandwidth

Set Peer Bandwidth Message 用于限制对端的带宽输出。接收到此消息的端通过将已发送但未 ack (未收到 Acknowledgement message)的数据大小限制在 Set Peer Bandwidth Message 指示的 Acknowledgement Window size 之内来限制其输出带宽。

接收到 Set Peer Bandwidth Message 的端需要重新发送一个 Window Acknowledgement Size Message 指示相同的 Acknowledgement Window size,否则有可能无法触发 Acknowledgement 。

格式:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|            Acknowledgement Window size (4 bytes)              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  Limit Type   |
+-+-+-+-+-+-+-+-+

其中:

  • Limit Type:
    • 0 : HARD,对端应该严格将其输出带宽限制为指定的窗口大小。
    • 1 : SOFT, 对端应该将其输出带宽限制为指定的窗口大小或已生效的限制中的最小值。
    • 2 : DYNIMIC,如果前一个 Limit Type 是 HARD 则为 HARD,否则无视当前消息。

User Control Message

User Control Message 的 message stream ID 应该等于 0 (0表示控制协议)(同 Protocal Control Message ),并且 chunk stream ID 是 2 (同 Protocal Control Message )。User Control Message 在收到后需要即刻处理,其时间戳可以忽略。

格式:

+------------------------------+-------------------------
|      Event Type (16 bits)    | Event Data
+------------------------------+-------------------------
event

Audio Message

Audio Message 用于发送音频数据,Audio Message Payload 详见 Audio 文件格式,例如:FLV Audio Tag 。

Video Message

Video Message 用于发送视频数据,Video Message Payload 详见 Video 文件格式,例如:FLV Video Tag 。

Data Message

Data Message 通常被用于发送 MetaData 或任意 user data 到对端。Metadata 通常包括音视频数据的 creation time,duration,theme 等。

Data Message Payload 是 AMF 二进制格式数据。

例如:FLV 中的 Script Tag Data,使用两个 AMF 包描述 MetaData:


FLV Script Tag Data
  1. 第一个 AMF 包是 onMetaData 包。第 1 个字节表示的是 AMF 包的类型,第一个 AMF 包一般是字符串类型,字符串类型的值表示是 0x02,之后是 2 字节表示的长度,一般长度总是 10,值是 0x000A,之后就是 10 字节长度字符串,值是 onMetaData 。
  2. 第二个 AMF 包是一个 Objcet 类型的 AMF 包,Object 类型的值表示是 0x03,Objcet 元素为元素名称和值组成的对,常见的元素如下:


    metadata

Shared Object Message

Shared Object 是一个 Flash Object (一个 name 和 value 对的集合),用于跨多个 客户端、实例等进行同步。

+------+------+-------+-----+-----+------+-----+ +-----+------+-----+
|Header|Shared|Current|Flags|Event|Event |Event|.|Event|Event |Event|
|      |Object|Version|     |Type |data  |data |.|Type |data  |data |
|      |Name  |       |     |     |length|     |.|     |length|     |
+------+------+-------+-----+-----+------+-----+ +-----+------+-----+
       |                                                            |
       |<- - - - - - - - - - - - - - - - - - - - - - - - - - - - - >|
       |              AMF Shared Object Message body                |

Shared Objcet Event :

Shared Objcet Event

Aggregate Message

Aggregate Message 是 Message 的一个聚合体,包含一系列的 message 。
Aggregate Message format :

+---------+-------------------------+
| Header  | Aggregate Message body  |
+---------+-------------------------+

Aggregate Message body format :

+--------+-------+---------+--------+-------+---------+ - - - -
|Header 0|Message|Back     |Header 1|Message|Back     |
|        |Data 0 |Pointer 0|        |Data 1 |Pointer 1|
+--------+-------+---------+--------+-------+---------+ - - - -

Back Pointer 包含前一个 message (包含 Header 和 Message Data)的大小,Back Pointer 用于匹配 FLV 文件格式,用于向后查找。

Aggregate Message 的好处:

  1. chunk stream 在一个 chunk 内最多只能发送一个单个完整的 message。因此,可以合并多个小的 message 到一个 Aggregate Message 中,使用一个 chunk 发送,便可以减少 chunk 的发送次数。
  2. 子 message 可以连续存储在内存中,在系统调用时,在网络上发送数据效率更高。

Command Messages

Command Message 采用 AMF 编码。

  • 发送端发送的 command message 包含 command name,transaction ID 以及包含相关参数的 command object (name 和 value 组合的 pair)。
  • 接收端处理 command 并且返回 response 携带相同的 transaction ID,response 的字符串可以是 _result, _error, or method name (例如:verifyClient or contactExternalServer)。
    • _result 或 _error 命令字符串表示响应。transaction ID 指示响应所指的 command。
    • method name 表示尝试在对端运行 method。

下面的对象是用来发送不一样的 command :

  • NetConnection: 服务器和客户端之间连接的高级表示形式。
  • NetStream: 表示发送音频流、视频流和其他相关数据的通道的对象。当然也发送诸如播放、暂停等控制数据流的命令。

NetConnection Commands

NetConnection 管理客户端应用程序和服务器之间的双向连接。此外,它还提供对异步远程方法调用的支持。
可以在 NetConnection 上发送以下命令:

  • connect
  • call
  • close
  • createStream

connect
client 向 server 发送 connect 命令请求建连:

+----------------+---------+---------------------------------------+
|  Field Name    |  Type   |           Description                 |
+--------------- +---------+---------------------------------------+
| Command Name   | String  | Name of the command. Set to "connect".|
+----------------+---------+---------------------------------------+
| Transaction ID | Number  | Always set to 1.                      |
+----------------+---------+---------------------------------------+
| Command Object | Object  | Command information object which has  |
|                |         | the name-value pairs.                 |
+----------------+---------+---------------------------------------+
| Optional User  | Object  | Any optional information              |
| Arguments      |         |                                       |
+----------------+---------+---------------------------------------+

Command Object 包含以下 key/value pairs :

+-----------+--------+-----------------------------+----------------+
| Property  |  Type  |        Description          | Example Value  |
+-----------+--------+-----------------------------+----------------+
|   app     | String | The Server application name |    testapp     |
|           |        | the client is connected to. |                |
+-----------+--------+-----------------------------+----------------+
| flashver  | String | Flash Player version. It is |    FMSc/1.0    |
|           |        | the same string as returned |                |
|           |        | by the ApplicationScript    |                |
|           |        | getversion () function.     |                |
+-----------+--------+-----------------------------+----------------+
|  swfUrl   | String | URL of the source SWF file  | file://C:/     |
|           |        | making the connection.      | FlvPlayer.swf  |
+-----------+--------+-----------------------------+----------------+
|  tcUrl    | String | URL of the Server.          | rtmp://local   |
|           |        | It has the following format.| host:1935/test |
|           |        | protocol://servername:port/ | app/instance1  |
|           |        | appName/appInstance         |                |
+-----------+--------+-----------------------------+----------------+
|  fpad     | Boolean| True if proxy is being used.| true or false  |
+-----------+--------+-----------------------------+----------------+
|audioCodecs| Number | Indicates what audio codecs | SUPPORT_SND    |
|           |        | the client supports.        | _MP3           |
+-----------+--------+-----------------------------+----------------+
|videoCodecs| Number | Indicates what video codecs | SUPPORT_VID    |
|           |        | are supported.              | _SORENSON      |
+-----------+--------+-----------------------------+----------------+
|videoFunct-| Number | Indicates what special video| SUPPORT_VID    |
|ion        |        | functions are supported.    | _CLIENT_SEEK   |
+-----------+--------+-----------------------------+----------------+
|  pageUrl  | String | URL of the web page from    | http://        |
|           |        | where the SWF file was      | somehost/      |
|           |        | loaded.                     | sample.html    |
+-----------+--------+-----------------------------+----------------+
| object    | Number | AMF encoding method.        |     AMF3       |
| Encoding  |        |                             |                |
+-----------+--------+-----------------------------+----------------+
  • app 是 client 请求 connect 的 server 的 application name
  • tcUrl 是 server 的 url,格式为 protocol://servername:port/appName/appInstance 例如:rtmp://localhost:1935/testapp/instance1

注意:play 或 publish 时只需指定 stream_name ,完整的 stream url 将基于 tcUrl 进行拼接。

audioCodecs 的值和含义:

+----------------------+----------------------------+--------------+
|      Codec Flag      |          Usage             |     Value    |
+----------------------+----------------------------+--------------+
|  SUPPORT_SND_NONE    | Raw sound, no compression  |    0x0001    |
+----------------------+----------------------------+--------------+
|  SUPPORT_SND_ADPCM   | ADPCM compression          |    0x0002    |
+----------------------+----------------------------+--------------+
|  SUPPORT_SND_MP3     | mp3 compression            |    0x0004    |
+----------------------+----------------------------+--------------+
|  SUPPORT_SND_INTEL   | Not used                   |    0x0008    |
+----------------------+----------------------------+--------------+
|  SUPPORT_SND_UNUSED  | Not used                   |    0x0010    |
+----------------------+----------------------------+--------------+
|  SUPPORT_SND_NELLY8  | NellyMoser at 8-kHz        |    0x0020    |
|                      | compression                |              |
+----------------------+----------------------------+--------------+
|  SUPPORT_SND_NELLY   | NellyMoser compression     |    0x0040    |
|                      | (5, 11, 22, and 44 kHz)    |              |
+----------------------+----------------------------+--------------+
|  SUPPORT_SND_G711A   | G711A sound compression    |    0x0080    |
|                      | (Flash Media Server only)  |              |
+----------------------+----------------------------+--------------+
|  SUPPORT_SND_G711U   | G711U sound compression    |    0x0100    |
|                      | (Flash Media Server only)  |              |
+----------------------+----------------------------+--------------+
|  SUPPORT_SND_NELLY16 | NellyMouser at 16-kHz      |    0x0200    |
|                      | compression                |              |
+----------------------+----------------------------+--------------+
|  SUPPORT_SND_AAC     | Advanced audio coding      |    0x0400    |
|                      | (AAC) codec                |              |
+----------------------+----------------------------+--------------+
|  SUPPORT_SND_SPEEX   | Speex Audio                |    0x0800    |
+----------------------+----------------------------+--------------+
| SUPPORT_SND_ALL      | All RTMP-supported audio   |    0x0FFF    |
|                      | codecs                     |              |
+----------------------+----------------------------+--------------+

videoCodecs 的值和含义:

+----------------------+----------------------------+--------------+
|      Codec Flag      |            Usage           |    Value     |
+----------------------+----------------------------+--------------+
|  SUPPORT_VID_UNUSED  | Obsolete value             |    0x0001    |
+----------------------+----------------------------+--------------+
|  SUPPORT_VID_JPEG    | Obsolete value             |    0x0002    |
+----------------------+----------------------------+--------------+
| SUPPORT_VID_SORENSON | Sorenson Flash video       |    0x0004    |
+----------------------+----------------------------+--------------+
| SUPPORT_VID_HOMEBREW | V1 screen sharing          |    0x0008    |
+----------------------+----------------------------+--------------+
| SUPPORT_VID_VP6 (On2)| On2 video (Flash 8+)       |    0x0010    |
+----------------------+----------------------------+--------------+
| SUPPORT_VID_VP6ALPHA | On2 video with alpha       |    0x0020    |
| (On2 with alpha      | channel                    |              |
| channel)             |                            |              |
+----------------------+----------------------------+--------------+
| SUPPORT_VID_HOMEBREWV| Screen sharing version 2   |    0x0040    |
| (screensharing v2)   | (Flash 8+)                 |              |
+----------------------+----------------------------+--------------+
| SUPPORT_VID_H264     | H264 video                 |    0x0080    |
+----------------------+----------------------------+--------------+
| SUPPORT_VID_ALL      | All RTMP-supported video   |    0x00FF    |
|                      | codecs                     |              |
+----------------------+----------------------------+--------------+

server 向 client 应答的 response :

+--------------+----------+----------------------------------------+
| Field Name   |   Type   |             Description                |
+--------------+----------+----------------------------------------+
| Command Name |  String  | _result or _error; indicates whether   |
|              |          | the response is result or error.       |
+--------------+----------+----------------------------------------+
| Transaction  |  Number  | Transaction ID is 1 for connect        |
| ID           |          | responses                              |
|              |          |                                        |
+--------------+----------+----------------------------------------+
| Properties   |  Object  | Name-value pairs that describe the     |
|              |          | properties(fmsver etc.) of the         |
|              |          | connection.                            |
+--------------+----------+----------------------------------------+
| Information  |  Object  | Name-value pairs that describe the     |
|              |          | response from|the server. ’code’,      |
|              |          | ’level’, ’description’ are names of few|
|              |          | among such information.                |
+--------------+----------+----------------------------------------+

完整消息流(message flow)顺序 :

+--------------+                              +-------------+
|    Client    |             |                |    Server   |
+------+-------+             |                +------+------+
       |              Handshaking done               |         
       |                     |                       |         
       |                     |                       |         
       |                     |                       |         
       |                     |                       |
       |----------- Command Message(connect) ------->|
       |                                             |              
       |<------- Window Acknowledgement Size --------|
       |                                             |              
       |<----------- Set Peer Bandwidth -------------|
       |                                             |              
       |-------- Window Acknowledgement Size ------->|
       |                                             |              
       |<------ User Control Message(StreamBegin) ---|
       |                                             |              
       |<------------ Command Message ---------------|              
       |       (_result- connect response)           |
       |                                             |
  1. 完成 RTMP Handshake
  2. client 向 server 发送 connect 的 Command Message 请求与服务器的 application instance 建立连接。
  3. server 向 client 发送 Window Acknowledgement Size 进行 protocol control。
  4. server 向 client 发送 Set Peer Bandwidth 进行 protocol control。
  5. client 向 server 发送 Window Acknowledgement Size 进行 protocol control。
  6. server 向 client 发送 User Control Message(StreamBegin) 表明 stream 已经可用,可以开始通信。
  7. server 向 client 发送 connect 的 Command Message 的响应结果。

call
call command message 用于实现远程过程调用 。

sender to receiver :

+--------------+----------+----------------------------------------+
|Field Name    |   Type   |             Description                |
+--------------+----------+----------------------------------------+
| Procedure    |  String  | Name of the remote procedure that is   |
| Name         |          | called.                                |
+--------------+----------+----------------------------------------+
| Transaction  |  Number  | If a response is expected we give a    |
|              |          | transaction Id. Else we pass a value of|
| ID           |          | 0                                      |
+--------------+----------+----------------------------------------+
| Command      |  Object  | If there exists any command info this  |
| Object       |          | is set, else this is set to null type. |
+--------------+----------+----------------------------------------+
| Optional     |  Object  | Any optional arguments to be provided  |
| Arguments    |          |                                        |
+--------------+----------+----------------------------------------+

response :

+--------------+----------+----------------------------------------+
 | Field Name   |   Type   |             Description                |
 +--------------+----------+----------------------------------------+
 | Command Name |  String  | Name of the command.                   |
 |              |          |                                        |
 +--------------+----------+----------------------------------------+
 | Transaction  |  Number  | ID of the command, to which the        |
 | ID           |          | response belongs.                      |
 +--------------+----------+----------------------------------------+
 | Command      |  Object  | If there exists any command info this  |
 | Object       |          | is set, else this is set to null type. |
 +--------------+----------+----------------------------------------+
 | Response     | Object   | Response from the method that was      |
 |              |          | called.                                |
 +------------------------------------------------------------------+

createStream
createStream 用于 client 向 server 请求创建用于消息通信的逻辑通道 message stream 。音频、视频和元数据的发布通过使用 createStream 命令来创建的 stream channel 进行。

NetConnection 是默认的通信通道,其流 ID 为 0 。Protocol Control Message、User Control Message 以及 NetConnection Command Message 使用默认的通信通道 NetConnection 。

NetStream Commands 将使用 createStream 创建的 stream channel 进行传输。

client to server :

+--------------+----------+----------------------------------------+
| Field Name   |   Type   |             Description                |
+--------------+----------+----------------------------------------+
| Command Name |  String  | Name of the command. Set to            |
|              |          | "createStream".                        |
+--------------+----------+----------------------------------------+
| Transaction  |  Number  | Transaction ID of the command.         |
| ID           |          |                                        |
+--------------+----------+----------------------------------------+
| Command      |  Object  | If there exists any command info this  |
| Object       |          | is set, else this is set to null type. |
+--------------+----------+----------------------------------------+

command object :

+--------------+----------+----------------------------------------+
| Field Name   |   Type   |             Description                |
+--------------+----------+----------------------------------------+
| Command Name |  String  | _result or _error; indicates whether   |
|              |          | the response is result or error.       |
+--------------+----------+----------------------------------------+
| Transaction  |  Number  | ID of the command that response belongs|
| ID           |          | to.                                    |
+--------------+----------+----------------------------------------+
| Command      |  Object  | If there exists any command info this  |
| Object       |          | is set, else this is set to null type. |
+--------------+----------+----------------------------------------+
| Stream       |  Number  | The return value is either a stream ID |
| ID           |          | or an error information object.        |
+--------------+----------+----------------------------------------+

NetStream Commands

在发送 NetStream Commands 前需要通过 createStream 的 NetConnection Command 创建 NetStream 的逻辑通信通道。

client 可以通过 NetStream 向 server 发送以下命令:

  • play
  • play2
  • deleteStream
  • closeStream
  • receiveAudio
  • receiveVideo
  • publish
  • seek
  • pause

server 通过 onStatus 命令通知客户端 NetStream 状态更新,onStatus 被用于各个 NetStream Commands 的响应中。

onStatus command 的 message 格式:

+--------------+----------+----------------------------------------+
| Field Name   |   Type   |             Description                |
+--------------+----------+----------------------------------------+
| Command Name |  String  | The command name "onStatus".           |
+--------------+----------+----------------------------------------+
| Transaction  |  Number  | Transaction ID set to 0.               |
| ID           |          |                                        |
+--------------+----------+----------------------------------------+
| Command      |  Null    | There is no command object for         |
| Object       |          | onStatus messages.                     |
+--------------+----------+----------------------------------------+
| Info Object  | Object   | An AMF object having at least the      |
|              |          | following three properties: "level"    |
|              |          | (String): the level for this message,  |
|              |          | one of "warning", "status", or "error";|
|              |          | "code" (String): the message code, for |
|              |          | example "NetStream.Play.Start"; and    |
|              |          | "description" (String): a human-       |
|              |          | readable description of the message.   |
|              |          | The Info object MAY contain other      |
|              |          | properties as appropriate to the code. |
+--------------+----------+----------------------------------------+

play
client 通过 play command 向 server 请求播放流。client 可以通过多次使用 play command 创建播放列表。

如果 client 想要创建一个动态播放列表,并且希望在不同的 live 或 recorded 流之间切换,可多次调用 play command,并将字段 reset 的值设置为 false 。

如果 client 要立即播放指定的流,清楚排队等待播放的任何其他流,则将字段 reset 的值设置为 true 进行重置。

play command 格式:

+--------------+----------+-----------------------------------------+
| Field Name   |   Type   |             Description                 |
+--------------+----------+-----------------------------------------+
| Command Name |  String  | Name of the command. Set to "play".     |
+--------------+----------+-----------------------------------------+
| Transaction  |  Number  | Transaction ID set to 0.                |
| ID           |          |                                         |
+--------------+----------+-----------------------------------------+
| Command      |   Null   | Command information does not exist.     |
| Object       |          | Set to null type.                       |
+--------------+----------+-----------------------------------------+
| Stream Name  |  String  |                                         |
+--------------+----------+-----------------------------------------+
| Start        |  Number  |                                         |
+--------------+----------+-----------------------------------------+
| Duration     |  Number  |                                         |
+--------------+----------+-----------------------------------------+
| Reset        | Boolean  |                                         |
+--------------+----------+-----------------------------------------+

其中:

  • Stream Name :播放的流的名称(将和 connect 中的 tcUrl 拼接成 stream url)
    • 播放视频文件(FLV),需指定不带文件扩展名的流名称(如 sample
    • 播放 MP3 或 ID3 标签,必须在流名称前加上 mp3: (如 mp3:sample
    • 播放 H.264/AAC 文件,必须在流名称前加上 mp4: 并加上文件扩展名 (如 mp4:sample.m4v1
  • Start :一个可选参数,以秒为单位指定开始时间。
    • -2 (默认值),表示订阅者首先尝试播放 Stream Name 字段中指定的直播流。如果没有找到同名的直播流,则播放同名的录制流。如果没有具有该名称的录制流,则订阅者等待具有该名称的新直播流并在可用时播放。
    • -1,仅播放 Stream Name 字段中指定的直播流。
    • 0 或 正数,播放 Stream Name 字段中指定的录制流,从 Start 字段中指定的时间开始播放。如果没有找到录制的流,则播放播放列表中的下一个项目。
  • Duration
    • 负数,解释为 -1 。
    • -1,表示直播流一直播放到不再可用,或者播放录制的流直到结束。
    • 0,假设 start 字段中指定的值等于或大于0,从 Start 字段中指定的时间开始播放单帧(一帧)
    • 正数,播放 Duration 时长的直播流或录制流(如果流在 Duration 字段中指定的时间之前结束,则播发结束时结束)。
  • Reset :一个可选的布尔值或数字,指定是否刷新任何以前的播放列表。

play command 完整的交互流程:

     +-------------+                            +------------+
     | Play Client |             |              |   Server   |
     +------+------+             |              +------+-----+
            |        Handshaking and Application       |
            |             connect done                 |
            |                    |                     |
            |                    |                     |
            |                    |                     |
            |                    |                     |
   ---+---- |----- Command Message(createStream) ----->|
Create|     |                                          |
Stream|     |                                          |
   ---+---- |<---------- Command Message --------------|
            |     (_result- createStream response)     |
            |                                          |
   ---+---- |------ Command Message (play) ----------->|
      |     |                                          |
      |     |<------------- SetChunkSize --------------|
      |     |                                          |
      |     |<---- User Control (StreamIsRecorded) ----|
 Play |     |                                          |
      |     |<---- UserControl (StreamBegin) ----------|
      |     |                                          |
      |     |<- Command Message(onStatus-play reset) --|
      |     |                                          |
      |     |<- Command Message(onStatus-play start) --|
      |     |                                          |
      |     |<------------ Audio Message --------------|
      |     |                                          |
      |     |<------------ Video Message --------------|
      |     |                    |                     |

play 部分流程为:

  1. client 在收到 createStream 的 response 成功后,向 server 发送 play
  2. server 收到 play 后,发送 Set Chunk Size 来设置块大小。
  3. server 发送 User control message 指示 StreamIsRecorded
  4. server 发送 User control message 指示 StreamBegin
  5. 如果 play 在 server 中执行成功,则 server 发送 onStatus Message 表示 NetStream.Play.reset 和 NetStream.Play.start ,其中 NetStream.Play.reset 只有在 client 发送的 play command 设置 reset = true 时才会发生和响应。如果没有找到要播放的流,则 server 发生 onStatus Message 表示 NetStream.Play.StreamNotFound。
  6. 此后,server 向 client 发送 audio message 和 video message 。

play2
play2 与 play 命令不同:play2 可以切换到不同的比特率,而无需更改播放内容的时间线。

play2 command 格式:

+--------------+----------+----------------------------------------+
| Field Name   |   Type   |             Description                |
+--------------+----------+----------------------------------------+
| Command Name |  String  | Name of the command, set to "play2".   |
+--------------+----------+----------------------------------------+
| Transaction  |  Number  | Transaction ID set to 0.               |
| ID           |          |                                        |
+--------------+----------+----------------------------------------+
| Command      |   Null   | Command information does not exist.    |
| Object       |          | Set to null type.                      |
+--------------+----------+----------------------------------------+
| Parameters   |  Object  | An AMF encoded object whose properties |
|              |          | are the public properties described    |
|              |          | for the flash.net.NetStreamPlayOptions |
|              |          | ActionScript object.                   |
+--------------+----------+----------------------------------------+

play2 交互流程:

  +--------------+                          +-------------+
    | Play2 Client |              |           |    Server   |
    +--------+-----+              |           +------+------+
             |      Handshaking and Application      |
             |               connect done            |
             |                    |                  |
             |                    |                  |
             |                    |                  |
             |                    |                  |
    ---+---- |---- Command Message(createStream) --->|
Create |     |                                       |
Stream |     |                                       |
    ---+---- |<---- Command Message (_result) -------|
             |                                       |
    ---+---- |------ Command Message (play) -------->|
       |     |                                       |
       |     |<------------ SetChunkSize ------------|
       |     |                                       |
       |     |<--- UserControl (StreamIsRecorded)----|
  Play |     |                                       |
       |     |<------- UserControl (StreamBegin)-----|
       |     |                                       |
       |     |<- Command Message(onStatus-playstart)-|
       |     |                                       |
       |     |<---------- Audio Message -------------|
       |     |                                       |
       |     |<---------- Video Message -------------|
       |     |                                       |
             |                                       |
    ---+---- |-------- Command Message(play2) ------>|
       |     |                                       |
       |     |<------- Audio Message (new rate) -----|
 Play2 |     |                                       |
       |     |<------- Video Message (new rate) -----|
       |     |                    |                  |
       |     |                    |                  |
       |  Keep receiving audio and video stream till finishes
                                  |

deleteStream
NetStream 在 NetStream 对象被销毁时发送 deleteStream Command

+--------------+----------+----------------------------------------+
| Field Name   |   Type   |             Description                |
+--------------+----------+----------------------------------------+
| Command Name |  String  | Name of the command, set to            |
|              |          | "deleteStream".                        |
+--------------+----------+----------------------------------------+
| Transaction  |  Number  | Transaction ID set to 0.               |
| ID           |          |                                        |
+--------------+----------+----------------------------------------+
| Command      |  Null    | Command information object does not    |
| Object       |          | exist. Set to null type.               |
+--------------+----------+----------------------------------------+
| Stream ID    |  Number  | The ID of the stream that is destroyed |
|              |          | on the server.                         |
+--------------+----------+----------------------------------------+

receiveAudio
NetStream 发送 receiveAudio Command,通知 server 是否发送音频数据到 client 。
client 向 server 发送的 receiveAudio Command 结构:

+--------------+----------+----------------------------------------+
| Field Name   |   Type   |             Description                |
+--------------+----------+----------------------------------------+
| Command Name |  String  | Name of the command, set to            |
|              |          | "receiveAudio".                        |
+--------------+----------+----------------------------------------+
| Transaction  |  Number  | Transaction ID set to 0.               |
| ID           |          |                                        |
+--------------+----------+----------------------------------------+
| Command      |  Null    | Command information object does not    |
| Object       |          | exist. Set to null type.               |
+--------------+----------+----------------------------------------+
| Bool Flag    |  Boolean | true or false to indicate whether to   |
|              |          | receive audio or not.                  |
+--------------+----------+----------------------------------------+

如果 receiveAudio 的 Bool Flag 设置为 false,则 server 不会发送任何 response,如果 Bool Flag 设置为 true,则 server 将使用 status message 的 NetStream.Seek.Notify 和 NetStream.Play.Start 进行响应。

receiveVideo
NetStream 发送 receiveVideo Command,通知 server 是否发送视频数据到 client 。
client 向 server 发送的 receiveVideo Command 结构:

+--------------+----------+----------------------------------------+
| Field Name   |   Type   |             Description                |
+--------------+----------+----------------------------------------+
| Command Name |  String  | Name of the command, set to            |
|              |          | "receiveVideo".                        |
+--------------+----------+----------------------------------------+
| Transaction  |  Number  | Transaction ID set to 0.               |
| ID           |          |                                        |
+--------------+----------+----------------------------------------+
| Command      |  Null    | Command information object does not    |
| Object       |          | exist. Set to null type.               |
+--------------+----------+----------------------------------------+
| Bool Flag    |  Boolean | true or false to indicate whether to   |
|              |          | receive video or not.                  |
+--------------+----------+----------------------------------------+

如果 receiveVideo 的 Bool Flag 设置为 false,则 server 不会发送任何 response,如果 Bool Flag 设置为 true,则 server 将使用 status message 的 NetStream.Seek.Notify 和 NetStream.Play.Start 进行响应。

publish
client 发送 publish 命令用于将一个命名的 stream (Publishing Name)发布到 server。客户端可以 play 该 stream 并receive已发布的音频、视频和数据消息。

client 向 server 发送的 publish Command 结构:

+--------------+----------+----------------------------------------+
| Field Name   |   Type   |             Description                |
+--------------+----------+----------------------------------------+
| Command Name |  String  | Name of the command, set to "publish". |
+--------------+----------+----------------------------------------+
| Transaction  |  Number  | Transaction ID set to 0.               |
| ID           |          |                                        |
+--------------+----------+----------------------------------------+
| Command      |  Null    | Command information object does not    |
| Object       |          | exist. Set to null type.               |
+--------------+----------+----------------------------------------+
| Publishing   |  String  | Name with which the stream is          |
| Name         |          | published.                             |
+--------------+----------+----------------------------------------+
| Publishing   |  String  | Type of publishing. Set to "live",     |
| Type         |          | "record", or "append".                 |
|              |          | record: The stream is published and the|
|              |          | data is recorded to a new file.The file|
|              |          | is stored on the server in a           |
|              |          | subdirectory within the directory that |
|              |          | contains the server application. If the|
|              |          | file already exists, it is overwritten.|
|              |          | append: The stream is published and the|
|              |          | data is appended to a file. If no file |
|              |          | is found, it is created.               |
|              |          | live: Live data is published without   |
|              |          | recording it in a file.                |
+--------------+----------+----------------------------------------+

server 响应 onStatus 命令用于标记 publish 的开始。

seek
client 发送 seek command 来寻找媒体文件或播放列表中的偏移量(以毫秒为单位)。
client 向 server 发送的 seek Command 结构:

+--------------+----------+----------------------------------------+
| Field Name   |   Type   |             Description                |
+--------------+----------+----------------------------------------+
| Command Name |  String  | Name of the command, set to "seek".    |
+--------------+----------+----------------------------------------+
| Transaction  |  Number  | Transaction ID set to 0.               |
| ID           |          |                                        |
+--------------+----------+----------------------------------------+
| Command      |  Null    | There is no command information object |
| Object       |          | for this command. Set to null type.    |
+--------------+----------+----------------------------------------+
| milliSeconds |  Number  | Number of milliseconds to seek into    |
|              |          | the playlist.                          |
+--------------+----------+----------------------------------------+

server 使用 onStatus 命令 NetStream.Seek.Notify 表示成功,_error message 表示失败。

pause
client 发送 pause command 来告知 server 停止(Pause Flag=true)或(Pause Flag = false)播放。
client 向 server 发送的 pause Command 结构:

+--------------+----------+----------------------------------------+
| Field Name   |   Type   |             Description                |
+--------------+----------+----------------------------------------+
| Command Name |  String  | Name of the command, set to "pause".   |
+--------------+----------+----------------------------------------+
| Transaction  |  Number  | There is no transaction ID for this    |
| ID           |          | command. Set to 0.                     |
+--------------+----------+----------------------------------------+
| Command      |  Null    | Command information object does not    |
| Object       |          | exist. Set to null type.               |
+--------------+----------+----------------------------------------+
|Pause/Unpause |  Boolean | true or false, to indicate pausing or  |
| Flag         |          | resuming play                          |
+--------------+----------+----------------------------------------+
| milliSeconds |  Number  | Number of milliseconds at which the    |
|              |          | the stream is paused or play resumed.  |
|              |          | This is the current stream time at the |
|              |          | Client when stream was paused. When the|
|              |          | playback is resumed, the server will   |
|              |          | only send messages with timestamps     |
|              |          | greater than this value.               |
+--------------+----------+----------------------------------------+

server 使用 onStatus 命令 NetStream.Pause.Notify 表示暂停,NetStream.Unpause.Notify 表示不暂停,_error message 表示失败。

Message Exchange Examples

Publish Recorded Video

  +--------------------+                     +-----------+
     |  Publisher Client  |        |            |   Server  |
     +----------+---------+        |            +-----+-----+
                |           Handshaking Done          |
                |                  |                  |
                |                  |                  |
       ---+---- |----- Command Message(connect) ----->|
          |     |                                     |
          |     |<----- Window Acknowledge Size ------|
  Connect |     |                                     |
          |     |<------ Set Peer BandWidth ----------|
          |     |                                     |
          |     |------ Window Acknowledge Size ----->|
          |     |                                     |
          |     |<----- User Control(StreamBegin) ----|
          |     |                                     |
       ---+---- |<-------- Command Message -----------|
                |   (_result- connect response)       |
                |                                     |
       ---+---- |--- Command Message(createStream) -->|
   Create |     |                                     |
   Stream |     |                                     |
       ---+---- |<------- Command Message ------------|
                | (_result- createStream response)    |
                |                                     |
       ---+---- |---- Command Message(publish) ------>|
          |     |                                     |
          |     |<----- User Control(StreamBegin) ----|
          |     |                                     |
          |     |---- Data Message (Metadata) ------->|
          |     |                                     |
Publishing|     |------------ Audio Data ------------>|
  Content |     |                                     |
          |     |------------ SetChunkSize ---------->|
          |     |                                     |
          |     |<--------- Command Message ----------|
          |     |      (_result- publish result)      |
          |     |                                     |
          |     |------------- Video Data ----------->|
          |     |                  |                  |
          |     |                  |                  |
                |    Until the stream is complete     |
                |                  |                  |

Broadcast a Shared Object Message

 +----------+                       +----------+
                 |  Client  |           |           |  Server  |
                 +-----+----+           |           +-----+----+
                       |   Handshaking and Application    |
                       |          connect done            |
                       |                |                 |
                       |                |                 |
                       |                |                 |
                       |                |                 |
   Create and ---+---- |---- Shared Object Event(Use)---->|
   connect       |     |                                  |
   Shared Object |     |                                  |
              ---+---- |<---- Shared Object Event --------|
                       |       (UseSuccess,Clear)         |
                       |                                  |
              ---+---- |------ Shared Object Event ------>|
   Shared object |     |         (RequestChange)          |
   Set Property  |     |                                  |
              ---+---- |<------ Shared Object Event ------|
                       |            (Success)             |
                       |                                  |
              ---+---- |------- Shared Object Event ----->|
    Shared object|     |           (SendMessage)          |
    Message      |     |                                  |
    Broadcast ---+---- |<------- Shared Object Event -----|
                       |           (SendMessage)          |
                                        |                 |
                                        |                 |

Publish Metadata from Recorded Stream

+------------------+                       +---------+
         | Publisher Client |         |             |   FMS   |
         +---------+--------+         |             +----+----+
                   |     Handshaking and Application     |
                   |            connect done             |
                   |                  |                  |
                   |                  |                  |
           ---+--- |-- Command Messsage (createStream) ->|
       Create |    |                                     |
       Stream |    |                                     |
           ---+--- |<-------- Command Message -----------|
                   |   (_result - command response)      |
                   |                                     |
           ---+--- |---- Command Message (publish) ----->|
   Publishing |    |                                     |
     metadata |    |<----- UserControl (StreamBegin) ----|
    from file |    |                                     |
              |    |---- Data Message (Metadata) ------->|
                   |                                     |

参考

Adobe RTMP Specification

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容

  • RTMP 协议 一、概述 RTMP协议是Real Time Message Protocol(实时信息传输协议)的...
    O2Space_Xiu阅读 26,976评论 4 13
  • 前言 2018 年 12 月,接到了一个调查原有直播系统播放端与编码端时差对不上的任务,于是首先开始学习 RTMP...
    jerryyyq阅读 1,229评论 0 6
  • 版本记录 前言 大家都知道很多视频应用的app中都是使用RTMP格式的协议,这个是国际上共同使用的协议,我自己虽然...
    刀客传奇阅读 12,078评论 5 15
  • 概述 本文篇幅较长,主要介绍RTMP协议格式、信令过程、FLV tag三个部分,文档结构参考rtmp_specif...
    半岛夏天阅读 2,254评论 0 1
  • RTMP协议是Real Time Message Protocol(实时信息传输协议)的缩写,它是由Adobe公司...
    我是李小胖阅读 1,260评论 0 0