fix: WebRTC audio — glare handling (Perfect Negotiation) + autoplay + signalingState guard
This commit is contained in:
@@ -193,6 +193,18 @@ class ClassroomRTC {
|
||||
|
||||
async _handleOffer(fromUid, payload) {
|
||||
const peer = this._getOrCreate(fromUid);
|
||||
|
||||
// Perfect Negotiation: resolve offer collision (both peers sending offers simultaneously).
|
||||
// Polite peer (higher uid) yields — rolls back its own offer and handles the incoming one.
|
||||
// Impolite peer (lower uid) ignores the collision offer and waits for the answer to its own.
|
||||
const polite = this._uid > fromUid;
|
||||
const collision = peer.pc.signalingState !== 'stable' || peer.negotiating;
|
||||
if (collision) {
|
||||
if (!polite) return;
|
||||
// Polite: explicit rollback for older browsers; modern Chrome handles it implicitly
|
||||
await peer.pc.setLocalDescription({ type: 'rollback' }).catch(() => {});
|
||||
}
|
||||
|
||||
await peer.pc.setRemoteDescription({ type: 'offer', sdp: payload.sdp });
|
||||
peer.remoteSet = true;
|
||||
await this._flushCandidates(peer);
|
||||
@@ -277,7 +289,7 @@ class ClassroomRTC {
|
||||
|
||||
/* Renegotiation: fires when tracks are added/removed (e.g. screen share) */
|
||||
pc.onnegotiationneeded = async () => {
|
||||
if (peer.negotiating || !peer.remoteSet) return;
|
||||
if (peer.negotiating || !peer.remoteSet || pc.signalingState !== 'stable') return;
|
||||
peer.negotiating = true;
|
||||
try {
|
||||
const offer = await pc.createOffer();
|
||||
@@ -299,6 +311,7 @@ class ClassroomRTC {
|
||||
document.body.appendChild(peer.audioEl);
|
||||
}
|
||||
peer.audioEl.srcObject = stream;
|
||||
peer.audioEl.play().catch(() => {});
|
||||
this._startVAD(uid, stream);
|
||||
} else if (track.kind === 'video') {
|
||||
if (this._onScreen) this._onScreen(stream);
|
||||
|
||||
Reference in New Issue
Block a user