Selkies

lib/util

Helpers shared by the streaming cores and the dashboard: a small FIFO queue, the human-readable labels for wire values, the decodability checks that keep a client from asking for a stream it cannot play, the route prefix and localStorage namespace every caller derives the same way, and whether the server answers at all before a page reloads into it.

Classes

Queue

Defined in: lib/util.js:17

A FIFO queue over an array.

Constructors

Constructor
new Queue(...elements): Queue;

Defined in: lib/util.js:19

Parameters
ParameterTypeDescription
...elementsany[]Initial items, enqueued in order.
Returns

Queue

Properties

items
items: any[];

Defined in: lib/util.js:21

Methods

enqueue()
enqueue(...elements): void;

Defined in: lib/util.js:27

Parameters
ParameterTypeDescription
...elementsany[]Items appended in order.
Returns

void

dequeue()
dequeue(count?): any;

Defined in: lib/util.js:36

Removes the oldest count items.

Parameters
ParameterTypeDefault valueDescription
count?number1How many items to drop.
Returns

any

The oldest of the removed items.

size()
size(): number;

Defined in: lib/util.js:41

Returns

number

isEmpty()
isEmpty(): boolean;

Defined in: lib/util.js:46

Returns

boolean

toArray()
toArray(): any[];

Defined in: lib/util.js:51

Returns

any[]

A copy of the items, oldest first.

remove()
remove(element): void;

Defined in: lib/util.js:56

Removes the first occurrence of element.

Parameters
ParameterType
elementany
Returns

void

find()
find(element): boolean;

Defined in: lib/util.js:62

Parameters
ParameterType
elementany
Returns

boolean

Whether element is queued.

clear()
clear(): void;

Defined in: lib/util.js:67

Drops every item.

Returns

void

Variables

DISPLAY_LABELS

const DISPLAY_LABELS: object;

Defined in: lib/util.js:82

Human-readable names for the wire values surfaced in UIs (transport modes, encoders, rate-control modes). The raw values are what the server APIs speak and stay untouched; unknown values fall through unchanged so new wire values render as-is. Locale-invariant technical terms, so they live here once rather than in every dashboard's translation dictionaries.

Type Declaration

websockets
websockets: string = "WebSockets";
webrtc
webrtc: string = "WebRTC";
h264enc
h264enc: string = "H.264";
h265enc
h265enc: string = "H.265";
vp8enc
vp8enc: string = "VP8";
vp9enc
vp9enc: string = "VP9";
av1enc
av1enc: string = "AV1";
h264enc-striped
"h264enc-striped": string = "H.264 (Striped)";
jpeg
jpeg: string = "JPEG (Striped)";
cbr
cbr: string = "CBR (Constant Bitrate)";
crf
crf: string = "CRF (Constant Quality)";
auto
auto: string = "Auto";
h264
h264: string = "H.264";
h265
h265: string = "H.265";
vp8
vp8: string = "VP8";
vp9
vp9: string = "VP9";
av1
av1: string = "AV1";
mjpeg
mjpeg: string = "MJPEG";

DECODER_PROBE_TIMEOUT_MS

const DECODER_PROBE_TIMEOUT_MS: 10000 = 10000;

Defined in: lib/util.js:117

How long an engine is given to answer a decoder probe, and a page's first stream decoder configuration before the core's no-output watchdog counts the wait. A decoder that stalls instead of answering must not hold the session up: the answer settles one setting, while a session waits on it to start. Measured answers are immediate, the first in a page costing a media stack its warm-up.


decoderSupportReady

const decoderSupportReady: Promise<{
}>;

Defined in: lib/util.js:168

Resolves once every codec has been asked of the decoder. Menus built before it resolves offer every codec an engine with WebCodecs might play, and are rebuilt from the answer.


h264FramingReady

const h264FramingReady: Promise<string>;

Defined in: lib/util.js:185

How this engine's VideoDecoder takes H.264: annexb where a key frame in Annex B form decodes (Chromium, Firefox), avcc where it is refused and an avcC description with length-prefixed NAL units is needed instead (WebKit). Asked once, of a 16x16 key frame, in a worker so a slow refusal never holds the page; annexb without WebCodecs or when nothing answers.


isMobileClient

const isMobileClient: boolean;

Defined in: lib/util.js:561

Whether the client is touch-first: its primary pointer is coarse. True on phones and tablets, false on desktops -- including touch-screen laptops, whose primary pointer is still the pointing device. The form factor is fixed for the life of the document, so it is resolved once and available to every first render; a touch-capable device this misses is still caught by the first touchstart.

Functions

displayLabel()

function displayLabel(value): string;

Defined in: lib/util.js:107

Parameters

ParameterTypeDescription
valuestringA wire value.

Returns

string

Its display label, or the value itself when it has none.


decoderAccepts()

function decoderAccepts(
   codec, 
   width, 
   height
): Promise<boolean>;

Defined in: lib/util.js:128

Whether a VideoDecoder here accepts codec at width x height. False without WebCodecs, and when the engine throws on the question; null when it gives no answer within DECODER_PROBE_TIMEOUT_MS, which is not a refusal.

Parameters

ParameterTypeDescription
codecstringA WebCodecs codec string.
widthnumber-
heightnumber-

Returns

Promise<boolean>


h264Framing()

function h264Framing(): string;

Defined in: lib/util.js:218

Returns

string

The framing h264FramingReady settled on; annexb until it has.


canDecodeEncoder()

function canDecodeEncoder(encoder): boolean;

Defined in: lib/util.js:231

Whether this engine can play an encoder on the WebSocket transport: every video mode decodes through WebCodecs' VideoDecoder, whose answer per codec is read once it is in, while jpeg (striped JPEG painted through createImageBitmap) needs nothing. An engine without WebCodecs therefore still streams: the core's pre-flight pins jpeg instead of failing, and the settings offer nothing it cannot play. The WebRTC transport decodes in the browser's media stack and is not subject to this.

Parameters

ParameterTypeDescription
encoderstringAn encoder wire value.

Returns

boolean


canReceiveEncoder()

function canReceiveEncoder(encoder): boolean;

Defined in: lib/util.js:250

Whether this engine's own WebRTC stack receives an encoder's codec, from RTCRtpReceiver.getCapabilities; an engine that cannot answer the question is given the benefit of the doubt, and the server's negotiation fallback answers a codec it declines.

Parameters

ParameterTypeDescription
encoderstringAn encoder wire value.

Returns

boolean


canPlayEncoder()

function canPlayEncoder(encoder, webrtc): boolean;

Defined in: lib/util.js:270

Whether this engine plays an encoder on a transport: canDecodeEncoder on WebSockets, canReceiveEncoder on WebRTC. A menu lists what it cannot play disabled and marked, so a missing option reads as the browser's limit and never as the server's.

Parameters

ParameterTypeDescription
encoderstringAn encoder wire value.
webrtcbooleanWhether the transport is WebRTC.

Returns

boolean


canDecodeFullColor()

function canDecodeFullColor(codec?): Promise<boolean>;

Defined in: lib/util.js:291

Whether this engine's VideoDecoder will take codec at full color (4:4:4).

Engines differ on the 4:4:4 profiles (High 4:4:4 Predictive, HEVC RExt, VP9 profile 1), and one whose decoder lacks them cannot show the stream at all rather than showing it worse, so full color is asked of the decoder before it is asked of the server. Which engines have them changes with their releases, which is why this probes instead of naming them. The profile is the whole question, so it is asked with the constraint bits the encoders emit, at the smallest frame the level allows, since a level is a ceiling and a size beyond it is a pair an implementation may reject on its own. What a real stream is decoded with comes from its key frame's parameter sets instead.

Parameters

ParameterTypeDefault valueDescription
codec?string"h264"The codec name, h264 by default.

Returns

Promise<boolean>

False for a codec without 4:4:4, and where there is no VideoDecoder at all.


fullColorDecoded()

function fullColorDecoded(codec): boolean;

Defined in: lib/util.js:314

What canDecodeFullColor answered for codec, for a decision that cannot wait on the probe: undefined while it is still out or was never asked.

Parameters

ParameterTypeDescription
codecstringThe codec name.

Returns

boolean


tenBitFormat()

function tenBitFormat(codec, fullcolor?): string;

Defined in: lib/util.js:328

The name a 10-bit format goes by in TEN_BIT_SAMPLES, in tenBitDecoded, and in a WebRTC hello: the codec name for 4:2:0, with 444 after it for 4:4:4.

Parameters

ParameterTypeDefault valueDescription
codecstringundefinedThe codec name.
fullcolor?booleanfalseWhether the format is 4:4:4.

Returns

string


canDecodeTenBit()

function canDecodeTenBit(codec, fullcolor?): Promise<boolean>;

Defined in: lib/util.js:345

Whether this engine's VideoDecoder shows a picture of codec at 10 bits.

The decoder is handed a real 10-bit key frame (TEN_BIT_SAMPLES) and has to give a frame back, configured as a stream's decoder is, from the key frame's own codec string: engines accept 10-bit configurations they then fail to decode (Firefox and VP9 profile 2), and whether a profile decodes turns on the client's hardware where the engine has no software decoder for the codec (H.265 everywhere), so nothing short of a decode answers it.

Parameters

ParameterTypeDefault valueDescription
codecstringundefinedThe codec name.
fullcolor?booleanfalseWhether to ask for the codec's 4:4:4 at 10 bits.

Returns

Promise<boolean>

False for a format without a sample, where there is no VideoDecoder, and where no frame comes back within DECODER_PROBE_TIMEOUT_MS.


tenBitDecoded()

function tenBitDecoded(codec, fullcolor?): boolean;

Defined in: lib/util.js:383

What canDecodeTenBit answered, for a decision that cannot wait on the decode: undefined while it is still out or was never asked.

Parameters

ParameterTypeDefault valueDescription
codecstringundefinedThe codec name.
fullcolor?booleanfalseWhether the format is 4:4:4.

Returns

boolean


getRoutePrefix()

function getRoutePrefix(): string;

Defined in: lib/util.js:392

Directory this document is served from, without a trailing slash ('' at the server root). Every request the client builds hangs off it, so a deployment reverse-proxied under a subfolder reaches its own routes, and an iframed client reads its own path instead of the frame's.

Returns

string

The path prefix, e.g. /desk.


serverAnswers()

function serverAnswers(url, headers): Promise<Response>;

Defined in: lib/util.js:413

The server's answer to a plain GET on one of its endpoints, or null where it gave none: nothing answered within SERVER_ANSWER_MS (a path that swallows packets holds a fetch open for minutes), or the answer says it is not up (502, 503, 504: a gateway in front of a stopped server, or the server itself still starting). A page reloaded into a server stopping or starting lands on an error page with nothing left to retry, so a core reloads only once this returns an answer.

Parameters

ParameterTypeDescription
urlstring-
headers{ }-

Returns

Promise<Response>


entryPageTag()

function entryPageTag(headers?): Promise<string>;

Defined in: lib/util.js:429

The validators (ETag, Last-Modified) the server answers for the page's own entry document now: two answers differ when a new build is served.

Parameters

ParameterTypeDescription
headers?{ }Credentials the page's API calls carry.

Returns

Promise<string>

Null when the server does not answer.


getStorageAppName()

function getStorageAppName(): string;

Defined in: lib/util.js:448

The localStorage namespace every stored key is prefixed with.

Origin and pathname only, not the full URL: a per-session ?token= must not mint a new namespace on each connect. Cores and dashboards share one prefix, so this derivation is the single one they all call.

Returns

string

Sanitized namespace, empty outside a browser.


isMacDesktop()

function isMacDesktop(): boolean;

Defined in: lib/util.js:463

macOS proper, a narrower question than the Mac family navigator.platform names: an iPad in its desktop-class default reports MacIntel like a Mac does, and its touch points are what tell the two apart. navigator.platform decides and the client hint is only a fallback for an engine that has dropped the deprecated field, since the question is which pointer acceleration curve the OS applies, not which brand the browser reports.

Returns

boolean


pageTabId()

function pageTabId(): string;

Defined in: lib/util.js:476

This browser tab's id, kept in its sessionStorage: a reload of the page keeps it and another tab has its own, which is how the server tells a page taking its own display back from another page joining it.

Returns

string

Null where the tab has no storage.


ccStartKey()

function ccStartKey(displayId): string;

Defined in: lib/util.js:495

The localStorage key of a display's remembered start rate, per origin as every key is.

Parameters

ParameterType
displayIdany

Returns

string


rememberCcStart()

function rememberCcStart(displayId, kbps): void;

Defined in: lib/util.js:505

Keep the rate congestion control held a display at, as its server reports it, so a restarted server starts the page's next stream there rather than at the configured rate.

Parameters

ParameterTypeDescription
displayIdstring-
kbpsnumber-

Returns

void


recalledCcStart()

function recalledCcStart(displayId): number;

Defined in: lib/util.js:517

The display's remembered start rate, or null: none kept, a day old, or unreadable.

Parameters

ParameterTypeDescription
displayIdstring-

Returns

number


forgetCcStart()

function forgetCcStart(displayId): void;

Defined in: lib/util.js:531

Drop the display's remembered start rate, once the user sets a bitrate of their own.

Parameters

ParameterTypeDescription
displayIdstring-

Returns

void


isSkiaWebKit()

function isSkiaWebKit(userAgent, platform): boolean;

Defined in: lib/util.js:547

WebKit's Linux ports (WPE, WebKitGTK), which draw a canvas through Skia's GPU context, as against Safari, which draws through CoreGraphics. No capability tells the two apart and WebKit on Linux can carry Safari's macOS user agent, so the engine is read from the user agent and the OS from the platform, both of which a worker has too.

Parameters

ParameterTypeDescription
userAgentstringnavigator.userAgent.
platformstringnavigator.platform.

Returns

boolean


isCaptureRefusal()

function isCaptureRefusal(error): boolean;

Defined in: lib/util.js:573

Whether a capture the server asked for failed for good: permission withheld, or no getUserMedia at all. A busy camera or an unmet constraint may clear by the next demand.

Parameters

ParameterTypeDescription
erroranyThe rejection from getUserMedia, or the capture's own error.

Returns

boolean

True where asking again could not succeed.

References

codecOfEncoder

Re-exports codecOfEncoder


codecCarriesFullColor

Re-exports codecCarriesFullColor


codecCarriesTenBit

Re-exports codecCarriesTenBit