const validStates = {
[PlayerState.LOADING]: [PlayerState.LOADED],
[PlayerState.LOADED]: [PlayerState.PLAYING],
- [PlayerState.PLAYING]: [PlayerState.PAUSE, PlayerState.ENDED],
+ [PlayerState.PLAYING]: [PlayerState.PAUSE, PlayerState.ENDED, PlayerState.BUFFERING],
[PlayerState.PAUSE]: [PlayerState.PLAYING, PlayerState.ENDED],
[PlayerState.ENDED]: [PlayerState.PLAYING],
- [PlayerState.BUFFERING]: [],
+ [PlayerState.BUFFERING]: [PlayerState.READY, PlayerState.PAUSE, PlayerState.PLAYING],
+ [PlayerState.READY]: [PlayerState.PLAYING],
[PlayerState.UNSTARTED]: [],
};
public options: MultiVideoPlayerOptions;
public $container: Element | null;
public videoPlayers: VideoPlayer[] = [];
+ public state: PlayerState;
private readyCount: number;
- private state: PlayerState;
private currentTime: number;
constructor(options: MultiVideoPlayerOptions) {
this.currentTime = 0;
}
+ public getDebugInfo() {
+ return {
+ state: this.state,
+ currentTime: this.currentTime,
+ videoPlayers: this.videoPlayers.map(vp => {
+ return {
+ id: vp.videoId,
+ state: vp.videoState,
+ currentTime: vp.getCurrentTime(),
+ duration: vp.getDuration,
+ isMain: vp.main,
+ isPlaying: vp.isPlaying,
+ };
+ }),
+ };
+ }
+
public mount() {
this.render();
this.addVideoPlayers(this.options.videoPlayers);
}
public async changeState(state: PlayerState, videoPlayer: VideoPlayer) {
+ // console.log("change global state", state)
if (state === PlayerState.BUFFERING) {
+ return this.pause()
+ // console.log("change state to BUFFERING")
+ // return this.pause();
// TODO: find out how to handle buffering
- return;
+ // return;
}
if (state === PlayerState.PLAYING) {
- console.log("set state : playing")
return this.play();
}
if (state === PlayerState.PAUSE) {
}
});
}
+ if (state === PlayerState.READY) {
+ // player has finished buffering and ready to start
+ // check if every player is ready
+
+ const allReady = this.videoPlayers.every(vp => {
+ return [PlayerState.READY, PlayerState.PLAYING, PlayerState.PAUSE].includes(vp.videoState);
+ });
+ if(allReady){
+ console.log("all ready, start playing")
+ await this.play();
+ }
+
+ }
if (state === PlayerState.UNSTARTED) {
if (this.state === PlayerState.ENDED) {
if (this.options.loop) {
}
private async moveStateTo(state: PlayerState, success?: (newState: PlayerState, oldState: PlayerState) => void, fail?: (newState: PlayerState, oldState: PlayerState) => void) {
+ if(state === this.state) return;
if (isValidState(this.state, state)) {
const oldState = this.state;
this.state = state;
public async onReady() {
this.readyCount++;
if (this.readyCount !== this.videoPlayers.length) return;
- console.log("READY2")
await this.moveStateTo(PlayerState.LOADED);
}
// build a list of video ids equal to multivideos length
const ids = ref(Array.from({ length: multivideos.length }, (_, i) => `video-${i}`))
+const debugInfo = ref({})
onMounted(() => {
syncVideoPlayer.mount()
+
+ setInterval(() => {
+ debugInfo.value = syncVideoPlayer.getDebugInfo()
+ }, 50)
// const mainVideo = document.getElementById("main-video").querySelector("video")
// mainVideo.addEventListener("loadeddata", () => {
// thumbnailsVideos!.style.height = `${thumbnailsVideoHeight}px`
// }
+
async function onPlay() {
await syncVideoPlayer.play()
}
const value = (e.target as HTMLInputElement).value
await syncVideoPlayer.timeTo(parseFloat(value))
}
+
+
</script>
<template>
</div>
<div class="controls">
+ MAIN STATE = {{ debugInfo.state }}
+ <ul>
+ <li v-for="player in debugInfo.videoPlayers" :key="player.id">
+ {{ player.id }} - {{ player.state }}
+ </li>
+ </ul>
+
<button @click="onAdd">Add video</button>
<button @click="onPlay">Play</button>
<button @click="onPause">Pause</button>
public controls: boolean;
public main: boolean;
public sound: boolean;
+ public canWait: boolean;
public videoPlayerArea: Element | null;
// public videoElement: HTMLVideoElement | null;
public videoState: PlayerState;
this.videoPlayerConfig = videoPlayerConfig;
this.main = this.videoPlayerConfig.main ?? false;
this.sound = this.videoPlayerConfig.sound ?? false;
+ this.canWait = true;
this.controls = this.videoPlayerConfig.controls ?? player.options.controls ?? true;
this.loop = player.options.loop ?? false;
this.videojs.on('timeupdate', this.onTimeUpdate.bind(this))
this.videojs.on('seeking', this.onSeeking.bind(this))
this.videojs.on('ended', this.onStateChange.bind(this, PlayerState.ENDED))
- this.videojs.on('waiting', this.onStateChange.bind(this, PlayerState.BUFFERING))
+
this.videojs.on('pause', this.onStateChange.bind(this, PlayerState.PAUSE))
this.videojs.on('play', this.onStateChange.bind(this, PlayerState.PLAYING))
+ // this.videojs.on('waiting', () => {
+ // // console.log("WAITING FOR BUFFERING", this.videoState, e)
+ // // if(this.isPlaying)
+
+ // this.onStateChange(PlayerState.BUFFERING)
+ // })
+ this.videojs.on('canplay', () => {
+ // console.log("CAN PLAY IS READTY")
+ // debugger;
+ if (![PlayerState.LOADING, PlayerState.LOADED].includes(this.player.state as PlayerState)) {
+ this.onStateChange(PlayerState.READY)
+ }
+ this.canWait = false
+ setTimeout(() => {
+ this.canWait = true
+
+ }, 1000)
+ })
+
+ this.videojs.on('waiting', () => {
+ if(!this.canWait)
+ return
+ if(this.videoElement!.readyState === 4)
+ return
+ console.log("BUFFERING...", this.videoElement!.readyState)
+ if (![PlayerState.LOADING, PlayerState.LOADED].includes(this.player.state as PlayerState)) {
+ this.onStateChange(PlayerState.BUFFERING)
+ }
+ })
+
+ // this.videojs.on('progress', (e:any) => {
+ // console.log('progress')
+ // console.log(e)
+ // this.onStateChange.bind(this, PlayerState.PLAYING)
+ // })
// this.videoElement.addEventListener('play', this.onStateChange.bind(this, PlayerState.PLAYING), false);
}
private onReady() {
- console.log("READY")
Logger.debug(this.videoId, '::[onReady]');
this.timeTo(0);
this.player.onReady();
}
private onStateChange(state: PlayerState) {
- console.log("on state change", state)
Logger.debug(this.videoId, '::[onStateChange]', state);
this.videoState = state;
const statesToPropagate = [
PlayerState.BUFFERING,
+ PlayerState.READY,
PlayerState.PLAYING,
PlayerState.PAUSE,
PlayerState.ENDED,
}
}
+ if (state === PlayerState.BUFFERING) {
+ this.isPlaying = false;
+ }
+
if (statesToPropagate.includes(state)) {
this.player.changeState(state, this);
}
}
private render() {
- console.log("render")
// if video area is defined, add video to area
this.videoPlayerArea = document.querySelector(this.videoPlayerConfig.id);
if (!this.videoPlayerArea) {
*/
public async play() {
- console.log("start playing !")
+ console.trace()
if (!this.videoElement) return;
if (this.videoState === PlayerState.PLAYING) return;
- console.log("play")
+
+
+ var isPlaying = this.videoElement.currentTime > 0 && !this.videoElement.paused && !this.videoElement.ended && this.videoElement.readyState > this.videoElement.HAVE_CURRENT_DATA;
+ if (isPlaying) {
+ console.log("already playing")
+ return
+ }
// debugger;
// const video = document.getElementById(this.videoElement.id)
// await video!.play()
+ console.log("play")
await this.videoElement.play().catch(error => console.log(error)); //.catch(noop);
}
public async pause() {
- console.log(this.videoElement)
- console.log(this.videoId)
if (!this.videoElement) return;
if (this.videoState === PlayerState.PAUSE) return;
await this.videoElement.pause();
public setControls(controls: boolean) {
if (!this.videoElement) return;
- console.log(this.videojs)
-
- console.log("set controls", controls)
this.videojs.controls(controls)
// this.videojs.player.userActive(false)
// this.videoElement.controls = controls;
}
public getPlayLength() {
- console.log("duration", this.getDuration(), this._startSeconds)
return this.getDuration() - this._startSeconds;
}
export enum PlayerState {
- UNSTARTED = 'UNSARTED',
+ UNSTARTED = 'UNSTARTED',
ENDED = 'ENDED',
PLAYING = 'PLAYING',
PAUSE = 'PAUSE',
BUFFERING = 'BUFFERING',
+ READY = 'READY', // ready to be played after buffering
LOADING = 'LOADING',
LOADED = 'LOADED',
}
\ No newline at end of file
import VideoPlayer from "./video/VideoPlayer";
type MultiVideoPlayer = {
+ state: PlayerState,
options: MultiVideoPlayerOptions,
$container: Element | null,
videoPlayers: VideoPlayer[],