Apple 低延迟HLS分析
HLS協議
HTTP Live Streaming(HLS)是Apple公司主導提出并實現的基于HTTP的自適應碼率流媒體通信協議(RFC8216),作為其產品QuickTime,Safari,OS X和iOS的一部分,在Apple的產品生態鏈中占有重要地位。同時,越來越多的第三方廠商的產品,如Microsoft Edge,Firefox和 Google Chrome、安卓操作系統也都實現了對HLS的支持。而且有大量的流媒體服務器都支持HLS。
HLS類似于MPEG-DASH,通過將理論上可以無限時長的直播流分解為一系列基于HTTP的小文件下載來完成流媒體的傳輸,每次下載獲得整個流的一小部分。M3U8播放列表里也可以包含不同碼率的節目流列表。
因為HLS基于標準HTTP協議,所以它可以穿透任何允許通過標準HTTP流量的防火墻或代理服務器,這一點比基于UDP的協議(比如HLS出現之前常用的RTP)優越。這也允許它基于常規 HTTP服務器提供內容,并通過HTTP CDN進行內容分發,這一點對于流媒體廠商和CDN廠商來說是一個非常大的便利,可以復用大量現成的早就非常成熟的針對HTTP的解決方案。基于HTTP還讓客戶端的實現變得比較簡單,復用已有代碼按playlist文件的指示順序下載流媒體片段即可。該標準還包括標準加密機制和基于HTTPS的安全密鑰分發機制,它們共同提供了一個簡單的DRM系統。該協議還提供快進和快退以及字幕集成等功能。
HLS的方案同時兼容了直播和點播業務,并且很好地利用了已有的基礎設施,在它提出的那個時代,著實讓大家眼前一亮。
Apple的HLS技術lead Roger Pantos從2009年5月1日起開始將HLS提交為IETF草案,經過7個版本24次草案修改,直到8年后的2017年8月,最終成為RFC 8216。
低延遲HLS技術草案
2019年的WWDC上,Pantos宣布了最新的HLS草案,今年的變化旨在減少實時視頻流的延遲。這個消息一出,業界反響很大,幾家歡樂幾家愁。高興的是終于蘋果正視這個被廣為詬病的問題,開始從協議層面開始提出解決方案;也有一些廠商高興不起來,因為他們已經在HLS的基礎上實施了一些自己的低延遲改進方案,蘋果方案的提出,一定程度上導致他們過去的研究和投資打了水漂,蘋果這次提出的新方案技術上需要比較復雜的技術棧來支持,也導致不少廠商頗有微詞。
為了搞清延遲問題的來龍去脈,首先我們看看HLS的基本內容:
簡單來說,HLS包含兩部分,m3u8文件(playlist)和承載具體媒體內容的文件(ts、CMAF、fMP4等),客戶端根據m3u8的指示下載媒體內容并定時刷新m3u8文件獲得最新內容列表。
以下是一個包含多種碼率的master playlist(如果沒有多個碼率,這個m3u8 playlist可以沒有):
以下是其中一個playlist的m3u8內容(如果只有一個碼率,可以只提供這個m3u8):
以上出現的tag描述如下,還有很多tag,具體可以參考RFC:
EXT-X-TARGETDURATION通常就是GOP(Group Of Picture)的大小,如果EXT-X-TARGETDURATION是6秒,那么需要6秒編碼完成一個片段并更新playlist,客戶端采用輪詢方案來獲取下一版playlist, 輪詢的時機在(6,12)秒區間內,都將獲得僅包含第一份片段的playlist,并且playlist的請求和響應本身需要一個RTT來回傳,在移動網絡上,這個可能增加數百毫秒的延遲,之后才是片段的請求,擁有足夠多數據以后,播放器才能開始播放(有的播放器要緩存2-3個片段才開始播放,也就是延遲可能高達18秒以上)。
CDN的緩存機制也會導致延時加長:
如下圖所示,源站已經前進到第四個片段,但是CDN邊緣節點還緩存著上一個版本(只包含3個片段)的playlist,必須等文件的TTL過期邊緣節點才會去獲取最新版本的列表,所以最差情況下又要多等待一個TTL。而這個緩存TTL也不能取消,如果每個端上的請求到達CDN邊緣節點時都去找源站要最新版本,源站就可能會被流量沖垮
為了將10-30的延遲降低到2秒以下,蘋果提出了5點改進
減少片段發布延遲
優化片段發現機制
消除片段請求時間
m3u8采用增量升級機制
加速不同碼率直播流切換速度
下面針對每個改進做一個介紹
減少片段發布延遲
為了減少發布延遲,引入了EXT-X-PART和EXT-X-PART-INF tag,示例m3u8如下所示,也就是在片段還沒有最終成型的時候就將片段的part(類似CMAF的chunk)先發布出來,EXT-X-PART-INF包含了片段part的最長時長,服務器必須每隔EXT-X-PART-INF時長就發布一個片段part。同時part必須是“完整的”part才能發布,也就是說不能再阻塞客戶端、占用CDN額外時間等待片段part生成。這個片段part的格式和CMAF也是兼容的。
當part足夠多,能夠拼成一個完整的片段時候,EXT-X-PART內容就從m3u8中消失,合并成一個傳統的EXTINF項。也就是說part只用于描述直播的“最前沿”(Live edge),當part數據不再是最前沿的直播內容時就被合并刪除。
優化片段發現機制
優化片段發現機制采用的方法是阻塞式m3u8加載,草案里增加了EXT-X-SERVER-CONTROL來告知客戶端服務端支持的低延遲功能特性,包括支持阻塞式m3u8加載機制和后面要說的m3u8增量更新機制。每個低延遲m3u8都必須帶上這個tag,并且內容應該一樣。CAN-BLOCK-RELOAD=YES就是告知客戶端服務器支持阻塞式m3u8加載機制。客戶端看見之后就可以直接開始發起下一個片段的請求,具體過程是這樣:
m3u8里面包含EXT-X-MEDIA-SEQUENCE,客戶端可以根據收到的片段數和EXT-X-MEDIA-SEQUENCE基數,計算出下一個片段的序列號,然后直接找服務端請求對應的m3u8:
?GET?https://example.com/live.m3u8?_HLS_msn=1803
上述請求表示當直播流中出現1803的ts的時候,停止阻塞,返回m3u8內容。
結合1的內容,允許服務端下發片段part的話,則請求如下:
上述請求表示當直播流中出現1803的第一部分(_HLS_part=0)的時候就停止阻塞,返回m3u8內容。
消除片段請求時間
上述請求的最后一部分——_HLS_push比較微妙,也是這次HLS協議升級的一個很大的改變,要求服務器支持HTTP/2,請求playlist的時候就直接將片段/part的內容跟隨push下來,減少一個RTT延遲。
HTTP/2是2015年由RFC7540標準化的協議,已經被大量廠商接受,比如天貓、淘寶、騰訊、新浪等。其中相比HTTP/1.1很重要的改進就是連接多路復用和push,服務端可以根據需要,不需要客戶端發起請求就主動將客戶端必須的內容通過同一個連接提前推送給客戶端,減少重新握手、請求的幾個RTT延遲。
經過上述三點改進后,可以看到相比之前的舊版HLS方案,現在可以在很低的延遲下就獲得首幀數據開始解碼播放,圖上示例的part時長是1秒,網絡傳輸0.5秒的話,客戶端觀察到的延遲可以低到1.5秒左右,part長度可以進一步縮小,比如0.2秒,以獲得更低的延遲。
m3u8采用增量升級機制
因為m3u8的請求可能高達每秒鐘3-4次,為了進一步減少網絡傳輸數據大小,蘋果引入了增量更新機制,
其中EXT-X-SERVER-CONTROL: CAN-SKIP-UNTIL=36.0告訴客戶端,比當前直播前沿數據早36秒以上的數據會被忽略 。這個數值要求是EXT-X-TARGETDURATION的6倍以上。客戶端就可以通過請求的參數_HLS_skip=YES告訴服務端下發增量更新內容。
這個功能在一些場合比較有用,有些直播流允許用戶往前回看一段時間,所以它們的m3u8文件會很大,上百K都有可能。使用增量更新機制能極大減小傳輸量。
加速不同碼率直播流切換速度
最后一個,加速不同碼率直播流切換速度的實現方案是在m3u8的最后帶上EXT-X-RENDITION-REPORT,告訴客戶端其它碼率直播流的當前進展(片段序號和part序號)和加載地址。
當客戶端決定要切換到另一個直播流上的時候,它不用發起一個新的請求,只要直接在原來的連接上請求即可(不過在蘋果的參考實現上并沒有實現這部分內容)
以上基本上就是這次蘋果對低延遲HLS提出的技術草案,蘋果也提供了參考實現用于測試和演示。不過從我的測試來看,iOS13 beta版里帶的AVPlayer實現并沒有完整實現低延遲HLS,確實只是個“參考”。
低延遲HLS demo
為了讓參考實現跑起來,需要架設一個支持http2、https、php的服務器,首先嘗試了MAMP最新版帶的apache,發現缺少http2模塊,需要自己編譯一個apache,感覺比較麻煩,好在MAMP還支持nginx,將web server切換到nginx,將nginx.conf里的HTTPS Server配置前面的注釋全部刪除,將”listen 443 ssl;” 一行改為”listen 443 ssl http2 default_server;”, 配置好https證書,啟用php支持,web server就準備好了。按照蘋果的demo說明配置好推流服務、切片服務,找個目錄放置master.m3u8, lowLatencyHLS.php, 一切就準備就緒了。
使用iOS13 beta里的Safari訪問master.m3u8,視頻播放正常,不過延遲似乎沒有達到預期的2秒以內,還是在8-15秒的范圍。用Charles抓包查看協議運行情況,如下幾張截圖所示,發現確實是基于HTTP/2協議,并且也確實請求了EXT-X-PART里指定的片段part文件,但是并沒有上述5點改進中的后4點相關的內容,對服務端的lowLatencyHLS.php的訪問并沒有帶上必要的參數,請求頻率也只有4秒一次(按照EXT-X-TARGETDURATION的設置)。之后又按照蘋果的要求自己寫了一個基于AVPlayer的demo app,配置好了必要的provision profile和entitlements,但結果也是一樣。初步分析認為iOS13 beta里Apple還沒有完全實現低延遲HLS的客戶端功能。
分析總結
demo告一段落,評估一下要想應用到實際生產環境中的成本,發現還有不少注意點和難點:
源站要提供HTTP / 2支持,因為低延遲HLS依賴多個HTTP / 2特性:多流控制,H2推送和H2 Ping。服務器必須支持H2優先級控制(依賴性和權重)。建議使用TCP,蘋果不承諾在第一個版本中支持QUIC。每個服務器必須在主播放列表中提供所有的碼率層級,這樣才可以快速進行碼率切換而無需重新建立連接。
不僅源站需要HTTP/2支持,CDN也需要支持HTTP/2。
上面說過客戶端會發起阻塞式請求來獲取還沒有生成的播放列表,而真正的部署環境顯然必須有CDN,加上CDN后,就要求CDN支持將多個相同的客戶端請求聚合成一個請求發送給后端源站,也就是新的請求到來的時候,判斷是否有相同的請求正在請求源站,如果有就等待在同樣的請求隊列上而不是發起新請求,數據回來之后再即時下發給客戶端。否則按照CDN的缺省行為,不存在的內容直接轉去請求源站的話,源站會迅速被打垮。
AVPlayer的實現發現服務端對低延遲HLS支持不好的話,會自動切換回標準的HLS,讓視頻繼續正常播放,所以測試低延遲HLS的時候只看視頻是否能播放還不行,要抓包分析,確認低延遲HLS機制正常工作。
播放列表請求必須是冪等的。服務器應支持TLS 1.3或更高版本以減少連接時間。服務器還應支持媒體播放列表和媒體段的TLS 1.3 0-RTT連接。
播放列表本身必須采用gzip格式。這能加快媒體播放列表的重新加載和播放切換速度。
不同碼率的直播流必須同步更新,誤差在1個part時長內。
阻塞式請求實現時要注意超過3倍片段時長后還沒有片段/part數據生成的話,要報503錯。
為了讓TCP性能最大化,從客戶端到服務器整個鏈路上的所有設備的TCP協議棧要支持選擇性確認(SACK, RFC 2018、2883 和 3517);支持擁塞期間設置顯式擁塞通知(ECN,RFC 3168),并使用TCP時間戳;支持尾部丟失探測(TLP Tail Loss Probe)和TCP RACK等高性能TCP選項。
如果采用多家CDN,則需要協調多家CDN都對上述技術細節進行一致的支持。
蘋果推出的低延遲HLS整套方案從現在來看實現成本還比較高,相對比較激進,有沒有相對比較廉價的技術方案呢?其實早在2017年,Periscope就提出過一個沒那么激進的LHLS方案,從實測數據來看也已經比較優異。
它的方案的主要技術點是基于HTTP/1.1的分塊傳輸編碼Chunked Transfer Coding機制(RFC 2616),分塊傳輸編碼主要用于發送未知長度、客戶端請求時還未完全準備好的數據,用一個HTTP 響應數據簡單說明如下:
上面這個過程可以看出,分塊傳輸編碼天生適合用于傳輸“還未到來的”HLS片段數據。Periscope的方案對標準HLS做的核心變化是提前幾個片段時長就將片段網址添加到播放列表中。舉例來說,當直播流正在啟動并且流的第一幀從推流端到達服務器時,服務器將立即發布包含三個(數量可配置)片段的HLS媒體播放列表。當客戶端收到播放列表時,它們會請求全部三個片段。服務器使用分塊傳輸編碼來響應每個請求。對于第一段的請求將首先獲得在請求到達時在該段中累積的數據,但是之后的數據(在該段的剩余持續時間內)將在真正到達時候才傳輸給客戶端 。同時,對第二段的請求最初僅接收一些MPEG傳輸流(TS)段報頭,然后在第一段完成前不接收任何內容,第一段完成后才開始在這個連接上實時傳輸數據。類似地,對第三段的請求僅接收TS報頭數據,直到第二段完成才開始接收數據。在播放列表可用之前就廣播片段的好處是它消除了由于客戶端播放列表輪詢頻率和CDN高速緩存中的播放列表TTL而導致的播放列表延遲問題。由于片段在它們實際包含媒體之前幾秒鐘被廣播,如果播放列表可以被CDN聚合請求,就不會影響延遲。客戶端會提前幾秒鐘獲知即將到來的片段并請求它們,這樣就可以在服務器獲得數據后立即接收數據。
從Periscope的實踐來看LHLS這個機制運行得非常順利,CDN網絡對持續數秒鐘的分塊傳輸編碼的HTTP請求支持得非常好,并且聚合請求也都能正常工作;有一個小特性需要在播放器端進行特殊處理,就是不連續(EXT-X-DISCONTINUITY)標記,Periscope的方案是直接讓播放器忽略不連續標記,僅根據時間戳和ES流中的SPS來控制播放行為,不過理論上直播流也不太可能出現這個標記。另外一個比較大的問題是采用分塊傳輸編碼后無法很方便地推算出用戶的瞬時網速,不方便在多個不同碼率的直播流中進行切換,這個只能想辦法通過其它方式來計算瞬時網速。
Periscope的方案確實是一個物美價廉的方案,根據他們給出的圖表來看,效果確實很明顯,數據傳輸變得非常平滑,延遲也得到明顯降低。
綜合來看,如果想短期內實現低延遲HLS,Periscope的LHLS方案應該是一個比較好的選擇,而蘋果則選擇了一條相對艱難、成本高昂的道路,在蘋果的方案沒有完全成型之前自己去實現難度很大,但是蘋果的業界影響力也不容小覷,之前就有強力推動IPV6、HTTPS的先例,相信假以時日,Apple低延遲HLS也會成為業界標配。
參考文檔:
https://en.wikipedia.org/wiki/HTTP_Live_Streaming
https://segmentfault.com/a/1190000008810572
https://developer.apple.com/documentation/http_live_streaming/protocol_extension_for_low-latency_hls_preliminary_specification
https://developer.apple.com/videos/play/wwdc2019/502/
https://tools.ietf.org/html/rfc7323
https://tools.ietf.org/html/rfc2018
https://tools.ietf.org/html/rfc2883
https://tools.ietf.org/html/rfc3517
https://tools.ietf.org/html/rfc3168
https://tools.ietf.org/html/rfc8216
https://tools.ietf.org/html/rfc7540
https://tools.ietf.org/html/rfc2616
https://medium.com/@periscopecode/introducing-lhls-media-streaming-eb6212948bef
總結
以上是生活随笔為你收集整理的Apple 低延迟HLS分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 快手于冰:跟最优秀的人一起追求极致
- 下一篇: 蔡砚刚:uAVS3对标x265 very