import Button from '@material-ui/core/Button'; import Ajv from 'ajv'; import { useEffect, useState } from 'react'; import timeline, { slide } from '../timeline'; import * as timelineSchema from '../timeline.schema.json'; import ExitToAppRoundedIcon from '@material-ui/icons/ExitToAppRounded'; import PlayArrowRoundedIcon from '@material-ui/icons/PlayArrowRounded'; import SettingsRemoteRoundedIcon from '@material-ui/icons/SettingsRemoteRounded'; import SettingsRoundedIcon from '@material-ui/icons/SettingsRounded'; import CodeRoundedIcon from '@material-ui/icons/CodeRounded'; import MovieRoundedIcon from '@material-ui/icons/MovieRounded'; class TimedVideoPlayer { slide: number; timeline: timeline; precision: number; player: HTMLVideoElement; video: string; constructor(public framerate: number) { this.slide = -1; this.precision = 3; } timestampToFrame(timestamp: number): number { return Math.round((timestamp * 1e3) / (1e3 / this.framerate)); } frameToTimestamp(frame: number): number { return frame / this.framerate; } registerPlayer(player: HTMLVideoElement) { this.player = player; if (this.video) this.player.src = this.video; this.registerEventListeners(); } jumpToFrame(frame: number) { this.player.currentTime = this.frameToTimestamp(frame); } handleSlide(slide: slide) { this.jumpToFrame(slide.frame); this.player.pause(); } registerEventListeners() { if ( !this.video || !this.player || !this.timeline ) { return; } setInterval(() => { if (this.player.paused) return; var frame = this.timestampToFrame(this.player.currentTime); // debug document.getElementById('frame').innerText = frame.toString(); var slide = this.timeline.slides[this.slide]; if (!slide) return; if (frame >= slide.frame) { this.handleSlide(slide); } }, 1e3 / (this.precision * this.framerate)); } loadVideo(base64Video: string) { this.video = base64Video; if (this.player) this.player.src = this.video; this.registerEventListeners(); } loadSlides(jsonString: string) { try { var timeline = JSON.parse(jsonString); } catch (e) { console.log('invalid json object!' + e); return; } var ajv = new Ajv({ allErrors: true }); var validate = ajv.compile(timelineSchema); if (!validate(timeline)) { console.log('schema not passed!'); return; } this.timeline = timeline as timeline; this.registerEventListeners(); } next() { this.slide++; this.player.play(); } previous() { console.log('previous slide'); } } export default function Present() { useEffect(() => { setInterval(() => { document.getElementById('time').innerText = new Date().toLocaleTimeString(); }, 500); }, []); var player = new TimedVideoPlayer(60); useEffect(() => { var videoEL = document.getElementById('player') as HTMLVideoElement; player.registerPlayer(videoEL); /* videoEL.addEventListener('loadeddata', () => { */ /* console.log('initial load'); */ /* }); */ /* videoEL.addEventListener('canplaythrough', () => { */ /* console.log('full load') */ /* }); */ }, []); return