Merge remote-tracking branch 'remotes/upstream/develop' into tiles-start-positions
This commit is contained in:
commit
7f61e9addd
182 changed files with 17118 additions and 4494 deletions
|
@ -1,19 +1,22 @@
|
|||
import type * as SimplePeerNamespace from "simple-peer";
|
||||
import {mediaManager} from "./MediaManager";
|
||||
import {STUN_SERVER, TURN_PASSWORD, TURN_SERVER, TURN_USER} from "../Enum/EnvironmentVariable";
|
||||
import type {RoomConnection} from "../Connexion/RoomConnection";
|
||||
import {blackListManager} from "./BlackListManager";
|
||||
import type {Subscription} from "rxjs";
|
||||
import type {UserSimplePeerInterface} from "./SimplePeer";
|
||||
import {get} from "svelte/store";
|
||||
import {obtainedMediaConstraintStore} from "../Stores/MediaStore";
|
||||
import { mediaManager } from "./MediaManager";
|
||||
import { STUN_SERVER, TURN_PASSWORD, TURN_SERVER, TURN_USER } from "../Enum/EnvironmentVariable";
|
||||
import type { RoomConnection } from "../Connexion/RoomConnection";
|
||||
import { blackListManager } from "./BlackListManager";
|
||||
import type { Subscription } from "rxjs";
|
||||
import type { UserSimplePeerInterface } from "./SimplePeer";
|
||||
import { get, readable, Readable } from "svelte/store";
|
||||
import { obtainedMediaConstraintStore } from "../Stores/MediaStore";
|
||||
import { discussionManager } from "./DiscussionManager";
|
||||
|
||||
const Peer: SimplePeerNamespace.SimplePeer = require('simple-peer');
|
||||
const Peer: SimplePeerNamespace.SimplePeer = require("simple-peer");
|
||||
|
||||
export const MESSAGE_TYPE_CONSTRAINT = 'constraint';
|
||||
export const MESSAGE_TYPE_MESSAGE = 'message';
|
||||
export const MESSAGE_TYPE_BLOCKED = 'blocked';
|
||||
export const MESSAGE_TYPE_UNBLOCKED = 'unblocked';
|
||||
export type PeerStatus = "connecting" | "connected" | "error" | "closed";
|
||||
|
||||
export const MESSAGE_TYPE_CONSTRAINT = "constraint";
|
||||
export const MESSAGE_TYPE_MESSAGE = "message";
|
||||
export const MESSAGE_TYPE_BLOCKED = "blocked";
|
||||
export const MESSAGE_TYPE_UNBLOCKED = "unblocked";
|
||||
/**
|
||||
* A peer connection used to transmit video / audio signals between 2 peers.
|
||||
*/
|
||||
|
@ -22,60 +25,130 @@ export class VideoPeer extends Peer {
|
|||
public _connected: boolean = false;
|
||||
private remoteStream!: MediaStream;
|
||||
private blocked: boolean = false;
|
||||
private userId: number;
|
||||
private userName: string;
|
||||
public readonly userId: number;
|
||||
public readonly uniqueId: string;
|
||||
private onBlockSubscribe: Subscription;
|
||||
private onUnBlockSubscribe: Subscription;
|
||||
public readonly streamStore: Readable<MediaStream | null>;
|
||||
public readonly statusStore: Readable<PeerStatus>;
|
||||
public readonly constraintsStore: Readable<MediaStreamConstraints | null>;
|
||||
|
||||
constructor(public user: UserSimplePeerInterface, initiator: boolean, private connection: RoomConnection, localStream: MediaStream | null) {
|
||||
constructor(
|
||||
public user: UserSimplePeerInterface,
|
||||
initiator: boolean,
|
||||
public readonly userName: string,
|
||||
private connection: RoomConnection,
|
||||
localStream: MediaStream | null
|
||||
) {
|
||||
super({
|
||||
initiator: initiator ? initiator : false,
|
||||
//reconnectTimer: 10000,
|
||||
config: {
|
||||
iceServers: [
|
||||
{
|
||||
urls: STUN_SERVER.split(',')
|
||||
urls: STUN_SERVER.split(","),
|
||||
},
|
||||
TURN_SERVER !== '' ? {
|
||||
urls: TURN_SERVER.split(','),
|
||||
username: user.webRtcUser || TURN_USER,
|
||||
credential: user.webRtcPassword || TURN_PASSWORD
|
||||
} : undefined,
|
||||
].filter((value) => value !== undefined)
|
||||
}
|
||||
TURN_SERVER !== ""
|
||||
? {
|
||||
urls: TURN_SERVER.split(","),
|
||||
username: user.webRtcUser || TURN_USER,
|
||||
credential: user.webRtcPassword || TURN_PASSWORD,
|
||||
}
|
||||
: undefined,
|
||||
].filter((value) => value !== undefined),
|
||||
},
|
||||
});
|
||||
|
||||
this.userId = user.userId;
|
||||
this.userName = user.name || '';
|
||||
this.uniqueId = "video_" + this.userId;
|
||||
|
||||
this.streamStore = readable<MediaStream | null>(null, (set) => {
|
||||
const onStream = (stream: MediaStream | null) => {
|
||||
set(stream);
|
||||
};
|
||||
const onData = (chunk: Buffer) => {
|
||||
this.on("data", (chunk: Buffer) => {
|
||||
const message = JSON.parse(chunk.toString("utf8"));
|
||||
if (message.type === MESSAGE_TYPE_CONSTRAINT) {
|
||||
if (!message.video) {
|
||||
set(null);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.on("stream", onStream);
|
||||
this.on("data", onData);
|
||||
|
||||
return () => {
|
||||
this.off("stream", onStream);
|
||||
this.off("data", onData);
|
||||
};
|
||||
});
|
||||
|
||||
this.constraintsStore = readable<MediaStreamConstraints | null>(null, (set) => {
|
||||
const onData = (chunk: Buffer) => {
|
||||
const message = JSON.parse(chunk.toString("utf8"));
|
||||
if (message.type === MESSAGE_TYPE_CONSTRAINT) {
|
||||
set(message);
|
||||
}
|
||||
};
|
||||
|
||||
this.on("data", onData);
|
||||
|
||||
return () => {
|
||||
this.off("data", onData);
|
||||
};
|
||||
});
|
||||
|
||||
this.statusStore = readable<PeerStatus>("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);
|
||||
};
|
||||
});
|
||||
|
||||
//start listen signal for the peer connection
|
||||
this.on('signal', (data: unknown) => {
|
||||
this.on("signal", (data: unknown) => {
|
||||
this.sendWebrtcSignal(data);
|
||||
});
|
||||
|
||||
this.on('stream', (stream: MediaStream) => this.stream(stream));
|
||||
this.on("stream", (stream: MediaStream) => this.stream(stream));
|
||||
|
||||
this.on('close', () => {
|
||||
this.on("close", () => {
|
||||
this._connected = false;
|
||||
this.toClose = true;
|
||||
this.destroy();
|
||||
});
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
this.on('error', (err: any) => {
|
||||
this.on("error", (err: any) => {
|
||||
console.error(`error => ${this.userId} => ${err.code}`, err);
|
||||
mediaManager.isError("" + this.userId);
|
||||
});
|
||||
|
||||
this.on('connect', () => {
|
||||
this.on("connect", () => {
|
||||
this._connected = true;
|
||||
mediaManager.isConnected("" + this.userId);
|
||||
console.info(`connect => ${this.userId}`);
|
||||
});
|
||||
|
||||
this.on('data', (chunk: Buffer) => {
|
||||
const message = JSON.parse(chunk.toString('utf8'));
|
||||
if(message.type === MESSAGE_TYPE_CONSTRAINT) {
|
||||
this.on("data", (chunk: Buffer) => {
|
||||
const message = JSON.parse(chunk.toString("utf8"));
|
||||
if (message.type === MESSAGE_TYPE_CONSTRAINT) {
|
||||
if (message.audio) {
|
||||
mediaManager.enabledMicrophoneByUserId(this.userId);
|
||||
} else {
|
||||
|
@ -87,23 +160,23 @@ export class VideoPeer extends Peer {
|
|||
} else {
|
||||
mediaManager.disabledVideoByUserId(this.userId);
|
||||
}
|
||||
} else if(message.type === MESSAGE_TYPE_MESSAGE) {
|
||||
} else if (message.type === MESSAGE_TYPE_MESSAGE) {
|
||||
if (!blackListManager.isBlackListed(message.userId)) {
|
||||
mediaManager.addNewMessage(message.name, message.message);
|
||||
}
|
||||
} else if(message.type === MESSAGE_TYPE_BLOCKED) {
|
||||
} else if (message.type === MESSAGE_TYPE_BLOCKED) {
|
||||
//FIXME when A blacklists B, the output stream from A is muted in B's js client. This is insecure since B can manipulate the code to unmute A stream.
|
||||
// Find a way to block A's output stream in A's js client
|
||||
//However, the output stream stream B is correctly blocked in A client
|
||||
this.blocked = true;
|
||||
this.toggleRemoteStream(false);
|
||||
} else if(message.type === MESSAGE_TYPE_UNBLOCKED) {
|
||||
} else if (message.type === MESSAGE_TYPE_UNBLOCKED) {
|
||||
this.blocked = false;
|
||||
this.toggleRemoteStream(true);
|
||||
}
|
||||
});
|
||||
|
||||
this.once('finish', () => {
|
||||
this.once("finish", () => {
|
||||
this._onFinish();
|
||||
});
|
||||
|
||||
|
@ -122,23 +195,32 @@ export class VideoPeer extends Peer {
|
|||
});
|
||||
|
||||
if (blackListManager.isBlackListed(this.userId)) {
|
||||
this.sendBlockMessage(true)
|
||||
this.sendBlockMessage(true);
|
||||
}
|
||||
}
|
||||
|
||||
private sendBlockMessage(blocking: boolean) {
|
||||
this.write(new Buffer(JSON.stringify({type: blocking ? MESSAGE_TYPE_BLOCKED : MESSAGE_TYPE_UNBLOCKED, name: this.userName.toUpperCase(), userId: this.userId, message: ''})));
|
||||
this.write(
|
||||
new Buffer(
|
||||
JSON.stringify({
|
||||
type: blocking ? MESSAGE_TYPE_BLOCKED : MESSAGE_TYPE_UNBLOCKED,
|
||||
name: this.userName.toUpperCase(),
|
||||
userId: this.userId,
|
||||
message: "",
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private toggleRemoteStream(enable: boolean) {
|
||||
this.remoteStream.getTracks().forEach(track => track.enabled = enable);
|
||||
this.remoteStream.getTracks().forEach((track) => (track.enabled = enable));
|
||||
mediaManager.toggleBlockLogo(this.userId, !enable);
|
||||
}
|
||||
|
||||
private sendWebrtcSignal(data: unknown) {
|
||||
try {
|
||||
this.connection.sendWebrtcSignal(data, this.userId);
|
||||
}catch (e) {
|
||||
} catch (e) {
|
||||
console.error(`sendWebrtcSignal => ${this.userId}`, e);
|
||||
}
|
||||
}
|
||||
|
@ -152,8 +234,7 @@ export class VideoPeer extends Peer {
|
|||
if (blackListManager.isBlackListed(this.userId) || this.blocked) {
|
||||
this.toggleRemoteStream(false);
|
||||
}
|
||||
mediaManager.addStreamRemoteVideo("" + this.userId, stream);
|
||||
}catch (err){
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
@ -163,45 +244,47 @@ export class VideoPeer extends Peer {
|
|||
*/
|
||||
public destroy(error?: Error): void {
|
||||
try {
|
||||
this._connected = false
|
||||
if(!this.toClose){
|
||||
this._connected = false;
|
||||
if (!this.toClose) {
|
||||
return;
|
||||
}
|
||||
this.onBlockSubscribe.unsubscribe();
|
||||
this.onUnBlockSubscribe.unsubscribe();
|
||||
mediaManager.removeActiveVideo("" + this.userId);
|
||||
discussionManager.removeParticipant(this.userId);
|
||||
// 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.
|
||||
super.destroy(error);
|
||||
} catch (err) {
|
||||
console.error("VideoPeer::destroy", err)
|
||||
console.error("VideoPeer::destroy", err);
|
||||
}
|
||||
}
|
||||
|
||||
_onFinish () {
|
||||
if (this.destroyed) return
|
||||
_onFinish() {
|
||||
if (this.destroyed) return;
|
||||
const destroySoon = () => {
|
||||
this.destroy();
|
||||
}
|
||||
};
|
||||
if (this._connected) {
|
||||
destroySoon();
|
||||
} else {
|
||||
this.once('connect', destroySoon);
|
||||
this.once("connect", destroySoon);
|
||||
}
|
||||
}
|
||||
|
||||
private pushVideoToRemoteUser(localStream: MediaStream | null) {
|
||||
try {
|
||||
this.write(new Buffer(JSON.stringify({type: MESSAGE_TYPE_CONSTRAINT, ...get(obtainedMediaConstraintStore)})));
|
||||
this.write(
|
||||
new Buffer(JSON.stringify({ type: MESSAGE_TYPE_CONSTRAINT, ...get(obtainedMediaConstraintStore) }))
|
||||
);
|
||||
|
||||
if(!localStream){
|
||||
if (!localStream) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const track of localStream.getTracks()) {
|
||||
this.addTrack(track, localStream);
|
||||
}
|
||||
}catch (e) {
|
||||
} catch (e) {
|
||||
console.error(`pushVideoToRemoteUser => ${this.userId}`, e);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue