当前位置: 首页 > news >正文

vite+vue3中使用FFmpeg@0.12.15实现视频编辑功能,不依赖SharedArrayBuffer!!!

FFmpeg@0.12.15完全不依赖SharedArrayBuffer!!!强烈推荐使用

本文章主要是在vite+vue3项目中使用FFmpeg,只展示了如何在项目中引入和基础的使用

更多详细参数可参照 ffmpeg官网https://ffmpeg.org/

一、安装FFmpeg

可通过npm直接安装

npm install @ffmpeg/core@0.12.10 @ffmpeg/ffmpeg@0.12.15 @ffmpeg/util@0.12.2

二、在代码中使用

1、初始化FFmpeg

首先找到node_modules/@ffmpeg/core/dist/esm下的这两个文件,并放到public文件夹下,例如

import { FFmpeg } from '@ffmpeg/ffmpeg'
import { fetchFile, toBlobURL } from '@ffmpeg/util'
import { ref, onMounted } from 'vue'
let ffmpeg = null
const initFFmpegFn = async () => {if (ffmpeg) return// 初始化ffmpeg ffmpeg = new FFmpeg();// 如果需要当前编辑视频进度let progress = ref(0);ffmpeg.on('log', ({ type, message }) => {if (type === 'stderr') {// 匹配日志中的时间信息(例如:time=00:00:05.12)const timeMatch = message.match(/time=(\d+:\d+:\d+\.\d+)/);if (timeMatch && totalDuration.value) {const currentTime = parseTimeToSeconds(timeMatch[1]);const newProgress = Math.min(Math.round((currentTime / totalDuration.value) * 100), 100);progress.value = newProgress;}}});// 加载FFmpeg核心try {// ffmpeg.loaded 核心是否加载if (!ffmpeg.loaded) {let ffmpegBaseUrl = '/FFmpeg/dist'await ffmpeg.load({coreURL: await toBlobURL(`${ffmpegBaseUrl}/ffmpeg-core.js`, 'text/javascript'),wasmURL: await toBlobURL(`${ffmpegBaseUrl}/ffmpeg-core.wasm`, 'application/wasm'),});console.log('加载完成', ffmpeg);}} catch (err) {console.error('FFmpeg核心加载失败:', err);}}
// 工具函数:将时间字符串(如00:00:05.12)转换为秒
const parseTimeToSeconds = (timeStr) => {const [hours, minutes, seconds] = timeStr.split(':').map(Number);return hours * 3600 + minutes * 60 + seconds;
};
onMounted(() => {initFFmpegFn()
})

2、加载出错处理(如果通过上一步能正常加载完成,可忽略)

由于ffmpeg中会使用vite中的worker,可能会导致控制台中有一个链接为http://localhost:9090/node_modules/.vite/deps/worker.js?worker_file&type=module一直处于pending状态,无法加载成功

需要在vite.config.js中使用optimizeDeps.exclude 是指定不需要进行依赖预构建。

3、编辑视频:是否静音-调整视频宽高-裁剪视频时长

const processVideoFn = async (fileBlob, processOptions = {}) => {// fileBlob 视频文件blob对象if (!fileBlob || !(fileBlob instanceof Blob)) {console.error('错误:请传入有效的视频Blob对象');return { success: false, error: '无效的视频Blob', url: null, blob: null };}if (fileBlob.size === 0) {console.error('错误:传入的Blob为空(大小0字节)');return { success: false, error: '输入Blob为空', url: null, blob: null };}const {needClearVoice = false,//裁剪后是否静音resizeInfo = {width:800,height:800},//裁剪后视频宽高cropInfo = { startTime:0, duration:60 }// startTime:裁剪视频开始时间   duration:总裁剪时长} = processOptions;const { startTime = 0, duration = 60 } = cropInfo;progress.value = 0;//进度条归0totalDuration.value = duration;//总裁剪时长,用于计算进度条const timestamp = Date.now();const inputFileName = `input_video.mp4`;const outputFileName = `output_video_${timestamp}.mp4`;let outputData = null; // 存储输出数据,避免提前清理try {if (!ffmpeg || !loadSuccess.value) {throw new Error('FFmpeg核心未加载完成');}const fileData = await fetchFile(fileBlob); // fetchFile返回Uint8Arrayawait ffmpeg.writeFile(inputFileName, fileData); // 直接传文件名+数据const ffmpegCommand = ['-i', inputFileName];// 裁剪处理(校验参数)if (cropInfo) {if (startTime < 0 || duration <= 0) {throw new Error(`裁剪参数无效:startTime=${startTime}(需≥0),duration=${duration}(需>0)`);}ffmpegCommand.push('-ss', startTime.toFixed(2), '-t', duration.toFixed(2));}// 尺寸调整(校验参数)if (resizeInfo) {let { width, height } = resizeInfo;if (width <= 0 || height <= 0) {throw new Error(`尺寸参数无效:width=${width}(需>0),height=${height}(需>0)`);}const scaleFilter = `scale=w=${width}:h=${height}:force_original_aspect_ratio=decrease`;const padFilter = `pad=w=${width}:h=${height}:x=(ow-iw)/2:y=(oh-ih)/2:color=white`;//视频宽高不够时填充白色边框ffmpegCommand.push('-vf', `${scaleFilter},${padFilter}`);}// 音频处理if (needClearVoice) {ffmpegCommand.push('-an'); // 移除音频} else {ffmpegCommand.push('-c:a', 'aac', '-strict', 'experimental'); // 保留音频}ffmpegCommand.push('-c:v', 'libx264',       // H.264编码(通用)'-pix_fmt', 'yuv420p',   // 兼容所有播放器(避免仅支持yuv444p的问题)'-crf', '30',            // 控制质量(值越小质量越高,28为平衡值)'-preset', 'ultrafast',  // ultrafast/superfast/veryfast/faster/fast/medium(默认值)/slow/slower/veryslow 从前到后处理速度越来越慢,处理视频越精致'-b:v', '1M',            // 视频比特率(避免输出过小/过大)'-y',                    // 覆盖已有文件outputFileName);await ffmpeg.exec(ffmpegCommand);outputData = await ffmpeg.readFile(outputFileName);if (!outputData || outputData.length === 0) {throw new Error('FFmpeg读取输出文件为空');}const resultBlob = new Blob([outputData], { type: 'video/mp4' });if (resultBlob.size === 0) {throw new Error('生成的Blob为空');}const previewUrl = URL.createObjectURL(resultBlob);return {url: previewUrl,//生成的临时视频urlblob: resultBlob,//生成的新视频blob对象};} catch (err) {console.error('视频处理失败:',err);} finally {if (ffmpeg && outputData) {if (await ffmpeg.listDir(inputFileName).catch(() => false)) {await ffmpeg.unlink(inputFileName).catch(err => console.warn('清理输入文件失败:', err));}if (await ffmpeg.listDir(outputFileName).catch(() => false) && outputData) { // 确保读取后再删除输出await ffmpeg.unlink(outputFileName).catch(err => console.warn('清理输出文件失败:', err));}}}
};


文章转载自:

http://jaQFNC1E.zdqsc.cn
http://4N5lPS0j.zdqsc.cn
http://LgsEMKqt.zdqsc.cn
http://R27aOOVQ.zdqsc.cn
http://RNgczXQt.zdqsc.cn
http://5owIw0k6.zdqsc.cn
http://a7xMcmi4.zdqsc.cn
http://h3QVB5Pa.zdqsc.cn
http://bwUU85oS.zdqsc.cn
http://dq7RNYdf.zdqsc.cn
http://p0mJPnzl.zdqsc.cn
http://js4LxSyC.zdqsc.cn
http://yq3H30Uz.zdqsc.cn
http://4cTlQ1DX.zdqsc.cn
http://8UVsxVNt.zdqsc.cn
http://slq4cXo7.zdqsc.cn
http://GhRy8YNi.zdqsc.cn
http://s1rZiVBl.zdqsc.cn
http://QrVsQDBT.zdqsc.cn
http://ZnlILZDv.zdqsc.cn
http://1s5zQYMo.zdqsc.cn
http://7E1mSkT0.zdqsc.cn
http://zTAbikuA.zdqsc.cn
http://O6nPDqCQ.zdqsc.cn
http://XhlXJAoX.zdqsc.cn
http://e0W7xuYI.zdqsc.cn
http://M3Z1CrMN.zdqsc.cn
http://nLMWOwmn.zdqsc.cn
http://CWJYFj8x.zdqsc.cn
http://vIzN1bla.zdqsc.cn
http://www.dtcms.com/a/386587.html

相关文章:

  • AI智能问数能力全面升级,DataEase开源BI工具v2.10.13 LTS版本发布
  • 【pytorch】tensor的定义与属性
  • 【问题】使用腾讯宝塔部署并启动Nodejs应用异常处理Cannot find module ‘express‘
  • vue-office 在线预览
  • 嵌入式基本概念:什么是指令集,微架构,IDE,DFP等等是什么意思,有什么关系???
  • Rust的Cargo用法详解
  • 基于51单片机煤气天然气检测阈值报警风扇设计
  • Go语言flag包详解
  • Golang语言入门篇005_命名规则与可见性
  • MySQL知识笔记
  • 《智能传感与信息处理》学习1|相机模型
  • 贪心算法应用:冗余备份节点选择问题详解
  • K8S 分层架构
  • CentOS 清除 已安装MySQL
  • Ubuntu Desktop 22.04.5 LTS 使用默认的 VNC 远程桌面
  • 【脚本注入网页】XSS
  • 设计模式之:备忘录模式
  • 网页抓包怎么做?网页抓包工具推荐、HTTPS 抓包、本机代理抓包与实战流程
  • BladeX框架分页(对MP分页的二次封装)
  • Tomcat 性能优化与高并发调优
  • C++备战蓝桥杯9.13-9.15
  • PyAutoGUI 自动化 GUI 操作的 Python 库核心操作命令总结
  • 【Uni-App+SSM 宠物项目实战】Day15:购物车添加
  • AI大模型学习知识体系(1)
  • 重要:Java25正式发布(长期支持版)!
  • OneTerm开源堡垒机实战(二):快速部署与基本使用
  • 网络问题排查
  • linux之套接字Socket
  • 【Uni-App+SSM 宠物项目实战】Day14:商家服务列表
  • MCP 协议客户端与服务端python demo