A lightweight, headless WebRTC-based communication SDK for 1-on-1 voice calls
Project description
📞 OmniCall Headless WebRTC SDK
OmniCall is a lightweight, production-grade, headless WebRTC engine designed for seamless 1-on-1 voice calls. It operates seamlessly across browsers (desktop and mobile web) and headless Python environments, supported by a robust, secure FastAPI signaling service.
With built-in secure authentication, automatic network switch recovery, dynamic TURN credential provisioning, and a synchronized state machine, OmniCall is the ultimate plug-and-play SDK for developer-first real-time audio systems.
Install: pip install ominicall · Latest: v0.1.7 · Repo: github.com/mani1028/Ominicall
🏗️ Architecture Flow
This diagram illustrates how client SDKs coordinate session handshakes, authentication, dynamic Coturn credentials, and WebRTC audio bridges:
graph TD
A[JS Browser Client] <-->|Secure WS: Token / API Key| B(FastAPI Signaling Server)
C[Headless Python Client] <-->|Secure WS: Token / API Key| B
B <-->|REST: Auth / Credentials| D[Authentication Database]
B -->|Generates time-limited keys| E[Coturn STUN / TURN Server]
A -.->|ICE Relay Fallback: UDP & TCP| E
C -.->|ICE Relay Fallback: UDP & TCP| E
A <===>|WebRTC Direct Peer Audio Bridge| C
🚀 Key Core Features
- Headless & UI-agnostic: Focuses entirely on WebRTC logic, letting you build custom, gorgeous user interfaces without layout constraints.
- Dynamic Coturn TURN Integration: Supports STUN/TURN auto-configuration, dynamically generating time-limited usernames and passwords to bypass complex firewalls, office Wi-Fi networks, and mobile data blocks.
- Robust Multi-Layer Auth (JWT & API Keys): Employs query-string secure WebSocket handshakes, JWT access/refresh token structures, and automatic client token-refresh managers.
- State-of-the-Art Reconnection Engine: Monitors connection quality and switches between cellular data and Wi-Fi seamlessly using WebRTC ICE Restarts (
iceRestart: true) without dropping active calls. - Silent Stream Watchdog: A background stats monitor checks audio packet flows every 2 seconds. If a packet freeze is detected (silent call killer), it triggers a silent ICE restart.
- 7-State Call State Machine: Cleanly tracks the call lifecycle (
IDLE,RINGING,CONNECTING,CONNECTED,RECONNECTING,FAILED,ENDED) to simplify UI development. - Developer-Loved Error Handling: Provides clear and actionable errors (
AUTH_FAILED,NETWORK_ERROR,TURN_FAILED,MIC_PERMISSION_DENIED,CALL_TIMEOUT).
🛠️ FastAPI Signaling Server Setup
The backend manages user registries, signals forwardings, JWT auth lifecycle, and Coturn credential generation.
1. Installation
Ensure dependencies are installed:
cd backend-signaling
pip install -r requirements.txt
2. Configuration Settings
Set environment variables (recommended for production) or use defaults for local dev. See docs/CONFIGURATION.md for the full reference.
| Variable | Purpose |
|---|---|
OMNICALL_JWT_SECRET |
Signs JWT access/refresh tokens |
OMNICALL_API_KEYS |
Comma-separated dev API keys |
OMNICALL_COTURN_SECRET |
Coturn shared auth secret |
OMNICALL_COTURN_URIS |
TURN server URIs |
3. Startup
Start the signaling server using Uvicorn:
python -m uvicorn app.main:app --host 0.0.0.0 --port 8000
🌐 JavaScript Web SDK Integration
Import and integrate OmniCall into any modern browser framework (Vanilla JS, React, Vue, Next.js, etc.).
1. Basic Setup (API Key Auth)
Perfect for testing and internal developer environments:
import { OmniCall } from './sdk_core/web/core.js';
const omni = new OmniCall({
userId: "UserA",
serverUrl: "ws://localhost:8000",
apiKey: "dev-api-key-12345", // Static developer key
onReady: () => console.log("Registered online!"),
onIncoming: (callerId) => {
// Render incoming call card
if (confirm(`Incoming voice call from ${callerId}`)) {
omni.acceptCall();
} else {
omni.rejectCall();
}
},
onAccepted: () => console.log("Call active and streaming!"),
onBusy: () => alert("Recipient is currently busy"),
onHangup: () => console.log("Call hung up.")
});
// Establish socket connection
omni.connect();
2. Production Setup (Secure JWT Refresh Flow)
For production environments, employ short-lived JWT tokens and the automatic token refresh provider callback:
import { OmniCall } from './sdk_core/web/core.js';
import { CALL_STATES } from './sdk_core/common/constants.js';
// 1. Fetch initial JWT tokens from your secure server
const response = await fetch("http://localhost:8000/auth/token", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ username: "UserA" })
});
const credentials = await response.json();
// 2. Initialize OmniCall with token and refresh provider
const omni = new OmniCall({
userId: "UserA",
serverUrl: "ws://localhost:8000",
token: credentials.accessToken, // Short-lived access token
// Auto-triggered when the WS drops due to token expiry
tokenProvider: async () => {
const refreshResponse = await fetch("http://localhost:8000/auth/token", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ username: "UserA" })
});
const freshData = await refreshResponse.json();
return { accessToken: freshData.accessToken };
},
// Lifecycle state observer
onStateChange: (newState, oldState) => {
console.log(`UI State transition: ${oldState} -> ${newState}`);
updateCallUIBadge(newState); // IDLE, RINGING, CONNECTED, etc.
},
// Developer error handler
onError: (error) => {
console.error(`Error [${error.code}]: ${error.message}`);
// Handle AUTH_FAILED, NETWORK_ERROR, MIC_PERMISSION_DENIED
}
});
omni.connect();
3. Controlling Media Streams
// Start Call
omni.startCall("UserB");
// Mute Mic
omni.localStream.getAudioTracks()[0].enabled = false;
// Unmute Mic
omni.localStream.getAudioTracks()[0].enabled = true;
// Hangup Call
omni.hangup();
🐍 Headless Python SDK Integration
Install the package:
pip install ominicall
OmniCall is equipped with a python engine powered by aiortc and aiohttp. Perfect for headless bots, interactive voice systems, or recording gateways.
1. Basic Setup
import asyncio
from ominicall import OmniCallApp
async def main():
app = OmniCallApp(
user_id="HeadlessBot",
server_url="ws://localhost:8000",
config={
"apiKey": "dev-api-key-12345",
"audioSource": "silence", # or "microphone" for real mic input
"onReady": lambda: print("🤖 Bot registered online!"),
"onIncoming": lambda caller: asyncio.create_task(handle_incoming(app, caller)),
"onAccepted": lambda: print("📞 Audio stream started!"),
"onStateChange": lambda new_s, old_s: print(f"State: {old_s} -> {new_s}"),
"onError": lambda err: print(f"Error: {err}"),
},
)
await app.start()
try:
while True:
await asyncio.sleep(1)
finally:
await app.stop()
async def handle_incoming(app, caller):
print(f"Incoming call from {caller}. Accepting call automatically...")
await app.accept_call()
if __name__ == "__main__":
asyncio.run(main())
Note: Always call
await app.start()inside a running event loop. The signaling server must be running separately (see DEVELOPMENT.md).
🚦 Call State Transitions Reference
Understanding these state flows ensures smooth interface rendering and prevents conflicting layout operations:
| Transition Name | Triggering Event | Next Call State |
|---|---|---|
| Idle | Initial configuration / Call ended successfully | IDLE |
| Outgoing Offer | Triggering startCall(peerId) |
CONNECTING |
| Incoming Offer | Received remote offer signal | RINGING |
| Call Answered | Triggering acceptCall() or remote accepted |
CONNECTED |
| Network Loss | Network offline / watchdog freeze detected | RECONNECTING |
| ICE Restart Success | Peer Connection re-established | CONNECTED |
| ICE Timeout Failure | ICE restart fails to recover in 15 seconds | FAILED (error dispatched) |
| Hangup | Invoked hangup() or received remote hangup |
ENDED -> returns to IDLE |
🛡️ Actionable Error Reference
Developers can listen to errors via the onError config callback. Use this grid to present clear visual error alerts to users:
| Error Code | Potential Cause | Best Action for Developer |
|---|---|---|
AUTH_FAILED |
Expired/invalid JWT token, or incorrect API Key. | Direct the user to re-authenticate or verify config parameters. |
NETWORK_ERROR |
Connection to the signaling server failed. | Prompt user to check their internet connectivity. |
TURN_FAILED |
ICE candidate exchange failed (blocked firewall/relays). | Ensure Coturn service is active or suggest changing network links. |
MIC_PERMISSION_DENIED |
Browser/OS denied access to microphone. | Guide the user to browser site-settings to enable mic access. |
CALL_TIMEOUT |
Peer did not accept outbound call within 30s. | Notify the user that the recipient did not answer the call. |
🔒 Production Deployment Best Practices
For live, production-grade deployments, execute these structural setups:
-
Enable TLS/SSL (WSS & HTTPS):
- WebRTC requires secure contexts in modern browsers. Always run the FastAPI signaling server behind a reverse proxy (like Nginx, Caddy, or Cloudflare) with valid SSL certificates.
- Use
wss://protocols for WebSocket connections instead of plainws://.
-
Configure Coturn properly:
- In
/etc/turnserver.conf, ensure you define the REST API shared secret:use-auth-secret static-auth-secret=ominicall_coturn_secret_xyz realm=turn.example.com
- Make sure port
3478(UDP/TCP) and port range49152-65535are open on your firewalls.
- In
📚 Additional documentation
- DEVELOPMENT.md — local setup, testing, and PyPI release process
- docs/CONFIGURATION.md — environment variables and SDK config reference
- CHANGELOG.md — version history
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ominicall-0.1.7.tar.gz.
File metadata
- Download URL: ominicall-0.1.7.tar.gz
- Upload date:
- Size: 20.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.20
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
80af2275934559c856fa950150a10156db09bf4ed5a43722daffd6e2396450c7
|
|
| MD5 |
f7abab04d81cbfcd622104aa1be725d3
|
|
| BLAKE2b-256 |
6662a8f6d95ff42f852b0c4a2592ec7d1285248f28710477a92b3e07addc3b23
|
File details
Details for the file ominicall-0.1.7-py3-none-any.whl.
File metadata
- Download URL: ominicall-0.1.7-py3-none-any.whl
- Upload date:
- Size: 22.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.20
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4c30d4c797fba39ecd384410d9ef5159e7ebd04867296469b1ac37183c9eac2e
|
|
| MD5 |
c4d74bfe43e7e4de49826ca99f356ae4
|
|
| BLAKE2b-256 |
2d45b0dc528c5a9634094fdc36f6b55f28b1749f254f0e92284ba3b3a4975895
|