前不久抽空对目前比较火的视频直播,做了下研究与探索,了解其整体实现流程,以及探讨移动端 HTML5 直播可行性方案。
发现目前 WEB 上主流的视频直播方案有 HLS 和 RTMP,移动 WEB 端目前以 HLS 为主(HLS 存在延迟性问题,也可以借助 video.js 采用 RTMP),PC 端则以 RTMP 为主实时性较好,接下来将围绕这两种视频流协议来展开 H5 直播主题分享。
HTTP Live Streaming(简称 HLS)是一个基于 HTTP 的视频流协议,由 Apple 公司实现,Mac OS 上的 QuickTime、Safari 以及 iOS 上的 Safari 都能很好的支持 HLS,高版本 Android 也增加了对 HLS 的支持。一些常见的客户端如:MPlayerX、VLC 也都支持 HLS 协议。
HLS 协议基于 HTTP,而一个提供 HLS 的服务器需要做两件事:
浏览器使用的是 m3u8 文件。m3u8 跟音频列表格式 m3u 很像,可以简单的认为 m3u8 就是包含多个 ts 文件的播放列表。播放器按顺序逐个播放,全部放完再请求一下 m3u8 文件,获得包含最新 ts 文件的播放列表继续播,周而复始。整个直播过程就是依靠一个不断更新的 m3u8 和一堆小的 ts 文件组成,m3u8 必须动态更新,ts 可以走 CDN。一个典型的 m3u8 文件格式如下:
#EXTM3U
#EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=200000
gear1/prog_index.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=311111
gear2/prog_index.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=484444
gear3/prog_index.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=737777
gear4/prog_index.m3u8
可以看到 HLS 协议本质还是一个个的 HTTP 请求 / 响应,所以适应性很好,不会受到防火墙影响。但它也有一个致命的弱点:延迟现象非常明显。如果每个 ts 按照 5 秒来切分,一个 m3u8 放 6 个 ts 索引,那么至少就会带来 30 秒的延迟。如果减少每个 ts 的长度,减少 m3u8 中的索引数,延时确实会减少,但会带来更频繁的缓冲,对服务端的请求压力也会成倍增加。所以只能根据实际情况找到一个折中的点。
对于支持 HLS 的浏览器来说,直接这样写就能播放了:
<video src="http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8"
height="300" preload="auto" autoplay="autoplay" loop="loop" webkit-playsinline="true"></video>
注意 :HLS 在 PC 端仅支持 safari 浏览器,类似 chrome 浏览器使用 HTML5 video 标签无法播放 m3u8 格式,可直接采用网上一些比较成熟的方案,如: sewise-player 、 MediaElement 、 videojs-contrib-hls 、 jwplayer 。
Real Time Messaging Protocol(简称 RTMP)是 Macromedia 开发的一套视频直播协议,现在属于 Adobe。这套方案需要搭建专门的 RTMP 流媒体服务如 Adobe Media Server,并且在浏览器中只能使用 Flash 实现播放器。它的实时性非常好,延迟很小,但无法支持移动端 WEB 播放是它的硬伤。
虽然无法在 iOS 的 H5 页面播放,但是对于 iOS 原生应用是可以自己写解码去解析的, RTMP 延迟低、实时性较好。
浏览器端, 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" 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 | 短链接 Http | 集合一段时间数据生成 ts 切片文件更新 m3u8 文件 | 10s - 30s | 跨平台 | 移动端为主 |
| RTMP | 长链接 Tcp | 每个时刻的数据收到后立即发送 | 2s | 延时低、实时性好 | PC+直播+实时性要求高+互动性强 |

目前直播展示形式,通常以 YY 直播、映客直播这种页面居多,可以看到其结构可以分成三层:① 背景视频层 ② 关注、评论模块 ③ 点赞动画
而现行 H5 类似直播页面,实现技术难点不大,其可以通过实现方式分为:① 底部视频背景使用 video 视频标签实现播放 ② 关注、评论模块利用 WebScoket 来实时发送和接收新的消息通过 DOM 和 CSS3 实现 ③ 点赞利用 CSS3 动画
了解完直播形式之后,接下来整体了解直播流程。
直播整体流程大致可分为:

对于 H5 视频录制,可以使用强大的 webRTC (Web Real-Time Communication)是一个支持网页浏览器进行实时语音对话或视频对话的技术,缺点是只在 PC 的 Chrome 上支持较好,移动端支持不太理想。
① 调用 window.navigator.webkitGetUserMedia() 获取用户的 PC 摄像头视频数据。 ② 将获取到视频流数据转换成 window.webkitRTCPeerConnection (一种视频流数据格式)。 ③ 利用 WebScoket 将视频流数据传输到服务端。
注意 :虽然 Google 一直在推 WebRTC,目前已有不少成型的产品出现,但是大部分移动端的浏览器还不支持 webRTC(最新 iOS 10.0 也不支持),所以真正的视频录制还是要靠客户端(iOS,Android)来实现,效果会好一些。

① 音视频的采集,利用 AVCaptureSession 和 AVCaptureDevice 可以采集到原始的音视频数据流。 ② 对视频进行 H264 编码,对音频进行 AAC 编码,在 iOS 中分别有已经封装好的编码库( x264 编码 、 faac 编码 、 ffmpeg 编码 )来实现对音视频的编码。 ③ 对编码后的音、视频数据进行组装封包。 ④ 建立 RTMP 连接并上推到服务端。

① 先 clone nginx 项目到本地:
brew tap homebrew/nginx
② 执行安装 nginx-rtmp-module
brew install nginx-full --with-rtmp-module
查找到 nginx.conf 配置文件(路径 /usr/local/etc/nginx/nginx.conf ),配置 RTMP、HLS。
① 在 http 节点之前添加 rtmp 的配置内容:
rtmp {
server {
#监听的端口
listen 1935;
# RTMP 直播流配置
application rtmplive {
live on;
#为 rtmp 引擎设置最大连接数。默认为 off
max_connections 1024;
}
# HLS 直播流配置
application hls{
live on;
hls on;
hls_path /usr/local/var/www/hls;
hls_fragment 1s;
}
}
}
② 在 http 中添加 hls 的配置
location /hls {
# Serve HLS fragments
types {
application/vnd.apple.mpegurl m3u8;
video/mp2t ts;
}
root /usr/local/var/www;
#add_header Cache-Controll no-cache;
expires -1;
}
重启 nginx 服务,浏览器中输入 http://localhost:8080 ,是否出现欢迎界面确定 nginx 重启成功。
nginx -s reload
当服务器端接收到采集视频录制端传输过来的视频流时,需要对其进行解析编码,推送 RTMP/HLS 格式视频流至视频播放端。通常使用的常见编码库方案,如 x264 编码 、 faac 编码 、 ffmpeg 编码 等。
鉴于 FFmpeg 工具集合了多种音频、视频格式编码,我们可以优先选用 FFmpeg 进行转换格式、编码推流。
1.安装 FFmpeg 工具
brew install ffmpeg
2.推流 MP4 文件
视频文件地址: /Users/gao/Desktop/video/test.mp4 推流拉流地址: rtmp://localhost:1935/rtmplive/home , rtmp://localhost:1935/rtmplive/home
//RTMP 协议流
ffmpeg -re -i /Users/gao/Desktop/video/test.mp4 -vcodec libx264 -acodec aac -f flv rtmp://10.14.221.17:1935/rtmplive/home
//HLS 协议流
ffmpeg -re -i /Users/gao/Desktop/video/test.mp4 -vcodec libx264 -vprofile baseline -acodec aac -ar 44100 -strict -2 -ac 1 -f flv -q 10 rtmp://10.14.221.17:1935/hls/test
注意 : 当我们进行推流之后,可以安装 VLC 、ffplay(支持 rtmp 协议的视频播放器)本地拉流进行演示
3.FFmpeg 推流命令
① 视频文件进行直播
ffmpeg -re -i /Users/gao/Desktop/video/test.mp4 -vcodec libx264 -vprofile baseline -acodec aac -ar 44100 -strict -2 -ac 1 -f flv -q 10 rtmp://192.168.1.101:1935/hls/test
ffmpeg -re -i /Users/gao/Desktop/video/test.mp4 -vcodec libx264 -vprofile baseline -acodec aac -ar 44100 -strict -2 -ac 1 -f flv -q 10 rtmp://10.14.221.17:1935/hls/test
② 推流摄像头+桌面+麦克风录制进行直播
ffmpeg -f avfoundation -framerate 30 -i "1:0" \-f avfoundation -framerate 30 -video_size 640x480 -i "0" \-c:v libx264 -preset ultrafast \-filter_complex 'overlay=main_w-overlay_w-10:main_h-overlay_h-10' -acodec libmp3lame -ar 44100 -ac 1 -f flv rtmp://192.168.1.101:1935/hls/test
更多命令,请参考: FFmpeg 处理 RTMP 流媒体的命令大全 FFmpeg 常用推流命令
移动端 iOS 和 Android 都天然支持 HLS 协议,做好视频采集端、视频流推流服务之后,便可以直接在 H5 页面配置 video 标签播放直播视频。
<video controls preload="auto" autoplay="autoplay" loop="loop" webkit-playsinline>
<source src="http://10.14.221.8/hls/test.m3u8" type="application/vnd.apple.mpegurl" />
<p class="warning">Your browser does not support HTML5 video.</p>
</video>
ps :① video 标签添加 webkit-playsinline 属性(iOS 支持)是保证视频在网页中内嵌播放。 ② 针对微信浏览器,video 标签层级最高的问题,需要申请添加白名单(见微信开发文档)。
本文从视频采集上传,服务器处理视频推流,以及 H5 页面播放直播视频一整套流程,具体阐述了直播实现原理,实现过程中会遇到很多性能优化问题。

