summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2008-07-19 15:40:21 +0000
committerBenny Prijono <bennylp@teluu.com>2008-07-19 15:40:21 +0000
commit2a67327941910feadbfd953e4f8b53e675d5fe1a (patch)
treeda3fbd97af216a62992ebfe23e3110bbad88719c
parentc2574cdf8965f1f7f77a77152c49e930af36a15b (diff)
Added WAV playlist and conf_set/get_level API to Python module
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@2158 74dad513-b988-da41-8d7b-12977e46ad98
-rw-r--r--pjsip-apps/src/python/_pjsua.c127
-rw-r--r--pjsip-apps/src/python/pjsua.py228
2 files changed, 342 insertions, 13 deletions
diff --git a/pjsip-apps/src/python/_pjsua.c b/pjsip-apps/src/python/_pjsua.c
index 6590dd5c..65a48e90 100644
--- a/pjsip-apps/src/python/_pjsua.c
+++ b/pjsip-apps/src/python/_pjsua.c
@@ -3457,6 +3457,76 @@ static PyObject *py_pjsua_conf_disconnect
}
/*
+ * py_pjsua_conf_set_tx_level
+ */
+static PyObject *py_pjsua_conf_set_tx_level
+(PyObject *pSelf, PyObject *pArgs)
+{
+ int slot;
+ float level;
+ int status;
+
+ PJ_UNUSED_ARG(pSelf);
+
+ if (!PyArg_ParseTuple(pArgs, "if", &slot, &level))
+ {
+ return NULL;
+ }
+
+ status = pjsua_conf_adjust_tx_level(slot, level);
+
+
+ return Py_BuildValue("i", status);
+}
+
+/*
+ * py_pjsua_conf_set_rx_level
+ */
+static PyObject *py_pjsua_conf_set_rx_level
+(PyObject *pSelf, PyObject *pArgs)
+{
+ int slot;
+ float level;
+ int status;
+
+ PJ_UNUSED_ARG(pSelf);
+
+ if (!PyArg_ParseTuple(pArgs, "if", &slot, &level))
+ {
+ return NULL;
+ }
+
+ status = pjsua_conf_adjust_rx_level(slot, level);
+
+
+ return Py_BuildValue("i", status);
+}
+
+/*
+ * py_pjsua_conf_get_signal_level
+ */
+static PyObject *py_pjsua_conf_get_signal_level
+(PyObject *pSelf, PyObject *pArgs)
+{
+ int slot;
+ unsigned tx_level, rx_level;
+ int status;
+
+ PJ_UNUSED_ARG(pSelf);
+
+ if (!PyArg_ParseTuple(pArgs, "i", &slot))
+ {
+ return NULL;
+ }
+
+ status = pjsua_conf_get_signal_level(slot, &tx_level, &rx_level);
+
+
+ return Py_BuildValue("iff", status, (float)(tx_level/255.0),
+ (float)(rx_level/255.0));
+}
+
+/*
* py_pjsua_player_create
*/
static PyObject *py_pjsua_player_create
@@ -3482,6 +3552,45 @@ static PyObject *py_pjsua_player_create
}
/*
+ * py_pjsua_playlist_create
+ */
+static PyObject *py_pjsua_playlist_create
+(PyObject *pSelf, PyObject *pArgs)
+{
+ int id;
+ int options;
+ PyObject *arg_label, *arg_filelist;
+ pj_str_t label;
+ int count;
+ pj_str_t files[64];
+ int status;
+
+ PJ_UNUSED_ARG(pSelf);
+
+ if (!PyArg_ParseTuple(pArgs, "OOi", &arg_label, &arg_filelist, &options))
+ {
+ return NULL;
+ }
+ label.ptr = PyString_AsString(arg_label);
+ label.slen = PyString_Size(arg_label);
+
+ if (!PyList_Check(arg_filelist))
+ return NULL;
+
+ count = 0;
+ for (count=0; count<PyList_Size(arg_filelist) &&
+ count<PJ_ARRAY_SIZE(files); ++count)
+ {
+ files[count].ptr = PyString_AsString(PyList_GetItem(arg_filelist, count));
+ files[count].slen = PyString_Size(PyList_GetItem(arg_filelist, count));
+ }
+
+ status = pjsua_playlist_create(files, count, &label, options, &id);
+
+ return Py_BuildValue("ii", status, id);
+}
+
+/*
* py_pjsua_player_get_conf_port
*/
static PyObject *py_pjsua_player_get_conf_port
@@ -5765,10 +5874,28 @@ static PyMethodDef py_pjsua_methods[] =
pjsua_conf_disconnect_doc
},
{
+ "conf_set_tx_level", py_pjsua_conf_set_tx_level, METH_VARARGS,
+ "Adjust the signal level to be transmitted from the bridge to the"
+ " specified port by making it louder or quieter"
+ },
+ {
+ "conf_set_rx_level", py_pjsua_conf_set_rx_level, METH_VARARGS,
+ "Adjust the signal level to be received from the specified port (to"
+ " the bridge) by making it louder or quieter"
+ },
+ {
+ "conf_get_signal_level", py_pjsua_conf_get_signal_level, METH_VARARGS,
+ "Get last signal level transmitted to or received from the specified port"
+ },
+ {
"player_create", py_pjsua_player_create, METH_VARARGS,
pjsua_player_create_doc
},
{
+ "playlist_create", py_pjsua_playlist_create, METH_VARARGS,
+ "Create WAV playlist"
+ },
+ {
"player_get_conf_port", py_pjsua_player_get_conf_port, METH_VARARGS,
pjsua_player_get_conf_port_doc
},
diff --git a/pjsip-apps/src/python/pjsua.py b/pjsip-apps/src/python/pjsua.py
index 65069440..96dd0e17 100644
--- a/pjsip-apps/src/python/pjsua.py
+++ b/pjsip-apps/src/python/pjsua.py
@@ -11,21 +11,23 @@ This implements a fully featured multimedia communication client
library based on PJSIP stack (http://www.pjsip.org)
-FEATURES
+1. FEATURES
- - Session Initiation Protocol (SIP:
+ - Session Initiation Protocol (SIP) features:
- Basic registration and call
- Multiple accounts
- Call hold, attended and unattended call transfer
- Presence
- Instant messaging
- - Media stack:
+ - Multiple SIP accounts
+ - Media features:
- Audio
- Conferencing
- Narrowband and wideband
- Codecs: PCMA, PCMU, GSM, iLBC, Speex, G.722, L16
- RTP/RTCP
- - Secure RTP (SRTP
+ - Secure RTP (SRTP)
+ - WAV playback, recording, and playlist
- NAT traversal features
- Symmetric RTP
- STUN
@@ -33,6 +35,118 @@ FEATURES
- ICE
+2. USING
+
+See http://www.pjsip.org/trac/wiki/Python_SIP_Tutorial for a more thorough
+tutorial. The paragraphs below explain basic tasks on using this module.
+
+
+2.1 Initialization
+
+Instantiate Lib class. This class is a singleton class, there can only be
+one instance of this class in the program.
+
+Initialize the library with lib.init() method, and optionally specify various
+settings like UAConfig, MediaConfig, and LogConfig.
+
+Create one or more SIP Transport instances.
+
+Create one or more SIP Account's instances, as explained below.
+
+Once initialization is complete, call lib.start().
+
+
+2.2 Accounts
+
+At least one Account must be created in the program. Use Lib's create_account()
+or create_account_for_transport() to create the account instance.
+
+Account may emit events, and to capture these events, application must derive
+a class from AccountCallback class, and install the callback to the Account
+instance using set_callback() method.
+
+
+2.3 Calls
+
+Calls are represented with Call class. Use the Call methods to operate the
+call. Outgoing calls are made with make_call() method of Account class.
+Incoming calls are reported by on_incoming_call() callback of AccountCallback
+class.
+
+Call may emit events, and to capture these events, application must derive
+a class from CallCallback class, and install the callback to the Call instance
+using set_callback() method.
+
+Note that just like every other operations in this module, the make_call()
+method is non-blocking (i.e. it doesn't wait until the call is established
+before the function returns). Progress to the Call is reported via CallCallback
+class above.
+
+
+2.4 Media
+
+Every objects that can transmit or receive media/audio (e.g. calls, WAV player,
+WAV recorder, WAV playlist) are connected to a central conference bridge.
+Application can use the object's method or Lib's method to retrieve the slot
+number of that particular object in the conference bridge:
+ - to retrieve the slot number of a call, use Call.info().conf_slot
+ - to retrieve the slot number of a WAV player, use Lib.player_get_slot()
+ - to retrieve the slot number of a WAV recorder, use Lib.recorder_get_slot()
+ - to retrieve the slot number of a playlist, use Lib.playlist_get_slot()
+ - the slot number zero is used to identity the sound device.
+
+The conference bridge provides powerful switching and mixing functionality
+for application. With the conference bridge, each conference slot (e.g.
+a call) can transmit to multiple destinations, and one destination can
+receive from multiple sources. If more than one media terminations are
+terminated in the same slot, the conference bridge will mix the signal
+automatically.
+
+Application connects one media termination/slot to another by calling
+lib.conf_connect() method. This will establish unidirectional media flow from
+the source termination to the sink termination. To establish bidirectional
+media flow, application would need to make another call to lib/conf_connect(),
+this time inverting the source and destination slots in the parameter.
+
+
+2.5 Presence
+
+To subscribe to presence information of a buddy, add Buddy object with
+add_buddy() method of Account class. Subsequent presence information for that
+Buddy will be reported via BuddyCallback class (which application should
+device and install to the Buddy object).
+
+Each Account has presence status associated with it, which will be informed
+to remote buddies when they subscribe to our presence information. Incoming
+presence subscription request by default will be accepted automatically,
+unless on_incoming_subscribe() method of AccountCallback is implemented.
+
+
+2.6 Instant Messaging
+
+Use Buddy's send_pager() and send_typing_ind() to send instant message and
+typing indication to the specified buddy.
+
+Incoming instant messages and typing indications will be reported via one of
+the three mechanisms below.
+
+If the instant message or typing indication is received in the context of an
+active call, then it will be reported via on_pager() or on_typing() method
+of CallCallback class.
+
+If the instant message or typing indication is received outside any call
+contexts, and it is received from a registered buddy, then it will be reported
+via on_pager() or on_typing() method of BuddyCallback class.
+
+If the criterias above are not met, the instant message or typing indication
+will be reported via on_pager() or on_typing() method of the AccountCallback
+class.
+
+The delivery status of outgoing instant messages are reported via
+on_pager_status() method of CallCallback, BuddyCallback, or AccountCallback,
+depending on whether the instant message was sent using Call, Buddy, or
+Account's send_pager() method.
+
"""
import _pjsua
import thread
@@ -222,7 +336,7 @@ class UAConfig:
Member documentation:
max_calls -- maximum number of calls to be supported.
- nameserver -- array of nameserver hostnames or IP addresses. Nameserver
+ nameserver -- list of nameserver hostnames or IP addresses. Nameserver
must be configured if DNS SRV resolution is desired.
stun_domain -- if nameserver is configured, this can be used to query
the STUN server with DNS SRV.
@@ -2049,12 +2163,12 @@ class Lib:
"""Enumerate sound devices in the system.
Return:
- array of SoundDeviceInfo. The index of the element specifies
+ list of SoundDeviceInfo. The index of the element specifies
the device ID for the device.
"""
- sdi_array = _pjsua.enum_snd_devs()
+ sdi_list = _pjsua.enum_snd_devs()
info = []
- for sdi in sdi_array:
+ for sdi in sdi_list:
info.append(SoundDeviceInfo(sdi))
return info
@@ -2132,18 +2246,60 @@ class Lib:
err = _pjsua.conf_disconnect(src_slot, dst_slot)
self._err_check("conf_disconnect()", self, err)
+ def conf_set_tx_level(self, slot, level):
+ """Adjust the signal level to be transmitted from the bridge to
+ the specified port by making it louder or quieter.
+
+ Keyword arguments:
+ slot -- integer to identify the conference slot number.
+ level -- Signal level adjustment. Value 1.0 means no level
+ adjustment, while value 0 means to mute the port.
+ """
+ err = _pjsua.conf_set_tx_level(slot, level)
+ self._err_check("conf_set_tx_level()", self, err)
+
+ def conf_set_rx_level(self, slot, level):
+ """Adjust the signal level to be received from the specified port
+ (to the bridge) by making it louder or quieter.
+
+ Keyword arguments:
+ slot -- integer to identify the conference slot number.
+ level -- Signal level adjustment. Value 1.0 means no level
+ adjustment, while value 0 means to mute the port.
+ """
+ err = _pjsua.conf_set_rx_level(slot, level)
+ self._err_check("conf_set_rx_level()", self, err)
+
+ def conf_get_signal_level(self, slot):
+ """Get last signal level transmitted to or received from the
+ specified port. The signal levels are float values from 0.0 to 1.0,
+ with 0.0 indicates no signal, and 1.0 indicates the loudest signal
+ level.
+
+ Keyword arguments:
+ slot -- integer to identify the conference slot number.
+
+ Return value:
+ (tx_level, rx_level) tuple.
+ """
+ err, tx_level, rx_level = _pjsua.conf_get_signal_level(slot)
+ self._err_check("conf_get_signal_level()", self, err)
+ return (tx_level, rx_level)
+
+
+
# Codecs API
def enum_codecs(self):
"""Return list of codecs supported by pjsua.
Return:
- array of CodecInfo
+ list of CodecInfo
"""
- ci_array = _pjsua.enum_codecs()
+ ci_list = _pjsua.enum_codecs()
codec_info = []
- for ci in ci_array:
+ for ci in ci_list:
cp = _pjsua.codec_get_param(ci.id)
if cp:
codec_info.append(CodecInfo(ci, cp))
@@ -2191,8 +2347,8 @@ class Lib:
Keyword arguments
filename -- WAV file name
- loop -- boolean to specify wheter playback shoudl
- automatically restart
+ loop -- boolean to specify whether playback should
+ automatically restart upon EOF
Return:
WAV player ID
@@ -2241,6 +2397,52 @@ class Lib:
err = _pjsua.player_destroy(player_id)
self._err_check("player_destroy()", self, err)
+ def create_playlist(self, filelist, label="playlist", loop=True):
+ """Create WAV playlist.
+
+ Keyword arguments:
+ filelist -- List of WAV file names.
+ label -- Optional name to be assigned to the playlist
+ object (useful for logging)
+ loop -- boolean to specify whether playback should
+ automatically restart upon EOF
+
+ Return:
+ playlist_id
+ """
+ opt = 0
+ if not loop:
+ opt = opt + 1
+ err, playlist_id = _pjsua.playlist_create(label, filelist, opt)
+ self._err_check("create_playlist()", self, err)
+ return playlist_id
+
+ def playlist_get_slot(self, playlist_id):
+ """Get the conference port ID for the specified playlist.
+
+ Keyword arguments:
+ playlist_id -- the WAV playlist ID
+
+ Return:
+ Conference slot number for the playlist
+
+ """
+ slot = _pjsua.player_get_conf_port(playlist_id)
+ if slot < 0:
+ self._err_check("playlist_get_slot()", self, -1,
+ "Invalid playlist id")
+ return slot
+
+ def playlist_destroy(self, playlist_id):
+ """Destroy the WAV playlist.
+
+ Keyword arguments:
+ playlist_id -- the WAV playlist ID.
+
+ """
+ err = _pjsua.player_destroy(playlist_id)
+ self._err_check("playlist_destroy()", self, err)
+
def create_recorder(self, filename):
"""Create WAV file recorder.