我正在开发一个 Web 应用程序,它使用 WebRTC 和 Socket.IO 将音频从管理员实时传输到多个用户。但是,我遇到了一个问题,即用户端的音频播放断断续续或间歇性停止。以下是设置和问题的概述:
设置: 管理员使用 getUserMedia 和 MediaRecorder 从麦克风捕获音频。 使用lamejs将音频从WAV编码为MP3,并通过Socket.IO传输给用户。 用户接收音频块并尝试使用该元素播放它们。
问题:
虽然音频播放成功开始,但经常会在短时间内卡顿或停止。
管理员端代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Admin Broadcasting</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lamejs/1.2.0/lame.min.js"></script>
</head>
<body>
<h1>Admin Broadcasting</h1>
<button id="startBroadcastBtn">Start Broadcasting</button>
<script src="/socket.io/socket.io.js"></script>
<script>
const startBroadcastBtn = document.getElementById('startBroadcastBtn');
const socket = io('ws://localhost:9000');
let mediaRecorder;
startBroadcastBtn.addEventListener('click', async () => {
try {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
if (!stream) {
console.error('getUserMedia failed to return a valid stream');
return;
}
// Initialize MediaRecorder with WAV format
mediaRecorder = new MediaRecorder(stream);
// Create WAV to MP3 converter using lamejs
const mp3Encoder = new lamejs.Mp3Encoder(1, 44100, 128);
// Add event listener for data availability
mediaRecorder.ondataavailable = async (event) => {
try {
if (event.data.size > 0) {
console.log('Audio chunk captured:', event.data);
// Convert WAV audio data to MP3
const reader = new FileReader();
reader.onload = async () => {
const arrayBuffer = reader.result;
const buffer = new Int8Array(arrayBuffer);
const length = buffer.length;
const pcm = new Int16Array(length / 2);
// Convert Int8Array to Int16Array (PCM data)
for (let i = 0; i < length; i += 2) {
pcm[i / 2] = buffer[i] | (buffer[i + 1] << 8);
}
const mp3Data = mp3Encoder.encodeBuffer(pcm);
const mp3Blob = new Blob([mp3Data], { type: 'audio/mp3' });
console.log('Created MP3 blob:', mp3Blob);
socket.emit('admin-audio-chunk', mp3Blob);
};
reader.readAsArrayBuffer(event.data);
}
} catch (error) {
console.error('Error processing audio data:', error);
}
};
// Start recording
console.log('Starting recording...');
mediaRecorder.start(1000);
console.log('Recording started.');
} catch (error) {
console.error('Error accessing media devices:', error);
}
});
</script>
</body>
</html>
用户端代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>User Listening</title>
</head>
<body>
<h1>User Listening</h1>
<audio id="audioPlayer" controls preload="auto"></audio>
<script src="/socket.io/socket.io.js"></script>
<script>
const audioPlayer = document.getElementById('audioPlayer');
const socket = io('ws://localhost:9000');
let audioChunks = [];
socket.on('user-audio-chunk', (data) => {
console.log('Received audio chunk from admin:', data);
audioChunks.push(data);
playAudioChunks();
});
function playAudioChunks() {
if (audioChunks.length > 0 && audioPlayer.paused) {
const audioBlob = new Blob(audioChunks, { type: 'audio/mp3' });
audioPlayer.src = URL.createObjectURL(audioBlob);
audioPlayer.play().catch((error) => {
console.error('Error playing audio:', error);
});
audioChunks = [];
}
}
</script>
</body>
</html>
如何流畅播放音频并解决问题?
设置:管理员使用 getUserMedia 和 MediaRecorder 从麦克风捕获音频。使用lamejs将音频从WAV编码为MP3,并通过Socket.IO传输给用户。用户接收音频块并尝试使用该元素播放它们。
您正在编码块并播放块......所以这就是为什么您有厚实的声音音频。如果您想要音频流,您需要将其视为流。
不要每次都实例化新的编解码器。我不知道你的 LAME 版本如何,但常规 LAME 将利用位存储库,它将数据从一帧传输到另一帧。因此,您需要设置一个实例并不断向其推送 PCM 数据。
现在,在播放方面,根本不要使用 Web Sockets 之类的东西。比那更简单。使用简单的
<audio>
元素并通过 HTTP stream 数据:
<audio src="https://stream.example.com/something"></audio>
您的服务器应该获取它获得的块并立即将它们写入客户端。相反,如果您尝试单独解码每个片段,您只会得到解码的块,并且它们不会神奇地及时播放并排队。