diff --git a/front/dist/index.tmpl.html b/front/dist/index.tmpl.html index 6264bbbb..112588e0 100644 --- a/front/dist/index.tmpl.html +++ b/front/dist/index.tmpl.html @@ -126,6 +126,15 @@ +
diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 9192e533..cd83a0a5 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -97,6 +97,7 @@ import {waScaleManager} from "../Services/WaScaleManager"; import {peerStore} from "../../Stores/PeerStore"; import {EmoteManager} from "./EmoteManager"; import { InteractiveLayer } from "../Map/InteractiveLayer"; +import {videoManager} from "../../WebRtc/VideoManager"; export interface GameSceneInitInterface { initPosition: PointInterface|null, @@ -721,6 +722,19 @@ export class GameScene extends DirtyScene implements CenterListener { } } + private playVideo(url: string|number|boolean|undefined, loop=false): void { + if (url === undefined) { + videoManager.unloadVideo(); + } else { + const realVideoPath = '' + url; + videoManager.loadVideo(realVideoPath); + + if (loop) { + videoManager.loop(); + } + } + } + private triggerOnMapLayerPropertyChange(){ this.gameMap.onPropertyChange('exitSceneUrl', (newValue, oldValue) => { if (newValue) this.onMapExit(newValue as string); @@ -866,6 +880,13 @@ ${escapedMessage} this.popUpElements.set(openPopupEvent.popupId, domElement); })); + this.gameMap.onPropertyChange('playVideo', (newValue, oldValue) => { + this.playVideo(newValue); + }); + + this.gameMap.onPropertyChange('playVideoLoop', (newValue, oldValue) => { + this.playVideo(newValue, true); + }); this.iframeSubscriptionList.push(iframeListener.closePopupStream.subscribe((closePopupEvent) => { const popUpElement = this.popUpElements.get(closePopupEvent.popupId); diff --git a/front/src/WebRtc/VideoManager.ts b/front/src/WebRtc/VideoManager.ts new file mode 100644 index 00000000..b0f7425f --- /dev/null +++ b/front/src/WebRtc/VideoManager.ts @@ -0,0 +1,159 @@ +import {HtmlUtils} from "./HtmlUtils"; +import {isUndefined} from "generic-type-guard"; + +enum videoStates { + closed = 0, + loading = 1, + playing = 2 +} + +const videoPlayerDivId = "videoplayer"; +const videoPlayerCtrlId = "videoplayerctrl"; +const animationTime = 500; + +class VideoManager { + private opened = videoStates.closed; + + private videoPlayerDiv: HTMLDivElement; + private videoPlayerCtrl: HTMLDivElement; + private videoPlayerElem: HTMLVideoElement | undefined; + + private volume = 1; + private muted = false; + private decreaseWhileTalking = true; + private volumeReduced = false; + + constructor() { + this.videoPlayerDiv = HtmlUtils.getElementByIdOrFail(videoPlayerDivId); + this.videoPlayerCtrl = HtmlUtils.getElementByIdOrFail(videoPlayerCtrlId); + + const storedVolume = localStorage.getItem('volume') + if (storedVolume === null) { + this.setVolume(1); + } else { + this.volume = parseFloat(storedVolume); + //HtmlUtils.getElementByIdOrFail('videoplayer_volume').value = storedVolume; + } + + //HtmlUtils.getElementByIdOrFail('videoplayer_volume').value = '' + this.volume; + } + + private close(): void { + this.videoPlayerCtrl.classList.remove('loading'); + this.videoPlayerCtrl.classList.add('hidden'); + this.opened = videoStates.closed; + } + + private load(): void { + this.videoPlayerCtrl.classList.remove('hidden'); + this.videoPlayerCtrl.classList.add('loading'); + this.opened = videoStates.loading; + } + + private open(): void { + this.videoPlayerCtrl.classList.remove('hidden', 'loading'); + this.opened = videoStates.playing; + } + + private changeVolume(talking = false): void { + if (!isUndefined(this.videoPlayerElem)) { + this.videoPlayerElem.volume = this.naturalVolume(talking && this.decreaseWhileTalking); + this.videoPlayerElem.muted = this.muted; + } + } + + private naturalVolume(makeSofter: boolean = false): number { + const volume = this.volume + const retVol = makeSofter && !this.volumeReduced ? Math.pow(volume * 0.5, 3) : volume + this.volumeReduced = makeSofter + return retVol; + } + + private setVolume(volume: number): void { + this.volume = volume; + localStorage.setItem('volume', '' + volume); + } + + + public loadVideo(url: string): void { + this.load(); + + /* Solution 1, remove whole video player */ + this.videoPlayerDiv.innerHTML = ''; // necessary, if switching from one video context to another! (else both streams would play simultaneously) + + this.videoPlayerElem = document.createElement('video'); + this.videoPlayerElem.id = 'videoplayerelem'; + this.videoPlayerElem.controls = true; + this.videoPlayerElem.preload = 'none'; + + const srcElem = document.createElement('source'); + srcElem.src = url; + + this.videoPlayerElem.append(srcElem); + + this.videoPlayerDiv.append(this.videoPlayerElem); + this.changeVolume(); + this.videoPlayerElem.play(); + + /*const muteElem = HtmlUtils.getElementByIdOrFail('videoplayer_mute'); + muteElem.onclick = (ev: Event)=> { + this.muted = !this.muted; + this.changeVolume(); + + if (this.muted) { + HtmlUtils.getElementByIdOrFail('videoplayer_volume_icon_playing').classList.add('muted'); + } else { + HtmlUtils.getElementByIdOrFail('videoplayer_volume_icon_playing').classList.remove('muted'); + } + } + + const volumeElem = HtmlUtils.getElementByIdOrFail('videoplayer_volume'); + volumeElem.oninput = (ev: Event)=> { + this.setVolume(parseFloat((ev.currentTarget).value)); + this.changeVolume(); + + (ev.currentTarget).blur(); + }*/ + + const decreaseElem = HtmlUtils.getElementByIdOrFail('videoplayer_decrease_while_talking'); + decreaseElem.oninput = (ev: Event)=> { + this.decreaseWhileTalking = (ev.currentTarget).checked; + this.changeVolume(); + } + + this.open(); + } + + public loop(): void { + if (this.videoPlayerElem !== undefined) { + this.videoPlayerElem.loop = true; + } + } + + public unloadVideo(): void { + try { + const videoElem = HtmlUtils.getElementByIdOrFail('videoplayerelem'); + this.volume = videoElem.volume; + this.muted = videoElem.muted; + videoElem.pause(); + videoElem.loop = false; + videoElem.src = ""; + videoElem.innerHTML = ""; + videoElem.load(); + } catch (e) { + console.log('No video element loaded to unload'); + } + + this.close(); + } + + public decreaseVolume(): void { + this.changeVolume(true); + } + + public restoreVolume(): void { + this.changeVolume(false); + } +} + +export const videoManager = new VideoManager(); diff --git a/front/style/style.css b/front/style/style.css index bb6fe54a..3a5872ea 100644 --- a/front/style/style.css +++ b/front/style/style.css @@ -581,6 +581,30 @@ input[type=range]:focus::-ms-fill-upper { background: #FFFFFF; } +#videoplayerctrl { + position: fixed; + width: 25%; + top: 0; + right: 75%; + transform: translate(50%, 0); + padding: 0.3rem 0.5rem; + color: white; + transition: transform 0.5s; +} + +/* + * sollte eigentlich in den aspect-ratio teil .. + */ +#videoplayerctrl.loading { + transform: translate(50%, -90%); +} +#videoplayerctrl.hidden { + transform: translate(50%, -100%); +} + +#videoplayerelem { + width: 100%; +} .game-overlay { display: none;