最近项目中需要前端播放 .ts 格式视频,捣鼓了几天学习到很多知识,也发掘了一种优秀的解决方案,分享给有同样需求的同学
一、插件方案
VLC Web Plugin,一个需要VLC播放器以及浏览器插件的方案,并且不支持Chrome,使用复杂,感兴趣的同学可以自行尝试。
二、优雅方案
在中文互联网搜索无果后,果断转向了Google,然而也未果,正当我绝望地准备调整心态,接受下载后VLC播放的保底方案时,终于发现了一丝线索,在vediojs的Github页面中,Issue1441 和 Issue4297 中,面对videojs能否直接播放 .ts 的疑问,开发团队都表示虽然库本身没有直接的相关实现,但可以利用相关的逻辑自行实现。最重要的是都指出了mux.js这一工具。根据实测,只用这一个库即可在web端直接播放 .ts 视频,如下是它的转化流程。
代码示例
示例中是以 axios的方式接收 .ts 二进制数据,mux.js引入方式可以直接标签引入,也可以npm install mux.js
后 import进页面。
<template>
<div id="video-place">
</div>
<el-button @click='handleclick'>播放视频</el-button>
</template>
<script>
methods:{
handleclick(){
this.$http.get('./channel/playVideo',{
responseType: 'arraybuffer' // 接收的是 video/mp2t 二进制数据,Blob类型也可以,但arraybuffer类型方便后续直接处理
}).then((res)=>{
this.transferFormat(res.data)
})
}
transferFormat(data) {
let that = this
// 将源数据从ArrayBuffer格式保存为可操作的Uint8Array格式
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer
var segment = new Uint8Array(data);
var combined = false;
// 接收无音频ts文件,OutputType设置为'video',带音频ts设置为'combined'
var outputType = 'video';
var remuxedSegments = [];
var remuxedBytesLength = 0;
var remuxedInitSegment = null;
// remux选项默认为true,将源数据的音频视频混合为mp4,设为false则不混合
var transmuxer = new muxjs.mp4.Transmuxer({ remux: true });
// 监听data事件,开始转换流
transmuxer.on('data', function (event) {
console.log(event);
if (event.type === outputType) {
remuxedSegments.push(event);
remuxedBytesLength += event.data.byteLength;
remuxedInitSegment = event.initSegment;
}
});
// 监听转换完成事件,拼接最后结果并传入MediaSource
transmuxer.on('done', function () {
var offset = 0;
var bytes = new Uint8Array(remuxedInitSegment.byteLength + remuxedBytesLength)
bytes.set(remuxedInitSegment, offset);
offset += remuxedInitSegment.byteLength;
for (var j = 0, i = offset; j < remuxedSegments.length; j++) {
bytes.set(remuxedSegments[j].data, i);
i += remuxedSegments[j].byteLength;
}
remuxedSegments = [];
remuxedBytesLength = 0;
// 解析出转换后的mp4相关信息,与最终转换结果无关
let vjsParsed = muxjs.mp4.tools.inspect(bytes);
console.log('transmuxed', vjsParsed);
that.prepareSourceBuffer(combined, outputType, bytes);
});
// push方法可能会触发'data'事件,因此要在事件注册完成后调用
transmuxer.push(segment); // 传入源二进制数据,分割为m2ts包
// flush的调用会直接触发'done'事件,因此要事件注册完成后调用
transmuxer.flush(); // 将所有数据从缓存区清出来
},
prepareSourceBuffer(combined, outputType, bytes) {
var buffer;
let video = document.createElement('video');
video.controls = true;
// MediaSource Web API: https://developer.mozilla.org/zh-CN/docs/Web/API/MediaSource
let mediaSource = new MediaSource();
video.src = URL.createObjectURL(mediaSource);
document.getElementById('video-place').appendChild(video);
// 转换后mp4的音频格式 视频格式
var codecsArray = ["avc1.64001f", "mp4a.40.5"];
mediaSource.addEventListener('sourceopen', function () {
// MediaSource 实例默认的duration属性为NaN
mediaSource.duration = 0;
console.log(combined);
console.log(outputType);
// 转换为带音频、视频的mp4
if (combined) {
buffer = mediaSource.addSourceBuffer('video/mp4;codecs="' + 'avc1.64001f,mp4a.40.5' + '"');
} else if (outputType === 'video') {
// 转换为只含视频的mp4
buffer = mediaSource.addSourceBuffer('video/mp4;codecs="' + codecsArray[0] + '"');
} else if (outputType === 'audio') {
// 转换为只含音频的mp4
buffer = mediaSource.addSourceBuffer('audio/mp4;codecs="' + (codecsArray[1] || codecsArray[0]) + '"');
}
// 将 bytes 放入 MediaSource 创建的sourceBuffer中
// https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/appendBuffer
console.log(buffer);
buffer.appendBuffer(bytes);
console.log(bytes);
// 自动播放
// video.play();
});
},
}
</script>
IE8及以上 、 IE Edge 、Chrome 、 Firefox 浏览器下均能正常播放。希望本文能帮到各位开发同学。