Skip to main content

moq

Python bindings for Media over QUIC: real-time pub/sub with built-in caching, fan-out, and prioritization, on top of QUIC.

moq wraps the auto-generated moq-ffi UniFFI bindings with a Pythonic API: no Moq prefixes, async iterators, context managers, and simplified connection setup. At session setup it negotiates either the moq-lite or moq-transport wire protocol.

Installation

pip install moq

This pulls in the moq-ffi native bindings automatically. moq is pure Python and is versioned independently of moq-ffi; it floats to the latest compatible moq-ffi patch.

Quick Start

Subscribe to a stream

import asyncio
import moq

async def main():
    async with moq.Client("https://relay.quic.video") as client:
        async for announcement in client.announced():
            catalog = await announcement.broadcast.catalog()

            for name in catalog.audio:
                async for frame in announcement.broadcast.subscribe_media(name):
                    print(f"Got frame: {len(frame.payload)} bytes, ts={frame.timestamp_us}")

asyncio.run(main())

Publish a stream

import asyncio
import moq

async def main():
    async with moq.Client("https://relay.quic.video") as client:
        broadcast = moq.BroadcastProducer()

        # Publish an Opus audio track (init bytes from your encoder)
        audio = broadcast.publish_media("opus", opus_init_bytes)
        client.publish("my-stream", broadcast)

        # Write frames
        audio.write_frame(payload, timestamp_us=0)
        audio.write_frame(payload, timestamp_us=20000)

        # Clean up
        audio.finish()
        broadcast.finish()

asyncio.run(main())

Host a server

import asyncio
import moq

async def main():
    async with moq.Server("127.0.0.1:4443", tls_generate=["localhost"]) as server:
        broadcast = moq.BroadcastProducer()
        track = broadcast.publish_track("events")
        server.publish("hello", broadcast)
        print(f"listening on https://{server.local_addr}")

        sessions = []
        async for request in server:
            print(f"  + {request.transport} from {request.url}")
            sessions.append(await request.ok())

asyncio.run(main())

Reject a request instead of accepting it with await request.close(403).

Advanced: Manual origin wiring

For full control over the origin topology:

import moq

origin = moq.OriginProducer()
client = moq.Client(
    "https://relay.quic.video",
    publish=origin,
    subscribe=origin,
)

API

Connection

  • Client(url, *, tls_verify=True, tls_roots=None, tls_fingerprints=None, bind=None, publish=None, subscribe=None). Async context manager for connecting to a relay.
    • tls_roots. PEM root certificate file path(s) to trust instead of the system roots.
    • tls_fingerprints. Hex SHA-256 fingerprint(s) to pin the peer's certificate to, the native equivalent of serverCertificateHashes. Accepts the values a server reports via cert_fingerprints(), so you can trust a self-signed certificate without tls_verify=False.
  • Server(bind="[::]:443", *, tls_cert=(), tls_key=(), tls_generate=(), publish=None, subscribe=None). Async context manager + async iterator of incoming Requests.
    • .local_addr. The bound address (useful when binding to port 0).
    • .cert_fingerprints(). SHA-256 fingerprints of the configured TLS certificates, for serverCertificateHashes browser cert pinning.
    • .publish(path, broadcast). Publish a broadcast to be served.
  • Request. An incoming session, yielded by async for request in server.
    • .url, .transport. Properties.
    • .set_publish(origin), .set_consume(origin). Per-request overrides.
    • await .ok(). Complete the handshake, returns a session (hold it to keep the connection alive).
    • await .close(code). Reject with an HTTP status code.
    • .cancel(). Cancel an in-flight ok()/close() call.

Publishing

  • BroadcastProducer(). Create a broadcast to publish tracks into.
    • .dynamic() → BroadcastDynamic
    • .publish_media(format, init) → MediaProducer
    • .finish()
  • BroadcastDynamic. Async source of tracks requested by subscribers.
    • await .requested_track() → TrackProducer
    • Async iterator yielding TrackProducer
  • MediaProducer. Write frames to a track.
    • .write_frame(payload, timestamp_us)
    • .finish()

Subscribing

  • BroadcastConsumer. Subscribe to tracks within a broadcast.
    • .subscribe_catalog() → CatalogConsumer
    • .subscribe_media(name, max_latency_ms=10000) → MediaConsumer
    • await .catalog() → Catalog (convenience)
  • CatalogConsumer. Async iterator of Catalog.
  • MediaConsumer. Async iterator of Frame.

Origin (advanced)

  • OriginProducer(). Manage broadcast announcements.
    • .consume() → OriginConsumer
    • .publish(path, broadcast)
  • OriginConsumer. Discover broadcasts.
    • .announced(prefix) → Announced (async iterator)
    • .announced_broadcast(path) → AnnouncedBroadcast (awaitable)

Types

  • Catalog. .audio: dict[str, Audio], .video: dict[str, Video], .display, .rotation, .flip.
  • Frame. .payload: bytes, .timestamp_us: int, .keyframe: bool.
  • Audio. .codec, .sample_rate, .channel_count, .bitrate, .description.
  • Video. .codec, .coded: Dimensions, .display_ratio, .bitrate, .framerate, .description.
  • Dimensions. .width: int, .height: int.

See Also

  • moq-ffi. The raw UniFFI bindings this package wraps. Use it directly only if you need the unwrapped Moq-prefixed API.
  • MoQ project. Full monorepo with Rust server, TypeScript browser lib, and more.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

moq_rs-0.3.3.tar.gz (17.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

moq_rs-0.3.3-py3-none-any.whl (14.3 kB view details)

Uploaded Python 3

File details

Details for the file moq_rs-0.3.3.tar.gz.

File metadata

  • Download URL: moq_rs-0.3.3.tar.gz
  • Upload date:
  • Size: 17.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for moq_rs-0.3.3.tar.gz
Algorithm Hash digest
SHA256 fc1a0b54fa5c4ba8a1883e9d79bc49f5cbfdbb0a95f49840b32b147011053f34
MD5 face7df632472b468db027026ae8af0a
BLAKE2b-256 7dacc921b16209513c4a8ecc32d316336d052e1697b821b58509a9ed6108434c

See more details on using hashes here.

Provenance

The following attestation bundles were made for moq_rs-0.3.3.tar.gz:

Publisher: release-py.yml on moq-dev/moq

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file moq_rs-0.3.3-py3-none-any.whl.

File metadata

  • Download URL: moq_rs-0.3.3-py3-none-any.whl
  • Upload date:
  • Size: 14.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for moq_rs-0.3.3-py3-none-any.whl
Algorithm Hash digest
SHA256 a6070b2774b38ba0b547c58ac79e90683d13f03c3ff7cb9aa5557dbcabcac2ee
MD5 8d891a7c61575c7f4eff96d9f6e7b61e
BLAKE2b-256 d05a6beacaa5de3a15e4979087060fb5d4edbe3285f2493f6ae69dc0b4a89571

See more details on using hashes here.

Provenance

The following attestation bundles were made for moq_rs-0.3.3-py3-none-any.whl:

Publisher: release-py.yml on moq-dev/moq

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.4.5

2 files

0.4.4

2 files

0.4.3

2 files

0.4.2

2 files

0.4.1

2 files

0.4.0

2 files

This release

0.3.3 This release

2 files

0.3.2

2 files

0.3.1

2 files

0.3.0

2 files

0.2.17

6 files

0.2.16

6 files

0.2.15

6 files

0.2.14

6 files

0.2.13

6 files

0.2.12

6 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page