Skip to content

moqtap-codec

Zero-dependency MoQT wire codec — draft-conforming parser and writer for every MoQT draft from draft-07 through draft-17.

crates.io | API docs

Pure encoding and decoding of MoQT protocol messages — no I/O, no async runtime, no network dependencies. It is the foundational building block that every other moqtap crate depends on.

  • Every MoQT control message (setup, subscribe, publish, fetch, namespace, track status, goaway) per supported draft
  • Data stream headers (subgroup, datagram, fetch, object)
  • QUIC variable-length integer (varint) encoding per RFC 9000
  • Key-value parameter (KVP) pairs
  • Core protocol types: TrackNamespace, Location, FilterType, GroupOrder, ObjectStatus
  • Session and request error codes, per-draft
  • Runtime draft dispatch via the dispatch module

Each draft lives in its own module with an independent implementation; no wire-level code is shared across drafts. Shared primitives sit at the crate root.

moqtap_codec::
varint, kvp, types, version, error (shared)
dispatch (runtime Any* enums)
draft07, draft08, ..., draft17 (per-draft wire format)

Each draft is behind a Cargo feature. The default is all-drafts.

# every draft (default)
moqtap-codec = "0.1"
# draft-14 only
moqtap-codec = { version = "0.1", default-features = false, features = ["draft14"] }
# draft-07 plus draft-14 for runtime dispatch
moqtap-codec = { version = "0.1", default-features = false, features = ["draft07", "draft14"] }

Available features: draft07, draft08, …, draft17, plus all-drafts.

use moqtap_codec::draft14::message::{ControlMessage, ClientSetup};
use moqtap_codec::varint::VarInt;
let msg = ControlMessage::ClientSetup(ClientSetup {
supported_versions: vec![VarInt::from_u64(0xff00000e).unwrap()],
parameters: vec![],
});
let mut buf = Vec::new();
msg.encode(&mut buf).unwrap();
let mut cursor = &buf[..];
let decoded = ControlMessage::decode(&mut cursor).unwrap();
assert_eq!(msg, decoded);

When you don’t know the draft at compile time (e.g., a proxy that observes any client), use the dispatch module. It exposes one Any* enum per message category, each with one variant per enabled draft feature:

  • AnyControlMessage
  • AnySubgroupHeader
  • AnyFetchHeader
  • AnyDatagramHeader
  • AnyObjectHeader
use moqtap_codec::dispatch::AnyControlMessage;
use moqtap_codec::version::DraftVersion;
let mut cursor: &[u8] = &wire_bytes;
let msg = AnyControlMessage::decode(DraftVersion::Draft14, &mut cursor)?;
match msg {
AnyControlMessage::Draft14(inner) => { /* ... */ }
_ => {}
}

Zero runtime dependencies. The codec is pure byte manipulation — no tokio, no bytes, no serde.