Web 播放.ts视频

2,024 阅读1分钟

安装插件

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