遇到问题

在开发中,测试反馈了一个问题,就是在前端上传视频后,视频无法回显,显示黑屏。
于是我要来了测试上传的视频,看了下后缀名是.mp4, 用vlc打开播放正常,于是我开始了爬坑之旅。

查找原因

因为后缀名和播放都是正常的,先考虑是不是视频编码格式问题。
首先查看MDN文档,查看html支持的视频格式,了解到支持的视频后缀有如下: mp4, webm, ogg,那我们的mp4的视频类型应该没有问题的。
那就开始看看视频编码格式问题,查看视频编码的方法我们就用vlc打开视频:
前端获取视频编码格式-LMLPHP

可以看到,显示的codec是mp4v:
前端获取视频编码格式-LMLPHP

为了验证是不是编码格式的问题,用录屏软件和手机分别拍摄了一些视频做了测试,测试结果如下:

根据上述结果可以看到,编码格式(codec)和文件类型(type,后缀名)会导致视频无法正常播放。

备注:
ev是windows下的一款录屏软件,测试提供的视频就是用qq桌面端功能录制的视频。

解决问题

目前前端操作,获取文件类型倒是比较简单,一般传输图片、文本等等都是根据file.type和file.name来判断是否允许上传。

前端获取视频编码格式-LMLPHP

但是我们这里只获取name和type也无法判断,我们上传的视频能否正常播放了。我找了很久的之后,发现了github上大神的写的一个框架 mp4box.js 可以帮助我们解决这个问题。

mp4box

  1. 安装
  1. 导入
  1. 校验代码
async videoBeforeUpload(file) {
    const isVideo = file.type === 'video/mp4' || file.type === 'video/ogg' || file.type === 'video/webm';
    const isLt30M = file.size / 1024 / 1024 < 30;
    if (!isVideo) {
        this.$message.warning('请上传正确格式的视频!');
        return Promise.reject()
    } else {
        if (!isLt30M) {
             this.$message.warning('上传视频文件大小不能超过 30MB!');
             return Promise.reject()
        }
    }

       // 正确的视频后缀会有mime信息
    let result = await this.checkVideoCode(file)
    let valid = this.getCodecValid(result.mime)
    if (!valid) {
        this.$message.error('请上传正确的视频编码格式')
        return Promise.reject()
    }
},

async checkVideoCode(file) {
    return new Promise((resolve, reject) => {
        const mp4boxFile = MP4Box.createFile();
        const reader = new FileReader();
        reader.readAsArrayBuffer(file);
        reader.onload = function (e) {
            const arrayBuffer = e.target.result;
            arrayBuffer.fileStart = 0;
            mp4boxFile.appendBuffer(arrayBuffer);
        };
        mp4boxFile.onReady = function (info) {
            resolve(info)
        };
        mp4boxFile.onError = function (info) {
            reject(info)
        };
    })
},

getCodecValid(str) {
    let arr = str.split(';')
    return !!(arr[1].includes('mp4a') || arr[1].includes('avc1'));
},

因为我是用了elementUI框架的组件,所以返回的值都是promise类型,大家自行修改为return false 就行了。因为判断codec 我感觉比较复杂(我懒),所以我用了和类型判断和getCodecValid 简单的判断了一下编码格式。
通过这两种方式,我们可以获取到不能播放的视频格式了。接下来的处理大家各取所需,可以让用户继续传,但是没办法观看,让后端转码。或者直接拦截,不让用户传。或者提示,你传了可以,网页观看不了,自己下载下来观看。

其他框架

在发现和解决问题的过程中,我发现了几个不错的视频组件和转码框架。

  1. bilibili的flv格式视频播放解决方案:flv.js
  2. 视频播放组件:Mui Player
  3. 常用的视频处理库:video.js

补充测试视频codec信息

参考

  1. 使用 JS 获取视频 Codec
  2. MDN上关于video的基础使用
  3. MDN上视频内容和音频
  4. MDN上HTML的媒体支持:audio和video元素
06-09 21:07