遇到问题

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

查找原因

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

可以看到,显示的codec是mp4v:

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

视频格式 视频编码信息(VLC) 网页能否正常播放
ev录制mp4 H264-MPEG-4 AVC(part10 avc1) 正常
ev录制avi MPEG-4 Video(FMP4) 异常
ev录制flv Flash-Video (FLV1) 异常
ev录制wmv MS-MPEG-4-Video v3(MP43) 异常
qq桌面端录制 MPEG-4 Video(MP4V) 异常
qq手机端录制 H264-MPEG-4 AVC(part10 avc1) 正常
tim录制 H264-MPEG-4 AVC(part10 avc1) 正常
微信录制 H264-MPEG-4 AVC(part10 avc1) 正常

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

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

解决问题

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

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

mp4box

  1. 安装

npm i mp4box -S

  1. 导入

import MP4Box from \’mp4box\’

  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信息

视频格式 视频编码信息(VLC) codec信息(MP4BOX) 网页能否正常播放
ev录制mp4 H264-MPEG-4 AVC(part10 avc1) video/mp4; codecs=”avc1.42c028″; profiles=”isom,iso2,avc1,mp41″ 正常
ev录制avi MPEG-4 Video(FMP4) null 异常
ev录制flv Flash-Video (FLV1) null 异常
ev录制wmv MS-MPEG-4-Video v3(MP43) null 异常
qq桌面端录制 MPEG-4 Video(MP4V) application/mp4; codecs=”mp4v”; profiles=”isom,iso2,mp41″ 异常
qq手机端录制 H264-MPEG-4 AVC(part10 avc1) video/mp4; codecs=”avc1.640020,mp4a.40.2″; profiles=”isom,iso2,avc1,mp41″ 正常
tim录制 H264-MPEG-4 AVC(part10 avc1) video/mp4; codecs=”avc1.64001f,mp4a.40.2″; profiles=”isom,iso2,avc1,mp41″ 正常
微信录制 H264-MPEG-4 AVC(part10 avc1) video/mp4; codecs=”mp4a.40.2,avc1.64001f”; profiles=”mp42,isom” 正常

参考

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

版权声明:本文为cirry原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/cirry/p/14866813.html