个人简介
👀个人主页: 前端杂货铺
🙋♂️学习方向: 主攻前端方向,正逐渐往全干发展
📃个人状态: 研发工程师,现效力于中国工业软件事业
🚀人生格言: 积跬步至千里,积小流成江海
🥇推荐学习:🍍前端面试宝典 🍉Vue2 🍋Vue3 🍓Vue2/3项目实战 🥝Node.js🍒Three.js🍖数据结构与算法体系教程🌕个人推广:每篇文章最下方都有加入方式,旨在交流学习&资源分享,快加入进来吧
内容 | 参考链接 |
---|---|
Canvas 录制视频 | Canvas 录制视频 |
录制视频的解决方案 | 使用 Canvas 录制视频尚存问题的解决方案 |
在 Vue 项目中使用 FFmpeg |
文章目录
- 前言
- 在 Vue 项目中使用 FFmpeg
- FFmpeg.wasm 简介
- 在 Vue 项目中安装并配置 FFmpeg
- 在 Vue 项目中使用 FFmpeg
- SharedArrayBuffer 的前世今生
- 解决 SharedArrayBuffer 报错问题
- FFmpeg 的一些坑
- 总结
前言
大家好,这里是前端杂货铺。
在 Canvas 录制视频 一文中,我们使用了 MediaRecorder API 在 Canvas 画布中进行了页面的视频录制,并成功输出了 WebM 格式 的视频。
在 使用 Canvas 录制视频尚存问题的解决方案 一文中,我们通过下载 FFmpeg,进行了视频格式的转换及视频帧率的设置。
但在 Vue 项目中怎么使用 FFmpeg 呢?接下来,我们一起进行详细探索!
在 Vue 项目中使用 FFmpeg
在浏览器中我们是无法直接使用 FFmpeg 软件的,但好在有个东西叫 FFmpeg.wasm,它可以让 FFmpeg 的功能在浏览器中使用!当然,我们可以在 Vue 项目中使用 FFmpeg.wasm 来代替手动输入命令行操作的 FFmpeg 软件!!
FFmpeg.wasm 简介
什么是 FFmpeg.wasm?
FFmpeg.wasm 是 FFmpeg 的纯 WebAssembly 接口,可以在浏览器内录制音频和视频,并进行转换和流式传输。
什么是 WebAssembly?
WebAssembly(简称wasm)是一个虚拟指令集体系架构(virtual ISA),整体架构包括核心的ISA定义、二进制编码、程序语义的定义与执行,以及面向不同的嵌入环境(如Web)的应用编程接口(WebAssembly API)。其目标是为 C/C++等语言编写的程序经过编译,在确保安全和接近原生应用的运行速度更好地在 Web 平台上运行。
所以,FFmpeg.wasm 就是 C语言 编写的程序,通过编译后,它可以在 Web 平台(比如浏览器)上运行。
FFmpeg.wasm(Github源码)
在 Vue 项目中安装并配置 FFmpeg
第一步:想要在 Vue 项目中使用 FFmpeg 第一步当然是要有个 Vue 项目,Vue 项目的搭建就不做介绍了,不清楚的同学可以自行百度查询(资料很多)。在此,博主使用的是 Vue2 的项目(具体版本为 ^2.6.11)【说明:大家并不需要刻意与我的项目版本保持一致】。
第二步:找到 package.json
文件,在指定区域输入下图中蓝框的内容【说明:@ffmpeg/ffmpeg 和 @ffmpeg/ffmpeg 并非最新版本,请按照自己需要安装所需版本】
第三步:终端键入 npm i
或 cnpm i
, 回车
我们可以在 node_modules 文件夹中查看是否真正安装完成
第四步:复制 ffmpeg-core.js
、 ffmpeg-core.wasm
、ffmpeg-core.worker.js
文件,放入静态资源文件夹中【说明:Vue项目创建完之后默认生成 public 文件夹(用来存放静态资源的),所以我们需要把它们三个复制一份放入 public 文件夹中(若静态资源文件夹名称改了,就放入改到的地方即可)】
第五步:在需要使用 ffmpeg 的 vue 组件中引入 ffmpeg
import FFmpeg from "@ffmpeg/ffmpeg";
const { createFFmpeg, fetchFile } = FFmpeg;
const ffmpeg = createFFmpeg({
corePath: "./ffmpeg-core.js", // 核心文件的路径
log: true, // 是否在控制台打印日志,true => 打印
});
至此,安装和配置 FFmpeg 就告一段落…
在 Vue 项目中使用 FFmpeg
在 Canvas 录制视频 一文中,我们编写了通过 MediaRecorder API 在 Canvas 画布中进行了页面的视频录制的代码,把它稍作改变放入 vue 组件中就得到了下面的代码
简单讲解一下:
- draw() 方法用于绘制画布上显示的内容
- startRecording() 方法用于开始对画布进行视频录制
- stopAndblobDownload() 方法用于结束对画布进行的视频录制,并使用 FFmpeg 对视频流进行处理(转换格式、设置帧率等)
<template>
<div>
<canvas id="webm-canvas" height="500" width="500" style="margin: auto"></canvas>
<button @click="startRecording">开始录制</button>
<button @click="stopAndblobDownload">停止录制</button>
</div>
</template>
<script>
import FFmpeg from "@ffmpeg/ffmpeg";
const { createFFmpeg, fetchFile } = FFmpeg;
const ffmpeg = createFFmpeg({
corePath: "./ffmpeg-core.js", // 核心文件的路径
log: true, // 是否在控制台打印日志,true => 打印
});
export default {
name: "HelloWorld",
data() {
return {
recorder: null,
allChunks: []
};
},
mounted() {
this.draw();
},
methods: {
draw() {
let canvas = document.getElementById("webm-canvas");
let context = canvas.getContext("2d");
// window.requestAnimationFrame(draw);
var x = 0;
var speed = 1;
var text = "前端杂货铺";
var fontSize = 20;
setInterval(() => {
// 清空画布(否则字体移动后颜色会遗留在画布上)
context.clearRect(0, 0, canvas.width, canvas.height);
// 设置字体
context.font = fontSize + "px Arial"; //设置字体大小和类型
context.fillStyle = "orange"; //设置文本颜色
context.fillText(text, x, canvas.height / 2); //绘制文本
// x要移动
x += speed;
// 如果文本达到画布边缘,改变方向
if (x > canvas.width - 100 || x < 0) {
speed = -speed;
}
}, 10);
},
startRecording() {
let canvas = document.getElementById("webm-canvas");
// 实时视频捕获画布,参数为帧率
const stream = canvas.captureStream(60); // 60 FPS
// 创建一个对指定的 stream 进行录制的 MediaRecorder 对象
this.recorder = new MediaRecorder(stream, {
mimeType: "video/webm;codecs=vp9", // 设置媒体类型
});
// 当数据有效时触发的事件,数据有效时可以把数据存储到缓存区里
this.recorder.ondataavailable = (e) => {
console.log("TCL: e", e);
this.allChunks.push(e.data);
};
this.recorder.start(10);
},
async stopAndblobDownload() {
// 异步加载 ffmpeg
await ffmpeg.load();
// 结束录像
this.recorder.stop();
// createElement() 方法通过指定名称创建一个元素
const link = document.createElement("a");
link.style.display = "none";
// 创建一个 Blob 对象,用于存储二进制数据
const fullBlob = new Blob(this.allChunks);
let name = "demo";
// 写文件,fullBlob 存放录制视频流的 blob 数据
ffmpeg.FS("writeFile", name, await fetchFile(fullBlob));
// 执行 ffmpeg ('-r', '10' 表示设置视频帧率为 10fps)('output.avi' 表示生成视频的格式为 .avi)
await ffmpeg.run('-i', name, '-r', '10', 'output.avi');
// 读取刚刚执行的文件,存放到 data 中
const data = ffmpeg.FS('readFile', 'output.avi');
// 把 data 的 buffer 数据传入 blob 实例中,创建一个包含所需数据的 URL
const downloadUrl = window.URL.createObjectURL(new Blob([data.buffer], {type:'video/avi'}));
// 获取或设置链接的 URL 属性
link.href = downloadUrl;
// 点击链接时,浏览器下载文件
link.download = `前端杂货铺${Math.random().toFixed(4)}.avi`;
// 向节点的子节点列表的末尾添加新的子节点
document.body.appendChild(link);
// 模拟用户点击链接的操作
link.click();
// 删除 HTML 文档中的链接元素
link.remove();
},
},
};
</script>
<style>
canvas {
box-shadow: 0 0 10px gray;
display: block;
}
body {
text-align: center;
}
button {
margin-top: 20px;
}
</style>
此时,运行我们的项目,点击开始录制后,再点击结束录制
SharedArrayBuffer 的前世今生
此时,控制台就会暴露出一个问题:SharedArrayBuffer is not defined
不要慌,我们先来了解一下 SharedArrayBuffer 是什么?
MDN 上是这样解释的:SharedArrayBuffer 对象用来表示一个通用的原始二进制数据缓冲区,类似于 ArrayBuffer 对象,但它可以用来在共享内存上创建视图。与可转移的 ArrayBuffer 不同,SharedArrayBuffer 不是可转移对象。
官方的说明有点抽象。其实 SharedArrayBuffer 就是一种 JavaScript 对象,它允许多个 Web Worker 线程共享相同的内存空间。这就意味着不同的线程可以同时访问和修改相同的数据,而无需复制数据或担心数据同步的问题。它允许以更有效的方式进行多线程编程,从而提高性能。
那么此时为什么会报 SharedArrayBuffer is not defined 的错误呢?
这就要溯源到 2018 年了,其实在 2018 之前,在支持 SharedArrayBuffer 的各大主流浏览器中都是可以正常使用它的。直到 2018 年暴露出了 幽灵漏洞(Spectre漏洞)*,它导致了浏览器性能的严重倒退,各大主流浏览器厂商便都禁用了 SharedArrayBuffer!!
直到 2021 年才正式放开了对 SharedArrayBuffer 的使用。
SharedArrayBuffer 在各大主流浏览器中的支持情况:
既然都放开了对 SharedArrayBuffer 的使用,那么为什么还会报错呢?
虽然各大主流浏览器都已经陆续开始支持 SharedArrayBuffer ,但它也不是那么随便就能使用的,而是需要我们进行一些配置,开启了跨域隔离 才能正常使用。
在控制台中输入 crossOriginIsolated 回车,为 false,则说明并没有开启跨域隔离
解决 SharedArrayBuffer 报错问题
在我们的项目中创建并打开 vue.config.js
文件,如果你的项目使用了 webpack 打包工具,那么就打开你的 webpack.config.js
文件,进行如下配置,配置完成之后记得重启一下项目,让我们刚刚的配置生效
module.exports = {
devServer: {
headers: {
"Cross-Origin-Opener-Policy": "same-origin", // 保护你的源站点免受攻击
"Cross-Origin-Embedder-Policy": "require-corp", // 保护受害者免受你的源站点的影响
},
},
}
我们看一下是否开启了跨域隔离,为 true,确实已开启
此时,再点击开始录制后点击结束录制,控制台便不会报错了,并且还会生成我们所需要的视频文件,很完美
FFmpeg 的一些坑
我们解决了 SharedArrayBuffer 问题后,在 ffmpeg 执行的时候也可能遇到执行失败的问题。
比如说,在 webm 转 mp4 格式时当你的分辨率设置成奇数(比如:499x499)时,就可能导致 ffmpeg 转码失败(错误:width not divisible by 2 (499x499)),所以我们尽量控制把分辨率为偶数。
建议大家在开发过程中把 log: true
打开,方便调试开发!!
总结
本文,我们首先讲解了 FFmpeg 在 Vue 项目中的安装与配置,之后在 Vue 项目中编码简单使用了一下 FFmpeg,然后在运行项目时报了错让我们认识到了 SharedArrayBuffer 并成功找到了解决方案,最后我们了解到了 FFmpeg 在使用时的一些坑,明确了要注意查看 log 日志内容…
FFmpeg 无疑是一款非常强大的多媒体处理工具,在 Vue 项目中利用 “纯前端” 就能实现视频的录制和视频编码转码后的输出,这看起来确实是一件非常 cool 的事!
其实坚持学习,热爱生活得你更 cool
好啦,本篇文章到这里就要和大家说再见啦,祝你这篇文章阅读愉快,你下篇文章的阅读愉快留着我下篇文章再祝!
参考资料:
- 百度百科 · FFmpeg、幽灵漏洞
- MDN官方文档 · SharedArrayBuffer
- 由一个报错引发的浏览器跨域隔离探索【作者:网络安全小肖_知乎】
- Canvas录制视频【作者:前端杂货铺_CSDN】
- FFmpeg——使用Canvas录制视频尚存问题的解决方案 【作者:前端杂货铺_CSDN】