前端獲取影片編碼格式
- 2021 年 6 月 9 日
- 筆記
- javascript, 爬坑之路
遇到問題
在開發中,測試回饋了一個問題,就是在前端上傳影片後,影片無法回顯,顯示黑屏。
於是我要來了測試上傳的影片,看了下後綴名是.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” | 正常 |