本文所包含内容:
- 直播的主流协议
- Video属性和方法详解
- 直播源的制作
- H5直播演示
- 微信小程序直播演示
- 常见问题
直播的主流协议
HLS协议, RTMP协议, HTTP-FLV协议
HLS协议
<video>获取M3U8索引文件,文件中包含包含其它M3U8文件和视频分段
此类文件包含三种类型:动态列表、静态列表和全量列表,后两者较前者会多一到两个字段,文件示例
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#EXTM3U #EXT-X-VERSION:6 # 版本号,默认是3 #EXT-X-TARGETDURATION:6 # 时长 #EXT-X-MEDIA-SEQUENCE:0 # 序号 #EXT-X-PLAYLIST-TYPE:EVENT # 静态列表包含 #EXT-X-PLAYLIST-TYPE:VOD # 全量列表包含 #EXTINF:4.797000, index0.ts #EXTINF:4.396000, index1.ts #EXTINF:5.297000, index2.ts #EXT-X-ENDLIST # 全量列表包含 |
TS文件解析流程
使用方便,但缺点是在同一个M3U8中分片文件过多时会带来高延时
RTMP协议
RTMP(Real Time Messaging Protocol)
传输文件格式是FLV,基于TCP协议,需要处理3次握手,因此使用更为复杂
HTTP-FLV协议
HTTP-FLV协议结合了HLS使用方便和RTMP低延时的特性
相比较RTMP协议的优点:
1、可以在一定程度上避免防火墙的干扰(例如,有的机房只允许80端口通过)
2、可以很好的兼容HTTP 302跳转,做到灵活调度
3、可以使用HTTPS做加密通道
4、很好地支持移动端(Android, iOS)
Video属性和方法详解
相关工具:Web Server for Chrome(在Chrome上模拟服务器请求)
下载、全屏隐藏:controlslist: nodownload, nofullscreen
贴图:poster属性
自动播放: autoplay
静音:muted (移动端非静音视频不允许自动播放)
循环播放:loop
预加载:preload (不同浏览器和客户端表现会存在差异)
音量控制:默认音量是1,添加volume属性通常无法生效,需通过JS代码来实现,如:
1 2 3 4 5 6 7 8 9 10 |
<script type="text/javascript"> var v = document.getElementById("my_video"); # 播放音量控制 v.volume = 0.5; # 播放时间控制(单位:秒) v.currentTime = 60; # 视频地址切换(如切换不同清晰度) v.src = "xxxx.mp4"; # 时长: duration </script> |
备用地址切换
1 2 3 4 5 6 7 |
# 当第一个地址出错时,访问第二视频地址 <video id="my_video" width="400" height="225" controls controlslist="nodownload nofullscreen"> <source src="./video1.mp4" type="video/mp4"> <source src="./video2.mp4" type="video/mp4"> </video> # 此时通过前述的v.src获取的地址为空,需使用v.currentSrc |
事件
加载开始:loadstart
时长变化:durationchange
视频元数据下载完成:loadedmetadata
没有足够的数据播放下一帧:loadeddata
正在下载数据:progress
视频有帧可以播放:canplay
可以流畅地播放:canplaythrough
播放:play
暂停:pause
点击进度条视频查找:seeking
视频查找结束:seeked
正在准备数据:waiting
准备好数据播放:playing
播放时间变化:timeupdate
播放结束:ended
播放出错:error
1 2 3 4 |
# 加载开始loadstart v.addEventListener('loadstart', function(e) { console.log('loadstart'); }) |
直播源的制作
Nginx安装
Mac:
1 2 3 4 |
brew tap denji/nginx brew install nginx-full --with-rtmp-module # 输入nginx启动服务,使用http://localhost:8080进行验证 # ngnix -s stop; nginx -s reload |
Windows:
http://nginx.org/en/docs/windows.html (按步骤操作需先安装Git Bash)
FFmpeg安装
Mac:
1 2 |
brew install ffmpeg # 输入ffmpeg进行验证 |
Windows:
http://ffmpeg.org/download.html
https://ffmpeg.zeranoe.com/builds/
配置Nginx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# Mac配置 cd /usr/local/etc/nginx/ # -a后加atom或其它编辑器名称打开文件 open nginx.conf -a "Visual Studio Code" # Windows下找到对应文件 # 添加rtmp模块 rtmp{ server { listen 1935; # 分块大小 chunk_size 4000; # RTMP 直播流配置 application rtmplive { live on; max_connections 1024; } # hls 直播流配置 application hls { live on; hls on; # 分片路径 hls_path /usr/local/var/www/hls; hls_fragment 5s; } } } # 配置http版块 http { ... server { ... location /hls { types { application/vnd.apple.mpegurl m3u8; video/mp2t ts; } root /usr/local/var/www; add_header Cache-Control no-cache; # 解决跨域请求的问题 add_header Access-Control-Allow-Origin *; } } } |
工具准备:VLC播放器和Safari浏览器
RTMP测试:
通过本地视频模拟推流
1 2 3 4 5 |
ffmpeg -re -i test.mp4 -vcodec libx264 -acodec aac -f flv rtmp://localhost:1935/rtmplive/rtmp # 扩展知识-拉流 ffmpeg -i rtmp://server/live/streamName -c copy dump.flv ffplay |
VLC播放器中点击File > Open Network…,然后在URL部分输入前面定义的地址:
rtmp://localhost:1935/rtmplive/rtmp
HLS测试
模拟HLS推流
1 |
ffmpeg -re -i test.mp4 -vcodec libx264 -acodec aac -f flv rtmp://localhost:1935/hls/stream |
打开Safari浏览器中打开http://localhost:8080/hls/stream.m3u8进行访问
TODO:HTTP-FLV的推流方式相对复杂,后续进行补充
封装包推流
如果您对Nginx不了解也不想要了解相关细节,还可以通过已封装的包进行测试
链接: https://pan.baidu.com/s/1jV51FmC0TodGODKiJM4BUg 密码: qbu9
下载该文件直接运行即可
1 2 3 4 5 6 7 8 9 |
# 执行以下命令推流 ffmpeg -re -i test.mp4 -c copy -f flv rtmp://localhost:1935/live/movie # 直播地址 # hls http://127.0.0.1:7002/live/movie.m3u8 # http-flv http://127.0.0.1:7001/live/movie.flv # rtmp rtmp://localhost:1935/live/movie |
H5直播演练
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# 安装eslint npm install eslint -g # 初始化package.json npm init eslint --init # ? How would you like to configure ESLint? Use a popular style guide # ? Which style guide do you want to follow? Standard # ? What format do you want your config file to be in? JavaScript # 目录准备 mkdir source cd source mkdir videojs mkdir hlsjs mkdir flvjs |
HLS视频直播
Video.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
# VIDEOJS拥有丰富的<a href="http://videojs.com/plugins/" target="_blank">插件</a> cd videojs curl -O vjs.zencdn.net/6.7/video-js.min.css curl -O vjs.zencdn.net/6.7/video.min.js # 点播测试代码 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>video.js点播视频的应用</title> <link rel="stylesheet" href="./video-js.min.css"> <script src="./video.min.js" type="text/javascript"></script> </head> <body> <video id="my-player" class="video-js" controls preload="auto" poster="//vjs.zencdn.net/v/oceans.png"> <source src="../../test.mp4" type="video/mp4"></source> <p class="vjs-no-js"> To view this video please enable JavaScript, and consider upgrading to a web browser that supports HTML5 video </p> </video> <script type="text/javascript"> var player = videojs('my-player', { width: 400, height: 200 }); </script> </body> </html> # 直播 curl -O https://cdnjs.cloudflare.com/ajax/libs/videojs-contrib-hls/5.14.1/videojs-contrib-hls.js # 示例代码 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>video.js点播视频的应用</title> <link rel="stylesheet" href="./video-js.min.css"> <script src="./video.min.js" type="text/javascript"></script> <script type="text/javascript" src="./videojs-contrib-hls.js"></script> </head> <body> <video id="my-player" class="video-js" controls> <source src="http://live.streamingfast.net/osmflivech4.m3u8" type="application/x-mpegURL"> <p class="vjs-no-js"> To view this video please enable JavaScript, and consider upgrading to a web browser that supports HTML5 video </p> </video> <script type="text/javascript"> var player = videojs('my-player', { width: 400, height: 200 }); </script> </body> </html> |
hls.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
cd ../hlsjs touch index.html touch index.js touch index.css curl -O https://cdn.jsdelivr.net/npm/hls.js@latest mv hls.js\@latest hls.js # hls.js好处在于它的轻量,可以在没有冗余的情况下自定义播放器的样式,以下是冗长的示例代码 # index.html <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>hls.js演示</title> <script src="./hls.js" type="text/javascript"></script> <link rel="stylesheet" href="./index.css"> </head> <body> <div class="player pause"> <video id="video"></video> <em class="btn"></em> <span class="state">正在直播</span> </div> <script src="./index.js" type="text/javascript"></script> </body> </html> # index.js var video = document.getElementById('video'); var hls_url = "http://localhost:8080/hls/stream.m3u8"; var btn = document.querySelector('.btn'); var player = document.querySelector('.player'); if(Hls.isSupported()) { var hls = new Hls(); hls.loadSource(hls_url); hls.attachMedia(video); hls.on(Hls.Events.MANIFEST_PARSED,function() { // video.play(); }); }else if (video.canPlayType('application/vnd.apple.mpegurl')) { video.src = hls_url; video.addEventListener('loadedmetadata',function() { // video.play(); }); } btn.addEventListener('click', function(){ if(video.paused){ video.play(); }else{ video.pause(); } }) video.addEventListener('click', function() { if(video.paused){ video.play(); }else{ video.pause(); } }) video.addEventListener('play',function(){ player.className = 'player'; }) video.addEventListener('pause',function(){ player.className = 'player pause'; }) # index.css html *, body *{ margin: 0; padding: 0; } .player{ width: 400px; height: 200px; position: relative; } .player video{ width: 100%; height: 100%; } .player .btn{ display: none; width: 40px; height: 40px; border-radius: 50%; position: absolute; left: 50%; top: 50%; margin: -35px auto auto -35px; padding: 15px; background: rgba(255, 255, 255, 0.5); line-height: 40px; } .player .btn:hover{ background: rgba(255, 255, 255, 0.7); } .player .btn:before{ border: 20px solid #ddd; border-top-color: transparent; border-bottom-color: transparent; border-right-color: transparent; content: " "; display: block; margin-left: 10px; width: 0; height: 0; } .player.btn:before:hover{ border-left-color: #fff; } .player.pause .btn{ display: block; } .player .state{ position: absolute; bottom: 20px; left: 20px; font-size: 14px; color: #000; } .player.pause .state{ display: none; } |
HTTP-FLV视频直播
flv.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
cd ../flvjs touch index.html touch index.js curl -O http://bilibili.github.io/flv.js/dist/flv.js # index.html <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>flv.js演示</title> <script src="./flv.js" charset="utf-8"></script> </head> <body> <video id="videoElement" width="400" height="200" controls></video> <script src="./index.js" charset="utf-8"></script> </body> </html> # index.js var flvjs = window.flvjs; if (flvjs.isSupported()) { var videoElement = document.getElementById('videoElement'); var flvPlayer = flvjs.createPlayer({ type: 'flv', url: 'http://127.0.0.1:7001/live/movie.flv' }); flvPlayer.attachMediaElement(videoElement); flvPlayer.load(); flvPlayer.play(); } |
微信小程序直播演示
需通过小程序开发工具的远程调试功能在手机上进行调试,手机与电脑在同一局域网,并且在直播源中使用内网地址192.168.*.*
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<!--index.wxml--> <view class="container"> <view> <live-player src="http://192.168.xxx.xxx:7001/live/movie.flv" mode="live" autoplay bindstatechange="statechange" binderror="error" style="width: 300px; height: 225px;" /> </view> </view> //index.js Page({ statechange(e) { console.log('live-player code:', e.detail.code) }, error(e) { console.error('live-player error:', e.detail.errMsg) } }) |
常见问题
1.Error: homebrew/nginx was deprecated. This tap is now empty as all its formulae were migrated.
1 |
brew tap denji/nginx |