jenkins e035cadb4d
Some checks failed
Tests / Declarative: Post Actions failed: 42, skipped: 89, passed: 4273
lesavka: host no-upload receiver observer at dedicated HTTPS path
2026-10-02 17:02:43 -05:00

31 lines
1.6 KiB
JavaScript

// Metadata only. No frames or PCM are retained. These are OBSERVER metrics,
// never an automatically passing sync/freshness/artifact report.
export class VideoMetrics {
constructor() { this.last = null; this.intervals = []; this.callbacks = 0; this.unobserved = 0; this.resets = 0; }
observe(timeMs, presentedFrames) {
if (!Number.isFinite(timeMs) || !Number.isSafeInteger(presentedFrames) || presentedFrames < 0) return;
if (this.last) {
if (timeMs <= this.last.timeMs || presentedFrames <= this.last.presentedFrames) this.resets++;
else {
if (this.intervals.length < 15000) this.intervals.push(timeMs-this.last.timeMs);
this.unobserved += Math.max(0,presentedFrames-this.last.presentedFrames-1);
}
}
this.last = {timeMs,presentedFrames}; this.callbacks++;
}
summary() {
const sorted = [...this.intervals].sort((a,b)=>a-b);
return {callbacks:this.callbacks,unobservedPresentedFrames:this.unobserved,clockOrCounterResets:this.resets,
callbackIntervalP95Ms:sorted.length ? sorted[Math.ceil(sorted.length*.95)-1] : null,
callbackGapMaxMs:sorted.at(-1) ?? null,
note:"Browser presentation callbacks; gaps are not proof of transport loss or corrupted video."};
}
}
export function receipt(video,audio,settings,elapsedMs,reason) {
return {schema:"lesavka.rct-av-observer.v1",kind:"diagnostic-only",reason,elapsedMs,
settings,video:video.summary(),audio,
syncMeasured:false,freshnessMeasured:false,artifactsMeasured:false,
releaseEligible:false,recordedMedia:false};
}