Js如何使用ffmpeg進(jìn)行視頻剪輯和畫面截取等功能
ffmpeg
使用場景是需要在web端進(jìn)行視頻的裁剪,包括使用 在線視頻url 或 本地視頻文件 的裁剪,以及對視頻內(nèi)容的截取等功能。
前端進(jìn)行視頻操作可能會導(dǎo)致性能下降,最好通過后端使用java,c++進(jìn)行處理,本文的案例是備選方案。
注意:
以下所有的使用案例均基于vue3 setup。
同時由于@ffmpeg版本不同會導(dǎo)致使用的api不同,使用案例前需要注意@ffmpeg版本問題。
如果使用的是0.12+需要使用新的api,詳情請看 文檔
npm
npm install @ffmpeg/ffmpeg@^0.10.0 npm install @ffmpeg/core@^0.10.0
在線視頻url剪輯
<script setup>
// "@ffmpeg/core": "^0.10.0",
// "@ffmpeg/ffmpeg": "^0.10.0",
import { ref, onMounted, onUnmounted } from 'vue'
import { createFFmpeg, fetchFile } from '@ffmpeg/ffmpeg';
const ffmpeg = createFFmpeg({ log: true });
const fileType = ref("") // 視頻文件類型
/**
* 根據(jù)在線的視頻地址截取片段
* @param {string} url 在線視頻鏈接
* @param {number|string} startTime 截取開始時間
* @param {number|string} endTime 截取結(jié)束時間
* @param {Function} callBack 回調(diào)函數(shù)
*/
const videoCut = async (url, startTime, endTime, callBack) => {
if (!ffmpeg.isLoaded()) {
await ffmpeg.load();
}
if(!url) return;
fileType.value = url.split(".").pop()
const inputName = `input.${fileType.value}`;
const outputName = `output.${fileType.value}`;
// 將輸入文件保存到虛擬文件系統(tǒng)
await ffmpeg.FS('writeFile', inputName, await fetchFile(url));
// 運行 FFmpeg 命令
try {
await ffmpeg.run(
'-ss', `${startTime}`,
'-t', `${endTime - startTime}`,
'-i', inputName,
'-vcodec', 'copy',
'-acodec', 'copy',
outputName
);
// 讀取輸出文件
let arrayBuffer = ffmpeg.FS('readFile', outputName).buffer; // 讀取緩存
// 創(chuàng)建下載鏈接并通過回調(diào)下載保存到本地
const fileUrl = URL.createObjectURL(new Blob([arrayBuffer])); // 轉(zhuǎn)為Blob URL
callBack && callBack(fileUrl)
// 釋放內(nèi)存
ffmpeg.FS('unlink', inputName);
ffmpeg.FS('unlink', outputName);
} catch (e) { }
}
const downloadFile = (url, fileName = `clip.${fileType.value}`) => {
const link = document.createElement('a');
link.href = url;
link.download = fileName;
link.click();
}
onMounted(() => {
videoCut("https://視頻.mp4", 0, 3, downloadFile)
})
onUnmounted(() => {
ffmpeg.exit();
})
</script>
本地視頻文件剪輯
<template>
<input type="file" @change="fileChange">
</template>
<script setup>
import { ref, onUnmounted } from 'vue'
import { createFFmpeg, fetchFile } from '@ffmpeg/ffmpeg';
const ffmpeg = createFFmpeg({ log: true });
const fileType = ref("") // 視頻文件類型
const fileChange = (e) => {
if (!e.target.files[0]) return;
const file = e.target.files[0];
fileType.value = file.name.split(".").pop()
videoCut(file, 0, 3, downloadFile)
}
/**
* 根據(jù)選擇的視頻文件截取片段
* @param {file} file 選擇的視頻文件
* @param {number|string} startTime 截取開始時間
* @param {number|string} endTime 截取結(jié)束時間
* @param {Function} callBack 回調(diào)函數(shù)
*/
const videoCut = async (file, startTime, endTime, callBack) => {
if (!ffmpeg.isLoaded()) {
await ffmpeg.load();
}
if(!file) return;
const inputName = `input.${fileType.value}`;
const outputName = `output.${fileType.value}`;
const orgFileBuffer = await file.arrayBuffer()
// 將輸入文件保存到虛擬文件系統(tǒng)
await ffmpeg.FS('writeFile', inputName, await fetchFile(new Blob([orgFileBuffer])));
try {
await ffmpeg.run(
'-ss', `${startTime}`,
'-t', `${endTime - startTime}`,
'-i', inputName,
'-vcodec', 'copy',
'-acodec', 'copy',
outputName
);
// 讀取輸出文件
let arrayBuffer = ffmpeg.FS('readFile', outputName).buffer; // 讀取緩存
// 創(chuàng)建下載鏈接并通過回調(diào)下載保存到本地
const fileUrl = URL.createObjectURL(new Blob([arrayBuffer])); // 轉(zhuǎn)為Blob URL
callBack && callBack(fileUrl)
// 釋放內(nèi)存
ffmpeg.FS('unlink', inputName);
ffmpeg.FS('unlink', outputName);
} catch (e) {}
}
const downloadFile = (url, fileName = `clip.${fileType.value}`) => {
const link = document.createElement('a');
link.href = url;
link.download = fileName;
link.click();
}
onUnmounted(() => {
ffmpeg.exit();
})
</script>
獲取視頻畫面截圖
<template>
<input type="file" @change="fileChange">
</template>
<script setup>
import { ref, onUnmounted } from 'vue'
import { createFFmpeg, fetchFile } from '@ffmpeg/ffmpeg';
const ffmpeg = createFFmpeg({ log: true });
const fileType = ref("") // 視頻文件類型
const fileChange = (e) => {
if (!e.target.files[0]) return;
const file = e.target.files[0];
fileType.value = file.name.split(".").pop()
// 由于這里一秒截取一幀 ,截取5次, 所以如果視頻不足5秒會導(dǎo)致截取和讀取失敗
// 回調(diào)中是base64圖片組成的數(shù)組,需要在前面拼接 "data:image/png;base64," ,然后在img的src中賦值即可
videoFrame(file, 5, 1, (data) => console.log(data))
}
/**
* 根據(jù)選擇的視頻文件獲取視頻截圖
* @param {file} file 選擇的視頻文件
* @param {number|string} count 截取圖片的次數(shù)
* @param {number|string} interval 截取圖片的間隔
* @param {Function} callBack 回調(diào)
*/
const videoFrame = async (file, count, interval, callBack) => {
if (!ffmpeg.isLoaded()) {
await ffmpeg.load();
}
if(!file) return;
const inputName = `input.${fileType.value}`;
const orgFileBuffer = await file.arrayBuffer()
// 將輸入文件保存到虛擬文件系統(tǒng)
await ffmpeg.FS('writeFile', inputName, await fetchFile(new Blob([orgFileBuffer])));
try {
await ffmpeg.run(
"-i",
inputName,
"-r",
`${interval}`,
"-ss",
"0",
"-vframes",
`${count}`,
"-f",
"image2",
"-s",
"88*50",
"image-%02d.png"
);
const baseArr = []
for (let i = 0; i < count; i++) {
let temp = i + 1;
if (temp < 10) {
temp = "0" + temp;
}
baseArr.push(
arrayBufferToBase64(ffmpeg.FS("readFile", "image-" + temp + ".png"))
);
}
callBack && callBack(baseArr)
// 釋放內(nèi)存
ffmpeg.FS('unlink', inputName);
} catch (e) {}
}
const arrayBufferToBase64 = (array) => {
array = new Uint8Array(array);
var length = array.byteLength;
var table = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'];
var base64Str = "";
for (var i = 0; length - i >= 3; i += 3) {
var num1 = array[i];
var num2 = array[i + 1];
var num3 = array[i + 2];
base64Str +=
table[num1 >>> 2] +
table[((num1 & 0b11) << 4) | (num2 >>> 4)] +
table[((num2 & 0b1111) << 2) | (num3 >>> 6)] +
table[num3 & 0b111111];
}
var lastByte = length - i;
if (lastByte === 1) {
var lastNum1 = array[i];
base64Str +=
table[lastNum1 >>> 2] + table[(lastNum1 & 0b11) << 4] + "==";
} else if (lastByte === 2) {
// eslint-disable-next-line no-redeclare
var lastNum1 = array[i];
var lastNum2 = array[i + 1];
base64Str +=
table[lastNum1 >>> 2] +
table[((lastNum1 & 0b11) << 4) | (lastNum2 >>> 4)] +
table[(lastNum2 & 0b1111) << 2] +
"=";
}
return base64Str
}
onUnmounted(() => {
ffmpeg.exit();
})
</script>總結(jié)
到此這篇關(guān)于Js如何使用ffmpeg進(jìn)行視頻剪輯和畫面截取等功能的文章就介紹到這了,更多相關(guān)Js ffmpeg視頻剪輯和畫面截取內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
小程序卡片切換效果組件wxCardSwiper的實現(xiàn)
這篇文章主要介紹了小程序卡片切換效果組件wxCardSwiper的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02
javascript 折半查找字符在數(shù)組中的位置(有序列表)
折半查找字符在數(shù)組中的位置(有序列表),需要的朋友可以參考下。2010-12-12
JS寫XSS cookie stealer來竊取密碼的步驟詳解
JavaScript是web中最常用的腳本開發(fā)語言,js可以自動執(zhí)行站點組件,管理站點內(nèi)容,在web業(yè)內(nèi)實現(xiàn)其他有用的函數(shù)。這篇文章主要介紹了JS寫XSS cookie stealer來竊取密碼的步驟詳解,需要的朋友可以參考下2017-11-11
【經(jīng)驗總結(jié)】編寫JavaScript代碼時應(yīng)遵循的14條規(guī)律
這篇文章主要介紹了編寫JavaScript代碼時應(yīng)遵循的14條規(guī)律,涉及javascript變量的定義,函數(shù)、表單、json的使用,邏輯運算與頁面元素操作技巧等,非常具有實用價值,需要的朋友可以參考下2016-06-06
神奇的代碼 通殺各種網(wǎng)站-可隨意修改復(fù)制頁面內(nèi)容
在瀏覽器地址欄輸入這一行代碼,然后回車,就發(fā)現(xiàn)整個頁面都可以隨意編輯了。僅僅是一行很短的代碼。2008-07-07

