【Vue3】前端使用 FFmpeg.wasm 完成用户视频录制,并对视频进行压缩处理

06-01 1014阅读

强烈推荐这篇博客!非常全面的一篇文章,本文是对该博客的简要概括和补充,在不同技术栈中提供一种可行思路,可先阅读该篇文章再阅读本篇:

FFmpeg——在Vue项目中使用FFmpeg(安装、配置、使用、SharedArrayBuffer、跨域隔离、避坑...)_vue ffmpeg-CSDN博客文章浏览阅读1.1w次,点赞71次,收藏96次。本文介绍了FFmpeg在Vue项目中从0到1的使用,从安装 => 配置 => 简单使用 => 认识SharedArrayBuffer和跨域隔离 => 避坑..._vue ffmpeghttps://zahuopu.blog.csdn.net/article/details/135032429?fromshare=blogdetail&sharetype=blogdetail&sharerId=135032429&sharerefer=PC&sharesource=Andye11&sharefrom=from_link

上述博客技术栈: Vue2 + Webpack + JS

本文技术栈: Vue3 + Vite + TS

本文运行环境: windows11 + Edge浏览器 + 22.11 node版本

1、安装配置(等同上述博客):

npm i @ffmpeg/ffmpeg@0.9.8
npm i @ffmpeg/core@0.10.0

2、复制下载的核心js文件到 /public 静态文件夹中(基本等同上述博客)

【Vue3】前端使用 FFmpeg.wasm 完成用户视频录制,并对视频进行压缩处理

3、配置Vite配置项(和webpack在代码位置上有细微区别  内容完全一致)

server: {
   ......
   headers: { // 目的是为了消除浏览器报错: SharedArrayBufferis not defined
      'Cross-Origin-Opener-Policy': 'same-origin',
      'Cross-Origin-Embedder-Policy': 'require-corp'
   },
   ......
 },

特别注意!

1、配置该配置项之后, 本地运行时 请使用 localhost 服务进行开发,因为 ffmpeg仅支持 本地 和 https 环境运行,不支持 http 环境;

2、如果需要打包到线上环境运行,还需要 后端同学对nginx 进行同样头部配置,仅有前端自己配置只能在自己本地服务中运行。

4、完成配置,代码相关内容如下:

预览效果图:

【Vue3】前端使用 FFmpeg.wasm 完成用户视频录制,并对视频进行压缩处理    【Vue3】前端使用 FFmpeg.wasm 完成用户视频录制,并对视频进行压缩处理

可以看到,压缩率还是很高的,时间性能上大概需要原视频长度的 50% (也就是 10分钟的视频大概需要接近5分钟去压缩(我的设备信息:笔记本电脑 CPU - 英特尔 i5-12500H)

在此过程中 页面内存消耗巨大,基本无法与用户进行其他互动),同时牺牲了一定清晰度,但最终可以缩小90%左右的大小。

也就是说:如果你的目标是 使用时间和性能来换取极致的大小压缩,这个库是可用的。

代码原文:

  
    
      
      
        

{{ page }}

翻页 开始录制 {{ timer_min }} {{ old_timer_min }} {{ scope.row.type == 1 ? '原文件' : '压缩后' }} import { ref, nextTick, computed } from 'vue' import FFmpeg from '@ffmpeg/ffmpeg/dist/ffmpeg.min.js' const page = ref(1) // ============================================= 时间相关 ============================================= const timer_sec = ref(0) const timer_min = computed(() => { let mm = Math.floor(timer_sec.value / 60) let ss = Math.floor(timer_sec.value % 60) return (mm { if (type == 1) { let spendTime = '' let lastItem = tableData.value.findLast(item => item.type == 1) if (lastItem) { spendTime = secondToMinute((Date.now() - lastItem.time) / 1000) } else { spendTime = secondToMinute(timer_sec.value) } tableData.value.push({ type: 1, size: (blobSize / 1024 / 1024).toFixed(2), time: Date.now(), spendTime: spendTime, page: page.value }) } else { let spendTime2 = secondToMinute((Date.now() - tableData.value[tableData.value.length - 1].time) / 1000) tableData.value.push({ type: 2, page: page.value, time: Date.now(), spendTime: spendTime2, size: (blobSize / 1024 / 1024).toFixed(2) }) } } // ===================================== 视频录制相关 ===================================== const videoElement = ref(null) // 用于播放的 VideoElement 对象 let mediaRecorder: MediaRecorder | null = null // 用于录制的 MediaRecorder 对象 const isRecording = ref(false) // 是否正在录制 const recordBlob = ref([]) // 记录视频Blob数据 const isLoading_fullScreen = ref(false) const timeSlice = 10 * 1000 // 获取摄像头和麦克风访问权限 async function getMedia() { isLoading_fullScreen.value = true await ffmpeg.load() // 配置视频录制分辨率为480p 帧率为24,但是在实际测试中发现各大浏览器基本都不支持此配置,设置与否不影响视频大小 let mediaStreamConstraints: MediaStreamConstraints = { video: { width: { ideal: 640 }, height: { ideal: 480 }, frameRate: { ideal: 24, max: 24 } }, audio: true } navigator.mediaDevices .getUserMedia(mediaStreamConstraints) .then(stream => { isRecording.value = true // 1、记录视频数据 mediaRecorder = new MediaRecorder(stream) // 2、开始录制 mediaRecorder.start(timeSlice) // 处理录制数据 mediaRecorder.ondataavailable = (e: BlobEvent) => { if (e.data.size > 0) { recordBlob.value.push(e.data) } else { console.error('最近10秒内数据异常') } } mediaRecorder.onstop = async () => { if (recordBlob.value.length > 0) { let fullBlob = new Blob(recordBlob.value, { type: 'video/mp4' }) // 处理原文件数据到表格中 addTableData(1, fullBlob.size) let compressedBlob = await compressVideoBlob(fullBlob) // 处理压缩文件数据到表格中 addTableData(2, compressedBlob.size) page.value += 1 // 页数+1 recordBlob.value = [] // 清空数组 mediaRecorder!.start(timeSlice) // 重新开始录制 } else { console.error('录制异常,请重新录制本页,可尝试缩短录制时长') mediaRecorder!.start(timeSlice) } } mediaRecorder.onerror = (e: Event) => { console.error('触发了 error 事件' + mediaRecorder?.state) } // 3、实时显示摄像画面 nextTick(() => { videoElement.value!.srcObject = stream videoElement.value!.play() setInterval(() => { timer_sec.value += 1 }, 1000) }) isLoading_fullScreen.value = false }) .catch(err => { console.error('错误:', err) alert('无法访问摄像头或麦克风') }) } const nextPage = async () => { if (mediaRecorder) { mediaRecorder.stop() old_timer_min.value = timer_min.value } } const { createFFmpeg, fetchFile } = FFmpeg const ffmpeg = createFFmpeg({ corePath: '/FFMPEG/ffmpeg-core.js', // 核心文件的路径 log: true // 是否在控制台打印日志,true => 打印 }) // 压缩视频Blob数据 const compressVideoBlob = async (blob: Blob) => { const data = await blob.arrayBuffer() const inputName = 'input.mp4' ffmpeg.FS('writeFile', inputName, new Uint8Array(data)) await ffmpeg.run('-i', inputName, '-vcodec', 'libx264', '-crf', '28', '-acodec', 'copy', 'output.mp4') const outputData = ffmpeg.FS('readFile', 'output.mp4') const compressedBlob = new Blob([outputData.buffer], { type: 'video/mp4' }) return compressedBlob } // 处理时间 把秒数处理为分钟 const secondToMinute = (second: number, pad: string = '') => { second = Math.floor(second) return Math.floor(second / 60) + '分' + pad + (second % 60) + '秒' } .test-page { padding: 20px; } .flx-center { display: flex; align-items: center; justify-content: center; } .card { box-sizing: border-box; padding: 20px; overflow-x: hidden; background-color: #ffffff; border: 1px solid #e4e7ed; border-radius: 6px; box-shadow: 0 0 12px rgb(0 0 0 / 5%); }

注意:请自行删除 HTML中 element-plus UI库的相关内容,使用它们仅为了样式美观,不影响实际代码运行

额外注意事项:

1、import 引入 FFmpeg时 需要指定完整路径:即使用

import FFmpeg from '@ffmpeg/ffmpeg/dist/ffmpeg.min.js'
// 虽然这样会损失代码提示,但起码能在Vite环境跑起来 这点和博主原文不同

2、在 createFFmpeg 函数中, 需要额外注意路径一定要使用绝对路径

// 我把那3个文件复制到 /public/FFMPEG下面,但是这里不要带 /public ,主要是为了防止线上环境路径失效, 具体为什么可查询我的【Vite项目中静态资源路径处理】一文
const { createFFmpeg, fetchFile } = FFmpeg
const ffmpeg = createFFmpeg({
    corePath: '/FFMPEG/ffmpeg-core.js', 
})

3、最后,我在0.9.8版本的 ffmpeg.wasm 中发现:

微信内置浏览器乃至移动设备大部分浏览器都不兼容 SharedArrayBuffer,我在win11的 edge和chrome浏览器中可以非常非常顺利地运行代码,哪怕是打包到线上环境也是可以正常运行,

但手机端无论是 微信浏览器还是 夸克浏览器、safari浏览器 均会报 SharedArrayBuffer is not defined 错误,IOS手机还会报出 Range Error 错误。

听说最新0.12版本已经可以修复这个问题了,如果有机会,我也会补充到这里来。

Q:0.12版本ffmpeg.wasm可以在移动设备上正常使用了吗?

A:十分抱歉,笔者在使用0.12版本的库时遇到了 ffmpeg.load 不执行也不报错的未知情况,至今仍未能成功运行。

但笔者发现canvas也可以间接完成视频压缩的功能,大概可以把原视频压缩到原本的25%~35%大小,链接放在这里:
【Vue3】使用canvas来实现H5页面摄像头录制的【视频压缩】功能-CSDN博客文章浏览阅读2次。用户边说话边进行摄像头录制,同时要把用户说的话转为字幕显示在评测结果中,但iPhone手机10秒钟基本就能录制一个大约12MB大小的视频,如果考试时间达到30分钟,那么消耗的流量将十分恐怖,而且极其容易出现网络问题,导致视频录制失败。,效果虽然不算非常好,但是没有引入任何第三方库,代码量非常少,所以也算有它的价值所在。之前尝试使用 FFmege.wasm失败,一是性能比较差,需要大概原视频50%的时间来进行压缩,二是移动设备兼容性比较差,没办法成功跑起来。代码效果图:(大小为 PC端未被压缩视频大小)https://blog.csdn.net/Andye11/article/details/144084235?fromshare=blogdetail&sharetype=blogdetail&sharerId=144084235&sharerefer=PC&sharesource=Andye11&sharefrom=from_link

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。

相关阅读

目录[+]

取消
微信二维码
微信二维码
支付宝二维码