jenkins 9204773e7d av-test: publish bounded continuity watch and recent observations
Vendor tested Lesavka observer assets and numeric receipt schema from 6b97ceb1. Verified 65 JS tests, 10 service tests, kustomize render and Kubernetes client dry-run. No media upload, capture activation or changes to the homepage and private OCR.
2026-10-22 21:47:13 -05:00

77 lines
3.9 KiB
JavaScript

// Known synthetic signatures only. No PCM/frame retention or content upload.
import {PilotWindow} from './pilot-window.js';
export class VideoContinuity {
constructor(){this.frames=0;this.bad=0;this.duplicates=0;this.missing=0;this.ambiguous=0;this.previous=null;}
observe(rgba,timeMs) {
// Waiting for the known source is not a scored part of the test.
const reject=()=>{if(this.previous){this.frames++;this.bad++;}};
if(!rgba||rgba.length!==80||!Number.isFinite(timeMs)){reject();return;}
const bits=[];
for(let i=0;i<20;i++) {
const rgb=Array.from(rgba.slice(i*4,i*4+3));
if(rgb.some(x=>!Number.isFinite(x)||x<0||x>255)||Math.max(...rgb)-Math.min(...rgb)>35) {reject();return;}
const mean=rgb.reduce((a,b)=>a+b,0)/3;
if(mean>70&&mean<185){reject();return;}
bits.push(mean>=185?1:0);
}
const value=bits.slice(2,18).reduce((n,b)=>(n<<1)|b,0);
const parity=bits.slice(2,18).reduce((a,b)=>a^b,0);
if(bits[0]!==1||bits[1]!==0||bits[18]!==parity||bits[19]===parity||value>=16) {reject();return;}
this.frames++;
if(this.previous) {
const gap=timeMs-this.previous.time,delta=(value-this.previous.value+16)%16;
// Large time/sequence gaps can hide wraps. Unknown is never zero.
if(gap<=0||gap>100||delta>8)this.ambiguous++;
else if(delta===0)this.duplicates++;
else this.missing+=delta-1;
}
this.previous={time:timeMs,value};
}
summary() {
return {frames:this.frames,badMarkers:this.bad,ambiguousGaps:this.ambiguous,
duplicateFraction:this.frames&&!this.ambiguous?this.duplicates/this.frames:null,
missingFraction:this.frames&&!this.ambiguous?this.missing/(this.frames+this.missing):null};
}
}
export class AudioPilot {
constructor(rate) {
if(!Number.isFinite(rate)||rate<16000||rate>192000)throw new RangeError('sample rate');
this.rate=rate;this.size=Math.round(rate*.01);this.coeff=2*Math.cos(2*Math.PI*7000/rate);
this.windowMs=this.size/rate*1000;this.recent=new PilotWindow(this.windowMs);
this.q1=0;this.q2=0;this.count=0;this.windows=0;this.missing=0;this.pending=0;
this.maxGap=0;this.first=null;this.last=null;this.expected=null;this.discontinuities=0;
}
process(samples,frame) {
if(this.expected!==null&&frame!==this.expected) {
this.discontinuities++;this.q1=0;this.q2=0;this.count=0;
this.recent.interrupt();
}
this.expected=frame+samples.length;
for(let i=0;i<samples.length;i++) {
const x=samples[i];
if(!Number.isFinite(x)){this.discontinuities++;this.q1=0;this.q2=0;this.count=0;this.recent.interrupt();continue;}
const q=x+this.coeff*this.q1-this.q2;this.q2=this.q1;this.q1=q;
if(++this.count!==this.size)continue;
const power=this.q1**2+this.q2**2-this.coeff*this.q1*this.q2;
const present=2*Math.sqrt(Math.max(0,power))/this.size>=.008;
const now=(frame+i+1)/this.rate*1000;
if(present) {
if(this.first===null)this.first=now-this.size/this.rate*1000;
this.last=now;this.windows+=1+this.pending;this.missing+=this.pending;
this.maxGap=Math.max(this.maxGap,this.pending*this.windowMs);this.pending=0;
} else if(this.first!==null)this.pending++;
if(this.first!==null)this.recent.observe(present,now);
this.q1=0;this.q2=0;this.count=0;
}
}
summary(nowMs) {
return {windows:this.windows,firstMs:this.first,lastMs:this.last,
missingFraction:this.windows&&!this.discontinuities?this.missing/this.windows:null,
gapMaxMs:this.windows&&!this.discontinuities?this.maxGap:null,
trailingGapMs:this.pending*this.windowMs,discontinuities:this.discontinuities,
recent:this.recent.summary(nowMs)};
}
}