summaryrefslogtreecommitdiff
path: root/pjsip-apps/src/python
diff options
context:
space:
mode:
authorDavid M. Lee <dlee@digium.com>2013-01-07 14:24:28 -0600
committerDavid M. Lee <dlee@digium.com>2013-01-07 14:24:28 -0600
commitf3ab456a17af1c89a6e3be4d20c5944853df1cb0 (patch)
treed00e1a332cd038a6d906a1ea0ac91e1a4458e617 /pjsip-apps/src/python
Import pjproject-2.0.1
Diffstat (limited to 'pjsip-apps/src/python')
-rw-r--r--pjsip-apps/src/python/Makefile12
-rw-r--r--pjsip-apps/src/python/_pjsua.c4605
-rw-r--r--pjsip-apps/src/python/_pjsua.def2
-rw-r--r--pjsip-apps/src/python/_pjsua.h3508
-rw-r--r--pjsip-apps/src/python/helper.mak17
-rw-r--r--pjsip-apps/src/python/pjsua.py2862
-rw-r--r--pjsip-apps/src/python/samples/call.py169
-rw-r--r--pjsip-apps/src/python/samples/presence.py175
-rw-r--r--pjsip-apps/src/python/samples/registration.py70
-rw-r--r--pjsip-apps/src/python/samples/simplecall.py88
-rw-r--r--pjsip-apps/src/python/setup-vc.py80
-rw-r--r--pjsip-apps/src/python/setup.py113
12 files changed, 11701 insertions, 0 deletions
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 <benny@prijono.org>
+ *
+ * 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<PyList_Size(py_hdr_list); ++i) {
+ pj_str_t hname, hvalue;
+ pjsip_generic_string_hdr * new_hdr;
+ PyObject * tuple = PyList_GetItem(py_hdr_list, i);
+
+ if (PyTuple_Check(tuple)) {
+ if (PyTuple_Size(tuple) >= 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; count<PyList_Size(pFileList) &&
+ count<PJ_ARRAY_SIZE(files); ++count)
+ {
+ files[count] = PyString_ToPJ(PyList_GetItem(pFileList, count));
+ }
+
+ status = pjsua_playlist_create(files, count, &label, options, &id);
+
+ return Py_BuildValue("ii", status, id);
+}
+
+/*
+ * py_pjsua_player_get_conf_port
+ */
+static PyObject *py_pjsua_player_get_conf_port(PyObject *pSelf,
+ PyObject *pArgs)
+{
+
+ int id, port_id;
+
+ PJ_UNUSED_ARG(pSelf);
+
+ if (!PyArg_ParseTuple(pArgs, "i", &id)) {
+ return NULL;
+ }
+
+ port_id = pjsua_player_get_conf_port(id);
+
+ return Py_BuildValue("i", port_id);
+}
+
+/*
+ * py_pjsua_player_set_pos
+ */
+static PyObject *py_pjsua_player_set_pos(PyObject *pSelf, PyObject *pArgs)
+{
+ int id;
+ int samples;
+ int status;
+
+ PJ_UNUSED_ARG(pSelf);
+
+ if (!PyArg_ParseTuple(pArgs, "ii", &id, &samples)) {
+ return NULL;
+ }
+
+ if (samples < 0)
+ samples = 0;
+
+ status = pjsua_player_set_pos(id, samples);
+
+ return Py_BuildValue("i", status);
+}
+
+/*
+ * py_pjsua_player_destroy
+ */
+static PyObject *py_pjsua_player_destroy(PyObject *pSelf, PyObject *pArgs)
+{
+ int id;
+ int status;
+
+ PJ_UNUSED_ARG(pSelf);
+
+ if (!PyArg_ParseTuple(pArgs, "i", &id)) {
+ return NULL;
+ }
+
+ status = pjsua_player_destroy(id);
+
+ return Py_BuildValue("i", status);
+}
+
+/*
+ * py_pjsua_recorder_create
+ */
+static PyObject *py_pjsua_recorder_create(PyObject *pSelf, PyObject *pArgs)
+{
+ int id, options;
+ int max_size;
+ PyObject *pFilename, *pEncParam;
+ pj_str_t filename;
+ int enc_type;
+
+ int status;
+
+ PJ_UNUSED_ARG(pSelf);
+
+ if (!PyArg_ParseTuple(pArgs, "OiOii", &pFilename, &enc_type, &pEncParam,
+ &max_size, &options))
+ {
+ return NULL;
+ }
+
+ filename = PyString_ToPJ(pFilename);
+
+ status = pjsua_recorder_create(&filename, enc_type, NULL, max_size,
+ options, &id);
+
+ return Py_BuildValue("ii", status, id);
+}
+
+/*
+ * py_pjsua_recorder_get_conf_port
+ */
+static PyObject *py_pjsua_recorder_get_conf_port(PyObject *pSelf,
+ PyObject *pArgs)
+{
+
+ int id, port_id;
+
+ PJ_UNUSED_ARG(pSelf);
+
+ if (!PyArg_ParseTuple(pArgs, "i", &id)) {
+ return NULL;
+ }
+
+ port_id = pjsua_recorder_get_conf_port(id);
+
+ return Py_BuildValue("i", port_id);
+}
+
+/*
+ * py_pjsua_recorder_destroy
+ */
+static PyObject *py_pjsua_recorder_destroy(PyObject *pSelf, PyObject *pArgs)
+{
+ int id;
+ int status;
+
+ PJ_UNUSED_ARG(pSelf);
+
+ if (!PyArg_ParseTuple(pArgs, "i", &id)) {
+ return NULL;
+ }
+
+ status = pjsua_recorder_destroy(id);
+
+ return Py_BuildValue("i", status);
+}
+
+/*
+ * py_pjsua_enum_snd_devs
+ */
+static PyObject *py_pjsua_enum_snd_devs(PyObject *pSelf, PyObject *pArgs)
+{
+ pj_status_t status;
+ PyObject *ret;
+ pjmedia_snd_dev_info info[SND_DEV_NUM];
+ unsigned c, i;
+
+ PJ_UNUSED_ARG(pSelf);
+ PJ_UNUSED_ARG(pArgs);
+
+ c = PJ_ARRAY_SIZE(info);
+ status = pjsua_enum_snd_devs(info, &c);
+ if (status != PJ_SUCCESS)
+ c = 0;
+
+ ret = PyList_New(c);
+ for (i = 0; i < c; i++) {
+ PyObj_pjmedia_snd_dev_info * obj;
+
+ obj = (PyObj_pjmedia_snd_dev_info *)
+ pjmedia_snd_dev_info_new(&PyTyp_pjmedia_snd_dev_info,
+ NULL, NULL);
+ obj->default_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, &param);
+ 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, &param);
+
+ } 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 <benny@prijono.org>
+ *
+ * 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 <Python.h>
+#include <structmember.h>
+#include <pjsua-lib/pjsua.h>
+
+
+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; i<cfg->nameserver_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; i<cfg->proxy_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; i<cfg->cred_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 <benny@prijono.org>
+#
+# 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 + "<sip:" + userpart + domain + ">"
+ 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 <benny@prijono.org>
+#
+# 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 <benny@prijono.org>
+#
+# 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 <benny@prijono.org>
+#
+# 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 <benny@prijono.org>
+#
+# 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 <dst-URI>"
+ 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 <ENTER> 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 <benny@prijono.org>
+#
+# 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 <benny@prijono.org>
+#
+# 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"]
+ )
+
+