|
|
|
/*
|
|
|
|
* https://github.com/morethanwords/tweb
|
|
|
|
* Copyright (C) 2019-2021 Eduard Kuzmenko
|
|
|
|
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
|
|
|
*/
|
|
|
|
|
|
|
|
import safeAssign from "../../helpers/object/safeAssign";
|
|
|
|
import { logger } from "../logger";
|
|
|
|
import createDataChannel from "./helpers/createDataChannel";
|
|
|
|
import createPeerConnection from "./helpers/createPeerConnection";
|
|
|
|
import LocalConferenceDescription from "./localConferenceDescription";
|
|
|
|
import StreamManager from "./streamManager";
|
|
|
|
import { Ssrc } from "./types";
|
|
|
|
|
|
|
|
export type CallConnectionInstanceOptions = {
|
|
|
|
streamManager: StreamManager,
|
|
|
|
connection?: RTCPeerConnection,
|
|
|
|
log?: ReturnType<typeof logger>
|
|
|
|
};
|
|
|
|
|
|
|
|
export default abstract class CallConnectionInstanceBase {
|
|
|
|
public connection: RTCPeerConnection;
|
|
|
|
public streamManager: StreamManager;
|
|
|
|
public dataChannel: RTCDataChannel;
|
|
|
|
public description: LocalConferenceDescription;
|
|
|
|
public sources: {
|
|
|
|
audio: Ssrc,
|
|
|
|
video?: Ssrc,
|
|
|
|
};
|
|
|
|
protected negotiating: Promise<void>;
|
|
|
|
protected log: ReturnType<typeof logger>;
|
|
|
|
|
|
|
|
constructor(options: CallConnectionInstanceOptions) {
|
|
|
|
safeAssign(this, options);
|
|
|
|
|
|
|
|
if(!this.log) {
|
|
|
|
this.log = this.connection?.log || logger('CALL-CONNECTION-BASE');
|
|
|
|
}
|
|
|
|
|
|
|
|
this.sources = {} as any;
|
|
|
|
}
|
|
|
|
|
|
|
|
public createPeerConnection(config?: RTCConfiguration) {
|
|
|
|
return this.connection || (this.connection = createPeerConnection(config, this.log.bindPrefix('connection')).connection);
|
|
|
|
}
|
|
|
|
|
|
|
|
public createDataChannel(dict?: RTCDataChannelInit) {
|
|
|
|
return this.dataChannel || (this.dataChannel = createDataChannel(this.connection, dict, this.log.bindPrefix('data')));
|
|
|
|
}
|
|
|
|
|
|
|
|
public createDescription() {
|
|
|
|
return this.description || (this.description = new LocalConferenceDescription(this.connection));
|
|
|
|
}
|
|
|
|
|
|
|
|
public appendStreamToConference() {
|
|
|
|
return this.streamManager.appendToConference(this.description);
|
|
|
|
}
|
|
|
|
|
|
|
|
public closeConnection() {
|
|
|
|
const {connection} = this;
|
|
|
|
if(!connection) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
connection.log('close');
|
|
|
|
connection.close();
|
|
|
|
} catch(e) {
|
|
|
|
this.log.error(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public closeConnectionAndStream(stopStream: boolean) {
|
|
|
|
this.closeConnection();
|
|
|
|
stopStream && this.streamManager.stop();
|
|
|
|
}
|
|
|
|
|
|
|
|
protected abstract negotiateInternal(): CallConnectionInstanceBase['negotiating'];
|
|
|
|
|
|
|
|
public negotiate() {
|
|
|
|
let promise = this.negotiating;
|
|
|
|
if(promise) {
|
|
|
|
return promise;
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.negotiating = this.negotiateInternal().finally(() => {
|
|
|
|
this.negotiating = undefined;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public sendDataChannelData(data: any) {
|
|
|
|
if(this.dataChannel.readyState !== 'open') {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.dataChannel.send(JSON.stringify(data));
|
|
|
|
}
|
|
|
|
}
|