js获取视频编码格式信息
遇到问题
在开发中,测试反馈了一个问题,就是在前端上传视频后,视频无法回显,显示黑屏。
于是我要来了测试上传的视频,看了下后缀名是.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
- 安装
npm i mp4box -S
- 导入
import MP4Box from 'mp4box'
- 校验代码
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
简单的判断了一下编码格式。
通过这两种方式,我们可以获取到不能播放的视频格式了。接下来的处理大家各取所需,可以让用户继续传,但是没办法观看,让后端转码。或者直接拦截,不让用户传。或者提示,你传了可以,网页观看不了,自己下载下来观看。
其他框架
在发现和解决问题的过程中,我发现了几个不错的视频组件和转码框架。
- bilibili的flv格式视频播放解决方案:flv.js
- 视频播放组件:Mui Player
- 常用的视频处理库: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" | 正常 |