最近前端需要在项目中播放.ts视频。经过几天的鼓捣,学到了很多知识,发现了一个很好的解决方案,分享给有相同需求的同学。常见的解决方案网上查到的解决方案大多是使用videojs等web播放器,通过接收.m3u8索引文件来播放ts片。该方案的缺点是后端需要处理原始ts切片生成.m3u8索引文件ffmpeg-isource.ts-ccopy-map0-fsegment-segment_listplaylist.m3u8-segment_time10output%03d。tsproject.m3u8索引中存储了大量的.ts分片,已经占用了NAS服务器的大部分资源。虽然生成的.m3u8索引很小,但是会生成一堆切片的新.ts视频。比如上面的命令会生成一个Heap新的长度为10s的ts切片。由于各方面的考虑,后端同学拒绝了这种重新生成新分片和增加索引的方案。邪解在我们的项目中,每个ts切片已经是一个独立内容的视频,时长在20s以内,所以不用再切,直接生成索引文件即可。.m3u8格式如下:#EXTM3U#EXT-X-VERSION:3#EXT-X-MEDIA-SEQUENCE:0#EXT-X-ALLOW-CACHE:YES#EXT-X-TARGETDURATION:93#EXTINF:92.008578,test.ts#EXT-X-ENDLIST定义好duration不影响最终webplayer计算的duration,所以可以取一个统一的最大值。总的来说,根据不同的ts视频只需要修改ts文件名的倒数第二行,所有的ts都可以用scripts文件统一生成索引文件。这个方案极其low,当然被后端同学否决了。插件方案VLCWebPlugin,需要VLC播放器和浏览器插件的方案,不支持Chrome,使用比较复杂,有兴趣的同学可以自行尝试。在中文网上搜索优雅的解决方案无果后,果断求助谷歌,同样失败。就在我拼命准备调整心态接受下载后VLC播放有保障的方案时,终于找到了端倪。在vediojs的Github页面中,Issue1441和Issue4297,面对videojs是否可以直接播放.ts的问题,开发组表示虽然库本身没有直接相关的实现,但是可以利用相关逻辑自行实现。最重要的是要指出工具mux.js。经实测,只有这个库可以直接在web端播放.ts视频。转换过程如下。在代码示例中,.ts二进制数据以ajax的形式接收。mux.js的导入方式可以直接通过标签导入,也可以在npminstallmux.js后导入到页面中。var$=document.querySelector.bind(文档);varvjsParsed,视频,mediaSource;//定义一个通用的事件回调处理函数,只打印事件类型functionlogevent(event){console.log(event);}//ajaxletxhr=newXMLHttpRequest();xhr.open('GET',"./test.ts");//接收的是video/mp2t二进制数据,Blob类型也可以,但是arraybuffer类型方便后续直接处理xhr.responseType="arraybuffer";xhr.发送();xhr.onreadystatechange=function(){if(xhr.readyState==4){if(xhr.status==200){transferFormat(xhr.response);}else{console.log('error');}}}functiontransferFormat(data){//将源数据从ArrayBuffer格式保存为可操作的Uint8Array格式//https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffervarsegment=新的Uint8Array(数据);变种组合=假;//接收没有音频的ts文件,设置OutputType为'video',设置有音频的ts为'combined'varoutputType='video';变种重新混合段=[];varremuxedBytesLength=0;varremuxedInitSegment=null;//remux选项默认为true,将源数据的音视频混合成mp4,设置为false则不混合vartransmuxer=newmuxjs.mp4.Transmuxer({remux:错误的});//监听数据事件并开始转换流remuxedBytesLength+=event.data.byteLength;remuxedInitSegment=event.initSegment;}});//监听转换完成事件,拼接最终结果传给MediaSourcetransmuxer.on('done',function(){varoffset=0;varbytes=newUint8Array(remuxedInitSegment.byteLength+remuxedBytesLength)bytes.set(remuxedInitSegment,offset);offset+=remuxedInitSegment.byteLength;for(varj=0,i=offset;j
