You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 20:47:50