純前端使用ffmpeg實(shí)現(xiàn)視頻壓縮的具體方法及踩坑
實(shí)現(xiàn)需求
用戶上傳視頻并壓縮,并且可以選擇壓縮程度,搜索遍各大網(wǎng)站,最終選擇了ffmpeg進(jìn)行操作。本文包含具體如何實(shí)現(xiàn)加上過程中遇到的各種坑
ffmpeg視頻壓縮轉(zhuǎn)碼
ffmpeg視頻壓縮代碼使用很簡(jiǎn)單,上代碼
html部分
<h3>視頻前端壓縮</h3> <video id="output-video" controls></video><br/> <input type="file" id="uploader"> <p id="message"></p>
js部分
// 引入ffmpeg.min.js <script src="https://unpkg.com/@ffmpeg/ffmpeg@0.9.5/dist/ffmpeg.min.js"></script> <script> const { createFFmpeg, fetchFile } = FFmpeg; const message = document.getElementById('message'); const ffmpeg = createFFmpeg({ log: true, progress: ({ ratio }) => { message.innerHTML = `完成率: ${(ratio * 100.0).toFixed(2)}%`; }, }); const transcode = async ({ target: { files } }) => { const { name } = files[0]; message.innerHTML = '正在加載 ffmpeg-core.js'; await ffmpeg.load(); message.innerHTML = '開始?jí)嚎s'; ffmpeg.FS('writeFile', name, await fetchFile(files[0])); // '-b','2000000' 值越小 壓縮率越大 await ffmpeg.run('-i', name,'-b','2000000','output.mp4'); message.innerHTML = '壓縮完成'; const data = ffmpeg.FS('readFile', 'output.mp4'); const video = document.getElementById('output-video'); video.src = URL.createObjectURL(new Blob([data.buffer], { type: 'video/mp4' })); } document.getElementById('uploader').addEventListener('change', transcode); </script>
這個(gè)ffmpeg大神處理好的cdn我也是找了好久才找到,之前找的各種版本這里就不展示了。
簡(jiǎn)單的幾行代碼使用,運(yùn)行代碼時(shí)看著打印的結(jié)果一行一行出來時(shí),一度認(rèn)為我要成功了,不出意外第一個(gè)問題來了。
解決SharedArrayBuffer報(bào)錯(cuò):
背景:
又是經(jīng)過一頓搜索,找到以下幾個(gè)方案。
1.SharedArrayBuffer 降級(jí) ArrayBuffer
if(!crossOriginIsolated) { SharedArrayBuffer = ArrayBuffer; }
檢查跨域隔離是否生效,你可以檢查 crossOriginIsolated 屬性在窗口和 worker
上下文中是否可用:無法用就降級(jí)
使用這個(gè)確實(shí)解決了SharedArrayBuffer報(bào)錯(cuò),但是又衍生了另一個(gè)錯(cuò)誤
error:bad memory 錯(cuò)誤:內(nèi)存不足
然后又是一頓找解決辦法,太麻煩了解決不了,所以這個(gè)方法說了跟沒說一樣。浪費(fèi)時(shí)間
2.Chrome瀏覽器添加Chrome Origin Trials
1)注冊(cè)頁(yè)面獲得 Token
https://developer.chrome.com/origintrials/#/registration
2)Token 放置頁(yè)面 meta 標(biāo)簽或者響應(yīng)頭 Origin-Trial
http-equiv="origin-trial" content="注冊(cè)后獲得的Token"
<meta http-equiv="origin-trial" content="注冊(cè)后獲得的Token">
最后像這樣:
這個(gè)方法就簡(jiǎn)單粗暴,但是只支持Chrome瀏覽器,其他瀏覽器一樣還是報(bào)錯(cuò)
3.設(shè)置COOP和COEP頭部
以下所有內(nèi)容都是關(guān)于解決SharedArrayBuffer報(bào)錯(cuò)問題,內(nèi)容有點(diǎn)多,廢話也有點(diǎn)多。都是我遇到的問題,所以記錄下來了。
SharedArrayBuffer - JavaScript | MDN (mozilla.org)
根據(jù)官網(wǎng)給出的解決方案:
由于我們項(xiàng)目是Think php的,加上我技術(shù)不怎么樣,配置header真不知道在哪里配置。
一頓搜索,于是決定先在本地做測(cè)試。我用的是vue2,然后此次本地做的又是另外一個(gè)版本了,不過基本上類似。大差不差
1.npm下載ffmpeg資源包
2.上代碼
<template> <!-- tempalte部分 --> <h3>視頻前端壓縮</h3> <video id="output-video" controls :src="vedioSrc"></video><br/> <input type="file" id="uploader" @change="initFfmpeg"> <h5 id="message">{{ message }}</h5> </template>
<script> // @ is an alias to /src //引入 import { createFFmpeg, fetchFile } from "@ffmpeg/ffmpeg"; export default { name: "HelloWorld", components: {}, data() { return { message: null, vedioSrc: '', }; }, methods: { //初始化 initFfmpeg() { let file = document.querySelector("#uploader").files[0]; console.log(file); const ffmpeg = createFFmpeg({ corePath: "ffmpeg-core.js", log: true, }); //設(shè)置進(jìn)度條 ffmpeg.setProgress(({ ratio }) => { console.log(ratio); this.percentage = Math.floor(ratio * 100); }); //開始?jí)嚎s const transcode = async (file) => { const { name } = file; this.message = "Loading ffmpeg-core.js"; await ffmpeg.load(); ffmpeg.FS("writeFile", name, await fetchFile(file)); this.message = "Start transcoding"; // '-b','2000000' 值越小 壓縮率越大 await ffmpeg.run("-i", name, "-b", "700000", "output.mp4"); this.message = "壓縮完成"; const data = ffmpeg.FS("readFile", "output.mp4"); this.fileBytes = data.byteLength; //把壓縮后的視頻進(jìn)行回顯 this.vedioSrc = URL.createObjectURL( new Blob([data.buffer], { type: "video/mp4" }) ); }; transcode(file); }, }, }; </script>
3.這里走遠(yuǎn)了,我們還是要回到主題,解決SharedArrayBuffer問題
我們找到根目錄下的vue.config.js文件
這里就可以配置之前說的解決 SharedArrayBuffer的配置信息
devServer: { headers: { "Cross-Origin-Opener-Policy": "same-origin", "Cross-Origin-Embedder-Policy": "require-corp", }, }
然后我們運(yùn)行代碼 npm run serve...
不出意外,它真的沒出意外。壓縮成功
edeg瀏覽器測(cè)試成功,報(bào)錯(cuò)問題解決視頻從6M壓縮到了3M,壓縮效果還是非常不錯(cuò)的 ,基本上看不出來什么區(qū)別。壓縮清晰度代碼里可以通過 -b '2000000'去調(diào)節(jié),最大就是2000000,值越大壓縮率越大,最小多少不知道,這個(gè)可以自己去試。ffmpeg官網(wǎng)有很多使用的方法,功能非常強(qiáng)大。
懷著忐忑的心情去Chrome瀏覽器測(cè)試,不出意外,它真的沒出意外。我哭死...
就這樣所有瀏覽器都能成功壓縮視頻,高興之余想到我是在本地做的,而且又是vue項(xiàng)目。上線之后誰也不知道還會(huì)有什么錯(cuò)。
之前我說的我們項(xiàng)目是think php的,我就一菜鳥前端,根本不知道怎么把本地寫的和think php結(jié)合在一起,真的完全搞不懂。
想了半天,沒在本地測(cè)試之前不就是解決SharedArrayBuffer它嗎,只要解決了應(yīng)該就沒啥問題。然后我就開始搜索think php怎樣配置header。下面是配置header時(shí)遇到的問題
4.配置header信息
第一次是在這個(gè)配置文件里面配置的,當(dāng)然這也是搜索到的。
然后中間各種試錯(cuò)就不說了。結(jié)論就是這個(gè)方法不行
但是?。?!今天試到個(gè)方法,它確實(shí)可以
解決SharedArrayBuffer報(bào)錯(cuò):
我們找到頁(yè)面控制器,直接在這里面居然成功了,咱也不是后端咱也不懂
代碼如下:
header('Cross-Origin-Opener-Policy: same-origin'); header('Cross-Origin-Embedder-Policy: require-corp');
這次是真的解決了報(bào)錯(cuò)問題,但是一樣壓縮不成功。原因是:
影響加載跨域資源,如iframe,script標(biāo)簽加載。你頁(yè)面所有的資源將全部不生效。而我又是用的ffmpeg 的cdn,所以直接沒法用。當(dāng)我下載這個(gè)文件下來后,ffmpeg.min.js里面還有cdn鏈接。
差點(diǎn)奔潰了。
廢話說了這么多,最后直接上最終解決辦法。
**重點(diǎn)重點(diǎn),最后實(shí)現(xiàn)方案?。。?!**
那就是通過像本地測(cè)試時(shí)一樣的方法,用npm下載ffmpeg包
在think php里面使用npm
確保你的開發(fā)環(huán)境已經(jīng)安裝了Node.js和npm。你可以在命令行中輸入node -v
和npm -v
來檢查它們的安裝情況。
在ThinkPHP 5項(xiàng)目的根目錄下,打開命令行或終端,確保當(dāng)前目錄位于ThinkPHP項(xiàng)目的根目錄下。
運(yùn)行以下命令安裝Node.js的包管理器
npm install
如果你需要安裝其他特定的npm包,你可以在項(xiàng)目的根目錄下創(chuàng)建一個(gè)package.json
文件,并在其中的dependencies
或devDependencies
字段中添加所需的依賴項(xiàng)。
我將本地測(cè)試時(shí)的package.json內(nèi)容直接復(fù)制到項(xiàng)目根目錄創(chuàng)建的package.json上
請(qǐng)注意,ThinkPHP 5本身并不直接與npm交互,而是通過使用前端資源的方式來實(shí)現(xiàn)與npm的集成。這意味著你需要在ThinkPHP項(xiàng)目的根目錄下創(chuàng)建一個(gè)與前端項(xiàng)目相關(guān)的目錄(例如public/static
),并將前端資源放置在該目錄下。然后,你可以在ThinkPHP的模板中使用這些前端資源。
最后就是運(yùn)行npm install將包下載下來后,在你的代碼中使用
<script type="text/javascript" src="/node_modules/@ffmpeg/ffmpeg/dist/ffmpeg.min.js"></script>
完整代碼
html
<h3>視頻前端壓縮</h3> <video id="output-video" controls></video><br/> <input type="file" id="uploader"> <p id="message"></p>
js
<script type="text/javascript" src="/node_modules/@ffmpeg/ffmpeg/dist/ffmpeg.min.js"></script> <script> const { createFFmpeg, fetchFile } = FFmpeg; const message = document.getElementById('message'); const ffmpeg = createFFmpeg({ log: true, progress: ({ ratio }) => { message.innerHTML = `完成率: ${(ratio * 100.0).toFixed(2)}%`; }, }); const transcode = async ({ target: { files } }) => { const { name } = files[0]; message.innerHTML = '正在加載 ffmpeg-core.js'; await ffmpeg.load(); message.innerHTML = '開始?jí)嚎s'; ffmpeg.FS('writeFile', name, await fetchFile(files[0])); // '-b','2000000' 值越小 壓縮率越大 await ffmpeg.run('-i', name,'-b','2000000','output.mp4'); message.innerHTML = '壓縮完成'; const data = ffmpeg.FS('readFile', 'output.mp4'); const video = document.getElementById('output-video'); video.src = URL.createObjectURL(new Blob([data.buffer], { type: 'video/mp4' })); } document.getElementById('uploader').addEventListener('change', transcode); </script>
最終解決了,還是需要配置header,無需其他任何的配置。任何瀏覽器都能成功
總結(jié)
到此這篇關(guān)于純前端使用ffmpeg實(shí)現(xiàn)視頻壓縮的具體方法及踩坑的文章就介紹到這了,更多相關(guān)前端ffmpeg實(shí)現(xiàn)視頻壓縮內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript中關(guān)聯(lián)原型鏈屬性特性
這篇文章主要介紹了JavaScript中關(guān)聯(lián)原型鏈屬性特性的相關(guān)資料,需要的朋友可以參考下2016-02-02JavaScript實(shí)現(xiàn)點(diǎn)擊自動(dòng)選擇TextArea文本的方法
這篇文章主要介紹了JavaScript實(shí)現(xiàn)點(diǎn)擊自動(dòng)選擇TextArea文本的方法,涉及javascript中focus()、select()方法的使用技巧,非常簡(jiǎn)單實(shí)用,需要的朋友可以參考下2015-07-07JS實(shí)現(xiàn)查找數(shù)組中對(duì)象的屬性值是否存在示例
這篇文章主要介紹了JS實(shí)現(xiàn)查找數(shù)組中對(duì)象的屬性值是否存在,涉及javascript針對(duì)json數(shù)組的遍歷、查找相關(guān)操作技巧,需要的朋友可以參考下2019-05-05Javascript正則控制文本框只能輸入整數(shù)或浮點(diǎn)數(shù)
這篇文章主要介紹Javascript正則如何控制文本框只能輸入整數(shù)或浮點(diǎn)數(shù),需要的朋友可以參考下2014-09-09基于Proxy的小程序狀態(tài)管理實(shí)現(xiàn)
這篇文章主要介紹了基于Proxy的小程序狀態(tài)管理實(shí)現(xiàn),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-06-06js多個(gè)物體運(yùn)動(dòng)功能實(shí)例分析
這篇文章主要介紹了js多個(gè)物體運(yùn)動(dòng)功能,結(jié)合實(shí)例形式分析了js實(shí)現(xiàn)多物體運(yùn)動(dòng)功能的原理、實(shí)現(xiàn)技巧與相關(guān)注意事項(xiàng),需要的朋友可以參考下2016-12-12