From 9c4611432c62d297604ed0c467aad050c4d043c6 Mon Sep 17 00:00:00 2001 From: Benny Prijono Date: Thu, 10 Jul 2008 22:41:20 +0000 Subject: [PATCH] Ticket #556: New object oriented Python abstraction for pjsua API git-svn-id: https://svn.pjsip.org/repos/pjproject/trunk@2119 74dad513-b988-da41-8d7b-12977e46ad98 --- pjsip-apps/build/pjsip_apps.dsw | 56 +- pjsip-apps/build/python_pjsua.dsp | 116 + pjsip-apps/src/py_pjsua/DEPRECATED.txt | 2 + pjsip-apps/src/python/Makefile | 6 + pjsip-apps/src/python/_pjsua.c | 6104 +++++++++++++++++ pjsip-apps/src/python/_pjsua.def | 2 + pjsip-apps/src/python/_pjsua.h | 2945 ++++++++ pjsip-apps/src/python/helper.mak | 17 + pjsip-apps/src/python/pjsua.py | 2099 ++++++ pjsip-apps/src/python/samples/call.py | 153 + pjsip-apps/src/python/samples/presence.py | 113 + pjsip-apps/src/python/samples/registration.py | 56 + pjsip-apps/src/python/setup.py | 44 + 13 files changed, 11685 insertions(+), 28 deletions(-) create mode 100644 pjsip-apps/build/python_pjsua.dsp create mode 100644 pjsip-apps/src/py_pjsua/DEPRECATED.txt create mode 100644 pjsip-apps/src/python/Makefile create mode 100644 pjsip-apps/src/python/_pjsua.c create mode 100644 pjsip-apps/src/python/_pjsua.def create mode 100644 pjsip-apps/src/python/_pjsua.h create mode 100644 pjsip-apps/src/python/helper.mak create mode 100644 pjsip-apps/src/python/pjsua.py create mode 100644 pjsip-apps/src/python/samples/call.py create mode 100644 pjsip-apps/src/python/samples/presence.py create mode 100644 pjsip-apps/src/python/samples/registration.py create mode 100644 pjsip-apps/src/python/setup.py diff --git a/pjsip-apps/build/pjsip_apps.dsw b/pjsip-apps/build/pjsip_apps.dsw index 021ce1494..09c8a5767 100644 --- a/pjsip-apps/build/pjsip_apps.dsw +++ b/pjsip-apps/build/pjsip_apps.dsw @@ -237,7 +237,7 @@ Package=<4> ############################################################################### -Project: "py_pjsua"=".\py_pjsua.dsp" - Package Owner=<4> +Project: "python_pjsua"=".\python_pjsua.dsp" - Package Owner=<4> Package=<5> {{{ @@ -245,33 +245,6 @@ Package=<5> Package=<4> {{{ - Begin Project Dependency - Project_Dep_Name pjlib - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjlib_util - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjmedia - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjmedia_codec - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjsip_core - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjsip_simple - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjsip_ua - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjsua_lib - End Project Dependency - Begin Project Dependency - Project_Dep_Name pjnath - End Project Dependency Begin Project Dependency Project_Dep_Name libgsmcodec End Project Dependency @@ -290,6 +263,33 @@ Package=<4> Begin Project Dependency Project_Dep_Name libsrtp End Project Dependency + Begin Project Dependency + Project_Dep_Name pjlib + End Project Dependency + Begin Project Dependency + Project_Dep_Name pjlib_util + End Project Dependency + Begin Project Dependency + Project_Dep_Name pjmedia + End Project Dependency + Begin Project Dependency + Project_Dep_Name pjmedia_codec + End Project Dependency + Begin Project Dependency + Project_Dep_Name pjnath + End Project Dependency + Begin Project Dependency + Project_Dep_Name pjsip_core + End Project Dependency + Begin Project Dependency + Project_Dep_Name pjsip_simple + End Project Dependency + Begin Project Dependency + Project_Dep_Name pjsip_ua + End Project Dependency + Begin Project Dependency + Project_Dep_Name pjsua_lib + End Project Dependency }}} ############################################################################### diff --git a/pjsip-apps/build/python_pjsua.dsp b/pjsip-apps/build/python_pjsua.dsp new file mode 100644 index 000000000..6b495e1cd --- /dev/null +++ b/pjsip-apps/build/python_pjsua.dsp @@ -0,0 +1,116 @@ +# Microsoft Developer Studio Project File - Name="python_pjsua" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=python_pjsua - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "python_pjsua.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "python_pjsua.mak" CFG="python_pjsua - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "python_pjsua - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "python_pjsua - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "python_pjsua - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir ".\output\python_pjsua-i386-win32-vc6-release" +# PROP Intermediate_Dir ".\output\python_pjsua-i386-win32-vc6-release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PY_PJSUA_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\pjlib\include" /I "..\..\pjlib-util\include" /I "..\..\pjmedia\include" /I "..\..\pjsip\include" /I "../../pjnath/include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PY_PJSUA_EXPORTS" /FR /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x421 /d "NDEBUG" +# ADD RSC /l 0x421 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 python24.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib iphlpapi.lib /nologo /dll /map /machine:I386 /nodefaultlib:"libcmt.lib" /out:"..\lib\_pjsua.pyd" /libpath:"../../pjlib/lib" /libpath:"../../pjlib-util/lib" /libpath:"../../pjmedia/lib" /libpath:"../../pjsip/lib" +# SUBTRACT LINK32 /nodefaultlib + +!ELSEIF "$(CFG)" == "python_pjsua - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir ".\output\python_pjsua-i386-win32-vc6-debug" +# PROP Intermediate_Dir ".\output\python_pjsua-i386-win32-vc6-debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PY_PJSUA_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "..\..\pjlib\include" /I "..\..\pjlib-util\include" /I "..\..\pjmedia\include" /I "..\..\pjsip\include" /I "../../pjnath/include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PY_PJSUA_EXPORTS" /FR /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x421 /d "_DEBUG" +# ADD RSC /l 0x421 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 python24_d.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib iphlpapi.lib /nologo /dll /debug /machine:I386 /out:"..\lib\_pjsua_d.pyd" /pdbtype:sept /libpath:"../../pjlib/lib" /libpath:"../../pjlib-util/lib" /libpath:"../../pjmedia/lib" /libpath:"../../pjsip/lib" /libpath:"F:\incoming\projects\divusi\Python-2.4\Python-2.4\PCbuild" /libpath:"F:\incoming\projects\divusi\Python-2.4\Python-2.4\PC\VC6" + +!ENDIF + +# Begin Target + +# Name "python_pjsua - Win32 Release" +# Name "python_pjsua - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\src\python\_pjsua.c +# End Source File +# Begin Source File + +SOURCE=..\src\python\_pjsua.def +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\src\python\_pjsua.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/pjsip-apps/src/py_pjsua/DEPRECATED.txt b/pjsip-apps/src/py_pjsua/DEPRECATED.txt new file mode 100644 index 000000000..c5e85876a --- /dev/null +++ b/pjsip-apps/src/py_pjsua/DEPRECATED.txt @@ -0,0 +1,2 @@ +This Python module is now deprecated. Please use the new implementation under +pjsip-apps/python directory. diff --git a/pjsip-apps/src/python/Makefile b/pjsip-apps/src/python/Makefile new file mode 100644 index 000000000..10ed2fb30 --- /dev/null +++ b/pjsip-apps/src/python/Makefile @@ -0,0 +1,6 @@ +all: + python setup.py install + +clean: + python setup.py clean + rm -rf ./build diff --git a/pjsip-apps/src/python/_pjsua.c b/pjsip-apps/src/python/_pjsua.c new file mode 100644 index 000000000..ab55077e4 --- /dev/null +++ b/pjsip-apps/src/python/_pjsua.c @@ -0,0 +1,6104 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2008 Benny Prijono + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "_pjsua.h" + +#define THIS_FILE "main.c" +#define POOL_SIZE 4000 +#define SND_DEV_NUM 64 +#define SND_NAME_LEN 64 + +/* LIB BASE */ + +static PyObject* obj_log_cb; +static long thread_id; + +#define ENTER_PYTHON() PyGILState_STATE state = PyGILState_Ensure() +#define LEAVE_PYTHON() PyGILState_Release(state) + +/* + * 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(thread_id) == 0) + return; + + if (PyCallable_Check(obj_log_cb)) + { + ENTER_PYTHON(); + + PyObject_CallFunctionObjArgs( + obj_log_cb, Py_BuildValue("i",level), + PyString_FromString(data), Py_BuildValue("i",len), NULL + ); + + LEAVE_PYTHON(); + } +} + + + +/* + * The global callback object. + */ +static PyObj_pjsua_callback * g_obj_callback; + + +/* + * 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) +{ + if (PyCallable_Check(g_obj_callback->on_call_state)) + { + PyObj_pjsip_event * obj; + + ENTER_PYTHON(); + + obj = (PyObj_pjsip_event *)PyType_GenericNew(&PyTyp_pjsip_event, + NULL, NULL); + + obj->event = e; + + PyObject_CallFunctionObjArgs( + g_obj_callback->on_call_state, + Py_BuildValue("i",call_id), + obj, + NULL + ); + + 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) +{ + if (PyCallable_Check(g_obj_callback->on_incoming_call)) + { + PyObj_pjsip_rx_data * obj; + + ENTER_PYTHON(); + + obj = (PyObj_pjsip_rx_data *)PyType_GenericNew(&PyTyp_pjsip_rx_data, + NULL, NULL); + obj->rdata = rdata; + + PyObject_CallFunctionObjArgs( + g_obj_callback->on_incoming_call, + Py_BuildValue("i",acc_id), + Py_BuildValue("i",call_id), + obj, + NULL + ); + + 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]; + + ENTER_PYTHON(); + + pj_ansi_snprintf(digit_str, sizeof(digit_str), "%c", digit); + + PyObject_CallFunctionObjArgs( + g_obj_callback->on_dtmf_digit, + Py_BuildValue("i",call_id), + PyString_FromString(digit_str), + NULL + ); + + LEAVE_PYTHON(); + } +} + + +/* + * 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; + int cd; + + ENTER_PYTHON(); + + ret = PyObject_CallFunctionObjArgs( + g_obj_callback->on_call_transfer_request, + Py_BuildValue("i",call_id), + PyString_FromStringAndSize(dst->ptr, dst->slen), + Py_BuildValue("i",*code), + NULL + ); + if (ret != NULL) { + if (ret != Py_None) { + if (PyArg_Parse(ret,"i",&cd)) { + *code = cd; + } + } + } + + 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; + int cnt; + + ENTER_PYTHON(); + + ret = PyObject_CallFunctionObjArgs( + g_obj_callback->on_call_transfer_status, + Py_BuildValue("i",call_id), + Py_BuildValue("i",status_code), + PyString_FromStringAndSize(status_text->ptr, status_text->slen), + Py_BuildValue("i",final), + Py_BuildValue("i",*p_cont), + NULL + ); + if (ret != NULL) { + if (ret != Py_None) { + if (PyArg_Parse(ret,"i",&cnt)) { + *p_cont = cnt; + } + } + } + + 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) +{ + if (PyCallable_Check(g_obj_callback->on_call_replace_request)) + { + PyObject * ret; + PyObject * txt; + int cd; + PyObj_pjsip_rx_data * obj; + + ENTER_PYTHON(); + + obj = (PyObj_pjsip_rx_data *)PyType_GenericNew(&PyTyp_pjsip_rx_data, + NULL, NULL); + obj->rdata = rdata; + + ret = PyObject_CallFunctionObjArgs( + g_obj_callback->on_call_replace_request, + Py_BuildValue("i",call_id), + obj, + Py_BuildValue("i",*st_code), + PyString_FromStringAndSize(st_text->ptr, st_text->slen), + NULL + ); + if (ret != NULL) { + if (ret != Py_None) { + if (PyArg_ParseTuple(ret,"iO",&cd, &txt)) { + *st_code = cd; + st_text->ptr = PyString_AsString(txt); + st_text->slen = strlen(PyString_AsString(txt)); + } + } + } + + 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_CallFunctionObjArgs( + g_obj_callback->on_call_replaced, + Py_BuildValue("i",old_call_id), + Py_BuildValue("i",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_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) +{ + if (PyCallable_Check(g_obj_callback->on_pager)) + { + ENTER_PYTHON(); + + PyObject_CallFunctionObjArgs( + g_obj_callback->on_pager, + Py_BuildValue("i",call_id), + PyString_FromStringAndSize(from->ptr, from->slen), + PyString_FromStringAndSize(to->ptr, to->slen), + PyString_FromStringAndSize(contact->ptr, contact->slen), + PyString_FromStringAndSize(mime_type->ptr, mime_type->slen), + PyString_FromStringAndSize(body->ptr, body->slen), + Py_BuildValue("i",acc_id), + NULL + ); + + 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 * obj_user_data; + + ENTER_PYTHON(); + + PJ_UNUSED_ARG(tdata); + PJ_UNUSED_ARG(rdata); + + obj_user_data = Py_BuildValue("i", user_data); + + PyObject_CallFunctionObjArgs( + g_obj_callback->on_pager_status, + Py_BuildValue("i",call_id), + PyString_FromStringAndSize(to->ptr, to->slen), + PyString_FromStringAndSize(body->ptr, body->slen), + obj_user_data, + Py_BuildValue("i",status), + PyString_FromStringAndSize(reason->ptr,reason->slen), + Py_BuildValue("i",acc_id), + NULL + ); + + 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)) + { + ENTER_PYTHON(); + + PJ_UNUSED_ARG(rdata); + + PyObject_CallFunctionObjArgs( + g_obj_callback->on_typing,Py_BuildValue("i",call_id), + PyString_FromStringAndSize(from->ptr, from->slen), + PyString_FromStringAndSize(to->ptr, to->slen), + PyString_FromStringAndSize(contact->ptr, contact->slen), + Py_BuildValue("i",is_typing), + Py_BuildValue("i",acc_id), + NULL + ); + + 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)) + { + hname.ptr = PyString_AsString(PyTuple_GetItem(tuple,0)); + hname.slen = strlen(PyString_AsString + (PyTuple_GetItem(tuple,0))); + hvalue.ptr = PyString_AsString(PyTuple_GetItem(tuple,1)); + hvalue.slen = strlen(PyString_AsString + (PyTuple_GetItem(tuple,1))); + } 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); + } + } +} + +/* + * translate_hdr_rev + * internal function + * translate from pjsip_generic_string_hdr to hdr_list + */ + +void translate_hdr_rev(pjsip_generic_string_hdr *hdr, PyObject *py_hdr_list) +{ + int i; + int len; + pjsip_generic_string_hdr * p_hdr; + + len = pj_list_size(hdr); + + if (len > 0) + { + p_hdr = hdr; + Py_XDECREF(py_hdr_list); + py_hdr_list = PyList_New(len); + + for (i = 0; i < len && p_hdr != NULL; i++) + { + PyObject * tuple; + PyObject * str; + + tuple = PyTuple_New(2); + + str = PyString_FromStringAndSize(p_hdr->name.ptr, p_hdr->name.slen); + PyTuple_SetItem(tuple, 0, str); + str = PyString_FromStringAndSize + (hdr->hvalue.ptr, p_hdr->hvalue.slen); + PyTuple_SetItem(tuple, 1, str); + PyList_SetItem(py_hdr_list, i, tuple); + p_hdr = p_hdr->next; + } + } + + +} + +/* + * py_pjsua_thread_register + * !added @ 061206 + */ +static PyObject *py_pjsua_thread_register(PyObject *pSelf, PyObject *pArgs) +{ + + pj_status_t status; + const char *name; + PyObject *py_desc; + pj_thread_t *thread; + void *thread_desc; +#if 0 + int size; + int i; + int *td; +#endif + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "sO", &name, &py_desc)) + { + return NULL; + } +#if 0 + size = PyList_Size(py_desc); + td = (int *)malloc(size * sizeof(int)); + for (i = 0; i < size; i++) + { + if (!PyArg_Parse(PyList_GetItem(py_desc,i),"i", td[i])) + { + return NULL; + } + } + thread_desc = td; +#else + thread_desc = malloc(sizeof(pj_thread_desc)); +#endif + status = pj_thread_register(name, thread_desc, &thread); + + if (status == PJ_SUCCESS) + status = pj_thread_local_set(thread_id, (void*)1); + return Py_BuildValue("i",status); +} + +/* + * py_pjsua_logging_config_default + * !modified @ 051206 + */ +static PyObject *py_pjsua_logging_config_default(PyObject *pSelf, + PyObject *pArgs) +{ + PyObj_pjsua_logging_config *obj; + pjsua_logging_config cfg; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "")) + { + return NULL; + } + + 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 + * !modified @ 051206 + */ +static PyObject *py_pjsua_config_default(PyObject *pSelf, PyObject *pArgs) +{ + PyObj_pjsua_config *obj; + pjsua_config cfg; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "")) + { + return NULL; + } + 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 + * !modified @ 051206 + */ +static PyObject * py_pjsua_media_config_default(PyObject *pSelf, + PyObject *pArgs) +{ + PyObj_pjsua_media_config *obj; + pjsua_media_config cfg; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "")) + { + return NULL; + } + 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 + * !modified @ 051206 + */ +static PyObject *py_pjsua_msg_data_init(PyObject *pSelf, PyObject *pArgs) +{ + PyObj_pjsua_msg_data *obj; + pjsua_msg_data msg; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "")) + { + return NULL; + } + pjsua_msg_data_init(&msg); + obj = (PyObj_pjsua_msg_data *)PyObj_pjsua_msg_data_new(&PyTyp_pjsua_msg_data, NULL, NULL); + Py_XDECREF(obj->content_type); + obj->content_type = PyString_FromStringAndSize( + msg.content_type.ptr, msg.content_type.slen + ); + Py_XDECREF(obj->msg_body); + obj->msg_body = PyString_FromStringAndSize( + msg.msg_body.ptr, msg.msg_body.slen + ); + + translate_hdr_rev((pjsip_generic_string_hdr *)&msg.hdr_list,obj->hdr_list); + + return (PyObject *)obj; +} + + +/* + * py_pjsua_reconfigure_logging + */ +static PyObject *py_pjsua_reconfigure_logging(PyObject *pSelf, PyObject *pArgs) +{ + PyObject * logObj; + PyObj_pjsua_logging_config *log; + pjsua_logging_config cfg; + pj_status_t status; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "O", &logObj)) + { + return NULL; + } + if (logObj != Py_None) + { + 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.ptr = PyString_AsString(log->log_filename); + cfg.log_filename.slen = strlen(cfg.log_filename.ptr); + Py_XDECREF(obj_log_cb); + obj_log_cb = log->cb; + Py_INCREF(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_pool_create + */ +static PyObject *py_pjsua_pool_create(PyObject *pSelf, PyObject *pArgs) +{ + pj_size_t init_size; + pj_size_t increment; + const char * name; + pj_pool_t *p; + PyObj_pj_pool *pool; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "sII", &name, &init_size, &increment)) + { + return NULL; + } + + p = pjsua_pool_create(name, init_size, increment); + pool = (PyObj_pj_pool *)PyType_GenericNew(&PyTyp_pj_pool_t, NULL, NULL); + pool->pool = p; + return (PyObject *)pool; + +} + + +/* + * py_pjsua_get_pjsip_endpt + */ +static PyObject *py_pjsua_get_pjsip_endpt(PyObject *pSelf, PyObject *pArgs) +{ + PyObj_pjsip_endpoint *endpt; + pjsip_endpoint *e; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "")) + { + return NULL; + } + e = pjsua_get_pjsip_endpt(); + endpt = (PyObj_pjsip_endpoint *)PyType_GenericNew( + &PyTyp_pjsip_endpoint, NULL, NULL + ); + endpt->endpt = e; + return (PyObject *)endpt; +} + + +/* + * py_pjsua_get_pjmedia_endpt + */ +static PyObject *py_pjsua_get_pjmedia_endpt(PyObject *pSelf, PyObject *pArgs) +{ + PyObj_pjmedia_endpt *endpt; + pjmedia_endpt *e; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "")) + { + return NULL; + } + e = pjsua_get_pjmedia_endpt(); + endpt = (PyObj_pjmedia_endpt *)PyType_GenericNew( + &PyTyp_pjmedia_endpt, NULL, NULL + ); + endpt->endpt = e; + return (PyObject *)endpt; +} + + +/* + * py_pjsua_get_pool_factory + */ +static PyObject *py_pjsua_get_pool_factory(PyObject *pSelf, PyObject *pArgs) +{ + PyObj_pj_pool_factory *pool; + pj_pool_factory *p; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "")) + { + return NULL; + } + p = pjsua_get_pool_factory(); + pool = (PyObj_pj_pool_factory *)PyType_GenericNew( + &PyTyp_pj_pool_factory, NULL, NULL + ); + pool->pool_fact = p; + return (PyObject *)pool; +} + + +/* + * 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); + Py_INCREF(Py_None); + return Py_None; +} + + +/* + * py_pjsua_create + */ +static PyObject *py_pjsua_create(PyObject *pSelf, PyObject *pArgs) +{ + pj_status_t status; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "")) + { + return NULL; + } + status = pjsua_create(); + + if (status == PJ_SUCCESS) + { + status = pj_thread_local_alloc(&thread_id); + if (status == PJ_SUCCESS) + status = pj_thread_local_set(thread_id, (void*)1); + } + + 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); + + 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_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; + + 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(obj_log_cb); + obj_log_cb = obj_log->cb; + Py_INCREF(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); + + if (!PyArg_ParseTuple(pArgs, "")) + { + return NULL; + } + 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); + + if (!PyArg_ParseTuple(pArgs, "")) + { + return NULL; + } + status = pjsua_destroy(); + + return Py_BuildValue("i",status); +} + + +/* + * py_pjsua_handle_events + */ +static PyObject *py_pjsua_handle_events(PyObject *pSelf, PyObject *pArgs) +{ + int ret; + unsigned msec; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "i", &msec)) + { + return NULL; + } + + /* 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 + ret = pjsua_handle_events(msec); + Py_END_ALLOW_THREADS + + 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_pool_create_doc[] = + "_pjsua.Pj_Pool _pjsua.pool_create (string name, int init_size, " + "int increment) " + "Create memory pool Parameters: " + "name: Optional pool name; " + "init_size: Initial size of the pool; " + "increment: Increment size."; + +static char pjsua_get_pjsip_endpt_doc[] = + "_pjsua.Pjsip_Endpoint _pjsua.get_pjsip_endpt (void) " + "Internal function to get SIP endpoint instance of pjsua, which is needed " + "for example to register module, create transports, etc. Probably is only " + "valid after pjsua_init() is called."; + +static char pjsua_get_pjmedia_endpt_doc[] = + "_pjsua.Pjmedia_Endpt _pjsua.get_pjmedia_endpt (void) " + "Internal function to get media endpoint instance. Only valid after " + "pjsua_init() is called."; + +static char pjsua_get_pool_factory_doc[] = + "_pjsua.Pj_Pool_Factory _pjsua.get_pool_factory (void) " + "Internal function to get PJSUA pool factory. Only valid after " + "pjsua_init() is called."; + +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 + * !modified @ 051206 + */ +static PyObject *py_pjsua_transport_config_default(PyObject *pSelf, + PyObject *pArgs) +{ + PyObj_pjsua_transport_config *obj; + pjsua_transport_config cfg; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "")) { + return NULL; + } + + 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 + * !modified @ 051206 + */ +static PyObject *py_pjsua_transport_create(PyObject *pSelf, PyObject *pArgs) +{ + pj_status_t status; + int type; + PyObject * tmpObj; + pjsua_transport_config cfg; + pjsua_transport_id id; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "iO", &type, &tmpObj)) { + return NULL; + } + + if (tmpObj != Py_None) { + PyObj_pjsua_transport_config *obj; + obj = (PyObj_pjsua_transport_config*)tmpObj; + 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 + * !modified @ 261206 + */ +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); + + if (!PyArg_ParseTuple(pArgs, "")) + { + return NULL; + } + + c = PJ_ARRAY_SIZE(id); + status = pjsua_enum_transports(id, &c); + + list = PyList_New(c); + for (i = 0; i < c; i++) + { + int ret = PyList_SetItem(list, i, Py_BuildValue("i", id[i])); + if (ret == -1) + { + return NULL; + } + } + + return Py_BuildValue("O",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 Py_BuildValue("O", obj); + } else { + Py_INCREF(Py_None); + return Py_None; + } +} + +/* + * 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 + * !modified @ 051206 + */ +static PyObject *py_pjsua_acc_config_default(PyObject *pSelf, PyObject *pArgs) +{ + PyObj_pjsua_acc_config *obj; + pjsua_acc_config cfg; + + PJ_UNUSED_ARG(pSelf); + + 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); + + if (!PyArg_ParseTuple(pArgs, "")) { + return NULL; + } + + 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); + + if (!PyArg_ParseTuple(pArgs, "")) { + return NULL; + } + + id = pjsua_acc_get_default(); + + return Py_BuildValue("i", id); +} + +/* + * py_pjsua_acc_add + * !modified @ 051206 + */ +static PyObject *py_pjsua_acc_add(PyObject *pSelf, PyObject *pArgs) +{ + int is_default; + PyObject * acObj; + PyObj_pjsua_acc_config * ac; + int acc_id; + int status; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "Oi", &acObj, &is_default)) { + return NULL; + } + + if (acObj != Py_None) { + pjsua_acc_config cfg; + + pjsua_acc_config_default(&cfg); + ac = (PyObj_pjsua_acc_config *)acObj; + 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 + * !modified @ 051206 + */ +static PyObject *py_pjsua_acc_add_local(PyObject *pSelf, PyObject *pArgs) +{ + int is_default; + int tid; + int p_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, &p_acc_id); + + return Py_BuildValue("ii", status, p_acc_id); +} + +/* + * py_pjsua_acc_del + */ +static PyObject *py_pjsua_acc_del(PyObject *pSelf, PyObject *pArgs) +{ + int acc_id; + int status; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "i", &acc_id)) + { + return NULL; + } + + + 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 * acObj; + PyObj_pjsua_acc_config * ac; + int acc_id; + int status; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "iO", &acc_id, &acObj)) { + return NULL; + } + + if (acObj != Py_None) { + pjsua_acc_config cfg; + + pjsua_acc_config_default(&cfg); + ac = (PyObj_pjsua_acc_config *)acObj; + 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; + const char *rpid_id; + 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; + 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 + * !modified @ 051206 + */ +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 Py_BuildValue("O", obj); + } else { + Py_INCREF(Py_None); + return Py_None; + } +} + +/* + * py_pjsua_enum_accs + * !modified @ 241206 + */ +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); + + if (!PyArg_ParseTuple(pArgs, "")) + { + return NULL; + } + c = PJ_ARRAY_SIZE(id); + + status = pjsua_enum_accs(id, &c); + + list = PyList_New(c); + for (i = 0; i < c; i++) + { + int ret = PyList_SetItem(list, i, Py_BuildValue("i", id[i])); + if (ret == -1) + { + return NULL; + } + } + + return Py_BuildValue("O",list); +} + +/* + * py_pjsua_acc_enum_info + * !modified @ 241206 + */ +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); + + if (!PyArg_ParseTuple(pArgs, "")) { + return NULL; + } + + c = PJ_ARRAY_SIZE(info); + status = pjsua_acc_enum_info(info, &c); + + 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 Py_BuildValue("O",list); +} + +/* + * py_pjsua_acc_find_for_outgoing + */ +static PyObject *py_pjsua_acc_find_for_outgoing(PyObject *pSelf, + PyObject *pArgs) +{ + int acc_id; + PyObject * url; + pj_str_t str; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "O", &url)) + { + return NULL; + } + str.ptr = PyString_AsString(url); + str.slen = strlen(PyString_AsString(url)); + + acc_id = pjsua_acc_find_for_outgoing(&str); + + return Py_BuildValue("i", acc_id); +} + +/* + * py_pjsua_acc_find_for_incoming + */ +static PyObject *py_pjsua_acc_find_for_incoming(PyObject *pSelf, + PyObject *pArgs) +{ + int acc_id; + PyObject * tmpObj; + PyObj_pjsip_rx_data * obj; + pjsip_rx_data * rdata; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "O", &tmpObj)) + { + return NULL; + } + if (tmpObj != Py_None) + { + obj = (PyObj_pjsip_rx_data *)tmpObj; + rdata = obj->rdata; + acc_id = pjsua_acc_find_for_incoming(rdata); + } else { + acc_id = pjsua_acc_find_for_incoming(NULL); + } + return Py_BuildValue("i", acc_id); +} + +/* + * py_pjsua_acc_create_uac_contact + * !modified @ 061206 + */ +static PyObject *py_pjsua_acc_create_uac_contact(PyObject *pSelf, + PyObject *pArgs) +{ + int status; + int acc_id; + PyObject * pObj; + PyObj_pj_pool * p; + pj_pool_t * pool; + PyObject * strc; + pj_str_t contact; + PyObject * stru; + pj_str_t uri; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "OiO", &pObj, &acc_id, &stru)) + { + return NULL; + } + if (pObj != Py_None) + { + p = (PyObj_pj_pool *)pObj; + pool = p->pool; + uri.ptr = PyString_AsString(stru); + uri.slen = strlen(PyString_AsString(stru)); + status = pjsua_acc_create_uac_contact(pool, &contact, acc_id, &uri); + } else { + status = pjsua_acc_create_uac_contact(NULL, &contact, acc_id, &uri); + } + strc = PyString_FromStringAndSize(contact.ptr, contact.slen); + + return Py_BuildValue("O", strc); +} + +/* + * py_pjsua_acc_create_uas_contact + * !modified @ 061206 + */ +static PyObject *py_pjsua_acc_create_uas_contact(PyObject *pSelf, + PyObject *pArgs) +{ + int status; + int acc_id; + PyObject * pObj; + PyObj_pj_pool * p; + pj_pool_t * pool; + PyObject * strc; + pj_str_t contact; + PyObject * rObj; + PyObj_pjsip_rx_data * objr; + pjsip_rx_data * rdata; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "OiO", &pObj, &acc_id, &rObj)) + { + return NULL; + } + if (pObj != Py_None) + { + p = (PyObj_pj_pool *)pObj; + pool = p->pool; + } else { + pool = NULL; + } + if (rObj != Py_None) + { + objr = (PyObj_pjsip_rx_data *)rObj; + rdata = objr->rdata; + } else { + rdata = NULL; + } + status = pjsua_acc_create_uas_contact(pool, &contact, acc_id, rdata); + strc = PyString_FromStringAndSize(contact.ptr, contact.slen); + + return Py_BuildValue("O", strc); +} + +/* + * 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); +} + + +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."; +static char pjsua_acc_find_for_outgoing_doc[] = + "int _pjsua.acc_find_for_outgoing (string url) " + "This is an internal function to find the most appropriate account " + "to used to reach to the specified URL."; +static char pjsua_acc_find_for_incoming_doc[] = + "int _pjsua.acc_find_for_incoming (PyObj_pjsip_rx_data rdata) " + "This is an internal function to find the most appropriate account " + "to be used to handle incoming calls."; +static char pjsua_acc_create_uac_contact_doc[] = + "string _pjsua.acc_create_uac_contact (PyObj_pj_pool pool, " + "int acc_id, string uri) " + "Create a suitable URI to be put as Contact based on the specified " + "target URI for the specified account."; +static char pjsua_acc_create_uas_contact_doc[] = + "string _pjsua.acc_create_uas_contact (PyObj_pj_pool pool, " + "int acc_id, PyObj_pjsip_rx_data rdata) " + "Create a suitable URI to be put as Contact based on the information " + "in the incoming request."; + +/* 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); + + if (!PyArg_ParseTuple(pArgs, "")) { + return NULL; + } + + 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) +{ + int ret; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "")) { + return NULL; + } + ret = pjsua_get_buddy_count(); + + return Py_BuildValue("i", ret); +} + +/* + * 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 + * !modified @ 241206 + */ +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); + + if (!PyArg_ParseTuple(pArgs, "")) { + return NULL; + } + c = PJ_ARRAY_SIZE(id); + status = pjsua_enum_buddies(id, &c); + list = PyList_New(c); + for (i = 0; i < c; i++) { + PyList_SetItem(list, i, Py_BuildValue("i", id[i])); + } + + return Py_BuildValue("O",list); +} + +/* + * py_pjsua_buddy_get_info + * !modified @ 071206 + */ +static PyObject *py_pjsua_buddy_get_info(PyObject *pSelf, PyObject *pArgs) +{ + int buddy_id; + PyObj_pjsua_buddy_info * obj; + 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) { + obj = (PyObj_pjsua_buddy_info *) + PyObj_pjsua_buddy_config_new(&PyTyp_pjsua_buddy_info,NULL,NULL); + PyObj_pjsua_buddy_info_import(obj, &info); + return Py_BuildValue("O", obj); + } else { + Py_INCREF(Py_None); + return Py_None; + } +} + +/* + * py_pjsua_buddy_add + * !modified @ 061206 + */ +static PyObject *py_pjsua_buddy_add(PyObject *pSelf, PyObject *pArgs) +{ + PyObject * bcObj; + int buddy_id; + int status; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "O", &bcObj)) { + return NULL; + } + + if (bcObj != Py_None) { + pjsua_buddy_config cfg; + PyObj_pjsua_buddy_config * bc; + + bc = (PyObj_pjsua_buddy_config *)bcObj; + + 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; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "i", &buddy_id)) { + return NULL; + } + + + status = pjsua_buddy_del(buddy_id); + return Py_BuildValue("i", status); +} + +/* + * 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); + Py_INCREF(Py_None); + return Py_None; +} + +/* + * py_pjsua_im_send + * !modified @ 071206 + */ +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 * st; + PyObject * smt; + PyObject * sc; + pjsua_msg_data msg_data; + PyObject * omdObj; + PyObj_pjsua_msg_data * omd; + + int user_data; + pj_pool_t *pool; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "iOOOOi", &acc_id, + &st, &smt, &sc, &omdObj, &user_data)) + { + return NULL; + } + if (smt != Py_None) { + mime_type = &tmp_mime_type; + tmp_mime_type = PyString_to_pj_str(smt); + } else { + mime_type = NULL; + } + to = PyString_to_pj_str(st); + content = PyString_to_pj_str(sc); + + if (omdObj != Py_None) { + + omd = (PyObj_pjsua_msg_data *)omdObj; + msg_data.content_type = PyString_to_pj_str(omd->content_type); + msg_data.msg_body = PyString_to_pj_str(omd->msg_body); + pool = pjsua_pool_create("pjsua", 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 *)user_data); + pj_pool_release(pool); + } else { + + status = pjsua_im_send(acc_id, &to, mime_type, + &content, NULL, NULL); + } + + 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 * st; + int is_typing; + pjsua_msg_data msg_data; + PyObject * omdObj; + PyObj_pjsua_msg_data * omd; + pj_pool_t * pool; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "iOiO", &acc_id, &st, &is_typing, &omdObj)) { + return NULL; + } + + to = PyString_to_pj_str(st); + + if (omdObj != Py_None) { + omd = (PyObj_pjsua_msg_data *)omdObj; + msg_data.content_type = PyString_to_pj_str(omd->content_type); + msg_data.msg_body = PyString_to_pj_str(omd->msg_body); + pool = pjsua_pool_create("pjsua", 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); + pj_pool_release(pool); + } else { + status = pjsua_im_typing(acc_id, &to, is_typing, NULL); + } + 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 */ + + + +/* + * PyObj_pjsua_codec_info + * Codec Info + * !modified @ 071206 + */ +typedef struct +{ + PyObject_HEAD + /* Type-specific fields go here. */ + + PyObject * codec_id; + pj_uint8_t priority; + char buf_[32]; +} PyObj_pjsua_codec_info; + + +/* + * codec_info_dealloc + * deletes a codec_info from memory + * !modified @ 071206 + */ +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 + * !modified @ 071206 + */ +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(""); + if (self->codec_id == NULL) + { + Py_DECREF(self); + return NULL; + } + + + } + 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 objects", /* 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; + PyListObject * 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(""); + if (self->name == NULL) + { + Py_DECREF(self); + return NULL; + } + + self->listeners = (PyListObject *)PyList_New(0); + if (self->listeners == NULL) + { + Py_DECREF(self); + return NULL; + } + } + 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_port + */ +typedef struct +{ + PyObject_HEAD + /* Type-specific fields go here. */ + pjmedia_port * port; +} PyObj_pjmedia_port; + + +/* + * PyTyp_pjmedia_port + */ +static PyTypeObject PyTyp_pjmedia_port = +{ + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_pjsua.PJMedia_Port", /*tp_name*/ + sizeof(PyObj_pjmedia_port), /*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_port objects", /* tp_doc */ + +}; + +/* + * 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(""); + if (self->name == NULL) + { + Py_DECREF(self); + return NULL; + } + + } + return (PyObject *)self; +} + +/* + * pjmedia_snd_dev_info_members + */ +static PyMemberDef pjmedia_snd_dev_info_members[] = +{ + + { + "name", T_OBJECT_EX, + offsetof(PyObj_pjmedia_snd_dev_info, name), 0, + "Device name" + }, + { + "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." + }, + + + {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 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_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; + unsigned reserved; + pj_uint8_t enc_fmtp_mode; + pj_uint8_t dec_fmtp_mode; + +} 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" + }, + { + "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" + }, + { + "reserved", T_INT, + offsetof(PyObj_pjmedia_codec_param_setting, reserved), 0, + "Reserved, must be zero" + }, + { + "cng", T_INT, + offsetof(PyObj_pjmedia_codec_param_setting, cng), 0, + "Comfort Noise Generator" + }, + { + "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)" + }, + + {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 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_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); + if (self->info == NULL) + { + Py_DECREF(self); + return NULL; + } + self->setting = (PyObj_pjmedia_codec_param_setting *) + PyType_GenericNew(&PyTyp_pjmedia_codec_param_setting, NULL, NULL); + if (self->setting == NULL) + { + Py_DECREF(self); + return 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 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_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 */ + +}; + +/* + * py_pjsua_conf_get_max_ports + */ +static PyObject *py_pjsua_conf_get_max_ports +(PyObject *pSelf, PyObject *pArgs) +{ + int ret; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "")) + { + return NULL; + } + ret = pjsua_conf_get_max_ports(); + + return Py_BuildValue("i", ret); +} + +/* + * py_pjsua_conf_get_active_ports + */ +static PyObject *py_pjsua_conf_get_active_ports +(PyObject *pSelf, PyObject *pArgs) +{ + int ret; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "")) + { + return NULL; + } + ret = pjsua_conf_get_active_ports(); + + return Py_BuildValue("i", ret); +} + +/* + * py_pjsua_enum_conf_ports + * !modified @ 241206 + */ +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); + + if (!PyArg_ParseTuple(pArgs, "")) + { + return NULL; + } + + c = PJ_ARRAY_SIZE(id); + status = pjsua_enum_conf_ports(id, &c); + + list = PyList_New(c); + for (i = 0; i < c; i++) + { + int ret = PyList_SetItem(list, i, Py_BuildValue("i", id[i])); + if (ret == -1) + { + return NULL; + } + } + + return Py_BuildValue("O",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 * obj; + 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); + obj = (PyObj_pjsua_conf_port_info *)conf_port_info_new + (&PyTyp_pjsua_conf_port_info,NULL,NULL); + obj->bits_per_sample = info.bits_per_sample; + obj->channel_count = info.bits_per_sample; + obj->clock_rate = info.clock_rate; + obj->name = PyString_FromStringAndSize(info.name.ptr, info.name.slen); + obj->samples_per_frame = info.samples_per_frame; + obj->slot_id = info.slot_id; + + obj->listeners = (PyListObject *)PyList_New(info.listener_cnt); + for (i = 0; i < info.listener_cnt; i++) { + PyObject * item = Py_BuildValue("i",info.listeners[i]); + PyList_SetItem((PyObject *)obj->listeners, i, item); + } + return Py_BuildValue("O", obj); +} + +/* + * py_pjsua_conf_add_port + */ +static PyObject *py_pjsua_conf_add_port +(PyObject *pSelf, PyObject *pArgs) +{ + int p_id; + PyObject * oportObj; + PyObj_pjmedia_port * oport; + pjmedia_port * port; + PyObject * opoolObj; + PyObj_pj_pool * opool; + pj_pool_t * pool; + + int status; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "OO", &opoolObj, &oportObj)) + { + return NULL; + } + if (opoolObj != Py_None) + { + opool = (PyObj_pj_pool *)opoolObj; + pool = opool->pool; + } else { + opool = NULL; + pool = NULL; + } + if (oportObj != Py_None) + { + oport = (PyObj_pjmedia_port *)oportObj; + port = oport->port; + } else { + oport = NULL; + port = NULL; + } + + status = pjsua_conf_add_port(pool, port, &p_id); + + + return Py_BuildValue("ii", status, p_id); +} + +/* + * 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_player_create + */ +static PyObject *py_pjsua_player_create +(PyObject *pSelf, PyObject *pArgs) +{ + int id; + int options; + PyObject * filename; + pj_str_t str; + int status; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "Oi", &filename, &options)) + { + return NULL; + } + str.ptr = PyString_AsString(filename); + str.slen = strlen(PyString_AsString(filename)); + status = pjsua_player_create(&str, 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; + pj_uint32_t samples; + int status; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "iI", &id, &samples)) + { + return NULL; + } + + 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 + * !modified @ 261206 + */ +static PyObject *py_pjsua_recorder_create +(PyObject *pSelf, PyObject *pArgs) +{ + int p_id; + int options; + int max_size; + PyObject * filename; + pj_str_t str; + PyObject * enc_param; + pj_str_t strparam; + int enc_type; + + int status; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "OiOii", &filename, + &enc_type, &enc_param, &max_size, &options)) + { + return NULL; + } + str.ptr = PyString_AsString(filename); + str.slen = strlen(PyString_AsString(filename)); + if (enc_param != Py_None) + { + strparam.ptr = PyString_AsString(enc_param); + strparam.slen = strlen(PyString_AsString(enc_param)); + status = pjsua_recorder_create + (&str, enc_type, NULL, max_size, options, &p_id); + } else { + status = pjsua_recorder_create + (&str, enc_type, NULL, max_size, options, &p_id); + } + return Py_BuildValue("ii", status, p_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 *list; + + pjmedia_snd_dev_info info[SND_DEV_NUM]; + unsigned c, i; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "")) + { + return NULL; + } + + c = PJ_ARRAY_SIZE(info); + status = pjsua_enum_snd_devs(info, &c); + + list = PyList_New(c); + for (i = 0; i < c; i++) + { + int ret; + int j; + char * str; + + 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; + str = (char *)malloc(SND_NAME_LEN * sizeof(char)); + memset(str, 0, SND_NAME_LEN); + for (j = 0; j < SND_NAME_LEN; j++) + { + str[j] = info[i].name[j]; + } + obj->name = PyString_FromStringAndSize(str, SND_NAME_LEN); + free(str); + ret = PyList_SetItem(list, i, (PyObject *)obj); + if (ret == -1) + { + return NULL; + } + } + + return Py_BuildValue("O",list); +} + +/* + * 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); + + if (!PyArg_ParseTuple(pArgs, "")) + { + return NULL; + } + + 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); + + if (!PyArg_ParseTuple(pArgs, "")) + { + return NULL; + } + + status = pjsua_set_null_snd_dev(); + + + return Py_BuildValue("i", status); +} + +/* + * py_pjsua_set_no_snd_dev + */ +static PyObject *py_pjsua_set_no_snd_dev +(PyObject *pSelf, PyObject *pArgs) +{ + + PyObj_pjmedia_port * obj; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "")) + { + return NULL; + } + + obj = (PyObj_pjmedia_port *)PyType_GenericNew + (&PyTyp_pjmedia_port, NULL, NULL); + obj->port = pjsua_set_no_snd_dev(); + return Py_BuildValue("O", obj); +} + +/* + * 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 p_tail_ms; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "")) + { + return NULL; + } + + status = pjsua_get_ec_tail(&p_tail_ms); + + + return Py_BuildValue("i", p_tail_ms); +} + +/* + * py_pjsua_enum_codecs + * !modified @ 261206 + */ +static PyObject *py_pjsua_enum_codecs(PyObject *pSelf, PyObject *pArgs) +{ + pj_status_t status; + PyObject *list; + + pjsua_codec_info info[PJMEDIA_CODEC_MGR_MAX_CODECS]; + unsigned c, i; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "")) + { + return NULL; + } + + c = PJ_ARRAY_SIZE(info); + status = pjsua_enum_codecs(info, &c); + + list = PyList_New(c); + for (i = 0; i < c; i++) + { + int ret; + int j; + PyObj_pjsua_codec_info * obj; + obj = (PyObj_pjsua_codec_info *)codec_info_new + (&PyTyp_pjsua_codec_info, NULL, NULL); + obj->codec_id = PyString_FromStringAndSize + (info[i].codec_id.ptr, info[i].codec_id.slen); + obj->priority = info[i].priority; + for (j = 0; j < 32; j++) + { + obj->buf_[j] = info[i].buf_[j]; + } + ret = PyList_SetItem(list, i, (PyObject *)obj); + if (ret == -1) { + return NULL; + } + } + + + return Py_BuildValue("O",list); +} + +/* + * py_pjsua_codec_set_priority + */ +static PyObject *py_pjsua_codec_set_priority +(PyObject *pSelf, PyObject *pArgs) +{ + + int status; + PyObject * id; + pj_str_t str; + pj_uint8_t priority; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "OB", &id, &priority)) + { + return NULL; + } + str.ptr = PyString_AsString(id); + str.slen = strlen(PyString_AsString(id)); + status = pjsua_codec_set_priority(&str, 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 * id; + pj_str_t str; + pjmedia_codec_param param; + PyObj_pjmedia_codec_param *obj; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "O", &id)) + { + return NULL; + } + str.ptr = PyString_AsString(id); + str.slen = strlen(PyString_AsString(id)); + status = pjsua_codec_get_param(&str, ¶m); + obj = (PyObj_pjmedia_codec_param *)pjmedia_codec_param_new + (&PyTyp_pjmedia_codec_param, NULL, NULL); + obj->info->avg_bps = param.info.avg_bps; + obj->info->channel_cnt = param.info.channel_cnt; + obj->info->clock_rate = param.info.clock_rate; + obj->info->frm_ptime = param.info.frm_ptime; + obj->info->pcm_bits_per_sample = param.info.pcm_bits_per_sample; + obj->info->pt = param.info.pt; + obj->setting->cng = param.setting.cng; + obj->setting->dec_fmtp_mode = param.setting.dec_fmtp_mode; + obj->setting->enc_fmtp_mode = param.setting.enc_fmtp_mode; + obj->setting->frm_per_pkt = param.setting.frm_per_pkt; + obj->setting->penh = param.setting.penh; + obj->setting->plc = param.setting.plc; + obj->setting->reserved = param.setting.reserved; + obj->setting->vad = param.setting.vad; + + return Py_BuildValue("O", obj); +} +/* + * py_pjsua_codec_set_param + */ +static PyObject *py_pjsua_codec_set_param +(PyObject *pSelf, PyObject *pArgs) +{ + + int status; + PyObject * id; + pj_str_t str; + pjmedia_codec_param param; + PyObject * tmpObj; + PyObj_pjmedia_codec_param *obj; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "OO", &id, &tmpObj)) + { + return NULL; + } + + str.ptr = PyString_AsString(id); + str.slen = strlen(PyString_AsString(id)); + if (tmpObj != Py_None) + { + obj = (PyObj_pjmedia_codec_param *)tmpObj; + 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.reserved = obj->setting->reserved; + param.setting.vad = obj->setting->vad; + status = pjsua_codec_set_param(&str, ¶m); + } else { + status = pjsua_codec_set_param(&str, 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_add_port_doc[] = + "int, int _pjsua.conf_add_port " + "(_pjsua.Pj_Pool pool, _pjsua.PJMedia_Port port) " + "Add arbitrary media port to PJSUA's conference bridge. " + "Application can use this function to add the media port " + "that it creates. For media ports that are created by PJSUA-LIB " + "(such as calls, file player, or file recorder), PJSUA-LIB will " + "automatically add the port to the bridge."; +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_no_snd_dev_doc[] = + "_pjsua.PJMedia_Port _pjsua.set_no_snd_dev () " + "Disconnect the main conference bridge from any sound devices, " + "and let application connect the bridge to it's " + "own sound device/master port."; +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 */ + +/* + * PyObj_pj_time_val + * PJ Time Val + */ +typedef struct +{ + PyObject_HEAD + /* Type-specific fields go here. */ + long sec; + long msec; + +} PyObj_pj_time_val; + + + +/* + * pj_time_val_members + */ +static PyMemberDef pj_time_val_members[] = +{ + + { + "sec", T_INT, + offsetof(PyObj_pj_time_val, sec), 0, + "The seconds part of the time" + }, + { + "msec", T_INT, + offsetof(PyObj_pj_time_val, sec), 0, + "The milliseconds fraction of the time" + }, + + + {NULL} /* Sentinel */ +}; + + + + +/* + * PyTyp_pj_time_val + */ +static PyTypeObject PyTyp_pj_time_val = +{ + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_pjsua.PJ_Time_Val", /*tp_name*/ + sizeof(PyObj_pj_time_val), /*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*/ + "PJ Time Val objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + pj_time_val_members, /* tp_members */ + + +}; + +/* + * 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; + PyObj_pj_time_val * connect_duration; + PyObj_pj_time_val * total_duration; + struct { + char local_info[128]; + char local_contact[128]; + char remote_info[128]; + char remote_contact[128]; + char call_id[128]; + char last_status_text[128]; + } buf_; + +} 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); + Py_XDECREF(self->connect_duration); + Py_XDECREF(self->total_duration); + 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(""); + if (self->local_info == NULL) + { + Py_DECREF(self); + return NULL; + } + self->local_contact = PyString_FromString(""); + if (self->local_contact == NULL) + { + Py_DECREF(self); + return NULL; + } + self->remote_info = PyString_FromString(""); + if (self->remote_info == NULL) + { + Py_DECREF(self); + return NULL; + } + self->remote_contact = PyString_FromString(""); + if (self->remote_contact == NULL) + { + Py_DECREF(self); + return NULL; + } + self->call_id = PyString_FromString(""); + if (self->call_id == NULL) + { + Py_DECREF(self); + return NULL; + } + self->state_text = PyString_FromString(""); + if (self->state_text == NULL) + { + Py_DECREF(self); + return NULL; + } + self->last_status_text = PyString_FromString(""); + if (self->last_status_text == NULL) + { + Py_DECREF(self); + return NULL; + } + self->connect_duration = (PyObj_pj_time_val *)PyType_GenericNew + (&PyTyp_pj_time_val,NULL,NULL); + if (self->connect_duration == NULL) + { + Py_DECREF(self); + return NULL; + } + self->total_duration = (PyObj_pj_time_val *)PyType_GenericNew + (&PyTyp_pj_time_val,NULL,NULL); + if (self->total_duration == NULL) + { + Py_DECREF(self); + return NULL; + } + } + 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_OBJECT_EX, + offsetof(PyObj_pjsua_call_info, connect_duration), 0, + "Up-to-date call connected duration(zero when call is not established)" + }, + { + "total_duration", T_OBJECT_EX, + 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 objects", /* 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 */ + +}; + +/* + * py_pjsua_call_get_max_count + */ +static PyObject *py_pjsua_call_get_max_count +(PyObject *pSelf, PyObject *pArgs) +{ + int count; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "")) + { + return NULL; + } + + 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); + + if (!PyArg_ParseTuple(pArgs, "")) + { + return NULL; + } + + 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 *list; + + pjsua_transport_id id[PJSUA_MAX_CALLS]; + unsigned c, i; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "")) + { + return NULL; + } + + c = PJ_ARRAY_SIZE(id); + status = pjsua_enum_calls(id, &c); + + list = PyList_New(c); + for (i = 0; i < c; i++) + { + int ret = PyList_SetItem(list, i, Py_BuildValue("i", id[i])); + if (ret == -1) + { + return NULL; + } + } + + return Py_BuildValue("O",list); +} + +/* + * 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 * sd; + unsigned options; + pjsua_msg_data msg_data; + PyObject * omdObj; + PyObj_pjsua_msg_data * omd; + int user_data; + int call_id; + pj_pool_t * pool; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple + (pArgs, "iOIiO", &acc_id, &sd, &options, &user_data, &omdObj)) + { + return NULL; + } + + dst_uri.ptr = PyString_AsString(sd); + dst_uri.slen = strlen(PyString_AsString(sd)); + if (omdObj != Py_None) + { + omd = (PyObj_pjsua_msg_data *)omdObj; + msg_data.content_type.ptr = PyString_AsString(omd->content_type); + msg_data.content_type.slen = strlen + (PyString_AsString(omd->content_type)); + msg_data.msg_body.ptr = PyString_AsString(omd->msg_body); + msg_data.msg_body.slen = strlen(PyString_AsString(omd->msg_body)); + pool = pjsua_pool_create("pjsua", POOL_SIZE, POOL_SIZE); + translate_hdr(pool, &msg_data.hdr_list, omd->hdr_list); + status = pjsua_call_make_call(acc_id, &dst_uri, + options, (void*)user_data, &msg_data, &call_id); + pj_pool_release(pool); + } else { + + status = pjsua_call_make_call(acc_id, &dst_uri, + options, (void*)user_data, NULL, &call_id); + } + + 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 isActive; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "i", &call_id)) + { + return NULL; + } + + isActive = pjsua_call_is_active(call_id); + + + return Py_BuildValue("i", isActive); +} + +/* + * py_pjsua_call_has_media + */ +static PyObject *py_pjsua_call_has_media +(PyObject *pSelf, PyObject *pArgs) +{ + int call_id; + int hasMedia; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "i", &call_id)) + { + return NULL; + } + + hasMedia = pjsua_call_has_media(call_id); + + + return Py_BuildValue("i", hasMedia); +} + +/* + * 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 * oi; + 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) + { + oi = (PyObj_pjsua_call_info *)call_info_new(&PyTyp_pjsua_call_info, NULL, NULL); + oi->acc_id = info.acc_id; + pj_ansi_snprintf(oi->buf_.call_id, sizeof(oi->buf_.call_id), + "%.*s", (int)info.call_id.slen, info.call_id.ptr); + pj_ansi_snprintf(oi->buf_.last_status_text, + sizeof(oi->buf_.last_status_text), + "%.*s", (int)info.last_status_text.slen, info.last_status_text.ptr); + pj_ansi_snprintf(oi->buf_.local_contact, sizeof(oi->buf_.local_contact), + "%.*s", (int)info.local_contact.slen, info.local_contact.ptr); + pj_ansi_snprintf(oi->buf_.local_info, sizeof(oi->buf_.local_info), + "%.*s", (int)info.local_info.slen, info.local_info.ptr); + pj_ansi_snprintf(oi->buf_.remote_contact, + sizeof(oi->buf_.remote_contact), + "%.*s", (int)info.remote_contact.slen, info.remote_contact.ptr); + pj_ansi_snprintf(oi->buf_.remote_info, sizeof(oi->buf_.remote_info), + "%.*s", (int)info.remote_info.slen, info.remote_info.ptr); + + oi->call_id = PyString_FromStringAndSize(info.call_id.ptr, + info.call_id.slen); + oi->conf_slot = info.conf_slot; + oi->connect_duration->sec = info.connect_duration.sec; + oi->connect_duration->msec = info.connect_duration.msec; + oi->total_duration->sec = info.total_duration.sec; + oi->total_duration->msec = info.total_duration.msec; + oi->id = info.id; + oi->last_status = info.last_status; + oi->last_status_text = PyString_FromStringAndSize( + info.last_status_text.ptr, info.last_status_text.slen); + oi->local_contact = PyString_FromStringAndSize( + info.local_contact.ptr, info.local_contact.slen); + oi->local_info = PyString_FromStringAndSize( + info.local_info.ptr, info.local_info.slen); + oi->remote_contact = PyString_FromStringAndSize( + info.remote_contact.ptr, info.remote_contact.slen); + oi->remote_info = PyString_FromStringAndSize( + info.remote_info.ptr, info.remote_info.slen); + oi->media_dir = info.media_dir; + oi->media_status = info.media_status; + oi->role = info.role; + oi->state = info.state; + oi->state_text = PyString_FromStringAndSize( + info.state_text.ptr, info.state_text.slen); + + return Py_BuildValue("O", oi); + } else { + Py_INCREF(Py_None); + return Py_None; + } +} + +/* + * py_pjsua_call_set_user_data + */ +static PyObject *py_pjsua_call_set_user_data +(PyObject *pSelf, PyObject *pArgs) +{ + int call_id; + int user_data; + int status; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "ii", &call_id, &user_data)) + { + return NULL; + } + + status = pjsua_call_set_user_data(call_id, (void*)user_data); + + + 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; + void * user_data; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "i", &call_id)) + { + return NULL; + } + + user_data = pjsua_call_get_user_data(call_id); + + + return Py_BuildValue("i", (int)user_data); +} + +/* + * 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 * sr; + unsigned code; + pjsua_msg_data msg_data; + PyObject * omdObj; + PyObj_pjsua_msg_data * omd; + pj_pool_t * pool; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "iIOO", &call_id, &code, &sr, &omdObj)) + { + return NULL; + } + if (sr == Py_None) + { + reason = NULL; + } else { + reason = &tmp_reason; + tmp_reason.ptr = PyString_AsString(sr); + tmp_reason.slen = strlen(PyString_AsString(sr)); + } + if (omdObj != Py_None) + { + omd = (PyObj_pjsua_msg_data *)omdObj; + msg_data.content_type.ptr = PyString_AsString(omd->content_type); + msg_data.content_type.slen = strlen + (PyString_AsString(omd->content_type)); + msg_data.msg_body.ptr = PyString_AsString(omd->msg_body); + msg_data.msg_body.slen = strlen(PyString_AsString(omd->msg_body)); + pool = pjsua_pool_create("pjsua", POOL_SIZE, POOL_SIZE); + translate_hdr(pool, &msg_data.hdr_list, omd->hdr_list); + + status = pjsua_call_answer(call_id, code, reason, &msg_data); + + pj_pool_release(pool); + } else { + + status = pjsua_call_answer(call_id, code, reason, NULL); + + } + + 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 * sr; + unsigned code; + pjsua_msg_data msg_data; + PyObject * omdObj; + PyObj_pjsua_msg_data * omd; + pj_pool_t * pool = NULL; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "iIOO", &call_id, &code, &sr, &omdObj)) + { + return NULL; + } + if (sr == Py_None) + { + reason = NULL; + } else { + reason = &tmp_reason; + tmp_reason.ptr = PyString_AsString(sr); + tmp_reason.slen = strlen(PyString_AsString(sr)); + } + if (omdObj != Py_None) + { + omd = (PyObj_pjsua_msg_data *)omdObj; + msg_data.content_type.ptr = PyString_AsString(omd->content_type); + msg_data.content_type.slen = strlen + (PyString_AsString(omd->content_type)); + msg_data.msg_body.ptr = PyString_AsString(omd->msg_body); + msg_data.msg_body.slen = strlen(PyString_AsString(omd->msg_body)); + pool = pjsua_pool_create("pjsua", POOL_SIZE, POOL_SIZE); + translate_hdr(pool, &msg_data.hdr_list, omd->hdr_list); + status = pjsua_call_hangup(call_id, code, reason, &msg_data); + pj_pool_release(pool); + } else { + status = pjsua_call_hangup(call_id, code, reason, NULL); + } + + 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; + PyObj_pjsua_msg_data * omd; + pj_pool_t * pool; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "iO", &call_id, &omdObj)) + { + return NULL; + } + + if (omdObj != Py_None) + { + omd = (PyObj_pjsua_msg_data *)omdObj; + msg_data.content_type.ptr = PyString_AsString(omd->content_type); + msg_data.content_type.slen = strlen + (PyString_AsString(omd->content_type)); + msg_data.msg_body.ptr = PyString_AsString(omd->msg_body); + msg_data.msg_body.slen = strlen(PyString_AsString(omd->msg_body)); + pool = pjsua_pool_create("pjsua", POOL_SIZE, POOL_SIZE); + translate_hdr(pool, &msg_data.hdr_list, omd->hdr_list); + status = pjsua_call_set_hold(call_id, &msg_data); + pj_pool_release(pool); + } else { + status = pjsua_call_set_hold(call_id, NULL); + } + 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; + PyObj_pjsua_msg_data * omd; + pj_pool_t * pool; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "iiO", &call_id, &unhold, &omdObj)) + { + return NULL; + } + + if (omdObj != Py_None) + { + omd = (PyObj_pjsua_msg_data *)omdObj; + msg_data.content_type.ptr = PyString_AsString(omd->content_type); + msg_data.content_type.slen = strlen + (PyString_AsString(omd->content_type)); + msg_data.msg_body.ptr = PyString_AsString(omd->msg_body); + msg_data.msg_body.slen = strlen(PyString_AsString(omd->msg_body)); + pool = pjsua_pool_create("pjsua", POOL_SIZE, POOL_SIZE); + translate_hdr(pool, &msg_data.hdr_list, omd->hdr_list); + status = pjsua_call_reinvite(call_id, unhold, &msg_data); + pj_pool_release(pool); + } else { + status = pjsua_call_reinvite(call_id, unhold, NULL); + } + 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; + PyObj_pjsua_msg_data * omd; + pj_pool_t * pool; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "iiO", &call_id, &option, &omdObj)) + { + return NULL; + } + + if (omdObj != Py_None) + { + omd = (PyObj_pjsua_msg_data *)omdObj; + msg_data.content_type.ptr = PyString_AsString(omd->content_type); + msg_data.content_type.slen = strlen + (PyString_AsString(omd->content_type)); + msg_data.msg_body.ptr = PyString_AsString(omd->msg_body); + msg_data.msg_body.slen = strlen(PyString_AsString(omd->msg_body)); + pool = pjsua_pool_create("pjsua", POOL_SIZE, POOL_SIZE); + translate_hdr(pool, &msg_data.hdr_list, omd->hdr_list); + status = pjsua_call_update(call_id, option, &msg_data); + pj_pool_release(pool); + } else { + status = pjsua_call_update(call_id, option, NULL); + } + 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 *method_obj; + pj_str_t method; + pjsua_msg_data msg_data; + PyObject * omdObj; + PyObj_pjsua_msg_data * omd; + pj_pool_t * pool; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "iOO", &call_id, &method_obj, &omdObj)) + { + return NULL; + } + + if (!PyString_Check(method_obj)) { + return NULL; + } + + method.ptr = PyString_AsString(method_obj); + method.slen = PyString_Size(method_obj); + + if (omdObj != Py_None) + { + omd = (PyObj_pjsua_msg_data *)omdObj; + msg_data.content_type.ptr = PyString_AsString(omd->content_type); + msg_data.content_type.slen = strlen + (PyString_AsString(omd->content_type)); + msg_data.msg_body.ptr = PyString_AsString(omd->msg_body); + msg_data.msg_body.slen = strlen(PyString_AsString(omd->msg_body)); + pool = pjsua_pool_create("pjsua", POOL_SIZE, POOL_SIZE); + translate_hdr(pool, &msg_data.hdr_list, omd->hdr_list); + status = pjsua_call_send_request(call_id, &method, &msg_data); + pj_pool_release(pool); + } else { + status = pjsua_call_send_request(call_id, &method, NULL); + } + 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 * sd; + pjsua_msg_data msg_data; + PyObject * omdObj; + PyObj_pjsua_msg_data * omd; + pj_pool_t * pool; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "iOO", &call_id, &sd, &omdObj)) + { + return NULL; + } + + dest.ptr = PyString_AsString(sd); + dest.slen = strlen(PyString_AsString(sd)); + + if (omdObj != Py_None) + { + omd = (PyObj_pjsua_msg_data *)omdObj; + msg_data.content_type.ptr = PyString_AsString(omd->content_type); + msg_data.content_type.slen = strlen + (PyString_AsString(omd->content_type)); + msg_data.msg_body.ptr = PyString_AsString(omd->msg_body); + msg_data.msg_body.slen = strlen(PyString_AsString(omd->msg_body)); + pool = pjsua_pool_create("pjsua", POOL_SIZE, POOL_SIZE); + translate_hdr(pool, &msg_data.hdr_list, omd->hdr_list); + status = pjsua_call_xfer(call_id, &dest, &msg_data); + pj_pool_release(pool); + } else { + status = pjsua_call_xfer(call_id, &dest, NULL); + } + 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; + PyObj_pjsua_msg_data * omd; + pj_pool_t * pool; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple + (pArgs, "iiIO", &call_id, &dest_call_id, &options, &omdObj)) + { + return NULL; + } + + if (omdObj != Py_None) + { + omd = (PyObj_pjsua_msg_data *)omdObj; + msg_data.content_type.ptr = PyString_AsString(omd->content_type); + msg_data.content_type.slen = strlen + (PyString_AsString(omd->content_type)); + msg_data.msg_body.ptr = PyString_AsString(omd->msg_body); + msg_data.msg_body.slen = strlen(PyString_AsString(omd->msg_body)); + pool = pjsua_pool_create("pjsua", 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); + pj_pool_release(pool); + } else { + status = pjsua_call_xfer_replaces(call_id, dest_call_id,options, NULL); + } + 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 * sd; + pj_str_t digits; + int status; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "iO", &call_id, &sd)) + { + return NULL; + } + digits.ptr = PyString_AsString(sd); + digits.slen = strlen(PyString_AsString(sd)); + 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 * sm; + PyObject * sc; + pjsua_msg_data msg_data; + PyObject * omdObj; + PyObj_pjsua_msg_data * omd; + int user_data; + pj_pool_t * pool; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple + (pArgs, "iOOOi", &call_id, &sm, &sc, &omdObj, &user_data)) + { + return NULL; + } + if (sm == Py_None) + { + mime_type = NULL; + } else { + mime_type = &tmp_mime_type; + tmp_mime_type.ptr = PyString_AsString(sm); + tmp_mime_type.slen = strlen(PyString_AsString(sm)); + } + content.ptr = PyString_AsString(sc); + content.slen = strlen(PyString_AsString(sc)); + + if (omdObj != Py_None) + { + omd = (PyObj_pjsua_msg_data *)omdObj; + msg_data.content_type.ptr = PyString_AsString(omd->content_type); + msg_data.content_type.slen = strlen + (PyString_AsString(omd->content_type)); + msg_data.msg_body.ptr = PyString_AsString(omd->msg_body); + msg_data.msg_body.slen = strlen(PyString_AsString(omd->msg_body)); + pool = pjsua_pool_create("pjsua", 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 *)user_data); + pj_pool_release(pool); + } else { + status = pjsua_call_send_im + (call_id, mime_type, &content, NULL, (void *)user_data); + } + + 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; + PyObj_pjsua_msg_data * omd; + pj_pool_t * pool; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "iiO", &call_id, &is_typing, &omdObj)) + { + return NULL; + } + + if (omdObj != Py_None) + { + omd = (PyObj_pjsua_msg_data *)omdObj; + msg_data.content_type.ptr = PyString_AsString(omd->content_type); + msg_data.content_type.slen = strlen + (PyString_AsString(omd->content_type)); + msg_data.msg_body.ptr = PyString_AsString(omd->msg_body); + msg_data.msg_body.slen = strlen(PyString_AsString(omd->msg_body)); + pool = pjsua_pool_create("pjsua", 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); + pj_pool_release(pool); + } else { + status = pjsua_call_send_typing_ind(call_id, is_typing, NULL); + } + 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); + + if (!PyArg_ParseTuple(pArgs, "")) + { + return NULL; + } + + pjsua_call_hangup_all(); + + Py_INCREF(Py_None); + return Py_None; +} + +/* + * py_pjsua_call_dump + */ +static PyObject *py_pjsua_call_dump +(PyObject *pSelf, PyObject *pArgs) +{ + int call_id; + int with_media; + PyObject * sb; + PyObject * si; + char * buffer; + char * indent; + unsigned maxlen; + int status; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "iiIO", &call_id, &with_media, &maxlen, &si)) + { + return NULL; + } + buffer = (char *) malloc (maxlen * sizeof(char)); + indent = PyString_AsString(si); + + status = pjsua_call_dump(call_id, with_media, buffer, maxlen, indent); + sb = PyString_FromStringAndSize(buffer, maxlen); + free(buffer); + return Py_BuildValue("O", sb); +} + + +/* + * py_pjsua_dump + * Dump application states. + */ +static PyObject *py_pjsua_dump(PyObject *pSelf, PyObject *pArgs) +{ + unsigned old_decor; + char buf[1024]; + int detail; + + PJ_UNUSED_ARG(pSelf); + + if (!PyArg_ParseTuple(pArgs, "i", &detail)) + { + return NULL; + } + + PJ_LOG(3,(THIS_FILE, "Start dumping application states:")); + + old_decor = pj_log_get_decor(); + pj_log_set_decor(old_decor & (PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_CR)); + + if (detail) + pj_dump_config(); + + pjsip_endpt_dump(pjsua_get_pjsip_endpt(), detail); + pjmedia_endpt_dump(pjsua_get_pjmedia_endpt()); + pjsip_tsx_layer_dump(detail); + pjsip_ua_dump(detail); + + + /* Dump all invite sessions: */ + PJ_LOG(3,(THIS_FILE, "Dumping invite sessions:")); + + if (pjsua_call_get_count() == 0) { + + PJ_LOG(3,(THIS_FILE, " - no sessions -")); + + } else { + unsigned i, max; + + max = pjsua_call_get_max_count(); + for (i=0; iptr, + pjsip_uri_get_scheme(uri)->slen); + PyTuple_SetItem(ret, 0, item); + + /* Username */ + item = PyString_FromStringAndSize(sip_uri->user.ptr, sip_uri->user.slen); + PyTuple_SetItem(ret, 1, item); + + /* Host */ + item = PyString_FromStringAndSize(sip_uri->host.ptr, sip_uri->host.slen); + 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_FromStringAndSize(sip_uri->transport_param.ptr, + sip_uri->transport_param.slen); + PyTuple_SetItem(ret, 4, item); + + 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 + }, + { + "pool_create", py_pjsua_pool_create, METH_VARARGS, + pjsua_pool_create_doc + }, + { + "get_pjsip_endpt", py_pjsua_get_pjsip_endpt, METH_VARARGS, + pjsua_get_pjsip_endpt_doc + }, + { + "get_pjmedia_endpt", py_pjsua_get_pjmedia_endpt, METH_VARARGS, + pjsua_get_pjmedia_endpt_doc + }, + { + "get_pool_factory", py_pjsua_get_pool_factory, METH_VARARGS, + pjsua_get_pool_factory_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_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 + }, + { + "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_find_for_outgoing", py_pjsua_acc_find_for_outgoing, METH_VARARGS, + pjsua_acc_find_for_outgoing_doc + }, + { + "acc_find_for_incoming", py_pjsua_acc_find_for_incoming, METH_VARARGS, + pjsua_acc_find_for_incoming_doc + }, + { + "acc_create_uac_contact", py_pjsua_acc_create_uac_contact, METH_VARARGS, + pjsua_acc_create_uac_contact_doc + }, + { + "acc_create_uas_contact", py_pjsua_acc_create_uas_contact, METH_VARARGS, + pjsua_acc_create_uas_contact_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_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_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_add_port", py_pjsua_conf_add_port, METH_VARARGS, + pjsua_conf_add_port_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 + }, + { + "player_create", py_pjsua_player_create, METH_VARARGS, + pjsua_player_create_doc + }, + { + "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_no_snd_dev", py_pjsua_set_no_snd_dev, METH_VARARGS, + pjsua_set_no_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_event.tp_new = PyType_GenericNew; + if (PyType_Ready(&PyTyp_pjsip_event) < 0) + return; + PyTyp_pjsip_rx_data.tp_new = PyType_GenericNew; + if (PyType_Ready(&PyTyp_pjsip_rx_data) < 0) + return; + PyTyp_pj_pool_t.tp_new = PyType_GenericNew; + if (PyType_Ready(&PyTyp_pj_pool_t) < 0) + return; + PyTyp_pjsip_endpoint.tp_new = PyType_GenericNew; + if (PyType_Ready(&PyTyp_pjsip_endpoint) < 0) + return; + PyTyp_pjmedia_endpt.tp_new = PyType_GenericNew; + if (PyType_Ready(&PyTyp_pjmedia_endpt) < 0) + return; + PyTyp_pj_pool_factory.tp_new = PyType_GenericNew; + if (PyType_Ready(&PyTyp_pj_pool_factory) < 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; + + PyTyp_pjmedia_port.tp_new = PyType_GenericNew; + if (PyType_Ready(&PyTyp_pjmedia_port) < 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 */ + + PyTyp_pj_time_val.tp_new = PyType_GenericNew; + if (PyType_Ready(&PyTyp_pj_time_val) < 0) + return; + + 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_event); + PyModule_AddObject(m, "Pjsip_Event", (PyObject *)&PyTyp_pjsip_event); + + Py_INCREF(&PyTyp_pjsip_rx_data); + PyModule_AddObject(m, "Pjsip_Rx_Data", (PyObject *)&PyTyp_pjsip_rx_data); + + Py_INCREF(&PyTyp_pj_pool_t); + PyModule_AddObject(m, "Pj_Pool", (PyObject *)&PyTyp_pj_pool_t); + + Py_INCREF(&PyTyp_pjsip_endpoint); + PyModule_AddObject(m, "Pjsip_Endpoint", (PyObject *)&PyTyp_pjsip_endpoint); + + Py_INCREF(&PyTyp_pjmedia_endpt); + PyModule_AddObject(m, "Pjmedia_Endpt", (PyObject *)&PyTyp_pjmedia_endpt); + + Py_INCREF(&PyTyp_pj_pool_factory); + PyModule_AddObject( + m, "Pj_Pool_Factory", (PyObject *)&PyTyp_pj_pool_factory + ); + + 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_port); + PyModule_AddObject(m, "PJMedia_Port", (PyObject *)&PyTyp_pjmedia_port); + 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_pj_time_val); + PyModule_AddObject(m, "PJ_Time_Val", (PyObject *)&PyTyp_pj_time_val); + + Py_INCREF(&PyTyp_pjsua_call_info); + PyModule_AddObject(m, "Call_Info", (PyObject *)&PyTyp_pjsua_call_info); + + /* END OF LIB CALL */ + + + /* + * Add various constants. + */ + + /* Call states */ + ADD_CONSTANT(m, PJSIP_INV_STATE_NULL); + ADD_CONSTANT(m, PJSIP_INV_STATE_CALLING); + ADD_CONSTANT(m, PJSIP_INV_STATE_INCOMING); + ADD_CONSTANT(m, PJSIP_INV_STATE_EARLY); + ADD_CONSTANT(m, PJSIP_INV_STATE_CONNECTING); + ADD_CONSTANT(m, PJSIP_INV_STATE_CONFIRMED); + ADD_CONSTANT(m, PJSIP_INV_STATE_DISCONNECTED); + + /* Call media status (enum pjsua_call_media_status) */ + ADD_CONSTANT(m, PJSUA_CALL_MEDIA_NONE); + ADD_CONSTANT(m, PJSUA_CALL_MEDIA_ACTIVE); + ADD_CONSTANT(m, PJSUA_CALL_MEDIA_LOCAL_HOLD); + ADD_CONSTANT(m, PJSUA_CALL_MEDIA_REMOTE_HOLD); + + /* Buddy status */ + ADD_CONSTANT(m, PJSUA_BUDDY_STATUS_UNKNOWN); + ADD_CONSTANT(m, PJSUA_BUDDY_STATUS_ONLINE); + ADD_CONSTANT(m, PJSUA_BUDDY_STATUS_OFFLINE); + + /* PJSIP transport types (enum pjsip_transport_type_e) */ + ADD_CONSTANT(m, PJSIP_TRANSPORT_UNSPECIFIED); + ADD_CONSTANT(m, PJSIP_TRANSPORT_UDP); + ADD_CONSTANT(m, PJSIP_TRANSPORT_TCP); + ADD_CONSTANT(m, PJSIP_TRANSPORT_TLS); + ADD_CONSTANT(m, PJSIP_TRANSPORT_SCTP); + ADD_CONSTANT(m, PJSIP_TRANSPORT_LOOP); + ADD_CONSTANT(m, PJSIP_TRANSPORT_LOOP_DGRAM); + + + /* Invalid IDs */ + ADD_CONSTANT(m, PJSUA_INVALID_ID); + + + /* Various compile time constants */ + ADD_CONSTANT(m, PJSUA_ACC_MAX_PROXIES); + ADD_CONSTANT(m, PJSUA_MAX_ACC); + ADD_CONSTANT(m, PJSUA_REG_INTERVAL); + ADD_CONSTANT(m, PJSUA_PUBLISH_EXPIRATION); + ADD_CONSTANT(m, PJSUA_DEFAULT_ACC_PRIORITY); + ADD_CONSTANT(m, PJSUA_MAX_BUDDIES); + ADD_CONSTANT(m, PJSUA_MAX_CONF_PORTS); + ADD_CONSTANT(m, PJSUA_DEFAULT_CLOCK_RATE); + ADD_CONSTANT(m, PJSUA_DEFAULT_CODEC_QUALITY); + ADD_CONSTANT(m, PJSUA_DEFAULT_ILBC_MODE); + ADD_CONSTANT(m, PJSUA_DEFAULT_EC_TAIL_LEN); + ADD_CONSTANT(m, PJSUA_MAX_CALLS); + ADD_CONSTANT(m, PJSUA_XFER_NO_REQUIRE_REPLACES); + + +#undef ADD_CONSTANT +} diff --git a/pjsip-apps/src/python/_pjsua.def b/pjsip-apps/src/python/_pjsua.def new file mode 100644 index 000000000..9b150a963 --- /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 000000000..269c30dff --- /dev/null +++ b/pjsip-apps/src/python/_pjsua.h @@ -0,0 +1,2945 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2008 Benny Prijono + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PY_PJSUA_H__ +#define __PY_PJSUA_H__ + +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include +#include + + +PJ_INLINE(pj_str_t) PyString_to_pj_str(const PyObject *obj) +{ + pj_str_t str; + + if (obj) { + str.ptr = PyString_AS_STRING(obj); + str.slen = PyString_GET_SIZE(obj); + } else { + str.ptr = NULL; + str.slen = 0; + } + + return str; +} + + +////////////////////////////////////////////////////////////////////////////// +/* + * PyObj_pj_pool + */ +typedef struct +{ + PyObject_HEAD + /* Type-specific fields go here. */ + pj_pool_t * pool; +} PyObj_pj_pool; + + +/* + * PyTyp_pj_pool_t + */ +static PyTypeObject PyTyp_pj_pool_t = +{ + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_pjsua.Pj_Pool", /*tp_name*/ + sizeof(PyObj_pj_pool), /*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*/ + "pj_pool_t objects", /* tp_doc */ + +}; + + +////////////////////////////////////////////////////////////////////////////// +/* + * PyObj_pjsip_endpoint + */ +typedef struct +{ + PyObject_HEAD + /* Type-specific fields go here. */ + pjsip_endpoint * endpt; +} PyObj_pjsip_endpoint; + + +/* + * PyTyp_pjsip_endpoint + */ +static PyTypeObject PyTyp_pjsip_endpoint = +{ + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_pjsua.Pjsip_Endpoint", /*tp_name*/ + sizeof(PyObj_pjsip_endpoint),/*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*/ + "pjsip_endpoint objects", /* tp_doc */ +}; + + +/* + * PyObj_pjmedia_endpt + */ +typedef struct +{ + PyObject_HEAD + /* Type-specific fields go here. */ + pjmedia_endpt * endpt; +} PyObj_pjmedia_endpt; + + +////////////////////////////////////////////////////////////////////////////// +/* + * PyTyp_pjmedia_endpt + */ +static PyTypeObject PyTyp_pjmedia_endpt = +{ + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_pjsua.Pjmedia_Endpt", /*tp_name*/ + sizeof(PyObj_pjmedia_endpt), /*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_endpt objects", /* tp_doc */ + +}; + + +////////////////////////////////////////////////////////////////////////////// +/* + * PyObj_pj_pool_factory + */ +typedef struct +{ + PyObject_HEAD + /* Type-specific fields go here. */ + pj_pool_factory * pool_fact; +} PyObj_pj_pool_factory; + + + +/* + * PyTyp_pj_pool_factory + */ +static PyTypeObject PyTyp_pj_pool_factory = +{ + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_pjsua.Pj_Pool_Factory",/*tp_name*/ + sizeof(PyObj_pj_pool_factory), /*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*/ + "pj_pool_factory objects", /* tp_doc */ + +}; + + +////////////////////////////////////////////////////////////////////////////// +/* + * 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_FromStringAndSize(cfg->realm.ptr, cfg->realm.slen); + Py_XDECREF(obj->scheme); + obj->scheme = PyString_FromStringAndSize(cfg->scheme.ptr, cfg->scheme.slen); + Py_XDECREF(obj->username); + obj->username = PyString_FromStringAndSize(cfg->username.ptr, cfg->username.slen); + obj->data_type = cfg->data_type; + Py_XDECREF(obj->data); + obj->data = PyString_FromStringAndSize(cfg->data.ptr, cfg->data.slen); +} + +static void PyObj_pjsip_cred_info_export(pjsip_cred_info *cfg, + PyObj_pjsip_cred_info *obj) +{ + cfg->realm = PyString_to_pj_str(obj->realm); + cfg->scheme = PyString_to_pj_str(obj->scheme); + cfg->username = PyString_to_pj_str(obj->username); + cfg->data_type = obj->data_type; + cfg->data = PyString_to_pj_str(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(""); + if (self->realm == NULL) + { + Py_DECREF(self); + return NULL; + } + self->scheme = PyString_FromString(""); + if (self->scheme == NULL) + { + Py_DECREF(self); + return NULL; + } + self->username = PyString_FromString(""); + if (self->username == NULL) + { + Py_DECREF(self); + return NULL; + } + self->data_type = PJSIP_CRED_DATA_PLAIN_PASSWD; + self->data = PyString_FromString(""); + if (self->data == NULL) + { + Py_DECREF(self); + return NULL; + } + } + + 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_pjsip_event + * C/python typewrapper for event struct + */ +typedef struct +{ + PyObject_HEAD + /* Type-specific fields go here. */ + pjsip_event * event; +} PyObj_pjsip_event; + + + +/* + * PyTyp_pjsip_event + * event struct signatures + */ +static PyTypeObject PyTyp_pjsip_event = +{ + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_pjsua.Pjsip_Event", /*tp_name*/ + sizeof(PyObj_pjsip_event), /*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*/ + "pjsip_event object", /*tp_doc */ +}; + + +////////////////////////////////////////////////////////////////////////////// +/* + * PyObj_pjsip_rx_data + * C/python typewrapper for pjsip_rx_data struct + */ +typedef struct +{ + PyObject_HEAD + /* Type-specific fields go here. */ + pjsip_rx_data * rdata; +} PyObj_pjsip_rx_data; + + +/* + * PyTyp_pjsip_rx_data + */ +static PyTypeObject PyTyp_pjsip_rx_data = +{ + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_pjsua.Pjsip_Rx_Data", /*tp_name*/ + sizeof(PyObj_pjsip_rx_data), /*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*/ + "pjsip_rx_data object", /*tp_doc*/ +}; + + + +////////////////////////////////////////////////////////////////////////////// +/* + * 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_buddy_state; + PyObject * on_pager; + PyObject * on_pager_status; + PyObject * on_typing; +} 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_buddy_state); + Py_XDECREF(self->on_pager); + Py_XDECREF(self->on_pager_status); + Py_XDECREF(self->on_typing); + 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) + { + Py_INCREF(Py_None); + self->on_call_state = Py_None; + if (self->on_call_state == NULL) + { + Py_DECREF(Py_None); + return NULL; + } + Py_INCREF(Py_None); + self->on_incoming_call = Py_None; + if (self->on_incoming_call == NULL) + { + Py_DECREF(Py_None); + return NULL; + } + Py_INCREF(Py_None); + self->on_call_media_state = Py_None; + if (self->on_call_media_state == NULL) + { + Py_DECREF(Py_None); + return NULL; + } + Py_INCREF(Py_None); + self->on_dtmf_digit = Py_None; + if (self->on_dtmf_digit == NULL) + { + Py_DECREF(Py_None); + return NULL; + } + Py_INCREF(Py_None); + self->on_call_transfer_request = Py_None; + if (self->on_call_transfer_request == NULL) + { + Py_DECREF(Py_None); + return NULL; + } + Py_INCREF(Py_None); + self->on_call_transfer_status = Py_None; + if (self->on_call_transfer_status == NULL) + { + Py_DECREF(Py_None); + return NULL; + } + Py_INCREF(Py_None); + self->on_call_replace_request = Py_None; + if (self->on_call_replace_request == NULL) + { + Py_DECREF(Py_None); + return NULL; + } + Py_INCREF(Py_None); + self->on_call_replaced = Py_None; + if (self->on_call_replaced == NULL) + { + Py_DECREF(Py_None); + return NULL; + } + Py_INCREF(Py_None); + self->on_reg_state = Py_None; + if (self->on_reg_state == NULL) + { + Py_DECREF(Py_None); + return NULL; + } + Py_INCREF(Py_None); + self->on_buddy_state = Py_None; + if (self->on_buddy_state == NULL) + { + Py_DECREF(Py_None); + return NULL; + } + Py_INCREF(Py_None); + self->on_pager = Py_None; + if (self->on_pager == NULL) + { + Py_DECREF(Py_None); + return NULL; + } + Py_INCREF(Py_None); + self->on_pager_status = Py_None; + if (self->on_pager_status == NULL) + { + Py_DECREF(Py_None); + return NULL; + } + Py_INCREF(Py_None); + self->on_typing = Py_None; + if (self->on_typing == NULL) + { + Py_DECREF(Py_None); + return NULL; + } + } + + 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_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." + }, + {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_passw", 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_FromStringAndSize(cfg->turn_server.ptr, + cfg->turn_server.slen); + 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_FromStringAndSize(cred->data.static_cred.realm.ptr, + cred->data.static_cred.realm.slen); + Py_XDECREF(obj->turn_username); + obj->turn_username = PyString_FromStringAndSize(cred->data.static_cred.username.ptr, + cred->data.static_cred.username.slen); + obj->turn_passwd_type = cred->data.static_cred.data_type; + Py_XDECREF(obj->turn_passwd); + obj->turn_passwd = PyString_FromStringAndSize(cred->data.static_cred.data.ptr, + cred->data.static_cred.data.slen); + } 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_to_pj_str(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_to_pj_str(obj->turn_realm); + cfg->turn_auth_cred.data.static_cred.username = PyString_to_pj_str(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_to_pj_str(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); + obj->outbound_proxy = PyString_FromStringAndSize(cfg->outbound_proxy[0].ptr, + cfg->outbound_proxy[0].slen); + Py_XDECREF(obj->stun_domain); + obj->stun_domain = PyString_FromStringAndSize(cfg->stun_domain.ptr, + cfg->stun_domain.slen); + Py_XDECREF(obj->stun_host); + obj->stun_host = PyString_FromStringAndSize(cfg->stun_host.ptr, + cfg->stun_host.slen); + Py_XDECREF(obj->nameserver); + obj->nameserver = (PyListObject *)PyList_New(0); + for (i=0; inameserver_count; ++i) { + PyObject * str; + str = PyString_FromStringAndSize(cfg->nameserver[i].ptr, + cfg->nameserver[i].slen); + PyList_Append((PyObject *)obj->nameserver, str); + } + Py_XDECREF(obj->user_agent); + obj->user_agent = PyString_FromStringAndSize(cfg->user_agent.ptr, + cfg->user_agent.slen); +} + + +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_to_pj_str(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++) { + cfg->nameserver[i] = PyString_to_pj_str(PyList_GetItem((PyObject *)obj->nameserver,i)); + } + cfg->stun_domain = PyString_to_pj_str(obj->stun_domain); + cfg->stun_host = PyString_to_pj_str(obj->stun_host); + cfg->user_agent = PyString_to_pj_str(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(""); + if (self->user_agent == NULL) + { + Py_DECREF(self); + return NULL; + } + self->outbound_proxy = PyString_FromString(""); + if (self->outbound_proxy == NULL) + { + Py_DECREF(self); + return NULL; + } + self->cb = (PyObj_pjsua_callback *) + PyType_GenericNew(&PyTyp_pjsua_callback, NULL, NULL); + if (self->cb == NULL) + { + Py_DECREF(Py_None); + return 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; +} + +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_to_pj_str(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(""); + if (self->log_filename == NULL) + { + Py_DECREF(self); + return NULL; + } + Py_INCREF(Py_None); + self->cb = Py_None; + if (self->cb == NULL) + { + Py_DECREF(Py_None); + return NULL; + } + } + + 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) + { + Py_INCREF(Py_None); + self->hdr_list = Py_None; + if (self->hdr_list == NULL) { + Py_DECREF(self); + return NULL; + } + self->content_type = PyString_FromString(""); + if (self->content_type == NULL) { + Py_DECREF(self); + return NULL; + } + self->msg_body = PyString_FromString(""); + if (self->msg_body == NULL) { + Py_DECREF(self); + return NULL; + } + } + + 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) +{ + cfg->public_addr = PyString_to_pj_str(obj->public_addr); + cfg->bound_addr = PyString_to_pj_str(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_FromStringAndSize(cfg->public_addr.ptr, + cfg->public_addr.slen); + + Py_XDECREF(obj->bound_addr); + obj->bound_addr = PyString_FromStringAndSize(cfg->bound_addr.ptr, + cfg->bound_addr.slen); + + 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(""); + if (self->public_addr == NULL) { + Py_DECREF(self); + return NULL; + } + self->bound_addr = PyString_FromString(""); + if (self->bound_addr == NULL) { + Py_DECREF(self); + return NULL; + } + } + + 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_FromStringAndSize(info->type_name.ptr, + info->type_name.slen); + obj->info = PyString_FromStringAndSize(info->info.ptr, + info->info.slen); + obj->flag = info->flag; + obj->addr = PyString_FromStringAndSize(info->local_name.host.ptr, + info->local_name.host.slen); + 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(""); + if (self->type_name == NULL) { + Py_DECREF(self); + return NULL; + } + self->info = PyString_FromString(""); + if (self->info == NULL) { + Py_DECREF(self); + return NULL; + } + self->addr = PyString_FromString(""); + if (self->addr == NULL) { + Py_DECREF(self); + return NULL; + } + } + + 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, id), 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; + PyObject *force_contact; + /*pj_str_t proxy[8];*/ + PyListObject *proxy; + unsigned reg_timeout; + /*pjsip_cred_info cred_info[8];*/ + PyListObject *cred_info; + int transport_id; + + int auth_initial_send; + PyObject *auth_initial_algorithm; + PyObject *pidf_tuple_id; + int require_100rel; + 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->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_FromStringAndSize(cfg->id.ptr, cfg->id.slen); + Py_XDECREF(obj->reg_uri); + obj->reg_uri = PyString_FromStringAndSize(cfg->reg_uri.ptr, + cfg->reg_uri.slen); + obj->publish_enabled = cfg->publish_enabled; + Py_XDECREF(obj->force_contact); + obj->force_contact = PyString_FromStringAndSize(cfg->force_contact.ptr, + cfg->force_contact.slen); + Py_XDECREF(obj->proxy); + obj->proxy = (PyListObject *)PyList_New(0); + for (i=0; iproxy_cnt; ++i) { + PyObject * str; + str = PyString_FromStringAndSize(cfg->proxy[i].ptr, + cfg->proxy[i].slen); + PyList_Append((PyObject *)obj->proxy, str); + } + + obj->reg_timeout = cfg->reg_timeout; + + Py_XDECREF(obj->cred_info); + obj->cred_info = (PyListObject *)PyList_New(0); + for (i=0; icred_count; ++i) { + PyObj_pjsip_cred_info * ci; + + ci = (PyObj_pjsip_cred_info *) + PyObj_pjsip_cred_info_new(&PyTyp_pjsip_cred_info,NULL,NULL); + PyObj_pjsip_cred_info_import(ci, &cfg->cred_info[i]); + PyList_Append((PyObject *)obj->cred_info, (PyObject *)ci); + } + + obj->transport_id = cfg->transport_id; + + obj->auth_initial_send = cfg->auth_pref.initial_auth; + Py_XDECREF(obj->auth_initial_algorithm); + obj->auth_initial_algorithm = PyString_FromStringAndSize(cfg->auth_pref.algorithm.ptr, + cfg->auth_pref.algorithm.slen); + Py_XDECREF(obj->pidf_tuple_id); + obj->pidf_tuple_id = PyString_FromStringAndSize(cfg->pidf_tuple_id.ptr, + cfg->pidf_tuple_id.slen); + obj->require_100rel = cfg->require_100rel; + obj->allow_contact_rewrite = cfg->allow_contact_rewrite; + obj->ka_interval = cfg->ka_interval; + Py_XDECREF(obj->ka_data); + obj->ka_data = PyString_FromStringAndSize(cfg->ka_data.ptr, cfg->ka_data.slen); + 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_to_pj_str(obj->id); + cfg->reg_uri = PyString_to_pj_str(obj->reg_uri); + cfg->publish_enabled = obj->publish_enabled; + cfg->force_contact = PyString_to_pj_str(obj->force_contact); + + cfg->proxy_cnt = PyList_Size((PyObject*)obj->proxy); + for (i = 0; i < cfg->proxy_cnt; i++) { + /*cfg.proxy[i] = ac->proxy[i];*/ + cfg->proxy[i] = PyString_to_pj_str(PyList_GetItem((PyObject *)obj->proxy,i)); + } + + cfg->reg_timeout = obj->reg_timeout; + + cfg->cred_count = PyList_Size((PyObject*)obj->cred_info); + for (i = 0; i < cfg->cred_count; i++) { + /*cfg.cred_info[i] = ac->cred_info[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_to_pj_str(obj->auth_initial_algorithm); + cfg->pidf_tuple_id = PyString_to_pj_str(obj->pidf_tuple_id); + cfg->require_100rel = obj->require_100rel; + cfg->allow_contact_rewrite = obj->allow_contact_rewrite; + cfg->ka_interval = obj->ka_interval; + cfg->ka_data = PyString_to_pj_str(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(""); + if (self->id == NULL) { + Py_DECREF(self); + return NULL; + } + self->reg_uri = PyString_FromString(""); + if (self->reg_uri == NULL) { + Py_DECREF(self); + return NULL; + } + self->force_contact = PyString_FromString(""); + if (self->force_contact == NULL) { + Py_DECREF(self); + return NULL; + } + self->proxy = (PyListObject *)PyList_New(0); + if (self->proxy == NULL) { + Py_DECREF(self); + return NULL; + } + self->cred_info = (PyListObject *)PyList_New(0); + if (self->cred_info == NULL) { + Py_DECREF(self); + return NULL; + } + self->auth_initial_algorithm = PyString_FromString(""); + self->pidf_tuple_id = 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? " + }, + { + "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). " + }, + { + "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." + }, + { + "require_100rel", T_INT, + offsetof(PyObj_pjsua_acc_config, require_100rel), 0, + "Require reliable provisional response." + }, + { + "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*/ + "Acc Config objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0/*acc_config_methods*/, /* 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; + obj->acc_uri = PyString_FromStringAndSize(info->acc_uri.ptr, + info->acc_uri.slen); + obj->has_registration = info->has_registration; + obj->expires = info->expires; + obj->status = info->status; + obj->status_text= PyString_FromStringAndSize(info->status_text.ptr, + info->status_text.slen); + obj->online_status = info->online_status; + obj->online_status_text = PyString_FromStringAndSize(info->online_status_text.ptr, + info->online_status_text.slen); +} + + +/* + * 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(""); + if (self->acc_uri == NULL) { + Py_DECREF(self); + return NULL; + } + self->status_text = PyString_FromString(""); + if (self->status_text == NULL) { + Py_DECREF(self); + return NULL; + } + self->online_status_text = PyString_FromString(""); + if (self->online_status_text == NULL) { + Py_DECREF(self); + return NULL; + } + } + + 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*/ + "Acc Info objects", /* 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_FromStringAndSize(cfg->uri.ptr, cfg->uri.slen); + obj->subscribe = cfg->subscribe; +} + + +static void PyObj_pjsua_buddy_config_export(pjsua_buddy_config *cfg, + PyObj_pjsua_buddy_config *obj) +{ + cfg->uri = PyString_to_pj_str(obj->uri); + cfg->subscribe = obj->subscribe; +} + + + +/* + * 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(""); + if (self->uri == NULL) { + Py_DECREF(self); + return NULL; + } + } + 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 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_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; +} 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); + + 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_FromStringAndSize(info->uri.ptr, info->uri.slen); + Py_XDECREF(obj->contact); + obj->contact = PyString_FromStringAndSize(info->contact.ptr, info->contact.slen); + obj->status = info->status; + Py_XDECREF(obj->status_text); + obj->status_text = PyString_FromStringAndSize(info->status_text.ptr, + info->status_text.slen); + obj->monitor_pres = info->monitor_pres; + obj->activity = info->rpid.activity; +} + + +/* + * 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(""); + if (self->uri == NULL) { + Py_DECREF(self); + return NULL; + } + self->contact = PyString_FromString(""); + if (self->contact == NULL) { + Py_DECREF(self); + return NULL; + } + self->status_text = PyString_FromString(""); + if (self->status_text == NULL) { + Py_DECREF(self); + return NULL; + } + + } + 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. " + }, + + + {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 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_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 */ + +}; + + + + + +#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 000000000..b4acce615 --- /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 000000000..c12d477ff --- /dev/null +++ b/pjsip-apps/src/python/pjsua.py @@ -0,0 +1,2099 @@ +# $Id:$ +# +# Object oriented PJSUA wrapper. +# +# Copyright (C) 2003-2008 Benny Prijono +# + +"""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) + + +FEATURES + + - Session Initiation Protocol (SIP: + - Basic registration and call + - Multiple accounts + - Call hold, attended and unattended call transfer + - Presence + - Instant messaging + - Media stack: + - Audio + - Conferencing + - Narrowband and wideband + - Codecs: PCMA, PCMU, GSM, iLBC, Speex, G.722, L16 + - RTP/RTCP + - Secure RTP (SRTP + - NAT traversal features + - Symmetric RTP + - STUN + - TURN + - ICE + + +""" +import _pjsua +import thread + +class Error: + "Error exception class" + 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" + UNSPECIFIED = 0 + UDP = 1 + TCP = 2 + TLS = 3 + IPV6 = 128 + UDP_IPV6 = UDP + IPV6 + TCP_IPV6 = TCP + IPV6 + +class TransportFlag: + "Transport flags" + RELIABLE = 1 + SECURE = 2 + DATAGRAM = 4 + +class CallRole: + "Call role constants" + CALLER = 0 + CALLEE = 1 + +class CallState: + "Call state constants" + NULL = 0 + CALLING = 1 + INCOMING = 2 + EARLY = 3 + CONNECTING = 4 + CONFIRMED = 5 + DISCONNECTED = 6 + + +class MediaState: + "Call media state constants" + NONE = 0 + ACTIVE = 1 + LOCAL_HOLD = 2 + REMOTE_HOLD = 3 + ERROR = 4 + + +class MediaDir: + "Media direction constants" + NONE = 0 + ENCODING = 1 + DECODING = 2 + ENCODING_DECODING = 3 + + +class PresenceActivity: + "Presence activities constants" + UNKNOWN = 0 + AWAY = 1 + BUSY = 2 + +class TURNConnType: + "TURN connection type constants" + UDP = 17 + TCP = 6 + TLS = 255 + + +class UAConfig: + "User agent configuration class" + 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 class." + 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 class." + 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." + port = 0 + bound_addr = "" + public_addr = "" + + def __init__(self, port=5060, + 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. + """ + 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 = lib + self._id = id + self._obj_name = "Transport " + self.info().description + + def __str__(self): + return self._obj_name + + def info(self): + """Get TransportInfo. + """ + 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): + err = _pjsua.transport_set_enable(self._id, True) + self._lib._err_check("enable()", self, err) + + def disable(self): + err = _pjsua.transport_set_enable(self._id, 0) + self._lib._err_check("disable()", self, err) + + def close(self, force=False): + err = _pjsua.transport_close(self._id, force) + self._lib._err_check("close()", self, err) + + +class SIPUri: + scheme = "" + user = "" + host = "" + port = 0 + transport = "" + + def __init__(self, uri): + self.decode(uri) + + def decode(self, uri): + self.scheme, self.user, self.host, self.port, self.transport = \ + _pjsua.parse_simple_uri(uri) + + def encode(self): + 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." + 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. + """ + priority = 0 + id = "" + force_contact = "" + reg_uri = "" + reg_timeout = 0 + require_100rel = False + publish_enabled = False + pidf_tuple_id = "" + proxy = [] + auth_cred = [] + auth_initial_send = False + auth_initial_algorithm = "" + transport_id = -1 + allow_contact_rewrite = True + ka_interval = 15 + ka_data = "\r\n" + use_srtp = 0 + srtp_secure_signaling = 1 + + def __init__(self, domain="", username="", password="", + display="", registrar="", proxy=""): + """ + Construct account config. If domain argument is specified, + a typical configuration will be built. + + Keyword arguments: + domain -- domain name of the server. + username -- user name. + password -- plain-text password. + display -- optional display name for the user name. + registrar -- the registrar URI. If domain name is specified + and this argument is empty, the registrar URI + will be constructed from the domain name. + proxy -- the proxy URI. If domain name is specified + and this argument is empty, the proxy URI + will be constructed from the domain name. + + """ + default = _pjsua.acc_config_default() + self._cvt_from_pjsua(default) + if domain!="": + self.build_config(domain, username, password, + display, registrar, proxy) + + def build_config(self, domain, username, password, display="", + registrar="", proxy=""): + """ + Construct account config. If domain argument is specified, + a typical configuration will be built. + + Keyword arguments: + domain -- domain name of the server. + username -- user name. + password -- plain-text password. + display -- optional display name for the user name. + registrar -- the registrar URI. If domain name is specified + and this argument is empty, the registrar URI + will be constructed from the domain name. + proxy -- the proxy URI. If domain name is specified + and this argument is empty, the proxy URI + will be constructed from the domain name. + + """ + if display != "": + display = display + " " + userpart = username + if userpart != "": + userpart = userpart + "@" + self.id = display + "" + self.reg_uri = registrar + if self.reg_uri == "": + self.reg_uri = "sip:" + domain + if proxy == "": + proxy = "sip:" + domain + ";lr" + if proxy.find(";lr") == -1: + proxy = proxy + ";lr" + self.proxy.append(proxy) + if username != "": + self.auth_cred.append(AuthCred("*", username, password)) + + def _cvt_from_pjsua(self, cfg): + self.priority = cfg.priority + self.id = cfg.id + self.force_contact = cfg.force_contact + self.reg_uri = cfg.reg_uri + self.reg_timeout = cfg.reg_timeout + self.require_100rel = cfg.require_100rel + self.publish_enabled = cfg.publish_enabled + self.pidf_tuple_id = cfg.pidf_tuple_id + self.proxy = cfg.proxy + for cred in cfg.cred_info: + self.auth_cred.append(AuthCred(cred.realm, cred.username, + cred.data, cred.scheme, + cred.data_type)) + self.auth_initial_send = cfg.auth_initial_send + self.auth_initial_algorithm = cfg.auth_initial_algorithm + self.transport_id = cfg.transport_id + self.allow_contact_rewrite = cfg.allow_contact_rewrite + self.ka_interval = cfg.ka_interval + self.ka_data = cfg.ka_data + self.use_srtp = cfg.use_srtp + self.srtp_secure_signaling = cfg.srtp_secure_signaling + + def _cvt_to_pjsua(self): + cfg = _pjsua.acc_config_default() + cfg.priority = self.priority + cfg.id = self.id + cfg.force_contact = self.force_contact + cfg.reg_uri = self.reg_uri + cfg.reg_timeout = self.reg_timeout + cfg.require_100rel = self.require_100rel + cfg.publish_enabled = self.publish_enabled + cfg.pidf_tuple_id = self.pidf_tuple_id + cfg.proxy = self.proxy + for cred in self.auth_cred: + c = _pjsua.Pjsip_Cred_Info() + c.realm = cred.realm + c.scheme = cred.scheme + c.username = cred.username + c.data_type = cred.passwd_type + c.data = cred.passwd + cfg.cred_info.append(c) + cfg.auth_initial_send = self.auth_initial_send + cfg.auth_initial_algorithm = self.auth_initial_algorithm + cfg.transport_id = self.transport_id + cfg.allow_contact_rewrite = self.allow_contact_rewrite + cfg.ka_interval = self.ka_interval + cfg.ka_data = self.ka_data + cfg.use_srtp = self.use_srtp + cfg.srtp_secure_signaling = self.srtp_secure_signaling + return cfg + + +# Account information +class AccountInfo: + """This describes Account info. Application retrives account info + with Account.info(). + + """ + 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. + """ + account = None + + def __init__(self, account): + self.account = account + + 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_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 + + + +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): + """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(self) + self._id = id + self._lib = lib + self._lib._associate_account(self._id, self) + self._obj_name = "Account " + self.info().uri + + def __del__(self): + self._lib._disassociate_account(self._id, self) + + def __str__(self): + return self._obj_name + + def info(self): + """Retrieve AccountInfo for this account. + """ + 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. + + """ + 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) + + 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. + """ + 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. + + """ + def_id = _pjsua.acc_get_default() + return self.is_valid() and def_id==self._id + + def delete(self): + """ Delete this account. + + """ + err = _pjsua.acc_del(self._id) + self._lib._err_check("delete()", self, err) + + def set_basic_status(self, is_online): + """ Set basic presence status of this account. + + Keyword argument: + is_online -- boolean to indicate basic presence availability. + + """ + 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. + + """ + 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. + + """ + err = _pjsua.acc_set_registration(self._id, renew) + self._lib._err_check("set_registration()", self, err) + + def has_registration(self): + """Returns True if registration is active for this account. + + """ + acc_info = _pjsua.acc_get_info(self._id) + if not acc_info: + self._lib._err_check("has_registration()", self, -1, + "invalid account") + return acc_info.has_registration + + def set_transport(self, transport): + """Set this account to only use the specified transport to send + outgoing requests. + + Keyword argument: + transport -- Transport object. + + """ + err = _pjsua.acc_set_transport(self._id, transport._id) + self._lib._err_check("set_transport()", self, err) + + def make_call(self, dst_uri, hdr_list=None): + """Make outgoing call to the specified URI. + + Keyword arguments: + dst_uri -- Destination SIP URI. + hdr_list -- Optional list of headers to be sent with outgoing + INVITE + + """ + err, cid = _pjsua.call_make_call(self._id, dst_uri, 0, + 0, Lib._create_msg_data(hdr_list)) + self._lib._err_check("make_call()", self, err) + return Call(self._lib, cid) + + def add_buddy(self, uri): + """Add new buddy. + + Keyword argument: + uri -- SIP URI of the buddy + + Return: + Buddy object + """ + 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) + return Buddy(self._lib, buddy_id, self) + + +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. + """ + call = None + + def __init__(self, call): + self.call = call + + 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(). + """ + 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.NONE + media_dir = MediaDir.NONE + 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.sec + self.total_time = ci.total_duration.sec + + +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): + self._cb = CallCallback(self) + self._id = call_id + self._lib = lib + self._lib._associate_call(call_id, self) + self._obj_name = "Call " + self.info().remote_uri + + def __del__(self): + self._lib._disassociate_call(self._id, self) + + def __str__(self): + return self._obj_name + + 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) + + def info(self): + """ + Get the CallInfo. + """ + ci = _pjsua.call_get_info(self._id) + if not ci: + self._lib._err_check("info", self, -1, "Invalid call") + return CallInfo(self._lib, ci) + + def is_valid(self): + """ + Check if this call is still valid. + """ + return _pjsua.call_is_active(self._id) + + def dump_status(self, with_media=True, indent="", max_len=1024): + """ + Dump the call status. + """ + 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. + + """ + 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. + + """ + 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. + """ + 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. + + """ + 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. + + """ + 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. + + """ + 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. + + """ + 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. + + """ + 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. + + """ + 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. + + """ + if hdr_list and 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) + + +class BuddyInfo: + """This class contains information about Buddy. Application may + retrieve this information by calling Buddy.info(). + """ + uri = "" + contact = "" + online_status = 0 + online_text = "" + activity = PresenceActivity.UNKNOWN + subscribed = False + + 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 + + +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(). + """ + buddy = None + + def __init__(self, buddy): + self.buddy = buddy + + 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): + self._cb = BuddyCallback(self) + self._lib = lib + self._id = id + self._acc = account + lib._associate_buddy(self._id, self) + self._obj_name = "Buddy " + self.info().uri + + def __del__(self): + self._lib._disassociate_buddy(self) + + def __str__(self): + return self._obj_name + + def info(self): + """ + Get buddy info as BuddyInfo. + """ + 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) + + def subscribe(self): + """ + Subscribe to buddy's presence status notification. + """ + 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. + """ + 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. + """ + 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. + + """ + 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. + + """ + 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: + name = "" + input_channels = 0 + output_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: + 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.id + self.priority = codec_info.priority + self.clock_rate = codec_param.info.clock_rate + self.channel_count = codec_param.info.channel_count + 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.id = self.name + ci.priority = self.priority + return ci + + +# Codec parameter +class CodecParameter: + 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 + + +# PJSUA Library +_lib = None +class Lib: + """Library instance. + + """ + call = {} + account = {} + buddy = {} + buddy_by_uri = {} + buddy_by_contact = {} + _quit = False + _has_thread = False + + def __init__(self): + global _lib + if _lib: + raise Error("__init()__", None, -1, + "Library instance already exist") + + err = _pjsua.create() + self._err_check("_pjsua.create()", None, err) + _lib = self + + def __del__(self): + _pjsua.destroy() + + 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_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 + + 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: + _pjsua.handle_events(50) + loop = loop + 1 + _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. + + """ + 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. + + """ + return _pjsua.handle_events(timeout) + + 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. + + """ + 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 + + """ + if not cfg: cfg=TransportConfig(type) + 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): + """ + 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. + + Return: + Account instance + + """ + 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) + + def create_account_for_transport(self, transport, set_default=True): + """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. + + Return: + Account instance + + """ + 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) + + def hangup_all(self): + """Hangup all calls. + + """ + _pjsua.call_hangup_all() + + # Sound device API + + def enum_snd_dev(self): + """Enumerate sound devices in the system. + + Return: + array of SoundDeviceInfo. The index of the element specifies + the device ID for the device. + """ + sdi_array = _pjsua.enum_snd_devs() + info = [] + for sdi in sdi_array: + 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 + """ + 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. + + """ + 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. + + """ + 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. + + """ + 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. + + """ + 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. + + """ + err = _pjsua.conf_disconnect(src_slot, dst_slot) + self._err_check("conf_disconnect()", self, err) + + # Codecs API + + def enum_codecs(self): + """Return list of codecs supported by pjsua. + + Return: + array of CodecInfo + + """ + ci_array = _pjsua.enum_codecs() + codec_info = [] + for ci in ci_array: + cp = _pjsua.codec_get_param(ci.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. + + """ + 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. + + """ + 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. + + """ + 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 wheter playback shoudl + automatically restart + Return: + WAV player ID + + """ + 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 + + """ + slot = _pjsua.player_get_conf_port(player_id) + 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 + + """ + 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. + + """ + err = _pjsua.player_destroy(player_id) + self._err_check("player_destroy()", self, err) + + def create_recorder(self, filename): + """Create WAV file recorder. + + Keyword arguments + filename -- WAV file name + + Return: + WAV recorder ID + + """ + 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 + + """ + slot = _pjsua.recorder_get_conf_port(rec_id) + 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. + + """ + 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 + + # Internal dictionary manipulation for calls, accounts, and buddies + + def _associate_call(self, call_id, call): + self.call[call_id] = call + + def _lookup_call(self, call_id): + return self.call.has_key(call_id) and self.call[call_id] or None + + def _disassociate_call(self, call): + if self._lookup_call(call._id)==call: + del self.call[call._id] + + def _associate_account(self, acc_id, account): + self.account[acc_id] = account + + def _lookup_account(self, acc_id): + return self.account.has_key(acc_id) and self.account[acc_id] or None + + def _disassociate_account(self, account): + if self._lookup_account(account._id)==account: + del self.account[account._id] + + def _associate_buddy(self, buddy_id, buddy): + self.buddy[buddy_id] = buddy + uri = SIPUri(buddy.info().uri) + self.buddy_by_uri[(uri.user, uri.host)] = buddy + + def _lookup_buddy(self, buddy_id, uri=None): + print "lookup_buddy, id=", buddy_id + buddy = self.buddy.has_key(buddy_id) and self.buddy[buddy_id] or None + if uri and not buddy: + sip_uri = SIPUri(uri) + print "lookup_buddy, uri=", sip_uri.user, sip_uri.host + buddy = self.buddy_by_uri.has_key( (sip_uri.user, sip_uri.host) ) \ + and self.buddy_by_uri[(sip_uri.user, sip_uri.host)] or \ + None + return buddy + + def _disassociate_buddy(self, buddy): + if self._lookup_buddy(buddy._id)==buddy: + del self.buddy[buddy._id] + if self.buddy_by_uri.has_key(buddy.info().uri): + del self.buddy_by_uri[buddy.info().uri] + + # 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_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: + call._cb.on_state() + + 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_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_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) + + +# Worker thread +def _worker_thread_main(arg): + global _lib + thread_desc = 0; + err = _pjsua.thread_register("python worker", thread_desc) + _lib._err_check("thread_register()", _lib, err) + while _lib._quit == 0: + _pjsua.handle_events(50) + _lib._quit = 2 + + diff --git a/pjsip-apps/src/python/samples/call.py b/pjsip-apps/src/python/samples/call.py new file mode 100644 index 000000000..2f44f62b5 --- /dev/null +++ b/pjsip-apps/src/python/samples/call.py @@ -0,0 +1,153 @@ +# $Id:$ +# +# SIP call sample. +# +# Copyright (C) 2003-2008 Benny Prijono +# +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): + 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): + 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 + + # 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 + call = acc.make_call(uri) + call_cb = MyCallCallback(call) + call.set_callback(call_cb) + return call + except pj.Error, e: + print "Error: " + 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) + acc_cb = MyAccountCallback(acc) + acc.set_callback(acc_cb) + + # If argument is specified then make call to the URI + if len(sys.argv) > 1: + current_call = make_call(sys.argv[1]) + + 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 + current_call = make_call(input) + + 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 + 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 000000000..d7a3843d7 --- /dev/null +++ b/pjsip-apps/src/python/samples/presence.py @@ -0,0 +1,113 @@ +# $Id:$ +# +# Presence and instant messaging +# +# Copyright (C) 2003-2008 Benny Prijono +# +import sys +import pjsua as pj +import threading + +LOG_LEVEL = 3 + +def log_cb(level, str, len): + print str, + +class MyBuddyCallback(pj.BuddyCallback): + def __init__(self, buddy): + 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) + + 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, 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) + buddy.set_callback(cb) + + 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 == "q": + break + + # Shutdown the library + 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 000000000..16cae8b20 --- /dev/null +++ b/pjsip-apps/src/python/samples/registration.py @@ -0,0 +1,56 @@ +# $Id:$ +# +# SIP account and registration sample. In this sample, the program +# will block to wait until registration is complete +# +# Copyright (C) 2003-2008 Benny Prijono +# +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/setup.py b/pjsip-apps/src/python/setup.py new file mode 100644 index 000000000..8390872e0 --- /dev/null +++ b/pjsip-apps/src/python/setup.py @@ -0,0 +1,44 @@ +from distutils.core import setup, Extension +import os +import sys + +# 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 sys.platform == 'darwin': + extra_link_args = ["-framework", "CoreFoundation", + "-framework", "AudioToolbox"] +else: + extra_link_args = [] + + +setup(name="_pjsua", version="0.9", + 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), + ]) +