直播原理與web直播實戰

富途web開發團隊發表於2018-03-26

直播在網際網路中已經成為一個必不可少的東西,話說各大直播平臺 bilibili,虎牙,鬥魚都準備上市了。

富途證券作為了一家互聯證券公司直播當然也少不了的。目前直播在富途牛牛軟體(以下簡稱:牛牛)中主要功能是教育跟宣傳作用。

富途牛牛為領先的港股美股交易軟體。

文章結構

  1. 直播構成
  2. 直播流程
  3. web中直播技術
    • HLS協議
    • RTMP協議
    • HLS與RTMP對比
  4. 直播實戰
    • 安裝nginx、nginx-rtmp-module、FFmpeg(以下操作均在mac下進行)
    • nginx.conf配置檔案,配置RTMP、HLS
    • 重啟nginx
    • 檢視埠是否啟動
    • FFmpeg執行命令
    • 程式碼
    • 效果
  5. 直播中遇到坑
  6. 總結

直播構成

直播構成

目前牛牛支援情況:

  1. 移動牛牛(安卓、IOS)主播端只支援攝像頭直播,觀眾端支援展示

  2. PC牛牛主播端支援攝像頭直播、錄屏直播、攝像頭+錄屏直播,觀眾端支援展示

  3. MAC牛牛跟web端主播端不支援,觀眾端支援展示

直播流程

視訊直播可以分為採集,前處理,編碼,傳輸,解碼,渲染 這幾個環節。

採集

一般是由客戶端(IOS、安卓、PC或其它工具,如OBS)完成的,iOS是比較簡單的,Android則要做些機型適配工作,PC最麻煩各種奇葩攝像頭驅動,當然這些問題,騰訊雲已經幫我們處理好了,呵呵。

前處理

主要是處理直播美顏,美顏演算法需要用到GPU程式設計,需要懂影象處理演算法的人,沒有好的開源實現,要自己參考論文去研究。難點不在於美顏效果,而在於GPU佔用和美顏效果之間找平衡。

編碼

肯定要採用硬編碼,軟編碼720p完全沒希望,勉強能編碼也會導致CPU過熱燙到攝像頭。編碼要在解析度,幀率,位元速率,GOP等引數設計上找到最佳平衡點。

傳輸

一般交給了CDN服務商。

解碼

是對之前編碼的操作,進行解碼,在web裡需要解碼是HLS。

渲染

主要用播放器來解決,web中常用到的播放器有 video.js ,目前我們使用是騰訊雲播放器。

其實一個完成直播,遠遠不上面這幾個環節,下面是騰訊雲直播方案的整個流程圖:

直播方案

web中直播技術

目前網際網路上web做直播,主要是展示,主流web展示的話可能涉及到HLS跟RTMP這兩個東西,現在我們重點講解hls跟RTMP協議。

HLS

HLS(HTTP Live Streaming全稱)是一個基於HTTP的視訊流協議,由Apple公司實現,Mac OS上的QuickTime、Safari 以及iOS上的 Safari都能很好的支援 HLS,高版本 Android 也增加了對 HLS 的支援。

一些常見的客戶端如:MPlayerX、VLC 也都支援HLS協議,如果需要在chrome上播放,需要使用 videojs-contrib-hls.js 解析。

HLS工作流程

HLS工作流程圖

Server伺服器

HLS的伺服器元件負責獲取的媒體輸入流 , 然後Media編碼後 MPEG-4(H.264 video 和 AAC audio)格式然後用硬體打包到 MPEG-2 (MPEG-2 transport stream)的傳輸流中。圖中顯示,傳輸流會經過stream segmenter, 這裡的工作是MPEG-2傳輸流會被分散為小片段然後儲存為一個或多個系列的 .ts 格式的媒體檔案。這個過程需要藉助編碼工具來完成,比如 Apple stream segmenter。 (視訊類是.ts檔案,純音訊會被編碼為一些音訊小片段,通常為 ADTS頭的AAC、MP3、或者 AC-3格式。) 服務端可以採用硬體編碼和軟體編碼兩種形式,其功能都是按照上文描述的規則對現有的媒體檔案進行切片並使用索引檔案進行管理。而軟體切片通常會使用 Apple 公司提供的工具或者第三方的整合工具。

Distribution分配元件

同時上面提到的那個切片器(segmenter)也會建立一個索引檔案,通常會包含這些媒體檔案的一個列表,也能包含後設資料。它一般都是一個個.M38U的列表。列表元素會關聯一個 URL 用於客戶端訪問。然後按序去請求這些 URL。

索引檔案結構圖

索引檔案結構圖

主索引檔案
#EXTM3U
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=409037,RESOLUTION=416x234,CODECS="mp4a.40.2, avc1.42001e"
Gear1/prog_index.m3u8

複製程式碼

第一行:#EXTM3U

每個M3U檔案第一行必須是這個tag,起標示作用

第二行:#EXT-X-STREAM-INF

標籤的屬性列表中直接指明當前流是VIDEO還是AUDIO

包含屬性 :

  1. BANDWIDTH 指定位元速率
  2. PROGRAM-ID 唯一ID (這個屬性在後面的協議版本廢除了)
  3. CODECS 指定流的編碼型別
  4. RESOLUTION:解析度
子索引檔案
#EXTM3U
#EXT-X-TARGETDURATION:11
#EXT-X-VERSION:3
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-PLAYLIST-TYPE:VOD
#EXTINF:10.133333,
fileSequence0.ts
#EXTINF:10.000666,
fileSequence1.ts
#EXTINF:10.667334,
fileSequence2.ts
#EXTINF:9.686001,
fileSequence3.ts
#EXTINF:9.768665,
fileSequence4.ts
#EXTINF:10.000000,
fileSequence5.ts
#EXT-X-ENDLIST

複製程式碼

#EXTM3U m3u檔案頭,必須放在第一行。

#EXT-X-TARGETDURATION 每個分片TS的最大的時長。

#EXT-X-VERSION 用以標示協議版本。

#EXT-X-MEDIA-SEQUENCE TS分片的序列號。

#EXT-X-PLAYLIST-TYPE 提供關於PlayList的可變性的資訊, 這個對整個PlayList檔案有效,是可選的。

#EXTINF extra info,分片TS的資訊,如時長,頻寬等。

主索引檔案與子索引檔案的區別
  • 主索引檔案和子索引檔案都是.M3U8的playlist
  • 主索引檔案只需下載一次,但對於直播節目子索引檔案定期重新載入

client客戶端

分配元件由標準的網路伺服器。他們負責接受Client客戶端請求並提供相關聯的資源給客戶端。

videojs-contrib-hls.js元件解析過程

videojs-contrib-hls.js

videojs-contrib-hls.js解析圖

HLS簡單講就是把整個流分成一個個小的片段,基於 HTTP 的檔案來下載,每次只下載一小部分。

前面提到了用於 H5 播放直播視訊時引入的一個 .m3u8 的檔案,這個檔案就是基於 HLS 協議,存放視訊流後設資料的檔案。

每一個 .m3u8 檔案,分別對應若干個 ts 檔案,這些 ts 檔案才是真正存放視訊的資料,m3u8 檔案只是存放了一些 ts 檔案的配置資訊和相關路徑,當視訊播放時,.m3u8 是動態改變的,再通過解析器(videojs-contrib-hls.js)解析這個檔案,並找到對應的 ts 檔案來播放,所以一般為了加快速度,.m3u8 放在 web 伺服器上,ts 檔案放在 cdn 上。

RTMP

Real Time Messaging Protocol(簡稱 RTMP)是 Macromedia 開發的一套視訊直播協議,現在屬於 Adobe。這套方案需要搭建專門的 RTMP 流媒體服務如 Adobe Media Server,並且在瀏覽器中只能使用 Flash 實現播放器。它的實時性非常好,延遲很小,但無法支援移動端 WEB 播放是它的硬傷。

瀏覽器端,HTML5 video標籤無法播放 RTMP 協議的視訊,可以通過 video.js 來實現。

<link href="http://vjs.zencdn.net/5.8.8/video-js.css" rel="stylesheet">
 
<video id="example_video_1" class="video-js vjs-default-skin" controls preload="auto" width="640" height="264" loop="loop" webkit-playsinline>
<source src="rtmp://10.14.221.17:1935/rtmplive/home" type='rtmp/flv'>
</video>
 
<script src="http://vjs.zencdn.net/5.8.8/video.js"></script>
<script>
videojs.options.flash.swf = 'video.swf';
videojs('example_video_1').ready(function() {
this.play();
});
</script>
複製程式碼

HLS VS RTMP 優缺點對比

協議 原理 延時 優點 缺點 使用場景
HLS(http) 集合一段時間資料生成ts切片檔案更m3u8檔案 10s-30s 跨平臺 延時性高 移動端
RTMP(TCP) 每個時刻的資料收到後立即傳送 2s 延時低、實時性好 跨平臺差 PC+直播+實時性要求高+互動性強

直播實戰(搭建RTMP、HLS直播流服務)

安裝nginx

brew install nginx
複製程式碼

安裝nginx-rtmp-module

brew install nginx-full --with-rtmp-module
複製程式碼

然後安裝FFmpeg(是一個集錄制、轉換、音/視訊編碼解碼功能 為一體的完整的開源工具,我們下面用它來做推流跟切片)。

brew install ffmpeg
複製程式碼

nginx.conf配置檔案,配置RTMP、HLS

查詢到nginx.conf配置檔案(/usr/local/etc/nginx/nginx.conf),並配置相應的直播流配置。

rtmp {
    server {
    	#監聽的埠
    	listen 1935;
    	# RTMP 直播流配置
    	application rtmplive {
            live on;
            #為 rtmp 引擎設定最大連線數。預設為 off
            max_connections 1024;
        }
    	# HLS 直播流配置
    	application hls{
            live on;
            hls on;
            hls_path /Users/youname/Documents/notes/live/public/hls;  #這裡的路徑切片需要儲存的路徑
            hls_fragment 1s;
        }
    }
}
複製程式碼

在http中新增 HLS 的配置:

location /hls {
    # Serve HLS fragments
    types {
        application/vnd.apple.mpegurl m3u8;
        video/mp2t ts;
    }
    root /Users/youname/Documents/notes/live/public;#切片的路徑
    #add_header Cache-Controll no-cache;
    expires -1;
}
複製程式碼

重啟nginx

sudo nginx -s reload
複製程式碼

檢視埠是否啟動

netstat -an | grep 1935
複製程式碼

如果顯示如下,顯示已經啟用。

ng

http埠同理。

到現在我們已經完成了,服務的搭建。

  1. RTMP推流地址為 rtmp://127.0.0.1:1935/rtmplive/home
  2. HLS推流地址為 rtmp://localhost:1935/rtmplive/hls

FFmpeg執行命令

我們以推流MP4檔案為例,我的視訊檔案地址:/Users/youname/Desktop/w01661pl9vw.p702.1.mp4

RTMP 協議推流命令:

ffmpeg -re -i /Users/youname/Desktop/w01661pl9vw.p702.1.mp4 -vcodec libx264 -acodec aac -f flv rtmp://127.0.0.1:1935/rtmplive/home

複製程式碼

HLS 協議推流命令:

ffmpeg -re -i  /Users/youname/Desktop/w01661pl9vw.p702.1.mp4 -vcodec libx264 -vprofile baseline -acodec aac -ar 44100 -strict -2 -ac 1 -f flv -q 10 rtmp://127.0.0.1:1935/hls/test 

複製程式碼

引數:

  1. 視訊的地址:/Users/youname/Desktop/w01661pl9vw.p702.1.mp4
  2. 推流地址:rtmp://127.0.0.1:1935/rtmplive/home,rtmp://localhost:1935/rtmplive/hls

上面的命令操作後,命令列出現瞭如下圖,表示已經成功了。

ffmpeg

關於FFmpeg功能命令可參考:FFmpeg功能命令集合

web實現直播

兩種推流方式播放的話,我們都使用video.js播放器播放(牛牛裡使用的是騰訊雲播放器)

RTMP協議程式碼實現

需要注意的是src中的地址填的是RTMP推流地址。播放時,如果出現“當前系統環境不支援播放該視訊格式”,瀏覽器需要啟用flash,才能正常播放。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link href="http://vjs.zencdn.net/5.19/video-js.min.css" rel="stylesheet">
<script src="http://vjs.zencdn.net/5.19/video.min.js"></script>
</head>
<body>
<video
    id="my-player"
    class="video-js"
    controls
    preload="auto"
    data-setup='{}'>
    <source src='rtmp://127.0.0.1/rtmplive/home' type='rtmp/flv'/>  
  </p>
</video>
<script type="text/javascript">
   var player = videojs('my-player');
   var options = {};
   var player = videojs('my-player', options, function onPlayerReady() {
     videojs.log('Your player is ready!');
     // In this context, `this` is the player that was created by Video.js.
     this.play();
     // How about an event listener?
     this.on('ended', function() {
       videojs.log('Awww...over so soon?!');
     });
   });
</script>
</body>
</html>
複製程式碼

HLS程式碼實現

下面的src填的是切片地址。

<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>videojs-contrib-hls embed</title>
  <link href="https://unpkg.com/video.js/dist/video-js.css" rel="stylesheet">
  <script src="https://unpkg.com/video.js/dist/video.js"></script>
  <script src="https://unpkg.com/videojs-contrib-hls/dist/videojs-contrib-hls.js"></script>
</head>
<body>
  <video id="my_video_1" class="video-js vjs-default-skin" controls preload="auto" width="640" height="268" 
  data-setup='{}'>
    <source src="http://www.tony.com/hls/test.m3u8" type="application/x-mpegURL">
  </video>
</body>
</html>
複製程式碼

效果

我們來看看真正的效果如何。

RTMP效果

RTMP效果圖

HLS效果

HLS效果

ts和m3u8檔案

ts和m3u8檔案

實戰中遇到的坑

  • 自動播放問題
  • 各平臺播放器表現不統一
  • 內頁面除錯困難
  • Native與web通訊問題

自動播放問題

在X5核心瀏覽器裡必須使用觸發touchend、click、doubleclick或keydown事件等標準的事件才能觸發。

各平臺播放器表現不統一

安卓下很多瀏覽器把video標籤替換成了原生自帶播放器樣式跟行為,很難控制其行為跟樣式。

內嵌頁面除錯困難

目前使用的是weinre除錯,但weinre除錯看不到在native實際效果,比如web呼叫native,需要native反饋一種效果,weinre是看不到效果。

Native與web通訊

schema跟jsBridge,schema只能做到web呼叫native,而且做不到native呼叫web;jsBridge雖然可以做native呼叫web,但在iframe沒載入完的情況下,也是通知不到web的;

總結

整個直播是一個非常複雜的過程,實現過程中會遇到很多效能問題,需要在效能跟即時性做一個權衡,ts跟m3u8儘量做到快取,瀏覽器裡儘量使用推流。

歡迎大家關注富途web開發團隊,原文連結

參考資料:

如何搭建一個完整的視訊直播系統?

H5直播起航

有支援M3U8格式的HTML5播放器嗎?

HTTP Live Streaming (HLS) - 概念

M3U8格式講解及實際應用分析

相關文章