Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

OmniStream

OmniStream is an open-source audio broadcasting server that lets one laptop stream audio to any number of listeners over a local WiFi network. Listeners join through their phone or laptop browser — no app download, no account, no Bluetooth pairing required. Every listener hears the same moment of audio, synchronized to within 5 milliseconds.


The Problem It Solves

You have one laptop playing a movie, a playlist, or a game. Everyone else in the room has their own phone and earbuds but no way to hear what's on the laptop.

Common scenarios where this matters:

  • Movie night — one laptop, five people, weak speakers. Everyone wants their own earbuds.
  • Road trip — one person controls the music, everyone else wants to hear it without sharing.
  • Flights — one downloaded movie, multiple seats. No internet needed, just a hotspot.
  • Study group — shared focus music without bothering others in a library.
  • No external speaker — use everyone's phone earbuds as distributed speakers.

OmniStream connects the laptop (host) to every phone in the room (listeners) over WiFi. Works offline. Works on a hotspot. No internet required.


How It Works

The host laptop runs a Python server. Anyone on the same network opens the server URL in their browser and enters a room code. The host drops an audio file on the dashboard (or streams system audio), clicks play, and every listener hears it.

Under the hood:

  • Audio is converted to raw PCM by FFmpeg and sliced into 20ms packets
  • Each packet is stamped with a server timestamp and sent over WebSocket
  • Every listener's browser runs an NTP-like clock sync protocol to calculate its exact offset from the server clock
  • Packets are buffered, reordered if needed, and played at the mathematically precise moment so all devices stay in sync
  • Result: less than 5ms difference between any two listeners

Compatibility

Host (runs the server) Listener (opens browser)
macOS Full support Safari, Chrome, Firefox
Windows Full support Any browser
Linux Full support Any browser
iPhone / iPad Safari
Android Chrome

Getting Started

Requirements

  • Python 3.11+
  • FFmpeg

Install FFmpeg:

# macOS
brew install ffmpeg

# Ubuntu / Debian
sudo apt install ffmpeg

# Windows (Chocolatey)
choco install ffmpeg

Setup

git clone https://github.com/Aujasyarajput18/Omnistream.git
cd omnistream
./scripts/setup.sh

Windows: cd server && pip install -e . && python -m omnistream

Run

./scripts/run.sh

The terminal prints the server URL. Open it on your laptop to access the host dashboard. Share the same URL (or the room code) with anyone else on your network.


Streaming System Audio

Stream whatever is playing on your laptop — Spotify, a movie, YouTube — not just files.

macOS — requires BlackHole (free virtual audio driver):

brew install blackhole-2ch

Open Audio MIDI Setup, create a Multi-Output Device with your speakers and BlackHole 2ch, and set it as your Sound Output. Then click "Capture System Audio" on the dashboard.

Linux — works automatically via PulseAudio or PipeWire. No setup needed.

Windows — works via DirectShow loopback. If it fails, enable Stereo Mix in Sound Settings → Recording.


Room Types

Type Behavior
Public Anyone with the room code joins immediately
Private Host sees a request and approves or rejects each person
Invite-only Only people with a specific invite link can join

Architecture

Single Python process. No Docker. No database. No microservices.

server/omnistream/
├── api/
│   ├── app.py              # FastAPI routes and WebSocket handler
│   └── routes_rooms.py     # Room management API
├── core/
│   ├── audio_capture.py    # FFmpeg subprocess manager
│   ├── distributor.py      # Broadcast to WebSocket clients
│   ├── sync_engine.py      # NTP-like clock synchronization
│   ├── room_manager.py     # In-memory room lifecycle
│   ├── packet.py           # 16-byte binary wire protocol
│   └── discovery.py        # UDP LAN beacon
└── utils/
    └── platform.py         # OS-specific audio capture

web-client/
├── index.html
└── js/
    ├── app.js                      # Room and connection logic
    ├── audio-player.js             # Web Audio API playback engine
    ├── sync-client.js              # Clock sync client
    ├── jitter-buffer.js            # Packet reordering and buffering
    └── audio-worklet-processor.js  # Low-latency audio thread

Wire Protocol

Every audio packet has a fixed 16-byte header:

Bytes 0:     Version (2 bits) + Padding (1 bit) + Type (5 bits)
Bytes 1:     Channel ID
Bytes 2–3:   Sequence number (uint16, big-endian)
Bytes 4–11:  Server timestamp in microseconds (uint64, big-endian)
Bytes 12–13: Payload length (uint16)
Bytes 14–15: Flags and reserved
Bytes 16+:   Raw PCM audio (s16le, 48kHz, stereo)

50 packets per second. 20ms of audio per packet.


Tech Stack

Layer Technology
Server Python 3.11+, FastAPI, Uvicorn
Audio processing FFmpeg
Transport WebSocket binary frames
Client playback Web Audio API — AudioWorklet with ScriptProcessor fallback
Clock sync Custom NTP-like protocol, EMA-smoothed
Jitter buffer Adaptive, 60–300ms target

Development

cd server
pip install -e ".[dev]"

# Tests — 126 passing
pytest tests/ -v

# Lint
ruff check omnistream/

# Type check
mypy omnistream/

Configuration

Variable Default Description
OMNISTREAM_HOST 0.0.0.0 Bind address
OMNISTREAM_PORT 8000 HTTP port
OMNISTREAM_SAMPLE_RATE 48000 Audio sample rate (Hz)
OMNISTREAM_CHANNELS 2 1 = mono, 2 = stereo
OMNISTREAM_FRAME_DURATION_MS 20 Packet size in milliseconds

Troubleshooting

Listeners can't connect — host and listeners must be on the same WiFi. Use the IP address URL if .local hostname doesn't resolve.

No sound on phone — mobile browsers block audio until the user interacts. Tap the "Tap to Enable Audio" banner when it appears.

Choppy audio — caused by WiFi packet loss. Move closer to the router. The jitter buffer handles brief spikes but sustained loss above 5% will cause gaps.

System audio not working (macOS) — install BlackHole (brew install blackhole-2ch) and configure a Multi-Output Device in Audio MIDI Setup.

System audio not working (Windows) — enable Stereo Mix in Sound Settings → More sound settings → Recording tab.

"Cannot bind socket" on macOS — run the server from Terminal.app, not from an IDE terminal. Or run ./scripts/fix-macos-network.sh once.


Roadmap

  • Opus encoding (6x compression vs current raw PCM)
  • Internet relay via Cloudflare Tunnel
  • Synchronized seek and pause controls
  • Multi-channel (different groups hear different streams)
  • PWA install support for phone home screen

License

MIT

Contributing

Pull requests welcome. Run pytest tests/ and ruff check omnistream/ before submitting.

About

Stream audio from your laptop to everyone's phone over WiFi. No app needed.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages