Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ Key architectural rule: The CDN/relay does not know anything about media. Anythi
demo/ # Demo applications

/doc/ # Documentation site (VitePress, deployed via Cloudflare)
spec/ # moq-lite and hang protocol specifications
/dev/ # Development config and test media files
/cdn/ # CDN infrastructure (Terraform)
```
Expand Down
12 changes: 12 additions & 0 deletions doc/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,24 @@ export default defineConfig({
nav: [
{ text: "Setup", link: "/setup/" },
{ text: "Concepts", link: "/concept/" },
{ text: "Specs", link: "/spec/" },
{ text: "Apps", link: "/app/" },
{ text: "Rust", link: "/rs/" },
{ text: "TypeScript", link: "/js/" },
],

sidebar: {
"/spec/": [
{
text: "Specifications",
link: "/spec/",
items: [
{ text: "moq-lite", link: "/spec/draft-lcurley-moq-lite" },
{ text: "hang", link: "/spec/draft-lcurley-moq-hang" },
{ text: "Use Cases", link: "/spec/draft-lcurley-moq-use-cases" },
],
},
],
"/setup/": [
{
text: "Setup",
Expand Down
4 changes: 1 addition & 3 deletions doc/concept/layer/hang.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ description: A simple, WebCodecs-based media format utilizing MoQ.
---

# hang
A simple, WebCodecs-based media format utilizing MoQ.

See the draft: [draft-lcurley-moq-hang](https://www.ietf.org/archive/id/draft-lcurley-moq-hang-01.html).
A simple, WebCodecs-based media format utilizing MoQ. See the [specification](/spec/draft-lcurley-moq-hang) for wire-level details.

## Catalog
`catalog.json` is a special track that contains a JSON description of available tracks.
Expand Down
2 changes: 1 addition & 1 deletion doc/concept/layer/moq-lite.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ description: A fraction of the calories with none of the fat.
---

# moq-lite
[moq-lite](https://www.ietf.org/archive/id/draft-lcurley-moq-lite-02.html) is a subset of the [MoqTransport](/concept/standard/moq-transport) specification.
[moq-lite](/spec/draft-lcurley-moq-lite) is a subset of the [MoqTransport](/concept/standard/moq-transport) specification. See the [specification](/spec/draft-lcurley-moq-lite) for wire-level details.
The goal is to keep the core transport layer simple and focused on practical use-cases.

There's too much fringe functionality in the MoqTransport draft that's not practical to implement.
Expand Down
2 changes: 1 addition & 1 deletion doc/js/@moq/hang/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ import * as Publish from "@moq/publish";

## Protocol Specification

See the [hang specification](https://moq-dev.github.io/drafts/draft-lcurley-moq-hang.html).
See the [hang specification](/spec/draft-lcurley-moq-hang).

## Next Steps

Expand Down
4 changes: 2 additions & 2 deletions doc/js/@moq/lite.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ description: Core pub/sub protocol for browsers
[![npm](https://img.shields.io/npm/v/@moq/lite)](https://www.npmjs.com/package/@moq/lite)
[![TypeScript](https://img.shields.io/badge/TypeScript-ready-blue.svg)](https://www.typescriptlang.org/)

A TypeScript implementation of [Media over QUIC](https://moq.dev/) providing real-time data delivery in web browsers. Implements the [moq-lite specification](https://moq-dev.github.io/drafts/draft-lcurley-moq-lite.html).
A TypeScript implementation of [Media over QUIC](https://moq.dev/) providing real-time data delivery in web browsers. Implements the [moq-lite specification](/spec/draft-lcurley-moq-lite).

## Overview

Expand Down Expand Up @@ -242,7 +242,7 @@ For more examples, see:

## Protocol Specification

See the [moq-lite specification](https://moq-dev.github.io/drafts/draft-lcurley-moq-lite.html) for protocol details.
See the [moq-lite specification](/spec/draft-lcurley-moq-lite) for protocol details.

## Next Steps

Expand Down
2 changes: 1 addition & 1 deletion doc/js/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ The TypeScript implementation brings MoQ to web browsers using modern APIs like

[![npm](https://img.shields.io/npm/v/@moq/lite)](https://www.npmjs.com/package/@moq/lite)

Core pub/sub transport protocol for browsers. Implements the [moq-lite specification](https://moq-dev.github.io/drafts/draft-lcurley-moq-lite.html).
Core pub/sub transport protocol for browsers. Implements the [moq-lite specification](/spec/draft-lcurley-moq-lite).

**Features:**
- WebTransport-based QUIC
Expand Down
2 changes: 1 addition & 1 deletion doc/rs/crate/hang.md
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ Key types:

## Protocol Specification

See the [hang specification](https://moq-dev.github.io/drafts/draft-lcurley-moq-hang.html) for protocol details.
See the [hang specification](/spec/draft-lcurley-moq-hang) for protocol details.

## Next Steps

Expand Down
4 changes: 2 additions & 2 deletions doc/rs/crate/moq-lite.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ description: Core pub/sub transport protocol in Rust
[![docs.rs](https://docs.rs/moq-lite/badge.svg)](https://docs.rs/moq-lite)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/moq-dev/moq/blob/main/LICENSE-MIT)

The core pub/sub transport protocol implementing the [moq-lite specification](https://moq-dev.github.io/drafts/draft-lcurley-moq-lite.html).
The core pub/sub transport protocol implementing the [moq-lite specification](/spec/draft-lcurley-moq-lite).

## Overview

Expand Down Expand Up @@ -227,7 +227,7 @@ Key types:

## Protocol Specification

See the [moq-lite specification](https://moq-dev.github.io/drafts/draft-lcurley-moq-lite.html) for protocol details.
See the [moq-lite specification](/spec/draft-lcurley-moq-lite) for protocol details.

## Next Steps

Expand Down
2 changes: 1 addition & 1 deletion doc/rs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ The Rust implementation provides the reference implementation of the MoQ protoco
[![crates.io](https://img.shields.io/crates/v/moq-lite)](https://crates.io/crates/moq-lite)
[![docs.rs](https://docs.rs/moq-lite/badge.svg)](https://docs.rs/moq-lite)

The core pub/sub transport protocol implementing the [moq-lite specification](https://moq-dev.github.io/drafts/draft-lcurley-moq-lite.html).
The core pub/sub transport protocol implementing the [moq-lite specification](/spec/draft-lcurley-moq-lite).

**Features:**
- Broadcasts, tracks, groups, and frames
Expand Down
201 changes: 201 additions & 0 deletions doc/spec/draft-lcurley-moq-hang.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
---
title: "hang"
---

# hang

Hang is a real-time conferencing protocol built on top of moq-lite.
A room consists of multiple participants who publish media tracks.
All updates are live, such as a change in participants or media tracks.


# Terminology
Hang is built on top of moq-lite [moql] and uses much of the same terminology.
A quick recap:

- **Broadcast**: A collection of Tracks from a single publisher.
- **Track**: A series of Groups, each of which can be delivered and decoded *out-of-order*.
- **Group**: A series of Frames, each of which must be delivered and decoded *in-order*.
- **Frame**: A sized payload of bytes representing a single moment in time.

Hang introduces additional terminology:

- **Room**: A collection of participants, publishing under a common prefix.
- **Participant**: A moq-lite broadcaster that may produce any number of media tracks.
- **Catalog**: A JSON document that describes each available media track, supporting live updates.
- **Container**: A tiny header in front of each media payload containing the timestamp.


# Discovery
The first requirement for a real-time conferencing application is to discover other participants in the same room.
Hang does this using moq-lite's ANNOUNCE capabilities.

A room consists of a path.
Any participants within the room MUST publish a broadcast with the room path as a prefix which SHOULD end with the `.hang` suffix.

For example:

```text
/room123/alice.hang
/room123/bob.hang
/room456/zoe.hang
```

A participant issues an ANNOUNCE_PLEASE message to discover any other participants in the same room.
The server (relay) will then respond with an ANNOUNCE message for any matching broadcasts, including their own.

For example:

```text
ANNOUNCE_PLEASE prefix=/room/
ANNOUNCE suffix=alice.hang active=true
ANNOUNCE suffix=bob.hang active=true
```

If a publisher no longer wants to participate, or is disconnected somehow, their presence will be unannounced.
Publishers and subscribers SHOULD terminate any subscriptions once a participant is unannounced.

```text
ANNOUNCE suffix=alice.hang active=false
```

# Catalog
The catalog describes the available media tracks for a single participant.
It's a JSON document that extends the W3C WebCodecs specification.

The catalog is published as a `catalog.json` track within the broadcast so it can be updated live as the participant's media tracks change.
A participant MAY forgo publishing a catalog if it does not wish to publish any media tracks now and in the future.

The catalog track consists of multiple groups, one for each update.
Each group contains a single frame with UTF-8 JSON.

A publisher MUST NOT write multiple frames to a group until a future specification includes a delta-encoding mechanism (via JSON Patch most likely).

## Root
The root of the catalog is a JSON document with the following schema:

```text
type Catalog = {
"audio": AudioSchema | undefined,
"video": VideoSchema | undefined,
// ... any custom fields ...
}
```

Additional fields MAY be added based on the application.
The catalog SHOULD be mostly static, delegating any dynamic content to other tracks.

For example, a `"chat"` section should include the name of a chat track, not individual chat messages.
This way catalog updates are rare and a client MAY choose to not subscribe.

This specification currently only defines audio and video tracks.

## Video
A video track contains the necessary information to decode a video stream.


```text
type VideoSchema = {
"renditions": Map<TrackName, VideoDecoderConfig>,
"priority": u8,
"display": {
"width": number,
"height": number,
} | undefined,
"rotation": number | undefined,
"flip": boolean | undefined,
}
```

The `renditions` field contains a map of track names to video decoder configurations.
See the [WebCodecs specification](https://www.w3.org/TR/webcodecs/#video-decoder-config) for specifics and registered codecs.
Any Uint8Array fields are hex-encoded as a string.

For example:

```json
{
"renditions": {
"720p": {
"codec": "avc1.64001f",
"codedWidth": 1280,
"codedHeight": 720,
"bitrate": 6000000,
"framerate": 30.0
},
"480p": {
"codec": "avc1.64001e",
"codedWidth": 848,
"codedHeight": 480,
"bitrate": 2000000,
"framerate": 30.0
}
},
"priority": 2,
"display": {
"width": 1280,
"height": 720
},
"rotation": 0,
"flip": false,
}
```


## Audio
An audio track contains the necessary information to decode an audio stream.

```text
type AudioSchema = {
"renditions": Map<TrackName, AudioDecoderConfig>,
"priority": u8,
}
```

The `renditions` field contains a map of track names to audio decoder configurations.
See the [WebCodecs specification](https://www.w3.org/TR/webcodecs/#audio-decoder-config) for specifics and registered codecs.
Any Uint8Array fields are hex-encoded as a string.

For example:

```json
{
"renditions": {
"stereo": {
"codec": "opus",
"sampleRate": 48000,
"numberOfChannels": 2,
"bitrate": 128000
},
"mono": {
"codec": "opus",
"sampleRate": 48000,
"numberOfChannels": 1,
"bitrate": 64000
}
},
"priority": 1,
}
```

# Container
Audio and video tracks use a lightweight container to encapsulate the media payload.

Each moq-lite group MUST start with a keyframe.
If codec does not support delta frames (ex. audio), then a group MAY consist of multiple keyframes.
Otherwise, a group MUST consist of a single keyframe followed by zero or more delta frames.

Each frame starts with a timestamp, a QUIC variable-length integer (62-bit max) encoded in microseconds.
The remainder of the payload is codec specific; see the WebCodecs specification for specifics.

For example, h.264 with no `description` field would be annex.b encoded, while h.264 with a `description` field would be AVCC encoded.


# Security Considerations
TODO Security


# IANA Considerations

This document has no IANA actions.

Loading
Loading