Skip to main content

RushAudio — Python port

A pure-Python, zero-dependency implementation of the RushAudio protocol: a binary protocol for low-latency audio streaming over UDP.

This port mirrors the public API and wire format of the Rust reference implementation, so it is interoperable byte-for-byte on the wire.

Property Value
Transport UDP (default port 4210)
Header size 14 bytes
Max payload 4096 bytes
Latency target 30–100ms
Loss recovery XOR FEC (recovers 1 loss per group)
Codecs Opus, PCM S16LE, A-law, μ-law
Metadata TLV key-value pairs (built-in + custom)
Byte order Big-endian (except PCM samples: little-endian)
Python ≥ 3.9, standard library only

Install

pip install rushaudio            # from PyPI (once published)
# or build/install from source:
cd ports/python
python -m pip wheel . -w dist    # -> dist/rushaudio-1.1.0-py3-none-any.whl
pip install dist/rushaudio-1.1.0-py3-none-any.whl

Quick start

import rushaudio
from rushaudio.prelude import *

server = rushaudio.create_server("0.0.0.0:4210")   # receiver
client = rushaudio.create_client()                 # sender
peer = "127.0.0.1:4210"

# Encode/decode an audio frame (AudioData payload, spec §4.1)
payload = Packet.encode_audio_payload(2, 48000, AudioCodec.OPUS, opus_bytes)
pkt = Packet.new(PacketType.AUDIO_DATA, seq=1, ts=0, payload=payload)
client.send_packet(pkt, peer)

got = server.recv_packet()          # non-blocking: None if nothing available
if got is not None:
    packet, addr = got
    ch, sr, codec, frame = Packet.decode_audio_payload(packet.payload)

Metadata (TLV)

meta = (
    MetadataBuilder()
    .track_title("My Song")
    .artist("Artist Name")
    .album("Album")
    .sample_rate(48000)
    .channels(2)
    .codec_info("Opus")
    .bitrate(128000)
    .custom(0x80, b"app-specific-data")
    .build()
)
client.send_packet(meta.to_packet(seq=2, ts=20), peer)

received = Metadata.from_packet(packet)          # on the other side
title = received.get_string(META_TRACK_TITLE)    # -> "My Song"

Handshake

config = StreamConfig(sample_rate=48000, channels=2, bitrate=128000, codec=AudioCodec.OPUS)
hs = Handshake(role=HandshakeRole.INITIATOR, ssrc=12345, config=config)
client.send_packet(hs.build_request(0, 0), peer)

# server side:
got = server.recv_packet()
remote_ssrc, cfg = Handshake.parse_request(got[0])
responder = Handshake(role=HandshakeRole.RESPONDER, ssrc=67890, config=cfg)
server.send_packet(responder.build_response(0, 0, accepted=True), got[1])

FEC recovery

group = [pkt1, pkt2, pkt3]                      # original AudioData packets
repair = FecEncoder.generate_repair(group, seq=100, ts=0)
client.send_packet(repair, peer)

# receiver, having lost pkt2 of the group:
recovered = FecEncoder.try_recover(repair, [pkt1, pkt3])   # -> (2, b"...")

Jitter buffer

jb = JitterBuffer()                     # 80ms target delay
jb.push(seq, ts, payload_bytes)         # inserts in sequence order
out = jb.pop()                          # (ts, payload) once aged, else None
jb.adapt_delay()                        # clamp(2*jitter + 10ms, 20ms, 400ms)
stats = jb.stats()                      # depth, dropped, late, jitter

Wire / raw bytes

wire = pkt.encode()                 # header + payload, 14+N bytes
parsed = Packet.decode(wire)        # raises PacketError subclasses on invalid data

UdpTransport.recv_packet() returns (Packet, (host, port)) or None when nothing is available (non-blocking). Non-parseable datagrams are skipped.

Layout

ports/python/
├── pyproject.toml
├── rushaudio/
│   ├── __init__.py      # public API: create_server / create_client / DEFAULT_PORT
│   ├── prelude.py       # from rushaudio.prelude import *
│   ├── constants.py     # magic, metadata keys, timing, FEC/level constants
│   ├── types.py         # PacketType, AudioCodec, StreamConfig, StreamStats
│   ├── packet.py        # 14-byte header, Packet encode/decode, errors
│   ├── metadata.py      # Metadata / MetadataBuilder TLV codec
│   ├── levels.py        # AudioLevels (VU meter) packet type
│   ├── fec.py           # XOR FEC encode + single-loss recovery
│   ├── jitter.py        # adaptive jitter buffer
│   ├── handshake.py     # handshake state machine + payload builders
│   ├── transport.py     # non-blocking UdpTransport
│   ├── connection.py    # Connection + bounded ConnectionPool
│   └── session.py       # SessionManager (keepalive, stale sweeping)
└── tests/
    └── test_protocol.py # 37 tests (mirrors tests/integration.rs)

Running the tests

cd ports/python
python -m unittest discover -s tests -v    # 37 tests, stdlib only

Parity with the Rust reference

Rust (v1.1.0) Python
Packet::new / encode / decode Packet.new / encode / decode
PacketType PacketType (AUDIO_DATA, …)
AudioCodec AudioCodec (OPUS, RAW_PCM_I16, …)
Packet::encode_audio_payload / decode_audio_payload same names, static methods
MetadataBuilder fluent API identical fluent API
Metadata {get,get_string,get_u32,get_u16,encode,decode,to_packet,…} identical methods
AudioLevels AudioLevels
FecEncoder::generate_repair / try_recover FecEncoder.generate_repair / try_recover
JitterBuffer JitterBuffer
Handshake (roles/states, build/parse) Handshake
UdpTransport::bind / send_packet / recv_packet UdpTransport.bind / send_packet / recv_packet
Connection, ConnectionPool Connection, ConnectionPool
SessionManager SessionManager
prelude rushaudio.prelude
create_server(addr) / create_client() identical

Naming intentionally follows PEP 8 in Python (e.g. PacketType.AUDIO_DATA, ConnectionState.STREAMING) while behavior remains wire-identical.

License

MIT

Metadata

Release files for rushaudio 1.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for rushaudio 1.1.0
File Size Uploaded
rushaudio-1.1.0.tar.gz 20.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for rushaudio 1.1.0
File Interpreter ABI Platform
rushaudio-1.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 40.4 kB

Release files / rushaudio-1.1.0.tar.gz

Download URL rushaudio-1.1.0.tar.gz
Size 20.4 kB
Tags Source
SHA-256 checksum
How to use checksums
7bbb2fe44eebc7dc118fc968a7b20249fadc36eee082544d137f033554ee9a2b
BLAKE2b-256 checksum
How to use checksums
2d075459401bd0afda948b265562fbe8bc5a62194fdcd86f355e124b23176d3e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.12.14

Release files / rushaudio-1.1.0-py3-none-any.whl

Download URL rushaudio-1.1.0-py3-none-any.whl
Size 20.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a0f49c48490ea87e5ad6ef11b7fed06d4a6b75d64f0daa82329d01be3f98ed29
BLAKE2b-256 checksum
How to use checksums
9cbfc7a2de6fa9e9034ac57b60b3d84b497ad25411b3b562f34c40bb70130994
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.12.14

Release history Release notifications | RSS feed

This release

1.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page