SRS之分發HLS


來自: Delivery HLS

1. 綜述

SRS支持HLS/RTMP兩種成熟而且廣泛應用的流媒體分發方式。

RTMP指Adobe的RTMP(Realtime Message Protocol),廣泛應用於低延時直播,也是編碼器和服務器對接的實際標准協議,在PC(Flash)上有最佳觀看體驗和最佳穩定性。

HLS指Apple的HLS(Http Live Streaming),本身就是Live(直播)的,不過Vod(點播)也能支持。HLS是Apple平台的標准流媒體協議,和RTMP在PC上一樣支持得天衣無縫。

HLS和RTMP兩種分發方式,就可以支持所有的終端。RTMP參考 RTMP分發

RTMP和HLS的比較參考: RTMP PK HLS

部署分發HLS的實例,參考: Usage: HLS

2. 應用場景

  • 跨平台:PC主要的直播方案是RTMP,也有一些庫能播放HLS,譬如jwplayer,基於osmf的hls插件也一大堆。所以實際上如果選一種協議能跨PC/Android/IOS,那就是HLS。
  • IOS上苛刻的穩定性要求:IOS上最穩定的當然是HLS,穩定性不差於RTMP在PC-flash上的表現。
  • 友好的CDN分發方式:目前CDN對於RTMP也是基本協議,但是HLS分發的基礎是HTTP,所以CDN的接入和分發會比RTMP更加完善。能在各種CDN之間切換,RTMP也能,只是可能需要對接測試。
  • 簡單:HLS作為流媒體協議非常簡單,apple支持得也很完善。Android對HLS的支持也會越來越完善。至於DASH/HDS,好像沒有什么特別的理由,就像linux已經大行其道而且開放,其他的系統很難再廣泛應用。

3. HLS 介紹

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影響,詳見下面配置HLS的說明。
  • ts文件的數目:SRS可配置hls_window,指定m3u8中保存多少個切片,SRS會自動清理舊的切片。
  • livestream-67.ts:SRS會自動維護ts切片的文件名,在編碼器重推之后,這個編號會繼續增長,保證流的連續性。直到SRS重啟,這個編號才重置為0。

譬如,每個ts切片為10秒,窗口為60秒,那么m3u8中會保存6個ts切片。

4. HLS 工作流程

  1. FFMPEG 或 FMLE 或編碼器,推送 RTMP 流到 SRS,編碼為 H264/AAC(其他編碼器需要 SRS 轉碼)
  2. SRS 將 RTMP 切片為 TS,並生成 M3U8。若流非 H264 和 AAC,則停止輸出 HLS(可使用 SRS 轉碼到 SRS 其他 vhost 或流,然后再切 HLS)。
  3. 訪問 M3U8,SRS 內置的 HTTP 服務器(或者通用 HTTP 服務器)提供 HTTP 服務。

注意:SRS 只需要在 Vhost 上配置 HLS,會自動根據流的 app 創建目錄,但是配置的 hls_path 必須自己創建。

5. HLS 配置

conf/full.conf中的with-hls.vhost.com是HLS配置的實例,可以拷貝到默認的Vhost,例如:

vhost __defaultVhost__ {
    hls {
        # whether the hls is enabled.
        # if off, donot write hls(ts and m3u8) when publish.
        # default: off
        enabled         on;
        # the hls fragment in seconds, the duration of a piece of ts.
        # default: 10
        hls_fragment    10;
        # the hls m3u8 target duration ratio,
        #   EXT-X-TARGETDURATION = hls_td_ratio * hls_fragment // init
        #   EXT-X-TARGETDURATION = max(ts_duration, EXT-X-TARGETDURATION) // for each
        # @see https://github.com/ossrs/srs/issues/304#issuecomment-74000081
        # default: 1.5
        hls_td_ratio    1.5;
        # the audio overflow ratio.
        # for pure audio, the duration to reap the segment.
        # for example, the hls_fragment is 10s, hls_aof_ratio is 2.0,
        # the segment will reap to 20s for pure audio.
        # default: 2.0
        hls_aof_ratio   2.0;
        # the hls window in seconds, the number of ts in m3u8.
        # defualt: 60
        hls_window      60;
        # the error strategy. canbe:
        #       ignore, disable the hls.
        #       disconnect, require encoder republish.
        #       continue, ignore failed try to continue output hls.
        # @see https://github.com/ossrs/srs/issues/264
        # default: continue
        hls_on_error    continue;
        # the hls output path.
        # the m3u8 file is configed by hls_path/hls_m3u8_file, the defualt is:
        #       ./objs/nginx/html/[app]/[stream].m3u8
        # the ts file is cofiged by hls_path/hls_ts_file, the defualt is:
        #       ./objs/nginx/html/[app]/[stream]-[seq].ts
        # @remark, the hls_path is compatible with srs v1 config.
        # default: ./objs/nginx/html
        hls_path        ./objs/nginx/html;
        # the hls m3u8 file name.
        # we supports some variables to generate the filename.
        #       [vhost], the vhost of stream.
        #       [app], the app of stream.
        #       [stream], the stream name of stream.
        # default: [app]/[stream].m3u8
        hls_m3u8_file   [app]/[stream].m3u8;
        # the hls ts file name.
        # we supports some variables to generate the filename.
        #       [vhost], the vhost of stream.
        #       [app], the app of stream.
        #       [stream], the stream name of stream.
        #       [2006], replace this const to current year.
        #       [01], replace this const to current month.
        #       [02], replace this const to current date.
        #       [15], replace this const to current hour.
        #       [04], repleace this const to current minute.
        #       [05], repleace this const to current second.
        #       [999], repleace this const to current millisecond.
        #       [timestamp],replace this const to current UNIX timestamp in ms.
        #       [seq], the sequence number of ts.
        # @see https://github.com/ossrs/srs/wiki/v2_CN_DVR#custom-path
        # @see https://github.com/ossrs/srs/wiki/v2_CN_DeliveryHLS#hls-config
        # default: [app]/[stream]-[seq].ts
        hls_ts_file     [app]/[stream]-[seq].ts;
        # whether use floor for the hls_ts_file path generation.
        # if on, use floor(timestamp/hls_fragment) as the variable [timestamp],
        #       and use enahanced algorithm to calc deviation for segment.
        # @remark, when floor on, recommend the hls_segment >= 2*gop.
        # defualt: off
        hls_ts_floor    off;
        # the hls entry prefix, which is base url of ts url.
        # if specified, the ts path in m3u8 will be like:
        #       http://your-server/live/livestream-0.ts
        #       http://your-server/live/livestream-1.ts
        #       ...
        # optional, defualt to empty string.
        hls_entry_prefix http://your-server;
        # the default audio codec of hls.
        # when codec changed, write the PAT/PMT table, but maybe ok util next ts.
        # so user can set the default codec for mp3.
        # the available audio codec: 
        #       aac, mp3, an
        # default: aac
        hls_acodec      aac;
        # the default video codec of hls.
        # when codec changed, write the PAT/PMT table, but maybe ok util next ts.
        # so user can set the default codec for pure audio(without video) to vn.
        # the available video codec:
        #       h264, vn
        # default: h264
        hls_vcodec      h264;
        # whether cleanup the old expired ts files.
        # default: on
        hls_cleanup     on;
        # the timeout in seconds to dispose the hls,
        # dispose is to remove all hls files, m3u8 and ts files.
        # when publisher timeout dispose hls.
        # @remark 0 to disable dispose for publisher.
        # @remark apply for publisher timeout only, while "etc/init.d/srs stop" 
        #     always dispose hls.
        # default: 0
        hls_dispose     0;
        # the max size to notify hls,
        # to read max bytes from ts of specified cdn network,
        # @remark only used when on_hls_notify is config.
        # default: 64
        hls_nb_notify   64;
        # whether wait keyframe to reap segment,
        # if off, reap segment when duration exceed the fragment,
        # if on, reap segment when duration exceed and got keyframe.
        # default: on
        hls_wait_keyframe       on;
        
        # on_hls, never config in here, should config in http_hooks.
        # for the hls http callback, @see http_hooks.on_hls of vhost 
        # hooks.callback.srs.com
        # @read https://github.com/ossrs/srs/wiki/v2_CN_DeliveryHLS#http-callback
        # @read https://github.com/ossrs/srs/wiki/v2_CN_DeliveryHLS#http-callback
        
        # on_hls_notify, never config in here, should config in http_hooks.
        # we support the variables to generate the notify url:
        #       [app], replace with the app.
        #       [stream], replace with the stream.
        #       [ts_url], replace with the ts url.
        # for the hls http callback, @see http_hooks.on_hls_notify of 
        # vhost hooks.callback.srs.com
        # @read https://github.com/ossrs/srs/wiki/v2_CN_DeliveryHLS#on-hls-notify
        # @read https://github.com/ossrs/srs/wiki/v2_CN_DeliveryHLS#on-hls-notify
    }
}

其中 hls 配置就是 HLS 的配置,主要配置項如下:

  • enabled:是否開啟 HLS,on/off,默認 off。
  • hls_fragment:秒,指定 ts 切片的最小長度。實際上 ts 文件的長度由以下公式決定:
ts 文件時長 = max(hls_fragment, gop_size)
hls_fragment:配置文件中的長度。譬如:5秒。
gop_size:編碼器配置的 gop 的長度,譬如 ffmpeg 指定fps為20幀/秒,gop為200幀,則gop_size=gop/fps=10秒。
那么,最終ts的時長為max(5, 10) = 10秒。這也是為什么有些流配置了hls_fragment,
但是ts時長仍然比這個大的原因。
  • hls_td_ratio:倍數。控制 m3u8 的 EXT-X-TARGETDURATION,參考 Rewrite HLS寫入ts部分
  • hls_aof_ratio:倍數。純音頻時,當 ts 時長超過配置的 hls_fragment 乘以這個系數時就切割文件。例如,但 hls_fragment 是 10 秒,hls_aof_ratio 是 2.0 時,對於純音頻,10s*2.0=20秒時就切割 ts 文件。
  • hls_window:秒,指定 HLS 窗口大小,即 m3u8 中 ts 文件的時長之和,超過總時長后,丟棄第一個 m3u8 中的第一個切片,直到 ts 的總時長在這個配置項范圍之內。即 SRS 保證下面的公式:
hls_window >= sum(m3u8中每個ts的時長)
  • hls_path:HLS 的 m3u8 和 ts 文件保存的路徑。m3u8 和 ts 文件都保存在這個目錄中。
  • hls_m3u8_file:HLS 的 m3u8 文件名,包含可替換的 [vhost], [app] 和 [stream] 變量。
  • hls_ts_file:HLS的ts文件名,包含可替換的一系列變量,參考 dvr variables,另外,[seq] 是 ts 的 sequence number。
對於RTMP流:rtmp://localhost/live/livestream
HLS配置路徑:
        hls_path        /data/nginx/html;
        hls_m3u8_file   [app]/[stream].m3u8;
        hls_ts_file     [app]/[stream]-[seq].ts;
那么會生成以下文件:
/data/nginx/html/live/livestream.m3u8
/data/nginx/html/live/livestream-0.ts
/data/nginx/html/live/livestream-1.ts
/data/nginx/html/live/livestream-2.ts
最后的HLS地址為:http://localhost/live/livestream.m3u8
  • hls_entry_prefix:TS 的 base url。可選默認為空字符串;非空時加在 ts 前面作為 base url。
對於ts切片:live/livestream-0.ts
若配置為:hls_entry_prefix http://your-server;
則最后的TS的URL是:http://your-server/live/livestream-0.ts
  • hls_acodec:默認的音頻編碼。當流的編碼改變時,會更新 PMT/PAT 信息;默認是 aac,因此默認的 PMT/PAT 信息是 aac;如果流是 mp3,那么可以配置這個參數為 mp3,避免 PMT/PAT 改變。
  • hls_vcodec:默認的視頻編碼。當流的編碼改變時,會更新 PMT/PAT 信息;默認是 h264. 如果是純音頻 HLS,可以配置為 vn,可以減少 SRS 檢測純音頻時間,直接進入純音頻模式。
  • hls_cleanup:是否刪除過期的 ts 切片,不在 hls_window 中就是過期。可以關閉清除 ts 切片,實現時移和存儲,使用自己的切片管理系統。
  • hls_dispose:HLS 清理的過期時間(秒),系統重啟或者超過這個時間時,清理 HLS 的所有文件,包括 m3u8 和 ts。默認為 0,即不清理。
  • hls_wait_keyframe:是否按 gop 切片,即等待到關鍵幀后開始切片。測試發現 OS X 和 android 上可以不用按 gop 切片。
  • hls_nb_notify:從 notify 服務器讀取數據的長度。
  • on_hls:當切片生成時,回調這個 url,使用 POST 回調。用來和自己的系統集成,譬如實現切片移動等。
  • on_hls_notify:當切片生成時,回調這個 url,使用 GET 回調。用來和系統集成,可以使用 [ts_url] 變量,實現預分發(即下載一次 ts 片)。

部署分發HLS的實例,參考: Usage: HLS

6. HTTP Callback

可以配置 on_hls 實現回調,應該在 http_hooks 中配置,而不是在 hls 中配置。

備注:HLS 熱備可以基於這個回調實現,參考 #351

備注:HLS 熱備必須保證兩個服務器的切片完全一樣,因為負載均衡器或者邊緣可能從兩個服務器取切片,必須完全一樣。因此在切片上保證兩個服務器切片完全一致,是一個非常非常復雜的流媒體問題;但是通過業務系統和回調,通過選擇兩個服務器的切片的方式,可以做到非常簡單可靠的 HLS 熱備系統。

7. ON HLS Notify

可以配置 on_hls_notify 實現 CDN 預分發,應該在 http_hooks 中配置,而不是在 hls 中配置。

8. HLSAudioOnly

SRS 支持分發 HLS 純音頻,當 RTMP 流沒有視頻,且音頻為 aac(可以使用轉碼轉為 aac,參考 Usage: Transcode2HLS),SRS 只切片音頻。

若 RTMP 流中已經有視頻和音頻,需要支持純音頻 HLS 流,可以用轉碼將視頻去掉,參考: 轉碼: 禁用流。然后分發音頻流。

分發純音頻流不需要特殊配置,和 HLS 分發一樣,參考: Usage: HLS

9. HLS and Forward

Forward 的流和普通流不做區分,若 forward 的流所在的 VHOST 配置了 HLS,一樣會應用 HLS 配置進行切片。

因此,可以對原始流進行 Transcode 之后,保證流符合 h.264/aac 的規范,然后 forward 到多個配置了 HLS 的 vhost 進行切片。支持多個源站的熱備。

10. HLS and Transcode

HLS 要求 RTMP 流的編碼為 h.264+aac/mp3,否則會自動禁用 HLS,會出現 RTMP 流能看 HLS 流不能看(或者看到的 HLS 是之前的流)。

Transcode 將 RTMP 流轉碼后,可以讓 SRS 接入任何編碼的 RTMP 流,然后轉換成 HLS 要求的 h.264/aac/mp3 編碼方式。

配置 Transcode 時,若需要控制 ts 長度,需要配置 配置ffmpeg編碼的gop,譬如:

vhost hls.transcode.vhost.com {
    transcode {
        enabled     on;
        ffmpeg      ./objs/ffmpeg/bin/ffmpeg;
        engine hls {
            enabled         on;
            vfilter {
            }
            vcodec          libx264;
            vbitrate        500;
            vfps            20;
            vwidth          768;
            vheight         320;
            vthreads        2;
            vprofile        baseline;
            vpreset         superfast;
            vparams {
                g           100;
            }
            acodec          libaacplus;
            abitrate        45;
            asample_rate    44100;
            achannels       2;
            aparams {
            }
            output          rtmp://127.0.0.1:[port]/[app]?vhost=[vhost]/[stream]_[engine];
        }
    }
}

該 FFMPEG 轉碼參數,指定 gop 時長為 100/20 = 5秒,fps 幀率(vfps=20),gop 幀數(g=100)。

11. HLS M3u8 Examples

live.m3u8

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-ALLOW-CACHE:YES
#EXT-X-TARGETDURATION:13
#EXT-X-MEDIA-SEQUENCE:430
#EXTINF:11.800
news-430.ts
#EXTINF:10.120
news-431.ts
#EXTINF:11.160
news-432.ts

event.m3u8

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-ALLOW-CACHE:YES
#EXT-X-TARGETDURATION:13
#EXT-X-MEDIA-SEQUENCE:430
#EXT-X-PLAYLIST-TYPE:EVENT
#EXTINF:11.800
news-430.ts
#EXTINF:10.120
news-431.ts
#EXTINF:11.160
news-432.ts

vod.m3u8

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-ALLOW-CACHE:YES
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-TARGETDURATION:12
#EXTINF:10.120,
livestream-184.ts
#EXTINF:10.029,
livestream-185.ts
#EXTINF:10.206,
livestream-186.ts
#EXTINF:10.160,
livestream-187.ts
#EXTINF:11.360,
livestream-188.ts
#EXTINF:9.782,
livestream-189.ts
#EXT-X-ENDLIST

loop.m3u8

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-ALLOW-CACHE:YES
#EXT-X-TARGETDURATION:13
#EXT-X-MEDIA-SEQUENCE:430
#EXT-X-PLAYLIST-TYPE:VOD
#EXTINF:11.800
news-430.ts
#EXTINF:10.120
news-431.ts
#EXT-X-DISCONTINUITY
#EXTINF:11.952
news-430.ts
#EXTINF:12.640
news-431.ts
#EXTINF:11.160
news-432.ts
#EXT-X-DISCONTINUITY
#EXTINF:11.751
news-430.ts
#EXTINF:2.040
news-431.ts
#EXT-X-ENDLIST

Apple

https://developer.apple.com/library/ios/technotes/tn2288/_index.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM