cat / goq
clone

turns a linux box into a private steam streaming appliance: plug in, tap your key, play from another computer

fix/portal-dist-prebuild
goq / portal / av-sync.mjs
av-sync.mjs 2.4K
1export function exactMediaTimestampMicros(value) {2 return Number.isSafeInteger(value) && value >= 0 ? value : null;3}45export function nextAvSyncEpoch(current) {6 if (!Number.isSafeInteger(current) || current < 0) {7 throw new TypeError('invalid A/V sync epoch');8 }9 return current === Number.MAX_SAFE_INTEGER ? 0 : current + 1;10}1112export function isCurrentAvSyncEpoch(value, current) {13 return Number.isSafeInteger(value) && value >= 0 && value === current;14}1516export function audioOutputTimelineFromStats(value) {17 const mediaEndPtsMicros = value?.renderedMediaEndPtsMicros;18 const outputContextTimeEndSeconds = value?.outputContextTimeEndSeconds;19 if (20 !Number.isFinite(mediaEndPtsMicros)21 || mediaEndPtsMicros < 022 || !Number.isFinite(outputContextTimeEndSeconds)23 || outputContextTimeEndSeconds < 024 ) return null;25 return { mediaEndPtsMicros, outputContextTimeEndSeconds };26}2728export function projectAudioMediaPtsMicros(29 timeline,30 outputTimestamp,31 presentationTimeMs,32) {33 if (34 !timeline35 || !Number.isFinite(timeline.mediaEndPtsMicros)36 || timeline.mediaEndPtsMicros < 037 || !Number.isFinite(timeline.outputContextTimeEndSeconds)38 || timeline.outputContextTimeEndSeconds < 039 || !Number.isFinite(outputTimestamp?.contextTime)40 || outputTimestamp.contextTime < 041 || !Number.isFinite(outputTimestamp?.performanceTime)42 || outputTimestamp.performanceTime < 043 || (outputTimestamp.contextTime === 0 && outputTimestamp.performanceTime === 0)44 || !Number.isFinite(presentationTimeMs)45 ) return null;4647 const contextTimeAtPresentation = outputTimestamp.contextTime48 + ((presentationTimeMs - outputTimestamp.performanceTime) / 1000);49 const projected = timeline.mediaEndPtsMicros50 + ((contextTimeAtPresentation - timeline.outputContextTimeEndSeconds) * 1_000_000);51 return Number.isFinite(projected) && projected >= 0 ? projected : null;52}5354// Positive means audio is ahead of video; negative means audio is behind.55export function audioMinusVideoSkewMs(audioPtsMicros, videoPtsMicros) {56 if (57 !Number.isFinite(audioPtsMicros)58 || audioPtsMicros < 059 || !Number.isFinite(videoPtsMicros)60 || videoPtsMicros < 061 ) return null;62 return (audioPtsMicros - videoPtsMicros) / 1000;63}6465export function formatSignedMilliseconds(value) {66 if (!Number.isFinite(value)) return null;67 const sign = value >= 0 ? '+' : '−';68 return `${sign}${Math.abs(value).toFixed(1)}`;69}