2021-05-12 09:13:25 +02:00
import type {
2020-08-20 00:05:00 +02:00
WebRtcDisconnectMessageInterface ,
2020-08-20 16:56:10 +02:00
WebRtcSignalReceivedMessageInterface ,
2020-09-25 18:29:22 +02:00
} from "../Connexion/ConnexionModels" ;
2021-06-25 18:14:40 +02:00
import { mediaManager , StartScreenSharingCallback , StopScreenSharingCallback } from "./MediaManager" ;
import { ScreenSharingPeer } from "./ScreenSharingPeer" ;
2021-08-31 18:28:59 +02:00
import { VideoPeer } from "./VideoPeer" ;
2021-06-25 18:14:40 +02:00
import type { RoomConnection } from "../Connexion/RoomConnection" ;
import { blackListManager } from "./BlackListManager" ;
import { get } from "svelte/store" ;
import { screenSharingLocalStreamStore } from "../Stores/ScreenSharingStore" ;
2021-07-07 11:24:51 +02:00
import { playersStore } from "../Stores/PlayersStore" ;
2021-09-03 15:16:21 +02:00
import { peerStore , screenSharingPeerStore } from "../Stores/PeerStore" ;
2021-06-25 18:14:40 +02:00
export interface UserSimplePeerInterface {
2020-09-18 13:57:38 +02:00
userId : number ;
2020-05-14 20:39:30 +02:00
initiator? : boolean ;
2021-06-25 18:14:40 +02:00
webRtcUser? : string | undefined ;
webRtcPassword? : string | undefined ;
2020-04-26 19:59:51 +02:00
}
2020-06-05 13:07:18 +02:00
2021-06-11 11:29:36 +02:00
export type RemotePeer = VideoPeer | ScreenSharingPeer ;
2020-06-05 13:07:18 +02:00
/ * *
* This class manages connections to all the peers in the same group as me .
* /
2020-06-03 22:32:43 +02:00
export class SimplePeer {
2020-11-27 14:51:50 +01:00
private Users : Array < UserSimplePeerInterface > = new Array < UserSimplePeerInterface > ( ) ; //todo: this array should be fusionned with PeerConnectionArray
2020-05-01 21:15:00 +02:00
2020-09-18 13:57:38 +02:00
private PeerScreenSharingConnectionArray : Map < number , ScreenSharingPeer > = new Map < number , ScreenSharingPeer > ( ) ;
private PeerConnectionArray : Map < number , VideoPeer > = new Map < number , VideoPeer > ( ) ;
2020-08-31 15:21:05 +02:00
private readonly sendLocalScreenSharingStreamCallback : StartScreenSharingCallback ;
private readonly stopLocalScreenSharingStreamCallback : StopScreenSharingCallback ;
2021-05-29 22:04:08 +02:00
private readonly unsubscribers : ( ( ) = > void ) [ ] = [ ] ;
2021-02-02 18:19:51 +01:00
private readonly userId : number ;
2021-06-25 18:14:40 +02:00
private lastWebrtcUserName : string | undefined ;
private lastWebrtcPassword : string | undefined ;
2020-04-25 16:05:33 +02:00
2021-08-31 18:28:59 +02:00
constructor ( private Connection : RoomConnection ) {
2021-09-03 15:16:21 +02:00
//we make sure we don't get any old peer.
peerStore . cleanupStore ( ) ;
screenSharingPeerStore . cleanupStore ( ) ;
2020-06-23 14:56:57 +02:00
// We need to go through this weird bound function pointer in order to be able to "free" this reference later.
2020-08-20 00:05:00 +02:00
this . sendLocalScreenSharingStreamCallback = this . sendLocalScreenSharingStream . bind ( this ) ;
2020-08-21 22:53:17 +02:00
this . stopLocalScreenSharingStreamCallback = this . stopLocalScreenSharingStream . bind ( this ) ;
2020-10-13 19:56:42 +02:00
2021-06-25 18:14:40 +02:00
let localScreenCapture : MediaStream | null = null ;
2021-05-29 22:04:08 +02:00
2021-08-31 18:28:59 +02:00
//todo
2021-06-25 18:14:40 +02:00
this . unsubscribers . push (
screenSharingLocalStreamStore . subscribe ( ( streamResult ) = > {
if ( streamResult . type === "error" ) {
// Let's ignore screen sharing errors, we will deal with those in a different way.
return ;
}
2021-05-29 22:04:08 +02:00
2021-06-25 18:14:40 +02:00
if ( streamResult . stream !== null ) {
localScreenCapture = streamResult . stream ;
this . sendLocalScreenSharingStream ( localScreenCapture ) ;
} else {
if ( localScreenCapture ) {
this . stopLocalScreenSharingStream ( localScreenCapture ) ;
localScreenCapture = null ;
}
2021-05-29 22:04:08 +02:00
}
2021-06-25 18:14:40 +02:00
} )
) ;
2021-05-29 22:04:08 +02:00
2021-02-02 18:19:51 +01:00
this . userId = Connection . getUserId ( ) ;
2020-04-29 01:40:32 +02:00
this . initialise ( ) ;
}
/ * *
* permit to listen when user could start visio
* /
2020-05-02 20:46:02 +02:00
private initialise() {
2020-05-01 21:15:00 +02:00
//receive signal by gemer
2020-08-20 16:56:10 +02:00
this . Connection . receiveWebrtcSignal ( ( message : WebRtcSignalReceivedMessageInterface ) = > {
2020-05-01 21:15:00 +02:00
this . receiveWebrtcSignal ( message ) ;
} ) ;
2020-04-25 16:05:33 +02:00
2020-06-11 23:18:06 +02:00
//receive signal by gemer
2020-08-20 16:56:10 +02:00
this . Connection . receiveWebrtcScreenSharingSignal ( ( message : WebRtcSignalReceivedMessageInterface ) = > {
2020-06-11 23:18:06 +02:00
this . receiveWebrtcScreenSharingSignal ( message ) ;
} ) ;
2020-08-20 00:05:00 +02:00
mediaManager . showGameOverlay ( ) ;
2021-05-19 11:17:43 +02:00
//receive message start
this . Connection . receiveWebrtcStart ( ( message : UserSimplePeerInterface ) = > {
this . receiveWebrtcStart ( message ) ;
2020-04-25 16:05:33 +02:00
} ) ;
2020-05-01 21:15:00 +02:00
2020-06-05 13:07:18 +02:00
this . Connection . disconnectMessage ( ( data : WebRtcDisconnectMessageInterface ) : void = > {
2020-05-24 23:14:12 +02:00
this . closeConnection ( data . userId ) ;
2020-05-02 20:46:02 +02:00
} ) ;
2020-04-25 16:05:33 +02:00
}
2021-02-02 18:19:51 +01:00
private receiveWebrtcStart ( user : UserSimplePeerInterface ) : void {
2020-09-29 16:01:22 +02:00
this . Users . push ( user ) ;
2020-08-20 00:05:00 +02:00
// Note: the clients array contain the list of all clients (even the ones we are already connected to in case a user joints a group)
2020-06-05 13:07:18 +02:00
// So we can receive a request we already had before. (which will abort at the first line of createPeerConnection)
// This would be symmetrical to the way we handle disconnection.
2021-02-16 09:58:08 +01:00
2020-05-24 23:14:12 +02:00
//start connection
2021-06-25 18:14:40 +02:00
if ( ! user . initiator ) {
2020-09-29 16:01:22 +02:00
return ;
}
2021-05-29 22:34:38 +02:00
2021-08-31 18:28:59 +02:00
this . createPeerConnection ( user ) ;
2020-04-25 16:05:33 +02:00
}
2020-05-02 20:46:02 +02:00
/ * *
2020-05-24 23:14:12 +02:00
* create peer connection to bind users
2020-05-02 20:46:02 +02:00
* /
2021-08-31 18:28:59 +02:00
private createPeerConnection ( user : UserSimplePeerInterface ) : VideoPeer | null {
2021-06-25 18:14:40 +02:00
const peerConnection = this . PeerConnectionArray . get ( user . userId ) ;
2020-10-20 20:39:33 +02:00
if ( peerConnection ) {
if ( peerConnection . destroyed ) {
2020-10-20 18:02:44 +02:00
peerConnection . toClose = true ;
peerConnection . destroy ( ) ;
2020-10-20 18:03:10 +02:00
const peerConnexionDeleted = this . PeerConnectionArray . delete ( user . userId ) ;
2020-10-20 20:39:33 +02:00
if ( ! peerConnexionDeleted ) {
2021-06-25 18:14:40 +02:00
throw "Error to delete peer connection" ;
2020-10-20 18:02:44 +02:00
}
2021-05-29 22:34:38 +02:00
//return this.createPeerConnection(user, localStream);
2020-10-20 20:39:33 +02:00
} else {
2020-10-20 18:02:44 +02:00
peerConnection . toClose = false ;
2021-05-29 22:34:38 +02:00
return null ;
2020-10-20 18:02:44 +02:00
}
2020-05-02 20:46:02 +02:00
}
2020-05-01 21:15:00 +02:00
2021-07-06 17:13:08 +02:00
const name = this . getName ( user . userId ) ;
2020-06-08 09:20:36 +02:00
2021-02-18 11:52:30 +01:00
this . lastWebrtcUserName = user . webRtcUser ;
this . lastWebrtcPassword = user . webRtcPassword ;
2021-08-31 18:28:59 +02:00
const peer = new VideoPeer ( user , user . initiator ? user.initiator : false , name , this . Connection ) ;
2020-10-25 21:59:14 +01:00
2020-10-20 18:02:44 +02:00
peer . toClose = false ;
2020-08-20 16:56:10 +02:00
// When a connection is established to a video stream, and if a screen sharing is taking place,
// the user sharing screen should also initiate a connection to the remote user!
2021-06-25 18:14:40 +02:00
peer . on ( "connect" , ( ) = > {
2021-05-29 22:04:08 +02:00
const streamResult = get ( screenSharingLocalStreamStore ) ;
2021-06-25 18:14:40 +02:00
if ( streamResult . type === "success" && streamResult . stream !== null ) {
2021-05-29 22:04:08 +02:00
this . sendLocalScreenSharingStreamToUser ( user . userId , streamResult . stream ) ;
2020-08-20 00:05:00 +02:00
}
2020-05-02 20:46:02 +02:00
} ) ;
2021-05-07 01:37:05 +02:00
//Create a notification for first user in circle discussion
2021-06-25 18:14:40 +02:00
if ( this . PeerConnectionArray . size === 0 ) {
2021-07-06 17:13:08 +02:00
mediaManager . createNotification ( name ) ;
2021-05-07 01:37:05 +02:00
}
2020-08-20 16:56:10 +02:00
this . PeerConnectionArray . set ( user . userId , peer ) ;
2020-05-02 20:46:02 +02:00
2021-09-03 15:16:21 +02:00
peerStore . pushNewPeer ( peer ) ;
2020-08-20 16:56:10 +02:00
return peer ;
}
2020-06-06 17:03:10 +02:00
2021-06-11 11:29:36 +02:00
private getName ( userId : number ) : string {
2021-07-07 11:24:51 +02:00
return playersStore . getPlayerById ( userId ) ? . name || "" ;
2021-06-11 11:29:36 +02:00
}
2020-08-20 16:56:10 +02:00
/ * *
* create peer connection to bind users
* /
2021-06-25 18:14:40 +02:00
private createPeerScreenSharingConnection (
user : UserSimplePeerInterface ,
stream : MediaStream | null
) : ScreenSharingPeer | null {
2020-10-20 20:39:33 +02:00
const peerConnection = this . PeerScreenSharingConnectionArray . get ( user . userId ) ;
2021-06-25 18:14:40 +02:00
if ( peerConnection ) {
if ( peerConnection . destroyed ) {
2020-10-20 20:39:33 +02:00
peerConnection . toClose = true ;
peerConnection . destroy ( ) ;
const peerConnexionDeleted = this . PeerScreenSharingConnectionArray . delete ( user . userId ) ;
2021-06-25 18:14:40 +02:00
if ( ! peerConnexionDeleted ) {
throw "Error to delete peer connection" ;
2020-10-20 20:39:33 +02:00
}
2021-08-31 18:28:59 +02:00
this . createPeerConnection ( user ) ;
2021-06-25 18:14:40 +02:00
} else {
2020-10-20 20:39:33 +02:00
peerConnection . toClose = false ;
}
2020-08-20 16:56:10 +02:00
return null ;
}
2020-05-23 14:00:36 +02:00
2021-02-18 11:52:30 +01:00
// Enrich the user with last known credentials (if they are not set in the user object, which happens when a user triggers the screen sharing)
if ( user . webRtcUser === undefined ) {
user . webRtcUser = this . lastWebrtcUserName ;
user . webRtcPassword = this . lastWebrtcPassword ;
}
2021-06-11 11:29:36 +02:00
const name = this . getName ( user . userId ) ;
2021-06-25 18:14:40 +02:00
const peer = new ScreenSharingPeer (
user ,
user . initiator ? user.initiator : false ,
name ,
this . Connection ,
stream
) ;
2020-08-20 16:56:10 +02:00
this . PeerScreenSharingConnectionArray . set ( user . userId , peer ) ;
2021-09-03 15:16:21 +02:00
screenSharingPeerStore . pushNewPeer ( peer ) ;
2020-06-08 09:20:36 +02:00
return peer ;
2020-04-26 19:12:01 +02:00
}
2020-06-05 13:07:18 +02:00
/ * *
* This is triggered twice . Once by the server , and once by a remote client disconnecting
* /
2021-06-25 18:14:40 +02:00
private closeConnection ( userId : number ) {
2020-05-02 20:46:02 +02:00
try {
2020-06-09 23:13:26 +02:00
const peer = this . PeerConnectionArray . get ( userId ) ;
2020-06-05 13:07:18 +02:00
if ( peer === undefined ) {
2021-06-25 18:14:40 +02:00
console . warn (
"closeConnection => Tried to close connection for user " + userId + " but could not find user"
) ;
2020-05-02 20:46:02 +02:00
return ;
}
2020-10-20 18:02:44 +02:00
//create temp perr to close
peer . toClose = true ;
2020-08-20 22:23:22 +02:00
peer . destroy ( ) ;
2020-06-05 13:07:18 +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.
2020-10-20 18:02:44 +02:00
2020-06-14 14:47:16 +02:00
this . closeScreenSharingConnection ( userId ) ;
2020-10-20 18:02:44 +02:00
2021-06-25 18:14:40 +02:00
const userIndex = this . Users . findIndex ( ( user ) = > user . userId === userId ) ;
if ( userIndex < 0 ) {
throw "Couldn't delete user" ;
2020-11-27 14:51:50 +01:00
} else {
this . Users . splice ( userIndex , 1 ) ;
}
2020-05-02 20:46:02 +02:00
} catch ( err ) {
2021-06-25 18:14:40 +02:00
console . error ( "closeConnection" , err ) ;
2020-05-02 20:46:02 +02:00
}
2021-01-07 12:58:45 +01:00
//if user left discussion, clear array peer connection of sharing
2021-06-25 18:14:40 +02:00
if ( this . Users . length === 0 ) {
2021-01-07 12:58:45 +01:00
for ( const userId of this . PeerScreenSharingConnectionArray . keys ( ) ) {
this . closeScreenSharingConnection ( userId ) ;
this . PeerScreenSharingConnectionArray . delete ( userId ) ;
2021-09-03 15:16:21 +02:00
screenSharingPeerStore . removePeer ( userId ) ;
2021-01-07 12:58:45 +01:00
}
}
2021-03-19 15:03:55 +01:00
2021-09-03 15:16:21 +02:00
peerStore . removePeer ( userId ) ;
2020-04-25 16:05:33 +02:00
}
2020-06-11 23:18:06 +02:00
/ * *
* This is triggered twice . Once by the server , and once by a remote client disconnecting
* /
2021-06-25 18:14:40 +02:00
private closeScreenSharingConnection ( userId : number ) {
2020-06-11 23:18:06 +02:00
try {
2020-08-18 14:59:50 +02:00
const peer = this . PeerScreenSharingConnectionArray . get ( userId ) ;
2020-06-11 23:18:06 +02:00
if ( peer === undefined ) {
2021-06-25 18:14:40 +02:00
console . warn (
"closeScreenSharingConnection => Tried to close connection for user " +
userId +
" but could not find user"
) ;
2020-06-11 23:18:06 +02:00
return ;
}
// 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.
2020-08-20 22:23:22 +02:00
peer . destroy ( ) ;
2020-06-11 23:18:06 +02:00
} catch ( err ) {
2021-06-25 18:14:40 +02:00
console . error ( "closeConnection" , err ) ;
2020-06-11 23:18:06 +02:00
}
}
2020-06-22 15:00:23 +02:00
public closeAllConnections() {
for ( const userId of this . PeerConnectionArray . keys ( ) ) {
this . closeConnection ( userId ) ;
}
2020-08-20 16:56:10 +02:00
for ( const userId of this . PeerScreenSharingConnectionArray . keys ( ) ) {
this . closeScreenSharingConnection ( userId ) ;
}
2020-06-22 15:00:23 +02:00
}
2020-06-23 14:56:57 +02:00
/ * *
* Unregisters any held event handler .
* /
public unregister() {
2021-05-29 22:04:08 +02:00
for ( const unsubscriber of this . unsubscribers ) {
unsubscriber ( ) ;
}
2021-09-03 15:16:21 +02:00
peerStore . cleanupStore ( ) ;
screenSharingPeerStore . cleanupStore ( ) ;
2020-06-23 14:56:57 +02:00
}
2020-06-19 16:36:40 +02:00
// eslint-disable-next-line @typescript-eslint/no-explicit-any
2020-08-20 16:56:10 +02:00
private receiveWebrtcSignal ( data : WebRtcSignalReceivedMessageInterface ) {
2020-05-02 20:46:02 +02:00
try {
2020-05-24 23:14:12 +02:00
//if offer type, create peer connection
2021-06-25 18:14:40 +02:00
if ( data . signal . type === "offer" ) {
2021-08-31 18:28:59 +02:00
this . createPeerConnection ( data ) ;
2020-05-02 20:46:02 +02:00
}
2020-06-09 23:13:26 +02:00
const peer = this . PeerConnectionArray . get ( data . userId ) ;
2020-06-03 22:32:43 +02:00
if ( peer !== undefined ) {
peer . signal ( data . signal ) ;
} else {
2021-06-25 18:14:40 +02:00
console . error ( 'Could not find peer whose ID is "' + data . userId + '" in PeerConnectionArray' ) ;
2020-06-03 22:32:43 +02:00
}
2020-05-02 20:46:02 +02:00
} catch ( e ) {
console . error ( ` receiveWebrtcSignal => ${ data . userId } ` , e ) ;
2020-04-25 16:05:33 +02:00
}
}
2020-08-20 16:56:10 +02:00
private receiveWebrtcScreenSharingSignal ( data : WebRtcSignalReceivedMessageInterface ) {
2021-07-07 11:24:51 +02:00
const uuid = playersStore . getPlayerById ( data . userId ) ? . userUuid || "" ;
if ( blackListManager . isBlackListed ( uuid ) ) return ;
2021-05-29 22:04:08 +02:00
const streamResult = get ( screenSharingLocalStreamStore ) ;
2021-06-25 18:14:40 +02:00
let stream : MediaStream | null = null ;
if ( streamResult . type === "success" && streamResult . stream !== null ) {
2021-05-29 22:04:08 +02:00
stream = streamResult . stream ;
}
2020-06-11 23:18:06 +02:00
try {
//if offer type, create peer connection
2021-06-25 18:14:40 +02:00
if ( data . signal . type === "offer" ) {
2021-05-29 22:04:08 +02:00
this . createPeerScreenSharingConnection ( data , stream ) ;
2020-06-11 23:18:06 +02:00
}
2020-08-18 14:59:50 +02:00
const peer = this . PeerScreenSharingConnectionArray . get ( data . userId ) ;
2020-06-11 23:18:06 +02:00
if ( peer !== undefined ) {
peer . signal ( data . signal ) ;
} else {
2021-06-25 18:14:40 +02:00
console . error (
'Could not find peer whose ID is "' + data . userId + '" in receiveWebrtcScreenSharingSignal'
) ;
2021-07-27 14:29:09 +02:00
console . info ( "Attempt to create new peer connection" ) ;
2021-05-29 22:04:08 +02:00
if ( stream ) {
this . sendLocalScreenSharingStreamToUser ( data . userId , stream ) ;
}
2020-06-11 23:18:06 +02:00
}
} catch ( e ) {
console . error ( ` receiveWebrtcSignal => ${ data . userId } ` , e ) ;
2021-07-27 14:29:09 +02:00
//Comment this peer connection because if we delete and try to reshare screen, the RTCPeerConnection send renegotiate event. This array will be remove when user left circle discussion
2020-10-20 14:48:59 +02:00
this . receiveWebrtcScreenSharingSignal ( data ) ;
2020-06-11 23:18:06 +02:00
}
}
2021-05-29 22:04:08 +02:00
private pushScreenSharingToRemoteUser ( userId : number , localScreenCapture : MediaStream ) {
2020-08-18 14:59:50 +02:00
const PeerConnection = this . PeerScreenSharingConnectionArray . get ( userId ) ;
2020-06-11 23:18:06 +02:00
if ( ! PeerConnection ) {
2021-06-25 18:14:40 +02:00
throw new Error ( "While pushing screen sharing, cannot find user with ID " + userId ) ;
2020-06-11 23:18:06 +02:00
}
2020-08-20 00:05:00 +02:00
for ( const track of localScreenCapture . getTracks ( ) ) {
2020-06-11 23:18:06 +02:00
PeerConnection . addTrack ( track , localScreenCapture ) ;
2020-08-20 00:05:00 +02:00
}
2020-06-11 23:18:06 +02:00
return ;
}
2020-08-20 00:05:00 +02:00
/ * *
* Triggered locally when clicking on the screen sharing button
* /
2021-05-29 22:04:08 +02:00
public sendLocalScreenSharingStream ( localScreenCapture : MediaStream ) {
2020-08-21 22:53:17 +02:00
for ( const user of this . Users ) {
2021-05-29 22:04:08 +02:00
this . sendLocalScreenSharingStreamToUser ( user . userId , localScreenCapture ) ;
2020-08-21 22:53:17 +02:00
}
}
/ * *
* Triggered locally when clicking on the screen sharing button
* /
public stopLocalScreenSharingStream ( stream : MediaStream ) {
for ( const user of this . Users ) {
this . stopLocalScreenSharingStreamToUser ( user . userId , stream ) ;
2020-06-08 09:20:36 +02:00
}
}
2020-08-20 00:05:00 +02:00
2021-05-29 22:04:08 +02:00
private sendLocalScreenSharingStreamToUser ( userId : number , localScreenCapture : MediaStream ) : void {
2021-07-07 11:24:51 +02:00
const uuid = playersStore . getPlayerById ( userId ) ? . userUuid || "" ;
if ( blackListManager . isBlackListed ( uuid ) ) return ;
2020-08-20 00:05:00 +02:00
// If a connection already exists with user (because it is already sharing a screen with us... let's use this connection)
if ( this . PeerScreenSharingConnectionArray . has ( userId ) ) {
2021-05-29 22:04:08 +02:00
this . pushScreenSharingToRemoteUser ( userId , localScreenCapture ) ;
2020-08-20 00:05:00 +02:00
return ;
}
const screenSharingUser : UserSimplePeerInterface = {
userId ,
2021-06-25 18:14:40 +02:00
initiator : true ,
2020-08-20 00:05:00 +02:00
} ;
2021-06-25 18:14:40 +02:00
const PeerConnectionScreenSharing = this . createPeerScreenSharingConnection (
screenSharingUser ,
localScreenCapture
) ;
2020-08-20 00:05:00 +02:00
if ( ! PeerConnectionScreenSharing ) {
return ;
}
}
2020-09-18 13:57:38 +02:00
private stopLocalScreenSharingStreamToUser ( userId : number , stream : MediaStream ) : void {
2020-08-20 00:05:00 +02:00
const PeerConnectionScreenSharing = this . PeerScreenSharingConnectionArray . get ( userId ) ;
if ( ! PeerConnectionScreenSharing ) {
2021-06-25 18:14:40 +02:00
throw new Error ( "Weird, screen sharing connection to user " + userId + "not found" ) ;
2020-08-20 00:05:00 +02:00
}
console . log ( "updatedScreenSharing => destroy" , PeerConnectionScreenSharing ) ;
2020-08-21 22:53:17 +02:00
// Stop sending stream and close peer connection if peer is not sending stream too
PeerConnectionScreenSharing . stopPushingScreenSharingToRemoteUser ( stream ) ;
if ( ! PeerConnectionScreenSharing . isReceivingScreenSharingStream ( ) ) {
PeerConnectionScreenSharing . destroy ( ) ;
}
2020-08-20 00:05:00 +02:00
}
2020-05-14 22:00:31 +02:00
}