forked from mystiq/hydrogen-web
fix typescript errors
This commit is contained in:
parent
f674492685
commit
1bccbbfa08
2 changed files with 92 additions and 15 deletions
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue