Skip to content

Generic MoQ core

@caprid/moq-core runs MOQT sessions that carry arbitrary track objects. It has no runtime dependencies and does not import Relay product code or Video.js.

The core speaks MOQT draft 22, and draft 20 with a peer that selects it. It receives subscription objects over reliable streams. It does not implement application timing, decoding, datagram reception, or automatic session recovery.

createMoqtSession takes an established transport and owns it from then on. Subscribe, fetch, and track-status requests take callback handlers. They pass payloads and object properties through uninterpreted and report protocol lifecycle notifications. MoqTrackSource is borrowed access to a session’s subscribe, fetch, and trackStatus: a holder can make requests but cannot close the transport.

createMoqtPublishSession serves a namespace and its registered tracks over a separate transport. Track properties are explicit, so a track has no default media timescale. createSubgroupWriter writes arbitrary object payloads and metadata. The publishing policy above it, such as @caprid/relay’s bounded single-object groups, belongs to the SDK.

Two publish-session hooks serve embedders that track more than the SDK does:

  • A registered track’s getLargestObject puts LARGEST_OBJECT in SUBSCRIBE_OK and REQUEST_OK. The session keeps no objects, so it answers a nonempty fill request with a fill fetch stream that it resets after the FETCH_HEADER.
  • end({ after }) holds a track’s end until a last group is written. It also holds during an orderly close.

The package index also exports the wire constants and pure helpers that an embedder needs around a session: TRACK_PROPERTY, compareLocations, isEmptyFetchRange, encodeFetchHeader, isRetryableRequestErrorCode, isRetryablePublishDoneStatus, utf8Decode, and the close bounds CLOSE_FLUSH_TIMEOUT_MS and CLOSE_END_ACK_TIMEOUT_MS.

A test that plays the relay’s end against a publish session needs two things the client surface leaves out.

createMoqtSession(transport, { perspective: 'server' }) allocates the server’s Request IDs, odd and starting at 1, and requires the peer’s to be even (§6.4.2.1). The default, 'client', allocates even IDs from 0 and requires odd ones. A publish session is always the client and takes each SUBSCRIBE’s Request ID as the subscription’s Track Alias, so a server-perspective session’s first two subscriptions get aliases 1 and 3.

@caprid/moq-core/wire exports the codec for driving a session byte by byte, under the same semver as the index: StreamReader and utf8Encode; ControlMessageDeframer, decodeControlMessage and the ControlMessage type; encodeSetup, encodeSubscribe, encodeSubscribeOk, encodeSubscribeNamespace, encodePublish, encodePublishDone, encodeRequestError and encodeGoaway; STREAM_TYPE, isSubgroupHeaderType, readSubgroupHeader, readSubgroupObjects and readFetchHeader; encodeVarint and decodeVarint. Import from it rather than from dist/protocol/*, whose layout is not part of the API.

import { createMoqtPublishSession, createMoqtSession } from '@caprid/moq-core';
import { encodeSubscribe } from '@caprid/moq-core/wire';
const publisher = createMoqtPublishSession(pair.client);
const relay = createMoqtSession(pair.server, { perspective: 'server' });
// relay.subscribe(...) sends Request IDs 1, 3, 5, ...; a raw SUBSCRIBE from encodeSubscribe needs an odd one too.

The implementation and its regression tests come from Video.js, under Apache-2.0. UPSTREAM.json records the source revision, the original file hashes, and every change made since, and NOTICE carries the attribution. Video.js still has its own copy of these modules. Moving Video.js onto this package is a separate step.

For Relay application usage and local installation, start with the JavaScript quickstart.

Before 1.0, a minor version can break the API. This package releases together with @caprid/relay at the same version. See the changelog.