From b84c90891ca90038c05e0e65186cb2bd4369ed78 Mon Sep 17 00:00:00 2001 From: Bruno Windels <274386+bwindels@users.noreply.github.com> Date: Mon, 11 Apr 2022 15:53:34 +0200 Subject: [PATCH] add very early datachannel support --- src/matrix/calls/LocalMedia.ts | 9 +++++++-- src/matrix/calls/PeerCall.ts | 18 ++++++++++++++++-- src/matrix/calls/group/Member.ts | 4 ++++ src/platform/types/WebRTC.ts | 10 ++-------- src/platform/web/dom/WebRTC.ts | 6 +++--- 5 files changed, 32 insertions(+), 15 deletions(-) diff --git a/src/matrix/calls/LocalMedia.ts b/src/matrix/calls/LocalMedia.ts index b64bdee5..fd91c61c 100644 --- a/src/matrix/calls/LocalMedia.ts +++ b/src/matrix/calls/LocalMedia.ts @@ -22,7 +22,8 @@ export class LocalMedia { constructor( public readonly cameraTrack?: Track, public readonly screenShareTrack?: Track, - public readonly microphoneTrack?: AudioTrack + public readonly microphoneTrack?: AudioTrack, + public readonly dataChannelOptions?: RTCDataChannelInit, ) {} withTracks(tracks: Track[]) { @@ -32,7 +33,11 @@ export class LocalMedia { if (cameraTrack && microphoneTrack && cameraTrack.streamId !== microphoneTrack.streamId) { throw new Error("The camera and audio track should have the same stream id"); } - return new LocalMedia(cameraTrack, screenShareTrack, microphoneTrack as AudioTrack); + return new LocalMedia(cameraTrack, screenShareTrack, microphoneTrack as AudioTrack, this.dataChannelOptions); + } + + withDataChannel(options: RTCDataChannelInit): LocalMedia { + return new LocalMedia(this.cameraTrack, this.screenShareTrack, this.microphoneTrack as AudioTrack, options); } get tracks(): Track[] { diff --git a/src/matrix/calls/PeerCall.ts b/src/matrix/calls/PeerCall.ts index ed10eebb..7e4dbb4a 100644 --- a/src/matrix/calls/PeerCall.ts +++ b/src/matrix/calls/PeerCall.ts @@ -85,6 +85,8 @@ export class PeerCall implements IDisposable { private sentEndOfCandidates: boolean = false; private iceDisconnectedTimeout?: Timeout; + private _dataChannel?: any; + constructor( private callId: string, private readonly options: Options, @@ -112,7 +114,12 @@ export class PeerCall implements IDisposable { outer.options.emitUpdate(outer, undefined); }); }, - onDataChannelChanged(dataChannel: DataChannel | undefined) {}, + onRemoteDataChannel(dataChannel: any | undefined) { + outer.logItem.wrap("onRemoteDataChannel", log => { + outer._dataChannel = dataChannel; + outer.options.emitUpdate(outer, undefined); + }); + }, onNegotiationNeeded() { const promiseCreator = () => { return outer.logItem.wrap("onNegotiationNeeded", log => { @@ -127,6 +134,8 @@ export class PeerCall implements IDisposable { }); } + get dataChannel(): any | undefined { return this._dataChannel; } + get state(): CallState { return this._state; } get remoteTracks(): Track[] { @@ -144,6 +153,9 @@ export class PeerCall implements IDisposable { for (const t of this.localMedia.tracks) { this.peerConnection.addTrack(t); } + if (this.localMedia.dataChannelOptions) { + this._dataChannel = this.peerConnection.createDataChannel(this.localMedia.dataChannelOptions); + } // after adding the local tracks, and wait for handleNegotiation to be called, // or invite glare where we give up our invite and answer instead await this.waitForState([CallState.InviteSent, CallState.CreateAnswer]); @@ -160,7 +172,9 @@ export class PeerCall implements IDisposable { for (const t of this.localMedia.tracks) { this.peerConnection.addTrack(t); } - + if (this.localMedia.dataChannelOptions) { + this._dataChannel = this.peerConnection.createDataChannel(this.localMedia.dataChannelOptions); + } let myAnswer: RTCSessionDescriptionInit; try { myAnswer = await this.peerConnection.createAnswer(); diff --git a/src/matrix/calls/group/Member.ts b/src/matrix/calls/group/Member.ts index 05d64b92..7d849929 100644 --- a/src/matrix/calls/group/Member.ts +++ b/src/matrix/calls/group/Member.ts @@ -66,6 +66,10 @@ export class Member { return this.callDeviceMembership.device_id; } + get dataChannel(): any | undefined { + return this.peerCall?.dataChannel; + } + /** @internal */ connect(localMedia: LocalMedia) { this.logItem.wrap("connect", () => { diff --git a/src/platform/types/WebRTC.ts b/src/platform/types/WebRTC.ts index df8133ee..130cb7cb 100644 --- a/src/platform/types/WebRTC.ts +++ b/src/platform/types/WebRTC.ts @@ -26,21 +26,15 @@ export interface PeerConnectionHandler { onLocalIceCandidate(candidate: RTCIceCandidate); onIceGatheringStateChange(state: RTCIceGatheringState); onRemoteTracksChanged(tracks: Track[]); - onDataChannelChanged(dataChannel: DataChannel | undefined); + onRemoteDataChannel(dataChannel: any | undefined); onNegotiationNeeded(); // request the type of incoming stream getPurposeForStreamId(streamId: string): SDPStreamMetadataPurpose; } -// does it make sense to wrap this? -export interface DataChannel { - close(); - send(); -} export interface PeerConnection { notifyStreamPurposeChanged(): void; get remoteTracks(): Track[]; - get dataChannel(): DataChannel | undefined; get iceGatheringState(): RTCIceGatheringState; get signalingState(): RTCSignalingState; get localDescription(): RTCSessionDescription | undefined; @@ -52,7 +46,7 @@ export interface PeerConnection { addTrack(track: Track): void; removeTrack(track: Track): boolean; replaceTrack(oldTrack: Track, newTrack: Track): Promise; - createDataChannel(): DataChannel; + createDataChannel(options: RTCDataChannelInit): any; dispose(): void; close(): void; } diff --git a/src/platform/web/dom/WebRTC.ts b/src/platform/web/dom/WebRTC.ts index 8c840ddc..78977eb8 100644 --- a/src/platform/web/dom/WebRTC.ts +++ b/src/platform/web/dom/WebRTC.ts @@ -46,7 +46,6 @@ class DOMPeerConnection implements PeerConnection { } get remoteTracks(): Track[] { return this._remoteTracks; } - get dataChannel(): DataChannel | undefined { return undefined; } get iceGatheringState(): RTCIceGatheringState { return this.peerConnection.iceGatheringState; } get localDescription(): RTCSessionDescription | undefined { return this.peerConnection.localDescription ?? undefined; } get signalingState(): RTCSignalingState { return this.peerConnection.signalingState; } @@ -119,8 +118,8 @@ class DOMPeerConnection implements PeerConnection { } } - createDataChannel(): DataChannel { - return undefined as any;// new DataChannel(this.peerConnection.createDataChannel()); + createDataChannel(options: RTCDataChannelInit): any { + return this.peerConnection.createDataChannel("channel", options); } private registerHandler() { @@ -164,6 +163,7 @@ class DOMPeerConnection implements PeerConnection { this.handler.onNegotiationNeeded(); break; case "datachannel": + this.handler.onRemoteDataChannel((evt as RTCDataChannelEvent).channel); break; } }