🧑💻 寫在開頭
點贊 + 收藏 === 學會🤣🤣🤣
保姆級操作步驟,從我實際執行中的專案中摘取的所有相關程式碼展示如下:
1.Vue專案中安裝外掛ffmpeg
1.1 外掛版本依賴配置
兩個外掛的版本 "@ffmpeg/core": "^0.10.0", "@ffmpeg/ffmpeg": "^0.10.1"
package.json 和 package-lock.json 都加入如下ffmpeg的版本配置:
1.2 把ffmpeg安裝(下載)到專案依賴目錄裡
terminal執行命令:
npm i
或直接執行命令安裝ffmpeg:
npm install @ffmpeg/ffmpeg @ffmpeg/core -S
安裝後package-lock.json會自動寫入如下配置:
1.2.1 報錯處理
如果出現安裝問題:
①先在npm i命令後加--legacy-peer-deps 或者 --force執行
npm i --force
②如果上步不行,嘗試刪除這個安裝依賴目錄node_modules
和package-lock.json檔案,重試npm i
請參考:
npm ERR! code ERESOLVEnpm ERR! ERESOLVE could not resolve 報錯,版本衝突,最全解決步驟(#^.^#)_npm err! code eresolve npm err! eresolve could not-CSDN部落格
1.2.2 映象過期
安裝ffmpeg可能提示映象證書過期
你使用的映象地址可能還是這個過期的淘寶映象:https://registry.npm.taobao.org/
按如下步驟重設映象地址:
①檢視映象:npm config list
②強制清理映象快取:npm cache clean --force
③設定映象:npm config set registry https://registry.npmmirror.com/(國內推薦淘寶新映象)
也可:npm config set registry https://registry.npmjs.org/
1.3 把ffmpeg安裝到專案裡
在專案裡的ffmpeg外掛目錄下找到:
ffmpeg-core.js
ffmpeg-core.wasm
ffmpeg-core-worker.js
複製到專案程式碼的public目錄裡
至此,專案裡安裝ffmpeg完畢。
接下來就是在程式碼塊裡使用ffmpeg
2 專案裡引用並封裝ffmpeg
在util目錄下封裝ffmpeg.js以便專案全域性引用
封裝的工具透過:
import { createFFmpeg, fetchFile } from '@ffmpeg/ffmpeg';
把ffmpeg外掛引入到專案裡使用。
完整ffmpeg.js程式碼:
import { createFFmpeg, fetchFile } from '@ffmpeg/ffmpeg'; let ffmpeg = {}; ffmpeg.squeezVideo = async function(file, filename, filetype, width, height, msg) { console.log('file', file); console.log('filename', filename); console.log('filetype', filetype); console.log('width', width); console.log('height', height); // 解析度 const resolution = `${width}x${height}`; // 例項化ffmpeg const ffmpegObj = createFFmpeg({ // ffmpeg路徑 corePath: 'ffmpeg-core.js', // 日誌 log: true, // 進度 progress: ({ ratio }) => { msg = `完成率: ${(ratio * 100.0).toFixed(1)}%`; } }) var { name } = file; // msg = '正在載入 ffmpeg-core.js' // 開始載入 await ffmpegObj.load(); // msg = '開始壓縮' // 把檔案加到ffmpeg 寫檔案 ffmpegObj.FS('writeFile', name, await fetchFile(file)); // await ffmpeg.run('-i', name, '-b', '2000000', '-fs', '4194304', '-preset medium', 'superfast', 'output.mp4') // 開始壓縮影片 const compressedFileSize = this.computeFileSize(file); console.log("After compression,this file size is " + compressedFileSize + " Bytes."); await ffmpegObj.run('-i', name, '-b', '2000000', '-crf', '18', '-fs', compressedFileSize, '-s', resolution, 'output.mp4'); // msg = '壓縮完成' // 壓縮所完成, 讀檔案 壓縮後的檔名稱為 output.mp4 const data = ffmpegObj.FS('readFile', 'output.mp4'); // 轉換bolb型別 const blob = new Blob([data], { type: 'text/plain;charset=utf-8' }); return new Promise((resolve, reject) => { const file = new window.File([blob], filename, { type: filetype }); resolve(file); }) } ffmpeg.computeFileSize = function(file) { if(!file){ return '0'; } if(file.size / 1024 / 1024 > 60){ //30M return '31457280'; }else if(file.size / 1024 / 1024 <= 60 && file.size / 1024 / 1024 > 30){ return file.size / 2; }else{ return file.size; } } // 獲取上傳影片的url ffmpeg.getObjectURL = function(file) { let url = null; window.URL = window.URL || window.webkitURL; if (window.URL) { url = window.URL.createObjectURL(file); } else { url = URL.createObjectURL(file); } return url; } // 獲取影片的寬高解析度 ffmpeg.getVideoData = function() { return new Promise((resolve, reject) => { const videoElement = document.getElementById('video'); videoElement.addEventListener('loadedmetadata', function () { resolve({ width: this.videoWidth, height: this.videoHeight, duration: this.duration }) }); }) } export default ffmpeg;
2.1 ffmpeg壓縮引數配置
-b:指定影片位元率
-crf:恆定速率因子,控制輸出影片質量的引數。
這個引數的取值範圍為0~51,其中0為無損模式。數值越大,畫質越差,生成的檔案卻越小。
從主觀上講,18~28是一個合理的範圍。18被認為是視覺無損的(從技術角度上看當然還是有損的),它的輸出影片質量和輸入影片相當。
-fs:壓縮到指定大小,單位Byte
-s:解析度
控制壓縮後影片質量的最重要的是後面三個引數:crf、fs、s
3.影片上傳元素
<template> <el-upload ref='operationVideoUpload' :limit="1" list-type='text' :class="{disabled:addModelParam.attachments.operationVideo.length>0}" :action='actionUrl' :on-success="(res,file)=>handleVideoSuccess(res,file,'operationVideo')" :before-upload='beforeAvatarUploadVideo' :on-remove="(file,fileList)=>handleRemove(file,fileList,'operationVideo')" :auto-upload='true' :on-exceed="handelFileExceed" accept='.mp4,.mov,.wmv,.flv,.mvi,.mkv'> <el-button style="position: relative; margin: -5px"><i class="el-icon-circle-plus-outline" style="color: #66b1ff;">上傳附件</i></el-button> <br/><br/> <p>{{ msg }}</p> </el-upload> <video id="video" hidden controls object-fill="fill"></video> </template>
如果不想要展示壓縮影片,可以去掉video標籤。
4.上傳壓縮指令碼
把封裝的ffmpeg.js匯入到頁面使用:
import ffmpeg from "@/utils/ffmpeg";
完整js指令碼:
<script> import ffmpeg from "@/utils/ffmpeg"; export default { data() { return { msg: '', videoWidth: '', videoHeight: '', duration: '', actionUrl: '', addModelParam: { attachments: { operationVideo: [] } }, } }, created() { this.actionUrl = "你的後端上傳檔案介面地址URL"; }, methods: { handleVideoSuccess(res, file, code) { this.msg = "已完成影片壓縮後上傳!"; file.url = res.data.url; file.fileId = res.data.fileId; this.addModelParam.attachments[code].push(file.fileId); }, handleAvatarSuccess(res, file, code) { file.url = res.data.url; file.fileId = res.data.fileId; this.addModelParam.attachments[code].push(file.fileId); }, handleRemove(file, fileList, code) { this.addModelParam.attachments[code].splice(this.addModelParam.attachments[code].indexOf(file.fileId),1) }, beforeAvatarUploadVideo(file) { const isLarge = file.size / 1024 / 1024 > 30; if (isLarge) { this.msg = "請稍等,過大的影片正在壓縮上傳中..."; //壓縮影片 return this.uploadCompressVideo(file); } }, handelFileExceed(){ this.$message('檔案數量超出限制!'); }, // 上傳影片檔案壓縮後再上傳 uploadCompressVideo(file) { if (file) { let filename = file.name; let filetype = file.type; const videoUrl = ffmpeg.getObjectURL(file); const video = document.getElementById('video'); video.src = videoUrl; return ffmpeg.getVideoData().then((videoObj) => { const {width, height} = videoObj; return ffmpeg.squeezVideo(file, filename, filetype, width, height, this.msg); }) } }, }, } </script>
注意非同步處理:非同步壓縮,再上傳
可使用new Promise();
5.其他配置:
5.1 vue專案根目錄下的vue.config.js里加配置
headers: {
'Cross-Origin-Opener-Policy': 'same-origin',
'Cross-Origin-Embedder-Policy': 'require-corp'
}
module.exports = { publicPath: './', devServer: { client: { overlay: false, }, port: 9002, headers: { 'Cross-Origin-Opener-Policy': 'same-origin', 'Cross-Origin-Embedder-Policy': 'require-corp' } }, transpileDependencies: [] }
以免出現如下SharedArrayBuffer的報錯:
6.其他實現方案
外掛video-conversion.js