From bf182c65ad2b6c80814772f5c05dcc89e8a681ce Mon Sep 17 00:00:00 2001 From: Tamius Han Date: Tue, 22 Oct 2024 23:43:25 +0200 Subject: [PATCH] Aard should be nominally complete. Probably. --- src/ext/lib/aard/Aard.ts | 135 +++++++++++++----- .../interfaces/aard-test-results.interface.ts | 7 +- src/ext/lib/video-data/VideoData.ts | 27 ---- 3 files changed, 103 insertions(+), 66 deletions(-) diff --git a/src/ext/lib/aard/Aard.ts b/src/ext/lib/aard/Aard.ts index b3280d0..1925166 100644 --- a/src/ext/lib/aard/Aard.ts +++ b/src/ext/lib/aard/Aard.ts @@ -8,7 +8,7 @@ import { GlCanvas } from './gl/GlCanvas'; import { AardCanvasStore } from './interfaces/aard-canvas-store.interface'; import { AardDetectionSample, generateSampleArray } from './interfaces/aard-detection-sample.interface'; import { AardStatus, initAardStatus } from './interfaces/aard-status.interface'; -import { AardTestResults, initAardTestResults } from './interfaces/aard-test-results.interface'; +import { AardTestResults, initAardTestResults, resetAardTestResults } from './interfaces/aard-test-results.interface'; import { AardTimers, initAardTimers } from './interfaces/aard-timers.interface'; @@ -214,7 +214,7 @@ class Aard { //#region configuration parameters private logger: Logger; - private conf: VideoData; + private videoData: VideoData; private settings: Settings; private eventBus: EventBus; private arid: string; @@ -256,7 +256,7 @@ class Aard { //#region lifecycle constructor(videoData: VideoData){ this.logger = videoData.logger; - this.conf = videoData; + this.videoData = videoData; this.video = videoData.video; this.settings = videoData.settings; this.eventBus = videoData.eventBus; @@ -284,6 +284,10 @@ class Aard { } } + /** + * Initializes Aard with default values and starts autodetection loop. + * This method should only ever be called from constructor. + */ private init() { this.canvasStore = { main: new GlCanvas(new GlCanvas(this.settings.active.arDetect.canvasDimensions.sampleCanvas)), @@ -300,16 +304,17 @@ class Aard { ), }; - this.start(); } - - //#endregion + + /** + * Starts autodetection loop. + */ start() { - if (this.conf.resizer.lastAr.type === AspectRatioType.AutomaticUpdate) { + if (this.videoData.resizer.lastAr.type === AspectRatioType.AutomaticUpdate) { // ensure first autodetection will run in any case - this.conf.resizer.lastAr = {type: AspectRatioType.AutomaticUpdate, ratio: this.defaultAr}; + this.videoData.resizer.lastAr = {type: AspectRatioType.AutomaticUpdate, ratio: this.defaultAr}; } if (this.animationFrame) { @@ -318,15 +323,63 @@ class Aard { this.status.aardActive = true; this.animationFrame = window.requestAnimationFrame( (ts: DOMHighResTimeStamp) => this.onAnimationFrame(ts)); - // this.logger.log('info', 'debug', `"%c[ArDetect::startLoop] <@${this.arid}> AARD loop started.`, _ard_console_start); + } + + /** + * Runs autodetection ONCE. + * If autodetection loop is running, this will also stop autodetection loop. + */ + step() { + this.stop(); + this.main(); + } + + /** + * Stops autodetection. + */ + stop() { + if (this.animationFrame) { + window.cancelAnimationFrame(this.animationFrame); + } + } + + //#region animationFrame, scheduling, and other shit + /** + * Checks whether conditions for granting a frame check are fulfilled + * @returns + */ + private canTriggerFrameCheck() { + // if (this._paused || this._halted || this._exited) { + // return false; + // } + + // if video was paused & we know that we already checked that frame, + // we will not check it again. + const videoState = this.getVideoPlaybackState(); + + if (videoState !== VideoPlaybackState.Playing) { + if (this.status.lastVideoStatus === videoState) { + return false; + } + } + this.status.lastVideoStatus = videoState; + + if (Date.now() < this.timers.nextFrameCheckTime) { + return false; + } + + this.timers.nextFrameCheckTime = Date.now() + this.settings.active.arDetect.timers.playing; + return true; } private onAnimationFrame(ts: DOMHighResTimeStamp) { if (this.canTriggerFrameCheck()) { + resetAardTestResults(this.testResults); this.main(); } this.animationFrame = window.requestAnimationFrame( (ts: DOMHighResTimeStamp) => this.onAnimationFrame(ts)); } + //#endregion /** * Main loop for scanning aspect ratio changes @@ -390,42 +443,33 @@ class Aard { } while (false); + // TODO: subtitle check goes here. + // Note that subtitle check should reset aspect ratio outright, regardless of what other tests revealed. + // Also note that subtitle check should run on newest aspect ratio data, rather than lag one frame behind + // But implementation details are something for future Tam to figure out + + // if detection is uncertain, we don't do anything at all + if (this.testResults.aspectRatioUncertain) { + return; + } + // TODO: emit debug values if debugging is enabled this.testResults.isFinished = true; + + // if edge width changed, emit update event. + if (this.testResults.aspectRatioUpdated) { + this.videoData.resizer.updateAr({ + type: AspectRatioType.AutomaticUpdate, + ratio: this.getAr(), + offset: this.testResults.letterboxOffset + }); + } } catch (e) { console.warn('[Ultrawidify] Aspect ratio autodetection crashed for some reason.\n\nsome reason:', e); + this.videoData.resizer.setAr({type: AspectRatioType.AutomaticUpdate, ratio: this.defaultAr}); } } - /** - * Checks whether conditions for granting a frame check are fulfilled - * @returns - */ - private canTriggerFrameCheck() { - // if (this._paused || this._halted || this._exited) { - // return false; - // } - - // if video was paused & we know that we already checked that frame, - // we will not check it again. - const videoState = this.getVideoPlaybackState(); - - if (videoState !== VideoPlaybackState.Playing) { - if (this.status.lastVideoStatus === videoState) { - return false; - } - } - this.status.lastVideoStatus = videoState; - - if (Date.now() < this.timers.nextFrameCheckTime) { - return false; - } - - this.timers.nextFrameCheckTime = Date.now() + this.settings.active.arDetect.timers.playing; - return true; - } - - private getVideoPlaybackState(): VideoPlaybackState { try { if (this.video.ended) { @@ -1568,7 +1612,22 @@ class Aard { this.testResults.aspectRatioUncertain = false; this.testResults.letterboxWidth = candidateAvg; this.testResults.letterboxOffset = diff; + this.testResults.aspectRatioUpdated = true; } + + /** + * Calculates video's current aspect ratio based on data in testResults. + * @returns + */ + private getAr() { + const fileAr = this.video.videoWidth / this.video.videoHeight; + const canvasAr = this.canvasStore.main.width / this.canvasStore.main.height; + + const compensatedWidth = fileAr === canvasAr ? this.canvasStore.main.width : this.canvasStore.main.width * fileAr; + + return compensatedWidth / (this.canvasStore.main.height - (this.testResults.letterboxWidth * 2)); + } + //#endregion } diff --git a/src/ext/lib/aard/interfaces/aard-test-results.interface.ts b/src/ext/lib/aard/interfaces/aard-test-results.interface.ts index 3e97303..d77c02b 100644 --- a/src/ext/lib/aard/interfaces/aard-test-results.interface.ts +++ b/src/ext/lib/aard/interfaces/aard-test-results.interface.ts @@ -28,6 +28,7 @@ export interface AardTestResults { bottomCandidateQuality: number, }, aspectRatioUncertain: boolean, + aspectRatioUpdated: boolean, letterboxWidth: number, letterboxOffset: number, logoDetected: [boolean, boolean, boolean, boolean] @@ -61,10 +62,11 @@ export function initAardTestResults(settings: AardSettings): AardTestResults { bottomCandidate: 0, bottomCandidateQuality: 0, }, + aspectRatioUncertain: false, + aspectRatioUpdated: false, letterboxWidth: 0, letterboxOffset: 0, logoDetected: [false, false, false, false] - } } @@ -77,4 +79,7 @@ export function resetAardTestResults(results: AardTestResults): void { results.guardLine.cornerViolations[1] = false; results.guardLine.cornerViolations[2] = false; results.guardLine.cornerViolations[3] = false; + results.letterboxWidth = 0; + results.letterboxOffset = 0; + results.aspectRatioUpdated = false; } diff --git a/src/ext/lib/video-data/VideoData.ts b/src/ext/lib/video-data/VideoData.ts index 8756ad4..f806734 100644 --- a/src/ext/lib/video-data/VideoData.ts +++ b/src/ext/lib/video-data/VideoData.ts @@ -709,39 +709,12 @@ class VideoData { //#endregion //#region shit that gets propagated to resizer and should be removed. Implement an event bus instead - - panHandler(event, forcePan?: boolean) { - if (this.invalid) { - return; - } - if(this.destroyed) { - // throw {error: 'VIDEO_DATA_DESTROYED', data: {videoData: this}}; - return; - } - if(!this.resizer) { - this.destroy(); - return; - } - this.resizer.panHandler(event, forcePan); - } - - setPanMode(mode) { - if (this.invalid) { - return; - } - this.resizer.setPanMode(mode); - } - restoreAr(){ if (this.invalid) { return; } this.resizer.restore(); } - - isPlaying() { - return this.video && this.video.currentTime > 0 && !this.video.paused && !this.video.ended; - } //#endregion checkVideoSizeChange(){