

大家好,我是Twitch的視頻工程師,今晚我的演講主題是SRT協(xié)議的內(nèi)幕。在過去,我看過許多關(guān)于支持SRT功能的軟解的精彩演講以及它的各種潛能。但是今天,我將掀開幕布,看看SRT協(xié)議背后的東西。

此SRT(Secure Reliable Transport)非彼SRT(SubRip Subtitle:它是一種字幕格式),這個視頻傳輸協(xié)議可以在具有挑戰(zhàn)性的網(wǎng)絡(luò)之下進(jìn)行直播。它基于UDP的單播(一對一的形式),注重contribution而不是delivery。此外,它也帶來亞秒可調(diào)的constant latency。
這么說吧,可調(diào)(tunable)意味著你可以配置協(xié)議并調(diào)整延遲,可以在數(shù)據(jù)包的丟失與延遲中做出權(quán)衡(trade-off)。一旦開始廣播的時候,延遲即被鎖定,所以不會因為不同的網(wǎng)絡(luò)的情況而累積更多的延遲,同時,該系統(tǒng)也提供content encryption。
這么說吧,可調(diào)(tunable)意味著你可以配置協(xié)議并調(diào)整延遲,可以在數(shù)據(jù)包的丟失與延遲中做出權(quán)衡(trade-off)。一旦開始廣播的時候,延遲即被鎖定,所以不會因為不同的網(wǎng)絡(luò)的情況而累積更多的延遲,同時,該系統(tǒng)也提供content encryption。

為什么我覺得SRT有趣?我們知道RTMP是公共互聯(lián)網(wǎng)上直播視頻的事實標(biāo)準(zhǔn);但RTMP已經(jīng)存在了很長一段時間,其標(biāo)準(zhǔn)在2012年最后一次更新過后就被放棄了。新的Codec標(biāo)準(zhǔn)諸如HEVC或AV1一般都沒有RTMP標(biāo)準(zhǔn)支持。退一步來說,即使有人在RTMP中hack了這些Codec的支持,在移動網(wǎng)絡(luò)上RTMP仍然工作的不大好。
SRT作為RTMP潛在替換技術(shù)的一種,最近正獲得不錯的增長勢頭。SRT聯(lián)盟現(xiàn)在有250多名成員,而在最近的一些展會上,似乎每個展位都具有 SRT 聯(lián)盟成員或 SRT-Ready貼紙。

SRT功能在VLC,Gstreamer和Ffmpeg中基本開箱即用,對于 OBS Studio 等工具則有些patches正在流程中。SRT 的源于一個稱為 UDT 的舊協(xié)議。UDT在2001年創(chuàng)建,仍然在Source Forge上有網(wǎng)頁,但UDT的設(shè)計目標(biāo)是在公共網(wǎng)絡(luò)上以最短時間傳輸大型的文件。

UDT開發(fā)者向IETF提交過幾份草案去描述UDT工作原理?偣灿兴姆莶莅福罱K的IETF草案是在2010年發(fā)布的。之后,UDT的主要開發(fā)者繼續(xù)在此協(xié)議工作了3年,其實現(xiàn)的最終版本停留在了2013年。

Haivision,一家編碼器供應(yīng)商,采用UDT且將它由file protocol變成一個live video協(xié)議。在2013年,他們首次在 IBC大會上使用了UDT,主要是為了演示HEVC的編碼器。

過了四年,他們覺得自己的自定義協(xié)議可能不是創(chuàng)建interoperable ecosystem的最好方式。因此在2017年,他們開源了SRT。

Haivision 與Wowza 聯(lián)合創(chuàng)建了SRT聯(lián)盟,以促進(jìn)發(fā)展及開發(fā)SRT。

2018年初,他們發(fā)布了SRT的v1.3.0更新版本。這是自最初的開源以來,對protocol最大型的修改。同時,其版權(quán)協(xié)議改成了MPL(Mozilla public license);重新把文件傳輸模式加了回來。

在2018年,他們也發(fā)布了SRT Technical Overview(SRT技術(shù)概述),但實際上更像規(guī)范(specification)。該文檔共有89頁,與此對應(yīng)的RTMP Spec則是52頁。該文檔描述了各種細(xì)節(jié)信息,即使是一些競爭對手也承認(rèn)SRT規(guī)范做得非常不錯。

從高層看,SRT使用的一個雙向UDP socket,它可以通過同一個socket復(fù)用數(shù)據(jù)和控制流。因為沒有使用TCP,SRT自行實現(xiàn)了可靠性、有序性和擁塞控制。SRT使用 selective retransmit的方式處理數(shù)據(jù)包丟失,另外了基于標(biāo)準(zhǔn)加密原語(standard primitives)(而不是DTLS)構(gòu)建了其獨特的 “unique”加密系統(tǒng),

SRT Data Payload支持可分片的有效負(fù)載(payload)。在實踐中,我僅僅看過它與MPEG transport stream一起使用。整個傳輸流引入SRT包,每個傳輸流包都有自己的同步字節(jié)和傳輸流頭。我確信這些sync byte 用以對抗丟包以及重新同步。
在1500-byte Ethernet MTU情況下,如果你試圖放入188-byte的數(shù)據(jù)包,會發(fā)現(xiàn)并沒有足夠的空間可以容納8個TS包,這也是使用7個TS包的原因。與此同時,這也可以給SRT Header留出足夠的空間。

上圖概述了SRT數(shù)據(jù)包的布局。初起是UDP header, 還有UDT header,實際上SRT header改自UDT header。

第一bit是0,表示的是數(shù)據(jù)包,之后是packet sequence number,它是從握手過程中確定的random value開始的,隨后每一個packet值都會增加1。該值用于標(biāo)識數(shù)據(jù)包、packet acknowledgement和數(shù)據(jù)包丟失消息。有個message number, message number從0算起,會在我的視頻中每增加一條消息時候加一,但看起來沒什么用,怎么回事呢?
我們可以看見一個微秒單位的時間戳,這是發(fā)送端的運行時間(elapsed time)。在接收端,它將這個packet從SRT的緩沖區(qū)中播放到下游的TST MUX RN 視頻解碼器中。這個實時視頻的片段與順序總會是“1 1 0”。1 1 指的的是獨立編號,而消息編號是針對跨多個數(shù)據(jù)包分段的信息。但在實時視頻模式下,我們只需要盡可能多地填充TS,我們稱之為standalone message。
Ordering flag下的兩個標(biāo)志是encryption和retransmission flags;如果出現(xiàn)了什么問題,ordering flag可以將其信息無序交付。Encryption flag會提醒你正使用的key,而retransmit flag會告訴你這是否是第一次發(fā)送還是一個重復(fù)的步驟。Retransmitted packets 通常會保留原始序列號,原始信息號和原始時間戳。

SRT的核心理念是發(fā)送方和接收方都同意延遲緩沖時間,并且他們試圖在數(shù)據(jù)包開始流出接收方時同步其內(nèi)容。目前VLC支持現(xiàn)成的SRT,OBS也有了SRT的patch,發(fā)送方所創(chuàng)建的數(shù)據(jù)包,同時會將其放在延遲緩沖區(qū),因為在網(wǎng)絡(luò)中,該包到達(dá)接收方需要一段時間。

發(fā)送方不斷生成數(shù)據(jù)包,接收方最終獲得數(shù)據(jù)包。發(fā)送方再不斷地生成,接收方也繼續(xù)接收。如此往復(fù)。

這里展示了一個不妙的情況,上圖的packet 3已被丟失,到目前為止,沒人發(fā)現(xiàn)了數(shù)據(jù)的差錯。本來期待packet 3,竟然收取了packet 4,它隨即生成negative acknowledgement packet,將packet 4 放入緩沖區(qū),為packet 3 留下一個空位(hole)。
發(fā)送方繼續(xù)分發(fā)packet,直到下一個packet送達(dá)。突然間sender得到NACK,它發(fā)現(xiàn)接受者丟失了packet 3,因為它把之前發(fā)送的數(shù)據(jù)包都保存在了buffer,結(jié)果,發(fā)送方重新發(fā)送數(shù)據(jù)包3,它繼續(xù)生成數(shù)據(jù)包;接收方繼續(xù)接收數(shù)據(jù)包。注意,此時發(fā)送者的buffer現(xiàn)在已裝滿了數(shù)據(jù)包。

Packet 1被吐出,直接被丟掉。接收端終于收取到packet 3,它馬上填補之前的空洞(hole),操作恢復(fù)正常。接收端buffer最終填滿了指定的packet,隨后把packet 1給了 TS deuxers 和解碼器。

這是協(xié)議概述中Nak的表示圖。首先有SRT header,因為它是一個控制包所以1開頭,最后是丟失包的列表。
每一個丟失list包含一個或多個條目。每個條目要么是一個single packet,要么是一個范圍(range),你可在一條消息內(nèi)說丟失了packet 5、9以及11直到15的所有包。

除了negative acknowledgment,SRT也有positive acknowledgment。接收器每10毫秒發(fā)送一次acknowledgment。每一次收到 ACK,發(fā)送者立即確認(rèn)ACK以響應(yīng)之前的ACK,你可以據(jù)此估算往返時間。如果確認(rèn)之間的數(shù)據(jù)速率超過64個數(shù)據(jù)包,則接收器將發(fā)送lightweight acknowledgement。此Ack不會被重新確認(rèn),也不包含Ack所接受的元數(shù)據(jù)類型。

RTT有點不尋常,因為似乎沒有辦法在不啟動新廣播的情況下調(diào)整延遲緩沖區(qū)的大小,所以對于廣播場景有些限制。

以上是acknowledgement packet所顯示的Ack/AckAck包。最重要的是最后接收到的packet包序列號,當(dāng)然還有的是往返時間,往返時間的方差,緩沖統(tǒng)計available size和packets,數(shù)據(jù)包接受速率及數(shù)據(jù)接收速率。在lightweight Ack,你只需得到序列號,Ack的acknowledgment將會顯示它正在確認(rèn)的Ack,以便于你知道在正確的時間戳做出扣除。

我們都知道握手(handshake)非常簡單。在這個GIF中,Bill Maher 不斷地誤讀Snoop Dogg (史努比。狗狗)的手勢,Snoop Dogg也不斷地嘗試配合Bill,這場景是body language misinterpretation的一個例子,我認(rèn)為它適合隱喻SRT handshake的過程。

SRT在sender和receiver之間有四次handshakes(因為后向兼容,所以所有版本都支持)。V4 和V5的rendezvous handshake (匯合握手)比較特殊,不在這次講解。
V5 以及v4最大的區(qū)別在于數(shù)據(jù)包交換的數(shù)量。v4共有四次往返;在v5只有兩次往返。

圖中是packets的布局,其核心思想是左邊的v4使用了未修改的UDT包加上SRT擴展,接著是一個包含所需延遲和初始序列號的SRT握手包,其后的密鑰素材用于對于數(shù)據(jù)有效載荷進(jìn)行加密,右邊的v5則更將這些信息smush (合拼) 到一個包中(指V5直接修改了原始UDT包的布局)。

之后,我們將看到兩方嘗試v5握手,發(fā)起方創(chuàng)建handshake induction packet (握手感應(yīng)包)。

其版本號設(shè)置為4,但cookie字段并未設(shè)置,它將提示初始端在短時間內(nèi)獲得cookie,使得響應(yīng)端不必處理混亂的數(shù)據(jù)包,而是需要解析其數(shù)據(jù)包以將某些內(nèi)容發(fā)送回去;實際上,響應(yīng)端接收到該包之后,創(chuàng)建一個版本5的induction packet,并設(shè)置Cookie。

此時,v4 initiator將忽略v5,并繼續(xù)填充v4以及重復(fù)改cookie。但是,如果initiator是通過v5運行,所以它會在version字段中填寫 v5程序,加上SRT handshake extension values包括延遲值等。
Initiator可能在握手的第二個包產(chǎn)生stream ID,首先填充加密握手,然后responder會使用latency value 和cyypto handshake進(jìn)行響應(yīng)。

在于Stream ID,這是在handshake的第二個包中所發(fā)送的可選標(biāo)識符,因為第一個包是有可能被拋棄掉的。在此會有個application-specific parameter,用于通知你initiator想干什么。
這與RTMP形成一些對比,在RTMP中,你執(zhí)行TCP握手和RTMP握手。然后,執(zhí)行帶寬估計之后調(diào)用一組RPCs來設(shè)置RTMP媒體流。

我之前提到過SRT不使用DTLS。它以自定義方式使用industry standard primitives,可看見它受到到了DVB scrambling的重大影響。DVB是歐洲廣播標(biāo)準(zhǔn),keying material是由共享密碼短語生成。隨著這些retransmits和密鑰旋轉(zhuǎn),一次有兩個密鑰有效。你有一個偶數(shù)鍵和一個奇數(shù)鍵,在這兩個鍵中你交替使用哪一個,你就在更新。如果你得到重發(fā),你可以參考舊的密鑰。

規(guī)范中的小注釋說:‘嘿,全數(shù)據(jù)包加密看起來是最安全的選擇,但實際上,加密header在暴力破解時候卻有點脆弱。最初的MPEG TS 同步字節(jié),其設(shè)計可能是不讓你把TS頭加密。事實上,我們會嘗試使用快速的key rotation來獲得更高的加密強度。

你可以使用Wireshark 來分析包,我們會有個加密數(shù)據(jù)包,有效載荷的第一個字節(jié)是12(十六進(jìn)制)。你可能已知道如果是一個未加密的TS 同步字節(jié),那它將是47(十六進(jìn)制)。

如果想進(jìn)一步了解其中的協(xié)議,你可以前往SRT GitHub repository,以及technical overview Wireshark的SRT解析器。

如果你想了解更多關(guān)于SRT生態(tài)系統(tǒng),或者有關(guān)于SRT的產(chǎn)品或信息請前往srtalliance.org。