如何从video/webm格式的MediaRecorder录制文件中提取audio/wav格式音频用于Google语音转文字服务
解决WebM视频提取音频并转为WAV格式的问题
你遇到的核心问题是:直接将WebM视频的Blob标记为audio/wav并不会实际转换数据格式——你只是改了MIME类型标签,但底层还是WebM视频数据,所以系统会识别成video/x-matroska(WebM基于Matroska容器)。要得到真正的WAV音频,需要从视频流中提取音频轨道,再将其编码为WAV格式,同时保留原视频用于其他服务。
下面是具体的解决方案,分两步实现:
1. 保留原视频的录制逻辑
你的原录制代码不需要修改,因为要保留完整的WebM视频。mediaRecorderRef.current = new MediaRecorder(webcamRef.current.stream, { mimeType: "video/webm" }); 这部分可以继续使用,录制的recordedChunks会保存完整的视频数据,后续可以直接生成WebM Blob给其他服务使用。
2. 提取音频并转码为WAV格式
我们需要借助Web Audio API来提取视频中的音频,然后将原始PCM数据封装为标准WAV文件。以下是修改后的代码:
新增WAV格式转换工具函数
首先添加一个工具函数,用于将AudioBuffer(原始音频数据)转换为WAV格式的Blob:
// 将AudioBuffer转换为WAV格式的Blob function audioBufferToWav(audioBuffer) { const numberOfChannels = audioBuffer.numberOfChannels; const sampleRate = audioBuffer.sampleRate; const bytesPerSample = 2; // 16位PCM const length = audioBuffer.length * numberOfChannels * bytesPerSample + 44; const buffer = new ArrayBuffer(length); const view = new DataView(buffer); // 写入WAV文件头 const setUint16 = (offset, value) => { view.setUint16(offset, value, true); }; const setUint32 = (offset, value) => { view.setUint32(offset, value, true); }; setUint32(0, 0x46464952); // "RIFF" setUint32(4, length - 8); // 文件长度 - 8 setUint32(8, 0x45564157); // "WAVE" setUint32(12, 0x20746d66); // "fmt " setUint32(16, 16); // PCM格式的大小 setUint16(20, 1); // PCM编码 setUint16(22, numberOfChannels); setUint32(24, sampleRate); setUint32(28, sampleRate * numberOfChannels * bytesPerSample); // 比特率 setUint16(32, numberOfChannels * bytesPerSample); // 块对齐 setUint16(34, 8 * bytesPerSample); // 位深度 setUint32(36, 0x61746164); // "data" setUint32(40, length - 44); // 音频数据长度 // 写入PCM数据 let offset = 44; const channels = []; for (let i = 0; i < numberOfChannels; i++) { channels.push(audioBuffer.getChannelData(i)); } for (let i = 0; i < audioBuffer.length; i++) { for (let channel = 0; channel < numberOfChannels; channel++) { let sample = Math.max(-1, Math.min(1, channels[channel][i])); // 限制在[-1,1] sample = sample < 0 ? sample * 0x8000 : sample * 0x7fff; // 转换为16位有符号整数 view.setInt16(offset, sample, true); offset += 2; } } return new Blob([buffer], { type: "audio/wav" }); }
修改下载/转换逻辑
更新handleDownload函数,先将录制的WebM视频Blob解码为音频,再转换为WAV:
const handleDownload = useCallback(async () => { if (recordedChunks.length) { // 1. 生成原视频Blob(用于其他服务) const videoBlob = new Blob(recordedChunks, { type: "video/webm" }); // 2. 提取音频并转码为WAV try { const audioContext = new (window.AudioContext || window.webkitAudioContext)(); const arrayBuffer = await videoBlob.arrayBuffer(); const audioBuffer = await audioContext.decodeAudioData(arrayBuffer); const wavBlob = audioBufferToWav(audioBuffer); // 3. 下载WAV文件 const url = URL.createObjectURL(wavBlob); const a = document.createElement("a"); document.body.appendChild(a); a.style = "display: none"; a.href = url; a.download = "react-webcam-stream-capture.wav"; a.click(); window.URL.revokeObjectURL(url); } catch (error) { console.error("转换音频失败:", error); } setRecordedChunks([]); } }, [recordedChunks]);
关键说明
- 为什么原代码无效?:
recordedChunks存储的是WebM视频的二进制数据,直接修改Blob的type为audio/wav只是修改了元数据,实际内容还是视频,所以系统会识别为Matroska格式。 - Web Audio API的作用:
decodeAudioData方法可以从视频Blob中提取原始音频PCM数据,我们再手动添加WAV文件头,就能生成标准的WAV文件。 - 兼容性:
AudioContext在现代浏览器中都支持,若需要兼容旧版浏览器,可以添加webkitAudioContext的 fallback。
内容的提问来源于stack exchange,提问作者users
相关产品推荐
相关产品推荐

