Merge branch 'resizeSelect' of github.com:thecodingmachine/workadventure into resizeSelect
This commit is contained in:
commit
5735e9da36
@ -2,9 +2,20 @@
|
|||||||
import {Game} from "../../Phaser/Game/Game";
|
import {Game} from "../../Phaser/Game/Game";
|
||||||
import {EnableCameraSceneName} from "../../Phaser/Login/EnableCameraScene";
|
import {EnableCameraSceneName} from "../../Phaser/Login/EnableCameraScene";
|
||||||
import {localStreamStore} from "../../Stores/MediaStore";
|
import {localStreamStore} from "../../Stores/MediaStore";
|
||||||
|
import {
|
||||||
|
audioConstraintStore,
|
||||||
|
cameraListStore,
|
||||||
|
localStreamStore,
|
||||||
|
microphoneListStore,
|
||||||
|
videoConstraintStore
|
||||||
|
} from "../../Stores/MediaStore";
|
||||||
import {onDestroy} from "svelte";
|
import {onDestroy} from "svelte";
|
||||||
|
import HorizontalSoundMeterWidget from "./HorizontalSoundMeterWidget.svelte";
|
||||||
|
import cinemaCloseImg from "../images/cinema-close.svg";
|
||||||
|
|
||||||
export let game: Game;
|
export let game: Game;
|
||||||
|
let selectedCamera : string|null = null;
|
||||||
|
let selectedMicrophone : string|null = null;
|
||||||
|
|
||||||
const enableCameraScene = game.scene.getScene(EnableCameraSceneName);
|
const enableCameraScene = game.scene.getScene(EnableCameraSceneName);
|
||||||
|
|
||||||
@ -23,28 +34,84 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let stream : MediaStream|null;
|
let stream: MediaStream | null;
|
||||||
|
|
||||||
const unsubscribe = localStreamStore.subscribe(value => {
|
const unsubscribe = localStreamStore.subscribe(value => {
|
||||||
if (value.type === 'success') {
|
if (value.type === 'success') {
|
||||||
stream = value.stream;
|
stream = value.stream;
|
||||||
|
|
||||||
|
if (stream !== null) {
|
||||||
|
const videoTracks = stream.getVideoTracks();
|
||||||
|
if (videoTracks.length > 0) {
|
||||||
|
selectedCamera = videoTracks[0].getSettings().deviceId;
|
||||||
|
}
|
||||||
|
const audioTracks = stream.getAudioTracks();
|
||||||
|
if (audioTracks.length > 0) {
|
||||||
|
selectedMicrophone = audioTracks[0].getSettings().deviceId;
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
stream = null;
|
stream = null;
|
||||||
|
selectedCamera = null;
|
||||||
|
selectedMicrophone = null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
onDestroy(unsubscribe);
|
onDestroy(unsubscribe);
|
||||||
|
|
||||||
|
function normalizeDeviceName(label: string): string {
|
||||||
|
// remove text in parenthesis
|
||||||
|
return label.replace(/\([^()]*\)/g, '').trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectCamera() {
|
||||||
|
videoConstraintStore.setDeviceId(selectedCamera);
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectMicrophone() {
|
||||||
|
audioConstraintStore.setDeviceId(selectedMicrophone);
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<form class="enableCameraScene" on:submit|preventDefault={submit}>
|
<form class="enableCameraScene" on:submit|preventDefault={submit}>
|
||||||
<section class="title text-center">
|
<section class="text-center">
|
||||||
<h2>Turn on your camera and microphone</h2>
|
<h2>Turn on your camera and microphone</h2>
|
||||||
</section>
|
</section>
|
||||||
<div id="webRtcSetup" class="webrtcsetup">
|
<div id="webRtcSetup" class="webrtcsetup">
|
||||||
<img class="background-img" src="resources/logos/cinema-close.svg" alt="" class:hide={$localStreamStore.stream}>
|
<img class="background-img" src={cinemaCloseImg} alt="" class:hide={$localStreamStore.stream}>
|
||||||
<video id="myCamVideoSetup" use:srcObject={$localStreamStore.stream} autoplay muted></video>
|
<video id="myCamVideoSetup" use:srcObject={$localStreamStore.stream} autoplay muted class:hide={!$localStreamStore.stream}></video>
|
||||||
</div>
|
</div>
|
||||||
|
<HorizontalSoundMeterWidget stream={stream}></HorizontalSoundMeterWidget>
|
||||||
|
|
||||||
|
<section class="selectWebcamForm">
|
||||||
|
|
||||||
|
{#if $cameraListStore.length > 1 }
|
||||||
|
<div class="nes-select">
|
||||||
|
<select bind:value={selectedCamera} on:change={selectCamera}>
|
||||||
|
{#each $cameraListStore as camera}
|
||||||
|
<option value={camera.deviceId}>
|
||||||
|
{normalizeDeviceName(camera.label)}
|
||||||
|
</option>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if $microphoneListStore.length > 1 }
|
||||||
|
<div class="nes-select">
|
||||||
|
<select bind:value={selectedMicrophone} on:change={selectMicrophone}>
|
||||||
|
{#each $microphoneListStore as microphone}
|
||||||
|
<option value={microphone.deviceId}>
|
||||||
|
{normalizeDeviceName(microphone.label)}
|
||||||
|
</option>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
|
||||||
<!--<section class="text-center">
|
<!--<section class="text-center">
|
||||||
<video id="myCamVideoSetup" autoplay muted></video>
|
<video id="myCamVideoSetup" autoplay muted></video>
|
||||||
@ -70,22 +137,42 @@
|
|||||||
/*background: #000000;*/
|
/*background: #000000;*/
|
||||||
margin: 20px auto 0;
|
margin: 20px auto 0;
|
||||||
color: #ebeeee;
|
color: #ebeeee;
|
||||||
|
height: 100vh;
|
||||||
|
overflow: auto;
|
||||||
/*max-height: 48vh;
|
/*max-height: 48vh;
|
||||||
width: 42vw;
|
width: 42vw;
|
||||||
max-width: 300px;*/
|
max-width: 300px;*/
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
section.title {
|
/*section.title {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 1vh;
|
top: 1vh;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
section.selectWebcamForm {
|
||||||
|
margin-top: 3vh;
|
||||||
|
margin-bottom: 3vh;
|
||||||
|
min-height: 10vh;
|
||||||
|
width: 50%;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
|
||||||
|
select {
|
||||||
|
font-family: "Press Start 2P";
|
||||||
|
margin-top: 1vh;
|
||||||
|
margin-bottom: 1vh;
|
||||||
|
|
||||||
|
option {
|
||||||
|
font-family: "Press Start 2P";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
section.action{
|
section.action{
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
position: absolute;
|
/*position: absolute;
|
||||||
top: 85vh;
|
top: 85vh;*/
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,29 +190,31 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.webrtcsetup{
|
.webrtcsetup{
|
||||||
position: absolute;
|
/*position: absolute;
|
||||||
top: 140px;
|
top: 140px;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;*/
|
||||||
|
margin-top: 2vh;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
height: 50%;
|
height: 28.125vw;
|
||||||
width: 50%;
|
width: 50vw;
|
||||||
border: white 6px solid;
|
border: white 6px solid;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
img.background-img {
|
img.background-img {
|
||||||
position: relative;
|
/*position: relative;*/
|
||||||
display: block;
|
/*display: block;*/
|
||||||
width: 40%;
|
width: 40%;
|
||||||
height: 60%;
|
/*height: 60%;*/
|
||||||
margin-left: auto;
|
/*top: 50%;*/
|
||||||
margin-right: auto;
|
/*transform: translateY(-50%);*/
|
||||||
top: 50%;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
img.hide {
|
.hide {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,77 @@
|
|||||||
|
<script lang="typescript">
|
||||||
|
import {SoundMeter} from "../../Phaser/Components/SoundMeter";
|
||||||
|
import {onDestroy} from "svelte";
|
||||||
|
|
||||||
|
export let stream: MediaStream | null;
|
||||||
|
let volume = 0;
|
||||||
|
|
||||||
|
const NB_BARS = 20;
|
||||||
|
|
||||||
|
let timeout;
|
||||||
|
const soundMeter = new SoundMeter();
|
||||||
|
|
||||||
|
$: {
|
||||||
|
if (stream && stream.getAudioTracks().length > 0) {
|
||||||
|
soundMeter.connectToSource(stream, new AudioContext());
|
||||||
|
|
||||||
|
if (timeout) {
|
||||||
|
clearInterval(timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
timeout = setInterval(() => {
|
||||||
|
try{
|
||||||
|
volume = parseInt((soundMeter.getVolume() / 100 * NB_BARS).toFixed(0));
|
||||||
|
//console.log(volume);
|
||||||
|
}catch(err){
|
||||||
|
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onDestroy(() => {
|
||||||
|
soundMeter.stop();
|
||||||
|
if (timeout) {
|
||||||
|
clearInterval(timeout);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
function color(i: number, volume: number) {
|
||||||
|
const red = 255 * i / NB_BARS;
|
||||||
|
const green = 255 * (1 - i / NB_BARS);
|
||||||
|
|
||||||
|
let alpha = 1;
|
||||||
|
if (i >= volume) {
|
||||||
|
alpha = 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'background-color:rgba('+red+', '+green+', 0, '+alpha+')';
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="horizontal-sound-meter" class:active={stream?.getAudioTracks().length > 0}>
|
||||||
|
{#each [...Array(NB_BARS).keys()] as i}
|
||||||
|
<div style={color(i, volume)}></div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.horizontal-sound-meter {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
width: 50%;
|
||||||
|
height: 30px;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
margin-top: 1vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.horizontal-sound-meter div {
|
||||||
|
margin-left: 5px;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
<form class="helpCameraSettings nes-container" on:submit|preventDefault={close}>
|
<form class="helpCameraSettings nes-container" on:submit|preventDefault={close}>
|
||||||
<section>
|
<section>
|
||||||
<h2>Camera/Microphone access needed</h2>
|
<h2>Camera / Microphone access needed</h2>
|
||||||
<p class="err">Permission denied</p>
|
<p class="err">Permission denied</p>
|
||||||
<p>You must allow camera and microphone access in your browser.</p>
|
<p>You must allow camera and microphone access in your browser.</p>
|
||||||
<p>
|
<p>
|
||||||
|
@ -2,28 +2,40 @@
|
|||||||
import {SoundMeter} from "../Phaser/Components/SoundMeter";
|
import {SoundMeter} from "../Phaser/Components/SoundMeter";
|
||||||
import {onDestroy} from "svelte";
|
import {onDestroy} from "svelte";
|
||||||
|
|
||||||
export let stream: MediaStream;
|
export let stream: MediaStream|null;
|
||||||
let volume = 0;
|
let volume = 0;
|
||||||
|
|
||||||
console.log('stream', stream);
|
const NB_BARS = 5;
|
||||||
|
|
||||||
if (stream.getAudioTracks().length > 0) {
|
let timeout;
|
||||||
const soundMeter = new SoundMeter();
|
const soundMeter = new SoundMeter();
|
||||||
|
|
||||||
|
$: {
|
||||||
|
if (stream && stream.getAudioTracks().length > 0) {
|
||||||
soundMeter.connectToSource(stream, new AudioContext());
|
soundMeter.connectToSource(stream, new AudioContext());
|
||||||
|
|
||||||
const timeout = setInterval(() => {
|
if (timeout) {
|
||||||
|
clearInterval(timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
timeout = setInterval(() => {
|
||||||
try{
|
try{
|
||||||
volume = parseInt((soundMeter.getVolume() / 10).toFixed(0));
|
volume = parseInt((soundMeter.getVolume() / 100 * NB_BARS).toFixed(0));
|
||||||
console.log(volume);
|
//console.log(volume);
|
||||||
}catch(err){
|
}catch(err){
|
||||||
|
|
||||||
}
|
}
|
||||||
}, 100);
|
}, 100);
|
||||||
|
|
||||||
onDestroy(() => {
|
|
||||||
clearInterval(timeout);
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onDestroy(() => {
|
||||||
|
soundMeter.stop();
|
||||||
|
if (timeout) {
|
||||||
|
clearInterval(timeout);
|
||||||
|
}
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
@ -27,6 +27,10 @@ export class SoundMeter {
|
|||||||
|
|
||||||
public connectToSource(stream: MediaStream, context: AudioContext): void
|
public connectToSource(stream: MediaStream, context: AudioContext): void
|
||||||
{
|
{
|
||||||
|
if (this.source !== undefined) {
|
||||||
|
this.stop();
|
||||||
|
}
|
||||||
|
|
||||||
this.init(context);
|
this.init(context);
|
||||||
|
|
||||||
this.source = this.context?.createMediaStreamSource(stream);
|
this.source = this.context?.createMediaStreamSource(stream);
|
||||||
|
@ -1,44 +0,0 @@
|
|||||||
import Container = Phaser.GameObjects.Container;
|
|
||||||
import type {Scene} from "phaser";
|
|
||||||
import GameObject = Phaser.GameObjects.GameObject;
|
|
||||||
import Rectangle = Phaser.GameObjects.Rectangle;
|
|
||||||
|
|
||||||
|
|
||||||
export class SoundMeterSprite extends Container {
|
|
||||||
private rectangles: Rectangle[] = new Array<Rectangle>();
|
|
||||||
private static readonly NB_BARS = 20;
|
|
||||||
|
|
||||||
constructor(scene: Scene, x?: number, y?: number, children?: GameObject[]) {
|
|
||||||
super(scene, x, y, children);
|
|
||||||
|
|
||||||
for (let i = 0; i < SoundMeterSprite.NB_BARS; i++) {
|
|
||||||
const rectangle = new Rectangle(scene, i * 13, 0, 10, 20, (Math.round(255 - i * 255 / SoundMeterSprite.NB_BARS) << 8) + (Math.round(i * 255 / SoundMeterSprite.NB_BARS) << 16));
|
|
||||||
this.add(rectangle);
|
|
||||||
this.rectangles.push(rectangle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A number between 0 and 100
|
|
||||||
*
|
|
||||||
* @param volume
|
|
||||||
*/
|
|
||||||
public setVolume(volume: number): void {
|
|
||||||
|
|
||||||
const normalizedVolume = volume / 100 * SoundMeterSprite.NB_BARS;
|
|
||||||
for (let i = 0; i < SoundMeterSprite.NB_BARS; i++) {
|
|
||||||
if (normalizedVolume < i) {
|
|
||||||
this.rectangles[i].alpha = 0.5;
|
|
||||||
} else {
|
|
||||||
this.rectangles[i].alpha = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public getWidth(): number {
|
|
||||||
return SoundMeterSprite.NB_BARS * 13;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -3,7 +3,6 @@ import {TextField} from "../Components/TextField";
|
|||||||
import Image = Phaser.GameObjects.Image;
|
import Image = Phaser.GameObjects.Image;
|
||||||
import {mediaManager} from "../../WebRtc/MediaManager";
|
import {mediaManager} from "../../WebRtc/MediaManager";
|
||||||
import {SoundMeter} from "../Components/SoundMeter";
|
import {SoundMeter} from "../Components/SoundMeter";
|
||||||
import {SoundMeterSprite} from "../Components/SoundMeterSprite";
|
|
||||||
import {HtmlUtils} from "../../WebRtc/HtmlUtils";
|
import {HtmlUtils} from "../../WebRtc/HtmlUtils";
|
||||||
import {touchScreenManager} from "../../Touch/TouchScreenManager";
|
import {touchScreenManager} from "../../Touch/TouchScreenManager";
|
||||||
import {PinchManager} from "../UserInput/PinchManager";
|
import {PinchManager} from "../UserInput/PinchManager";
|
||||||
@ -11,280 +10,41 @@ import Zone = Phaser.GameObjects.Zone;
|
|||||||
import { MenuScene } from "../Menu/MenuScene";
|
import { MenuScene } from "../Menu/MenuScene";
|
||||||
import {ResizableScene} from "./ResizableScene";
|
import {ResizableScene} from "./ResizableScene";
|
||||||
import {
|
import {
|
||||||
audioConstraintStore,
|
|
||||||
localStreamStore,
|
|
||||||
enableCameraSceneVisibilityStore,
|
enableCameraSceneVisibilityStore,
|
||||||
mediaStreamConstraintsStore,
|
|
||||||
videoConstraintStore
|
|
||||||
} from "../../Stores/MediaStore";
|
} from "../../Stores/MediaStore";
|
||||||
import type {Unsubscriber} from "svelte/store";
|
|
||||||
|
|
||||||
export const EnableCameraSceneName = "EnableCameraScene";
|
export const EnableCameraSceneName = "EnableCameraScene";
|
||||||
enum LoginTextures {
|
|
||||||
playButton = "play_button",
|
|
||||||
icon = "icon",
|
|
||||||
mainFont = "main_font",
|
|
||||||
arrowRight = "arrow_right",
|
|
||||||
arrowUp = "arrow_up"
|
|
||||||
}
|
|
||||||
|
|
||||||
export class EnableCameraScene extends ResizableScene {
|
export class EnableCameraScene extends ResizableScene {
|
||||||
private textField!: TextField;
|
|
||||||
private cameraNameField!: TextField;
|
|
||||||
private arrowLeft!: Image;
|
|
||||||
private arrowRight!: Image;
|
|
||||||
private arrowDown!: Image;
|
|
||||||
private arrowUp!: Image;
|
|
||||||
private microphonesList: MediaDeviceInfo[] = new Array<MediaDeviceInfo>();
|
|
||||||
private camerasList: MediaDeviceInfo[] = new Array<MediaDeviceInfo>();
|
|
||||||
private cameraSelected: number = 0;
|
|
||||||
private microphoneSelected: number = 0;
|
|
||||||
private soundMeter: SoundMeter;
|
|
||||||
private soundMeterSprite!: SoundMeterSprite;
|
|
||||||
private microphoneNameField!: TextField;
|
|
||||||
|
|
||||||
private mobileTapZone!: Zone;
|
|
||||||
private localStreamStoreUnsubscriber!: Unsubscriber;
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super({
|
super({
|
||||||
key: EnableCameraSceneName
|
key: EnableCameraSceneName
|
||||||
});
|
});
|
||||||
this.soundMeter = new SoundMeter();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
preload() {
|
preload() {
|
||||||
this.load.image(LoginTextures.playButton, "resources/objects/play_button.png");
|
|
||||||
this.load.image(LoginTextures.arrowRight, "resources/objects/arrow_right.png");
|
|
||||||
this.load.image(LoginTextures.arrowUp, "resources/objects/arrow_up.png");
|
|
||||||
// Note: arcade.png from the Phaser 3 examples at: https://github.com/photonstorm/phaser3-examples/tree/master/public/assets/fonts/bitmap
|
|
||||||
this.load.bitmapFont(LoginTextures.mainFont, 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
create() {
|
create() {
|
||||||
if (touchScreenManager.supportTouchScreen) {
|
|
||||||
new PinchManager(this);
|
|
||||||
}
|
|
||||||
//this.scale.setZoom(ZOOM_LEVEL);
|
|
||||||
//Phaser.Display.Align.In.BottomCenter(this.pressReturnField, zone);
|
|
||||||
|
|
||||||
/* FIX ME */
|
|
||||||
this.textField = new TextField(this, this.scale.width / 2, 20, '');
|
|
||||||
|
|
||||||
// For mobile purposes - we need a big enough touchable area.
|
|
||||||
this.mobileTapZone = this.add.zone(this.scale.width / 2,this.scale.height - 30,200,50)
|
|
||||||
.setInteractive().on("pointerdown", () => {
|
|
||||||
this.login();
|
|
||||||
});
|
|
||||||
|
|
||||||
this.cameraNameField = new TextField(this, this.game.renderer.width / 2, this.game.renderer.height - 60, '');
|
|
||||||
|
|
||||||
this.microphoneNameField = new TextField(this, this.game.renderer.width / 2, this.game.renderer.height - 40, '');
|
|
||||||
|
|
||||||
this.arrowRight = new Image(this, 0, 0, LoginTextures.arrowRight);
|
|
||||||
this.arrowRight.setVisible(false);
|
|
||||||
this.arrowRight.setInteractive().on('pointerdown', this.nextCam.bind(this));
|
|
||||||
this.add.existing(this.arrowRight);
|
|
||||||
|
|
||||||
this.arrowLeft = new Image(this, 0, 0, LoginTextures.arrowRight);
|
|
||||||
this.arrowLeft.setVisible(false);
|
|
||||||
this.arrowLeft.flipX = true;
|
|
||||||
this.arrowLeft.setInteractive().on('pointerdown', this.previousCam.bind(this));
|
|
||||||
this.add.existing(this.arrowLeft);
|
|
||||||
|
|
||||||
this.arrowUp = new Image(this, 0, 0, LoginTextures.arrowUp);
|
|
||||||
this.arrowUp.setVisible(false);
|
|
||||||
this.arrowUp.setInteractive().on('pointerdown', this.previousMic.bind(this));
|
|
||||||
this.add.existing(this.arrowUp);
|
|
||||||
|
|
||||||
this.arrowDown = new Image(this, 0, 0, LoginTextures.arrowUp);
|
|
||||||
this.arrowDown.setVisible(false);
|
|
||||||
this.arrowDown.flipY = true;
|
|
||||||
this.arrowDown.setInteractive().on('pointerdown', this.nextMic.bind(this));
|
|
||||||
this.add.existing(this.arrowDown);
|
|
||||||
|
|
||||||
this.input.keyboard.on('keyup-ENTER', () => {
|
this.input.keyboard.on('keyup-ENTER', () => {
|
||||||
this.login();
|
this.login();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.localStreamStoreUnsubscriber = localStreamStore.subscribe((result) => {
|
|
||||||
if (result.type === 'error') {
|
|
||||||
// TODO: we could handle the error in a specific way on the EnableCameraScene page.
|
|
||||||
// TODO: we could handle the error in a specific way on the EnableCameraScene page.
|
|
||||||
return;
|
|
||||||
//throw result.error;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.getDevices();
|
|
||||||
if (result.stream !== null) {
|
|
||||||
this.setupStream(result.stream);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
/*const mediaPromise = mediaManager.getCamera();
|
|
||||||
mediaPromise.then(this.getDevices.bind(this));
|
|
||||||
mediaPromise.then(this.setupStream.bind(this));*/
|
|
||||||
|
|
||||||
this.input.keyboard.on('keydown-RIGHT', this.nextCam.bind(this));
|
|
||||||
this.input.keyboard.on('keydown-LEFT', this.previousCam.bind(this));
|
|
||||||
this.input.keyboard.on('keydown-DOWN', this.nextMic.bind(this));
|
|
||||||
this.input.keyboard.on('keydown-UP', this.previousMic.bind(this));
|
|
||||||
|
|
||||||
this.soundMeterSprite = new SoundMeterSprite(this, 50, 50);
|
|
||||||
this.soundMeterSprite.setVisible(false);
|
|
||||||
this.add.existing(this.soundMeterSprite);
|
|
||||||
|
|
||||||
enableCameraSceneVisibilityStore.showEnableCameraScene();
|
enableCameraSceneVisibilityStore.showEnableCameraScene();
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
this.onResize();
|
|
||||||
}, 100);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private previousCam(): void {
|
|
||||||
if (this.cameraSelected === 0 || this.camerasList.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.cameraSelected--;
|
|
||||||
videoConstraintStore.setDeviceId(this.camerasList[this.cameraSelected].deviceId);
|
|
||||||
|
|
||||||
//mediaManager.setCamera(this.camerasList[this.cameraSelected].deviceId).then(this.setupStream.bind(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
private nextCam(): void {
|
|
||||||
if (this.cameraSelected === this.camerasList.length - 1 || this.camerasList.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.cameraSelected++;
|
|
||||||
videoConstraintStore.setDeviceId(this.camerasList[this.cameraSelected].deviceId);
|
|
||||||
|
|
||||||
// TODO: the change of camera should be OBSERVED (reactive)
|
|
||||||
//mediaManager.setCamera(this.camerasList[this.cameraSelected].deviceId).then(this.setupStream.bind(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
private previousMic(): void {
|
|
||||||
if (this.microphoneSelected === 0 || this.microphonesList.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.microphoneSelected--;
|
|
||||||
audioConstraintStore.setDeviceId(this.microphonesList[this.microphoneSelected].deviceId);
|
|
||||||
//mediaManager.setMicrophone(this.microphonesList[this.microphoneSelected].deviceId).then(this.setupStream.bind(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
private nextMic(): void {
|
|
||||||
if (this.microphoneSelected === this.microphonesList.length - 1 || this.microphonesList.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.microphoneSelected++;
|
|
||||||
audioConstraintStore.setDeviceId(this.microphonesList[this.microphoneSelected].deviceId);
|
|
||||||
// TODO: the change of camera should be OBSERVED (reactive)
|
|
||||||
//mediaManager.setMicrophone(this.microphonesList[this.microphoneSelected].deviceId).then(this.setupStream.bind(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function called each time a camera is changed
|
|
||||||
*/
|
|
||||||
private setupStream(stream: MediaStream): void {
|
|
||||||
this.soundMeter.connectToSource(stream, new window.AudioContext());
|
|
||||||
this.soundMeterSprite.setVisible(true);
|
|
||||||
|
|
||||||
this.updateWebCamName();
|
|
||||||
}
|
|
||||||
|
|
||||||
private updateWebCamName(): void {
|
|
||||||
if (this.camerasList.length > 1) {
|
|
||||||
let label = this.camerasList[this.cameraSelected].label;
|
|
||||||
// remove text in parenthesis
|
|
||||||
label = label.replace(/\([^()]*\)/g, '').trim();
|
|
||||||
// remove accents
|
|
||||||
label = label.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
|
|
||||||
this.cameraNameField.text = label;
|
|
||||||
|
|
||||||
this.arrowRight.setVisible(this.cameraSelected < this.camerasList.length - 1);
|
|
||||||
this.arrowLeft.setVisible(this.cameraSelected > 0);
|
|
||||||
}
|
|
||||||
if (this.microphonesList.length > 1) {
|
|
||||||
let label = this.microphonesList[this.microphoneSelected].label;
|
|
||||||
// remove text in parenthesis
|
|
||||||
label = label.replace(/\([^()]*\)/g, '').trim();
|
|
||||||
// remove accents
|
|
||||||
label = label.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
|
|
||||||
|
|
||||||
this.microphoneNameField.text = label;
|
|
||||||
|
|
||||||
this.arrowDown.setVisible(this.microphoneSelected < this.microphonesList.length - 1);
|
|
||||||
this.arrowUp.setVisible(this.microphoneSelected > 0);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public onResize(): void {
|
public onResize(): void {
|
||||||
let div = HtmlUtils.getElementByIdOrFail<HTMLVideoElement>('myCamVideoSetup');
|
|
||||||
let bounds = div.getBoundingClientRect();
|
|
||||||
if (!div.srcObject) {
|
|
||||||
div = HtmlUtils.getElementByIdOrFail<HTMLVideoElement>('webRtcSetup');
|
|
||||||
bounds = div.getBoundingClientRect();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.textField.x = this.game.renderer.width / 2;
|
|
||||||
this.mobileTapZone.x = this.game.renderer.width / 2;
|
|
||||||
this.cameraNameField.x = this.game.renderer.width / 2;
|
|
||||||
this.microphoneNameField.x = this.game.renderer.width / 2;
|
|
||||||
|
|
||||||
this.cameraNameField.y = bounds.top / this.scale.zoom - 8;
|
|
||||||
|
|
||||||
this.soundMeterSprite.x = this.game.renderer.width / 2 - this.soundMeterSprite.getWidth() / 2;
|
|
||||||
this.soundMeterSprite.y = bounds.bottom / this.scale.zoom + 16;
|
|
||||||
|
|
||||||
this.microphoneNameField.y = this.soundMeterSprite.y + 22;
|
|
||||||
|
|
||||||
this.arrowRight.x = bounds.right / this.scale.zoom + 16;
|
|
||||||
this.arrowRight.y = (bounds.top + bounds.height / 2) / this.scale.zoom;
|
|
||||||
|
|
||||||
this.arrowLeft.x = bounds.left / this.scale.zoom - 16;
|
|
||||||
this.arrowLeft.y = (bounds.top + bounds.height / 2) / this.scale.zoom;
|
|
||||||
|
|
||||||
this.arrowDown.x = this.microphoneNameField.x + this.microphoneNameField.width / 2 + 16;
|
|
||||||
this.arrowDown.y = this.microphoneNameField.y;
|
|
||||||
|
|
||||||
this.arrowUp.x = this.microphoneNameField.x - this.microphoneNameField.width / 2 - 16;
|
|
||||||
this.arrowUp.y = this.microphoneNameField.y;
|
|
||||||
|
|
||||||
/*const actionBtn = document.querySelector<HTMLDivElement>('#enableCameraScene .action');
|
|
||||||
if (actionBtn !== null) {
|
|
||||||
actionBtn.style.top = (this.scale.height - 65) + 'px';
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
update(time: number, delta: number): void {
|
update(time: number, delta: number): void {
|
||||||
this.soundMeterSprite.setVolume(this.soundMeter.getVolume());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public login(): void {
|
public login(): void {
|
||||||
HtmlUtils.getElementByIdOrFail<HTMLDivElement>('webRtcSetup').style.display = 'none';
|
|
||||||
this.soundMeter.stop();
|
|
||||||
|
|
||||||
enableCameraSceneVisibilityStore.hideEnableCameraScene();
|
enableCameraSceneVisibilityStore.hideEnableCameraScene();
|
||||||
this.localStreamStoreUnsubscriber();
|
|
||||||
//mediaManager.stopCamera();
|
|
||||||
//mediaManager.stopMicrophone();
|
|
||||||
|
|
||||||
this.scene.sleep(EnableCameraSceneName);
|
this.scene.sleep(EnableCameraSceneName);
|
||||||
gameManager.goToStartingMap(this.scene);
|
gameManager.goToStartingMap(this.scene);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getDevices() {
|
|
||||||
// TODO: switch this in a store.
|
|
||||||
const mediaDeviceInfos = await navigator.mediaDevices.enumerateDevices();
|
|
||||||
this.microphonesList = [];
|
|
||||||
this.camerasList = [];
|
|
||||||
for (const mediaDeviceInfo of mediaDeviceInfos) {
|
|
||||||
if (mediaDeviceInfo.kind === 'audioinput') {
|
|
||||||
this.microphonesList.push(mediaDeviceInfo);
|
|
||||||
} else if (mediaDeviceInfo.kind === 'videoinput') {
|
|
||||||
this.camerasList.push(mediaDeviceInfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.updateWebCamName();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -508,3 +508,47 @@ export const obtainedMediaConstraintStore = derived(localStreamStore, ($localStr
|
|||||||
return $localStreamStore.constraints;
|
return $localStreamStore.constraints;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Device list
|
||||||
|
*/
|
||||||
|
export const deviceListStore = readable<MediaDeviceInfo[]>([], function start(set) {
|
||||||
|
let deviceListCanBeQueried = false;
|
||||||
|
|
||||||
|
const queryDeviceList = () => {
|
||||||
|
// Note: so far, we are ignoring any failures.
|
||||||
|
navigator.mediaDevices.enumerateDevices().then((mediaDeviceInfos) => {
|
||||||
|
set(mediaDeviceInfos);
|
||||||
|
}).catch((e) => {
|
||||||
|
console.error(e);
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const unsubscribe = localStreamStore.subscribe((streamResult) => {
|
||||||
|
if (streamResult.type === "success" && streamResult.stream !== null) {
|
||||||
|
if (deviceListCanBeQueried === false) {
|
||||||
|
queryDeviceList();
|
||||||
|
deviceListCanBeQueried = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (navigator.mediaDevices) {
|
||||||
|
navigator.mediaDevices.addEventListener('devicechange', queryDeviceList);
|
||||||
|
}
|
||||||
|
|
||||||
|
return function stop() {
|
||||||
|
unsubscribe();
|
||||||
|
if (navigator.mediaDevices) {
|
||||||
|
navigator.mediaDevices.removeEventListener('devicechange', queryDeviceList);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
export const cameraListStore = derived(deviceListStore, ($deviceListStore) => {
|
||||||
|
return $deviceListStore.filter(device => device.kind === 'videoinput');
|
||||||
|
});
|
||||||
|
|
||||||
|
export const microphoneListStore = derived(deviceListStore, ($deviceListStore) => {
|
||||||
|
return $deviceListStore.filter(device => device.kind === 'audioinput');
|
||||||
|
});
|
||||||
|
@ -126,7 +126,7 @@ export const screenSharingLocalStreamStore = derived<Readable<MediaStreamConstra
|
|||||||
let currentStreamPromise: Promise<MediaStream>;
|
let currentStreamPromise: Promise<MediaStream>;
|
||||||
if (navigator.getDisplayMedia) {
|
if (navigator.getDisplayMedia) {
|
||||||
currentStreamPromise = navigator.getDisplayMedia({constraints});
|
currentStreamPromise = navigator.getDisplayMedia({constraints});
|
||||||
} else if (navigator.mediaDevices.getDisplayMedia) {
|
} else if (navigator.mediaDevices && navigator.mediaDevices.getDisplayMedia) {
|
||||||
currentStreamPromise = navigator.mediaDevices.getDisplayMedia({constraints});
|
currentStreamPromise = navigator.mediaDevices.getDisplayMedia({constraints});
|
||||||
} else {
|
} else {
|
||||||
stopScreenSharing();
|
stopScreenSharing();
|
||||||
@ -183,7 +183,7 @@ export const screenSharingLocalStreamStore = derived<Readable<MediaStreamConstra
|
|||||||
* A store containing whether the screen sharing button should be displayed or hidden.
|
* A store containing whether the screen sharing button should be displayed or hidden.
|
||||||
*/
|
*/
|
||||||
export const screenSharingAvailableStore = derived(peerStore, ($peerStore, set) => {
|
export const screenSharingAvailableStore = derived(peerStore, ($peerStore, set) => {
|
||||||
if (!navigator.getDisplayMedia && !navigator.mediaDevices.getDisplayMedia) {
|
if (!navigator.getDisplayMedia && (!navigator.mediaDevices || !navigator.mediaDevices.getDisplayMedia)) {
|
||||||
set(false);
|
set(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,15 @@ module.exports = {
|
|||||||
preprocess: sveltePreprocess({
|
preprocess: sveltePreprocess({
|
||||||
scss: true,
|
scss: true,
|
||||||
sass: true,
|
sass: true,
|
||||||
})
|
}),
|
||||||
|
onwarn: function (warning: { code: string }, handleWarning: (warning: { code: string }) => void) {
|
||||||
|
// See https://github.com/sveltejs/svelte/issues/4946#issuecomment-662168782
|
||||||
|
|
||||||
|
if (warning.code === 'a11y-no-onchange') { return }
|
||||||
|
|
||||||
|
// process as usual
|
||||||
|
handleWarning(warning);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user