add very early datachannel support
This commit is contained in:
parent
c02e1de001
commit
b84c90891c
5 changed files with 32 additions and 15 deletions
|
@ -22,7 +22,8 @@ export class LocalMedia {
|
||||||
constructor(
|
constructor(
|
||||||
public readonly cameraTrack?: Track,
|
public readonly cameraTrack?: Track,
|
||||||
public readonly screenShareTrack?: Track,
|
public readonly screenShareTrack?: Track,
|
||||||
public readonly microphoneTrack?: AudioTrack
|
public readonly microphoneTrack?: AudioTrack,
|
||||||
|
public readonly dataChannelOptions?: RTCDataChannelInit,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
withTracks(tracks: Track[]) {
|
withTracks(tracks: Track[]) {
|
||||||
|
@ -32,7 +33,11 @@ export class LocalMedia {
|
||||||
if (cameraTrack && microphoneTrack && cameraTrack.streamId !== microphoneTrack.streamId) {
|
if (cameraTrack && microphoneTrack && cameraTrack.streamId !== microphoneTrack.streamId) {
|
||||||
throw new Error("The camera and audio track should have the same stream id");
|
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[] {
|
get tracks(): Track[] {
|
||||||
|
|
|
@ -85,6 +85,8 @@ export class PeerCall implements IDisposable {
|
||||||
private sentEndOfCandidates: boolean = false;
|
private sentEndOfCandidates: boolean = false;
|
||||||
private iceDisconnectedTimeout?: Timeout;
|
private iceDisconnectedTimeout?: Timeout;
|
||||||
|
|
||||||
|
private _dataChannel?: any;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private callId: string,
|
private callId: string,
|
||||||
private readonly options: Options,
|
private readonly options: Options,
|
||||||
|
@ -112,7 +114,12 @@ export class PeerCall implements IDisposable {
|
||||||
outer.options.emitUpdate(outer, undefined);
|
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() {
|
onNegotiationNeeded() {
|
||||||
const promiseCreator = () => {
|
const promiseCreator = () => {
|
||||||
return outer.logItem.wrap("onNegotiationNeeded", log => {
|
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 state(): CallState { return this._state; }
|
||||||
|
|
||||||
get remoteTracks(): Track[] {
|
get remoteTracks(): Track[] {
|
||||||
|
@ -144,6 +153,9 @@ export class PeerCall implements IDisposable {
|
||||||
for (const t of this.localMedia.tracks) {
|
for (const t of this.localMedia.tracks) {
|
||||||
this.peerConnection.addTrack(t);
|
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,
|
// after adding the local tracks, and wait for handleNegotiation to be called,
|
||||||
// or invite glare where we give up our invite and answer instead
|
// or invite glare where we give up our invite and answer instead
|
||||||
await this.waitForState([CallState.InviteSent, CallState.CreateAnswer]);
|
await this.waitForState([CallState.InviteSent, CallState.CreateAnswer]);
|
||||||
|
@ -160,7 +172,9 @@ export class PeerCall implements IDisposable {
|
||||||
for (const t of this.localMedia.tracks) {
|
for (const t of this.localMedia.tracks) {
|
||||||
this.peerConnection.addTrack(t);
|
this.peerConnection.addTrack(t);
|
||||||
}
|
}
|
||||||
|
if (this.localMedia.dataChannelOptions) {
|
||||||
|
this._dataChannel = this.peerConnection.createDataChannel(this.localMedia.dataChannelOptions);
|
||||||
|
}
|
||||||
let myAnswer: RTCSessionDescriptionInit;
|
let myAnswer: RTCSessionDescriptionInit;
|
||||||
try {
|
try {
|
||||||
myAnswer = await this.peerConnection.createAnswer();
|
myAnswer = await this.peerConnection.createAnswer();
|
||||||
|
|
|
@ -66,6 +66,10 @@ export class Member {
|
||||||
return this.callDeviceMembership.device_id;
|
return this.callDeviceMembership.device_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get dataChannel(): any | undefined {
|
||||||
|
return this.peerCall?.dataChannel;
|
||||||
|
}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
connect(localMedia: LocalMedia) {
|
connect(localMedia: LocalMedia) {
|
||||||
this.logItem.wrap("connect", () => {
|
this.logItem.wrap("connect", () => {
|
||||||
|
|
|
@ -26,21 +26,15 @@ export interface PeerConnectionHandler {
|
||||||
onLocalIceCandidate(candidate: RTCIceCandidate);
|
onLocalIceCandidate(candidate: RTCIceCandidate);
|
||||||
onIceGatheringStateChange(state: RTCIceGatheringState);
|
onIceGatheringStateChange(state: RTCIceGatheringState);
|
||||||
onRemoteTracksChanged(tracks: Track[]);
|
onRemoteTracksChanged(tracks: Track[]);
|
||||||
onDataChannelChanged(dataChannel: DataChannel | undefined);
|
onRemoteDataChannel(dataChannel: any | undefined);
|
||||||
onNegotiationNeeded();
|
onNegotiationNeeded();
|
||||||
// request the type of incoming stream
|
// request the type of incoming stream
|
||||||
getPurposeForStreamId(streamId: string): SDPStreamMetadataPurpose;
|
getPurposeForStreamId(streamId: string): SDPStreamMetadataPurpose;
|
||||||
}
|
}
|
||||||
// does it make sense to wrap this?
|
|
||||||
export interface DataChannel {
|
|
||||||
close();
|
|
||||||
send();
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface PeerConnection {
|
export interface PeerConnection {
|
||||||
notifyStreamPurposeChanged(): void;
|
notifyStreamPurposeChanged(): void;
|
||||||
get remoteTracks(): Track[];
|
get remoteTracks(): Track[];
|
||||||
get dataChannel(): DataChannel | undefined;
|
|
||||||
get iceGatheringState(): RTCIceGatheringState;
|
get iceGatheringState(): RTCIceGatheringState;
|
||||||
get signalingState(): RTCSignalingState;
|
get signalingState(): RTCSignalingState;
|
||||||
get localDescription(): RTCSessionDescription | undefined;
|
get localDescription(): RTCSessionDescription | undefined;
|
||||||
|
@ -52,7 +46,7 @@ export interface PeerConnection {
|
||||||
addTrack(track: Track): void;
|
addTrack(track: Track): void;
|
||||||
removeTrack(track: Track): boolean;
|
removeTrack(track: Track): boolean;
|
||||||
replaceTrack(oldTrack: Track, newTrack: Track): Promise<boolean>;
|
replaceTrack(oldTrack: Track, newTrack: Track): Promise<boolean>;
|
||||||
createDataChannel(): DataChannel;
|
createDataChannel(options: RTCDataChannelInit): any;
|
||||||
dispose(): void;
|
dispose(): void;
|
||||||
close(): void;
|
close(): void;
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,6 @@ class DOMPeerConnection implements PeerConnection {
|
||||||
}
|
}
|
||||||
|
|
||||||
get remoteTracks(): Track[] { return this._remoteTracks; }
|
get remoteTracks(): Track[] { return this._remoteTracks; }
|
||||||
get dataChannel(): DataChannel | undefined { return undefined; }
|
|
||||||
get iceGatheringState(): RTCIceGatheringState { return this.peerConnection.iceGatheringState; }
|
get iceGatheringState(): RTCIceGatheringState { return this.peerConnection.iceGatheringState; }
|
||||||
get localDescription(): RTCSessionDescription | undefined { return this.peerConnection.localDescription ?? undefined; }
|
get localDescription(): RTCSessionDescription | undefined { return this.peerConnection.localDescription ?? undefined; }
|
||||||
get signalingState(): RTCSignalingState { return this.peerConnection.signalingState; }
|
get signalingState(): RTCSignalingState { return this.peerConnection.signalingState; }
|
||||||
|
@ -119,8 +118,8 @@ class DOMPeerConnection implements PeerConnection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
createDataChannel(): DataChannel {
|
createDataChannel(options: RTCDataChannelInit): any {
|
||||||
return undefined as any;// new DataChannel(this.peerConnection.createDataChannel());
|
return this.peerConnection.createDataChannel("channel", options);
|
||||||
}
|
}
|
||||||
|
|
||||||
private registerHandler() {
|
private registerHandler() {
|
||||||
|
@ -164,6 +163,7 @@ class DOMPeerConnection implements PeerConnection {
|
||||||
this.handler.onNegotiationNeeded();
|
this.handler.onNegotiationNeeded();
|
||||||
break;
|
break;
|
||||||
case "datachannel":
|
case "datachannel":
|
||||||
|
this.handler.onRemoteDataChannel((evt as RTCDataChannelEvent).channel);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue