轉自: https://blog.csdn.net/u014219492/article/details/110184490
1.HLS綜述
談HLS 就不得不談蘋果,談蘋果就不得不提喬幫主。HLS就是“HTTP Live Streaming”的縮寫,它誕生自2009年,QuickTime和iPhone3GS黃金搭檔下的一個標准,一個意在顛覆流媒體產業的新協議。
它的工作原理簡單來說就是把一段視頻流,分成一個個小的基於HTTP的文件來下載。當媒體流正在播放時,客戶端可以根據當前網絡環境,方便地在不同的碼率流中做切換,以實現更好的觀影體驗。
HLS的出現是為了解決蘋果原生環境中的流媒體播放,這個協議可以方便地讓Mac和iPhone播放視頻流,不依賴Adobe,更不用去管什么標准委員會。依賴自己,永遠是最大力量的保障。
HLS(HTTP Live Streaming) 把整個流分成一個個小的基於 HTTP 的文件來下載,每次只下載一些。HLS 協議由三部分組成:HTTP、M3U8、TS。這三部分中,HTTP 是傳輸協議,M3U8 是索引文件,TS 是音視頻的媒體信息。
HLS 提供一個 m3u8 地址,Apple 的 Safari 瀏覽器直接就能打開 m3u8 地址,譬如:http://demo.srs.com/live/livestream.m3u8
Android 不能直接打開,需要使用 html5 的 video 標簽,然后在瀏覽器中打開這個頁面即可,譬如:
<!-- livestream.html -->
<video width="640" height="360"
autoplay controls autobuffer
src="http://demo.srs.com/live/livestream.m3u8"
type="application/vnd.apple.mpegurl">
</video>
HLS 的 m3u8,是一個 TS 的列表,也就是告訴瀏覽器可以播放這些 TS 文件,譬如:
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-MEDIA-SEQUENCE:64
#EXT-X-TARGETDURATION:12
#EXTINF:11.550
livestream-64.TS
#EXTINF:5.250
livestream-65.TS
#EXTINF:7.700
livestream-66.TS
#EXTINF:6.850
livestream-67.TS
有幾個關鍵的參數,這些參數在 SRS 的配置文件中都有配置項:
EXT-X-TARGETDURATION:所有切片的最大時長。有些 Apple 設備這個參數不正確會無法播放。SRS 會自動計算出 TS 文件的最大時長,然后更新 m3u8 時會自動更新這個值。用戶不必自己配置。
EXTINF:TS 切片的實際時長,SRS 提供配置項 hls_fragment,但實際上的 TS 時長還受 gop 影響。
TS 文件的數目:SRS 可配置 hls_window,指定 m3u8 中保存多少個切片,SRS 會自動清理舊的切片
livestream-67.TS:SRS 會自動維護 TS 切片的文件名,在編碼器重推之后,這個編號會繼續增長,保證流的連續性。直到 SRS 重啟,這個編號才重置為 0。
譬如,每個 TS 切片為 10 秒,窗口為 60 秒,那么 m3u8 中會保存 6 個 TS 切片。
每一個 .m3u8 文件,分別對應若干個 TS 文件,這些 TS 文件才是真正存放視頻的數據,m3u8 文件只是存放了一些 TS 文件的配置信息和相關路徑,當視頻播放時,.m3u8 是動態改變的,video 標簽會解析這個文件,並找到對應的 TS 文件來播放,所以一般為了加快速度,.m3u8 放在 web 服務器上,TS 文件放在 CDN 上。
.m3u8 文件,其實就是以 utf-8 編碼的 m3u 文件,這個文件本身不能播放,只是存放了播放信息的文本文件。
HLS 整體框架圖:Server、CDN 和 Client
HLS 協議編碼格式要求
視頻的編碼格式:H264
音頻的編碼格式:AAC、MP3、AC-3
視頻的封裝格式:TS
保存 TS 索引的 m3u8 文件
HLS 協議優勢
HLS 相對於 RTMP 來講使用了標准的 HTTP 協議來傳輸數據,可以避免在一些特殊的網絡環境下被屏蔽。
HLS 相比 RTMP 在服務器端做負載均衡要簡單得多。因為 HLS 是基於無狀態協議 HTTP 實現的,客戶端只需要按照順序使用下載存儲在服務器的普通 TS 文件進行播放就可以。而 RTMP 是一種有狀態協議,很難對視頻服務器進行平滑擴展,因為需要為每一個播放視頻流的客戶端維護狀態。
HLS 協議本身實現了碼率自適應,在不同帶寬情況下,設備可以自動切換到最適合自己碼率的視頻播放。
HLS 協議缺點
HLS 協議在直播的視頻延遲時間很難做到 10 s 以下延時,而 RTMP 協議的延時可以降到 3s-4s 左右。
2. HLS 之 M3U8
m3u8 文件是用文件方式對媒體文件進行描述,由一些列標簽組成。
m3u8 文件示例 1:單碼率適配流
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-ALLOW-CACHE:YES
#EXT-X-MEDIA-SEQUENCE:2
#EXT-X-TARGETDURATION:16
#EXTINF:14.357, no desc
livestream-2.TS
#EXTINF:15.617, no desc
livestream-3.TS
#EXTINF:14.358, no desc
livestream-4.TS
#EXTINF:15.618, no desc
livestream-5.TS
#EXTINF:11.130, no desc
livestream-6.TS
該 m3u8 文件只是一個簡單的 Media Playlist。
m3u8 文件示例 2:多碼率適配流
#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
1
2
3
4
5
6
7
8
9
10
以上是包含多種比特率的 Master Playlist。該文件是一個實際使用中的頂級 m3u8 文件,該文件中又定義了
http://example.com/low.m3u8
http://example.com/mid.m3u8
http://example.com/hi.m3u8
等幾個二級文件。頂級 m3u8 文件主要是做碼率適配的,二級 m3u8 才是真正的切片文件,客戶端會默認選擇碼率最高的請求,如果發現碼率達不到,會請求降低碼率的流。客戶端拿到二級 m3u8 文件后,會繼續請求里面的文件,這時就可以進行播放了。
2.1 基礎概念
2.1.1 Playlist file
一個 m3u 的 Playlist 就是一個由多個獨立行組成的文本文件,每行由回車/換行區分。每一行可以是一個 URL、空白行或是一個 以 # 號開頭的字符串,並且空格只能存在於一行中不同元素間的分隔。
一個 URL表示一個媒體段或是 variant Playlist file(最多支持一層嵌套,即一個 m3u8 文件中嵌套另一個 m3u8),以 "EXT" 開頭的表示一個 "tag",否則表示注釋,直接忽略。
2.1.2 Tags
#EXTM3U:
每個 m3u8 文件第一行必須是這個 tag,如上面的兩個示例。
#EXTINF:
指定每個媒體段(TS)的持續時間,這個僅對其后面的 URL有效,每兩個媒體段 URL間被這個 tag 分隔開
其格式為:#EXTINF:<duration>,<title>
duration:表示持續的時間(秒),objectivec Durations MUST be integers if the protocol version of the Playlist file is less than 3,否則可以是浮點數。
#EXT-X-BYTERANGE:
表示媒體段是一個媒體 URL資源中的一段,只對其后的 media URL 有效,格式為:#EXT-X-BYTERANGE:<n>[@o]
n:表示這個區間的大小
o:表示在 URL 中的 offset
The EXT-X-BYTERANGE tag appeared in version 4 of the protocol
#EXT-X-TARGETDURATION:
指定當前視頻流中的單個切片(即 TS)文件的最大時長(秒)。所以 #EXTINF 中指定的時間長度必須小於或是等於這個最大值。這個 tag 在整個 Playlist 文件中只能出現一次(在嵌套的情況下,一般有真正
TS url 的 m3u8 才會出現該 tag)。格式為:#EXT-X-TARGETDURATION:<s>
s:表示最大的秒數。
#EXT-X-MEDIA-SEQUENCE:
每一個 media URL 在 Playlist 中只有唯一的序號,相鄰之間序號 +1。
格式為:#EXT-X-MEDIA-SEQUENCE:<number>。一個 media URL 並不是必須要包含的,如果沒有,默認為 0.
#EXT-X-KEY:
表示怎么對 media segmenTS 進行解碼。其作用范圍是下次該 tag 出現前的所有 media URL。
格式為:#EXT-X-KEY:<attribute-list>
NONE 或者 AES-128。如果是 NONE,則 URL 以及 IV 屬性必須不存在,如果是 AES-128(Advanced Encryption Standard),則 URL 必須存在,IV 可以不存在。
對於 AES-128 的情況,keytag 和 URL 屬性共同表示了一個 key 文件,通過 URL 可以獲得這個 key,如果沒有 IV(Initialization Vector),則使用序列號作為 IV 進行編解碼,將序列號的高位賦到 16 個字節的 buffer 中,左邊補 0;如果有 IV,則將該值當成 16 個字節的 16 進制數。
#EXT-X-PROGRAM-DATE-TIME:
將一個絕對時間或是日期和一個媒體段中的第一個 sample 相關聯,只對下一個 media URL 有效,格式如下:#EXT-X-PROGRAM-DATE-TIME:<YYYY-MM-DDThh:mm:ssZ>
例如:#EXT-X-PROGRAM-DATE-TIME:2010-02-19T14:54:23.031+08:00
#EXT-X-ALLOW-CACHE:
是否允許做 cache,這個可以在 Playlist 文件中任意地方出現,並且最多只出現一次,作用效果是所有的媒體段。格式如下:#EXT-X-ALLOW-CACHE:<YES|NO>
#EXT-X-PLAYLIST-TYPE:
提供關於 Playlist 的可變性的信息,這個對整個 Playlist 文件有效,是可選的,格式如下:#EXT-X-PLAYLIST-TYPE:<EVENT|VOD>
VOD,即為點播視頻,服務器不能改變 Playlist 文件,換句話說就是該視頻全部的 TS 文件已經被生成好了。
EVENT,就是實時生成 m3u8 和 TS 文件。服務器不能改變或是刪除 Playlist 文件中的任何部分,但是可以向該文件中增加新的一行內容。它的索引文件一直處於動態變化中,播放的時候需要不斷下載二級 index 文件。
#EXT-X-ENDLIST:
表示 m3u8 文件的結束,live m3u8 沒有該 tag。它可以在 Playlist 中任意位置出現,但是只能出現一個,格式如下:#EXT-X-ENDLIST
#EXT-X-MEDIA:
被用來在 Playlist 中表示相同內容的不同語種/譯文的版本,比如可以通過使用 3 個這種 tag 表示 3 種不同語音的音頻,或者用 2 個這個 tag 表示不同角度的 video。在 Playlist 中,這個標簽是獨立存在的,其格式如下:#EXT-X-MEDIA:<attribute-list>
該屬性列表中包含:URL、TYPE、GROUP-ID、LANGUAGE、NAME、DEFAULT、AUTOSELECT。
URL:如果沒有,則表示這個 tag 描述的可選擇版本在主 PlayList 的 EXT-X-STREAM-INF 中存在
TYPE:AUDIO and VIDEO
GROUP-ID:具有相同 ID 的 MEDIAtag,組成一組樣式
LANGUAGE:identifies the primary language used in the rendition
NAME:The value is a quoted-string containing a human-readable description of the rendition. If the LANGUAGE attribute is present then this description SHOULD be in that language
DEFAULT:YES 或是 NO,默認是 NO,如果是 YES,則客戶端會以這種選項來播放,除非用戶自己進行選擇
AUTOSELECT:YES 或是 NO,默認是 NO,如果是 YES,則客戶端會根據當前播放環境來進行選擇(用戶沒有根據自己偏好進行選擇的前提下)
The EXT-X-MEDIA tag appeared in version 4 of the protocol。
#EXT-X-STREAM-INF:
指定一個包含多媒體信息的 media URL 作為 Playlist,一般做 m3u8 的嵌套使用,它只對緊跟后面的 URL 有效,格式如下:#EXT-X-STREAM-INF:<attribute-list>
常用的屬性如下:
BANDWIDTH:帶寬,必須有
PROGRAM-ID:該值是一個十進制整數,唯一地標識一個在 Playlist 文件范圍內的特定的描述。一個 Playlist 文件中可能包含多個有相同 ID 的此 tag
CODECS:指定流的編碼類型,不是必須的
RESOLUTION:分辨率
AUDIO:這個值必須和 AUDIO 類別的 EXT-X-MEDIA 標簽中 GROUP-ID 屬性值相匹配
VIDEO:同上
#EXT-X-DISCONTINUITY:
當遇到該 tag 的時候說明以下屬性發生了變化:
file format
number and type of tracks
encoding parameters
encoding sequence
timestamp sequence
#ZEN-TOTAL-DURATION:
表示這個 m3u8 所含 TS 的總時間長度
3. HLS 之 TS
TS 文件為傳輸流文件,視頻編碼主要格式為 H264/MPEG4,音頻為 AAC/MP3。
TS 文件分為三層:
TS 層:Transport Stream,是在 pes 層的基礎上加入數據流的識別和傳輸必須的信息。
pes 層: Packet Elemental Stream,是在音視頻數據上加了時間戳等對數據幀的說明信息。
es 層:Elementary Stream,即音視頻數據。
3.1 TS 層:Transport Stream
TS 包大小固定為 188 字節,TS 層分為三個部分:TS header、adaptation field、payload。TS header 固定 4 個字節;adaptation field可能存在也可能不存在,主要作用是給不足 188 字節的數據做填充;payload 是 pes 數據。
3.1.1 TS header
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.1.2 adaptation field
自適應區的長度要包含傳輸錯誤指示符標識的一個字節。pcr 是節目時鍾參考,pcr、dTS、pTS 都是對同一個系統時鍾的采樣值,pcr 是遞增的,因此可以將其設置為 dTS 值,音頻數據不需要 pcr。如果沒有字段,ipad 是可以播放的,但 vlc 無法播放。打包 TS 流時 PAT 和 PMT 表是沒有 adaptation field 的,不夠的長度直接補 0xff 即可。視頻流和音頻流都需要加 adaptation field,通常加在一個幀的第一個 TS 包和最后一個 TS 包里,中間的 TS 包不加。如下圖所示:
PAT 格式如下圖
PMT 格式如下圖
3.2 pes 層:Packet Elemental Stream
pes 層是在每一個視頻/音頻幀上加入了時間戳等信息,pes 包內容很多,這里只留下最常用的。
pes 層格式如下圖:
pes 層內容如下圖:
pTS 是顯示時間戳、dTS 是解碼時間戳,視頻數據兩種時間戳都需要,音頻數據的 pTS 和 dTS 相同,所以只需要 pTS。有 pTS 和 dTS 兩種時間戳是 B 幀引起的,I 幀 和 P 幀的 pTS 等於 dTS。如果一個視頻沒有 B 幀,則 pTS 永遠和 dTS 相同。從文件中順序讀取視頻幀,取出的幀順序和 dTS 順序相同。dTS 算法比較簡單,初始值 + 增量即可,pTS 計算比較復雜,需要在 dTS 的基礎上加偏移量。
音頻的 pes 中只有 pTS(同 dTS),視頻的 I、P 幀兩種時間戳都要有,視頻 B 幀只要 pTS(同 dTS)。打包 pTS 和 dTS 就需要知道視頻幀類型,但是通過容器格式我們是無法判斷幀類型的,必須解析 h.264 內容才可以獲取幀類型。
舉例說明:
. I P B B B P
讀取順序: 1 2 3 4 5 6
dTS 順序: 1 2 3 4 5 6
pTS 順序: 1 5 3 2 4 6
點播視頻 dTS 算法:
dTS = 初始值 + 90000 / video_frame_rate,初始值可以隨便指定,但是最好不要取 0,video_frame_rate 就是幀率,比如 23、30。
pTS 和 dTS 是以 timestamp 為單位的,1s = 90000 time scale,一幀就應該是 90000/video_frame_rate 個 timescale。
用一幀的 timescale 除以采樣頻率就可以轉換為一幀的播放時長。
點播音頻 dTS 算法:
dTS = 初始值 + (90000 * audio_samples_per_frame) / audio_sample_rate,audio_samples_per_frame 這個值與編解碼相關,AAC 取值 1024,mp3 取值 1158,audio_sample_rate 是采樣率,比如 24000、41000. AAC 一般解碼出來是每聲道 1024 個 sample,也就是說一幀的時長為 1024/sample_rate 秒。所以每一幀時間戳依次0,1024/sample_rate, …, 1024*n/sample_rate秒。
注:直播視頻的 dTS 和 pTS 應該直接用直播數據流中的時間,不應該按公式計算。
3.3 es 層:Elementary Stream
ES 層指的就是音視頻數據。這里只介紹 h.264 視頻和 AAC 音頻。
3.3.1 h.264 視頻
打包 h.264 數據時必須給視頻數據加上一個 nalu(Network Abstraction Layer Unit),nalu 包括 nalu header 和 nalu type,nalu header 固定為 0x00000001(幀開始)或 0x000001(幀中)。h.264 的數據是由 slice 組成的,slice 的內容包括:視頻、sps、pps 等。nalu type 決定了后面的 h.264 數據內容。
0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
|F|NRI| TYPE |
+-+-+-+-+-+-+-+-+
1
2
3
4
F:1bit,forbidden_zero_bit,h.264 規定必須取 0。
NRI:2biTS,nal_ref_idc,取值為 0~3,指示這個 nalu 的重要性,I 幀、sps、pps 通常取 3,P 幀常取 2,B 幀通常取 0
Type:5biTS,取值如下表所示:
打包 es 層數據時 pes 頭和 es 數據之間要加入一個 type=9 的 nalu,關鍵幀 slice 前必須要加入 type=7 和 type=8 的 nalu,而且是緊鄰的。如下圖所示:
————————————————
版權聲明:本文為CSDN博主「一了百-了」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/u014219492/article/details/110184490