通过 WebSocket 流式传输音频 - 网络音频
Stream Audio via WebSocket - Web Audio
我非常接近通过 Websockets 进行音频聊天。我正在构建的这个应用程序的想法是在浏览器中进行群组语音聊天。
我正在使用 socket.io 服务器来转发此信息。
音频传输正常。使用此代码:
let hasHeader = false
export function emitAudioStream(mic, sock, room) {
console.log('beginning record')
const recorder = new MediaRecorder(mic)
recorder.ondataavailable = (evt) => {
// fetch the header
if (!hasHeader) {
console.log('header:', evt.data)
sock.emit('header:audio', evt.data)
hasHeader = true
}
// console.log(evt.data.size)
sock.emit('stream:audio', ({room, streamData: evt.data}))
}
recorder.start()
console.log(`Recording begin. (State: "${recorder.state}")`)
setInterval(() => {
recorder.requestData()
}, 1e3/60)
}
有 'participants' 个房间已连接 - 个人。服务器处理这样的请求:
sock.on('header:audio', (packet) => {
console.log(`setting audio header for ${sock.id}`)
sock.__audioHeader = packet
})
sock.on('stream:audio', ({room, streamData}) => {
const participants = rooms[room]
if (!participants) {
console.log(`not found ${room} room`)
return
}
// create a getParticipants to handle not found
// add flag to include current socket
participants.forEach(participant => {
// if (participant.id === sock.id) return
participant.emit('stream:audio:packet', {header: sock.__audioHeader, streamData})
})
})
回到客户端,我正在尝试玩(这一切都失败了),它看起来像这样。我可能误解了网络音频文档。谁能给我指出正确的方向 direction/explain 为什么这不是正确的方法?
sck.on('stream:audio:packet', ({header, streamData}) => {
playSound(streamData)
})
function playSound(buffer) {
const context = new AudioContext()
var source = context.createBufferSource()
source.buffer = buffer
source.connect(context.destination)
source.start(0)
}
我使用的另一种解码尝试:
sck.on('stream:audio:packet',async ({header, streamData}) => {
if (streamData === 'data:') return
const b64ToBuffer = (data) => fetch(data).then(r => r.blob())
const buff = await b64ToBuffer(streamData)
playSound(await buff.arrayBuffer())
})
let context = new AudioContext()
async function playSound(buffer) {
try {
const buff = await context.decodeAudioData(buffer)
let source = context.createBufferSource()
source.connect(context.destination)
console.log(buff)
source.buffer = buff
source.start(0)
} catch (err) {
console.warn('error decoding data:', err)
}
}
您当前的解决方案不起作用的原因是 MediaRecorder
不需要发出可以自行编码的块。在停止 MediaRecorder
之后需要将所有块拼接在一起以获得有效文件。此外,网络音频 API 只能使用其 decodeAudioData()
方法解码完整文件。
正如上面评论中所述,WebRTC 是专门为此用例制作的 API。如果你想有单独的房间,你可以确保你的信号处理只连接属于同一个房间的客户端。
如果您想避免使用 WebRTC,您可以尝试我编写的一个库,它为 MediaRecorder
添加了 WAVE 支持。该库名为 extendable-media-recorder。当被要求发出块时,这些块本身也不是有效的 WAVE 文件,但手动解码部分 WAVE 文件比解码压缩文件容易得多。尽管包含 header 的前 44 个字节只是原始 PCM 数据。
您也可以反其道而行之,保留原生 MediaRecorder
并将其与接收端的自定义解码器结合使用。如果您配置 MediaRecorder
来编码 Opus 文件 opus-stream-decoder 应该能够解码块。
使用流,您可以使用 navigator.mediaDevices.getUserMedia(constraints)
MDN Reference 获取流
并将 socket.io-streams 添加到 socket.io
并使用音频元素或视频元素播放它们
我非常接近通过 Websockets 进行音频聊天。我正在构建的这个应用程序的想法是在浏览器中进行群组语音聊天。
我正在使用 socket.io 服务器来转发此信息。
音频传输正常。使用此代码:
let hasHeader = false
export function emitAudioStream(mic, sock, room) {
console.log('beginning record')
const recorder = new MediaRecorder(mic)
recorder.ondataavailable = (evt) => {
// fetch the header
if (!hasHeader) {
console.log('header:', evt.data)
sock.emit('header:audio', evt.data)
hasHeader = true
}
// console.log(evt.data.size)
sock.emit('stream:audio', ({room, streamData: evt.data}))
}
recorder.start()
console.log(`Recording begin. (State: "${recorder.state}")`)
setInterval(() => {
recorder.requestData()
}, 1e3/60)
}
有 'participants' 个房间已连接 - 个人。服务器处理这样的请求:
sock.on('header:audio', (packet) => {
console.log(`setting audio header for ${sock.id}`)
sock.__audioHeader = packet
})
sock.on('stream:audio', ({room, streamData}) => {
const participants = rooms[room]
if (!participants) {
console.log(`not found ${room} room`)
return
}
// create a getParticipants to handle not found
// add flag to include current socket
participants.forEach(participant => {
// if (participant.id === sock.id) return
participant.emit('stream:audio:packet', {header: sock.__audioHeader, streamData})
})
})
回到客户端,我正在尝试玩(这一切都失败了),它看起来像这样。我可能误解了网络音频文档。谁能给我指出正确的方向 direction/explain 为什么这不是正确的方法?
sck.on('stream:audio:packet', ({header, streamData}) => {
playSound(streamData)
})
function playSound(buffer) {
const context = new AudioContext()
var source = context.createBufferSource()
source.buffer = buffer
source.connect(context.destination)
source.start(0)
}
我使用的另一种解码尝试:
sck.on('stream:audio:packet',async ({header, streamData}) => {
if (streamData === 'data:') return
const b64ToBuffer = (data) => fetch(data).then(r => r.blob())
const buff = await b64ToBuffer(streamData)
playSound(await buff.arrayBuffer())
})
let context = new AudioContext()
async function playSound(buffer) {
try {
const buff = await context.decodeAudioData(buffer)
let source = context.createBufferSource()
source.connect(context.destination)
console.log(buff)
source.buffer = buff
source.start(0)
} catch (err) {
console.warn('error decoding data:', err)
}
}
您当前的解决方案不起作用的原因是 MediaRecorder
不需要发出可以自行编码的块。在停止 MediaRecorder
之后需要将所有块拼接在一起以获得有效文件。此外,网络音频 API 只能使用其 decodeAudioData()
方法解码完整文件。
正如上面评论中所述,WebRTC 是专门为此用例制作的 API。如果你想有单独的房间,你可以确保你的信号处理只连接属于同一个房间的客户端。
如果您想避免使用 WebRTC,您可以尝试我编写的一个库,它为 MediaRecorder
添加了 WAVE 支持。该库名为 extendable-media-recorder。当被要求发出块时,这些块本身也不是有效的 WAVE 文件,但手动解码部分 WAVE 文件比解码压缩文件容易得多。尽管包含 header 的前 44 个字节只是原始 PCM 数据。
您也可以反其道而行之,保留原生 MediaRecorder
并将其与接收端的自定义解码器结合使用。如果您配置 MediaRecorder
来编码 Opus 文件 opus-stream-decoder 应该能够解码块。
使用流,您可以使用 navigator.mediaDevices.getUserMedia(constraints)
MDN Reference 获取流
并将 socket.io-streams 添加到 socket.io
并使用音频元素或视频元素播放它们