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

网页中的音视频裁剪拼接合并

一、需求描述

        项目中有一个配音需求:

        1)首先,前台会拿到一个英语视频,视频的内容是A和B用英语交流;

        2)然后,用户可以选择为某一个角色配音,假如选择为A配音,那么视频在播放到A的位置时会静音,并录制用户的声音。以此类推,直到视频播放结束;

        3)最后,将用户的录音替换到视频中,并生成新的视频文件,后续上传服务器。

        另外,已知每个角色说话的起始时间和结束时间(这个由后台管理来配置)。

二、需求分析

        2.1 实现方式

        实现该功能的方式大体有两个:

        1)使用ffmpeg.wasm

        2)使用Web Audio API等原生JS

        第二种方式我没实践,但理论上应该可以实现,只是估计会较复杂,代码较多;此处,我选择方式一。

        2.2 功能拆分

        根据该功能的操作流程,可将其拆分为:

        1)音视频分离,获得纯音频文件和纯视频文件

        2)音频剪切,从上一步得到的音频文件中裁剪到除配音角色外的其它音频片段

        3)录音,获取到配音音频片段

        4)音频拼接,将上面两步得到的音频片段按顺序拼接成一个

        5)音视频合并,将纯视频文件和上一步得到的音频文件合并为一个文件

三、代码实现

        3.1 引入依赖库

<!-- 注:文末附件会提供,或自行从网上获取 -->
<script src="/js/ffmpeg/umd/ffmpeg.js"></script>
<script src="/js/util/umd/index.js"></script>

        3.2 初始化ffmpeg

const { fetchFile } = FFmpegUtil;
const { FFmpeg } = FFmpegWASM;
let ffmpeg = new FFmpeg();
await ffmpeg.load({coreURL: "/js/core/umd/ffmpeg-core.js",});

        3.3 音视频分离

// 在Demo中,视频文件通过input[type=file]标签获得
const { name, size } = files[0];
await ffmpeg.writeFile(name, await fetchFile(files[0]));
// 音视频分离
await ffmpeg.exec(['-i', name, '-c:v', 'copy', '-an', 'output.mp4'])
await ffmpeg.exec(['-i', name, '-vn', '-acodec', 'libmp3lame', 'output.mp3'])

        在上面代码中-acodec标识了使用mp3音频编码器,如果使用copy原音频的编码方式,在网页中可能会报错“Invalid audio stream. Exactly one MP3 audio stream is required”

await ffmpeg.exec(['-i', name, '-vn', '-acodec', 'copy', 'output.mp3']); // 会报错

        3.4 音频剪切

// -ss 起始时间,-t 持续时间
await ffmpeg.exec(['-i', 'output.mp3', '-ss', '00:00:00.000', '-t', '00:00:10.000', 'split_0.mp3'])
await ffmpeg.exec(['-i', 'output.mp3', '-ss', '00:00:20.000', '-t', '00:00:10.000', 'split_2.mp3'])

        3.5 配音录制

const record = (duration, callback) => {if (!duration) return;// 变量及函数声明recorder = [];recordTimer = null;let _isStop = false;async function startRecording () {const stream = await navigator.mediaDevices.getUserMedia({ audio: true });mediaRecorder = new MediaRecorder(stream, { mimeType: 'audio/webm' });mediaRecorder.ondataavailable = handleDataAvailable;mediaRecorder.start();}function handleDataAvailable(event) {if (recorder) { recorder.push(event.data); }if (_isStop) {callback && callback();}}function stopRecording() {mediaRecorder.stop();_isStop = true;}// 调用startRecording();recordTimer = setTimeout(() => {stopRecording();}, duration);
}

        在上面这段代码中,需要注意的是:录音结束后的回调函数是放在handleDataAvailable中的,这是因为当mediaRecorder.stop()停止录制后,会再出发一次dataavailable事件,然后才把最后的数据分片存储到recorder中。所以代码中定义了一个_isStop变量来辅助完成这个过程。

// 将配音数据保存到文件
let split_1 = await audioChunks2Unit8Array(recorder);
await ffmpeg.writeFile('split_1.mp3', split_1);

        在上面这段代码中,之前获得的录音数据是个Blob数组,ffmpeg不支持直接对其进行操作,所以要将它转换为Unit8Array才能写到文件。

        3.6 音频拼接

await ffmpeg.exec(['-i', 'split_0.mp3', '-i', 'split_1.mp3', '-i', 'split_2.mp3', '-filter_complex', '[0:a][1:a][2:a]concat=n=3:v=0:a=1', '-ac', '2', '-c:a', 'libmp3lame', '-q:a', '4', 'merge.mp3'])

参数解释:

[0:a][1:a][2:a]concat=n=3: 将第一段素材的音频、a1和a2合并,n=3表示三段。

v=0:a=1: 不要声音,只要音频。

-ac:设定声音的channel数

-c:a:指定音频编码器

libmp3lame:mp3音频编码器

-q:a:表示输出的音频质量,一般是1到5之间(1为质量最高)

        3.7 音视频合并

await ffmpeg.exec(['-i', 'output.mp4', '-i', 'merge.mp3', '-c:v', 'copy', '-c:a', 'copy', 'result.mp4'])

参数解释:

-c:v copy:视频编码不变。

-c:a copy :音频编码不变。

        最后得到合并后的视频数据(Unit8Array)。

四、附件

        之前在网上查找ffmpeg.wasm资源时,很多都残缺不全,所以把相关的依赖库放在网盘了(文件来自官方github仓库,其中的示例页面我稍微美化了一下样式)。

        https://download.csdn.net/download/xueshen1106/88772981

相关文章:

网页中的音视频裁剪拼接合并

一、需求描述 项目中有一个配音需求&#xff1a; 1&#xff09;首先&#xff0c;前台会拿到一个英语视频&#xff0c;视频的内容是A和B用英语交流&#xff1b; 2&#xff09;然后&#xff0c;用户可以选择为某一个角色配音&#xff0c;假如选择为A配音&#xff0c;那么视频在播…...

【入门】使用sklearn实现的KNN算法:鸢尾花数据集分类预测

目录 前言 第一步&#xff1a;安装和导入sklean模块 第二步&#xff1a;获取数据 第二步&#xff1a;分割出训练集和测试集 第三步&#xff1a;训练模型 第四步&#xff1a;测试结果 总结 前言 本文将介绍如何利用K最近邻&#xff08;KNN&#xff09;算法对经典的鸢尾花数…...

nss做题

[NCTF 2018]签到题 1.f12在index.php中找到flag [NSSCTF 2022 Spring Recruit]ezgame 1.在js源码中就有flag [UUCTF 2022 新生赛]websign 1.打开环境后发现ctrlu和右键&#xff0c;f12都被禁用了。两种方法&#xff0c;第一种&#xff1a;禁用js&#xff1b;第二中提前打开…...

​第18章:JDK8-17新特性

1. 新特性概述 > 角度1&#xff1a;新的语法规则 &#xff08;多关注&#xff09;比如&#xff1a;lambda表达式、enum、annotation、自动拆箱装箱、接口中的默认方法和静态方法、switch表达式、record等> 角度2&#xff1a;增加、过时、删除API比如&#xff1a;新的日期…...

哈希表练习题(2024/5/29)

1有效的字母异位词 给定两个字符串 s 和 t &#xff0c;编写一个函数来判断 t 是否是 s 的字母异位词。 注意&#xff1a;若 s 和 t 中每个字符出现的次数都相同&#xff0c;则称 s 和 t 互为字母异位词。 示例 1: 输入: s "anagram", t "nagaram" 输…...

java —— 连接 MySQL 操作

MySQL 是独立于 java 之外的数据库&#xff0c;二者之间建立连接需要提前引入 mysql-connector-java 的 jar 包。 一、引入方法&#xff1a; ① 在项目中新建一个 Folder&#xff08;即文件夹&#xff09;&#xff0c;该文件夹通常命名为 lib&#xff0c;意思是存放项目所依赖…...

从 0 开始实现一个博客系统 (SSM 项目)

相关技术 Spring Spring Boot Spring MVC MyBatis Html Css JS pom 文件我就不放出来了, 之前用的 jdk8 做的, MySQL 用的 5.7, 都有点老了, 你们自己看着配版本就好 实现功能 用户注册 - 密码加盐加密 (md5 加密)前后端用户信息存储 - 令牌技术用户登录 - (使用 拦截…...

C++标准模板(STL)- C 内存管理库 - 分配并清零内存 (std::calloc)

C 内存管理库 分配并清零内存 std::calloc void* calloc( std::size_t num, std::size_t size ); 分配 num 个大小为 size 的对象的数组&#xff0c;并初始化所有位为零。 若分配成功&#xff0c;则返回指向为任何对象类型适当对齐的&#xff0c;被分配内存块最低&#xf…...

嵌入式开发面试问题总结(持续更新)

面试问题总结 c/c 封装、继承和多态 封装&#xff1a;将属性和方法封装起来&#xff0c;并加以权限区分。继承&#xff1a;子类继承父类的特征和行为&#xff0c;复用了从基类复制而来的数据成员和成员函数&#xff08;基类私有成员无法被访问&#xff09;&#xff0c;其中构…...

意外发现openGauss兼容Oracle的几个条件表达式

意外发现openGauss兼容Oracle的几个条件表达式 最近工作中发现openGauss在兼容oracle模式下&#xff0c;可以兼容常用的两个表达式&#xff0c;因此就随手测试了一下。 查看数据库版本 [ommopenGauss ~]$ gsql -r gsql ((openGauss 6.0.0-RC1 build ed7f8e37) compiled at 2…...

使用Keepalived提高吞吐量和负载均衡ip_hash.

一 . Nginx使用Keepalived提高吞吐量案例 Keepalived[表示把连接保持一定长连接数来提高吞吐量] 1.1没有使用keepalived参数 upstream tomcats {server 192.168.28.102:8080; } server {listen 88;server_name www.tomcats.com;location / {proxy_pass http://to…...

网络故障与排除(一)

一、Router-ID冲突导致OSPF路由环路 路由器收到相同Router-ID的两台设备发送的LSA&#xff0c;所以查看路由表看到的OSPF缺省路由信息就会不断变动。而当C1的缺省路由从C2中学到&#xff0c;C2的缺省路由又从C1中学到时&#xff0c;就形成了路由环路&#xff0c;因此出现路由不…...

C++之运算符重载

1、运算符重载 //Complex.h #ifndef _COMPLEX_H_ #define _COMPLEX_H_class Complex { public:Complex(int real_, int imag_);Complex();~Complex();Complex& Add(const Complex& other); void Display() const;Complex operator(const Complex& other);privat…...

使用springdoc-openapi-starter-webmvc-ui后访问swagger-ui/index.html 报错404

按照官网说明&#xff0c;引入 springdoc-openapi-starter-webmvc-ui后应该就可以直接访问swagger-ui.html或者swagger-ui/index.html就可以出现swagger页面了&#xff0c;但是我引入后&#xff0c;访问提示报错404. 在我的项目中&#xff0c;有其他依赖间接引入了org.webjars…...

深入理解计算机系统 家庭作业4.52

练习题4.3 p.254 \sim\seq\seq-full.hcl文件内已经说的很清楚了哪些不能更改,哪些是题目要求更改的控制逻辑块. 依据家庭作业4.51的答案,在seq-full.hcl文件内更改对应的HCL描述即可 以下答案注释了#changed的就是更改部分 #/* $begin seq-all-hcl */ ######################…...

深度学习:手撕 RNN(2)-RNN 的常见模型架构

本文首次发表于知乎&#xff0c;欢迎关注作者。 上一篇文章我们介绍了一个基本的 RNN 模块。有了 这个 RNN 模块后&#xff0c;就像搭积木一样&#xff0c;以 RNN 为基本单元&#xff0c;根据不同的任务或者需求&#xff0c;可以构建不同的模型架构。本节介绍的所有结构&#…...

【Linux进程篇】Linux进程管理——进程创建与终止

W...Y的主页 &#x1f60a; 代码仓库分享&#x1f495; 目录 进程创建 fork函数初识 写时拷贝 fork常规用法 fork调用失败的原因 进程终止 进程退出场景 _exit函数 exit函数 return退出 进程创建 fork函数初识 在linux中fork函数时非常重要的函数&#xff0c;它从已…...

Python爬虫实战(实战篇)—17获取【CSDN某一专栏】数据转为Markdown列表放入文章中

文章目录 专栏导读背景结果预览1、页面分析2、通过返回数据发现适合利用lxmlxpath3、进行Markdown语言拼接总结 专栏导读 在这里插入图片描述 &#x1f525;&#x1f525;本文已收录于《Python基础篇爬虫》 &#x1f251;&#x1f251;本专栏专门针对于有爬虫基础准备的一套基…...

Go语言-big.Int

文章目录 Go 语言 big.Int应用场景&#xff1a;大整数位运算使用举例&#xff1a; go sdk中crypto/ecdsa 椭圆曲线生成私钥相关结构中就有使用 Go 语言 big.Int Go 语言 big.Int 参考URL: https://blog.csdn.net/wzygis/article/details/82867793 math/big 作为 Go 语言提供的…...

getContentView(mBinding.getRoot()); 会导致内存泄露吗?里面有SurfaceView ViewBinding

在上述代码中&#xff0c;ActivityTestingBinding 是一个 Data Binding 库生成的类&#xff0c;用于绑定 XML 布局到 Activity 中。inflate(getLayoutInflater()) 用于将布局文件解析并转换为对应的视图层次结构。然后 getWindow().setFlags() 设置窗口属性&#xff0c;保持屏幕…...

OpenLayers 可视化之热力图

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 热力图&#xff08;Heatmap&#xff09;又叫热点图&#xff0c;是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...

简易版抽奖活动的设计技术方案

1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...

C++ 基础特性深度解析

目录 引言 一、命名空间&#xff08;namespace&#xff09; C 中的命名空间​ 与 C 语言的对比​ 二、缺省参数​ C 中的缺省参数​ 与 C 语言的对比​ 三、引用&#xff08;reference&#xff09;​ C 中的引用​ 与 C 语言的对比​ 四、inline&#xff08;内联函数…...

深度学习习题2

1.如果增加神经网络的宽度&#xff0c;精确度会增加到一个特定阈值后&#xff0c;便开始降低。造成这一现象的可能原因是什么&#xff1f; A、即使增加卷积核的数量&#xff0c;只有少部分的核会被用作预测 B、当卷积核数量增加时&#xff0c;神经网络的预测能力会降低 C、当卷…...

中医有效性探讨

文章目录 西医是如何发展到以生物化学为药理基础的现代医学&#xff1f;传统医学奠基期&#xff08;远古 - 17 世纪&#xff09;近代医学转型期&#xff08;17 世纪 - 19 世纪末&#xff09;​现代医学成熟期&#xff08;20世纪至今&#xff09; 中医的源远流长和一脉相承远古至…...

基于SpringBoot在线拍卖系统的设计和实现

摘 要 随着社会的发展&#xff0c;社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 在线拍卖系统&#xff0c;主要的模块包括管理员&#xff1b;首页、个人中心、用户管理、商品类型管理、拍卖商品管理、历史竞拍管理、竞拍订单…...

LabVIEW双光子成像系统技术

双光子成像技术的核心特性 双光子成像通过双低能量光子协同激发机制&#xff0c;展现出显著的技术优势&#xff1a; 深层组织穿透能力&#xff1a;适用于活体组织深度成像 高分辨率观测性能&#xff1a;满足微观结构的精细研究需求 低光毒性特点&#xff1a;减少对样本的损伤…...

Rust 开发环境搭建

环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行&#xff1a; rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu ​ 2、Hello World fn main() { println…...

零知开源——STM32F103RBT6驱动 ICM20948 九轴传感器及 vofa + 上位机可视化教程

STM32F1 本教程使用零知标准板&#xff08;STM32F103RBT6&#xff09;通过I2C驱动ICM20948九轴传感器&#xff0c;实现姿态解算&#xff0c;并通过串口将数据实时发送至VOFA上位机进行3D可视化。代码基于开源库修改优化&#xff0c;适合嵌入式及物联网开发者。在基础驱动上新增…...

十九、【用户管理与权限 - 篇一】后端基础:用户列表与角色模型的初步构建

【用户管理与权限 - 篇一】后端基础:用户列表与角色模型的初步构建 前言准备工作第一部分:回顾 Django 内置的 `User` 模型第二部分:设计并创建 `Role` 和 `UserProfile` 模型第三部分:创建 Serializers第四部分:创建 ViewSets第五部分:注册 API 路由第六部分:后端初步测…...