Skip to main content

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:

  1. 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 plain ws://.
  2. 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 range 49152-65535 are open on your firewalls.

📚 Additional documentation

Project details


Download files

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

Source Distribution

ominicall-0.1.7.tar.gz (20.4 kB view details)

Uploaded Source

Built Distribution

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

ominicall-0.1.7-py3-none-any.whl (22.2 kB view details)

Uploaded Python 3

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

Hashes for ominicall-0.1.7.tar.gz
Algorithm Hash digest
SHA256 80af2275934559c856fa950150a10156db09bf4ed5a43722daffd6e2396450c7
MD5 f7abab04d81cbfcd622104aa1be725d3
BLAKE2b-256 6662a8f6d95ff42f852b0c4a2592ec7d1285248f28710477a92b3e07addc3b23

See more details on using hashes here.

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

Hashes for ominicall-0.1.7-py3-none-any.whl
Algorithm Hash digest
SHA256 4c30d4c797fba39ecd384410d9ef5159e7ebd04867296469b1ac37183c9eac2e
MD5 c4d74bfe43e7e4de49826ca99f356ae4
BLAKE2b-256 2d45b0dc528c5a9634094fdc36f6b55f28b1749f254f0e92284ba3b3a4975895

See more details on using hashes here.

Supported by

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