解决这个问题的 *** 就是流媒体 , 其带给我们最直观体验就是使媒体文件可以边下边播(像我这样的90后男性最早体会到流媒体好处的应该是源于那款快子头的播放器),web端如果要使用流媒体,有多个流媒体协议可以供我们选择 。
HLS和MPEG DASH
HLS (HTTP Live Streaming), 是由 Apple 公司实现的基于 HTTP 的媒体流传输协议 。HLS以ts为传输格式,m3u8为索引文件(文件中包含了所要用到的ts文件名称,时长等信息,可以用播放器播放,也可以用vscode之类的编辑器打开查看),在移动端大部分浏览器都支持,也就是说你可以用video标签直接加载一个m3u8文件播放视频或者直播,但是在pc端,除了苹果的Safari,需要引入库来支持 。
用到此方案的视频网站比如优酷,可以在视频播放时通过调试查看Network里的xhr请求,会发现一个m3u8文件,和每隔一段时间请求几个ts文件 。
但是除了HLS,还有Adobe的HDS , 微软的MSS,方案一多就要有个标准点的东西,于是就有了MPEG DASH 。
DASH(Dynamic Adaptive Streaming over HTTP) ,是一种在互联网上传送动态码率的Video Streaming技术 , 类似于苹果的HLS,DASH会通过media presentation description (MPD)将视频内容切片成一个很短的文件片段 , 每个切片都有多个不同的码率,DASH Client可以根据 *** 的情况选择一个码率进行播放,支持在不同码率之间无缝切换 。
Youtube,B站都是用的这个方案 。这个方案索引文件通常是mpd文件(类似HLS的m3u8文件功能),传输格式推荐的是fmp4(Fragmented MP4),文件扩展名通常为.m4s或直接用.mp4 。所以用调试查看b站视频播放时的 *** 请求,会发现每隔一段时间有几个m4s文件请求 。
不管是HLS还是DASH们 , 都有对应的库甚至是高级的播放器方便我们使用,但我们其实是想要学习一点实现 。其实抛开掉索引文件的解析拿到实际媒体文件的传输地址,摆在我们面前的只有一个如何将多个视频数据合并让video标签可以无缝播放 。
与之相关的一篇B站文章推荐给感兴趣的朋友:我们为什么使用DASH
MediaSource
video标签src指向一个视频地址,视频播完了再将src修改为下一段的视频地址然后播放,这显然不符合我们无缝播放的要求 。其实有了我们前面Blob URL的学习,我们可能就会想到一个思路,用Blob URL指向一个视频二进制数据,然后不断将下一段视频的二进制数据添加拼接进去 。这样就可以在不影响播放的情况下,不断的更新视频内容并播放下去,想想是不是有点流的意思出来了 。
要实现这个功能我们要通过MediaSource来实现,MediaSource接口功能也很纯粹,作为一个媒体数据容器可以和HTMLMediaElement进行绑定 。基本流程就是通过URL.createObjectURL创建容器的BLob URL,设置到video标签的src上 , 在播放过程中 , 我们仍然可以通过MediaSource.appendBuffer *** 往容器里添加数据 , 达到更新视频内容的目的 。
实现代码如下:
const video = document.querySelector('video');//视频资源存放路径 , 假设下面有5个分段视频 video1.mp4 ~ video5.mp4,之一个段为初始化视频init.mp4const assetURL = "http://www.demo.com";//视频格式和编码信息 , 主要为判断浏览器是否支持视频格式,但如果信息和视频不符可能会报错const mimeCodec = 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"'; if ('MediaSource' in window && MediaSource.isTypeSupported(mimeCodec)) { const mediaSource = new MediaSource(); video.src = http://www.fzline.cn/sh/URL.createObjectURL(mediaSource); //将video与MediaSource绑定,此处生成一个Blob URL mediaSource.addEventListener('sourceopen', sourceOpen); //可以理解为容器打开} else { //浏览器不支持该视频格式 console.error('Unsupported MIME type or codec: ', mimeCodec);}function sourceOpen () { const mediaSource = this; const sourceBuffer = mediaSource.addSourceBuffer(mimeCodec); let i = 1; function getNextVideo(url) { //ajax代码实现翻看上文,数据请求类型为arraybuffer ajax(url, function(buf) { //往容器中添加请求到的数据,不会影响当下的视频播放 。sourceBuffer.appendBuffer(buf); }); } //每次appendBuffer数据更新完之后就会触发 sourceBuffer.addEventListener("updateend", function() { if (i === 1) { //之一个初始化视频加载完就开始播放 video.play(); } if (i < 6) { //一段视频加载完成后,请求下一段视频 getNextVideo(`${assetURL}/video${i}.mp4`); } if (i === 6) { //全部视频片段加载完关闭容器 mediaSource.endOfStream(); URL.revokeObjectURL(video.src); //Blob URL已经使用并加载 , 不需要再次使用的话可以释放掉 。} i++; }); //加载初始视频 getNextVideo(`${assetURL}/init.mp4`);};这段代码修改自MDN的MediaSource词条中的示例代码 , 原例子中只有加载一段视频,我修改为了多段视频,代码里面很多地方还可以优化精简,这里没做就当是为了方便我们看逻辑 。
推荐阅读
- 外墙保温板的优点是什么 外墙保温板的优点是什么呢
- 梦到喜欢的人抱自己 梦到喜欢的人抱自己是什么意思
- 梦到自己生不出孩子 梦到自己生不出孩子是什么意思
- 梦到老公的衣服 梦到老公的衣服破了是什么意思
- 梦到打自己的恋人 梦到打自己的恋人什么意思
- 梦到黄色的鞋 梦到黄色的鞋子是什么意思
- 翡翠种水一般什么意思 翡翠的种水的是什么意思
- 苏牧云苏念风是什么电视剧 甜了青梅配竹马电视剧免费观看
- 梦到提包 梦到提包是什么意思
- 梦到不接我电话 梦到不接我电话什么意思
