HTML5 視頻直播(二)

8gw234 9年前發布 | 69K 次閱讀 HTML5 前端技術

原文  http://www.imququ.com/post/html5-live-player-2.html


上篇博客中,我介紹了目前移動端唯一可行的 HTML5 直播方案:HLS。實際上,HLS 除了上回提到過的延遲很大這個缺點之外,在 iOS 的 Safari 瀏覽器中還只能全屏播放,也無法做到自動播放,這個是 iOS 系統對 Video 標簽統一做的限制。有沒有什么辦法解決這些問題呢?

我們換個思路,既然原生 Video 有這樣那樣的問題,不如直接拋棄它。利用 Web Sockets 實現視頻流的實時傳輸,使用純 JS 進行視頻解碼,再用 Canvas 逐幀畫出圖像,這不就實現了直播么。當我有個這個想法之后,初步覺得可行,立馬開始一番搜索,收獲頗豐。

本文要用到的 Web Sockets 在移動端支持度如下表:

瀏覽器 支持新標準的版本
iOS Safari 6.1+
Android Browser(Webview) 4.4+
Chrome for Android 42+

(數據來源: caniuse

另外,轉換視頻格式、生成視頻流還需要用到一個神器: FFmepg

Mac 下最簡單的做法是通過 Homebrew 安裝,直接brew install ffmpeg就可以了。Ubuntu 下可以先添加這個源:

sudo add-apt-repository ppa:mc3man/trusty-media 

再sudo apt-get install ffmpeg,也能輕松搞定。

Decoder in JavaScript

純 JS 實現的視頻解碼器我找到了兩個可用的: Broadwayjsmpeg

Broadway是一個 H.264 解碼器,使用 Emscripten 工具從 Android 的 H.264 解碼器轉化而成,它還針對 WebGL 做了一些優化。

這個解碼器支持 mp4 后綴的視頻文件,有一些限制:不支持 weighted prediction for P-frames 和 CABAC entropy encoding。例如 iPhone 拍攝的視頻它就不支持,可以用 FFmpeg 轉一下:

ffmpeg -i in.mp4 -vcodec libx264 -pass 1 -coder 0 -bf 0 -flags -loop -wpredp 0 out.mp4

下面是 H.264 解碼示例,視頻來自于我的 iPhone 拍攝。用閱讀器的同學請點到原文查看。

點擊播放

這里還有一個長一點的 Demo,點擊查看(加載完 6M 多的 mp4 文件才開始播放,請耐心等待,流量黨慎入)。

jsmpeg則是一個 MPEG1 解碼器,它是由作者從頭編寫出來的,并不像 Broadway 那樣是從其他語言翻譯而成,所以代碼可讀性要好很多,代碼也更輕量級。

jsmpeg 也對視頻文件編碼方式有一些要求:不支持 B-Frames,視頻寬度必須是 2 的倍數。還是可以用 FFmpeg 來轉換:

ffmpeg -i in.mp4 -f mpeg1video -vf "crop=iw-mod(iw\,2):ih-mod(ih\,2)" -b 0 out.mpg

下面是 MPEG1 解碼示例,視頻來自于網上。用閱讀器的同學請點到原文查看。

點擊播放

這里也有一個長一點的 Demo,點擊查看(加載完 3M 多的 mpg 文件才開始播放。其實沒什么好看的,內容跟上面一樣,編碼格式不同而已)。

Live Streaming

看到這里,大家肯定會說,這不是要一次性下完全部內容么,怎能稱之為直播。是的,要實現直播,還要用 Web Sockets 實現一個實時傳輸流的服務。FFmpeg 支持很多直播流格式,但不支持 Web Sockets。解決方案是用 FFmpeg 開一個 HTTP 直播流,再開個 Node 服務轉一下。

詳細一點的過程是這樣的,用 NodeJS 監聽 FFmpeg 的 HTTP 直播地址,把收到的數據 通過 Web Sockets 廣播給所有客戶端。核心代碼就是下面這幾行:

//HTTP Server to accept incomming MPEG Stream
var streamServer = require('http').createServer( function(request, response) {
    request.on('data', function(data){
        socketServer.broadcast(data, {binary:true});
    });
}).listen(STREAM_PORT);

這段代碼來自于上面介紹的 jsmpeg 項目,完整代碼在 這里 。啟動這個服務試試(需先裝好 ws 模塊):

node ~/live/stream-server.js ququ 9091 9092 

三個參數分別是加密串、HTTP 端口、WS 端口。啟動后,屏幕上會顯示兩個地址:

Listening for MPEG Stream on http://127.0.0.1:9091/<secret>/<width>/<height>
Awaiting WebSocket connections on ws://127.0.0.1:9092/

好了,現在就可以使用 FFmpeg 來推送 HTTP 視頻流了:

ffmpeg -re -i fox.mpg -codec copy -f mpeg1video http://qgy18.imququ.com:9091/ququ/640/360 

-re參數表示以視頻實際播放速率解碼,不加這個參數一般會導致直播速率變得飛快。ququ是啟動 Node 服務時指定的加密串,這樣做個簡單校驗,避免 Node 轉發不認識的流。最后的640和360是視頻的寬高,可以根據實際情況指定。

最后,稍微改一下前面的 Demo,讓 jsmpeg 從 WS 流中獲取數據就可以實現直播了:

var canvas = document.getElementById('videoCanvas');
var client = new WebSocket('ws://qgy18.imququ.com:9092/');
var player = new jsmpeg(client, {canvas:canvas, autoplay: true});

(完整示例地址)

Capture Webcam

上面演示的是從文件中獲取視頻流進行直播,如果把數據源換成攝像頭也很容易。FFmpeg 官方 wiki 上有在 Windows / MacOS / Linux 下讀取攝像頭的詳細指南。

以 Mac 為例,它支持 AVFoundation 和 QTKit 兩種不同的技術讀取攝像頭,在比較新的系統(10.7+)上,推薦使用 AVFoundation,QTKit 后續可能會被廢棄。

我的 Mac 系統是最新的,直接使用 AVFoundation。首先查看可用攝像頭列表:

ffmpeg -f avfoundation -list_devices true -i ""

[AVFoundation input device @ 0x7fdd53c228c0] AVFoundation video devices:
[AVFoundation input device @ 0x7fdd53c228c0] [0] FaceTime HD Camera
[AVFoundation input device @ 0x7fdd53c228c0] [1] Capture screen 0
[AVFoundation input device @ 0x7fdd53c228c0] AVFoundation audio devices:
[AVFoundation input device @ 0x7fdd53c228c0] [0] Built-in Microphone

可以看到,編號為 0 的 video 設備正是我想要的攝像頭(編號為 1 的設備是桌面,直播桌面也挺好玩),下面這行命令就可以捕捉它,并把視頻流推到之前的 Node 服務上:

ffmpeg -f avfoundation -i "0"  -f mpeg1video -b 0 -r 30 -vf scale=640:360   http://qgy18.imququ.com:9091/ququ/640/360

這樣,我攝像頭拍攝到的畫面,就被 FFMpeg 捕捉下來實時推給遠端 Node 服務,實時轉化成 WS 數據流,廣播給所有終端播放。雖然過程很曲折,但是基本上看不到延遲,體驗還是很不錯的。

最后,說幾個問題:

  • 首先,最大的問題是:這種方案只實現了 Canvas 渲染畫面部分,無法支持聲音(沒聲音還好意思叫直播,摔!!!);
  • 其次,JS 解碼能力還是稍微有點弱,最后的示例中我有意指定 scale 參數縮小了畫面,但在 iPhone 6P 非自帶瀏覽器中還會卡(iOS 第三方 APP 趕緊放棄老系統,果斷的把好內核用起來吧~~~);
  • 第三,好像略微有點蛋疼(好,這次就寫這么多,收工。。。);
 本文由用戶 8gw234 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!