refactor: Clean lint errors and organise files
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable complexity */
|
||||
import {
|
||||
SIGNALING_TYPE_ANSWER,
|
||||
SIGNALING_TYPE_OFFER,
|
||||
@@ -11,6 +10,7 @@ import {
|
||||
PeerConnectionManagerContext,
|
||||
PeerConnectionManagerState
|
||||
} from '../shared';
|
||||
import type { PeerData } from '../../realtime.types';
|
||||
|
||||
/**
|
||||
* Queue a negotiation task so SDP operations for a single peer never overlap.
|
||||
@@ -95,6 +95,97 @@ function replaceUnusablePeer(
|
||||
state.peerNegotiationQueue.delete(peerId);
|
||||
}
|
||||
|
||||
function getOrCreatePeerForOffer(
|
||||
state: PeerConnectionManagerState,
|
||||
fromUserId: string,
|
||||
handlers: NegotiationHandlers
|
||||
): PeerData {
|
||||
return state.activePeerConnections.get(fromUserId) ?? handlers.createPeerConnection(fromUserId, false);
|
||||
}
|
||||
|
||||
async function resolveOfferCollision(
|
||||
peerData: PeerData,
|
||||
callbacks: PeerConnectionManagerContext['callbacks'],
|
||||
logger: PeerConnectionManagerContext['logger'],
|
||||
fromUserId: string
|
||||
): Promise<boolean> {
|
||||
const signalingState = peerData.connection.signalingState;
|
||||
const hasCollision = signalingState === 'have-local-offer' || signalingState === 'have-local-pranswer';
|
||||
|
||||
if (!hasCollision)
|
||||
return true;
|
||||
|
||||
const localOderId = callbacks.getIdentifyCredentials()?.oderId ?? null;
|
||||
const isPolite = !localOderId || localOderId > fromUserId;
|
||||
|
||||
if (!isPolite) {
|
||||
logger.info('Ignoring colliding offer (impolite side)', { fromUserId, localOderId });
|
||||
return false;
|
||||
}
|
||||
|
||||
logger.info('Rolling back local offer (polite side)', { fromUserId, localOderId });
|
||||
|
||||
await peerData.connection.setLocalDescription({
|
||||
type: 'rollback'
|
||||
} as RTCSessionDescriptionInit);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function syncPeerSendersFromTransceivers(peerData: PeerData): void {
|
||||
const transceivers = peerData.connection.getTransceivers();
|
||||
|
||||
for (const transceiver of transceivers) {
|
||||
const receiverKind = transceiver.receiver.track?.kind;
|
||||
|
||||
if (receiverKind === TRACK_KIND_AUDIO) {
|
||||
if (!peerData.audioSender) {
|
||||
peerData.audioSender = transceiver.sender;
|
||||
}
|
||||
|
||||
transceiver.direction = TRANSCEIVER_SEND_RECV;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (receiverKind === TRACK_KIND_VIDEO && !peerData.videoSender) {
|
||||
peerData.videoSender = transceiver.sender;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function attachAnswererLocalTracks(
|
||||
peerData: PeerData,
|
||||
localStream: MediaStream | null,
|
||||
logger: PeerConnectionManagerContext['logger'],
|
||||
fromUserId: string
|
||||
): Promise<void> {
|
||||
if (!localStream)
|
||||
return;
|
||||
|
||||
logger.logStream(`localStream->${fromUserId} (answerer)`, localStream);
|
||||
|
||||
for (const track of localStream.getTracks()) {
|
||||
if (track.kind === TRACK_KIND_AUDIO && peerData.audioSender) {
|
||||
await peerData.audioSender.replaceTrack(track);
|
||||
logger.info('audio replaceTrack (answerer) ok', { fromUserId });
|
||||
continue;
|
||||
}
|
||||
|
||||
if (track.kind === TRACK_KIND_VIDEO && peerData.videoSender) {
|
||||
await peerData.videoSender.replaceTrack(track);
|
||||
logger.info('video replaceTrack (answerer) ok', { fromUserId });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function applyPendingIceCandidates(peerData: PeerData): Promise<void> {
|
||||
for (const candidate of peerData.pendingIceCandidates) {
|
||||
await peerData.connection.addIceCandidate(new RTCIceCandidate(candidate));
|
||||
}
|
||||
|
||||
peerData.pendingIceCandidates = [];
|
||||
}
|
||||
|
||||
export async function doHandleOffer(
|
||||
context: PeerConnectionManagerContext,
|
||||
fromUserId: string,
|
||||
@@ -107,72 +198,18 @@ export async function doHandleOffer(
|
||||
|
||||
replaceUnusablePeer(context, fromUserId, 'incoming offer');
|
||||
|
||||
let peerData = state.activePeerConnections.get(fromUserId);
|
||||
|
||||
if (!peerData) {
|
||||
peerData = handlers.createPeerConnection(fromUserId, false);
|
||||
}
|
||||
const peerData = getOrCreatePeerForOffer(state, fromUserId, handlers);
|
||||
|
||||
try {
|
||||
const signalingState = peerData.connection.signalingState;
|
||||
const hasCollision =
|
||||
signalingState === 'have-local-offer' || signalingState === 'have-local-pranswer';
|
||||
const shouldApplyOffer = await resolveOfferCollision(peerData, callbacks, logger, fromUserId);
|
||||
|
||||
if (hasCollision) {
|
||||
const localOderId = callbacks.getIdentifyCredentials()?.oderId ?? null;
|
||||
const isPolite = !localOderId || localOderId > fromUserId;
|
||||
|
||||
if (!isPolite) {
|
||||
logger.info('Ignoring colliding offer (impolite side)', { fromUserId, localOderId });
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info('Rolling back local offer (polite side)', { fromUserId, localOderId });
|
||||
|
||||
await peerData.connection.setLocalDescription({
|
||||
type: 'rollback'
|
||||
} as RTCSessionDescriptionInit);
|
||||
}
|
||||
if (!shouldApplyOffer)
|
||||
return;
|
||||
|
||||
await peerData.connection.setRemoteDescription(new RTCSessionDescription(sdp));
|
||||
|
||||
const transceivers = peerData.connection.getTransceivers();
|
||||
|
||||
for (const transceiver of transceivers) {
|
||||
const receiverKind = transceiver.receiver.track?.kind;
|
||||
|
||||
if (receiverKind === TRACK_KIND_AUDIO) {
|
||||
if (!peerData.audioSender) {
|
||||
peerData.audioSender = transceiver.sender;
|
||||
}
|
||||
|
||||
transceiver.direction = TRANSCEIVER_SEND_RECV;
|
||||
} else if (receiverKind === TRACK_KIND_VIDEO && !peerData.videoSender) {
|
||||
peerData.videoSender = transceiver.sender;
|
||||
}
|
||||
}
|
||||
|
||||
const localStream = callbacks.getLocalMediaStream();
|
||||
|
||||
if (localStream) {
|
||||
logger.logStream(`localStream->${fromUserId} (answerer)`, localStream);
|
||||
|
||||
for (const track of localStream.getTracks()) {
|
||||
if (track.kind === TRACK_KIND_AUDIO && peerData.audioSender) {
|
||||
await peerData.audioSender.replaceTrack(track);
|
||||
logger.info('audio replaceTrack (answerer) ok', { fromUserId });
|
||||
} else if (track.kind === TRACK_KIND_VIDEO && peerData.videoSender) {
|
||||
await peerData.videoSender.replaceTrack(track);
|
||||
logger.info('video replaceTrack (answerer) ok', { fromUserId });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const candidate of peerData.pendingIceCandidates) {
|
||||
await peerData.connection.addIceCandidate(new RTCIceCandidate(candidate));
|
||||
}
|
||||
|
||||
peerData.pendingIceCandidates = [];
|
||||
syncPeerSendersFromTransceivers(peerData);
|
||||
await attachAnswererLocalTracks(peerData, callbacks.getLocalMediaStream(), logger, fromUserId);
|
||||
await applyPendingIceCandidates(peerData);
|
||||
|
||||
const answer = await peerData.connection.createAnswer();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user