cat / goq
clone

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

fix/release-manifest-language
goq / portal / video-format-transition.mjs
video-format-transition.mjs 2.2K
1function validFormat({ codec, width, height }) {2 if (codec !== 'h264') throw new TypeError('invalid video codec');3 if (!Number.isSafeInteger(width) || width <= 0 || !Number.isSafeInteger(height) || height <= 0) {4 throw new TypeError('invalid video dimensions');5 }6 return Object.freeze({ codec, width, height });7}89function sameFormat(left, right) {10 return left !== null11 && left.codec === right.codec12 && left.width === right.width13 && left.height === right.height;14}1516export class VideoFormatTransitionGuard {17 #format = null;18 #lastSequence = null;19 #epoch = 0;20 #revision = 0;2122 get format() { return this.#format; }23 get epoch() { return this.#epoch; }2425 reset() {26 this.#format = null;27 this.#lastSequence = null;28 this.#epoch = 0;29 this.#revision++;30 }3132 plan({ sequence, codec, width, height, keyframe, codecConfig, discontinuity }) {33 const format = validFormat({ codec, width, height });34 if (sequence !== null && sequence !== undefined35 && (!Number.isSafeInteger(sequence) || sequence < 0)) {36 throw new TypeError('invalid video sequence');37 }38 if (codecConfig && !keyframe) throw new Error('codec configuration requires a keyframe');39 if (sequence !== null && sequence !== undefined40 && this.#lastSequence !== null && sequence <= this.#lastSequence) {41 return Object.freeze({ action: 'drop-stale', epoch: this.#epoch });42 }4344 const reconfigure = !sameFormat(this.#format, format) || discontinuity;45 if (reconfigure && !(keyframe && codecConfig)) {46 return Object.freeze({ action: 'recover', epoch: this.#epoch });47 }48 return Object.freeze({49 action: 'accept',50 reconfigure,51 format,52 sequence: sequence ?? null,53 epoch: this.#epoch + (reconfigure ? 1 : 0),54 revision: this.#revision,55 });56 }5758 commit(plan) {59 if (plan?.action !== 'accept' || plan.revision !== this.#revision) {60 throw new Error('stale video format transition');61 }62 if (plan.reconfigure) this.#format = plan.format;63 if (plan.sequence !== null) this.#lastSequence = plan.sequence;64 this.#epoch = plan.epoch;65 this.#revision++;66 return Object.freeze({ format: this.#format, epoch: this.#epoch });67 }68}