Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/moqtail/moqtail/llms.txt

Use this file to discover all available pages before exploring further.

Overview

Tracks are the fundamental unit of content organization in MOQT. A track represents a logical stream of media or data, identified by a unique namespace and name combination. Publishers create tracks to make content available to subscribers.
Each track must be registered with the client using addOrUpdateTrack() before it can be published to subscribers.

Track Structure

Every track in Moqtail is defined by the Track interface:
type Track = {
  fullTrackName: FullTrackName
  forwardingPreference: ObjectForwardingPreference
  trackSource: TrackSource
  publisherPriority: number
  trackAlias?: bigint
}

Track Components

1

Full Track Name

The globally unique identifier for your track, consisting of a namespace and name:
import { FullTrackName } from 'moqtail-ts'

// Using string path (namespace/name)
const trackName = FullTrackName.tryNew('live/conference', 'video')

// Using Tuple for namespace
const trackName2 = FullTrackName.tryNew(
  Tuple.tryNew(['media', 'camera', 'main']),
  'stream'
)
  • Namespace can have 1-32 segments separated by /
  • Total serialized length must be under 4096 bytes
  • Names are case-sensitive
2

Forwarding Preference

Controls how objects should be delivered to subscribers:
import { ObjectForwardingPreference } from 'moqtail-ts'

// Datagram delivery (low-latency, loss-tolerant)
const preference = ObjectForwardingPreference.Datagram

// Subgroup delivery (reliable, ordered)
const preference = ObjectForwardingPreference.Subgroup
PreferenceUse CaseTransport
DatagramReal-time audio/video where latency matters more than reliabilityQUIC datagrams
SubgroupReliable delivery where all objects must arriveQUIC streams
3

Track Source

Defines where content comes from - can be live, static, or hybrid. See content source patterns for details.
4

Publisher Priority

Controls delivery priority (0 = highest, 255 = lowest):
// Highest priority for primary video
const track: Track = {
  // ...
  publisherPriority: 0
}

// Lower priority for auxiliary data
const track: Track = {
  // ...
  publisherPriority: 128
}

Creating Objects

All content is packaged as MoqtObject instances before being added to a track:
import { MoqtObject, Location, ObjectForwardingPreference } from 'moqtail-ts'

// Create an object with payload
const object = MoqtObject.newWithPayload(
  fullTrackName,
  new Location(groupId, objectId), // Position in track
  publisherPriority,
  ObjectForwardingPreference.Subgroup,
  null, // subgroupId (optional)
  null, // extension headers (optional)
  payloadData // Uint8Array
)

Object Location

Objects are positioned using a two-level hierarchy:
import { Location } from 'moqtail-ts'

// Group: logical segment (e.g., GOP in video)
// Object: item within the group (e.g., frame)
const location = new Location(
  groupId,   // bigint or number
  objectId   // bigint or number
)
// For video encoding:
// - Group = GOP (Group of Pictures)
// - Object = Individual frame
const keyframeLocation = new Location(5n, 0n)  // GOP 5, first frame
const pFrameLocation = new Location(5n, 1n)    // GOP 5, second frame

Registering Tracks

Once you’ve defined a track, register it with the client:
import { MOQtailClient } from 'moqtail-ts'

const client = await MOQtailClient.new({
  url: 'https://relay.example.com',
  supportedVersions: [0xff00000b]
})

// Add the track
client.addOrUpdateTrack(track)

// Update an existing track (same fullTrackName)
track.publisherPriority = 64
client.addOrUpdateTrack(track)
Tracks must be registered before calling publishNamespace(). Objects cannot be delivered until the namespace is announced.

Complete Example

Here’s a complete example creating a live video track:
import {
  MOQtailClient,
  FullTrackName,
  Track,
  ObjectForwardingPreference,
  LiveTrackSource,
  MoqtObject,
  Location
} from 'moqtail-ts'

// 1. Create a stream of MoqtObjects
const videoObjectStream = new ReadableStream<MoqtObject>({
  async start(controller) {
    let groupId = 0n
    let objectId = 0n
    
    // Generate video objects (simplified)
    for await (const frame of videoEncoder) {
      const object = MoqtObject.newWithPayload(
        fullTrackName,
        new Location(groupId, objectId),
        0, // highest priority
        ObjectForwardingPreference.Subgroup,
        null,
        null,
        frame.data
      )
      
      controller.enqueue(object)
      
      // Move to next GOP on keyframe
      if (frame.isKeyframe) {
        groupId++
        objectId = 0n
      } else {
        objectId++
      }
    }
  }
})

// 2. Create track configuration
const fullTrackName = FullTrackName.tryNew('conference/room42', 'video')

const videoTrack: Track = {
  fullTrackName,
  forwardingPreference: ObjectForwardingPreference.Subgroup,
  trackSource: {
    live: new LiveTrackSource(videoObjectStream)
  },
  publisherPriority: 0
}

// 3. Connect and register
const client = await MOQtailClient.new({
  url: 'https://relay.example.com',
  supportedVersions: [0xff00000b]
})

client.addOrUpdateTrack(videoTrack)

// 4. Announce namespace
await client.publishNamespace(['conference', 'room42'])

console.log('Track is now available to subscribers!')

Removing Tracks

To stop publishing a track:
// Remove from client registry
client.removeTrack(track)

// Optionally notify subscribers the namespace is done
await client.publishNamespaceDone(['conference', 'room42'])
Removing a track doesn’t cancel active subscriptions immediately. They continue until normal completion or until subscribers unsubscribe.

Best Practices

Naming Convention

Use hierarchical namespaces that reflect your application structure:
  • app/feature/resource (e.g., video/conference/room42)
  • Keep names descriptive but concise
  • Use consistent separators (e.g., always / not mixed with -)

Priority Management

  • Reserve 0-31 for critical content (keyframes, control data)
  • Use 32-127 for normal content (regular frames)
  • Use 128-255 for low-priority content (thumbnails, analytics)

Object Organization

  • Align groups with natural boundaries (GOPs, segments, files)
  • Keep object IDs sequential within groups
  • Use end-of-group markers for proper signaling

Next Steps

Live Streaming

Learn how to create real-time streaming tracks

Static Content

Publish pre-recorded or cached content

Hybrid Content

Combine live and static delivery

Namespaces

Organize and announce your tracks