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

The ObjectCache interface provides an in-memory index for storing and retrieving MoqtObject instances. Objects are keyed by their Location (group/object coordinates) and maintained in sorted order for efficient binary search operations. MOQtail provides two built-in implementations optimized for different use cases:

MemoryObjectCache

Unlimited in-memory storage with automatic sorting

RingBufferObjectCache

Fixed-size cache with automatic eviction of oldest objects

Interface

The ObjectCache interface defines the contract for all cache implementations:
export interface ObjectCache {
  /** Insert a new MoqtObject, preserving sorted order */
  add(obj: MoqtObject): void
  
  /** Return objects whose Location is >= start and < end (end exclusive) */
  getRange(start?: Location, end?: Location): MoqtObject[]
  
  /** Return the object whose Location exactly matches or undefined if absent */
  getByLocation(location: Location): MoqtObject | undefined
  
  /** Current number of cached objects */
  size(): number
  
  /** Remove all cached objects */
  clear(): void
}

Location-Based Indexing

All objects are stored and retrieved by their Location, which consists of:
  • group: Group index (e.g., segment, GOP, logical group)
  • object: Object index within the group (e.g., frame, chunk)
Objects are kept sorted in ascending order by (groupId, objectId) to enable efficient binary search operations.

MemoryObjectCache

The MemoryObjectCache provides unlimited in-memory storage with automatic sorting and binary search indexing.

Basic Usage

import { MemoryObjectCache } from 'moqtail'
import { Location } from 'moqtail'

// Create a new memory cache
const cache = new MemoryObjectCache()

// Add objects - automatically sorted by location
cache.add(videoFrame1) // groupId: 1, objectId: 0
cache.add(videoFrame2) // groupId: 1, objectId: 1
cache.add(videoFrame3) // groupId: 2, objectId: 0

console.log(`Cache size: ${cache.size()}`) // 3

Range Queries

Retrieve objects within a specific range using Location boundaries:
// Get all objects in group 1
const startLoc = new Location(1, 0)
const endLoc = new Location(2, 0) // Exclusive
const group1Objects = cache.getRange(startLoc, endLoc)

// Get all objects from a specific point onwards
const fromLocation = new Location(1, 5)
const remainingObjects = cache.getRange(fromLocation)

// Get all objects
const allObjects = cache.getRange()

Exact Lookups

// Retrieve a specific object by location
const location = new Location(1, 0)
const object = cache.getByLocation(location)

if (object) {
  console.log(`Found object: group ${object.groupId}, object ${object.objectId}`)
} else {
  console.log('Object not found in cache')
}

Cache Management

// Check cache size
if (cache.size() > 1000) {
  console.log('Cache is getting large')
}

// Clear all objects
cache.clear()
console.log(`Cache size after clear: ${cache.size()}`) // 0
The MemoryObjectCache has no size limits and will grow unbounded. Use RingBufferObjectCache if you need automatic eviction.

RingBufferObjectCache

The RingBufferObjectCache provides a fixed-size cache that automatically evicts the oldest objects when capacity is reached.

Initialization

import { RingBufferObjectCache } from 'moqtail'

// Create a ring buffer with capacity for 100 objects
const cache = new RingBufferObjectCache(100)

// Default capacity is 100 if not specified
const defaultCache = new RingBufferObjectCache()

Automatic Eviction

const cache = new RingBufferObjectCache(3)

// Add objects
cache.add(object1) // Cache: [object1]
cache.add(object2) // Cache: [object1, object2]
cache.add(object3) // Cache: [object1, object2, object3]

// Adding a 4th object evicts the oldest
cache.add(object4) // Cache: [object2, object3, object4]

console.log(cache.size()) // Still 3

Use Cases

The ring buffer cache is ideal for:
  • Live streaming - Keep only recent frames in memory
  • Memory-constrained environments - Prevent unbounded growth
  • Rolling window buffering - Maintain a fixed-size window of content
// Buffering live video frames with a 5-second window
const framesPerSecond = 30
const windowSeconds = 5
const frameCache = new RingBufferObjectCache(framesPerSecond * windowSeconds)

// As new frames arrive, old ones are automatically evicted
for (const frame of liveVideoStream) {
  frameCache.add(frame)
}

Integration with Content Sources

Object caches are commonly used with StaticTrackSource for on-demand content delivery:

Static Content

import { StaticTrackSource, MemoryObjectCache } from 'moqtail'
import { FullTrackName, ObjectForwardingPreference } from 'moqtail'

// Create and populate cache with file chunks
const fileCache = new MemoryObjectCache()

// Add file chunks as objects
for (let i = 0; i < fileChunks.length; i++) {
  const object = new MoqtObject({
    groupId: 0n,
    objectId: BigInt(i),
    payload: fileChunks[i],
    objectStatus: ObjectStatus.Normal,
    // ... other fields
  })
  fileCache.add(object)
}

// Create static content source with cache
const contentSource = new StaticTrackSource(fileCache)

// Create track for on-demand delivery
const fileTrack: Track = {
  fullTrackName: FullTrackName.tryNew('files/documents', 'presentation.pdf'),
  trackAlias: 2n,
  forwardingPreference: ObjectForwardingPreference.Datagram,
  contentSource,
}

client.addOrUpdateTrack(fileTrack)

Hybrid Content

import { HybridTrackSource, RingBufferObjectCache } from 'moqtail'

// Create cache for recent historical data
const recentCache = new RingBufferObjectCache(300) // Last 10 seconds at 30fps

// Hybrid source supports both live streaming and historical fetch
const hybridSource = new HybridTrackSource(videoObjectStream, recentCache)

// Live objects are automatically added to cache
// Subscribers can fetch recent history or subscribe to live stream

Performance Characteristics

Time Complexity

Both implementations use binary search for efficient operations:
add
O(n)
Insertion requires finding the correct position (O(log n)) and inserting into array (O(n))
getRange
O(log n + k)
Binary search to find range bounds (O(log n)) plus copying k matching objects
getByLocation
O(log n)
Binary search for exact match
size
O(1)
Constant-time array length check
clear
O(1)
Replaces array reference

Space Complexity

MemoryObjectCache
O(n)
Stores all n objects without bounds
RingBufferObjectCache
O(min(n, maxSize))
Stores at most maxSize objects

Thread Safety

ObjectCache implementations are not thread-safe. Concurrent mutation from web workers or multiple threads is not supported. Callers must implement their own synchronization if needed.

Example: Video-on-Demand System

Complete example of a VOD system using object cache:
import {
  MOQtailClient,
  MemoryObjectCache,
  StaticTrackSource,
  Track,
  FullTrackName,
  ObjectForwardingPreference,
  MoqtObject,
  ObjectStatus,
} from 'moqtail'

async function createVODTrack(videoId: string, segments: Uint8Array[]) {
  // Create cache for VOD content
  const vodCache = new MemoryObjectCache()

  // Populate cache with video segments
  for (let groupId = 0; groupId < segments.length; groupId++) {
    const segment = segments[groupId]
    
    // Each segment is a group containing one object
    const object = new MoqtObject({
      groupId: BigInt(groupId),
      objectId: 0n,
      payload: segment,
      objectStatus: ObjectStatus.Normal,
      publisherPriority: 0,
    })
    
    vodCache.add(object)
    
    // Add end-of-group marker
    const eogMarker = new MoqtObject({
      groupId: BigInt(groupId),
      objectId: 1n,
      objectStatus: ObjectStatus.EndOfGroup,
    })
    vodCache.add(eogMarker)
  }

  // Add end-of-track marker
  const endMarker = new MoqtObject({
    groupId: BigInt(segments.length),
    objectId: 0n,
    objectStatus: ObjectStatus.EndOfTrack,
  })
  vodCache.add(endMarker)

  // Create static content source
  const contentSource = new StaticTrackSource(vodCache)

  // Create track
  const track: Track = {
    fullTrackName: FullTrackName.tryNew('vod/library', videoId),
    trackAlias: 1n,
    forwardingPreference: ObjectForwardingPreference.Subgroup,
    contentSource,
  }

  return track
}

// Usage
const client = await MOQtailClient.new(clientSetup, webTransport)
const vodTrack = await createVODTrack('movie-123', videoSegments)
client.addOrUpdateTrack(vodTrack)

Content Sources

Learn about LiveTrackSource, StaticTrackSource, and HybridTrackSource

Publishing

Guide to publishing tracks with object caches