[時間:2018-03] [狀態:Open]
[關鍵詞:流媒體,stream,HLS]
本文是上一篇的后續部分,鏈接如下:HLS協議綜述
2 playlist(m3u8)介紹
HLS中的playlist是一個UTF-8編碼的文本文件,其中包含了URL和描述性標簽。一個常規的playlist如下所示:
#EXT-X-VERSION:3
#EXTM3U
#EXT-X-TARGETDURATION:10
#EXT-X-MEDIA-SEQUENCE:1
# Old-style integer duration; avoid for newer clients.
#EXTINF:10,
http://media.example.com/segment0.ts
# New-style floating-point duration; use for modern clients.
#EXTINF:10.0,
http://media.example.com/segment1.ts
#EXTINF:9.5,
http://media.example.com/segment2.ts
#EXT-X-ENDLIST
其中以'#'打頭的行都是標簽,HLS標准規定對於標准中未定義的標簽,可以直接忽略;也就是說'#'也可以作為注釋行。這里說明下上面M3U8文件的構成:
#EXT-X-VERSION:<n>
表示協議的版本號,而且每個M3U8中只能出現一次該標簽。對於具體版本號的定義,可以參考標准的第7節。#EXTM3U
作為M3U文件的標識符,可以用於文件類型識別,這是必須的字段。#EXT-X-TARGETDURATION:<s>
表示最長分片的時長,這是必須的字段。#EXT-X-MEDIA-SEQUENCE:<number>
表示playlist文件中第一個分片的序列號(整數值)。如果M3U8文件中沒有該字段,則playlist中第一個分片的序列號必須是0。#EXTINF:<duration>,[<title>]
表示下一個分片的時長。對於每個分片,必須有該字段。 對於#EXT-X-VERSION
小於3的情況下,duration必須是整數;其他情況下duration可以是浮點數和整數。title是一個可選字段,僅用於增強可讀性。#EXT-X-ENDLIST
該字段表示分片結束,不會在playlist文件中添加新的分片。
上面介紹的是最常見的playlist,還有一種playlist,僅包含播放節目列表信息,在HLS中稱為master playlist。其示例如下:
#EXTM3U
#EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=1280000,AVERAGE-BANDWIDTH=1000000, RESOLUTION=720x480
http://example.com/low.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=2, BANDWIDTH=2560000,AVERAGE-BANDWIDTH=2000000, RESOLUTION=1080x720
http://example.com/mid.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=3, BANDWIDTH=7680000,AVERAGE-BANDWIDTH=6000000, RESOLUTION=1920x1080
http://example.com/high.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=4, BANDWIDTH=65000,CODECS="mp4a.40.5"
http://example.com/audio-only.m3u8
其中包含的標簽說明如下:
#EXT-X-STREAM-INF:<attribute-list>
用於標識一個Variant Stream,這是由一系列的Redition組成的。該標簽的屬性列表中包含了Variant Stream的描述信息。例如:
BANDWIDTH
表示Variant Stream中的峰值比特率,單位bits/s。AVERAGE-BANDWIDTH
表示Variant Stream中的平均比特率,單位bits/s。CODECS
包含Variant Stream中音視頻編碼格式相關的信息,比如上面的"mp4a.40.5"。RESOLUTION
包含Variant Stream中對應視頻流的分辨率。FRAME-RATE
表示Variant Stream中的視頻幀率。
M3U8中還有一個標簽需要關注下,EXT-X-PLAYLIST-TYPE
。該標簽只有兩個值:EVENT、VOD。EVENT指的是分片工具只能在M3U8末尾添加新的分片的信息,但不能刪除老的分片,通常比較適用於直播+錄播的情況(既要提供給客戶端點播功能,也要對實時場景進行錄制,直播完成之后EVENT就自然退化為VOD)。但是對於M3U8中存在#EXT-X-ENDLIST
標簽時,可以忽略EXT-X-PLAYLIST-TYPE
。
還有一種情況,如果M3U8中不存在#EXT-X-ENDLIST
以及EXT-X-PLAYLIST-TYPE
標簽,則服務器端可以任意更新playlist內容。
到此我們基本介紹了完了M3U8的格式。
3 服務器端和客戶端的主要實現邏輯
HLS之服務器端
在第一部分介紹過,HLS的服務器端包括三個部分:轉碼器、分片器、分發端。對於VOD而言,這三個部分是可以獨立工作的,並不需要太多的配合。但對於直播而言,三者需要密切配合,以保證直播的實時性和流暢度。典型的服務器端邏輯是這樣的:
- 將原始音視頻數據或者多媒體素材通過轉碼器編碼復用;
- 將復用后的文件切片,並生成對應的M3U8(注意切片應該符合HLS規范);
- 為每個切片生成對應的URL,並更新M3U8文件;
- 直播需要實時更新Playlist對應的M3U8,點播生成完成之后服務器端不得修改Playlist
HLS之客戶端
從邏輯上來講,HLS客戶端更為簡單。其典型處理邏輯如下:
- 通過給定URI獲取 Playlist。若是Master Playlist,客戶端需選擇一個Variant Stream來播放。
- 客戶端檢查#EXT-X-VERSION版本是否滿足。
- 客戶端應該忽略不可識別的 tags,忽略不可識別的屬性鍵值對。
- 加載Media Playlist file,並選擇一個segment開始播放;
- 播放完成一個segment之后,根據客戶端當前的具體情況選擇一個新的segment,並重復執行播放操作;
- 對於直播,需要定期刷新Media Playlist file,並選擇合適的segment播放。
4 碼率切換的基本邏輯
從HLS協議的定義來看,所有的碼率切換策略都是有客戶端定義的。服務器端僅僅提供幾種可供選擇的碼率。所以主要的碼率切換邏輯也是在客戶端完成。
客戶端判斷是否切換的主要因素如下:
- 設備實際下載速度
- 設備運行情況(CPU、內存、以及屏幕分辨率)
比較簡單的客戶端會直接根據實際下載速度和Variant Stream標稱的碼率做比較,如果滿足就上切,不滿足就下切。
當然在實際切換時還需要考慮播放器的用戶體驗,最好做到平滑切換,用戶沒有直接的可見的播放卡頓。
5 常見的開源HLS框架
到目前為止,我所遇到的HLS播放框架主要有:ffmpeg中的HLS模塊、AOSP中的HLS模塊。當然在Mac或iOS中原生就支持HLS,不過沒有源碼。
6 總結
后面的文章將中主要關注ffmpeg和AOSP中的HLS相關的實現。同時會給出一個基於ffmpeg搭建的HLS直播系統(本地文件模擬的直播源)。
本文主要簡單總結了HLS協議相關的基礎知識,以及HLS服務器端和客戶端的常規實現邏輯。僅供后續參考及查閱。