fix typescript errors

This commit is contained in:
Bruno Windels 2022-03-16 19:10:51 +01:00
parent f674492685
commit 1bccbbfa08
2 changed files with 92 additions and 15 deletions

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {StreamPurpose} from "../../platform/types/WebRTC"; import {SDPStreamMetadataPurpose} from "./callEventTypes";
import {Track, AudioTrack, TrackType} from "../../platform/types/MediaDevices"; import {Track, AudioTrack, TrackType} from "../../platform/types/MediaDevices";
import {SDPStreamMetadata} from "./callEventTypes"; import {SDPStreamMetadata} from "./callEventTypes";
@ -42,14 +42,14 @@ export class LocalMedia {
const userMediaTrack = this.microphoneTrack ?? this.cameraTrack; const userMediaTrack = this.microphoneTrack ?? this.cameraTrack;
if (userMediaTrack) { if (userMediaTrack) {
metadata[userMediaTrack.streamId] = { metadata[userMediaTrack.streamId] = {
purpose: StreamPurpose.UserMedia, purpose: SDPStreamMetadataPurpose.Usermedia,
audio_muted: this.microphoneTrack?.muted ?? false, audio_muted: this.microphoneTrack?.muted ?? false,
video_muted: this.cameraTrack?.muted ?? false, video_muted: this.cameraTrack?.muted ?? false,
}; };
} }
if (this.screenShareTrack) { if (this.screenShareTrack) {
metadata[this.screenShareTrack.streamId] = { metadata[this.screenShareTrack.streamId] = {
purpose: StreamPurpose.ScreenShare purpose: SDPStreamMetadataPurpose.Screenshare
}; };
} }
return metadata; return metadata;

View file

@ -21,6 +21,7 @@ import {Disposables, IDisposable} from "../../utils/Disposables";
import type {Room} from "../room/Room"; import type {Room} from "../room/Room";
import type {StateEvent} from "../storage/types"; import type {StateEvent} from "../storage/types";
import type {ILogItem} from "../../logging/types"; import type {ILogItem} from "../../logging/types";
import {Instance as logger} from "../../logging/NullLogger";
import type {TimeoutCreator, Timeout} from "../../platform/types/types"; import type {TimeoutCreator, Timeout} from "../../platform/types/types";
import {WebRTC, PeerConnection, PeerConnectionHandler, DataChannel} from "../../platform/types/WebRTC"; import {WebRTC, PeerConnection, PeerConnectionHandler, DataChannel} from "../../platform/types/WebRTC";
@ -81,16 +82,28 @@ export class PeerCall implements IDisposable {
// perfect negotiation flags // perfect negotiation flags
private makingOffer: boolean = false; private makingOffer: boolean = false;
private ignoreOffer: boolean = false; private ignoreOffer: boolean = false;
private sentEndOfCandidates: boolean = false;
private iceDisconnectedTimeout?: Timeout;
constructor( constructor(
private callId: string, // generated or from invite private callId: string,
private readonly options: Options private readonly options: Options
) { ) {
const outer = this; const outer = this;
this.peerConnection = options.webRTC.createPeerConnection({ this.peerConnection = options.webRTC.createPeerConnection({
onIceConnectionStateChange(state: RTCIceConnectionState) {}, onIceConnectionStateChange(state: RTCIceConnectionState) {
onLocalIceCandidate(candidate: RTCIceCandidate) {}, outer.onIceConnectionStateChange(state);
onIceGatheringStateChange(state: RTCIceGatheringState) {}, },
onRemoteTracksChanged(tracks: Track[]) {}, onLocalIceCandidate(candidate: RTCIceCandidate) {
outer.handleLocalIceCandidate(candidate);
},
onIceGatheringStateChange(state: RTCIceGatheringState) {
outer.handleIceGatheringState(state);
},
onRemoteTracksChanged(tracks: Track[]) {
outer.options.emitUpdate(outer, undefined);
},
onDataChannelChanged(dataChannel: DataChannel | undefined) {}, onDataChannelChanged(dataChannel: DataChannel | undefined) {},
onNegotiationNeeded() { onNegotiationNeeded() {
const promiseCreator = () => outer.handleNegotiation(); const promiseCreator = () => outer.handleNegotiation();
@ -158,7 +171,7 @@ export class PeerCall implements IDisposable {
} }
// Allow a short time for initial candidates to be gathered // Allow a short time for initial candidates to be gathered
await this.delay(200); await this.delay(200);
this.sendAnswer(); await this.sendAnswer();
} }
async setMedia(localMediaPromise: Promise<LocalMedia>) { async setMedia(localMediaPromise: Promise<LocalMedia>) {
@ -187,7 +200,11 @@ export class PeerCall implements IDisposable {
} }
async hangup(errorCode: CallErrorCode) { async hangup(errorCode: CallErrorCode): Promise<void> {
if (this._state !== CallState.Ended) {
this._state = CallState.Ended;
await this.sendHangupWithCallId(this.callId, errorCode);
}
} }
async handleIncomingSignallingMessage<B extends MCallBase>(message: SignallingMessage<B>, partyId: PartyId, log: ILogItem): Promise<void> { async handleIncomingSignallingMessage<B extends MCallBase>(message: SignallingMessage<B>, partyId: PartyId, log: ILogItem): Promise<void> {
@ -222,7 +239,7 @@ export class PeerCall implements IDisposable {
return this.options.sendSignallingMessage({ return this.options.sendSignallingMessage({
type: EventType.Hangup, type: EventType.Hangup,
content content
}, undefined); }, logger.item);
} }
// calls are serialized and deduplicated by responsePromiseChain // calls are serialized and deduplicated by responsePromiseChain
@ -262,7 +279,7 @@ export class PeerCall implements IDisposable {
lifetime: CALL_TIMEOUT_MS lifetime: CALL_TIMEOUT_MS
}; };
if (this._state === CallState.CreateOffer) { if (this._state === CallState.CreateOffer) {
await this.options.sendSignallingMessage({type: EventType.Invite, content}, undefined); await this.options.sendSignallingMessage({type: EventType.Invite, content}, logger.item);
this.setState(CallState.InviteSent); this.setState(CallState.InviteSent);
} else if (this._state === CallState.Connected || this._state === CallState.Connecting) { } else if (this._state === CallState.Connected || this._state === CallState.Connecting) {
// send Negotiate message // send Negotiate message
@ -406,7 +423,42 @@ export class PeerCall implements IDisposable {
} }
} }
async handleRemoteIceCandidates(content: MCallCandidates<MCallBase>, partyId) { private handleIceGatheringState(state: RTCIceGatheringState) {
this.logger.debug(`Call ${this.callId} ice gathering state changed to ${state}`);
if (state === 'complete' && !this.sentEndOfCandidates) {
// If we didn't get an empty-string candidate to signal the end of candidates,
// create one ourselves now gathering has finished.
// We cast because the interface lists all the properties as required but we
// only want to send 'candidate'
// XXX: We probably want to send either sdpMid or sdpMLineIndex, as it's not strictly
// correct to have a candidate that lacks both of these. We'd have to figure out what
// previous candidates had been sent with and copy them.
const c = {
candidate: '',
} as RTCIceCandidate;
this.queueCandidate(c);
this.sentEndOfCandidates = true;
}
}
private handleLocalIceCandidate(candidate: RTCIceCandidate) {
this.logger.debug(
"Call " + this.callId + " got local ICE " + candidate.sdpMid + " candidate: " +
candidate.candidate,
);
if (this._state === CallState.Ended) return;
// As with the offer, note we need to make a copy of this object, not
// pass the original: that broke in Chrome ~m43.
if (candidate.candidate !== '' || !this.sentEndOfCandidates) {
this.queueCandidate(candidate);
if (candidate.candidate === '') this.sentEndOfCandidates = true;
}
}
private async handleRemoteIceCandidates(content: MCallCandidates<MCallBase>, partyId) {
if (this.state === CallState.Ended) { if (this.state === CallState.Ended) {
//debuglog("Ignoring remote ICE candidate because call has ended"); //debuglog("Ignoring remote ICE candidate because call has ended");
return; return;
@ -512,7 +564,7 @@ export class PeerCall implements IDisposable {
this.candidateSendQueue = []; this.candidateSendQueue = [];
try { try {
await this.options.sendSignallingMessage({type: EventType.Answer, content: answerContent}, undefined); await this.options.sendSignallingMessage({type: EventType.Answer, content: answerContent}, logger.item);
} catch (error) { } catch (error) {
this.terminate(CallParty.Local, CallErrorCode.SendAnswer, false); this.terminate(CallParty.Local, CallErrorCode.SendAnswer, false);
throw error; throw error;
@ -561,7 +613,7 @@ export class PeerCall implements IDisposable {
version: 1, version: 1,
candidates candidates
}, },
}, undefined); }, logger.item);
// Try to send candidates again just in case we received more candidates while sending. // Try to send candidates again just in case we received more candidates while sending.
this.sendCandidateQueue(); this.sendCandidateQueue();
} catch (error) { } catch (error) {
@ -620,6 +672,31 @@ export class PeerCall implements IDisposable {
} }
} }
private onIceConnectionStateChange = (state: RTCIceConnectionState): void => {
if (this._state === CallState.Ended) {
return; // because ICE can still complete as we're ending the call
}
this.logger.debug(
"Call ID " + this.callId + ": ICE connection state changed to: " + state,
);
// ideally we'd consider the call to be connected when we get media but
// chrome doesn't implement any of the 'onstarted' events yet
if (state == 'connected') {
this.iceDisconnectedTimeout?.abort();
this.iceDisconnectedTimeout = undefined;
this.setState(CallState.Connected);
} else if (state == 'failed') {
this.iceDisconnectedTimeout?.abort();
this.iceDisconnectedTimeout = undefined;
this.hangup(CallErrorCode.IceFailed);
} else if (state == 'disconnected') {
this.iceDisconnectedTimeout = this.options.createTimeout(30 * 1000);
this.iceDisconnectedTimeout.elapsed().then(() => {
this.hangup(CallErrorCode.IceFailed);
}, () => { /* ignore AbortError */ });
}
};
private setState(state: CallState): void { private setState(state: CallState): void {
const oldState = this._state; const oldState = this._state;
this._state = state; this._state = state;