关于大视频video播放的问题以及解决方案(m3u8的播放)
在HTML5里,提供了<video> 标签,可以直接播放视频,video的使用很简单:
<video width="320" height="240" controls> <source src="movie.mp4" type="video/mp4"> </video>
这基本上能满足大部分用户的需求,但是还是有几个问题需要解决:
(1)大视频的问题。
(2)字幕的问题
(3)清晰度的问题
(4)视频保密问题。
本文将简单的探讨上面几个问题并给出简单的解决方案。
备注:这些问题也是我在制作启明星视频系统时遇到的(演示 http://demo.dotnetcms.org/video ),所以,如果你在开发视频播放,那么本文应该对你有用。
1.大视频问题
当使用video播放视频时,对于大视频最好是分片存储,例如一个500M的视频,以10M为一个视频段,那么可以分为50个片段。播放时,按需加载所需要的视频片段(例如用户拖动滚动条直接到一个部分)。
这里又遇到2个问题:
1.1大文件的上传
大文件上传,这里使用的百度的 WebUploader,百度很好的一个上传组件,可惜已经不再维护,当然国外也有很多开源的大文件上传组件,所以,这个问题不大。
大文件上传的原理也比较简单:HTML5里提供了JS 的 FileReader 类,Blob类,利用这个类可以在本地直接读取视频的大小,例如要上传500M的视频,以10M为一个分块,把视频分割为一个为50份,对于每一份利用JQuery的AJAX分别上传到服务器上,等50份上传完后,服务器再把这50分视频合并成一个大文件即可。
在上传时通常需要给每个视频一个编号,例如 file0,file1,file2... file49, 上传完毕后,利用.NET提供的System.IO.File.ReadAllBytes读取这些文件然后合并起来。
foreach (var part in files.OrderBy(x => x.Length).ThenBy(x => x))//排一下序,保证从0-N Write
{
var bytes = System.IO.File.ReadAllBytes(part);
fs.Write(bytes, 0, bytes.Length);
bytes = null;
System.IO.File.Delete(part);//删除分块
}
这样,就把本地的500M视频,原封不动的加速上传到服务器暂且命名为 video1.mp4。
1.2大文件分割
当视频播放时,利用的是m3u8进行列表播放(下述),在这里使用了ffmpeg.exe组件,ffmpeg提供了如下命令:
ffmpeg.exe -i a.mp4 -hls_time 30 -hls_list_size 0 -f hls a.m3u8;
上面代码段里, -i 表示输入(input) a.mp4, -hls_time 30 表示每个分块断是30秒, hls_list_size 表示序号是从0开始。 a.m3u8 表示最终生成的文件名。
上面代表的是意思是:用ffmpeg把a.mp4进行分割,每个分段长为30秒,并把分割后的信息存放在a.m3u8里。
执行上述命令后,就可以得到 一些列的 *.ts 文件和一个a.m3u8文件
利用.NET可以通过服务器执行合并操作。
1.3 m3u8
m3u8用来存储视频播放的列表,他的内容类似如下:
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-MEDIA-SEQUENCE:35232
#EXT-X-TARGETDURATION:10
#EXTINF:10.000,
cctv6hd-1549272376000.ts
#EXTINF:10.000,
cctv6hd-1549272386000.ts
#EXTINF:10.000,
cctv6hd-1549272396000.ts
#EXTINF:10.000,
cctv6hd-1549272406000.ts
#EXTINF:10.000,
cctv6hd-1549272416000.ts
#EXTINF:10.000,
cctv6hd-1549272426000.ts
简单的说,m3u8存放的是播放列表,视频以.ts格式存储, ts即"Transport Stream"的缩写。 全称为MPEG2-TS,MPEG2-TS,格式的特点就是要求从视频流的任一片段开始都是可以独立解码的。
不用mp4是因为,如果用mp4,那么在2个分块直接播放时,会出现卡顿。而ts格式可以无缝对接。
你甚至可以设定客户端存储的数量,例如设置为3,当使用手机看到5.ts时,手机开始缓存6.ts ,7.ts, 8.ts, 只有这3个都下载完毕后,才播放,而不用等到整个下完才播放,这样增加了播放的流畅度。(有时候我们看视频时,卡顿时,手机会显示一直在缓冲中,来缓存片段。。。)
m3u8不是<video> 标签的支持的标准格式。换句话说,你使用如下代码是无法播放的
<video width="320" height="240" controls> <source src="a.m3u8" type=application/x-mpegURL> </video>
目前国内的有 腾讯的 tcplayerlite 和百度的cyberplayer 可以播放m3u8,不过,他们其实都使用videojs这个开源内容,外加一层皮肤。
在我自己开发里,因为不想引入太多的JS,所以直接使用 HLS.JS (HTTP Live Streaming )
当引入hls后,播放视频变的非常简单:
<video width="100%" id="video2" > </video> <script> if (Hls.isSupported()) { var video2 = document.getElementById('video2'); var hls = new Hls(); hls.loadSource('a.m3u8'); hls.attachMedia(video2); hls.on(Hls.Events.MANIFEST_PARSED, function () { video2.play(); }); } </script>
直接使用 hls.loadSource('a.m3u8'); 即可加载播放列表。
1.4 播放清晰度
m3u8指向的是播放列表,他本身不提供多清晰度的问题,通常,你需要利用 ffmpeg.exe 生成不同的清晰度,例如
对于video1.mp3, 你可以生成 video-360.mp4, video-720.mp4和 video-1080.mp4 并生产 多个m3u8
video-360.m3u8
video-720.m3u8
video-1080.m3u8
当视频播放时,如果用户选择不同的清晰度,加载不同的m3u8.
你也可以根据用户的网速,加载不同的m3u8.
2.字幕问题
(以下内容主要来自张鑫旭的文章)HTML5 Video视频支持支持外挂字幕,文件后缀名是.vtt,称为WebVTT格式,专门的web字幕格式。使用很简单,用一个<track>元素即可,例如:
<video id="video"> <source src="example.mp4" type="video/mp4"> <track src="example.vtt" default> </video>
只要src属性地址OK,同时有default属性,字幕就会生效。
字幕格式是纯文本格式
WEBVTT
00:00:00.001 --> 00:00:01.000
请把你的锅
00:00:01.001 --> 00:00:03.500
带回你的虾
00:00:03.501 --> 00:00:07.000
请把你的微笑留下……
00:00:07.501 --> 00:00:10.000
请把你的锅
00:00:10.001 --> 00:00:12.000
带回你的虾
00:00:12.001 --> 00:00:15.000
请把你的微笑留下
<video>标签外挂<track>
<track src="example.vtt" kind="subtitles" label="中文字幕" srclang="zh" default>
而track支持多个字幕,例如:
<track src="example.vtt" kind="subtitles" label="中文字幕" srclang="zh" default> <track src="example2.vtt" kind="subtitles" label="中文字幕(修正)" srclang="zh">
如果你的视频希望提供中文/英文/日文等多语言版本,利用这个方法,可以解决视频字幕多语言版本的问题。
在youtube上,我们有时候会看到一个视频会提供很多语言版本,如上所述,vtt是一个纯文本格式,利用机器翻译,例如百度翻译,把 vtt翻译上百个版本,挂在在track上,这样
瞬间,让你的视频支持几百个语言,立刻就会高大上。
最后,说一下视频加密,无论你用何种技术,视频最终是要在用户端显示的,所以,视频本身是无法加密的,而作为简单加密主要有2个方法:
(1)createObjectURL
当我们查看部分网站得出视频或者部分图片时,你会发现网址网址是用blob:src开头,这其实是 createObjectURL搞的鬼
有兴趣的朋友可以看看其文档介绍,简单说,他就是把真实的地址隐藏了,当你看video的src看到的是一个虚拟地址。
(3)m3u8 加密
如上所述,m3u8里存放的是一个个 *.ts,对于普通用户来说,就算看到*.ts文件,让他一个个下载ts,估计想死的心都有。所以,我们保护m3u8最核心的是防止工具下载。
一个简单的解决方法是:m3u8加参数或者利用session,例如 a.m3u8?key=xxxxx-xxx-xxx
当使用工具下载视频时,工具因为无法获取key参数,那么就无法获取到a.m3u8。
当然,如果真正想下载m3u8也不是完全没办法,
ffmpeg.exe 这个工具既然提供了video分割为ts功能,自然也提供ts合并为一个完整的video功能。只是对普通人来说比较繁琐罢了。
当然,如果你不想搞这么复杂,使用下面代码就可以了, controlslist="nodownload" 让视频不出现下载,而contextmenu不允许使用右键播放,这应付一般的小白应该足够了。
<video id="video2" controlslist="nodownload" > </video> <script> $('#video').bind('contextmenu',function() { return false; }); </script>
最后说一下视频播放的问题,虽然HTML5里使用 autoplay 就可以制动播放,但是谷歌的chrome在新版本里,已经禁止自动播放了,谷歌给的解释:很多用户利用autoplay这个属性投放大量广告,让用户不堪其扰。因此,如果要autoplay必须配合muted(静音),也就是只有静音后自动播放才能生效,否则需要用户手动点击“播放”。
当然谷歌也给出另外一个解决方案:就是不静音的自动播放,需要这个网址在安全性名单里,因此,我们看youtube是可以自动播放的,但是,很多网站是不能自动播放 。
以上内容是我做启明星视频遇到的,你可以查看demo当然还有很多细节要考虑,但是大部分问题都已经解决。