原生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…...
华为认证和红帽认证哪个比较好考呢
华为认证和红帽认证的考试难度、学习内容、适用范围等方面都有所不同,因此哪个比较好考要视具体情况而定: 考试难度:红帽认证的考试难度较高,需要考生具备较高的技术水平和实践经验;而华为认证则更注重基础知识的考察…...
实战演练:基于快马平台快速开发一个可动态切换主题色的网站Demo
今天想和大家分享一个非常实用的前端小项目——如何快速开发一个能动态切换主题色的网站Demo。这个功能在实际项目中特别常见,比如我们常见的深色模式切换、企业官网的主题定制等。下面我就用InsCode(快马)平台来演示整个实现过程。 项目结构设计 首先我们需要规划…...
告别裸机轮询:在GD32F30x上用USART中断和回调函数实现驱动解耦
GD32F30x串口驱动架构升级:从轮询到中断回调的工程化实践 在嵌入式开发中,串口通信作为最基础的外设接口之一,其实现方式往往决定了整个系统的响应效率和代码质量。许多工程师在项目初期为了快速验证功能,常采用简单的轮询方式处理…...
问题解决:AI股票分析师启动失败?自查脚本与Ollama服务加载
问题解决:AI股票分析师启动失败?自查脚本与Ollama服务加载 1. 引言 你满怀期待地部署了那个“AI股票分析师”镜像,点击启动,然后……页面一片空白,或者提示服务不可用。这种感觉就像准备大展拳脚时,发现工…...
开箱即用!LongCat动物百变秀本地部署指南,小白也能快速上手
开箱即用!LongCat动物百变秀本地部署指南,小白也能快速上手 1. 什么是LongCat动物百变秀? LongCat动物百变秀是一款基于美团开源模型开发的AI图片编辑工具,专门用于动物图片的创意编辑。它最大的特点是能够通过简单的自然语言描…...
Granite TimeSeries FlowState R1电商销量预测实战:Vue前端可视化大屏
Granite TimeSeries FlowState R1电商销量预测实战:Vue前端可视化大屏 最近和几个做电商的朋友聊天,他们都在头疼同一个问题:备货。备多了怕压库存,备少了又怕错过销售高峰,眼睁睁看着流量来了却没货可发。传统的经验…...
AnotherRedisDesktopManager:让Redis管理变得简单高效的5个理由
AnotherRedisDesktopManager:让Redis管理变得简单高效的5个理由 【免费下载链接】AnotherRedisDesktopManager qishibo/AnotherRedisDesktopManager: Another Redis Desktop Manager 是一款跨平台的Redis桌面管理工具,提供图形用户界面,支持连…...
嵌入式C++ RAII互斥锁封装器MutexLocker详解
1. MutexLocker:嵌入式C RAII风格互斥锁封装器深度解析1.1 设计动机与工程价值在基于mbed RTOS(现为Mbed OS中CMSIS-RTOS v2兼容层)的嵌入式实时系统开发中,互斥量(Mutex)是保障多任务共享资源安全访问的核…...
鸿蒙SpeechKit离线语音识别避坑指南:从PCM格式到权限配置,一次搞定
鸿蒙SpeechKit离线语音识别实战避坑指南 1. 音频格式的致命陷阱 PCM格式是鸿蒙SpeechKit离线语音识别的唯一选择,但开发者常犯的错误远不止文件类型这么简单。我曾见过一个团队花费三天时间排查识别率低的问题,最终发现是采样深度设置错误——这个细节在…...
Obsidian Local Images Plus 插件使用指南
Obsidian Local Images Plus 插件使用指南 【免费下载链接】obsidian-local-images-plus This repo is a reincarnation of obsidian-local-images plugin which main aim was downloading images in md notes to local storage. 项目地址: https://gitcode.com/gh_mirrors/o…...
OpenClaw多设备同步:GLM-4.7-Flash配置共享方案
OpenClaw多设备同步:GLM-4.7-Flash配置共享方案 1. 为什么需要多设备同步配置? 去年冬天,我在办公室和家里两台MacBook上分别部署了OpenClaw对接GLM-4.7-Flash模型。很快发现一个头疼的问题:每次在办公室调试好的技能参数&#…...
