# HLS 協議詳解

HLS 協議詳解

HLS 概述

HLS 全稱是 HTTP Live Streaming, 是一個由 Apple 公司實現的基於 HTTP 的媒體流傳輸協議. 他跟 DASH 協議的原理很是相似. 經過將整條流切割成一個小的能夠經過 HTTP 下載的媒體文件, 而後提供一個配套的媒體列表文件, 提供給客戶端, 讓客戶端順序地拉取這些媒體文件播放, 來實現看上去是在播放一條流的效果.html

因爲傳輸層協議只須要標準的 HTTP 協議, HLS 能夠方便的透過防火牆或者代理服務器, 並且能夠很方便的利用 CDN 進行分發加速, 而且客戶端實現起來也很方便.ios

HLS 目前普遍地應用於點播和直播領域.git

在 HTML5 頁面上使用 HLS 很是簡單:github

直接:golang

<video src="example.m3u8" controls></video>

或者:緩存

<video controls>
    <source src="example.m3u8"></source>
</video>

下面, 我將會歸納性地介紹 HLS 協議的方方面面(暫時不包括 AES 加密部分的內容), 配合 HLS 的 RFC 食用效果更佳.服務器

HLS 協議詳解

hls_arch

上面是 HLS 總體架構圖, 能夠看出, 總共有三個部分: Server, CDN, Client.網絡

其實, HLS 協議的主要內容是關於 M3U8 這個文本協議的, 其實生成與解析都很是簡單. 爲了更加直接地說明這一點, 我下面舉兩個簡單的例子:session

簡單的 Media Playlist:架構

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:8
#EXT-X-MEDIA-SEQUENCE:2680

#EXTINF:7.975,
https://priv.example.com/fileSequence2680.ts
#EXTINF:7.941,
https://priv.example.com/fileSequence2681.ts
#EXTINF:7.975,
https://priv.example.com/fileSequence2682.ts

包含多種比特率的 Master Playlist:

#EXTM3U
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1280000
http://example.com/low.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=2560000
http://example.com/mid.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=7680000
http://example.com/hi.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=65000,CODECS="mp4a.40.5"
http://example.com/audio-only.m3u8
  • HLS 經過 URI(RFC3986) 指向的一個 Playlist 來表示一個媒體流.

  • 一個 Playlist 能夠是一個 Media Playlist 或者 Master Playlist, 使用 UTF-8 編碼的文本文件, 包含一些 URI 跟描述性的 tags.

  • 一個 Media Playlist 包含一個 Media Segments 列表,當順序播放時, 能播放整個完整的流.

  • 要想播放這個 Playlist, 客戶端須要首先下載他, 而後播放裏面的每個 Media Segment.

  • 更加複雜的狀況是, Playlist 是一個 Master Playlist, 包含一個 Variant Stream 集合, 一般每一個 Variant Stream 裏面是同一個流的多個不一樣版本(如: 分辨率, 碼率不一樣).

HLS Media Segments

  • 每個 Media Segment 經過一個 URI 指定, 可能包含一個 byte range.

  • 每個 Media Segment 的 duration 經過 EXTINF tag 指定.

  • 每個 Media Segment 有一個惟一的整數 Media Segment Number.

  • 有些媒體格式須要一個 format-specific sequence 來初始化一個 parser, 在 Media Segment 被 parse 以前. 這個字段叫作 Media Initialization Section, 經過 EXT-X-MAP tag 來指定.

支持的 Media Segment 格式

MPEG-2 Transport Streams
  • 即最多見的 TS 文件.

  • RFC: ISO_13818.

  • Media Initialization Section: PAT(Program Association Table) 跟 PMT(Program Map Table).

  • 每一個 TS segment 必須值含一個 MPEG-2 Program.

  • 每個 TS segment 包含一個 PAT 和 PMT, 最好在 segment 的開始處, 或者經過一個 EXT-X-MAP tag 來指定.

Fragmented MPEG-4
  • 即常提到的 fMP4.

  • RFC: ISOBMFF.

  • Media Initialization Section: ftyp box(包含一個高於 ios6 的 brand), ftypbox 必須緊跟在 moov box 以後. moov box 必須包含一個 trak box(對於每一個 fMP4 segment 裏面的 traf box, 包含匹配的 track_ID). 每一個 trakbox 應該包含一個 sample table, 可是他的 sample count 必須爲 0. mvhd box 跟 tkhd 的 duration 必須爲 0. mvex box 必須跟在上一個 trak box 後面.

  • 不像普通的 MP4 文件包含一個 moov box(包含 sample tables) 和一個 mdatbox(包含對應的 samples), 一個 fMP4 包含一個 moof box (包含 sample table 的子集), 和一個 mdat box(包含對應的 samples).

  • 在每個 fMP4 segment 裏面, 每個 traf box 必須包含一個 tfdt box, fMP4 segment 必須使用 movie-fragment relative addressing. fMP4 segments 絕對不能使用外部的 data references.

  • 每個 fMP4 segment 必須有一個 EXT-X-MAP tag.

Packed Audio
  • 一個 Packed Audio Segment 包含編碼的 audio samples 和 ID3 tags. 簡單的打包到一塊兒, 包含最小的 framing, 而且沒有 per-sample timestamp.

  • 支持的 Packed Audio: AAC with ADTS framing [ISO_13818_7], MP3 [ISO_13818_3], AC-3 [AC_3], Enhanced AC-3 [AC_3].

  • 一個 Packed Audio Segment 沒有 Media Initialization Section.

  • 每個 Packed Audio Segment 必須在他的第一個 sample 指定 timestamp 經過一個 ID3 PRIV tag.

  • ID3 PRIV owner identifier 必須是 com.apple.streaming.transportStreamTimestamp.

  • ID3 payload 必須是一個 33-bit MPEG-2 Program Elementary Stream timestamp 的大端 eight-octet number, 高 31 爲設置爲 0.

WebVTT
  • 一個 WebVTT Segment 是一個 WebVTT 文件的一個 section, WebVTT Segment 包含 subtitles.

  • Media Initialization Section: WebVTT header.

  • 每個 WebVTT Segment 必須有以一個 WebVTT header 開始, 或者有一個 EXT-X-MAP tag 來指定.

  • 每個 WebVTT header 應該有一個 X-TIMESTAMP-MAP 來保證音視頻同步.

HLS Playlists

  • Playlist 文件的格式是起源於 M3U, 而且繼承兩個 tag: EXTM3UEXTINF

  • 下面的 tags 經過 BNF-style 語法來指定.

  • 一個 Playlist 文件必須經過 URI(.m3u8 或 m3u) 或者 HTTP Content-Type 來識別(application/vnd.apple.mpegurl 或 audio/mpegurl).

  • 換行符能夠用 \n 或者 \r\n.

  • # 開頭的是 tag 或者註釋, 以 #EXT 開頭的是 tag, 其他的爲註釋, 在解析時應該忽略.

  • Playlist 裏面的 URI 能夠用絕對地址或者相對地址, 若是使用相對地址, 那麼是相對於 Playlist 文件的地址.

Attribute Lists

  • 有的 tags 的值是 Attribute Lists.

  • 一個 Attribute List 是一個用逗號分隔的 attribute/value 對列表.

  • 格式爲: AttributeName=AttributeValue.

Basic Tags

Basic Tags 能夠用在 Media Playlist 和 Master Playlist 裏面.

  • EXTM3U: 必須在文件的第一行, 標識是一個 Extended M3U Playlist 文件.

  • EXT-X-VERSION: 表示 Playlist 兼容的版本.

Media Segment Tags

每個 Media Segment 經過一系列的 Media Segment tags 跟一個 URI 來指定. 有的 Media Segment tags 只應用與下一個 segment, 有的則是應用全部下面的 segments. 一個 Media Segment tag 只能出如今 Media Playlist 裏面.

  • EXTINF: 用於指定 Media Segment 的 duration

  • EXT-X-BYTERANGE: 用於指定 URI 的 sub-range

  • EXT-X-DISCONTINUITY: 表示不連續.

  • EXT-X-KEY: 表示 Media Segment 已加密, 該值用於解密.

  • EXT-X-MAP: 用於指定 Media Initialization Section.

  • EXT-X-PROGRAM-DATE-TIME: 和 Media Segment 的第一個 sample 一塊兒來肯定時間戳.

  • EXT-X-DATERANGE: 將一個時間範圍和一組屬性鍵值對結合到一塊兒.

Media Playlist Tags

Media Playlist tags 描述 Media Playlist 的全局參數. 一樣地, Media Playlist tags 只能出如今 Media Playlist 裏面.

  • EXT-X-TARGETDURATION: 用於指定最大的 Media Segment duration.

  • EXT-X-MEDIA-SEQUENCE: 用於指定第一個 Media Segment 的 Media Sequence Number.

  • EXT-X-DISCONTINUITY-SEQUENCE: 用於不一樣 Variant Stream 之間同步.

  • EXT-X-ENDLIST: 表示結束.

  • EXT-X-PLAYLIST-TYPE: 可選, 指定整個 Playlist 的類型.

  • EXT-X-I-FRAMES-ONLY: 表示每一個 Media Segment 描述一個單一的 I-frame.

Master Playlist Tags

Master Playlist tags 定義 Variant Streams, Renditions 和 其餘顯示的全局參數. Master Playlist tags 只能出如今 Master Playlist 中.

  • EXT-X-MEDIA: 用於關聯同一個內容的多個 Media Playlist 的多種 renditions.

  • EXT-X-STREAM-INF: 用於指定一個 Variant Stream.

  • EXT-X-I-FRAME-STREAM-INF: 用於指定一個 Media Playlist 包含媒體的 I-frames.

  • EXT-X-SESSION-DATA: 存放一些 session 數據.

  • EXT-X-SESSION-KEY: 用於解密.

Media or Master Playlist Tags

這裏的 tags 能夠出如今 Media Playlist 或者 Master Playlist 中. 可是若是同時出如今同一個 Master Playlist 和 Media Playlist 中時, 必須爲相同值.

  • EXT-X-INDEPENDENT-SEGMENTS: 表示每一個 Media Segment 能夠獨立解碼.

  • EXT-X-START: 標識一個優選的點來播放這個 Playlist.

服務器端與客戶端邏輯

如下流程僅供參考, 其實不一樣的播放器客戶端以及服務器端的拉取規則都有不少細節差別.

服務器端邏輯

  1. 將媒體源切片成 Media Segment, 應該優先從能夠高效解碼的時間點來進行切片(如: I-frame).

  2. 爲每個 Media Segment 生成 URI.

  3. Server 須要支持 「gzip」 方式壓縮文本內容.

  4. 建立一個 Media Playlist 索引文件, EXT-X-VERSION 不要高於他須要的版本, 來提供更好的兼容性.

  5. Server 不能隨便修改 Media Playlist, 除了 Append 文本到文件末尾, 按順序移除 Media Segment URIs, 增加 EXT-X-MEDIA-SEQUENCEEXT-X-DISCONTINUITY-SEQUENCE, 添加 EXT-X-ENDLIST 到文件尾.

  6. 在最後添加 EXT-X-ENDLIST tag, 來減小 Client reload Playlist 的次數.

  7. 注意點播與直播服務器不一樣的地方是, 直播的 m3u8 文件會不斷更新, 而點播的 m3u8 文件是不會變的, 只須要客戶端在開始時請求一次便可.

客戶端邏輯

  1. 客戶端經過 URI 獲取 Playlist. 若是是 Master Playlist, 客戶端能夠選擇一個 Variant Stream 來播放.

  2. 客戶端檢查 EXT-X-VERSION 版本是否知足.

  3. 客戶端應該忽略不可識別的 tags, 忽略不可識別的屬性鍵值對.

  4. 加載 Media Playlist file.

  5. 播放 Media Playlist file.

  6. 重加載 Media Playlist file.

  7. 決定下一次要加載的 Media Segment.

HLS 的優點

  • 客戶端支持簡單, 只須要支持 HTTP 請求便可, HTTP 協議無狀態, 只須要按順序下載媒體片斷便可.

  • 使用 HTTP 協議網絡兼容性好, HTTP 數據包也能夠方便地經過防火牆或者代理服務器, CDN 支持良好.

  • Apple 的全系列產品支持, 因爲 HLS 是蘋果提出的, 因此在 Apple 的全系列產品包括 iphone, ipad, safari 都不須要安裝任何插件就能夠原生支持播放 HLS, 如今, Android 也加入了對 HLS 的支持.

  • 自帶多碼率自適應, Apple 在提出 HLS 時, 就已經考慮了碼流自適應的問題.

HLS 的劣勢

  • 相比 RTMP 這類長鏈接協議, 延時較高, 難以用到互動直播場景.

  • 對於點播服務來講, 因爲 TS 切片一般較小, 海量碎片在文件分發, 一致性緩存, 存儲等方面都有較大挑戰.

改進的 HLS 技術

因爲客戶端每次請求 TS 或 M3U8 有可能都是一個新的鏈接請求, 因此, 咱們沒法有效的標識客戶端, 一旦出現問題, 基本沒法有效的定位問題, 因此, 通常工業級的服務器都會對傳統的 HLS 作一些改進.

這裏主要介紹網宿的 Variant HLS 與又拍雲的 HLS+.

網宿的 Variant HLS

首先, 咱們能夠下載一條網宿的 M3U8 文件:

wget http://bililive.kksmg.com/hls/stvd6edb9a6_45b34047833af658bf4945a8/playlist.m3u8

而後, 打開下載獲得的 playlist 文件:

#EXTM3U
#EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=781000
http://bililive.kksmg.com/hls/stvd6edb9a6_45b34047833af658bf4945a8/playlist.m3u8?wsSession=0105cb4e8fe63bccab511a4a-149017212774715&wsIPSercert=b80d38c068c9e3634a7ebb2f2bbf9b89&wsMonitor=-1

能夠看出這是一個 Master Playlist, 裏面嵌套了一層 M3U8, 同時能夠看出網宿採用 wsSession 來標識一條播放鏈接.

又拍雲的 HLS+

Variant HLS

首先, 咱們能夠下載一條又拍雲的 M3U8 文件:

wget http://uplive.b0.upaiyun.com/live/loading.m3u8

而後, 打開下載獲得的 playlist 文件:

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-ALLOW-CACHE:YES
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-TARGETDURATION:1
#EXTINF:0.998, no desc
http://183.158.35.12:8080/uplive.b0.upaiyun.com/live/loading-0.ts?shp_uuid=e4989f34fcab282e21ef1fd2980284cb&shp_ts=1490172420851&shp_cid=17906&shp_pid=3370578&shp_sip0=127.0.0.1&shp_sip1=183.158.35.12&domain=uplive.b0.upaiyun.com&shp_seqno=0

能夠看出又拍雲的 HLS+ 也支持這種 Variant HLS 方式來標識一條 HLS 鏈接, 能夠看出, 又拍雲使用 uuid 來表示一條 HLS 鏈接.

HTTP 302

首先, 以 HTTP 302 方式來請求播放地址.

❯ curl -v http://uplive.b0.upaiyun.com/live/loading.m3u8\?shp_identify\=302 -o playlist
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0*   Trying 183.158.35.59...
* TCP_NODELAY set
* Connected to uplive.b0.upaiyun.com (183.158.35.59) port 80 (#0)
> GET /live/loading.m3u8?shp_identify=302 HTTP/1.1
> Host: uplive.b0.upaiyun.com
> User-Agent: curl/7.51.0
> Accept: */*
>
< HTTP/1.1 302 Found
< Server: marco/0.26
< Date: Wed, 22 Mar 2017 08:54:11 GMT
< Content-Type: text/plain; charset=utf-8
< Content-Length: 259
< Connection: keep-alive
< Access-Control-Allow-Methods: GET
< Access-Control-Allow-Origin: *
< Location: http://183.158.35.19:8080/uplive.b0.upaiyun.com/live/loading.m3u8?shp_uuid=2862b1b817a74cf719b1cd8f554616cd&shp_ts=1490172851450&shp_cid=59553&shp_pid=1730488&shp_sip0=127.0.0.1&shp_sip1=183.158.35.19&domain=uplive.b0.upaiyun.com&shp_identify=302
<
{ [259 bytes data]
* Curl_http_done: called premature == 0
100   259  100   259    0     0   4813      0 --:--:-- --:--:-- --:--:--  4886
* Connection #0 to host uplive.b0.upaiyun.com left intact

打開 playlist 內容:

Redirect to http://183.158.35.19:8080/uplive.b0.upaiyun.com/live/loading.m3u8?shp_uuid=2862b1b817a74cf719b1cd8f554616cd&shp_ts=1490172851450&shp_cid=59553&shp_pid=1730488&shp_sip0=127.0.0.1&shp_sip1=183.158.35.19&domain=uplive.b0.upaiyun.com&shp_identify=302

在跳轉以後的地址存放真正的 playlist, 同時, 也可以將 uuid 加入到了鏈接上.

總地來講, 無論經過哪一種方式, 最終咱們都能經過一個惟一的 id 來標識一條流, 這樣在排查問題時就能夠根據這個 id 來定位播放過程當中的問題.

HLS 延時分析

HLS 理論延時 = 1 個切片的時長 + 0-1個 td (td 是 EXT-X-TARGETDURATION, 可簡單理解爲播放器取片的間隔時間) + 0-n 個啓動切片(蘋果官方建議是請求到 3 個片以後纔開始播放) + 播放器最開始請求的片的網絡延時(網絡鏈接耗時)

爲了追求低延時效果, 能夠將切片切的更小, 取片間隔作的更小, 播放器未取到 3 個片就啓動播放. 可是, 這些優化方式都會增長 HLS 不穩定和出現錯誤的風險.

Demo

Refs

轉自:http://akagi201.org/post/hls-...

相關文章
相關標籤/搜索