mirror of https://github.com/sipwise/rtpengine.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
128 lines
3.8 KiB
128 lines
3.8 KiB
from ctypes import *
|
|
import typing
|
|
from ._types import *
|
|
from ._lib import *
|
|
from ._Waiter import *
|
|
|
|
|
|
class Call:
|
|
"""Represents a SIP call."""
|
|
|
|
_ci: call_info = None
|
|
_aw: Waiter = None
|
|
|
|
def __init__(self, ci: call_info, answer_waiter: Waiter = None):
|
|
self._ci = ci
|
|
self._aw = answer_waiter
|
|
|
|
def __del__(self):
|
|
lib.bsw_call_destroy(self._ci.call)
|
|
|
|
def sdp(self) -> str:
|
|
"""Returns the (remote) SDP body"""
|
|
return self._ci.body.decode()
|
|
|
|
def call_id(self) -> str:
|
|
"""Returns the call ID"""
|
|
return self._ci.call_id.decode()
|
|
|
|
def from_addr(self) -> str:
|
|
"""Returns the SIP "from" address"""
|
|
return self._ci.from_addr.decode()
|
|
|
|
def from_tag(self) -> str:
|
|
"""Returns the SIP "from" tag"""
|
|
return self._ci.from_tag.decode()
|
|
|
|
def to_tag(self) -> str:
|
|
"""Returns the SIP "to" tag"""
|
|
return self._ci.to_tag.decode()
|
|
|
|
def ringing(self, sdp: str = None) -> None:
|
|
"""Sets a pending incoming call to "ringing" state by sending a 180
|
|
response"""
|
|
ok: bool = lib.bsw_call_answer(
|
|
self._ci.call, 180, sdp.encode() if sdp else None
|
|
)
|
|
if not ok:
|
|
raise RuntimeError("wrong call state")
|
|
|
|
def reject(self, code: int) -> None:
|
|
"""Rejects a pending incoming call with an error code.
|
|
|
|
:param code: integer SIP error code (>= 400)
|
|
"""
|
|
lib.bsw_call_answer(self._ci.call, code, None)
|
|
|
|
def answer(self, sdp: str = None, code: int = 200) -> None:
|
|
"""Answers a pending incoming call.
|
|
|
|
:param sdp: SDP body to answer with
|
|
:param code: SIP response code (defaults to 200, or 183)
|
|
"""
|
|
ok: bool = lib.bsw_call_answer(
|
|
self._ci.call, code, sdp.encode() if sdp else None
|
|
)
|
|
if not ok:
|
|
raise RuntimeError("wrong call state")
|
|
|
|
async def finished(self) -> None:
|
|
"""Wait for an established or pending call to terminate"""
|
|
w = Waiter()
|
|
done: bool = lib.bsw_call_finished(self._ci.call, w.fd())
|
|
if not done:
|
|
await w.wait()
|
|
|
|
def stop(self) -> None:
|
|
"""Stop an existing call.
|
|
|
|
Sends a BYE for an established call, or CANCEL for a pending outgoing
|
|
call. For a pending incoming call, sends an appropriate response code.
|
|
"""
|
|
ok: bool = lib.bsw_call_terminate(self._ci.call)
|
|
if not ok:
|
|
raise RuntimeError("call not established")
|
|
|
|
async def wait(self, state: int = 200) -> int:
|
|
"""Wait for a state change on an outgoing call.
|
|
|
|
Returns the new state as a SIP response code.
|
|
|
|
The state to wait for defaults to 200, which means wait for the call to
|
|
be fully established. To wait for a "ringing" state, wait for code
|
|
180, and wait for 183 to catch early media.
|
|
|
|
The returned state can be different from what was waited for (e.g. >=
|
|
400 for a failed call when waiting for 200, or 200 when waiting for
|
|
18x).
|
|
|
|
:param state: state to wait for as integer SIP code
|
|
"""
|
|
assert self._aw
|
|
|
|
while True:
|
|
res: str = await self._aw.wait_val()
|
|
if res == "0":
|
|
return False
|
|
|
|
ci = call_info()
|
|
code: int = lib.bsw_call_wait(self._ci.call, byref(ci))
|
|
|
|
if code == 183 or code == 200:
|
|
if code == 183 and state >= 200:
|
|
continue
|
|
|
|
# copy remote data
|
|
self._ci.body = ci.body
|
|
self._ci.content_type = ci.content_type
|
|
self._ci.to_tag = ci.to_tag
|
|
|
|
return code
|
|
if code >= 400:
|
|
return 400
|
|
if code == 180:
|
|
if state >= 200:
|
|
continue
|
|
return 180
|
|
return code # ?
|