Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initiate connection when we receive SDP and don't connect when null #5451

Merged
merged 8 commits into from
Feb 22, 2025
98 changes: 68 additions & 30 deletions src/lang/std/engineConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,20 @@ export enum EngineConnectionEvents {
NewTrack = 'new-track', // (track: NewTrackArgs) => void
}

function toRTCSessionDescriptionInit(
desc: Models['RtcSessionDescription_type']
): RTCSessionDescriptionInit | undefined {
if (desc.type === 'unspecified') {
console.error('Invalid SDP answer: type is "unspecified".')
return undefined
}
return {
sdp: desc.sdp,
// Force the type to be one of the valid RTCSdpType values
type: desc.type as RTCSdpType,
}
}

// EngineConnection encapsulates the connection(s) to the Engine
// for the EngineCommandManager; namely, the underlying WebSocket
// and WebRTC connections.
Expand All @@ -250,7 +264,7 @@ class EngineConnection extends EventTarget {
mediaStream?: MediaStream
idleMode: boolean = false
promise?: Promise<void>
sdpAnswer?: Models['RtcSessionDescription_type']
sdpAnswer?: RTCSessionDescriptionInit
triggeredStart = false

onIceCandidate = function (
Expand Down Expand Up @@ -549,6 +563,50 @@ class EngineConnection extends EventTarget {
this.disconnectAll()
}

initiateConnectionExclusive(): boolean {
// Only run if:
// - A peer connection exists,
// - ICE gathering is complete,
// - We have an SDP answer,
// - And we haven’t already triggered this connection.
if (!this.pc || this.triggeredStart || !this.sdpAnswer) {
return false
}
this.triggeredStart = true

// Transition to the connecting state
this.state = {
type: EngineConnectionStateType.Connecting,
value: { type: ConnectingType.WebRTCConnecting },
}

// Attempt to set the remote description to initiate connection
this.pc
.setRemoteDescription(this.sdpAnswer)
.then(() => {
// Update state once the remote description has been set
this.state = {
type: EngineConnectionStateType.Connecting,
value: { type: ConnectingType.SetRemoteDescription },
}
})
.catch((error: Error) => {
console.error('Failed to set remote description:', error)
this.state = {
type: EngineConnectionStateType.Disconnecting,
value: {
type: DisconnectingType.Error,
value: {
error: ConnectionError.LocalDescriptionInvalid,
context: error,
},
},
}
this.disconnectAll()
})
return true
}

/**
* Attempts to connect to the Engine over a WebSocket, and
* establish the WebRTC connections.
Expand Down Expand Up @@ -588,38 +646,13 @@ class EngineConnection extends EventTarget {
},
}

const initiateConnectingExclusive = () => {
if (that.triggeredStart) return
that.triggeredStart = true

// Start connecting.
that.state = {
type: EngineConnectionStateType.Connecting,
value: {
type: ConnectingType.WebRTCConnecting,
},
}

// As soon as this is set, RTCPeerConnection tries to
// establish a connection.
// @ts-expect-error: Have to ignore because dom.ts doesn't have the right type
void that.pc?.setRemoteDescription(that.sdpAnswer)

that.state = {
type: EngineConnectionStateType.Connecting,
value: {
type: ConnectingType.SetRemoteDescription,
},
}
}

this.onIceCandidate = (event: RTCPeerConnectionIceEvent) => {
console.log('icecandidate', event.candidate)

// This is null when the ICE gathering state is done.
// Windows ONLY uses this to signal it's done!
if (event.candidate === null) {
initiateConnectingExclusive()
that.initiateConnectionExclusive()
return
}

Expand All @@ -643,7 +676,9 @@ class EngineConnection extends EventTarget {
// Sometimes the remote end doesn't report the end of candidates.
// They have 3 seconds to.
setTimeout(() => {
initiateConnectingExclusive()
if (that.initiateConnectionExclusive()) {
console.warn('connected after 3 second delay')
}
}, 3000)
}
this.pc?.addEventListener?.('icecandidate', this.onIceCandidate)
Expand All @@ -653,7 +688,7 @@ class EngineConnection extends EventTarget {
console.log('icegatheringstatechange', this.iceGatheringState)

if (this.iceGatheringState !== 'complete') return
initiateConnectingExclusive()
that.initiateConnectionExclusive()
}
)

Expand Down Expand Up @@ -1192,8 +1227,11 @@ class EngineConnection extends EventTarget {
},
}

this.sdpAnswer = answer
this.sdpAnswer = toRTCSessionDescriptionInit(answer)

// We might have received this after ice candidates finish
// Make sure we attempt to connect when we do.
this.initiateConnectionExclusive()
break

case 'trickle_ice':
Expand Down
Loading