diff --git a/sipsimple/core/_core.invitation.pxi b/sipsimple/core/_core.invitation.pxi index 52f07074..586614c9 100644 --- a/sipsimple/core/_core.invitation.pxi +++ b/sipsimple/core/_core.invitation.pxi @@ -1,1855 +1,1856 @@ import weakref from errno import EADDRNOTAVAIL, ENETUNREACH from operator import itemgetter # classes cdef class SDPPayloads: def __init__(self): self.proposed_local = None self.proposed_remote = None self.active_local = None self.active_remote = None cdef class StateCallbackTimer(Timer): def __init__(self, state, sub_state, rdata, tdata, originator): self.state = state self.sub_state = sub_state self.rdata = rdata self.tdata = tdata self.originator = originator cdef class SDPCallbackTimer(Timer): def __init__(self, int status, active_local, active_remote): self.status = status self.active_local = active_local self.active_remote = active_remote cdef class TransferStateCallbackTimer(Timer): def __init__(self, state, code, reason): self.state = state self.code = code self.reason = reason cdef class TransferResponseCallbackTimer(Timer): def __init__(self, method, rdata): self.method = method self.rdata = rdata cdef class TransferRequestCallbackTimer(Timer): def __init__(self, rdata): self.rdata = rdata class DialogID(tuple): call_id = property(itemgetter(0)) local_tag = property(itemgetter(1)) remote_tag = property(itemgetter(2)) def __new__(cls, call_id, local_tag, remote_tag): return tuple.__new__(cls, (call_id, local_tag, remote_tag)) def __repr__(self): return 'DialogID(call_id=%r, local_tag=%r, remote_tag=%r)' % self cdef class Invitation: expire_warning_time = 30 def __cinit__(self, *args, **kwargs): cdef int status self.weakref = weakref.ref(self) Py_INCREF(self.weakref) status = pj_mutex_create_recursive(_get_ua()._pjsip_endpoint._pool, "invitation_lock", &self._lock) if status != 0: raise PJSIPError("failed to create lock", status) pj_list_init( &self._route_set) self._invite_session = NULL self._dialog = NULL self._reinvite_transaction = NULL self._transfer_usage = NULL self._sdp_neg_status = -1 self._failed_response = 0 self._timer = None self._transfer_timeout_timer = None self._transfer_refresh_timer = None self.from_header = None self.to_header = None self.request_uri = None self.route_header = None self.local_contact_header = None self.remote_contact_header = None self.credentials = None self.sdp = SDPPayloads() self.remote_user_agent = None self.state = None self.sub_state = None self.transport = None self.transfer_state = None self.direction = None self.call_id = None self.peer_address = None cdef int init_incoming(self, PJSIPUA ua, pjsip_rx_data *rdata, unsigned int inv_options) except -1: cdef int status cdef pj_mutex_t *lock = self._lock cdef pjmedia_sdp_session_ptr_const sdp cdef pjsip_dialog *replaced_dialog = NULL cdef pjsip_tpselector tp_sel cdef pjsip_tx_data *tdata = NULL cdef PJSTR contact_str cdef char *error_message with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: # Validate replaces header with nogil: status = pjsip_replaces_verify_request(rdata, &replaced_dialog, 0, &tdata) if status != 0: if tdata != NULL: pjsip_endpt_send_response2(ua._pjsip_endpoint._obj, rdata, tdata, NULL, NULL) else: pjsip_endpt_respond_stateless(ua._pjsip_endpoint._obj, rdata, 500, NULL, NULL, NULL) if status != 0: return 0 self.direction = "incoming" self.transport = rdata.tp_info.transport.type_name.decode().lower() self.request_uri = FrozenSIPURI_create( pjsip_uri_get_uri(rdata.msg_info.msg.line.req.uri)) if _is_valid_ip(pj_AF_INET(), self.request_uri.host.encode()): self.local_contact_header = FrozenContactHeader(self.request_uri) else: self.local_contact_header = FrozenContactHeader(FrozenSIPURI(host=_pj_str_to_str(rdata.tp_info.transport.local_name.host), user=self.request_uri.user, port=rdata.tp_info.transport.local_name.port, parameters=(frozendict(transport=self.transport) if self.transport != "udp" else frozendict()))) contact_str = PJSTR(str(self.local_contact_header.body).encode()) tp_sel.type = PJSIP_TPSELECTOR_TRANSPORT tp_sel.u.transport = rdata.tp_info.transport with nogil: status = pjsip_dlg_create_uas_and_inc_lock(pjsip_ua_instance(), rdata, &contact_str.pj_str, &self._dialog) if status != 0: error_message = "Could not create dialog for new INVITE session" else: pjsip_dlg_set_transport(self._dialog, &tp_sel) status = pjsip_inv_create_uas(self._dialog, rdata, NULL, inv_options, &self._invite_session) pjsip_dlg_dec_lock(self._dialog) if status != 0: error_message = "Could not create new INVITE session" else: status = pjsip_inv_initial_answer(self._invite_session, rdata, 100, NULL, NULL, &tdata) if status != 0: error_message = "Could not create initial (unused) response to INVITE" else: pjsip_tx_data_dec_ref(tdata) if status != 0: raise PJSIPError(error_message, status) if self._invite_session.neg != NULL: if pjmedia_sdp_neg_get_state(self._invite_session.neg) == PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER: pjmedia_sdp_neg_get_neg_remote(self._invite_session.neg, &sdp) self.sdp.proposed_remote = FrozenSDPSession_create(sdp) self._invite_session.sdp_neg_flags = PJMEDIA_SDP_NEG_ALLOW_MEDIA_CHANGE self._invite_session.mod_data[ua._module.id] = self.weakref self.call_id = _pj_str_to_str(self._dialog.call_id.id) self.peer_address = EndpointAddress(rdata.pkt_info.src_name, rdata.pkt_info.src_port) event_dict = dict(obj=self, prev_state=self.state, state="incoming", originator="remote") _pjsip_msg_to_dict(rdata.msg_info.msg, event_dict) print(event_dict) self.state = "incoming" self.remote_user_agent = event_dict['headers']['User-Agent'].body if 'User-Agent' in event_dict['headers'] else None try: self.remote_contact_header = event_dict['headers']['Contact'][0] except LookupError: pass _add_event("SIPInvitationChangedState", event_dict) self.from_header = FrozenFromHeader_create(rdata.msg_info.from_hdr) self.to_header = FrozenToHeader_create(rdata.msg_info.to_hdr) except: if self._invite_session != NULL: with nogil: pjsip_inv_terminate(self._invite_session, 500, 0) self._invite_session = NULL elif self._dialog != NULL: with nogil: pjsip_dlg_terminate(self._dialog) self._dialog = NULL else: with nogil: status = pjsip_endpt_create_response(ua._pjsip_endpoint._obj, rdata, 500, NULL, &tdata) if status != 0: error_message = "Could not create response" else: status = pjsip_endpt_send_response2(ua._pjsip_endpoint._obj, rdata, tdata, NULL, NULL) if status != 0: pjsip_tx_data_dec_ref(tdata) error_message = "Could not send response" if status != 0: raise PJSIPError(error_message, status) raise finally: with nogil: pj_mutex_unlock(lock) return 0 cdef int process_incoming_transfer(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1: global _incoming_transfer_cb global _event_hdr_name cdef int status, status2 cdef dict rdata_dict = dict(obj=self) cdef pjsip_tx_data *tdata cdef pjsip_transaction *initial_tsx cdef Timer timer cdef char *error_message if self._transfer_usage != NULL: with nogil: status = pjsip_endpt_create_response(ua._pjsip_endpoint._obj, rdata, 480, NULL, &tdata) if status != 0: error_message = "Could not create response" else: status = pjsip_endpt_send_response2(ua._pjsip_endpoint._obj, rdata, tdata, NULL, NULL) if status != 0: pjsip_tx_data_dec_ref(tdata) error_message = "Could not send response" if status != 0: raise PJSIPError(error_message, status) return 0 _pjsip_msg_to_dict(rdata.msg_info.msg, rdata_dict) try: refer_to_hdr = rdata_dict["headers"]["Refer-To"] SIPURI.parse(refer_to_hdr.uri) except (KeyError, SIPCoreError): with nogil: status = pjsip_endpt_create_response(ua._pjsip_endpoint._obj, rdata, 400, NULL, &tdata) if status != 0: error_message = "Could not create response" else: status = pjsip_endpt_send_response2(ua._pjsip_endpoint._obj, rdata, tdata, NULL, NULL) if status != 0: pjsip_tx_data_dec_ref(tdata) error_message = "Could not send response" if status != 0: raise PJSIPError(error_message, status) return 0 try: self._set_transfer_state("INCOMING") _add_event("SIPInvitationTransferNewIncoming", rdata_dict) # PJSIP event framework needs an Event header, even if it's not needed for REFER, so we insert a fake one event_header = pjsip_msg_find_hdr_by_name(rdata.msg_info.msg, &_event_hdr_name.pj_str, NULL) if event_header == NULL: event_header = pjsip_event_hdr_create(rdata.tp_info.pool) event_header.event_type = _refer_event.pj_str pjsip_msg_add_hdr(rdata.msg_info.msg, event_header) initial_tsx = pjsip_rdata_get_tsx(rdata) with nogil: status = pjsip_evsub_create_uas(self._dialog, &_incoming_transfer_cb, rdata, 0, &self._transfer_usage) if status != 0: pjsip_tsx_terminate(initial_tsx, 500) error_message = "Could not create incoming REFER session" else: self._transfer_usage_role = PJSIP_ROLE_UAS pjsip_evsub_set_mod_data(self._transfer_usage, ua._event_module.id, self.weakref) status = pjsip_dlg_create_response(self._dialog, rdata, 202, NULL, &tdata) if status != 0: pjsip_tsx_terminate(initial_tsx, 500) error_message = "Could not create response for incoming REFER" else: pjsip_evsub_update_expires(self._transfer_usage, 90) status = pjsip_dlg_send_response(self._dialog, initial_tsx, tdata) if status != 0: status2 = pjsip_dlg_modify_response(self._dialog, tdata, 500, NULL) if status2 != 0: error_message = "Could not modify response" status = status2 else: pjsip_tx_data_dec_ref(tdata) # pjsip_dlg_modify_response() increases ref count unnecessarily error_message = "Could not send response" if status != 0: raise PJSIPError(error_message, status) except PJSIPError, e: code = 0 reason = e.args[0] if self._transfer_usage != NULL: with nogil: pjsip_evsub_terminate(self._transfer_usage, 0) # Manually trigger the state callback since we handle the timeout ourselves state_timer = TransferStateCallbackTimer("TERMINATED", code, reason) state_timer.schedule(0, self._transfer_cb_state, self) raise else: self._set_transfer_state("ACTIVE") _add_event("SIPInvitationTransferDidStart", dict(obj=self)) timer = Timer() timer.schedule(0, self._start_incoming_transfer, self) return 0 cdef int process_incoming_options(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1: cdef pjsip_tx_data *tdata cdef pjsip_transaction *initial_tsx cdef int status cdef char *error_message initial_tsx = pjsip_rdata_get_tsx(rdata) with nogil: status = pjsip_dlg_create_response(self._dialog, rdata, 200, NULL, &tdata) if status != 0: pjsip_tsx_terminate(initial_tsx, 500) error_message = "Could not create response for incoming OPTIONS" else: status = pjsip_dlg_send_response(self._dialog, initial_tsx, tdata) if status != 0: error_message = "Could not send response" if status != 0: raise PJSIPError(error_message, status) def send_invite(self, SIPURI request_uri not None, FromHeader from_header not None, ToHeader to_header not None, RouteHeader route_header not None, ContactHeader contact_header not None, SDPSession sdp not None, Credentials credentials=None, list extra_headers not None=list(), timeout=None): cdef int status cdef pj_mutex_t *lock = self._lock cdef pjmedia_sdp_session *local_sdp cdef pjsip_cred_info *cred_info cdef pjsip_replaces_hdr *pj_replaces_hdr cdef pjsip_route_hdr *route_set cdef pjsip_tx_data *tdata cdef PJSIPUA ua cdef PJSTR contact_str cdef PJSTR from_header_str cdef PJSTR to_header_str cdef PJSTR request_uri_str ua = _get_ua() with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: route_set = &self._route_set if self.state is not None: raise SIPCoreInvalidStateError('Can only transition to the "outgoing" state from the "None" state, currently in the "%s" state' % self.state) if timeout is not None and timeout <= 0: raise ValueError("Timeout value must be positive") self.transport = route_header.uri.transport self.direction = "outgoing" self.credentials = FrozenCredentials.new(credentials) if credentials is not None else None self.request_uri = FrozenSIPURI.new(request_uri) self.route_header = FrozenRouteHeader.new(route_header) self.route_header.uri.parameters.dict[b"lr"] = None # always send lr parameter in Route header self.route_header.uri.parameters.dict[b"hide"] = None # always hide Route header self.local_contact_header = FrozenContactHeader.new(contact_header) self.sdp.proposed_local = FrozenSDPSession.new(sdp) if sdp is not None else None from_header_parameters = from_header.parameters.copy() from_header_parameters.pop("tag", None) from_header.parameters = {} from_header_str = PJSTR(from_header.body) to_header_parameters = to_header.parameters.copy() to_header_parameters.pop("tag", None) to_header.parameters = {} to_header_str = PJSTR(to_header.body) contact_str = PJSTR(str(self.local_contact_header.body)) request_uri_str = PJSTR(str(request_uri)) with nogil: status = pjsip_dlg_create_uac(pjsip_ua_instance(), &from_header_str.pj_str, &contact_str.pj_str, &to_header_str.pj_str, &request_uri_str.pj_str, &self._dialog) if status != 0: raise PJSIPError("Could not create dialog for outgoing INVITE session", status) with nogil: pjsip_dlg_inc_lock(self._dialog) if contact_header.expires is not None: self._dialog.local.contact.expires = contact_header.expires if contact_header.q is not None: self._dialog.local.contact.q1000 = int(contact_header.q*1000) contact_parameters = contact_header.parameters.copy() contact_parameters.pop("q", None) contact_parameters.pop("expires", None) _dict_to_pjsip_param(contact_parameters, &self._dialog.local.contact.other_param, self._dialog.pool) _dict_to_pjsip_param(from_header_parameters, &self._dialog.local.info.other_param, self._dialog.pool) _dict_to_pjsip_param(to_header_parameters, &self._dialog.remote.info.other_param, self._dialog.pool) self.from_header = FrozenFromHeader_create(self._dialog.local.info) self.to_header = FrozenToHeader.new(to_header) self.call_id = _pj_str_to_str(self._dialog.call_id.id) local_sdp = self.sdp.proposed_local.get_sdp_session() if sdp is not None else NULL with nogil: status = pjsip_inv_create_uac(self._dialog, local_sdp, 0, &self._invite_session) if status != 0: raise PJSIPError("Could not create outgoing INVITE session", status) self._invite_session.sdp_neg_flags = PJMEDIA_SDP_NEG_ALLOW_MEDIA_CHANGE self._invite_session.mod_data[ua._module.id] = self.weakref if self.credentials is not None: cred_info = self.credentials.get_cred_info() with nogil: status = pjsip_auth_clt_set_credentials(&self._dialog.auth_sess, 1, cred_info) if status != 0: raise PJSIPError("Could not set credentials for INVITE session", status) _BaseRouteHeader_to_pjsip_route_hdr(self.route_header, &self._route_header, self._dialog.pool) pj_list_insert_after( &self._route_set, &self._route_header) with nogil: status = pjsip_dlg_set_route_set(self._dialog, route_set) if status != 0: raise PJSIPError("Could not set route for INVITE session", status) with nogil: status = pjsip_inv_invite(self._invite_session, &tdata) if status != 0: raise PJSIPError("Could not create INVITE message", status) replaces_headers = [header for header in extra_headers if isinstance(header, BaseReplacesHeader)] if len(replaces_headers) > 1: raise SIPCoreError("Only one Replaces header is allowed") try: replaces_header = replaces_headers[0] except IndexError: pass else: extra_headers.remove(replaces_header) pj_replaces_hdr = pjsip_replaces_hdr_create(self._dialog.pool) _str_to_pj_str(replaces_header.call_id, &pj_replaces_hdr.call_id) _str_to_pj_str(replaces_header.to_tag, &pj_replaces_hdr.to_tag) _str_to_pj_str(replaces_header.from_tag, &pj_replaces_hdr.from_tag) _dict_to_pjsip_param(replaces_header.parameters, &pj_replaces_hdr.other_param, self._dialog.pool) pjsip_msg_add_hdr(tdata.msg, pj_replaces_hdr) _add_headers_to_tdata(tdata, extra_headers) with nogil: status = pjsip_inv_send_msg(self._invite_session, tdata) if status != 0: raise PJSIPError("Could not send initial INVITE", status) if timeout is not None: self._timer = Timer() self._timer.schedule(timeout, self._cb_timer_disconnect, self) with nogil: pjsip_dlg_dec_lock(self._dialog) except Exception, e: if isinstance(e, PJSIPError) and e.errno == EADDRNOTAVAIL: self._invite_session = NULL pjsip_dlg_dec_lock(self._dialog) self._dialog = NULL raise if self._invite_session != NULL: pjsip_inv_terminate(self._invite_session, 500, 0) self._invite_session = NULL elif self._dialog != NULL: pjsip_dlg_dec_lock(self._dialog) self._dialog = NULL raise finally: with nogil: pj_mutex_unlock(lock) def send_response(self, int code, str reason=None, BaseContactHeader contact_header=None, BaseSDPSession sdp=None, list extra_headers not None=list()): cdef int status cdef int clean_tdata = 0 cdef pj_mutex_t *lock = self._lock cdef pj_str_t reason_str cdef pjmedia_sdp_session_ptr_const lsdp = NULL cdef pjmedia_sdp_session *local_sdp cdef pjsip_inv_session *invite_session cdef pjsip_msg_body *body cdef pjsip_tx_data *tdata cdef PJSIPUA ua ua = _get_ua() with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: invite_session = self._invite_session if reason is not None: _str_to_pj_str(reason, &reason_str) if self.state not in ("incoming", "early", "connected"): raise SIPCoreInvalidStateError('Can only send response from the "incoming", "early" and "connected" states current in the "%s" state.' % self.state) if self.state == "early" and self.direction != "incoming": raise SIPCoreInvalidStateError('Cannot send response in the "early" state for an outgoing INVITE') if self.state == "connected" and self.sub_state not in ("received_proposal", "received_proposal_request"): raise SIPCoreInvalidStateError('Cannot send response in the "connected" state if a proposal has not been received') if contact_header is not None: self._update_contact_header(contact_header) if 200 <= code < 300 and sdp is None: raise SIPCoreError("Local SDP needs to be set for a positive response") if code >= 300 and sdp is not None: raise SIPCoreError("Local SDP cannot be specified for a negative response") self.sdp.proposed_local = FrozenSDPSession.new(sdp) if sdp is not None else None local_sdp = self.sdp.proposed_local.get_sdp_session() if sdp is not None else NULL if sdp is not None and self.sdp.proposed_remote is None: # There was no remote proposal, this is a reply with an offer with nogil: status = pjmedia_sdp_neg_modify_local_offer(self._dialog.pool, invite_session.neg, local_sdp) if status != 0: raise PJSIPError("Could not modify local SDP offer", status) # Retrieve the "fixed" offer from negotiator pjmedia_sdp_neg_get_neg_local(invite_session.neg, &lsdp) local_sdp = lsdp with nogil: status = pjsip_inv_answer(invite_session, code, &reason_str if reason is not None else NULL, local_sdp, &tdata) if status != 0: raise PJSIPError("Could not create %d reply to INVITE" % code, status) _add_headers_to_tdata(tdata, extra_headers) with nogil: status = pjsip_inv_send_msg(invite_session, tdata) if status != 0: exc = PJSIPError("Could not send %d response" % code, status) if sdp is not None and self.sdp.proposed_remote is not None and exc.errno in (EADDRNOTAVAIL, ENETUNREACH): self._failed_response = 1 raise exc self._failed_response = 0 finally: with nogil: pj_mutex_unlock(lock) def send_reinvite(self, BaseContactHeader contact_header=None, BaseSDPSession sdp=None, list extra_headers not None=list()): cdef int status cdef pj_mutex_t *lock = self._lock cdef pjmedia_sdp_session *local_sdp cdef pjsip_inv_session *invite_session cdef pjsip_tx_data *tdata cdef PJSIPUA ua ua = _get_ua() with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: invite_session = self._invite_session if self.state != "connected": raise SIPCoreError('Can only send re-INVITE in "connected" state, not "%s" state' % self.state) if self.sub_state != "normal": raise SIPCoreError('Can only send re-INVITE if no another re-INVITE transaction is active') if contact_header is not None: self._update_contact_header(contact_header) self.sdp.proposed_local = FrozenSDPSession.new(sdp) if sdp is not None else self.sdp.active_local local_sdp = self.sdp.proposed_local.get_sdp_session() with nogil: status = pjsip_inv_reinvite(invite_session, NULL, local_sdp, &tdata) if status != 0: raise PJSIPError("Could not create re-INVITE message", status) _add_headers_to_tdata(tdata, extra_headers) with nogil: status = pjsip_inv_send_msg(invite_session, tdata) if status != 0: raise PJSIPError("Could not send re-INVITE", status) self._failed_response = 0 # TODO: use a callback tiner here instead? self._reinvite_transaction = self._invite_session.invite_tsx self.sub_state = "sent_proposal" event_dict = dict(obj=self, prev_state="connected", state="connected", prev_sub_state="normal", sub_state="sent_proposal", originator="local") _pjsip_msg_to_dict(tdata.msg, event_dict) _add_event("SIPInvitationChangedState", event_dict) finally: with nogil: pj_mutex_unlock(lock) def cancel_reinvite(self): cdef int status cdef pj_mutex_t *lock = self._lock cdef pjsip_inv_session *invite_session cdef pjsip_tx_data *tdata cdef PJSIPUA ua ua = _get_ua() with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: invite_session = self._invite_session if not self.sub_state == "sent_proposal": raise SIPCoreError("re-INVITE can only be cancelled if INVITE session is in 'sent_proposal' sub state") if self._invite_session == NULL: raise SIPCoreError("INVITE session is not active") if self._reinvite_transaction == NULL: raise SIPCoreError("there is no active re-INVITE transaction") with nogil: status = pjsip_inv_cancel_reinvite(invite_session, &tdata) if status != 0: raise PJSIPError("Could not create message to CANCEL re-INVITE transaction", status) if tdata != NULL: with nogil: status = pjsip_inv_send_msg(invite_session, tdata) if status != 0: raise PJSIPError("Could not send %s" % _pj_str_to_str(tdata.msg.line.req.method.name), status) finally: with nogil: pj_mutex_unlock(lock) def transfer(self, SIPURI target_uri, object replaced_dialog_id=None, list extra_headers not None=list()): global _refer_event global _refer_method cdef int status cdef PJSIPUA ua cdef pj_mutex_t *lock = self._lock cdef pjsip_method refer_method cdef pjsip_tx_data *tdata cdef dict tdata_dict = dict(obj=self) ua = _get_ua() with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: if self.state != "connected": raise SIPCoreError('Can only start transfer in "connected" state, not "%s" state' % self.state) if self._transfer_usage != NULL: raise SIPCoreError('Another transfer is in progress') with nogil: status = pjsip_evsub_create_uac(self._dialog, &_transfer_cb, &_refer_event.pj_str, PJSIP_EVSUB_NO_EVENT_ID, &self._transfer_usage) if status != 0: raise PJSIPError("Could not create REFER", status) self._transfer_usage_role = PJSIP_ROLE_UAC pjsip_evsub_set_mod_data(self._transfer_usage, ua._event_module.id, self.weakref) pjsip_method_init_np(&refer_method, &_refer_method.pj_str) with nogil: status = pjsip_evsub_initiate(self._transfer_usage, &refer_method, -1, &tdata) if status != 0: raise PJSIPError("Could not create REFER message", status) if replaced_dialog_id is not None and None not in replaced_dialog_id: target_uri.headers["Replaces"] = "%s;from-tag=%s;to-tag=%s" % replaced_dialog_id refer_to_header = ReferToHeader(str(target_uri)) _add_headers_to_tdata(tdata, [refer_to_header, Header('Referred-By', str(self.local_identity.uri))]) _add_headers_to_tdata(tdata, extra_headers) # We can't remove the Event header or PJSIP will fail to match responses to this request _remove_headers_from_tdata(tdata, ["Expires"]) with nogil: status = pjsip_evsub_send_request(self._transfer_usage, tdata) if status != 0: raise PJSIPError("Could not send REFER message", status) _pjsip_msg_to_dict(tdata.msg, tdata_dict) _add_event("SIPInvitationTransferNewOutgoing", tdata_dict) self._transfer_timeout_timer = Timer() self._transfer_timeout_timer.schedule(90, self._transfer_cb_timeout_timer, self) finally: with nogil: pj_mutex_unlock(lock) def notify_transfer_progress(self, int code, str reason=None): cdef int status cdef PJSIPUA ua cdef pj_mutex_t *lock = self._lock ua = _get_ua() with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: if self._transfer_usage == NULL: raise SIPCoreError("No transfer is in progress") if self._transfer_usage_role != PJSIP_ROLE_UAS: raise SIPCoreError("Transfer progress can only be notified by the transfer UAS") self._set_sipfrag_payload(code, reason) if 200 <= code < 700: self._terminate_transfer_uas() else: self._send_notify() finally: with nogil: pj_mutex_unlock(lock) def end(self, list extra_headers not None=list(), timeout=None): cdef int status cdef pj_mutex_t *lock = self._lock cdef pjsip_inv_session *invite_session cdef pjsip_tx_data *tdata cdef PJSIPUA ua ua = _get_ua() with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: invite_session = self._invite_session if self.state == "disconnected": return if self.state == "disconnecting": raise SIPCoreError('INVITE session is already in the "disconnecting" state') if self._invite_session == NULL: raise SIPCoreError("INVITE session is not active") if self.state not in ("outgoing", "early", "connecting", "connected"): raise SIPCoreError('Can only end the INVITE dialog from the "outgoing", "early", "connecting" and "connected" states' + 'current in the "%s" state.' % self.state) if self.state == "early" and self.direction != "outgoing": raise SIPCoreError('Cannot end incoming INVITE dialog while in the "early" state') if timeout is not None and timeout <= 0: raise ValueError("Timeout value cannot be negative") # End ongoing transfer self._terminate_transfer() with nogil: status = pjsip_inv_end_session(invite_session, 0, NULL, &tdata) if status != 0: raise PJSIPError("Could not create message to end INVITE session", status) if tdata != NULL: _add_headers_to_tdata(tdata, extra_headers) with nogil: status = pjsip_inv_send_msg(invite_session, tdata) if status != 0: raise PJSIPError("Could not send %s" % _pj_str_to_str(tdata.msg.line.req.method.name), status) if self._timer is not None: self._timer.cancel() self._timer = None if timeout is not None and timeout > 0: self._timer = Timer() self._timer.schedule(timeout, self._cb_timer_disconnect, self) event_dict = dict(obj=self, prev_state=self.state, state="disconnecting", originator="local") if self.state == "connected": event_dict["prev_sub_state"] = self.sub_state self.state = "disconnecting" self.sub_state = None if tdata != NULL: _pjsip_msg_to_dict(tdata.msg, event_dict) _add_event("SIPInvitationChangedState", event_dict) finally: with nogil: pj_mutex_unlock(lock) property local_identity: def __get__(self): if self.direction == 'outgoing': return self.from_header elif self.direction == 'incoming': return self.to_header else: return None property remote_identity: def __get__(self): if self.direction == 'incoming': return self.from_header elif self.direction == 'outgoing': return self.to_header else: return None property dialog_id: def __get__(self): local_tag = remote_tag = None if self.local_identity is not None: local_tag = self.local_identity.tag if self.remote_identity is not None: remote_tag = self.remote_identity.tag return DialogID(self.call_id, local_tag, remote_tag) cdef PJSIPUA _check_ua(self): try: return _get_ua() except: self.state = "disconnected" self.sub_state = None self._dialog = NULL self._invite_session = NULL self._reinvite_transaction = NULL cdef int _do_dealloc(self) except -1: cdef int status cdef pj_mutex_t *lock = self._lock cdef pjsip_inv_session *invite_session cdef PJSIPUA ua try: ua = _get_ua() except SIPCoreError: return 0 with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: invite_session = self._invite_session if self._invite_session != NULL: self._invite_session.mod_data[ua._module.id] = NULL if self.state != "disconnecting": with nogil: pjsip_inv_terminate(invite_session, 481, 0) self._dialog = NULL self._invite_session = NULL self._reinvite_transaction = NULL if self._timer is not None: self._timer.cancel() self._timer = None finally: with nogil: pj_mutex_unlock(lock) return 0 def __dealloc__(self): cdef Timer timer self._do_dealloc() if self._lock != NULL: pj_mutex_destroy(self._lock) timer = Timer() try: timer.schedule(60, deallocate_weakref, self.weakref) except SIPCoreError: pass cdef int _update_contact_header(self, BaseContactHeader contact_header) except -1: # The PJSIP functions called here don't do much, so there is no need to call them # without the gil. cdef pj_str_t contact_str_pj cdef pjsip_uri *contact contact_str = str(contact_header.uri) if contact_header.display_name: - contact_str = "%s <%s>" % (contact_header.display_name.encode('utf-8'), contact_str) - pj_strdup2_with_null(self._dialog.pool, &contact_str_pj, contact_str) + contact_str = "%s <%s>" % (contact_header.display_name, contact_str) + print('Built contact_str %s' % contact_str) + pj_strdup2_with_null(self._dialog.pool, &contact_str_pj, contact_str.encode()) contact = pjsip_parse_uri(self._dialog.pool, contact_str_pj.ptr, contact_str_pj.slen, PJSIP_PARSE_URI_AS_NAMEADDR) if contact == NULL: raise SIPCoreError("Not a valid Contact header: %s" % contact_str) self._dialog.local.contact = pjsip_contact_hdr_create(self._dialog.pool) self._dialog.local.contact.uri = contact if contact_header.expires is not None: self._dialog.local.contact.expires = contact_header.expires if contact_header.q is not None: self._dialog.local.contact.q1000 = int(contact_header.q*1000) parameters = contact_header.parameters.copy() parameters.pop("q", None) parameters.pop("expires", None) _dict_to_pjsip_param(parameters, &self._dialog.local.contact.other_param, self._dialog.pool) self.local_contact_header = FrozenContactHeader.new(contact_header) return 0 cdef int _fail(self, PJSIPUA ua) except -1: cdef Timer timer ua._handle_exception(0) if self._transfer_usage != NULL: with nogil: pjsip_evsub_terminate(self._transfer_usage, 0) pjsip_evsub_set_mod_data(self._transfer_usage, ua._event_module.id, NULL) if self._transfer_timeout_timer is not None: self._transfer_timeout_timer.cancel() self._transfer_timeout_timer = None if self._transfer_refresh_timer is not None: self._transfer_refresh_timer.cancel() self._transfer_refresh_timer = None self._transfer_usage = NULL _add_event("SIPInvitationTransferDidFail", dict(obj=self, code=0, reason="internal error")) self._invite_session.mod_data[ua._module.id] = NULL if self.state != "disconnected": event_dict = dict(obj=self, prev_state=self.state, state="disconnected", originator="local", code=0, reason="internal error", disconnect_reason="internal error") if self.state == "connected": event_dict["prev_sub_state"] = self.sub_state self.state = "disconnected" self.sub_state = None _add_event("SIPInvitationChangedState", event_dict) # calling do_dealloc from within a callback makes PJSIP crash # the handler will be executed after pjsip_endpt_handle_events returns timer = Timer() timer.schedule(0, self._cb_postpoll_fail, self) return 0 cdef int _cb_state(self, StateCallbackTimer timer) except -1: cdef int status cdef bint pjsip_error = False cdef pj_mutex_t *lock = self._lock cdef pjmedia_sdp_session_ptr_const sdp cdef pjsip_inv_session *invite_session cdef object state cdef object sub_state cdef object rdata cdef object tdata cdef object originator cdef PJSIPUA ua ua = self._check_ua() if ua is None: return 0 with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: invite_session = self._invite_session state = timer.state sub_state = timer.sub_state rdata = timer.rdata tdata = timer.tdata originator = timer.originator if state != "early" and state == self.state and sub_state == self.sub_state: return 0 if state == "connected": if self.state == "connecting" and self._sdp_neg_status != 0: self.end() return 0 if state == "disconnected" and self.state != "disconnecting": # the invite session may have been destroyed if it failed if not self._invite_session: return 0 # we either sent a cancel or a negative reply to an incoming INVITE if self._invite_session.cancelling or (self.state in ("incoming", "early") and self.direction == "incoming" and rdata is None): # we caused the disconnect so send the transition to the disconnecting state pjsip_error = True event_dict = dict(obj=self, prev_state=self.state, state="disconnecting", originator="local") self.state = "disconnecting" _add_event("SIPInvitationChangedState", event_dict) if self.direction == "outgoing" and state in ('connecting', 'connected') and self.state in ('outgoing', 'early') and rdata is not None: self.to_header = rdata['headers']['To'] if self.direction == "incoming" and state in ('connecting', 'connected') and self.state in ('incoming', 'early') and tdata is not None: self.to_header = tdata['headers']['To'] event_dict = dict(obj=self, prev_state=self.state, state=state) if self.state == "connected": event_dict["prev_sub_state"] = self.sub_state if state == "connected": event_dict["sub_state"] = sub_state event_dict["originator"] = originator if rdata is not None: event_dict.update(rdata) if tdata is not None: event_dict.update(tdata) if rdata is None and tdata is None: event_dict['headers'] = dict() event_dict['body'] = None if self.remote_user_agent is None and state in ('connecting', 'connected') and rdata is not None: if 'User-Agent' in event_dict['headers']: self.remote_user_agent = event_dict['headers']['User-Agent'].body elif 'Server' in event_dict['headers']: self.remote_user_agent = event_dict['headers']['Server'].body if state not in ('disconnecting', 'disconnected') and rdata is not None: try: self.remote_contact_header = event_dict['headers']['Contact'][0] except LookupError: pass if state == "connected": if sub_state == "received_proposal": self._reinvite_transaction = self._invite_session.invite_tsx if pjmedia_sdp_neg_get_state(self._invite_session.neg) == PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER: pjmedia_sdp_neg_get_neg_remote(self._invite_session.neg, &sdp) self.sdp.proposed_remote = FrozenSDPSession_create(sdp) elif sub_state == "sent_proposal": if pjmedia_sdp_neg_get_state(self._invite_session.neg) == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER: pjmedia_sdp_neg_get_neg_local(self._invite_session.neg, &sdp) self.sdp.proposed_local = FrozenSDPSession_create(sdp) elif sub_state == "received_proposal_request": self._reinvite_transaction = self._invite_session.invite_tsx if pjmedia_sdp_neg_get_state(self._invite_session.neg) == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER: pjmedia_sdp_neg_get_neg_local(self._invite_session.neg, &sdp) self.sdp.proposed_local = FrozenSDPSession_create(sdp) elif self.sub_state in ("received_proposal", "sent_proposal", "received_proposal_request"): if (rdata, tdata) == (None, None): event_dict['code'] = 408 event_dict['reason'] = 'Request Timeout' if pjmedia_sdp_neg_get_state(self._invite_session.neg) in (PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER, PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER): pjmedia_sdp_neg_cancel_offer(self._invite_session.neg) self._reinvite_transaction = NULL if state == "disconnected": event_dict["disconnect_reason"] = "user request" if not pjsip_error else "internal error" event_dict["code"] = self._invite_session.cause if self._invite_session.cause > 0: event_dict["reason"] = _pj_str_to_str(self._invite_session.cause_text) else: event_dict["reason"] = "" if not self._invite_session.cancelling and rdata is None and self._invite_session.cause > 0: # pjsip internally generates 408 and 503 if self._invite_session.cause == 408: if self.direction == "incoming" and self.state == "connecting": event_dict["disconnect_reason"] = "missing ACK" else: event_dict["disconnect_reason"] = "timeout" else: event_dict["disconnect_reason"] = _pj_str_to_str(self._invite_session.cause_text) elif self._invite_session.cancelling and rdata is None and self._invite_session.cause == 408 and self.state == "disconnecting": # silly pjsip sets cancelling field when we call pjsip_inv_end_session in end even if we send a BYE event_dict["disconnect_reason"] = "timeout" elif rdata is not None and 'Reason' in event_dict['headers']: try: reason = event_dict['headers']['Reason'].text if reason: event_dict["disconnect_reason"] = reason except (ValueError, IndexError): pass if self._transfer_usage != NULL: with nogil: pjsip_evsub_terminate(self._transfer_usage, 0) pjsip_evsub_set_mod_data(self._transfer_usage, ua._event_module.id, NULL) if self._transfer_timeout_timer is not None: self._transfer_timeout_timer.cancel() self._transfer_timeout_timer = None if self._transfer_refresh_timer is not None: self._transfer_refresh_timer.cancel() self._transfer_refresh_timer = None self._transfer_usage = NULL _add_event("SIPInvitationTransferDidFail", dict(obj=self, code=0, reason="invite dialog ended")) self._invite_session.mod_data[ua._module.id] = NULL self._invite_session = NULL self._dialog = NULL if self._timer is not None: self._timer.cancel() self._timer = None elif state in ("early", "connecting") and self._timer is not None: self._timer.cancel() self._timer = None self.state = state self.sub_state = sub_state _add_event("SIPInvitationChangedState", event_dict) finally: with nogil: pj_mutex_unlock(lock) return 0 cdef int _cb_sdp_done(self, SDPCallbackTimer timer) except -1: cdef int status cdef pj_mutex_t *lock = self._lock with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: if self._failed_response == 1: return 0 self._sdp_neg_status = timer.status self.sdp.proposed_local = None self.sdp.proposed_remote = None if timer.status == 0: self.sdp.active_local = timer.active_local self.sdp.active_remote = timer.active_remote if self.state in ["disconnecting", "disconnected"]: return 0 event_dict = dict(obj=self, succeeded=timer.status == 0) if timer.status == 0: event_dict["local_sdp"] = self.sdp.active_local event_dict["remote_sdp"] = self.sdp.active_remote else: event_dict["error"] = _pj_status_to_str(timer.status) _add_event("SIPInvitationGotSDPUpdate", event_dict) finally: with nogil: pj_mutex_unlock(lock) return 0 cdef int _cb_timer_disconnect(self, timer) except -1: cdef pjsip_inv_session *invite_session = self._invite_session with nogil: pjsip_inv_terminate(invite_session, 408, 1) cdef int _cb_postpoll_fail(self, timer) except -1: self._do_dealloc() cdef int _start_incoming_transfer(self, timer) except -1: cdef int status cdef pj_mutex_t *lock = self._lock cdef PJSIPUA ua ua = self._check_ua() if ua is None: return 0 with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: self._set_sipfrag_payload(100, "Trying") self._send_notify() finally: with nogil: pj_mutex_unlock(lock) return 0 cdef int _terminate_transfer(self) except -1: if self._transfer_usage == NULL: return 0 if self._transfer_usage_role == PJSIP_ROLE_UAC: self._terminate_transfer_uac() else: self._terminate_transfer_uas() cdef int _terminate_transfer_uac(self) except -1: cdef pjsip_tx_data *tdata cdef int status cdef TransferStateCallbackTimer state_timer try: with nogil: status = pjsip_evsub_initiate(self._transfer_usage, NULL, 0, &tdata) if status != 0: raise PJSIPError("Could not create SUBSCRIBE message", status) with nogil: status = pjsip_evsub_send_request(self._transfer_usage, tdata) if status != 0: raise PJSIPError("Could not send SUBSCRIBE message", status) if self._transfer_timeout_timer is not None: self._transfer_timeout_timer.cancel() self._transfer_timeout_timer = None if self._transfer_refresh_timer is not None: self._transfer_refresh_timer.cancel() self._transfer_refresh_timer = None self._transfer_timeout_timer = Timer() self._transfer_timeout_timer.schedule(1, self._transfer_cb_timeout_timer, self) except PJSIPError, e: if self._transfer_usage != NULL: code = 0 reason = e.args[0] with nogil: pjsip_evsub_terminate(self._transfer_usage, 0) # Manually trigger the state callback since we handle the timeout ourselves state_timer = TransferStateCallbackTimer("TERMINATED", code, reason) state_timer.schedule(0, self._transfer_cb_state, self) cdef int _terminate_transfer_uas(self) except -1: global sipfrag_re cdef int code cdef TransferStateCallbackTimer state_timer if self.transfer_state == "TERMINATED": return 0 self._set_transfer_state("TERMINATED") self._send_notify() with nogil: pjsip_evsub_terminate(self._transfer_usage, 0) match = sipfrag_re.match(self._sipfrag_payload.str) code = int(match.group('code')) reason = match.group('reason') state_timer = TransferStateCallbackTimer("TERMINATED", code, reason) state_timer.schedule(0, self._transfer_cb_state, self) cdef int _set_transfer_state(self, str state) except -1: cdef str prev_state prev_state = self.transfer_state self.transfer_state = state if prev_state != state: _add_event("SIPInvitationTransferChangedState", dict(obj=self, prev_state=prev_state, state=state)) cdef int _set_sipfrag_payload(self, int code, str status) except -1: cdef str content if status is None: try: status = sip_status_messages[code] except IndexError: status = "Unknown" content = "SIP/2.0 %d %s\r\n" % (code, status) self._sipfrag_payload = PJSTR(content) cdef int _send_notify(self) except -1: cdef pjsip_evsub_state state cdef pj_str_t *reason_p = NULL cdef pjsip_tx_data *tdata cdef int status cdef dict _sipfrag_version = dict(version="2.0") cdef PJSTR _content_type = PJSTR(b"message") cdef PJSTR _content_subtype = PJSTR(b"sipfrag") cdef PJSTR noresource = PJSTR(b"noresource") cdef PJSTR content if self.transfer_state == "ACTIVE": state = PJSIP_EVSUB_STATE_ACTIVE else: state = PJSIP_EVSUB_STATE_TERMINATED reason_p = &noresource.pj_str with nogil: status = pjsip_evsub_notify(self._transfer_usage, state, NULL, reason_p, &tdata) if status != 0: raise PJSIPError("Could not create NOTIFY request", status) if self.transfer_state in ("ACTIVE", "TERMINATED"): tdata.msg.body = pjsip_msg_body_create(tdata.pool, &_content_type.pj_str, &_content_subtype.pj_str, &self._sipfrag_payload.pj_str) _dict_to_pjsip_param(_sipfrag_version, &tdata.msg.body.content_type.param, tdata.pool) with nogil: status = pjsip_evsub_send_request(self._transfer_usage, tdata) if status != 0: raise PJSIPError("Could not send NOTIFY request", status) return 0 cdef int _transfer_cb_timeout_timer(self, timer) except -1: global sip_status_messages cdef int code cdef str reason cdef int status cdef TransferStateCallbackTimer state_timer cdef pj_mutex_t *lock = self._lock cdef PJSIPUA ua ua = self._check_ua() if ua is None: return 0 with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: if self._transfer_usage != NULL: code = PJSIP_SC_TSX_TIMEOUT reason = sip_status_messages[PJSIP_SC_TSX_TIMEOUT] with nogil: pjsip_evsub_terminate(self._transfer_usage, 0) # Manually trigger the state callback since we handle the timeout ourselves state_timer = TransferStateCallbackTimer("TERMINATED", code, reason) state_timer.schedule(0, self._transfer_cb_state, self) finally: with nogil: pj_mutex_unlock(lock) return 0 cdef int _transfer_cb_refresh_timer(self, timer) except -1: cdef int status cdef pj_mutex_t *lock = self._lock cdef PJSIPUA ua ua = self._check_ua() if ua is None: return 0 with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: self._terminate_transfer() finally: with nogil: pj_mutex_unlock(lock) return 0 cdef int _transfer_cb_state(self, TransferStateCallbackTimer timer) except -1: cdef int status cdef str prev_state cdef pj_mutex_t *lock = self._lock cdef PJSIPUA ua ua = self._check_ua() if ua is None: return 0 with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: prev_state = self.transfer_state self._set_transfer_state(timer.state) if timer.state == "ACCEPTED" and prev_state == "SENT": _add_event("SIPInvitationTransferDidStart", dict(obj=self)) elif timer.state == "TERMINATED": # If a NOTIFY is rejected with 408 or 481 PJSIP will erase the subscription if self._transfer_usage != NULL: pjsip_evsub_set_mod_data(self._transfer_usage, ua._event_module.id, NULL) if self._transfer_timeout_timer is not None: self._transfer_timeout_timer.cancel() self._transfer_timeout_timer = None if self._transfer_refresh_timer is not None: self._transfer_refresh_timer.cancel() self._transfer_refresh_timer = None self._transfer_usage = NULL if timer.code/100 == 2: _add_event("SIPInvitationTransferDidEnd", dict(obj=self)) else: _add_event("SIPInvitationTransferDidFail", dict(obj=self, code=timer.code, reason=timer.reason)) finally: with nogil: pj_mutex_unlock(lock) return 0 cdef int _transfer_cb_response(self, TransferResponseCallbackTimer timer) except -1: cdef int expires cdef int status cdef pj_mutex_t *lock = self._lock cdef PJSIPUA ua ua = self._check_ua() if ua is None: return 0 with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: if self._transfer_timeout_timer is not None: self._transfer_timeout_timer.cancel() self._transfer_timeout_timer = None finally: with nogil: pj_mutex_unlock(lock) return 0 cdef int _transfer_cb_notify(self, TransferRequestCallbackTimer timer) except -1: cdef pj_time_val refresh cdef int expires cdef dict notify_dict = dict(obj=self) cdef pj_mutex_t *lock = self._lock cdef PJSIPUA ua ua = self._check_ua() if ua is None: return 0 with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: sub_state_hdr = timer.rdata["headers"].get("Subscription-State", None) if self.transfer_state != "TERMINATED" and sub_state_hdr is not None and sub_state_hdr.expires > 0: if self._transfer_refresh_timer is not None: self._transfer_refresh_timer.cancel() self._transfer_refresh_timer = None expires = max(1, sub_state_hdr.expires - self.expire_warning_time, sub_state_hdr.expires/2) self._transfer_refresh_timer = Timer() self._transfer_refresh_timer.schedule(expires, self._transfer_cb_refresh_timer, self) notify_dict["request_uri"] = timer.rdata["request_uri"] notify_dict["from_header"] = timer.rdata["headers"].get("From", None) notify_dict["to_header"] = timer.rdata["headers"].get("To", None) notify_dict["headers"] = timer.rdata["headers"] notify_dict["body"] = timer.rdata["body"] content_type = notify_dict["headers"].get("Content-Type", None) notify_dict["content_type"] = content_type.content_type if content_type else None event = notify_dict["headers"].get("Event", None) notify_dict["event"] = event.event if event else None _add_event("SIPInvitationTransferGotNotify", notify_dict) finally: with nogil: pj_mutex_unlock(lock) return 0 cdef int _transfer_cb_server_timeout(self, timer) except -1: cdef int status cdef pj_mutex_t *lock = self._lock cdef PJSIPUA ua ua = self._check_ua() if ua is None: return 0 with nogil: status = pj_mutex_lock(lock) if status != 0: raise PJSIPError("failed to acquire lock", status) try: self._terminate_transfer() finally: with nogil: pj_mutex_unlock(lock) return 0 # Callback functions # cdef void _Invitation_cb_state(pjsip_inv_session *inv, pjsip_event *e) with gil: cdef pjsip_rx_data *rdata = NULL cdef pjsip_tx_data *tdata = NULL cdef object state cdef object rdata_dict = None cdef object tdata_dict = None cdef object originator = None cdef Invitation invitation cdef PJSIPUA ua cdef StateCallbackTimer timer try: ua = _get_ua() except: return try: if inv.state == PJSIP_INV_STATE_INCOMING: return if inv.mod_data[ua._module.id] != NULL: invitation = ( inv.mod_data[ua._module.id])() if invitation is None: return state = pjsip_inv_state_name(inv.state).decode().lower() sub_state = None if state == "calling": state = "outgoing" elif state == "confirmed": state = "connected" sub_state = "normal" elif state == "disconnctd": state = "disconnected" if e != NULL: if e.type == PJSIP_EVENT_TSX_STATE and e.body.tsx_state.type == PJSIP_EVENT_TX_MSG: tdata = e.body.tsx_state.src.tdata if (tdata.msg.type == PJSIP_RESPONSE_MSG and tdata.msg.line.status.code == 487 and state == "disconnected" and invitation.state in ["incoming", "early"]): return elif e.type == PJSIP_EVENT_RX_MSG: rdata = e.body.rx_msg.rdata elif e.type == PJSIP_EVENT_TSX_STATE and e.body.tsx_state.type == PJSIP_EVENT_RX_MSG: if (inv.state != PJSIP_INV_STATE_CONFIRMED or e.body.tsx_state.src.rdata.msg_info.msg.type == PJSIP_REQUEST_MSG): rdata = e.body.tsx_state.src.rdata elif e.type == PJSIP_EVENT_TSX_STATE and e.body.tsx_state.type == PJSIP_EVENT_TRANSPORT_ERROR and e.body.tsx_state.tsx.role == PJSIP_ROLE_UAC: # A transport error occurred, fake a local reply rdata_dict = dict() rdata_dict["code"] = 408 rdata_dict["reason"] = "Transport Error" rdata_dict["headers"] = dict() rdata_dict["body"] = None originator = "local" if rdata != NULL: if invitation.peer_address is None: invitation.peer_address = EndpointAddress(rdata.pkt_info.src_name, rdata.pkt_info.src_port) else: invitation.peer_address.ip = rdata.pkt_info.src_name invitation.peer_address.port = rdata.pkt_info.src_port rdata_dict = dict() _pjsip_msg_to_dict(rdata.msg_info.msg, rdata_dict) originator = "remote" if tdata != NULL: tdata_dict = dict() _pjsip_msg_to_dict(tdata.msg, tdata_dict) originator = "local" try: timer = StateCallbackTimer(state, sub_state, rdata_dict, tdata_dict, originator) timer.schedule(0, invitation._cb_state, invitation) except: invitation._fail(ua) except: ua._handle_exception(1) cdef void _Invitation_cb_sdp_done(pjsip_inv_session *inv, int status) with gil: cdef Invitation invitation cdef PJSIPUA ua cdef SDPCallbackTimer timer cdef pjmedia_sdp_session_ptr_const sdp try: ua = _get_ua() except: return try: if inv.mod_data[ua._module.id] != NULL: invitation = ( inv.mod_data[ua._module.id])() if invitation is None: return if status == 0: if pjmedia_sdp_neg_get_active_local(invitation._invite_session.neg, &sdp) == 0: local_sdp = SDPSession_create(sdp) else: local_sdp = None if pjmedia_sdp_neg_get_active_remote(invitation._invite_session.neg, &sdp) == 0: remote_sdp = SDPSession_create(sdp) else: remote_sdp = None if local_sdp is None or remote_sdp is None: active_local = None active_remote = None else: if len(local_sdp.media) > len(remote_sdp.media): local_sdp.media = local_sdp.media[:len(remote_sdp.media)] if len(remote_sdp.media) > len(local_sdp.media): remote_sdp.media = remote_sdp.media[:len(local_sdp.media)] for index, local_media in enumerate(local_sdp.media): remote_media = remote_sdp.media[index] if not local_media.port and remote_media.port: remote_media.port = 0 if not remote_media.port and local_media.port: local_media.port = 0 active_local = FrozenSDPSession.new(local_sdp) active_remote = FrozenSDPSession.new(remote_sdp) else: active_local = None active_remote = None try: timer = SDPCallbackTimer(status, active_local, active_remote) timer.schedule(0, invitation._cb_sdp_done, invitation) except: invitation._fail(ua) except: ua._handle_exception(1) cdef int _Invitation_cb_rx_reinvite(pjsip_inv_session *inv, pjmedia_sdp_session_ptr_const offer, pjsip_rx_data *rdata) with gil: cdef int status cdef pjsip_tx_data *answer_tdata cdef object rdata_dict = None cdef Invitation invitation cdef PJSIPUA ua cdef StateCallbackTimer timer try: ua = _get_ua() except: return 1 try: if inv.mod_data[ua._module.id] != NULL: invitation = ( inv.mod_data[ua._module.id])() if invitation is None: return 1 if invitation.peer_address is None: invitation.peer_address = EndpointAddress(rdata.pkt_info.src_name, rdata.pkt_info.src_port) else: invitation.peer_address.ip = rdata.pkt_info.src_name invitation.peer_address.port = rdata.pkt_info.src_port rdata_dict = dict() _pjsip_msg_to_dict(rdata.msg_info.msg, rdata_dict) with nogil: status = pjsip_inv_initial_answer(inv, rdata, 100, NULL, NULL, &answer_tdata) if status != 0: raise PJSIPError("Could not create initial (unused) response to re-INVITE", status) with nogil: pjsip_tx_data_dec_ref(answer_tdata) if offer != NULL: sub_state = "received_proposal" else: sub_state = "received_proposal_request" try: timer = StateCallbackTimer("connected", sub_state, rdata_dict, None, "remote") timer.schedule(0, invitation._cb_state, invitation) except: invitation._fail(ua) return 1 return 0 except: ua._handle_exception(1) return 1 cdef void _Invitation_cb_tsx_state_changed(pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_event *e) with gil: cdef pjsip_rx_data *rdata = NULL cdef pjsip_tx_data *tdata = NULL cdef object rdata_dict = None cdef object tdata_dict = None cdef object originator = None cdef Invitation invitation cdef PJSIPUA ua cdef StateCallbackTimer timer cdef TransferRequestCallbackTimer transfer_timer try: ua = _get_ua() except: return try: if tsx == NULL or e == NULL: return if e.type == PJSIP_EVENT_TSX_STATE and e.body.tsx_state.type == PJSIP_EVENT_RX_MSG: rdata = e.body.tsx_state.src.rdata if e.type == PJSIP_EVENT_TSX_STATE and e.body.tsx_state.type == PJSIP_EVENT_TX_MSG: tdata = e.body.tsx_state.src.tdata if inv.mod_data[ua._module.id] != NULL: invitation = ( inv.mod_data[ua._module.id])() if invitation is None: return if rdata != NULL: if invitation.peer_address is None: invitation.peer_address = EndpointAddress(rdata.pkt_info.src_name, rdata.pkt_info.src_port) else: invitation.peer_address.ip = rdata.pkt_info.src_name invitation.peer_address.port = rdata.pkt_info.src_port if ((tsx.state == PJSIP_TSX_STATE_TERMINATED or tsx.state == PJSIP_TSX_STATE_COMPLETED) and (inv.neg != NULL and pjmedia_sdp_neg_get_state(inv.neg) in (PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER, PJMEDIA_SDP_NEG_STATE_DONE)) and invitation._reinvite_transaction != NULL and invitation._reinvite_transaction == tsx): if rdata != NULL: rdata_dict = dict() _pjsip_msg_to_dict(rdata.msg_info.msg, rdata_dict) originator = "remote" if tdata != NULL: tdata_dict = dict() _pjsip_msg_to_dict(tdata.msg, tdata_dict) originator = "local" try: timer = StateCallbackTimer("connected", "normal", rdata_dict, tdata_dict, originator) timer.schedule(0, invitation._cb_state, invitation) except: invitation._fail(ua) elif (invitation.state in ("incoming", "early") and invitation.direction == "incoming" and rdata != NULL and rdata.msg_info.msg.type == PJSIP_REQUEST_MSG and rdata.msg_info.msg.line.req.method.id == PJSIP_CANCEL_METHOD): rdata_dict = dict() _pjsip_msg_to_dict(rdata.msg_info.msg, rdata_dict) originator = "remote" try: timer = StateCallbackTimer("disconnected", None, rdata_dict, None, originator) timer.schedule(0, invitation._cb_state, invitation) except: invitation._fail(ua) elif (tsx.role == PJSIP_ROLE_UAS and tsx.state == PJSIP_TSX_STATE_TRYING and rdata != NULL and rdata.msg_info.msg.type == PJSIP_REQUEST_MSG and _pj_str_to_str(tsx.method.name) == "REFER"): invitation.process_incoming_transfer(ua, rdata) elif (tsx.role == PJSIP_ROLE_UAS and tsx.state == PJSIP_TSX_STATE_TRYING and rdata != NULL and rdata.msg_info.msg.type == PJSIP_REQUEST_MSG and tsx.method.id == PJSIP_OPTIONS_METHOD): invitation.process_incoming_options(ua, rdata) except: ua._handle_exception(1) cdef void _Invitation_cb_new(pjsip_inv_session *inv, pjsip_event *e) with gil: # As far as I can tell this is never actually called! pass cdef void _Invitation_transfer_cb_state(pjsip_evsub *sub, pjsip_event *event) with gil: cdef void *invitation_void cdef Invitation invitation cdef object state cdef int code = 0 cdef dict event_dict = dict() cdef str reason = None cdef pjsip_rx_data *rdata = NULL cdef PJSIPUA ua try: ua = _get_ua() except: return try: invitation_void = pjsip_evsub_get_mod_data(sub, ua._event_module.id) if invitation_void == NULL: return invitation = ( invitation_void)() if invitation is None: return state = pjsip_evsub_get_state_name(sub) if (event != NULL and event.type == PJSIP_EVENT_TSX_STATE and (event.body.tsx_state.tsx.state == PJSIP_TSX_STATE_COMPLETED or event.body.tsx_state.tsx.state == PJSIP_TSX_STATE_TERMINATED)): if state == "TERMINATED": if event.body.tsx_state.tsx.role == PJSIP_ROLE_UAC: code = event.body.tsx_state.tsx.status_code reason = _pj_str_to_str(event.body.tsx_state.tsx.status_text) else: reason = "Referral has expired" if event.body.tsx_state.type == PJSIP_EVENT_RX_MSG and _pj_str_to_str(event.body.tsx_state.tsx.method.name) == "NOTIFY": # Extract code and reason from the sipfrag payload rdata = event.body.tsx_state.src.rdata if rdata != NULL: _pjsip_msg_to_dict(rdata.msg_info.msg, event_dict) if event_dict.get('body', None) is not None: match = sipfrag_re.match(event_dict['body']) if match: code = int(match.group('code')) reason = match.group('reason') try: timer = TransferStateCallbackTimer(state, code, reason) timer.schedule(0, invitation._transfer_cb_state, invitation) except: invitation._fail(ua) except: ua._handle_exception(1) cdef void _Invitation_transfer_cb_tsx(pjsip_evsub *sub, pjsip_transaction *tsx, pjsip_event *event) with gil: cdef void *invitation_void cdef Invitation invitation cdef pjsip_rx_data *rdata cdef PJSIPUA ua try: ua = _get_ua() except: return try: invitation_void = pjsip_evsub_get_mod_data(sub, ua._event_module.id) if invitation_void == NULL: return invitation = ( invitation_void)() if invitation is None: return if (event != NULL and event.type == PJSIP_EVENT_TSX_STATE and event.body.tsx_state.type == PJSIP_EVENT_RX_MSG and event.body.tsx_state.tsx.role == PJSIP_ROLE_UAC and event.body.tsx_state.tsx.state == PJSIP_TSX_STATE_COMPLETED and _pj_str_to_str(event.body.tsx_state.tsx.method.name) in ("REFER", "SUBSCRIBE") and event.body.tsx_state.tsx.status_code/100 == 2): rdata = event.body.tsx_state.src.rdata if rdata != NULL: rdata_dict = dict() _pjsip_msg_to_dict(rdata.msg_info.msg, rdata_dict) try: timer = TransferResponseCallbackTimer(_pj_str_to_bytes(event.body.tsx_state.tsx.method.name), rdata_dict) timer.schedule(0, invitation._transfer_cb_response, invitation) except: invitation._fail(ua) except: ua._handle_exception(1) cdef void _Invitation_transfer_cb_notify(pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body) with gil: cdef void *invitation_void cdef Invitation invitation cdef TransferRequestCallbackTimer timer cdef PJSIPUA ua try: ua = _get_ua() except: return try: invitation_void = pjsip_evsub_get_mod_data(sub, ua._event_module.id) if invitation_void == NULL: return invitation = ( invitation_void)() if invitation is None: return if rdata != NULL: rdata_dict = dict() _pjsip_msg_to_dict(rdata.msg_info.msg, rdata_dict) try: timer = TransferRequestCallbackTimer(rdata_dict) timer.schedule(0, invitation._transfer_cb_notify, invitation) except: invitation._fail(ua) except: ua._handle_exception(1) cdef void _Invitation_transfer_cb_refresh(pjsip_evsub *sub) with gil: # We want to handle the refresh timer oursevles, ignore the PJSIP provided timer pass cdef void _Invitation_transfer_in_cb_rx_refresh(pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body) with gil: cdef void *invitation_void cdef dict rdata_dict cdef pjsip_expires_hdr *expires_header cdef Invitation invitation cdef Timer timer cdef PJSIPUA ua try: ua = _get_ua() except: return try: invitation_void = pjsip_evsub_get_mod_data(sub, ua._event_module.id) if invitation_void == NULL: p_st_code[0] = 481 return invitation = ( invitation_void)() if invitation is None: p_st_code[0] = 481 return expires_header = pjsip_msg_find_hdr(rdata.msg_info.msg, PJSIP_H_EXPIRES, NULL) if expires_header != NULL and expires_header.ivalue == 0: try: timer = Timer() timer.schedule(0, invitation._terminate_transfer, invitation) except: invitation._fail(ua) p_st_code[0] = 200 return p_st_code[0] = 501 except: ua._handle_exception(1) cdef void _Invitation_transfer_in_cb_server_timeout(pjsip_evsub *sub) with gil: cdef void *invitation_void cdef Invitation invitation cdef Timer timer cdef PJSIPUA ua try: ua = _get_ua() except: return try: invitation_void = pjsip_evsub_get_mod_data(sub, ua._event_module.id) if invitation_void == NULL: return invitation = ( invitation_void)() if invitation is None: return try: timer = Timer() timer.schedule(0, invitation._transfer_cb_server_timeout, invitation) except: invitation._fail(ua) except: ua._handle_exception(1) cdef void _Invitation_transfer_in_cb_tsx(pjsip_evsub *sub, pjsip_transaction *tsx, pjsip_event *event) with gil: cdef void *invitation_void cdef Invitation invitation cdef PJSIPUA ua cdef pjsip_rx_data *rdata cdef dict event_dict cdef int code cdef str reason cdef TransferStateCallbackTimer timer try: ua = _get_ua() except: return try: invitation_void = pjsip_evsub_get_mod_data(sub, ua._event_module.id) if invitation_void == NULL: return invitation = ( invitation_void)() if invitation is None: return if (event != NULL and event.type == PJSIP_EVENT_TSX_STATE and event.body.tsx_state.tsx.role == PJSIP_ROLE_UAC and _pj_str_to_str(event.body.tsx_state.tsx.method.name) == "NOTIFY" and event.body.tsx_state.tsx.state in (PJSIP_TSX_STATE_COMPLETED, PJSIP_TSX_STATE_TERMINATED)): code = event.body.tsx_state.tsx.status_code reason = _pj_str_to_str(event.body.tsx_state.tsx.status_text) if code in (408, 481) or code/100==7: # Be careful! PJSIP will erase the subscription timer = TransferStateCallbackTimer("TERMINATED", code, reason) timer.schedule(0, invitation._transfer_cb_state, invitation) except: ua._handle_exception(1) # Globals # cdef pjsip_inv_callback _inv_cb _inv_cb.on_state_changed = _Invitation_cb_state _inv_cb.on_media_update = _Invitation_cb_sdp_done _inv_cb.on_rx_reinvite = _Invitation_cb_rx_reinvite _inv_cb.on_tsx_state_changed = _Invitation_cb_tsx_state_changed _inv_cb.on_new_session = _Invitation_cb_new cdef pjsip_evsub_user _transfer_cb _transfer_cb.on_evsub_state = _Invitation_transfer_cb_state _transfer_cb.on_tsx_state = _Invitation_transfer_cb_tsx _transfer_cb.on_rx_notify = _Invitation_transfer_cb_notify _transfer_cb.on_client_refresh = _Invitation_transfer_cb_refresh cdef pjsip_evsub_user _incoming_transfer_cb _incoming_transfer_cb.on_rx_refresh = _Invitation_transfer_in_cb_rx_refresh _incoming_transfer_cb.on_server_timeout = _Invitation_transfer_in_cb_server_timeout _incoming_transfer_cb.on_tsx_state = _Invitation_transfer_in_cb_tsx diff --git a/sipsimple/core/_core.pxd b/sipsimple/core/_core.pxd index d798c2f3..7aa4766b 100644 --- a/sipsimple/core/_core.pxd +++ b/sipsimple/core/_core.pxd @@ -1,2640 +1,2645 @@ # cython: language_level=2 cdef extern from *: ctypedef char *char_ptr_const "const char *" enum: PJ_SVN_REV "PJ_SVN_REVISION" # system imports from libc.stdlib cimport malloc, free from libc.string cimport memcpy # Python C imports from cpython.float cimport PyFloat_AsDouble from cpython.ref cimport Py_INCREF, Py_DECREF from cpython.bytes cimport PyBytes_FromString, PyBytes_FromStringAndSize, PyBytes_AsString, PyBytes_Size cdef extern from "Python.h": object PyUnicode_FromString(const char *u) # PJSIP imports cdef extern from "pjlib.h": # constants enum: PJ_ERR_MSG_SIZE enum: PJ_ERRNO_START_SYS PJ_EBUG PJ_ETOOMANY enum: PJ_MAX_OBJ_NAME # init / shutdown int pj_init() nogil void pj_shutdown() nogil # version char *pj_get_version() nogil # string struct pj_str_t: char *ptr int slen ctypedef pj_str_t *pj_str_ptr_const "const pj_str_t *" # errors pj_str_t pj_strerror(int statcode, char *buf, int bufsize) nogil # logging enum: PJ_LOG_MAX_LEVEL enum pj_log_decoration: PJ_LOG_HAS_YEAR PJ_LOG_HAS_MONTH PJ_LOG_HAS_DAY_OF_MON PJ_LOG_HAS_TIME PJ_LOG_HAS_MICRO_SEC PJ_LOG_HAS_SENDER PJ_LOG_HAS_INDENT void pj_log_set_decor(int decor) nogil int pj_log_get_level() nogil void pj_log_set_level(int level) nogil void pj_log_set_log_func(void func(int level, char_ptr_const data, int len)) nogil # memory management struct pj_pool_t struct pj_pool_factory_policy: pass pj_pool_factory_policy pj_pool_factory_default_policy struct pj_pool_factory: pass struct pj_caching_pool: pj_pool_factory factory void pj_caching_pool_init(pj_caching_pool *ch_pool, pj_pool_factory_policy *policy, int max_capacity) nogil void pj_caching_pool_destroy(pj_caching_pool *ch_pool) nogil void *pj_pool_alloc(pj_pool_t *pool, int size) nogil void pj_pool_reset(pj_pool_t *pool) nogil pj_pool_t *pj_pool_create_on_buf(char *name, void *buf, int size) nogil pj_str_t *pj_strdup2_with_null(pj_pool_t *pool, pj_str_t *dst, char *src) nogil void pj_pool_release(pj_pool_t *pool) nogil # threads enum: PJ_THREAD_DESC_SIZE struct pj_mutex_t struct pj_rwmutex_t struct pj_thread_t int pj_mutex_create_simple(pj_pool_t *pool, char *name, pj_mutex_t **mutex) nogil int pj_mutex_create_recursive(pj_pool_t *pool, char *name, pj_mutex_t **mutex) nogil int pj_mutex_lock(pj_mutex_t *mutex) nogil int pj_mutex_unlock(pj_mutex_t *mutex) nogil int pj_mutex_destroy(pj_mutex_t *mutex) nogil int pj_rwmutex_create(pj_pool_t *pool, char *name, pj_rwmutex_t **mutex) nogil int pj_rwmutex_lock_read(pj_rwmutex_t *mutex) nogil int pj_rwmutex_lock_write(pj_rwmutex_t *mutex) nogil int pj_rwmutex_unlock_read(pj_rwmutex_t *mutex) nogil int pj_rwmutex_unlock_write(pj_rwmutex_t *mutex) nogil int pj_rwmutex_destroy(pj_rwmutex_t *mutex) nogil int pj_thread_is_registered() nogil int pj_thread_register(char *thread_name, long *thread_desc, pj_thread_t **thread) nogil # sockets enum: PJ_INET6_ADDRSTRLEN struct pj_ioqueue_t struct pj_addr_hdr: unsigned int sa_family struct pj_sockaddr_in: pass struct pj_sockaddr_in6: pass ctypedef union pj_sockaddr: pj_addr_hdr addr pj_sockaddr_in ipv4 pj_sockaddr_in6 ipv6 ctypedef pj_sockaddr *pj_sockaddr_ptr_const "const pj_sockaddr *" int pj_AF_INET() nogil int pj_AF_INET6() nogil int pj_sockaddr_in_init(pj_sockaddr_in *addr, pj_str_t *cp, int port) nogil int pj_sockaddr_get_port(pj_sockaddr *addr) nogil char *pj_sockaddr_print(pj_sockaddr *addr, char *buf, int size, unsigned int flags) nogil int pj_sockaddr_has_addr(pj_sockaddr *addr) nogil int pj_sockaddr_init(int af, pj_sockaddr *addr, pj_str_t *cp, unsigned int port) nogil int pj_inet_pton(int af, pj_str_t *src, void *dst) nogil # dns struct pj_dns_resolver int pj_dns_resolver_set_ns(pj_dns_resolver *resolver, unsigned count, pj_str_t *servers, int *ports) nogil # time struct pj_time_val: long sec long msec void pj_gettimeofday(pj_time_val *tv) nogil void pj_time_val_normalize(pj_time_val *tv) nogil # timers struct pj_timer_heap_t struct pj_timer_entry: void *user_data int id pj_timer_entry *pj_timer_entry_init(pj_timer_entry *entry, int id, void *user_data, void cb(pj_timer_heap_t *timer_heap, pj_timer_entry *entry) with gil) nogil # lists struct pj_list: void *prev void *next void pj_list_init(pj_list *node) nogil void pj_list_insert_after(pj_list *pos, pj_list *node) nogil # random void pj_srand(unsigned int seed) nogil # maths struct pj_math_stat: int n int max int min int last int mean cdef extern from "pjlib-util.h": # init int pjlib_util_init() nogil cdef extern from "pjnath.h": # init int pjnath_init() nogil # STUN enum: PJ_STUN_PORT struct pj_stun_config: pass struct pj_stun_sock_cfg: pj_sockaddr bound_addr void pj_stun_config_init(pj_stun_config *cfg, pj_pool_factory *factory, unsigned int options, pj_ioqueue_t *ioqueue, pj_timer_heap_t *timer_heap) nogil # NAT detection struct pj_stun_nat_detect_result: int status char *status_text char *nat_type_name ctypedef pj_stun_nat_detect_result *pj_stun_nat_detect_result_ptr_const "const pj_stun_nat_detect_result *" int pj_stun_detect_nat_type(pj_sockaddr_in *server, pj_stun_config *stun_cfg, void *user_data, void pj_stun_nat_detect_cb(void *user_data, pj_stun_nat_detect_result_ptr_const res) with gil) nogil # ICE struct pj_ice_strans struct pj_ice_sess_cand: int type int comp_id int prio pj_sockaddr addr pj_sockaddr rel_addr struct pj_ice_sess_check: pj_ice_sess_cand *lcand pj_ice_sess_cand *rcand int state int nominated ctypedef pj_ice_sess_check *pj_ice_sess_check_ptr_const "const pj_ice_sess_check *" struct pj_ice_sess_comp: pj_ice_sess_check *valid_check struct pj_ice_sess_checklist: int count pj_ice_sess_check *checks struct pj_ice_sess: int comp_cnt pj_ice_sess_comp *comp int lcand_cnt pj_ice_sess_cand *lcand int rcand_cnt pj_ice_sess_cand *rcand pj_ice_sess_checklist valid_list struct pj_ice_strans_cfg_stun: pj_stun_sock_cfg cfg pj_str_t server unsigned int port struct pj_ice_strans_cfg: int af pj_stun_config stun_cfg pj_ice_strans_cfg_stun stun enum pj_ice_strans_op: PJ_ICE_STRANS_OP_INIT PJ_ICE_STRANS_OP_NEGOTIATION enum pj_ice_strans_state: PJ_ICE_STRANS_STATE_NULL PJ_ICE_STRANS_STATE_INIT PJ_ICE_STRANS_STATE_READY PJ_ICE_STRANS_STATE_SESS_READY PJ_ICE_STRANS_STATE_NEGO PJ_ICE_STRANS_STATE_RUNNING PJ_ICE_STRANS_STATE_FAILED enum pj_ice_cand_type: PJ_ICE_CAND_TYPE_HOST PJ_ICE_CAND_TYPE_SRFLX PJ_ICE_CAND_TYPE_PRFLX PJ_ICE_CAND_TYPE_RELAYED enum pj_ice_sess_check_state: PJ_ICE_SESS_CHECK_STATE_FROZEN PJ_ICE_SESS_CHECK_STATE_WAITING PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS PJ_ICE_SESS_CHECK_STATE_SUCCEEDED PJ_ICE_SESS_CHECK_STATE_FAILED struct pjmedia_ice_transport_info: int active pj_ice_strans_state sess_state void pj_ice_strans_cfg_default(pj_ice_strans_cfg *cfg) nogil pj_ice_sess* pj_ice_strans_get_session(pj_ice_strans *ice_st) pj_time_val pj_ice_strans_get_start_time(pj_ice_strans *ice_st) pj_ice_strans_state pj_ice_strans_get_state(pj_ice_strans *ice_st) pj_ice_sess_check_ptr_const pj_ice_strans_get_valid_pair(pj_ice_strans *ice_st, unsigned comp_id) cdef extern from "pjmedia.h": enum: PJMEDIA_ENOSNDREC PJMEDIA_ENOSNDPLAY enum: PJMEDIA_AUD_DEFAULT_CAPTURE_DEV PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV PJMEDIA_AUD_DEV_CAP_EC PJMEDIA_AUD_DEV_CAP_EC_TAIL enum: PJMEDIA_VID_DEFAULT_CAPTURE_DEV PJMEDIA_VID_DEFAULT_RENDER_DEV PJMEDIA_VID_INVALID_DEV enum pjmedia_dir: PJMEDIA_DIR_NONE PJMEDIA_DIR_ENCODING PJMEDIA_DIR_DECODING PJMEDIA_DIR_ENCODING_DECODING PJMEDIA_DIR_PLAYBACK = PJMEDIA_DIR_DECODING PJMEDIA_DIR_RENDER = PJMEDIA_DIR_DECODING PJMEDIA_DIR_CAPTURE = PJMEDIA_DIR_ENCODING PJMEDIA_DIR_CAPTURE_PLAYBACK = PJMEDIA_DIR_ENCODING_DECODING PJMEDIA_DIR_CAPTURE_RENDER = PJMEDIA_DIR_ENCODING_DECODING enum pjmedia_type: PJMEDIA_TYPE_NONE PJMEDIA_TYPE_NONEYPE_AUDIO PJMEDIA_TYPE_VIDEO PJMEDIA_TYPE_APPLICATION PJMEDIA_TYPE_UNKNOWN struct pjmedia_rect_size: unsigned int w unsigned int h struct pjmedia_ratio: int num int denum # frame struct pjmedia_frame: void *buf int size ctypedef pjmedia_frame *pjmedia_frame_ptr_const "const pjmedia_frame *" # codec manager struct pjmedia_codec_mgr enum: PJMEDIA_CODEC_MGR_MAX_CODECS struct pjmedia_codec_info: pj_str_t encoding_name unsigned int clock_rate unsigned int channel_cnt int pjmedia_codec_mgr_enum_codecs(pjmedia_codec_mgr *mgr, unsigned int *count, pjmedia_codec_info *info, unsigned int *prio) nogil int pjmedia_codec_mgr_set_codec_priority(pjmedia_codec_mgr *mgr, pj_str_t *codec_id, unsigned int prio) nogil # transport manager struct pjsip_tpmgr struct pjsip_transport_state_info: int status enum pjsip_transport_state: PJSIP_TP_STATE_CONNECTED PJSIP_TP_STATE_DISCONNECTED ctypedef pjsip_transport_state_info *pjsip_transport_state_info_ptr_const "const pjsip_transport_state_info *" ctypedef void (*pjsip_tp_state_callback)(pjsip_transport *tp, pjsip_transport_state state, pjsip_transport_state_info_ptr_const info) with gil int pjsip_tpmgr_set_state_cb(pjsip_tpmgr *mgr, pjsip_tp_state_callback cb) # formats enum pjmedia_format_detail_type: PJMEDIA_FORMAT_DETAIL_NONE PJMEDIA_FORMAT_DETAIL_AUDIO PJMEDIA_FORMAT_DETAIL_VIDEO PJMEDIA_FORMAT_DETAIL_MAX struct pjmedia_audio_format_detail: unsigned int clock_rate unsigned int channel_count unsigned int frame_time_usec unsigned int baseits_per_sample unsigned int avg_bps unsigned int max_bps struct pjmedia_video_format_detail: pjmedia_rect_size size pjmedia_ratio fps unsigned int avg_bps unsigned int max_bps cdef union pjmedia_format_union: pjmedia_audio_format_detail aud pjmedia_video_format_detail vid char user[1] struct pjmedia_format: unsigned int id pjmedia_type type pjmedia_format_detail_type detail_type pjmedia_format_union det ctypedef pjmedia_format *pjmedia_format_ptr_const "const pjmedia_format *" enum: PJMEDIA_CODEC_MAX_FMTP_CNT struct _param: pj_str_t name pj_str_t val struct pjmedia_codec_fmtp: int cnt _param param[PJMEDIA_CODEC_MAX_FMTP_CNT] # video codec parameters enum pjmedia_vid_packing: PJMEDIA_VID_PACKING_PACKETS struct pjmedia_vid_codec_param: pjmedia_dir dir pjmedia_vid_packing packing pjmedia_format enc_fmt pjmedia_codec_fmtp enc_fmtp pjmedia_format dec_fmt pjmedia_codec_fmtp dec_fmtp ctypedef pjmedia_vid_codec_param *pjmedia_vid_codec_param_ptr_const "const pjmedia_vid_codec_param *" # video codec manager enum: PJMEDIA_VID_CODEC_MGR_MAX_CODECS struct pjmedia_vid_codec_info: pj_str_t encoding_name unsigned int pt unsigned int clock_rate unsigned int packings ctypedef pjmedia_vid_codec_info *pjmedia_vid_codec_info_ptr_const "const pjmedia_vid_codec_info *" struct pjmedia_vid_codec_mgr int pjmedia_vid_codec_mgr_create(pj_pool_t *pool, pjmedia_vid_codec_mgr **mgr) nogil void pjmedia_vid_codec_mgr_destroy(pjmedia_vid_codec_mgr *mgr) nogil pjmedia_vid_codec_mgr* pjmedia_vid_codec_mgr_instance() nogil int pjmedia_vid_codec_mgr_enum_codecs(pjmedia_vid_codec_mgr *mgr, unsigned int *count, pjmedia_vid_codec_info *info, unsigned int *prio) nogil int pjmedia_vid_codec_mgr_set_codec_priority(pjmedia_vid_codec_mgr *mgr, pj_str_t *codec_id, unsigned int prio) nogil int pjmedia_vid_codec_mgr_get_default_param(pjmedia_vid_codec_mgr *mgr, pjmedia_vid_codec_info_ptr_const info, pjmedia_vid_codec_param *param) nogil int pjmedia_vid_codec_mgr_set_default_param(pjmedia_vid_codec_mgr *mgr, pjmedia_vid_codec_info_ptr_const info, pjmedia_vid_codec_param_ptr_const param) nogil # video format manager struct pjmedia_video_format_mgr int pjmedia_video_format_mgr_create(pj_pool_t *pool, unsigned int max_fmt, unsigned int options, pjmedia_video_format_mgr **p_mgr) nogil void pjmedia_video_format_mgr_destroy(pjmedia_video_format_mgr *mgr) nogil pjmedia_video_format_mgr* pjmedia_video_format_mgr_instance() nogil # converter manager struct pjmedia_converter_mgr int pjmedia_converter_mgr_create(pj_pool_t *pool, pjmedia_converter_mgr **mgr) nogil void pjmedia_converter_mgr_destroy(pjmedia_converter_mgr *mgr) nogil pjmedia_converter_mgr* pjmedia_converter_mgr_instance() nogil # event manager struct pjmedia_event_mgr enum pjmedia_event_type: PJMEDIA_EVENT_FMT_CHANGED PJMEDIA_EVENT_KEYFRAME_FOUND PJMEDIA_EVENT_KEYFRAME_MISSING PJMEDIA_EVENT_KEYFRAME_REQUESTED struct pjmedia_event_fmt_changed_data: pjmedia_dir dir pjmedia_format new_fmt cdef union pjmedia_event_data_union: pjmedia_event_fmt_changed_data fmt_changed void* ptr struct pjmedia_event: pjmedia_event_type type pjmedia_event_data_union data int pjmedia_event_mgr_create(pj_pool_t *pool, unsigned int options, pjmedia_event_mgr **mgr) nogil void pjmedia_event_mgr_destroy(pjmedia_event_mgr *mgr) nogil pjmedia_event_mgr* pjmedia_event_mgr_instance() nogil ctypedef int pjmedia_event_cb(pjmedia_event *event, void *user_data) int pjmedia_event_subscribe(pjmedia_event_mgr *mgr, pjmedia_event_cb *cb, void *user_data, void *epub) nogil int pjmedia_event_unsubscribe(pjmedia_event_mgr *mgr, pjmedia_event_cb *cb, void *user_data, void *epub) nogil # endpoint struct pjmedia_endpt int pjmedia_endpt_create(pj_pool_factory *pf, pj_ioqueue_t *ioqueue, int worker_cnt, pjmedia_endpt **p_endpt) nogil pj_pool_t *pjmedia_endpt_create_pool(pjmedia_endpt *endpt, char *pool_name, int initial, int increment) nogil int pjmedia_endpt_destroy(pjmedia_endpt *endpt) nogil pj_ioqueue_t *pjmedia_endpt_get_ioqueue(pjmedia_endpt *endpt) nogil pjmedia_codec_mgr *pjmedia_endpt_get_codec_mgr(pjmedia_endpt *endpt) nogil pjsip_tpmgr* pjsip_endpt_get_tpmgr(pjsip_endpoint *endpt) # sound devices struct pjmedia_aud_dev_info: char *name int input_count int output_count struct pjmedia_aud_param: pjmedia_dir dir int play_id int rec_id int clock_rate int channel_count int samples_per_frame int bits_per_sample int flags int ec_enabled int ec_tail_ms struct pjmedia_aud_stream int pjmedia_aud_dev_count() nogil int pjmedia_aud_dev_get_info(int index, pjmedia_aud_dev_info *info) nogil int pjmedia_aud_stream_get_param(pjmedia_aud_stream *strm, pjmedia_aud_param *param) nogil enum pjmedia_aud_dev_event: PJMEDIA_AUD_DEV_DEFAULT_INPUT_CHANGED PJMEDIA_AUD_DEV_DEFAULT_OUTPUT_CHANGED PJMEDIA_AUD_DEV_LIST_WILL_REFRESH PJMEDIA_AUD_DEV_LIST_DID_REFRESH ctypedef void (*pjmedia_aud_dev_observer_callback)(pjmedia_aud_dev_event event) int pjmedia_aud_dev_set_observer_cb(pjmedia_aud_dev_observer_callback cb) nogil int pjmedia_aud_dev_default_param(int index, pjmedia_aud_param *param) nogil int pjmedia_aud_dev_refresh() nogil # sound port struct pjmedia_port_info: pjmedia_format fmt struct pjmedia_port: pjmedia_port_info info struct pjmedia_snd_port struct pjmedia_snd_port_param: pjmedia_aud_param base ctypedef pjmedia_snd_port_param *pjmedia_snd_port_param_ptr_const "const pjmedia_snd_port_param *" int pjmedia_snd_port_create2(pj_pool_t *pool, pjmedia_snd_port_param_ptr_const prm, pjmedia_snd_port **p_port) nogil void pjmedia_snd_port_param_default(pjmedia_snd_port_param *prm) int pjmedia_snd_port_connect(pjmedia_snd_port *snd_port, pjmedia_port *port) nogil int pjmedia_snd_port_disconnect(pjmedia_snd_port *snd_port) nogil int pjmedia_snd_port_set_ec(pjmedia_snd_port *snd_port, pj_pool_t *pool, unsigned int tail_ms, int options) nogil int pjmedia_snd_port_reset_ec_state(pjmedia_snd_port *snd_port) nogil int pjmedia_snd_port_destroy(pjmedia_snd_port *snd_port) nogil pjmedia_aud_stream *pjmedia_snd_port_get_snd_stream(pjmedia_snd_port *snd_port) nogil int pjmedia_null_port_create(pj_pool_t *pool, unsigned int sampling_rate, unsigned int channel_count, unsigned int samples_per_frame, unsigned int bits_per_sample, pjmedia_port **p_port) nogil int pjmedia_mixer_port_create(pj_pool_t *pool, unsigned int sampling_rate, unsigned int channel_count, unsigned int samples_per_frame, unsigned int bits_per_sample, pjmedia_port **p_port) nogil # master port struct pjmedia_master_port int pjmedia_master_port_create(pj_pool_t *pool, pjmedia_port *u_port, pjmedia_port *d_port, unsigned int options, pjmedia_master_port **p_m) nogil int pjmedia_master_port_start(pjmedia_master_port *m) nogil int pjmedia_master_port_destroy(pjmedia_master_port *m, int destroy_ports) nogil # conference bridge enum pjmedia_conf_option: PJMEDIA_CONF_NO_DEVICE struct pjmedia_conf int pjmedia_conf_create(pj_pool_t *pool, int max_slots, int sampling_rate, int channel_count, int samples_per_frame, int bits_per_sample, int options, pjmedia_conf **p_conf) nogil int pjmedia_conf_destroy(pjmedia_conf *conf) nogil pjmedia_port *pjmedia_conf_get_master_port(pjmedia_conf *conf) nogil int pjmedia_conf_add_port(pjmedia_conf *conf, pj_pool_t *pool, pjmedia_port *strm_port, pj_str_t *name, unsigned int *p_slot) nogil int pjmedia_conf_remove_port(pjmedia_conf *conf, unsigned int slot) nogil int pjmedia_conf_connect_port(pjmedia_conf *conf, unsigned int src_slot, unsigned int sink_slot, int level) nogil int pjmedia_conf_disconnect_port(pjmedia_conf *conf, unsigned int src_slot, unsigned int sink_slot) nogil int pjmedia_conf_adjust_rx_level(pjmedia_conf *conf, unsigned slot, int adj_level) nogil int pjmedia_conf_adjust_tx_level(pjmedia_conf *conf, unsigned slot, int adj_level) nogil # video devices enum pjmedia_vid_dev_cap: PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW_FLAGS enum pjmedia_vid_dev_wnd_flag: PJMEDIA_VID_DEV_WND_BORDER PJMEDIA_VID_DEV_WND_RESIZABLE struct pjmedia_vid_dev_info: int id char *name char *driver int dir cdef struct pjmedia_hwnd_win: void *hwnd cdef struct pjmedia_hwnd_x11: void *window void *display cdef struct pjmedia_hwnd_cocoa: void *window cdef struct pjmedia_hwnd_ios: void *window cdef union pjmedia_hwnd_union: pjmedia_hwnd_win win pjmedia_hwnd_x11 x11 pjmedia_hwnd_cocoa cocoa pjmedia_hwnd_ios ios void *window struct pjmedia_vid_dev_hwnd: pjmedia_hwnd_union info struct pjmedia_vid_dev_param: pjmedia_dir dir int cap_id int rend_id unsigned int flags pjmedia_format fmt pjmedia_vid_dev_hwnd window pjmedia_rect_size disp_size int window_hide unsigned int window_flags struct pjmedia_vid_dev_stream int pjmedia_vid_dev_count() nogil int pjmedia_vid_dev_get_info(int index, pjmedia_vid_dev_info *info) nogil int pjmedia_vid_dev_subsys_init(pj_pool_factory *pf) nogil int pjmedia_vid_dev_subsys_shutdown() nogil int pjmedia_vid_dev_default_param(pj_pool_t *pool, int id, pjmedia_vid_dev_param *param) nogil int pjmedia_vid_dev_stream_get_cap(pjmedia_vid_dev_stream *strm, pjmedia_vid_dev_cap cap, void *value) nogil int pjmedia_vid_dev_stream_set_cap(pjmedia_vid_dev_stream *strm, pjmedia_vid_dev_cap cap, void *value) nogil int pjmedia_vid_dev_stream_get_param(pjmedia_vid_dev_stream *strm, pjmedia_vid_dev_param *param) nogil int pjmedia_vid_dev_refresh() nogil int pjmedia_vid_dev_lookup(char_ptr_const drv_name, char_ptr_const dev_name, int *id) # video stream struct pjmedia_vid_stream_info: pjmedia_vid_codec_param *codec_param pjmedia_vid_codec_info codec_info int use_ka struct pjmedia_vid_stream ctypedef pjmedia_vid_stream *pjmedia_vid_stream_ptr_const "const pjmedia_vid_stream *" int pjmedia_vid_stream_get_stat(pjmedia_vid_stream_ptr_const stream, pjmedia_rtcp_stat *stat) nogil int pjmedia_vid_stream_get_info(pjmedia_vid_stream_ptr_const stream, pjmedia_vid_stream_info *info) nogil int pjmedia_vid_stream_info_from_sdp(pjmedia_vid_stream_info *si, pj_pool_t *pool, pjmedia_endpt *endpt, pjmedia_sdp_session *local, pjmedia_sdp_session *remote, unsigned int stream_idx) nogil int pjmedia_vid_stream_create(pjmedia_endpt *endpt, pj_pool_t *pool, pjmedia_vid_stream_info *info, pjmedia_transport *tp, void *user_data, pjmedia_vid_stream **p_stream) nogil int pjmedia_vid_stream_start(pjmedia_vid_stream *stream) nogil int pjmedia_vid_stream_destroy(pjmedia_vid_stream *stream) nogil int pjmedia_vid_stream_get_port(pjmedia_vid_stream *stream, pjmedia_dir dir, pjmedia_port **p_port) nogil int pjmedia_vid_stream_pause(pjmedia_vid_stream *stream, pjmedia_dir dir) nogil int pjmedia_vid_stream_resume(pjmedia_vid_stream *stream, pjmedia_dir dir) nogil int pjmedia_vid_stream_send_keyframe(pjmedia_vid_stream *stream) nogil int pjmedia_vid_stream_send_rtcp_sdes(pjmedia_vid_stream *stream) nogil int pjmedia_vid_stream_send_rtcp_bye(pjmedia_vid_stream *stream) nogil int pjmedia_vid_stream_send_rtcp_pli(pjmedia_vid_stream *stream) nogil # video port struct pjmedia_vid_port struct pjmedia_vid_port_param: pjmedia_vid_dev_param vidparam int active void pjmedia_vid_port_param_default(pjmedia_vid_port_param *prm) nogil ctypedef pjmedia_vid_port_param *pjmedia_vid_port_param_ptr_const "const pjmedia_vid_port_param *" int pjmedia_vid_port_create(pj_pool_t *pool, pjmedia_vid_port_param_ptr_const prm, pjmedia_vid_port **p_vp) nogil int pjmedia_vid_port_start(pjmedia_vid_port *vid_port) nogil int pjmedia_vid_port_is_running(pjmedia_vid_port *vid_port) nogil int pjmedia_vid_port_stop(pjmedia_vid_port *vid_port) nogil int pjmedia_vid_port_connect(pjmedia_vid_port *vid_port, pjmedia_port *port, int destroy) nogil pjmedia_port *pjmedia_vid_port_get_passive_port(pjmedia_vid_port *vid_port) nogil int pjmedia_vid_port_disconnect(pjmedia_vid_port *vid_port) nogil void pjmedia_vid_port_destroy(pjmedia_vid_port *vid_port) nogil pjmedia_vid_dev_stream *pjmedia_vid_port_get_stream(pjmedia_vid_port *vid_port) nogil # video tee int pjmedia_vid_tee_create(pj_pool_t *pool, pjmedia_format_ptr_const fmt, unsigned int max_dst_cnt, pjmedia_port **p_vid_tee) nogil int pjmedia_vid_tee_add_dst_port2(pjmedia_port *vid_tee, unsigned int option, pjmedia_port *port) nogil int pjmedia_vid_tee_remove_dst_port(pjmedia_port *vid_tee, pjmedia_port *port) nogil # sdp enum: PJMEDIA_MAX_SDP_FMT enum: PJMEDIA_MAX_SDP_ATTR enum: PJMEDIA_MAX_SDP_MEDIA enum: PJMEDIA_MAX_SDP_BANDW struct pjmedia_sdp_attr: pj_str_t name pj_str_t value struct pjmedia_sdp_bandw: pj_str_t modifier unsigned int value struct pjmedia_sdp_conn: pj_str_t net_type pj_str_t addr_type pj_str_t addr struct pjmedia_sdp_media_desc: pj_str_t media unsigned int port unsigned int port_count pj_str_t transport unsigned int fmt_count pj_str_t fmt[PJMEDIA_MAX_SDP_FMT] struct pjmedia_sdp_media: pjmedia_sdp_media_desc desc pjmedia_sdp_conn *conn unsigned int attr_count pjmedia_sdp_attr *attr[PJMEDIA_MAX_SDP_ATTR] unsigned int bandw_count pjmedia_sdp_bandw *bandw[PJMEDIA_MAX_SDP_BANDW] struct pjmedia_sdp_session_origin: pj_str_t user unsigned int id unsigned int version pj_str_t net_type pj_str_t addr_type pj_str_t addr struct pjmedia_sdp_session_time: unsigned int start unsigned int stop struct pjmedia_sdp_session: pjmedia_sdp_session_origin origin pj_str_t name pjmedia_sdp_conn *conn pjmedia_sdp_session_time time unsigned int attr_count pjmedia_sdp_attr *attr[PJMEDIA_MAX_SDP_ATTR] unsigned int bandw_count pjmedia_sdp_bandw *bandw[PJMEDIA_MAX_SDP_BANDW] unsigned int media_count pjmedia_sdp_media *media[PJMEDIA_MAX_SDP_MEDIA] ctypedef pjmedia_sdp_session *pjmedia_sdp_session_ptr_const "const pjmedia_sdp_session *" pjmedia_sdp_media *pjmedia_sdp_media_clone(pj_pool_t *pool, pjmedia_sdp_media *rhs) nogil pjmedia_sdp_session *pjmedia_sdp_session_clone(pj_pool_t *pool, pjmedia_sdp_session_ptr_const sdp) nogil int pjmedia_sdp_print(pjmedia_sdp_session_ptr_const sdp, char *buf, int length) int pjmedia_sdp_parse(pj_pool_t *pool, char *buf, int length, pjmedia_sdp_session **p_sdp) nogil # sdp negotiation enum pjmedia_sdp_neg_state: PJMEDIA_SDP_NEG_STATE_NULL PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER PJMEDIA_SDP_NEG_STATE_WAIT_NEGO PJMEDIA_SDP_NEG_STATE_DONE struct pjmedia_sdp_neg int pjmedia_sdp_neg_create_w_local_offer(pj_pool_t *pool, pjmedia_sdp_session_ptr_const local, pjmedia_sdp_neg **neg) nogil int pjmedia_sdp_neg_create_w_remote_offer(pj_pool_t *pool, pjmedia_sdp_session_ptr_const initial, pjmedia_sdp_session_ptr_const remote, pjmedia_sdp_neg **neg) nogil int pjmedia_sdp_neg_set_local_answer(pj_pool_t *pool, pjmedia_sdp_neg *neg, pjmedia_sdp_session_ptr_const local) nogil int pjmedia_sdp_neg_set_remote_answer(pj_pool_t *pool, pjmedia_sdp_neg *neg, pjmedia_sdp_session_ptr_const remote) nogil int pjmedia_sdp_neg_set_remote_offer(pj_pool_t *pool, pjmedia_sdp_neg *neg, pjmedia_sdp_session_ptr_const remote) nogil int pjmedia_sdp_neg_get_neg_remote(pjmedia_sdp_neg *neg, pjmedia_sdp_session_ptr_const *remote) nogil int pjmedia_sdp_neg_get_neg_local(pjmedia_sdp_neg *neg, pjmedia_sdp_session_ptr_const *local) nogil int pjmedia_sdp_neg_get_active_remote(pjmedia_sdp_neg *neg, pjmedia_sdp_session_ptr_const *remote) nogil int pjmedia_sdp_neg_get_active_local(pjmedia_sdp_neg *neg, pjmedia_sdp_session_ptr_const *local) nogil int pjmedia_sdp_neg_modify_local_offer (pj_pool_t *pool, pjmedia_sdp_neg *neg, pjmedia_sdp_session_ptr_const local) nogil int pjmedia_sdp_neg_cancel_offer(pjmedia_sdp_neg *neg) nogil int pjmedia_sdp_neg_negotiate(pj_pool_t *poll, pjmedia_sdp_neg *neg, int allow_asym) nogil pjmedia_sdp_neg_state pjmedia_sdp_neg_get_state(pjmedia_sdp_neg *neg) nogil char *pjmedia_sdp_neg_state_str(pjmedia_sdp_neg_state state) nogil # transport enum pjmedia_transport_type: PJMEDIA_TRANSPORT_TYPE_ICE PJMEDIA_TRANSPORT_TYPE_SRTP PJMEDIA_TRANSPORT_TYPE_ZRTP struct pjmedia_sock_info: pj_sockaddr rtp_addr_name ctypedef pjmedia_sock_info *pjmedia_sock_info_ptr_const "const pjmedia_sock_info *" struct pjmedia_transport: char *name pjmedia_transport_type type void *user_data struct pjmedia_transport_specific_info: pjmedia_transport_type type char *buffer struct pjmedia_transport_info: pjmedia_sock_info sock_info pj_sockaddr src_rtp_name int specific_info_cnt pjmedia_transport_specific_info *spc_info void pjmedia_transport_info_init(pjmedia_transport_info *info) nogil int pjmedia_transport_udp_create3(pjmedia_endpt *endpt, int af, char *name, pj_str_t *addr, int port, unsigned int options, pjmedia_transport **p_tp) nogil int pjmedia_transport_get_info(pjmedia_transport *tp, pjmedia_transport_info *info) nogil void *pjmedia_transport_info_get_spc_info(pjmedia_transport_info *info, pjmedia_transport_type type) int pjmedia_transport_close(pjmedia_transport *tp) nogil int pjmedia_transport_media_create(pjmedia_transport *tp, pj_pool_t *sdp_pool, unsigned int options, pjmedia_sdp_session *rem_sdp, unsigned int media_index) nogil int pjmedia_transport_encode_sdp(pjmedia_transport *tp, pj_pool_t *sdp_pool, pjmedia_sdp_session *sdp, pjmedia_sdp_session *rem_sdp, unsigned int media_index) nogil int pjmedia_transport_media_start(pjmedia_transport *tp, pj_pool_t *tmp_pool, pjmedia_sdp_session *sdp_local, pjmedia_sdp_session *sdp_remote, unsigned int media_index) nogil int pjmedia_transport_media_stop(pjmedia_transport *tp) nogil int pjmedia_endpt_create_sdp(pjmedia_endpt *endpt, pj_pool_t *pool, unsigned int stream_cnt, pjmedia_sock_info *sock_info, pjmedia_sdp_session **p_sdp) nogil int pjmedia_endpt_create_base_sdp(pjmedia_endpt *endpt, pj_pool_t *pool, pj_str_ptr_const sess_name, pj_sockaddr_ptr_const origin, pjmedia_sdp_session **p_sdp) nogil int pjmedia_endpt_create_audio_sdp(pjmedia_endpt *endpt, pj_pool_t *pool, pjmedia_sock_info_ptr_const si, unsigned int options, pjmedia_sdp_media **p_media) nogil int pjmedia_endpt_create_video_sdp(pjmedia_endpt *endpt, pj_pool_t *pool, pjmedia_sock_info_ptr_const si, unsigned int options, pjmedia_sdp_media **p_media) nogil # SRTP struct pjmedia_srtp_crypto: pj_str_t key pj_str_t name unsigned int flags struct pjmedia_srtp_info: int active pjmedia_srtp_crypto tx_policy enum pjmedia_srtp_use: PJMEDIA_SRTP_MANDATORY struct pjmedia_srtp_setting: pjmedia_srtp_use use void pjmedia_srtp_setting_default(pjmedia_srtp_setting *opt) nogil int pjmedia_transport_srtp_create(pjmedia_endpt *endpt, pjmedia_transport *tp, pjmedia_srtp_setting *opt, pjmedia_transport **p_tp) nogil # ZRTP struct pjmedia_zrtp_info: int active char cipher[128] struct pjmedia_zrtp_cb: void secure_on(pjmedia_transport *tp, char* cipher) with gil void secure_off(pjmedia_transport *tp) with gil void show_sas(pjmedia_transport *tp, char* sas, int verified) with gil void confirm_go_clear(pjmedia_transport *tp) with gil void show_message(pjmedia_transport *tp, int severity, int subCode) with gil void negotiation_failed(pjmedia_transport *tp, int severity, int subCode) with gil void not_supported_by_other(pjmedia_transport *tp) with gil void ask_enrollment(pjmedia_transport *tp, int info) with gil void inform_enrollment(pjmedia_transport *tp, int info) with gil void sign_sas(pjmedia_transport *tp, unsigned char* sas) with gil int check_sas_signature(pjmedia_transport *tp, unsigned char* sas) with gil int pjmedia_transport_zrtp_create(pjmedia_endpt *endpt, pj_timer_heap_t *timer_heap, pjmedia_transport *tp, pjmedia_transport **p_tp, int close_slave) nogil int pjmedia_transport_zrtp_initialize(pjmedia_transport *tp, char_ptr_const zidFilename, int autoEnable, pjmedia_zrtp_cb *zrtp_cb) nogil void pjmedia_transport_zrtp_setSASVerified(pjmedia_transport *tp, int verified) nogil char* pjmedia_transport_zrtp_getPeerName(pjmedia_transport *tp) nogil void pjmedia_transport_zrtp_putPeerName(pjmedia_transport *tp, const char *name) nogil int pjmedia_transport_zrtp_getPeerZid(pjmedia_transport *tp, unsigned char* data) nogil void pjmedia_transport_zrtp_setEnableZrtp(pjmedia_transport *tp, int onOff) nogil char* pjmedia_transport_zrtp_getMultiStreamParameters(pjmedia_transport *tp, int *length) nogil void pjmedia_transport_zrtp_setMultiStreamParameters(pjmedia_transport *tp, const char *parameters, int length, pjmedia_transport *master_tp) nogil # ICE struct pjmedia_ice_cb: void on_ice_complete(pjmedia_transport *tp, pj_ice_strans_op op, int status) with gil void on_ice_state(pjmedia_transport *tp, pj_ice_strans_state prev, pj_ice_strans_state curr) with gil void on_ice_stop(pjmedia_transport *tp, char *reason, int err) with gil int pjmedia_ice_create2(pjmedia_endpt *endpt, char *name, unsigned int comp_cnt, pj_ice_strans_cfg *cfg, pjmedia_ice_cb *cb, unsigned int options, pjmedia_transport **p_tp) nogil pj_ice_strans *pjmedia_ice_get_strans(pjmedia_transport *tp) with gil # stream struct pjmedia_codec_param_setting: unsigned int vad struct pjmedia_codec_param: pjmedia_codec_param_setting setting struct pjmedia_stream_info: pjmedia_codec_info fmt pjmedia_codec_param *param unsigned int tx_event_pt int use_ka struct pjmedia_rtcp_stream_stat_loss_type: unsigned int burst unsigned int random struct pjmedia_rtcp_stream_stat: unsigned int pkt unsigned int bytes unsigned int discard unsigned int loss unsigned int reorder unsigned int dup pj_math_stat loss_period pjmedia_rtcp_stream_stat_loss_type loss_type pj_math_stat jitter struct pjmedia_rtcp_stat: pjmedia_rtcp_stream_stat tx pjmedia_rtcp_stream_stat rx pj_math_stat rtt struct pjmedia_stream int pjmedia_stream_info_from_sdp(pjmedia_stream_info *si, pj_pool_t *pool, pjmedia_endpt *endpt, pjmedia_sdp_session *local, pjmedia_sdp_session *remote, unsigned int stream_idx) nogil int pjmedia_stream_create(pjmedia_endpt *endpt, pj_pool_t *pool, pjmedia_stream_info *info, pjmedia_transport *tp, void *user_data, pjmedia_stream **p_stream) nogil int pjmedia_stream_destroy(pjmedia_stream *stream) nogil int pjmedia_stream_get_port(pjmedia_stream *stream, pjmedia_port **p_port) nogil int pjmedia_stream_start(pjmedia_stream *stream) nogil int pjmedia_stream_dial_dtmf(pjmedia_stream *stream, pj_str_t *ascii_digit) nogil int pjmedia_stream_set_dtmf_callback(pjmedia_stream *stream, void cb(pjmedia_stream *stream, void *user_data, int digit) with gil, void *user_data) nogil int pjmedia_stream_pause(pjmedia_stream *stream, pjmedia_dir dir) nogil int pjmedia_stream_resume(pjmedia_stream *stream, pjmedia_dir dir) nogil int pjmedia_stream_get_stat(pjmedia_stream *stream, pjmedia_rtcp_stat *stat) nogil # wav player enum: PJMEDIA_FILE_NO_LOOP int pjmedia_port_destroy(pjmedia_port *port) nogil int pjmedia_wav_player_port_create(pj_pool_t *pool, char *filename, unsigned int ptime, unsigned int flags, unsigned int buff_size, pjmedia_port **p_port) nogil int pjmedia_wav_player_set_eof_cb(pjmedia_port *port, void *user_data, int cb(pjmedia_port *port, void *usr_data) with gil) nogil int pjmedia_wav_player_port_set_pos(pjmedia_port *port, unsigned int offset) nogil # wav recorder enum pjmedia_file_writer_option: PJMEDIA_FILE_WRITE_PCM int pjmedia_wav_writer_port_create(pj_pool_t *pool, char *filename, unsigned int clock_rate, unsigned int channel_count, unsigned int samples_per_frame, unsigned int bits_per_sample, unsigned int flags, int buff_size, pjmedia_port **p_port) nogil # tone generator enum: PJMEDIA_TONEGEN_MAX_DIGITS struct pjmedia_tone_desc: short freq1 short freq2 short on_msec short off_msec short volume short flags struct pjmedia_tone_digit: char digit short on_msec short off_msec short volume int pjmedia_tonegen_create(pj_pool_t *pool, unsigned int clock_rate, unsigned int channel_count, unsigned int samples_per_frame, unsigned int bits_per_sample, unsigned int options, pjmedia_port **p_port) nogil int pjmedia_tonegen_play(pjmedia_port *tonegen, unsigned int count, pjmedia_tone_desc *tones, unsigned int options) nogil int pjmedia_tonegen_play_digits(pjmedia_port *tonegen, unsigned int count, pjmedia_tone_digit *digits, unsigned int options) nogil int pjmedia_tonegen_stop(pjmedia_port *tonegen) nogil int pjmedia_tonegen_is_busy(pjmedia_port *tonegen) nogil cdef extern from "pjmedia_videodev.h": ctypedef void (*pjmedia_vid_dev_fb_frame_cb)(pjmedia_frame_ptr_const frame, pjmedia_rect_size size, void *user_data) int pjmedia_vid_dev_fb_set_callback(pjmedia_vid_dev_stream *strm, pjmedia_vid_dev_fb_frame_cb cb, void *user_data) cdef extern from "pjmedia-codec.h": # codecs enum: PJMEDIA_SPEEX_NO_NB struct speex_options: unsigned int option int quality int complexity struct ilbc_options: unsigned mode struct pjmedia_audio_codec_config: speex_options speex ilbc_options ilbc void pjmedia_audio_codec_config_default(pjmedia_audio_codec_config *cfg) int pjmedia_codec_register_audio_codecs(pjmedia_endpt *endpt, const pjmedia_audio_codec_config *c) nogil int pjmedia_codec_ffmpeg_vid_init(pjmedia_vid_codec_mgr *mgr, pj_pool_factory *pf) nogil int pjmedia_codec_ffmpeg_vid_deinit() nogil int pjmedia_codec_vpx_init(pjmedia_vid_codec_mgr *mgr, pj_pool_factory *pf) nogil int pjmedia_codec_vpx_deinit() nogil cdef extern from "pjsip.h": # messages enum pjsip_status_code: PJSIP_SC_TSX_TIMEOUT PJSIP_SC_TSX_TRANSPORT_ERROR PJSIP_TLS_EUNKNOWN PJSIP_TLS_EINVMETHOD PJSIP_TLS_ECACERT PJSIP_TLS_ECERTFILE PJSIP_TLS_EKEYFILE PJSIP_TLS_ECIPHER PJSIP_TLS_ECTX enum pjsip_uri_context_e: PJSIP_URI_IN_CONTACT_HDR struct pjsip_param: pj_str_t name pj_str_t value struct pjsip_uri struct pjsip_sip_uri: pj_str_t user pj_str_t passwd pj_str_t host int port pj_str_t user_param pj_str_t method_param pj_str_t transport_param int ttl_param int lr_param pj_str_t maddr_param pjsip_param other_param pjsip_param header_param struct pjsip_name_addr: pj_str_t display pjsip_uri *uri struct pjsip_media_type: pj_str_t type pj_str_t subtype pjsip_param param enum pjsip_method_e: PJSIP_OPTIONS_METHOD PJSIP_CANCEL_METHOD PJSIP_OTHER_METHOD struct pjsip_method: pjsip_method_e id pj_str_t name struct pjsip_host_port: pj_str_t host int port enum pjsip_hdr_e: PJSIP_H_VIA PJSIP_H_CALL_ID PJSIP_H_CONTACT PJSIP_H_CSEQ PJSIP_H_EXPIRES PJSIP_H_FROM struct pjsip_hdr: pjsip_hdr_e type pj_str_t name ctypedef pjsip_hdr *pjsip_hdr_ptr_const "const pjsip_hdr*" struct pjsip_generic_array_hdr: unsigned int count pj_str_t *values struct pjsip_generic_string_hdr: pj_str_t name pj_str_t hvalue struct pjsip_cid_hdr: pj_str_t id struct pjsip_contact_hdr: int star pjsip_uri *uri int q1000 int expires pjsip_param other_param struct pjsip_clen_hdr: int len struct pjsip_ctype_hdr: pjsip_media_type media struct pjsip_cseq_hdr: int cseq pjsip_method method struct pjsip_generic_int_hdr: int ivalue ctypedef pjsip_generic_int_hdr pjsip_expires_hdr struct pjsip_fromto_hdr: pjsip_uri *uri pj_str_t tag pjsip_param other_param struct pjsip_routing_hdr: pjsip_name_addr name_addr pjsip_param other_param ctypedef pjsip_routing_hdr pjsip_route_hdr struct pjsip_retry_after_hdr: int ivalue pjsip_param param pj_str_t comment struct pjsip_via_hdr: pj_str_t transport pjsip_host_port sent_by int ttl_param int rport_param pj_str_t maddr_param pj_str_t recvd_param pj_str_t branch_param pjsip_param other_param pj_str_t comment enum: PJSIP_MAX_ACCEPT_COUNT struct pjsip_msg_body: pjsip_media_type content_type void *data unsigned int len struct pjsip_request_line: pjsip_method method pjsip_uri *uri struct pjsip_status_line: int code pj_str_t reason union pjsip_msg_line: pjsip_request_line req pjsip_status_line status enum pjsip_msg_type_e: PJSIP_REQUEST_MSG PJSIP_RESPONSE_MSG struct pjsip_msg: pjsip_msg_type_e type pjsip_msg_line line pjsip_hdr hdr pjsip_msg_body *body struct pjsip_buffer: char *start char *cur struct pjsip_tx_data_tp_info: char *dst_name int dst_port pjsip_transport *transport struct pjsip_tx_data: pjsip_msg *msg pj_pool_t *pool pjsip_buffer buf pjsip_tx_data_tp_info tp_info struct pjsip_rx_data_tp_info: pj_pool_t *pool pjsip_transport *transport struct pjsip_rx_data_pkt_info: pj_time_val timestamp char *packet int len char *src_name int src_port struct pjsip_rx_data_msg_info: pjsip_msg *msg pjsip_fromto_hdr *from_hdr "from" pjsip_fromto_hdr *to_hdr "to" pjsip_via_hdr *via struct pjsip_rx_data: pjsip_rx_data_pkt_info pkt_info pjsip_rx_data_tp_info tp_info pjsip_rx_data_msg_info msg_info void *pjsip_hdr_clone(pj_pool_t *pool, void *hdr) nogil void pjsip_msg_add_hdr(pjsip_msg *msg, pjsip_hdr *hdr) nogil void *pjsip_msg_find_hdr(pjsip_msg *msg, pjsip_hdr_e type, void *start) nogil void *pjsip_msg_find_hdr_by_name(pjsip_msg *msg, pj_str_t *name, void *start) nogil void *pjsip_msg_find_remove_hdr_by_name(pjsip_msg *msg, pj_str_t *name, void *start) nogil pjsip_generic_string_hdr *pjsip_generic_string_hdr_create(pj_pool_t *pool, pj_str_t *hname, pj_str_t *hvalue) nogil pjsip_contact_hdr *pjsip_contact_hdr_create(pj_pool_t *pool) nogil pjsip_expires_hdr *pjsip_expires_hdr_create(pj_pool_t *pool, int value) nogil pjsip_msg_body *pjsip_msg_body_create(pj_pool_t *pool, pj_str_t *type, pj_str_t *subtype, pj_str_t *text) nogil pjsip_msg_body *pjsip_msg_body_clone(pj_pool_t *pool, const pjsip_msg_body *body) nogil pjsip_route_hdr *pjsip_route_hdr_init(pj_pool_t *pool, void *mem) nogil void pjsip_sip_uri_init(pjsip_sip_uri *url, int secure) nogil int pjsip_tx_data_dec_ref(pjsip_tx_data *tdata) nogil void pjsip_tx_data_add_ref(pjsip_tx_data *tdata) nogil pj_str_t *pjsip_uri_get_scheme(pjsip_uri *uri) nogil void *pjsip_uri_get_uri(pjsip_uri *uri) nogil int pjsip_uri_print(pjsip_uri_context_e context, void *uri, char *buf, unsigned int size) nogil int PJSIP_URI_SCHEME_IS_SIP(pjsip_sip_uri *uri) nogil enum: PJSIP_PARSE_URI_AS_NAMEADDR pjsip_uri *pjsip_parse_uri(pj_pool_t *pool, char *buf, unsigned int size, unsigned int options) nogil void pjsip_method_init_np(pjsip_method *m, pj_str_t *str) nogil pj_str_t *pjsip_get_status_text(int status_code) nogil int pjsip_print_body(pjsip_msg_body *msg_body, char **buf, int *len) # module enum pjsip_module_priority: PJSIP_MOD_PRIORITY_APPLICATION PJSIP_MOD_PRIORITY_DIALOG_USAGE PJSIP_MOD_PRIORITY_TRANSPORT_LAYER struct pjsip_event struct pjsip_transaction struct pjsip_module: pj_str_t name int id int priority int on_rx_request(pjsip_rx_data *rdata) with gil int on_rx_response(pjsip_rx_data *rdata) with gil int on_tx_request(pjsip_tx_data *tdata) with gil int on_tx_response(pjsip_tx_data *tdata) with gil void on_tsx_state(pjsip_transaction *tsx, pjsip_event *event) with gil # endpoint struct pjsip_endpoint int pjsip_endpt_create(pj_pool_factory *pf, char *name, pjsip_endpoint **endpt) nogil void pjsip_endpt_destroy(pjsip_endpoint *endpt) nogil pj_pool_t *pjsip_endpt_create_pool(pjsip_endpoint *endpt, char *pool_name, int initial, int increment) nogil void pjsip_endpt_release_pool(pjsip_endpoint *endpt, pj_pool_t *pool) nogil int pjsip_endpt_handle_events(pjsip_endpoint *endpt, pj_time_val *max_timeout) nogil int pjsip_endpt_register_module(pjsip_endpoint *endpt, pjsip_module *module) nogil int pjsip_endpt_schedule_timer(pjsip_endpoint *endpt, pj_timer_entry *entry, pj_time_val *delay) nogil void pjsip_endpt_cancel_timer(pjsip_endpoint *endpt, pj_timer_entry *entry) nogil enum: PJSIP_H_ACCEPT PJSIP_H_ALLOW PJSIP_H_SUPPORTED pjsip_hdr_ptr_const pjsip_endpt_get_capability(pjsip_endpoint *endpt, int htype, pj_str_t *hname) nogil int pjsip_endpt_add_capability(pjsip_endpoint *endpt, pjsip_module *mod, int htype, pj_str_t *hname, unsigned count, pj_str_t *tags) nogil int pjsip_endpt_create_response(pjsip_endpoint *endpt, pjsip_rx_data *rdata, int st_code, pj_str_t *st_text, pjsip_tx_data **p_tdata) nogil int pjsip_endpt_send_response2(pjsip_endpoint *endpt, pjsip_rx_data *rdata, pjsip_tx_data *tdata, void *token, void *cb) nogil int pjsip_endpt_respond_stateless(pjsip_endpoint *endpt, pjsip_rx_data *rdata, int st_code, pj_str_t *st_text, pjsip_hdr *hdr_list, pjsip_msg_body *body) nogil int pjsip_endpt_create_request(pjsip_endpoint *endpt, pjsip_method *method, pj_str_t *target, pj_str_t *frm, pj_str_t *to, pj_str_t *contact, pj_str_t *call_id, int cseq,pj_str_t *text, pjsip_tx_data **p_tdata) nogil pj_timer_heap_t *pjsip_endpt_get_timer_heap(pjsip_endpoint *endpt) nogil int pjsip_endpt_create_resolver(pjsip_endpoint *endpt, pj_dns_resolver **p_resv) nogil int pjsip_endpt_set_resolver(pjsip_endpoint *endpt, pj_dns_resolver *resv) nogil pj_dns_resolver* pjsip_endpt_get_resolver(pjsip_endpoint *endpt) nogil # transports enum pjsip_ssl_method: PJSIP_TLSV1_METHOD PJSIP_SSLV23_METHOD struct pjsip_transport: char *type_name pj_sockaddr local_addr pjsip_host_port local_name pjsip_host_port remote_name struct pjsip_tpfactory: pjsip_host_port addr_name int destroy(pjsip_tpfactory *factory) nogil struct pjsip_tls_setting: pj_str_t ca_list_file pj_str_t cert_file pj_str_t privkey_file int method int verify_server pj_time_val timeout enum pjsip_tpselector_type: PJSIP_TPSELECTOR_TRANSPORT union pjsip_tpselector_u: pjsip_transport *transport struct pjsip_tpselector: pjsip_tpselector_type type pjsip_tpselector_u u int pjsip_transport_shutdown(pjsip_transport *tp) nogil int pjsip_udp_transport_start(pjsip_endpoint *endpt, pj_sockaddr_in *local, pjsip_host_port *a_name, unsigned int async_cnt, pjsip_transport **p_transport) nogil int pjsip_tcp_transport_start2(pjsip_endpoint *endpt, pj_sockaddr_in *local, pjsip_host_port *a_name, unsigned int async_cnt, pjsip_tpfactory **p_tpfactory) nogil int pjsip_tls_transport_start(pjsip_endpoint *endpt, pjsip_tls_setting *opt, pj_sockaddr_in *local, pjsip_host_port *a_name, unsigned async_cnt, pjsip_tpfactory **p_factory) nogil void pjsip_tls_setting_default(pjsip_tls_setting *tls_opt) nogil int pjsip_transport_shutdown(pjsip_transport *tp) nogil # transaction layer enum pjsip_role_e: PJSIP_ROLE_UAC PJSIP_ROLE_UAS enum pjsip_tsx_state_e: PJSIP_TSX_STATE_TRYING PJSIP_TSX_STATE_PROCEEDING PJSIP_TSX_STATE_COMPLETED PJSIP_TSX_STATE_TERMINATED struct pjsip_transaction: int status_code pj_str_t status_text pjsip_role_e role pjsip_tx_data *last_tx pjsip_tsx_state_e state void **mod_data pjsip_method method int pjsip_tsx_layer_init_module(pjsip_endpoint *endpt) nogil int pjsip_tsx_create_key(pj_pool_t *pool, pj_str_t *key, pjsip_role_e role, pjsip_method *method, pjsip_rx_data *rdata) nogil pjsip_transaction *pjsip_tsx_layer_find_tsx(pj_str_t *key, int lock) nogil int pjsip_tsx_create_uac(pjsip_module *tsx_user, pjsip_tx_data *tdata, pjsip_transaction **p_tsx) nogil int pjsip_tsx_terminate(pjsip_transaction *tsx, int code) nogil int pjsip_tsx_send_msg(pjsip_transaction *tsx, pjsip_tx_data *tdata) nogil pjsip_transaction *pjsip_rdata_get_tsx(pjsip_rx_data *rdata) nogil int pjsip_tsx_create_uas(pjsip_module *tsx_user, pjsip_rx_data *rdata, pjsip_transaction **p_tsx) nogil void pjsip_tsx_recv_msg(pjsip_transaction *tsx, pjsip_rx_data *rdata) nogil # event enum pjsip_event_id_e: PJSIP_EVENT_TSX_STATE PJSIP_EVENT_RX_MSG PJSIP_EVENT_TX_MSG PJSIP_EVENT_TRANSPORT_ERROR PJSIP_EVENT_TIMER union pjsip_event_body_tsx_state_src: pjsip_rx_data *rdata pjsip_tx_data *tdata struct pjsip_event_body_tsx_state: pjsip_event_body_tsx_state_src src pjsip_transaction *tsx pjsip_event_id_e type struct pjsip_event_body_rx_msg: pjsip_rx_data *rdata union pjsip_event_body: pjsip_event_body_tsx_state tsx_state pjsip_event_body_rx_msg rx_msg struct pjsip_event: pjsip_event_id_e type pjsip_event_body body int pjsip_endpt_send_request(pjsip_endpoint *endpt, pjsip_tx_data *tdata, int timeout, void *token, void cb(void *token, pjsip_event *e) with gil) nogil # auth enum: PJSIP_EFAILEDCREDENTIAL enum pjsip_cred_data_type: PJSIP_CRED_DATA_PLAIN_PASSWD PJSIP_CRED_DATA_DIGEST struct pjsip_cred_info: pj_str_t realm pj_str_t scheme pj_str_t username pjsip_cred_data_type data_type pj_str_t data struct pjsip_auth_clt_sess: pass int pjsip_auth_clt_init(pjsip_auth_clt_sess *sess, pjsip_endpoint *endpt, pj_pool_t *pool, unsigned int options) nogil int pjsip_auth_clt_set_credentials(pjsip_auth_clt_sess *sess, int cred_cnt, pjsip_cred_info *c) nogil int pjsip_auth_clt_reinit_req(pjsip_auth_clt_sess *sess, pjsip_rx_data *rdata, pjsip_tx_data *old_request, pjsip_tx_data **new_request) nogil # dialog layer ctypedef pjsip_module pjsip_user_agent struct pjsip_dlg_party: pjsip_contact_hdr *contact pjsip_fromto_hdr *info struct pjsip_dialog: pjsip_auth_clt_sess auth_sess pjsip_cid_hdr *call_id pj_pool_t *pool pjsip_dlg_party local pjsip_dlg_party remote struct pjsip_ua_init_param: pjsip_dialog *on_dlg_forked(pjsip_dialog *first_set, pjsip_rx_data *res) nogil int pjsip_ua_init_module(pjsip_endpoint *endpt, pjsip_ua_init_param *prm) nogil pjsip_user_agent *pjsip_ua_instance() nogil int pjsip_dlg_create_uac(pjsip_user_agent *ua, pj_str_t *local_uri, pj_str_t *local_contact, pj_str_t *remote_uri, pj_str_t *target, pjsip_dialog **p_dlg) nogil int pjsip_dlg_set_route_set(pjsip_dialog *dlg, pjsip_route_hdr *route_set) nogil int pjsip_dlg_create_uas_and_inc_lock(pjsip_user_agent *ua, pjsip_rx_data *rdata, pj_str_t *contact, pjsip_dialog **p_dlg) nogil int pjsip_dlg_terminate(pjsip_dialog *dlg) nogil int pjsip_dlg_set_transport(pjsip_dialog *dlg, pjsip_tpselector *sel) nogil int pjsip_dlg_respond(pjsip_dialog *dlg, pjsip_rx_data *rdata, int st_code, pj_str_t *st_text, pjsip_hdr *hdr_list, pjsip_msg_body *body) nogil int pjsip_dlg_create_response(pjsip_dialog *dlg, pjsip_rx_data *rdata, int st_code, pj_str_t *st_text, pjsip_tx_data **tdata) nogil int pjsip_dlg_modify_response(pjsip_dialog *dlg, pjsip_tx_data *tdata, int st_code, pj_str_t *st_text) nogil int pjsip_dlg_send_response(pjsip_dialog *dlg, pjsip_transaction *tsx, pjsip_tx_data *tdata) nogil void pjsip_dlg_inc_lock(pjsip_dialog *dlg) nogil void pjsip_dlg_dec_lock(pjsip_dialog *dlg) nogil int pjsip_dlg_inc_session(pjsip_dialog *dlg, pjsip_module *mod) nogil int pjsip_dlg_dec_session(pjsip_dialog *dlg, pjsip_module *mod) nogil cdef extern from "pjsip-simple/evsub_msg.h": struct pjsip_event_hdr: pj_str_t event_type pj_str_t id_param pjsip_param other_param struct pjsip_sub_state_hdr: pj_str_t sub_state pj_str_t reason_param int expires_param int retry_after pjsip_param other_param pjsip_event_hdr *pjsip_event_hdr_create(pj_pool_t *pool) nogil cdef extern from "pjsip_simple.h": # subscribe / notify enum: PJSIP_EVSUB_NO_EVENT_ID enum pjsip_evsub_state: PJSIP_EVSUB_STATE_PENDING PJSIP_EVSUB_STATE_ACTIVE PJSIP_EVSUB_STATE_TERMINATED struct pjsip_evsub struct pjsip_evsub_user: void on_evsub_state(pjsip_evsub *sub, pjsip_event *event) with gil void on_tsx_state(pjsip_evsub *sub, pjsip_transaction *tsx, pjsip_event *event) with gil void on_rx_refresh(pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body) with gil void on_rx_notify(pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text,pjsip_hdr *res_hdr, pjsip_msg_body **p_body) with gil void on_client_refresh(pjsip_evsub *sub) with gil void on_server_timeout(pjsip_evsub *sub) with gil int pjsip_evsub_init_module(pjsip_endpoint *endpt) nogil int pjsip_evsub_register_pkg(pjsip_module *pkg_mod, pj_str_t *event_name, unsigned int expires, unsigned int accept_cnt, pj_str_t *accept) nogil int pjsip_evsub_create_uac(pjsip_dialog *dlg, pjsip_evsub_user *user_cb, pj_str_t *event, int option, pjsip_evsub **p_evsub) nogil int pjsip_evsub_create_uas(pjsip_dialog *dlg, pjsip_evsub_user *user_cb, pjsip_rx_data *rdata, unsigned int option, pjsip_evsub **p_evsub) nogil int pjsip_evsub_initiate(pjsip_evsub *sub, void *method, unsigned int expires, pjsip_tx_data **p_tdata) nogil int pjsip_evsub_send_request(pjsip_evsub *sub, pjsip_tx_data *tdata) nogil int pjsip_evsub_terminate(pjsip_evsub *sub, int notify) nogil char *pjsip_evsub_get_state_name(pjsip_evsub *sub) nogil void pjsip_evsub_set_mod_data(pjsip_evsub *sub, int mod_id, void *data) nogil void *pjsip_evsub_get_mod_data(pjsip_evsub *sub, int mod_id) nogil void pjsip_evsub_update_expires(pjsip_evsub *sub, int interval) nogil void pjsip_evsub_set_timer(pjsip_evsub *sub, int timer_id, int seconds) nogil pjsip_hdr *pjsip_evsub_get_allow_events_hdr(pjsip_module *m) nogil int pjsip_evsub_notify(pjsip_evsub *sub, pjsip_evsub_state state, pj_str_t *state_str, pj_str_t *reason, pjsip_tx_data **p_tdata) nogil cdef extern from "pjsip_ua.h": # 100rel / PRACK int pjsip_100rel_init_module(pjsip_endpoint *endpt) nogil # invite sessions enum pjsip_inv_option: PJSIP_INV_SUPPORT_100REL enum pjsip_inv_state: PJSIP_INV_STATE_INCOMING PJSIP_INV_STATE_CONFIRMED enum pjmedia_mod_offer_flag: PJMEDIA_SDP_NEG_ALLOW_MEDIA_CHANGE struct pjsip_inv_session: pjsip_inv_state state void **mod_data pjmedia_sdp_neg *neg unsigned sdp_neg_flags int cause pj_str_t cause_text int cancelling pjsip_transaction *invite_tsx struct pjsip_inv_callback: void on_state_changed(pjsip_inv_session *inv, pjsip_event *e) with gil void on_new_session(pjsip_inv_session *inv, pjsip_event *e) with gil void on_tsx_state_changed(pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_event *e) with gil void on_media_update(pjsip_inv_session *inv, int status) with gil int on_rx_reinvite(pjsip_inv_session *inv, pjmedia_sdp_session_ptr_const offer, pjsip_rx_data *rdata) with gil int pjsip_inv_usage_init(pjsip_endpoint *endpt, pjsip_inv_callback *cb) nogil int pjsip_inv_terminate(pjsip_inv_session *inv, int st_code, int notify) nogil int pjsip_inv_end_session(pjsip_inv_session *inv, int st_code, pj_str_t *st_text, pjsip_tx_data **p_tdata) nogil int pjsip_inv_cancel_reinvite(pjsip_inv_session *inv, pjsip_tx_data **p_tdata) nogil int pjsip_inv_send_msg(pjsip_inv_session *inv, pjsip_tx_data *tdata) nogil int pjsip_inv_verify_request(pjsip_rx_data *rdata, unsigned int *options, pjmedia_sdp_session *sdp, pjsip_dialog *dlg, pjsip_endpoint *endpt, pjsip_tx_data **tdata) nogil int pjsip_inv_create_uas(pjsip_dialog *dlg, pjsip_rx_data *rdata, pjmedia_sdp_session *local_sdp, unsigned int options, pjsip_inv_session **p_inv) nogil int pjsip_inv_initial_answer(pjsip_inv_session *inv, pjsip_rx_data *rdata, int st_code, pj_str_t *st_text, pjmedia_sdp_session *sdp, pjsip_tx_data **p_tdata) nogil int pjsip_inv_answer(pjsip_inv_session *inv, int st_code, pj_str_t *st_text, pjmedia_sdp_session *local_sdp, pjsip_tx_data **p_tdata) nogil int pjsip_inv_create_uac(pjsip_dialog *dlg, pjmedia_sdp_session *local_sdp, unsigned int options, pjsip_inv_session **p_inv) nogil int pjsip_inv_invite(pjsip_inv_session *inv, pjsip_tx_data **p_tdata) nogil char *pjsip_inv_state_name(pjsip_inv_state state) nogil int pjsip_inv_reinvite(pjsip_inv_session *inv, pj_str_t *new_contact, pjmedia_sdp_session *new_offer, pjsip_tx_data **p_tdata) nogil int pjsip_create_sdp_body(pj_pool_t *pool, pjmedia_sdp_session *sdp, pjsip_msg_body **p_body) nogil # Replaces struct pjsip_replaces_hdr: pj_str_t call_id pj_str_t to_tag pj_str_t from_tag int early_only pjsip_param other_param pjsip_replaces_hdr *pjsip_replaces_hdr_create(pj_pool_t *pool) nogil int pjsip_replaces_verify_request(pjsip_rx_data *rdata, pjsip_dialog **p_dlg, int lock_dlg, pjsip_tx_data **p_tdata) nogil int pjsip_replaces_init_module(pjsip_endpoint *endpt) nogil # declarations # core.util cdef class frozenlist(object): # attributes cdef int initialized cdef list list cdef long hash cdef class frozendict(object): # attributes cdef int initialized cdef dict dict cdef long hash cdef class PJSTR(object): # attributes cdef pj_str_t pj_str cdef object str # core.lib cdef class PJLIB(object): # attributes cdef int _init_done cdef class PJCachingPool(object): # attributes cdef pj_caching_pool _obj cdef int _init_done cdef class PJSIPEndpoint(object): # attributes cdef pjsip_endpoint *_obj cdef pj_pool_t *_pool cdef pjsip_transport *_udp_transport cdef pjsip_tpfactory *_tcp_transport cdef pjsip_tpfactory *_tls_transport cdef int _tls_verify_server cdef PJSTR _tls_ca_file cdef PJSTR _tls_cert_file cdef PJSTR _tls_privkey_file cdef object _local_ip_used cdef int _tls_timeout # private methods cdef int _make_local_addr(self, pj_sockaddr_in *local_addr, object ip_address, int port) except -1 cdef int _start_udp_transport(self, int port) except -1 cdef int _stop_udp_transport(self) except -1 cdef int _start_tcp_transport(self, int port) except -1 cdef int _stop_tcp_transport(self) except -1 cdef int _start_tls_transport(self, port) except -1 cdef int _stop_tls_transport(self) except -1 cdef int _set_dns_nameservers(self, list servers) except -1 cdef class PJMEDIAEndpoint(object): # attributes cdef pjmedia_endpt *_obj cdef pj_pool_t *_pool cdef int _has_audio_codecs cdef int _has_video cdef int _has_ffmpeg_video cdef int _has_vpx # private methods cdef list _get_codecs(self) cdef list _get_all_codecs(self) cdef list _get_current_codecs(self) cdef int _set_codecs(self, list req_codecs) except -1 cdef list _get_video_codecs(self) cdef list _get_all_video_codecs(self) cdef list _get_current_video_codecs(self) cdef int _set_video_codecs(self, list req_codecs) except -1 cdef void _audio_subsystem_init(self, PJCachingPool caching_pool) cdef void _audio_subsystem_shutdown(self) cdef void _video_subsystem_init(self, PJCachingPool caching_pool) cdef void _video_subsystem_shutdown(self) cdef void _set_h264_options(self, str profile, int level) cdef void _set_video_options(self, tuple max_resolution, int max_framerate, float max_bitrate) # core.helper cdef class BaseCredentials(object): # attributes cdef pjsip_cred_info _credentials # private methods cdef pjsip_cred_info* get_cred_info(self) cdef class Credentials(BaseCredentials): # attributes cdef str _username cdef str _realm cdef str _password cdef bint _digest cdef class FrozenCredentials(BaseCredentials): # attributes cdef int initialized cdef readonly str username cdef readonly str realm cdef readonly str password cdef readonly bint digest cdef class BaseSIPURI(object): pass cdef class SIPURI(BaseSIPURI): # attributes cdef public object user cdef public object password cdef public object host cdef public bint secure cdef public dict parameters cdef public dict headers cdef object _port cdef class FrozenSIPURI(BaseSIPURI): # attributes cdef int initialized cdef readonly object user cdef readonly object password cdef readonly object host cdef readonly object port cdef readonly bint secure cdef readonly frozendict parameters cdef readonly frozendict headers cdef SIPURI SIPURI_create(pjsip_sip_uri *base_uri) cdef FrozenSIPURI FrozenSIPURI_create(pjsip_sip_uri *base_uri) # core.headers cdef class BaseHeader(object): pass cdef class Header(BaseHeader): # attributes cdef str _name cdef str _body cdef class FrozenHeader(BaseHeader): # attributes cdef readonly str name cdef readonly str body cdef class BaseContactHeader(object): pass cdef class ContactHeader(BaseContactHeader): # attributes cdef SIPURI _uri cdef unicode _display_name cdef dict _parameters cdef class FrozenContactHeader(BaseContactHeader): # attributes cdef int initialized cdef readonly FrozenSIPURI uri cdef readonly unicode display_name cdef readonly frozendict parameters cdef class BaseContentTypeHeader(object): pass cdef class ContentTypeHeader(BaseContentTypeHeader): # attributes cdef str _content_type cdef dict _parameters cdef class FrozenContentTypeHeader(BaseContentTypeHeader): # attributes cdef int initialized cdef readonly str _content_type cdef readonly frozendict parameters cdef class BaseIdentityHeader(object): pass cdef class IdentityHeader(BaseIdentityHeader): # attributes cdef SIPURI _uri cdef public unicode display_name cdef dict _parameters cdef class FrozenIdentityHeader(BaseIdentityHeader): # attributes cdef int initialized cdef readonly FrozenSIPURI uri cdef readonly unicode display_name cdef readonly frozendict parameters cdef class FromHeader(IdentityHeader): pass cdef class FrozenFromHeader(FrozenIdentityHeader): pass cdef class ToHeader(IdentityHeader): pass cdef class FrozenToHeader(FrozenIdentityHeader): pass cdef class RouteHeader(IdentityHeader): pass cdef class FrozenRouteHeader(FrozenIdentityHeader): pass cdef class RecordRouteHeader(IdentityHeader): pass cdef class FrozenRecordRouteHeader(FrozenIdentityHeader): pass cdef class BaseRetryAfterHeader(object): pass cdef class RetryAfterHeader(BaseRetryAfterHeader): # attributes cdef public int seconds cdef public str comment cdef dict _parameters cdef class FrozenRetryAfterHeader(BaseRetryAfterHeader): # attributes cdef int initialized cdef readonly int seconds cdef readonly str comment cdef readonly frozendict parameters cdef class BaseViaHeader(object): pass cdef class ViaHeader(BaseViaHeader): # attributes cdef str _transport cdef str _host cdef int _port cdef dict _parameters cdef class FrozenViaHeader(BaseViaHeader): # attributes cdef int initialized cdef readonly str transport cdef readonly str host cdef readonly int port cdef readonly frozendict parameters cdef class BaseWarningHeader(object): pass cdef class WarningHeader(BaseWarningHeader): # attributes cdef int _code cdef str _agent cdef str _text cdef class FrozenWarningHeader(BaseWarningHeader): # attributes cdef int initialized cdef readonly int code cdef readonly str agent cdef readonly str text cdef class BaseEventHeader(object): pass cdef class EventHeader(BaseEventHeader): # attributes cdef public event cdef dict _parameters cdef class FrozenEventHeader(BaseEventHeader): # attributes cdef int initialized cdef readonly str event cdef readonly frozendict parameters cdef class BaseSubscriptionStateHeader(object): pass cdef class SubscriptionStateHeader(BaseSubscriptionStateHeader): # attributes cdef public state cdef dict _parameters cdef class FrozenSubscriptionStateHeader(BaseSubscriptionStateHeader): # attributes cdef int initialized cdef readonly str state cdef readonly frozendict parameters cdef class BaseReasonHeader(object): pass cdef class ReasonHeader(BaseReasonHeader): # attributes cdef public str protocol cdef public dict parameters cdef class FrozenReasonHeader(BaseReasonHeader): # attributes cdef int initialized cdef readonly str protocol cdef readonly frozendict parameters cdef class BaseReferToHeader(object): pass cdef class ReferToHeader(BaseReferToHeader): # attributes cdef public str uri cdef dict _parameters cdef class FrozenReferToHeader(BaseReferToHeader): # attributes cdef int initialized cdef readonly str uri cdef readonly frozendict parameters cdef class BaseSubjectHeader(object): pass cdef class SubjectHeader(BaseSubjectHeader): # attributes cdef public unicode subject cdef class FrozenSubjectHeader(BaseSubjectHeader): # attributes cdef int initialized cdef readonly unicode subject cdef class BaseReplacesHeader(object): pass cdef class ReplacesHeader(BaseReplacesHeader): # attributes cdef public str call_id cdef public str from_tag cdef public str to_tag cdef public int early_only cdef dict _parameters cdef class FrozenReplacesHeader(BaseReplacesHeader): # attributes cdef int initialized cdef readonly str call_id cdef readonly str from_tag cdef readonly str to_tag cdef readonly int early_only cdef readonly frozendict parameters cdef Header Header_create(pjsip_generic_string_hdr *header) cdef FrozenHeader FrozenHeader_create(pjsip_generic_string_hdr *header) cdef ContactHeader ContactHeader_create(pjsip_contact_hdr *header) cdef FrozenContactHeader FrozenContactHeader_create(pjsip_contact_hdr *header) cdef ContentTypeHeader ContentTypeHeader_create(pjsip_ctype_hdr *header) cdef FrozenContentTypeHeader FrozenContentTypeHeader_create(pjsip_ctype_hdr *header) cdef FromHeader FromHeader_create(pjsip_fromto_hdr *header) cdef FrozenFromHeader FrozenFromHeader_create(pjsip_fromto_hdr *header) cdef ToHeader ToHeader_create(pjsip_fromto_hdr *header) cdef FrozenToHeader FrozenToHeader_create(pjsip_fromto_hdr *header) cdef RouteHeader RouteHeader_create(pjsip_routing_hdr *header) cdef FrozenRouteHeader FrozenRouteHeader_create(pjsip_routing_hdr *header) cdef RecordRouteHeader RecordRouteHeader_create(pjsip_routing_hdr *header) cdef FrozenRecordRouteHeader FrozenRecordRouteHeader_create(pjsip_routing_hdr *header) cdef RetryAfterHeader RetryAfterHeader_create(pjsip_retry_after_hdr *header) cdef FrozenRetryAfterHeader FrozenRetryAfterHeader_create(pjsip_retry_after_hdr *header) cdef ViaHeader ViaHeader_create(pjsip_via_hdr *header) cdef FrozenViaHeader FrozenViaHeader_create(pjsip_via_hdr *header) cdef EventHeader EventHeader_create(pjsip_event_hdr *header) cdef FrozenEventHeader FrozenEventHeader_create(pjsip_event_hdr *header) cdef SubscriptionStateHeader SubscriptionStateHeader_create(pjsip_sub_state_hdr *header) cdef FrozenSubscriptionStateHeader FrozenSubscriptionStateHeader_create(pjsip_sub_state_hdr *header) cdef ReferToHeader ReferToHeader_create(pjsip_generic_string_hdr *header) cdef FrozenReferToHeader FrozenReferToHeader_create(pjsip_generic_string_hdr *header) cdef SubjectHeader SubjectHeader_create(pjsip_generic_string_hdr *header) cdef FrozenSubjectHeader FrozenSubjectHeader_create(pjsip_generic_string_hdr *header) cdef ReplacesHeader ReplacesHeader_create(pjsip_replaces_hdr *header) cdef FrozenReplacesHeader FrozenReplacesHeader_create(pjsip_replaces_hdr *header) # core.util cdef int _str_to_pj_str(object string, pj_str_t *pj_str) except -1 cdef object _pj_str_to_bytes(pj_str_t pj_bytes) cdef object _pj_str_to_str(pj_str_t pj_str) cdef object _pj_status_to_str(int status) cdef object _pj_status_to_def(int status) +cdef object _buf_to_str(object buf) +cdef object _str_as_str(object string) +cdef object _str_as_size(object string) + + cdef dict _pjsip_param_to_dict(pjsip_param *param_list) cdef int _dict_to_pjsip_param(object params, pjsip_param *param_list, pj_pool_t *pool) cdef int _pjsip_msg_to_dict(pjsip_msg *msg, dict info_dict) except -1 cdef int _is_valid_ip(int af, object ip) except -1 cdef int _get_ip_version(object ip) except -1 cdef int _add_headers_to_tdata(pjsip_tx_data *tdata, object headers) except -1 cdef int _remove_headers_from_tdata(pjsip_tx_data *tdata, object headers) except -1 cdef int _BaseSIPURI_to_pjsip_sip_uri(BaseSIPURI uri, pjsip_sip_uri *pj_uri, pj_pool_t *pool) except -1 cdef int _BaseRouteHeader_to_pjsip_route_hdr(BaseIdentityHeader header, pjsip_route_hdr *pj_header, pj_pool_t *pool) except -1 # core.ua ctypedef int (*timer_callback)(object, object) except -1 cdef class Timer(object): # attributes cdef int _scheduled cdef double schedule_time cdef timer_callback callback cdef object obj # private methods cdef int schedule(self, float delay, timer_callback callback, object obj) except -1 cdef int cancel(self) except -1 cdef int call(self) except -1 cdef class PJSIPThread(object): # attributes cdef pj_thread_t *_obj cdef long _thread_desc[PJ_THREAD_DESC_SIZE] cdef class PJSIPUA(object): # attributes cdef object _threads cdef object _event_handler cdef list _timers cdef PJLIB _pjlib cdef PJCachingPool _caching_pool cdef PJSIPEndpoint _pjsip_endpoint cdef PJMEDIAEndpoint _pjmedia_endpoint cdef pjsip_module _module cdef PJSTR _module_name cdef pjsip_module _opus_fix_module cdef PJSTR _opus_fix_module_name cdef pjsip_module _trace_module cdef PJSTR _trace_module_name cdef pjsip_module _ua_tag_module cdef PJSTR _ua_tag_module_name cdef pjsip_module _event_module cdef PJSTR _event_module_name cdef int _trace_sip cdef int _detect_sip_loops cdef int _enable_colorbar_device cdef PJSTR _user_agent cdef object _events cdef object _sent_messages cdef object _ip_address cdef int _rtp_port_start cdef int _rtp_port_count cdef int _rtp_port_usable_count cdef int _rtp_port_index cdef pj_stun_config _stun_cfg cdef int _fatal_error cdef set _incoming_events cdef set _incoming_requests cdef pj_rwmutex_t *audio_change_rwlock cdef pj_mutex_t *video_lock cdef list old_devices cdef list old_video_devices cdef object _zrtp_cache # private methods cdef object _get_sound_devices(self, int is_output) cdef object _get_default_sound_device(self, int is_output) cdef object _get_video_devices(self) cdef object _get_default_video_device(self) cdef int _poll_log(self) except -1 cdef int _handle_exception(self, int is_fatal) except -1 cdef int _check_self(self) except -1 cdef int _check_thread(self) except -1 cdef int _add_timer(self, Timer timer) except -1 cdef int _remove_timer(self, Timer timer) except -1 cdef int _cb_rx_request(self, pjsip_rx_data *rdata) except 0 cdef pj_pool_t* create_memory_pool(self, bytes name, int initial_size, int resize_size) cdef void release_memory_pool(self, pj_pool_t* pool) cdef void reset_memory_pool(self, pj_pool_t* pool) cdef int _PJSIPUA_cb_rx_request(pjsip_rx_data *rdata) with gil cdef void _cb_detect_nat_type(void *user_data, pj_stun_nat_detect_result_ptr_const res) with gil cdef int _cb_opus_fix_tx(pjsip_tx_data *tdata) with gil cdef int _cb_trace_rx(pjsip_rx_data *rdata) with gil cdef int _cb_trace_tx(pjsip_tx_data *tdata) with gil cdef int _cb_add_user_agent_hdr(pjsip_tx_data *tdata) with gil cdef int _cb_add_server_hdr(pjsip_tx_data *tdata) with gil cdef PJSIPUA _get_ua() cdef int deallocate_weakref(object weak_ref, object timer) except -1 with gil # core.sound cdef class AudioMixer(object): # attributes cdef int _input_volume cdef int _output_volume cdef bint _muted cdef pj_mutex_t *_lock cdef pj_pool_t *_conf_pool cdef pj_pool_t *_snd_pool cdef pjmedia_conf *_obj cdef pjmedia_master_port *_master_port cdef pjmedia_port *_null_port cdef pjmedia_snd_port *_snd cdef list _connected_slots cdef readonly int ec_tail_length cdef readonly int sample_rate cdef readonly int slot_count cdef readonly int used_slot_count cdef readonly unicode input_device cdef readonly unicode output_device cdef readonly unicode real_input_device cdef readonly unicode real_output_device # private methods cdef void _start_sound_device(self, PJSIPUA ua, unicode input_device, unicode output_device, int ec_tail_length) cdef void _stop_sound_device(self, PJSIPUA ua) cdef int _add_port(self, PJSIPUA ua, pj_pool_t *pool, pjmedia_port *port) except -1 with gil cdef int _remove_port(self, PJSIPUA ua, unsigned int slot) except -1 with gil cdef int _cb_postpoll_stop_sound(self, timer) except -1 cdef class ToneGenerator(object): # attributes cdef int _slot cdef int _volume cdef pj_mutex_t *_lock cdef pj_pool_t *_pool cdef pjmedia_port *_obj cdef Timer _timer cdef readonly AudioMixer mixer # private methods cdef PJSIPUA _get_ua(self, int raise_exception) cdef int _stop(self, PJSIPUA ua) except -1 cdef int _cb_check_done(self, timer) except -1 cdef class RecordingWaveFile(object): # attributes cdef int _slot cdef int _was_started cdef pj_mutex_t *_lock cdef pj_pool_t *_pool cdef pjmedia_port *_port cdef readonly str filename cdef readonly AudioMixer mixer # private methods cdef PJSIPUA _check_ua(self) cdef int _stop(self, PJSIPUA ua) except -1 cdef class WaveFile(object): # attributes cdef object __weakref__ cdef object weakref cdef int _slot cdef int _volume cdef pj_mutex_t *_lock cdef pj_pool_t *_pool cdef pjmedia_port *_port cdef readonly str filename cdef readonly AudioMixer mixer # private methods cdef PJSIPUA _check_ua(self) cdef int _stop(self, PJSIPUA ua, int notify) except -1 cdef int _cb_eof(self, timer) except -1 cdef class MixerPort(object): cdef int _slot cdef int _was_started cdef pj_mutex_t *_lock cdef pj_pool_t *_pool cdef pjmedia_port *_port cdef readonly AudioMixer mixer # private methods cdef PJSIPUA _check_ua(self) cdef int _stop(self, PJSIPUA ua) except -1 cdef int _AudioMixer_dealloc_handler(object obj) except -1 cdef int cb_play_wav_eof(pjmedia_port *port, void *user_data) with gil # core.video cdef class VideoFrame(object): cdef readonly str data cdef readonly int width cdef readonly int height cdef class VideoConsumer(object): cdef pjmedia_port *consumer_port cdef pjmedia_vid_port *_video_port cdef pj_pool_t *_pool cdef pj_mutex_t *_lock cdef int _running cdef int _closed cdef VideoProducer _producer cdef object __weakref__ cdef object weakref cdef void _set_producer(self, VideoProducer producer) cdef class VideoProducer(object): cdef pjmedia_port *producer_port cdef pjmedia_vid_port *_video_port cdef pj_pool_t *_pool cdef pj_mutex_t *_lock cdef int _running cdef int _started cdef int _closed cdef object _consumers cdef void _add_consumer(self, VideoConsumer consumer) cdef void _remove_consumer(self, VideoConsumer consumer) cdef class VideoCamera(VideoProducer): cdef pjmedia_port *_video_tee cdef readonly unicode name cdef readonly unicode real_name cdef void _start(self) cdef void _stop(self) cdef class FrameBufferVideoRenderer(VideoConsumer): cdef pjmedia_vid_dev_stream *_video_stream cdef object _frame_handler cdef _initialize(self, VideoProducer producer) cdef void _destroy_video_port(self) cdef void _start(self) cdef void _stop(self) cdef class LocalVideoStream(VideoConsumer): cdef void _initialize(self, pjmedia_port *media_port) cdef class RemoteVideoStream(VideoProducer): cdef pjmedia_vid_stream *_video_stream cdef object _event_handler cdef void _initialize(self, pjmedia_vid_stream *stream) cdef LocalVideoStream_create(pjmedia_vid_stream *stream) cdef RemoteVideoStream_create(pjmedia_vid_stream *stream, format_change_handler=*) cdef int RemoteVideoStream_on_event(pjmedia_event *event, void *user_data) with gil cdef void _start_video_port(pjmedia_vid_port *port) cdef void _stop_video_port(pjmedia_vid_port *port) # core.event cdef struct _core_event cdef struct _handler_queue cdef int _event_queue_append(_core_event *event) cdef void _cb_log(int level, char_ptr_const data, int len) cdef int _add_event(object event_name, dict params) except -1 cdef list _get_clear_event_queue() cdef int _add_handler(int func(object obj) except -1, object obj, _handler_queue *queue) except -1 cdef int _remove_handler(object obj, _handler_queue *queue) except -1 cdef int _process_handler_queue(PJSIPUA ua, _handler_queue *queue) except -1 # core.request cdef class EndpointAddress(object): # attributes cdef readonly bytes ip cdef readonly int port cdef class Request(object): # attributes cdef readonly object state cdef PJSTR _method cdef readonly EndpointAddress peer_address cdef readonly FrozenCredentials credentials cdef readonly FrozenFromHeader from_header cdef readonly FrozenToHeader to_header cdef readonly FrozenSIPURI request_uri cdef readonly FrozenContactHeader contact_header cdef readonly FrozenRouteHeader route_header cdef PJSTR _call_id cdef readonly int cseq cdef readonly frozenlist extra_headers cdef PJSTR _content_type cdef PJSTR _content_subtype cdef PJSTR _body cdef pjsip_tx_data *_tdata cdef pjsip_transaction *_tsx cdef pjsip_auth_clt_sess _auth cdef pjsip_route_hdr _route_header cdef int _need_auth cdef pj_timer_entry _timer cdef int _timer_active cdef int _expire_rest cdef object _expire_time cdef object _timeout # private methods cdef PJSIPUA _get_ua(self) cdef int _cb_tsx_state(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1 cdef int _cb_timer(self, PJSIPUA ua) except -1 cdef class IncomingRequest(object): # attributes cdef readonly str state cdef pjsip_transaction *_tsx cdef pjsip_tx_data *_tdata cdef readonly EndpointAddress peer_address # methods cdef int init(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1 cdef void _Request_cb_tsx_state(pjsip_transaction *tsx, pjsip_event *event) with gil cdef void _Request_cb_timer(pj_timer_heap_t *timer_heap, pj_timer_entry *entry) with gil # core.referral cdef class Referral(object): # attributes cdef pjsip_evsub *_obj cdef pjsip_dialog *_dlg cdef pjsip_route_hdr _route_header cdef pj_list _route_set cdef int _create_subscription cdef readonly object state cdef pj_timer_entry _timeout_timer cdef int _timeout_timer_active cdef pj_timer_entry _refresh_timer cdef int _refresh_timer_active cdef readonly EndpointAddress peer_address cdef readonly FrozenFromHeader from_header cdef readonly FrozenToHeader to_header cdef readonly FrozenReferToHeader refer_to_header cdef readonly FrozenRouteHeader route_header cdef readonly FrozenCredentials credentials cdef readonly FrozenContactHeader local_contact_header cdef readonly FrozenContactHeader remote_contact_header cdef readonly int refresh cdef readonly frozenlist extra_headers cdef pj_time_val _request_timeout cdef int _want_end cdef int _term_code cdef object _term_reason # private methods cdef PJSIPUA _get_ua(self) cdef int _update_contact_header(self, BaseContactHeader contact_header) except -1 cdef int _cancel_timers(self, PJSIPUA ua, int cancel_timeout, int cancel_refresh) except -1 cdef int _send_refer(self, PJSIPUA ua, pj_time_val *timeout, FrozenReferToHeader refer_to_header, frozenlist extra_headers) except -1 cdef int _send_subscribe(self, PJSIPUA ua, int expires, pj_time_val *timeout, frozenlist extra_headers) except -1 cdef int _cb_state(self, PJSIPUA ua, object state, int code, str reason) except -1 cdef int _cb_got_response(self, PJSIPUA ua, pjsip_rx_data *rdata, str method) except -1 cdef int _cb_notify(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1 cdef int _cb_timeout_timer(self, PJSIPUA ua) cdef int _cb_refresh_timer(self, PJSIPUA ua) cdef class IncomingReferral(object): cdef pjsip_evsub *_obj cdef pjsip_dialog *_dlg cdef pjsip_tx_data *_initial_response cdef pjsip_transaction *_initial_tsx cdef pj_time_val _expires_time cdef int _create_subscription cdef readonly str state cdef readonly EndpointAddress peer_address cdef readonly FrozenContactHeader local_contact_header cdef readonly FrozenContactHeader remote_contact_header cdef PJSTR _content cdef int init(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1 cdef PJSIPUA _get_ua(self, int raise_exception) cdef int _set_content(self, int code, str reason) except -1 cdef int _set_state(self, str state) except -1 cdef int _send_initial_response(self, int code) except -1 cdef int _send_notify(self) except -1 cdef int _terminate(self, PJSIPUA ua, int do_cleanup) except -1 cdef int _cb_rx_refresh(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1 cdef int _cb_server_timeout(self, PJSIPUA ua) except -1 cdef int _cb_tsx(self, PJSIPUA ua, pjsip_event *event) except -1 cdef void _Referral_cb_state(pjsip_evsub *sub, pjsip_event *event) with gil cdef void _Referral_cb_notify(pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body) with gil cdef void _Referral_cb_refresh(pjsip_evsub *sub) with gil cdef void _IncomingReferral_cb_rx_refresh(pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body) with gil cdef void _IncomingReferral_cb_server_timeout(pjsip_evsub *sub) with gil cdef void _IncomingReferral_cb_tsx(pjsip_evsub *sub, pjsip_transaction *tsx, pjsip_event *event) with gil # core.subscription cdef class Subscription(object): # attributes cdef pjsip_evsub *_obj cdef pjsip_dialog *_dlg cdef pjsip_route_hdr _route_header cdef pj_list _route_set cdef pj_timer_entry _timeout_timer cdef int _timeout_timer_active cdef pj_timer_entry _refresh_timer cdef int _refresh_timer_active cdef readonly object state cdef readonly EndpointAddress peer_address cdef readonly FrozenFromHeader from_header cdef readonly FrozenToHeader to_header cdef readonly FrozenContactHeader contact_header cdef readonly object event cdef readonly FrozenRouteHeader route_header cdef readonly FrozenCredentials credentials cdef readonly int refresh cdef readonly frozenlist extra_headers cdef readonly object body cdef readonly object content_type cdef readonly str call_id cdef pj_time_val _subscribe_timeout cdef int _want_end cdef int _term_code cdef object _term_reason cdef int _expires # private methods cdef PJSIPUA _get_ua(self) cdef int _cancel_timers(self, PJSIPUA ua, int cancel_timeout, int cancel_refresh) except -1 cdef int _send_subscribe(self, PJSIPUA ua, int expires, pj_time_val *timeout, object extra_headers, object content_type, object body) except -1 cdef int _cb_state(self, PJSIPUA ua, object state, int code, object reason, dict headers) except -1 cdef int _cb_got_response(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1 cdef int _cb_notify(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1 cdef int _cb_timeout_timer(self, PJSIPUA ua) cdef int _cb_refresh_timer(self, PJSIPUA ua) cdef class IncomingSubscription(object): # attributes cdef pjsip_evsub *_obj cdef pjsip_dialog *_dlg cdef PJSTR _content_type cdef PJSTR _content_subtype cdef PJSTR _content cdef pjsip_tx_data *_initial_response cdef pjsip_transaction *_initial_tsx cdef int _expires cdef readonly str state cdef readonly str event cdef readonly str call_id cdef readonly EndpointAddress peer_address # methods cdef int _set_state(self, str state) except -1 cdef PJSIPUA _get_ua(self, int raise_exception) cdef int init(self, PJSIPUA ua, pjsip_rx_data *rdata, str event) except -1 cdef int _send_initial_response(self, int code) except -1 cdef int _send_notify(self, str reason=*) except -1 cdef int _terminate(self, PJSIPUA ua, str reason, int do_cleanup) except -1 cdef int _cb_rx_refresh(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1 cdef int _cb_server_timeout(self, PJSIPUA ua) except -1 cdef int _cb_tsx(self, PJSIPUA ua, pjsip_event *event) except -1 cdef void _Subscription_cb_state(pjsip_evsub *sub, pjsip_event *event) with gil cdef void _Subscription_cb_notify(pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body) with gil cdef void _Subscription_cb_refresh(pjsip_evsub *sub) with gil cdef void _IncomingSubscription_cb_rx_refresh(pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body) with gil cdef void _IncomingSubscription_cb_server_timeout(pjsip_evsub *sub) with gil cdef void _IncomingSubscription_cb_tsx(pjsip_evsub *sub, pjsip_transaction *tsx, pjsip_event *event) with gil # core.sdp cdef class BaseSDPConnection(object): # attributes cdef pjmedia_sdp_conn _sdp_connection # private methods cdef pjmedia_sdp_conn* get_sdp_connection(self) cdef class SDPConnection(BaseSDPConnection): # attributes cdef str _address cdef str _net_type cdef str _address_type cdef class FrozenSDPConnection(BaseSDPConnection): # attributes cdef int initialized cdef readonly str address cdef readonly str net_type cdef readonly str address_type cdef class SDPAttributeList(list): pass cdef class FrozenSDPAttributeList(frozenlist): pass cdef class SDPBandwidthInfoList(list): pass cdef class FrozenSDPBandwidthInfoList(frozenlist): pass cdef class BaseSDPSession(object): # attributes cdef pjmedia_sdp_session _sdp_session # private methods cdef pjmedia_sdp_session* get_sdp_session(self) cdef class SDPSession(BaseSDPSession): # attributes cdef str _address cdef str _user cdef str _net_type cdef str _address_type cdef str _name cdef str _info cdef SDPConnection _connection cdef list _attributes cdef list _bandwidth_info cdef list _media # private methods cdef int _update(self) except -1 cdef class FrozenSDPSession(BaseSDPSession): # attributes cdef int initialized cdef readonly str address cdef readonly unsigned int id cdef readonly unsigned int version cdef readonly str user cdef readonly str net_type cdef readonly str address_type cdef readonly str name cdef readonly str info cdef readonly FrozenSDPConnection connection cdef readonly int start_time cdef readonly int stop_time cdef readonly FrozenSDPAttributeList attributes cdef readonly FrozenSDPBandwidthInfoList bandwidth_info cdef readonly frozenlist media cdef class BaseSDPMediaStream(object): # attributes cdef pjmedia_sdp_media _sdp_media # private methods cdef pjmedia_sdp_media* get_sdp_media(self) cdef class SDPMediaStream(BaseSDPMediaStream): # attributes cdef str _media cdef str _transport cdef list _formats cdef list _codec_list cdef str _info cdef SDPConnection _connection cdef SDPAttributeList _attributes cdef SDPBandwidthInfoList _bandwidth_info # private methods cdef int _update(self, SDPMediaStream media) except -1 cdef class FrozenSDPMediaStream(BaseSDPMediaStream): # attributes cdef int initialized cdef readonly str media cdef readonly int port cdef readonly str transport cdef readonly int port_count cdef readonly frozenlist formats cdef readonly frozenlist codec_list cdef readonly str info cdef readonly FrozenSDPConnection connection cdef readonly FrozenSDPAttributeList attributes cdef readonly FrozenSDPBandwidthInfoList bandwidth_info cdef class BaseSDPAttribute(object): # attributes cdef pjmedia_sdp_attr _sdp_attribute # private methods cdef pjmedia_sdp_attr* get_sdp_attribute(self) cdef class SDPAttribute(BaseSDPAttribute): # attributes cdef str _name cdef str _value cdef class FrozenSDPAttribute(BaseSDPAttribute): # attributes cdef int initialized cdef readonly str name cdef readonly str value cdef class BaseSDPBandwidthInfo(object): # attributes cdef pjmedia_sdp_bandw _sdp_bandwidth_info # private methods cdef pjmedia_sdp_bandw* get_sdp_bandwidth_info(self) cdef class SDPBandwidthInfo(BaseSDPBandwidthInfo): # attributes cdef str _modifier cdef int _value cdef class FrozenSDPBandwidthInfo(BaseSDPBandwidthInfo): # attributes cdef int initialized cdef readonly str modifier cdef readonly int value cdef SDPSession SDPSession_create(pjmedia_sdp_session_ptr_const pj_session) cdef FrozenSDPSession FrozenSDPSession_create(pjmedia_sdp_session_ptr_const pj_session) cdef SDPMediaStream SDPMediaStream_create(pjmedia_sdp_media *pj_media) cdef FrozenSDPMediaStream FrozenSDPMediaStream_create(pjmedia_sdp_media *pj_media) cdef SDPConnection SDPConnection_create(pjmedia_sdp_conn *pj_conn) cdef FrozenSDPConnection FrozenSDPConnection_create(pjmedia_sdp_conn *pj_conn) cdef SDPAttribute SDPAttribute_create(pjmedia_sdp_attr *pj_attr) cdef FrozenSDPAttribute FrozenSDPAttribute_create(pjmedia_sdp_attr *pj_attr) cdef SDPBandwidthInfo SDPBandwidthInfo_create(pjmedia_sdp_bandw *pj_bandw) cdef FrozenSDPBandwidthInfo FrozenSDPBandwidthInfo_create(pjmedia_sdp_bandw *pj_bandw) cdef class SDPNegotiator(object): # attributes cdef pjmedia_sdp_neg* _neg cdef pj_pool_t *_pool # core.invitation cdef class SDPPayloads: cdef readonly FrozenSDPSession proposed_local cdef readonly FrozenSDPSession proposed_remote cdef readonly FrozenSDPSession active_local cdef readonly FrozenSDPSession active_remote cdef class StateCallbackTimer(Timer): cdef object state cdef object sub_state cdef object rdata cdef object tdata cdef object originator cdef class SDPCallbackTimer(Timer): cdef int status cdef object active_local cdef object active_remote cdef class TransferStateCallbackTimer(Timer): cdef object state cdef object code cdef object reason cdef class TransferResponseCallbackTimer(Timer): cdef object method cdef object rdata cdef class TransferRequestCallbackTimer(Timer): cdef object rdata cdef class Invitation(object): # attributes cdef object __weakref__ cdef object weakref cdef int _sdp_neg_status cdef int _failed_response cdef pj_list _route_set cdef pj_mutex_t *_lock cdef pjsip_inv_session *_invite_session cdef pjsip_evsub *_transfer_usage cdef pjsip_role_e _transfer_usage_role cdef pjsip_dialog *_dialog cdef pjsip_route_hdr _route_header cdef pjsip_transaction *_reinvite_transaction cdef PJSTR _sipfrag_payload cdef Timer _timer cdef Timer _transfer_timeout_timer cdef Timer _transfer_refresh_timer cdef readonly str call_id cdef readonly str direction cdef readonly str remote_user_agent cdef readonly str state cdef readonly str sub_state cdef readonly str transport cdef readonly str transfer_state cdef readonly EndpointAddress peer_address cdef readonly FrozenCredentials credentials cdef readonly FrozenContactHeader local_contact_header cdef readonly FrozenContactHeader remote_contact_header cdef readonly FrozenFromHeader from_header cdef readonly FrozenToHeader to_header cdef readonly FrozenSIPURI request_uri cdef readonly FrozenRouteHeader route_header cdef readonly SDPPayloads sdp # private methods cdef int init_incoming(self, PJSIPUA ua, pjsip_rx_data *rdata, unsigned int inv_options) except -1 cdef int process_incoming_transfer(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1 cdef int process_incoming_options(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1 cdef PJSIPUA _check_ua(self) cdef int _do_dealloc(self) except -1 cdef int _update_contact_header(self, BaseContactHeader contact_header) except -1 cdef int _fail(self, PJSIPUA ua) except -1 cdef int _cb_state(self, StateCallbackTimer timer) except -1 cdef int _cb_sdp_done(self, SDPCallbackTimer timer) except -1 cdef int _cb_timer_disconnect(self, timer) except -1 cdef int _cb_postpoll_fail(self, timer) except -1 cdef int _start_incoming_transfer(self, timer) except -1 cdef int _terminate_transfer(self) except -1 cdef int _terminate_transfer_uac(self) except -1 cdef int _terminate_transfer_uas(self) except -1 cdef int _set_transfer_state(self, str state) except -1 cdef int _set_sipfrag_payload(self, int code, str reason) except -1 cdef int _send_notify(self) except -1 cdef int _transfer_cb_timeout_timer(self, timer) except -1 cdef int _transfer_cb_refresh_timer(self, timer) except -1 cdef int _transfer_cb_state(self, TransferStateCallbackTimer timer) except -1 cdef int _transfer_cb_response(self, TransferResponseCallbackTimer timer) except -1 cdef int _transfer_cb_notify(self, TransferRequestCallbackTimer timer) except -1 cdef int _transfer_cb_server_timeout(self, timer) except -1 cdef void _Invitation_cb_state(pjsip_inv_session *inv, pjsip_event *e) with gil cdef void _Invitation_cb_sdp_done(pjsip_inv_session *inv, int status) with gil cdef int _Invitation_cb_rx_reinvite(pjsip_inv_session *inv, pjmedia_sdp_session_ptr_const offer, pjsip_rx_data *rdata) with gil cdef void _Invitation_cb_tsx_state_changed(pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_event *e) with gil cdef void _Invitation_cb_new(pjsip_inv_session *inv, pjsip_event *e) with gil cdef void _Invitation_transfer_cb_state(pjsip_evsub *sub, pjsip_event *event) with gil cdef void _Invitation_transfer_cb_tsx(pjsip_evsub *sub, pjsip_transaction *tsx, pjsip_event *event) with gil cdef void _Invitation_transfer_cb_notify(pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body) with gil cdef void _Invitation_transfer_cb_refresh(pjsip_evsub *sub) with gil cdef void _Invitation_transfer_in_cb_rx_refresh(pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body) with gil cdef void _Invitation_transfer_in_cb_server_timeout(pjsip_evsub *sub) with gil # core.mediatransport cdef class ICECandidate(object): # attributes cdef readonly str component cdef readonly str type cdef readonly str address cdef readonly int port cdef readonly int priority cdef readonly str rel_address cdef class ICECheck(object): cdef readonly ICECandidate local_candidate cdef readonly ICECandidate remote_candidate cdef readonly str state cdef readonly int nominated cdef class RTPTransport(object): # attributes cdef object __weakref__ cdef object weakref cdef int _af cdef pj_mutex_t *_lock cdef pj_pool_t *_pool cdef pjmedia_transport *_obj cdef pjmedia_transport *_wrapped_transport cdef ICECheck _rtp_valid_pair cdef str _encryption cdef readonly object ice_stun_address cdef readonly object ice_stun_port cdef readonly object state cdef readonly object use_ice # private methods cdef PJSIPUA _check_ua(self) cdef void _get_info(self, pjmedia_transport_info *info) cdef int _init_local_sdp(self, BaseSDPSession local_sdp, BaseSDPSession remote_sdp, int sdp_index) cdef int _ice_active(self) cdef class MediaCheckTimer(Timer): # attributes cdef int media_check_interval cdef class SDPInfo(object): # attributes cdef BaseSDPMediaStream _local_media cdef BaseSDPSession _local_sdp cdef BaseSDPSession _remote_sdp cdef public int index cdef class AudioTransport(object): # attributes cdef object __weakref__ cdef object weakref cdef int _is_offer cdef int _is_started cdef int _slot cdef int _volume cdef unsigned int _packets_received cdef unsigned int _vad cdef pj_mutex_t *_lock cdef pj_pool_t *_pool cdef pjmedia_stream *_obj cdef pjmedia_stream_info _stream_info cdef Timer _timer cdef readonly object direction cdef readonly AudioMixer mixer cdef readonly RTPTransport transport cdef SDPInfo _sdp_info # private methods cdef PJSIPUA _check_ua(self) cdef int _cb_check_rtp(self, MediaCheckTimer timer) except -1 cdef class VideoTransport(object): # attributes cdef object __weakref__ cdef object weakref cdef int _is_offer cdef int _is_started cdef unsigned int _packets_received cdef pj_mutex_t *_lock cdef pj_pool_t *_pool cdef pjmedia_vid_stream *_obj cdef pjmedia_vid_stream_info _stream_info cdef Timer _timer cdef readonly object direction cdef readonly RTPTransport transport cdef SDPInfo _sdp_info cdef readonly LocalVideoStream local_video cdef readonly RemoteVideoStream remote_video # private methods cdef PJSIPUA _check_ua(self) cdef int _cb_check_rtp(self, MediaCheckTimer timer) except -1 cdef void _RTPTransport_cb_ice_complete(pjmedia_transport *tp, pj_ice_strans_op op, int status) with gil cdef void _RTPTransport_cb_ice_state(pjmedia_transport *tp, pj_ice_strans_state prev, pj_ice_strans_state curr) with gil cdef void _RTPTransport_cb_ice_stop(pjmedia_transport *tp, char *reason, int err) with gil cdef void _RTPTransport_cb_zrtp_secure_on(pjmedia_transport *tp, char* cipher) with gil cdef void _RTPTransport_cb_zrtp_secure_off(pjmedia_transport *tp) with gil cdef void _RTPTransport_cb_zrtp_show_sas(pjmedia_transport *tp, char* sas, int verified) with gil cdef void _RTPTransport_cb_zrtp_confirm_goclear(pjmedia_transport *tp) with gil cdef void _RTPTransport_cb_zrtp_show_message(pjmedia_transport *tp, int severity, int subCode) with gil cdef void _RTPTransport_cb_zrtp_negotiation_failed(pjmedia_transport *tp, int severity, int subCode) with gil cdef void _RTPTransport_cb_zrtp_not_supported_by_other(pjmedia_transport *tp) with gil cdef void _RTPTransport_cb_zrtp_ask_enrollment(pjmedia_transport *tp, int info) with gil cdef void _RTPTransport_cb_zrtp_inform_enrollment(pjmedia_transport *tp, int info) with gil cdef void _AudioTransport_cb_dtmf(pjmedia_stream *stream, void *user_data, int digit) with gil cdef ICECandidate ICECandidate_create(pj_ice_sess_cand *cand) cdef ICECheck ICECheck_create(pj_ice_sess_check *check) cdef str _ice_state_to_str(int state) cdef dict _extract_ice_session_data(pj_ice_sess *ice_sess) cdef object _extract_rtp_transport(pjmedia_transport *tp) cdef dict _pj_math_stat_to_dict(pj_math_stat *stat) cdef dict _pjmedia_rtcp_stream_stat_to_dict(pjmedia_rtcp_stream_stat *stream_stat) diff --git a/sipsimple/core/_core.referral.pxi b/sipsimple/core/_core.referral.pxi index f3d8c619..50eb6f67 100644 --- a/sipsimple/core/_core.referral.pxi +++ b/sipsimple/core/_core.referral.pxi @@ -1,1003 +1,1003 @@ import re cdef class Referral: expire_warning_time = 30 def __cinit__(self, *args, **kwargs): self.state = "NULL" pj_timer_entry_init(&self._timeout_timer, 0, self, _Referral_cb_timer) self._timeout_timer_active = 0 pj_timer_entry_init(&self._refresh_timer, 1, self, _Referral_cb_timer) self._refresh_timer_active = 0 self.extra_headers = frozenlist() self.peer_address = None self._create_subscription = 1 self.local_contact_header = None self.remote_contact_header = None def __init__(self, SIPURI request_uri not None, FromHeader from_header not None, ToHeader to_header not None, ReferToHeader refer_to_header not None, ContactHeader contact_header not None, RouteHeader route_header not None, Credentials credentials=None): global _refer_cb global _refer_event cdef PJSTR from_header_str cdef PJSTR to_header_str cdef PJSTR contact_str cdef PJSTR request_uri_str cdef pjsip_cred_info *cred_info cdef PJSIPUA ua = _get_ua() cdef int status if self._obj != NULL or self.state != "NULL": raise SIPCoreError("Referral.__init__() was already called") self.local_contact_header = FrozenContactHeader.new(contact_header) self.route_header = FrozenRouteHeader.new(route_header) self.route_header.uri.parameters.dict["lr"] = None # always send lr parameter in Route header self.route_header.uri.parameters.dict["hide"] = None # always hide Route header if credentials is not None: self.credentials = FrozenCredentials.new(credentials) from_header_parameters = from_header.parameters.copy() from_header_parameters.pop("tag", None) from_header.parameters = {} from_header_str = PJSTR(from_header.body) to_header_parameters = to_header.parameters.copy() to_header_parameters.pop("tag", None) to_header.parameters = {} to_header_str = PJSTR(to_header.body) contact_str = PJSTR(str(contact_header.body)) request_uri_str = PJSTR(str(request_uri)) with nogil: status = pjsip_dlg_create_uac(pjsip_ua_instance(), &from_header_str.pj_str, &contact_str.pj_str, &to_header_str.pj_str, &request_uri_str.pj_str, &self._dlg) if status != 0: raise PJSIPError("Could not create dialog for REFER", status) # Increment dialog session count so that it's never destroyed by PJSIP with nogil: status = pjsip_dlg_inc_session(self._dlg, &ua._module) if contact_header.expires is not None: self._dlg.local.contact.expires = contact_header.expires if contact_header.q is not None: self._dlg.local.contact.q1000 = int(contact_header.q*1000) contact_parameters = contact_header.parameters.copy() contact_parameters.pop("q", None) contact_parameters.pop("expires", None) _dict_to_pjsip_param(contact_parameters, &self._dlg.local.contact.other_param, self._dlg.pool) _dict_to_pjsip_param(from_header_parameters, &self._dlg.local.info.other_param, self._dlg.pool) _dict_to_pjsip_param(to_header_parameters, &self._dlg.remote.info.other_param, self._dlg.pool) self.from_header = FrozenFromHeader_create(self._dlg.local.info) self.to_header = FrozenToHeader.new(to_header) self.refer_to_header = FrozenReferToHeader.new(refer_to_header) with nogil: status = pjsip_evsub_create_uac(self._dlg, &_refer_cb, &_refer_event.pj_str, PJSIP_EVSUB_NO_EVENT_ID, &self._obj) if status != 0: raise PJSIPError("Could not create REFER", status) pjsip_evsub_set_mod_data(self._obj, ua._event_module.id, self) _BaseRouteHeader_to_pjsip_route_hdr(self.route_header, &self._route_header, self._dlg.pool) pj_list_init( &self._route_set) pj_list_insert_after( &self._route_set, &self._route_header) with nogil: status = pjsip_dlg_set_route_set(self._dlg, &self._route_set) if status != 0: raise PJSIPError("Could not set route on REFER", status) if self.credentials is not None: cred_info = self.credentials.get_cred_info() with nogil: status = pjsip_auth_clt_set_credentials(&self._dlg.auth_sess, 1, cred_info) if status != 0: raise PJSIPError("Could not set credentials for REFER", status) def __dealloc__(self): cdef PJSIPUA ua = self._get_ua() if ua is not None: self._cancel_timers(ua, 1, 1) if self._obj != NULL: pjsip_evsub_set_mod_data(self._obj, ua._event_module.id, NULL) with nogil: pjsip_evsub_terminate(self._obj, 0) self._obj = NULL if self._dlg != NULL: with nogil: pjsip_dlg_dec_session(self._dlg, &ua._module) self._dlg = NULL def send_refer(self, int create_subscription=1, list extra_headers not None=list(), object timeout=None): cdef PJSIPUA ua = self._get_ua() with nogil: pjsip_dlg_inc_lock(self._dlg) try: if self.state != "NULL": raise SIPCoreError('This method may only be called in the "NULL" state') if timeout is not None: if timeout <= 0: raise ValueError("Timeout value cannot be negative") self._request_timeout.sec = int(timeout) self._request_timeout.msec = (timeout * 1000) % 1000 else: self._request_timeout.sec = 0 self._request_timeout.msec = 0 if extra_headers is not None: self.extra_headers = frozenlist([header.frozen_type.new(header) for header in extra_headers]) self._create_subscription = create_subscription self._send_refer(ua, &self._request_timeout, self.refer_to_header, self.extra_headers) _add_event("SIPReferralWillStart", dict(obj=self)) finally: with nogil: pjsip_dlg_dec_lock(self._dlg) def refresh(self, ContactHeader contact_header=None, list extra_headers not None=list(), object timeout=None): cdef PJSIPUA ua = self._get_ua() with nogil: pjsip_dlg_inc_lock(self._dlg) try: if self.state not in ("ACCEPTED", "ACTIVE", "PENDING"): raise SIPCoreError('This method may only be called in the "ACCEPTED", "ACTIVE" or "PENDING" states') if timeout is not None: if timeout <= 0: raise ValueError("Timeout value cannot be negative") self._request_timeout.sec = int(timeout) self._request_timeout.msec = (timeout * 1000) % 1000 else: self._request_timeout.sec = 0 self._request_timeout.msec = 0 if contact_header is not None: self._update_contact_header(contact_header) if extra_headers is not None: self.extra_headers = frozenlist([header.frozen_type.new(header) for header in extra_headers]) self._send_subscribe(ua, 600, &self._request_timeout, self.extra_headers) finally: with nogil: pjsip_dlg_dec_lock(self._dlg) def end(self, object timeout=None): cdef pj_time_val end_timeout cdef PJSIPUA ua = self._get_ua() with nogil: pjsip_dlg_inc_lock(self._dlg) try: if self.state == "TERMINATED": return if self.state == "NULL": raise SIPCoreError('This method may not be called in the "NULL" state') if timeout is not None: if timeout <= 0: raise ValueError("Timeout value cannot be negative") end_timeout.sec = int(timeout) end_timeout.msec = (timeout * 1000) % 1000 else: end_timeout.sec = 0 end_timeout.msec = 0 self._want_end = 1 self._cancel_timers(ua, 1, 1) _add_event("SIPReferralWillEnd", dict(obj=self)) try: self._send_subscribe(ua, 0, &end_timeout, frozenlist([])) except PJSIPError, e: self._term_reason = e.args[0] if self._obj != NULL: pjsip_evsub_terminate(self._obj, 1) finally: with nogil: pjsip_dlg_dec_lock(self._dlg) cdef PJSIPUA _get_ua(self): cdef PJSIPUA ua try: ua = _get_ua() except SIPCoreError: self._obj = NULL self._timeout_timer_active = 0 self._refresh_timer_active = 0 self.state = "TERMINATED" return None else: return ua cdef int _update_contact_header(self, BaseContactHeader contact_header) except -1: # The PJSIP functions called here don't do much, so there is no need to call them # without the gil. cdef pj_str_t contact_str_pj cdef pjsip_uri *contact contact_str = str(contact_header.uri) if contact_header.display_name: - contact_str = "%s <%s>" % (contact_header.display_name.encode('utf-8'), contact_str) - pj_strdup2_with_null(self._dlg.pool, &contact_str_pj, contact_str) + contact_str = "%s <%s>" % (contact_header.display_name, contact_str) + pj_strdup2_with_null(self._dlg.pool, &contact_str_pj, contact_str.encode()) contact = pjsip_parse_uri(self._dlg.pool, contact_str_pj.ptr, contact_str_pj.slen, PJSIP_PARSE_URI_AS_NAMEADDR) if contact == NULL: raise SIPCoreError("Not a valid Contact header: %s" % contact_str) self._dlg.local.contact = pjsip_contact_hdr_create(self._dlg.pool) self._dlg.local.contact.uri = contact if contact_header.expires is not None: self._dlg.local.contact.expires = contact_header.expires if contact_header.q is not None: self._dlg.local.contact.q1000 = int(contact_header.q*1000) parameters = contact_header.parameters.copy() parameters.pop("q", None) parameters.pop("expires", None) _dict_to_pjsip_param(parameters, &self._dlg.local.contact.other_param, self._dlg.pool) self.local_contact_header = FrozenContactHeader.new(contact_header) return 0 cdef int _cancel_timers(self, PJSIPUA ua, int cancel_timeout, int cancel_refresh) except -1: if cancel_timeout and self._timeout_timer_active: pjsip_endpt_cancel_timer(ua._pjsip_endpoint._obj, &self._timeout_timer) self._timeout_timer_active = 0 if cancel_refresh and self._refresh_timer_active: pjsip_endpt_cancel_timer(ua._pjsip_endpoint._obj, &self._refresh_timer) self._refresh_timer_active = 0 cdef int _send_refer(self, PJSIPUA ua, pj_time_val *timeout, FrozenReferToHeader refer_to_header, frozenlist extra_headers) except -1: global _refer_method cdef pjsip_method refer_method cdef pjsip_tx_data *tdata cdef int status pjsip_method_init_np(&refer_method, &_refer_method.pj_str) with nogil: status = pjsip_evsub_initiate(self._obj, &refer_method, -1, &tdata) if status != 0: raise PJSIPError("Could not create REFER message", status) _add_headers_to_tdata(tdata, [refer_to_header, Header('Referred-By', str(self.from_header.uri))]) _add_headers_to_tdata(tdata, extra_headers) if not self._create_subscription: _add_headers_to_tdata(tdata, [Header('Refer-Sub', 'false')]) # We can't remove the Event header or PJSIP will fail to match responses to this request _remove_headers_from_tdata(tdata, ["Expires"]) with nogil: status = pjsip_evsub_send_request(self._obj, tdata) if status != 0: raise PJSIPError("Could not send REFER message", status) if timeout.sec or timeout.msec: status = pjsip_endpt_schedule_timer(ua._pjsip_endpoint._obj, &self._timeout_timer, timeout) if status == 0: self._timeout_timer_active = 1 cdef int _send_subscribe(self, PJSIPUA ua, int expires, pj_time_val *timeout, frozenlist extra_headers) except -1: cdef pjsip_tx_data *tdata cdef int status with nogil: status = pjsip_evsub_initiate(self._obj, NULL, expires, &tdata) if status != 0: raise PJSIPError("Could not create SUBSCRIBE message", status) _add_headers_to_tdata(tdata, extra_headers) with nogil: status = pjsip_evsub_send_request(self._obj, tdata) if status != 0: raise PJSIPError("Could not send SUBSCRIBE message", status) self._cancel_timers(ua, 1, 0) if timeout.sec or timeout.msec: status = pjsip_endpt_schedule_timer(ua._pjsip_endpoint._obj, &self._timeout_timer, timeout) if status == 0: self._timeout_timer_active = 1 cdef int _cb_state(self, PJSIPUA ua, object state, int code, str reason) except -1: # PJSIP holds the dialog lock when this callback is entered cdef object prev_state = self.state cdef int status self.state = state if state == "ACCEPTED" and prev_state == "SENT": _add_event("SIPReferralDidStart", dict(obj=self)) if not self._create_subscription: # Terminate the subscription self._want_end = 1 _add_event("SIPReferralWillEnd", dict(obj=self)) with nogil: pjsip_evsub_terminate(self._obj, 1) elif state == "TERMINATED": pjsip_evsub_set_mod_data(self._obj, ua._event_module.id, NULL) self._cancel_timers(ua, 1, 1) self._obj = NULL if self._want_end: _add_event("SIPReferralDidEnd", dict(obj=self)) else: if self._term_reason is not None: _add_event("SIPReferralDidFail", dict(obj=self, code=self._term_code, reason=self._term_reason)) elif code/100 == 2: _add_event("SIPReferralDidEnd", dict(obj=self)) else: _add_event("SIPReferralDidFail", dict(obj=self, code=code, reason=reason)) if prev_state != state: _add_event("SIPReferralChangedState", dict(obj=self, prev_state=prev_state, state=state)) cdef int _cb_got_response(self, PJSIPUA ua, pjsip_rx_data *rdata, str method) except -1: # PJSIP holds the dialog lock when this callback is entered global _refer_sub_hdr_name cdef int expires cdef int status cdef dict event_dict = dict() cdef pj_time_val refresh cdef pjsip_generic_int_hdr *expires_hdr cdef pjsip_generic_string_hdr *refer_sub_header self.to_header = FrozenToHeader_create(rdata.msg_info.to_hdr) if self.state != "TERMINATED" and not self._want_end: self._cancel_timers(ua, 1, 0) if method == "REFER": refer_sub_header = pjsip_msg_find_hdr_by_name(rdata.msg_info.msg, &_refer_sub_hdr_name.pj_str, NULL); if not self._create_subscription: if not (refer_sub_header != NULL and _pj_str_to_str(refer_sub_header.hvalue) == "false"): self._create_subscription = 1 elif method == "SUBSCRIBE": # For the REFER method the expires value will be taken from the NOTIFY Subscription-State header expires_hdr = pjsip_msg_find_hdr(rdata.msg_info.msg, PJSIP_H_EXPIRES, NULL) if expires_hdr != NULL and not self._refresh_timer_active: expires = expires_hdr.ivalue refresh.sec = max(1, expires - self.expire_warning_time, expires/2) refresh.msec = 0 status = pjsip_endpt_schedule_timer(ua._pjsip_endpoint._obj, &self._refresh_timer, &refresh) if status == 0: self._refresh_timer_active = 1 if self.state != "TERMINATED": _pjsip_msg_to_dict(rdata.msg_info.msg, event_dict) try: self.remote_contact_header = event_dict['headers']['Contact'][0] except LookupError: pass cdef int _cb_notify(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1: # PJSIP holds the dialog lock when this callback is entered global _subscription_state_hdr_name cdef pjsip_sub_state_hdr *sub_state_hdr cdef pj_time_val refresh cdef int expires cdef dict event_dict = dict() cdef dict notify_dict = dict(obj=self) sub_state_hdr = pjsip_msg_find_hdr_by_name(rdata.msg_info.msg, &_subscription_state_hdr_name.pj_str, NULL) if self.state != "TERMINATED" and sub_state_hdr != NULL and sub_state_hdr.expires_param > 0 and not self._refresh_timer_active: expires = sub_state_hdr.expires_param refresh.sec = max(1, expires - self.expire_warning_time, expires/2) refresh.msec = 0 status = pjsip_endpt_schedule_timer(ua._pjsip_endpoint._obj, &self._refresh_timer, &refresh) if status == 0: self._refresh_timer_active = 1 _pjsip_msg_to_dict(rdata.msg_info.msg, event_dict) if self.state != "TERMINATED": try: self.remote_contact_header = event_dict['headers']['Contact'][0] except LookupError: pass notify_dict["request_uri"] = event_dict["request_uri"] notify_dict["from_header"] = event_dict["headers"].get("From", None) notify_dict["to_header"] = event_dict["headers"].get("To", None) notify_dict["headers"] = event_dict["headers"] notify_dict["body"] = event_dict["body"] content_type = notify_dict["headers"].get("Content-Type", None) notify_dict["content_type"] = content_type.content_type if content_type else None event = notify_dict["headers"].get("Event", None) notify_dict["event"] = event.event if event else None _add_event("SIPReferralGotNotify", notify_dict) cdef int _cb_timeout_timer(self, PJSIPUA ua): # Timer callback, dialog lock is not held by PJSIP global sip_status_messages with nogil: pjsip_dlg_inc_lock(self._dlg) try: self._term_code = PJSIP_SC_TSX_TIMEOUT self._term_reason = sip_status_messages[PJSIP_SC_TSX_TIMEOUT] if self._obj != NULL: with nogil: pjsip_evsub_terminate(self._obj, 1) finally: with nogil: pjsip_dlg_dec_lock(self._dlg) cdef int _cb_refresh_timer(self, PJSIPUA ua): # Timer callback, dialog lock is not held by PJSIP with nogil: pjsip_dlg_inc_lock(self._dlg) try: self._send_subscribe(ua, 600, &self._request_timeout, self.extra_headers) except PJSIPError, e: self._term_reason = e.args[0] if self._obj != NULL: with nogil: pjsip_evsub_terminate(self._obj, 1) finally: with nogil: pjsip_dlg_dec_lock(self._dlg) cdef class IncomingReferral: def __cinit__(self): self.state = None self.peer_address = None self._create_subscription = 1 self.local_contact_header = None self.remote_contact_header = None def __dealloc__(self): cdef PJSIPUA ua = self._get_ua(0) self._initial_response = NULL self._initial_tsx = NULL if self._obj != NULL: pjsip_evsub_set_mod_data(self._obj, ua._event_module.id, NULL) with nogil: pjsip_evsub_terminate(self._obj, 0) self._obj = NULL if self._dlg != NULL and ua is not None: with nogil: pjsip_dlg_dec_session(self._dlg, &ua._module) self._dlg = NULL cdef int init(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1: global _incoming_refer_subs_cb global _event_hdr_name global _refer_event global _refer_to_hdr_name global _refer_sub_hdr_name cdef int status cdef str transport cdef FrozenSIPURI request_uri cdef FrozenContactHeader contact_header cdef PJSTR contact_str cdef dict event_dict cdef pjsip_generic_string_hdr *refer_to_header cdef pjsip_generic_string_hdr *refer_sub_header cdef pjsip_tpselector tp_sel cdef pjsip_event_hdr *event_header refer_to_header = pjsip_msg_find_hdr_by_name(rdata.msg_info.msg, &_refer_to_hdr_name.pj_str, NULL); if refer_to_header == NULL: with nogil: status = pjsip_endpt_create_response(ua._pjsip_endpoint._obj, rdata, 400, NULL, &self._initial_response) if status != 0: raise PJSIPError("Could not create response", status) with nogil: status = pjsip_endpt_send_response2(ua._pjsip_endpoint._obj, rdata, self._initial_response, NULL, NULL) if status != 0: with nogil: pjsip_tx_data_dec_ref(self._initial_response) raise PJSIPError("Could not send response", status) return 0 # If there is a Ref-Sub header and it contains 'false', don't establish a subscription refer_sub_header = pjsip_msg_find_hdr_by_name(rdata.msg_info.msg, &_refer_sub_hdr_name.pj_str, NULL); if refer_sub_header != NULL and _pj_str_to_str(refer_sub_header.hvalue) == "false": self._create_subscription = 0 self._set_state("incoming") self.peer_address = EndpointAddress(rdata.pkt_info.src_name, rdata.pkt_info.src_port) event_dict = dict(obj=self, prev_state=self.state, state="incoming") _pjsip_msg_to_dict(rdata.msg_info.msg, event_dict) try: self.remote_contact_header = event_dict['headers']['Contact'][0] except LookupError: # Contact header is required with nogil: status = pjsip_endpt_create_response(ua._pjsip_endpoint._obj, rdata, 400, NULL, &self._initial_response) if status != 0: raise PJSIPError("Could not create response", status) with nogil: status = pjsip_endpt_send_response2(ua._pjsip_endpoint._obj, rdata, self._initial_response, NULL, NULL) if status != 0: with nogil: pjsip_tx_data_dec_ref(self._initial_response) raise PJSIPError("Could not send response", status) return 0 event_dict["refer_to"] = event_dict["headers"].get("Refer-To") transport = rdata.tp_info.transport.type_name.lower() request_uri = event_dict["request_uri"] if _is_valid_ip(pj_AF_INET(), request_uri.host): self.local_contact_header = FrozenContactHeader(request_uri) else: self.local_contact_header = FrozenContactHeader(FrozenSIPURI(host=_pj_str_to_str(rdata.tp_info.transport.local_name.host), user=request_uri.user, port=rdata.tp_info.transport.local_name.port, parameters=(frozendict(transport=transport) if transport != "udp" else frozendict()))) contact_str = PJSTR(self.local_contact_header.body) with nogil: status = pjsip_dlg_create_uas_and_inc_lock(pjsip_ua_instance(), rdata, &contact_str.pj_str, &self._dlg) if status != 0: with nogil: status = pjsip_endpt_create_response(ua._pjsip_endpoint._obj, rdata, 400, NULL, &self._initial_response) if status != 0: raise PJSIPError("Could not create response", status) with nogil: status = pjsip_endpt_send_response2(ua._pjsip_endpoint._obj, rdata, self._initial_response, NULL, NULL) if status != 0: with nogil: pjsip_tx_data_dec_ref(self._initial_response) raise PJSIPError("Could not send response", status) return 0 # Increment dialog session count so that it's never destroyed by PJSIP with nogil: status = pjsip_dlg_inc_session(self._dlg, &ua._module) if status != 0: pjsip_dlg_dec_lock(self._dlg) raise PJSIPError("Could not increment dialog session count", status) # PJSIP event framework needs an Event header, even if it's not needed for REFER, so we insert a fake one event_header = pjsip_msg_find_hdr_by_name(rdata.msg_info.msg, &_event_hdr_name.pj_str, NULL) if event_header == NULL: event_header = pjsip_event_hdr_create(rdata.tp_info.pool) event_header.event_type = _refer_event.pj_str pjsip_msg_add_hdr(rdata.msg_info.msg, event_header) self._initial_tsx = pjsip_rdata_get_tsx(rdata) with nogil: status = pjsip_evsub_create_uas(self._dlg, &_incoming_refer_subs_cb, rdata, 0, &self._obj) pjsip_dlg_dec_lock(self._dlg) if status != 0: with nogil: pjsip_tsx_terminate(self._initial_tsx, 500) self._initial_tsx = NULL self._dlg = NULL raise PJSIPError("Could not create incoming REFER session", status) pjsip_evsub_set_mod_data(self._obj, ua._event_module.id, self) with nogil: status = pjsip_dlg_create_response(self._dlg, rdata, 500, NULL, &self._initial_response) if status != 0: with nogil: pjsip_tsx_terminate(self._initial_tsx, 500) self._initial_tsx = NULL raise PJSIPError("Could not create response for incoming REFER", status) _add_event("SIPIncomingReferralGotRefer", event_dict) return 0 def accept(self, int code=202, int duration=180): cdef PJSIPUA ua = self._get_ua(1) with nogil: pjsip_dlg_inc_lock(self._dlg) try: if self.state != "incoming": raise SIPCoreInvalidStateError('Can only accept an incoming REFER in the "incoming" state, '+ 'object is currently in the "%s" state' % self.state) pjsip_evsub_update_expires(self._obj, duration) self._send_initial_response(code) self._set_state("active") if not self._create_subscription: pjsip_evsub_set_mod_data(self._obj, ua._event_module.id, NULL) with nogil: pjsip_evsub_terminate(self._obj, 0) self._obj = NULL self._set_state("terminated") _add_event("SIPIncomingReferralDidEnd", dict(obj=self)) else: self._set_content(100, "Trying") self._send_notify() finally: with nogil: pjsip_dlg_dec_lock(self._dlg) def reject(self, int code): cdef PJSIPUA ua = self._get_ua(1) with nogil: pjsip_dlg_inc_lock(self._dlg) try: if self.state != "incoming": raise SIPCoreInvalidStateError('Can only reject an incoming REFER in the "incoming" state, '+ 'object is currently in the "%s" state' % self.state) if not (300 <= code < 700): raise ValueError("Invalid negative SIP response code: %d" % code) self._send_initial_response(code) pjsip_evsub_set_mod_data(self._obj, ua._event_module.id, NULL) with nogil: pjsip_evsub_terminate(self._obj, 0) self._obj = NULL self._set_state("terminated") _add_event("SIPIncomingReferralDidEnd", dict(obj=self)) finally: with nogil: pjsip_dlg_dec_lock(self._dlg) def send_notify(self, int code, str status=None): cdef PJSIPUA ua = self._get_ua(1) cdef str content with nogil: pjsip_dlg_inc_lock(self._dlg) try: if self.state != "active": raise SIPCoreInvalidStateError('Can only send NOTIFY for a REFER session in the "active" state, ' 'object is currently in the "%s" state' % self.state) self._set_content(code, status) self._send_notify() finally: with nogil: pjsip_dlg_dec_lock(self._dlg) def end(self, int code, str status=None): cdef PJSIPUA ua = self._get_ua(0) with nogil: pjsip_dlg_inc_lock(self._dlg) try: if self.state == "terminated": return if self.state not in ("pending", "active"): raise SIPCoreInvalidStateError('Can only end an incoming REFER session in the "pending" or '+ '"active" state, object is currently in the "%s" state' % self.state) self._set_content(code, status) self._terminate(ua, 1) finally: with nogil: pjsip_dlg_dec_lock(self._dlg) cdef PJSIPUA _get_ua(self, int raise_exception): cdef PJSIPUA ua try: ua = _get_ua() except SIPCoreError: self._obj = NULL self._set_state("terminated") if raise_exception: raise else: return None else: return ua cdef int _set_content(self, int code, str reason) except -1: cdef str content if reason is None: try: reason = sip_status_messages[code] except IndexError: reason = "Unknown" content = "SIP/2.0 %d %s\r\n" % (code, reason) self._content = PJSTR(content) cdef int _set_state(self, str state) except -1: cdef str prev_state prev_state = self.state self.state = state if prev_state != state and prev_state is not None: _add_event("SIPIncomingReferralChangedState", dict(obj=self, prev_state=prev_state, state=state)) cdef int _send_initial_response(self, int code) except -1: cdef int status with nogil: status = pjsip_dlg_modify_response(self._dlg, self._initial_response, code, NULL) if status != 0: raise PJSIPError("Could not modify response", status) # pjsip_dlg_modify_response() increases ref count unncessarily with nogil: pjsip_tx_data_dec_ref(self._initial_response) if not self._create_subscription: _add_headers_to_tdata(self._initial_response, [Header('Refer-Sub', 'false')]) with nogil: status = pjsip_dlg_send_response(self._dlg, self._initial_tsx, self._initial_response) if status != 0: raise PJSIPError("Could not send response", status) self._initial_response = NULL self._initial_tsx = NULL cdef int _send_notify(self) except -1: cdef pjsip_evsub_state state cdef pj_str_t *reason_p cdef pjsip_tx_data *tdata cdef int status cdef dict _sipfrag_version = dict(version="2.0") cdef PJSTR _content_type = PJSTR(b"message") cdef PJSTR _content_subtype = PJSTR(b"sipfrag") cdef PJSTR reason = PJSTR(b"noresource") reason_p = NULL if self.state == "pending": state = PJSIP_EVSUB_STATE_PENDING elif self.state == "active": state = PJSIP_EVSUB_STATE_ACTIVE else: state = PJSIP_EVSUB_STATE_TERMINATED reason_p = &reason.pj_str with nogil: status = pjsip_evsub_notify(self._obj, state, NULL, reason_p, &tdata) if status != 0: raise PJSIPError("Could not create NOTIFY request", status) if self.state in ("active", "terminated"): tdata.msg.body = pjsip_msg_body_create(tdata.pool, &_content_type.pj_str, &_content_subtype.pj_str, &self._content.pj_str) _dict_to_pjsip_param(_sipfrag_version, &tdata.msg.body.content_type.param, tdata.pool) with nogil: status = pjsip_evsub_send_request(self._obj, tdata) if status != 0: raise PJSIPError("Could not send NOTIFY request", status) event_dict = dict(obj=self) _pjsip_msg_to_dict(tdata.msg, event_dict) _add_event("SIPIncomingReferralSentNotify", event_dict) return 0 cdef int _terminate(self, PJSIPUA ua, int do_cleanup) except -1: cdef int status self._set_state("terminated") self._send_notify() if do_cleanup: pjsip_evsub_set_mod_data(self._obj, ua._event_module.id, NULL) self._obj = NULL _add_event("SIPIncomingReferralDidEnd", dict(obj=self)) cdef int _cb_rx_refresh(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1: # PJSIP holds the dialog lock when this callback is entered cdef int status cdef pjsip_expires_hdr *expires_header cdef int expires cdef dict event_dict event_dict = dict(obj=self) _pjsip_msg_to_dict(rdata.msg_info.msg, event_dict) expires_header = pjsip_msg_find_hdr(rdata.msg_info.msg, PJSIP_H_EXPIRES, NULL) if expires_header == NULL: self._expires_time.sec = 600 self._expires_time.msec = 0 else: if expires_header.ivalue == 0: _add_event("SIPIncomingReferralGotUnsubscribe", event_dict) # cleanup will be done by _cb_tsx self._terminate(ua, 0) return 200 else: expires = min(expires_header.ivalue, 600) self._expires_time.sec = expires self._expires_time.msec = 0 _add_event("SIPIncomingReferralGotRefreshingSubscribe", event_dict) # Last NOTIFY will be resent self._send_notify() if self.state == "active": return 200 else: return 202 cdef int _cb_server_timeout(self, PJSIPUA ua) except -1: # PJSIP holds the dialog lock when this callback is entered self._terminate(ua, 1) cdef int _cb_tsx(self, PJSIPUA ua, pjsip_event *event) except -1: # PJSIP holds the dialog lock when this callback is entered cdef pjsip_rx_data *rdata cdef dict event_dict cdef int status_code if (event != NULL and event.type == PJSIP_EVENT_TSX_STATE and event.body.tsx_state.tsx.role == PJSIP_ROLE_UAC and _pj_str_to_str(event.body.tsx_state.tsx.method.name) == "NOTIFY" and event.body.tsx_state.tsx.state == PJSIP_TSX_STATE_COMPLETED): event_dict = dict(obj=self) rdata = event.body.tsx_state.src.rdata if rdata != NULL: if self.peer_address is None: self.peer_address = EndpointAddress(rdata.pkt_info.src_name, rdata.pkt_info.src_port) else: self.peer_address.ip = rdata.pkt_info.src_name self.peer_address.port = rdata.pkt_info.src_port status_code = event.body.tsx_state.tsx.status_code if event.body.tsx_state.type==PJSIP_EVENT_RX_MSG and status_code/100==2: _pjsip_msg_to_dict(rdata.msg_info.msg, event_dict) try: self.remote_contact_header = event_dict['headers']['Contact'][0] except LookupError: pass _add_event("SIPIncomingReferralNotifyDidSucceed", event_dict) else: if event.body.tsx_state.type == PJSIP_EVENT_RX_MSG: _pjsip_msg_to_dict(rdata.msg_info.msg, event_dict) else: event_dict["code"] = status_code event_dict["reason"] = _pj_str_to_str(event.body.tsx_state.tsx.status_text) _add_event("SIPIncomingReferralNotifyDidFail", event_dict) if status_code in (408, 481) or status_code/100==7: # PJSIP will terminate the subscription and the dialog will be destroyed self._terminate(ua, 1) elif (event != NULL and event.type == PJSIP_EVENT_TSX_STATE and event.body.tsx_state.tsx.role == PJSIP_ROLE_UAC and _pj_str_to_str(event.body.tsx_state.tsx.method.name) == "NOTIFY" and event.body.tsx_state.tsx.state == PJSIP_TSX_STATE_TERMINATED): event_dict = dict(obj=self) status_code = event.body.tsx_state.tsx.status_code if status_code == 408: # Local timeout, PJSIP will terminate the subscription and the dialog will be destroyed event_dict["code"] = status_code event_dict["reason"] = _pj_str_to_str(event.body.tsx_state.tsx.status_text) _add_event("SIPIncomingReferralNotifyDidFail", event_dict) self._terminate(ua, 1) elif (event != NULL and event.type == PJSIP_EVENT_TSX_STATE and event.body.tsx_state.tsx.role == PJSIP_ROLE_UAS and _pj_str_to_str(event.body.tsx_state.tsx.method.name) == "REFER" and event.body.tsx_state.tsx.state == PJSIP_TSX_STATE_COMPLETED and event.body.tsx_state.type == PJSIP_EVENT_TX_MSG): event_dict = dict(obj=self) _pjsip_msg_to_dict(event.body.tsx_state.src.tdata.msg, event_dict) _add_event("SIPIncomingReferralAnsweredRefer", event_dict) if self.state == "terminated" and self._obj != NULL: pjsip_evsub_set_mod_data(self._obj, ua._event_module.id, NULL) self._obj = NULL cdef void _Referral_cb_state(pjsip_evsub *sub, pjsip_event *event) with gil: cdef void *referral_void cdef Referral referral cdef object state cdef int code = 0 cdef dict event_dict = dict() cdef str reason = None cdef pjsip_rx_data *rdata = NULL cdef PJSIPUA ua try: ua = _get_ua() except: return try: referral_void = pjsip_evsub_get_mod_data(sub, ua._event_module.id) if referral_void == NULL: return referral = referral_void state = pjsip_evsub_get_state_name(sub) if (event != NULL and event.type == PJSIP_EVENT_TSX_STATE and (event.body.tsx_state.tsx.state == PJSIP_TSX_STATE_COMPLETED or event.body.tsx_state.tsx.state == PJSIP_TSX_STATE_TERMINATED)): if state == "TERMINATED": if event.body.tsx_state.tsx.role == PJSIP_ROLE_UAC: code = event.body.tsx_state.tsx.status_code reason = _pj_str_to_str(event.body.tsx_state.tsx.status_text) else: reason = "Referral has expired" if event.body.tsx_state.type == PJSIP_EVENT_RX_MSG and _pj_str_to_str(event.body.tsx_state.tsx.method.name) == "NOTIFY": # Extract code and reason from the sipfrag payload rdata = event.body.tsx_state.src.rdata if rdata != NULL: _pjsip_msg_to_dict(rdata.msg_info.msg, event_dict) if event_dict.get('body', None) is not None: match = sipfrag_re.match(event_dict['body']) if match: code = int(match.group('code')) reason = match.group('reason') referral._cb_state(ua, state, code, reason) except: ua._handle_exception(1) cdef void _Referral_cb_tsx(pjsip_evsub *sub, pjsip_transaction *tsx, pjsip_event *event) with gil: cdef void *referral_void cdef Referral referral cdef pjsip_rx_data *rdata cdef PJSIPUA ua try: ua = _get_ua() except: return try: referral_void = pjsip_evsub_get_mod_data(sub, ua._event_module.id) if referral_void == NULL: return referral = referral_void if (event != NULL and event.type == PJSIP_EVENT_TSX_STATE and event.body.tsx_state.type == PJSIP_EVENT_RX_MSG and event.body.tsx_state.tsx.role == PJSIP_ROLE_UAC and event.body.tsx_state.tsx.state == PJSIP_TSX_STATE_COMPLETED and _pj_str_to_str(event.body.tsx_state.tsx.method.name) in ("REFER", "SUBSCRIBE") and event.body.tsx_state.tsx.status_code/100 == 2): rdata = event.body.tsx_state.src.rdata if rdata != NULL: if referral.peer_address is None: referral.peer_address = EndpointAddress(rdata.pkt_info.src_name, rdata.pkt_info.src_port) else: referral.peer_address.ip = rdata.pkt_info.src_name referral.peer_address.port = rdata.pkt_info.src_port referral._cb_got_response(ua, rdata, _pj_str_to_bytes(event.body.tsx_state.tsx.method.name)) except: ua._handle_exception(1) cdef void _Referral_cb_notify(pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body) with gil: cdef void *referral_void cdef Referral referral cdef PJSIPUA ua try: ua = _get_ua() except: return try: referral_void = pjsip_evsub_get_mod_data(sub, ua._event_module.id) if referral_void == NULL: return referral = referral_void if rdata != NULL: if referral.peer_address is None: referral.peer_address = EndpointAddress(rdata.pkt_info.src_name, rdata.pkt_info.src_port) else: referral.peer_address.ip = rdata.pkt_info.src_name referral.peer_address.port = rdata.pkt_info.src_port referral._cb_notify(ua, rdata) except: ua._handle_exception(1) cdef void _Referral_cb_refresh(pjsip_evsub *sub) with gil: # We want to handle the refresh timer oursevles, ignore the PJSIP provided timer pass cdef void _Referral_cb_timer(pj_timer_heap_t *timer_heap, pj_timer_entry *entry) with gil: cdef Referral referral cdef PJSIPUA ua try: ua = _get_ua() except: return try: if entry.user_data != NULL: referral = entry.user_data if entry.id == 1: referral._refresh_timer_active = 0 referral._cb_refresh_timer(ua) else: referral._timeout_timer_active = 0 referral._cb_timeout_timer(ua) except: ua._handle_exception(1) cdef void _IncomingReferral_cb_rx_refresh(pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body) with gil: cdef void *referral_void cdef IncomingReferral referral cdef PJSIPUA ua try: ua = _get_ua() except: return try: referral_void = pjsip_evsub_get_mod_data(sub, ua._event_module.id) if referral_void == NULL: p_st_code[0] = 481 return referral = referral_void if rdata != NULL: if referral.peer_address is None: referral.peer_address = EndpointAddress(rdata.pkt_info.src_name, rdata.pkt_info.src_port) else: referral.peer_address.ip = rdata.pkt_info.src_name referral.peer_address.port = rdata.pkt_info.src_port p_st_code[0] = referral._cb_rx_refresh(ua, rdata) except: ua._handle_exception(1) cdef void _IncomingReferral_cb_server_timeout(pjsip_evsub *sub) with gil: cdef void *referral_void cdef IncomingReferral referral cdef PJSIPUA ua try: ua = _get_ua() except: return try: referral_void = pjsip_evsub_get_mod_data(sub, ua._event_module.id) if referral_void == NULL: return referral = referral_void referral._cb_server_timeout(ua) except: ua._handle_exception(1) cdef void _IncomingReferral_cb_tsx(pjsip_evsub *sub, pjsip_transaction *tsx, pjsip_event *event) with gil: cdef void *referral_void cdef IncomingReferral referral cdef PJSIPUA ua try: ua = _get_ua() except: return try: referral_void = pjsip_evsub_get_mod_data(sub, ua._event_module.id) if referral_void == NULL: return referral = referral_void referral._cb_tsx(ua, event) except: ua._handle_exception(1) # Globals # cdef pjsip_evsub_user _refer_cb _refer_cb.on_evsub_state = _Referral_cb_state _refer_cb.on_tsx_state = _Referral_cb_tsx _refer_cb.on_rx_notify = _Referral_cb_notify _refer_cb.on_client_refresh = _Referral_cb_refresh cdef pjsip_evsub_user _incoming_refer_subs_cb _incoming_refer_subs_cb.on_rx_refresh = _IncomingReferral_cb_rx_refresh _incoming_refer_subs_cb.on_server_timeout = _IncomingReferral_cb_server_timeout _incoming_refer_subs_cb.on_tsx_state = _IncomingReferral_cb_tsx sipfrag_re = re.compile(r'^SIP/2\.0\s+(?P\d{3})\s+(?P[ a-zA-Z0-9_-]+)') cdef PJSTR _refer_method = PJSTR(b"REFER") cdef PJSTR _refer_event = PJSTR(b"refer") cdef PJSTR _refer_to_hdr_name = PJSTR(b"Refer-To") cdef PJSTR _refer_sub_hdr_name = PJSTR(b"Refer-Sub") cdef PJSTR _subscription_state_hdr_name = PJSTR(b"Subscription-State") diff --git a/sipsimple/core/_core.request.pxi b/sipsimple/core/_core.request.pxi index 80aa5c9b..67246c7a 100644 --- a/sipsimple/core/_core.request.pxi +++ b/sipsimple/core/_core.request.pxi @@ -1,507 +1,506 @@ from datetime import datetime, timedelta cdef class EndpointAddress: def __init__(self, ip, port): self.ip = ip self.port = port def __repr__(self): return "%s(%r, %r)" % (self.__class__.__name__, self.ip, self.port) def __str__(self): return "%s:%d" % (self.ip, self.port) cdef class Request: expire_warning_time = 30 # properties property method: def __get__(self): return self._method.str property call_id: def __get__(self): return self._call_id.str property content_type: def __get__(self): if self._content_type is None: return None else: return "/".join([self._content_type.str, self._content_subtype.str]) property body: def __get__(self): if self._body is None: return None else: return self._body.str property expires_in: def __get__(self): cdef object dt self._get_ua() if self.state != "EXPIRING" or self._expire_time is None: return 0 else: dt = self._expire_time - datetime.now() return max(0, dt.seconds) # public methods def __cinit__(self, *args, **kwargs): self.state = "INIT" self.peer_address = None pj_timer_entry_init(&self._timer, 0, self, _Request_cb_timer) self._timer_active = 0 def __init__(self, method, SIPURI request_uri not None, FromHeader from_header not None, ToHeader to_header not None, RouteHeader route_header not None, Credentials credentials=None, ContactHeader contact_header=None, call_id=None, cseq=None, object extra_headers=None, content_type=None, body=None): cdef pjsip_method method_pj cdef PJSTR from_header_str cdef PJSTR to_header_str cdef PJSTR request_uri_str cdef PJSTR contact_header_str cdef pj_str_t *contact_header_pj = NULL cdef pj_str_t *call_id_pj = NULL cdef object content_type_spl cdef pjsip_hdr *hdr cdef pjsip_contact_hdr *contact_hdr cdef pjsip_cid_hdr *cid_hdr cdef pjsip_cseq_hdr *cseq_hdr cdef int status cdef dict event_dict cdef PJSIPUA ua = _get_ua() if self._tsx != NULL or self.state != "INIT": raise SIPCoreError("Request.__init__() was already called") if cseq is not None and cseq < 0: raise ValueError("cseq argument cannot be negative") if extra_headers is not None: header_names = set([header.name for header in extra_headers]) if "Route" in header_names: raise ValueError("Route should be specified with route_header argument, not extra_headers") if "Content-Type" in header_names: raise ValueError("Content-Type should be specified with content_type argument, not extra_headers") else: header_names = () if content_type is not None and body is None: raise ValueError("Cannot specify a content_type without a body") if content_type is None and body is not None: raise ValueError("Cannot specify a body without a content_type") self._method = PJSTR(method.encode()) pjsip_method_init_np(&method_pj, &self._method.pj_str) if credentials is not None: self.credentials = FrozenCredentials.new(credentials) from_header_str = PJSTR(from_header.body.encode()) self.to_header = FrozenToHeader.new(to_header) to_header_str = PJSTR(to_header.body.encode()) struri = str(request_uri) self.request_uri = FrozenSIPURI.new(request_uri) request_uri_str = PJSTR(struri.encode()) self.route_header = FrozenRouteHeader.new(route_header) #self.route_header = FrozenRouteHeader(FrozenSIPURI(host=route_header.uri.host)) self.route_header.uri.parameters.dict["lr"] = None # always send lr parameter in Route header self.route_header.uri.parameters.dict["hide"] = None # always hide Route header if contact_header is not None: self.contact_header = FrozenContactHeader.new(contact_header) contact_parameters = contact_header.parameters.copy() contact_parameters.pop("q", None) contact_parameters.pop("expires", None) contact_header.parameters = {} contact_header_str = PJSTR(contact_header.body.encode()) contact_header_pj = &contact_header_str.pj_str if call_id is not None: self._call_id = PJSTR(call_id.encode()) call_id_pj = &self._call_id.pj_str if cseq is None: self.cseq = -1 else: self.cseq = cseq if extra_headers is None: self.extra_headers = frozenlist() else: self.extra_headers = frozenlist([header.frozen_type.new(header) for header in extra_headers]) if body is not None: content_type_spl = content_type.split("/", 1) self._content_type = PJSTR(content_type_spl[0].encode()) self._content_subtype = PJSTR(content_type_spl[1].encode()) self._body = PJSTR(body) status = pjsip_endpt_create_request(ua._pjsip_endpoint._obj, &method_pj, &request_uri_str.pj_str, &from_header_str.pj_str, &to_header_str.pj_str, contact_header_pj, call_id_pj, self.cseq, NULL, &self._tdata) if status != 0: raise PJSIPError("Could not create request", status) if body is not None: self._tdata.msg.body = pjsip_msg_body_create(self._tdata.pool, &self._content_type.pj_str, &self._content_subtype.pj_str, &self._body.pj_str) status = _BaseRouteHeader_to_pjsip_route_hdr(self.route_header, &self._route_header, self._tdata.pool) pjsip_msg_add_hdr(self._tdata.msg, &self._route_header) hdr = ( &self._tdata.msg.hdr).next while hdr != &self._tdata.msg.hdr: hdr_name = _pj_str_to_str(hdr.name) if hdr_name in header_names: raise ValueError("Cannot override %s header value in extra_headers" % _pj_str_to_bytes(hdr.name)) if hdr.type == PJSIP_H_CONTACT: contact_hdr = hdr _dict_to_pjsip_param(contact_parameters, &contact_hdr.other_param, self._tdata.pool) elif hdr.type == PJSIP_H_CALL_ID: cid_hdr = hdr self._call_id = PJSTR(_pj_str_to_bytes(cid_hdr.id)) elif hdr.type == PJSIP_H_CSEQ: cseq_hdr = hdr self.cseq = cseq_hdr.cseq elif hdr.type == PJSIP_H_FROM: self.from_header = FrozenFromHeader_create( hdr) else: pass hdr = ( hdr).next _add_headers_to_tdata(self._tdata, self.extra_headers) event_dict = dict(obj=self) _pjsip_msg_to_dict(self._tdata.msg, event_dict) print('Request dict %s' % event_dict) if self.credentials is not None: status = pjsip_auth_clt_init(&self._auth, ua._pjsip_endpoint._obj, self._tdata.pool, 0) if status != 0: raise PJSIPError("Could not init authentication credentials", status) status = pjsip_auth_clt_set_credentials(&self._auth, 1, self.credentials.get_cred_info()) if status != 0: raise PJSIPError("Could not set authentication credentials", status) self._need_auth = 1 else: self._need_auth = 0 status = pjsip_tsx_create_uac(&ua._module, self._tdata, &self._tsx) if status != 0: raise PJSIPError("Could not create transaction for request", status) self._tsx.mod_data[ua._module.id] = self def __dealloc__(self): cdef PJSIPUA ua = self._get_ua() if self._tsx != NULL: self._tsx.mod_data[ua._module.id] = NULL if self._tsx.state < PJSIP_TSX_STATE_COMPLETED: pjsip_tsx_terminate(self._tsx, 500) self._tsx = NULL if self._tdata != NULL: pjsip_tx_data_dec_ref(self._tdata) self._tdata = NULL if self._timer_active: pjsip_endpt_cancel_timer(ua._pjsip_endpoint._obj, &self._timer) self._timer_active = 0 def send(self, timeout=None): cdef pj_time_val timeout_pj cdef int status cdef PJSIPUA ua = self._get_ua() if self.state != "INIT": raise SIPCoreError('This method may only be called in the "INIT" state, current state is "%s"' % self.state) if timeout is not None: if timeout <= 0: raise ValueError("Timeout value cannot be negative") timeout_pj.sec = int(timeout) timeout_pj.msec = (timeout * 1000) % 1000 self._timeout = timeout status = pjsip_tsx_send_msg(self._tsx, self._tdata) if status != 0: raise PJSIPError("Could not send request", status) pjsip_tx_data_add_ref(self._tdata) if timeout: status = pjsip_endpt_schedule_timer(ua._pjsip_endpoint._obj, &self._timer, &timeout_pj) if status == 0: self._timer_active = 1 self.state = "IN_PROGRESS" def end(self): cdef PJSIPUA ua = self._get_ua() if self.state == "IN_PROGRESS": pjsip_tsx_terminate(self._tsx, 408) elif self.state == "EXPIRING": pjsip_endpt_cancel_timer(ua._pjsip_endpoint._obj, &self._timer) self._timer_active = 0 self.state = "TERMINATED" _add_event("SIPRequestDidEnd", dict(obj=self)) # private methods cdef PJSIPUA _get_ua(self): cdef PJSIPUA ua try: ua = _get_ua() except SIPCoreError: self._tsx = NULL self._tdata = NULL self._timer_active = 0 self.state = "TERMINATED" return None else: return ua cdef int _cb_tsx_state(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1: cdef pjsip_tx_data *tdata_auth cdef pjsip_transaction *tsx_auth cdef pjsip_cseq_hdr *cseq cdef dict event_dict cdef int expires = -1 cdef SIPURI contact_uri cdef dict contact_params cdef pj_time_val timeout_pj cdef int status if rdata != NULL: self.to_header = FrozenToHeader_create(rdata.msg_info.to_hdr) if self.peer_address is None: self.peer_address = EndpointAddress(rdata.pkt_info.src_name, rdata.pkt_info.src_port) else: self.peer_address.ip = rdata.pkt_info.src_name self.peer_address.port = rdata.pkt_info.src_port if self._tsx.state == PJSIP_TSX_STATE_PROCEEDING: if rdata == NULL: return 0 event_dict = dict(obj=self) _pjsip_msg_to_dict(rdata.msg_info.msg, event_dict) _add_event("SIPRequestGotProvisionalResponse", event_dict) elif self._tsx.state == PJSIP_TSX_STATE_COMPLETED: if self._timer_active: pjsip_endpt_cancel_timer(ua._pjsip_endpoint._obj, &self._timer) self._timer_active = 0 if self._need_auth and self._tsx.status_code in [401, 407]: self._need_auth = 0 status = pjsip_auth_clt_reinit_req(&self._auth, rdata, self._tdata, &tdata_auth) if status != 0: _add_event("SIPRequestDidFail", dict(obj=self, code=0, reason="Could not add auth data to request %s" % _pj_status_to_str(status))) self.state = "TERMINATED" _add_event("SIPRequestDidEnd", dict(obj=self)) return 0 cseq = pjsip_msg_find_hdr(tdata_auth.msg, PJSIP_H_CSEQ, NULL) if cseq != NULL: cseq.cseq += 1 self.cseq = cseq.cseq status = pjsip_tsx_create_uac(&ua._module, tdata_auth, &tsx_auth) if status != 0: pjsip_tx_data_dec_ref(tdata_auth) _add_event("SIPRequestDidFail", dict(obj=self, code=0, reason="Could not create transaction for request with auth %s" % _pj_status_to_str(status))) self.state = "TERMINATED" _add_event("SIPRequestDidEnd", dict(obj=self)) return 0 self._tsx.mod_data[ua._module.id] = NULL self._tsx = tsx_auth self._tsx.mod_data[ua._module.id] = self status = pjsip_tsx_send_msg(self._tsx, tdata_auth) if status != 0: pjsip_tx_data_dec_ref(tdata_auth) _add_event("SIPRequestDidFail", dict(obj=self, code=0, reason="Could not send request with auth %s" % _pj_status_to_str(status))) self.state = "TERMINATED" _add_event("SIPRequestDidEnd", dict(obj=self)) return 0 elif self._timeout is not None: timeout_pj.sec = int(self._timeout) timeout_pj.msec = (self._timeout * 1000) % 1000 status = pjsip_endpt_schedule_timer(ua._pjsip_endpoint._obj, &self._timer, &timeout_pj) if status == 0: self._timer_active = 1 else: event_dict = dict(obj=self) if rdata != NULL: # This shouldn't happen, but safety fist! _pjsip_msg_to_dict(rdata.msg_info.msg, event_dict) if self._tsx.status_code / 100 == 2: if rdata != NULL: if "Expires" in event_dict["headers"]: expires = event_dict["headers"]["Expires"] elif self.contact_header is not None: for contact_header in event_dict["headers"].get("Contact", []): if contact_header.uri == self.contact_header.uri and contact_header.expires is not None: expires = contact_header.expires if expires == -1: expires = 0 for header in self.extra_headers: if header.name == "Expires": try: expires = int(header.body) except ValueError: pass break event_dict["expires"] = expires self._expire_time = datetime.now() + timedelta(seconds=expires) _add_event("SIPRequestDidSucceed", event_dict) else: expires = 0 _add_event("SIPRequestDidFail", event_dict) if expires == 0: self.state = "TERMINATED" _add_event("SIPRequestDidEnd", dict(obj=self)) else: timeout_pj.sec = max(1, expires - self.expire_warning_time, expires/2) timeout_pj.msec = 0 status = pjsip_endpt_schedule_timer(ua._pjsip_endpoint._obj, &self._timer, &timeout_pj) if status == 0: self._timer_active = 1 self.state = "EXPIRING" self._expire_rest = max(1, expires - timeout_pj.sec) else: self.state = "TERMINATED" _add_event("SIPRequestDidEnd", dict(obj=self)) elif self._tsx.state == PJSIP_TSX_STATE_TERMINATED: if self.state == "IN_PROGRESS": if self._timer_active: pjsip_endpt_cancel_timer(ua._pjsip_endpoint._obj, &self._timer) self._timer_active = 0 _add_event("SIPRequestDidFail", dict(obj=self, code=self._tsx.status_code, reason=_pj_str_to_bytes(self._tsx.status_text))) self.state = "TERMINATED" _add_event("SIPRequestDidEnd", dict(obj=self)) self._tsx.mod_data[ua._module.id] = NULL self._tsx = NULL else: pass cdef int _cb_timer(self, PJSIPUA ua) except -1: cdef pj_time_val expires cdef int status if self.state == "IN_PROGRESS": pjsip_tsx_terminate(self._tsx, 408) elif self.state == "EXPIRING": if self._expire_rest > 0: _add_event("SIPRequestWillExpire", dict(obj=self, expires=self._expire_rest)) expires.sec = self._expire_rest expires.msec = 0 self._expire_rest = 0 status = pjsip_endpt_schedule_timer(ua._pjsip_endpoint._obj, &self._timer, &expires) if status == 0: self._timer_active = 1 else: self.state = "TERMINATED" _add_event("SIPRequestDidEnd", dict(obj=self)) else: self.state = "TERMINATED" _add_event("SIPRequestDidEnd", dict(obj=self)) return 0 cdef class IncomingRequest: def __cinit__(self, *args, **kwargs): self.peer_address = None def __dealloc__(self): cdef PJSIPUA ua try: ua = _get_ua() except SIPCoreError: return if self._tsx != NULL: pjsip_tsx_terminate(self._tsx, 500) self._tsx = NULL if self._tdata != NULL: pjsip_tx_data_dec_ref(self._tdata) self._tdata = NULL def answer(self, int code, str reason=None, object extra_headers=None): cdef bytes reason_bytes cdef dict event_dict cdef int status cdef PJSIPUA ua = _get_ua() if self.state != "incoming": raise SIPCoreInvalidStateError('Can only answer an incoming request in the "incoming" state, ' 'object is currently in the "%s" state' % self.state) if code < 200 or code >= 700: raise ValueError("Invalid SIP final response code: %d" % code) self._tdata.msg.line.status.code = code if reason is None: self._tdata.msg.line.status.reason = pjsip_get_status_text(code)[0] else: - reason_bytes = reason.encode() - pj_strdup2_with_null(self._tdata.pool, &self._tdata.msg.line.status.reason, reason_bytes) + pj_strdup2_with_null(self._tdata.pool, &self._tdata.msg.line.status.reason, reason.encode()) if extra_headers is not None: _add_headers_to_tdata(self._tdata, extra_headers) event_dict = dict(obj=self) _pjsip_msg_to_dict(self._tdata.msg, event_dict) status = pjsip_tsx_send_msg(self._tsx, self._tdata) if status != 0: raise PJSIPError("Could not send response", status) self.state = "answered" self._tdata = NULL self._tsx = NULL _add_event("SIPIncomingRequestSentResponse", event_dict) cdef int init(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1: cdef dict event_dict cdef int status status = pjsip_endpt_create_response(ua._pjsip_endpoint._obj, rdata, 500, NULL, &self._tdata) if status != 0: raise PJSIPError("Could not create response", status) status = pjsip_tsx_create_uas(&ua._module, rdata, &self._tsx) if status != 0: pjsip_tx_data_dec_ref(self._tdata) self._tdata = NULL raise PJSIPError("Could not create transaction for incoming request", status) pjsip_tsx_recv_msg(self._tsx, rdata) self.state = "incoming" self.peer_address = EndpointAddress(rdata.pkt_info.src_name, rdata.pkt_info.src_port) event_dict = dict(obj=self) _pjsip_msg_to_dict(rdata.msg_info.msg, event_dict) _add_event("SIPIncomingRequestGotRequest", event_dict) # callback functions cdef void _Request_cb_tsx_state(pjsip_transaction *tsx, pjsip_event *event) with gil: cdef PJSIPUA ua cdef void *req_ptr cdef Request req cdef pjsip_rx_data *rdata = NULL try: ua = _get_ua() except: return try: req_ptr = tsx.mod_data[ua._module.id] if req_ptr != NULL: req = req_ptr if event.type == PJSIP_EVENT_RX_MSG: rdata = event.body.rx_msg.rdata elif event.type == PJSIP_EVENT_TSX_STATE and event.body.tsx_state.type == PJSIP_EVENT_RX_MSG: rdata = event.body.tsx_state.src.rdata req._cb_tsx_state(ua, rdata) except: ua._handle_exception(1) cdef void _Request_cb_timer(pj_timer_heap_t *timer_heap, pj_timer_entry *entry) with gil: cdef PJSIPUA ua cdef Request req try: ua = _get_ua() except: return try: if entry.user_data != NULL: req = entry.user_data req._timer_active = 0 req._cb_timer(ua) except: ua._handle_exception(1) diff --git a/sipsimple/core/_core.sdp.pxi b/sipsimple/core/_core.sdp.pxi index f907348b..ec090e91 100644 --- a/sipsimple/core/_core.sdp.pxi +++ b/sipsimple/core/_core.sdp.pxi @@ -1,1227 +1,1233 @@ import re from application.python.descriptor import WriteOnceAttribute cdef object BaseSDPSession_richcmp(object self, object other, int op) with gil: cdef int eq = 1 if op not in [2,3]: return NotImplemented if not isinstance(other, BaseSDPSession): return NotImplemented for attr in ("id", "version", "user", "net_type", "address_type", "address", "address", "name", "connection", "start_time", "stop_time", "attributes", "bandwidth_info", "media"): if getattr(self, attr) != getattr(other, attr): eq = 0 break if op == 2: return bool(eq) else: return not eq cdef pjmedia_sdp_session* _parse_sdp_session(str sdp): cdef int status cdef pjmedia_sdp_session *sdp_session status = pjmedia_sdp_parse(_get_ua()._pjsip_endpoint._pool, _str_as_str(sdp), _str_as_size(sdp), &sdp_session) if status != 0: raise PJSIPError("failed to parse SDP", status) return sdp_session cdef class BaseSDPSession: def __init__(self, *args, **kwargs): raise TypeError("BaseSDPSession cannot be instantiated directly") def __repr__(self): return "%s(%r, %r, %r, %r, %r, %r, %r, %r, %r, %r, %r, %r, %r)" % (self.__class__.__name__, self.address, self.id, self.version, self.user, self.net_type, self.address_type, self.name, self.connection, self.start_time, self.stop_time, self.attributes, self.bandwidth_info, self.media) def __str__(self): cdef char cbuf[2048] cdef int buf_len buf_len = pjmedia_sdp_print(self.get_sdp_session(), cbuf, sizeof(cbuf)) if buf_len > -1: return _pj_buf_len_to_str(cbuf, buf_len) return '' def __richcmp__(self, other, op): return BaseSDPSession_richcmp(self, other, op) cdef pjmedia_sdp_session* get_sdp_session(self): self._sdp_session.media_count = len(self.media) for index, m in enumerate(self.media): if m is not None: self._sdp_session.media[index] = (m).get_sdp_media() else: self._sdp_session.media[index] = NULL self._sdp_session.attr_count = len(self.attributes) for index, attr in enumerate(self.attributes): self._sdp_session.attr[index] = (attr).get_sdp_attribute() self._sdp_session.bandw_count = len(self.bandwidth_info) for index, info in enumerate(self.bandwidth_info): self._sdp_session.bandw[index] = (info).get_sdp_bandwidth_info() return &self._sdp_session property has_ice_attributes: def __get__(self): return set([attr.name for attr in self.attributes]).issuperset(['ice-pwd', 'ice-ufrag']) cdef class SDPSession(BaseSDPSession): def __init__(self, str address not None, object id=None, object version=None, str user not None="-", str net_type not None="IN", str address_type not None="IP4", str name not None=" ", SDPConnection connection=None, unsigned long start_time=0, unsigned long stop_time=0, list attributes=None, list bandwidth_info=None, list media=None): cdef unsigned int version_id = 2208988800UL cdef pj_time_val tv pj_gettimeofday(&tv) version_id += tv.sec self.address = address self.id = id if id is not None else version_id self.version = version if version is not None else version_id self.user = user self.net_type = net_type self.address_type = address_type self.name = name self.connection = connection self.start_time = start_time self.stop_time = stop_time self.attributes = attributes if attributes is not None else [] self.bandwidth_info = bandwidth_info if bandwidth_info is not None else [] self.media = media if media is not None else [] @classmethod def new(cls, BaseSDPSession sdp_session): connection = SDPConnection.new(sdp_session.connection) if (sdp_session.connection is not None) else None attributes = [SDPAttribute.new(attr) for attr in sdp_session.attributes] bandwidth_info = [SDPBandwidthInfo.new(info) for info in sdp_session.bandwidth_info] media = [SDPMediaStream.new(m) if m is not None else None for m in sdp_session.media] return cls(sdp_session.address, sdp_session.id, sdp_session.version, sdp_session.user, sdp_session.net_type, sdp_session.address_type, sdp_session.name, connection, sdp_session.start_time, sdp_session.stop_time, attributes, bandwidth_info, media) @classmethod def parse(cls, str sdp): cdef pjmedia_sdp_session *sdp_session sdp_session = _parse_sdp_session(sdp) return SDPSession_create(sdp_session) property address: def __get__(self): return self._address def __set__(self, str address not None): _str_to_pj_str(address.encode(), &self._sdp_session.origin.addr) self._address = address property id: def __get__(self): return self._sdp_session.origin.id def __set__(self, unsigned int id): self._sdp_session.origin.id = id property version: def __get__(self): return self._sdp_session.origin.version def __set__(self, unsigned int version): self._sdp_session.origin.version = version property user: def __get__(self): return self._user def __set__(self, str user not None): _str_to_pj_str(user.encode(), &self._sdp_session.origin.user) self._user = user property net_type: def __get__(self): return self._net_type def __set__(self, str net_type not None): _str_to_pj_str(net_type.encode(), &self._sdp_session.origin.net_type) self._net_type = net_type property address_type: def __get__(self): return self._address_type def __set__(self, str address_type not None): _str_to_pj_str(address_type.encode(), &self._sdp_session.origin.addr_type) self._address_type = address_type property name: def __get__(self): return self._name def __set__(self, str name not None): _str_to_pj_str(name.encode(), &self._sdp_session.name) self._name = name property connection: def __get__(self): return self._connection def __set__(self, SDPConnection connection): if connection is None: self._sdp_session.conn = NULL else: self._sdp_session.conn = connection.get_sdp_connection() self._connection = connection property start_time: def __get__(self): return self._sdp_session.time.start def __set__(self, unsigned long start_time): self._sdp_session.time.start = start_time property stop_time: def __get__(self): return self._sdp_session.time.stop def __set__(self, unsigned long stop_time): self._sdp_session.time.stop = stop_time property attributes: def __get__(self): return self._attributes def __set__(self, list attributes not None): if len(attributes) > PJMEDIA_MAX_SDP_ATTR: raise SIPCoreError("Too many attributes") for attr in attributes: if not isinstance(attr, SDPAttribute): raise TypeError("Items in SDPSession attribute list must be SDPAttribute instancess") if not isinstance(attributes, SDPAttributeList): attributes = SDPAttributeList(attributes) self._attributes = attributes property bandwidth_info: def __get__(self): return self._bandwidth_info def __set__(self, list infos not None): if len(infos) > PJMEDIA_MAX_SDP_BANDW: raise SIPCoreError("Too many bandwidth info attributes") for info in infos: if not isinstance(info, SDPBandwidthInfo): raise TypeError("Items in SDPSession attribute list must be SDPBandwidthInfo instancess") if not isinstance(infos, SDPBandwidthInfoList): infos = SDPBandwidthInfoList(infos) self._bandwidth_info = infos property media: def __get__(self): return self._media def __set__(self, list media not None): if len(media) > PJMEDIA_MAX_SDP_MEDIA: raise SIPCoreError("Too many media objects") for m in media: if m is not None and not isinstance(m, SDPMediaStream): raise TypeError("Items in SDPSession media list must be SDPMediaStream instancess") self._media = media cdef int _update(self) except -1: cdef SDPSession session cdef SDPMediaStream media, old_media session = SDPSession_create(&(self)._sdp_session) if len(self._media) != len(session._media): raise ValueError("Number of media streams in SDPSession got changed") if len(self._attributes) > len(session._attributes): raise ValueError("Number of attributes in SDPSession got reduced") for attr in ("id", "version", "user", "net_type", "address_type", "address", "name", "start_time", "stop_time"): setattr(self, attr, getattr(session, attr)) if session._connection is None: self.connection = None elif self._connection is None or self._connection != session._connection: self.connection = session._connection for index, attribute in enumerate(session._attributes): try: old_attribute = self._attributes[index] except IndexError: self._attributes.append(attribute) else: if old_attribute != attribute: self._attributes[index] = attribute for index, info in enumerate(session._bandwidth_info): try: old_info = self._bandwidth_info[index] except IndexError: self._bandwidth_info.append(info) else: if old_info != info: self._bandwidth_info[index] = info for index, media in enumerate(session._media): old_media = self._media[index] if old_media is not None: old_media._update(media) cdef class FrozenSDPSession(BaseSDPSession): def __init__(self, str address not None, object id=None, object version=None, str user not None="-", str net_type not None="IN", str address_type not None="IP4", str name not None=" ", FrozenSDPConnection connection=None, unsigned long start_time=0, unsigned long stop_time=0, frozenlist attributes not None=frozenlist(), frozenlist bandwidth_info not None=frozenlist(), frozenlist media not None=frozenlist()): cdef unsigned int version_id = 2208988800UL cdef pj_time_val tv if not self.initialized: if len(attributes) > PJMEDIA_MAX_SDP_ATTR: raise SIPCoreError("Too many attributes") for attr in attributes: if not isinstance(attr, FrozenSDPAttribute): raise TypeError("Items in FrozenSDPSession attribute list must be FrozenSDPAttribute instances") if len(bandwidth_info) > PJMEDIA_MAX_SDP_BANDW: raise SIPCoreError("Too many bandwidth info attributes") for info in bandwidth_info: if not isinstance(info, FrozenSDPBandwidthInfo): raise TypeError("Items in FrozenSDPSession bandwidth info attribute list must be FrozenSDPBandwidthInfo instances") if len(media) > PJMEDIA_MAX_SDP_MEDIA: raise SIPCoreError("Too many media objects") for m in media: if not isinstance(m, FrozenSDPMediaStream): raise TypeError("Items in FrozenSDPSession media list must be FrozenSDPMediaStream instancess") pj_gettimeofday(&tv) version_id += tv.sec self.address = address _str_to_pj_str(address.encode(), &self._sdp_session.origin.addr) self.id = id if id is not None else version_id self._sdp_session.origin.id = id if id is not None else version_id self.version = version if version is not None else version_id self._sdp_session.origin.version = version if version is not None else version_id self.user = user _str_to_pj_str(user.encode(), &self._sdp_session.origin.user) self.net_type = net_type _str_to_pj_str(net_type.encode(), &self._sdp_session.origin.net_type) self.address_type = address_type _str_to_pj_str(address_type.encode(), &self._sdp_session.origin.addr_type) self.name = name _str_to_pj_str(name.encode(), &self._sdp_session.name) self.connection = connection if connection is None: self._sdp_session.conn = NULL else: self._sdp_session.conn = connection.get_sdp_connection() self.start_time = start_time self._sdp_session.time.start = start_time self.stop_time = stop_time self._sdp_session.time.stop = stop_time self.attributes = FrozenSDPAttributeList(attributes) if not isinstance(attributes, FrozenSDPAttributeList) else attributes self.bandwidth_info = FrozenSDPBandwidthInfoList(bandwidth_info) if not isinstance(bandwidth_info, FrozenSDPBandwidthInfo) else bandwidth_info self.media = media self.initialized = 1 @classmethod def new(cls, BaseSDPSession sdp_session): if isinstance(sdp_session, FrozenSDPSession): return sdp_session connection = FrozenSDPConnection.new(sdp_session.connection) if (sdp_session.connection is not None) else None attributes = frozenlist([FrozenSDPAttribute.new(attr) for attr in sdp_session.attributes]) bandwidth_info = frozenlist([FrozenSDPBandwidthInfo.new(info) for info in sdp_session.bandwidth_info]) media = frozenlist([FrozenSDPMediaStream.new(m) for m in sdp_session.media]) return cls(sdp_session.address, sdp_session.id, sdp_session.version, sdp_session.user, sdp_session.net_type, sdp_session.address_type, sdp_session.name, connection, sdp_session.start_time, sdp_session.stop_time, attributes, bandwidth_info, media) @classmethod def parse(cls, str sdp): cdef pjmedia_sdp_session *sdp_session sdp_session = _parse_sdp_session(sdp) return FrozenSDPSession_create(sdp_session) def __hash__(self): return hash((self.address, self.id, self.version, self.user, self.net_type, self.address_type, self.name, self.connection, self.start_time, self.stop_time, self.attributes, self.bandwidth_info, self.media)) def __richcmp__(self, other, op): return BaseSDPSession_richcmp(self, other, op) class MediaCodec(object): name = WriteOnceAttribute() rate = WriteOnceAttribute() def __init__(self, name, rate): self.name = name self.rate = int(rate) def __repr__(self): return "%s(%r, %r)" % (self.__class__.__name__, self.name, self.rate) def __str__(self): return "%s/%s" % (self.name, self.rate) def __hash__(self): return hash(self.name) def __eq__(self, other): if isinstance(other, MediaCodec): return self.name.lower() == other.name.lower() and self.rate == other.rate elif isinstance(other, basestring): if '/' in other: return self.__str__().lower() == other.lower() else: return self.name.lower() == other.lower() return False def __ne__(self, other): return not self.__eq__(other) cdef object BaseSDPMediaStream_richcmp(object self, object other, int op) with gil: cdef int eq = 1 if op not in [2,3]: return NotImplemented if not isinstance(other, BaseSDPMediaStream): return NotImplemented for attr in ("media", "port", "port_count", "transport", "formats", "connection", "attributes", "bandwidth_info"): if getattr(self, attr) != getattr(other, attr): eq = 0 break if op == 2: return bool(eq) else: return not eq cdef class BaseSDPMediaStream: rtpmap_re = re.compile(r"""^(?P\d+)\s+(?P[-\w]+)/(?P\d+)(?:/\w+)?$""", re.IGNORECASE | re.MULTILINE) rtp_mappings = { 0: MediaCodec('PCMU', 8000), 3: MediaCodec('GSM', 8000), 4: MediaCodec('G723', 8000), 5: MediaCodec('DVI4', 8000), 6: MediaCodec('DVI4', 16000), 7: MediaCodec('LPC', 8000), 8: MediaCodec('PCMA', 8000), 9: MediaCodec('G722', 8000), 10: MediaCodec('L16', 44100), # 2 channels 11: MediaCodec('L16', 44100), # 1 channel 12: MediaCodec('QCELP', 8000), 13: MediaCodec('CN', 8000), 14: MediaCodec('MPA', 90000), 15: MediaCodec('G728', 8000), 16: MediaCodec('DVI4', 11025), 17: MediaCodec('DVI4', 22050), 18: MediaCodec('G729', 8000)} def __init__(self, *args, **kwargs): raise TypeError("BaseSDPMediaStream cannot be instantiated directly") def __repr__(self): return "%s(%r, %r, %r, %r, %r, %r, %r, %r)" % (self.__class__.__name__, self.media, self.port, self.transport, self.port_count, self.formats, self.connection, self.attributes, self.bandwidth_info) def __richcmp__(self, other, op): return BaseSDPMediaStream_richcmp(self, other, op) property direction: def __get__(self): for attribute in self.attributes: if attribute.name in ("sendrecv", "sendonly", "recvonly", "inactive"): return attribute.name return "sendrecv" property has_ice_attributes: def __get__(self): return set([attr.name for attr in self.attributes]).issuperset(['ice-pwd', 'ice-ufrag']) property has_ice_candidates: def __get__(self): return 'candidate' in self.attributes cdef pjmedia_sdp_media* get_sdp_media(self): self._sdp_media.attr_count = len(self.attributes) for index, attr in enumerate(self.attributes): self._sdp_media.attr[index] = (attr).get_sdp_attribute() self._sdp_media.bandw_count = len(self.bandwidth_info) for index, info in enumerate(self.bandwidth_info): self._sdp_media.bandw[index] = (info).get_sdp_bandwidth_info() return &self._sdp_media cdef class SDPMediaStream(BaseSDPMediaStream): def __init__(self, str media not None, int port, str transport not None, int port_count=1, list formats=None, SDPConnection connection=None, list attributes=None, list bandwidth_info=None): self.media = media self.port = port self.transport = transport self.port_count = port_count self.formats = formats if formats is not None else [] self.connection = connection self.attributes = attributes if attributes is not None else [] self.bandwidth_info = bandwidth_info if bandwidth_info is not None else [] @classmethod def new(cls, BaseSDPMediaStream sdp_media): connection = SDPConnection.new(sdp_media.connection) if (sdp_media.connection is not None) else None attributes = [SDPAttribute.new(attr) for attr in sdp_media.attributes] bandwidth_info = [SDPBandwidthInfo.new(bi) for bi in sdp_media.bandwidth_info] return cls(sdp_media.media, sdp_media.port, sdp_media.transport, sdp_media.port_count, list(sdp_media.formats), connection, attributes, bandwidth_info) property media: def __get__(self): return self._media def __set__(self, str media not None): _str_to_pj_str(media.encode(), &self._sdp_media.desc.media) self._media = media property port: def __get__(self): return self._sdp_media.desc.port def __set__(self, int port): self._sdp_media.desc.port = port property transport: def __get__(self): return self._transport def __set__(self, str transport not None): _str_to_pj_str(transport.encode(), &self._sdp_media.desc.transport) self._transport = transport property port_count: def __get__(self): return self._sdp_media.desc.port_count def __set__(self, int port_count): self._sdp_media.desc.port_count = port_count property formats: def __get__(self): return self._formats def __set__(self, list formats not None): if len(formats) > PJMEDIA_MAX_SDP_FMT: raise SIPCoreError("Too many formats") self._sdp_media.desc.fmt_count = len(formats) for index, format in enumerate(formats): _str_to_pj_str(format.encode(), &self._sdp_media.desc.fmt[index]) self._formats = formats property codec_list: def __get__(self): return self._codec_list property connection: def __get__(self): return self._connection def __set__(self, SDPConnection connection): if connection is None: self._sdp_media.conn = NULL else: self._sdp_media.conn = connection.get_sdp_connection() self._connection = connection property attributes: def __get__(self): return self._attributes def __set__(self, list attributes not None): if len(attributes) > PJMEDIA_MAX_SDP_ATTR: raise SIPCoreError("Too many attributes") for attr in attributes: if not isinstance(attr, SDPAttribute): raise TypeError("Items in SDPMediaStream attribute list must be SDPAttribute instances") if not isinstance(attributes, SDPAttributeList): attributes = SDPAttributeList(attributes) self._attributes = attributes if self._media in ("audio", "video"): rtp_mappings = self.rtp_mappings.copy() rtpmap_lines = '\n'.join([attr.value for attr in attributes if attr.name=='rtpmap']) # iterators are not supported -Dan rtpmap_codecs = dict([(int(type), MediaCodec(name, rate)) for type, name, rate in self.rtpmap_re.findall(rtpmap_lines)]) rtp_mappings.update(rtpmap_codecs) self._codec_list = [rtp_mappings.get(int(format), MediaCodec('Unknown', 0)) for format in self.formats] else: self._codec_list = list() property bandwidth_info: def __get__(self): return self._bandwidth_info def __set__(self, list infos not None): if len(infos) > PJMEDIA_MAX_SDP_BANDW: raise SIPCoreError("Too many bandwidth information attributes") for info in infos: if not isinstance(info, SDPBandwidthInfo): raise TypeError("Items in SDPMediaStream bandwidth_info list must be SDPBandwidthInfo instances") if not isinstance(infos, SDPBandwidthInfoList): infos = SDPBandwidthInfoList(infos) self._bandwidth_info = infos cdef int _update(self, SDPMediaStream media) except -1: if len(self._attributes) > len(media._attributes): raise ValueError("Number of attributes in SDPMediaStream got reduced") if len(self._bandwidth_info) > len(media._bandwidth_info): raise ValueError("Number of bandwidth info attributes in SDPMediaStream got reduced") for attr in ("media", "port", "transport", "port_count", "formats"): setattr(self, attr, getattr(media, attr)) if media._connection is None: self.connection = None elif self._connection is None or self._connection != media.connection: self.connection = media._connection for index, attribute in enumerate(media._attributes): try: old_attribute = self._attributes[index] except IndexError: self._attributes.append(attribute) else: if old_attribute != attribute: self._attributes[index] = attribute for index, info in enumerate(media._bandwidth_info): try: old_info = self._bandwidth_info[index] except IndexError: self._bandwidth_info.append(info) else: if old_info != info: self._bandwidth_info[index] = info cdef class FrozenSDPMediaStream(BaseSDPMediaStream): def __init__(self, str media not None, int port, str transport not None, int port_count=1, frozenlist formats not None=frozenlist(), FrozenSDPConnection connection=None, frozenlist attributes not None=frozenlist(), frozenlist bandwidth_info not None=frozenlist()): if not self.initialized: if len(formats) > PJMEDIA_MAX_SDP_FMT: raise SIPCoreError("Too many formats") if len(attributes) > PJMEDIA_MAX_SDP_ATTR: raise SIPCoreError("Too many attributes") for attr in attributes: if not isinstance(attr, FrozenSDPAttribute): raise TypeError("Items in FrozenSDPMediaStream attribute list must be FrozenSDPAttribute instances") if len(bandwidth_info) > PJMEDIA_MAX_SDP_BANDW: raise SIPCoreError("Too many bandwidth info attributes") for info in bandwidth_info: if not isinstance(info, FrozenSDPBandwidthInfo): raise TypeError("Items in FrozenSDPMediaStream bandwidth info list must be FrozenSDPBandwidthInfo instances") self.media = media _str_to_pj_str(media.encode(), &self._sdp_media.desc.media) self.port = port self._sdp_media.desc.port = port self.transport = transport _str_to_pj_str(transport.encode(), &self._sdp_media.desc.transport) self.port_count = port_count self._sdp_media.desc.port_count = port_count self.formats = formats self._sdp_media.desc.fmt_count = len(self.formats) for index, format in enumerate(self.formats): _str_to_pj_str(format.encode(), &self._sdp_media.desc.fmt[index]) self.connection = connection if connection is None: self._sdp_media.conn = NULL else: self._sdp_media.conn = connection.get_sdp_connection() self.attributes = FrozenSDPAttributeList(attributes) if not isinstance(attributes, FrozenSDPAttributeList) else attributes if self.media in ("audio", "video"): rtp_mappings = self.rtp_mappings.copy() rtpmap_lines = '\n'.join([attr.value for attr in attributes if attr.name=='rtpmap']) # iterators are not supported -Dan rtpmap_codecs = dict([(int(type), MediaCodec(name, rate)) for type, name, rate in self.rtpmap_re.findall(rtpmap_lines)]) rtp_mappings.update(rtpmap_codecs) self.codec_list = frozenlist([rtp_mappings.get(int(format) if format.isdigit() else None, MediaCodec('Unknown', 0)) for format in self.formats]) else: self.codec_list = frozenlist() self.bandwidth_info = FrozenSDPBandwidthInfoList(bandwidth_info) if not isinstance(bandwidth_info, FrozenSDPBandwidthInfoList) else bandwidth_info self.initialized = 1 @classmethod def new(cls, BaseSDPMediaStream sdp_media): if isinstance(sdp_media, FrozenSDPMediaStream): return sdp_media connection = FrozenSDPConnection.new(sdp_media.connection) if (sdp_media.connection is not None) else None attributes = frozenlist([FrozenSDPAttribute.new(attr) for attr in sdp_media.attributes]) bandwidth_info = frozenlist([FrozenSDPBandwidthInfo.new(info) for info in sdp_media.bandwidth_info]) return cls(sdp_media.media, sdp_media.port, sdp_media.transport, sdp_media.port_count, frozenlist(sdp_media.formats), connection, attributes, bandwidth_info) def __hash__(self): return hash((self.media, self.port, self.transport, self.port_count, self.formats, self.connection, self.attributes, self.bandwidth_info)) def __richcmp__(self, other, op): return BaseSDPMediaStream_richcmp(self, other, op) cdef object BaseSDPConnection_richcmp(object self, object other, int op) with gil: cdef int eq = 1 if op not in [2,3]: return NotImplemented if not isinstance(other, BaseSDPConnection): return NotImplemented for attr in ("net_type", "address_type", "address"): if getattr(self, attr) != getattr(other, attr): eq = 0 break if op == 2: return bool(eq) else: return not eq cdef class BaseSDPConnection: def __init__(self, *args, **kwargs): raise TypeError("BaseSDPConnection cannot be instantiated directly") def __repr__(self): return "%s(%r, %r, %r)" % (self.__class__.__name__, self.address, self.net_type, self.address_type) def __richcmp__(self, other, op): return BaseSDPConnection_richcmp(self, other, op) cdef pjmedia_sdp_conn* get_sdp_connection(self): return &self._sdp_connection cdef class SDPConnection(BaseSDPConnection): def __init__(self, str address not None, str net_type not None="IN", str address_type not None="IP4"): self.address = address self.net_type = net_type self.address_type = address_type @classmethod def new(cls, BaseSDPConnection sdp_connection): return cls(sdp_connection.address, sdp_connection.net_type, sdp_connection.address_type) property address: def __get__(self): return self._address def __set__(self, str address not None): _str_to_pj_str(address.encode(), &self._sdp_connection.addr) self._address = address property net_type: def __get__(self): return self._net_type def __set__(self, str net_type not None): _str_to_pj_str(net_type.encode(), &self._sdp_connection.net_type) self._net_type = net_type property address_type: def __get__(self): return self._address_type def __set__(self, str address_type not None): _str_to_pj_str(address_type.encode(), &self._sdp_connection.addr_type) self._address_type = address_type cdef class FrozenSDPConnection(BaseSDPConnection): def __init__(self, str address not None, str net_type not None="IN", str address_type not None="IP4"): if not self.initialized: _str_to_pj_str(address.encode(), &self._sdp_connection.addr) _str_to_pj_str(net_type.encode(), &self._sdp_connection.net_type) _str_to_pj_str(address_type.encode(), &self._sdp_connection.addr_type) self.address = address self.net_type = net_type self.address_type = address_type self.initialized = 1 @classmethod def new(cls, BaseSDPConnection sdp_connection): if isinstance(sdp_connection, FrozenSDPConnection): return sdp_connection return cls(sdp_connection.address, sdp_connection.net_type, sdp_connection.address_type) def __hash__(self): return hash((self.address, self.net_type, self.address_type)) def __richcmp__(self, other, op): return BaseSDPConnection_richcmp(self, other, op) cdef class SDPAttributeList(list): def __contains__(self, item): if isinstance(item, BaseSDPAttribute): return list.__contains__(self, item) else: return item in [attr.name for attr in self] def getall(self, name): return [attr.value for attr in self if attr.name == name] def getfirst(self, name, default=None): for attr in self: if attr.name == name: return attr.value return default cdef class FrozenSDPAttributeList(frozenlist): def __contains__(self, item): if isinstance(item, BaseSDPAttribute): return list.__contains__(self, item) else: return item in [attr.name for attr in self] def getall(self, name): return [attr.value for attr in self if attr.name == name] def getfirst(self, name, default=None): for attr in self: if attr.name == name: return attr.value return default cdef object BaseSDPAttribute_richcmp(object self, object other, int op) with gil: cdef int eq = 1 if op not in [2,3]: return NotImplemented if not isinstance(other, BaseSDPAttribute): return NotImplemented for attr in ("name", "value"): if getattr(self, attr) != getattr(other, attr): eq = 0 break if op == 2: return bool(eq) else: return not eq cdef class BaseSDPAttribute: def __init__(self, *args, **kwargs): raise TypeError("BaseSDPAttribute cannot be instantiated directly") def __repr__(self): return "%s(%r, %r)" % (self.__class__.__name__, self.name, self.value) def __richcmp__(self, other, op): return BaseSDPAttribute_richcmp(self, other, op) cdef pjmedia_sdp_attr* get_sdp_attribute(self): return &self._sdp_attribute cdef class SDPAttribute(BaseSDPAttribute): def __init__(self, str name not None, str value not None): self.name = name self.value = value @classmethod def new(cls, BaseSDPAttribute sdp_attribute): return cls(sdp_attribute.name, sdp_attribute.value) property name: def __get__(self): return self._name def __set__(self, str name not None): _str_to_pj_str(name.encode(), &self._sdp_attribute.name) self._name = name property value: def __get__(self): return self._value def __set__(self, str value not None): _str_to_pj_str(value.encode(), &self._sdp_attribute.value) self._value = value cdef class FrozenSDPAttribute(BaseSDPAttribute): def __init__(self, str name not None, str value not None): if not self.initialized: _str_to_pj_str(name.encode(), &self._sdp_attribute.name) _str_to_pj_str(value.encode(), &self._sdp_attribute.value) self.name = name self.value = value self.initialized = 1 @classmethod def new(cls, BaseSDPAttribute sdp_attribute): if isinstance(sdp_attribute, FrozenSDPAttribute): return sdp_attribute return cls(sdp_attribute.name, sdp_attribute.value) def __hash__(self): return hash((self.name, self.value)) def __richcmp__(self, other, op): return BaseSDPAttribute_richcmp(self, other, op) cdef class SDPBandwidthInfoList(list): def __contains__(self, item): if isinstance(item, BaseSDPBandwidthInfo): return list.__contains__(self, item) else: return item in [attr.name.decode() for attr in self] cdef class FrozenSDPBandwidthInfoList(frozenlist): def __contains__(self, item): if isinstance(item, BaseSDPBandwidthInfo): return list.__contains__(self, item) else: return item in [info.modifier.decode() for info in self] cdef object BaseSDPBandwidthInfo_richcmp(object self, object other, int op) with gil: cdef int eq = 1 if op not in [2,3]: return NotImplemented if not isinstance(other, BaseSDPBandwidthInfo): return NotImplemented for attr in ("modifier", "value"): if getattr(self, attr) != getattr(other, attr): eq = 0 break if op == 2: return bool(eq) else: return not eq cdef class BaseSDPBandwidthInfo: def __init__(self, *args, **kwargs): raise TypeError("BaseSDPBandwidthInfo cannot be instantiated directly") def __repr__(self): return "%s(%r, %r)" % (self.__class__.__name__, self.modifier, self.value) def __richcmp__(self, other, op): return BaseSDPBandwidthInfo_richcmp(self, other, op) cdef pjmedia_sdp_bandw* get_sdp_bandwidth_info(self): return &self._sdp_bandwidth_info cdef class SDPBandwidthInfo(BaseSDPBandwidthInfo): def __init__(self, str modifier not None, object value not None): self.modifier = modifier self.value = value @classmethod def new(cls, BaseSDPBandwidthInfo sdp_bandwidth_info): return cls(sdp_bandwidth_info.modifier, sdp_bandwidth_info.value) property modifier: def __get__(self): return self._modifier def __set__(self, str modifier not None): _str_to_pj_str(modifier.encode(), &self._sdp_bandwidth_info.modifier) self._modifier = modifier property value: def __get__(self): return self._value def __set__(self, object value not None): self._value = value self._sdp_bandwidth_info.value = self._value cdef class FrozenSDPBandwidthInfo(BaseSDPBandwidthInfo): def __init__(self, str modifier not None, object value not None): if not self.initialized: _str_to_pj_str(modifier.encode(), &self._sdp_bandwidth_info.modifier) self.modifier = modifier self._sdp_bandwidth_info.value = value self.value = value self.initialized = 1 @classmethod def new(cls, BaseSDPBandwidthInfo sdp_bandwidth_info): if isinstance(sdp_bandwidth_info, FrozenSDPBandwidthInfo): return sdp_bandwidth_info return cls(sdp_bandwidth_info.modifier, sdp_bandwidth_info.value) def __hash__(self): return hash((self.modifier, self.value)) def __richcmp__(self, other, op): return BaseSDPBandwidthInfo_richcmp(self, other, op) # Factory functions # cdef SDPSession SDPSession_create(pjmedia_sdp_session_ptr_const pj_session): cdef SDPConnection connection = None cdef int i if pj_session.conn != NULL: connection = SDPConnection_create(pj_session.conn) + print('pj_session.origin.user %s' % pj_session.origin.user ) + print('pj_session.origin.user %s' % _pj_str_to_str(pj_session.origin.user)) + print('pj_session.origin.addr_type %s' % pj_session.origin.addr_type) + print('pj_session.origin.addr_type %s' % _pj_str_to_bytes(pj_session.origin.addr_type)) + print('pj_session.origin.net_type %s' % pj_session.origin.net_type ) + print('pj_session.origin.name %s' % pj_session.name ) return SDPSession(_pj_str_to_str(pj_session.origin.addr), pj_session.origin.id, pj_session.origin.version, _pj_str_to_str(pj_session.origin.user), _pj_str_to_str(pj_session.origin.net_type), _pj_str_to_str(pj_session.origin.addr_type), _pj_str_to_str(pj_session.name), connection, pj_session.time.start, pj_session.time.stop, [SDPAttribute_create(pj_session.attr[i]) for i in range(pj_session.attr_count)], [SDPBandwidthInfo_create(pj_session.bandw[i]) for i in range(pj_session.bandw_count)], [SDPMediaStream_create(pj_session.media[i]) if pj_session.media[i] != NULL else None for i in range(pj_session.media_count)]) cdef FrozenSDPSession FrozenSDPSession_create(pjmedia_sdp_session_ptr_const pj_session): cdef FrozenSDPConnection connection = None cdef int i if pj_session.conn != NULL: connection = FrozenSDPConnection_create(pj_session.conn) return FrozenSDPSession(_pj_str_to_str(pj_session.origin.addr), pj_session.origin.id, pj_session.origin.version, _pj_str_to_str(pj_session.origin.user), _pj_str_to_str(pj_session.origin.net_type), _pj_str_to_str(pj_session.origin.addr_type), _pj_str_to_str(pj_session.name), connection, pj_session.time.start, pj_session.time.stop, frozenlist([FrozenSDPAttribute_create(pj_session.attr[i]) for i in range(pj_session.attr_count)]), frozenlist([FrozenSDPBandwidthInfo_create(pj_session.bandw[i]) for i in range(pj_session.bandw_count)]), frozenlist([FrozenSDPMediaStream_create(pj_session.media[i]) if pj_session.media[i] != NULL else None for i in range(pj_session.media_count)])) cdef SDPMediaStream SDPMediaStream_create(pjmedia_sdp_media *pj_media): cdef SDPConnection connection = None cdef int i if pj_media.conn != NULL: connection = SDPConnection_create(pj_media.conn) return SDPMediaStream(_pj_str_to_str(pj_media.desc.media), pj_media.desc.port, _pj_str_to_str(pj_media.desc.transport), pj_media.desc.port_count, [_pj_str_to_str(pj_media.desc.fmt[i]) for i in range(pj_media.desc.fmt_count)], connection, [SDPAttribute_create(pj_media.attr[i]) for i in range(pj_media.attr_count)], [SDPBandwidthInfo_create(pj_media.bandw[i]) for i in range(pj_media.bandw_count)]) cdef FrozenSDPMediaStream FrozenSDPMediaStream_create(pjmedia_sdp_media *pj_media): cdef FrozenSDPConnection connection = None cdef int i if pj_media.conn != NULL: connection = FrozenSDPConnection_create(pj_media.conn) return FrozenSDPMediaStream(_pj_str_to_str(pj_media.desc.media), pj_media.desc.port, _pj_str_to_str(pj_media.desc.transport), pj_media.desc.port_count, frozenlist([_pj_str_to_str(pj_media.desc.fmt[i]) for i in range(pj_media.desc.fmt_count)]), connection, frozenlist([FrozenSDPAttribute_create(pj_media.attr[i]) for i in range(pj_media.attr_count)]), frozenlist([FrozenSDPBandwidthInfo_create(pj_media.bandw[i]) for i in range(pj_media.bandw_count)])) cdef SDPConnection SDPConnection_create(pjmedia_sdp_conn *pj_conn): return SDPConnection(_pj_str_to_str(pj_conn.addr), _pj_str_to_str(pj_conn.net_type), _pj_str_to_str(pj_conn.addr_type)) cdef FrozenSDPConnection FrozenSDPConnection_create(pjmedia_sdp_conn *pj_conn): return FrozenSDPConnection(_pj_str_to_str(pj_conn.addr), _pj_str_to_str(pj_conn.net_type), _pj_str_to_str(pj_conn.addr_type)) cdef SDPAttribute SDPAttribute_create(pjmedia_sdp_attr *pj_attr): return SDPAttribute(_pj_str_to_str(pj_attr.name), _pj_str_to_str(pj_attr.value)) cdef FrozenSDPAttribute FrozenSDPAttribute_create(pjmedia_sdp_attr *pj_attr): return FrozenSDPAttribute(_pj_str_to_str(pj_attr.name), _pj_str_to_str(pj_attr.value)) cdef SDPBandwidthInfo SDPBandwidthInfo_create(pjmedia_sdp_bandw *pj_bandw): return SDPBandwidthInfo(_pj_str_to_str(pj_bandw.modifier), int(pj_bandw.value)) cdef FrozenSDPBandwidthInfo FrozenSDPBandwidthInfo_create(pjmedia_sdp_bandw *pj_bandw): return FrozenSDPBandwidthInfo(_pj_str_to_str(pj_bandw.modifier), int(pj_bandw.value)) # SDP negotiator # cdef class SDPNegotiator: def __cinit__(self, *args, **kwargs): cdef pj_pool_t *pool cdef bytes pool_name cdef PJSIPUA ua ua = _get_ua() pool_name = b"SDPNegotiator_%d" % id(self) pool = ua.create_memory_pool(pool_name, 4096, 4096) self._pool = pool self._neg = NULL def __dealloc__(self): cdef PJSIPUA ua try: ua = _get_ua() except: return ua.release_memory_pool(self._pool) self._pool = NULL @classmethod def create_with_local_offer(cls, BaseSDPSession sdp_session): cdef int status cdef pjmedia_sdp_neg *neg cdef pj_pool_t *pool cdef SDPNegotiator obj obj = cls() pool = obj._pool status = pjmedia_sdp_neg_create_w_local_offer(pool, sdp_session.get_sdp_session(), &neg) if status != 0: raise PJSIPError("failed to create SDPNegotiator with local offer", status) obj._neg = neg return obj @classmethod def create_with_remote_offer(cls, BaseSDPSession sdp_session): cdef int status cdef pjmedia_sdp_neg *neg cdef pj_pool_t *pool cdef SDPNegotiator obj obj = cls() pool = obj._pool status = pjmedia_sdp_neg_create_w_remote_offer(pool, NULL, sdp_session.get_sdp_session(), &neg) if status != 0: raise PJSIPError("failed to create SDPNegotiator with remote offer", status) obj._neg = neg return obj def __repr__(self): return "%s, state=%s" % (self.__class__.__name__, self.state) property state: def __get__(self): if self._neg == NULL: return None return _buf_to_str(pjmedia_sdp_neg_state_str(pjmedia_sdp_neg_get_state(self._neg))) property active_local: def __get__(self): cdef int status cdef pjmedia_sdp_session_ptr_const sdp if self._neg == NULL: return None status = pjmedia_sdp_neg_get_active_local(self._neg, &sdp) if status != 0: return None return FrozenSDPSession_create(sdp) property active_remote: def __get__(self): cdef int status cdef pjmedia_sdp_session_ptr_const sdp if self._neg == NULL: return None status = pjmedia_sdp_neg_get_active_remote(self._neg, &sdp) if status != 0: return None return FrozenSDPSession_create(sdp) property current_local: def __get__(self): cdef int status cdef pjmedia_sdp_session_ptr_const sdp if self._neg == NULL: return None status = pjmedia_sdp_neg_get_neg_local(self._neg, &sdp) if status != 0: return None return FrozenSDPSession_create(sdp) property current_remote: def __get__(self): cdef int status cdef pjmedia_sdp_session_ptr_const sdp if self._neg == NULL: return None status = pjmedia_sdp_neg_get_neg_remote(self._neg, &sdp) if status != 0: return None return FrozenSDPSession_create(sdp) def _check_self(self): if self._neg == NULL: raise RuntimeError('SDPNegotiator was not properly initialized') def set_local_answer(self, BaseSDPSession sdp_session): self._check_self() cdef int status cdef pj_pool_t *pool = self._pool status = pjmedia_sdp_neg_set_local_answer(pool, self._neg, sdp_session.get_sdp_session()) if status != 0: raise PJSIPError("failed to set local answer", status) def set_local_offer(self, BaseSDPSession sdp_session): # PJSIP has an asymmetric API here. This function will modify the local session with the new SDP and treat it as a local offer. self._check_self() cdef int status cdef pj_pool_t *pool = self._pool status = pjmedia_sdp_neg_modify_local_offer(pool, self._neg, sdp_session.get_sdp_session()) if status != 0: raise PJSIPError("failed to set local offer", status) def set_remote_answer(self, BaseSDPSession sdp_session): self._check_self() cdef int status cdef pj_pool_t *pool = self._pool status = pjmedia_sdp_neg_set_remote_answer(pool, self._neg, sdp_session.get_sdp_session()) if status != 0: raise PJSIPError("failed to set remote answer", status) def set_remote_offer(self, BaseSDPSession sdp_session): self._check_self() cdef int status cdef pj_pool_t *pool = self._pool status = pjmedia_sdp_neg_set_remote_offer(pool, self._neg, sdp_session.get_sdp_session()) if status != 0: raise PJSIPError("failed to set remote offer", status) def cancel_offer(self): self._check_self() cdef int status status = pjmedia_sdp_neg_cancel_offer(self._neg) if status != 0: raise PJSIPError("failed to cancel offer", status) def negotiate(self): self._check_self() cdef int status cdef pj_pool_t *pool = self._pool status = pjmedia_sdp_neg_negotiate(pool, self._neg, 0) if status != 0: raise PJSIPError("SDP negotiation failed", status) diff --git a/sipsimple/core/_core.util.pxi b/sipsimple/core/_core.util.pxi index 53d9f9ae..e62513bf 100644 --- a/sipsimple/core/_core.util.pxi +++ b/sipsimple/core/_core.util.pxi @@ -1,433 +1,433 @@ import platform import re import sys from application.version import Version cdef class PJSTR: def __cinit__(self, str): self.str = str _str_to_pj_str(str, &self.pj_str) def __str__(self): return self.str cdef class SIPStatusMessages: cdef object _default_status def __cinit__(self, *args, **kwargs): self._default_status = _pj_str_to_str(pjsip_get_status_text(0)[0]) def __getitem__(self, int val): cdef object _status _status = _pj_str_to_str(pjsip_get_status_text(val)[0]) if _status == self._default_status: raise IndexError("Unknown SIP response code: %d" % val) return _status cdef class frozenlist: def __cinit__(self, *args, **kw): self.list = list() self.initialized = 0 self.hash = 0 def __init__(self, *args, **kw): if not self.initialized: self.list = list(*args, **kw) self.initialized = 1 self.hash = hash(tuple(self.list)) def __reduce__(self): return (self.__class__, (self.list,), None) def __repr__(self): return "frozenlist(%r)" % self.list def __len__(self): return self.list.__len__() def __hash__(self): return self.hash def __iter__(self): return self.list.__iter__() def __cmp__(self, frozenlist other): return self.list.__cmp__(other.list) def __richcmp__(frozenlist self, other, op): if isinstance(other, frozenlist): other = (other).list if op == 0: return self.list.__cmp__(other) < 0 elif op == 1: return self.list.__cmp__(other) <= 0 elif op == 2: return self.list.__eq__(other) elif op == 3: return self.list.__ne__(other) elif op == 4: return self.list.__cmp__(other) > 0 elif op == 5: return self.list.__cmp__(other) >= 0 else: return NotImplemented def __contains__(self, item): return self.list.__contains__(item) def __getitem__(self, key): return self.list.__getitem__(key) def __add__(first, second): if isinstance(first, frozenlist): first = (first).list if isinstance(second, frozenlist): second = (second).list return frozenlist(first+second) def __mul__(first, second): if isinstance(first, frozenlist): first = (first).list if isinstance(second, frozenlist): second = (second).list return frozenlist(first*second) def __reversed__(self): return self.list.__reversed__() def count(self, elem): return self.list.count(elem) def index(self, elem): return self.list.index(elem) cdef class frozendict: def __cinit__(self, *args, **kw): self.dict = dict() self.initialized = 0 def __init__(self, *args, **kw): if not self.initialized: self.dict = dict(*args, **kw) self.initialized = 1 self.hash = hash(tuple(self.dict.iteritems())) def __reduce__(self): return (self.__class__, (self.dict,), None) def __repr__(self): return "frozendict(%r)" % self.dict def __len__(self): return self.dict.__len__() def __hash__(self): return self.hash def __iter__(self): return self.dict.__iter__() def __cmp__(self, frozendict other): return self.dict.__cmp__(other.dict) def __richcmp__(frozendict self, other, op): if isinstance(other, frozendict): other = (other).dict if op == 0: return self.dict.__cmp__(other) < 0 elif op == 1: return self.dict.__cmp__(other) <= 0 elif op == 2: return self.dict.__eq__(other) elif op == 3: return self.dict.__ne__(other) elif op == 4: return self.dict.__cmp__(other) > 0 elif op == 5: return self.dict.__cmp__(other) >= 0 else: return NotImplemented def __contains__(self, item): return self.dict.__contains__(item) def __getitem__(self, key): return self.dict.__getitem__(key) def copy(self): return self def get(self, *args): return self.dict.get(*args) def has_key(self, key): return self.dict.has_key(key) def items(self): return list(self.dict.items()) def iteritems(self): return list(self.dict.items()) def iterkeys(self): return list(self.dict.keys()) def itervalues(self): return list(self.dict.values()) def keys(self): return list(self.dict.keys()) def values(self): return list(self.dict.values()) # functions cdef int _str_to_pj_str(object string, pj_str_t *pj_str) except -1: #print('Convert %s (%s)' % (string, type(string))) pj_str.ptr = PyBytes_AsString(string) pj_str.slen = len(string) cdef object _pj_str_to_bytes(pj_str_t pj_str): return PyBytes_FromStringAndSize(pj_str.ptr, pj_str.slen) cdef object _pj_str_to_str(pj_str_t pj_str): return PyBytes_FromStringAndSize(pj_str.ptr, pj_str.slen).decode() cdef object _pj_buf_len_to_str(object buf, int buf_len): return PyBytes_FromStringAndSize(buf, buf_len) cdef object _buf_to_str(object buf): - return PyBytes_FromString(buf) + return PyBytes_FromString(buf).decode() cdef object _str_as_str(object string): return PyBytes_AsString(string) cdef object _str_as_size(object string): return PyBytes_Size(string) cdef object _pj_status_to_str(int status): cdef char buf[PJ_ERR_MSG_SIZE] return _pj_str_to_str(pj_strerror(status, buf, PJ_ERR_MSG_SIZE)) cdef object _pj_status_to_def(int status): return _re_pj_status_str_def.match(_pj_status_to_str(status)).group(1) cdef dict _pjsip_param_to_dict(pjsip_param *param_list): cdef pjsip_param *param cdef dict retval = dict() param = ( param_list).next while param != param_list: if param.value.slen == 0: retval[_pj_str_to_str(param.name)] = None else: retval[_pj_str_to_str(param.name)] = _pj_str_to_str(param.value) param = ( param).next return retval cdef int _dict_to_pjsip_param(object params, pjsip_param *param_list, pj_pool_t *pool): cdef pjsip_param *param = NULL for name, value in params.iteritems(): param = pj_pool_alloc(pool, sizeof(pjsip_param)) if param == NULL: return -1 _str_to_pj_str(name, ¶m.name) if value is None: param.value.slen = 0 else: _str_to_pj_str(value, ¶m.value) pj_list_insert_after( param_list, param) return 0 cdef int _pjsip_msg_to_dict(pjsip_msg *msg, dict info_dict) except -1: cdef pjsip_msg_body *body cdef pjsip_hdr *header cdef pjsip_generic_array_hdr *array_header cdef pjsip_ctype_hdr *ctype_header cdef pjsip_cseq_hdr *cseq_header cdef char *buf cdef int buf_len, i, status headers = {} header = ( &msg.hdr).next while header != &msg.hdr: header_name = _pj_str_to_str(header.name) header_data = None multi_header = False if header_name in ("Accept", "Allow", "Require", "Supported", "Unsupported", "Allow-Events"): array_header = header header_data = [] for i from 0 <= i < array_header.count: pass # TODO crash here #header_data.append(_pj_str_to_str(array_header.values[i])) elif header_name == "Contact": multi_header = True header_data = FrozenContactHeader_create( header) elif header_name == "Content-Length": header_data = ( header).len elif header_name == "Content-Type": header_data = FrozenContentTypeHeader_create( header) elif header_name == "CSeq": cseq_header = header hvalue = _pj_str_to_str(cseq_header.method.name) header_data = (cseq_header.cseq, hvalue) elif header_name in ("Expires", "Max-Forwards", "Min-Expires"): header_data = ( header).ivalue elif header_name == "From": header_data = FrozenFromHeader_create( header) elif header_name == "To": header_data = FrozenToHeader_create( header) elif header_name == "Route": multi_header = True header_data = FrozenRouteHeader_create( header) elif header_name == "Reason": value = _pj_str_to_str((header).hvalue) protocol, sep, params_str = value.partition(';') params = frozendict([(name, value or None) for name, sep, value in [param.partition('=') for param in params_str.split(';')]]) header_data = FrozenReasonHeader(protocol, params) elif header_name == "Record-Route": multi_header = True header_data = FrozenRecordRouteHeader_create( header) elif header_name == "Retry-After": header_data = FrozenRetryAfterHeader_create( header) elif header_name == "Via": multi_header = True header_data = FrozenViaHeader_create( header) elif header_name == "Warning": match = _re_warning_hdr.match(_pj_str_to_str((header).hvalue)) if match is not None: warning_params = match.groupdict() warning_params['code'] = int(warning_params['code']) header_data = FrozenWarningHeader(**warning_params) elif header_name == "Event": header_data = FrozenEventHeader_create( header) elif header_name == "Subscription-State": header_data = FrozenSubscriptionStateHeader_create( header) elif header_name == "Refer-To": header_data = FrozenReferToHeader_create( header) elif header_name == "Subject": header_data = FrozenSubjectHeader_create( header) elif header_name == "Replaces": header_data = FrozenReplacesHeader_create( header) # skip the following headers: elif header_name not in ("Authorization", "Proxy-Authenticate", "Proxy-Authorization", "WWW-Authenticate"): hvalue = ( header).hvalue header_value = _pj_str_to_str(hvalue) header_data = FrozenHeader(header_name, header_value) if header_data is not None: if multi_header: headers.setdefault(header_name, []).append(header_data) else: if header_name not in headers: headers[header_name] = header_data header = ( header).next info_dict["headers"] = headers body = msg.body if body == NULL: info_dict["body"] = None else: status = pjsip_print_body(body, &buf, &buf_len) if status != 0: info_dict["body"] = None else: info_dict["body"] = _pj_buf_len_to_str(buf, buf_len).decode() if msg.type == PJSIP_REQUEST_MSG: info_dict["method"] = _pj_str_to_str(msg.line.req.method.name) # You need to call pjsip_uri_get_uri on the request URI if the message is for transmitting, # but it isn't required if message is one received. Otherwise, a seg fault occurs. Don't ask. info_dict["request_uri"] = FrozenSIPURI_create(pjsip_uri_get_uri(msg.line.req.uri)) else: info_dict["code"] = msg.line.status.code info_dict["reason"] = _pj_str_to_str(msg.line.status.reason) return 0 cdef int _is_valid_ip(int af, object ip) except -1: cdef char buf[16] cdef pj_str_t src cdef int status _str_to_pj_str(ip, &src) status = pj_inet_pton(af, &src, buf) if status == 0: return 1 else: return 0 cdef int _get_ip_version(object ip) except -1: if _is_valid_ip(pj_AF_INET(), ip): return pj_AF_INET() elif _is_valid_ip(pj_AF_INET6(), ip): return pj_AF_INET() else: return 0 cdef int _add_headers_to_tdata(pjsip_tx_data *tdata, object headers) except -1: cdef pj_str_t name_pj, value_pj cdef pjsip_hdr *hdr for header in headers: hb = header.name.encode() bb = header.body.encode() _str_to_pj_str(hb, &name_pj) _str_to_pj_str(bb, &value_pj) hdr = pjsip_generic_string_hdr_create(tdata.pool, &name_pj, &value_pj) pjsip_msg_add_hdr(tdata.msg, hdr) cdef int _remove_headers_from_tdata(pjsip_tx_data *tdata, object headers) except -1: cdef pj_str_t header_name_pj cdef pjsip_hdr *hdr for header in headers: _str_to_pj_str(header, &header_name_pj) hdr = pjsip_msg_find_remove_hdr_by_name(tdata.msg, &header_name_pj, NULL) cdef int _BaseRouteHeader_to_pjsip_route_hdr(BaseIdentityHeader header, pjsip_route_hdr *pj_header, pj_pool_t *pool) except -1: cdef pjsip_param *param cdef pjsip_sip_uri *sip_uri pjsip_route_hdr_init(NULL, pj_header) sip_uri = pj_pool_alloc(pool, sizeof(pjsip_sip_uri)) _BaseSIPURI_to_pjsip_sip_uri(header.uri, sip_uri, pool) pj_header.name_addr.uri = sip_uri if header.display_name: _str_to_pj_str(header.display_name, &pj_header.name_addr.display) _dict_to_pjsip_param(header.parameters, &pj_header.other_param, pool) return 0 cdef int _BaseSIPURI_to_pjsip_sip_uri(BaseSIPURI uri, pjsip_sip_uri *pj_uri, pj_pool_t *pool) except -1: cdef pjsip_param *param pjsip_sip_uri_init(pj_uri, uri.secure) if uri.user: _str_to_pj_str(uri.user.encode(), &pj_uri.user) if uri.password: _str_to_pj_str(uri.password.encode(), &pj_uri.passwd) if uri.host: _str_to_pj_str(uri.host.encode(), &pj_uri.host) if uri.port: pj_uri.port = uri.port for name, value in uri.parameters.iteritems(): #print('Parse parameter %s (%s): %s (%s)' % (name, type(name), value, type(value))) if value is not None: try: int(value) except ValueError: value = value.encode() name = name.encode() if name == "lr": pj_uri.lr_param = 1 elif name == "maddr": _str_to_pj_str(value, &pj_uri.maddr_param) elif name == "method": _str_to_pj_str(value, &pj_uri.method_param) elif name == "transport": _str_to_pj_str(value, &pj_uri.transport_param) elif name == "ttl": pj_uri.ttl_param = int(value) elif name == "user": _str_to_pj_str(value, &pj_uri.user_param) else: param = pj_pool_alloc(pool, sizeof(pjsip_param)) _str_to_pj_str(name, ¶m.name) if value is None: param.value.slen = 0 else: _str_to_pj_str(value, ¶m.value) pj_list_insert_after( &pj_uri.other_param, param) _dict_to_pjsip_param(uri.headers, &pj_uri.header_param, pool) return 0 def _get_device_name_encoding(): if sys.platform == 'win32': encoding = 'mbcs' elif sys.platform.startswith('linux2') and Version.parse(platform.release()) < Version(2,6,31): encoding = 'latin1' else: encoding = 'utf-8' return encoding _device_name_encoding = _get_device_name_encoding() def decode_device_name(device_name): # ignore decoding errors, some systems (I'm looking at you, OSX), seem to misbehave return device_name.decode(_device_name_encoding, 'ignore') # globals cdef object _re_pj_status_str_def = re.compile("^.*\((.*)\)$") cdef object _re_warning_hdr = re.compile('(?P[0-9]{3}) (?P.*?) "(?P.*?)"') sip_status_messages = SIPStatusMessages()