固定发送端场景下,WebRTC接收端(用户B)连接音频流步骤问询
固定发送端的WebRTC音频单向收发实现步骤
一、核心简化思路
因为发送端(用户A)固定,不需要复杂的呼叫协商逻辑,只需把发送端的SDP Offer和ICE候选持久化,接收端(用户B)直接拉取并应答即可。以下是分角色的具体实现步骤:
二、发送端(用户A)操作流程
获取麦克风媒体流
请求用户授权麦克风权限,获取音频流:const localStream = await navigator.mediaDevices.getUserMedia({ audio: true });初始化RTCPeerConnection
配置STUN服务器(用于NAT穿透),创建连接实例:const pcConfig = { iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] }; const senderPc = new RTCPeerConnection(pcConfig);添加音频轨道到连接
将麦克风流的音频轨道加入PeerConnection:localStream.getAudioTracks().forEach(track => { senderPc.addTrack(track, localStream); });生成并存储Offer SDP
创建Offer并设置本地描述,同时收集ICE候选:let storedOffer = null; let iceCandidates = []; // 收集ICE候选 senderPc.onicecandidate = (e) => { if (e.candidate) { iceCandidates.push(e.candidate); } else { // 候选收集完成,将Offer和候选存入信令存储(比如后端接口) fetch('/api/save-sender-data', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ offer: storedOffer, iceCandidates }) }); } }; // 创建Offer const offer = await senderPc.createOffer(); await senderPc.setLocalDescription(offer); storedOffer = offer;监听接收端的Answer
轮询或通过WebSocket接收接收端的Answer SDP,设置为远程描述:setInterval(async () => { const res = await fetch('/api/get-receiver-answer'); const answer = await res.json(); if (answer && !senderPc.currentRemoteDescription) { await senderPc.setRemoteDescription(answer); } }, 1000);
三、接收端(用户B)操作流程
初始化RTCPeerConnection
同样配置STUN服务器,创建连接实例:const pcConfig = { iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] }; const receiverPc = new RTCPeerConnection(pcConfig);监听音频流并播放
收到发送端的音频轨道时,创建Audio元素播放:receiverPc.ontrack = (e) => { const audioEl = document.createElement('audio'); audioEl.srcObject = e.streams[0]; audioEl.play(); document.body.appendChild(audioEl); };拉取发送端的Offer和ICE候选
从信令存储获取发送端的预存数据:const res = await fetch('/api/get-sender-data'); const { offer, iceCandidates } = await res.json();生成并发送Answer SDP
设置远程描述为发送端的Offer,创建Answer并发送到信令存储:await receiverPc.setRemoteDescription(offer); const answer = await receiverPc.createAnswer(); await receiverPc.setLocalDescription(answer); // 发送Answer到后端 fetch('/api/save-receiver-answer', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(answer) });添加发送端的ICE候选
将收集到的ICE候选逐个添加到本地连接:for (const candidate of iceCandidates) { await receiverPc.addIceCandidate(candidate); }
四、轻量信令后端(Node.js + Express示例)
用简单接口实现数据存储,替代复杂的Firestore逻辑:
const express = require('express'); const app = express(); app.use(express.json()); // 存储发送端数据 let senderData = null; // 存储接收端Answer let receiverAnswer = null; app.post('/api/save-sender-data', (req, res) => { senderData = req.body; res.sendStatus(200); }); app.get('/api/get-sender-data', (req, res) => { res.json(senderData); }); app.post('/api/save-receiver-answer', (req, res) => { receiverAnswer = req.body; res.sendStatus(200); }); app.get('/api/get-receiver-answer', (req, res) => { res.json(receiverAnswer); }); app.listen(3000, () => console.log('信令服务器运行在3000端口'));
五、关键注意事项
- HTTPS环境:
getUserMedia仅在HTTPS或localhost环境下生效,部署时需配置SSL。 - ICE候选完整性:必须等发送端的ICE候选全部收集完成后,再让接收端拉取,否则可能导致连接失败。
- 浏览器兼容性:主流浏览器(Chrome、Firefox、Edge)均支持标准WebRTC API,无需前缀。
- 测试顺序:先启动发送端并完成授权、Offer存储,再启动接收端拉取数据。
内容的提问来源于stack exchange,提问作者DDave
相关产品推荐
相关产品推荐

