Js使用ffmpeg进行视频剪辑和画面截取
ffmpeg
使用场景是需要在web端进行视频的裁剪,包括使用 在线视频url
或 本地视频文件
的裁剪,以及对视频内容的截取等功能。
前端进行视频操作可能会导致性能下降,最好通过后端使用java,c++进行处理,本文的案例是备选方案。
注意:
以下所有的使用案例均基于vue3 setup。
同时由于@ffmpeg版本不同会导致使用的api不同,使用案例前需要注意@ffmpeg版本问题
。
如果使用的是0.12+需要使用新的api,详情请看 文档
npm
npm install @ffmpeg/ffmpeg@^0.10.0npm install @ffmpeg/core@^0.10.0
在线视频url剪辑
<script setup>
// "@ffmpeg/core": "^0.10.0",
// "@ffmpeg/ffmpeg": "^0.10.0",import { ref, onMounted, onUnmounted } from 'vue'
import { createFFmpeg, fetchFile } from '@ffmpeg/ffmpeg';const ffmpeg = createFFmpeg({ log: true });
const fileType = ref("") // 视频文件类型/*** 根据在线的视频地址截取片段* @param {String} url 在线视频链接* @param {Number|String} startTime 截取开始时间* @param {Number|String} endTime 截取结束时间* @param {Function} callBack 回调函数*/
const videoCut = async (url, startTime, endTime, callBack) => {if (!ffmpeg.isLoaded()) {await ffmpeg.load();}if(!url) return;fileType.value = url.split(".").pop()const inputName = `input.${fileType.value}`;const outputName = `output.${fileType.value}`;// 将输入文件保存到虚拟文件系统await ffmpeg.FS('writeFile', inputName, await fetchFile(url));// 运行 FFmpeg 命令try {await ffmpeg.run('-ss', `${startTime}`,'-t', `${endTime - startTime}`,'-i', inputName,'-vcodec', 'copy','-acodec', 'copy',outputName);// 读取输出文件let arrayBuffer = ffmpeg.FS('readFile', outputName).buffer; // 读取缓存// 创建下载链接并通过回调下载保存到本地const fileUrl = URL.createObjectURL(new Blob([arrayBuffer])); // 转为Blob URLcallBack && callBack(fileUrl)// 释放内存ffmpeg.FS('unlink', inputName);ffmpeg.FS('unlink', outputName);} catch (e) { }
}const downloadFile = (url, fileName = `clip.${fileType.value}`) => {const link = document.createElement('a');link.href = url;link.download = fileName;link.click();
}onMounted(() => {videoCut("https://视频.mp4", 0, 3, downloadFile)
})onUnmounted(() => {ffmpeg.exit();
})
</script>
本地视频文件剪辑
<template><input type="file" @change="fileChange">
</template><script setup>
import { ref, onUnmounted } from 'vue'
import { createFFmpeg, fetchFile } from '@ffmpeg/ffmpeg';const ffmpeg = createFFmpeg({ log: true });
const fileType = ref("") // 视频文件类型const fileChange = (e) => {if (!e.target.files[0]) return;const file = e.target.files[0];fileType.value = file.name.split(".").pop()videoCut(file, 0, 3, downloadFile)
}/*** 根据选择的视频文件截取片段* @param {File} file 选择的视频文件* @param {Number|String} startTime 截取开始时间* @param {Number|String} endTime 截取结束时间* @param {Function} callBack 回调函数*/
const videoCut = async (file, startTime, endTime, callBack) => {if (!ffmpeg.isLoaded()) {await ffmpeg.load();}if(!file) return;const inputName = `input.${fileType.value}`;const outputName = `output.${fileType.value}`;const orgFileBuffer = await file.arrayBuffer()// 将输入文件保存到虚拟文件系统await ffmpeg.FS('writeFile', inputName, await fetchFile(new Blob([orgFileBuffer])));try {await ffmpeg.run('-ss', `${startTime}`,'-t', `${endTime - startTime}`,'-i', inputName,'-vcodec', 'copy','-acodec', 'copy',outputName);// 读取输出文件let arrayBuffer = ffmpeg.FS('readFile', outputName).buffer; // 读取缓存// 创建下载链接并通过回调下载保存到本地const fileUrl = URL.createObjectURL(new Blob([arrayBuffer])); // 转为Blob URLcallBack && callBack(fileUrl)// 释放内存ffmpeg.FS('unlink', inputName);ffmpeg.FS('unlink', outputName);} catch (e) {}
}const downloadFile = (url, fileName = `clip.${fileType.value}`) => {const link = document.createElement('a');link.href = url;link.download = fileName;link.click();
}onUnmounted(() => {ffmpeg.exit();
})
</script>
获取视频画面截图
<template><input type="file" @change="fileChange">
</template><script setup>
import { ref, onUnmounted } from 'vue'
import { createFFmpeg, fetchFile } from '@ffmpeg/ffmpeg';const ffmpeg = createFFmpeg({ log: true });
const fileType = ref("") // 视频文件类型const fileChange = (e) => {if (!e.target.files[0]) return;const file = e.target.files[0];fileType.value = file.name.split(".").pop()// 由于这里一秒截取一帧 ,截取5次, 所以如果视频不足5秒会导致截取和读取失败// 回调中是base64图片组成的数组,需要在前面拼接 "data:image/png;base64," ,然后在img的src中赋值即可videoFrame(file, 5, 1, (data) => console.log(data))
}/*** 根据选择的视频文件获取视频截图* @param {File} file 选择的视频文件* @param {Number|String} count 截取图片的次数* @param {Number|String} interval 截取图片的间隔* @param {Function} callBack 回调*/
const videoFrame = async (file, count, interval, callBack) => {if (!ffmpeg.isLoaded()) {await ffmpeg.load();}if(!file) return;const inputName = `input.${fileType.value}`;const orgFileBuffer = await file.arrayBuffer()// 将输入文件保存到虚拟文件系统await ffmpeg.FS('writeFile', inputName, await fetchFile(new Blob([orgFileBuffer])));try {await ffmpeg.run("-i",inputName,"-r",`${interval}`,"-ss","0","-vframes",`${count}`,"-f","image2","-s","88*50","image-%02d.png");const baseArr = []for (let i = 0; i < count; i++) {let temp = i + 1;if (temp < 10) {temp = "0" + temp;}baseArr.push(arrayBufferToBase64(ffmpeg.FS("readFile", "image-" + temp + ".png")));}callBack && callBack(baseArr)// 释放内存ffmpeg.FS('unlink', inputName);} catch (e) {}
}const arrayBufferToBase64 = (array) => {array = new Uint8Array(array);var length = array.byteLength;var table = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'];var base64Str = "";for (var i = 0; length - i >= 3; i += 3) {var num1 = array[i];var num2 = array[i + 1];var num3 = array[i + 2];base64Str +=table[num1 >>> 2] +table[((num1 & 0b11) << 4) | (num2 >>> 4)] +table[((num2 & 0b1111) << 2) | (num3 >>> 6)] +table[num3 & 0b111111];}var lastByte = length - i;if (lastByte === 1) {var lastNum1 = array[i];base64Str +=table[lastNum1 >>> 2] + table[(lastNum1 & 0b11) << 4] + "==";} else if (lastByte === 2) {// eslint-disable-next-line no-redeclarevar lastNum1 = array[i];var lastNum2 = array[i + 1];base64Str +=table[lastNum1 >>> 2] +table[((lastNum1 & 0b11) << 4) | (lastNum2 >>> 4)] +table[(lastNum2 & 0b1111) << 2] +"=";}return base64Str
}onUnmounted(() => {ffmpeg.exit();
})
</script>
相关文章:
Js使用ffmpeg进行视频剪辑和画面截取
ffmpeg 使用场景是需要在web端进行视频的裁剪,包括使用 在线视频url 或 本地视频文件 的裁剪,以及对视频内容的截取等功能。 前端进行视频操作可能会导致性能下降,最好通过后端使用java,c进行处理,本文的案例是备选方…...
Linux基本命令,基础知识
进到当前用户目录:cd ~ 回到上级目录:cd .. 查看当前目录层级:pwd 创建目录:mkdir mkdir ruanjian4/linux/zqm41 -p级联创建文件夹(同时创建多个文件夹需要加-p) 查看详细信息:ls -l (即 ll) 查看所有详细信息:ls -al 隐藏文件是以.开头的 查看:l…...

【Android知识笔记】进程通信(三)
在上一篇探索Binder通信原理时,提到了内存映射的概念,其核心是通过mmap函数,将一块 Linux 内核缓存区映射到一块物理内存(匿名文件),这块物理内存其实是作为Binder开辟的数据接收缓存区。这里有两个概念,需要理解清楚,那就是操作系统中的虚拟内存和物理内存,理解了这两…...

云上亚运:所使用的高新技术,你知道吗?
作者简介:一名云计算网络运维人员、每天分享网络与运维的技术与干货。 公众号:网络豆云计算学堂 座右铭:低头赶路,敬事如仪 个人主页: 网络豆的主页 目录 前言 一.什么是云上亚运会 二.为什么要使用云…...
数据结构简述,时间、空间复杂度,学习网站推荐
目录 IT 学习路线 相关坚韧大厚书 相关有趣/耐看书或视频 数据结构与算法学习网站推荐 刷题 时间、空间复杂度 数据结构简述 基本概念 数据结构与算法简述和CS综述整理。本文非基础的教程,本文会列出大量学习和参考网站。老惯例,一个文章是一个集…...

在线安装qt5.15之后任意版本
下载qt现在安装包: window安装包链接 进入cmd,用命令行打开安装包,并指定组件下载地址(这个是关键,之前用的是腾讯镜像,出现了版本灰色无法选中问题) .\qt-unified-windows-x64-4.6.1-online…...

【kafka实战】01 3分钟在Linux上安装kafka
本节采用docker安装Kafka。采用的是bitnami的镜像。Bitnami是一个提供各种流行应用的Docker镜像和软件包的公司。采用docker的方式3分钟就可以把我们想安装的程序运行起来,不得不说真的很方便啊,好了,开搞。使用前提:Linux虚拟机&…...
yum安装mysql8
记录一下安装过程用于后面项目参考 目录 说明安装步骤yum安装默认目录修改默认的数据目录必要的my.cnf属性修改卸载Mysql 说明 一般情况下都是docker安装,部分特殊情况下,例如老外的项目部分禁用docker,那一般二进制安装或者yum直接安装。 …...
十五)Stable Diffusion使用教程:另一个线稿出3D例子
案例:黄金首饰出图 1)线稿,可以进行色阶加深,不易丢失细节; 2)文生图,精确材质、光泽、工艺(抛光、拉丝等)、形状(包括深度等,比如镂空)和渲染方式(3D、素描、线稿等)提示词,负面提示词; 3)seed调-1,让ai随机出图; 4)开启controlnet,上传线稿图,选择cann…...
2023icpc网络预选赛I. Pa?sWorD(dp)
题目给定字符串长度n以及字符串s 其中出现小写字母可以代表小写字母和大写字母 比如a可以代表a和A 出现?可以代表26个小写字母和26个大写字母和10个数字 出现大写字母和数字就是原本的数 同时要求大写字母,小写字母,数字一定都存在替换完的字符串中…...

maven本地安装jar包
在实际开发中,有些jar包不能通过公共库下载,只能本地安装。可以按照以下步骤操作: 1、安装命令 mvn install:install-file -DgroupIdcom.chinacreator.sm -DartifactIdfbm-sm-common -Dversion0.0.1 -Dpackagingjar -Dfile../newJar/fbm-sm…...
QT中的inherits
目录 简介: 实例: 简介: 在Qt中,可以使用inherits函数来判断一个对象是否属于某个类或其派生类。inherits函数是QObject类的成员函数,因此只能用于继承自QObject的类的对象。 以下是inherits函数的一般用法…...

全国职业技能大赛云计算--高职组赛题卷①(容器云)
全国职业技能大赛云计算--高职组赛题卷①(容器云) 第二场次题目:容器云平台部署与运维任务1 Docker CE及私有仓库安装任务(5分)任务2 基于容器的web应用系统部署任务(15分)任务3 基于容器的持续…...

基于springboot+vue的入校申报审批系统
博主主页:猫头鹰源码 博主简介:Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容:毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…...

安卓逆向 - EdXposed LSPosed VirtualXposed
一、引言 接上篇:安卓逆向 - Xposed入门教程_小馒头yy的博客-CSDN博客 我们介绍了Xposed入门安装使用,但是只支持到Android 8,并且安装模块需要重启。今天我们来看看Xposed的其他版本。 二、各种Xposed框架对比 1、Xposed 只支持到安卓8&…...

Linux三大搜索指令的区别
find:可以在指定的路径下进行文件的搜索 —— 真的在磁盘文件中查找 例如find /usr/bin/ -name ls which 可以在指令路径下,/usr/bin,搜索指令文件 例如:which ls whereis:在系统特定的路径下查找,既可以找到可执行程序ÿ…...

C++ -- 特殊类设计
目录 设计一个类,不能被拷贝 C98的做法 C11的做法 设计一个类,只能在堆上创建对象 实现方式1 实现方式2 设计一个类,只能在栈上创建对象 实现方式1 方式1的优化 实现方式2 设计一个类,不能被继承 设计模式 什么是设计…...

指针和数组笔试题的透析
指针---进阶篇(三) 一、前言二、一维数组例题透析:三、指针笔试题1.例一:2.例二:3.例三:4.例四:5.例五:6.例六: 一、前言 那么好了好了,宝子们,从…...

「UG/NX」Block UI 超级点SuperPoint
✨博客主页何曾参静谧的博客📌文章专栏「UG/NX」BlockUI集合📚全部专栏「UG/NX」NX二次开发「UG/NX」BlockUI集合「VS」Visual Studio「QT」QT5程序设计「C/C+&#...
Linux——kafka常用命令
一、Kafka的常用命令包括: 1. 启动Zookeeper服务 前台启动: ./bin/zookeeper-server-start.sh config/zookeeper.properties 后台启动: ./bin/zookeeper-server-start.sh -daemon config/zookeeper.properties 2. 停止Zookeeper服务 .…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...

Appium+python自动化(十六)- ADB命令
简介 Android 调试桥(adb)是多种用途的工具,该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具,其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利,如安装和调试…...
IGP(Interior Gateway Protocol,内部网关协议)
IGP(Interior Gateway Protocol,内部网关协议) 是一种用于在一个自治系统(AS)内部传递路由信息的路由协议,主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...
线程与协程
1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...
基于数字孪生的水厂可视化平台建设:架构与实践
分享大纲: 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年,数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段,基于数字孪生的水厂可视化平台的…...

React19源码系列之 事件插件系统
事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...

如何理解 IP 数据报中的 TTL?
目录 前言理解 前言 面试灵魂一问:说说对 IP 数据报中 TTL 的理解?我们都知道,IP 数据报由首部和数据两部分组成,首部又分为两部分:固定部分和可变部分,共占 20 字节,而即将讨论的 TTL 就位于首…...
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...
苹果AI眼镜:从“工具”到“社交姿态”的范式革命——重新定义AI交互入口的未来机会
在2025年的AI硬件浪潮中,苹果AI眼镜(Apple Glasses)正在引发一场关于“人机交互形态”的深度思考。它并非简单地替代AirPods或Apple Watch,而是开辟了一个全新的、日常可接受的AI入口。其核心价值不在于功能的堆叠,而在于如何通过形态设计打破社交壁垒,成为用户“全天佩戴…...

【Linux手册】探秘系统世界:从用户交互到硬件底层的全链路工作之旅
目录 前言 操作系统与驱动程序 是什么,为什么 怎么做 system call 用户操作接口 总结 前言 日常生活中,我们在使用电子设备时,我们所输入执行的每一条指令最终大多都会作用到硬件上,比如下载一款软件最终会下载到硬盘上&am…...