Skip to content

Latest commit

 

History

History
1144 lines (918 loc) · 81.1 KB

流媒体协议.md

File metadata and controls

1144 lines (918 loc) · 81.1 KB

流媒体协议

2.1. RTMP 简介

  • RTMP(Real Time Messaging Protocol)是一个应用层协议,主要用于在Flash player和服务器之间传输视频、音频、控制命令等内容。该协议的突出优点是低延时。RTMP基于TCP,默认使用端口1935
  • RTMP协议是应用层协议,是要靠底层可靠的传输层协议(通常是TCP)来保证信息传输的可靠性的。在基于传输层协议的连接建立完成后,RTMP协议也要客户端和服务器通过"握⼿"来建立基于传输层连接之上的RTMP Connection连接,在 Connection 连接上会传输一些控制信息,如 SetChunkSize,SetACKWindowSize。其中CreateStream命令会创建一个Stream连接,用于传输具体的音视频数据和控制这些信息传输的命令信息。RTMP协议传输时会对数据做自己的格式化,这种格式的消息我们称之为RTMP Message,而实际传输的时候为了更好地实现多路复用、分包和信息的公平性,发送端会把 Message 划分为带有 Message ID 的 Chunk,每个 Chunk 可能是一个单独的 Message,也可能是 Message 的一部分,在接收端会根据 chunk 中包含的 data 的长度、message id 和 message 的长度把 chunk 还原成完整的 Message,从而实现信息的收发。

2.2. RTMP 播放基本流程

RTMP播放基本流程

2.3. RTMP 推流流程

RTMP推流流程

2.4. RTMP 拉流流程

RTMP拉流流程

2.5. 握手

RTMP握手

  • 要建立一个有效的RTMP Connection连接,首先要"握手"。

  • 客户端要向服务器发送C0、C1、C2(按序)三个 chunk,服务器向客户端发送S0、S1、S2(按序)三个 chunk,然后才能进行有效的信息传输。RTMP 协议本身并没有规定这 6 个Message的具体传输顺序,但 RTMP 协议的实现者需要保证这⼏点:

    • 客户端要等收到 S1 之后才能发送 C2
    • 客户端要等收到 S2 之后才能发送其他信息(控制信息和真实音视频等数据)
    • 服务端要等到收到 C0 之后发送 S1
    • 服务端必须等到收到 C1 之后才能发送 S2
    • 服务端必须等到收到 C2 之后才能发送其他信息(控制信息和真实音视频等数据)
  • 实际上,为了减少发送次数,一般是这样的:

    RTMP握手实际实现

    • Client -> Server:C0+C1
    • Server -> Client:S0+S1+S2
    • Client -> Server:C2

2.5.1. C0 和 S0

RTMP的C0和S0消息

  • C0S0包都是一个单一的8 位字节
  • C0中,这一字段指示出客户端要求的 RTMP 版本号。
  • S0中,这一字段指示出服务器端选择的 RTMP 版本号。
  • 0、1、2三个值是由早期 ݊ 其他产品使用的,是废弃值。
  • 3为规范的版本。
  • 4-31被保留为 RTMP 协议的未来实现版本使用。
  • 32-255不允许使用(来区分开RTMP和其他常用一个可打印字符开始的文本协议)。
  • 无法识别客户端所要求版本号的服务器应该以版本3响应,(收到响应的)客户端可以选择降低到版本3,或者放弃握手。

2.5.2. C1 和 S1

RTMP的C1和S1消息

  • C1S1数据包的长度都是1536字节。
  • time(4 个字节),这个字段包含一个timestamp,用于本终端发送的所有后续块的时间起点。这个值可以是 0,或者一些任意值。要同步多个块流,终端可以发送其他块流当前的timestamp的值。
  • zero(4 个字节),这个字段必需都是 0。
  • random bytes(1528 个字节),这个字段可以包含任意值。终端需要区分出响应来自它发起的握手还是对端发起的握手,这个数据应该发送一些足够随机的数。这个不需要对随机数进行加密保护,也不需要动态值。

2.5.3. C2 和 S2

RTMP的C2和S2消息

  • C2S2数据包的长度都是1536字节。
  • time(4 个字节),这个字段必须包含终端在(C2响应的)S1或者(S2响应的)C1发的timestamp
  • time2(4 个字节),这个字段必须包含终端先前(S1或者C1)发出数据包timestamp
  • random echo(1528 个字节),这个字段必须包含终端发的(C2响应的)S1或者(S2响应的)C1的随机数。两端都可以一起使用timetime2字段再加当前timestamp快速估算带宽或者连接延迟,但这不太可能是有多大用处。

2.6. RTMP Chunk Stream

  • Chunk Stream是对传输RTMP Chunk的流的逻辑上的抽象,客户端和服务器之间有关RTMP的信息都在这个流上通信。

2.6.1. Message

RTMP消息

  • TypeID(类型 ID):消息的类型 ID,1个字节。
  • Payload Length(长度):是指Message Payload(消息负载)音视频等信息的数据的长度,3个字节。
  • Timestamp(时间戳):消息的时间戳(但不一定是当前时间),4个字节。
  • Message Stream ID(消息的流 ID):每个消息的唯一标识,划分成Chunk和还原ChunkMessage的时候都是根据这个ID来辨识是否是同一个消息的Chunk的,4(3?)个字节,并且以小端格式存储。

2.6.2. Chunk

  • RTMP在收发数据的时候并不是以Message为单位的,而是把Message拆分成Chunk发送,而且必须在一个Chunk发送完成之后才能开始发送下一个Chunk。每个Chunk中带有MessageID代表属于哪个Message,接收端也会按照这个id来将chunk组装成Message
  • Chunk的默认大小是128字节,在传输过程中,通过一个叫做Set Chunk Size的控制信息可以设置Chunk数据量的最大值,在发送端和接收端会各自维护一个Chunk Size,可以分别设置这个值来改变自己这一方发送的Chunk最大大小
2.6.2.1. Chunk Format(块格式)

RTMP块格式 RTMP的块头1 RTMP的块头2

2.6.2.1.1. Basic Header
  • 包含了chunk stream ID(流通道 ID)和chunk type(chunk 的类型),chunk stream id一般被简写为CSID,用来唯一标识一个特定的流通道,chunk type决定了后面Message Header的格式。
  • Basic Header的长度可能是1、2 或 3 个字节,其中chunk type的长度是固定的2bit,Basic Header是变长的,其长度取决于CSID的大小,在足够存储这两个字段的前提下最好用尽量少的字节从而减少由于引入 Header 增加的数据量。
  • RTMP协议最多支持65597个用户自定义chunk stream ID,范围为 [3,65599],ID 0、1、2被协议规范直接使用,其中ID 值为 0、1 分表表示了 Basic Header 占用 2 个字节和 3 个字节
  • CSID 值 0:代表Basic Header占用2个字节,CSID[64,319] 之间。
  • CSID 值 1:代表Basic Header占用3个字节,CSID[64,65599] 之间。
  • CSID 值 2:代表该chunk控制信息和一些命令信息
  • Basic Header1个字节时,CSID6位,6 位最多可以表示 64 个数,因此这种情况下CSID[0,63] 之间,其中用户可自定义的范围为 [3,63]

RTMP块基础头1

  • Basic Header2个字节时,CSID只占8位,第一个字节除chunk type占用的 bit 都置为0,第二个字节用来表示CSID - 64,8 位可以表示 [0, 255] 共 256 个数,ID 的计算方法为(第二个字节+64),范围为 [64,319]

RTMP块基础头2

  • Basic Header3个字节时,ID 的计算方法为(第三字节*256+第二字节+64)(Basic Header 是采用小端存储的方式),范围为 [64,65599]

RTMP块基础头3

2.6.2.1.2. Message Header
  • Message Header的格式和长度取决于Basic Headerchunk type,共有4种不同的格式,由上面所提到的Basic Header中的 fmt(CSID) 字段控制。

####### 2.6.2.1.2.1. fmt=00

RTMP的块消息头1

  • Message Header占用11个字节,其他三种能表示的数据它都能表示,但在chunk stream的开始的第一个chunk和头信息中的时间戳后退(即值与上一个chunk相比减小,通常在回退播放的时候会出现这种情况)的时候必须采用这种格式。
  • timestamp(时间戳):占用3个字节,因此它最多能表示到16777215 = 0xFFFFFF = (2^24)-1, 当它的值超过这个最大值时,这三个字节都置为1,这样实际的timestamp会转存到Extended Timestamp字段中,接收端在判断timestamp字段24个位都为1时就会去Extended timestamp中解析实际的时间戳。
  • message length(消息数据的长度):占用3个字节,表示实际发送的消息的数据如音频帧、视频帧等数据的长度,单位是字节。注意这里是Message的长度,也就是chunk属于的Message的总数据长度,而不是chunk本身Data的数据的长度。
  • message type id(消息的类型 ID):占用1个字节,表示实际发送的数据的类型,如 8 代表音频数据、9 代表视频数据。
  • msg stream id(消息的流 ID):占用4个字节,表示该chunk所在的流的ID,和Basic HeaderCSID一样,它采用小端存储的方式。

####### 2.6.2.1.2.2. fmt=01

RTMP的块消息头2

  • Message Header占用7个字节,省去了表示msg stream id4个字节,表示此chunk和上一次发的chunk所在的流相同,如果在发送端只和对端有一个流连接的时候可以尽量去采取这种格式。
  • timestamp delta占用3个字节,注意这里和fmt=00时不同,存储的是和上一个chunk的时间差。类似上面提到的timestamp,当它的值超过3个字节所能表示的最大值时,三个字节都置为1,实际的时间戳差值就会转存到Extended Timestamp字段中,接收端在判断timestamp delta字段24个位都为1时就会去Extended timestamp中解析时机的与上次时间戳的差值。

####### 2.6.2.1.2.3. fmt=10

RTMP的块消息头3

  • Message Header占用3个字节,相对于fmt=01格式又省去了表示消息长度的3个字节和表示消息类型的1个字节,表示此chunk和上一次发送的chunk所在的流、消息的长度和消息的类型都相同。余下的这三个字节表示timestamp delta,使用同fmt=01

####### 2.6.2.1.2.4. fmt=11

  • 0字节!!!表示这个chunkMessage Header和上一个是完全相同的,自然就不用再传输一遍了。
  • 当它跟在fmt=00chunk后面时,表示和前一个chunk的时间戳都是相同的。就是一个Message拆分成了多个chunk,这个chunk和上一个chunk同属于一个Message
  • 当它跟在fmt=01或者fmt=10chunk后面时,表示和前一个chunk的时间戳的差是相同的。
2.6.2.1.3. Extended Timestamp
  • 只有时间戳大于3个字节能表示的最大数值0xFFFFFF=16777215时,才会用这个字段来表示真正的时间戳,否则这个字段为0
  • 扩展时间戳占4个字节,能表示的最大数值就是0xFFFFFFFF=4294967295。当扩展时间戳启用时,timestamp字段或者timestamp delta要全置为0xFFFFFF,表示应该去扩展时间戳字段来提取真正的时间戳或者时间戳差。
  • 扩展时间戳存储的是完整值,而不是减去时间戳或者时间戳差的值。
2.6.2.1.4. Chunk Data
  • 用户层真正要发送的与协议无关的数据,长度在 (0,chunkSize] 之间。

2.6.3. 协议控制消息

  • RTMPchunk流会用一些特殊的值来代表协议的控制消息,它们的Message Stream ID必须为0(代表控制流信息),CSID必须为2,Message Type ID可以为1、2、3、5、6。控制消息的接收端会忽略掉chunk中的时间戳,收到后立即生效。
2.6.3.1. Set Chunk Size(Message Type ID=1)

RTMP的SetChunkSize消息数据

  • 设置chunkData字段所能承载的最大字节数,默认为128B,通信过程中可以通过发送该消息来设置chunk size的大小(不得小于 128B),而且通信双方会各自维护一个chunk size,两端的chunkSize是独立的。其中第一位必须为0,chunk size31个位,最大可代表2147483647 = 0x7FFFFFFF = (2^31)-1
2.6.3.2. Abort Message(Message Type ID=2)

RTMP的AbortMessage消息数据

  • 当一个Message被切分为多个chunk,接收端只接收到了部分chunk时,发送该控制消息表示发送端不再传输同Messagechunk,接收端接收到这个消息后要丢弃这些不完整的chunkData数据中只需要一个CSID,表示丢弃该CSID的所有已接收到的chunk
2.6.3.3. Acknowledgement(Message Type ID=3)和 Window Acknowledgement Size(Message Type ID=5)

RTMP的Acknowledgement消息数据 RTMP的WindowAcknowledgementSize消息数据

  • Window Acknowledgement Size用于设置窗口确认大小,Acknowledgement是窗口确认消息。

  • 会话开始时,双方都要向对端发送Window Acknowledgement Size,用于指明期望获得确认的大小。当一端收到内容大小超过Window Acknowledgement Size,就要向对方发送Acknowledgement

    • 会话开始计算收到 byte 个数的时间点是收到Window Acknowledgement Size消息开始。
    • byte size 不包括 tcp 包头,应该是 chunk 的大小,即从 tcp 的 recv 函数中获得的内容大小。
    • 双方都要向对方发送Window Acknowledgement SizeAcknowledgement
    • 发送端发送完Window Acknowledgement Size消息后,没有收到Acknowledgement是不再发送进一步的消息的(这样会容易引起错误,导致再也发送不出消息了)。
    • 对于拉流端,一般在收到av_createStream后,接着play,然后发送Acknowledgement以让服务器继续发送数据。
2.6.3.4. Set Peer Bandwidth(Message Type ID=6)

RTMP的SetPeerBandwidth消息数据

  • 限制对端的输出带宽。接收端接收到该消息后会通过设置消息中的Window ACK Size来限制已发送但未接收到反馈的消息的大小来限制发送端的发送带宽。如果消息中的Window ACK Size与上一次发送给发送端的size不同的话要回馈一个Window Acknowledgement Size的控制消息。
  • Hard(Limit Type=0):接收端应该将Window Ack Size设置为消息中的值。
  • Soft(Limit Type=1):接收端可以将Window Ack Size设为消息中的值,也可以保存原来的值(前提是原来的Size小于该控制消息中的Window Ack Size)。
  • Dynamic(Limit Type=2):如果上次的Set Peer Bandwidth消息中的Limit Type0,本次也按Hard处理,否则忽略本消息,不去设置Window Ack Size
2.6.3.5. Command Message(Message Type ID=17 或 20)
  • 发送端发送时会带有命令的名字,TransactionID表示此次命令的标识,Command Object表示相关参数。接收端收到命令后,会返回以下三种消息中的一种:

    • _result消息表示接收该命令,对端可以继续往下执行流程。
    • _error消息代表拒绝该命令要执行的操作。
    • method name消息代表要在之前命令的发送端执行的函数名称。
    • 这三种回应的消息都要带有收到的命令消息中的TransactionID来表示本次的回应作用于哪个命令。可以认为发送命令消息的对象有两种,一种是NetConnection,表示双端的上层连接,一种是NetStream,表示流信息的传输通道,控制流信息的状态,如 Play 播放流、Pause 暂停。
2.6.3.5.1. NetConnection Commands(连接层的命令)
  • 用来管理双端之间的连接状态,同时也提供了异步远程方法调用(RPC)在对端执行某方法。

####### 2.6.3.5.1.1. connect

  • 用于客户端向服务器发送连接请求。

    字段 类型 说明
    Command Name(命令名字) String "connect"
    Transaction ID(事务 ID) Number 1
    Command Object(命令包含的参数对象) Object 键值对集合表示的命令参数
    Optional User Arguments(额外的用户参数) Object 用户自定义的额外信息
  • 连接命令对象中使用的名称-值对:

    属性 类型 描述 范例
    app String 客户端连接到的服务器端应用的名字 testapp
    flashver String Flash Player 版本号
    和 ApplicationScript getversion()方法返回的是同一个字符串
    FMSc/1.0
    swfUrl String 进行当前连接的 SWF 文件源地址 file://C:/FlvPlayer.swf
    tcUrl String 服务器 URL
    格式:protocol://servername:port/appName/appInstance
    rtmp://localhost:1935/testapp/instance
    fpad Boolean 使用了代理 true
    audioCodecs Number 表明客户端所支持的音频编码 SUPPORT_SND_MP3
    videoCodecs Number 表明支持的视频编码 SUPPORT_VID_SORENSON
    videoFunction Number 表明所支持的特殊视频方法 SUPPORT_VID_CLIENT_SEEK
    pageUrl String SWF 文件所加载的网页 URL http://somehost/sample.html
    objectEncoding Number AMF 编码方法 AMF3

####### 2.6.3.5.1.2. call

  • 用于在对端执行某函数,即常说的RPC(远程进程调用)。

    字段 类型 说明
    Procedure Name(进程名) String 要调用的进程名称
    Transaction ID(事务 ID) Number 如果想要对端响应的话置为⾮ 0 值,否则置为 0
    Command Object(命令包含的参数对象) Object 键值对集合表示的命令参数
    Optional User Arguments(额外的用户参数) Object 用户自定义的额外信息
  • 如果消息中的TransactionID不为0的话,对端需要对该命令做出响应,响应的消息结构:

    字段 类型 说明
    Transaction ID(事务 ID) Number 上面接收到的命令消息中的 TransactionID
    Command Object(命令包含的参数对象) Object 键值对集合表示的命令参数
    Optional User Arguments(额外的用户参数) Object 用户自定义的额外信息

####### 2.6.3.5.1.3. createStream

  • 创建传递具体信息的通道,从而可以在这个流中传递具体信息,传输信息单元为chunk。当发送完createStream消息之后,解析服务器返回的消息会得到一个stream ID,这个ID也就是以后和服务器通信的message stream ID,一般返回的是1,不固定。

    字段 类型 说明
    Command Name(命令名字) String "createStream"
    Transaction ID(事务 ID) Number 一般返回的是1,不固定
    Command Object(命令包含的参数对象) Object 键值对集合表示的命令参数
    Optional User Arguments(额外的用户参数) Object 用户自定义的额外信息
2.6.3.5.2. NetStream Commands(流连接上的命令)
  • NetStream建立在NetConnection之上,通过NetConnectioncreateStream命令创建,用于传输具体的音频、视频等信息。在传输层协议之上只能连接一个NetConnection(TCP 连接),但一个NetConnection可以建立多个NetStream来建立不同的流通道传输数据。

####### 2.6.3.5.2.1. onStatus

  • 服务端收到NetStream命令后会通过onStatus的命令来响应客户端,表示当前NetStream的状态。

    字段 类型 说明
    Command Name(命令名字) String "onStatus"
    Transaction ID(事务 ID) Number 0
    Command Object(命令包含的参数对象) NULL
    Info Object Object AMF 类型的 Object,至少包含以下三个属性:
    1、"level",String 类型,可以为"warning"、"status"、"error"中的一种
    2、"code",String 类型,代表具体状态的关键字,比如"NetStream.Play.Start"表示开始播流
    3、"description",String 类型,代表对当前状态的描述,提供对当前状态可读性更好的解释,除了这三种必要信息,用户还可以自⼰增加自定义的键值对

####### 2.6.3.5.2.2. play

  • 由客户端向服务器发起请求从服务器端接收数据(如果传输的信息是视频的话就是请求开始播流),可以多次调用,这样本地就会形成一组数据流的接收者。其中有一个reset字段,表示是覆盖之前的播流(设为 true)还是重新开始一路播放(设为 false)。

    字段 类型 说明
    Command Name(命令名字) String "play"
    Transaction ID(事务 ID) Number 0
    Command Object(命令包含的参数对象) NULL
    StreamName(流名称) String 要播放的流的名称
    开始位置 Number 可选参数,表示从何时开始播流,以秒为单 位。默认为-2,代表选取对应该流名称的直播流,即当前正在推送的流开始播放,如果对应该名称的直播流不存在,就选取该名称的流的录播版本,如果这也没有,当前播流端要等待直到对端开始该名称的流的直播。如果传值-1,那么只会选取直播流进行播放,即使有录播流也不会播放;如果传值或者正数,就代表从该流的该时间点开始 播放,如果流不存在的话就会自动播放播放列表中的下一个流
    周期 Number 可选参数,表示回退的最小间隔单位,以秒为单位计数。默认值为-1,代表直到直播流不再可用或者录播流停止后才能回退播放;如果传值为 0,代表从当前帧开始播放
    重置 Boolean 可选参数,true 代表清除之前的流,重新开始一路播放,false 代表保留原来的流,向本地的播放列表中再添加一条播放流

####### 2.6.3.5.2.3. play2

  • 和上面的play命令不同,play2命令可以将当前正在播放的流切换到同样数据但不同比特率的流上,服务器端会维护多种比特率的文件来供客户端使用 play2 命令来切换。

    字段 类型 说明
    Command Name(命令名字) String "play"
    Transaction ID(事务 ID) Number 0
    Command Object(命令包含的参数对象) NULL
    parameters Object AMF 编码的 Flash 对象,包括了一些用于描述 flash.net.NetstreamPlayOptions ActionScript obejct 的参数

####### 2.6.3.5.2.4. deleteStream

  • 用于客户端告知服务器端本地的某个流对象已被删除,不需要再传输此路流。

    字段 类型 说明
    Command Name(命令名字) String "deleteStream"
    Transaction ID(事务 ID) Number 0
    Command Object(命令包含的参数对象) NULL
    Stream ID(流 ID) Number 本地已删除,不再需要服务器传输的流的 ID

####### 2.6.3.5.2.5. receiveAudio

  • 通知服务器端该客户端是否要发送音频。

    字段 类型 说明
    Command Name(命令名字) String "receiveAudio"
    Transaction ID(事务 ID) Number 0
    Command Object(命令包含的参数对象) NULL
    Bool Flag Boolean true 表示发送音频,如果该值为 false,服务器端不做响应,如果为 true 的话,服务器端就会准备接收音频数据,会向客户端回复 NetStream.Seek.Notify 和 NetStream.Play.Start 的 Onstatus 命令告知客户端当前流的状态

####### 2.6.3.5.2.6. receiveVideo

  • 通知服务器端该客户端是否要发送视频。

    字段 类型 说明
    Command Name(命令名字) String "receiveVideo"
    Transaction ID(事务 ID) Number 0
    Command Object(命令包含的参数对象) NULL
    Bool Flag Boolean true 表示发送视频,如果该值为 false,服务器端不做响应,如果为 true 的话,服务器端就会准备接收视频数据,会向客户端回复 NetStream.Seek.Notify 和 NetStream.Play.Start 的 Onstatus 命令告知客户端当前流的状态

####### 2.6.3.5.2.7. publish

  • 由客户端向服务器发起请求推流到服务器。

    字段 类型 说明
    Command Name(命令名字) String "publish"
    Transaction ID(事务 ID) Number 0
    Command Object(命令包含的参数对象) NULL
    Publishing Name(推流的名称) String 流名称
    Command Object(命令包含的参数对象) String "live"、"record"、"append"中的一种。
    live 表示该推流文件不会在服务器端存储
    record 表示该推流的文件会在服务器应用程序下的子目录下保存以便后续播放,如果文件已经存在的话删除原来所有的内容重新写入
    append 也会将推流数据保存在服务器端,如果文件不存在的话就会建立一个新文件写入,如果对应该流的文件已经存在的话保存原来的数据,在文件末尾接着写入

####### 2.6.3.5.2.8. seek

  • 定位到视频或音频的某个位置,以毫秒为单位。

    字段 类型 说明
    Command Name(命令名字) String "seek"
    Transaction ID(事务 ID) Number 0
    Command Object(命令包含的参数对象) NULL
    milliSeconds Number 定位到该文件的 xx 毫秒处

####### 2.6.3.5.2.9. pause

  • 客户端告知服务端停止或恢复播放。

  • 如果Pausetrue即表示客户端请求暂停的话,服务端暂停对应的流会返回NetStream.Pause.NotifyonStatus命令来告知客户端当前流处于暂停的状态,当Pausefalse时,服务端会返回NetStream.Unpause.Notify的命令来告知客户端当前流恢复。如果服务端对该命令响应失败,返回 _error 信息。

    字段 类型 说明
    Command Name(命令名字) String "pause"
    Transaction ID(事务 ID) Number 0
    Command Object(命令包含的参数对象) NULL
    Pause/Unpause Flag Boolean true 表示暂停,false 表示恢复
    milliSeconds Number 暂停或者恢复的时间,以毫秒为单位

3.1. HLS 简介

HLS框架

  • 作为Apple提出的一种基于HTTP的协议,HLS(HTTP Live Streaming)用于解决实时音视频流的传输。尤其是在移动端,由于iOS/H5不支持flash,使得HLS成了移动端实时视频流传输的首选。HLS经常用在直播领域,一些国内的直播云通常用HLS拉流。

  • HLS值得诟病之处就是其延迟严重,延迟通常在10-30s之间。

  • 流分割器(Stream Segmenter)负责将编码器输出的MPEG-2 TS流分割为一系列连续的、长度均等的TS文件,并依次发送至内容分发组件中的Web服务器进行存储。

  • 为了跟踪播放过程中媒体文件的可用性和当前位置,流分割器还需创建一个含有指向这些小TS文件指针的索引文件,同样放置于Web服务器之中。这个索引文件可以看作是一个连续媒体流中的播放列表滑动窗口,每当流分割器生成一个新的TS文件时,这个索引文件的内容也被更新,新的文件URI加入到滑动窗口的末尾,老的文件URI则被移去,这样索引文件中将始终包含最新的固定数量的 x 个分段。流分割器还可以对其生成的每个小TS文件进行加密,并生成相应的密钥文件。

  • 之所以采用MPEG-2 TS格式来对编码后的媒体流进行统一封装,是因为它能够将音视频媒体流严格按时序进行交织复用,任意截取和分段后,每一个分段都可能不依赖于之前的分段而独立进行解码和播放。为此,TS文件中必须仅包含一个MPEG-2节目,在每个文件的开头应包含一个节目关联表(PAT)和一个节目映射表(PMT),包含视频的文件中还必须含有至少一个关键帧和其他足够信息(如序列头)用以完成解码器的初始化。索引文件采用扩展的M3U播放列表格式,后缀名为 .m3u8 。M3U 播放列表是一个由若干文本行组成的文本文件,其中每一行要么是一个 URI,一个空行,或者是一个以注释符"#"起始的行。每个 URI 行指向一个分段的媒体文件,或者一个衍生的索引(播放列表)文件。除了以"#EXT"起始的行是标签行外,其他以"#"起始的行是注释,应予忽略。

  • HTTP Live Streaming并不是一个真正实时的流媒体系统,这是因为对应于媒体分段的大小和持续时间有一定潜在的时间延迟。在客户端中,至少在一个分段媒体文件被完全下载之后才能够开始播放,而通常要求下载完成两个分段媒体文件之后才开始播放以保证不同分段音视频之间的无缝连接。此外,在客户端开始下载之前,必须等待服务器端的编码器和流分割器至少生成一个 TS 文件,这也会带来潜在的时延。在推荐配置下,HTTP Live Streaming 系统的典型时延约在30s左右。

  • 在基于HTTP Live Streaming的流媒体系统中,服务器可以为同一节目源准备多份以不同码率和质量编码的替换流,并为每个替换流都生成一个衍生的索引文件。在主索引文件中通过包含一系列指向其他衍生索引文件的 URI 指针来找到相应的替换流。

  • 相对于常⻅的流媒体直播协议,例如 RTMP 协议、RTSP 协议、MMS 协议等,HLS 直播最大的不同在于,直播客户端获取到的,并不是一个完整的数据流。HLS 协议在服务器端将直播数据流存储为连续的、很短时长的媒体文件(MPEG-TS 格式),而客户端则不断的下载并播放这些小文件,因为服务器端总是会将最新的直播数据生成新的小文件,这样客户端只要不停的按顺序播放从服务器获取到的文件,就实现了直播。由此可⻅,基本上可以认为,HLS 是以点播的技术方式来实现直播。由于数据通过 HTTP 协议传输,所以完全不用考虑防⽕墙或者代理的问题,而且分段文件的时长很短,客户端可以很快的选择和切换码率,以适应不同带宽条件下的播放。不过 HLS 的这种技术特点,决定了它的延迟一般总是会高于普通的流媒体直播协议。

  • HLS 协议优势

    • HLS 相对于 RTMP 来讲使用了标准的 HTTP 协议来传输数据,可以避免在一些特殊的网络环境下被屏蔽。
    • HLS 相比 RTMP 在服务器端做负载均衡要简单得多。因为 HLS 是基于无状态协议 HTTP 实现的,客户端只需要按照顺序使用下载存储在服务器的普通 ts 文件进行播放就可以。而 RTMP 是一种有状态协议,很难对视频服务器进行平滑扩展,因为需要为每一个播放视频流的客户端维护状态。
    • HLS 协议本身实现了码率自适应,在不同带宽情况下,设备可以自动切换到最适合自己码率的视频播放。
  • HLS 协议劣势

    • HLS 协议在直播的视频延迟时间很难做到 10s 以下延时,而 RTMP 协议的延时可以降到 1s 左右。

3.2. M3U8

  • HLSm3u8,是一个ts的列表,也就是告诉浏览器可以播放这些ts文件。

3.2.1. m3u8 的常用参数

配置项 作用
#EXTM3U 每个 M3U 文件第一行必须是这个 tag,起标示作用
#EXT-X-VERSION 该属性可以没有,目前主要是 version 3,最新的是 7
#EXT-X-MEDIA-SEQUENCE 每一个 media URI 在 PlayList 中只有唯一的序号,相邻之间序号+1,一个 media URI 并不是必须要包含的,如果没有,默认为 0
#EXT-X-TARGETDURATION 所有切片的最大时长。有些 Apple 设备这个参数不正确会无法播放。SRS 会自动计算出 ts 文件的最大时长,然后更新 m3u8 时会自动更新这个值。用户不必自己配置
#EXTINF ts 切片的实际时长,SRS 提供配置项 hls_fragment,但实际上的 ts 时长还受 gop 影响
ts 文件的数目 SRS 可配置 hls_window(单位是秒,不是数量),指定 m3u8 中保存多少个切片。譬如,每个 ts 切片为 10 秒,窗口为 60 秒,那么 m3u8 中最多保存 6 个 ts 切片,SRS 会自动清理旧的切片
livestream-0.ts SRS 会自动维护 ts 切片的文件名,在编码器重推之后,这个编号会继续增长,保证流的连续性。直到 SRS 重启,这个编号才重置为 0

3.3. TS

TS文件分层

  • ts文件为传输流文件,视频编码主要格式为H264/MPEG4,音频为AAC/MP3
  • ts 传输流,是由固定长度的包组成,含有独立时间基准的一个或多个节目,适用于误码较多的环境,并且从流的任意一段开始都可以独立解码。在 MPEG-2 系统中,由视频、音频的 ES 流和辅助数据复接生成的用于实际传输的标准信息流称为 MPEG-2 传送流。TS 流是原始的 PES 流中按照一定的频率插入 PSI/SI 和一些标识符(辅助数据)信息,然后按固定长度打包形成的传输流。PSI/SI 信息在 TS 流中并不是只发送一次,而是按照一定的频率插入码流重复发送的。
  • TS 包的长度:188B 或 204B,204B 长度是在 188B 后面增加了 16B 的 CRC 校验数据。

3.3.1. TS 层

TS流

  • ts层:Transport Stream,是在 pes 层的基础上加入数据流的识别和传输必须的信息。

  • ts 包大小固定为188字节,ts 层分为三个部分:ts header、adaptation field、payload

  • ts 层的内容是通过PID值来标识的,主要内容包括:PAT 表、PMT 表、音频流、视频流。解析 ts 流要先找到 PAT 表,只要找到 PAT 就可以找到 PMT,然后就可以找到音视频流了。PAT 表和 PMT 表需要定期插入 ts 流,因为用户随时可能加入 ts 流,这个间隔比较小,通常每隔⼏个视频帧就要加入 PAT 和 PMT。PAT 和 PMT 表是必须的,还可以加入其它表如 SDT(业务描述表)等,不过 hls 流只要有 PAT 和 PMT 就可以播放了。

    • PAT 表:主要的作用就是指明了 PMT 表的 PID 值。
    • PMT 表:主要的作用就是指明了音视频流的 PID 值。
3.3.1.1. TS Header
名称 长度 说明
sync_byte 8b 同步字节,固定为 0x47
transport_error_indicator 1b 传输错误指示符,表明在 ts 头的 adapt 域后有一个无用字节,通常都为 0,这个字节算在 adapt 域长度内
payload_unit_start_indicator 1b 负载单元起始标示符,一个完整的数据包开始时标记为 1
transport_priority 1b 传输优先级,0 为低优先级,1 为高优先级,通常取 0
pid 13b pid 值
transport_scrambling_control 2b 传输加扰控制,00 表示未加密
adaptation_field_control 2b 是否包含自适应区,"00"保留;"01"为无自适应域,仅含有效负载;"10"为仅含自适应域,无有效负载;"11"为同时带有自适应域和有效负载
continuity_counter 4b 递增计数器,从 0-f,起始值不一定取 0,但必须是连续的
  • ts header 固定4个字节,adaptation field 可能存在也可能不存在,主要作用是给不足 188 字节的数据做填充,payload 是 pes 数据。
3.3.1.2. adaptation field
名称 长度 说明
adaptation_field_length 1B 自适应域长度,后面的字节数
flag 1B 取 0x50 表示包含 PCR 或 0x40 表示不包含 PCR
PCR 5B Program Clock Reference,节目时钟参考,用于恢复出与编码端一致的系统时序时钟 STC(System Time Clock)
stuffing_bytes xB 填充字节,取值 0xff
3.3.1.3. PAT
名称 长度 说明
table_id 8b PAT 表固定为 0x00
section_syntax_indicator 1b 固定为 14
zero 1b 固定为 0
reserved 2b 固定为 11
section_length 12b 后面数据的长度
transport_stream_id 16b 传输流 ID,固定为 0x0001
reserved 2b 固定为 11
version_number 5b 版本号,固定为 00000,如果 PAT 有变化则版本号加 1
current_next_indicator 1b 固定为 1,表示这个 PAT 表可以用,如果为 0 则要等待下一个 PAT 表
section_number 8b 固定为 0x00
last_section_number 8b 固定为 0x00
开始循环
program_number 16b 节目号为 0x0000 时表示这是 NIT,节目号为 0x0001 时,表示这是 PMT
reserved 3b 固定为 111
PID 13b 节目号对应内容的 PID 值
结束循环
CRC32 32b 前面数据的 CRC32 校验码
3.3.1.4. PMT
名称 长度 说明
table_id 8b PMT 表取值随意,0x02
section_syntax_indicator 1b 固定为 1
zero 1b 固定为 0
reserved 2b 固定为 11
section_length 12b 后面数据的长度
program_number 16b 频道号码,表示当前的 PMT 关联到的频道,取值 0x0001
reserved 2b 固定为 11
version_number 5b 版本号,固定为 00000,如果 PAT 有变化则版本号加 1
current_next_indicator 1b 固定为 1
section_number 8b 固定为 0x005
last_section_number 8b 固定为 0x00
reserved 3b 固定为 111
PCR_PID 13b PCR(节目参考时钟)所在 TS 分组的 PID,指定为视频 PID
reserved 4b 固定为 1111
program_info_length 12b 节目描述信息,指定为 0x000 表示没有
开始循环
stream_type 8b 流类型,标志是 Video 还是 Audio 还是其他数据,h.264 编码对应 0x1b,aac 编码对应 0x0f,mp3 编码对应 0x03
reserved 3b 固定为 111
elementary_PID 13b 与 stream_type 对应的 PID
reserved 4b 固定为 1111
ES_info_length 12b 描述信息,指定为 0x000 表示没有
结束循环
CRC32 32b 前面数据的 CRC32 校验码

3.3.2. PES 层

名称 长度 说明
pes start code 3B 开始码,固定为 0x000001
stream id 1B 音频取值(0xc0-0xdf),通常为 0xc0;视频取值(0xe0-0xef),通常为 0xe0
pes packet length 2B 后面 pes 数据的长度,0 表示长度不限制,只有视频数据长度会超过 0xffff
flag 1B 通常取值 0x80,表示数据不加密、无优先级、备份的数据
flag 1B 取值 0x80 表示只含有 pts,取值 0xc0 表示含有 pts 和 dts
pes data length 1B 后面数据的长度,取值 5 或 10
pts 5B 33bit 值
dts 5B 33bit 值
  • Packet Elemental Stream,是在音视频数据上加了时间戳等对数据帧的说明信息。分组的基本码流,将基本码流 ES 流根据需要分成长度不等的数据包,并加上包头就形成了打包的基本码流 PES 流。是用来传输 ES 的一种数据结构。

3.3.3. ES 层

  • Elementary Stream,即音视频数据。不分段的音频、视频或其他信息的连续码流。

RTSP 简介

  • RTSP(Real Time Streaming Protocol) 是由 Real Network 和 Netscape 共同提出的如何有效地在 IP 网络上传输流媒体数据的应用层协议。
  • RTSP 对流媒体提供了诸如暂停、快进等控制,而它本身并不传输数据,RTSP 的作用相当于流媒体服务器的远程控制。服务器端可以自行选择使用TCP 或 UDP来传送串流内容,它的语法和运作跟HTTP 1.1类似,但并不特别强调时间同步,所以比较能容忍网络延迟。

RTSP 和相关协议

RTSP和相关协议 RTSP工作架构

RTSP 方法

RTSP方法

  • Real-time Transport Protocol (实时传输协议)是 IETF 提出的一个标准,对应的 RFC 文档为RFC3550,主要用于音视频数据的传输。
  • RTP 报文由两部分组成: RTP 头部有效载荷
  • RTP 最大的数据包(包含 RTP 头部) = MTU(1500) - UDP 首部(8) – IP 报文首部(20)
  • 在 RTP 打包的时候是以 Slice 为单位打包的,而不是以帧为单位打包的。

RTP 头

RTP头

RTP 有效负荷

单一 NALU 模式

单一NALU模式

  • 这种打包方式适合于单个 RTP 包小于 1472 字节(MTU - UDP Head - IP Head)的时候。
  • 一般来说,一些 P 帧和 B 帧编码之后比较小,就可以使用这种打包方式。

组合打包模式

组合打包模式

  • 这种打包方式适合于单个 NALU 很小的时候。将多个 NALU 打包到一起也小于 1472 字节的时候就可以使用。
  • 由于一般多个视频帧加到一起还小于 1472 的情况比较少,所以视频数据的 RTP 打包一般来说用组合封包方式的情况也很少。

分片封包模式

分片封包模式

  • 这种打包方式主要用于将 NALU 数据打包成一个 RTP 包时大小大于 1472 字节的时候。
  • 这是经常使用的视频 RTP 打包方法。

RTP的AAC包结构

AU Header Section

RTP的AAC包AUHeaderSection

  • AU-headers-length:占 2 个字节,单位为 bits,表示所有 AU-header + padding bits 占用的位数。
  • 每个 AU-header 对应一个 AU data。
AU-headers-length
  • 占 2 个字节,单位为 bits,表示所有 AU-header + padding bits 占用的位数。
  • 一个 AU-header 长度是两个字节(16bit),因为可以有多个 au-header,所以 AU-headers-length 的值是 16 的倍数,一般音频都是单个音频数据流的发送,所以 AU-headers-length 的值是 16。
  • 因为单位是 bit,除以 8 就是 auHeader 的字节长度;又因为单个 auheader 字节长度是 2 字节,所以再除以 2 就是 auheader 的个数。
AU-header

RTP的AAC包AUHeader

名称 长度 说明
AU-size 由 sizeLength 决定 表示本段音频数据占用的字节数
AU-Index 由 indexLength 决定 表示本段的序号,通常 0 开始
AU-Index-delta 由 indexDeltaLength 决定 表示本段序号与上一段序号的差值

其它的值都是可选的,如果 sdp 中没有出现相关的参数(或者为 0),则表示它们不出现。

RTCP

  • RTCP(Real-time Transport Control Protocol),实时传输控制协议 ,是辅助 RTP 协议使用的。
  • RTCP 报文有很多种,分别负责不同的功能。常用的报文有发送端报告(SR)、接收端报告(RR)、RTP 反馈报告(RTPFB)等。而每一种报告的有效载荷都是不同的。通过这些报告在接收端和发送端传递当前统计的 RTP 包的传输情况的。使用这些统计信息来做丢包重传,以及预测带宽。
  • RTCP 协议只是用来传递 RTP 包的传输统计信息,本身不具有丢包重传和带宽预测的功能,而这些功能需要另外实现。

RTCP

WebSocket

  • WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议;

  • WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输;

    Ajax和WebSocket

  • 浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求,连接建立以后,客户端和服务器端就可以通过 TCP 连接直接交换数据;

  • 当你获取 Web Socket 连接后,你可以通过 send() 方法来向服务器发送数据,并通过 onmessage 事件来接收服务器返回的数据;

  • 为了建立一个 WebSocket 连接,客户端浏览器首先要向服务器发起一个 HTTP 请求,这个请求和通常的 HTTP 请求不同,包含了一些附加头信息,其中附加头信息"Upgrade: WebSocket"表明这是一个申请协议升级的 HTTP 请求,服务器端解析这些附加的头信息然后产生应答信息返回给客户端,客户端和服务器端的 WebSocket 连接就建立起来了,双方就可以通过这个连接通道自由的传递信息,并且这个连接会持续存在直到客户端或者服务器端的某一方主动的关闭连接;

  • 服务器端使用 websocket 需要安装 nodejs-­websocket

    cd 工程目录
    sudo npm init
    #创建package.json文件
    sudo npm install nodejs-websocket

WebRTC

WebRTC 框架

WebRTC架构

WebRTC 连接过程

WebRTC连接过程

ICE Candidate

ICE Candidate
{
  "candidate": "candidate:foundation [icegroupid] [type] [priority] [ip] [port] typ [nettype] generation [x] ufrag [xxxx] network-cost [xxx]",
  "sdpMid": "0",
  "sdpMLineIndex": 0
}
{
  "candidate": "candidate:545662605 1 udp 2113937151 a8e7c788-6636-4392-b5af-08253ad10ee5.local 51579 typ host generation 0 ufrag 0XDd network-cost 999",
  "sdpMid": "0",
  "sdpMLineIndex": 0
}
  • candidate:ICE 候选
    • foundation:用于标志和区分来自同一个 STUN 的不同的候选者,ID 标识
    • icegroupid:ICE 的组 ID
    • type:协议类型
    • priority:优先级
    • ip:ip 地址
    • port:端口
    • typ:标识后面字段的属性类型是候选类型
      • host:本地接口获取到的 candidate
      • srflx:NAT 网关在公网侧的 IP 地址,通过 STUN 或者 TURN 收集(server reflexive candidate)
      • prflx:可以在 ICE 的后续阶段中获取到(peer reflexive candidate)
      • relay:TURN 服务器的公网转发地址,通过 TURN 收集
    • generation [x]:表明当前是第几代的候选
    • ufrag [xxxx]:分配的用户名标识
    • network-cost [xxx]:网卡标识
  • sdpMid:sdp 的 id
  • sdpMLineIndex:sdp 的行索引

SDP

SDP
v=0
o=- 6430937235515578749 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE 0 1
a=extmap-allow-mixed
a=msid-semantic: WMS dGBa2UextnqOyqBq7ReErAi6YWmljy3LhuAA
m=audio 9 UDP/TLS/RTP/SAVPF 111 63 103 104 9 0 8 106 105 13 110 112 113 126
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:Acgf
a=ice-pwd:7zfBgyFzPfptbrklnxbgjOkF
a=ice-options:trickle
a=fingerprint:sha-256 4A:43:A2:A8:3A:50:84:EF:C2:E7:8A:20:1A:35:07:7E:BE:03:24:E3:CD:7C:1E:4F:98:A8:00:44:5A:96:29:5C
a=setup:active
a=mid:0
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid
a=sendrecv
a=msid:dGBa2UextnqOyqBq7ReErAi6YWmljy3LhuAA 5c7496b6-2cd9-4bb8-a521-e881d5d0a241
a=rtcp-mux
a=rtpmap:111 opus/48000/2
a=rtcp-fb:111 transport-cc
a=fmtp:111 minptime=10;useinbandfec=1
a=rtpmap:63 red/48000/2
a=fmtp:63 111/111
a=rtpmap:103 ISAC/16000
a=rtpmap:104 ISAC/32000
a=rtpmap:9 G722/8000
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:106 CN/32000
a=rtpmap:105 CN/16000
a=rtpmap:13 CN/8000
a=rtpmap:110 telephone-event/48000
a=rtpmap:112 telephone-event/32000
a=rtpmap:113 telephone-event/16000
a=rtpmap:126 telephone-event/8000
a=ssrc:1871063466 cname:csbDbPUNcSfBoceC
m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 100 101 102 121 127 120 125 107 108 109 124 119 123 117 35 36 114 115 116 62 118
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:Acgf
a=ice-pwd:7zfBgyFzPfptbrklnxbgjOkF
a=ice-options:trickle
a=fingerprint:sha-256 4A:43:A2:A8:3A:50:84:EF:C2:E7:8A:20:1A:35:07:7E:BE:03:24:E3:CD:7C:1E:4F:98:A8:00:44:5A:96:29:5C
a=setup:active
a=mid:1
a=extmap:14 urn:ietf:params:rtp-hdrext:toffset
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:13 urn:3gpp:video-orientation
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/video-content-type
a=extmap:7 http://www.webrtc.org/experiments/rtp-hdrext/video-timing
a=extmap:8 http://www.webrtc.org/experiments/rtp-hdrext/color-space
a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid
a=extmap:10 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id
a=extmap:11 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id
a=sendrecv
a=msid:dGBa2UextnqOyqBq7ReErAi6YWmljy3LhuAA 20da8f42-dfb8-44fa-9c51-8e3832ed4a05
a=rtcp-mux
a=rtcp-rsize
a=rtpmap:96 VP8/90000
a=rtcp-fb:96 goog-remb
a=rtcp-fb:96 transport-cc
a=rtcp-fb:96 ccm fir
a=rtcp-fb:96 nack
a=rtcp-fb:96 nack pli
a=rtpmap:97 rtx/90000
a=fmtp:97 apt=96
a=rtpmap:98 VP9/90000
a=rtcp-fb:98 goog-remb
a=rtcp-fb:98 transport-cc
a=rtcp-fb:98 ccm fir
a=rtcp-fb:98 nack
a=rtcp-fb:98 nack pli
a=fmtp:98 profile-id=0
a=rtpmap:99 rtx/90000
a=fmtp:99 apt=98
a=rtpmap:100 VP9/90000
a=rtcp-fb:100 goog-remb
a=rtcp-fb:100 transport-cc
a=rtcp-fb:100 ccm fir
a=rtcp-fb:100 nack
a=rtcp-fb:100 nack pli
a=fmtp:100 profile-id=2
a=rtpmap:101 rtx/90000
a=fmtp:101 apt=100
a=rtpmap:102 H264/90000
a=rtcp-fb:102 goog-remb
a=rtcp-fb:102 transport-cc
a=rtcp-fb:102 ccm fir
a=rtcp-fb:102 nack
a=rtcp-fb:102 nack pli
a=fmtp:102 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f
a=rtpmap:121 rtx/90000
a=fmtp:121 apt=102
a=rtpmap:127 H264/90000
a=rtcp-fb:127 goog-remb
a=rtcp-fb:127 transport-cc
a=rtcp-fb:127 ccm fir
a=rtcp-fb:127 nack
a=rtcp-fb:127 nack pli
a=fmtp:127 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42001f
a=rtpmap:120 rtx/90000
a=fmtp:120 apt=127
a=rtpmap:125 H264/90000
a=rtcp-fb:125 goog-remb
a=rtcp-fb:125 transport-cc
a=rtcp-fb:125 ccm fir
a=rtcp-fb:125 nack
a=rtcp-fb:125 nack pli
a=fmtp:125 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f
a=rtpmap:107 rtx/90000
a=fmtp:107 apt=125
a=rtpmap:108 H264/90000
a=rtcp-fb:108 goog-remb
a=rtcp-fb:108 transport-cc
a=rtcp-fb:108 ccm fir
a=rtcp-fb:108 nack
a=rtcp-fb:108 nack pli
a=fmtp:108 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f
a=rtpmap:109 rtx/90000
a=fmtp:109 apt=108
a=rtpmap:124 H264/90000
a=rtcp-fb:124 goog-remb
a=rtcp-fb:124 transport-cc
a=rtcp-fb:124 ccm fir
a=rtcp-fb:124 nack
a=rtcp-fb:124 nack pli
a=fmtp:124 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=4d001f
a=rtpmap:119 rtx/90000
a=fmtp:119 apt=124
a=rtpmap:123 H264/90000
a=rtcp-fb:123 goog-remb
a=rtcp-fb:123 transport-cc
a=rtcp-fb:123 ccm fir
a=rtcp-fb:123 nack
a=rtcp-fb:123 nack pli
a=fmtp:123 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=4d001f
a=rtpmap:117 rtx/90000
a=fmtp:117 apt=123
a=rtpmap:35 AV1/90000
a=rtcp-fb:35 goog-remb
a=rtcp-fb:35 transport-cc
a=rtcp-fb:35 ccm fir
a=rtcp-fb:35 nack
a=rtcp-fb:35 nack pli
a=rtpmap:36 rtx/90000
a=fmtp:36 apt=35
a=rtpmap:114 H264/90000
a=rtcp-fb:114 goog-remb
a=rtcp-fb:114 transport-cc
a=rtcp-fb:114 ccm fir
a=rtcp-fb:114 nack
a=rtcp-fb:114 nack pli
a=fmtp:114 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=64001f
a=rtpmap:115 rtx/90000
a=fmtp:115 apt=114
a=rtpmap:116 red/90000
a=rtpmap:62 rtx/90000
a=fmtp:62 apt=116
a=rtpmap:118 ulpfec/90000
a=ssrc-group:FID 84266309 3067595669
a=ssrc:84266309 cname:csbDbPUNcSfBoceC
a=ssrc:3067595669 cname:csbDbPUNcSfBoceC

SDP Plan B 和 Unified Plan

  • Plan B,仅仅支持一条音频 mline, 和一条视频 m line, 音频和视频的媒体流的标识(mid)分别被设置成 audio 和 video;如果同个媒体包括多个发送流,那么在 m line 下,可以列出多行 a=ssrc 属性。
  • Unified Plan, 一个 m line 表示一个发送或者接收流,每条 m line 都可以独立标识 mid; 如果存在多个流,那么可以创建出多个条 mline。

ICE FULL 和 ICE LITE

  • FULL 必须完整的实现连接检测,并从检测合格的 candidate pair list 中选举出一个用于媒体流的传输。

  • Lite 则是被动方,只需要响应 Response,同时 Full 会通知 Lite 端用使用哪个 candiate pai 进行媒体流的传输。

  • SDP 中指定 ICE 类型

    a=ice_mode:Full/Lite
    

session metadata

session metadata
v=0
o=- 6430937235515578749 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE 0 1
a=extmap-allow-mixed
a=msid-semantic: WMS dGBa2UextnqOyqBq7ReErAi6YWmljy3LhuAA
  • v=版本号
  • o=会话发起者信息
  • s=会话名称
  • t=会话起止时间
  • a=group:BUNDLE mid0 mid1 ... 复用传输层(五元组)的 mid
  • a=msid-semantic:WMS(WebRTC Media Stream(WMS))的 id

media section

media section
m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 100 101 102 121 127 120 125 107 108 109 124 119 123 117 35 36 114 115 116 62 118
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:Acgf
a=ice-pwd:7zfBgyFzPfptbrklnxbgjOkF
a=ice-options:trickle
a=fingerprint:sha-256 4A:43:A2:A8:3A:50:84:EF:C2:E7:8A:20:1A:35:07:7E:BE:03:24:E3:CD:7C:1E:4F:98:A8:00:44:5A:96:29:5C
a=setup:active
a=mid:1
a=extmap:14 urn:ietf:params:rtp-hdrext:toffset
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:13 urn:3gpp:video-orientation
a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/video-content-type
a=extmap:7 http://www.webrtc.org/experiments/rtp-hdrext/video-timing
a=extmap:8 http://www.webrtc.org/experiments/rtp-hdrext/color-space
a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid
a=extmap:10 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id
a=extmap:11 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id
a=sendrecv
a=msid:dGBa2UextnqOyqBq7ReErAi6YWmljy3LhuAA 20da8f42-dfb8-44fa-9c51-8e3832ed4a05
a=rtcp-mux
a=rtcp-rsize
a=rtpmap:96 VP8/90000
a=rtcp-fb:96 goog-remb
a=rtcp-fb:96 transport-cc
a=rtcp-fb:96 ccm fir
a=rtcp-fb:96 nack
a=rtcp-fb:96 nack pli
a=rtpmap:97 rtx/90000
a=fmtp:97 apt=96
a=rtpmap:98 VP9/90000
a=rtcp-fb:98 goog-remb
a=rtcp-fb:98 transport-cc
a=rtcp-fb:98 ccm fir
a=rtcp-fb:98 nack
a=rtcp-fb:98 nack pli
a=fmtp:98 profile-id=0
a=rtpmap:99 rtx/90000
a=fmtp:99 apt=98
a=rtpmap:100 VP9/90000
a=rtcp-fb:100 goog-remb
a=rtcp-fb:100 transport-cc
a=rtcp-fb:100 ccm fir
a=rtcp-fb:100 nack
a=rtcp-fb:100 nack pli
a=fmtp:100 profile-id=2
a=rtpmap:101 rtx/90000
a=fmtp:101 apt=100
a=rtpmap:102 H264/90000
a=rtcp-fb:102 goog-remb
a=rtcp-fb:102 transport-cc
a=rtcp-fb:102 ccm fir
a=rtcp-fb:102 nack
a=rtcp-fb:102 nack pli
a=fmtp:102 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f
a=rtpmap:121 rtx/90000
a=fmtp:121 apt=102
a=rtpmap:127 H264/90000
a=rtcp-fb:127 goog-remb
a=rtcp-fb:127 transport-cc
a=rtcp-fb:127 ccm fir
a=rtcp-fb:127 nack
a=rtcp-fb:127 nack pli
a=fmtp:127 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42001f
a=rtpmap:120 rtx/90000
a=fmtp:120 apt=127
a=rtpmap:125 H264/90000
a=rtcp-fb:125 goog-remb
a=rtcp-fb:125 transport-cc
a=rtcp-fb:125 ccm fir
a=rtcp-fb:125 nack
a=rtcp-fb:125 nack pli
a=fmtp:125 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f
a=rtpmap:107 rtx/90000
a=fmtp:107 apt=125
a=rtpmap:108 H264/90000
a=rtcp-fb:108 goog-remb
a=rtcp-fb:108 transport-cc
a=rtcp-fb:108 ccm fir
a=rtcp-fb:108 nack
a=rtcp-fb:108 nack pli
a=fmtp:108 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f
a=rtpmap:109 rtx/90000
a=fmtp:109 apt=108
a=rtpmap:124 H264/90000
a=rtcp-fb:124 goog-remb
a=rtcp-fb:124 transport-cc
a=rtcp-fb:124 ccm fir
a=rtcp-fb:124 nack
a=rtcp-fb:124 nack pli
a=fmtp:124 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=4d001f
a=rtpmap:119 rtx/90000
a=fmtp:119 apt=124
a=rtpmap:123 H264/90000
a=rtcp-fb:123 goog-remb
a=rtcp-fb:123 transport-cc
a=rtcp-fb:123 ccm fir
a=rtcp-fb:123 nack
a=rtcp-fb:123 nack pli
a=fmtp:123 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=4d001f
a=rtpmap:117 rtx/90000
a=fmtp:117 apt=123
a=rtpmap:35 AV1/90000
a=rtcp-fb:35 goog-remb
a=rtcp-fb:35 transport-cc
a=rtcp-fb:35 ccm fir
a=rtcp-fb:35 nack
a=rtcp-fb:35 nack pli
a=rtpmap:36 rtx/90000
a=fmtp:36 apt=35
a=rtpmap:114 H264/90000
a=rtcp-fb:114 goog-remb
a=rtcp-fb:114 transport-cc
a=rtcp-fb:114 ccm fir
a=rtcp-fb:114 nack
a=rtcp-fb:114 nack pli
a=fmtp:114 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=64001f
a=rtpmap:115 rtx/90000
a=fmtp:115 apt=114
a=rtpmap:116 red/90000
a=rtpmap:62 rtx/90000
a=fmtp:62 apt=116
a=rtpmap:118 ulpfec/90000
a=ssrc-group:FID 84266309 3067595669
a=ssrc:84266309 cname:csbDbPUNcSfBoceC
a=ssrc:3067595669 cname:csbDbPUNcSfBoceC
  • m=video/audio/application 可忽略端口 传输协议 数据格式列表
  • c=网络信息(WebRTC 使用 ICE 协议,所以这个信息无效)
  • a=rtcp:描述 RTCP 的网络信息,但是通常不是实际使用的 IP 和端口
  • a=ice-ufrag:ICE 连接用户名,匹配 ICE candidate 的 ufrag [xxxx]
  • a=ice-pwd:ICE 连接密码
  • a=ice-options:本端支持的 ICE 选项
  • a=fingerprint:DTLS 协商证书
  • a=setup:DTLS 协商时本端角色
    • active 发起端(client)
    • passive 接收端(server)
    • actpass 既可以是发起端也可以是接收端
  • a=mid:这个 media section 的 id,对应 BUNDLE 里面的标识
  • a=extmap:描述本端支持的 RTP extension header 的 id 和地址
  • a=媒体方向
    • sendrecv 同时收发
    • recvonly 只接收
    • sendonly 只发送
  • a=msid:媒体流 id 媒体轨 id
  • a=rtcp-mux 本端支持 RTP 和 RTCP 连接复用
  • a=rtcp-rsize 本端希望缩减 RTCP 协议所使用的流量
  • a=rtpmap:RTP 协议的 payload type 编码格式名称/音频采样率或视频时钟频率
  • a=fmtp:RTP 协议的 payload type 补充描述编码格式的参数
    • max-fr=最大帧率
    • profile-level-id=H264 的 profile level id
    • apt=关联的 payload type
  • a=rtpmap:RTP 协议的 payload typeRTCP 反馈机制
    • goog-remb Receiver Estimated Maximum Bitrate 接收端最大接收码率估测,使用 RTCP 反馈接收端的带宽估计信息
    • transport-cc 使用发送端拥塞控制机制
    • ccm fir 使用请求关键帧机制
    • nack 使用丢包通知机制
    • nack pli 使用图像丢失通知机制
  • a=ssrc-group:FID 数据源 重传流
  • a=ssrc:RTP 媒体流的一个数据源 数据源的属性
    • msid:、mslabel:、label:数据源关联的流 id 和轨 id

修改 SDP 的例子