2021-05-12 09:13:25 +02:00
|
|
|
import type * as SimplePeerNamespace from "simple-peer";
|
2020-08-20 16:56:10 +02:00
|
|
|
import {mediaManager} from "./MediaManager";
|
2021-06-11 11:29:36 +02:00
|
|
|
import {STUN_SERVER, TURN_PASSWORD, TURN_SERVER, TURN_USER} from "../Enum/EnvironmentVariable";
|
2021-05-12 09:13:25 +02:00
|
|
|
import type {RoomConnection} from "../Connexion/RoomConnection";
|
2020-10-25 21:59:14 +01:00
|
|
|
import {MESSAGE_TYPE_CONSTRAINT} from "./VideoPeer";
|
2021-05-12 09:13:25 +02:00
|
|
|
import type {UserSimplePeerInterface} from "./SimplePeer";
|
2021-06-11 11:29:36 +02:00
|
|
|
import {Readable, readable, writable, Writable} from "svelte/store";
|
|
|
|
import {DivImportance} from "./LayoutManager";
|
2021-06-14 17:59:50 +02:00
|
|
|
import type {ImportanceStore} from "../Stores/ImportanceStore";
|
|
|
|
import {createImportanceStore} from "../Stores/ImportanceStore";
|
2020-08-20 16:56:10 +02:00
|
|
|
|
|
|
|
const Peer: SimplePeerNamespace.SimplePeer = require('simple-peer');
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A peer connection used to transmit video / audio signals between 2 peers.
|
|
|
|
*/
|
|
|
|
export class ScreenSharingPeer extends Peer {
|
2020-08-21 22:53:17 +02:00
|
|
|
/**
|
|
|
|
* Whether this connection is currently receiving a video stream from a remote user.
|
|
|
|
*/
|
|
|
|
private isReceivingStream:boolean = false;
|
2020-10-20 20:39:33 +02:00
|
|
|
public toClose: boolean = false;
|
|
|
|
public _connected: boolean = false;
|
2021-06-11 11:29:36 +02:00
|
|
|
public readonly userId: number;
|
|
|
|
public readonly uniqueId: string;
|
|
|
|
public readonly streamStore: Readable<MediaStream | null>;
|
2021-06-14 17:59:50 +02:00
|
|
|
public readonly importanceStore: ImportanceStore;
|
2021-06-11 11:29:36 +02:00
|
|
|
public readonly statusStore: Readable<"connecting" | "connected" | "error" | "closed">;
|
2020-08-21 22:53:17 +02:00
|
|
|
|
2021-06-11 11:29:36 +02:00
|
|
|
constructor(user: UserSimplePeerInterface, initiator: boolean, public readonly userName: string, private connection: RoomConnection, stream: MediaStream | null) {
|
2020-08-20 16:56:10 +02:00
|
|
|
super({
|
|
|
|
initiator: initiator ? initiator : false,
|
2021-04-19 13:28:17 +02:00
|
|
|
//reconnectTimer: 10000,
|
2020-08-20 16:56:10 +02:00
|
|
|
config: {
|
|
|
|
iceServers: [
|
|
|
|
{
|
2021-02-08 20:46:26 +01:00
|
|
|
urls: STUN_SERVER.split(',')
|
2020-08-20 16:56:10 +02:00
|
|
|
},
|
2021-02-16 18:13:30 +01:00
|
|
|
TURN_SERVER !== '' ? {
|
2020-08-28 16:29:21 +02:00
|
|
|
urls: TURN_SERVER.split(','),
|
2021-02-16 11:08:03 +01:00
|
|
|
username: user.webRtcUser || TURN_USER,
|
|
|
|
credential: user.webRtcPassword || TURN_PASSWORD
|
2021-02-16 18:13:30 +01:00
|
|
|
} : undefined,
|
|
|
|
].filter((value) => value !== undefined)
|
2020-08-20 16:56:10 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2021-02-16 11:08:03 +01:00
|
|
|
this.userId = user.userId;
|
2021-06-11 11:29:36 +02:00
|
|
|
this.uniqueId = 'screensharing_'+this.userId;
|
|
|
|
|
|
|
|
this.streamStore = readable<MediaStream|null>(null, (set) => {
|
|
|
|
const onStream = (stream: MediaStream|null) => {
|
|
|
|
set(stream);
|
|
|
|
};
|
|
|
|
const onData = (chunk: Buffer) => {
|
|
|
|
// We unfortunately need to rely on an event to let the other party know a stream has stopped.
|
|
|
|
// It seems there is no native way to detect that.
|
|
|
|
// TODO: we might rely on the "ended" event: https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/ended_event
|
|
|
|
const message = JSON.parse(chunk.toString('utf8'));
|
|
|
|
if (message.streamEnded !== true) {
|
|
|
|
console.error('Unexpected message on screen sharing peer connection');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
set(null);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.on('stream', onStream);
|
|
|
|
this.on('data', onData);
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
this.off('stream', onStream);
|
|
|
|
this.off('data', onData);
|
|
|
|
};
|
|
|
|
});
|
|
|
|
|
2021-06-14 17:59:50 +02:00
|
|
|
this.importanceStore = createImportanceStore(DivImportance.Important);
|
2021-06-11 11:29:36 +02:00
|
|
|
|
|
|
|
this.statusStore = readable<"connecting" | "connected" | "error" | "closed">("connecting", (set) => {
|
|
|
|
const onConnect = () => {
|
|
|
|
set('connected');
|
|
|
|
};
|
|
|
|
const onError = () => {
|
|
|
|
set('error');
|
|
|
|
};
|
|
|
|
const onClose = () => {
|
|
|
|
set('closed');
|
|
|
|
};
|
|
|
|
|
|
|
|
this.on('connect', onConnect);
|
|
|
|
this.on('error', onError);
|
|
|
|
this.on('close', onClose);
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
this.off('connect', onConnect);
|
|
|
|
this.off('error', onError);
|
|
|
|
this.off('close', onClose);
|
|
|
|
};
|
|
|
|
});
|
2021-02-16 11:08:03 +01:00
|
|
|
|
2020-08-20 16:56:10 +02:00
|
|
|
//start listen signal for the peer connection
|
|
|
|
this.on('signal', (data: unknown) => {
|
|
|
|
this.sendWebrtcScreenSharingSignal(data);
|
|
|
|
});
|
|
|
|
|
|
|
|
this.on('stream', (stream: MediaStream) => {
|
|
|
|
this.stream(stream);
|
|
|
|
});
|
|
|
|
|
|
|
|
this.on('close', () => {
|
2020-10-20 20:39:33 +02:00
|
|
|
this._connected = false;
|
|
|
|
this.toClose = true;
|
2020-08-20 22:23:22 +02:00
|
|
|
this.destroy();
|
2020-08-20 16:56:10 +02:00
|
|
|
});
|
|
|
|
|
2021-06-11 11:29:36 +02:00
|
|
|
/*this.on('data', (chunk: Buffer) => {
|
2020-08-21 22:53:17 +02:00
|
|
|
// We unfortunately need to rely on an event to let the other party know a stream has stopped.
|
|
|
|
// It seems there is no native way to detect that.
|
2021-06-11 11:29:36 +02:00
|
|
|
// TODO: we might rely on the "ended" event: https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/ended_event
|
2020-08-21 22:53:17 +02:00
|
|
|
const message = JSON.parse(chunk.toString('utf8'));
|
|
|
|
if (message.streamEnded !== true) {
|
|
|
|
console.error('Unexpected message on screen sharing peer connection');
|
2021-05-31 17:03:29 +02:00
|
|
|
return;
|
2020-08-21 22:53:17 +02:00
|
|
|
}
|
2020-09-18 13:57:38 +02:00
|
|
|
mediaManager.removeActiveScreenSharingVideo("" + this.userId);
|
2021-06-11 11:29:36 +02:00
|
|
|
});*/
|
2020-08-21 22:53:17 +02:00
|
|
|
|
2020-08-20 16:56:10 +02:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
|
|
this.on('error', (err: any) => {
|
|
|
|
console.error(`screen sharing error => ${this.userId} => ${err.code}`, err);
|
|
|
|
//mediaManager.isErrorScreenSharing(this.userId);
|
|
|
|
});
|
|
|
|
|
|
|
|
this.on('connect', () => {
|
2020-10-20 20:39:33 +02:00
|
|
|
this._connected = true;
|
2020-08-20 16:56:10 +02:00
|
|
|
// FIXME: we need to put the loader on the screen sharing connection
|
2020-09-18 13:57:38 +02:00
|
|
|
mediaManager.isConnected("" + this.userId);
|
2020-08-20 16:56:10 +02:00
|
|
|
console.info(`connect => ${this.userId}`);
|
|
|
|
});
|
|
|
|
|
2020-10-20 20:39:33 +02:00
|
|
|
this.once('finish', () => {
|
|
|
|
this._onFinish();
|
|
|
|
});
|
|
|
|
|
2021-05-29 22:04:08 +02:00
|
|
|
if (stream) {
|
|
|
|
this.addStream(stream);
|
|
|
|
}
|
2020-08-20 16:56:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private sendWebrtcScreenSharingSignal(data: unknown) {
|
2020-08-31 15:21:05 +02:00
|
|
|
//console.log("sendWebrtcScreenSharingSignal", data);
|
2020-08-20 16:56:10 +02:00
|
|
|
try {
|
|
|
|
this.connection.sendWebrtcScreenSharingSignal(data, this.userId);
|
|
|
|
}catch (e) {
|
|
|
|
console.error(`sendWebrtcScreenSharingSignal => ${this.userId}`, e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sends received stream to screen.
|
|
|
|
*/
|
|
|
|
private stream(stream?: MediaStream) {
|
2020-08-31 15:21:05 +02:00
|
|
|
//console.log(`ScreenSharingPeer::stream => ${this.userId}`, stream);
|
|
|
|
//console.log(`stream => ${this.userId} => `, stream);
|
2020-08-20 16:56:10 +02:00
|
|
|
if(!stream){
|
2021-06-11 11:29:36 +02:00
|
|
|
//mediaManager.removeActiveScreenSharingVideo("" + this.userId);
|
2020-08-21 22:53:17 +02:00
|
|
|
this.isReceivingStream = false;
|
2020-08-20 16:56:10 +02:00
|
|
|
} else {
|
2021-06-11 11:29:36 +02:00
|
|
|
//mediaManager.addStreamRemoteScreenSharing("" + this.userId, stream);
|
2020-08-21 22:53:17 +02:00
|
|
|
this.isReceivingStream = true;
|
2020-08-20 16:56:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-21 22:53:17 +02:00
|
|
|
public isReceivingScreenSharingStream(): boolean {
|
|
|
|
return this.isReceivingStream;
|
|
|
|
}
|
|
|
|
|
2020-08-20 22:23:22 +02:00
|
|
|
public destroy(error?: Error): void {
|
2020-08-20 16:56:10 +02:00
|
|
|
try {
|
2020-10-20 20:39:33 +02:00
|
|
|
this._connected = false
|
|
|
|
if(!this.toClose){
|
|
|
|
return;
|
|
|
|
}
|
2021-06-11 11:29:36 +02:00
|
|
|
//mediaManager.removeActiveScreenSharingVideo("" + this.userId);
|
2020-08-20 16:56:10 +02:00
|
|
|
// FIXME: I don't understand why "Closing connection with" message is displayed TWICE before "Nb users in peerConnectionArray"
|
|
|
|
// I do understand the method closeConnection is called twice, but I don't understand how they manage to run in parallel.
|
|
|
|
//console.log('Closing connection with '+userId);
|
2020-08-20 22:23:22 +02:00
|
|
|
super.destroy(error);
|
2020-08-20 16:56:10 +02:00
|
|
|
//console.log('Nb users in peerConnectionArray '+this.PeerConnectionArray.size);
|
|
|
|
} catch (err) {
|
2020-08-20 22:23:22 +02:00
|
|
|
console.error("ScreenSharingPeer::destroy", err)
|
2020-08-20 16:56:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-20 20:39:33 +02:00
|
|
|
_onFinish () {
|
|
|
|
if (this.destroyed) return
|
|
|
|
const destroySoon = () => {
|
|
|
|
this.destroy();
|
|
|
|
}
|
|
|
|
if (this._connected) {
|
|
|
|
destroySoon();
|
|
|
|
} else {
|
|
|
|
this.once('connect', destroySoon);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-21 22:53:17 +02:00
|
|
|
public stopPushingScreenSharingToRemoteUser(stream: MediaStream) {
|
|
|
|
this.removeStream(stream);
|
2020-10-25 21:59:14 +01:00
|
|
|
this.write(new Buffer(JSON.stringify({type: MESSAGE_TYPE_CONSTRAINT, streamEnded: true})));
|
2020-08-21 22:53:17 +02:00
|
|
|
}
|
2020-08-20 16:56:10 +02:00
|
|
|
}
|