Vue.js消息项目音频Blob转MP3遇问题,求可靠转码方案
Vue项目中音频转MP3的可靠解决方案
一、搞定低版本FFmpeg的SharedArrayBuffer报错
这个错误是浏览器对SharedArrayBuffer有跨源隔离要求导致的,只需在Vue项目的开发服务器配置里加两个响应头:
- Vite项目:修改
vite.config.js
export default defineConfig({ server: { headers: { 'Cross-Origin-Opener-Policy': 'same-origin', 'Cross-Origin-Embedder-Policy': 'require-corp' } } })
- Vue CLI项目:修改
vue.config.js
module.exports = { devServer: { headers: { 'Cross-Origin-Opener-Policy': 'same-origin', 'Cross-Origin-Embedder-Policy': 'require-corp' } } }
改完重启服务,SharedArrayBuffer就能正常使用了。
二、用新版FFmpeg解决编译报错
高版本编译失败是因为@ffmpeg/core的文件结构变更,直接安装适配性较好的新版本即可:
- 安装命令:
npm install @ffmpeg/ffmpeg@0.20.4 @ffmpeg/core@0.20.1
- 完整Vue组件示例:
<template> <div> <button @click="startRecording">开始录制</button> <button @click="stopRecording">停止录制</button> <div id="waveform"></div> <audio ref="audioPlayer" controls></audio> </div> </template> <script setup> import { ref, onMounted } from 'vue' import WaveSurfer from 'wavesurfer.js' import { createFFmpeg, fetchFile } from '@ffmpeg/ffmpeg' const wavesurfer = ref(null) const audioPlayer = ref(null) const ffmpeg = createFFmpeg({ log: true }) onMounted(async () => { // 加载FFmpeg核心 await ffmpeg.load() // 初始化波形录制组件 wavesurfer.value = WaveSurfer.create({ container: '#waveform', waveColor: '#4F4A85', progressColor: '#383351', barWidth: 2, barRadius: 3, cursorWidth: 1, height: 100, barGap: 2 }) }) const startRecording = () => { wavesurfer.value.startRecording() } const stopRecording = async () => { const blob = await wavesurfer.value.stopRecording() // 转码为MP3 await transcodeToMP3(blob) } const transcodeToMP3 = async (inputBlob) => { const inputExt = inputBlob.type.split('/')[1] const inputFileName = `input.${inputExt}` const outputFileName = 'output.mp3' // 将录制的Blob写入FFmpeg虚拟文件系统 ffmpeg.FS('writeFile', inputFileName, await fetchFile(inputBlob)) // 执行转码命令:libmp3lame为MP3编码器,-q:a 2代表高质量压缩 await ffmpeg.run('-i', inputFileName, '-codec:a', 'libmp3lame', '-q:a', '2', outputFileName) // 读取转码后的MP3文件 const mp3Data = ffmpeg.FS('readFile', outputFileName) const mp3Blob = new Blob([mp3Data.buffer], { type: 'audio/mp3' }) // 预览转好的音频 audioPlayer.value.src = URL.createObjectURL(mp3Blob) // 可在此添加上传逻辑,示例: // await uploadMP3(mp3Blob) } // 音频上传示例(可选) const uploadMP3 = async (mp3Blob) => { const formData = new FormData() formData.append('audio', mp3Blob, 'record.mp3') try { await fetch('/api/upload-audio', { method: 'POST', body: formData }) console.log('上传成功') } catch (err) { console.error('上传失败', err) } } </script> <style scoped> #waveform { margin: 20px 0; } </style>
三、无FFmpeg依赖方案:lamejs纯前端转码
嫌FFmpeg配置麻烦?试试lamejs,纯JS实现MP3编码,无需依赖WebAssembly:
- 安装命令:
npm install lamejs
- 组件示例:
<template> <div> <button @click="startRecording">开始录制</button> <button @click="stopRecording">停止录制</button> <div id="waveform"></div> <audio ref="audioPlayer" controls></audio> </div> </template> <script setup> import { ref, onMounted } from 'vue' import WaveSurfer from 'wavesurfer.js' import lamejs from 'lamejs' const wavesurfer = ref(null) const audioPlayer = ref(null) onMounted(() => { wavesurfer.value = WaveSurfer.create({ container: '#waveform', waveColor: '#4F4A85', progressColor: '#383351', barWidth: 2, barRadius: 3, cursorWidth: 1, height: 100, barGap: 2 }) }) const startRecording = () => { wavesurfer.value.startRecording() } const stopRecording = async () => { const blob = await wavesurfer.value.stopRecording() await transcodeWithLamejs(blob) } const transcodeWithLamejs = async (blob) => { // 将Blob转为ArrayBuffer const arrayBuffer = await blob.arrayBuffer() const audioContext = new (window.AudioContext || window.webkitAudioContext)() const audioBuffer = await audioContext.decodeAudioData(arrayBuffer) // 提取单声道PCM数据(立体声需自行合并声道) const pcmData = audioBuffer.getChannelData(0) const samples = new Int16Array(audioBuffer.length) // 将Float32格式的音频数据转为Int16 pcmData.forEach((sample, idx) => { samples[idx] = sample < 0 ? sample * 0x8000 : sample * 0x7FFF }) // 初始化编码器:1声道、采样率、128kbps比特率 const mp3encoder = new lamejs.Mp3Encoder(1, audioBuffer.sampleRate, 128) const mp3Chunks = [] const chunkSize = 1152 // lamejs要求的固定分块大小 // 分块编码MP3 for (let i = 0; i < samples.length; i += chunkSize) { const chunk = samples.subarray(i, i + chunkSize) const mp3Buf = mp3encoder.encodeBuffer(chunk) if (mp3Buf.length > 0) { mp3Chunks.push(new Uint8Array(mp3Buf)) } } // 结束编码,获取剩余数据 const endBuf = mp3encoder.flush() if (endBuf.length > 0) { mp3Chunks.push(new Uint8Array(endBuf)) } // 生成MP3格式Blob并预览 const mp3Blob = new Blob(mp3Chunks, { type: 'audio/mp3' }) audioPlayer.value.src = URL.createObjectURL(mp3Blob) } </script> <style scoped> #waveform { margin: 20px 0; } </style>
注意事项
- FFmpeg方案首次加载会下载WebAssembly文件,生产环境可将文件部署到CDN加速加载
- lamejs默认仅支持单声道转码,若需立体声需自行处理声道合并逻辑
- 生产环境使用FFmpeg方案时,服务器也需配置跨源响应头,否则
SharedArrayBuffer仍会报错
内容的提问来源于stack exchange,提问作者Raphael M
相关产品推荐
相关产品推荐

