如何通过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
相关产品推荐
相关产品推荐

