Js使用ffmpeg在視頻中添加png或gif
ffmpeg
使用場景是需要在web端對視頻進行編輯 添加圖片和gif。
注意:
以下所有的使用案例均基于vue3 setup。
同時由于@ffmpeg版本不同會導致使用的api不同,使用案例前需要注意@ffmpeg版本問題
。
如果使用的是0.12+需要使用新的api,詳情請看 文檔
npm
npm install @ffmpeg/ffmpeg@^0.11.0
npm install @ffmpeg/core@^0.11.0
視頻添加png
<template></template>
<script setup>
import { ref, onUnmounted, onMounted } from 'vue'
import { createFFmpeg, fetchFile } from '@ffmpeg/ffmpeg';
const ffmpeg = createFFmpeg({ log: true });
const fileType = ref("") // 視頻文件類型
/**
* 將圖片合成到視頻中
* @param {string} url 視頻在線地址
* @param {object} picItem 圖片素材對象
* @param {string} picItem.startT 圖片素材出現(xiàn)的開始時間
* @param {number} picItem.duration 素材的出現(xiàn)持續(xù)時間
* @param {number} picItem.scale 素材的放大比例
* @param {string} picItem.url 圖片素材url地址
* @param {number} picItem.x 素材離視頻頂部的距離
* @param {number} picItem.y 素材離視頻左側(cè)的距離
* @return {Promise<{outputName: string, fileUrl: string}> | undefined}
*/
const videoCompose = async (url, picItem) => {
if (!ffmpeg.isLoaded()) {
await ffmpeg.load();
}
if (!url) return;
const { duration, scale, startT, url: picUrl, x, y } = picItem;
fileType.value = url.split(".").pop();
const inputName = `input.${fileType.value}`;
const outputName = `output.${fileType.value}`;
const imageType = picUrl.split(".").pop();
const imageFileName = `image.${imageType}`;
await ffmpeg.FS('writeFile', inputName, await fetchFile(url));
await ffmpeg.FS('writeFile', imageFileName, await fetchFile(picUrl));
// 運行 FFmpeg 命令
try {
await ffmpeg.run(
`-i`, `${inputName}`,
`-i`, `${imageFileName}`,
`-filter_complex`, `[1:v]scale=iw*${(scale).toFixed(1)}:ih*${(scale).toFixed(1)}[scaled];[0:v][scaled]overlay=${x}:${y}:enable='between(t,${+startT},${+startT + duration})'`,
`${outputName}`,
"-hide_banner"
);
// 讀取輸出文件
let arrayBuffer = ffmpeg.FS('readFile', outputName).buffer; // 讀取緩存
// 創(chuàng)建下載鏈接并通過回調(diào)下載保存到本地
const fileUrl = URL.createObjectURL(new Blob([arrayBuffer])); // 轉(zhuǎn)為Blob URL
// 釋放內(nèi)存
ffmpeg.FS('unlink', inputName);
ffmpeg.FS('unlink', outputName);
return {
fileUrl,
outputName
};
} catch (e) {
console.log(e);
}
}
const downloadFile = (url, fileName = `clip.mp4`) => {
const link = document.createElement('a');
link.href = url;
link.download = fileName;
link.click();
}
onMounted(async () => {
const {fileUrl} = await videoCompose("http://xxx.mp4", {
duration: 3,
scale: 1,
startT: "0.00",
url: 'http://xxx.png',
x: 100,
y: 100
})
downloadFile(fileUrl)
})
onUnmounted(() => {
ffmpeg.exit();
})
</script>
視頻添加gif
流程與添加圖片類似,但添加濾鏡的命令不相同。文章來源:http://www.zghlxwxcb.cn/news/detail-759952.html
/*
執(zhí)行FFmpeg命令的部分替換
`-ignore_loop`, `0` 讓gif圖片循環(huán)播放 否則只播放一次
`-itsoffset`, `${+startT}` gif圖片在視頻中出現(xiàn)時間
fade=t=in:st=${+startT}:d=1:alpha=1[wm]; gif圖片在視頻中淡入的時間
:shortest=1 視頻的時長為初始視頻時長 否則由于gif添加會導致視頻時間增長
:enable='between(t,${+startT},${+startT + duration})' gif的出現(xiàn)時間
"-hide_banner" 隱藏ffmpeg的部分信息
*/
await ffmpeg.run(
`-i`, `${inputName}`,
`-ignore_loop`, `0`,
`-itsoffset`, `${+startT}`,
`-i`, `${imageFileName}`,
`-filter_complex`, `[0:0]scale=iw:ih[a];[1:0]scale=iw*${(scale).toFixed(1)}:ih*${(scale).toFixed(1)},fade=t=in:st=${+startT}:d=1:alpha=1[wm];[a][wm]overlay=x=${x}:y=${y}:shortest=1:enable='between(t,${+startT},${+startT + duration})'`,
`${outputName}`,
"-hide_banner"
);
整合
可以在添加時對圖片的類型進行判斷,執(zhí)行不同的添加邏輯文章來源地址http://www.zghlxwxcb.cn/news/detail-759952.html
/**
* 將圖片合成到視頻中
* @param {string} url 視頻在線地址
* @param {object} picItem 圖片素材對象
* @param {string} picItem.startT 圖片素材出現(xiàn)的開始時間
* @param {number} picItem.duration 素材的出現(xiàn)持續(xù)時間
* @param {number} picItem.scale 素材的放大比例
* @param {string} picItem.url 圖片素材url地址
* @param {number} picItem.x 素材離視頻頂部的距離
* @param {number} picItem.y 素材離視頻左側(cè)的距離
* @return {Promise<{outputName: string, fileUrl: string}> | undefined}
*/
const videoCompose = async (url, picItem) => {
if (!ffmpeg.isLoaded()) {
await ffmpeg.load();
}
if (!url) return;
const {duration, scale, startT, url: picUrl, x, y} = picItem;
const type = url.split(".").pop();
const inputName = `input.${type}`;
const outputName = `output.${type}`;
const imageType = picUrl.split(".").pop();
const imageFileName = `image.${imageType}`;
// 將輸入文件保存到虛擬文件系統(tǒng)
if (url.startsWith('blob:')) {
// 處理 Blob URL
const arrayBuffer = await fetchBlobAsArrayBuffer(url);
ffmpeg.FS('writeFile', inputName, new Uint8Array(arrayBuffer));
} else if (url.startsWith('http://') || url.startsWith('https://')) {
// 處理網(wǎng)絡(luò)地址
await ffmpeg.FS('writeFile', inputName, await fetchFile(url));
}
await ffmpeg.FS('writeFile', imageFileName, await fetchFile(picUrl));
// 運行 FFmpeg 命令
try {
if (imageType === 'gif') {
await ffmpeg.run(
`-i`, `${inputName}`,
`-ignore_loop`, `0`,
`-itsoffset`, `${+startT}`,
`-i`, `${imageFileName}`,
`-filter_complex`, `[0:0]scale=iw:ih[a];[1:0]scale=iw*${(scale).toFixed(1)}:ih*${(scale).toFixed(1)},fade=t=in:st=${+startT}:d=1:alpha=1[wm];[a][wm]overlay=x=${x}:y=${y}:shortest=1:enable='between(t,${+startT},${+startT + duration})'`,
`${outputName}`,
"-hide_banner"
);
} else {
await ffmpeg.run(
`-i`, `${inputName}`,
`-i`, `${imageFileName}`,
`-filter_complex`, `[1:v]scale=iw*${(scale).toFixed(1)}:ih*${(scale).toFixed(1)}[scaled];[0:v][scaled]overlay=${x}:${y}:enable='between(t,${+startT},${+startT + duration})'`,
`${outputName}`,
"-hide_banner"
);
}
// 讀取輸出文件
let arrayBuffer = ffmpeg.FS('readFile', outputName).buffer; // 讀取緩存
// 創(chuàng)建下載鏈接并通過回調(diào)下載保存到本地
const fileUrl = URL.createObjectURL(new Blob([arrayBuffer])); // 轉(zhuǎn)為Blob URL
// 釋放內(nèi)存
ffmpeg.FS('unlink', inputName);
ffmpeg.FS('unlink', outputName);
return {
fileUrl,
outputName
};
} catch (e) {
console.log(e);
}
}
到了這里,關(guān)于Js使用ffmpeg在視頻中添加png或gif的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!