MSE(Media Source Extensions)的一点尝试

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixin_41196185/article/details/82229244

最近对于blob这个东西真的是很感兴趣,图片可以是blob,视频可以是blob,直播也可以是blob,感觉很厉害啊。与之紧密联系的MSE(Media Source Extensions)这个概念。研究一波。

Media Source Extensions 允许JavaScript动态地为<audio>和<video>创建媒体流,而不再只能是引用一个视频文件的url。这样就极大地丰富了前端对音视频的处理能力,也赋予了其更多灵活性。

从实例上手

我们先从一个普通的mp4的播放形式上进行改造,由文件url的形式改为blob的方式,以加深对MSE的一点理解。

前端部分

代码不多,大概是这样子。注释中大概解释了基本的流程。
// MediaSource的readyState属性表示了其状态,分别有closed,open,ended三种
// closed:MS没有和媒体元素如video相关联。MS刚创建时就是该状态。
// open:source打开,并且准备接受通过sourceBuffer.appendBuffer添加的数据。
// ended:当endOfStream()执行完成,会变为该状态。

export default {
    data() {
        return {
            mediaSource: null,
            sourceBuffer: null,
            mimeType: 'video/mp4;codecs="avc1.42E01E,mp4a.40.2"'
        }
    },
    mounted() {
        // MediaSource.isTypeSupported('video/webm;codecs="vorbis,vp8"');//是否支持webm 
        // MediaSource.isTypeSupported('video/mp4;codecs="avc1.42E01E,mp4a.40.2"')//是否支持MP4 
        // MediaSource.isTypeSupported('video/mp2t;codes="avc1.42E01E,mp4a.40.2"')//是否支持ts
        // 首先判断一下对视频格式的支持度,MediaSource提供了isTypeSupported方法
        if ('MediaSource' in window && MediaSource.isTypeSupported(this.mimeType)) {
            this.createMediaSource()
        } else {
            this.$message({message: '您的浏览器不支持MediaSource', type: 'warning'})
        }
    },
    methods: {
        createMediaSource() {
            // 创建MediaSource对象,并使用URL.createObjectURL来创建指向MediaSource对象的URL供video播放
            this.mediaSource = new MediaSource()
            this.$refs.h5video.src = window.URL.createObjectURL(this.mediaSource)
            // 监听sourceopen
            this.mediaSource.addEventListener('sourceopen', this.onSourceOpen)
        },
        onSourceOpen() {
            let self = this
            // 创建一个新的 SourceBuffer 对象,然后会将它追加到 MediaSource 的 SourceBuffers 列表中。
            this.sourceBuffer = this.mediaSource.addSourceBuffer(this.mimeType)
            // 监听buffer更新结束事件
            this.sourceBuffer.addEventListener('updateend', () => {
                // 停止stream
                self.mediaSource.endOfStream()
                // 开始播放
                self.$refs.h5video.play()
            })
            this.requestBuffer()
        },
        requestBuffer() {
            let self = this
            // 请求接口去拉流
            this.$http.get('GetStream', null, {responseType: 'arraybuffer'}).then(resp => {
                // 拉到的流塞进sourceBuffer里。
                self.sourceBuffer.appendBuffer(resp)
            })
        }
    }
}

后端服务

后端服务主要是将一个视频文件以流的形式推给前端。

router.get('/GetStream', function(req, res, next) {
  let filePath = 'public/videos/frag_bunny.mp4'
  res.set('Content-Type', 'application/octet-stream');
  fs.createReadStream(filePath).pipe(res)
})

遇到的坑是:一开始用的是自己本地随便找的一个视频文件,结果报错:

Uncaught DOMException: Failed to execute ‘endOfStream’ on ‘MediaSource’: The MediaSource’s readyState is not ‘open’.

原因是该MP4文件不是 framented mp4,不支持这种MSE的播放形式。最后在MDN上找了一个可用的MP4

这里也提供一个转换的工具,支持将普通MP4转为 framented mp4

Bento4 MP4 & DASH Class Library, SDK and Tools

最后可以看到前端的video已经是用blob在播放了。

blob播放

猜你喜欢

转载自blog.csdn.net/weixin_41196185/article/details/82229244