Skip to main content

Pure-Python SIP/RTP user-agent library (UAC + UAS) with asyncio

Project description

opensip

PyPI Python License Status

Saf-Python, asyncio tabanlı SIP/RTP user-agent kütüphanesi. UAC + UAS, REGISTER, HTTP Digest auth, G.711 ses kodek'i, opsiyonel mikrofon/hoparlör köprüsü — sıfır C bağımlılığı, ~33 KB wheel.

pip install opensip          # sinyalleşme + RTP (numpy varsa hızlandırma)
pip install "opensip[audio]" # + mic/speaker köprüsü (sounddevice + numpy)

⚠️ Erken alfa. Gerçek bir Türk bulut-PBX provider'ı (netsantral.com) üzerinden uçtan uca iki yönlü ses ile doğrulandı. Yine de RFC 3261'in birkaç önemli parçası eksik — production'a almadan önce Bilinen sınırlamalar bölümünü okuyun.

Özellikler

  • 🎯 Saf Python, sıfır C bağımlılığı (audio extra hariç)
  • asyncio — tek event loop'ta binlerce eşzamanlı dialog
  • 📞 UAC + UAS — hem arama yap hem de cevap ver
  • 🔐 HTTP Digest auth — MD5, MD5-sess, SHA-256, qop=auth (RFC 7616)
  • 🎙️ G.711 PCMU/PCMA — numpy varsa vektörize, 20 ms frame başına ~1 µs
  • 🔄 OPTIONS keepalive — provider'ın "alive?" pingini otomatik 200 OK
  • 🎧 Mic/speaker köprüsüsounddevice ile sistem ses cihazlarına bağla
  • 🧱 Jitter buffer — reorder + gap-fill (silence) + RFC 3550 §A.8 jitter ölçümü
  • ☎️ DTMF her iki yöndesend_dtmf() (RFC 4733) gönderim, on_dtmf callback ile alım
  • 📊 RTP istatistikleriRTPSession.stats ile paket/byte sayaçları + jitter
  • 📦 Tip ipuçları her yerdepy.typed paketlenmiş

Sürüm notları

0.2.0 (yeni)

  • Jitter bufferRTPSession'a varsayılan açık (jitter_ms=60). Sıra dışı paketleri yeniden sıralar, kayıp frame yerine sessizlik koyar, jitter_ms=0 ile devre dışı bırakılabilir.
  • JitterBuffer.recommended_target_ms() — RFC 3550 §A.8 EWMA ile ağ koşullarına göre hedef derinlik önerir.
  • DTMF alımı (RFC 4733)RTPSession.on_dtmf / Call.on_dtmf callback; sürdürme + 3 redundant end-packet otomatik dedup.
  • RTPSession.statspackets_sent/recv, bytes_sent/recv, dtmf_recv + iç içe jitter sub-stats.

Hızlı başlangıç

Giden arama

import asyncio
from opensip import UserAgent, Account

async def main():
    ua = UserAgent(local_addr=("0.0.0.0", 5060))
    await ua.start()

    acc = Account(
        username="alice",
        domain="sip.example.com",
        password="s3cret",
        server=("sip.example.com", 5060),
    )
    await ua.register(acc)

    call = await ua.invite(acc, "sip:bob@sip.example.com")
    await call.wait_answered()
    await asyncio.sleep(10)
    await call.hangup()

    await ua.stop()

asyncio.run(main())

Gelen arama

import asyncio
from opensip import UserAgent, Account

async def main():
    ua = UserAgent(local_addr=("0.0.0.0", 5060))

    @ua.on_incoming_call
    async def handle(call):
        await call.answer()
        await call.wait_ended()

    await ua.start()
    acc = Account(username="alice", domain="sip.example.com",
                  password="s3cret", server=("sip.example.com", 5060))
    await ua.register(acc)
    await asyncio.Event().wait()  # sonsuza kadar bekle

asyncio.run(main())

Mikrofon ↔ uzak taraf köprüsü (audio extras ile)

from opensip.audio import AudioBridge

# ... yukarıdaki örnekteki gibi call'u kur ...

bridge = AudioBridge(sample_rate=8000)
bridge.start()
call.on_pcm(bridge.feed_speaker)         # uzak taraf → hoparlör

async def pump_mic():
    while call.is_active:
        pcm = await bridge.read_microphone()
        call.write_pcm(pcm)              # mikrofon → uzak taraf

asyncio.create_task(pump_mic())

Çalışan örnekler: examples/make_call.py, examples/receive_call.py.

API özeti

from opensip import UserAgent, Account, Call

UserAgent — top-level facade

Yöntem Açıklama
await ua.start() / stop() Transport'u aç / kapat
await ua.register(acc) REGISTER + otomatik yenileme
await ua.unregister(acc) Expires: 0 REGISTER
await ua.invite(acc, target)Call Giden çağrı kur
@ua.on_incoming_call Gelen INVITE handler decorator

Call — bir SIP dialog'u

Yöntem / property Açıklama
await call.wait_answered(timeout=None) 200 OK'i bekle
await call.wait_ended() BYE'ı bekle
await call.answer() UAS — gelen çağrıyı cevapla
await call.hangup() BYE gönder, RTP kapat
call.write_pcm(bytes) Uzak tarafa 16-bit PCM gönder
call.on_pcm(callback) Gelen PCM için handler
call.is_active Dialog "answered" durumunda mı
call.codec Anlaşılan codec (Codec nesnesi)

AudioBridge (opensip[audio] extras)

Yöntem Açıklama
bridge.start() / stop() Mikrofon + hoparlör stream'leri
await bridge.read_microphone() Bir frame PCM oku
bridge.feed_speaker(bytes) Hoparlöre PCM gönder

Performans

G.711 PCMU/PCMA hot path, numpy varsa vektörize LUT lookup'a düşer. Apple M-series üzerinde, 8 kHz × 20 ms × 16-bit frame için:

İşlem Saf-Python numpy Hızlanma
PCMU encode 13.1 µs/frame 1.0 µs/frame 13.6×
PCMU decode 25.8 µs/frame 1.0 µs/frame 25.3×
PCMA encode 12.4 µs/frame 1.0 µs/frame 12.7×
PCMA decode 25.6 µs/frame 1.0 µs/frame 24.4×

20 ms ptime bütçesinin %0.005'i — binlerce paralel çağrıda codec CPU yükü ihmal edilebilir. Çıktının bit-exact'liği 65,536 PCM değerinin tümü üzerinde doğrulanmıştır.

Yeniden üretmek için:

python tests/bench_codecs.py

Doğrulanmış provider'lar

Provider Sinyalleşme İki yönlü ses Notlar
netsantral.com Symmetric-RTP / SBC NAT handling sağlıyor
sip2sip.info ⚠️ test edilmedi Açık-kayıt test provider'ı, deneyebilirsiniz
Twilio SIP trunking (TLS) TLS-only; opensip henüz TLS desteklemiyor
Yerel Asterisk / FreeSWITCH 🟢 beklenen 🟢 beklenen LAN'da NAT olmadan çalışmalı

Mimari

opensip/
├── message.py     # SIP request/response parser + serializer (RFC 3261 §7-§20)
├── headers.py     # URI, NameAddr, Via — compact form, IPv6, quoted params
├── auth.py        # HTTP Digest (MD5/MD5-sess/SHA-256, qop=auth)
├── sdp.py         # SDP offer/answer (RFC 4566), audio m=line + codec seçimi
├── transport.py   # asyncio UDP transport
├── ua.py          # UserAgent — UAC + UAS facade, dialog state inline
├── rtp.py         # RTP packetization (RFC 3550) + 20 ms ptime sender loop
├── codecs.py      # G.711 µ-law / A-law — numpy hot path + pure-Python fallback
├── audio.py       # sounddevice wrapper (opsiyonel)
├── utils.py       # branch/tag/Call-ID üreteçleri, IP keşfi
└── exceptions.py  # Hata hiyerarşisi

Bilinen sınırlamalar

opensip v0.1 minimal-viable bir UA: gerçek bir provider ile çağrı yapar ama RFC 3261'in birkaç önemli parçası henüz yok. Telefon altyapısı kurmadan önce farkında olun.

  • Transaction katmanı yok (RFC 3261 §17). UDP retransmission timer'ları (Timer A–K) yok — paket düşerse istek timeout'a düşer. LAN / kayıpsız ağda fark edilmez; internet üzerinden kaybedilen ilk INVITE'ı tekrar göndermez.
  • Dialog state machine sınırlı. Re-INVITE (hold/resume), UPDATE, target refresh çalışmaz; route set INVITE/BYE/ACK'te kullanılmaz — uzun proxy zincirleri kırılır.
  • NAT handling client-side yok. SDP c= satırına LAN IP yazılır; iki yönlü RTP yalnızca provider symmetric-RTP / SBC NAT handling yapıyorsa çalışır (netsantral yapıyor, çoğu yapmaz). rport/received Contact'a yansıtılmaz, STUN/ICE yok.
  • Yalnızca UDP. TCP ve TLS yok; TLS-only provider'lar (bazı Twilio konfigürasyonları) için kullanılamaz.
  • RTCP yok. Jitter buffer yok — gelen RTP doğrudan callback'e gönderilir, out-of-order paket / sıralama boşluğu metrikleri yok.
  • DTMF yok. RFC 2833 payload type 101 alınır ama sessizce düşürülür.
  • Codec sınırlı. PCMU + PCMA + telephone-event (gönderme yok). Opus / G.722 / G.729 yok.
  • Authorization re-use yok. Her INVITE/BYE'da yeniden challenge — küçük gecikme katar.

Karşılaştırma

opensip aiosip baresip pjproject
Dil Pure Python Pure Python C C/C++
asyncio
Mic/speaker bridge
Transaction layer ❌ (roadmap)
NAT (ICE/STUN) ❌ (roadmap)
TLS / SRTP ❌ (roadmap)
Video
Wheel boyutu ~33 KB ~30 KB
Kullanım amacı Python-native scripting, prototip, embedded automation İlkel araştırma Production CLI/embedded Carrier-grade SDK

opensip "küçük, Python-native, hack'lemesi kolay" yönünde bir niş tutuyor. Carrier-grade telefon altyapısı için pjproject veya baresip + Python binding önerilir.

Yol haritası

  • Faz 1 — sağlamlaştırma: transaction katmanı (RFC 3261 §17), tam dialog state machine, rport/received NAT, TCP transport, RTCP SR/RR, jitter buffer, DTMF (RFC 2833 in/out)
  • Faz 2 — özellik: TLS + SRTP, ICE-lite (RFC 8445), Opus + G.722, MESSAGE/SUBSCRIBE/NOTIFY/REFER, re-INVITE/hold, video iskeleti
  • Faz 3 — test + perf: Docker'da Asterisk/Kamailio/FreeSWITCH entegrasyon testleri, parser fuzzing (hypothesis), zero-copy parsing

Detaylı önceliklendirme ve durum: issue tracker.

Geliştirme

git clone https://github.com/artan/opensip.git
cd opensip
python3.12 -m venv .venv
.venv/bin/pip install -e ".[dev,audio]"
.venv/bin/pytest -v          # 17 unit + 1 loopback testi
.venv/bin/ruff check src tests

Python 3.10+ gerekiyor. macOS Homebrew kullanıyorsan: brew install python@3.12.

G.711 mikrobenchmark:

.venv/bin/python tests/bench_codecs.py

Canlı arama testi: examples/.env.example'ı kopyalayıp credentials gir, ardından:

LOG_LEVEL=DEBUG .venv/bin/python examples/make_call.py

Katkı

PR'lar memnuniyetle. Açmadan önce:

  1. Public API değişikliği yapıyorsanız önce bir issue açın
  2. pytest ve ruff check src tests yeşil olmalı
  3. Yeni özelliklere unit test eklenmeli — özellikle parser/codec/auth modüllerinde
  4. Commit mesajları konvansiyonel format (feat:, fix:, docs:) tercih edilir ama zorunlu değil

Lisans

MIT © artan

Teşekkürler

  • numpy — G.711 vektörize hot path
  • sounddevice + PortAudio — mikrofon/hoparlör erişimi
  • hatchling — build sistemi
  • IETF RFC 3261, 3550, 4566, 7616 yazarları

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

opensip-0.2.0.tar.gz (42.5 kB view details)

Uploaded Source

Built Distribution

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

opensip-0.2.0-py3-none-any.whl (41.5 kB view details)

Uploaded Python 3

File details

Details for the file opensip-0.2.0.tar.gz.

File metadata

  • Download URL: opensip-0.2.0.tar.gz
  • Upload date:
  • Size: 42.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for opensip-0.2.0.tar.gz
Algorithm Hash digest
SHA256 67acf4d0a4fdd336006323b067a6344ff631601b6e3a610e6721551d71bef9b2
MD5 e9cf387def9a0846a840433bc0258f7c
BLAKE2b-256 05a64a987f2f42185399366400585e9a1719889cde7e053a101877f1568edd25

See more details on using hashes here.

File details

Details for the file opensip-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: opensip-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 41.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for opensip-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 4b3861b32cf1b8cab9a977141f0f64c3e6e76cfbf70a8be10eb8bae35626289a
MD5 7325f530d5abda96f7909142980e87ed
BLAKE2b-256 f04645de73f25a92e7bb988ff6551b3b68b6a4ebb21c31c7a8c59cfe561a011f

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