Fixing stop of stream in bi-directional screen sharing.
This commit is contained in:
parent
f60b02f1dc
commit
91f422d0c3
@ -1,5 +1,5 @@
|
|||||||
import * as SimplePeerNamespace from "simple-peer";
|
|
||||||
import {DivImportance, layoutManager} from "./LayoutManager";
|
import {DivImportance, layoutManager} from "./LayoutManager";
|
||||||
|
import {HtmlUtils} from "./HtmlUtils";
|
||||||
|
|
||||||
const videoConstraint: boolean|MediaTrackConstraints = {
|
const videoConstraint: boolean|MediaTrackConstraints = {
|
||||||
width: { ideal: 1280 },
|
width: { ideal: 1280 },
|
||||||
@ -8,7 +8,8 @@ const videoConstraint: boolean|MediaTrackConstraints = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type UpdatedLocalStreamCallback = (media: MediaStream) => void;
|
type UpdatedLocalStreamCallback = (media: MediaStream) => void;
|
||||||
type UpdatedScreenSharingCallback = (media: MediaStream) => void;
|
type StartScreenSharingCallback = (media: MediaStream) => void;
|
||||||
|
type StopScreenSharingCallback = (media: MediaStream) => void;
|
||||||
|
|
||||||
// TODO: Split MediaManager in 2 classes: MediaManagerUI (in charge of HTML) and MediaManager (singleton in charge of the camera only)
|
// TODO: Split MediaManager in 2 classes: MediaManagerUI (in charge of HTML) and MediaManager (singleton in charge of the camera only)
|
||||||
// TODO: verify that microphone event listeners are not triggered plenty of time NOW (since MediaManager is created many times!!!!)
|
// TODO: verify that microphone event listeners are not triggered plenty of time NOW (since MediaManager is created many times!!!!)
|
||||||
@ -29,7 +30,8 @@ export class MediaManager {
|
|||||||
video: videoConstraint
|
video: videoConstraint
|
||||||
};
|
};
|
||||||
updatedLocalStreamCallBacks : Set<UpdatedLocalStreamCallback> = new Set<UpdatedLocalStreamCallback>();
|
updatedLocalStreamCallBacks : Set<UpdatedLocalStreamCallback> = new Set<UpdatedLocalStreamCallback>();
|
||||||
updatedScreenSharingCallBacks : Set<UpdatedScreenSharingCallback> = new Set<UpdatedScreenSharingCallback>();
|
startScreenSharingCallBacks : Set<StartScreenSharingCallback> = new Set<StartScreenSharingCallback>();
|
||||||
|
stopScreenSharingCallBacks : Set<StopScreenSharingCallback> = new Set<StopScreenSharingCallback>();
|
||||||
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -87,9 +89,14 @@ export class MediaManager {
|
|||||||
this.updatedLocalStreamCallBacks.add(callback);
|
this.updatedLocalStreamCallBacks.add(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
public onUpdateScreenSharing(callback: UpdatedScreenSharingCallback): void {
|
public onStartScreenSharing(callback: StartScreenSharingCallback): void {
|
||||||
|
|
||||||
this.updatedScreenSharingCallBacks.add(callback);
|
this.startScreenSharingCallBacks.add(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public onStopScreenSharing(callback: StopScreenSharingCallback): void {
|
||||||
|
|
||||||
|
this.stopScreenSharingCallBacks.add(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
removeUpdateLocalStreamEventListener(callback: UpdatedLocalStreamCallback): void {
|
removeUpdateLocalStreamEventListener(callback: UpdatedLocalStreamCallback): void {
|
||||||
@ -102,8 +109,14 @@ export class MediaManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private triggerUpdatedScreenSharingCallbacks(stream: MediaStream): void {
|
private triggerStartedScreenSharingCallbacks(stream: MediaStream): void {
|
||||||
for (const callback of this.updatedScreenSharingCallBacks) {
|
for (const callback of this.startScreenSharingCallBacks) {
|
||||||
|
callback(stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private triggerStoppedScreenSharingCallbacks(stream: MediaStream): void {
|
||||||
|
for (const callback of this.stopScreenSharingCallBacks) {
|
||||||
callback(stream);
|
callback(stream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -164,20 +177,26 @@ export class MediaManager {
|
|||||||
this.monitorClose.style.display = "none";
|
this.monitorClose.style.display = "none";
|
||||||
this.monitor.style.display = "block";
|
this.monitor.style.display = "block";
|
||||||
this.getScreenMedia().then((stream) => {
|
this.getScreenMedia().then((stream) => {
|
||||||
this.triggerUpdatedScreenSharingCallbacks(stream);
|
this.triggerStartedScreenSharingCallbacks(stream);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private disableScreenSharing() {
|
private disableScreenSharing() {
|
||||||
this.monitorClose.style.display = "block";
|
this.monitorClose.style.display = "block";
|
||||||
this.monitor.style.display = "none";
|
this.monitor.style.display = "none";
|
||||||
|
this.removeActiveScreenSharingVideo('me');
|
||||||
this.localScreenCapture?.getTracks().forEach((track: MediaStreamTrack) => {
|
this.localScreenCapture?.getTracks().forEach((track: MediaStreamTrack) => {
|
||||||
track.stop();
|
track.stop();
|
||||||
});
|
});
|
||||||
this.localScreenCapture = null;
|
if (this.localScreenCapture === null) {
|
||||||
|
console.warn('Weird: trying to remove a screen sharing that is not enabled');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const localScreenCapture = this.localScreenCapture;
|
||||||
this.getCamera().then((stream) => {
|
this.getCamera().then((stream) => {
|
||||||
this.triggerUpdatedScreenSharingCallbacks(stream);
|
this.triggerStoppedScreenSharingCallbacks(localScreenCapture);
|
||||||
});
|
});
|
||||||
|
this.localScreenCapture = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
//get screen
|
//get screen
|
||||||
@ -194,6 +213,9 @@ export class MediaManager {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.addScreenSharingActiveVideo('me', DivImportance.Normal);
|
||||||
|
HtmlUtils.getElementByIdOrFail<HTMLVideoElement>('screen-sharing-me').srcObject = stream;
|
||||||
|
|
||||||
return stream;
|
return stream;
|
||||||
})
|
})
|
||||||
.catch((err: unknown) => {
|
.catch((err: unknown) => {
|
||||||
@ -306,8 +328,8 @@ export class MediaManager {
|
|||||||
*
|
*
|
||||||
* @param userId
|
* @param userId
|
||||||
*/
|
*/
|
||||||
addScreenSharingActiveVideo(userId : string){
|
addScreenSharingActiveVideo(userId : string, divImportance: DivImportance = DivImportance.Important){
|
||||||
this.webrtcInAudio.play();
|
//this.webrtcInAudio.play();
|
||||||
|
|
||||||
userId = `screen-sharing-${userId}`;
|
userId = `screen-sharing-${userId}`;
|
||||||
const html = `
|
const html = `
|
||||||
@ -316,7 +338,7 @@ export class MediaManager {
|
|||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
layoutManager.add(DivImportance.Important, userId, html);
|
layoutManager.add(divImportance, userId, html);
|
||||||
|
|
||||||
this.remoteVideo.set(userId, this.getElementByIdOrFail<HTMLVideoElement>(userId));
|
this.remoteVideo.set(userId, this.getElementByIdOrFail<HTMLVideoElement>(userId));
|
||||||
}
|
}
|
||||||
@ -389,6 +411,12 @@ export class MediaManager {
|
|||||||
remoteVideo.srcObject = stream;
|
remoteVideo.srcObject = stream;
|
||||||
}
|
}
|
||||||
addStreamRemoteScreenSharing(userId : string, stream : MediaStream){
|
addStreamRemoteScreenSharing(userId : string, stream : MediaStream){
|
||||||
|
// In the case of screen sharing (going both ways), we may need to create the HTML element if it does not exist yet
|
||||||
|
const remoteVideo = this.remoteVideo.get(`screen-sharing-${userId}`);
|
||||||
|
if (remoteVideo === undefined) {
|
||||||
|
this.addScreenSharingActiveVideo(userId);
|
||||||
|
}
|
||||||
|
|
||||||
this.addStreamRemoteVideo(`screen-sharing-${userId}`, stream);
|
this.addStreamRemoteVideo(`screen-sharing-${userId}`, stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,11 @@ const Peer: SimplePeerNamespace.SimplePeer = require('simple-peer');
|
|||||||
* A peer connection used to transmit video / audio signals between 2 peers.
|
* A peer connection used to transmit video / audio signals between 2 peers.
|
||||||
*/
|
*/
|
||||||
export class ScreenSharingPeer extends Peer {
|
export class ScreenSharingPeer extends Peer {
|
||||||
|
/**
|
||||||
|
* Whether this connection is currently receiving a video stream from a remote user.
|
||||||
|
*/
|
||||||
|
private isReceivingStream:boolean = false;
|
||||||
|
|
||||||
constructor(private userId: string, initiator: boolean, private connection: Connection) {
|
constructor(private userId: string, initiator: boolean, private connection: Connection) {
|
||||||
super({
|
super({
|
||||||
initiator: initiator ? initiator : false,
|
initiator: initiator ? initiator : false,
|
||||||
@ -35,13 +40,20 @@ export class ScreenSharingPeer extends Peer {
|
|||||||
this.stream(stream);
|
this.stream(stream);
|
||||||
});
|
});
|
||||||
|
|
||||||
/*this.on('track', (track: MediaStreamTrack, stream: MediaStream) => {
|
|
||||||
});*/
|
|
||||||
|
|
||||||
this.on('close', () => {
|
this.on('close', () => {
|
||||||
this.destroy();
|
this.destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.on('data', (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.
|
||||||
|
const message = JSON.parse(chunk.toString('utf8'));
|
||||||
|
if (message.streamEnded !== true) {
|
||||||
|
console.error('Unexpected message on screen sharing peer connection');
|
||||||
|
}
|
||||||
|
mediaManager.removeActiveScreenSharingVideo(this.userId);
|
||||||
|
});
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
this.on('error', (err: any) => {
|
this.on('error', (err: any) => {
|
||||||
console.error(`screen sharing error => ${this.userId} => ${err.code}`, err);
|
console.error(`screen sharing error => ${this.userId} => ${err.code}`, err);
|
||||||
@ -74,11 +86,17 @@ export class ScreenSharingPeer extends Peer {
|
|||||||
console.log(`stream => ${this.userId} => `, stream);
|
console.log(`stream => ${this.userId} => `, stream);
|
||||||
if(!stream){
|
if(!stream){
|
||||||
mediaManager.removeActiveScreenSharingVideo(this.userId);
|
mediaManager.removeActiveScreenSharingVideo(this.userId);
|
||||||
|
this.isReceivingStream = false;
|
||||||
} else {
|
} else {
|
||||||
mediaManager.addStreamRemoteScreenSharing(this.userId, stream);
|
mediaManager.addStreamRemoteScreenSharing(this.userId, stream);
|
||||||
|
this.isReceivingStream = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public isReceivingScreenSharingStream(): boolean {
|
||||||
|
return this.isReceivingStream;
|
||||||
|
}
|
||||||
|
|
||||||
public destroy(error?: Error): void {
|
public destroy(error?: Error): void {
|
||||||
try {
|
try {
|
||||||
mediaManager.removeActiveScreenSharingVideo(this.userId);
|
mediaManager.removeActiveScreenSharingVideo(this.userId);
|
||||||
@ -98,9 +116,12 @@ export class ScreenSharingPeer extends Peer {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const track of localScreenCapture.getTracks()) {
|
this.addStream(localScreenCapture);
|
||||||
this.addTrack(track, localScreenCapture);
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public stopPushingScreenSharingToRemoteUser(stream: MediaStream) {
|
||||||
|
this.removeStream(stream);
|
||||||
|
this.write(new Buffer(JSON.stringify({streamEnded: true})));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,7 @@ export class SimplePeer {
|
|||||||
private PeerConnectionArray: Map<string, VideoPeer> = new Map<string, VideoPeer>();
|
private PeerConnectionArray: Map<string, VideoPeer> = new Map<string, VideoPeer>();
|
||||||
private readonly sendLocalVideoStreamCallback: (media: MediaStream) => void;
|
private readonly sendLocalVideoStreamCallback: (media: MediaStream) => void;
|
||||||
private readonly sendLocalScreenSharingStreamCallback: (media: MediaStream) => void;
|
private readonly sendLocalScreenSharingStreamCallback: (media: MediaStream) => void;
|
||||||
|
private readonly stopLocalScreenSharingStreamCallback: (media: MediaStream) => void;
|
||||||
private readonly peerConnectionListeners: Array<PeerConnectionListener> = new Array<PeerConnectionListener>();
|
private readonly peerConnectionListeners: Array<PeerConnectionListener> = new Array<PeerConnectionListener>();
|
||||||
|
|
||||||
constructor(Connection: Connection, WebRtcRoomId: string = "test-webrtc") {
|
constructor(Connection: Connection, WebRtcRoomId: string = "test-webrtc") {
|
||||||
@ -42,8 +43,10 @@ export class SimplePeer {
|
|||||||
// We need to go through this weird bound function pointer in order to be able to "free" this reference later.
|
// We need to go through this weird bound function pointer in order to be able to "free" this reference later.
|
||||||
this.sendLocalVideoStreamCallback = this.sendLocalVideoStream.bind(this);
|
this.sendLocalVideoStreamCallback = this.sendLocalVideoStream.bind(this);
|
||||||
this.sendLocalScreenSharingStreamCallback = this.sendLocalScreenSharingStream.bind(this);
|
this.sendLocalScreenSharingStreamCallback = this.sendLocalScreenSharingStream.bind(this);
|
||||||
|
this.stopLocalScreenSharingStreamCallback = this.stopLocalScreenSharingStream.bind(this);
|
||||||
mediaManager.onUpdateLocalStream(this.sendLocalVideoStreamCallback);
|
mediaManager.onUpdateLocalStream(this.sendLocalVideoStreamCallback);
|
||||||
mediaManager.onUpdateScreenSharing(this.sendLocalScreenSharingStreamCallback);
|
mediaManager.onStartScreenSharing(this.sendLocalScreenSharingStreamCallback);
|
||||||
|
mediaManager.onStopScreenSharing(this.stopLocalScreenSharingStreamCallback);
|
||||||
this.initialise();
|
this.initialise();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -332,14 +335,22 @@ export class SimplePeer {
|
|||||||
* Triggered locally when clicking on the screen sharing button
|
* Triggered locally when clicking on the screen sharing button
|
||||||
*/
|
*/
|
||||||
public sendLocalScreenSharingStream() {
|
public sendLocalScreenSharingStream() {
|
||||||
if (mediaManager.localScreenCapture) {
|
if (!mediaManager.localScreenCapture) {
|
||||||
for (const user of this.Users) {
|
console.error('Could not find localScreenCapture to share')
|
||||||
this.sendLocalScreenSharingStreamToUser(user.userId);
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
for (const user of this.Users) {
|
for (const user of this.Users) {
|
||||||
this.stopLocalScreenSharingStreamToUser(user.userId);
|
this.sendLocalScreenSharingStreamToUser(user.userId);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggered locally when clicking on the screen sharing button
|
||||||
|
*/
|
||||||
|
public stopLocalScreenSharingStream(stream: MediaStream) {
|
||||||
|
for (const user of this.Users) {
|
||||||
|
this.stopLocalScreenSharingStreamToUser(user.userId, stream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -360,17 +371,21 @@ export class SimplePeer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private stopLocalScreenSharingStreamToUser(userId: string): void {
|
private stopLocalScreenSharingStreamToUser(userId: string, stream: MediaStream): void {
|
||||||
const PeerConnectionScreenSharing = this.PeerScreenSharingConnectionArray.get(userId);
|
const PeerConnectionScreenSharing = this.PeerScreenSharingConnectionArray.get(userId);
|
||||||
if (!PeerConnectionScreenSharing) {
|
if (!PeerConnectionScreenSharing) {
|
||||||
throw new Error('Weird, screen sharing connection to user ' + userId + 'not found')
|
throw new Error('Weird, screen sharing connection to user ' + userId + 'not found')
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("updatedScreenSharing => destroy", PeerConnectionScreenSharing);
|
console.log("updatedScreenSharing => destroy", PeerConnectionScreenSharing);
|
||||||
// FIXME: maybe we don't want to destroy the connexion if it is used in the other way around!
|
|
||||||
// FIXME: maybe we don't want to destroy the connexion if it is used in the other way around!
|
// Stop sending stream and close peer connection if peer is not sending stream too
|
||||||
// FIXME: maybe we don't want to destroy the connexion if it is used in the other way around!
|
PeerConnectionScreenSharing.stopPushingScreenSharingToRemoteUser(stream);
|
||||||
PeerConnectionScreenSharing.destroy();
|
|
||||||
this.PeerScreenSharingConnectionArray.delete(userId);
|
if (!PeerConnectionScreenSharing.isReceivingScreenSharingStream()) {
|
||||||
|
PeerConnectionScreenSharing.destroy();
|
||||||
|
|
||||||
|
this.PeerScreenSharingConnectionArray.delete(userId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user