btw: MoQ is under active development. The APIs and protocols are still evolving and will change. Most of this documentation is AI generated until things get more stable.

Skip to content

Python Libraries

The Python bindings expose Media over QUIC to scripts, services, and prototype tooling. Built on the same Rust core (moq-ffi) as the Swift and Kotlin packages, wrapped with an idiomatic asyncio API.

Packages

Two packages, split so the ergonomic API can evolve on its own cadence:

moq-rs

PyPI

The package you want. Install moq-rs (the moq name is taken on PyPI), import moq. Real-time pub/sub with built-in caching, fan-out, and prioritization on top of QUIC, with a Pythonic API (no Moq prefixes, async context managers, async iterators). At session setup it negotiates either the moq-lite or moq-transport wire protocol.

It is pure Python and depends on moq-ffi via a compatible-release pin, so it floats to the latest moq-ffi patch automatically. It is versioned independently of the Rust crates.

moq-ffi

PyPI

The raw UniFFI bindings (the Moq-prefixed classes), tracking the moq-ffi Rust crate one-to-one. moq-rs pulls this in for you. Install it directly only if you need the unwrapped API or are building your own wrapper.

Installation

bash
pip install moq-rs

# or with uv
uv add moq-rs        # into a project
uv pip install moq-rs # into the active environment

This pulls in moq-ffi, for which prebuilt wheels are published for:

  • Linux x86_64 / aarch64 (manylinux_2_28)
  • macOS x86_64 / aarch64
  • Windows x86_64

For other platforms (Alpine, BSD, etc.) pip falls back to building moq-ffi from source via the published sdist. You'll need a Rust toolchain and a C compiler.

Quickstart

Subscribe

python
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"frame: {len(frame.payload)} bytes, ts={frame.timestamp_us}")

asyncio.run(main())

Publish

python
import asyncio
import moq

async def main():
    async with moq.Client("https://relay.quic.video") as client:
        broadcast = moq.BroadcastProducer()
        audio = broadcast.publish_media("opus", opus_init_bytes)
        client.publish("my-stream", broadcast)

        audio.write_frame(payload, timestamp_us=0)
        audio.write_frame(payload, timestamp_us=20_000)

        audio.finish()
        broadcast.finish()

asyncio.run(main())

Source and issues

Licensed under MIT or Apache-2.0