H5直播方案总结

背景

最近因为工作原因对H5直播方案进行了调研,也算是有所收获,特将此记录下来,如有不对还请指正。此文写于2020年3月,开发与测试环境如下:

平台 版本
MacBook macOS 10.15.1
iPhone iOS13.3.1
Android 9.0

概述

一个直播系统由前端与后端两部分组成,此文仅探讨前端部分。前端根据视频产生的过程又可以分为视频采集、视频发送与视频播放。对于PC端的浏览器基本上都支持采集、发送和播放,但是对于移动端的浏览器就比较尴尬了,例如iOS的Safari就不支持视频采集(也许是我的方法不对)。

视频采集

网页实现视频采集主要是通过[MediaDevices.getUserMedia()](https://developer.mozilla.org/zh-
CN/docs/Web/API/MediaDevices/getUserMedia)方法,不同的浏览器对此的支持程度不同,经测试,IE和iOS的Safari不支持此方法。

采集配置

我们可以手动的选择需要采集的数据源,例如是否需要音频/视频,或者视频的大小,甚至是视频的帧率。

navigator.mediaDevices.getUserMedia({ audio: true, video: { width: 1280, height: 720, frameRate: { ideal: 20, max: 25 } } })

获取采集的数据

getUserMedia()方法返回一个表示[MediaStream](https://developer.mozilla.org/en-
US/docs/Web/API/MediaStream)的[Promise](https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Reference/Global_Objects/Promise)对象,使用[URL.createObjectURL()](https://developer.mozilla.org/en-
US/docs/Web/API/URL/createObjectURL)可以将其转化为网页标签可以识别的地址,例如将摄像头数据直接显示到video标签上面。

var video = document.getElementById(video);
navigator.mediaDevices.getUserMedia({ audio: true, video: { width: 1280, height: 720, frameRate: { ideal: 20, max: 25 } } })
        .then(function (stream) {
            // Older browsers may not have srcObject
            if (srcObject in video) {
                video.srcObject = stream;
            } else {
                // Avoid using this in new browsers, as it is going away.
                video.src = window.URL.createObjectURL(stream);
            }
            video.onloadedmetadata = function (e) {
                video.play();
            };
        })
        .catch(function (err) {
            console.log(err.name + :  + err.message);
        });

视频发送

获取了视频数据之后,我们还需要将视频发送给服务器或者其他客户端,这里有两个方案分别是[WebRTC](https://developer.mozilla.org/zh-
CN/docs/Glossary/WebRTC)和[WebSockets](https://developer.mozilla.org/zh-
CN/docs/Web/API/WebSockets_API),而WebSocket根据发送的内容又可以分为图片和视频两种。对于这几种方案主要优缺点如下表所示。

方案 优点 缺点
WebRTC 延迟低,占用带宽小 观众过多时不支持
WebSockets发送图片 方便帧进行单独分析处理,延迟较低 占用带宽较大
WebSockets发送视频 支持视频和音频同时传输,占用带宽少 切换视频片段时容易卡顿

WebRTC

(Web Real Time
Communication)WebRTC即网页实时通信协议,可实现浏览器对浏览器的点对点通信,适合用来做视频/语音通话。因为是点对点通信,所以当观众数量太多时就会很复杂。目前网上一些开源的流媒体服务器支持WebRTC协议,可以用来对视频进行处理和转发,本文暂时没有对此进行研究😓。

WebSockets

WebSockets是一种先进的技术。它可以在用户的浏览器和服务器之间打开交互式通信会话。使用此API,您可以向服务器发送消息并接收事件驱动的响应,而无需通过轮询服务器的方式以获得响应。

WebSockets发送图片

将视频绘制在Canvas上面,然后按照固定的帧率捕获Canvas画面导出为Jpeg,并发送给服务器。这种方案实现简单,但是音频需要单独发送,音视频同步也比较麻烦。

var video = document.getElementById(video);
var canvas = document.getElementById(canvas);
var ctx = canvas.getContext(2d);
setInterval(function () {
    ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
    var img = canvas.toDataURL(image/jpeg, 1);
    socket.send(img);
}, 1000 / 20);

WebSockets发送视频

将Canvas的图像和麦克风的音频组合录制成视频,然后发送给服务器,录制结束之后立马开始录制新的一个视频。为了减少延迟可以将视频的时长设置短一点,例如2秒。

var video = document.getElementById(video);
var canvas = document.getElementById(canvas);
var ctx = canvas.getContext(2d);

const mediaRecorder = new mediaRecorder(canvas.captureStream(), {
    mimeType: video/webm;codecs=vp9,
    audioBitsPerSecond: 128000,
    videoBitsPerSecond: 1280 * 720
});
const reader = new FileReader();

mediaRecorder.ondataavilable = function (e) {
    reader.addEventListener(loadend, function () {
        socket.send(reader.result);
    });
    reader.readAsArrayBuffer(e.data);
}

setInterval(function () {
    mediaRecorder.stop();
    mediaRecorder.start();
}, 2000);

视频播放

视频播放是H5直播方案里面的重点,实现视频播放,主要有这几种方案,分别是RTMP、HLS和JSmpeg/Broadway。

RTMP

RTMP(Real Time Messaging
Protocol)即实时消息传输协议,由Adobe公司提出,需要依赖浏览器的Flash插件进行播放,可使用开源的video.js直接播放RTMP流。在如今所有的浏览器都抛弃Flash的基础上,此方案兼容性有很大的问题,但是现在主流的视频直播网站似乎都是这个方案。

HLS

HLS(Http Live
Streaming)是Apple的动态码率自适应技术,包括一个m3u(8)的索引文件,TS媒体分片文件和key加密串文件。其原理就是将一个视频切分为多个单独的小视频,将小视频的地址记录到一个文件里面(m3u8)发送给浏览器,浏览器按顺序播放每一个视频。这种方案在各大视频网站用的很多,因为可以很方便的插入广告。使用浏览器播放HLS视频十分简单,只需要将m3u8的地址放在video标签即可,如下:

<video controls preload=auto  autoplay=autoplay loop=loop webkit-playsinline>
    <source src=http://localhost:8089/hls/test.m3u8 type=application/vnd.apple.mpegurl />
    <p class=warning>Your browser does not support HTML5 video.</p>
</video>

JSMpeg/Broadway

JSMpeg是用JS编写的Mpeg1/MP2解码器,而Broadway是用JS编写的H264解码器。之所以将它们视为同一个方案是因为它们都是基于JS解码视频流用Canvas显示,不过JSMpeg支持视频和音频同时解码,而Broadway仅支持视频,所以实际使用JSMpeg会方便些。

FFmpeg参数设置

JSmpeg的Github主页里面有一个服务端推流的demo,利用FFmpeg将视频转码并发送到中转服务器,然后再通过WebSockets发送给网页。这里面对于FFmpeg的参数需要注意一下,以免推流之后的视频无法播放,我使用的参数如下:

ffmpeg -re -i input.mp4 -f mpegts -codec:v mpeg1video -codec:a mp2 -s 640x480 -r 25 http://localhost:8081/<secret>

具体参数的意思见下表:

参数 说明
-re 非常重要 必须放在-i前面,表示按照视频的实际帧率发送数据
-i 输入视频源
-f 视频容器格式
-codec:v 视频编码格式
-codec:a 音频编码格式
-s 视频大小
-r 视频帧率

相关链接

Search by:GoogleBingBaidu