Changing the way we focus a video element.

Now, only one video element can be important.
This commit is contained in:
David Négrier 2021-06-15 14:45:01 +02:00
parent ac7fa164b6
commit 5cf5e0ce2b
12 changed files with 164 additions and 158 deletions

View File

@ -1,5 +1,6 @@
<script lang="typescript"> <script lang="typescript">
import {ScreenSharingLocalMedia} from "../../Stores/ScreenSharingStore"; import {ScreenSharingLocalMedia} from "../../Stores/ScreenSharingStore";
import {videoFocusStore} from "../../Stores/VideoFocusStore";
function srcObject(node, stream) { function srcObject(node, stream) {
node.srcObject = stream; node.srcObject = stream;
@ -18,6 +19,6 @@
</script> </script>
<div class="video-container {cssClass}" class:hide={!stream}> <div class="video-container {cssClass ? cssClass : ''}" class:hide={!stream}>
<video class="myCamVideo" use:srcObject={stream} autoplay muted playsinline on:click={() => peer.importanceStore.toggle()}></video> <video use:srcObject={stream} autoplay muted playsinline on:click={() => videoFocusStore.toggleFocus(peer)}></video>
</div> </div>

View File

@ -1,5 +1,6 @@
<script lang="ts"> <script lang="ts">
import {ScreenSharingPeer} from "../../WebRtc/ScreenSharingPeer"; import {ScreenSharingPeer} from "../../WebRtc/ScreenSharingPeer";
import {videoFocusStore} from "../../Stores/VideoFocusStore";
export let peer: ScreenSharingPeer; export let peer: ScreenSharingPeer;
let streamStore = peer.streamStore; let streamStore = peer.streamStore;
@ -45,7 +46,7 @@
{#if $streamStore === null} {#if $streamStore === null}
<i style="background-color: {getColorByString(name)};">{name}</i> <i style="background-color: {getColorByString(name)};">{name}</i>
{/if} {/if}
<video use:srcObject={$streamStore} autoplay playsinline on:click={() => peer.importanceStore.toggle()}></video> <video use:srcObject={$streamStore} autoplay playsinline on:click={() => videoFocusStore.toggleFocus(peer)}></video>
</div> </div>
<style lang="scss"> <style lang="scss">

View File

@ -5,13 +5,13 @@
import reportImg from "./images/report.svg"; import reportImg from "./images/report.svg";
import blockSignImg from "./images/blockSign.svg"; import blockSignImg from "./images/blockSign.svg";
import {DivImportance} from "../../WebRtc/LayoutManager"; import {DivImportance} from "../../WebRtc/LayoutManager";
import {videoFocusStore} from "../../Stores/VideoFocusStore";
export let peer: VideoPeer; export let peer: VideoPeer;
let streamStore = peer.streamStore; let streamStore = peer.streamStore;
let name = peer.userName; let name = peer.userName;
let statusStore = peer.statusStore; let statusStore = peer.statusStore;
let constraintStore = peer.constraintsStore; let constraintStore = peer.constraintsStore;
let importanceStore = peer.importanceStore;
constraintStore.subscribe((vl) => console.log('CONS', vl)); constraintStore.subscribe((vl) => console.log('CONS', vl));
@ -62,17 +62,10 @@
<img alt="Report this user" src={reportImg}> <img alt="Report this user" src={reportImg}>
<span>Report/Block</span> <span>Report/Block</span>
</button> </button>
<video use:srcObject={$streamStore} autoplay playsinline on:click={() => peer.importanceStore.toggle()}></video> <video use:srcObject={$streamStore} autoplay playsinline on:click={() => videoFocusStore.toggleFocus(peer)}></video>
<img src={blockSignImg} class="block-logo" alt="Block"> <img src={blockSignImg} class="block-logo" alt="Block">
{#if $constraintStore && $constraintStore.audio !== false} {#if $constraintStore && $constraintStore.audio !== false}
<SoundMeterWidget stream={$streamStore}></SoundMeterWidget> <SoundMeterWidget stream={$streamStore}></SoundMeterWidget>
{/if} {/if}
</div> </div>
<style lang="scss">
.video-container {
video {
width: 100%;
}
}
</style>

View File

@ -3,18 +3,23 @@
import {DivImportance} from "../../WebRtc/LayoutManager"; import {DivImportance} from "../../WebRtc/LayoutManager";
import Peer from "./Peer.svelte"; import Peer from "./Peer.svelte";
import {layoutStore} from "../../Stores/LayoutStore"; import {layoutStore} from "../../Stores/LayoutStore";
import {videoFocusStore} from "../../Stores/VideoFocusStore";
</script> </script>
<div class="video-overlay"> <div class="video-overlay">
<div class="main-section"> <div class="main-section">
{#each [...$layoutStore.get(DivImportance.Important).values()] as peer (peer.uniqueId)} {#each [...$layoutStore.values()] as peer (peer.uniqueId)}
{#if $videoFocusStore && peer === $videoFocusStore }
<Peer peer={peer}></Peer> <Peer peer={peer}></Peer>
{/if}
{/each} {/each}
</div> </div>
<aside class="sidebar"> <aside class="sidebar">
{#each [...$layoutStore.get(DivImportance.Normal).values()] as peer (peer.uniqueId)} {#each [...$layoutStore.values()] as peer (peer.uniqueId)}
{#if peer !== $videoFocusStore }
<Peer peer={peer}></Peer> <Peer peer={peer}></Peer>
{/if}
{/each} {/each}
</aside> </aside>
<div class="chat-mode three-col" style="display: none;"> <div class="chat-mode three-col" style="display: none;">

View File

@ -95,6 +95,7 @@ import {DEPTH_OVERLAY_INDEX} from "./DepthIndexes";
import {waScaleManager} from "../Services/WaScaleManager"; import {waScaleManager} from "../Services/WaScaleManager";
import {peerStore, screenSharingPeerStore} from "../../Stores/PeerStore"; import {peerStore, screenSharingPeerStore} from "../../Stores/PeerStore";
import {EmoteManager} from "./EmoteManager"; import {EmoteManager} from "./EmoteManager";
import {videoFocusStore} from "../../Stores/VideoFocusStore";
export interface GameSceneInitInterface { export interface GameSceneInitInterface {
initPosition: PointInterface|null, initPosition: PointInterface|null,
@ -647,6 +648,7 @@ export class GameScene extends DirtyScene implements CenterListener {
this.simplePeer = new SimplePeer(this.connection, !this.room.isPublic, this.playerName); this.simplePeer = new SimplePeer(this.connection, !this.room.isPublic, this.playerName);
peerStore.connectToSimplePeer(this.simplePeer); peerStore.connectToSimplePeer(this.simplePeer);
screenSharingPeerStore.connectToSimplePeer(this.simplePeer); screenSharingPeerStore.connectToSimplePeer(this.simplePeer);
videoFocusStore.connectToSimplePeer(this.simplePeer);
this.GlobalMessageManager = new GlobalMessageManager(this.connection); this.GlobalMessageManager = new GlobalMessageManager(this.connection);
userMessageManager.setReceiveBanListener(this.bannedUser.bind(this)); userMessageManager.setReceiveBanListener(this.bannedUser.bind(this));

View File

@ -1,26 +0,0 @@
import {VideoPeer} from "../WebRtc/VideoPeer";
import {Subscriber, Unsubscriber, writable} from "svelte/store";
import {RemotePeer, SimplePeer} from "../WebRtc/SimplePeer";
import {DivImportance} from "../WebRtc/LayoutManager";
export interface ImportanceStore {
subscribe: (this:void, run: Subscriber<DivImportance>, invalidate?: ((value?: DivImportance) => void | undefined)) => Unsubscriber,
toggle: () => void,
}
export function createImportanceStore(defaultImportance: DivImportance): ImportanceStore {
const { subscribe, set, update } = writable<DivImportance>(defaultImportance);
return {
subscribe,
toggle: () => {
update((importance) => {
if (importance === DivImportance.Important) {
return DivImportance.Normal;
} else {
return DivImportance.Important;
}
});
}
};
}

View File

@ -1,6 +1,5 @@
import {derived, get} from "svelte/store"; import {derived, get, writable} from "svelte/store";
import {ScreenSharingLocalMedia, screenSharingLocalMedia} from "./ScreenSharingStore"; import {ScreenSharingLocalMedia, screenSharingLocalMedia} from "./ScreenSharingStore";
import {DivImportance} from "../WebRtc/LayoutManager";
import { peerStore, screenSharingStreamStore} from "./PeerStore"; import { peerStore, screenSharingStreamStore} from "./PeerStore";
import type {RemotePeer} from "../WebRtc/SimplePeer"; import type {RemotePeer} from "../WebRtc/SimplePeer";
@ -27,26 +26,15 @@ function createLayoutStore() {
} }
unsubscribes = []; unsubscribes = [];
const peers = new Map<DivImportance, Map<string, DisplayableMedia>>(); const peers = new Map<string, DisplayableMedia>();
peers.set(DivImportance.Normal, new Map<string, DisplayableMedia>());
peers.set(DivImportance.Important, new Map<string, DisplayableMedia>());
const addPeer = (peer: DisplayableMedia) => { const addPeer = (peer: DisplayableMedia) => {
const importance = get(peer.importanceStore); peers.set(peer.uniqueId, peer);
peers.get(importance)?.set(peer.uniqueId, peer);
unsubscribes.push(peer.importanceStore.subscribe((importance) => {
peers.forEach((category) => {
category.delete(peer.uniqueId);
});
peers.get(importance)?.set(peer.uniqueId, peer);
set(peers);
}));
}; };
$screenSharingStreamStore.forEach(addPeer); $screenSharingStreamStore.forEach(addPeer);
$peerStore.forEach(addPeer); $peerStore.forEach(addPeer);
if ($screenSharingLocalMedia?.stream) { if ($screenSharingLocalMedia?.stream) {
addPeer($screenSharingLocalMedia); addPeer($screenSharingLocalMedia);
} }

View File

@ -5,7 +5,6 @@ import type {
} from "./MediaStore"; } from "./MediaStore";
import {DivImportance} from "../WebRtc/LayoutManager"; import {DivImportance} from "../WebRtc/LayoutManager";
import {gameOverlayVisibilityStore} from "./GameOverlayStoreVisibility"; import {gameOverlayVisibilityStore} from "./GameOverlayStoreVisibility";
import {createImportanceStore, ImportanceStore} from "./ImportanceStore";
declare const navigator:any; // eslint-disable-line @typescript-eslint/no-explicit-any declare const navigator:any; // eslint-disable-line @typescript-eslint/no-explicit-any
@ -189,7 +188,6 @@ export const screenSharingAvailableStore = derived(peerStore, ($peerStore, set)
export interface ScreenSharingLocalMedia { export interface ScreenSharingLocalMedia {
uniqueId: string; uniqueId: string;
importanceStore: ImportanceStore;
stream: MediaStream|null; stream: MediaStream|null;
//subscribe(this: void, run: Subscriber<ScreenSharingLocalMedia>, invalidate?: (value?: ScreenSharingLocalMedia) => void): Unsubscriber; //subscribe(this: void, run: Subscriber<ScreenSharingLocalMedia>, invalidate?: (value?: ScreenSharingLocalMedia) => void): Unsubscriber;
} }
@ -201,7 +199,6 @@ export const screenSharingLocalMedia = readable<ScreenSharingLocalMedia|null>(nu
const localMedia: ScreenSharingLocalMedia = { const localMedia: ScreenSharingLocalMedia = {
uniqueId: "localScreenSharingStream", uniqueId: "localScreenSharingStream",
importanceStore: createImportanceStore(DivImportance.Normal),
stream: null stream: null
} }

View File

@ -0,0 +1,48 @@
import {writable} from "svelte/store";
import type {RemotePeer, SimplePeer} from "../WebRtc/SimplePeer";
import {VideoPeer} from "../WebRtc/VideoPeer";
import {ScreenSharingPeer} from "../WebRtc/ScreenSharingPeer";
import type {DisplayableMedia} from "./LayoutStore";
/**
* A store that contains the peer / media that has currently the "importance" focus.
*/
function createVideoFocusStore() {
const { subscribe, set, update } = writable<DisplayableMedia | null>(null);
let focusedMedia: DisplayableMedia | null = null;
return {
subscribe,
focus: (media: DisplayableMedia) => {
focusedMedia = media;
set(media);
},
removeFocus: () => {
focusedMedia = null;
set(null);
},
toggleFocus: (media: DisplayableMedia) => {
if (media !== focusedMedia) {
focusedMedia = media;
} else {
focusedMedia = null;
}
console.log('MEDIA', focusedMedia)
set(focusedMedia);
},
connectToSimplePeer: (simplePeer: SimplePeer) => {
simplePeer.registerPeerConnectionListener({
onConnect(peer: RemotePeer) {
},
onDisconnect(userId: number) {
if ((focusedMedia instanceof VideoPeer || focusedMedia instanceof ScreenSharingPeer) && focusedMedia.userId === userId) {
set(null);
}
}
})
}
};
}
export const videoFocusStore = createVideoFocusStore();

View File

@ -5,9 +5,7 @@ import type {RoomConnection} from "../Connexion/RoomConnection";
import {MESSAGE_TYPE_CONSTRAINT} from "./VideoPeer"; import {MESSAGE_TYPE_CONSTRAINT} from "./VideoPeer";
import type {UserSimplePeerInterface} from "./SimplePeer"; import type {UserSimplePeerInterface} from "./SimplePeer";
import {Readable, readable, writable, Writable} from "svelte/store"; import {Readable, readable, writable, Writable} from "svelte/store";
import {DivImportance} from "./LayoutManager"; import {videoFocusStore} from "../Stores/VideoFocusStore";
import type {ImportanceStore} from "../Stores/ImportanceStore";
import {createImportanceStore} from "../Stores/ImportanceStore";
const Peer: SimplePeerNamespace.SimplePeer = require('simple-peer'); const Peer: SimplePeerNamespace.SimplePeer = require('simple-peer');
@ -24,7 +22,6 @@ export class ScreenSharingPeer extends Peer {
public readonly userId: number; public readonly userId: number;
public readonly uniqueId: string; public readonly uniqueId: string;
public readonly streamStore: Readable<MediaStream | null>; public readonly streamStore: Readable<MediaStream | null>;
public readonly importanceStore: ImportanceStore;
public readonly statusStore: Readable<"connecting" | "connected" | "error" | "closed">; public readonly statusStore: Readable<"connecting" | "connected" | "error" | "closed">;
constructor(user: UserSimplePeerInterface, initiator: boolean, public readonly userName: string, private connection: RoomConnection, stream: MediaStream | null) { constructor(user: UserSimplePeerInterface, initiator: boolean, public readonly userName: string, private connection: RoomConnection, stream: MediaStream | null) {
@ -50,6 +47,7 @@ export class ScreenSharingPeer extends Peer {
this.streamStore = readable<MediaStream|null>(null, (set) => { this.streamStore = readable<MediaStream|null>(null, (set) => {
const onStream = (stream: MediaStream|null) => { const onStream = (stream: MediaStream|null) => {
videoFocusStore.focus(this);
set(stream); set(stream);
}; };
const onData = (chunk: Buffer) => { const onData = (chunk: Buffer) => {
@ -73,8 +71,6 @@ export class ScreenSharingPeer extends Peer {
}; };
}); });
this.importanceStore = createImportanceStore(DivImportance.Important);
this.statusStore = readable<"connecting" | "connected" | "error" | "closed">("connecting", (set) => { this.statusStore = readable<"connecting" | "connected" | "error" | "closed">("connecting", (set) => {
const onConnect = () => { const onConnect = () => {
set('connected'); set('connected');

View File

@ -5,11 +5,9 @@ import type {RoomConnection} from "../Connexion/RoomConnection";
import {blackListManager} from "./BlackListManager"; 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, writable, Writable} from "svelte/store"; import {get, readable, Readable} from "svelte/store";
import {obtainedMediaConstraintStore} from "../Stores/MediaStore"; import {obtainedMediaConstraintStore} from "../Stores/MediaStore";
import {DivImportance} from "./LayoutManager"; import {DivImportance} from "./LayoutManager";
import type {ImportanceStore} from "../Stores/ImportanceStore";
import {createImportanceStore} from "../Stores/ImportanceStore";
const Peer: SimplePeerNamespace.SimplePeer = require('simple-peer'); const Peer: SimplePeerNamespace.SimplePeer = require('simple-peer');
@ -30,7 +28,6 @@ export class VideoPeer extends Peer {
private onBlockSubscribe: Subscription; private onBlockSubscribe: Subscription;
private onUnBlockSubscribe: Subscription; private onUnBlockSubscribe: Subscription;
public readonly streamStore: Readable<MediaStream | null>; public readonly streamStore: Readable<MediaStream | null>;
public readonly importanceStore: ImportanceStore;
public readonly statusStore: Readable<"connecting" | "connected" | "error" | "closed">; public readonly statusStore: Readable<"connecting" | "connected" | "error" | "closed">;
public readonly constraintsStore: Readable<MediaStreamConstraints|null>; public readonly constraintsStore: Readable<MediaStreamConstraints|null>;
@ -94,8 +91,6 @@ export class VideoPeer extends Peer {
}; };
}); });
this.importanceStore = createImportanceStore(DivImportance.Normal);
this.statusStore = readable<"connecting" | "connected" | "error" | "closed">("connecting", (set) => { this.statusStore = readable<"connecting" | "connected" | "error" | "closed">("connecting", (set) => {
const onConnect = () => { const onConnect = () => {
set('connected'); set('connected');

View File

@ -35,13 +35,20 @@ body .message-info.info{
body .message-info.warning{ body .message-info.warning{
background: #ffa500d6; background: #ffa500d6;
} }
.video-container { .video-container {
position: relative; position: relative;
transition: all 0.2s ease; transition: all 0.2s ease;
background-color: #00000099; background-color: #00000099;
cursor: url('./images/cursor_pointer.png'), pointer; cursor: url('./images/cursor_pointer.png'), pointer;
video {
width: 100%;
height: 100%;
cursor: url('./images/cursor_pointer.png'), pointer;
} }
.video-container i{
i {
position: absolute; position: absolute;
width: 100px; width: 100px;
height: 100px; height: 100px;
@ -56,7 +63,7 @@ body .message-info.warning{
overflow: hidden; overflow: hidden;
} }
.video-container img{ img {
position: absolute; position: absolute;
display: none; display: none;
width: 40px; width: 40px;
@ -66,14 +73,15 @@ body .message-info.warning{
padding: 10px; padding: 10px;
z-index: 2; z-index: 2;
} }
.video-container img.block-logo {
img.block-logo {
left: 30%; left: 30%;
bottom: 15%; bottom: 15%;
width: 150px; width: 150px;
height: 150px; height: 150px;
} }
.video-container button.report{ button.report{
display: block; display: block;
cursor: url('./images/cursor_pointer.png'), pointer; cursor: url('./images/cursor_pointer.png'), pointer;
background: none; background: none;
@ -90,18 +98,8 @@ body .message-info.warning{
overflow: hidden; overflow: hidden;
z-index: 2; z-index: 2;
transition: all .5s ease; transition: all .5s ease;
}
.video-container:hover button.report{ img{
width: 35px;
padding: 10px;
}
.video-container button.report:hover {
width: 160px;
}
.video-container button.report img{
position: absolute; position: absolute;
display: block; display: block;
bottom: 5px; bottom: 5px;
@ -112,7 +110,8 @@ body .message-info.warning{
width: 25px; width: 25px;
height: 25px; height: 25px;
} }
.video-container button.report span{
span {
position: absolute; position: absolute;
bottom: 6px; bottom: 6px;
left: 36px; left: 36px;
@ -120,18 +119,25 @@ body .message-info.warning{
font-size: 16px; font-size: 16px;
cursor: url('./images/cursor_pointer.png'), pointer; cursor: url('./images/cursor_pointer.png'), pointer;
} }
.video-container img.active {
img.active {
display: block !important; display: block !important;
} }
.video-container video{
height: 100%;
cursor: url('./images/cursor_pointer.png'), pointer;
} }
.video-container video:focus{ &:hover button.report{
width: 35px;
padding: 10px;
&:hover {
width: 160px;
}
}
video:focus{
outline: none; outline: none;
} }
}
.video-container.div-myCamVideo{ .video-container.div-myCamVideo{
border: none; border: none;