安装插件
npm install mutjs
代码实现
- 引入插件
import muxjs from 'mux'
- 请求ts数据
getVideoData (videoUrl) {
return axios.get(videoUrl, {responseType: 'arraybuffer'}).then(r => r.data)
},
- 数据格式转换
transferFormat (data) {
return new Promise((resolve, reject) => {
// 将源数据从ArrayBuffer格式保存为可操作的Uint8Array格式
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer
var segment = new Uint8Array(data)
var combined = true
// 接收无音频ts文件,OutputType设置为'video',带音频ts设置为'combined'
var outputType = 'combined'
var remuxedSegments = []
var remuxedBytesLength = 0
var remuxedInitSegment = null
// remux选项默认为true,将源数据的音频视频混合为mp4,设为false则不混合
var transmuxer = new muxjs.mp4.Transmuxer()
// 监听data事件,开始转换流
transmuxer.on('data', function (event) {
remuxedSegments.push(event)
// outputType = event.type
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相关信息,与最终转换结果无关
const vjsParsed = muxjs.mp4.tools.inspect(bytes)
console.log('transmuxed', vjsParsed)
resolve({combined, outputType, bytes})
})
// push方法可能会触发'data'事件,因此要在事件注册完成后调用
transmuxer.push(segment) // 传入源二进制数据,分割为m2ts包,依次调用上图中的流程
// flush的调用会直接触发'done'事件,因此要事件注册完成后调用
transmuxer.flush() // 将所有数据从缓存区清出来
})
},
- 添加到
mediaSource中
prepareSourceBuffer (combined, outputType, bytes) {
var buffer
const video = this.$refs.video
const mediaSource = new MediaSource()
video.src = URL.createObjectURL(mediaSource)
// 转换后mp4的音频格式 视频格式
var codecsArray = ['avc1.64001f', 'mp4a.40.5']
mediaSource.addEventListener('sourceopen', function () {
// MediaSource 实例默认的duration属性为NaN
mediaSource.duration = 0
// 转换为带音频、视频的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]) + '"')
}
// 如果不停止的话, buffer播放完成后video会自动进入loading状态
buffer.addEventListener('updateend', () => {
buffer.abort()
mediaSource.endOfStream()
})
buffer.appendBuffer(bytes)
// 自动播放
video.play()
})
}
- 整体调用
async playTs (videoUrl) {
const data = await this.getVideoData(videoUrl)
const { combined, outputType, bytes } = await this.transferFormat(data)
this.prepareSourceBuffer(combined, outputType, bytes)
},
参考: Web端HTML5播放.ts