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

PCTransportManager #909

Merged
merged 30 commits into from
Nov 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
2af29ff
Make peerconnection private on PCTransport
lukasIO Oct 25, 2023
ce056d5
revert enablesimulcast for this branch
lukasIO Oct 25, 2023
ea5aab2
Create moody-nails-flash.md
lukasIO Oct 25, 2023
31ded02
fix connection check
lukasIO Oct 25, 2023
011c0cf
Merge branch 'lukas/encapsulate-pctransport' of github.com:livekit/cl…
lukasIO Oct 25, 2023
2999699
initial pcmanager tap in
lukasIO Oct 26, 2023
a3a4b82
WIP managed transport connection
lukasIO Oct 26, 2023
1223ac1
merge main
lukasIO Oct 26, 2023
7af5228
shift more responsibilities to transport manager
lukasIO Oct 27, 2023
e43480c
leaky PC lifecycle
lukasIO Oct 27, 2023
77eb319
add subscriber methods to pcmanager
lukasIO Oct 27, 2023
e57148c
wip publisher methods on pcmanager
lukasIO Oct 27, 2023
a6a4855
move primary transport handling into manager
lukasIO Nov 3, 2023
59391fc
fix e2ee reconnect behaviour
lukasIO Nov 3, 2023
3e0bbf2
fix publisher reconnect scenarios
lukasIO Nov 3, 2023
7a1044a
wip transport lifecycle
lukasIO Nov 3, 2023
f47326b
merge main
lukasIO Nov 6, 2023
7ba3e85
negotiation in pc manager
lukasIO Nov 6, 2023
62cd815
fix peer connection leak
lukasIO Nov 6, 2023
5e455bf
cleanup
lukasIO Nov 13, 2023
757ad86
more cleanup
lukasIO Nov 13, 2023
86db583
pass state on transport callbacks
lukasIO Nov 13, 2023
a83e3d7
Log syncState failure
lukasIO Nov 13, 2023
bd3c93e
More explicit method naming
lukasIO Nov 13, 2023
543446e
make getConnectedAddress configurable
lukasIO Nov 13, 2023
17fa81b
Rename onLocalOffer to onPublisherOffer
lukasIO Nov 13, 2023
06edd10
Send sync state also for publisher only
lukasIO Nov 13, 2023
e81f3a0
Merge branch 'main' into lukas/pc-manager
lukasIO Nov 13, 2023
f57d7c1
move syncState into engine
lukasIO Nov 20, 2023
d857e98
Create fuzzy-waves-knock.md
lukasIO Nov 21, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fuzzy-waves-knock.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"livekit-client": patch
---

Move PeerConnection logic into PCTransportManager
4 changes: 2 additions & 2 deletions src/connectionHelper/checks/webrtc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ export class WebRTCCheck extends Checker {
}
};

if (this.room.engine.subscriber) {
this.room.engine.subscriber.onIceCandidateError = (ev) => {
if (this.room.engine.pcManager) {
this.room.engine.pcManager.subscriber.onIceCandidateError = (ev) => {
if (ev instanceof RTCPeerConnectionIceErrorEvent) {
this.appendWarning(
`error with ICE candidate: ${ev.errorCode} ${ev.errorText} ${ev.url}`,
Expand Down
91 changes: 62 additions & 29 deletions src/room/PCTransport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,16 @@ export default class PCTransport extends EventEmitter {
private _pc: RTCPeerConnection | null;

private get pc() {
if (this._pc) return this._pc;
throw new UnexpectedConnectionState('Expected peer connection to be available');
if (!this._pc) {
this._pc = this.createPC();
}
return this._pc;
}

private config?: RTCConfiguration;

private mediaConstraints: Record<string, unknown>;
lukasIO marked this conversation as resolved.
Show resolved Hide resolved

pendingCandidates: RTCIceCandidateInit[] = [];

restartingIce: boolean = false;
Expand All @@ -57,32 +63,53 @@ export default class PCTransport extends EventEmitter {

onConnectionStateChange?: (state: RTCPeerConnectionState) => void;

onIceConnectionStateChange?: (state: RTCIceConnectionState) => void;

onSignalingStatechange?: (state: RTCSignalingState) => void;

onDataChannel?: (ev: RTCDataChannelEvent) => void;

onTrack?: (ev: RTCTrackEvent) => void;

constructor(config?: RTCConfiguration, mediaConstraints: Record<string, unknown> = {}) {
super();
this._pc = isChromiumBased()
this.config = config;
this.mediaConstraints = mediaConstraints;
this._pc = this.createPC();
}

private createPC() {
const pc = isChromiumBased()
? // @ts-expect-error chrome allows additional media constraints to be passed into the RTCPeerConnection constructor
new RTCPeerConnection(config, mediaConstraints)
: new RTCPeerConnection(config);
this._pc.onicecandidate = (ev) => {
new RTCPeerConnection(this.config, this.mediaConstraints)
: new RTCPeerConnection(this.config);

pc.onicecandidate = (ev) => {
if (!ev.candidate) return;
this.onIceCandidate?.(ev.candidate);
};
this._pc.onicecandidateerror = (ev) => {
pc.onicecandidateerror = (ev) => {
this.onIceCandidateError?.(ev);
};
this._pc.onconnectionstatechange = () => {
this.onConnectionStateChange?.(this._pc?.connectionState ?? 'closed');

pc.oniceconnectionstatechange = () => {
lukasIO marked this conversation as resolved.
Show resolved Hide resolved
this.onIceConnectionStateChange?.(pc.iceConnectionState);
};
this._pc.ondatachannel = (ev) => {

pc.onsignalingstatechange = () => {
lukasIO marked this conversation as resolved.
Show resolved Hide resolved
this.onSignalingStatechange?.(pc.signalingState);
};

pc.onconnectionstatechange = () => {
this.onConnectionStateChange?.(pc.connectionState);
};
pc.ondatachannel = (ev) => {
this.onDataChannel?.(ev);
};
this._pc.ontrack = (ev) => {
pc.ontrack = (ev) => {
this.onTrack?.(ev);
};
return pc;
}

get isICEConnected(): boolean {
Expand Down Expand Up @@ -168,7 +195,7 @@ export default class PCTransport extends EventEmitter {

if (this.renegotiate) {
this.renegotiate = false;
this.createAndSendOffer();
await this.createAndSendOffer();
} else if (sd.type === 'answer') {
this.emit(PCEvents.NegotiationComplete);
if (sd.sdp) {
Expand All @@ -183,10 +210,10 @@ export default class PCTransport extends EventEmitter {
}

// debounced negotiate interface
negotiate = debounce((onError?: (e: Error) => void) => {
negotiate = debounce(async (onError?: (e: Error) => void) => {
this.emit(PCEvents.NegotiationStarted);
try {
this.createAndSendOffer();
await this.createAndSendOffer();
} catch (e) {
if (onError) {
onError(e as Error);
Expand All @@ -209,11 +236,11 @@ export default class PCTransport extends EventEmitter {
if (this._pc && this._pc.signalingState === 'have-local-offer') {
// we're waiting for the peer to accept our offer, so we'll just wait
// the only exception to this is when ICE restart is needed
const currentSD = this.pc.remoteDescription;
const currentSD = this._pc.remoteDescription;
if (options?.iceRestart && currentSD) {
// TODO: handle when ICE restart is needed but we don't have a remote description
// the best thing to do is to recreate the peerconnection
await this.pc.setRemoteDescription(currentSD);
await this._pc.setRemoteDescription(currentSD);
} else {
this.renegotiate = true;
return;
Expand Down Expand Up @@ -307,51 +334,57 @@ export default class PCTransport extends EventEmitter {
}

addTrack(track: MediaStreamTrack) {
return this.pc.addTrack(track);
if (!this._pc) {
throw new UnexpectedConnectionState('PC closed, cannot add track');
}
return this._pc.addTrack(track);
}

setTrackCodecBitrate(info: TrackBitrateInfo) {
this.trackBitrates.push(info);
}

setConfiguration(rtcConfig: RTCConfiguration) {
return this.pc.setConfiguration(rtcConfig);
if (!this._pc) {
throw new UnexpectedConnectionState('PC closed, cannot configure');
}
return this._pc?.setConfiguration(rtcConfig);
}

canRemoveTrack(): boolean {
return !!this.pc.removeTrack;
return !!this._pc?.removeTrack;
}

removeTrack(sender: RTCRtpSender) {
return this.pc.removeTrack(sender);
return this._pc?.removeTrack(sender);
}

getConnectionState() {
return this.pc.connectionState;
return this._pc?.connectionState ?? 'closed';
}

getICEConnectionState() {
return this.pc.iceConnectionState;
return this._pc?.iceConnectionState ?? 'closed';
}

getSignallingState() {
return this.pc.signalingState;
return this._pc?.signalingState ?? 'closed';
}

getTransceivers() {
return this.pc.getTransceivers();
return this._pc?.getTransceivers() ?? [];
}

getSenders() {
return this.pc.getSenders();
return this._pc?.getSenders() ?? [];
}

getLocalDescription() {
return this.pc.localDescription;
return this._pc?.localDescription;
}

getRemoteDescription() {
return this.pc.remoteDescription;
return this.pc?.remoteDescription;
}

getStats() {
Expand Down Expand Up @@ -395,7 +428,7 @@ export default class PCTransport extends EventEmitter {
return candidates.get(selectedID);
}

close() {
close = () => {
if (!this._pc) {
return;
}
Expand All @@ -412,7 +445,7 @@ export default class PCTransport extends EventEmitter {
this._pc.onconnectionstatechange = null;
this._pc.oniceconnectionstatechange = null;
this._pc = null;
}
};

private async setMungedSDP(sd: RTCSessionDescriptionInit, munged?: string, remote?: boolean) {
if (munged) {
Expand Down
Loading
Loading