方案1,获取推流拼接到数据,播放时将 Blob 转换为 URL,把audioUrl赋值给
ws.onmessage((res) => {
// console.log('onmessage res.data',res.data)
if (typeof res.data == "object") {
blobs.value.push(res.data);
const newBolb = new Blob([...blobs.value], { type: "audio/mp3" });
// 将 Blob 转换为 URL
audioUrl.value = URL.createObjectURL(newBolb);
}
// console.log('onmessage audioUrl.value',audioUrl.value)
});
<audio ref="audioPlayer" controls>
<source :src="audioUrl" type="audio/mpeg" autoplay />
Your browser does not support the audio element.
</audio>
方案2 边获取推流边解析
let mediaSource = new MediaSource();
let sourceBuffer;
let queue = [];
let isUpdating = false;
const audioPlayer = document.getElementById('audioPlayer'); // Assume you have an <audio> element with this id
audioPlayer.src = URL.createObjectURL(mediaSource);
const appendBuffer = (blob) => {
const reader = new FileReader();
reader.onload = () => {
const arrayBuffer = reader.result;
queue.push(arrayBuffer);
processQueue();
};
reader.readAsArrayBuffer(blob);
};
const processQueue = () => {
if (queue.length > 0 && !isUpdating && sourceBuffer && !audioPlayer.error) {
const buffer = queue.shift();
isUpdating = true;
sourceBuffer.appendBuffer(buffer);
}
};
const onSourceOpen = () => {
if (!sourceBuffer) {
sourceBuffer = mediaSource.addSourceBuffer('audio/mpeg');
sourceBuffer.mode = 'sequence'; // Ensure the buffer is appended in sequence
sourceBuffer.addEventListener('updateend', () => {
isUpdating = false;
processQueue(); // Continue processing the queue after each update
});
}
};
mediaSource.addEventListener('sourceopen', onSourceOpen);
ws.onmessage = (res) => {
if (typeof res.data === 'object') {
appendBuffer(res.data);
}
};
这段代码旨在通过WebSocket实时接收音频流,并使用MediaSource
和SourceBuffer
将其解析并播放。以下是代码的详细解析:
1. 变量定义与初始化
let mediaSource = new MediaSource();
let sourceBuffer;
let queue = [];
let isUpdating = false;
mediaSource
: 创建一个MediaSource
对象,用于管理媒体数据源。sourceBuffer
: 用于存储音频数据的SourceBuffer
对象,稍后会初始化。queue
: 队列,用于暂存从WebSocket接收到的音频数据,以防止在SourceBuffer
忙碌时丢失数据。isUpdating
: 布尔标志,用于指示SourceBuffer
是否正在更新。如果正在更新,则不会添加新的数据。
2. 音频播放器初始化
const audioPlayer = document.getElementById('audioPlayer');
audioPlayer.src = URL.createObjectURL(mediaSource);
- 获取页面中的音频播放器元素(假设ID为
audioPlayer
),并将MediaSource
对象与之关联。
3. 数据追加函数 appendBuffer
const appendBuffer = (blob) => {
const reader = new FileReader();
reader.onload = () => {
const arrayBuffer = reader.result;
queue.push(arrayBuffer);
processQueue();
};
reader.readAsArrayBuffer(blob);
};
appendBuffer
函数接收一个blob
对象,即通过WebSocket接收到的音频数据。- 使用
FileReader
将blob
对象转换为ArrayBuffer
,然后将其加入queue
队列中。 processQueue
函数在转换完成后被调用,用于处理队列中的数据。
4. 处理队列中的数据 processQueue
const processQueue = () => {
if (queue.length > 0 && !isUpdating && sourceBuffer && !audioPlayer.error) {
const buffer = queue.shift();
isUpdating = true;
sourceBuffer.appendBuffer(buffer);
}
};
processQueue
函数检查queue
队列中是否有数据,并确保SourceBuffer
没有在更新(即isUpdating
为false
)。- 如果满足条件,则从队列中取出第一个数据并将其添加到
SourceBuffer
中,同时将isUpdating
标志设置为true
,以防止在更新期间追加新的数据。
5. SourceBuffer的初始化与事件处理
const onSourceOpen = () => {
if (!sourceBuffer) {
sourceBuffer = mediaSource.addSourceBuffer('audio/mpeg');
sourceBuffer.mode = 'sequence';
sourceBuffer.addEventListener('updateend', () => {
isUpdating = false;
processQueue();
});
}
};
mediaSource.addEventListener('sourceopen', onSourceOpen);
- 当
mediaSource
打开时,onSourceOpen
函数被调用。 - 如果
sourceBuffer
还未初始化,则创建一个新的SourceBuffer
,并将mode
设置为'sequence'
,以确保数据按顺序追加。 - 为
sourceBuffer
添加updateend
事件监听器,当更新完成后,将isUpdating
标志重置为false
,然后继续处理队列中的下一个数据。
6. WebSocket消息处理
ws.onmessage = (res) => {
if (typeof res.data === 'object') {
appendBuffer(res.data);
}
};
- 当WebSocket接收到消息时,如果消息的数据类型是
object
(即二进制数据),则调用appendBuffer
函数将其处理并追加到队列中。
总结
该代码通过引入队列和isUpdating
标志来有效地管理音频流的实时处理,确保数据能按顺序、无缝地追加到SourceBuffer
中,并在更新完成后继续处理未处理的数据,避免了数据丢失或播放中断的情况。