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

如何通过Socket.IO向Node.js后端按指定大小实时发送浏览器录音片段

按指定字节大小实时发送录制的音频片段

要实现按指定千字节大小实时发送音频片段,你需要手动缓存MediaRecorder输出的数据,当缓存达到目标大小后立即发送,而非等待录制停止。以下是修改后的完整实现:

const socket = io();

const recordButton = document.getElementById('record');
const stopButton = document.getElementById('stop_rec');

let userId;
// 指定每块音频的目标大小(单位:KB),可按需调整
const TARGET_CHUNK_SIZE_KB = 10;
const TARGET_CHUNK_SIZE_BYTES = TARGET_CHUNK_SIZE_KB * 1024;
// 用于缓存音频数据的缓冲区
let audioChunkBuffer = new Uint8Array();

socket.on('connect', () => {
  userId = socket.id;

  socket.on(userId, (msg) => {
    console.log('Message from server:', msg);
  });

  recordButton.addEventListener('click', async() => {
    audioStream();
  });

  stopButton.addEventListener('click', () => {
    doneSpeaking();
  });
});

let mediaRecorder;

async function audioStream() {
  try {
    const constraints = {
      audio: {
        sampleRate: 16000,
        channelCount: 1
      }
    };
    const stream = await navigator.mediaDevices.getUserMedia(constraints);
    mediaRecorder = new MediaRecorder(stream);

    // 启动录制,传入100ms时间片让MediaRecorder频繁输出数据片段
    mediaRecorder.start(100);

    const handleDataAvailable = async(e) => {
      if (e.data.size === 0) return;

      // 将Blob转换为ArrayBuffer,方便合并和拆分
      const arrayBuffer = await e.data.arrayBuffer();
      const newData = new Uint8Array(arrayBuffer);
      
      // 合并到缓存缓冲区
      const combinedBuffer = new Uint8Array(audioChunkBuffer.length + newData.length);
      combinedBuffer.set(audioChunkBuffer);
      combinedBuffer.set(newData, audioChunkBuffer.length);
      audioChunkBuffer = combinedBuffer;

      // 循环处理缓存,确保一次性收到大量数据时能拆分多次发送
      while (audioChunkBuffer.length >= TARGET_CHUNK_SIZE_BYTES) {
        // 截取目标大小的片段
        const chunkToSend = audioChunkBuffer.slice(0, TARGET_CHUNK_SIZE_BYTES);
        // 转成Blob发送,保留原音频MIME类型
        const chunkBlob = new Blob([chunkToSend], { type: mediaRecorder.mimeType });
        socket.emit(userId, chunkBlob);

        // 更新缓存,保留剩余未发送的数据
        audioChunkBuffer = audioChunkBuffer.slice(TARGET_CHUNK_SIZE_BYTES);
      }
    };

    mediaRecorder.ondataavailable = handleDataAvailable;

    mediaRecorder.onstop = async() => {
      console.log('Recording stopped');
      // 发送剩余的缓存数据,避免丢失最后一段音频
      if (audioChunkBuffer.length > 0) {
        const remainingBlob = new Blob([audioChunkBuffer], { type: mediaRecorder.mimeType });
        socket.emit(userId, remainingBlob);
        audioChunkBuffer = new Uint8Array();
      }
    };
  } catch (error) {
    console.error('Error accessing microphone:', error);
  }
}

function doneSpeaking() {
  if (mediaRecorder && mediaRecorder.state === 'recording') {
    mediaRecorder.stop();
  }
}

关键改动说明

  • 分块大小配置:通过TARGET_CHUNK_SIZE_KB设置每块音频的大小,转换为字节后作为发送阈值。
  • 缓存缓冲区:用Uint8Array存储合并后的音频数据,便于进行拆分操作。
  • 数据合并与拆分:每次收到MediaRecorder输出的Blob后,转成ArrayBuffer合并到缓存;当缓存达到目标大小,就截取对应长度的片段转成Blob发送,剩余数据继续留在缓存。
  • 实时触发数据输出:调用mediaRecorder.start(100)传入100ms时间片,让MediaRecorder每隔100ms输出一段数据,保证实时性。
  • 停止时清理缓存:录制停止后发送剩余缓存数据,避免丢失最后一段音频。

注意事项

  • 确保服务器端能按录制顺序正确处理连续的音频Blob片段。
  • MediaRecorder的MIME类型(如audio/webm)需与服务器端处理逻辑匹配,避免格式不兼容。
  • 若网络延迟较高,可适当调整分块大小,平衡实时性与传输效率。

内容的提问来源于stack exchange,提问作者seriously

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 23:35:35