From f3ab456a17af1c89a6e3be4d20c5944853df1cb0 Mon Sep 17 00:00:00 2001 From: "David M. Lee" Date: Mon, 7 Jan 2013 14:24:28 -0600 Subject: Import pjproject-2.0.1 --- pjsip-apps/src/python/Makefile | 12 + pjsip-apps/src/python/_pjsua.c | 4605 +++++++++++++++++++++++++ pjsip-apps/src/python/_pjsua.def | 2 + pjsip-apps/src/python/_pjsua.h | 3508 +++++++++++++++++++ pjsip-apps/src/python/helper.mak | 17 + pjsip-apps/src/python/pjsua.py | 2862 +++++++++++++++ pjsip-apps/src/python/samples/call.py | 169 + pjsip-apps/src/python/samples/presence.py | 175 + pjsip-apps/src/python/samples/registration.py | 70 + pjsip-apps/src/python/samples/simplecall.py | 88 + pjsip-apps/src/python/setup-vc.py | 80 + pjsip-apps/src/python/setup.py | 113 + 12 files changed, 11701 insertions(+) create mode 100644 pjsip-apps/src/python/Makefile create mode 100644 pjsip-apps/src/python/_pjsua.c create mode 100644 pjsip-apps/src/python/_pjsua.def create mode 100644 pjsip-apps/src/python/_pjsua.h create mode 100644 pjsip-apps/src/python/helper.mak create mode 100644 pjsip-apps/src/python/pjsua.py create mode 100644 pjsip-apps/src/python/samples/call.py create mode 100644 pjsip-apps/src/python/samples/presence.py create mode 100644 pjsip-apps/src/python/samples/registration.py create mode 100644 pjsip-apps/src/python/samples/simplecall.py create mode 100644 pjsip-apps/src/python/setup-vc.py create mode 100644 pjsip-apps/src/python/setup.py (limited to 'pjsip-apps/src/python') diff --git a/pjsip-apps/src/python/Makefile b/pjsip-apps/src/python/Makefile new file mode 100644 index 0000000..19a7e81 --- /dev/null +++ b/pjsip-apps/src/python/Makefile @@ -0,0 +1,12 @@ +all: + python setup.py build + +clean distclean realclean: + python setup.py clean + rm -rf ./build + +install: + python setup.py $@ + +dep doc: + diff --git a/pjsip-apps/src/python/_pjsua.c b/pjsip-apps/src/python/_pjsua.c new file mode 100644 index 0000000..bc00189 --- /dev/null +++ b/pjsip-apps/src/python/_pjsua.c @@ -0,0 +1,4605 @@ +/* $Id: _pjsua.c 3999 2012-03-30 07:10:13Z bennylp $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "_pjsua.h" + +#define THIS_FILE "main.c" +#define POOL_SIZE 512 +#define SND_DEV_NUM 64 +#define SND_NAME_LEN 64 + +/* LIB BASE */ + +static PyObject* g_obj_log_cb; +static long g_thread_id; +static struct py_thread_desc +{ + struct py_thread_desc *next; + pj_thread_desc desc; +} *py_thread_desc; + +/* + * The global callback object. + */ +static PyObj_pjsua_callback * g_obj_callback; + +/* Set this to 1 if all threads are created by Python */ +#define NO_PJSIP_THREAD 1 + +#if NO_PJSIP_THREAD +# define ENTER_PYTHON() +# define LEAVE_PYTHON() +#else +# define ENTER_PYTHON() PyGILState_STATE state = PyGILState_Ensure() +# define LEAVE_PYTHON() PyGILState_Release(state) +#endif + + +static void clear_py_thread_desc(void) +{ + while (py_thread_desc) { + struct py_thread_desc *next = py_thread_desc->next; + free(py_thread_desc); + py_thread_desc = next; + } +} + + +/* + * cb_log_cb + * declares method for reconfiguring logging process for callback struct + */ +static void cb_log_cb(int level, const char *data, int len) +{ + + /* Ignore if this callback is called from alien thread context, + * or otherwise it will crash Python. + */ + if (pj_thread_local_get(g_thread_id) == 0) + return; + + if (PyCallable_Check(g_obj_log_cb)) { + PyObject *param_data; + + ENTER_PYTHON(); + + param_data = PyString_FromStringAndSize(data, len); + + PyObject_CallFunction( + g_obj_log_cb, + "iOi", + level, + param_data, + len, + NULL + ); + + Py_DECREF(param_data); + + LEAVE_PYTHON(); + } +} + +/* + * cb_on_call_state + * declares method on_call_state for callback struct + */ +static void cb_on_call_state(pjsua_call_id call_id, pjsip_event *e) +{ + PJ_UNUSED_ARG(e); + + if (PyCallable_Check(g_obj_callback->on_call_state)) { + PyObject * obj; + + ENTER_PYTHON(); + + obj = Py_BuildValue(""); + + PyObject_CallFunction( + g_obj_callback->on_call_state, + "iO", + call_id, + obj, + NULL + ); + + Py_DECREF(obj); + + LEAVE_PYTHON(); + } +} + + +/* + * cb_on_incoming_call + * declares method on_incoming_call for callback struct + */ +static void cb_on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id, + pjsip_rx_data *rdata) +{ + PJ_UNUSED_ARG(rdata); + + if (PyCallable_Check(g_obj_callback->on_incoming_call)) { + PyObject *obj; + + ENTER_PYTHON(); + + obj = Py_BuildValue(""); + + PyObject_CallFunction( + g_obj_callback->on_incoming_call, + "iiO", + acc_id, + call_id, + obj, + NULL + ); + + Py_DECREF(obj); + + LEAVE_PYTHON(); + } +} + + +/* + * cb_on_call_media_state + * declares method on_call_media_state for callback struct + */ +static void cb_on_call_media_state(pjsua_call_id call_id) +{ + if (PyCallable_Check(g_obj_callback->on_call_media_state)) { + + ENTER_PYTHON(); + + PyObject_CallFunction( + g_obj_callback->on_call_media_state, + "i", + call_id, + NULL + ); + + LEAVE_PYTHON(); + } +} + + +/* + * cb_on_dtmf_digit() + * Callback from PJSUA-LIB on receiving DTMF digit + */ +static void cb_on_dtmf_digit(pjsua_call_id call_id, int digit) +{ + if (PyCallable_Check(g_obj_callback->on_dtmf_digit)) { + char digit_str[10]; + + PyGILState_STATE state = PyGILState_Ensure(); + + pj_ansi_snprintf(digit_str, sizeof(digit_str), "%c", digit); + + PyObject_CallFunction( + g_obj_callback->on_dtmf_digit, + "is", + call_id, + digit_str, + NULL + ); + + PyGILState_Release(state); + } +} + + +/* + * Notify application on call being transfered. + * !modified @061206 + */ +static void cb_on_call_transfer_request(pjsua_call_id call_id, + const pj_str_t *dst, + pjsip_status_code *code) +{ + if (PyCallable_Check(g_obj_callback->on_call_transfer_request)) { + PyObject *ret, *param_dst; + int cd; + + ENTER_PYTHON(); + + param_dst = PyString_FromPJ(dst); + + ret = PyObject_CallFunction( + g_obj_callback->on_call_transfer_request, + "iOi", + call_id, + param_dst, + *code, + NULL + ); + + Py_DECREF(param_dst); + + if (ret != NULL) { + if (ret != Py_None) { + if (PyArg_Parse(ret,"i",&cd)) { + *code = cd; + } + } + Py_DECREF(ret); + } + + LEAVE_PYTHON(); + } +} + + +/* + * Notify application of the status of previously sent call + * transfer request. Application can monitor the status of the + * call transfer request, for example to decide whether to + * terminate existing call. + * !modified @061206 + */ +static void cb_on_call_transfer_status( pjsua_call_id call_id, + int status_code, + const pj_str_t *status_text, + pj_bool_t final, + pj_bool_t *p_cont) +{ + if (PyCallable_Check(g_obj_callback->on_call_transfer_status)) { + PyObject *ret, *param_reason; + + ENTER_PYTHON(); + + param_reason = PyString_FromPJ(status_text); + + ret = PyObject_CallFunction( + g_obj_callback->on_call_transfer_status, + "iiOii", + call_id, + status_code, + param_reason, + final, + *p_cont, + NULL + ); + + Py_DECREF(param_reason); + + if (ret != NULL) { + if (ret != Py_None) { + int cnt; + if (PyArg_Parse(ret,"i",&cnt)) { + *p_cont = cnt; + } + } + Py_DECREF(ret); + } + + LEAVE_PYTHON(); + } +} + + +/* + * Notify application about incoming INVITE with Replaces header. + * Application may reject the request by setting non-2xx code. + * !modified @061206 + */ +static void cb_on_call_replace_request( pjsua_call_id call_id, + pjsip_rx_data *rdata, + int *st_code, + pj_str_t *st_text) +{ + PJ_UNUSED_ARG(rdata); + + if (PyCallable_Check(g_obj_callback->on_call_replace_request)) { + PyObject *ret, *param_reason, *param_rdata; + int cd; + + ENTER_PYTHON(); + + param_reason = PyString_FromPJ(st_text); + param_rdata = Py_BuildValue(""); + + ret = PyObject_CallFunction( + g_obj_callback->on_call_replace_request, + "iOiO", + call_id, + param_rdata, + *st_code, + param_reason, + NULL + ); + + Py_DECREF(param_rdata); + Py_DECREF(param_reason); + + if (ret != NULL) { + if (ret != Py_None) { + PyObject * txt; + if (PyArg_ParseTuple(ret,"iO",&cd, &txt)) { + *st_code = cd; + *st_text = PyString_ToPJ(txt); + } + } + Py_DECREF(ret); + } + + LEAVE_PYTHON(); + } +} + + +/* + * Notify application that an existing call has been replaced with + * a new call. This happens when PJSUA-API receives incoming INVITE + * request with Replaces header. + */ +static void cb_on_call_replaced(pjsua_call_id old_call_id, + pjsua_call_id new_call_id) +{ + if (PyCallable_Check(g_obj_callback->on_call_replaced)) { + ENTER_PYTHON(); + + PyObject_CallFunction( + g_obj_callback->on_call_replaced, + "ii", + old_call_id, + new_call_id, + NULL + ); + + LEAVE_PYTHON(); + } +} + + +/* + * cb_on_reg_state + * declares method on_reg_state for callback struct + */ +static void cb_on_reg_state(pjsua_acc_id acc_id) +{ + if (PyCallable_Check(g_obj_callback->on_reg_state)) { + ENTER_PYTHON(); + + PyObject_CallFunction( + g_obj_callback->on_reg_state, + "i", + acc_id, + NULL + ); + + LEAVE_PYTHON(); + } +} + +/* + * cb_on_incoming_subscribe + */ +static void cb_on_incoming_subscribe( pjsua_acc_id acc_id, + pjsua_srv_pres *srv_pres, + pjsua_buddy_id buddy_id, + const pj_str_t *from, + pjsip_rx_data *rdata, + pjsip_status_code *code, + pj_str_t *reason, + pjsua_msg_data *msg_data) +{ + static char reason_buf[64]; + + PJ_UNUSED_ARG(rdata); + PJ_UNUSED_ARG(msg_data); + + if (PyCallable_Check(g_obj_callback->on_incoming_subscribe)) { + PyObject *ret, *param_from, *param_contact, *param_srv_pres; + pjsip_contact_hdr *contact_hdr; + pj_pool_t *pool = NULL; + + ENTER_PYTHON(); + + param_from = PyString_FromPJ(from); + param_srv_pres = PyLong_FromLong((long)srv_pres); + + contact_hdr = (pjsip_contact_hdr*) + pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, + NULL); + if (contact_hdr) { + char *contact; + int len; + + pool = pjsua_pool_create("pytmp", 512, 512); + contact = (char*) pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE+1); + len = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, contact_hdr->uri, + contact, PJSIP_MAX_URL_SIZE); + if (len < 1) + len = 0; + contact[len] = '\0'; + + param_contact = PyString_FromStringAndSize(contact, len); + } else { + param_contact = Py_BuildValue(""); + } + + ret = PyObject_CallFunction( + g_obj_callback->on_incoming_subscribe, + "iiOOO", + acc_id, + buddy_id, + param_from, + param_contact, + param_srv_pres, + NULL + ); + + if (pool) + pj_pool_release(pool); + + Py_DECREF(param_from); + Py_DECREF(param_contact); + Py_DECREF(param_srv_pres); + + if (ret && PyTuple_Check(ret)) { + if (PyTuple_Size(ret) >= 1) + *code = (int)PyInt_AsLong(PyTuple_GetItem(ret, 0)); + if (PyTuple_Size(ret) >= 2) { + if (PyTuple_GetItem(ret, 1) != Py_None) { + pj_str_t tmp; + tmp = PyString_ToPJ(PyTuple_GetItem(ret, 1)); + reason->ptr = reason_buf; + pj_strncpy(reason, &tmp, sizeof(reason_buf)); + } else { + reason->slen = 0; + } + } + Py_XDECREF(ret); + } else if (ret) { + Py_XDECREF(ret); + } + + LEAVE_PYTHON(); + } +} + +/* + * cb_on_buddy_state + * declares method on_buddy state for callback struct + */ +static void cb_on_buddy_state(pjsua_buddy_id buddy_id) +{ + if (PyCallable_Check(g_obj_callback->on_buddy_state)) { + ENTER_PYTHON(); + + PyObject_CallFunction( + g_obj_callback->on_buddy_state, + "i", + buddy_id, + NULL + ); + + LEAVE_PYTHON(); + } +} + +/* + * cb_on_pager + * declares method on_pager for callback struct + */ +static void cb_on_pager(pjsua_call_id call_id, const pj_str_t *from, + const pj_str_t *to, const pj_str_t *contact, + const pj_str_t *mime_type, const pj_str_t *body, + pjsip_rx_data *rdata, pjsua_acc_id acc_id) +{ + PJ_UNUSED_ARG(rdata); + + if (PyCallable_Check(g_obj_callback->on_pager)) { + PyObject *param_from, *param_to, *param_contact, *param_mime_type, + *param_body; + + ENTER_PYTHON(); + + param_from = PyString_FromPJ(from); + param_to = PyString_FromPJ(to); + param_contact = PyString_FromPJ(contact); + param_mime_type = PyString_FromPJ(mime_type); + param_body = PyString_FromPJ(body); + + PyObject_CallFunction( + g_obj_callback->on_pager, + "iOOOOOi", + call_id, + param_from, + param_to, + param_contact, + param_mime_type, + param_body, + acc_id, + NULL + ); + + Py_DECREF(param_body); + Py_DECREF(param_mime_type); + Py_DECREF(param_contact); + Py_DECREF(param_to); + Py_DECREF(param_from); + + LEAVE_PYTHON(); + } +} + + +/* + * cb_on_pager_status + * declares method on_pager_status for callback struct + */ +static void cb_on_pager_status(pjsua_call_id call_id, const pj_str_t *to, + const pj_str_t *body, void *user_data, + pjsip_status_code status, + const pj_str_t *reason, + pjsip_tx_data *tdata, + pjsip_rx_data *rdata, + pjsua_acc_id acc_id) +{ + if (PyCallable_Check(g_obj_callback->on_pager)) { + PyObject *param_call_id, *param_to, *param_body, + *param_user_data, *param_status, *param_reason, + *param_acc_id; + + ENTER_PYTHON(); + + PJ_UNUSED_ARG(tdata); + PJ_UNUSED_ARG(rdata); + + PyObject_CallFunctionObjArgs( + g_obj_callback->on_pager_status, + param_call_id = Py_BuildValue("i",call_id), + param_to = PyString_FromPJ(to), + param_body = PyString_FromPJ(body), + param_user_data = Py_BuildValue("i", user_data), + param_status = Py_BuildValue("i",status), + param_reason = PyString_FromPJ(reason), + param_acc_id = Py_BuildValue("i",acc_id), + NULL + ); + + Py_DECREF(param_call_id); + Py_DECREF(param_to); + Py_DECREF(param_body); + Py_DECREF(param_user_data); + Py_DECREF(param_status); + Py_DECREF(param_reason); + Py_DECREF(param_acc_id); + + LEAVE_PYTHON(); + } +} + + +/* + * cb_on_typing + * declares method on_typing for callback struct + */ +static void cb_on_typing(pjsua_call_id call_id, const pj_str_t *from, + const pj_str_t *to, const pj_str_t *contact, + pj_bool_t is_typing, pjsip_rx_data *rdata, + pjsua_acc_id acc_id) +{ + if (PyCallable_Check(g_obj_callback->on_typing)) { + PyObject *param_call_id, *param_from, *param_to, *param_contact, + *param_is_typing, *param_acc_id; + + ENTER_PYTHON(); + + PJ_UNUSED_ARG(rdata); + + PyObject_CallFunctionObjArgs( + g_obj_callback->on_typing, + param_call_id = Py_BuildValue("i",call_id), + param_from = PyString_FromPJ(from), + param_to = PyString_FromPJ(to), + param_contact = PyString_FromPJ(contact), + param_is_typing = Py_BuildValue("i",is_typing), + param_acc_id = Py_BuildValue("i",acc_id), + NULL + ); + + Py_DECREF(param_call_id); + Py_DECREF(param_from); + Py_DECREF(param_to); + Py_DECREF(param_contact); + Py_DECREF(param_is_typing); + Py_DECREF(param_acc_id); + + LEAVE_PYTHON(); + } +} + + +/* + * on_mwi_info + */ +static void cb_on_mwi_info(pjsua_acc_id acc_id, pjsua_mwi_info *mwi_info) +{ + if (PyCallable_Check(g_obj_callback->on_mwi_info)) { + PyObject *param_acc_id, *param_body; + pj_str_t body; + + ENTER_PYTHON(); + + body.ptr = mwi_info->rdata->msg_info.msg->body->data; + body.slen = mwi_info->rdata->msg_info.msg->body->len; + + PyObject_CallFunctionObjArgs( + g_obj_callback->on_mwi_info, + param_acc_id = Py_BuildValue("i",acc_id), + param_body = PyString_FromPJ(&body), + NULL + ); + + Py_DECREF(param_acc_id); + Py_DECREF(param_body); + + LEAVE_PYTHON(); + } +} + +/* + * translate_hdr + * internal function + * translate from hdr_list to pjsip_generic_string_hdr + */ +void translate_hdr(pj_pool_t *pool, pjsip_hdr *hdr, PyObject *py_hdr_list) +{ + pj_list_init(hdr); + + if (PyList_Check(py_hdr_list)) { + int i; + + for (i=0; i= 1) + hname = PyString_ToPJ(PyTuple_GetItem(tuple,0)); + else + hname.slen = 0; + if (PyTuple_Size(tuple) >= 2) + hvalue = PyString_ToPJ(PyTuple_GetItem(tuple,1)); + else + hvalue.slen = 0; + } else { + hname.ptr = ""; + hname.slen = 0; + hvalue.ptr = ""; + hvalue.slen = 0; + } + new_hdr = pjsip_generic_string_hdr_create(pool, &hname, &hvalue); + pj_list_push_back((pj_list_type *)hdr, (pj_list_type *)new_hdr); + } + } +} + +/* + * py_pjsua_thread_register + */ +static PyObject *py_pjsua_thread_register(PyObject *pSelf, PyObject *pArgs) +{ + pj_status_t status; + const char *name; + PyObject *py_desc; + pj_thread_t *thread; + struct py_thread_desc *thread_desc; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "sO", &name, &py_desc)) { + return NULL; + } + thread_desc = (struct py_thread_desc*) + malloc(sizeof(struct py_thread_desc)); + thread_desc->next = py_thread_desc; + py_thread_desc = thread_desc; + + status = pj_thread_register(name, thread_desc->desc, &thread); + + if (status == PJ_SUCCESS) + status = pj_thread_local_set(g_thread_id, (void*)1); + + return Py_BuildValue("i",status); +} + +/* + * py_pjsua_logging_config_default + */ +static PyObject *py_pjsua_logging_config_default(PyObject *pSelf, + PyObject *pArgs) +{ + PyObj_pjsua_logging_config *obj; + pjsua_logging_config cfg; + + PJ_UNUSED_ARG(pSelf); + PJ_UNUSED_ARG(pArgs); + + pjsua_logging_config_default(&cfg); + obj = (PyObj_pjsua_logging_config*) + PyObj_pjsua_logging_config_new(&PyTyp_pjsua_logging_config, + NULL, NULL); + PyObj_pjsua_logging_config_import(obj, &cfg); + + return (PyObject*)obj; +} + + +/* + * py_pjsua_config_default + */ +static PyObject *py_pjsua_config_default(PyObject *pSelf, PyObject *pArgs) +{ + PyObj_pjsua_config *obj; + pjsua_config cfg; + + PJ_UNUSED_ARG(pSelf); + PJ_UNUSED_ARG(pArgs); + + pjsua_config_default(&cfg); + obj = (PyObj_pjsua_config *) PyObj_pjsua_config_new(&PyTyp_pjsua_config, + NULL, NULL); + PyObj_pjsua_config_import(obj, &cfg); + + return (PyObject*)obj; +} + + +/* + * py_pjsua_media_config_default + */ +static PyObject * py_pjsua_media_config_default(PyObject *pSelf, + PyObject *pArgs) +{ + PyObj_pjsua_media_config *obj; + pjsua_media_config cfg; + + PJ_UNUSED_ARG(pSelf); + PJ_UNUSED_ARG(pArgs); + + pjsua_media_config_default(&cfg); + obj = (PyObj_pjsua_media_config *) + PyType_GenericNew(&PyTyp_pjsua_media_config, NULL, NULL); + PyObj_pjsua_media_config_import(obj, &cfg); + + return (PyObject *)obj; +} + + +/* + * py_pjsua_msg_data_init + */ +static PyObject *py_pjsua_msg_data_init(PyObject *pSelf, PyObject *pArgs) +{ + PJ_UNUSED_ARG(pSelf); + PJ_UNUSED_ARG(pArgs); + + return (PyObject *)PyObj_pjsua_msg_data_new(&PyTyp_pjsua_msg_data, + NULL, NULL); +} + + +/* + * py_pjsua_reconfigure_logging + */ +static PyObject *py_pjsua_reconfigure_logging(PyObject *pSelf, + PyObject *pArgs) +{ + PyObject *logObj; + pj_status_t status; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "O", &logObj)) { + return NULL; + } + + if (logObj != Py_None) { + PyObj_pjsua_logging_config *log; + pjsua_logging_config cfg; + + log = (PyObj_pjsua_logging_config*)logObj; + cfg.msg_logging = log->msg_logging; + cfg.level = log->level; + cfg.console_level = log->console_level; + cfg.decor = log->decor; + cfg.log_filename = PyString_ToPJ(log->log_filename); + Py_XDECREF(g_obj_log_cb); + g_obj_log_cb = log->cb; + Py_INCREF(g_obj_log_cb); + cfg.cb = &cb_log_cb; + status = pjsua_reconfigure_logging(&cfg); + } else { + status = pjsua_reconfigure_logging(NULL); + } + + return Py_BuildValue("i",status); +} + + +/* + * py_pjsua_perror + */ +static PyObject *py_pjsua_perror(PyObject *pSelf, PyObject *pArgs) +{ + const char *sender; + const char *title; + pj_status_t status; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "ssi", &sender, &title, &status)) { + return NULL; + } + + pjsua_perror(sender, title, status); + + return Py_BuildValue(""); +} + + +/* + * py_pjsua_create + */ +static PyObject *py_pjsua_create(PyObject *pSelf, PyObject *pArgs) +{ + pj_status_t status; + + PJ_UNUSED_ARG(pSelf); + PJ_UNUSED_ARG(pArgs); + + status = pjsua_create(); + + if (status == PJ_SUCCESS) { + status = pj_thread_local_alloc(&g_thread_id); + if (status == PJ_SUCCESS) + status = pj_thread_local_set(g_thread_id, (void*)1); + + pj_atexit(&clear_py_thread_desc); + } + + return Py_BuildValue("i",status); +} + + +/* + * py_pjsua_init + */ +static PyObject *py_pjsua_init(PyObject *pSelf, PyObject *pArgs) +{ + pj_status_t status; + PyObject *o_ua_cfg, *o_log_cfg, *o_media_cfg; + pjsua_config cfg_ua, *p_cfg_ua; + pjsua_logging_config cfg_log, *p_cfg_log; + pjsua_media_config cfg_media, *p_cfg_media; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "OOO", &o_ua_cfg, &o_log_cfg, &o_media_cfg)) { + return NULL; + } + + pjsua_config_default(&cfg_ua); + pjsua_logging_config_default(&cfg_log); + pjsua_media_config_default(&cfg_media); + + if (o_ua_cfg != Py_None) { + PyObj_pjsua_config *obj_ua_cfg = (PyObj_pjsua_config*)o_ua_cfg; + + PyObj_pjsua_config_export(&cfg_ua, obj_ua_cfg); + + Py_XDECREF(g_obj_callback); + g_obj_callback = obj_ua_cfg->cb; + Py_INCREF(g_obj_callback); + + cfg_ua.cb.on_call_state = &cb_on_call_state; + cfg_ua.cb.on_incoming_call = &cb_on_incoming_call; + cfg_ua.cb.on_call_media_state = &cb_on_call_media_state; + cfg_ua.cb.on_dtmf_digit = &cb_on_dtmf_digit; + cfg_ua.cb.on_call_transfer_request = &cb_on_call_transfer_request; + cfg_ua.cb.on_call_transfer_status = &cb_on_call_transfer_status; + cfg_ua.cb.on_call_replace_request = &cb_on_call_replace_request; + cfg_ua.cb.on_call_replaced = &cb_on_call_replaced; + cfg_ua.cb.on_reg_state = &cb_on_reg_state; + cfg_ua.cb.on_incoming_subscribe = &cb_on_incoming_subscribe; + cfg_ua.cb.on_buddy_state = &cb_on_buddy_state; + cfg_ua.cb.on_pager2 = &cb_on_pager; + cfg_ua.cb.on_pager_status2 = &cb_on_pager_status; + cfg_ua.cb.on_typing2 = &cb_on_typing; + cfg_ua.cb.on_mwi_info = &cb_on_mwi_info; + + p_cfg_ua = &cfg_ua; + + } else { + p_cfg_ua = NULL; + } + + if (o_log_cfg != Py_None) { + PyObj_pjsua_logging_config * obj_log; + + obj_log = (PyObj_pjsua_logging_config *)o_log_cfg; + + PyObj_pjsua_logging_config_export(&cfg_log, obj_log); + + Py_XDECREF(g_obj_log_cb); + g_obj_log_cb = obj_log->cb; + Py_INCREF(g_obj_log_cb); + + cfg_log.cb = &cb_log_cb; + p_cfg_log = &cfg_log; + + } else { + p_cfg_log = NULL; + } + + if (o_media_cfg != Py_None) { + PyObj_pjsua_media_config_export(&cfg_media, + (PyObj_pjsua_media_config*)o_media_cfg); + p_cfg_media = &cfg_media; + + } else { + p_cfg_media = NULL; + } + + status = pjsua_init(p_cfg_ua, p_cfg_log, p_cfg_media); + + return Py_BuildValue("i", status); +} + + +/* + * py_pjsua_start + */ +static PyObject *py_pjsua_start(PyObject *pSelf, PyObject *pArgs) +{ + pj_status_t status; + + PJ_UNUSED_ARG(pSelf); + PJ_UNUSED_ARG(pArgs); + + status = pjsua_start(); + + return Py_BuildValue("i", status); +} + + +/* + * py_pjsua_destroy + */ +static PyObject *py_pjsua_destroy(PyObject *pSelf, PyObject *pArgs) +{ + pj_status_t status; + + PJ_UNUSED_ARG(pSelf); + PJ_UNUSED_ARG(pArgs); + + status = pjsua_destroy(); + + return Py_BuildValue("i", status); +} + + +/* + * py_pjsua_handle_events + */ +static PyObject *py_pjsua_handle_events(PyObject *pSelf, PyObject *pArgs) +{ + int ret; + int msec; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "i", &msec)) { + return NULL; + } + + if (msec < 0) + msec = 0; + +#if !NO_PJSIP_THREAD + /* Since handle_events() will block, we must wrap it with ALLOW_THREADS + * construct, or otherwise many Python blocking functions (such as + * time.sleep(), readline(), etc.) may hang/block indefinitely. + * See http://www.python.org/doc/current/api/threads.html for more info. + */ + Py_BEGIN_ALLOW_THREADS +#endif + + ret = pjsua_handle_events(msec); + +#if !NO_PJSIP_THREAD + Py_END_ALLOW_THREADS +#endif + + return Py_BuildValue("i", ret); +} + + +/* + * py_pjsua_verify_sip_url + */ +static PyObject *py_pjsua_verify_sip_url(PyObject *pSelf, PyObject *pArgs) +{ + pj_status_t status; + const char *url; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "s", &url)) { + return NULL; + } + + status = pjsua_verify_sip_url(url); + + return Py_BuildValue("i", status); +} + + +/* + * function doc + */ + +static char pjsua_thread_register_doc[] = + "int _pjsua.thread_register(string name, int[] desc)"; +static char pjsua_perror_doc[] = + "void _pjsua.perror (string sender, string title, int status) " + "Display error message for the specified error code. Parameters: " + "sender: The log sender field; " + "title: Message title for the error; " + "status: Status code."; + +static char pjsua_create_doc[] = + "int _pjsua.create (void) " + "Instantiate pjsua application. Application " + "must call this function before calling any other functions, to make sure " + "that the underlying libraries are properly initialized. Once this " + "function has returned success, application must call pjsua_destroy() " + "before quitting."; + +static char pjsua_init_doc[] = + "int _pjsua.init (_pjsua.Config obj_ua_cfg, " + "_pjsua.Logging_Config log_cfg, _pjsua.Media_Config media_cfg) " + "Initialize pjsua with the specified settings. All the settings are " + "optional, and the default values will be used when the config is not " + "specified. Parameters: " + "obj_ua_cfg : User agent configuration; " + "log_cfg : Optional logging configuration; " + "media_cfg : Optional media configuration."; + +static char pjsua_start_doc[] = + "int _pjsua.start (void) " + "Application is recommended to call this function after all " + "initialization is done, so that the library can do additional checking " + "set up additional"; + +static char pjsua_destroy_doc[] = + "int _pjsua.destroy (void) " + "Destroy pjsua This function must be called once PJSUA is created. To " + "make it easier for application, application may call this function " + "several times with no danger."; + +static char pjsua_handle_events_doc[] = + "int _pjsua.handle_events (int msec_timeout) " + "Poll pjsua for events, and if necessary block the caller thread for the " + "specified maximum interval (in miliseconds) Parameters: " + "msec_timeout: Maximum time to wait, in miliseconds. " + "Returns: The number of events that have been handled during the poll. " + "Negative value indicates error, and application can retrieve the error " + "as (err = -return_value)."; + +static char pjsua_verify_sip_url_doc[] = + "int _pjsua.verify_sip_url (string c_url) " + "Verify that valid SIP url is given Parameters: " + "c_url: The URL, as NULL terminated string."; + +static char pjsua_reconfigure_logging_doc[] = + "int _pjsua.reconfigure_logging (_pjsua.Logging_Config c) " + "Application can call this function at any time (after pjsua_create(), of " + "course) to change logging settings. Parameters: " + "c: Logging configuration."; + +static char pjsua_logging_config_default_doc[] = + "_pjsua.Logging_Config _pjsua.logging_config_default () " + "Use this function to initialize logging config."; + +static char pjsua_config_default_doc[] = + "_pjsua.Config _pjsua.config_default (). Use this function to " + "initialize pjsua config. "; + +static char pjsua_media_config_default_doc[] = + "_pjsua.Media_Config _pjsua.media_config_default (). " + "Use this function to initialize media config."; + +static char pjsua_msg_data_init_doc[] = + "_pjsua.Msg_Data void _pjsua.msg_data_init () " + "Initialize message data "; + + +/* END OF LIB BASE */ + +/* LIB TRANSPORT */ + +/* + * py_pjsua_transport_config_default + */ +static PyObject *py_pjsua_transport_config_default(PyObject *pSelf, + PyObject *pArgs) +{ + PyObj_pjsua_transport_config *obj; + pjsua_transport_config cfg; + + PJ_UNUSED_ARG(pSelf); + PJ_UNUSED_ARG(pArgs); + + pjsua_transport_config_default(&cfg); + obj = (PyObj_pjsua_transport_config*) + PyObj_pjsua_transport_config_new(&PyTyp_pjsua_transport_config, + NULL, NULL); + PyObj_pjsua_transport_config_import(obj, &cfg); + + return (PyObject *)obj; +} + +/* + * py_pjsua_transport_create + */ +static PyObject *py_pjsua_transport_create(PyObject *pSelf, PyObject *pArgs) +{ + pj_status_t status; + int type; + PyObject *pCfg; + pjsua_transport_config cfg; + pjsua_transport_id id; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "iO", &type, &pCfg)) { + return NULL; + } + + if (pCfg != Py_None) { + PyObj_pjsua_transport_config *obj; + + obj = (PyObj_pjsua_transport_config*)pCfg; + PyObj_pjsua_transport_config_export(&cfg, obj); + status = pjsua_transport_create(type, &cfg, &id); + } else { + status = pjsua_transport_create(type, NULL, &id); + } + + + return Py_BuildValue("ii", status, id); +} + +/* + * py_pjsua_enum_transports + */ +static PyObject *py_pjsua_enum_transports(PyObject *pSelf, PyObject *pArgs) +{ + pj_status_t status; + PyObject *list; + pjsua_transport_id id[PJSIP_MAX_TRANSPORTS]; + unsigned c, i; + + PJ_UNUSED_ARG(pSelf); + PJ_UNUSED_ARG(pArgs); + + c = PJ_ARRAY_SIZE(id); + status = pjsua_enum_transports(id, &c); + + list = PyList_New(c); + for (i = 0; i < c; i++) { + PyList_SetItem(list, i, Py_BuildValue("i", id[i])); + } + + return (PyObject*)list; +} + +/* + * py_pjsua_transport_get_info + * !modified @ 051206 + */ +static PyObject *py_pjsua_transport_get_info(PyObject *pSelf, PyObject *pArgs) +{ + pj_status_t status; + int id; + pjsua_transport_info info; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "i", &id)) { + return NULL; + } + + status = pjsua_transport_get_info(id, &info); + if (status == PJ_SUCCESS) { + PyObj_pjsua_transport_info *obj; + obj = (PyObj_pjsua_transport_info *) + PyObj_pjsua_transport_info_new(&PyTyp_pjsua_transport_info, + NULL, NULL); + PyObj_pjsua_transport_info_import(obj, &info); + return (PyObject*)obj; + } else { + return Py_BuildValue(""); + } +} + +/* + * py_pjsua_transport_set_enable + */ +static PyObject *py_pjsua_transport_set_enable(PyObject *pSelf, + PyObject *pArgs) +{ + pj_status_t status; + int id; + int enabled; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "ii", &id, &enabled)) { + return NULL; + } + status = pjsua_transport_set_enable(id, enabled); + + return Py_BuildValue("i", status); +} + +/* + * py_pjsua_transport_close + */ +static PyObject *py_pjsua_transport_close(PyObject *pSelf, PyObject *pArgs) +{ + pj_status_t status; + int id; + int force; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "ii", &id, &force)) { + return NULL; + } + status = pjsua_transport_close(id, force); + + return Py_BuildValue("i", status); +} + +static char pjsua_transport_config_default_doc[] = + "_pjsua.Transport_Config _pjsua.transport_config_default () " + "Call this function to initialize UDP config with default values."; +static char pjsua_transport_create_doc[] = + "int, int _pjsua.transport_create (int type, " + "_pjsua.Transport_Config cfg) " + "Create SIP transport."; +static char pjsua_enum_transports_doc[] = + "int[] _pjsua.enum_transports () " + "Enumerate all transports currently created in the system."; +static char pjsua_transport_get_info_doc[] = + "void _pjsua.transport_get_info " + "(_pjsua.Transport_ID id, _pjsua.Transport_Info info) " + "Get information about transports."; +static char pjsua_transport_set_enable_doc[] = + "void _pjsua.transport_set_enable " + "(_pjsua.Transport_ID id, int enabled) " + "Disable a transport or re-enable it. " + "By default transport is always enabled after it is created. " + "Disabling a transport does not necessarily close the socket, " + "it will only discard incoming messages and prevent the transport " + "from being used to send outgoing messages."; +static char pjsua_transport_close_doc[] = + "void _pjsua.transport_close (_pjsua.Transport_ID id, int force) " + "Close the transport. If transport is forcefully closed, " + "it will be immediately closed, and any pending transactions " + "that are using the transport may not terminate properly. " + "Otherwise, the system will wait until all transactions are closed " + "while preventing new users from using the transport, and will close " + "the transport when it is safe to do so."; + +/* END OF LIB TRANSPORT */ + +/* LIB ACCOUNT */ + + +/* + * py_pjsua_acc_config_default + */ +static PyObject *py_pjsua_acc_config_default(PyObject *pSelf, PyObject *pArgs) +{ + PyObj_pjsua_acc_config *obj; + pjsua_acc_config cfg; + + PJ_UNUSED_ARG(pSelf); + PJ_UNUSED_ARG(pArgs); + + if (!PyArg_ParseTuple(pArgs, "")) { + return NULL; + } + + pjsua_acc_config_default(&cfg); + obj = (PyObj_pjsua_acc_config *) + PyObj_pjsua_acc_config_new(&PyTyp_pjsua_acc_config, + NULL, NULL); + PyObj_pjsua_acc_config_import(obj, &cfg); + return (PyObject *)obj; +} + +/* + * py_pjsua_acc_get_count + */ +static PyObject *py_pjsua_acc_get_count(PyObject *pSelf, PyObject *pArgs) +{ + int count; + + PJ_UNUSED_ARG(pSelf); + PJ_UNUSED_ARG(pArgs); + + count = pjsua_acc_get_count(); + return Py_BuildValue("i", count); +} + +/* + * py_pjsua_acc_is_valid + */ +static PyObject *py_pjsua_acc_is_valid(PyObject *pSelf, PyObject *pArgs) +{ + int id; + int is_valid; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "i", &id)) { + return NULL; + } + + is_valid = pjsua_acc_is_valid(id); + return Py_BuildValue("i", is_valid); +} + +/* + * py_pjsua_acc_set_default + */ +static PyObject *py_pjsua_acc_set_default(PyObject *pSelf, PyObject *pArgs) +{ + int id; + int status; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "i", &id)) { + return NULL; + } + status = pjsua_acc_set_default(id); + + return Py_BuildValue("i", status); +} + +/* + * py_pjsua_acc_get_default + */ +static PyObject *py_pjsua_acc_get_default(PyObject *pSelf, PyObject *pArgs) +{ + int id; + + PJ_UNUSED_ARG(pSelf); + PJ_UNUSED_ARG(pArgs); + + id = pjsua_acc_get_default(); + + return Py_BuildValue("i", id); +} + +/* + * py_pjsua_acc_add + */ +static PyObject *py_pjsua_acc_add(PyObject *pSelf, PyObject *pArgs) +{ + int is_default; + PyObject *pCfg; + int acc_id; + int status; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "Oi", &pCfg, &is_default)) { + return NULL; + } + + if (pCfg != Py_None) { + pjsua_acc_config cfg; + PyObj_pjsua_acc_config *ac; + + pjsua_acc_config_default(&cfg); + ac = (PyObj_pjsua_acc_config *)pCfg; + PyObj_pjsua_acc_config_export(&cfg, ac); + status = pjsua_acc_add(&cfg, is_default, &acc_id); + } else { + status = PJ_EINVAL; + acc_id = PJSUA_INVALID_ID; + } + + return Py_BuildValue("ii", status, acc_id); +} + +/* + * py_pjsua_acc_add_local + */ +static PyObject *py_pjsua_acc_add_local(PyObject *pSelf, PyObject *pArgs) +{ + int is_default; + int tid; + int acc_id; + int status; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "ii", &tid, &is_default)) { + return NULL; + } + + status = pjsua_acc_add_local(tid, is_default, &acc_id); + + return Py_BuildValue("ii", status, acc_id); +} + +/* + * py_pjsua_acc_set_user_data + */ +static PyObject *py_pjsua_acc_set_user_data(PyObject *pSelf, PyObject *pArgs) +{ + int acc_id; + PyObject *pUserData, *old_user_data; + int status; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "iO", &acc_id, &pUserData)) { + return NULL; + } + + old_user_data = (PyObject*) pjsua_acc_get_user_data(acc_id); + + status = pjsua_acc_set_user_data(acc_id, (void*)pUserData); + + if (status == PJ_SUCCESS) { + Py_XINCREF(pUserData); + Py_XDECREF(old_user_data); + } + + return Py_BuildValue("i", status); +} + +/* + * py_pjsua_acc_get_user_data + */ +static PyObject *py_pjsua_acc_get_user_data(PyObject *pSelf, PyObject *pArgs) +{ + int acc_id; + PyObject *user_data; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "i", &acc_id)) { + return NULL; + } + + user_data = (PyObject*) pjsua_acc_get_user_data(acc_id); + + return user_data ? Py_BuildValue("O", user_data) : Py_BuildValue(""); +} + +/* + * py_pjsua_acc_del + */ +static PyObject *py_pjsua_acc_del(PyObject *pSelf, PyObject *pArgs) +{ + int acc_id; + PyObject *user_data; + int status; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "i", &acc_id)) { + return NULL; + } + + user_data = (PyObject*) pjsua_acc_get_user_data(acc_id); + Py_XDECREF(user_data); + + status = pjsua_acc_del(acc_id); + + return Py_BuildValue("i", status); +} + +/* + * py_pjsua_acc_modify + */ +static PyObject *py_pjsua_acc_modify(PyObject *pSelf, PyObject *pArgs) +{ + PyObject *pCfg; + PyObj_pjsua_acc_config * ac; + int acc_id; + int status; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "iO", &acc_id, &pCfg)) { + return NULL; + } + + if (pCfg != Py_None) { + pjsua_acc_config cfg; + + pjsua_acc_config_default(&cfg); + ac = (PyObj_pjsua_acc_config*)pCfg; + PyObj_pjsua_acc_config_export(&cfg, ac); + + status = pjsua_acc_modify(acc_id, &cfg); + } else { + status = PJ_EINVAL; + } + return Py_BuildValue("i", status); +} + +/* + * py_pjsua_acc_set_online_status + */ +static PyObject *py_pjsua_acc_set_online_status(PyObject *pSelf, + PyObject *pArgs) +{ + int is_online; + int acc_id; + int status; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "ii", &acc_id, &is_online)) { + return NULL; + } + + status = pjsua_acc_set_online_status(acc_id, is_online); + + return Py_BuildValue("i", status); +} + +/* + * py_pjsua_acc_set_online_status2 + */ +static PyObject *py_pjsua_acc_set_online_status2(PyObject *pSelf, + PyObject *pArgs) +{ + int is_online; + int acc_id; + int activity_id; + const char *activity_text = NULL; + const char *rpid_id = NULL; + pjrpid_element rpid; + pj_status_t status; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "iiiss", &acc_id, &is_online, + &activity_id, &activity_text, &rpid_id)) + { + return NULL; + } + + pj_bzero(&rpid, sizeof(rpid)); + rpid.type = PJRPID_ELEMENT_TYPE_PERSON; + rpid.activity = activity_id; + if (activity_text) + rpid.note = pj_str((char*)activity_text); + + if (rpid_id) + rpid.id = pj_str((char*)rpid_id); + + status = pjsua_acc_set_online_status2(acc_id, is_online, &rpid); + + return Py_BuildValue("i", status); +} + +/* + * py_pjsua_acc_set_registration + */ +static PyObject *py_pjsua_acc_set_registration(PyObject *pSelf, + PyObject *pArgs) +{ + int renew; + int acc_id; + int status; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "ii", &acc_id, &renew)) { + return NULL; + } + + status = pjsua_acc_set_registration(acc_id, renew); + + return Py_BuildValue("i", status); +} + +/* + * py_pjsua_acc_get_info + */ +static PyObject *py_pjsua_acc_get_info(PyObject *pSelf, PyObject *pArgs) +{ + int acc_id; + PyObj_pjsua_acc_info * obj; + pjsua_acc_info info; + int status; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "i", &acc_id)) { + return NULL; + } + + status = pjsua_acc_get_info(acc_id, &info); + if (status == PJ_SUCCESS) { + obj = (PyObj_pjsua_acc_info*) + PyObj_pjsua_acc_info_new(&PyTyp_pjsua_acc_info, NULL, NULL); + PyObj_pjsua_acc_info_import(obj, &info); + return (PyObject*)obj; + } else { + return Py_BuildValue(""); + } +} + +/* + * py_pjsua_enum_accs + */ +static PyObject *py_pjsua_enum_accs(PyObject *pSelf, PyObject *pArgs) +{ + pj_status_t status; + PyObject *list; + pjsua_acc_id id[PJSUA_MAX_ACC]; + unsigned c, i; + + PJ_UNUSED_ARG(pSelf); + PJ_UNUSED_ARG(pArgs); + + c = PJ_ARRAY_SIZE(id); + status = pjsua_enum_accs(id, &c); + if (status != PJ_SUCCESS) + c = 0; + + list = PyList_New(c); + for (i = 0; i < c; i++) { + PyList_SetItem(list, i, Py_BuildValue("i", id[i])); + } + + return (PyObject*)list; +} + +/* + * py_pjsua_acc_enum_info + */ +static PyObject *py_pjsua_acc_enum_info(PyObject *pSelf, PyObject *pArgs) +{ + pj_status_t status; + PyObject *list; + pjsua_acc_info info[PJSUA_MAX_ACC]; + unsigned c, i; + + PJ_UNUSED_ARG(pSelf); + PJ_UNUSED_ARG(pArgs); + + if (!PyArg_ParseTuple(pArgs, "")) { + return NULL; + } + + c = PJ_ARRAY_SIZE(info); + status = pjsua_acc_enum_info(info, &c); + if (status != PJ_SUCCESS) + c = 0; + + list = PyList_New(c); + for (i = 0; i < c; i++) { + PyObj_pjsua_acc_info *obj; + obj = (PyObj_pjsua_acc_info *) + PyObj_pjsua_acc_info_new(&PyTyp_pjsua_acc_info, NULL, NULL); + + PyObj_pjsua_acc_info_import(obj, &info[i]); + + PyList_SetItem(list, i, (PyObject*)obj); + } + + return (PyObject*)list; +} + +/* + * py_pjsua_acc_set_transport + */ +static PyObject *py_pjsua_acc_set_transport(PyObject *pSelf, PyObject *pArgs) +{ + int acc_id, transport_id; + int status; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "ii", &acc_id, &transport_id)) { + return NULL; + } + + status = pjsua_acc_set_transport(acc_id, transport_id); + + + return Py_BuildValue("i", status); +} + + +/* + * py_pjsua_acc_pres_notify + */ +static PyObject *py_pjsua_acc_pres_notify(PyObject *pSelf, + PyObject *pArgs) +{ + int acc_id, state; + PyObject *arg_pres, *arg_msg_data, *arg_reason; + void *srv_pres; + pjsua_msg_data msg_data; + pj_str_t reason; + pj_bool_t with_body; + pj_pool_t *pool = NULL; + int status; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "iOiOO", &acc_id, &arg_pres, + &state, &arg_reason, &arg_msg_data)) + { + return NULL; + } + + srv_pres = (void*) PyLong_AsLong(arg_pres); + with_body = (state != PJSIP_EVSUB_STATE_TERMINATED); + + if (arg_reason && PyString_Check(arg_reason)) { + reason = PyString_ToPJ(arg_reason); + } else { + reason = pj_str(""); + } + + pjsua_msg_data_init(&msg_data); + if (arg_msg_data && arg_msg_data != Py_None) { + PyObj_pjsua_msg_data *omd = (PyObj_pjsua_msg_data *)arg_msg_data; + msg_data.content_type = PyString_ToPJ(omd->content_type); + msg_data.msg_body = PyString_ToPJ(omd->msg_body); + pool = pjsua_pool_create("pytmp", POOL_SIZE, POOL_SIZE); + translate_hdr(pool, &msg_data.hdr_list, omd->hdr_list); + } + + status = pjsua_pres_notify(acc_id, (pjsua_srv_pres*)srv_pres, + (pjsip_evsub_state)state, NULL, + &reason, with_body, &msg_data); + + if (pool) { + pj_pool_release(pool); + } + + return Py_BuildValue("i", status); +} + +static char pjsua_acc_config_default_doc[] = + "_pjsua.Acc_Config _pjsua.acc_config_default () " + "Call this function to initialize account config with default values."; +static char pjsua_acc_get_count_doc[] = + "int _pjsua.acc_get_count () " + "Get number of current accounts."; +static char pjsua_acc_is_valid_doc[] = + "int _pjsua.acc_is_valid (int acc_id) " + "Check if the specified account ID is valid."; +static char pjsua_acc_set_default_doc[] = + "int _pjsua.acc_set_default (int acc_id) " + "Set default account to be used when incoming " + "and outgoing requests doesn't match any accounts."; +static char pjsua_acc_get_default_doc[] = + "int _pjsua.acc_get_default () " + "Get default account."; +static char pjsua_acc_add_doc[] = + "int, int _pjsua.acc_add (_pjsua.Acc_Config cfg, " + "int is_default) " + "Add a new account to pjsua. PJSUA must have been initialized " + "(with pjsua_init()) before calling this function."; +static char pjsua_acc_add_local_doc[] = + "int,int _pjsua.acc_add_local (int tid, " + "int is_default) " + "Add a local account. A local account is used to identify " + "local endpoint instead of a specific user, and for this reason, " + "a transport ID is needed to obtain the local address information."; +static char pjsua_acc_del_doc[] = + "int _pjsua.acc_del (int acc_id) " + "Delete account."; +static char pjsua_acc_modify_doc[] = + "int _pjsua.acc_modify (int acc_id, _pjsua.Acc_Config cfg) " + "Modify account information."; +static char pjsua_acc_set_online_status_doc[] = + "int _pjsua.acc_set_online_status2(int acc_id, int is_online) " + "Modify account's presence status to be advertised " + "to remote/presence subscribers."; +static char pjsua_acc_set_online_status2_doc[] = + "int _pjsua.acc_set_online_status (int acc_id, int is_online, " + "int activity_id, string activity_text) " + "Modify account's presence status to be advertised " + "to remote/presence subscribers."; +static char pjsua_acc_set_registration_doc[] = + "int _pjsua.acc_set_registration (int acc_id, int renew) " + "Update registration or perform unregistration."; +static char pjsua_acc_get_info_doc[] = + "_pjsua.Acc_Info _pjsua.acc_get_info (int acc_id) " + "Get account information."; +static char pjsua_enum_accs_doc[] = + "int[] _pjsua.enum_accs () " + "Enum accounts all account ids."; +static char pjsua_acc_enum_info_doc[] = + "_pjsua.Acc_Info[] _pjsua.acc_enum_info () " + "Enum accounts info."; + +/* END OF LIB ACCOUNT */ + +/* LIB BUDDY */ + + + +/* + * py_pjsua_buddy_config_default + */ +static PyObject *py_pjsua_buddy_config_default(PyObject *pSelf, + PyObject *pArgs) +{ + PyObj_pjsua_buddy_config *obj; + pjsua_buddy_config cfg; + + PJ_UNUSED_ARG(pSelf); + PJ_UNUSED_ARG(pArgs); + + pjsua_buddy_config_default(&cfg); + obj = (PyObj_pjsua_buddy_config *) + PyObj_pjsua_buddy_config_new(&PyTyp_pjsua_buddy_config, NULL, NULL); + PyObj_pjsua_buddy_config_import(obj, &cfg); + + return (PyObject *)obj; +} + +/* + * py_pjsua_get_buddy_count + */ +static PyObject *py_pjsua_get_buddy_count(PyObject *pSelf, PyObject *pArgs) +{ + PJ_UNUSED_ARG(pSelf); + PJ_UNUSED_ARG(pArgs); + + return Py_BuildValue("i", pjsua_get_buddy_count()); +} + +/* + * py_pjsua_buddy_is_valid + */ +static PyObject *py_pjsua_buddy_is_valid(PyObject *pSelf, PyObject *pArgs) +{ + int id; + int is_valid; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "i", &id)) { + return NULL; + } + is_valid = pjsua_buddy_is_valid(id); + + return Py_BuildValue("i", is_valid); +} + +/* + * py_pjsua_enum_buddies + */ +static PyObject *py_pjsua_enum_buddies(PyObject *pSelf, PyObject *pArgs) +{ + pj_status_t status; + PyObject *list; + pjsua_buddy_id id[PJSUA_MAX_BUDDIES]; + unsigned c, i; + + PJ_UNUSED_ARG(pSelf); + PJ_UNUSED_ARG(pArgs); + + c = PJ_ARRAY_SIZE(id); + status = pjsua_enum_buddies(id, &c); + if (status != PJ_SUCCESS) + c = 0; + + list = PyList_New(c); + for (i = 0; i < c; i++) { + PyList_SetItem(list, i, Py_BuildValue("i", id[i])); + } + + return (PyObject*)list; +} + +/* + * py_pjsua_buddy_find + */ +static PyObject *py_pjsua_buddy_find(PyObject *pSelf, PyObject *pArgs) +{ + PyObject *pURI; + pj_str_t uri; + pjsua_buddy_id buddy_id; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "O", &pURI)) { + return NULL; + } + + if (!PyString_Check(pURI)) + return Py_BuildValue("i", PJSUA_INVALID_ID); + + uri = PyString_ToPJ(pURI); + buddy_id = pjsua_buddy_find(&uri); + + return Py_BuildValue("i", buddy_id); +} + +/* + * py_pjsua_buddy_get_info + */ +static PyObject *py_pjsua_buddy_get_info(PyObject *pSelf, PyObject *pArgs) +{ + int buddy_id; + pjsua_buddy_info info; + int status; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "i", &buddy_id)) { + return NULL; + } + + status = pjsua_buddy_get_info(buddy_id, &info); + if (status == PJ_SUCCESS) { + PyObj_pjsua_buddy_info *obj; + + obj = (PyObj_pjsua_buddy_info *) + PyObj_pjsua_buddy_config_new(&PyTyp_pjsua_buddy_info, + NULL, NULL); + PyObj_pjsua_buddy_info_import(obj, &info); + return (PyObject*)obj; + } else { + return Py_BuildValue(""); + } +} + +/* + * py_pjsua_buddy_add + */ +static PyObject *py_pjsua_buddy_add(PyObject *pSelf, PyObject *pArgs) +{ + PyObject *pCfg; + int buddy_id; + int status; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "O", &pCfg)) { + return NULL; + } + + if (pCfg != Py_None) { + pjsua_buddy_config cfg; + PyObj_pjsua_buddy_config *bc; + + bc = (PyObj_pjsua_buddy_config *)pCfg; + + pjsua_buddy_config_default(&cfg); + PyObj_pjsua_buddy_config_export(&cfg, bc); + + status = pjsua_buddy_add(&cfg, &buddy_id); + + } else { + status = PJ_EINVAL; + buddy_id = PJSUA_INVALID_ID; + } + return Py_BuildValue("ii", status, buddy_id); +} + +/* + * py_pjsua_buddy_del + */ +static PyObject *py_pjsua_buddy_del(PyObject *pSelf, PyObject *pArgs) +{ + int buddy_id; + int status; + PyObject *user_data; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "i", &buddy_id)) { + return NULL; + } + + user_data = (PyObject*) pjsua_buddy_get_user_data(buddy_id); + Py_XDECREF(user_data); + + status = pjsua_buddy_del(buddy_id); + + return Py_BuildValue("i", status); +} + +/* + * py_pjsua_buddy_set_user_data + */ +static PyObject *py_pjsua_buddy_set_user_data(PyObject *pSelf, PyObject *pArgs) +{ + int buddy_id; + int status; + PyObject *user_data, *old_user_data; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "iO", &buddy_id, &user_data)) { + return NULL; + } + + if (!pjsua_buddy_is_valid(buddy_id)) { + return Py_BuildValue("i", 0); + } + + old_user_data = (PyObject*) pjsua_buddy_get_user_data(buddy_id); + + status = pjsua_buddy_set_user_data(buddy_id, (void*)user_data); + + if (status == PJ_SUCCESS) { + Py_XINCREF(user_data); + Py_XDECREF(old_user_data); + } + + return Py_BuildValue("i", status); +} + +/* + * py_pjsua_buddy_get_user_data + */ +static PyObject *py_pjsua_buddy_get_user_data(PyObject *pSelf, PyObject *pArgs) +{ + int buddy_id; + PyObject *user_data; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "i", &buddy_id)) { + return NULL; + } + + user_data = (PyObject*) pjsua_buddy_get_user_data(buddy_id); + + return user_data? Py_BuildValue("O", user_data) : Py_BuildValue(""); +} + +/* + * py_pjsua_buddy_subscribe_pres + */ +static PyObject *py_pjsua_buddy_subscribe_pres(PyObject *pSelf, + PyObject *pArgs) +{ + int buddy_id; + int status; + int subscribe; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "ii", &buddy_id, &subscribe)) { + return NULL; + } + + status = pjsua_buddy_subscribe_pres(buddy_id, subscribe); + + return Py_BuildValue("i", status); +} + +/* + * py_pjsua_pres_dump + */ +static PyObject *py_pjsua_pres_dump(PyObject *pSelf, PyObject *pArgs) +{ + int verbose; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "i", &verbose)) { + return NULL; + } + + pjsua_pres_dump(verbose); + + return Py_BuildValue(""); +} + +/* + * py_pjsua_im_send + */ +static PyObject *py_pjsua_im_send(PyObject *pSelf, PyObject *pArgs) +{ + int status; + int acc_id; + pj_str_t *mime_type, tmp_mime_type; + pj_str_t to, content; + PyObject *pTo; + PyObject *pMimeType; + PyObject *pContent; + pjsua_msg_data msg_data; + PyObject *pMsgData; + int user_data; + pj_pool_t *pool = NULL; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "iOOOOi", &acc_id, + &pTo, &pMimeType, &pContent, &pMsgData, &user_data)) + { + return NULL; + } + + if (pMimeType != Py_None) { + mime_type = &tmp_mime_type; + tmp_mime_type = PyString_ToPJ(pMimeType); + } else { + mime_type = NULL; + } + + to = PyString_ToPJ(pTo); + content = PyString_ToPJ(pContent); + + pjsua_msg_data_init(&msg_data); + + if (pMsgData != Py_None) { + PyObj_pjsua_msg_data *omd; + + omd = (PyObj_pjsua_msg_data *)pMsgData; + msg_data.content_type = PyString_ToPJ(omd->content_type); + msg_data.msg_body = PyString_ToPJ(omd->msg_body); + pool = pjsua_pool_create("pytmp", POOL_SIZE, POOL_SIZE); + translate_hdr(pool, &msg_data.hdr_list, omd->hdr_list); + } + + status = pjsua_im_send(acc_id, &to, mime_type, &content, + &msg_data, (void*)(long)user_data); + if (pool) + pj_pool_release(pool); + + return Py_BuildValue("i",status); +} + +/* + * py_pjsua_im_typing + */ +static PyObject *py_pjsua_im_typing(PyObject *pSelf, PyObject *pArgs) +{ + int status; + int acc_id; + pj_str_t to; + PyObject *pTo; + int is_typing; + pjsua_msg_data msg_data; + PyObject *pMsgData; + pj_pool_t *pool = NULL; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "iOiO", &acc_id, &pTo, &is_typing, + &pMsgData)) + { + return NULL; + } + + to = PyString_ToPJ(pTo); + + pjsua_msg_data_init(&msg_data); + + if (pMsgData != Py_None) { + PyObj_pjsua_msg_data *omd; + + omd = (PyObj_pjsua_msg_data *)pMsgData; + msg_data.content_type = PyString_ToPJ(omd->content_type); + msg_data.msg_body = PyString_ToPJ(omd->msg_body); + pool = pjsua_pool_create("pytmp", POOL_SIZE, POOL_SIZE); + + translate_hdr(pool, &msg_data.hdr_list, omd->hdr_list); + } + + status = pjsua_im_typing(acc_id, &to, is_typing, &msg_data); + + if (pool) + pj_pool_release(pool); + + return Py_BuildValue("i", status); +} + +static char pjsua_buddy_config_default_doc[] = + "_pjsua.Buddy_Config _pjsua.buddy_config_default () " + "Set default values to the buddy config."; +static char pjsua_get_buddy_count_doc[] = + "int _pjsua.get_buddy_count () " + "Get total number of buddies."; +static char pjsua_buddy_is_valid_doc[] = + "int _pjsua.buddy_is_valid (int buddy_id) " + "Check if buddy ID is valid."; +static char pjsua_enum_buddies_doc[] = + "int[] _pjsua.enum_buddies () " + "Enum buddy IDs."; +static char pjsua_buddy_get_info_doc[] = + "_pjsua.Buddy_Info _pjsua.buddy_get_info (int buddy_id) " + "Get detailed buddy info."; +static char pjsua_buddy_add_doc[] = + "int,int _pjsua.buddy_add (_pjsua.Buddy_Config cfg) " + "Add new buddy."; +static char pjsua_buddy_del_doc[] = + "int _pjsua.buddy_del (int buddy_id) " + "Delete buddy."; +static char pjsua_buddy_subscribe_pres_doc[] = + "int _pjsua.buddy_subscribe_pres (int buddy_id, int subscribe) " + "Enable/disable buddy's presence monitoring."; +static char pjsua_pres_dump_doc[] = + "void _pjsua.pres_dump (int verbose) " + "Dump presence subscriptions to log file."; +static char pjsua_im_send_doc[] = + "int _pjsua.im_send (int acc_id, string to, string mime_type, " + "string content, _pjsua.Msg_Data msg_data, int user_data) " + "Send instant messaging outside dialog, using the specified account " + "for route set and authentication."; +static char pjsua_im_typing_doc[] = + "int _pjsua.im_typing (int acc_id, string to, int is_typing, " + "_pjsua.Msg_Data msg_data) " + "Send typing indication outside dialog."; + +/* END OF LIB BUDDY */ + +/* LIB MEDIA */ + + +/* + * py_pjsua_conf_get_max_ports + */ +static PyObject *py_pjsua_conf_get_max_ports(PyObject *pSelf, PyObject *pArgs) +{ + PJ_UNUSED_ARG(pSelf); + PJ_UNUSED_ARG(pArgs); + + return Py_BuildValue("i", pjsua_conf_get_max_ports()); +} + +/* + * py_pjsua_conf_get_active_ports + */ +static PyObject *py_pjsua_conf_get_active_ports(PyObject *pSelf, + PyObject *pArgs) +{ + PJ_UNUSED_ARG(pSelf); + PJ_UNUSED_ARG(pArgs); + + return Py_BuildValue("i", pjsua_conf_get_active_ports()); +} + +/* + * py_pjsua_enum_conf_ports + */ +static PyObject *py_pjsua_enum_conf_ports(PyObject *pSelf, PyObject *pArgs) +{ + pj_status_t status; + PyObject *list; + pjsua_conf_port_id id[PJSUA_MAX_CONF_PORTS]; + unsigned c, i; + + PJ_UNUSED_ARG(pSelf); + PJ_UNUSED_ARG(pArgs); + + c = PJ_ARRAY_SIZE(id); + status = pjsua_enum_conf_ports(id, &c); + if (status != PJ_SUCCESS) + c = 0; + + list = PyList_New(c); + for (i = 0; i < c; i++) { + PyList_SetItem(list, i, Py_BuildValue("i", id[i])); + } + + return (PyObject*)list; +} + +/* + * py_pjsua_conf_get_port_info + */ +static PyObject *py_pjsua_conf_get_port_info(PyObject *pSelf, PyObject *pArgs) +{ + int id; + PyObj_pjsua_conf_port_info *ret; + pjsua_conf_port_info info; + int status; + unsigned i; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "i", &id)) { + return NULL; + } + + status = pjsua_conf_get_port_info(id, &info); + ret = (PyObj_pjsua_conf_port_info *) + conf_port_info_new(&PyTyp_pjsua_conf_port_info, NULL, NULL); + ret->bits_per_sample = info.bits_per_sample; + ret->channel_count = info.channel_count; + ret->clock_rate = info.clock_rate; + ret->name = PyString_FromPJ(&info.name); + ret->samples_per_frame = info.samples_per_frame; + ret->slot_id = info.slot_id; + Py_XDECREF(ret->listeners); + ret->listeners = PyList_New(info.listener_cnt); + for (i = 0; i < info.listener_cnt; i++) { + PyObject *item = Py_BuildValue("i",info.listeners[i]); + PyList_SetItem(ret->listeners, i, item); + } + return (PyObject*)ret; +} + +/* + * py_pjsua_conf_remove_port + */ +static PyObject *py_pjsua_conf_remove_port(PyObject *pSelf, PyObject *pArgs) +{ + int id; + int status; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "i", &id)) { + return NULL; + } + + status = pjsua_conf_remove_port(id); + + return Py_BuildValue("i", status); +} + +/* + * py_pjsua_conf_connect + */ +static PyObject *py_pjsua_conf_connect(PyObject *pSelf, PyObject *pArgs) +{ + int source, sink; + int status; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "ii", &source, &sink)) { + return NULL; + } + + status = pjsua_conf_connect(source, sink); + + return Py_BuildValue("i", status); +} + +/* + * py_pjsua_conf_disconnect + */ +static PyObject *py_pjsua_conf_disconnect(PyObject *pSelf, PyObject *pArgs) +{ + int source, sink; + int status; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "ii", &source, &sink)) { + return NULL; + } + + status = pjsua_conf_disconnect(source, sink); + + return Py_BuildValue("i", status); +} + +/* + * py_pjsua_conf_set_tx_level + */ +static PyObject *py_pjsua_conf_set_tx_level(PyObject *pSelf, PyObject *pArgs) +{ + int slot; + float level; + int status; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "if", &slot, &level)) { + return NULL; + } + + status = pjsua_conf_adjust_tx_level(slot, level); + + return Py_BuildValue("i", status); +} + +/* + * py_pjsua_conf_set_rx_level + */ +static PyObject *py_pjsua_conf_set_rx_level(PyObject *pSelf, PyObject *pArgs) +{ + int slot; + float level; + int status; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "if", &slot, &level)) { + return NULL; + } + + status = pjsua_conf_adjust_rx_level(slot, level); + + return Py_BuildValue("i", status); +} + +/* + * py_pjsua_conf_get_signal_level + */ +static PyObject *py_pjsua_conf_get_signal_level(PyObject *pSelf, + PyObject *pArgs) +{ + int slot; + unsigned tx_level, rx_level; + int status; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "i", &slot)) { + return NULL; + } + + status = pjsua_conf_get_signal_level(slot, &tx_level, &rx_level); + + return Py_BuildValue("iff", status, (float)(tx_level/255.0), + (float)(rx_level/255.0)); +} + +/* + * py_pjsua_player_create + */ +static PyObject *py_pjsua_player_create(PyObject *pSelf, PyObject *pArgs) +{ + int id; + int options; + PyObject *pFilename; + pj_str_t filename; + int status; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "Oi", &pFilename, &options)) { + return NULL; + } + + filename = PyString_ToPJ(pFilename); + status = pjsua_player_create(&filename, options, &id); + + return Py_BuildValue("ii", status, id); +} + +/* + * py_pjsua_playlist_create + */ +static PyObject *py_pjsua_playlist_create(PyObject *pSelf, PyObject *pArgs) +{ + int id; + int options; + PyObject *pLabel, *pFileList; + pj_str_t label; + int count; + pj_str_t files[64]; + int status; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "OOi", &pLabel, &pFileList, &options)) { + return NULL; + } + + label = PyString_ToPJ(pLabel); + if (!PyList_Check(pFileList)) + return Py_BuildValue("ii", PJ_EINVAL, PJSUA_INVALID_ID); + + count = 0; + for (count=0; countdefault_samples_per_sec = info[i].default_samples_per_sec; + obj->input_count = info[i].input_count; + obj->output_count = info[i].output_count; + obj->name = PyString_FromString(info[i].name); + + PyList_SetItem(ret, i, (PyObject *)obj); + } + + return (PyObject*)ret; +} + +/* + * py_pjsua_get_snd_dev + */ +static PyObject *py_pjsua_get_snd_dev(PyObject *pSelf, PyObject *pArgs) +{ + int capture_dev, playback_dev; + int status; + + PJ_UNUSED_ARG(pSelf); + PJ_UNUSED_ARG(pArgs); + + status = pjsua_get_snd_dev(&capture_dev, &playback_dev); + + return Py_BuildValue("ii", capture_dev, playback_dev); +} + +/* + * py_pjsua_set_snd_dev + */ +static PyObject *py_pjsua_set_snd_dev(PyObject *pSelf, PyObject *pArgs) +{ + int capture_dev, playback_dev; + int status; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "ii", &capture_dev, &playback_dev)) { + return NULL; + } + + status = pjsua_set_snd_dev(capture_dev, playback_dev); + + return Py_BuildValue("i", status); +} + +/* + * py_pjsua_set_null_snd_dev + */ +static PyObject *py_pjsua_set_null_snd_dev(PyObject *pSelf, PyObject *pArgs) +{ + int status; + + PJ_UNUSED_ARG(pSelf); + PJ_UNUSED_ARG(pArgs); + + status = pjsua_set_null_snd_dev(); + + return Py_BuildValue("i", status); +} + +/* + * py_pjsua_set_ec + */ +static PyObject *py_pjsua_set_ec(PyObject *pSelf, PyObject *pArgs) +{ + int options; + int tail_ms; + int status; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "ii", &tail_ms, &options)) { + return NULL; + } + + status = pjsua_set_ec(tail_ms, options); + + return Py_BuildValue("i", status); +} + +/* + * py_pjsua_get_ec_tail + */ +static PyObject *py_pjsua_get_ec_tail(PyObject *pSelf, PyObject *pArgs) +{ + int status; + unsigned tail_ms; + + PJ_UNUSED_ARG(pSelf); + PJ_UNUSED_ARG(pArgs); + + status = pjsua_get_ec_tail(&tail_ms); + if (status != PJ_SUCCESS) + tail_ms = 0; + + return Py_BuildValue("i", tail_ms); +} + +/* + * py_pjsua_enum_codecs + */ +static PyObject *py_pjsua_enum_codecs(PyObject *pSelf, PyObject *pArgs) +{ + pj_status_t status; + PyObject *ret; + pjsua_codec_info info[PJMEDIA_CODEC_MGR_MAX_CODECS]; + unsigned c, i; + + PJ_UNUSED_ARG(pSelf); + PJ_UNUSED_ARG(pArgs); + + c = PJ_ARRAY_SIZE(info); + status = pjsua_enum_codecs(info, &c); + if (status != PJ_SUCCESS) + c = 0; + + ret = PyList_New(c); + for (i = 0; i < c; i++) { + PyObj_pjsua_codec_info * obj; + obj = (PyObj_pjsua_codec_info *) + codec_info_new(&PyTyp_pjsua_codec_info, NULL, NULL); + obj->codec_id = PyString_FromPJ(&info[i].codec_id); + obj->priority = info[i].priority; + + PyList_SetItem(ret, i, (PyObject *)obj); + } + + return (PyObject*)ret; +} + +/* + * py_pjsua_codec_set_priority + */ +static PyObject *py_pjsua_codec_set_priority(PyObject *pSelf, PyObject *pArgs) +{ + int status; + PyObject *pCodecId; + pj_str_t codec_id; + int priority; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "Oi", &pCodecId, &priority)) { + return NULL; + } + + codec_id = PyString_ToPJ(pCodecId); + if (priority < 0) + priority = 0; + if (priority > 255) + priority = 255; + + status = pjsua_codec_set_priority(&codec_id, (pj_uint8_t)priority); + + return Py_BuildValue("i", status); +} + +/* + * py_pjsua_codec_get_param + */ +static PyObject *py_pjsua_codec_get_param(PyObject *pSelf, PyObject *pArgs) +{ + int status; + PyObject *pCodecId; + pj_str_t codec_id; + pjmedia_codec_param param; + PyObj_pjmedia_codec_param *ret; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "O", &pCodecId)) { + return NULL; + } + + codec_id = PyString_ToPJ(pCodecId); + + status = pjsua_codec_get_param(&codec_id, ¶m); + if (status != PJ_SUCCESS) + return Py_BuildValue(""); + + ret = (PyObj_pjmedia_codec_param *) + pjmedia_codec_param_new(&PyTyp_pjmedia_codec_param, NULL, NULL); + + ret->info->avg_bps = param.info.avg_bps; + ret->info->channel_cnt = param.info.channel_cnt; + ret->info->clock_rate = param.info.clock_rate; + ret->info->frm_ptime = param.info.frm_ptime; + ret->info->pcm_bits_per_sample = param.info.pcm_bits_per_sample; + ret->info->pt = param.info.pt; + ret->setting->cng = param.setting.cng; + //ret->setting->dec_fmtp_mode = param.setting.dec_fmtp_mode; + //ret->setting->enc_fmtp_mode = param.setting.enc_fmtp_mode; + ret->setting->frm_per_pkt = param.setting.frm_per_pkt; + ret->setting->penh = param.setting.penh; + ret->setting->plc = param.setting.plc; + ret->setting->vad = param.setting.vad; + + return (PyObject*)ret; +} + + +/* + * py_pjsua_codec_set_param + */ +static PyObject *py_pjsua_codec_set_param(PyObject *pSelf, PyObject *pArgs) +{ + int status; + PyObject *pCodecId, *pCodecParam; + pj_str_t codec_id; + pjmedia_codec_param param; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "OO", &pCodecId, &pCodecParam)) { + return NULL; + } + + codec_id = PyString_ToPJ(pCodecId); + + if (pCodecParam != Py_None) { + PyObj_pjmedia_codec_param *obj; + + obj = (PyObj_pjmedia_codec_param *)pCodecParam; + + param.info.avg_bps = obj->info->avg_bps; + param.info.channel_cnt = obj->info->channel_cnt; + param.info.clock_rate = obj->info->clock_rate; + param.info.frm_ptime = obj->info->frm_ptime; + param.info.pcm_bits_per_sample = obj->info->pcm_bits_per_sample; + param.info.pt = obj->info->pt; + param.setting.cng = obj->setting->cng; + //param.setting.dec_fmtp_mode = obj->setting->dec_fmtp_mode; + //param.setting.enc_fmtp_mode = obj->setting->enc_fmtp_mode; + param.setting.frm_per_pkt = obj->setting->frm_per_pkt; + param.setting.penh = obj->setting->penh; + param.setting.plc = obj->setting->plc; + param.setting.vad = obj->setting->vad; + status = pjsua_codec_set_param(&codec_id, ¶m); + + } else { + status = pjsua_codec_set_param(&codec_id, NULL); + } + + return Py_BuildValue("i", status); +} + +static char pjsua_conf_get_max_ports_doc[] = + "int _pjsua.conf_get_max_ports () " + "Get maxinum number of conference ports."; +static char pjsua_conf_get_active_ports_doc[] = + "int _pjsua.conf_get_active_ports () " + "Get current number of active ports in the bridge."; +static char pjsua_enum_conf_ports_doc[] = + "int[] _pjsua.enum_conf_ports () " + "Enumerate all conference ports."; +static char pjsua_conf_get_port_info_doc[] = + "_pjsua.Conf_Port_Info _pjsua.conf_get_port_info (int id) " + "Get information about the specified conference port"; +static char pjsua_conf_remove_port_doc[] = + "int _pjsua.conf_remove_port (int id) " + "Remove arbitrary slot from the conference bridge. " + "Application should only call this function " + "if it registered the port manually."; +static char pjsua_conf_connect_doc[] = + "int _pjsua.conf_connect (int source, int sink) " + "Establish unidirectional media flow from souce to sink. " + "One source may transmit to multiple destinations/sink. " + "And if multiple sources are transmitting to the same sink, " + "the media will be mixed together. Source and sink may refer " + "to the same ID, effectively looping the media. " + "If bidirectional media flow is desired, application " + "needs to call this function twice, with the second " + "one having the arguments reversed."; +static char pjsua_conf_disconnect_doc[] = + "int _pjsua.conf_disconnect (int source, int sink) " + "Disconnect media flow from the source to destination port."; +static char pjsua_player_create_doc[] = + "int, int _pjsua.player_create (string filename, int options) " + "Create a file player, and automatically connect " + "this player to the conference bridge."; +static char pjsua_player_get_conf_port_doc[] = + "int _pjsua.player_get_conf_port (int) " + "Get conference port ID associated with player."; +static char pjsua_player_set_pos_doc[] = + "int _pjsua.player_set_pos (int id, int samples) " + "Set playback position."; +static char pjsua_player_destroy_doc[] = + "int _pjsua.player_destroy (int id) " + "Close the file, remove the player from the bridge, " + "and free resources associated with the file player."; +static char pjsua_recorder_create_doc[] = + "int, int _pjsua.recorder_create (string filename, " + "int enc_type, int enc_param, int max_size, int options) " + "Create a file recorder, and automatically connect this recorder " + "to the conference bridge. The recorder currently supports recording " + "WAV file, and on Windows, MP3 file. The type of the recorder to use " + "is determined by the extension of the file (e.g. '.wav' or '.mp3')."; +static char pjsua_recorder_get_conf_port_doc[] = + "int _pjsua.recorder_get_conf_port (int id) " + "Get conference port associated with recorder."; +static char pjsua_recorder_destroy_doc[] = + "int _pjsua.recorder_destroy (int id) " + "Destroy recorder (this will complete recording)."; +static char pjsua_enum_snd_devs_doc[] = + "_pjsua.PJMedia_Snd_Dev_Info[] _pjsua.enum_snd_devs (int count) " + "Enum sound devices."; +static char pjsua_get_snd_dev_doc[] = + "int, int _pjsua.get_snd_dev () " + "Get currently active sound devices. " + "If sound devices has not been created " + "(for example when pjsua_start() is not called), " + "it is possible that the function returns " + "PJ_SUCCESS with -1 as device IDs."; +static char pjsua_set_snd_dev_doc[] = + "int _pjsua.set_snd_dev (int capture_dev, int playback_dev) " + "Select or change sound device. Application may call this function " + "at any time to replace current sound device."; +static char pjsua_set_null_snd_dev_doc[] = + "int _pjsua.set_null_snd_dev () " + "Set pjsua to use null sound device. The null sound device only " + "provides the timing needed by the conference bridge, and will not " + "interract with any hardware."; +static char pjsua_set_ec_doc[] = + "int _pjsua.set_ec (int tail_ms, int options) " + "Configure the echo canceller tail length of the sound port."; +static char pjsua_get_ec_tail_doc[] = + "int _pjsua.get_ec_tail () " + "Get current echo canceller tail length."; +static char pjsua_enum_codecs_doc[] = + "_pjsua.Codec_Info[] _pjsua.enum_codecs () " + "Enum all supported codecs in the system."; +static char pjsua_codec_set_priority_doc[] = + "int _pjsua.codec_set_priority (string id, int priority) " + "Change codec priority."; +static char pjsua_codec_get_param_doc[] = + "_pjsua.PJMedia_Codec_Param _pjsua.codec_get_param (string id) " + "Get codec parameters"; +static char pjsua_codec_set_param_doc[] = + "int _pjsua.codec_set_param (string id, " + "_pjsua.PJMedia_Codec_Param param) " + "Set codec parameters."; + +/* END OF LIB MEDIA */ + +/* LIB CALL */ + +/* + * py_pjsua_call_get_max_count + */ +static PyObject *py_pjsua_call_get_max_count(PyObject *pSelf, PyObject *pArgs) +{ + int count; + + PJ_UNUSED_ARG(pSelf); + PJ_UNUSED_ARG(pArgs); + + count = pjsua_call_get_max_count(); + + return Py_BuildValue("i", count); +} + +/* + * py_pjsua_call_get_count + */ +static PyObject *py_pjsua_call_get_count(PyObject *pSelf, PyObject *pArgs) +{ + int count; + + PJ_UNUSED_ARG(pSelf); + PJ_UNUSED_ARG(pArgs); + + count = pjsua_call_get_count(); + + return Py_BuildValue("i", count); +} + +/* + * py_pjsua_enum_calls + */ +static PyObject *py_pjsua_enum_calls(PyObject *pSelf, PyObject *pArgs) +{ + pj_status_t status; + PyObject *ret; + pjsua_transport_id id[PJSUA_MAX_CALLS]; + unsigned c, i; + + PJ_UNUSED_ARG(pSelf); + PJ_UNUSED_ARG(pArgs); + + c = PJ_ARRAY_SIZE(id); + status = pjsua_enum_calls(id, &c); + if (status != PJ_SUCCESS) + c = 0; + + ret = PyList_New(c); + for (i = 0; i < c; i++) { + PyList_SetItem(ret, i, Py_BuildValue("i", id[i])); + } + + return (PyObject*)ret; +} + +/* + * py_pjsua_call_make_call + */ +static PyObject *py_pjsua_call_make_call(PyObject *pSelf, PyObject *pArgs) +{ + int status; + int acc_id; + pj_str_t dst_uri; + PyObject *pDstUri, *pMsgData, *pUserData; + unsigned options; + pjsua_msg_data msg_data; + int call_id; + pj_pool_t *pool = NULL; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "iOIOO", &acc_id, &pDstUri, &options, + &pUserData, &pMsgData)) + { + return NULL; + } + + dst_uri = PyString_ToPJ(pDstUri); + pjsua_msg_data_init(&msg_data); + + if (pMsgData != Py_None) { + PyObj_pjsua_msg_data * omd; + + omd = (PyObj_pjsua_msg_data *)pMsgData; + + msg_data.content_type = PyString_ToPJ(omd->content_type); + msg_data.msg_body = PyString_ToPJ(omd->msg_body); + pool = pjsua_pool_create("pytmp", POOL_SIZE, POOL_SIZE); + translate_hdr(pool, &msg_data.hdr_list, omd->hdr_list); + } + + Py_XINCREF(pUserData); + + status = pjsua_call_make_call(acc_id, &dst_uri, + options, (void*)pUserData, + &msg_data, &call_id); + if (pool != NULL) + pj_pool_release(pool); + + if (status != PJ_SUCCESS) { + Py_XDECREF(pUserData); + } + + return Py_BuildValue("ii", status, call_id); +} + +/* + * py_pjsua_call_is_active + */ +static PyObject *py_pjsua_call_is_active(PyObject *pSelf, PyObject *pArgs) +{ + int call_id; + int is_active; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "i", &call_id)) { + return NULL; + } + + is_active = pjsua_call_is_active(call_id); + + return Py_BuildValue("i", is_active); +} + +/* + * py_pjsua_call_has_media + */ +static PyObject *py_pjsua_call_has_media(PyObject *pSelf, PyObject *pArgs) +{ + int call_id; + int has_media; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "i", &call_id)) { + return NULL; + } + + has_media = pjsua_call_has_media(call_id); + + return Py_BuildValue("i", has_media); +} + +/* + * py_pjsua_call_get_conf_port + */ +static PyObject* py_pjsua_call_get_conf_port(PyObject *pSelf, PyObject *pArgs) +{ + int call_id; + int port_id; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "i", &call_id)) { + return NULL; + } + + port_id = pjsua_call_get_conf_port(call_id); + + return Py_BuildValue("i", port_id); +} + +/* + * py_pjsua_call_get_info + */ +static PyObject* py_pjsua_call_get_info(PyObject *pSelf, PyObject *pArgs) +{ + int call_id; + int status; + PyObj_pjsua_call_info *ret; + pjsua_call_info info; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "i", &call_id)) { + return NULL; + } + + status = pjsua_call_get_info(call_id, &info); + if (status != PJ_SUCCESS) + return Py_BuildValue(""); + + ret = (PyObj_pjsua_call_info *)call_info_new(&PyTyp_pjsua_call_info, + NULL, NULL); + ret->acc_id = info.acc_id; + Py_XDECREF(ret->call_id); + ret->call_id = PyString_FromPJ(&info.call_id); + ret->conf_slot = info.conf_slot; + ret->connect_duration = info.connect_duration.sec * 1000 + + info.connect_duration.msec; + ret->total_duration = info.total_duration.sec * 1000 + + info.total_duration.msec; + ret->id = info.id; + ret->last_status = info.last_status; + Py_XDECREF(ret->last_status_text); + ret->last_status_text = PyString_FromPJ(&info.last_status_text); + Py_XDECREF(ret->local_contact); + ret->local_contact = PyString_FromPJ(&info.local_contact); + Py_XDECREF(ret->local_info); + ret->local_info = PyString_FromPJ(&info.local_info); + Py_XDECREF(ret->remote_contact); + ret->remote_contact = PyString_FromPJ(&info.remote_contact); + Py_XDECREF(ret->remote_info); + ret->remote_info = PyString_FromPJ(&info.remote_info); + ret->media_dir = info.media_dir; + ret->media_status = info.media_status; + ret->role = info.role; + ret->state = info.state; + Py_XDECREF(ret->state_text); + ret->state_text = PyString_FromPJ(&info.state_text); + + return (PyObject*)ret; +} + +/* + * py_pjsua_call_set_user_data + */ +static PyObject *py_pjsua_call_set_user_data(PyObject *pSelf, PyObject *pArgs) +{ + int call_id; + PyObject *pUserData, *old_user_data; + int status; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "iO", &call_id, &pUserData)) { + return NULL; + } + + old_user_data = (PyObject*) pjsua_call_get_user_data(call_id); + + if (old_user_data == pUserData) { + return Py_BuildValue("i", PJ_SUCCESS); + } + + Py_XINCREF(pUserData); + Py_XDECREF(old_user_data); + + status = pjsua_call_set_user_data(call_id, (void*)pUserData); + + if (status != PJ_SUCCESS) { + Py_XDECREF(pUserData); + } + + return Py_BuildValue("i", status); +} + +/* + * py_pjsua_call_get_user_data + */ +static PyObject *py_pjsua_call_get_user_data(PyObject *pSelf, PyObject *pArgs) +{ + int call_id; + PyObject *user_data; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "i", &call_id)) { + return NULL; + } + + user_data = (PyObject*)pjsua_call_get_user_data(call_id); + return user_data ? Py_BuildValue("O", user_data) : Py_BuildValue(""); +} + +/* + * py_pjsua_call_answer + */ +static PyObject *py_pjsua_call_answer(PyObject *pSelf, PyObject *pArgs) +{ + int status; + int call_id; + pj_str_t * reason, tmp_reason; + PyObject *pReason; + unsigned code; + pjsua_msg_data msg_data; + PyObject * omdObj; + pj_pool_t * pool = NULL; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "iIOO", &call_id, &code, &pReason, &omdObj)) { + return NULL; + } + + if (pReason == Py_None) { + reason = NULL; + } else { + reason = &tmp_reason; + tmp_reason = PyString_ToPJ(pReason); + } + + pjsua_msg_data_init(&msg_data); + if (omdObj != Py_None) { + PyObj_pjsua_msg_data *omd; + + omd = (PyObj_pjsua_msg_data *)omdObj; + msg_data.content_type = PyString_ToPJ(omd->content_type); + msg_data.msg_body = PyString_ToPJ(omd->msg_body); + pool = pjsua_pool_create("pytmp", POOL_SIZE, POOL_SIZE); + translate_hdr(pool, &msg_data.hdr_list, omd->hdr_list); + } + + status = pjsua_call_answer(call_id, code, reason, &msg_data); + + if (pool) + pj_pool_release(pool); + + return Py_BuildValue("i", status); +} + +/* + * py_pjsua_call_hangup + */ +static PyObject *py_pjsua_call_hangup(PyObject *pSelf, PyObject *pArgs) +{ + int status; + int call_id; + pj_str_t *reason, tmp_reason; + PyObject *pReason; + unsigned code; + pjsua_msg_data msg_data; + PyObject *omdObj; + pj_pool_t *pool = NULL; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "iIOO", &call_id, &code, &pReason, + &omdObj)) + { + return NULL; + } + + if (pReason == Py_None) { + reason = NULL; + } else { + reason = &tmp_reason; + tmp_reason = PyString_ToPJ(pReason); + } + + pjsua_msg_data_init(&msg_data); + if (omdObj != Py_None) { + PyObj_pjsua_msg_data *omd; + + omd = (PyObj_pjsua_msg_data *)omdObj; + msg_data.content_type = PyString_ToPJ(omd->content_type); + msg_data.msg_body = PyString_ToPJ(omd->msg_body); + pool = pjsua_pool_create("pytmp", POOL_SIZE, POOL_SIZE); + translate_hdr(pool, &msg_data.hdr_list, omd->hdr_list); + } + + status = pjsua_call_hangup(call_id, code, reason, &msg_data); + if (pool) + pj_pool_release(pool); + + return Py_BuildValue("i", status); +} + +/* + * py_pjsua_call_set_hold + */ +static PyObject *py_pjsua_call_set_hold(PyObject *pSelf, PyObject *pArgs) +{ + int status; + int call_id; + pjsua_msg_data msg_data; + PyObject *omdObj; + pj_pool_t *pool = NULL; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "iO", &call_id, &omdObj)) { + return NULL; + } + + pjsua_msg_data_init(&msg_data); + if (omdObj != Py_None) { + PyObj_pjsua_msg_data *omd; + + omd = (PyObj_pjsua_msg_data *)omdObj; + msg_data.content_type = PyString_ToPJ(omd->content_type); + msg_data.msg_body = PyString_ToPJ(omd->msg_body); + pool = pjsua_pool_create("pytmp", POOL_SIZE, POOL_SIZE); + translate_hdr(pool, &msg_data.hdr_list, omd->hdr_list); + } + + status = pjsua_call_set_hold(call_id, &msg_data); + + if (pool) + pj_pool_release(pool); + + return Py_BuildValue("i",status); +} + +/* + * py_pjsua_call_reinvite + */ +static PyObject *py_pjsua_call_reinvite(PyObject *pSelf, PyObject *pArgs) +{ + int status; + int call_id; + int unhold; + pjsua_msg_data msg_data; + PyObject *omdObj; + pj_pool_t *pool = NULL; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "iiO", &call_id, &unhold, &omdObj)) { + return NULL; + } + + pjsua_msg_data_init(&msg_data); + if (omdObj != Py_None) { + PyObj_pjsua_msg_data *omd; + + omd = (PyObj_pjsua_msg_data *)omdObj; + msg_data.content_type = PyString_ToPJ(omd->content_type); + msg_data.msg_body = PyString_ToPJ(omd->msg_body); + pool = pjsua_pool_create("pytmp", POOL_SIZE, POOL_SIZE); + translate_hdr(pool, &msg_data.hdr_list, omd->hdr_list); + } + + status = pjsua_call_reinvite(call_id, unhold, &msg_data); + + if (pool) + pj_pool_release(pool); + + return Py_BuildValue("i", status); +} + +/* + * py_pjsua_call_update + */ +static PyObject *py_pjsua_call_update(PyObject *pSelf, PyObject *pArgs) +{ + int status; + int call_id; + int option; + pjsua_msg_data msg_data; + PyObject *omdObj; + pj_pool_t *pool = NULL; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "iiO", &call_id, &option, &omdObj)) { + return NULL; + } + + pjsua_msg_data_init(&msg_data); + if (omdObj != Py_None) { + PyObj_pjsua_msg_data *omd; + + omd = (PyObj_pjsua_msg_data *)omdObj; + msg_data.content_type = PyString_ToPJ(omd->content_type); + msg_data.msg_body = PyString_ToPJ(omd->msg_body); + pool = pjsua_pool_create("pytmp", POOL_SIZE, POOL_SIZE); + translate_hdr(pool, &msg_data.hdr_list, omd->hdr_list); + } + + status = pjsua_call_update(call_id, option, &msg_data); + + if (pool) + pj_pool_release(pool); + + return Py_BuildValue("i", status); +} + +/* + * py_pjsua_call_send_request + */ +static PyObject *py_pjsua_call_send_request(PyObject *pSelf, PyObject *pArgs) +{ + int status; + int call_id; + PyObject *pMethod; + pj_str_t method; + pjsua_msg_data msg_data; + PyObject *omdObj; + pj_pool_t *pool = NULL; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "iOO", &call_id, &pMethod, &omdObj)) { + return NULL; + } + + if (!PyString_Check(pMethod)) { + return NULL; + } + + method = PyString_ToPJ(pMethod); + pjsua_msg_data_init(&msg_data); + + if (omdObj != Py_None) { + PyObj_pjsua_msg_data *omd; + + omd = (PyObj_pjsua_msg_data *)omdObj; + msg_data.content_type = PyString_ToPJ(omd->content_type); + msg_data.msg_body = PyString_ToPJ(omd->msg_body); + pool = pjsua_pool_create("pytmp", POOL_SIZE, POOL_SIZE); + translate_hdr(pool, &msg_data.hdr_list, omd->hdr_list); + } + + status = pjsua_call_send_request(call_id, &method, &msg_data); + + if (pool) + pj_pool_release(pool); + + return Py_BuildValue("i", status); +} + +/* + * py_pjsua_call_xfer + */ +static PyObject *py_pjsua_call_xfer(PyObject *pSelf, PyObject *pArgs) +{ + int status; + int call_id; + pj_str_t dest; + PyObject *pDstUri; + pjsua_msg_data msg_data; + PyObject *omdObj; + pj_pool_t *pool = NULL; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "iOO", &call_id, &pDstUri, &omdObj)) { + return NULL; + } + + if (!PyString_Check(pDstUri)) + return NULL; + + dest = PyString_ToPJ(pDstUri); + pjsua_msg_data_init(&msg_data); + + if (omdObj != Py_None) { + PyObj_pjsua_msg_data *omd; + + omd = (PyObj_pjsua_msg_data *)omdObj; + msg_data.content_type = PyString_ToPJ(omd->content_type); + msg_data.msg_body = PyString_ToPJ(omd->msg_body); + pool = pjsua_pool_create("pytmp", POOL_SIZE, POOL_SIZE); + translate_hdr(pool, &msg_data.hdr_list, omd->hdr_list); + } + + status = pjsua_call_xfer(call_id, &dest, &msg_data); + + if (pool) + pj_pool_release(pool); + + return Py_BuildValue("i", status); +} + +/* + * py_pjsua_call_xfer_replaces + */ +static PyObject *py_pjsua_call_xfer_replaces(PyObject *pSelf, PyObject *pArgs) +{ + int status; + int call_id; + int dest_call_id; + unsigned options; + pjsua_msg_data msg_data; + PyObject *omdObj; + pj_pool_t *pool = NULL; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "iiIO", &call_id, &dest_call_id, + &options, &omdObj)) + { + return NULL; + } + + pjsua_msg_data_init(&msg_data); + + if (omdObj != Py_None) { + PyObj_pjsua_msg_data *omd; + + omd = (PyObj_pjsua_msg_data *)omdObj; + msg_data.content_type = PyString_ToPJ(omd->content_type); + msg_data.msg_body = PyString_ToPJ(omd->msg_body); + pool = pjsua_pool_create("pytmp", POOL_SIZE, POOL_SIZE); + translate_hdr(pool, &msg_data.hdr_list, omd->hdr_list); + } + + status = pjsua_call_xfer_replaces(call_id, dest_call_id, options, + &msg_data); + + if (pool) + pj_pool_release(pool); + + return Py_BuildValue("i", status); +} + +/* + * py_pjsua_call_dial_dtmf + */ +static PyObject *py_pjsua_call_dial_dtmf(PyObject *pSelf, PyObject *pArgs) +{ + int call_id; + PyObject *pDigits; + pj_str_t digits; + int status; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "iO", &call_id, &pDigits)) { + return NULL; + } + + if (!PyString_Check(pDigits)) + return Py_BuildValue("i", PJ_EINVAL); + + digits = PyString_ToPJ(pDigits); + status = pjsua_call_dial_dtmf(call_id, &digits); + + return Py_BuildValue("i", status); +} + +/* + * py_pjsua_call_send_im + */ +static PyObject *py_pjsua_call_send_im(PyObject *pSelf, PyObject *pArgs) +{ + int status; + int call_id; + pj_str_t content; + pj_str_t * mime_type, tmp_mime_type; + PyObject *pMimeType, *pContent, *omdObj; + pjsua_msg_data msg_data; + int user_data; + pj_pool_t *pool = NULL; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "iOOOi", &call_id, &pMimeType, &pContent, + &omdObj, &user_data)) + { + return NULL; + } + + if (!PyString_Check(pContent)) + return Py_BuildValue("i", PJ_EINVAL); + + content = PyString_ToPJ(pContent); + + if (PyString_Check(pMimeType)) { + mime_type = &tmp_mime_type; + tmp_mime_type = PyString_ToPJ(pMimeType); + } else { + mime_type = NULL; + } + + pjsua_msg_data_init(&msg_data); + if (omdObj != Py_None) { + PyObj_pjsua_msg_data * omd; + + omd = (PyObj_pjsua_msg_data *)omdObj; + msg_data.content_type = PyString_ToPJ(omd->content_type); + msg_data.msg_body = PyString_ToPJ(omd->msg_body); + pool = pjsua_pool_create("pytmp", POOL_SIZE, POOL_SIZE); + translate_hdr(pool, &msg_data.hdr_list, omd->hdr_list); + } + + status = pjsua_call_send_im(call_id, mime_type, &content, + &msg_data, (void*)(long)user_data); + + if (pool) + pj_pool_release(pool); + + return Py_BuildValue("i", status); +} + +/* + * py_pjsua_call_send_typing_ind + */ +static PyObject *py_pjsua_call_send_typing_ind(PyObject *pSelf, + PyObject *pArgs) +{ + int status; + int call_id; + int is_typing; + pjsua_msg_data msg_data; + PyObject *omdObj; + pj_pool_t *pool = NULL; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "iiO", &call_id, &is_typing, &omdObj)) { + return NULL; + } + + pjsua_msg_data_init(&msg_data); + if (omdObj != Py_None) { + PyObj_pjsua_msg_data *omd; + + omd = (PyObj_pjsua_msg_data *)omdObj; + msg_data.content_type = PyString_ToPJ(omd->content_type); + msg_data.msg_body = PyString_ToPJ(omd->msg_body); + pool = pjsua_pool_create("pytmp", POOL_SIZE, POOL_SIZE); + translate_hdr(pool, &msg_data.hdr_list, omd->hdr_list); + } + + status = pjsua_call_send_typing_ind(call_id, is_typing, &msg_data); + + if (pool) + pj_pool_release(pool); + + return Py_BuildValue("i", status); +} + +/* + * py_pjsua_call_hangup_all + */ +static PyObject *py_pjsua_call_hangup_all(PyObject *pSelf, PyObject *pArgs) +{ + PJ_UNUSED_ARG(pSelf); + PJ_UNUSED_ARG(pArgs); + + pjsua_call_hangup_all(); + + return Py_BuildValue(""); +} + +/* + * py_pjsua_call_dump + */ +static PyObject *py_pjsua_call_dump(PyObject *pSelf, PyObject *pArgs) +{ + int call_id; + int with_media; + PyObject *ret; + PyObject *pIndent; + char *buffer; + char *indent; + unsigned maxlen; + int status; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "iiIO", &call_id, &with_media, + &maxlen, &pIndent)) + { + return NULL; + } + + buffer = (char*) malloc(maxlen * sizeof(char)); + indent = PyString_AsString(pIndent); + + status = pjsua_call_dump(call_id, with_media, buffer, maxlen, indent); + if (status != PJ_SUCCESS) { + free(buffer); + return PyString_FromString(""); + } + + ret = PyString_FromString(buffer); + free(buffer); + return (PyObject*)ret; +} + + +/* + * py_pjsua_dump + * Dump application states. + */ +static PyObject *py_pjsua_dump(PyObject *pSelf, PyObject *pArgs) +{ + int detail; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "i", &detail)) { + return NULL; + } + + pjsua_dump(detail); + + return Py_BuildValue(""); +} + + +/* + * py_pj_strerror + */ +static PyObject *py_pj_strerror(PyObject *pSelf, PyObject *pArgs) +{ + int err; + char err_msg[PJ_ERR_MSG_SIZE]; + pj_str_t ret; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "i", &err)) { + return NULL; + } + + ret = pj_strerror(err, err_msg, sizeof(err_msg)); + + return PyString_FromStringAndSize(err_msg, ret.slen); +} + + +/* + * py_pj_parse_simple_sip + */ +static PyObject *py_pj_parse_simple_sip(PyObject *pSelf, PyObject *pArgs) +{ + const char *arg_uri; + pj_pool_t *pool; + char tmp[PJSIP_MAX_URL_SIZE]; + pjsip_uri *uri; + pjsip_sip_uri *sip_uri; + PyObject *ret, *item; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "s", &arg_uri)) { + return NULL; + } + + strncpy(tmp, arg_uri, sizeof(tmp)); + tmp[sizeof(tmp)-1] = '\0'; + + pool = pjsua_pool_create("py_pj_parse_simple_sip", 512, 512); + uri = pjsip_parse_uri(pool, tmp, strlen(tmp), 0); + + if (uri == NULL || (!PJSIP_URI_SCHEME_IS_SIP(uri) && + !PJSIP_URI_SCHEME_IS_SIPS(uri))) { + pj_pool_release(pool); + return Py_BuildValue(""); + } + + ret = PyTuple_New(5); + sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(uri); + + /* Scheme */ + item = PyString_FromPJ(pjsip_uri_get_scheme(uri)); + PyTuple_SetItem(ret, 0, item); + + /* Username */ + item = PyString_FromPJ(&sip_uri->user); + PyTuple_SetItem(ret, 1, item); + + /* Host */ + item = PyString_FromPJ(&sip_uri->host); + PyTuple_SetItem(ret, 2, item); + + /* Port */ + if (sip_uri->port == 5060) { + sip_uri->port = 0; + } + item = Py_BuildValue("i", sip_uri->port); + PyTuple_SetItem(ret, 3, item); + + /* Transport */ + if (pj_stricmp2(&sip_uri->transport_param, "udp")) { + sip_uri->transport_param.ptr = ""; + sip_uri->transport_param.slen = 0; + } + item = PyString_FromPJ(&sip_uri->transport_param); + PyTuple_SetItem(ret, 4, item); + + pj_pool_release(pool); + return ret; +} + + +static char pjsua_call_get_max_count_doc[] = + "int _pjsua.call_get_max_count () " + "Get maximum number of calls configured in pjsua."; +static char pjsua_call_get_count_doc[] = + "int _pjsua.call_get_count () " + "Get number of currently active calls."; +static char pjsua_enum_calls_doc[] = + "int[] _pjsua.enum_calls () " + "Get maximum number of calls configured in pjsua."; +static char pjsua_call_make_call_doc[] = + "int,int _pjsua.call_make_call (int acc_id, string dst_uri, int options," + "int user_data, _pjsua.Msg_Data msg_data) " + "Make outgoing call to the specified URI using the specified account."; +static char pjsua_call_is_active_doc[] = + "int _pjsua.call_is_active (int call_id) " + "Check if the specified call has active INVITE session and the INVITE " + "session has not been disconnected."; +static char pjsua_call_has_media_doc[] = + "int _pjsua.call_has_media (int call_id) " + "Check if call has an active media session."; +static char pjsua_call_get_conf_port_doc[] = + "int _pjsua.call_get_conf_port (int call_id) " + "Get the conference port identification associated with the call."; +static char pjsua_call_get_info_doc[] = + "_pjsua.Call_Info _pjsua.call_get_info (int call_id) " + "Obtain detail information about the specified call."; +static char pjsua_call_set_user_data_doc[] = + "int _pjsua.call_set_user_data (int call_id, int user_data) " + "Attach application specific data to the call."; +static char pjsua_call_get_user_data_doc[] = + "int _pjsua.call_get_user_data (int call_id) " + "Get user data attached to the call."; +static char pjsua_call_answer_doc[] = + "int _pjsua.call_answer (int call_id, int code, string reason, " + "_pjsua.Msg_Data msg_data) " + "Send response to incoming INVITE request."; +static char pjsua_call_hangup_doc[] = + "int _pjsua.call_hangup (int call_id, int code, string reason, " + "_pjsua.Msg_Data msg_data) " + "Hangup call by using method that is appropriate according " + "to the call state."; +static char pjsua_call_set_hold_doc[] = + "int _pjsua.call_set_hold (int call_id, _pjsua.Msg_Data msg_data) " + "Put the specified call on hold."; +static char pjsua_call_reinvite_doc[] = + "int _pjsua.call_reinvite (int call_id, int unhold, " + "_pjsua.Msg_Data msg_data) " + "Send re-INVITE (to release hold)."; +static char pjsua_call_xfer_doc[] = + "int _pjsua.call_xfer (int call_id, string dest, " + "_pjsua.Msg_Data msg_data) " + "Initiate call transfer to the specified address. " + "This function will send REFER request to instruct remote call party " + "to initiate a new INVITE session to the specified destination/target."; +static char pjsua_call_xfer_replaces_doc[] = + "int _pjsua.call_xfer_replaces (int call_id, int dest_call_id, " + "int options, _pjsua.Msg_Data msg_data) " + "Initiate attended call transfer. This function will send REFER request " + "to instruct remote call party to initiate new INVITE session to the URL " + "of dest_call_id. The party at dest_call_id then should 'replace' the call" + "with us with the new call from the REFER recipient."; +static char pjsua_call_dial_dtmf_doc[] = + "int _pjsua.call_dial_dtmf (int call_id, string digits) " + "Send DTMF digits to remote using RFC 2833 payload formats."; +static char pjsua_call_send_im_doc[] = + "int _pjsua.call_send_im (int call_id, string mime_type, string content," + "_pjsua.Msg_Data msg_data, int user_data) " + "Send instant messaging inside INVITE session."; +static char pjsua_call_send_typing_ind_doc[] = + "int _pjsua.call_send_typing_ind (int call_id, int is_typing, " + "_pjsua.Msg_Data msg_data) " + "Send IM typing indication inside INVITE session."; +static char pjsua_call_hangup_all_doc[] = + "void _pjsua.call_hangup_all () " + "Terminate all calls."; +static char pjsua_call_dump_doc[] = + "int _pjsua.call_dump (int call_id, int with_media, int maxlen, " + "string indent) " + "Dump call and media statistics to string."; + +/* END OF LIB CALL */ + +/* + * Map of function names to functions + */ +static PyMethodDef py_pjsua_methods[] = +{ + { + "thread_register", py_pjsua_thread_register, METH_VARARGS, + pjsua_thread_register_doc + }, + { + "perror", py_pjsua_perror, METH_VARARGS, pjsua_perror_doc + }, + { + "create", py_pjsua_create, METH_VARARGS, pjsua_create_doc + }, + { + "init", py_pjsua_init, METH_VARARGS, pjsua_init_doc + }, + { + "start", py_pjsua_start, METH_VARARGS, pjsua_start_doc + }, + { + "destroy", py_pjsua_destroy, METH_VARARGS, pjsua_destroy_doc + }, + { + "handle_events", py_pjsua_handle_events, METH_VARARGS, + pjsua_handle_events_doc + }, + { + "verify_sip_url", py_pjsua_verify_sip_url, METH_VARARGS, + pjsua_verify_sip_url_doc + }, + { + "reconfigure_logging", py_pjsua_reconfigure_logging, METH_VARARGS, + pjsua_reconfigure_logging_doc + }, + { + "logging_config_default", py_pjsua_logging_config_default, + METH_VARARGS, pjsua_logging_config_default_doc + }, + { + "config_default", py_pjsua_config_default, METH_VARARGS, + pjsua_config_default_doc + }, + { + "media_config_default", py_pjsua_media_config_default, METH_VARARGS, + pjsua_media_config_default_doc + }, + + + { + "msg_data_init", py_pjsua_msg_data_init, METH_VARARGS, + pjsua_msg_data_init_doc + }, + { + "transport_config_default", py_pjsua_transport_config_default, + METH_VARARGS,pjsua_transport_config_default_doc + }, + { + "transport_create", py_pjsua_transport_create, METH_VARARGS, + pjsua_transport_create_doc + }, + { + "transport_enum_transports", py_pjsua_enum_transports, METH_VARARGS, + pjsua_enum_transports_doc + }, + { + "transport_get_info", py_pjsua_transport_get_info, METH_VARARGS, + pjsua_transport_get_info_doc + }, + { + "transport_set_enable", py_pjsua_transport_set_enable, METH_VARARGS, + pjsua_transport_set_enable_doc + }, + { + "transport_close", py_pjsua_transport_close, METH_VARARGS, + pjsua_transport_close_doc + }, + { + "acc_config_default", py_pjsua_acc_config_default, METH_VARARGS, + pjsua_acc_config_default_doc + }, + { + "acc_get_count", py_pjsua_acc_get_count, METH_VARARGS, + pjsua_acc_get_count_doc + }, + { + "acc_is_valid", py_pjsua_acc_is_valid, METH_VARARGS, + pjsua_acc_is_valid_doc + }, + { + "acc_set_default", py_pjsua_acc_set_default, METH_VARARGS, + pjsua_acc_set_default_doc + }, + { + "acc_get_default", py_pjsua_acc_get_default, METH_VARARGS, + pjsua_acc_get_default_doc + }, + { + "acc_add", py_pjsua_acc_add, METH_VARARGS, + pjsua_acc_add_doc + }, + { + "acc_add_local", py_pjsua_acc_add_local, METH_VARARGS, + pjsua_acc_add_local_doc + }, + { + "acc_del", py_pjsua_acc_del, METH_VARARGS, + pjsua_acc_del_doc + }, + { + "acc_set_user_data", py_pjsua_acc_set_user_data, METH_VARARGS, + "Accociate user data with the account" + }, + { + "acc_get_user_data", py_pjsua_acc_get_user_data, METH_VARARGS, + "Get account's user data" + }, + { + "acc_modify", py_pjsua_acc_modify, METH_VARARGS, + pjsua_acc_modify_doc + }, + { + "acc_set_online_status", py_pjsua_acc_set_online_status, METH_VARARGS, + pjsua_acc_set_online_status_doc + }, + { + "acc_set_online_status2", py_pjsua_acc_set_online_status2, METH_VARARGS, + pjsua_acc_set_online_status2_doc + }, + { + "acc_set_registration", py_pjsua_acc_set_registration, METH_VARARGS, + pjsua_acc_set_registration_doc + }, + { + "acc_get_info", py_pjsua_acc_get_info, METH_VARARGS, + pjsua_acc_get_info_doc + }, + { + "acc_pres_notify", py_pjsua_acc_pres_notify, METH_VARARGS, + "Accept or reject subscription request" + }, + { + "enum_accs", py_pjsua_enum_accs, METH_VARARGS, + pjsua_enum_accs_doc + }, + { + "acc_enum_info", py_pjsua_acc_enum_info, METH_VARARGS, + pjsua_acc_enum_info_doc + }, + { + "acc_set_transport", py_pjsua_acc_set_transport, METH_VARARGS, + "Lock transport to use the specified transport" + }, + { + "buddy_config_default", py_pjsua_buddy_config_default, METH_VARARGS, + pjsua_buddy_config_default_doc + }, + { + "get_buddy_count", py_pjsua_get_buddy_count, METH_VARARGS, + pjsua_get_buddy_count_doc + }, + { + "buddy_is_valid", py_pjsua_buddy_is_valid, METH_VARARGS, + pjsua_buddy_is_valid_doc + }, + { + "enum_buddies", py_pjsua_enum_buddies, METH_VARARGS, + pjsua_enum_buddies_doc + }, + { + "buddy_find", py_pjsua_buddy_find, METH_VARARGS, + "Find buddy with the specified URI" + }, + { + "buddy_get_info", py_pjsua_buddy_get_info, METH_VARARGS, + pjsua_buddy_get_info_doc + }, + { + "buddy_add", py_pjsua_buddy_add, METH_VARARGS, + pjsua_buddy_add_doc + }, + { + "buddy_del", py_pjsua_buddy_del, METH_VARARGS, + pjsua_buddy_del_doc + }, + { + "buddy_set_user_data", py_pjsua_buddy_set_user_data, METH_VARARGS, + "Associate user data to the buddy object" + }, + { + "buddy_get_user_data", py_pjsua_buddy_get_user_data, METH_VARARGS, + "Get buddy user data" + }, + { + "buddy_subscribe_pres", py_pjsua_buddy_subscribe_pres, METH_VARARGS, + pjsua_buddy_subscribe_pres_doc + }, + { + "pres_dump", py_pjsua_pres_dump, METH_VARARGS, + pjsua_pres_dump_doc + }, + { + "im_send", py_pjsua_im_send, METH_VARARGS, + pjsua_im_send_doc + }, + { + "im_typing", py_pjsua_im_typing, METH_VARARGS, + pjsua_im_typing_doc + }, + { + "conf_get_max_ports", py_pjsua_conf_get_max_ports, METH_VARARGS, + pjsua_conf_get_max_ports_doc + }, + { + "conf_get_active_ports", py_pjsua_conf_get_active_ports, METH_VARARGS, + pjsua_conf_get_active_ports_doc + }, + { + "enum_conf_ports", py_pjsua_enum_conf_ports, METH_VARARGS, + pjsua_enum_conf_ports_doc + }, + { + "conf_get_port_info", py_pjsua_conf_get_port_info, METH_VARARGS, + pjsua_conf_get_port_info_doc + }, + { + "conf_remove_port", py_pjsua_conf_remove_port, METH_VARARGS, + pjsua_conf_remove_port_doc + }, + { + "conf_connect", py_pjsua_conf_connect, METH_VARARGS, + pjsua_conf_connect_doc + }, + { + "conf_disconnect", py_pjsua_conf_disconnect, METH_VARARGS, + pjsua_conf_disconnect_doc + }, + { + "conf_set_tx_level", py_pjsua_conf_set_tx_level, METH_VARARGS, + "Adjust the signal level to be transmitted from the bridge to the" + " specified port by making it louder or quieter" + }, + { + "conf_set_rx_level", py_pjsua_conf_set_rx_level, METH_VARARGS, + "Adjust the signal level to be received from the specified port (to" + " the bridge) by making it louder or quieter" + }, + { + "conf_get_signal_level", py_pjsua_conf_get_signal_level, METH_VARARGS, + "Get last signal level transmitted to or received from the specified port" + }, + { + "player_create", py_pjsua_player_create, METH_VARARGS, + pjsua_player_create_doc + }, + { + "playlist_create", py_pjsua_playlist_create, METH_VARARGS, + "Create WAV playlist" + }, + { + "player_get_conf_port", py_pjsua_player_get_conf_port, METH_VARARGS, + pjsua_player_get_conf_port_doc + }, + { + "player_set_pos", py_pjsua_player_set_pos, METH_VARARGS, + pjsua_player_set_pos_doc + }, + { + "player_destroy", py_pjsua_player_destroy, METH_VARARGS, + pjsua_player_destroy_doc + }, + { + "recorder_create", py_pjsua_recorder_create, METH_VARARGS, + pjsua_recorder_create_doc + }, + { + "recorder_get_conf_port", py_pjsua_recorder_get_conf_port, METH_VARARGS, + pjsua_recorder_get_conf_port_doc + }, + { + "recorder_destroy", py_pjsua_recorder_destroy, METH_VARARGS, + pjsua_recorder_destroy_doc + }, + { + "enum_snd_devs", py_pjsua_enum_snd_devs, METH_VARARGS, + pjsua_enum_snd_devs_doc + }, + { + "get_snd_dev", py_pjsua_get_snd_dev, METH_VARARGS, + pjsua_get_snd_dev_doc + }, + { + "set_snd_dev", py_pjsua_set_snd_dev, METH_VARARGS, + pjsua_set_snd_dev_doc + }, + { + "set_null_snd_dev", py_pjsua_set_null_snd_dev, METH_VARARGS, + pjsua_set_null_snd_dev_doc + }, + { + "set_ec", py_pjsua_set_ec, METH_VARARGS, + pjsua_set_ec_doc + }, + { + "get_ec_tail", py_pjsua_get_ec_tail, METH_VARARGS, + pjsua_get_ec_tail_doc + }, + { + "enum_codecs", py_pjsua_enum_codecs, METH_VARARGS, + pjsua_enum_codecs_doc + }, + { + "codec_set_priority", py_pjsua_codec_set_priority, METH_VARARGS, + pjsua_codec_set_priority_doc + }, + { + "codec_get_param", py_pjsua_codec_get_param, METH_VARARGS, + pjsua_codec_get_param_doc + }, + { + "codec_set_param", py_pjsua_codec_set_param, METH_VARARGS, + pjsua_codec_set_param_doc + }, + { + "call_get_max_count", py_pjsua_call_get_max_count, METH_VARARGS, + pjsua_call_get_max_count_doc + }, + { + "call_get_count", py_pjsua_call_get_count, METH_VARARGS, + pjsua_call_get_count_doc + }, + { + "enum_calls", py_pjsua_enum_calls, METH_VARARGS, + pjsua_enum_calls_doc + }, + { + "call_make_call", py_pjsua_call_make_call, METH_VARARGS, + pjsua_call_make_call_doc + }, + { + "call_is_active", py_pjsua_call_is_active, METH_VARARGS, + pjsua_call_is_active_doc + }, + { + "call_has_media", py_pjsua_call_has_media, METH_VARARGS, + pjsua_call_has_media_doc + }, + { + "call_get_conf_port", py_pjsua_call_get_conf_port, METH_VARARGS, + pjsua_call_get_conf_port_doc + }, + { + "call_get_info", py_pjsua_call_get_info, METH_VARARGS, + pjsua_call_get_info_doc + }, + { + "call_set_user_data", py_pjsua_call_set_user_data, METH_VARARGS, + pjsua_call_set_user_data_doc + }, + { + "call_get_user_data", py_pjsua_call_get_user_data, METH_VARARGS, + pjsua_call_get_user_data_doc + }, + { + "call_answer", py_pjsua_call_answer, METH_VARARGS, + pjsua_call_answer_doc + }, + { + "call_hangup", py_pjsua_call_hangup, METH_VARARGS, + pjsua_call_hangup_doc + }, + { + "call_set_hold", py_pjsua_call_set_hold, METH_VARARGS, + pjsua_call_set_hold_doc + }, + { + "call_reinvite", py_pjsua_call_reinvite, METH_VARARGS, + pjsua_call_reinvite_doc + }, + { + "call_update", py_pjsua_call_update, METH_VARARGS, + "Send UPDATE" + }, + { + "call_xfer", py_pjsua_call_xfer, METH_VARARGS, + pjsua_call_xfer_doc + }, + { + "call_xfer_replaces", py_pjsua_call_xfer_replaces, METH_VARARGS, + pjsua_call_xfer_replaces_doc + }, + { + "call_dial_dtmf", py_pjsua_call_dial_dtmf, METH_VARARGS, + pjsua_call_dial_dtmf_doc + }, + { + "call_send_im", py_pjsua_call_send_im, METH_VARARGS, + pjsua_call_send_im_doc + }, + { + "call_send_typing_ind", py_pjsua_call_send_typing_ind, METH_VARARGS, + pjsua_call_send_typing_ind_doc + }, + { + "call_hangup_all", py_pjsua_call_hangup_all, METH_VARARGS, + pjsua_call_hangup_all_doc + }, + { + "call_dump", py_pjsua_call_dump, METH_VARARGS, + pjsua_call_dump_doc + }, + { + "call_send_request", py_pjsua_call_send_request, METH_VARARGS, + "Send arbitrary request" + }, + { + "dump", py_pjsua_dump, METH_VARARGS, "Dump application state" + }, + { + "strerror", py_pj_strerror, METH_VARARGS, "Get error message" + }, + { + "parse_simple_uri", py_pj_parse_simple_sip, METH_VARARGS, "Parse URI" + }, + + + {NULL, NULL} /* end of function list */ +}; + + + +/* + * Mapping C structs from and to Python objects & initializing object + */ +DL_EXPORT(void) +init_pjsua(void) +{ + PyObject* m = NULL; +#define ADD_CONSTANT(mod,name) PyModule_AddIntConstant(mod,#name,name) + + + PyEval_InitThreads(); + + if (PyType_Ready(&PyTyp_pjsua_callback) < 0) + return; + if (PyType_Ready(&PyTyp_pjsua_config) < 0) + return; + if (PyType_Ready(&PyTyp_pjsua_logging_config) < 0) + return; + if (PyType_Ready(&PyTyp_pjsua_msg_data) < 0) + return; + PyTyp_pjsua_media_config.tp_new = PyType_GenericNew; + if (PyType_Ready(&PyTyp_pjsua_media_config) < 0) + return; + PyTyp_pjsip_cred_info.tp_new = PyType_GenericNew; + if (PyType_Ready(&PyTyp_pjsip_cred_info) < 0) + return; + + /* LIB TRANSPORT */ + + if (PyType_Ready(&PyTyp_pjsua_transport_config) < 0) + return; + + if (PyType_Ready(&PyTyp_pjsua_transport_info) < 0) + return; + + /* END OF LIB TRANSPORT */ + + /* LIB ACCOUNT */ + + + if (PyType_Ready(&PyTyp_pjsua_acc_config) < 0) + return; + if (PyType_Ready(&PyTyp_pjsua_acc_info) < 0) + return; + + /* END OF LIB ACCOUNT */ + + /* LIB BUDDY */ + + if (PyType_Ready(&PyTyp_pjsua_buddy_config) < 0) + return; + if (PyType_Ready(&PyTyp_pjsua_buddy_info) < 0) + return; + + /* END OF LIB BUDDY */ + + /* LIB MEDIA */ + + if (PyType_Ready(&PyTyp_pjsua_codec_info) < 0) + return; + + if (PyType_Ready(&PyTyp_pjsua_conf_port_info) < 0) + return; + + if (PyType_Ready(&PyTyp_pjmedia_snd_dev_info) < 0) + return; + + PyTyp_pjmedia_codec_param_info.tp_new = PyType_GenericNew; + if (PyType_Ready(&PyTyp_pjmedia_codec_param_info) < 0) + return; + PyTyp_pjmedia_codec_param_setting.tp_new = PyType_GenericNew; + if (PyType_Ready(&PyTyp_pjmedia_codec_param_setting) < 0) + return; + + if (PyType_Ready(&PyTyp_pjmedia_codec_param) < 0) + return; + + /* END OF LIB MEDIA */ + + /* LIB CALL */ + + if (PyType_Ready(&PyTyp_pjsua_call_info) < 0) + return; + + /* END OF LIB CALL */ + + m = Py_InitModule3( + "_pjsua", py_pjsua_methods, "PJSUA-lib module for python" + ); + + Py_INCREF(&PyTyp_pjsua_callback); + PyModule_AddObject(m, "Callback", (PyObject *)&PyTyp_pjsua_callback); + + Py_INCREF(&PyTyp_pjsua_config); + PyModule_AddObject(m, "Config", (PyObject *)&PyTyp_pjsua_config); + + Py_INCREF(&PyTyp_pjsua_media_config); + PyModule_AddObject(m, "Media_Config", (PyObject *)&PyTyp_pjsua_media_config); + + Py_INCREF(&PyTyp_pjsua_logging_config); + PyModule_AddObject(m, "Logging_Config", (PyObject *)&PyTyp_pjsua_logging_config); + + Py_INCREF(&PyTyp_pjsua_msg_data); + PyModule_AddObject(m, "Msg_Data", (PyObject *)&PyTyp_pjsua_msg_data); + + Py_INCREF(&PyTyp_pjsip_cred_info); + PyModule_AddObject(m, "Pjsip_Cred_Info", + (PyObject *)&PyTyp_pjsip_cred_info + ); + + /* LIB TRANSPORT */ + + Py_INCREF(&PyTyp_pjsua_transport_config); + PyModule_AddObject + (m, "Transport_Config", (PyObject *)&PyTyp_pjsua_transport_config); + + Py_INCREF(&PyTyp_pjsua_transport_info); + PyModule_AddObject(m, "Transport_Info", (PyObject *)&PyTyp_pjsua_transport_info); + + + /* END OF LIB TRANSPORT */ + + /* LIB ACCOUNT */ + + + Py_INCREF(&PyTyp_pjsua_acc_config); + PyModule_AddObject(m, "Acc_Config", (PyObject *)&PyTyp_pjsua_acc_config); + Py_INCREF(&PyTyp_pjsua_acc_info); + PyModule_AddObject(m, "Acc_Info", (PyObject *)&PyTyp_pjsua_acc_info); + + /* END OF LIB ACCOUNT */ + + /* LIB BUDDY */ + + Py_INCREF(&PyTyp_pjsua_buddy_config); + PyModule_AddObject(m, "Buddy_Config", (PyObject *)&PyTyp_pjsua_buddy_config); + Py_INCREF(&PyTyp_pjsua_buddy_info); + PyModule_AddObject(m, "Buddy_Info", (PyObject *)&PyTyp_pjsua_buddy_info); + + /* END OF LIB BUDDY */ + + /* LIB MEDIA */ + + Py_INCREF(&PyTyp_pjsua_codec_info); + PyModule_AddObject(m, "Codec_Info", (PyObject *)&PyTyp_pjsua_codec_info); + Py_INCREF(&PyTyp_pjsua_conf_port_info); + PyModule_AddObject(m, "Conf_Port_Info", (PyObject *)&PyTyp_pjsua_conf_port_info); + Py_INCREF(&PyTyp_pjmedia_snd_dev_info); + PyModule_AddObject(m, "PJMedia_Snd_Dev_Info", + (PyObject *)&PyTyp_pjmedia_snd_dev_info); + Py_INCREF(&PyTyp_pjmedia_codec_param_info); + PyModule_AddObject(m, "PJMedia_Codec_Param_Info", + (PyObject *)&PyTyp_pjmedia_codec_param_info); + Py_INCREF(&PyTyp_pjmedia_codec_param_setting); + PyModule_AddObject(m, "PJMedia_Codec_Param_Setting", + (PyObject *)&PyTyp_pjmedia_codec_param_setting); + Py_INCREF(&PyTyp_pjmedia_codec_param); + PyModule_AddObject(m, "PJMedia_Codec_Param", + (PyObject *)&PyTyp_pjmedia_codec_param); + + /* END OF LIB MEDIA */ + + /* LIB CALL */ + + Py_INCREF(&PyTyp_pjsua_call_info); + PyModule_AddObject(m, "Call_Info", (PyObject *)&PyTyp_pjsua_call_info); + + /* END OF LIB CALL */ + + + /* + * Add various constants. + */ + /* Skip it.. */ + +#undef ADD_CONSTANT +} diff --git a/pjsip-apps/src/python/_pjsua.def b/pjsip-apps/src/python/_pjsua.def new file mode 100644 index 0000000..9b150a9 --- /dev/null +++ b/pjsip-apps/src/python/_pjsua.def @@ -0,0 +1,2 @@ +EXPORTS + init_pjsua diff --git a/pjsip-apps/src/python/_pjsua.h b/pjsip-apps/src/python/_pjsua.h new file mode 100644 index 0000000..f2ed780 --- /dev/null +++ b/pjsip-apps/src/python/_pjsua.h @@ -0,0 +1,3508 @@ +/* $Id: _pjsua.h 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PY_PJSUA_H__ +#define __PY_PJSUA_H__ + +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include +#include + + +PJ_INLINE(pj_str_t) PyString_ToPJ(const PyObject *obj) +{ + pj_str_t str; + + if (obj && PyString_Check(obj)) { + str.ptr = PyString_AS_STRING(obj); + str.slen = PyString_GET_SIZE(obj); + } else { + str.ptr = NULL; + str.slen = 0; + } + + return str; +} + +PJ_INLINE(PyObject*) PyString_FromPJ(const pj_str_t *str) +{ + return PyString_FromStringAndSize(str->ptr, str->slen); +} + +////////////////////////////////////////////////////////////////////////////// +/* + * PyObj_pjsip_cred_info + */ +typedef struct +{ + PyObject_HEAD + + /* Type-specific fields go here. */ + PyObject *realm; + PyObject *scheme; + PyObject *username; + int data_type; + PyObject *data; + +} PyObj_pjsip_cred_info; + +/* + * cred_info_dealloc + * deletes a cred info from memory + */ +static void PyObj_pjsip_cred_info_delete(PyObj_pjsip_cred_info* self) +{ + Py_XDECREF(self->realm); + Py_XDECREF(self->scheme); + Py_XDECREF(self->username); + Py_XDECREF(self->data); + self->ob_type->tp_free((PyObject*)self); +} + + +static void PyObj_pjsip_cred_info_import(PyObj_pjsip_cred_info *obj, + const pjsip_cred_info *cfg) +{ + Py_XDECREF(obj->realm); + obj->realm = PyString_FromPJ(&cfg->realm); + Py_XDECREF(obj->scheme); + obj->scheme = PyString_FromPJ(&cfg->scheme); + Py_XDECREF(obj->username); + obj->username = PyString_FromPJ(&cfg->username); + obj->data_type = cfg->data_type; + Py_XDECREF(obj->data); + obj->data = PyString_FromPJ(&cfg->data); +} + +static void PyObj_pjsip_cred_info_export(pjsip_cred_info *cfg, + PyObj_pjsip_cred_info *obj) +{ + cfg->realm = PyString_ToPJ(obj->realm); + cfg->scheme = PyString_ToPJ(obj->scheme); + cfg->username = PyString_ToPJ(obj->username); + cfg->data_type = obj->data_type; + cfg->data = PyString_ToPJ(obj->data); +} + + +/* + * cred_info_new + * constructor for cred_info object + */ +static PyObject * PyObj_pjsip_cred_info_new(PyTypeObject *type, + PyObject *args, + PyObject *kwds) +{ + PyObj_pjsip_cred_info *self; + + PJ_UNUSED_ARG(args); + PJ_UNUSED_ARG(kwds); + + self = (PyObj_pjsip_cred_info *)type->tp_alloc(type, 0); + if (self != NULL) { + self->realm = PyString_FromString(""); + self->scheme = PyString_FromString(""); + self->username = PyString_FromString(""); + self->data_type = PJSIP_CRED_DATA_PLAIN_PASSWD; + self->data = PyString_FromString(""); + } + + return (PyObject *)self; +} + + +/* + * PyObj_pjsip_cred_info_members + */ +static PyMemberDef PyObj_pjsip_cred_info_members[] = +{ + { + "realm", T_OBJECT_EX, + offsetof(PyObj_pjsip_cred_info, realm), 0, + "Realm" + }, + { + "scheme", T_OBJECT_EX, + offsetof(PyObj_pjsip_cred_info, scheme), 0, + "Scheme" + }, + { + "username", T_OBJECT_EX, + offsetof(PyObj_pjsip_cred_info, username), 0, + "User name" + }, + { + "data", T_OBJECT_EX, + offsetof(PyObj_pjsip_cred_info, data), 0, + "The data, which can be a plaintext password or a hashed digest, " + "depending on the value of data_type" + }, + { + "data_type", T_INT, + offsetof(PyObj_pjsip_cred_info, data_type), 0, + "Type of data" + }, + + {NULL} /* Sentinel */ +}; + +/* + * PyTyp_pjsip_cred_info + */ +static PyTypeObject PyTyp_pjsip_cred_info = +{ + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_pjsua.Pjsip_Cred_Info", /*tp_name*/ + sizeof(PyObj_pjsip_cred_info), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyObj_pjsip_cred_info_delete,/*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + "PJSIP credential information", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + PyObj_pjsip_cred_info_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyObj_pjsip_cred_info_new, /* tp_new */ + +}; + + +////////////////////////////////////////////////////////////////////////////// +/* + * PyObj_pjsua_callback + * C/python typewrapper for callback struct + */ +typedef struct PyObj_pjsua_callback +{ + PyObject_HEAD + /* Type-specific fields go here. */ + PyObject * on_call_state; + PyObject * on_incoming_call; + PyObject * on_call_media_state; + PyObject * on_dtmf_digit; + PyObject * on_call_transfer_request; + PyObject * on_call_transfer_status; + PyObject * on_call_replace_request; + PyObject * on_call_replaced; + PyObject * on_reg_state; + PyObject * on_incoming_subscribe; + PyObject * on_buddy_state; + PyObject * on_pager; + PyObject * on_pager_status; + PyObject * on_typing; + PyObject * on_mwi_info; +} PyObj_pjsua_callback; + + +/* + * PyObj_pjsua_callback_delete + * destructor function for callback struct + */ +static void PyObj_pjsua_callback_delete(PyObj_pjsua_callback* self) +{ + Py_XDECREF(self->on_call_state); + Py_XDECREF(self->on_incoming_call); + Py_XDECREF(self->on_call_media_state); + Py_XDECREF(self->on_dtmf_digit); + Py_XDECREF(self->on_call_transfer_request); + Py_XDECREF(self->on_call_transfer_status); + Py_XDECREF(self->on_call_replace_request); + Py_XDECREF(self->on_call_replaced); + Py_XDECREF(self->on_reg_state); + Py_XDECREF(self->on_incoming_subscribe); + Py_XDECREF(self->on_buddy_state); + Py_XDECREF(self->on_pager); + Py_XDECREF(self->on_pager_status); + Py_XDECREF(self->on_typing); + Py_XDECREF(self->on_mwi_info); + self->ob_type->tp_free((PyObject*)self); +} + + +/* + * PyObj_pjsua_callback_new + * * declares constructor for callback struct + */ +static PyObject * PyObj_pjsua_callback_new(PyTypeObject *type, + PyObject *args, + PyObject *kwds) +{ + PyObj_pjsua_callback *self; + + PJ_UNUSED_ARG(args); + PJ_UNUSED_ARG(kwds); + + self = (PyObj_pjsua_callback *)type->tp_alloc(type, 0); + if (self != NULL) { + self->on_call_state = Py_BuildValue(""); + self->on_incoming_call = Py_BuildValue(""); + self->on_call_media_state = Py_BuildValue(""); + self->on_dtmf_digit = Py_BuildValue(""); + self->on_call_transfer_request = Py_BuildValue(""); + self->on_call_transfer_status = Py_BuildValue(""); + self->on_call_replace_request = Py_BuildValue(""); + self->on_call_replaced = Py_BuildValue(""); + self->on_reg_state = Py_BuildValue(""); + self->on_incoming_subscribe = Py_BuildValue(""); + self->on_buddy_state = Py_BuildValue(""); + self->on_pager = Py_BuildValue(""); + self->on_pager_status = Py_BuildValue(""); + self->on_typing = Py_BuildValue(""); + self->on_mwi_info = Py_BuildValue(""); + } + + return (PyObject *)self; +} + + +/* + * PyObj_pjsua_callback_members + * declares available functions for callback object + */ +static PyMemberDef PyObj_pjsua_callback_members[] = +{ + { + "on_call_state", T_OBJECT_EX, + offsetof(PyObj_pjsua_callback, on_call_state), 0, + "Notify application when invite state has changed. Application may " + "then query the call info to get the detail call states." + }, + { + "on_incoming_call", T_OBJECT_EX, + offsetof(PyObj_pjsua_callback, on_incoming_call), 0, + "Notify application on incoming call." + }, + { + "on_call_media_state", T_OBJECT_EX, + offsetof(PyObj_pjsua_callback, on_call_media_state), 0, + "Notify application when media state in the call has changed. Normal " + "application would need to implement this callback, e.g. to connect " + "the call's media to sound device." + }, + { + "on_dtmf_digit", T_OBJECT_EX, + offsetof(PyObj_pjsua_callback, on_dtmf_digit), 0, + "Notify application upon receiving incoming DTMF digit." + }, + { + "on_call_transfer_request", T_OBJECT_EX, + offsetof(PyObj_pjsua_callback, on_call_transfer_request), 0, + "Notify application on call being transfered. " + "Application can decide to accept/reject transfer request " + "by setting the code (default is 200). When this callback " + "is not defined, the default behavior is to accept the " + "transfer." + }, + { + "on_call_transfer_status", T_OBJECT_EX, + offsetof(PyObj_pjsua_callback, on_call_transfer_status), 0, + "Notify application of the status of previously sent call " + "transfer request. Application can monitor the status of the " + "call transfer request, for example to decide whether to " + "terminate existing call." + }, + { + "on_call_replace_request", T_OBJECT_EX, + offsetof(PyObj_pjsua_callback, on_call_replace_request), 0, + "Notify application about incoming INVITE with Replaces header. " + "Application may reject the request by setting non-2xx code." + }, + { + "on_call_replaced", T_OBJECT_EX, + offsetof(PyObj_pjsua_callback, on_call_replaced), 0, + "Notify application that an existing call has been replaced with " + "a new call. This happens when PJSUA-API receives incoming INVITE " + "request with Replaces header." + " " + "After this callback is called, normally PJSUA-API will disconnect " + "old_call_id and establish new_call_id." + }, + { + "on_reg_state", T_OBJECT_EX, + offsetof(PyObj_pjsua_callback, on_reg_state), 0, + "Notify application when registration status has changed. Application " + "may then query the account info to get the registration details." + }, + { + "on_incoming_subscribe", T_OBJECT_EX, + offsetof(PyObj_pjsua_callback, on_incoming_subscribe), 0, + "Notification when incoming SUBSCRIBE request is received." + }, + { + "on_buddy_state", T_OBJECT_EX, + offsetof(PyObj_pjsua_callback, on_buddy_state), 0, + "Notify application when the buddy state has changed. Application may " + "then query the buddy into to get the details." + }, + { + "on_pager", T_OBJECT_EX, + offsetof(PyObj_pjsua_callback, on_pager), 0, + "Notify application on incoming pager (i.e. MESSAGE request). " + "Argument call_id will be -1 if MESSAGE request is not related to an " + "existing call." + }, + { + "on_pager_status", T_OBJECT_EX, + offsetof(PyObj_pjsua_callback, on_pager_status), 0, + "Notify application about the delivery status of outgoing pager " + "request." + }, + { + "on_typing", T_OBJECT_EX, + offsetof(PyObj_pjsua_callback, on_typing), 0, + "Notify application about typing indication." + }, + { + "on_mwi_info", T_OBJECT_EX, + offsetof(PyObj_pjsua_callback, on_mwi_info), 0, + "Notify application about MWI indication." + }, + {NULL} /* Sentinel */ +}; + + +/* + * PyTyp_pjsua_callback + * callback class definition + */ +static PyTypeObject PyTyp_pjsua_callback = +{ + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_pjsua.Callback", /*tp_name*/ + sizeof(PyObj_pjsua_callback), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyObj_pjsua_callback_delete, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + "This structure describes application callback " + "to receive various event notifications from " + "PJSUA-API", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + PyObj_pjsua_callback_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyObj_pjsua_callback_new, /* tp_new */ + +}; + + +////////////////////////////////////////////////////////////////////////////// +/* + * PyObj_pjsua_media_config + * C/Python wrapper for pjsua_media_config object + */ +typedef struct +{ + PyObject_HEAD + /* Type-specific fields go here. */ + unsigned clock_rate; + unsigned snd_clock_rate; + unsigned channel_count; + unsigned audio_frame_ptime; + int snd_auto_close_time; + unsigned max_media_ports; + int has_ioqueue; + unsigned thread_cnt; + unsigned quality; + unsigned ptime; + int no_vad; + unsigned ilbc_mode; + unsigned tx_drop_pct; + unsigned rx_drop_pct; + unsigned ec_options; + unsigned ec_tail_len; + int jb_min; + int jb_max; + int enable_ice; + int enable_turn; + PyObject *turn_server; + int turn_conn_type; + PyObject *turn_realm; + PyObject *turn_username; + int turn_passwd_type; + PyObject *turn_passwd; +} PyObj_pjsua_media_config; + + +/* + * PyObj_pjsua_media_config_members + * declares attributes accessible from both C and Python for media_config file + */ +static PyMemberDef PyObj_pjsua_media_config_members[] = +{ + { + "clock_rate", T_INT, + offsetof(PyObj_pjsua_media_config, clock_rate), 0, + "Clock rate to be applied to the conference bridge. If value is zero, " + "default clock rate will be used (16KHz)." + }, + { + "snd_clock_rate", T_INT, + offsetof(PyObj_pjsua_media_config, snd_clock_rate), 0, + "Specify different clock rate of sound device, otherwise 0." + }, + { + "channel_count", T_INT, + offsetof(PyObj_pjsua_media_config, channel_count), 0, + "Specify channel count (default 1)." + }, + { + "audio_frame_ptime", T_INT, + offsetof(PyObj_pjsua_media_config, audio_frame_ptime), 0, + "Audio frame length in milliseconds." + }, + { + "snd_auto_close_time", T_INT, + offsetof(PyObj_pjsua_media_config, snd_auto_close_time), 0, + "Sound idle time before it's closed." + }, + { + "max_media_ports", T_INT, + offsetof(PyObj_pjsua_media_config, max_media_ports), 0, + "Specify maximum number of media ports to be created in the " + "conference bridge. Since all media terminate in the bridge (calls, " + "file player, file recorder, etc), the value must be large enough to " + "support all of them. However, the larger the value, the more " + "computations are performed." + }, + { + "has_ioqueue", T_INT, + offsetof(PyObj_pjsua_media_config, has_ioqueue), 0, + "Specify whether the media manager should manage its own ioqueue for " + "the RTP/RTCP sockets. If yes, ioqueue will be created and at least " + "one worker thread will be created too. If no, the RTP/RTCP sockets " + "will share the same ioqueue as SIP sockets, and no worker thread is " + "needed." + }, + { + "thread_cnt", T_INT, + offsetof(PyObj_pjsua_media_config, thread_cnt), 0, + "Specify the number of worker threads to handle incoming RTP packets. " + "A value of one is recommended for most applications." + }, + { + "quality", T_INT, + offsetof(PyObj_pjsua_media_config, quality), 0, + "The media quality also sets speex codec quality/complexity to the " + "number." + }, + { + "ptime", T_INT, + offsetof(PyObj_pjsua_media_config, ptime), 0, + "Specify default ptime." + }, + { + "no_vad", T_INT, + offsetof(PyObj_pjsua_media_config, no_vad), 0, + "Disable VAD?" + }, + { + "ilbc_mode", T_INT, + offsetof(PyObj_pjsua_media_config, ilbc_mode), 0, + "iLBC mode (20 or 30)." + }, + { + "tx_drop_pct", T_INT, + offsetof(PyObj_pjsua_media_config, tx_drop_pct), 0, + "Percentage of RTP packet to drop in TX direction (to simulate packet " + "lost)." + }, + { + "rx_drop_pct", T_INT, + offsetof(PyObj_pjsua_media_config, rx_drop_pct), 0, + "Percentage of RTP packet to drop in RX direction (to simulate packet " + "lost)."}, + { + "ec_options", T_INT, + offsetof(PyObj_pjsua_media_config, ec_options), 0, + "Echo canceller options (see pjmedia_echo_create())" + }, + { + "ec_tail_len", T_INT, + offsetof(PyObj_pjsua_media_config, ec_tail_len), 0, + "Echo canceller tail length, in miliseconds." + }, + { + "jb_min", T_INT, + offsetof(PyObj_pjsua_media_config, jb_min), 0, + "Jitter buffer minimum size in milliseconds." + }, + { + "jb_max", T_INT, + offsetof(PyObj_pjsua_media_config, jb_max), 0, + "Jitter buffer maximum size in milliseconds." + }, + { + "enable_ice", T_INT, + offsetof(PyObj_pjsua_media_config, enable_ice), 0, + "Enable ICE." + }, + { + "enable_turn", T_INT, + offsetof(PyObj_pjsua_media_config, enable_turn), 0, + "Enable TURN." + }, + { + "turn_server", T_OBJECT_EX, + offsetof(PyObj_pjsua_media_config, turn_server), 0, + "Specify the TURN server." + }, + { + "turn_conn_type", T_INT, + offsetof(PyObj_pjsua_media_config, turn_conn_type), 0, + "Specify TURN connection type." + }, + { + "turn_realm", T_OBJECT_EX, + offsetof(PyObj_pjsua_media_config, turn_realm), 0, + "Specify the TURN realm." + }, + { + "turn_username", T_OBJECT_EX, + offsetof(PyObj_pjsua_media_config, turn_username), 0, + "Specify the TURN username." + }, + { + "turn_passwd_type", T_INT, + offsetof(PyObj_pjsua_media_config, turn_passwd_type), 0, + "Specify TURN password type." + }, + { + "turn_passwd", T_OBJECT_EX, + offsetof(PyObj_pjsua_media_config, turn_passwd), 0, + "Specify the TURN password." + }, + + {NULL} /* Sentinel */ +}; + + +static PyObject *PyObj_pjsua_media_config_new(PyTypeObject *type, + PyObject *args, + PyObject *kwds) +{ + PyObj_pjsua_media_config *self; + + PJ_UNUSED_ARG(args); + PJ_UNUSED_ARG(kwds); + + self = (PyObj_pjsua_media_config*)type->tp_alloc(type, 0); + if (self != NULL) { + self->turn_server = PyString_FromString(""); + self->turn_realm = PyString_FromString(""); + self->turn_username = PyString_FromString(""); + self->turn_passwd = PyString_FromString(""); + } + + return (PyObject *)self; +} + +static void PyObj_pjsua_media_config_delete(PyObj_pjsua_media_config * self) +{ + Py_XDECREF(self->turn_server); + Py_XDECREF(self->turn_realm); + Py_XDECREF(self->turn_username); + Py_XDECREF(self->turn_passwd); + self->ob_type->tp_free((PyObject*)self); +} + +static void PyObj_pjsua_media_config_import(PyObj_pjsua_media_config *obj, + const pjsua_media_config *cfg) +{ + obj->clock_rate = cfg->clock_rate; + obj->snd_clock_rate = cfg->snd_clock_rate; + obj->channel_count = cfg->channel_count; + obj->audio_frame_ptime = cfg->audio_frame_ptime; + obj->snd_auto_close_time= cfg->snd_auto_close_time; + obj->max_media_ports = cfg->max_media_ports; + obj->has_ioqueue = cfg->has_ioqueue; + obj->thread_cnt = cfg->thread_cnt; + obj->quality = cfg->quality; + obj->ptime = cfg->ptime; + obj->no_vad = cfg->no_vad; + obj->ilbc_mode = cfg->ilbc_mode; + obj->jb_min = cfg->jb_min_pre; + obj->jb_max = cfg->jb_max; + obj->tx_drop_pct = cfg->tx_drop_pct; + obj->rx_drop_pct = cfg->rx_drop_pct; + obj->ec_options = cfg->ec_options; + obj->ec_tail_len = cfg->ec_tail_len; + obj->enable_ice = cfg->enable_ice; + obj->enable_turn = cfg->enable_turn; + Py_XDECREF(obj->turn_server); + obj->turn_server = PyString_FromPJ(&cfg->turn_server); + obj->turn_conn_type = cfg->turn_conn_type; + if (cfg->turn_auth_cred.type == PJ_STUN_AUTH_CRED_STATIC) { + const pj_stun_auth_cred *cred = &cfg->turn_auth_cred; + + Py_XDECREF(obj->turn_realm); + obj->turn_realm = PyString_FromPJ(&cred->data.static_cred.realm); + Py_XDECREF(obj->turn_username); + obj->turn_username = PyString_FromPJ(&cred->data.static_cred.username); + obj->turn_passwd_type = cred->data.static_cred.data_type; + Py_XDECREF(obj->turn_passwd); + obj->turn_passwd = PyString_FromPJ(&cred->data.static_cred.data); + } else { + Py_XDECREF(obj->turn_realm); + obj->turn_realm = PyString_FromString(""); + Py_XDECREF(obj->turn_username); + obj->turn_username = PyString_FromString(""); + obj->turn_passwd_type = 0; + Py_XDECREF(obj->turn_passwd); + obj->turn_passwd = PyString_FromString(""); + } +} + +static void PyObj_pjsua_media_config_export(pjsua_media_config *cfg, + const PyObj_pjsua_media_config *obj) +{ + cfg->clock_rate = obj->clock_rate; + cfg->snd_clock_rate = obj->snd_clock_rate; + cfg->channel_count = obj->channel_count; + cfg->audio_frame_ptime = obj->audio_frame_ptime; + cfg->snd_auto_close_time=obj->snd_auto_close_time; + cfg->max_media_ports = obj->max_media_ports; + cfg->has_ioqueue = obj->has_ioqueue; + cfg->thread_cnt = obj->thread_cnt; + cfg->quality = obj->quality; + cfg->ptime = obj->ptime; + cfg->no_vad = obj->no_vad; + cfg->jb_min_pre = obj->jb_min; + cfg->jb_max = obj->jb_max; + cfg->ilbc_mode = obj->ilbc_mode; + cfg->tx_drop_pct = obj->tx_drop_pct; + cfg->rx_drop_pct = obj->rx_drop_pct; + cfg->ec_options = obj->ec_options; + cfg->ec_tail_len = obj->ec_tail_len; + cfg->enable_ice = obj->enable_ice; + cfg->enable_turn = obj->enable_turn; + + if (cfg->enable_turn) { + cfg->turn_server = PyString_ToPJ(obj->turn_server); + cfg->turn_conn_type = obj->turn_conn_type; + cfg->turn_auth_cred.type = PJ_STUN_AUTH_CRED_STATIC; + cfg->turn_auth_cred.data.static_cred.realm = PyString_ToPJ(obj->turn_realm); + cfg->turn_auth_cred.data.static_cred.username = PyString_ToPJ(obj->turn_username); + cfg->turn_auth_cred.data.static_cred.data_type= obj->turn_passwd_type; + cfg->turn_auth_cred.data.static_cred.data = PyString_ToPJ(obj->turn_passwd); + } +} + + +/* + * PyTyp_pjsua_media_config + */ +static PyTypeObject PyTyp_pjsua_media_config = +{ + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_pjsua.Media_Config", /*tp_name*/ + sizeof(PyObj_pjsua_media_config),/*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyObj_pjsua_media_config_delete,/*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + "Media Config objects", /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + PyObj_pjsua_media_config_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyObj_pjsua_media_config_new, /* tp_new */ +}; + + +////////////////////////////////////////////////////////////////////////////// +/* + * PyObj_pjsua_config + * attribute list for config object + */ +typedef struct +{ + PyObject_HEAD + /* Type-specific fields go here. */ + unsigned max_calls; + unsigned thread_cnt; + PyObject *outbound_proxy; + PyObject *stun_domain; + PyObject *stun_host; + PyListObject *nameserver; + PyObj_pjsua_callback *cb; + PyObject *user_agent; +} PyObj_pjsua_config; + + +static void PyObj_pjsua_config_delete(PyObj_pjsua_config* self) +{ + Py_XDECREF(self->outbound_proxy); + Py_XDECREF(self->stun_domain); + Py_XDECREF(self->stun_host); + Py_XDECREF(self->nameserver); + Py_XDECREF(self->cb); + Py_XDECREF(self->user_agent); + self->ob_type->tp_free((PyObject*)self); +} + + +static void PyObj_pjsua_config_import(PyObj_pjsua_config *obj, + const pjsua_config *cfg) +{ + unsigned i; + + obj->max_calls = cfg->max_calls; + obj->thread_cnt = cfg->thread_cnt; + Py_XDECREF(obj->outbound_proxy); + if (cfg->outbound_proxy_cnt) + obj->outbound_proxy = PyString_FromPJ(&cfg->outbound_proxy[0]); + else + obj->outbound_proxy = PyString_FromString(""); + + Py_XDECREF(obj->stun_domain); + obj->stun_domain = PyString_FromPJ(&cfg->stun_domain); + Py_XDECREF(obj->stun_host); + obj->stun_host = PyString_FromPJ(&cfg->stun_host); + Py_XDECREF(obj->nameserver); + obj->nameserver = (PyListObject *)PyList_New(0); + for (i=0; inameserver_count; ++i) { + PyObject * str; + str = PyString_FromPJ(&cfg->nameserver[i]); + PyList_Append((PyObject *)obj->nameserver, str); + } + Py_XDECREF(obj->user_agent); + obj->user_agent = PyString_FromPJ(&cfg->user_agent); +} + + +static void PyObj_pjsua_config_export(pjsua_config *cfg, + PyObj_pjsua_config *obj) +{ + unsigned i; + + cfg->max_calls = obj->max_calls; + cfg->thread_cnt = obj->thread_cnt; + if (PyString_Size(obj->outbound_proxy) > 0) { + cfg->outbound_proxy_cnt = 1; + cfg->outbound_proxy[0] = PyString_ToPJ(obj->outbound_proxy); + } else { + cfg->outbound_proxy_cnt = 0; + } + cfg->nameserver_count = PyList_Size((PyObject*)obj->nameserver); + if (cfg->nameserver_count > PJ_ARRAY_SIZE(cfg->nameserver)) + cfg->nameserver_count = PJ_ARRAY_SIZE(cfg->nameserver); + for (i = 0; i < cfg->nameserver_count; i++) { + PyObject *item = PyList_GetItem((PyObject *)obj->nameserver,i); + cfg->nameserver[i] = PyString_ToPJ(item); + } + cfg->stun_domain = PyString_ToPJ(obj->stun_domain); + cfg->stun_host = PyString_ToPJ(obj->stun_host); + cfg->user_agent = PyString_ToPJ(obj->user_agent); + +} + + +static PyObject *PyObj_pjsua_config_new(PyTypeObject *type, + PyObject *args, + PyObject *kwds) +{ + PyObj_pjsua_config *self; + + PJ_UNUSED_ARG(args); + PJ_UNUSED_ARG(kwds); + + self = (PyObj_pjsua_config *)type->tp_alloc(type, 0); + if (self != NULL) { + self->user_agent = PyString_FromString(""); + self->outbound_proxy = PyString_FromString(""); + self->stun_domain = PyString_FromString(""); + self->stun_host = PyString_FromString(""); + self->cb = (PyObj_pjsua_callback *) + PyType_GenericNew(&PyTyp_pjsua_callback, NULL, NULL); + } + return (PyObject *)self; +} + + +/* + * PyObj_pjsua_config_members + * attribute list accessible from Python/C + */ +static PyMemberDef PyObj_pjsua_config_members[] = +{ + { + "max_calls", T_INT, + offsetof(PyObj_pjsua_config, max_calls), 0, + "Maximum calls to support (default: 4) " + }, + { + "thread_cnt", T_INT, + offsetof(PyObj_pjsua_config, thread_cnt), 0, + "Number of worker threads. Normally application will want to have at " + "least one worker thread, unless when it wants to poll the library " + "periodically, which in this case the worker thread can be set to " + "zero." + }, + { + "outbound_proxy", T_OBJECT_EX, + offsetof(PyObj_pjsua_config, outbound_proxy), 0, + "SIP URL of the outbound proxy (optional)" + }, + { + "stun_domain", T_OBJECT_EX, + offsetof(PyObj_pjsua_config, stun_domain), 0, + "Domain of the STUN server (optional). STUN server will be resolved " + "using DNS SRV resolution only when nameserver is configured. " + "Alternatively, if DNS SRV resolution for STUN is not desired, " + "application can specify the STUN server hostname or IP address " + "in stun_host attribute." + }, + { + "stun_host", T_OBJECT_EX, + offsetof(PyObj_pjsua_config, stun_host), 0, + "Hostname or IP address of the STUN server (optional)." + }, + { + "nameserver", T_OBJECT_EX, + offsetof(PyObj_pjsua_config, nameserver), 0, + "IP address of the nameserver." + }, + { + "cb", T_OBJECT_EX, offsetof(PyObj_pjsua_config, cb), 0, + "Application callback." + }, + { + "user_agent", T_OBJECT_EX, offsetof(PyObj_pjsua_config, user_agent), 0, + "User agent string (default empty)" + }, + {NULL} /* Sentinel */ +}; + + +/* + * PyTyp_pjsua_config + * type wrapper for config class + */ +static PyTypeObject PyTyp_pjsua_config = +{ + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_pjsua.Config", /*tp_name*/ + sizeof(PyObj_pjsua_config),/*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyObj_pjsua_config_delete,/*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + "Config object", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + PyObj_pjsua_config_members,/* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyObj_pjsua_config_new, /* tp_new */ + +}; + +////////////////////////////////////////////////////////////////////////////// +/* + * PyObj_pjsua_logging_config + * configuration class for logging_config object + */ +typedef struct +{ + PyObject_HEAD + /* Type-specific fields go here. */ + int msg_logging; + unsigned level; + unsigned console_level; + unsigned decor; + PyObject *log_filename; + PyObject *cb; +} PyObj_pjsua_logging_config; + + +/* + * PyObj_pjsua_logging_config_delete + * deletes a logging config from memory + */ +static void PyObj_pjsua_logging_config_delete(PyObj_pjsua_logging_config* self) +{ + Py_XDECREF(self->log_filename); + Py_XDECREF(self->cb); + self->ob_type->tp_free((PyObject*)self); +} + + +static void PyObj_pjsua_logging_config_import(PyObj_pjsua_logging_config *obj, + const pjsua_logging_config *cfg) +{ + obj->msg_logging = cfg->msg_logging; + obj->level = cfg->level; + obj->console_level = cfg->console_level; + obj->decor = cfg->decor; + Py_XDECREF(obj->log_filename); + obj->log_filename = PyString_FromPJ(&cfg->log_filename); +} + +static void PyObj_pjsua_logging_config_export(pjsua_logging_config *cfg, + PyObj_pjsua_logging_config *obj) +{ + cfg->msg_logging = obj->msg_logging; + cfg->level = obj->level; + cfg->console_level = obj->console_level; + cfg->decor = obj->decor; + cfg->log_filename = PyString_ToPJ(obj->log_filename); +} + + +/* + * PyObj_pjsua_logging_config_new + * constructor for logging_config object + */ +static PyObject * PyObj_pjsua_logging_config_new(PyTypeObject *type, + PyObject *args, + PyObject *kwds) +{ + PyObj_pjsua_logging_config *self; + + PJ_UNUSED_ARG(args); + PJ_UNUSED_ARG(kwds); + + self = (PyObj_pjsua_logging_config *)type->tp_alloc(type, 0); + if (self != NULL) { + self->log_filename = PyString_FromString(""); + self->cb = Py_BuildValue(""); + } + + return (PyObject *)self; +} + + +/* + * PyObj_pjsua_logging_config_members + */ +static PyMemberDef PyObj_pjsua_logging_config_members[] = +{ + { + "msg_logging", T_INT, + offsetof(PyObj_pjsua_logging_config, msg_logging), 0, + "Log incoming and outgoing SIP message? Yes!" + }, + { + "level", T_INT, + offsetof(PyObj_pjsua_logging_config, level), 0, + "Input verbosity level. Value 5 is reasonable." + }, + { + "console_level", T_INT, + offsetof(PyObj_pjsua_logging_config, console_level), + 0, "Verbosity level for console. Value 4 is reasonable." + }, + { + "decor", T_INT, + offsetof(PyObj_pjsua_logging_config, decor), 0, + "Log decoration" + }, + { + "log_filename", T_OBJECT_EX, + offsetof(PyObj_pjsua_logging_config, log_filename), 0, + "Optional log filename" + }, + { + "cb", T_OBJECT_EX, + offsetof(PyObj_pjsua_logging_config, cb), 0, + "Optional callback function to be called to write log to application " + "specific device. This function will be called forlog messages on " + "input verbosity level." + }, + {NULL} /* Sentinel */ +}; + + + + +/* + * PyTyp_pjsua_logging_config + */ +static PyTypeObject PyTyp_pjsua_logging_config = +{ + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_pjsua.Logging_Config", /*tp_name*/ + sizeof(PyObj_pjsua_logging_config), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyObj_pjsua_logging_config_delete,/*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + "Logging Config objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + PyObj_pjsua_logging_config_members,/* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyObj_pjsua_logging_config_new, /* tp_new */ + +}; + + +////////////////////////////////////////////////////////////////////////////// +/* + * PyObj_pjsua_msg_data + * typewrapper for MessageData class + * !modified @ 061206 + */ +typedef struct +{ + PyObject_HEAD + /* Type-specific fields go here. */ + PyObject * hdr_list; + PyObject * content_type; + PyObject * msg_body; +} PyObj_pjsua_msg_data; + + +/* + * PyObj_pjsua_msg_data_delete + * deletes a msg_data + * !modified @ 061206 + */ +static void PyObj_pjsua_msg_data_delete(PyObj_pjsua_msg_data* self) +{ + Py_XDECREF(self->hdr_list); + Py_XDECREF(self->content_type); + Py_XDECREF(self->msg_body); + self->ob_type->tp_free((PyObject*)self); +} + + +/* + * PyObj_pjsua_msg_data_new + * constructor for msg_data object + * !modified @ 061206 + */ +static PyObject * PyObj_pjsua_msg_data_new(PyTypeObject *type, + PyObject *args, + PyObject *kwds) +{ + PyObj_pjsua_msg_data *self; + + PJ_UNUSED_ARG(args); + PJ_UNUSED_ARG(kwds); + + self = (PyObj_pjsua_msg_data *)type->tp_alloc(type, 0); + if (self != NULL) { + self->hdr_list = PyList_New(0); + self->content_type = PyString_FromString(""); + self->msg_body = PyString_FromString(""); + } + + return (PyObject *)self; +} + + +/* + * PyObj_pjsua_msg_data_members + * !modified @ 061206 + */ +static PyMemberDef PyObj_pjsua_msg_data_members[] = +{ + { + "hdr_list", T_OBJECT_EX, + offsetof(PyObj_pjsua_msg_data, hdr_list), 0, + "Additional message headers as linked list of strings." + }, + { + "content_type", T_OBJECT_EX, + offsetof(PyObj_pjsua_msg_data, content_type), 0, + "MIME type of optional message body." + }, + { + "msg_body", T_OBJECT_EX, + offsetof(PyObj_pjsua_msg_data, msg_body), 0, + "Optional message body." + }, + {NULL} /* Sentinel */ +}; + + +/* + * PyTyp_pjsua_msg_data + */ +static PyTypeObject PyTyp_pjsua_msg_data = +{ + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_pjsua.Msg_Data", /*tp_name*/ + sizeof(PyObj_pjsua_msg_data), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyObj_pjsua_msg_data_delete,/*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + "msg_data objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + PyObj_pjsua_msg_data_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyObj_pjsua_msg_data_new, /* tp_new */ + +}; + + +////////////////////////////////////////////////////////////////////////////// +/* + * PyObj_pjsua_transport_config + * Transport configuration for creating UDP transports for both SIP + * and media. + */ +typedef struct +{ + PyObject_HEAD + /* Type-specific fields go here. */ + unsigned port; + PyObject *public_addr; + PyObject *bound_addr; +} PyObj_pjsua_transport_config; + + +/* + * PyObj_pjsua_transport_config_delete + * deletes a transport config from memory + */ +static void PyObj_pjsua_transport_config_delete(PyObj_pjsua_transport_config* self) +{ + Py_XDECREF(self->public_addr); + Py_XDECREF(self->bound_addr); + self->ob_type->tp_free((PyObject*)self); +} + + +static void PyObj_pjsua_transport_config_export(pjsua_transport_config *cfg, + PyObj_pjsua_transport_config *obj) +{ + pjsua_transport_config_default(cfg); + cfg->public_addr = PyString_ToPJ(obj->public_addr); + cfg->bound_addr = PyString_ToPJ(obj->bound_addr); + cfg->port = obj->port; + +} + +static void PyObj_pjsua_transport_config_import(PyObj_pjsua_transport_config *obj, + const pjsua_transport_config *cfg) +{ + Py_XDECREF(obj->public_addr); + obj->public_addr = PyString_FromPJ(&cfg->public_addr); + + Py_XDECREF(obj->bound_addr); + obj->bound_addr = PyString_FromPJ(&cfg->bound_addr); + + obj->port = cfg->port; +} + + +/* + * PyObj_pjsua_transport_config_new + * constructor for transport_config object + */ +static PyObject * PyObj_pjsua_transport_config_new(PyTypeObject *type, + PyObject *args, + PyObject *kwds) +{ + PyObj_pjsua_transport_config *self; + + PJ_UNUSED_ARG(args); + PJ_UNUSED_ARG(kwds); + + self = (PyObj_pjsua_transport_config *)type->tp_alloc(type, 0); + if (self != NULL) { + self->public_addr = PyString_FromString(""); + self->bound_addr = PyString_FromString(""); + } + + return (PyObject *)self; +} + + +/* + * PyObj_pjsua_transport_config_members + */ +static PyMemberDef PyObj_pjsua_transport_config_members[] = +{ + { + "port", T_INT, + offsetof(PyObj_pjsua_transport_config, port), 0, + "UDP port number to bind locally. This setting MUST be specified " + "even when default port is desired. If the value is zero, the " + "transport will be bound to any available port, and application " + "can query the port by querying the transport info." + }, + { + "public_addr", T_OBJECT_EX, + offsetof(PyObj_pjsua_transport_config, public_addr), 0, + "Optional address to advertise as the address of this transport. " + "Application can specify any address or hostname for this field, " + "for example it can point to one of the interface address in the " + "system, or it can point to the public address of a NAT router " + "where port mappings have been configured for the application." + }, + { + "bound_addr", T_OBJECT_EX, + offsetof(PyObj_pjsua_transport_config, bound_addr), 0, + "Optional address where the socket should be bound to. This option " + "SHOULD only be used to selectively bind the socket to particular " + "interface (instead of 0.0.0.0), and SHOULD NOT be used to set the " + "published address of a transport (the public_addr field should be " + "used for that purpose)." + }, + {NULL} /* Sentinel */ +}; + + + + +/* + * PyTyp_pjsua_transport_config + */ +static PyTypeObject PyTyp_pjsua_transport_config = +{ + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_pjsua.Transport_Config", /*tp_name*/ + sizeof(PyObj_pjsua_transport_config), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyObj_pjsua_transport_config_delete,/*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + "Transport setting", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + PyObj_pjsua_transport_config_members,/* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyObj_pjsua_transport_config_new,/* tp_new */ +}; + + +////////////////////////////////////////////////////////////////////////////// +/* + * PyObj_pjsua_transport_info + * Transport info + */ +typedef struct +{ + PyObject_HEAD + /* Type-specific fields go here. */ + int id; + int type; + PyObject *type_name; + PyObject *info; + unsigned flag; + PyObject *addr; + unsigned port; + unsigned usage_count; +} PyObj_pjsua_transport_info; + + +/* + * PyObj_pjsua_transport_info_delete + * deletes a transport info from memory + */ +static void PyObj_pjsua_transport_info_delete(PyObj_pjsua_transport_info* self) +{ + Py_XDECREF(self->type_name); + Py_XDECREF(self->info); + Py_XDECREF(self->addr); + self->ob_type->tp_free((PyObject*)self); +} + + +static void PyObj_pjsua_transport_info_import(PyObj_pjsua_transport_info *obj, + const pjsua_transport_info *info) +{ + obj->id = info->id; + obj->type = info->type; + obj->type_name = PyString_FromPJ(&info->type_name); + obj->info = PyString_FromPJ(&info->info); + obj->flag = info->flag; + obj->addr = PyString_FromPJ(&info->local_name.host); + obj->port = info->local_name.port; + obj->usage_count= info->usage_count; +} + +/* + * PyObj_pjsua_transport_info_new + * constructor for transport_info object + */ +static PyObject * PyObj_pjsua_transport_info_new(PyTypeObject *type, + PyObject *args, + PyObject *kwds) +{ + PyObj_pjsua_transport_info *self; + + PJ_UNUSED_ARG(args); + PJ_UNUSED_ARG(kwds); + + self = (PyObj_pjsua_transport_info *)type->tp_alloc(type, 0); + if (self != NULL) + { + self->type_name = PyString_FromString(""); + self->info = PyString_FromString(""); + self->addr = PyString_FromString(""); + } + + return (PyObject *)self; +} + + +/* + * PyObj_pjsua_transport_info_members + */ +static PyMemberDef PyObj_pjsua_transport_info_members[] = +{ + { + "id", T_INT, + offsetof(PyObj_pjsua_transport_info, id), 0, + "PJSUA transport identification." + }, + { + "type", T_INT, + offsetof(PyObj_pjsua_transport_info, type), 0, + "Transport type." + }, + { + "type_name", T_OBJECT_EX, + offsetof(PyObj_pjsua_transport_info, type_name), 0, + "Transport type name." + }, + { + "info", T_OBJECT_EX, + offsetof(PyObj_pjsua_transport_info, info), 0, + "Transport string info/description." + }, + { + "flag", T_INT, + offsetof(PyObj_pjsua_transport_info, flag), 0, + "Transport flag (see ##pjsip_transport_flags_e)." + }, + { + "addr", T_OBJECT_EX, + offsetof(PyObj_pjsua_transport_info, addr), 0, + "Published address (or transport address name)." + }, + { + "port", T_INT, + offsetof(PyObj_pjsua_transport_info, port), 0, + "Published port number." + }, + { + "usage_count", T_INT, + offsetof(PyObj_pjsua_transport_info, usage_count), 0, + "Current number of objects currently referencing this transport." + }, + {NULL} /* Sentinel */ +}; + + +/* + * PyTyp_pjsua_transport_info + */ +static PyTypeObject PyTyp_pjsua_transport_info = +{ + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_pjsua.Transport_Info", /*tp_name*/ + sizeof(PyObj_pjsua_transport_info), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyObj_pjsua_transport_info_delete,/*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + "Transport Info objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + PyObj_pjsua_transport_info_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyObj_pjsua_transport_info_new, /* tp_new */ + +}; + + +////////////////////////////////////////////////////////////////////////////// + +/* + * PyObj_pjsua_acc_config + * Acc Config + */ +typedef struct +{ + PyObject_HEAD + /* Type-specific fields go here. */ + int priority; + PyObject *id; + PyObject *reg_uri; + int publish_enabled; + int mwi_enabled; + PyObject *force_contact; + PyListObject *proxy; + unsigned reg_timeout; + unsigned reg_delay_before_refresh; + PyListObject *cred_info; + int transport_id; + int auth_initial_send; + PyObject *auth_initial_algorithm; + PyObject *pidf_tuple_id; + PyObject *contact_params; + PyObject *contact_uri_params; + int require_100rel; + int use_timer; + unsigned timer_se; + unsigned timer_min_se; + int allow_contact_rewrite; + int ka_interval; + PyObject *ka_data; + unsigned use_srtp; + unsigned srtp_secure_signaling; +} PyObj_pjsua_acc_config; + + +/* + * PyObj_pjsua_acc_config_delete + * deletes a acc_config from memory + */ +static void PyObj_pjsua_acc_config_delete(PyObj_pjsua_acc_config* self) +{ + Py_XDECREF(self->id); + Py_XDECREF(self->reg_uri); + Py_XDECREF(self->force_contact); + Py_XDECREF(self->proxy); + Py_XDECREF(self->cred_info); + Py_XDECREF(self->auth_initial_algorithm); + Py_XDECREF(self->pidf_tuple_id); + Py_XDECREF(self->contact_params); + Py_XDECREF(self->contact_uri_params); + Py_XDECREF(self->ka_data); + self->ob_type->tp_free((PyObject*)self); +} + + +static void PyObj_pjsua_acc_config_import(PyObj_pjsua_acc_config *obj, + const pjsua_acc_config *cfg) +{ + unsigned i; + + obj->priority = cfg->priority; + Py_XDECREF(obj->id); + obj->id = PyString_FromPJ(&cfg->id); + Py_XDECREF(obj->reg_uri); + obj->reg_uri = PyString_FromPJ(&cfg->reg_uri); + obj->publish_enabled = cfg->publish_enabled; + obj->mwi_enabled = cfg->mwi_enabled; + Py_XDECREF(obj->force_contact); + obj->force_contact = PyString_FromPJ(&cfg->force_contact); + Py_XDECREF(obj->proxy); + obj->proxy = (PyListObject *)PyList_New(0); + for (i=0; iproxy_cnt; ++i) { + PyObject * str; + str = PyString_FromPJ(&cfg->proxy[i]); + PyList_Append((PyObject *)obj->proxy, str); + } + + obj->reg_timeout = cfg->reg_timeout; + obj->reg_delay_before_refresh = cfg->reg_delay_before_refresh; + + Py_XDECREF(obj->cred_info); + obj->cred_info = (PyListObject *)PyList_New(0); + for (i=0; icred_count; ++i) { + PyObj_pjsip_cred_info * ci; + + ci = (PyObj_pjsip_cred_info *) + PyObj_pjsip_cred_info_new(&PyTyp_pjsip_cred_info,NULL,NULL); + PyObj_pjsip_cred_info_import(ci, &cfg->cred_info[i]); + PyList_Append((PyObject *)obj->cred_info, (PyObject *)ci); + } + + obj->transport_id = cfg->transport_id; + + obj->auth_initial_send = cfg->auth_pref.initial_auth; + Py_XDECREF(obj->auth_initial_algorithm); + obj->auth_initial_algorithm = PyString_FromPJ(&cfg->auth_pref.algorithm); + Py_XDECREF(obj->pidf_tuple_id); + obj->pidf_tuple_id = PyString_FromPJ(&cfg->pidf_tuple_id); + Py_XDECREF(obj->contact_params); + obj->contact_params = PyString_FromPJ(&cfg->contact_params); + Py_XDECREF(obj->contact_uri_params); + obj->contact_uri_params = PyString_FromPJ(&cfg->contact_uri_params); + obj->require_100rel = cfg->require_100rel; + obj->use_timer = cfg->use_timer; + obj->timer_se = cfg->timer_setting.sess_expires; + obj->timer_min_se = cfg->timer_setting.min_se; + obj->allow_contact_rewrite = cfg->allow_contact_rewrite; + obj->ka_interval = cfg->ka_interval; + Py_XDECREF(obj->ka_data); + obj->ka_data = PyString_FromPJ(&cfg->ka_data); + obj->use_srtp = cfg->use_srtp; + obj->srtp_secure_signaling = cfg->srtp_secure_signaling; +} + +static void PyObj_pjsua_acc_config_export(pjsua_acc_config *cfg, + PyObj_pjsua_acc_config *obj) +{ + unsigned i; + + cfg->priority = obj->priority; + cfg->id = PyString_ToPJ(obj->id); + cfg->reg_uri = PyString_ToPJ(obj->reg_uri); + cfg->publish_enabled = obj->publish_enabled; + cfg->mwi_enabled = obj->mwi_enabled; + cfg->force_contact = PyString_ToPJ(obj->force_contact); + + cfg->proxy_cnt = PyList_Size((PyObject*)obj->proxy); + if (cfg->proxy_cnt > PJ_ARRAY_SIZE(cfg->proxy)) + cfg->proxy_cnt = PJ_ARRAY_SIZE(cfg->proxy); + for (i = 0; i < cfg->proxy_cnt; i++) { + PyObject *item = PyList_GetItem((PyObject *)obj->proxy, i); + cfg->proxy[i] = PyString_ToPJ(item); + } + + cfg->reg_timeout = obj->reg_timeout; + cfg->reg_delay_before_refresh = obj->reg_delay_before_refresh; + + cfg->cred_count = PyList_Size((PyObject*)obj->cred_info); + if (cfg->cred_count > PJ_ARRAY_SIZE(cfg->cred_info)) + cfg->cred_count = PJ_ARRAY_SIZE(cfg->cred_info); + for (i = 0; i < cfg->cred_count; i++) { + PyObj_pjsip_cred_info *ci; + ci = (PyObj_pjsip_cred_info*) + PyList_GetItem((PyObject *)obj->cred_info, i); + PyObj_pjsip_cred_info_export(&cfg->cred_info[i], ci); + } + + cfg->transport_id = obj->transport_id; + cfg->auth_pref.initial_auth = obj->auth_initial_send; + cfg->auth_pref.algorithm = PyString_ToPJ(obj->auth_initial_algorithm); + cfg->pidf_tuple_id = PyString_ToPJ(obj->pidf_tuple_id); + cfg->contact_params = PyString_ToPJ(obj->contact_params); + cfg->contact_uri_params = PyString_ToPJ(obj->contact_uri_params); + cfg->require_100rel = obj->require_100rel; + cfg->use_timer = obj->use_timer; + cfg->timer_setting.sess_expires = obj->timer_se; + cfg->timer_setting.min_se = obj->timer_min_se; + cfg->allow_contact_rewrite = obj->allow_contact_rewrite; + cfg->ka_interval = obj->ka_interval; + cfg->ka_data = PyString_ToPJ(obj->ka_data); + cfg->use_srtp = obj->use_srtp; + cfg->srtp_secure_signaling = obj->srtp_secure_signaling; +} + + +/* + * PyObj_pjsua_acc_config_new + * constructor for acc_config object + */ +static PyObject * PyObj_pjsua_acc_config_new(PyTypeObject *type, + PyObject *args, + PyObject *kwds) +{ + PyObj_pjsua_acc_config *self; + + PJ_UNUSED_ARG(args); + PJ_UNUSED_ARG(kwds); + + self = (PyObj_pjsua_acc_config *)type->tp_alloc(type, 0); + if (self != NULL) { + self->id = PyString_FromString(""); + self->reg_uri = PyString_FromString(""); + self->force_contact = PyString_FromString(""); + self->proxy = (PyListObject *)PyList_New(0); + self->cred_info = (PyListObject *)PyList_New(0); + self->auth_initial_algorithm = PyString_FromString(""); + self->pidf_tuple_id = PyString_FromString(""); + self->contact_params = PyString_FromString(""); + self->contact_uri_params = PyString_FromString(""); + self->ka_data = PyString_FromString(""); + } + + return (PyObject *)self; +} + + + +/* + * PyObj_pjsua_acc_config_members + */ +static PyMemberDef PyObj_pjsua_acc_config_members[] = +{ + { + "priority", T_INT, offsetof(PyObj_pjsua_acc_config, priority), 0, + "Account priority, which is used to control the order of matching " + "incoming/outgoing requests. The higher the number means the higher " + "the priority is, and the account will be matched first. " + }, + { + "id", T_OBJECT_EX, + offsetof(PyObj_pjsua_acc_config, id), 0, + "The full SIP URL for the account. " + "The value can take name address or URL format, " + "and will look something like 'sip:account@serviceprovider'. " + "This field is mandatory." + }, + { + "reg_uri", T_OBJECT_EX, + offsetof(PyObj_pjsua_acc_config, reg_uri), 0, + "This is the URL to be put in the request URI for the registration, " + "and will look something like 'sip:serviceprovider'. " + "This field should be specified if registration is desired. " + "If the value is empty, no account registration will be performed. " + }, + { + "publish_enabled", T_INT, + offsetof(PyObj_pjsua_acc_config, publish_enabled), 0, + "Publish presence? " + }, + { + "mwi_enabled", T_INT, + offsetof(PyObj_pjsua_acc_config, mwi_enabled), 0, + "Enable MWI subscription " + }, + { + "force_contact", T_OBJECT_EX, + offsetof(PyObj_pjsua_acc_config, force_contact), 0, + "Optional URI to be put as Contact for this account. " + "It is recommended that this field is left empty, " + "so that the value will be calculated automatically " + "based on the transport address. " + }, + { + "proxy", T_OBJECT_EX, + offsetof(PyObj_pjsua_acc_config, proxy), 0, + "Optional URI of the proxies to be visited for all outgoing requests " + "that are using this account (REGISTER, INVITE, etc). Application need " + "to specify these proxies if the service provider requires " + "that requests destined towards its network should go through certain " + "proxies first (for example, border controllers)." + }, + { + "reg_timeout", T_INT, + offsetof(PyObj_pjsua_acc_config, reg_timeout), 0, + "Optional interval for registration, in seconds. " + "If the value is zero, default interval will be used " + "(PJSUA_REG_INTERVAL, 55 seconds). " + }, + { + "reg_delay_before_refresh", T_INT, + offsetof(PyObj_pjsua_acc_config, reg_delay_before_refresh), 0, + "Specify the number of seconds to refresh the client registration" + "before the registration expires." + "(PJSIP_REGISTER_CLIENT_DELAY_BEFORE_REFRESH, 5 seconds). " + }, + { + "cred_info", T_OBJECT_EX, + offsetof(PyObj_pjsua_acc_config, cred_info), 0, + "Array of credentials. If registration is desired, normally there " + "should be at least one credential specified, to successfully " + "authenticate against the service provider. More credentials can " + "be specified, for example when the requests are expected to be " + "challenged by the proxies in the route set." + }, + { + "transport_id", T_INT, + offsetof(PyObj_pjsua_acc_config, transport_id), 0, + "Optionally bind this account to specific transport. This normally is" + " not a good idea, as account should be able to send requests using" + " any available transports according to the destination. But some" + " application may want to have explicit control over the transport to" + " use, so in that case it can set this field." + }, + { + "auth_initial_send", T_INT, + offsetof(PyObj_pjsua_acc_config, auth_initial_send), 0, + "Send empty initial authorization header." + }, + { + "auth_initial_algorithm", T_OBJECT_EX, + offsetof(PyObj_pjsua_acc_config, auth_initial_algorithm), 0, + "Specify algorithm in empty initial authorization header." + }, + { + "pidf_tuple_id", T_OBJECT_EX, + offsetof(PyObj_pjsua_acc_config, pidf_tuple_id), 0, + "PIDF tuple id." + }, + { + "contact_params", T_OBJECT_EX, + offsetof(PyObj_pjsua_acc_config, contact_params), 0, + "Additional parameters for Contact header." + }, + { + "contact_uri_params", T_OBJECT_EX, + offsetof(PyObj_pjsua_acc_config, contact_uri_params), 0, + "Additional parameters for Contact URI." + }, + { + "require_100rel", T_INT, + offsetof(PyObj_pjsua_acc_config, require_100rel), 0, + "Require reliable provisional response." + }, + { + "use_timer", T_INT, + offsetof(PyObj_pjsua_acc_config, use_timer), 0, + "Use SIP session timers? (default=1)" + "0:inactive, 1:optional, 2:mandatory, 3:always" + }, + { + "timer_se", T_INT, + offsetof(PyObj_pjsua_acc_config, timer_se), 0, + "Session timer expiration period, in seconds." + }, + { + "timer_min_se", T_INT, + offsetof(PyObj_pjsua_acc_config, timer_min_se), 0, + "Session timer minimum expiration period, in seconds." + }, + { + "allow_contact_rewrite", T_INT, + offsetof(PyObj_pjsua_acc_config, allow_contact_rewrite), 0, + "Re-REGISTER if behind symmetric NAT." + }, + { + "ka_interval", T_INT, + offsetof(PyObj_pjsua_acc_config, ka_interval), 0, + "Keep-alive interval." + }, + { + "ka_data", T_OBJECT_EX, + offsetof(PyObj_pjsua_acc_config, ka_data), 0, + "Keep-alive data." + }, + { + "use_srtp", T_INT, + offsetof(PyObj_pjsua_acc_config, use_srtp), 0, + "Specify SRTP usage." + }, + { + "srtp_secure_signaling", T_INT, + offsetof(PyObj_pjsua_acc_config, srtp_secure_signaling), 0, + "Specify if SRTP requires secure signaling to be used." + }, + + {NULL} /* Sentinel */ +}; + + + + +/* + * PyTyp_pjsua_acc_config + */ +static PyTypeObject PyTyp_pjsua_acc_config = +{ + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_pjsua.Acc_Config", /*tp_name*/ + sizeof(PyObj_pjsua_acc_config), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyObj_pjsua_acc_config_delete,/*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + "Account settings", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + PyObj_pjsua_acc_config_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyObj_pjsua_acc_config_new, /* tp_new */ + +}; + + +////////////////////////////////////////////////////////////////////////////// +/* + * PyObj_pjsua_acc_info + * Acc Info + */ +typedef struct +{ + PyObject_HEAD + /* Type-specific fields go here. */ + int id; + int is_default; + PyObject *acc_uri; + int has_registration; + int expires; + int status; + PyObject *status_text; + int online_status; + PyObject *online_status_text; +} PyObj_pjsua_acc_info; + + +/* + * PyObj_pjsua_acc_info_delete + * deletes a acc_info from memory + */ +static void PyObj_pjsua_acc_info_delete(PyObj_pjsua_acc_info* self) +{ + Py_XDECREF(self->acc_uri); + Py_XDECREF(self->status_text); + Py_XDECREF(self->online_status_text); + self->ob_type->tp_free((PyObject*)self); +} + + +static void PyObj_pjsua_acc_info_import(PyObj_pjsua_acc_info *obj, + const pjsua_acc_info *info) +{ + obj->id = info->id; + obj->is_default = info->is_default; + Py_XDECREF(obj->acc_uri); + obj->acc_uri = PyString_FromPJ(&info->acc_uri); + obj->has_registration = info->has_registration; + obj->expires = info->expires; + obj->status = info->status; + Py_XDECREF(obj->status_text); + obj->status_text= PyString_FromPJ(&info->status_text); + obj->online_status = info->online_status; + Py_XDECREF(obj->online_status_text); + obj->online_status_text = PyString_FromPJ(&info->online_status_text); +} + + +/* + * PyObj_pjsua_acc_info_new + * constructor for acc_info object + */ +static PyObject * PyObj_pjsua_acc_info_new(PyTypeObject *type, + PyObject *args, + PyObject *kwds) +{ + PyObj_pjsua_acc_info *self; + + PJ_UNUSED_ARG(args); + PJ_UNUSED_ARG(kwds); + + self = (PyObj_pjsua_acc_info *)type->tp_alloc(type, 0); + if (self != NULL) { + self->acc_uri = PyString_FromString(""); + self->status_text = PyString_FromString(""); + self->online_status_text = PyString_FromString(""); + } + + return (PyObject *)self; +} + +/* + * acc_info_members + */ +static PyMemberDef acc_info_members[] = +{ + { + "id", T_INT, + offsetof(PyObj_pjsua_acc_info, id), 0, + "The account ID." + }, + { + "is_default", T_INT, + offsetof(PyObj_pjsua_acc_info, is_default), 0, + "Flag to indicate whether this is the default account. " + }, + { + "acc_uri", T_OBJECT_EX, + offsetof(PyObj_pjsua_acc_info, acc_uri), 0, + "Account URI" + }, + { + "has_registration", T_INT, + offsetof(PyObj_pjsua_acc_info, has_registration), 0, + "Flag to tell whether this account has registration setting " + "(reg_uri is not empty)." + }, + { + "expires", T_INT, + offsetof(PyObj_pjsua_acc_info, expires), 0, + "An up to date expiration interval for account registration session." + }, + { + "status", T_INT, + offsetof(PyObj_pjsua_acc_info, status), 0, + "Last registration status code. If status code is zero, " + "the account is currently not registered. Any other value indicates " + "the SIP status code of the registration. " + }, + { + "status_text", T_OBJECT_EX, + offsetof(PyObj_pjsua_acc_info, status_text), 0, + "String describing the registration status." + }, + { + "online_status", T_INT, + offsetof(PyObj_pjsua_acc_info, online_status), 0, + "Presence online status for this account. " + }, + { + "online_status_text", T_OBJECT_EX, + offsetof(PyObj_pjsua_acc_info, online_status_text), 0, + "Presence online status text." + }, + {NULL} /* Sentinel */ +}; + + + + +/* + * PyTyp_pjsua_acc_info + */ +static PyTypeObject PyTyp_pjsua_acc_info = +{ + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_pjsua.Acc_Info", /*tp_name*/ + sizeof(PyObj_pjsua_acc_info), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyObj_pjsua_acc_info_delete,/*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + "Account info", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + NULL, /* tp_methods */ + acc_info_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyObj_pjsua_acc_info_new, /* tp_new */ + +}; + + +////////////////////////////////////////////////////////////////////////////// + +/* + * PyObj_pjsua_buddy_config + * Buddy Config + */ +typedef struct +{ + PyObject_HEAD + /* Type-specific fields go here. */ + PyObject *uri; + int subscribe; +} PyObj_pjsua_buddy_config; + + +/* + * PyObj_pjsua_buddy_config_delete + * deletes a buddy_config from memory + */ +static void PyObj_pjsua_buddy_config_delete(PyObj_pjsua_buddy_config* self) +{ + Py_XDECREF(self->uri); + self->ob_type->tp_free((PyObject*)self); +} + + +static void PyObj_pjsua_buddy_config_import(PyObj_pjsua_buddy_config *obj, + const pjsua_buddy_config *cfg) +{ + Py_XDECREF(obj->uri); + obj->uri = PyString_FromPJ(&cfg->uri); + obj->subscribe = cfg->subscribe; +} + + +static void PyObj_pjsua_buddy_config_export(pjsua_buddy_config *cfg, + PyObj_pjsua_buddy_config *obj) +{ + cfg->uri = PyString_ToPJ(obj->uri); + cfg->subscribe = obj->subscribe; + cfg->user_data = NULL; +} + + +/* + * PyObj_pjsua_buddy_config_new + * constructor for buddy_config object + */ +static PyObject *PyObj_pjsua_buddy_config_new(PyTypeObject *type, + PyObject *args, + PyObject *kwds) +{ + PyObj_pjsua_buddy_config *self; + + PJ_UNUSED_ARG(args); + PJ_UNUSED_ARG(kwds); + + self = (PyObj_pjsua_buddy_config *)type->tp_alloc(type, 0); + if (self != NULL) { + self->uri = PyString_FromString(""); + } + return (PyObject *)self; +} + +/* + * PyObj_pjsua_buddy_config_members + */ +static PyMemberDef PyObj_pjsua_buddy_config_members[] = +{ + + { + "uri", T_OBJECT_EX, + offsetof(PyObj_pjsua_buddy_config, uri), 0, + "TBuddy URL or name address." + }, + + { + "subscribe", T_INT, + offsetof(PyObj_pjsua_buddy_config, subscribe), 0, + "Specify whether presence subscription should start immediately. " + }, + + {NULL} /* Sentinel */ +}; + + + + +/* + * PyTyp_pjsua_buddy_config + */ +static PyTypeObject PyTyp_pjsua_buddy_config = +{ + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_pjsua.Buddy_Config", /*tp_name*/ + sizeof(PyObj_pjsua_buddy_config),/*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyObj_pjsua_buddy_config_delete,/*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + "Buddy config", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + PyObj_pjsua_buddy_config_members,/* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyObj_pjsua_buddy_config_new, /* tp_new */ + +}; + +////////////////////////////////////////////////////////////////////////////// +/* + * PyObj_pjsua_buddy_info + * Buddy Info + */ +typedef struct +{ + PyObject_HEAD + /* Type-specific fields go here. */ + int id; + PyObject *uri; + PyObject *contact; + int status; + PyObject *status_text; + int monitor_pres; + int activity; + int sub_state; + PyObject *sub_term_reason; +} PyObj_pjsua_buddy_info; + + +/* + * PyObj_pjsua_buddy_info_delete + * deletes a buddy_info from memory + * !modified @ 071206 + */ +static void PyObj_pjsua_buddy_info_delete(PyObj_pjsua_buddy_info* self) +{ + Py_XDECREF(self->uri); + Py_XDECREF(self->contact); + Py_XDECREF(self->status_text); + Py_XDECREF(self->sub_term_reason); + + self->ob_type->tp_free((PyObject*)self); +} + + +static void PyObj_pjsua_buddy_info_import(PyObj_pjsua_buddy_info *obj, + const pjsua_buddy_info *info) +{ + obj->id = info->id; + Py_XDECREF(obj->uri); + obj->uri = PyString_FromPJ(&info->uri); + Py_XDECREF(obj->contact); + obj->contact = PyString_FromPJ(&info->contact); + obj->status = info->status; + Py_XDECREF(obj->status_text); + obj->status_text = PyString_FromPJ(&info->status_text); + obj->monitor_pres = info->monitor_pres; + obj->activity = info->rpid.activity; + obj->sub_state = info->sub_state; + Py_XDECREF(obj->sub_term_reason); + obj->sub_term_reason = PyString_FromPJ(&info->sub_term_reason); +} + + +/* + * PyObj_pjsua_buddy_info_new + * constructor for buddy_info object + * !modified @ 071206 + */ +static PyObject * PyObj_pjsua_buddy_info_new(PyTypeObject *type, + PyObject *args, + PyObject *kwds) +{ + PyObj_pjsua_buddy_info *self; + + PJ_UNUSED_ARG(args); + PJ_UNUSED_ARG(kwds); + + self = (PyObj_pjsua_buddy_info *)type->tp_alloc(type, 0); + if (self != NULL) { + self->uri = PyString_FromString(""); + self->contact = PyString_FromString(""); + self->status_text = PyString_FromString(""); + self->sub_term_reason = PyString_FromString(""); + } + return (PyObject *)self; +} + +/* + * PyObj_pjsua_buddy_info_members + * !modified @ 071206 + */ +static PyMemberDef PyObj_pjsua_buddy_info_members[] = +{ + { + "id", T_INT, + offsetof(PyObj_pjsua_buddy_info, id), 0, + "The buddy ID." + }, + { + "uri", T_OBJECT_EX, + offsetof(PyObj_pjsua_buddy_info, uri), 0, + "The full URI of the buddy, as specified in the configuration. " + }, + { + "contact", T_OBJECT_EX, + offsetof(PyObj_pjsua_buddy_info, contact), 0, + "Buddy's Contact, only available when presence subscription " + "has been established to the buddy." + }, + { + "status", T_INT, + offsetof(PyObj_pjsua_buddy_info, status), 0, + "Buddy's online status. " + }, + { + "status_text", T_OBJECT_EX, + offsetof(PyObj_pjsua_buddy_info, status_text), 0, + "Text to describe buddy's online status." + }, + { + "monitor_pres", T_INT, + offsetof(PyObj_pjsua_buddy_info, monitor_pres), 0, + "Flag to indicate that we should monitor the presence information " + "for this buddy (normally yes, unless explicitly disabled). " + }, + { + "activity", T_INT, + offsetof(PyObj_pjsua_buddy_info, activity), 0, + "Activity type. " + }, + { + "sub_state", T_INT, + offsetof(PyObj_pjsua_buddy_info, sub_state), 0, + "Subscription state." + }, + { + "sub_term_reason", T_INT, + offsetof(PyObj_pjsua_buddy_info, sub_term_reason), 0, + "Subscription termination reason." + }, + + + {NULL} /* Sentinel */ +}; + + + + +/* + * PyTyp_pjsua_buddy_info + */ +static PyTypeObject PyTyp_pjsua_buddy_info = +{ + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_pjsua.Buddy_Info", /*tp_name*/ + sizeof(PyObj_pjsua_buddy_info), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyObj_pjsua_buddy_info_delete,/*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + "Buddy Info object", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + PyObj_pjsua_buddy_info_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyObj_pjsua_buddy_info_new, /* tp_new */ + +}; + + +////////////////////////////////////////////////////////////////////////////// + +/* + * PyObj_pjsua_codec_info + * Codec Info + */ +typedef struct +{ + PyObject_HEAD + /* Type-specific fields go here. */ + + PyObject * codec_id; + pj_uint8_t priority; +} PyObj_pjsua_codec_info; + + +/* + * codec_info_dealloc + * deletes a codec_info from memory + */ +static void codec_info_dealloc(PyObj_pjsua_codec_info* self) +{ + Py_XDECREF(self->codec_id); + self->ob_type->tp_free((PyObject*)self); +} + + +/* + * codec_info_new + * constructor for codec_info object + */ +static PyObject * codec_info_new(PyTypeObject *type, PyObject *args, + PyObject *kwds) +{ + PyObj_pjsua_codec_info *self; + + PJ_UNUSED_ARG(args); + PJ_UNUSED_ARG(kwds); + + self = (PyObj_pjsua_codec_info *)type->tp_alloc(type, 0); + if (self != NULL) { + self->codec_id = PyString_FromString(""); + } + return (PyObject *)self; +} + +/* + * codec_info_members + * !modified @ 071206 + */ +static PyMemberDef codec_info_members[] = +{ + { + "codec_id", T_OBJECT_EX, + offsetof(PyObj_pjsua_codec_info, codec_id), 0, + "Codec unique identification." + }, + { + "priority", T_INT, + offsetof(PyObj_pjsua_codec_info, priority), 0, + "Codec priority (integer 0-255)." + }, + + {NULL} /* Sentinel */ +}; + +/* + * PyTyp_pjsua_codec_info + */ +static PyTypeObject PyTyp_pjsua_codec_info = +{ + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_pjsua.Codec_Info", /*tp_name*/ + sizeof(PyObj_pjsua_codec_info), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)codec_info_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + "Codec Info", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + codec_info_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + codec_info_new, /* tp_new */ + +}; + + +////////////////////////////////////////////////////////////////////////////// + +/* + * PyObj_pjsua_conf_port_info + * Conf Port Info + */ +typedef struct +{ + PyObject_HEAD + /* Type-specific fields go here. */ + + int slot_id; + PyObject *name; + unsigned clock_rate; + unsigned channel_count; + unsigned samples_per_frame; + unsigned bits_per_sample; + PyObject *listeners; + +} PyObj_pjsua_conf_port_info; + + +/* + * conf_port_info_dealloc + * deletes a conf_port_info from memory + */ +static void conf_port_info_dealloc(PyObj_pjsua_conf_port_info* self) +{ + Py_XDECREF(self->name); + Py_XDECREF(self->listeners); + self->ob_type->tp_free((PyObject*)self); +} + + +/* + * conf_port_info_new + * constructor for conf_port_info object + */ +static PyObject * conf_port_info_new(PyTypeObject *type, PyObject *args, + PyObject *kwds) +{ + PyObj_pjsua_conf_port_info *self; + + PJ_UNUSED_ARG(args); + PJ_UNUSED_ARG(kwds); + + self = (PyObj_pjsua_conf_port_info *)type->tp_alloc(type, 0); + if (self != NULL) { + self->name = PyString_FromString(""); + self->listeners = PyList_New(0); + } + return (PyObject *)self; +} + +/* + * conf_port_info_members + */ +static PyMemberDef conf_port_info_members[] = +{ + { + "slot_id", T_INT, + offsetof(PyObj_pjsua_conf_port_info, slot_id), 0, + "Conference port number." + }, + { + "name", T_OBJECT_EX, + offsetof(PyObj_pjsua_conf_port_info, name), 0, + "Port name" + }, + { + "clock_rate", T_INT, + offsetof(PyObj_pjsua_conf_port_info, clock_rate), 0, + "Clock rate" + }, + { + "channel_count", T_INT, + offsetof(PyObj_pjsua_conf_port_info, channel_count), 0, + "Number of channels." + }, + { + "samples_per_frame", T_INT, + offsetof(PyObj_pjsua_conf_port_info, samples_per_frame), 0, + "Samples per frame " + }, + { + "bits_per_sample", T_INT, + offsetof(PyObj_pjsua_conf_port_info, bits_per_sample), 0, + "Bits per sample" + }, + { + "listeners", T_OBJECT_EX, + offsetof(PyObj_pjsua_conf_port_info, listeners), 0, + "Array of listeners (in other words, ports where this port " + "is transmitting to" + }, + + {NULL} /* Sentinel */ +}; + + + + +/* + * PyTyp_pjsua_conf_port_info + */ +static PyTypeObject PyTyp_pjsua_conf_port_info = +{ + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_pjsua.Conf_Port_Info", /*tp_name*/ + sizeof(PyObj_pjsua_conf_port_info), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)conf_port_info_dealloc,/*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + "Conf Port Info objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + conf_port_info_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + conf_port_info_new, /* tp_new */ + +}; + +////////////////////////////////////////////////////////////////////////////// + +/* + * PyObj_pjmedia_snd_dev_info + * PJMedia Snd Dev Info + */ +typedef struct +{ + PyObject_HEAD + /* Type-specific fields go here. */ + + unsigned input_count; + unsigned output_count; + unsigned default_samples_per_sec; + PyObject *name; + +} PyObj_pjmedia_snd_dev_info; + +/* + * pjmedia_snd_dev_info_dealloc + * deletes a pjmedia_snd_dev_info from memory + */ +static void pjmedia_snd_dev_info_dealloc(PyObj_pjmedia_snd_dev_info* self) +{ + Py_XDECREF(self->name); + self->ob_type->tp_free((PyObject*)self); +} + +/* + * pjmedia_snd_dev_info_new + * constructor for pjmedia_snd_dev_info object + */ +static PyObject * pjmedia_snd_dev_info_new(PyTypeObject *type, + PyObject *args, + PyObject *kwds) +{ + PyObj_pjmedia_snd_dev_info *self; + + PJ_UNUSED_ARG(args); + PJ_UNUSED_ARG(kwds); + + self = (PyObj_pjmedia_snd_dev_info *)type->tp_alloc(type, 0); + if (self != NULL) { + self->name = PyString_FromString(""); + } + return (PyObject *)self; +} + +/* + * pjmedia_snd_dev_info_members + */ +static PyMemberDef pjmedia_snd_dev_info_members[] = +{ + { + "input_count", T_INT, + offsetof(PyObj_pjmedia_snd_dev_info, input_count), 0, + "Max number of input channels" + }, + { + "output_count", T_INT, + offsetof(PyObj_pjmedia_snd_dev_info, output_count), 0, + "Max number of output channels" + }, + { + "default_samples_per_sec", T_INT, + offsetof(PyObj_pjmedia_snd_dev_info, default_samples_per_sec), 0, + "Default sampling rate." + }, + { + "name", T_OBJECT_EX, + offsetof(PyObj_pjmedia_snd_dev_info, name), 0, + "Device name" + }, + + {NULL} /* Sentinel */ +}; + + +/* + * PyTyp_pjmedia_snd_dev_info + */ +static PyTypeObject PyTyp_pjmedia_snd_dev_info = +{ + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_pjsua.PJMedia_Snd_Dev_Info", /*tp_name*/ + sizeof(PyObj_pjmedia_snd_dev_info), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)pjmedia_snd_dev_info_dealloc,/*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + "PJMedia Snd Dev Info object", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + pjmedia_snd_dev_info_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + pjmedia_snd_dev_info_new, /* tp_new */ + +}; + +////////////////////////////////////////////////////////////////////////////// + +/* + * PyObj_pjmedia_codec_param_info + * PJMedia Codec Param Info + */ +typedef struct +{ + PyObject_HEAD + /* Type-specific fields go here. */ + + unsigned clock_rate; + unsigned channel_cnt; + pj_uint32_t avg_bps; + pj_uint16_t frm_ptime; + pj_uint8_t pcm_bits_per_sample; + pj_uint8_t pt; + +} PyObj_pjmedia_codec_param_info; + + + +/* + * pjmedia_codec_param_info_members + */ +static PyMemberDef pjmedia_codec_param_info_members[] = +{ + { + "clock_rate", T_INT, + offsetof(PyObj_pjmedia_codec_param_info, clock_rate), 0, + "Sampling rate in Hz" + }, + { + "channel_cnt", T_INT, + offsetof(PyObj_pjmedia_codec_param_info, channel_cnt), 0, + "Channel count" + }, + { + "avg_bps", T_INT, + offsetof(PyObj_pjmedia_codec_param_info, avg_bps), 0, + "Average bandwidth in bits/sec" + }, + { + "frm_ptime", T_INT, + offsetof(PyObj_pjmedia_codec_param_info, frm_ptime), 0, + "Base frame ptime in msec." + }, + { + "pcm_bits_per_sample", T_INT, + offsetof(PyObj_pjmedia_codec_param_info, pcm_bits_per_sample), 0, + "Bits/sample in the PCM side" + }, + { + "pt", T_INT, + offsetof(PyObj_pjmedia_codec_param_info, pt), 0, + "Payload type" + }, + + {NULL} /* Sentinel */ +}; + + +/* + * PyTyp_pjmedia_codec_param_info + */ +static PyTypeObject PyTyp_pjmedia_codec_param_info = +{ + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_pjsua.PJMedia_Codec_Param_Info", /*tp_name*/ + sizeof(PyObj_pjmedia_codec_param_info), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + 0, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + "PJMedia Codec Param Info objects",/* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + pjmedia_codec_param_info_members,/* tp_members */ +}; + + +////////////////////////////////////////////////////////////////////////////// + +/* + * PyObj_pjmedia_codec_param_setting + * PJMedia Codec Param Setting + */ +typedef struct +{ + PyObject_HEAD + /* Type-specific fields go here. */ + pj_uint8_t frm_per_pkt; + unsigned vad; + unsigned cng; + unsigned penh; + unsigned plc; +#if 0 + pj_uint8_t enc_fmtp_mode; + pj_uint8_t dec_fmtp_mode; +#endif + +} PyObj_pjmedia_codec_param_setting; + + + +/* + * pjmedia_codec_param_setting_members + */ +static PyMemberDef pjmedia_codec_param_setting_members[] = +{ + { + "frm_per_pkt", T_INT, + offsetof(PyObj_pjmedia_codec_param_setting, frm_per_pkt), 0, + "Number of frames per packet" + }, + { + "vad", T_INT, + offsetof(PyObj_pjmedia_codec_param_setting, vad), 0, + "Voice Activity Detector" + }, + { + "cng", T_INT, + offsetof(PyObj_pjmedia_codec_param_setting, cng), 0, + "Comfort Noise Generator" + }, + { + "penh", T_INT, + offsetof(PyObj_pjmedia_codec_param_setting, penh), 0, + "Perceptual Enhancement" + }, + { + "plc", T_INT, + offsetof(PyObj_pjmedia_codec_param_setting, plc), 0, + "Packet loss concealment" + }, +#if 0 // no longer valid with latest modification in codec + { + "enc_fmtp_mode", T_INT, + offsetof(PyObj_pjmedia_codec_param_setting, enc_fmtp_mode), 0, + "Mode param in fmtp (def:0)" + }, + { + "dec_fmtp_mode", T_INT, + offsetof(PyObj_pjmedia_codec_param_setting, dec_fmtp_mode), 0, + "Mode param in fmtp (def:0)" + }, +#endif + + {NULL} /* Sentinel */ +}; + + +/* + * PyTyp_pjmedia_codec_param_setting + */ +static PyTypeObject PyTyp_pjmedia_codec_param_setting = +{ + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_pjsua.PJMedia_Codec_Param_Setting",/*tp_name*/ + sizeof(PyObj_pjmedia_codec_param_setting), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + 0, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + "PJMedia Codec Param Setting", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + pjmedia_codec_param_setting_members,/* tp_members */ +}; + +////////////////////////////////////////////////////////////////////////////// + + +/* + * PyObj_pjmedia_codec_param + * PJMedia Codec Param + */ +typedef struct +{ + PyObject_HEAD + /* Type-specific fields go here. */ + + PyObj_pjmedia_codec_param_info * info; + PyObj_pjmedia_codec_param_setting * setting; + +} PyObj_pjmedia_codec_param; + +/* + * pjmedia_codec_param_dealloc + * deletes a pjmedia_codec_param from memory + */ +static void pjmedia_codec_param_dealloc(PyObj_pjmedia_codec_param* self) +{ + Py_XDECREF(self->info); + Py_XDECREF(self->setting); + self->ob_type->tp_free((PyObject*)self); +} + +/* + * pjmedia_codec_param_new + * constructor for pjmedia_codec_param object + */ +static PyObject * pjmedia_codec_param_new(PyTypeObject *type, + PyObject *args, + PyObject *kwds) +{ + PyObj_pjmedia_codec_param *self; + + PJ_UNUSED_ARG(args); + PJ_UNUSED_ARG(kwds); + + self = (PyObj_pjmedia_codec_param *)type->tp_alloc(type, 0); + if (self != NULL) { + self->info = (PyObj_pjmedia_codec_param_info *) + PyType_GenericNew(&PyTyp_pjmedia_codec_param_info, + NULL, NULL); + self->setting = (PyObj_pjmedia_codec_param_setting *) + PyType_GenericNew(&PyTyp_pjmedia_codec_param_setting, + NULL, NULL); + } + return (PyObject *)self; +} + +/* + * pjmedia_codec_param_members + */ +static PyMemberDef pjmedia_codec_param_members[] = +{ + + { + "info", T_OBJECT_EX, + offsetof(PyObj_pjmedia_codec_param, info), 0, + "The 'info' part of codec param describes the capability of the codec," + " and the value should NOT be changed by application." + }, + { + "setting", T_OBJECT_EX, + offsetof(PyObj_pjmedia_codec_param, setting), 0, + "The 'setting' part of codec param describes various settings to be " + "applied to the codec. When the codec param is retrieved from the " + "codec or codec factory, the values of these will be filled by " + "the capability of the codec. Any features that are supported by " + "the codec (e.g. vad or plc) will be turned on, so that application " + "can query which capabilities are supported by the codec. " + "Application may change the settings here before instantiating " + "the codec/stream." + }, + + {NULL} /* Sentinel */ +}; + +/* + * PyTyp_pjmedia_codec_param + */ +static PyTypeObject PyTyp_pjmedia_codec_param = +{ + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_pjsua.PJMedia_Codec_Param", /*tp_name*/ + sizeof(PyObj_pjmedia_codec_param),/*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)pjmedia_codec_param_dealloc,/*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + "PJMedia Codec Param", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + pjmedia_codec_param_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + pjmedia_codec_param_new, /* tp_new */ + +}; + +////////////////////////////////////////////////////////////////////////////// + +/* + * PyObj_pjsua_call_info + * Call Info + */ +typedef struct +{ + PyObject_HEAD + /* Type-specific fields go here. */ + + int id; + int role; + int acc_id; + PyObject *local_info; + PyObject *local_contact; + PyObject *remote_info; + PyObject *remote_contact; + PyObject *call_id; + int state; + PyObject *state_text; + int last_status; + PyObject *last_status_text; + int media_status; + int media_dir; + int conf_slot; + int connect_duration; + int total_duration; + +} PyObj_pjsua_call_info; + + +/* + * call_info_dealloc + * deletes a call_info from memory + */ +static void call_info_dealloc(PyObj_pjsua_call_info* self) +{ + Py_XDECREF(self->local_info); + Py_XDECREF(self->local_contact); + Py_XDECREF(self->remote_info); + Py_XDECREF(self->remote_contact); + Py_XDECREF(self->call_id); + Py_XDECREF(self->state_text); + Py_XDECREF(self->last_status_text); + self->ob_type->tp_free((PyObject*)self); +} + + +/* + * call_info_new + * constructor for call_info object + */ +static PyObject * call_info_new(PyTypeObject *type, PyObject *args, + PyObject *kwds) +{ + PyObj_pjsua_call_info *self; + + PJ_UNUSED_ARG(args); + PJ_UNUSED_ARG(kwds); + + self = (PyObj_pjsua_call_info *)type->tp_alloc(type, 0); + if (self != NULL) { + self->local_info = PyString_FromString(""); + self->local_contact = PyString_FromString(""); + self->remote_info = PyString_FromString(""); + self->remote_contact = PyString_FromString(""); + self->call_id = PyString_FromString(""); + self->state_text = PyString_FromString(""); + self->last_status_text = PyString_FromString(""); + } + return (PyObject *)self; +} + +/* + * call_info_members + */ +static PyMemberDef call_info_members[] = +{ + { + "id", T_INT, + offsetof(PyObj_pjsua_call_info, id), 0, + "Call identification" + }, + { + "role", T_INT, + offsetof(PyObj_pjsua_call_info, role), 0, + "Initial call role (UAC == caller)" + }, + { + "acc_id", T_INT, + offsetof(PyObj_pjsua_call_info, acc_id), 0, + "The account ID where this call belongs." + }, + { + "local_info", T_OBJECT_EX, + offsetof(PyObj_pjsua_call_info, local_info), 0, + "Local URI" + }, + { + "local_contact", T_OBJECT_EX, + offsetof(PyObj_pjsua_call_info, local_contact), 0, + "Local Contact" + }, + { + "remote_info", T_OBJECT_EX, + offsetof(PyObj_pjsua_call_info, remote_info), 0, + "Remote URI" + }, + { + "remote_contact", T_OBJECT_EX, + offsetof(PyObj_pjsua_call_info, remote_contact), 0, + "Remote Contact" + }, + { + "call_id", T_OBJECT_EX, + offsetof(PyObj_pjsua_call_info, call_id), 0, + "Dialog Call-ID string" + }, + { + "state", T_INT, + offsetof(PyObj_pjsua_call_info, state), 0, + "Call state" + }, + { + "state_text", T_OBJECT_EX, + offsetof(PyObj_pjsua_call_info, state_text), 0, + "Text describing the state " + }, + { + "last_status", T_INT, + offsetof(PyObj_pjsua_call_info, last_status), 0, + "Last status code heard, which can be used as cause code" + }, + { + "last_status_text", T_OBJECT_EX, + offsetof(PyObj_pjsua_call_info, last_status_text), 0, + "The reason phrase describing the status." + }, + { + "media_status", T_INT, + offsetof(PyObj_pjsua_call_info, media_status), 0, + "Call media status." + }, + { + "media_dir", T_INT, + offsetof(PyObj_pjsua_call_info, media_dir), 0, + "Media direction" + }, + { + "conf_slot", T_INT, + offsetof(PyObj_pjsua_call_info, conf_slot), 0, + "The conference port number for the call" + }, + { + "connect_duration", T_INT, + offsetof(PyObj_pjsua_call_info, connect_duration), 0, + "Up-to-date call connected duration(zero when call is not established)" + }, + { + "total_duration", T_INT, + offsetof(PyObj_pjsua_call_info, total_duration), 0, + "Total call duration, including set-up time" + }, + + {NULL} /* Sentinel */ +}; + + + + +/* + * PyTyp_pjsua_call_info + */ +static PyTypeObject PyTyp_pjsua_call_info = +{ + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_pjsua.Call_Info", /*tp_name*/ + sizeof(PyObj_pjsua_call_info), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)call_info_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + "Call Info", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + call_info_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + call_info_new, /* tp_new */ + +}; + + + +////////////////////////////////////////////////////////////////////////////// + +#endif /* __PY_PJSUA_H__ */ + diff --git a/pjsip-apps/src/python/helper.mak b/pjsip-apps/src/python/helper.mak new file mode 100644 index 0000000..b4acce6 --- /dev/null +++ b/pjsip-apps/src/python/helper.mak @@ -0,0 +1,17 @@ +include ../../../build.mak + +lib_dir: + @for token in `echo $(APP_LDFLAGS)`; do \ + echo $$token | grep L | sed 's/-L//'; \ + done + +inc_dir: + @for token in `echo $(APP_CFLAGS)`; do \ + echo $$token | grep I | sed 's/-I//'; \ + done + +libs: + @for token in `echo $(APP_LDLIBS)`; do \ + echo $$token | grep \\-l | sed 's/-l//'; \ + done + diff --git a/pjsip-apps/src/python/pjsua.py b/pjsip-apps/src/python/pjsua.py new file mode 100644 index 0000000..183ce0e --- /dev/null +++ b/pjsip-apps/src/python/pjsua.py @@ -0,0 +1,2862 @@ +# $Id: pjsua.py 2976 2009-10-29 08:16:46Z bennylp $ +# +# Object oriented PJSUA wrapper. +# +# Copyright (C) 2003-2008 Benny Prijono +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +"""Multimedia communication client library based on SIP protocol. + +This implements a fully featured multimedia communication client +library based on PJSIP stack (http://www.pjsip.org) + + +1. FEATURES + + - Session Initiation Protocol (SIP) features: + - Basic registration and call + - Multiple accounts + - Call hold, attended and unattended call transfer + - Presence + - Instant messaging + - Multiple SIP accounts + - Media features: + - Audio + - Conferencing + - Narrowband and wideband + - Codecs: PCMA, PCMU, GSM, iLBC, Speex, G.722, L16 + - RTP/RTCP + - Secure RTP (SRTP) + - WAV playback, recording, and playlist + - NAT traversal features + - Symmetric RTP + - STUN + - TURN + - ICE + + +2. USING + +See http://www.pjsip.org/trac/wiki/Python_SIP_Tutorial for a more thorough +tutorial. The paragraphs below explain basic tasks on using this module. + + +""" +import _pjsua +import thread +import threading +import weakref +import time + +class Error: + """Error exception class. + + Member documentation: + + op_name -- name of the operation that generated this error. + obj -- the object that generated this error. + err_code -- the error code. + + """ + op_name = "" + obj = None + err_code = -1 + _err_msg = "" + + def __init__(self, op_name, obj, err_code, err_msg=""): + self.op_name = op_name + self.obj = obj + self.err_code = err_code + self._err_msg = err_msg + + def err_msg(self): + "Retrieve the description of the error." + if self._err_msg != "": + return self._err_msg + self._err_msg = Lib.strerror(self.err_code) + return self._err_msg + + def __str__(self): + return "Object: " + str(self.obj) + ", operation=" + self.op_name + \ + ", error=" + self.err_msg() + +# +# Constants +# + +class TransportType: + """SIP transport type constants. + + Member documentation: + UNSPECIFIED -- transport type is unknown or unspecified + UDP -- UDP transport + TCP -- TCP transport + TLS -- TLS transport + IPV6 -- this is not a transport type but rather a flag + to select the IPv6 version of a transport + UDP_IPV6 -- IPv6 UDP transport + TCP_IPV6 -- IPv6 TCP transport + """ + UNSPECIFIED = 0 + UDP = 1 + TCP = 2 + TLS = 3 + IPV6 = 128 + UDP_IPV6 = UDP + IPV6 + TCP_IPV6 = TCP + IPV6 + +class TransportFlag: + """Transport flags to indicate the characteristics of the transport. + + Member documentation: + + RELIABLE -- transport is reliable. + SECURE -- transport is secure. + DATAGRAM -- transport is datagram based. + + """ + RELIABLE = 1 + SECURE = 2 + DATAGRAM = 4 + +class CallRole: + """Call role constants. + + Member documentation: + + CALLER -- role is caller + CALLEE -- role is callee + + """ + CALLER = 0 + CALLEE = 1 + +class CallState: + """Call state constants. + + Member documentation: + + NULL -- call is not initialized. + CALLING -- initial INVITE is sent. + INCOMING -- initial INVITE is received. + EARLY -- provisional response has been sent or received. + CONNECTING -- 200/OK response has been sent or received. + CONFIRMED -- ACK has been sent or received. + DISCONNECTED -- call is disconnected. + """ + NULL = 0 + CALLING = 1 + INCOMING = 2 + EARLY = 3 + CONNECTING = 4 + CONFIRMED = 5 + DISCONNECTED = 6 + + +class MediaState: + """Call media state constants. + + Member documentation: + + NULL -- media is not available. + ACTIVE -- media is active. + LOCAL_HOLD -- media is put on-hold by local party. + REMOTE_HOLD -- media is put on-hold by remote party. + ERROR -- media error (e.g. ICE negotiation failure). + """ + NULL = 0 + ACTIVE = 1 + LOCAL_HOLD = 2 + REMOTE_HOLD = 3 + ERROR = 4 + + +class MediaDir: + """Media direction constants. + + Member documentation: + + NULL -- media is not active + ENCODING -- media is active in transmit/encoding direction only. + DECODING -- media is active in receive/decoding direction only + ENCODING_DECODING -- media is active in both directions. + """ + NULL = 0 + ENCODING = 1 + DECODING = 2 + ENCODING_DECODING = 3 + + +class PresenceActivity: + """Presence activities constants. + + Member documentation: + + UNKNOWN -- the person activity is unknown + AWAY -- the person is currently away + BUSY -- the person is currently engaging in other activity + """ + UNKNOWN = 0 + AWAY = 1 + BUSY = 2 + + +class SubscriptionState: + """Presence subscription state constants. + + """ + NULL = 0 + SENT = 1 + ACCEPTED = 2 + PENDING = 3 + ACTIVE = 4 + TERMINATED = 5 + UNKNOWN = 6 + + +class TURNConnType: + """These constants specifies the connection type to TURN server. + + Member documentation: + UDP -- use UDP transport. + TCP -- use TCP transport. + TLS -- use TLS transport. + """ + UDP = 17 + TCP = 6 + TLS = 255 + + +class UAConfig: + """User agent configuration to be specified in Lib.init(). + + Member documentation: + + max_calls -- maximum number of calls to be supported. + nameserver -- list of nameserver hostnames or IP addresses. Nameserver + must be configured if DNS SRV resolution is desired. + stun_domain -- if nameserver is configured, this can be used to query + the STUN server with DNS SRV. + stun_host -- the hostname or IP address of the STUN server. This will + also be used if DNS SRV resolution for stun_domain fails. + user_agent -- Optionally specify the user agent name. + """ + max_calls = 4 + nameserver = [] + stun_domain = "" + stun_host = "" + user_agent = "pjsip python" + + def _cvt_from_pjsua(self, cfg): + self.max_calls = cfg.max_calls + self.thread_cnt = cfg.thread_cnt + self.nameserver = cfg.nameserver + self.stun_domain = cfg.stun_domain + self.stun_host = cfg.stun_host + self.user_agent = cfg.user_agent + + def _cvt_to_pjsua(self): + cfg = _pjsua.config_default() + cfg.max_calls = self.max_calls + cfg.thread_cnt = 0 + cfg.nameserver = self.nameserver + cfg.stun_domain = self.stun_domain + cfg.stun_host = self.stun_host + cfg.user_agent = self.user_agent + return cfg + + +class LogConfig: + """Logging configuration to be specified in Lib.init(). + + Member documentation: + + msg_logging -- specify if SIP messages should be logged. Set to + True. + level -- specify the input verbosity level. + console_level -- specify the output verbosity level. + decor -- specify log decoration. + filename -- specify the log filename. + callback -- specify callback to be called to write the logging + messages. Sample function: + + def log_cb(level, str, len): + print str, + + """ + msg_logging = True + level = 5 + console_level = 5 + decor = 0 + filename = "" + callback = None + + def __init__(self, level=-1, filename="", callback=None, + console_level=-1): + self._cvt_from_pjsua(_pjsua.logging_config_default()) + if level != -1: + self.level = level + if filename != "": + self.filename = filename + if callback != None: + self.callback = callback + if console_level != -1: + self.console_level = console_level + + def _cvt_from_pjsua(self, cfg): + self.msg_logging = cfg.msg_logging + self.level = cfg.level + self.console_level = cfg.console_level + self.decor = cfg.decor + self.filename = cfg.log_filename + self.callback = cfg.cb + + def _cvt_to_pjsua(self): + cfg = _pjsua.logging_config_default() + cfg.msg_logging = self.msg_logging + cfg.level = self.level + cfg.console_level = self.console_level + cfg.decor = self.decor + cfg.log_filename = self.filename + cfg.cb = self.callback + return cfg + + +class MediaConfig: + """Media configuration to be specified in Lib.init(). + + Member documentation: + + clock_rate -- specify the core clock rate of the audio, + most notably the conference bridge. + snd_clock_rate -- optionally specify different clock rate for + the sound device. + snd_auto_close_time -- specify the duration in seconds when the + sound device should be closed after inactivity + period. + channel_count -- specify the number of channels to open the sound + device and the conference bridge. + audio_frame_ptime -- specify the length of audio frames in millisecond. + max_media_ports -- specify maximum number of audio ports to be + supported by the conference bridge. + quality -- specify the audio quality setting (1-10) + ptime -- specify the audio packet length of transmitted + RTP packet. + no_vad -- disable Voice Activity Detector (VAD) or Silence + Detector (SD) + ilbc_mode -- specify iLBC codec mode (must be 30 for now) + tx_drop_pct -- randomly drop transmitted RTP packets (for + simulation). Number is in percent. + rx_drop_pct -- randomly drop received RTP packets (for + simulation). Number is in percent. + ec_options -- Echo Canceller option (specify zero). + ec_tail_len -- specify Echo Canceller tail length in milliseconds. + Value zero will disable the echo canceller. + jb_min -- specify the minimum jitter buffer size in + milliseconds. Put -1 for default. + jb_max -- specify the maximum jitter buffer size in + milliseconds. Put -1 for default. + enable_ice -- enable Interactive Connectivity Establishment (ICE) + enable_turn -- enable TURN relay. TURN server settings must also + be configured. + turn_server -- specify the domain or hostname or IP address of + the TURN server, in "host[:port]" format. + turn_conn_type -- specify connection type to the TURN server, from + the TURNConnType constant. + turn_cred -- specify AuthCred for the TURN credential. + """ + clock_rate = 16000 + snd_clock_rate = 0 + snd_auto_close_time = 5 + channel_count = 1 + audio_frame_ptime = 20 + max_media_ports = 32 + quality = 6 + ptime = 0 + no_vad = False + ilbc_mode = 30 + tx_drop_pct = 0 + rx_drop_pct = 0 + ec_options = 0 + ec_tail_len = 256 + jb_min = -1 + jb_max = -1 + enable_ice = True + enable_turn = False + turn_server = "" + turn_conn_type = TURNConnType.UDP + turn_cred = None + + def __init__(self): + default = _pjsua.media_config_default() + self._cvt_from_pjsua(default) + + def _cvt_from_pjsua(self, cfg): + self.clock_rate = cfg.clock_rate + self.snd_clock_rate = cfg.snd_clock_rate + self.snd_auto_close_time = cfg.snd_auto_close_time + self.channel_count = cfg.channel_count + self.audio_frame_ptime = cfg.audio_frame_ptime + self.max_media_ports = cfg.max_media_ports + self.quality = cfg.quality + self.ptime = cfg.ptime + self.no_vad = cfg.no_vad + self.ilbc_mode = cfg.ilbc_mode + self.tx_drop_pct = cfg.tx_drop_pct + self.rx_drop_pct = cfg.rx_drop_pct + self.ec_options = cfg.ec_options + self.ec_tail_len = cfg.ec_tail_len + self.jb_min = cfg.jb_min + self.jb_max = cfg.jb_max + self.enable_ice = cfg.enable_ice + self.enable_turn = cfg.enable_turn + self.turn_server = cfg.turn_server + self.turn_conn_type = cfg.turn_conn_type + if cfg.turn_username: + self.turn_cred = AuthCred(cfg.turn_realm, cfg.turn_username, + cfg.turn_passwd, cfg.turn_passwd_type) + else: + self.turn_cred = None + + def _cvt_to_pjsua(self): + cfg = _pjsua.media_config_default() + cfg.clock_rate = self.clock_rate + cfg.snd_clock_rate = self.snd_clock_rate + cfg.snd_auto_close_time = self.snd_auto_close_time + cfg.channel_count = self.channel_count + cfg.audio_frame_ptime = self.audio_frame_ptime + cfg.max_media_ports = self.max_media_ports + cfg.quality = self.quality + cfg.ptime = self.ptime + cfg.no_vad = self.no_vad + cfg.ilbc_mode = self.ilbc_mode + cfg.tx_drop_pct = self.tx_drop_pct + cfg.rx_drop_pct = self.rx_drop_pct + cfg.ec_options = self.ec_options + cfg.ec_tail_len = self.ec_tail_len + cfg.jb_min = self.jb_min + cfg.jb_max = self.jb_max + cfg.enable_ice = self.enable_ice + cfg.enable_turn = self.enable_turn + cfg.turn_server = self.turn_server + cfg.turn_conn_type = self.turn_conn_type + if self.turn_cred: + cfg.turn_realm = self.turn_cred.realm + cfg.turn_username = self.turn_cred.username + cfg.turn_passwd_type = self.turn_cred.passwd_type + cfg.turn_passwd = self.turn_cred.passwd + return cfg + + +class TransportConfig: + """SIP transport configuration class. + + Member configuration: + + port -- port number. + bound_addr -- optionally specify the address to bind the socket to. + Default is empty to bind to INADDR_ANY. + public_addr -- optionally override the published address for this + transport. If empty, the default behavior is to get + the public address from STUN or from the selected + local interface. Format is "host:port". + """ + port = 0 + bound_addr = "" + public_addr = "" + + def __init__(self, port=0, + bound_addr="", public_addr=""): + self.port = port + self.bound_addr = bound_addr + self.public_addr = public_addr + + def _cvt_to_pjsua(self): + cfg = _pjsua.transport_config_default() + cfg.port = self.port + cfg.bound_addr = self.bound_addr + cfg.public_addr = self.public_addr + return cfg + + +class TransportInfo: + """SIP transport info. + + Member documentation: + + type -- transport type, from TransportType constants. + description -- longer description for this transport. + is_reliable -- True if transport is reliable. + is_secure -- True if transport is secure. + is_datagram -- True if transport is datagram based. + host -- the IP address of this transport. + port -- the port number. + ref_cnt -- number of objects referencing this transport. + """ + type = "" + description = "" + is_reliable = False + is_secure = False + is_datagram = False + host = "" + port = 0 + ref_cnt = 0 + + def __init__(self, ti): + self.type = ti.type_name + self.description = ti.info + self.is_reliable = (ti.flag & TransportFlag.RELIABLE) + self.is_secure = (ti.flag & TransportFlag.SECURE) + self.is_datagram = (ti.flag & TransportFlag.DATAGRAM) + self.host = ti.addr + self.port = ti.port + self.ref_cnt = ti.usage_count + + +class Transport: + "SIP transport class." + _id = -1 + _lib = None + _obj_name = "" + + def __init__(self, lib, id): + self._lib = weakref.proxy(lib) + self._id = id + self._obj_name = "{Transport " + self.info().description + "}" + _Trace((self, 'created')) + + def __del__(self): + _Trace((self, 'destroyed')) + + def __str__(self): + return self._obj_name + + def info(self): + """Get TransportInfo. + """ + lck = self._lib.auto_lock() + ti = _pjsua.transport_get_info(self._id) + if not ti: + self._lib._err_check("info()", self, -1, "Invalid transport") + return TransportInfo(ti) + + def enable(self): + """Enable this transport.""" + lck = self._lib.auto_lock() + err = _pjsua.transport_set_enable(self._id, True) + self._lib._err_check("enable()", self, err) + + def disable(self): + """Disable this transport.""" + lck = self._lib.auto_lock() + err = _pjsua.transport_set_enable(self._id, 0) + self._lib._err_check("disable()", self, err) + + def close(self, force=False): + """Close and destroy this transport. + + Keyword argument: + force -- force deletion of this transport (not recommended). + """ + lck = self._lib.auto_lock() + err = _pjsua.transport_close(self._id, force) + self._lib._err_check("close()", self, err) + + +class SIPUri: + """Helper class to parse the most important components of SIP URI. + + Member documentation: + + scheme -- URI scheme ("sip" or "sips") + user -- user part of the URI (may be empty) + host -- host name part + port -- optional port number (zero if port is not specified). + transport -- transport parameter, or empty if transport is not + specified. + + """ + scheme = "" + user = "" + host = "" + port = 0 + transport = "" + + def __init__(self, uri=None): + if uri: + self.decode(uri) + + def decode(self, uri): + """Parse SIP URL. + + Keyword argument: + uri -- the URI string. + + """ + self.scheme, self.user, self.host, self.port, self.transport = \ + _pjsua.parse_simple_uri(uri) + + def encode(self): + """Encode this object into SIP URI string. + + Return: + URI string. + + """ + output = self.scheme + ":" + if self.user and len(self.user): + output = output + self.user + "@" + output = output + self.host + if self.port: + output = output + ":" + output(self.port) + if self.transport: + output = output + ";transport=" + self.transport + return output + + +class AuthCred: + """Authentication credential for SIP or TURN account. + + Member documentation: + + scheme -- authentication scheme (default is "Digest") + realm -- realm + username -- username + passwd_type -- password encoding (zero for plain-text) + passwd -- the password + """ + scheme = "Digest" + realm = "*" + username = "" + passwd_type = 0 + passwd = "" + + def __init__(self, realm, username, passwd, scheme="Digest", passwd_type=0): + self.scheme = scheme + self.realm = realm + self.username = username + self.passwd_type = passwd_type + self.passwd = passwd + + +class AccountConfig: + """ This describes account configuration to create an account. + + Member documentation: + + priority -- account priority for matching incoming + messages. + id -- SIP URI of this account. This setting is + mandatory. + force_contact -- force to use this URI as Contact URI. Setting + this value is generally not recommended. + reg_uri -- specify the registrar URI. Mandatory if + registration is required. + reg_timeout -- specify the SIP registration refresh interval + in seconds. + require_100rel -- specify if reliable provisional response is + to be enforced (with Require header). + publish_enabled -- specify if PUBLISH should be used. When + enabled, the PUBLISH will be sent to the + registrar. + pidf_tuple_id -- optionally specify the tuple ID in outgoing + PIDF document. + proxy -- list of proxy URI. + auth_cred -- list of AuthCred containing credentials to + authenticate against the registrars and + the proxies. + auth_initial_send -- specify if empty Authorization header should be + sent. May be needed for IMS. + auth_initial_algorithm -- when auth_initial_send is enabled, optionally + specify the authentication algorithm to use. + Valid values are "md5", "akav1-md5", or + "akav2-md5". + transport_id -- optionally specify the transport ID to be used + by this account. Shouldn't be needed unless + for specific requirements (e.g. in multi-homed + scenario). + allow_contact_rewrite -- specify whether the account should learn its + Contact address from REGISTER response and + update the registration accordingly. Default is + True. + ka_interval -- specify the interval to send NAT keep-alive + packet. + ka_data -- specify the NAT keep-alive packet contents. + use_srtp -- specify the SRTP usage policy. Valid values + are: 0=disable, 1=optional, 2=mandatory. + Default is 0. + srtp_secure_signaling -- specify the signaling security level required + by SRTP. Valid values are: 0=no secure + transport is required, 1=hop-by-hop secure + transport such as TLS is required, 2=end-to- + end secure transport is required (i.e. "sips"). + """ + priority = 0 + id = "" + force_contact = "" + reg_uri = "" + reg_timeout = 0 + require_100rel = False + publish_enabled = False + pidf_tuple_id = "" + proxy = [] + auth_cred = [] + auth_initial_send = False + auth_initial_algorithm = "" + transport_id = -1 + allow_contact_rewrite = True + ka_interval = 15 + ka_data = "\r\n" + use_srtp = 0 + srtp_secure_signaling = 1 + + def __init__(self, domain="", username="", password="", + display="", registrar="", proxy=""): + """ + Construct account config. If domain argument is specified, + a typical configuration will be built. + + Keyword arguments: + domain -- domain name of the server. + username -- user name. + password -- plain-text password. + display -- optional display name for the user name. + registrar -- the registrar URI. If domain name is specified + and this argument is empty, the registrar URI + will be constructed from the domain name. + proxy -- the proxy URI. If domain name is specified + and this argument is empty, the proxy URI + will be constructed from the domain name. + + """ + default = _pjsua.acc_config_default() + self._cvt_from_pjsua(default) + if domain!="": + self.build_config(domain, username, password, + display, registrar, proxy) + + def build_config(self, domain, username, password, display="", + registrar="", proxy=""): + """ + Construct account config. If domain argument is specified, + a typical configuration will be built. + + Keyword arguments: + domain -- domain name of the server. + username -- user name. + password -- plain-text password. + display -- optional display name for the user name. + registrar -- the registrar URI. If domain name is specified + and this argument is empty, the registrar URI + will be constructed from the domain name. + proxy -- the proxy URI. If domain name is specified + and this argument is empty, the proxy URI + will be constructed from the domain name. + + """ + if display != "": + display = display + " " + userpart = username + if userpart != "": + userpart = userpart + "@" + self.id = display + "" + self.reg_uri = registrar + if self.reg_uri == "": + self.reg_uri = "sip:" + domain + if proxy == "": + proxy = "sip:" + domain + ";lr" + if proxy.find(";lr") == -1: + proxy = proxy + ";lr" + self.proxy.append(proxy) + if username != "": + self.auth_cred.append(AuthCred("*", username, password)) + + def _cvt_from_pjsua(self, cfg): + self.priority = cfg.priority + self.id = cfg.id + self.force_contact = cfg.force_contact + self.reg_uri = cfg.reg_uri + self.reg_timeout = cfg.reg_timeout + self.require_100rel = cfg.require_100rel + self.publish_enabled = cfg.publish_enabled + self.pidf_tuple_id = cfg.pidf_tuple_id + self.proxy = cfg.proxy + for cred in cfg.cred_info: + self.auth_cred.append(AuthCred(cred.realm, cred.username, + cred.data, cred.scheme, + cred.data_type)) + self.auth_initial_send = cfg.auth_initial_send + self.auth_initial_algorithm = cfg.auth_initial_algorithm + self.transport_id = cfg.transport_id + self.allow_contact_rewrite = cfg.allow_contact_rewrite + self.ka_interval = cfg.ka_interval + self.ka_data = cfg.ka_data + self.use_srtp = cfg.use_srtp + self.srtp_secure_signaling = cfg.srtp_secure_signaling + + def _cvt_to_pjsua(self): + cfg = _pjsua.acc_config_default() + cfg.priority = self.priority + cfg.id = self.id + cfg.force_contact = self.force_contact + cfg.reg_uri = self.reg_uri + cfg.reg_timeout = self.reg_timeout + cfg.require_100rel = self.require_100rel + cfg.publish_enabled = self.publish_enabled + cfg.pidf_tuple_id = self.pidf_tuple_id + cfg.proxy = self.proxy + for cred in self.auth_cred: + c = _pjsua.Pjsip_Cred_Info() + c.realm = cred.realm + c.scheme = cred.scheme + c.username = cred.username + c.data_type = cred.passwd_type + c.data = cred.passwd + cfg.cred_info.append(c) + cfg.auth_initial_send = self.auth_initial_send + cfg.auth_initial_algorithm = self.auth_initial_algorithm + cfg.transport_id = self.transport_id + cfg.allow_contact_rewrite = self.allow_contact_rewrite + cfg.ka_interval = self.ka_interval + cfg.ka_data = self.ka_data + cfg.use_srtp = self.use_srtp + cfg.srtp_secure_signaling = self.srtp_secure_signaling + return cfg + + +# Account information +class AccountInfo: + """This describes Account info. Application retrives account info + with Account.info(). + + Member documentation: + + is_default -- True if this is the default account. + uri -- the account URI. + reg_active -- True if registration is active for this account. + reg_expires -- contains the current registration expiration value, + in seconds. + reg_status -- the registration status. If the value is less than + 700, it specifies SIP status code. Value greater than + this specifies the error code. + reg_reason -- contains the registration status text (e.g. the + error message). + online_status -- the account's presence online status, True if it's + publishing itself as online. + online_text -- the account's presence status text. + + """ + is_default = False + uri = "" + reg_active = False + reg_expires = -1 + reg_status = 0 + reg_reason = "" + online_status = False + online_text = "" + + def __init__(self, ai): + self.is_default = ai.is_default + self.uri = ai.acc_uri + self.reg_active = ai.has_registration + self.reg_expires = ai.expires + self.reg_status = ai.status + self.reg_reason = ai.status_text + self.online_status = ai.online_status + self.online_text = ai.online_status_text + +# Account callback +class AccountCallback: + """Class to receive notifications on account's events. + + Derive a class from this class and register it to the Account object + using Account.set_callback() to start receiving events from the Account + object. + + Member documentation: + + account -- the Account object. + + """ + account = None + + def __init__(self, account=None): + self._set_account(account) + + def __del__(self): + pass + + def _set_account(self, account): + if account: + self.account = weakref.proxy(account) + else: + self.account = None + + def on_reg_state(self): + """Notification that the registration status has changed. + """ + pass + + def on_incoming_call(self, call): + """Notification about incoming call. + + Unless this callback is implemented, the default behavior is to + reject the call with default status code. + + Keyword arguments: + call -- the new incoming call + """ + call.hangup() + + def on_incoming_subscribe(self, buddy, from_uri, contact_uri, pres_obj): + """Notification when incoming SUBSCRIBE request is received. + + Application may use this callback to authorize the incoming + subscribe request (e.g. ask user permission if the request + should be granted) + + Keyword arguments: + buddy -- The buddy object, if buddy is found. Otherwise + the value is None. + from_uri -- The URI string of the sender. + pres_obj -- Opaque presence subscription object, which is + needed by Account.pres_notify() + + Return: + Tuple (code, reason), where: + code: The status code. If code is >= 300, the + request is rejected. If code is 200, the + request is accepted and NOTIFY will be sent + automatically. If code is 202, application + must accept or reject the request later with + Account.press_notify(). + reason: Optional reason phrase, or None to use the + default reasoh phrase for the status code. + """ + return (200, None) + + def on_pager(self, from_uri, contact, mime_type, body): + """ + Notification that incoming instant message is received on + this account. + + Keyword arguments: + from_uri -- sender's URI + contact -- sender's Contact URI + mime_type -- MIME type of the instant message body + body -- the instant message body + + """ + pass + + def on_pager_status(self, to_uri, body, im_id, code, reason): + """ + Notification about the delivery status of previously sent + instant message. + + Keyword arguments: + to_uri -- the destination URI of the message + body -- the message body + im_id -- message ID + code -- SIP status code + reason -- SIP reason phrase + + """ + pass + + def on_typing(self, from_uri, contact, is_typing): + """ + Notification that remote is typing or stop typing. + + Keyword arguments: + buddy -- Buddy object for the sender, if found. Otherwise + this will be None + from_uri -- sender's URI of the indication + contact -- sender's contact URI + is_typing -- boolean to indicate whether remote is currently + typing an instant message. + + """ + pass + + def on_mwi_info(self, body): + """ + Notification about change in Message Summary / Message Waiting + Indication (RFC 3842) status. MWI subscription must be enabled + in the account config to receive this notification. + + Keyword arguments: + body -- String containing message body as received in the + NOTIFY request. + + """ + pass + + + +class Account: + """This describes SIP account class. + + PJSUA accounts provide identity (or identities) of the user who is + currently using the application. In SIP terms, the identity is used + as the From header in outgoing requests. + + Account may or may not have client registration associated with it. + An account is also associated with route set and some authentication + credentials, which are used when sending SIP request messages using + the account. An account also has presence's online status, which + will be reported to remote peer when they subscribe to the account's + presence, or which is published to a presence server if presence + publication is enabled for the account. + + Account is created with Lib.create_account(). At least one account + MUST be created. If no user association is required, application can + create a userless account by calling Lib.create_account_for_transport(). + A userless account identifies local endpoint instead of a particular + user, and it correspond with a particular transport instance. + + Also one account must be set as the default account, which is used as + the account to use when PJSUA fails to match a request with any other + accounts. + + """ + _id = -1 + _lib = None + _cb = AccountCallback(None) + _obj_name = "" + + def __init__(self, lib, id, cb=None): + """Construct this class. This is normally called by Lib class and + not by application. + + Keyword arguments: + lib -- the Lib instance. + id -- the pjsua account ID. + cb -- AccountCallback instance to receive events from this Account. + If callback is not specified here, it must be set later + using set_callback(). + """ + self._id = id + self._lib = weakref.ref(lib) + self._obj_name = "{Account " + self.info().uri + "}" + self.set_callback(cb) + _pjsua.acc_set_user_data(self._id, self) + _Trace((self, 'created')) + + def __del__(self): + if self._id != -1: + _pjsua.acc_set_user_data(self._id, 0) + _Trace((self, 'destroyed')) + + def __str__(self): + return self._obj_name + + def info(self): + """Retrieve AccountInfo for this account. + """ + lck = self._lib().auto_lock() + ai = _pjsua.acc_get_info(self._id) + if ai==None: + self._lib()._err_check("info()", self, -1, "Invalid account") + return AccountInfo(ai) + + def is_valid(self): + """ + Check if this account is still valid. + + """ + lck = self._lib().auto_lock() + return _pjsua.acc_is_valid(self._id) + + def set_callback(self, cb): + """Register callback to receive notifications from this object. + + Keyword argument: + cb -- AccountCallback instance. + + """ + if cb: + self._cb = cb + else: + self._cb = AccountCallback(self) + self._cb._set_account(self) + + def set_default(self): + """ Set this account as default account to send outgoing requests + and as the account to receive incoming requests when more exact + matching criteria fails. + """ + lck = self._lib().auto_lock() + err = _pjsua.acc_set_default(self._id) + self._lib()._err_check("set_default()", self, err) + + def is_default(self): + """ Check if this account is the default account. + + """ + lck = self._lib().auto_lock() + def_id = _pjsua.acc_get_default() + return self.is_valid() and def_id==self._id + + def delete(self): + """ Delete this account. + + """ + lck = self._lib().auto_lock() + err = _pjsua.acc_set_user_data(self._id, 0) + self._lib()._err_check("delete()", self, err) + err = _pjsua.acc_del(self._id) + self._lib()._err_check("delete()", self, err) + self._id = -1 + + def set_basic_status(self, is_online): + """ Set basic presence status of this account. + + Keyword argument: + is_online -- boolean to indicate basic presence availability. + + """ + lck = self._lib().auto_lock() + err = _pjsua.acc_set_online_status(self._id, is_online) + self._lib()._err_check("set_basic_status()", self, err) + + def set_presence_status(self, is_online, + activity=PresenceActivity.UNKNOWN, + pres_text="", rpid_id=""): + """ Set presence status of this account. + + Keyword arguments: + is_online -- boolean to indicate basic presence availability + activity -- value from PresenceActivity + pres_text -- optional string to convey additional information about + the activity (such as "On the phone") + rpid_id -- optional string to be placed as RPID ID. + + """ + lck = self._lib().auto_lock() + err = _pjsua.acc_set_online_status2(self._id, is_online, activity, + pres_text, rpid_id) + self._lib()._err_check("set_presence_status()", self, err) + + def set_registration(self, renew): + """Manually renew registration or unregister from the server. + + Keyword argument: + renew -- boolean to indicate whether registration is renewed. + Setting this value for False will trigger unregistration. + + """ + lck = self._lib().auto_lock() + err = _pjsua.acc_set_registration(self._id, renew) + self._lib()._err_check("set_registration()", self, err) + + def set_transport(self, transport): + """Set this account to only use the specified transport to send + outgoing requests. + + Keyword argument: + transport -- Transport object. + + """ + lck = self._lib().auto_lock() + err = _pjsua.acc_set_transport(self._id, transport._id) + self._lib()._err_check("set_transport()", self, err) + + def make_call(self, dst_uri, cb=None, hdr_list=None): + """Make outgoing call to the specified URI. + + Keyword arguments: + dst_uri -- Destination SIP URI. + cb -- CallCallback instance to be installed to the newly + created Call object. If this CallCallback is not + specified (i.e. None is given), it must be installed + later using call.set_callback(). + hdr_list -- Optional list of headers to be sent with outgoing + INVITE + + Return: + Call instance. + """ + lck = self._lib().auto_lock() + call = Call(self._lib(), -1, cb) + err, cid = _pjsua.call_make_call(self._id, dst_uri, 0, + call, Lib._create_msg_data(hdr_list)) + self._lib()._err_check("make_call()", self, err) + call.attach_to_id(cid) + return call + + def add_buddy(self, uri, cb=None): + """Add new buddy. + + Keyword argument: + uri -- SIP URI of the buddy + cb -- BuddyCallback instance to be installed to the newly + created Buddy object. If this callback is not specified + (i.e. None is given), it must be installed later using + buddy.set_callback(). + + Return: + Buddy object + """ + lck = self._lib().auto_lock() + buddy_cfg = _pjsua.buddy_config_default() + buddy_cfg.uri = uri + buddy_cfg.subscribe = False + err, buddy_id = _pjsua.buddy_add(buddy_cfg) + self._lib()._err_check("add_buddy()", self, err) + buddy = Buddy(self._lib(), buddy_id, self, cb) + return buddy + + def pres_notify(self, pres_obj, state, reason="", hdr_list=None): + """Send NOTIFY to inform account presence status or to terminate + server side presence subscription. + + Keyword arguments: + pres_obj -- The subscription object from on_incoming_subscribe() + callback + state -- Subscription state, from SubscriptionState + reason -- Optional reason phrase. + hdr_list -- Optional header list. + """ + lck = self._lib().auto_lock() + _pjsua.acc_pres_notify(self._id, pres_obj, state, reason, + Lib._create_msg_data(hdr_list)) + + def send_pager(self, uri, text, im_id=0, content_type="text/plain", \ + hdr_list=None): + """Send instant message to arbitrary URI. + + Keyword arguments: + text -- Instant message to be sent + uri -- URI to send the Instant Message to. + im_id -- Optional instant message ID to identify this + instant message when delivery status callback + is called. + content_type -- MIME type identifying the instant message + hdr_list -- Optional list of headers to be sent with the + request. + + """ + lck = self._lib().auto_lock() + err = _pjsua.im_send(self._id, uri, \ + content_type, text, \ + Lib._create_msg_data(hdr_list), \ + im_id) + self._lib()._err_check("send_pager()", self, err) + +class CallCallback: + """Class to receive event notification from Call objects. + + Use Call.set_callback() method to install instance of this callback + class to receive event notifications from the call object. + + Member documentation: + + call -- the Call object. + + """ + call = None + + def __init__(self, call=None): + self._set_call(call) + + def __del__(self): + pass + + def _set_call(self, call): + if call: + self.call = weakref.proxy(call) + else: + self.call = None + + def on_state(self): + """Notification that the call's state has changed. + + """ + pass + + def on_media_state(self): + """Notification that the call's media state has changed. + + """ + pass + + def on_dtmf_digit(self, digits): + """Notification on incoming DTMF digits. + + Keyword argument: + digits -- string containing the received digits. + + """ + pass + + def on_transfer_request(self, dst, code): + """Notification that call is being transfered by remote party. + + Application can decide to accept/reject transfer request by returning + code greater than or equal to 500. The default behavior is to accept + the transfer by returning 202. + + Keyword arguments: + dst -- string containing the destination URI + code -- the suggested status code to return to accept the request. + + Return: + the callback should return 202 to accept the request, or 300-699 to + reject the request. + + """ + return code + + def on_transfer_status(self, code, reason, final, cont): + """ + Notification about the status of previous call transfer request. + + Keyword arguments: + code -- SIP status code to indicate completion status. + text -- SIP status reason phrase. + final -- if True then this is a final status and no further + notifications will be sent for this call transfer + status. + cont -- suggested return value. + + Return: + If the callback returns false then no further notification will + be sent for the transfer request for this call. + + """ + return cont + + def on_replace_request(self, code, reason): + """Notification when incoming INVITE with Replaces header is received. + + Application may reject the request by returning value greather than + or equal to 500. The default behavior is to accept the request. + + Keyword arguments: + code -- default status code to return + reason -- default reason phrase to return + + Return: + The callback should return (code, reason) tuple. + + """ + return code, reason + + def on_replaced(self, new_call): + """ + Notification that this call will be replaced with new_call. + After this callback is called, this call will be disconnected. + + Keyword arguments: + new_call -- the new call that will replace this call. + """ + pass + + def on_pager(self, mime_type, body): + """ + Notification that incoming instant message is received on + this call. + + Keyword arguments: + mime_type -- MIME type of the instant message body. + body -- the instant message body. + + """ + pass + + def on_pager_status(self, body, im_id, code, reason): + """ + Notification about the delivery status of previously sent + instant message. + + Keyword arguments: + body -- message body + im_id -- message ID + code -- SIP status code + reason -- SIP reason phrase + + """ + pass + + def on_typing(self, is_typing): + """ + Notification that remote is typing or stop typing. + + Keyword arguments: + is_typing -- boolean to indicate whether remote is currently + typing an instant message. + + """ + pass + + +class CallInfo: + """This structure contains various information about Call. + + Application may retrieve this information with Call.info(). + + Member documentation: + + role -- CallRole + account -- Account object. + uri -- SIP URI of local account. + contact -- local Contact URI. + remote_uri -- remote SIP URI. + remote_contact -- remote Contact URI + sip_call_id -- call's Call-ID identification + state -- CallState + state_text -- state text. + last_code -- last SIP status code + last_reason -- text phrase for last_code + media_state -- MediaState + media_dir -- MediaDir + conf_slot -- conference slot number for this call. + call_time -- call's connected duration in seconds. + total_time -- total call duration in seconds. + """ + role = CallRole.CALLER + account = None + uri = "" + contact = "" + remote_uri = "" + remote_contact = "" + sip_call_id = "" + state = CallState.NULL + state_text = "" + last_code = 0 + last_reason = "" + media_state = MediaState.NULL + media_dir = MediaDir.NULL + conf_slot = -1 + call_time = 0 + total_time = 0 + + def __init__(self, lib=None, ci=None): + if lib and ci: + self._cvt_from_pjsua(lib, ci) + + def _cvt_from_pjsua(self, lib, ci): + self.role = ci.role + self.account = lib._lookup_account(ci.acc_id) + self.uri = ci.local_info + self.contact = ci.local_contact + self.remote_uri = ci.remote_info + self.remote_contact = ci.remote_contact + self.sip_call_id = ci.call_id + self.state = ci.state + self.state_text = ci.state_text + self.last_code = ci.last_status + self.last_reason = ci.last_status_text + self.media_state = ci.media_status + self.media_dir = ci.media_dir + self.conf_slot = ci.conf_slot + self.call_time = ci.connect_duration / 1000 + self.total_time = ci.total_duration / 1000 + + +class Call: + """This class represents SIP call. + + Application initiates outgoing call with Account.make_call(), and + incoming calls are reported in AccountCallback.on_incoming_call(). + """ + _id = -1 + _cb = None + _lib = None + _obj_name = "" + + def __init__(self, lib, call_id, cb=None): + self._lib = weakref.ref(lib) + self.set_callback(cb) + self.attach_to_id(call_id) + _Trace((self, 'created')) + + def __del__(self): + if self._id != -1: + _pjsua.call_set_user_data(self._id, 0) + _Trace((self, 'destroyed')) + + def __str__(self): + return self._obj_name + + def attach_to_id(self, call_id): + lck = self._lib().auto_lock() + if self._id != -1: + _pjsua.call_set_user_data(self._id, 0) + self._id = call_id + if self._id != -1: + _pjsua.call_set_user_data(self._id, self) + self._obj_name = "{Call " + self.info().remote_uri + "}" + else: + self._obj_name = "{Call object}" + + def set_callback(self, cb): + """ + Set callback object to retrieve event notifications from this call. + + Keyword arguments: + cb -- CallCallback instance. + """ + if cb: + self._cb = cb + else: + self._cb = CallCallback(self) + self._cb._set_call(self) + + def info(self): + """ + Get the CallInfo. + """ + lck = self._lib().auto_lock() + ci = _pjsua.call_get_info(self._id) + if not ci: + self._lib()._err_check("info", self, -1, "Invalid call") + call_info = CallInfo(self._lib(), ci) + return call_info + + def is_valid(self): + """ + Check if this call is still valid. + """ + lck = self._lib().auto_lock() + return _pjsua.call_is_active(self._id) + + def dump_status(self, with_media=True, indent="", max_len=1024): + """ + Dump the call status. + """ + lck = self._lib().auto_lock() + return _pjsua.call_dump(self._id, with_media, max_len, indent) + + def answer(self, code=200, reason="", hdr_list=None): + """ + Send provisional or final response to incoming call. + + Keyword arguments: + code -- SIP status code. + reason -- Reason phrase. Put empty to send default reason + phrase for the status code. + hdr_list -- Optional list of headers to be sent with the + INVITE response. + + """ + lck = self._lib().auto_lock() + err = _pjsua.call_answer(self._id, code, reason, + Lib._create_msg_data(hdr_list)) + self._lib()._err_check("answer()", self, err) + + def hangup(self, code=603, reason="", hdr_list=None): + """ + Terminate the call. + + Keyword arguments: + code -- SIP status code. + reason -- Reason phrase. Put empty to send default reason + phrase for the status code. + hdr_list -- Optional list of headers to be sent with the + message. + + """ + lck = self._lib().auto_lock() + err = _pjsua.call_hangup(self._id, code, reason, + Lib._create_msg_data(hdr_list)) + self._lib()._err_check("hangup()", self, err) + + def hold(self, hdr_list=None): + """ + Put the call on hold. + + Keyword arguments: + hdr_list -- Optional list of headers to be sent with the + message. + """ + lck = self._lib().auto_lock() + err = _pjsua.call_set_hold(self._id, Lib._create_msg_data(hdr_list)) + self._lib()._err_check("hold()", self, err) + + def unhold(self, hdr_list=None): + """ + Release the call from hold. + + Keyword arguments: + hdr_list -- Optional list of headers to be sent with the + message. + + """ + lck = self._lib().auto_lock() + err = _pjsua.call_reinvite(self._id, True, + Lib._create_msg_data(hdr_list)) + self._lib()._err_check("unhold()", self, err) + + def reinvite(self, hdr_list=None): + """ + Send re-INVITE and optionally offer new codecs to use. + + Keyword arguments: + hdr_list -- Optional list of headers to be sent with the + message. + + """ + lck = self._lib().auto_lock() + err = _pjsua.call_reinvite(self._id, True, + Lib._create_msg_data(hdr_list)) + self._lib()._err_check("reinvite()", self, err) + + def update(self, hdr_list=None, options=0): + """ + Send UPDATE and optionally offer new codecs to use. + + Keyword arguments: + hdr_list -- Optional list of headers to be sent with the + message. + options -- Must be zero for now. + + """ + lck = self._lib().auto_lock() + err = _pjsua.call_update(self._id, options, + Lib._create_msg_data(hdr_list)) + self._lib()._err_check("update()", self, err) + + def transfer(self, dest_uri, hdr_list=None): + """ + Transfer the call to new destination. + + Keyword arguments: + dest_uri -- Specify the SIP URI to transfer the call to. + hdr_list -- Optional list of headers to be sent with the + message. + + """ + lck = self._lib().auto_lock() + err = _pjsua.call_xfer(self._id, dest_uri, + Lib._create_msg_data(hdr_list)) + self._lib()._err_check("transfer()", self, err) + + def transfer_to_call(self, call, hdr_list=None, options=0): + """ + Attended call transfer. + + Keyword arguments: + call -- The Call object to transfer call to. + hdr_list -- Optional list of headers to be sent with the + message. + options -- Must be zero for now. + + """ + lck = self._lib().auto_lock() + err = _pjsua.call_xfer_replaces(self._id, call._id, options, + Lib._create_msg_data(hdr_list)) + self._lib()._err_check("transfer_to_call()", self, err) + + def dial_dtmf(self, digits): + """ + Send DTMF digits with RTP event package. + + Keyword arguments: + digits -- DTMF digit string. + + """ + lck = self._lib().auto_lock() + err = _pjsua.call_dial_dtmf(self._id, digits) + self._lib()._err_check("dial_dtmf()", self, err) + + def send_request(self, method, hdr_list=None, content_type=None, + body=None): + """ + Send arbitrary request to remote call. + + This is useful for example to send INFO request. Note that this + function should not be used to send request that will change the + call state such as CANCEL or BYE. + + Keyword arguments: + method -- SIP method name. + hdr_list -- Optional header list to be sent with the request. + content_type -- Content type to describe the body, if the body + is present + body -- Optional SIP message body. + + """ + lck = self._lib().auto_lock() + if hdr_list or body: + msg_data = _pjsua.Msg_Data() + if hdr_list: + msg_data.hdr_list = hdr_list + if content_type: + msg_data.content_type = content_type + if body: + msg_data.msg_body = body + else: + msg_data = None + + err = _pjsua.call_send_request(self._id, method, msg_data) + self._lib()._err_check("send_request()", self, err) + + def send_pager(self, text, im_id=0, content_type="text/plain", + hdr_list=None): + """Send instant message inside a call. + + Keyword arguments: + text -- Instant message to be sent + im_id -- Optional instant message ID to identify this + instant message when delivery status callback + is called. + content_type -- MIME type identifying the instant message + hdr_list -- Optional list of headers to be sent with the + request. + + """ + lck = self._lib().auto_lock() + err = _pjsua.call_send_im(self._id, \ + content_type, text, \ + Lib._create_msg_data(hdr_list), \ + im_id) + self._lib()._err_check("send_pager()", self, err) + + +class BuddyInfo: + """This class contains information about Buddy. Application may + retrieve this information by calling Buddy.info(). + + Member documentation: + + uri -- the Buddy URI. + contact -- the Buddy Contact URI, if available. + online_status -- the presence online status. + online_text -- the presence online status text. + activity -- the PresenceActivity + subscribed -- specify whether buddy's presence status is currently + being subscribed. + sub_state -- SubscriptionState + sub_term_reason -- The termination reason string of the last presence + subscription to this buddy, if any. + """ + uri = "" + contact = "" + online_status = 0 + online_text = "" + activity = PresenceActivity.UNKNOWN + subscribed = False + sub_state = SubscriptionState.NULL + sub_term_reason = "" + + def __init__(self, pjsua_bi=None): + if pjsua_bi: + self._cvt_from_pjsua(pjsua_bi) + + def _cvt_from_pjsua(self, inf): + self.uri = inf.uri + self.contact = inf.contact + self.online_status = inf.status + self.online_text = inf.status_text + self.activity = inf.activity + self.subscribed = inf.monitor_pres + self.sub_state = inf.sub_state + self.sub_term_reason = inf.sub_term_reason + + +class BuddyCallback: + """This class can be used to receive notifications about Buddy's + presence status change. Application needs to derive a class from + this class, and register the instance with Buddy.set_callback(). + + Member documentation: + + buddy -- the Buddy object. + """ + buddy = None + + def __init__(self, buddy=None): + self._set_buddy(buddy) + + def _set_buddy(self, buddy): + if buddy: + self.buddy = weakref.proxy(buddy) + else: + self.buddy = None + + def on_state(self): + """ + Notification that buddy's presence state has changed. Application + may then retrieve the new status with Buddy.info() function. + """ + pass + + def on_pager(self, mime_type, body): + """Notification that incoming instant message is received from + this buddy. + + Keyword arguments: + mime_type -- MIME type of the instant message body + body -- the instant message body + + """ + pass + + def on_pager_status(self, body, im_id, code, reason): + """Notification about the delivery status of previously sent + instant message. + + Keyword arguments: + body -- the message body + im_id -- message ID + code -- SIP status code + reason -- SIP reason phrase + + """ + pass + + def on_typing(self, is_typing): + """Notification that remote is typing or stop typing. + + Keyword arguments: + is_typing -- boolean to indicate whether remote is currently + typing an instant message. + + """ + pass + + +class Buddy: + """A Buddy represents person or remote agent. + + This class provides functions to subscribe to buddy's presence and + to send or receive instant messages from the buddy. + """ + _id = -1 + _lib = None + _cb = None + _obj_name = "" + _acc = None + + def __init__(self, lib, id, account, cb): + self._id = id + self._lib = weakref.ref(lib) + self._acc = weakref.ref(account) + self._obj_name = "{Buddy " + self.info().uri + "}" + self.set_callback(cb) + _pjsua.buddy_set_user_data(self._id, self) + _Trace((self, 'created')) + + def __del__(self): + if self._id != -1: + _pjsua.buddy_set_user_data(self._id, 0) + _Trace((self, 'destroyed')) + + def __str__(self): + return self._obj_name + + def info(self): + """ + Get buddy info as BuddyInfo. + """ + lck = self._lib().auto_lock() + return BuddyInfo(_pjsua.buddy_get_info(self._id)) + + def set_callback(self, cb): + """Install callback to receive notifications from this object. + + Keyword argument: + cb -- BuddyCallback instance. + """ + if cb: + self._cb = cb + else: + self._cb = BuddyCallback(self) + self._cb._set_buddy(self) + + def subscribe(self): + """ + Subscribe to buddy's presence status notification. + """ + lck = self._lib().auto_lock() + err = _pjsua.buddy_subscribe_pres(self._id, True) + self._lib()._err_check("subscribe()", self, err) + + def unsubscribe(self): + """ + Unsubscribe from buddy's presence status notification. + """ + lck = self._lib().auto_lock() + err = _pjsua.buddy_subscribe_pres(self._id, False) + self._lib()._err_check("unsubscribe()", self, err) + + def delete(self): + """ + Remove this buddy from the buddy list. + """ + lck = self._lib().auto_lock() + if self._id != -1: + _pjsua.buddy_set_user_data(self._id, 0) + err = _pjsua.buddy_del(self._id) + self._lib()._err_check("delete()", self, err) + + def send_pager(self, text, im_id=0, content_type="text/plain", \ + hdr_list=None): + """Send instant message to remote buddy. + + Keyword arguments: + text -- Instant message to be sent + im_id -- Optional instant message ID to identify this + instant message when delivery status callback + is called. + content_type -- MIME type identifying the instant message + hdr_list -- Optional list of headers to be sent with the + request. + + """ + lck = self._lib().auto_lock() + err = _pjsua.im_send(self._acc()._id, self.info().uri, \ + content_type, text, \ + Lib._create_msg_data(hdr_list), \ + im_id) + self._lib()._err_check("send_pager()", self, err) + + def send_typing_ind(self, is_typing=True, hdr_list=None): + """Send typing indication to remote buddy. + + Keyword argument: + is_typing -- boolean to indicate wheter user is typing. + hdr_list -- Optional list of headers to be sent with the + request. + + """ + lck = self._lib().auto_lock() + err = _pjsua.im_typing(self._acc()._id, self.info().uri, \ + is_typing, Lib._create_msg_data(hdr_list)) + self._lib()._err_check("send_typing_ind()", self, err) + + + +# Sound device info +class SoundDeviceInfo: + """This described the sound device info. + + Member documentation: + name -- device name. + input_channels -- number of capture channels supported. + output_channels -- number of playback channels supported. + default_clock_rate -- default sampling rate. + """ + name = "" + input_channels = 0 + output_channels = 0 + default_clock_rate = 0 + + def __init__(self, sdi): + self.name = sdi.name + self.input_channels = sdi.input_count + self.output_channels = sdi.output_count + self.default_clock_rate = sdi.default_samples_per_sec + + +# Codec info +class CodecInfo: + """This describes codec info. + + Member documentation: + name -- codec name + priority -- codec priority (0-255) + clock_rate -- clock rate + channel_count -- number of channels + avg_bps -- average bandwidth in bits per second + frm_ptime -- base frame length in milliseconds + ptime -- RTP frame length in milliseconds. + pt -- payload type. + vad_enabled -- specify if Voice Activity Detection is currently + enabled. + plc_enabled -- specify if Packet Lost Concealment is currently + enabled. + """ + name = "" + priority = 0 + clock_rate = 0 + channel_count = 0 + avg_bps = 0 + frm_ptime = 0 + ptime = 0 + pt = 0 + vad_enabled = False + plc_enabled = False + + def __init__(self, codec_info, codec_param): + self.name = codec_info.codec_id + self.priority = codec_info.priority + self.clock_rate = codec_param.info.clock_rate + self.channel_count = codec_param.info.channel_cnt + self.avg_bps = codec_param.info.avg_bps + self.frm_ptime = codec_param.info.frm_ptime + self.ptime = codec_param.info.frm_ptime * \ + codec_param.setting.frm_per_pkt + self.ptime = codec_param.info.pt + self.vad_enabled = codec_param.setting.vad + self.plc_enabled = codec_param.setting.plc + + def _cvt_to_pjsua(self): + ci = _pjsua.Codec_Info() + ci.codec_id = self.name + ci.priority = self.priority + return ci + + +# Codec parameter +class CodecParameter: + """This specifies various parameters that can be configured for codec. + + Member documentation: + + ptime -- specify the outgoing RTP packet length in milliseconds. + vad_enabled -- specify if VAD should be enabled. + plc_enabled -- specify if PLC should be enabled. + """ + ptime = 0 + vad_enabled = False + plc_enabled = False + _codec_param = None + + def __init__(self, codec_param): + self.ptime = codec_param.info.frm_ptime * \ + codec_param.setting.frm_per_pkt + self.vad_enabled = codec_param.setting.vad + self.plc_enabled = codec_param.setting.plc + self._codec_param = codec_param + + def _cvt_to_pjsua(self): + self._codec_param.setting.frm_per_pkt = self.ptime / \ + self._codec_param.info.frm_ptime + self._codec_param.setting.vad = self.vad_enabled + self._codec_param.setting.plc = self.plc_enabled + return self._codec_param + + +# Library mutex +class _LibMutex: + def __init__(self, lck): + self._lck = lck + self._lck.acquire() + #_Trace(('lock acquired',)) + + def __del__(self): + try: + self._lck.release() + #_Trace(('lock released',)) + except: + #_Trace(('lock release error',)) + pass + + +# PJSUA Library +_lib = None +enable_trace = False + +class Lib: + """Library instance. + + """ + _quit = False + _has_thread = False + _lock = None + + def __init__(self): + global _lib + if _lib: + raise Error("__init()__", None, -1, + "Library instance already exist") + + self._lock = threading.RLock() + err = _pjsua.create() + self._err_check("_pjsua.create()", None, err) + _lib = self + + def __del__(self): + _pjsua.destroy() + del self._lock + _Trace(('Lib destroyed',)) + + def __str__(self): + return "Lib" + + @staticmethod + def instance(): + """Return singleton instance of Lib. + """ + return _lib + + def init(self, ua_cfg=None, log_cfg=None, media_cfg=None): + """ + Initialize pjsua with the specified configurations. + + Keyword arguments: + ua_cfg -- optional UAConfig instance + log_cfg -- optional LogConfig instance + media_cfg -- optional MediaConfig instance + + """ + if not ua_cfg: ua_cfg = UAConfig() + if not log_cfg: log_cfg = LogConfig() + if not media_cfg: media_cfg = MediaConfig() + + py_ua_cfg = ua_cfg._cvt_to_pjsua() + py_ua_cfg.cb.on_call_state = _cb_on_call_state + py_ua_cfg.cb.on_incoming_call = _cb_on_incoming_call + py_ua_cfg.cb.on_call_media_state = _cb_on_call_media_state + py_ua_cfg.cb.on_dtmf_digit = _cb_on_dtmf_digit + py_ua_cfg.cb.on_call_transfer_request = _cb_on_call_transfer_request + py_ua_cfg.cb.on_call_transfer_status = _cb_on_call_transfer_status + py_ua_cfg.cb.on_call_replace_request = _cb_on_call_replace_request + py_ua_cfg.cb.on_call_replaced = _cb_on_call_replaced + py_ua_cfg.cb.on_reg_state = _cb_on_reg_state + py_ua_cfg.cb.on_incoming_subscribe = _cb_on_incoming_subscribe + py_ua_cfg.cb.on_buddy_state = _cb_on_buddy_state + py_ua_cfg.cb.on_pager = _cb_on_pager + py_ua_cfg.cb.on_pager_status = _cb_on_pager_status + py_ua_cfg.cb.on_typing = _cb_on_typing + py_ua_cfg.cb.on_mwi_info = _cb_on_mwi_info; + + err = _pjsua.init(py_ua_cfg, log_cfg._cvt_to_pjsua(), + media_cfg._cvt_to_pjsua()) + self._err_check("init()", self, err) + + def destroy(self): + """Destroy the library, and pjsua.""" + global _lib + if self._has_thread: + self._quit = 1 + loop = 0 + while self._quit != 2 and loop < 400: + self.handle_events(5) + loop = loop + 1 + time.sleep(0.050) + _pjsua.destroy() + _lib = None + + def start(self, with_thread=True): + """Start the library. + + Keyword argument: + with_thread -- specify whether the module should create worker + thread. + + """ + lck = self.auto_lock() + err = _pjsua.start() + self._err_check("start()", self, err) + self._has_thread = with_thread + if self._has_thread: + thread.start_new(_worker_thread_main, (0,)) + + def handle_events(self, timeout=50): + """Poll the events from underlying pjsua library. + + Application must poll the stack periodically if worker thread + is disable when starting the library. + + Keyword argument: + timeout -- in milliseconds. + + """ + lck = self.auto_lock() + return _pjsua.handle_events(timeout) + + def thread_register(self, name): + """Register external threads (threads that are not created by PJSIP, + such as threads that are created by Python API) to PJSIP. + + The call must be made from the new thread before calling any pjlib + functions. + + Keyword arguments: + name -- Non descriptive name for the thread + """ + dummy = 1 + err = _pjsua.thread_register(name, dummy) + self._err_check("thread_register()", self, err) + + def verify_sip_url(self, sip_url): + """Verify that the specified string is a valid URI. + + Keyword argument: + sip_url -- the URL string. + + Return: + 0 is the the URI is valid, otherwise the appropriate error + code is returned. + + """ + lck = self.auto_lock() + return _pjsua.verify_sip_url(sip_url) + + def create_transport(self, type, cfg=None): + """Create SIP transport instance of the specified type. + + Keyword arguments: + type -- transport type from TransportType constant. + cfg -- TransportConfig instance + + Return: + Transport object + + """ + lck = self.auto_lock() + if not cfg: cfg=TransportConfig() + err, tp_id = _pjsua.transport_create(type, cfg._cvt_to_pjsua()) + self._err_check("create_transport()", self, err) + return Transport(self, tp_id) + + def create_account(self, acc_config, set_default=True, cb=None): + """ + Create a new local pjsua account using the specified configuration. + + Keyword arguments: + acc_config -- AccountConfig + set_default -- boolean to specify whether to use this as the + default account. + cb -- AccountCallback instance. + + Return: + Account instance + + """ + lck = self.auto_lock() + err, acc_id = _pjsua.acc_add(acc_config._cvt_to_pjsua(), set_default) + self._err_check("create_account()", self, err) + return Account(self, acc_id, cb) + + def create_account_for_transport(self, transport, set_default=True, + cb=None): + """Create a new local pjsua transport for the specified transport. + + Keyword arguments: + transport -- the Transport instance. + set_default -- boolean to specify whether to use this as the + default account. + cb -- AccountCallback instance. + + Return: + Account instance + + """ + lck = self.auto_lock() + err, acc_id = _pjsua.acc_add_local(transport._id, set_default) + self._err_check("create_account_for_transport()", self, err) + return Account(self, acc_id, cb) + + def hangup_all(self): + """Hangup all calls. + + """ + lck = self.auto_lock() + _pjsua.call_hangup_all() + + # Sound device API + + def enum_snd_dev(self): + """Enumerate sound devices in the system. + + Return: + list of SoundDeviceInfo. The index of the element specifies + the device ID for the device. + """ + lck = self.auto_lock() + sdi_list = _pjsua.enum_snd_devs() + info = [] + for sdi in sdi_list: + info.append(SoundDeviceInfo(sdi)) + return info + + def get_snd_dev(self): + """Get the device IDs of current sound devices used by pjsua. + + Return: + (capture_dev_id, playback_dev_id) tuple + """ + lck = self.auto_lock() + return _pjsua.get_snd_dev() + + def set_snd_dev(self, capture_dev, playback_dev): + """Change the current sound devices. + + Keyword arguments: + capture_dev -- the device ID of capture device to be used + playback_dev -- the device ID of playback device to be used. + + """ + lck = self.auto_lock() + err = _pjsua.set_snd_dev(capture_dev, playback_dev) + self._err_check("set_current_sound_devices()", self, err) + + def set_null_snd_dev(self): + """Disable the sound devices. This is useful if the system + does not have sound device installed. + + """ + lck = self.auto_lock() + err = _pjsua.set_null_snd_dev() + self._err_check("set_null_snd_dev()", self, err) + + + # Conference bridge + + def conf_get_max_ports(self): + """Get the conference bridge capacity. + + Return: + conference bridge capacity. + + """ + lck = self.auto_lock() + return _pjsua.conf_get_max_ports() + + def conf_connect(self, src_slot, dst_slot): + """Establish unidirectional media flow from souce to sink. + + One source may transmit to multiple destinations/sink. And if + multiple sources are transmitting to the same sink, the media + will be mixed together. Source and sink may refer to the same ID, + effectively looping the media. + + If bidirectional media flow is desired, application needs to call + this function twice, with the second one having the arguments + reversed. + + Keyword arguments: + src_slot -- integer to identify the conference slot number of + the source/transmitter. + dst_slot -- integer to identify the conference slot number of + the destination/receiver. + + """ + lck = self.auto_lock() + err = _pjsua.conf_connect(src_slot, dst_slot) + self._err_check("conf_connect()", self, err) + + def conf_disconnect(self, src_slot, dst_slot): + """Disconnect media flow from the source to destination port. + + Keyword arguments: + src_slot -- integer to identify the conference slot number of + the source/transmitter. + dst_slot -- integer to identify the conference slot number of + the destination/receiver. + + """ + lck = self.auto_lock() + err = _pjsua.conf_disconnect(src_slot, dst_slot) + self._err_check("conf_disconnect()", self, err) + + def conf_set_tx_level(self, slot, level): + """Adjust the signal level to be transmitted from the bridge to + the specified port by making it louder or quieter. + + Keyword arguments: + slot -- integer to identify the conference slot number. + level -- Signal level adjustment. Value 1.0 means no level + adjustment, while value 0 means to mute the port. + """ + lck = self.auto_lock() + err = _pjsua.conf_set_tx_level(slot, level) + self._err_check("conf_set_tx_level()", self, err) + + def conf_set_rx_level(self, slot, level): + """Adjust the signal level to be received from the specified port + (to the bridge) by making it louder or quieter. + + Keyword arguments: + slot -- integer to identify the conference slot number. + level -- Signal level adjustment. Value 1.0 means no level + adjustment, while value 0 means to mute the port. + """ + lck = self.auto_lock() + err = _pjsua.conf_set_rx_level(slot, level) + self._err_check("conf_set_rx_level()", self, err) + + def conf_get_signal_level(self, slot): + """Get last signal level transmitted to or received from the + specified port. The signal levels are float values from 0.0 to 1.0, + with 0.0 indicates no signal, and 1.0 indicates the loudest signal + level. + + Keyword arguments: + slot -- integer to identify the conference slot number. + + Return value: + (tx_level, rx_level) tuple. + """ + lck = self.auto_lock() + err, tx_level, rx_level = _pjsua.conf_get_signal_level(slot) + self._err_check("conf_get_signal_level()", self, err) + return (tx_level, rx_level) + + + + # Codecs API + + def enum_codecs(self): + """Return list of codecs supported by pjsua. + + Return: + list of CodecInfo + + """ + lck = self.auto_lock() + ci_list = _pjsua.enum_codecs() + codec_info = [] + for ci in ci_list: + cp = _pjsua.codec_get_param(ci.codec_id) + if cp: + codec_info.append(CodecInfo(ci, cp)) + return codec_info + + def set_codec_priority(self, name, priority): + """Change the codec priority. + + Keyword arguments: + name -- Codec name + priority -- Codec priority, which range is 0-255. + + """ + lck = self.auto_lock() + err = _pjsua.codec_set_priority(name, priority) + self._err_check("set_codec_priority()", self, err) + + def get_codec_parameter(self, name): + """Get codec parameter for the specified codec. + + Keyword arguments: + name -- codec name. + + """ + lck = self.auto_lock() + cp = _pjsua.codec_get_param(name) + if not cp: + self._err_check("get_codec_parameter()", self, -1, + "Invalid codec name") + return CodecParameter(cp) + + def set_codec_parameter(self, name, param): + """Modify codec parameter for the specified codec. + + Keyword arguments: + name -- codec name + param -- codec parameter. + + """ + lck = self.auto_lock() + err = _pjsua.codec_set_param(name, param._cvt_to_pjsua()) + self._err_check("set_codec_parameter()", self, err) + + # WAV playback and recording + + def create_player(self, filename, loop=False): + """Create WAV file player. + + Keyword arguments + filename -- WAV file name + loop -- boolean to specify whether playback should + automatically restart upon EOF + Return: + WAV player ID + + """ + lck = self.auto_lock() + opt = 0 + if not loop: + opt = opt + 1 + err, player_id = _pjsua.player_create(filename, opt) + self._err_check("create_player()", self, err) + return player_id + + def player_get_slot(self, player_id): + """Get the conference port ID for the specified player. + + Keyword arguments: + player_id -- the WAV player ID + + Return: + Conference slot number for the player + + """ + lck = self.auto_lock() + slot = _pjsua.player_get_conf_port(player_id) + if slot < 0: + self._err_check("player_get_slot()", self, -1, + "Invalid player id") + return slot + + def player_set_pos(self, player_id, pos): + """Set WAV playback position. + + Keyword arguments: + player_id -- WAV player ID + pos -- playback position, in samples + + """ + lck = self.auto_lock() + err = _pjsua.player_set_pos(player_id, pos) + self._err_check("player_set_pos()", self, err) + + def player_destroy(self, player_id): + """Destroy the WAV player. + + Keyword arguments: + player_id -- the WAV player ID. + + """ + lck = self.auto_lock() + err = _pjsua.player_destroy(player_id) + self._err_check("player_destroy()", self, err) + + def create_playlist(self, filelist, label="playlist", loop=True): + """Create WAV playlist. + + Keyword arguments: + filelist -- List of WAV file names. + label -- Optional name to be assigned to the playlist + object (useful for logging) + loop -- boolean to specify whether playback should + automatically restart upon EOF + + Return: + playlist_id + """ + lck = self.auto_lock() + opt = 0 + if not loop: + opt = opt + 1 + err, playlist_id = _pjsua.playlist_create(label, filelist, opt) + self._err_check("create_playlist()", self, err) + return playlist_id + + def playlist_get_slot(self, playlist_id): + """Get the conference port ID for the specified playlist. + + Keyword arguments: + playlist_id -- the WAV playlist ID + + Return: + Conference slot number for the playlist + + """ + lck = self.auto_lock() + slot = _pjsua.player_get_conf_port(playlist_id) + if slot < 0: + self._err_check("playlist_get_slot()", self, -1, + "Invalid playlist id") + return slot + + def playlist_destroy(self, playlist_id): + """Destroy the WAV playlist. + + Keyword arguments: + playlist_id -- the WAV playlist ID. + + """ + lck = self.auto_lock() + err = _pjsua.player_destroy(playlist_id) + self._err_check("playlist_destroy()", self, err) + + def create_recorder(self, filename): + """Create WAV file recorder. + + Keyword arguments + filename -- WAV file name + + Return: + WAV recorder ID + + """ + lck = self.auto_lock() + err, rec_id = _pjsua.recorder_create(filename, 0, None, -1, 0) + self._err_check("create_recorder()", self, err) + return rec_id + + def recorder_get_slot(self, rec_id): + """Get the conference port ID for the specified recorder. + + Keyword arguments: + rec_id -- the WAV recorder ID + + Return: + Conference slot number for the recorder + + """ + lck = self.auto_lock() + slot = _pjsua.recorder_get_conf_port(rec_id) + if slot < 1: + self._err_check("recorder_get_slot()", self, -1, + "Invalid recorder id") + return slot + + def recorder_destroy(self, rec_id): + """Destroy the WAV recorder. + + Keyword arguments: + rec_id -- the WAV recorder ID. + + """ + lck = self.auto_lock() + err = _pjsua.recorder_destroy(rec_id) + self._err_check("recorder_destroy()", self, err) + + + # Internal functions + + @staticmethod + def strerror(err): + return _pjsua.strerror(err) + + def _err_check(self, op_name, obj, err_code, err_msg=""): + if err_code != 0: + raise Error(op_name, obj, err_code, err_msg) + + @staticmethod + def _create_msg_data(hdr_list): + if not hdr_list: + return None + msg_data = _pjsua.Msg_Data() + msg_data.hdr_list = hdr_list + return msg_data + + def auto_lock(self): + return _LibMutex(self._lock) + + # Internal dictionary manipulation for calls, accounts, and buddies + + def _lookup_call(self, call_id): + return _pjsua.call_get_user_data(call_id) + + def _lookup_account(self, acc_id): + return _pjsua.acc_get_user_data(acc_id) + + def _lookup_buddy(self, buddy_id, uri=None): + if buddy_id != -1: + buddy = _pjsua.buddy_get_user_data(buddy_id) + elif uri: + buddy_id = _pjsua.buddy_find(uri) + if buddy_id != -1: + buddy = _pjsua.buddy_get_user_data(buddy_id) + else: + buddy = None + else: + buddy = None + + return buddy + + # Account allbacks + + def _cb_on_reg_state(self, acc_id): + acc = self._lookup_account(acc_id) + if acc: + acc._cb.on_reg_state() + + def _cb_on_incoming_subscribe(self, acc_id, buddy_id, from_uri, + contact_uri, pres_obj): + acc = self._lookup_account(acc_id) + if acc: + buddy = self._lookup_buddy(buddy_id) + return acc._cb.on_incoming_subscribe(buddy, from_uri, contact_uri, + pres_obj) + else: + return (404, None) + + def _cb_on_incoming_call(self, acc_id, call_id, rdata): + acc = self._lookup_account(acc_id) + if acc: + acc._cb.on_incoming_call( Call(self, call_id) ) + else: + _pjsua.call_hangup(call_id, 603, None, None) + + # Call callbacks + + def _cb_on_call_state(self, call_id): + call = self._lookup_call(call_id) + if call: + if call._id == -1: + call.attach_to_id(call_id) + done = (call.info().state == CallState.DISCONNECTED) + call._cb.on_state() + if done: + _pjsua.call_set_user_data(call_id, 0) + else: + pass + + def _cb_on_call_media_state(self, call_id): + call = self._lookup_call(call_id) + if call: + call._cb.on_media_state() + + def _cb_on_dtmf_digit(self, call_id, digits): + call = self._lookup_call(call_id) + if call: + call._cb.on_dtmf_digit(digits) + + def _cb_on_call_transfer_request(self, call_id, dst, code): + call = self._lookup_call(call_id) + if call: + return call._cb.on_transfer_request(dst, code) + else: + return 603 + + def _cb_on_call_transfer_status(self, call_id, code, text, final, cont): + call = self._lookup_call(call_id) + if call: + return call._cb.on_transfer_status(code, text, final, cont) + else: + return cont + + def _cb_on_call_replace_request(self, call_id, rdata, code, reason): + call = self._lookup_call(call_id) + if call: + return call._cb.on_replace_request(code, reason) + else: + return code, reason + + def _cb_on_call_replaced(self, old_call_id, new_call_id): + old_call = self._lookup_call(old_call_id) + new_call = self._lookup_call(new_call_id) + if old_call and new_call: + old_call._cb.on_replaced(new_call) + + def _cb_on_pager(self, call_id, from_uri, to_uri, contact, mime_type, + body, acc_id): + call = None + if call_id != -1: + call = self._lookup_call(call_id) + if call: + call._cb.on_pager(mime_type, body) + else: + acc = self._lookup_account(acc_id) + buddy = self._lookup_buddy(-1, from_uri) + if buddy: + buddy._cb.on_pager(mime_type, body) + else: + acc._cb.on_pager(from_uri, contact, mime_type, body) + + def _cb_on_pager_status(self, call_id, to_uri, body, user_data, + code, reason, acc_id): + call = None + if call_id != -1: + call = self._lookup_call(call_id) + if call: + call._cb.on_pager_status(body, user_data, code, reason) + else: + acc = self._lookup_account(acc_id) + buddy = self._lookup_buddy(-1, to_uri) + if buddy: + buddy._cb.on_pager_status(body, user_data, code, reason) + else: + acc._cb.on_pager_status(to_uri, body, user_data, code, reason) + + def _cb_on_typing(self, call_id, from_uri, to_uri, contact, is_typing, + acc_id): + call = None + if call_id != -1: + call = self._lookup_call(call_id) + if call: + call._cb.on_typing(is_typing) + else: + acc = self._lookup_account(acc_id) + buddy = self._lookup_buddy(-1, from_uri) + if buddy: + buddy._cb.on_typing(is_typing) + else: + acc._cb.on_typing(from_uri, contact, is_typing) + + def _cb_on_mwi_info(self, acc_id, body): + acc = self._lookup_account(acc_id) + if acc: + return acc._cb.on_mwi_info(body) + + def _cb_on_buddy_state(self, buddy_id): + buddy = self._lookup_buddy(buddy_id) + if buddy: + buddy._cb.on_state() + +# +# Internal +# + +def _cb_on_call_state(call_id, e): + _lib._cb_on_call_state(call_id) + +def _cb_on_incoming_call(acc_id, call_id, rdata): + _lib._cb_on_incoming_call(acc_id, call_id, rdata) + +def _cb_on_call_media_state(call_id): + _lib._cb_on_call_media_state(call_id) + +def _cb_on_dtmf_digit(call_id, digits): + _lib._cb_on_dtmf_digit(call_id, digits) + +def _cb_on_call_transfer_request(call_id, dst, code): + return _lib._cb_on_call_transfer_request(call_id, dst, code) + +def _cb_on_call_transfer_status(call_id, code, reason, final, cont): + return _lib._cb_on_call_transfer_status(call_id, code, reason, + final, cont) +def _cb_on_call_replace_request(call_id, rdata, code, reason): + return _lib._cb_on_call_replace_request(call_id, rdata, code, reason) + +def _cb_on_call_replaced(old_call_id, new_call_id): + _lib._cb_on_call_replaced(old_call_id, new_call_id) + +def _cb_on_reg_state(acc_id): + _lib._cb_on_reg_state(acc_id) + +def _cb_on_incoming_subscribe(acc_id, buddy_id, from_uri, contact_uri, pres): + return _lib._cb_on_incoming_subscribe(acc_id, buddy_id, from_uri, + contact_uri, pres) + +def _cb_on_buddy_state(buddy_id): + _lib._cb_on_buddy_state(buddy_id) + +def _cb_on_pager(call_id, from_uri, to, contact, mime_type, body, acc_id): + _lib._cb_on_pager(call_id, from_uri, to, contact, mime_type, body, acc_id) + +def _cb_on_pager_status(call_id, to, body, user_data, status, reason, acc_id): + _lib._cb_on_pager_status(call_id, to, body, user_data, + status, reason, acc_id) + +def _cb_on_typing(call_id, from_uri, to, contact, is_typing, acc_id): + _lib._cb_on_typing(call_id, from_uri, to, contact, is_typing, acc_id) + +def _cb_on_mwi_info(acc_id, body): + _lib._cb_on_mwi_info(acc_id, body) + +# Worker thread +def _worker_thread_main(arg): + global _lib + _Trace(('worker thread started..',)) + thread_desc = 0; + err = _pjsua.thread_register("python worker", thread_desc) + _lib._err_check("thread_register()", _lib, err) + while _lib and _lib._quit == 0: + _lib.handle_events(1) + time.sleep(0.050) + if _lib: + _lib._quit = 2 + _Trace(('worker thread exited..',)) + +def _Trace(args): + global enable_trace + if enable_trace: + print "** ", + for arg in args: + print arg, + print " **" + diff --git a/pjsip-apps/src/python/samples/call.py b/pjsip-apps/src/python/samples/call.py new file mode 100644 index 0000000..60f9065 --- /dev/null +++ b/pjsip-apps/src/python/samples/call.py @@ -0,0 +1,169 @@ +# $Id: call.py 2171 2008-07-24 09:01:33Z bennylp $ +# +# SIP call sample. +# +# Copyright (C) 2003-2008 Benny Prijono +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +import sys +import pjsua as pj + +LOG_LEVEL=3 +current_call = None + +# Logging callback +def log_cb(level, str, len): + print str, + + +# Callback to receive events from account +class MyAccountCallback(pj.AccountCallback): + + def __init__(self, account=None): + pj.AccountCallback.__init__(self, account) + + # Notification on incoming call + def on_incoming_call(self, call): + global current_call + if current_call: + call.answer(486, "Busy") + return + + print "Incoming call from ", call.info().remote_uri + print "Press 'a' to answer" + + current_call = call + + call_cb = MyCallCallback(current_call) + current_call.set_callback(call_cb) + + current_call.answer(180) + + +# Callback to receive events from Call +class MyCallCallback(pj.CallCallback): + + def __init__(self, call=None): + pj.CallCallback.__init__(self, call) + + # Notification when call state has changed + def on_state(self): + global current_call + print "Call with", self.call.info().remote_uri, + print "is", self.call.info().state_text, + print "last code =", self.call.info().last_code, + print "(" + self.call.info().last_reason + ")" + + if self.call.info().state == pj.CallState.DISCONNECTED: + current_call = None + print 'Current call is', current_call + + # Notification when call's media state has changed. + def on_media_state(self): + if self.call.info().media_state == pj.MediaState.ACTIVE: + # Connect the call to sound device + call_slot = self.call.info().conf_slot + pj.Lib.instance().conf_connect(call_slot, 0) + pj.Lib.instance().conf_connect(0, call_slot) + print "Media is now active" + else: + print "Media is inactive" + +# Function to make call +def make_call(uri): + try: + print "Making call to", uri + return acc.make_call(uri, cb=MyCallCallback()) + except pj.Error, e: + print "Exception: " + str(e) + return None + + +# Create library instance +lib = pj.Lib() + +try: + # Init library with default config and some customized + # logging config. + lib.init(log_cfg = pj.LogConfig(level=LOG_LEVEL, callback=log_cb)) + + # Create UDP transport which listens to any available port + transport = lib.create_transport(pj.TransportType.UDP, + pj.TransportConfig(0)) + print "\nListening on", transport.info().host, + print "port", transport.info().port, "\n" + + # Start the library + lib.start() + + # Create local account + acc = lib.create_account_for_transport(transport, cb=MyAccountCallback()) + + # If argument is specified then make call to the URI + if len(sys.argv) > 1: + lck = lib.auto_lock() + current_call = make_call(sys.argv[1]) + print 'Current call is', current_call + del lck + + my_sip_uri = "sip:" + transport.info().host + \ + ":" + str(transport.info().port) + + # Menu loop + while True: + print "My SIP URI is", my_sip_uri + print "Menu: m=make call, h=hangup call, a=answer call, q=quit" + + input = sys.stdin.readline().rstrip("\r\n") + if input == "m": + if current_call: + print "Already have another call" + continue + print "Enter destination URI to call: ", + input = sys.stdin.readline().rstrip("\r\n") + if input == "": + continue + lck = lib.auto_lock() + current_call = make_call(input) + del lck + + elif input == "h": + if not current_call: + print "There is no call" + continue + current_call.hangup() + + elif input == "a": + if not current_call: + print "There is no call" + continue + current_call.answer(200) + + elif input == "q": + break + + # Shutdown the library + transport = None + acc.delete() + acc = None + lib.destroy() + lib = None + +except pj.Error, e: + print "Exception: " + str(e) + lib.destroy() + lib = None + diff --git a/pjsip-apps/src/python/samples/presence.py b/pjsip-apps/src/python/samples/presence.py new file mode 100644 index 0000000..afeaeab --- /dev/null +++ b/pjsip-apps/src/python/samples/presence.py @@ -0,0 +1,175 @@ +# $Id: presence.py 2171 2008-07-24 09:01:33Z bennylp $ +# +# Presence and instant messaging +# +# Copyright (C) 2003-2008 Benny Prijono +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +import sys +import pjsua as pj + +LOG_LEVEL = 3 +pending_pres = None +pending_uri = None + +def log_cb(level, str, len): + print str, + +class MyAccountCallback(pj.AccountCallback): + def __init__(self, account=None): + pj.AccountCallback.__init__(self, account) + + def on_incoming_subscribe(self, buddy, from_uri, contact_uri, pres): + global pending_pres, pending_uri + # Allow buddy to subscribe to our presence + if buddy: + return (200, None) + print 'Incoming SUBSCRIBE request from', from_uri + print 'Press "A" to accept and add, "R" to reject the request' + pending_pres = pres + pending_uri = from_uri + return (202, None) + + +class MyBuddyCallback(pj.BuddyCallback): + def __init__(self, buddy=None): + pj.BuddyCallback.__init__(self, buddy) + + def on_state(self): + print "Buddy", self.buddy.info().uri, "is", + print self.buddy.info().online_text + + def on_pager(self, mime_type, body): + print "Instant message from", self.buddy.info().uri, + print "(", mime_type, "):" + print body + + def on_pager_status(self, body, im_id, code, reason): + if code >= 300: + print "Message delivery failed for message", + print body, "to", self.buddy.info().uri, ":", reason + + def on_typing(self, is_typing): + if is_typing: + print self.buddy.info().uri, "is typing" + else: + print self.buddy.info().uri, "stops typing" + + +lib = pj.Lib() + +try: + # Init library with default config and some customized + # logging config. + lib.init(log_cfg = pj.LogConfig(level=LOG_LEVEL, callback=log_cb)) + + # Create UDP transport which listens to any available port + transport = lib.create_transport(pj.TransportType.UDP, + pj.TransportConfig(0)) + print "\nListening on", transport.info().host, + print "port", transport.info().port, "\n" + + # Start the library + lib.start() + + # Create local account + acc = lib.create_account_for_transport(transport, cb=MyAccountCallback()) + acc.set_basic_status(True) + + my_sip_uri = "sip:" + transport.info().host + \ + ":" + str(transport.info().port) + + buddy = None + + # Menu loop + while True: + print "My SIP URI is", my_sip_uri + print "Menu: a=add buddy, d=delete buddy, t=toggle", \ + " online status, i=send IM, q=quit" + + input = sys.stdin.readline().rstrip("\r\n") + if input == "a": + # Add buddy + print "Enter buddy URI: ", + input = sys.stdin.readline().rstrip("\r\n") + if input == "": + continue + + buddy = acc.add_buddy(input, cb=MyBuddyCallback()) + buddy.subscribe() + + elif input == "t": + acc.set_basic_status(not acc.info().online_status) + + elif input == "i": + if not buddy: + print "Add buddy first" + continue + + buddy.send_typing_ind(True) + + print "Type the message: ", + input = sys.stdin.readline().rstrip("\r\n") + if input == "": + buddy.send_typing_ind(False) + continue + + buddy.send_pager(input) + + elif input == "d": + if buddy: + buddy.delete() + buddy = None + else: + print 'No buddy was added' + + elif input == "A": + if pending_pres: + acc.pres_notify(pending_pres, pj.SubscriptionState.ACTIVE) + buddy = acc.add_buddy(pending_uri, cb=MyBuddyCallback()) + buddy.subscribe() + pending_pres = None + pending_uri = None + else: + print "No pending request" + + elif input == "R": + if pending_pres: + acc.pres_notify(pending_pres, pj.SubscriptionState.TERMINATED, + "rejected") + pending_pres = None + pending_uri = None + else: + print "No pending request" + + elif input == "q": + break + + # Shutdown the library + acc.delete() + acc = None + if pending_pres: + acc.pres_notify(pending_pres, pj.SubscriptionState.TERMINATED, + "rejected") + transport = None + lib.destroy() + lib = None + +except pj.Error, e: + print "Exception: " + str(e) + lib.destroy() + lib = None + diff --git a/pjsip-apps/src/python/samples/registration.py b/pjsip-apps/src/python/samples/registration.py new file mode 100644 index 0000000..06670da --- /dev/null +++ b/pjsip-apps/src/python/samples/registration.py @@ -0,0 +1,70 @@ +# $Id: registration.py 2171 2008-07-24 09:01:33Z bennylp $ +# +# SIP account and registration sample. In this sample, the program +# will block to wait until registration is complete +# +# Copyright (C) 2003-2008 Benny Prijono +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +import sys +import pjsua as pj +import threading + + +def log_cb(level, str, len): + print str, + +class MyAccountCallback(pj.AccountCallback): + sem = None + + def __init__(self, account): + pj.AccountCallback.__init__(self, account) + + def wait(self): + self.sem = threading.Semaphore(0) + self.sem.acquire() + + def on_reg_state(self): + if self.sem: + if self.account.info().reg_status >= 200: + self.sem.release() + +lib = pj.Lib() + +try: + lib.init(log_cfg = pj.LogConfig(level=4, callback=log_cb)) + lib.create_transport(pj.TransportType.UDP, pj.TransportConfig(5080)) + lib.start() + + acc = lib.create_account(pj.AccountConfig("pjsip.org", "bennylp", "***")) + + acc_cb = MyAccountCallback(acc) + acc.set_callback(acc_cb) + acc_cb.wait() + + print "\n" + print "Registration complete, status=", acc.info().reg_status, \ + "(" + acc.info().reg_reason + ")" + print "\nPress ENTER to quit" + sys.stdin.readline() + + lib.destroy() + lib = None + +except pj.Error, e: + print "Exception: " + str(e) + lib.destroy() + diff --git a/pjsip-apps/src/python/samples/simplecall.py b/pjsip-apps/src/python/samples/simplecall.py new file mode 100644 index 0000000..40ac28f --- /dev/null +++ b/pjsip-apps/src/python/samples/simplecall.py @@ -0,0 +1,88 @@ +# $Id: simplecall.py 2171 2008-07-24 09:01:33Z bennylp $ +# +# SIP account and registration sample. In this sample, the program +# will block to wait until registration is complete +# +# Copyright (C) 2003-2008 Benny Prijono +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +import sys +import pjsua as pj + +# Logging callback +def log_cb(level, str, len): + print str, + +# Callback to receive events from Call +class MyCallCallback(pj.CallCallback): + def __init__(self, call=None): + pj.CallCallback.__init__(self, call) + + # Notification when call state has changed + def on_state(self): + print "Call is ", self.call.info().state_text, + print "last code =", self.call.info().last_code, + print "(" + self.call.info().last_reason + ")" + + # Notification when call's media state has changed. + def on_media_state(self): + global lib + if self.call.info().media_state == pj.MediaState.ACTIVE: + # Connect the call to sound device + call_slot = self.call.info().conf_slot + lib.conf_connect(call_slot, 0) + lib.conf_connect(0, call_slot) + print "Hello world, I can talk!" + + +# Check command line argument +if len(sys.argv) != 2: + print "Usage: simplecall.py " + sys.exit(1) + +try: + # Create library instance + lib = pj.Lib() + + # Init library with default config + lib.init(log_cfg = pj.LogConfig(level=3, callback=log_cb)) + + # Create UDP transport which listens to any available port + transport = lib.create_transport(pj.TransportType.UDP) + + # Start the library + lib.start() + + # Create local/user-less account + acc = lib.create_account_for_transport(transport) + + # Make call + call = acc.make_call(sys.argv[1], MyCallCallback()) + + # Wait for ENTER before quitting + print "Press to quit" + input = sys.stdin.readline().rstrip("\r\n") + + # We're done, shutdown the library + lib.destroy() + lib = None + +except pj.Error, e: + print "Exception: " + str(e) + lib.destroy() + lib = None + sys.exit(1) + diff --git a/pjsip-apps/src/python/setup-vc.py b/pjsip-apps/src/python/setup-vc.py new file mode 100644 index 0000000..cab1498 --- /dev/null +++ b/pjsip-apps/src/python/setup-vc.py @@ -0,0 +1,80 @@ +# $Id: setup-vc.py 4122 2012-05-14 11:04:46Z bennylp $ +# +# pjsua Setup script for Visual Studio +# +# Copyright (C) 2003-2008 Benny Prijono +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +from distutils.core import setup, Extension +import os +import sys + +# Find version +pj_version="" +pj_version_major="" +pj_version_minor="" +pj_version_rev="" +pj_version_suffix="" +f = open('../../../version.mak', 'r') +for line in f: + if line.find("export PJ_VERSION_MAJOR") != -1: + tokens=line.split("=") + if len(tokens)>1: + pj_version_major= tokens[1].strip() + elif line.find("export PJ_VERSION_MINOR") != -1: + tokens=line.split("=") + if len(tokens)>1: + pj_version_minor= line.split("=")[1].strip() + elif line.find("export PJ_VERSION_REV") != -1: + tokens=line.split("=") + if len(tokens)>1: + pj_version_rev= line.split("=")[1].strip() + elif line.find("export PJ_VERSION_SUFFIX") != -1: + tokens=line.split("=") + if len(tokens)>1: + pj_version_suffix= line.split("=")[1].strip() + +f.close() +if not pj_version_major: + print 'Unable to get PJ_VERSION_MAJOR' + sys.exit(1) + +pj_version = pj_version_major + "." + pj_version_minor +if pj_version_rev: + pj_version += "." + pj_version_rev +if pj_version_suffix: + pj_version += "-" + pj_version_suffix + +#print 'PJ_VERSION = "'+ pj_version + '"' + + +# Check that extension has been built +if not os.access('../../lib/_pjsua.pyd', os.R_OK): + print 'Error: file "../../lib/_pjsua.pyd" does not exist!' + print '' + print 'Please build the extension with Visual Studio first' + print 'For more info, see http://trac.pjsip.org/repos/wiki/Python_SIP_Tutorial' + sys.exit(1) + +setup(name="pjsua", + version=pj_version, + description='SIP User Agent Library based on PJSIP', + url='http://trac.pjsip.org/repos/wiki/Python_SIP_Tutorial', + data_files=[('lib/site-packages', ['../../lib/_pjsua.pyd'])], + py_modules=["pjsua"] + ) + + diff --git a/pjsip-apps/src/python/setup.py b/pjsip-apps/src/python/setup.py new file mode 100644 index 0000000..ae22491 --- /dev/null +++ b/pjsip-apps/src/python/setup.py @@ -0,0 +1,113 @@ +# $Id: setup.py 4122 2012-05-14 11:04:46Z bennylp $ +# +# pjsua Setup script. +# +# Copyright (C) 2003-2008 Benny Prijono +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +from distutils.core import setup, Extension +import os +import sys +import platform + +# find pjsip version +pj_version="" +pj_version_major="" +pj_version_minor="" +pj_version_rev="" +pj_version_suffix="" +f = open('../../../version.mak', 'r') +for line in f: + if line.find("export PJ_VERSION_MAJOR") != -1: + tokens=line.split("=") + if len(tokens)>1: + pj_version_major= tokens[1].strip() + elif line.find("export PJ_VERSION_MINOR") != -1: + tokens=line.split("=") + if len(tokens)>1: + pj_version_minor= line.split("=")[1].strip() + elif line.find("export PJ_VERSION_REV") != -1: + tokens=line.split("=") + if len(tokens)>1: + pj_version_rev= line.split("=")[1].strip() + elif line.find("export PJ_VERSION_SUFFIX") != -1: + tokens=line.split("=") + if len(tokens)>1: + pj_version_suffix= line.split("=")[1].strip() + +f.close() +if not pj_version_major: + print 'Unable to get PJ_VERSION_MAJOR' + sys.exit(1) + +pj_version = pj_version_major + "." + pj_version_minor +if pj_version_rev: + pj_version += "." + pj_version_rev +if pj_version_suffix: + pj_version += "-" + pj_version_suffix + +#print 'PJ_VERSION = "'+ pj_version + '"' + + +# Fill in pj_inc_dirs +pj_inc_dirs = [] +f = os.popen("make -f helper.mak inc_dir") +for line in f: + pj_inc_dirs.append(line.rstrip("\r\n")) +f.close() + +# Fill in pj_lib_dirs +pj_lib_dirs = [] +f = os.popen("make -f helper.mak lib_dir") +for line in f: + pj_lib_dirs.append(line.rstrip("\r\n")) +f.close() + +# Fill in pj_libs +pj_libs = [] +f = os.popen("make -f helper.mak libs") +for line in f: + pj_libs.append(line.rstrip("\r\n")) +f.close() + +# Mac OS X depedencies +if platform.system() == 'Darwin': + extra_link_args = ["-framework", "CoreFoundation", + "-framework", "AudioToolbox"] + # OS X Lion support + if platform.mac_ver()[0].startswith("10.7"): + extra_link_args += ["-framework", "AudioUnit"] +else: + extra_link_args = [] + + +setup(name="pjsua", + version=pj_version, + description='SIP User Agent Library based on PJSIP', + url='http://trac.pjsip.org/repos/wiki/Python_SIP_Tutorial', + ext_modules = [Extension("_pjsua", + ["_pjsua.c"], + define_macros=[('PJ_AUTOCONF', '1'),], + include_dirs=pj_inc_dirs, + library_dirs=pj_lib_dirs, + libraries=pj_libs, + extra_link_args=extra_link_args + ) + ], + py_modules=["pjsua"] + ) + + -- cgit v1.2.3