FEATURE: improved the mediaStore code to disable tracks instead of deleting them

This commit is contained in:
kharhamel 2021-08-31 18:28:59 +02:00
parent 1e20466f74
commit 52fe79df47
6 changed files with 112 additions and 239 deletions

View File

@ -1,27 +1,11 @@
<script lang="typescript"> <script lang="typescript">
import {obtainedMediaConstraintStore} from "../Stores/MediaStore";
import {localStreamStore} from "../Stores/MediaStore"; import {localStreamStore} from "../Stores/MediaStore";
import SoundMeterWidget from "./SoundMeterWidget.svelte"; import SoundMeterWidget from "./SoundMeterWidget.svelte";
import {onDestroy} from "svelte"; import {onDestroy} from "svelte";
import {srcObject} from "./Video/utils";
function srcObject(node: HTMLVideoElement, stream: MediaStream) {
node.srcObject = stream;
return {
update(newStream: MediaStream) {
if (node.srcObject != newStream) {
node.srcObject = newStream
}
}
}
}
let stream : MediaStream|null; let stream : MediaStream|null;
/*$: {
if ($localStreamStore.type === 'success') {
stream = $localStreamStore.stream;
} else {
stream = null;
}
}*/
const unsubscribe = localStreamStore.subscribe(value => { const unsubscribe = localStreamStore.subscribe(value => {
if (value.type === 'success') { if (value.type === 'success') {
@ -37,9 +21,9 @@
<div> <div>
<div class="video-container div-myCamVideo" class:hide={!$localStreamStore.constraints.video}> <div class="video-container div-myCamVideo" class:hide={!$obtainedMediaConstraintStore.video}>
{#if $localStreamStore.type === "success" && $localStreamStore.stream } {#if $localStreamStore.type === "success" && $localStreamStore.stream}
<video class="myCamVideo" use:srcObject={$localStreamStore.stream} autoplay muted playsinline></video> <video class="myCamVideo" use:srcObject={stream} autoplay muted playsinline></video>
<SoundMeterWidget stream={stream}></SoundMeterWidget> <SoundMeterWidget stream={stream}></SoundMeterWidget>
{/if} {/if}
</div> </div>

View File

@ -712,7 +712,7 @@ export class GameScene extends DirtyScene {
}); });
// When connection is performed, let's connect SimplePeer // When connection is performed, let's connect SimplePeer
this.simplePeer = new SimplePeer(this.connection, !this.room.isPublic, this.playerName); this.simplePeer = new SimplePeer(this.connection);
peerStore.connectToSimplePeer(this.simplePeer); peerStore.connectToSimplePeer(this.simplePeer);
screenSharingPeerStore.connectToSimplePeer(this.simplePeer); screenSharingPeerStore.connectToSimplePeer(this.simplePeer);
videoFocusStore.connectToSimplePeer(this.simplePeer); videoFocusStore.connectToSimplePeer(this.simplePeer);

View File

@ -324,14 +324,11 @@ export type LocalStreamStoreValue = StreamSuccessValue | StreamErrorValue;
interface StreamSuccessValue { interface StreamSuccessValue {
type: "success"; type: "success";
stream: MediaStream | null; stream: MediaStream | null;
// The constraints that we got (and not the one that have been requested)
constraints: MediaStreamConstraints;
} }
interface StreamErrorValue { interface StreamErrorValue {
type: "error"; type: "error";
error: Error; error: Error;
constraints: MediaStreamConstraints;
} }
let currentStream: MediaStream | null = null; let currentStream: MediaStream | null = null;
@ -339,10 +336,13 @@ let currentStream: MediaStream | null = null;
/** /**
* Stops the camera from filming * Stops the camera from filming
*/ */
function stopCamera(): void { function applyCameraConstraints(currentStream: MediaStream | null, constraints: MediaTrackConstraints | boolean): void {
if (currentStream) { if (currentStream) {
for (const track of currentStream.getVideoTracks()) { for (const track of currentStream.getVideoTracks()) {
track.stop(); track.enabled = constraints !== false;
if (constraints && constraints !== true) {
track.applyConstraints(constraints);
}
} }
} }
} }
@ -350,10 +350,16 @@ function stopCamera(): void {
/** /**
* Stops the microphone from listening * Stops the microphone from listening
*/ */
function stopMicrophone(): void { function applyMicrophoneConstraints(
currentStream: MediaStream | null,
constraints: MediaTrackConstraints | boolean
): void {
if (currentStream) { if (currentStream) {
for (const track of currentStream.getAudioTracks()) { for (const track of currentStream.getAudioTracks()) {
track.stop(); track.enabled = constraints !== false;
if (constraints && constraints !== true) {
track.applyConstraints(constraints);
}
} }
} }
} }
@ -372,122 +378,96 @@ export const localStreamStore = derived<Readable<MediaStreamConstraints>, LocalS
set({ set({
type: "error", type: "error",
error: new Error("Unable to access your camera or microphone. You need to use a HTTPS connection."), error: new Error("Unable to access your camera or microphone. You need to use a HTTPS connection."),
constraints,
}); });
return; return;
} else if (isIOS()) { } else if (isIOS()) {
set({ set({
type: "error", type: "error",
error: new WebviewOnOldIOS(), error: new WebviewOnOldIOS(),
constraints,
}); });
return; return;
} else { } else {
set({ set({
type: "error", type: "error",
error: new BrowserTooOldError(), error: new BrowserTooOldError(),
constraints,
}); });
return; return;
} }
} }
if (constraints.audio === false) { applyMicrophoneConstraints(currentStream, constraints.audio || false);
stopMicrophone(); applyCameraConstraints(currentStream, constraints.video || false);
}
if (constraints.video === false) {
stopCamera();
}
if (constraints.audio === false && constraints.video === false) { if (currentStream === null) {
currentStream = null; // we need to assign a first value to the stream because getUserMedia is async
set({ set({
type: "success", type: "success",
stream: null, stream: null,
constraints,
}); });
return; (async () => {
}
(async () => {
try {
stopMicrophone();
stopCamera();
currentStream = await navigator.mediaDevices.getUserMedia(constraints);
set({
type: "success",
stream: currentStream,
constraints,
});
return;
} catch (e) {
if (constraints.video !== false) {
console.info(
"Error. Unable to get microphone and/or camera access. Trying audio only.",
$mediaStreamConstraintsStore,
e
);
// TODO: does it make sense to pop this error when retrying?
set({
type: "error",
error: e,
constraints,
});
// Let's try without video constraints
requestedCameraState.disableWebcam();
} else {
console.info(
"Error. Unable to get microphone and/or camera access.",
$mediaStreamConstraintsStore,
e
);
set({
type: "error",
error: e,
constraints,
});
}
/*constraints.video = false;
if (constraints.audio === false) {
console.info("Error. Unable to get microphone and/or camera access.", $mediaStreamConstraintsStore, e);
set({
type: 'error',
error: e,
constraints
});
// Let's make as if the user did not ask.
requestedCameraState.disableWebcam();
} else {
console.info("Error. Unable to get microphone and/or camera access. Trying audio only.", $mediaStreamConstraintsStore, e);
try { try {
currentStream = await navigator.mediaDevices.getUserMedia(constraints); currentStream = await navigator.mediaDevices.getUserMedia(constraints);
set({ set({
type: 'success', type: "success",
stream: currentStream, stream: currentStream,
constraints
}); });
return; return;
} catch (e2) { } catch (e) {
console.info("Error. Unable to get microphone fallback access.", $mediaStreamConstraintsStore, e2); if (constraints.video !== false) {
set({ console.info(
type: 'error', "Error. Unable to get microphone and/or camera access. Trying audio only.",
error: e, $mediaStreamConstraintsStore,
constraints e
}); );
// TODO: does it make sense to pop this error when retrying?
set({
type: "error",
error: e,
});
// Let's try without video constraints
requestedCameraState.disableWebcam();
} else {
console.info(
"Error. Unable to get microphone and/or camera access.",
$mediaStreamConstraintsStore,
e
);
set({
type: "error",
error: e,
});
}
} }
}*/ })();
} }
})();
} }
); );
export interface ObtainedMediaStreamConstraints {
video: boolean;
audio: boolean;
}
let obtainedMediaConstraint: ObtainedMediaStreamConstraints = {
audio: false,
video: false,
};
/** /**
* A store containing the real active media constrained (not the one requested by the user, but the one we got from the system) * A store containing the actual states of audio and video (activated or deactivated)
*/ */
export const obtainedMediaConstraintStore = derived(localStreamStore, ($localStreamStore) => { export const obtainedMediaConstraintStore = derived<Readable<MediaStreamConstraints>, ObtainedMediaStreamConstraints>(
return $localStreamStore.constraints; mediaStreamConstraintsStore,
}); ($mediaStreamConstraintsStore, set) => {
const newObtainedMediaConstraint = {
video: !!$mediaStreamConstraintsStore.video,
audio: !!$mediaStreamConstraintsStore.audio,
};
if (newObtainedMediaConstraint !== obtainedMediaConstraint) {
obtainedMediaConstraint = newObtainedMediaConstraint;
set(obtainedMediaConstraint);
}
}
);
/** /**
* Device list * Device list

View File

@ -1,7 +1,6 @@
import { derived, get, Readable, readable, writable, Writable } from "svelte/store"; import { derived, Readable, readable, writable } from "svelte/store";
import { peerStore } from "./PeerStore"; import { peerStore } from "./PeerStore";
import type { LocalStreamStoreValue } from "./MediaStore"; import type { LocalStreamStoreValue } from "./MediaStore";
import { DivImportance } from "../WebRtc/LayoutManager";
import { gameOverlayVisibilityStore } from "./GameOverlayStoreVisibility"; import { gameOverlayVisibilityStore } from "./GameOverlayStoreVisibility";
declare const navigator: any; // eslint-disable-line @typescript-eslint/no-explicit-any declare const navigator: any; // eslint-disable-line @typescript-eslint/no-explicit-any
@ -106,7 +105,6 @@ export const screenSharingLocalStreamStore = derived<Readable<MediaStreamConstra
set({ set({
type: "success", type: "success",
stream: null, stream: null,
constraints,
}); });
return; return;
} }
@ -121,7 +119,6 @@ export const screenSharingLocalStreamStore = derived<Readable<MediaStreamConstra
set({ set({
type: "error", type: "error",
error: new Error("Your browser does not support sharing screen"), error: new Error("Your browser does not support sharing screen"),
constraints,
}); });
return; return;
} }
@ -141,10 +138,6 @@ export const screenSharingLocalStreamStore = derived<Readable<MediaStreamConstra
set({ set({
type: "success", type: "success",
stream: null, stream: null,
constraints: {
video: false,
audio: false,
},
}); });
}; };
} }
@ -152,7 +145,6 @@ export const screenSharingLocalStreamStore = derived<Readable<MediaStreamConstra
set({ set({
type: "success", type: "success",
stream: currentStream, stream: currentStream,
constraints,
}); });
return; return;
} catch (e) { } catch (e) {
@ -162,7 +154,6 @@ export const screenSharingLocalStreamStore = derived<Readable<MediaStreamConstra
set({ set({
type: "error", type: "error",
error: e, error: e,
constraints,
}); });
} }
})(); })();
@ -184,7 +175,6 @@ export const screenSharingAvailableStore = derived(peerStore, ($peerStore, set)
export interface ScreenSharingLocalMedia { export interface ScreenSharingLocalMedia {
uniqueId: string; uniqueId: string;
stream: MediaStream | null; stream: MediaStream | null;
//subscribe(this: void, run: Subscriber<ScreenSharingLocalMedia>, invalidate?: (value?: ScreenSharingLocalMedia) => void): Unsubscriber;
} }
/** /**

View File

@ -4,16 +4,12 @@ import type {
} from "../Connexion/ConnexionModels"; } from "../Connexion/ConnexionModels";
import { mediaManager, StartScreenSharingCallback, StopScreenSharingCallback } from "./MediaManager"; import { mediaManager, StartScreenSharingCallback, StopScreenSharingCallback } from "./MediaManager";
import { ScreenSharingPeer } from "./ScreenSharingPeer"; import { ScreenSharingPeer } from "./ScreenSharingPeer";
import { MESSAGE_TYPE_BLOCKED, MESSAGE_TYPE_CONSTRAINT, MESSAGE_TYPE_MESSAGE, VideoPeer } from "./VideoPeer"; import { VideoPeer } from "./VideoPeer";
import type { RoomConnection } from "../Connexion/RoomConnection"; import type { RoomConnection } from "../Connexion/RoomConnection";
import { blackListManager } from "./BlackListManager"; import { blackListManager } from "./BlackListManager";
import { get } from "svelte/store"; import { get } from "svelte/store";
import { localStreamStore, LocalStreamStoreValue, obtainedMediaConstraintStore } from "../Stores/MediaStore";
import { screenSharingLocalStreamStore } from "../Stores/ScreenSharingStore"; import { screenSharingLocalStreamStore } from "../Stores/ScreenSharingStore";
import { discussionManager } from "./DiscussionManager";
import { playersStore } from "../Stores/PlayersStore"; import { playersStore } from "../Stores/PlayersStore";
import { newChatMessageStore } from "../Stores/ChatStore";
import { isMobile } from "../Enum/EnvironmentVariable";
export interface UserSimplePeerInterface { export interface UserSimplePeerInterface {
userId: number; userId: number;
@ -46,19 +42,14 @@ export class SimplePeer {
private lastWebrtcUserName: string | undefined; private lastWebrtcUserName: string | undefined;
private lastWebrtcPassword: string | undefined; private lastWebrtcPassword: string | undefined;
constructor(private Connection: RoomConnection, private enableReporting: boolean, private myName: string) { constructor(private Connection: RoomConnection) {
// 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.sendLocalScreenSharingStreamCallback = this.sendLocalScreenSharingStream.bind(this); this.sendLocalScreenSharingStreamCallback = this.sendLocalScreenSharingStream.bind(this);
this.stopLocalScreenSharingStreamCallback = this.stopLocalScreenSharingStream.bind(this); this.stopLocalScreenSharingStreamCallback = this.stopLocalScreenSharingStream.bind(this);
this.unsubscribers.push(
localStreamStore.subscribe((streamResult) => {
this.sendLocalVideoStream(streamResult);
})
);
let localScreenCapture: MediaStream | null = null; let localScreenCapture: MediaStream | null = null;
//todo
this.unsubscribers.push( this.unsubscribers.push(
screenSharingLocalStreamStore.subscribe((streamResult) => { screenSharingLocalStreamStore.subscribe((streamResult) => {
if (streamResult.type === "error") { if (streamResult.type === "error") {
@ -126,19 +117,14 @@ export class SimplePeer {
if (!user.initiator) { if (!user.initiator) {
return; return;
} }
const streamResult = get(localStreamStore);
let stream: MediaStream | null = null;
if (streamResult.type === "success" && streamResult.stream) {
stream = streamResult.stream;
}
this.createPeerConnection(user, stream); this.createPeerConnection(user);
} }
/** /**
* create peer connection to bind users * create peer connection to bind users
*/ */
private createPeerConnection(user: UserSimplePeerInterface, localStream: MediaStream | null): VideoPeer | null { private createPeerConnection(user: UserSimplePeerInterface): VideoPeer | null {
const peerConnection = this.PeerConnectionArray.get(user.userId); const peerConnection = this.PeerConnectionArray.get(user.userId);
if (peerConnection) { if (peerConnection) {
if (peerConnection.destroyed) { if (peerConnection.destroyed) {
@ -160,7 +146,7 @@ export class SimplePeer {
this.lastWebrtcUserName = user.webRtcUser; this.lastWebrtcUserName = user.webRtcUser;
this.lastWebrtcPassword = user.webRtcPassword; this.lastWebrtcPassword = user.webRtcPassword;
const peer = new VideoPeer(user, user.initiator ? user.initiator : false, name, this.Connection, localStream); const peer = new VideoPeer(user, user.initiator ? user.initiator : false, name, this.Connection);
peer.toClose = false; peer.toClose = false;
// When a connection is established to a video stream, and if a screen sharing is taking place, // When a connection is established to a video stream, and if a screen sharing is taking place,
@ -204,7 +190,7 @@ export class SimplePeer {
if (!peerConnexionDeleted) { if (!peerConnexionDeleted) {
throw "Error to delete peer connection"; throw "Error to delete peer connection";
} }
this.createPeerConnection(user, stream); this.createPeerConnection(user);
} else { } else {
peerConnection.toClose = false; peerConnection.toClose = false;
} }
@ -282,7 +268,6 @@ export class SimplePeer {
*/ */
private closeScreenSharingConnection(userId: number) { private closeScreenSharingConnection(userId: number) {
try { try {
//mediaManager.removeActiveScreenSharingVideo("" + userId);
const peer = this.PeerScreenSharingConnectionArray.get(userId); const peer = this.PeerScreenSharingConnectionArray.get(userId);
if (peer === undefined) { if (peer === undefined) {
console.warn( console.warn(
@ -295,12 +280,6 @@ export class SimplePeer {
// FIXME: I don't understand why "Closing connection with" message is displayed TWICE before "Nb users in peerConnectionArray" // 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. // I do understand the method closeConnection is called twice, but I don't understand how they manage to run in parallel.
peer.destroy(); peer.destroy();
//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
/*if(!this.PeerScreenSharingConnectionArray.delete(userId)){
throw 'Couln\'t delete peer screen sharing connexion';
}*/
//console.log('Nb users in peerConnectionArray '+this.PeerConnectionArray.size);
} catch (err) { } catch (err) {
console.error("closeConnection", err); console.error("closeConnection", err);
} }
@ -330,13 +309,7 @@ export class SimplePeer {
try { try {
//if offer type, create peer connection //if offer type, create peer connection
if (data.signal.type === "offer") { if (data.signal.type === "offer") {
const streamResult = get(localStreamStore); this.createPeerConnection(data);
let stream: MediaStream | null = null;
if (streamResult.type === "success" && streamResult.stream) {
stream = streamResult.stream;
}
this.createPeerConnection(data, stream);
} }
const peer = this.PeerConnectionArray.get(data.userId); const peer = this.PeerConnectionArray.get(data.userId);
if (peer !== undefined) { if (peer !== undefined) {
@ -379,42 +352,10 @@ export class SimplePeer {
} catch (e) { } catch (e) {
console.error(`receiveWebrtcSignal => ${data.userId}`, e); console.error(`receiveWebrtcSignal => ${data.userId}`, e);
//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 //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
//this.PeerScreenSharingConnectionArray.delete(data.userId);
this.receiveWebrtcScreenSharingSignal(data); this.receiveWebrtcScreenSharingSignal(data);
} }
} }
private pushVideoToRemoteUser(userId: number, streamResult: LocalStreamStoreValue) {
try {
const PeerConnection = this.PeerConnectionArray.get(userId);
if (!PeerConnection) {
throw new Error("While adding media, cannot find user with ID " + userId);
}
PeerConnection.write(
new Buffer(
JSON.stringify({
type: MESSAGE_TYPE_CONSTRAINT,
...streamResult.constraints,
isMobile: isMobile(),
})
)
);
if (streamResult.type === "error") {
return;
}
const localStream: MediaStream | null = streamResult.stream;
if (!localStream) {
return;
}
PeerConnection.addStream(localStream);
} catch (e) {
console.error(`pushVideoToRemoteUser => ${userId}`, e);
}
}
private pushScreenSharingToRemoteUser(userId: number, localScreenCapture: MediaStream) { private pushScreenSharingToRemoteUser(userId: number, localScreenCapture: MediaStream) {
const PeerConnection = this.PeerScreenSharingConnectionArray.get(userId); const PeerConnection = this.PeerScreenSharingConnectionArray.get(userId);
if (!PeerConnection) { if (!PeerConnection) {
@ -427,12 +368,6 @@ export class SimplePeer {
return; return;
} }
public sendLocalVideoStream(streamResult: LocalStreamStoreValue) {
for (const user of this.Users) {
this.pushVideoToRemoteUser(user.userId, streamResult);
}
}
/** /**
* Triggered locally when clicking on the screen sharing button * Triggered locally when clicking on the screen sharing button
*/ */
@ -486,8 +421,6 @@ export class SimplePeer {
if (!PeerConnectionScreenSharing.isReceivingScreenSharingStream()) { if (!PeerConnectionScreenSharing.isReceivingScreenSharingStream()) {
PeerConnectionScreenSharing.destroy(); PeerConnectionScreenSharing.destroy();
//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
//this.PeerScreenSharingConnectionArray.delete(userId);
} }
} }
} }

View File

@ -5,7 +5,12 @@ import { blackListManager } from "./BlackListManager";
import type { Subscription } from "rxjs"; import type { Subscription } from "rxjs";
import type { UserSimplePeerInterface } from "./SimplePeer"; import type { UserSimplePeerInterface } from "./SimplePeer";
import { get, readable, Readable, Unsubscriber } from "svelte/store"; import { get, readable, Readable, Unsubscriber } from "svelte/store";
import { obtainedMediaConstraintIsMobileStore, obtainedMediaConstraintStore } from "../Stores/MediaStore"; import {
localStreamStore,
obtainedMediaConstraintIsMobileStore,
obtainedMediaConstraintStore,
ObtainedMediaStreamConstraints,
} from "../Stores/MediaStore";
import { playersStore } from "../Stores/PlayersStore"; import { playersStore } from "../Stores/PlayersStore";
import { chatMessagesStore, chatVisibilityStore, newChatMessageStore } from "../Stores/ChatStore"; import { chatMessagesStore, chatVisibilityStore, newChatMessageStore } from "../Stores/ChatStore";
import { getIceServersConfig } from "../Components/Video/utils"; import { getIceServersConfig } from "../Components/Video/utils";
@ -34,16 +39,17 @@ export class VideoPeer extends Peer {
private onUnBlockSubscribe: Subscription; private onUnBlockSubscribe: Subscription;
public readonly streamStore: Readable<MediaStream | null>; public readonly streamStore: Readable<MediaStream | null>;
public readonly statusStore: Readable<PeerStatus>; public readonly statusStore: Readable<PeerStatus>;
public readonly constraintsStore: Readable<MediaStreamConstraints | null>; public readonly constraintsStore: Readable<ObtainedMediaStreamConstraints | null>;
private newMessageunsubscriber: Unsubscriber | null = null; private newMessageunsubscriber: Unsubscriber | null = null;
private closing: Boolean = false; //this is used to prevent destroy() from being called twice private closing: Boolean = false; //this is used to prevent destroy() from being called twice
private localStreamStoreSubscribe: Unsubscriber;
private obtainedMediaConstraintStoreSubscribe: Unsubscriber;
constructor( constructor(
public user: UserSimplePeerInterface, public user: UserSimplePeerInterface,
initiator: boolean, initiator: boolean,
public readonly userName: string, public readonly userName: string,
private connection: RoomConnection, private connection: RoomConnection
localStream: MediaStream | null
) { ) {
super({ super({
initiator, initiator,
@ -60,27 +66,15 @@ export class VideoPeer extends Peer {
const onStream = (stream: MediaStream | null) => { const onStream = (stream: MediaStream | null) => {
set(stream); 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("stream", onStream);
this.on("data", onData);
return () => { return () => {
this.off("stream", onStream); this.off("stream", onStream);
this.off("data", onData);
}; };
}); });
this.constraintsStore = readable<MediaStreamConstraints | null>(null, (set) => { this.constraintsStore = readable<ObtainedMediaStreamConstraints | null>(null, (set) => {
const onData = (chunk: Buffer) => { const onData = (chunk: Buffer) => {
const message = JSON.parse(chunk.toString("utf8")); const message = JSON.parse(chunk.toString("utf8"));
if (message.type === MESSAGE_TYPE_CONSTRAINT) { if (message.type === MESSAGE_TYPE_CONSTRAINT) {
@ -191,7 +185,6 @@ export class VideoPeer extends Peer {
this._onFinish(); this._onFinish();
}); });
this.pushVideoToRemoteUser(localStream);
this.onBlockSubscribe = blackListManager.onBlockStream.subscribe((userUuid) => { this.onBlockSubscribe = blackListManager.onBlockStream.subscribe((userUuid) => {
if (userUuid === this.userUuid) { if (userUuid === this.userUuid) {
this.toggleRemoteStream(false); this.toggleRemoteStream(false);
@ -208,6 +201,21 @@ export class VideoPeer extends Peer {
if (blackListManager.isBlackListed(this.userUuid)) { if (blackListManager.isBlackListed(this.userUuid)) {
this.sendBlockMessage(true); this.sendBlockMessage(true);
} }
this.localStreamStoreSubscribe = localStreamStore.subscribe((streamValue) => {
if (streamValue.type === "success" && streamValue.stream) this.addStream(streamValue.stream);
});
this.obtainedMediaConstraintStoreSubscribe = obtainedMediaConstraintStore.subscribe((constraints) => {
this.write(
new Buffer(
JSON.stringify({
type: MESSAGE_TYPE_CONSTRAINT,
...constraints,
isMobile: isMobile(),
})
)
);
});
} }
private sendBlockMessage(blocking: boolean) { private sendBlockMessage(blocking: boolean) {
@ -264,6 +272,8 @@ export class VideoPeer extends Peer {
this.onUnBlockSubscribe.unsubscribe(); this.onUnBlockSubscribe.unsubscribe();
if (this.newMessageunsubscriber) this.newMessageunsubscriber(); if (this.newMessageunsubscriber) this.newMessageunsubscriber();
chatMessagesStore.addOutcomingUser(this.userId); chatMessagesStore.addOutcomingUser(this.userId);
if (this.localStreamStoreSubscribe) this.localStreamStoreSubscribe();
if (this.obtainedMediaConstraintStoreSubscribe) this.obtainedMediaConstraintStoreSubscribe();
super.destroy(); super.destroy();
} catch (err) { } catch (err) {
console.error("VideoPeer::destroy", err); console.error("VideoPeer::destroy", err);
@ -281,28 +291,4 @@ export class VideoPeer extends Peer {
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),
isMobile: isMobile(),
})
)
);
if (!localStream) {
return;
}
for (const track of localStream.getTracks()) {
this.addTrack(track, localStream);
}
} catch (e) {
console.error(`pushVideoToRemoteUser => ${this.userId}`, e);
}
}
} }