uniapp + Vite + ffmpeg-wasm 0.12.x 集成示例

安装依赖

复制代码
pnpm add @ffmpeg/ffmpeg@0.12.15 @ffmpeg/util@0.12.1 @ffmpeg/core@0.12.6

查看依赖是否安装成功

将esm下的ffmpeg-core.js和ffmpeg-core.wasm 拷贝到static静态目录,并确保能访问

在vite.config.ts中配置 optimizeDeps.exclude

复制代码
optimizeDeps: {
	    exclude: ['@ffmpeg/ffmpeg', '@ffmpeg/core', '@ffmpeg/util'],
	  },

编写页面测试

复制代码
<template>
	<view>
		333
	</view>
</template>

<script setup>
	import { onLoad, onUnload } from '@dcloudio/uni-app'
	import { FFmpeg } from '@ffmpeg/ffmpeg'
	import { fetchFile } from '@ffmpeg/util'
	
	const ffmpeg = new FFmpeg()
	
	onLoad(async (options) => {
		await ffmpeg.load({
		    coreURL: `http://192.168.1.166:9000/shanqiapph/static/ffmpeg/ffmpeg-core.js`,//必须传,不然默认umd
		  });
		
		console.log('✅ FFmpeg ',ffmpeg.loaded)
		
		
		const videoData = await fetchFile("http://192.168.1.166:9000/shanqiapph/static/ffmpeg/test.mp4")
		
		await ffmpeg.writeFile('input.mp4', videoData)
		console.log('✅ 视频已写入 ffmpeg 环境')
		
		// 监听 ffmpeg 日志输出
		  ffmpeg.on('log', ({ message }) => {
			console.log(message)
		  })
		  
		await ffmpeg.exec(['-i', 'input.mp4'])  
	})
</script>

<style>
	       
</style>

结果

以上ffmpeg-wasm已集成完成

下面是封装一些接口,可在H5环境直接使用

ffmpegUtil.js

复制代码
import {
	FFmpeg
} from '@ffmpeg/ffmpeg'
import {
	fetchFile
} from '@ffmpeg/util'


export async function init() {
	const ffmpeg = new FFmpeg()
	await ffmpeg.load({
	    coreURL: `${window.location.origin}${import.meta.env.VITE_APP_PUBLIC_BASE}static/ffmpeg/ffmpeg-core.js`,//必须传,不然默认umd
	  });
	  
	ffmpeg.on('log', ({ message }) => {
		console.log(message)
	})  
	
	const rootPath = '/generateVideo'; 
	 
	//创建合成视频根,目录  
	await ffmpeg.createDir(rootPath)
	  
	  
	/**
	 * 查询指定路径下的所有文件
	 */  
	async function listMemfsFiles (dirPath = rootPath){
		const entries = await ffmpeg.listDir(dirPath)
		 const result = []
		
		 for (const name of entries) {
		   // 跳过当前目录和上级目录的伪条目(如果存在)
		   if (name.name === '.' || name.name === '..') continue
		
		   if(name.isDir == true){
			   const subFiles = await listMemfsFiles(`${dirPath}/${name.name}`)
			   if (subFiles.length > 0) {
			     result.push(...subFiles)
			   } else {
			     // 空目录也记录一下
			     result.push({
			       path: dirPath,
			       fullPath:`${dirPath}/${name.name}`,
			       size: 0,
			       sizeFormatted: '[空目录]',
			       type: 'dir'
			     })
			   }
		   }else {
			   // 尝试读取,成功则为文件
			   const data = await ffmpeg.readFile(`${dirPath}/${name.name}`)
			   const size = data.length || data.byteLength
			   		
			   result.push({
			     path: dirPath,
			     fullPath:`${dirPath}/${name.name}`,
			     size,
			     sizeFormatted: formatBytes(size),
			     type: 'file'
			   })
		   }
		 }
		 return result
	}
	
	/**
	 * 单位转换
	 * @param {Object} bytes
	 */
	function formatBytes(bytes){
	 if (bytes === 0) return '0 B'
	 const k = 1024
	 const sizes = ['B', 'KB', 'MB', 'GB']
	 const i = Math.floor(Math.log(bytes) / Math.log(k))
	 return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
	}
	
	
	/**
	 * 将url对应的文件写入到ffmpeg环境中
	 * @param {Object} fileUrl
	 * @param {Object} filePath
	 */
	async function writeFileByUrl (fileUrl,filePath){
		const fileData = await fetchFile(fileUrl)
		await ffmpeg.writeFile(rootPath+'/'+filePath, fileData)
	}
	
	/**
	 * 写入指定字符串到指定文件
	 * @param {Object} content
	 * @param {Object} filePath
	 */
	async function writeTxtByContent (content,filePath){
		await ffmpeg.writeFile(rootPath+'/'+filePath, content)
	}
	
	/**
	 * 读取指定文件为文本
	 * @param {Object} filePath
	 */
	async function readTxtByFilePath (filePath){
		const data999 = await ffmpeg.readFile(rootPath+'/'+filePath)
		const text = new TextDecoder().decode(data999)
		return text;
	}
	
	//执行命令
	async function exec (commandArray){
		await ffmpeg.exec(commandArray)
	}
	
	/**
	 * 下载ffmpeg合成结果视频
	 * @param {Object} filePath
	 * @param {Object} fileName
	 */
	async function downloadFile (filePath,fileName){
		// 3. 读取输出文件
		const data66 = await ffmpeg.readFile(filePath)
		const blob = new Blob([data66.buffer], { type: 'video/quicktime' })
		
		// 4. 生成下载链接或播放 URL
		const url = URL.createObjectURL(blob)
		console.log('✅ 转换完成:', url)
		
		// 5. 自动触发下载
		const link = document.createElement('a')
		link.href = url
		link.download = fileName  // 下载后的文件名
		document.body.appendChild(link)
		link.click()
		
		// 6. 清理(重要!防止内存泄漏)
		document.body.removeChild(link)
		URL.revokeObjectURL(url)
		
		console.log('✅ 文件已下载到本地')
	}
	
	/**
	 * 删除指定目录下的所有文件
	 * @param {Object} dirPath
	 */
	async function deleteFile (dirPath){
		const entries = await ffmpeg.listDir(dirPath)
		
		 for (const name of entries) {
		   // 跳过当前目录和上级目录的伪条目(如果存在)
		   if (name.name === '.' || name.name === '..') continue
		
		   if(name.isDir == true){
			   const subFiles = await deleteFile(`${dirPath}/${name.name}`)
		   }else {
			   await ffmpeg.deleteFile(`${dirPath}/${name.name}`)
		   }
		 }
	}
	
	return {
		listMemfsFiles,
		writeFileByUrl,
		writeTxtByContent,
		readTxtByFilePath,
		exec,
		downloadFile,
		deleteFile
	}
}

使用示例

复制代码
<template>
	<view>
		333
	</view>
</template>

<script setup>
	import { onLoad, onUnload } from '@dcloudio/uni-app'

	import { init } from '@/utils/ffmpegUtil'
	
	onLoad(async (options) => {
		var ffmpegUtil = await init();
		
		//写入资源文件
		await ffmpegUtil.writeFileByUrl("http://192.168.1.166:9000/shanqiapph/static/ffmpeg/test.mp4","input.mp4");
		
		//写入文本到文件
		await ffmpegUtil.writeTxtByContent("45666","test.txt");
		var textA = await ffmpegUtil.readTxtByFilePath("test.txt");
		console.log(textA);
		
		//执行命令
		await ffmpegUtil.exec(['-i', '/generateVideo/input.mp4', '-c', 'copy', '/generateVideo/output.mov'])
		
		//下载合成结果
		//await ffmpegUtil.downloadFile('/generateVideo/output.mov','测试转换视频下载.mov');
		
		//删除文件
		await ffmpegUtil.deleteFile("/generateVideo");
		
		//查看所有文件
		var fileListA =  await ffmpegUtil.listMemfsFiles();
		console.table(fileListA);
	})
</script>

<style>
	       
</style>
相关推荐
todoitbo8 小时前
把 K-Lite「什么都能播」搬上鸿蒙 PC:影匣播放器的 FFmpeg + libass 原生移植实录
华为·ffmpeg·harmonyos·鸿蒙
FlightYe1 天前
音视频修炼之容器与协议(四):RTSP详解
android·linux·ffmpeg·音视频·dash
码间留白1 天前
六轮真机实验后,我把鸿蒙直播推流的 UI 全逼成了原生——uni-app 适配 HarmonyOS NEXT 完整复盘
uni-app·鸿蒙·harmonyos next·直播推流·arkts/arkui
架构师伯子明3 天前
ffmpeg学习4:opencv在音视频领域常用指令
opencv·学习·ffmpeg
wuxianda10303 天前
2026 苹果审核 4.3 (a) 被拒全解析|Guideline 4.3a spam 马甲包拒审原因 + 避坑方案
flutter·uni-app·苹果上架
Android系统攻城狮3 天前
Linux FFmpeg深度解析之avio_open调用流程与实战(三十八)
linux·运维·服务器·ffmpeg·音视频进阶·ffmpeg音视频
Android系统攻城狮5 天前
Linux FFmpeg深度解析之av_guess_format调用流程与实战(三十六)
linux·运维·服务器·ffmpeg·ffmpeg音视频进阶
头发还在的女程序员5 天前
基于 Spring Boot + Vue 3 + uni-app 的灵活用工系统:四端架构与业务流程设计
vue.js·spring boot·uni-app·灵活用工·灵工系统
Android系统攻城狮6 天前
Linux FFmpeg深度解析之av_write_frame调用流程与实战(三十三)
linux·运维·服务器·ffmpeg·音视频进阶·ffmpeg音视频
Dumbness6 天前
订阅消息弹窗必须在用户点击里调:联调实验室里 timing 和 Mock 支付通知踩的坑
javascript·uni-app