长沙网站排名技术,图书网站建设的主要工作流程,哪家专门做特卖网站,网站下面的站长统计很逗Js使用ffmpeg在视频中添加png或gif
ffmpeg
使用场景是需要在web端对视频进行编辑 添加图片和gif。 注意:
以下所有的使用案例均基于vue3 setup。
同时由于ffmpeg版本不同会导致使用的api不同#xff0c;使用案例前需要注意ffmpeg版本问题。
如果使用的是0.12需要使用新的…Js使用ffmpeg在视频中添加png或gif
ffmpeg
使用场景是需要在web端对视频进行编辑 添加图片和gif。 注意:
以下所有的使用案例均基于vue3 setup。
同时由于ffmpeg版本不同会导致使用的api不同使用案例前需要注意ffmpeg版本问题。
如果使用的是0.12需要使用新的api详情请看 文档 npm
npm install ffmpeg/ffmpeg^0.11.0npm install ffmpeg/core^0.11.0视频添加png
template/templatescript 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 图片素材出现的开始时间* param {number} picItem.duration 素材的出现持续时间* param {number} picItem.scale 素材的放大比例* param {string} picItem.url 图片素材url地址* param {number} picItem.x 素材离视频顶部的距离* param {number} picItem.y 素材离视频左侧的距离* 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]scaleiw*${(scale).toFixed(1)}:ih*${(scale).toFixed(1)}[scaled];[0:v][scaled]overlay${x}:${y}:enablebetween(t,${startT},${startT duration}),${outputName},-hide_banner);// 读取输出文件let arrayBuffer ffmpeg.FS(readFile, outputName).buffer; // 读取缓存// 创建下载链接并通过回调下载保存到本地const fileUrl URL.createObjectURL(new Blob([arrayBuffer])); // 转为Blob URL// 释放内存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
流程与添加图片类似但添加滤镜的命令不相同。
/*执行FFmpeg命令的部分替换-ignore_loop, 0 让gif图片循环播放 否则只播放一次-itsoffset, ${startT} gif图片在视频中出现时间fadetin:st${startT}:d1:alpha1[wm]; gif图片在视频中淡入的时间:shortest1 视频的时长为初始视频时长 否则由于gif添加会导致视频时间增长:enablebetween(t,${startT},${startT duration}) gif的出现时间-hide_banner 隐藏ffmpeg的部分信息
*/
await ffmpeg.run(-i, ${inputName},-ignore_loop, 0,-itsoffset, ${startT},-i, ${imageFileName},-filter_complex, [0:0]scaleiw:ih[a];[1:0]scaleiw*${(scale).toFixed(1)}:ih*${(scale).toFixed(1)},fadetin:st${startT}:d1:alpha1[wm];[a][wm]overlayx${x}:y${y}:shortest1:enablebetween(t,${startT},${startT duration}),${outputName},-hide_banner);整合
可以在添加时对图片的类型进行判断执行不同的添加逻辑
/*** 将图片合成到视频中* param {string} url 视频在线地址* param {object} picItem 图片素材对象* param {string} picItem.startT 图片素材出现的开始时间* param {number} picItem.duration 素材的出现持续时间* param {number} picItem.scale 素材的放大比例* param {string} picItem.url 图片素材url地址* param {number} picItem.x 素材离视频顶部的距离* param {number} picItem.y 素材离视频左侧的距离* 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};// 将输入文件保存到虚拟文件系统if (url.startsWith(blob:)) {// 处理 Blob URLconst arrayBuffer await fetchBlobAsArrayBuffer(url);ffmpeg.FS(writeFile, inputName, new Uint8Array(arrayBuffer));} else if (url.startsWith(http://) || url.startsWith(https://)) {// 处理网络地址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]scaleiw:ih[a];[1:0]scaleiw*${(scale).toFixed(1)}:ih*${(scale).toFixed(1)},fadetin:st${startT}:d1:alpha1[wm];[a][wm]overlayx${x}:y${y}:shortest1:enablebetween(t,${startT},${startT duration}),${outputName},-hide_banner);} else {await ffmpeg.run(-i, ${inputName},-i, ${imageFileName},-filter_complex, [1:v]scaleiw*${(scale).toFixed(1)}:ih*${(scale).toFixed(1)}[scaled];[0:v][scaled]overlay${x}:${y}:enablebetween(t,${startT},${startT duration}),${outputName},-hide_banner);}// 读取输出文件let arrayBuffer ffmpeg.FS(readFile, outputName).buffer; // 读取缓存// 创建下载链接并通过回调下载保存到本地const fileUrl URL.createObjectURL(new Blob([arrayBuffer])); // 转为Blob URL// 释放内存ffmpeg.FS(unlink, inputName);ffmpeg.FS(unlink, outputName);return {fileUrl,outputName};} catch (e) {console.log(e);}
}