add very early datachannel support

This commit is contained in:
Bruno Windels 2022-04-11 15:53:34 +02:00
parent c02e1de001
commit b84c90891c
5 changed files with 32 additions and 15 deletions

View file

@ -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[] {

View file

@ -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();

View file

@ -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", () => {

View file

@ -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<boolean>;
createDataChannel(): DataChannel;
createDataChannel(options: RTCDataChannelInit): any;
dispose(): void;
close(): void;
}

View file

@ -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;
}
}