原生Js 提取视频中的音频
Js提取视频中的音频
将视频中的音频轨道分离出来,生成 wav 文件播放或下载( Vue3 setup )
代码实现
template
<button><label for="file" id="filename">选择视频文件</label><input type="file" name="file" id="file" accept="video/*,audio/*" @change="fileChange">
</button>
scss
button {position: absolute;top: calc(50vh - 30px);left: 10%;width: 80%;height: 60px;background-color: transparent;border: 1px solid gainsboro;border-radius: 10px;padding: 10px;
}input[type=file] {position: absolute;top: 0;left: 0;width: 100%;height: 100%;opacity: 0;filter: alpha(opacity=0);cursor: pointer;
}
setup
const fileChange = (e) => {const file = e.target.files[0]if (!file) returnconst label = document.getElementById('filename')label.innerHTML = file.namevideoToAudio(file).then(audio => {console.log('audio', audio)audio && (label.innerHTML = audio.fileName)})
}/**** video-to-audio* creater:qc* reference://github.com/mdn/webaudio-examples/tree/master/offline-audio-context-promise*/
const videoToAudio = async (file) => {try {console.log('videoToAudio file', file)const fileData = new Blob([file]) // video fileconst arrayBuffer = await new Promise((resolve) => {const reader = new FileReader()reader.onload = () => {const arrayBuffer = reader.resultresolve(arrayBuffer)}reader.readAsArrayBuffer(fileData)})console.log('arrayBuffer', arrayBuffer)const audioContext = new (window.AudioContext || window.webkitAudioContext || window.mozAudioContext || window.msAudioContext)()const decodedAudioData = await audioContext.decodeAudioData(arrayBuffer)console.log('decodedAudioData', decodedAudioData)const fileDuration = durationTrans(decodedAudioData.duration)console.log('fileDuration', fileDuration)const offlineAudioContext = new OfflineAudioContext(decodedAudioData.numberOfChannels, decodedAudioData.sampleRate * decodedAudioData.duration, decodedAudioData.sampleRate)const soundSource = offlineAudioContext.createBufferSource()soundSource.buffer = decodedAudioDatasoundSource.connect(offlineAudioContext.destination)soundSource.start()const renderedBuffer = await offlineAudioContext.startRendering()console.log('renderedBuffer', renderedBuffer) // outputs audiobufferconst wav = audioBufferToWav(renderedBuffer)const fileType = `wav`const fileName = `${file.name}.${fileType}`// -----------下载音频文件---------------------downloadWav(wav, fileName)// ------------------------------------------// -----------播放音频文件---------------------audioContext.decodeAudioData(wav, function (buffer) {const source = audioContext.createBufferSource();source.buffer = buffer;source.connect(audioContext.destination);source.start();// 在这里继续下一步}, function (error) {console.error('解码音频数据失败:', error);});// ------------------------------------------return {fileName, fileType, fileDuration}} catch (error) {// {code: 0, name: 'EncodingError', message: 'Unable to decode audio data'} Case:No audio in the video file ? Maybeconsole.log('videoToAudio error', error)return null} finally {console.log('videoToAudio finally')}
}/*** audiobuffer-to-wav* creater:https://github.com/Jam3/audiobuffer-to-wav*/
const audioBufferToWav = (buffer, opt) => {opt = opt || {}var numChannels = buffer.numberOfChannelsvar sampleRate = buffer.sampleRatevar format = opt.float32 ? 3 : 1var bitDepth = format === 3 ? 32 : 16var resultif (numChannels === 2) {result = interleave(buffer.getChannelData(0), buffer.getChannelData(1))} else {result = buffer.getChannelData(0)}return encodeWAV(result, format, sampleRate, numChannels, bitDepth)
}const encodeWAV = (samples, format, sampleRate, numChannels, bitDepth) => {var bytesPerSample = bitDepth / 8var blockAlign = numChannels * bytesPerSamplevar buffer = new ArrayBuffer(44 + samples.length * bytesPerSample)var view = new DataView(buffer)/* RIFF identifier */writeString(view, 0, 'RIFF')/* RIFF chunk length */view.setUint32(4, 36 + samples.length * bytesPerSample, true)/* RIFF type */writeString(view, 8, 'WAVE')/* format chunk identifier */writeString(view, 12, 'fmt ')/* format chunk length */view.setUint32(16, 16, true)/* sample format (raw) */view.setUint16(20, format, true)/* channel count */view.setUint16(22, numChannels, true)/* sample rate */view.setUint32(24, sampleRate, true)/* byte rate (sample rate * block align) */view.setUint32(28, sampleRate * blockAlign, true)/* block align (channel count * bytes per sample) */view.setUint16(32, blockAlign, true)/* bits per sample */view.setUint16(34, bitDepth, true)/* data chunk identifier */writeString(view, 36, 'data')/* data chunk length */view.setUint32(40, samples.length * bytesPerSample, true)if (format === 1) { // Raw PCMfloatTo16BitPCM(view, 44, samples)} else {writeFloat32(view, 44, samples)}return buffer
}const interleave = (inputL, inputR) => {var length = inputL.length + inputR.lengthvar result = new Float32Array(length)var index = 0var inputIndex = 0while (index < length) {result[index++] = inputL[inputIndex]result[index++] = inputR[inputIndex]inputIndex++}return result
}const writeFloat32 = (output, offset, input) => {for (var i = 0; i < input.length; i++, offset += 4) {output.setFloat32(offset, input[i], true)}
}const floatTo16BitPCM = (output, offset, input) => {for (var i = 0; i < input.length; i++, offset += 2) {var s = Math.max(-1, Math.min(1, input[i]))output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true)}
}const writeString = (view, offset, string) => {for (var i = 0; i < string.length; i++) {view.setUint8(offset + i, string.charCodeAt(i))}
}const downloadWav = (wav, fileName = 'audio') => {try {const blob = new window.Blob([new DataView(wav)], {type: 'audio/wav'})if ('download' in document.createElement('a')) {const url = window.URL.createObjectURL(blob)const anchor = document.createElement('a')document.body.appendChild(anchor)anchor.style = 'display: none'anchor.href = urlanchor.download = fileNameanchor.click()window.URL.revokeObjectURL(url)document.body.removeChild(anchor)} else {navigator.msSaveBlob(blob, fileName)}} catch (error) {console.log('downloadWav error', error)} finally {console.log('downloadWav finally')}
}const durationTrans = (a) => {let b = ''let h = parseInt(a / 3600),m = parseInt(a % 3600 / 60),s = parseInt(a % 3600 % 60)if (h > 0) {h = h < 10 ? '0' + h : hb += h + ':'}m = m < 10 ? '0' + m : ms = s < 10 ? '0' + s : sb += m + ":" + sreturn b
}
相关文章:
原生Js 提取视频中的音频
Js提取视频中的音频 将视频中的音频轨道分离出来,生成 wav 文件播放或下载( Vue3 setup ) 代码实现 template <button><label for"file" id"filename">选择视频文件</label><input type"fi…...
设计模式-备忘录模式(Memento Pattern)
文章目录 前言一、备忘录模式的概念二、备忘录模式的实现三、备忘录优缺点优点:缺点:总结 前言 备忘录模式(Memento Pattern)是一种行为型设计模式,它用于捕获和存储对象的内部状态,以便在以后可以恢复到先…...
PHP对接阿里云虚拟号的实现(号码隐私保护)
fastadmin 封装框架 实现功能:AXN隐私号绑定、解绑; 场景:为店铺手机号开通虚拟号,用户联系店铺展示虚拟号码; 官方开放文档地址:https://help.aliyun.com/document_detail/59655.html?spma2c4g.111742…...
刷新单年发射纪录:SpaceX成功发射62次猎鹰9号火箭
SpaceX一直都致力于推进航天领域的发展。近日,该公司的猎鹰9号火箭再次刷新了单年发射纪录,目前已经成功发射了62次。除此之外,今年SpaceX还发射了一枚猎鹰火箭和一枚巨型火箭。马斯克表示,他的目标是实现每月10次猎鹰飞行&#x…...
项目打包docker镜像 | 上传nexus | jenkins一键构建
文章目录 前言准备实操1、打开docker的远程访问2、编写dockerfile文件3、指定nexus环境4、配置jenkins5、使用jenkins构建 总结 前言 Docker部署项目是指使用Docker容器化技术将应用程序及其依赖项打包成一个独立的、可移植的运行环境,并在各种操作系统和平台上进行…...
ios 运行ipa包 日志查看方式
方法一: 使用ideviceinstaller工具 # 安装ipa命令 brew install ideviceinstaller ideviceinstaller -i xxx.ipa# 查看运行日志 idevicesyslog# idevicesyslog 查找命令 idevicesyslog | grep test -A 3 -B 2 # 输出关键字所在行后3行,前2行) idevic…...
AUTOSARCAN-Tp协议
目录 一.单帧、首帧、连续帧、流控帧 单帧传输 SF单帧: 多帧传输 FF(首帧): CF(连续帧): FC(流控帧): 一.单帧、首帧、连续帧、流控帧 CAN诊断由发送端…...
【设计模式】组合模式实现部门树实践
1.前言 几乎在每一个系统的开发过程中,都会遇到一些树状结构的开发需求,例如:组织机构树,部门树,菜单树等。只要是需要开发这种树状结构的需求,我们都可以使用组合模式来完成。 本篇将结合组合模式与Mysq…...
恒林家居引入纷享销客CRM系统,领跑家居行业营销数字化进程
近日,恒林家居股份有限公司((股票代码:603661以下简称为“恒林家居”)携手纷享销客在湖州召开了CRM项目启动会。双方领导及核心项目人员齐聚一堂,展开了深度交流并达成了重要共识。 作为家居行业的领军企业…...
多线程-锁的种类
1 作用 Java中的锁主要用于保障多并发线程情况下数据的一致性。在多线程编程中为了保障数据的一致性,我们通常需要在使用对象或者方法之前加锁,这时如果有其他线程也需要使用该对象或者该方法,则首先要获得锁,如果某个线程发现锁正在被其他线程使用,就会…...
Hive 和 HDFS、MySQL 之间的关系
文章目录 HiveHDFSMySQL三者的关系 Hive、MySQL 和 HDFS 是三个不同的数据存储和处理系统,它们在大数据生态系统中扮演不同的角色,但可以协同工作以支持数据管理和分析任务。 Hive Hive 是一个基于 Hadoop 生态系统的数据仓库工具,用于管理和…...
【面试题】如何实现数组去重的?有几种方式?
前端面试题库 (面试必备) 推荐:★★★★★ 地址:前端面试题库 【国庆头像】- 国庆爱国 程序员头像!总有一款适合你! 1. 方法一:利用两层循环数组的splice方法 通过两层循环对数组…...
使用TCP方式拉取Canal数据
1 Canal对接Kafka联调 1.1 配置修改 canal.properties 修改 zk: canal.zkServers 10.51.50.219:2181instance.properties 开启配置项: canal.mq.dynamicTopic 是 Canal 的 MQ 动态 Topic 配置项: test_javaedge_01 是kafka 的 topicte…...
Docker安装mysql实战说明
安装前准备 在安装MySQL之前,你需要确保已经正确安装和配置了Docker,可以通过以下命令检查Docker是否已正确安装: docker --version如果Docker已经成功安装,你将看到Docker的版本信息。 下载mysql的镜像 Docker Hub是一个存储…...
前端DOM操作精解:基础概念、方法与最佳实践
引言 本文将深入探讨前端开发中的DOM操作,包括基础概念、常用方法和最佳实践。通过清晰易懂的解释和实际案例分析,我们将一起了解如何最有效地使用DOM操作来提升前端应用的用户体验。 一、DOM操作入门 在深入探讨DOM操作之前,我们先要理解…...
python sorted函数详解2023.9.11
sorted函数详解 1. 输入和输出2. key传入函数 1. 输入和输出 help(sorted) Help on built-in function sorted in module builtins: sorted(iterable, /, *, keyNone, reverseFalse)Return a new list containing all items from the iterable in ascending order.A custom k…...
Spring Reactive:响应式编程与WebFlux的深度探索
🌷🍁 博主猫头虎(🐅🐾)带您 Go to New World✨🍁 🦄 博客首页——🐅🐾猫头虎的博客🎐 🐳 《面试题大全专栏》 🦕 文章图文…...
Qt应用开发(基础篇)——工具按钮类 QToolButton
一、前言 QToolButton类继承于QAbstractButton,该部件为命令或选项提供了一个快速访问按钮,通常用于QToolBar中。 按钮基类 QAbstractButton QToolButton是一个特殊的按钮,一般显示文本,只显示图标,结合toolBar使用。它…...
【数据结构面试题】栈与队列的相互实现
目录 1.队列实现栈 1.1创建栈 1.2判断是否为空 1.3入栈 1.4出栈 1.5获取栈顶元素 1.6完整代码 2. 用栈实现队列 2.1创建队列 2.2判断是否为空 2.3入队列 2.4出队列 2.5获取队头元素 2.6完整代码 1.队列实现栈 用队列实现栈https://leetcode.cn/problems/impleme…...
华为认证和红帽认证哪个比较好考呢
华为认证和红帽认证的考试难度、学习内容、适用范围等方面都有所不同,因此哪个比较好考要视具体情况而定: 考试难度:红帽认证的考试难度较高,需要考生具备较高的技术水平和实践经验;而华为认证则更注重基础知识的考察…...
2025年阿里云幻兽帕鲁联机服务器极速搭建指南
1. 为什么选择阿里云搭建幻兽帕鲁服务器? 最近很多朋友问我,为什么非要选择阿里云来搭建幻兽帕鲁的联机服务器?作为一个从游戏测试阶段就开始折腾服务器搭建的老玩家,我总结了几个关键原因。首先,阿里云的游戏服务器专…...
Dify工作流架构:声明式编排与可视化执行引擎的技术实现
Dify工作流架构:声明式编排与可视化执行引擎的技术实现 【免费下载链接】Awesome-Dify-Workflow 分享一些好用的 Dify DSL 工作流程,自用、学习两相宜。 Sharing some Dify workflows. 项目地址: https://gitcode.com/GitHub_Trending/aw/Awesome-Dify…...
星穹铁道自动化解决方案:用March7thAssistant释放游戏时间价值
星穹铁道自动化解决方案:用March7thAssistant释放游戏时间价值 【免费下载链接】March7thAssistant 🎉 崩坏:星穹铁道全自动 Honkai Star Rail 🎉 项目地址: https://gitcode.com/gh_mirrors/ma/March7thAssistant 副标题&…...
Datawhale AI冬令营-学习笔记-task1
很多企业训练出来的通用模型,我们在使用时并不能很好得解答我们生活中的疑惑,故我们需要一些定制专属大模型来解答在特殊情境下的特定问题,通过投喂一些特定的数据,使得让专属模型在特定领域有着更出色的表现。本次学习将 基于《甄…...
美胸-年美-造相Z-Turbo在网络安全领域的创新应用:恶意代码可视化分析
美胸-年美-造相Z-Turbo在网络安全领域的创新应用:恶意代码可视化分析 1. 当安全分析遇上图像生成:一个意想不到的跨界组合 最近在调试一个自动化威胁分析流程时,我偶然发现了一个有趣的现象:当把一段混淆后的JavaScript恶意代码…...
CSSCI论文写作03:确定论文的选题
什么是选题 选题:选择一个适合的研究指向!!! 选择: 而不是创造,创新是内在要求 你要坚信:所有的选题都有前人关注过研究过,我们不求“栽树”,只求“乘凉”,填补什么空白,只能说明自己的浅薄无知。 适合: 个人经验的学术表达,找到那双穿在自己脚上的鞋子没有不能…...
OpenClaw:四大使用挑战与破局思路
子玥酱 (掘金 / 知乎 / CSDN / 简书 同名) 大家好,我是 子玥酱,一名长期深耕在一线的前端程序媛 👩💻。曾就职于多家知名互联网大厂,目前在某国企负责前端软件研发相关工作,主要聚…...
S3 文件操作进阶实践:从基础上传到完整性保障
1. S3文件操作的核心挑战与解决方案 第一次接触AWS S3时,很多人会觉得文件上传下载不就是调用几个API的事?但真正投入生产环境后,各种问题就会接踵而至。我见过最典型的案例是某电商平台在促销期间,因为文件上传没有做完整性校验…...
从ADC的‘胃口’说起:深入浅出解析电平移位电路中基准源VREF与滤波电容的选型玄学
从ADC的"胃口"说起:深入浅出解析电平移位电路中基准源VREF与滤波电容的选型玄学 在模拟电路设计中,ADC(模数转换器)就像一位挑剔的美食家,对输入信号的"口味"有着严苛的要求。而电平移位电路则如同…...
别再让PowerBI报告挤成一团了!用按钮+书签,一个页面搞定趋势和明细分析
PowerBI交互设计进阶:用按钮与书签打造空间魔术 当业务分析报告遇上数据爆炸时代,信息过载与界面拥挤成为每个分析师挥之不去的噩梦。我曾见过某零售企业的季度分析仪表板——12个图表密密麻麻挤在A4纸大小的画布上,趋势线相互缠绕ÿ…...
