9. PJSUA2示例应用程序

9.1 示例应用程序

9.1.1 C++

pjsip-apps/src/samples/pjsua2_demo.cpp 是一个非常简单可用的C++示例应用程序。

 /* $Id: pjsua2_demo.cpp 5467 2016-10-21 07:55:41Z nanang $ */
/*
* Copyright (C) 2008-2013 Teluu Inc. (http://www.teluu.com)
*
* 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 <pjsua2.hpp>
#include <iostream>
#include <memory>
#include <pj/file_access.h> #define THIS_FILE "pjsua2_demo.cpp" using namespace pj; class MyAccount; class MyCall : public Call
{
private:
MyAccount *myAcc; public:
MyCall(Account &acc, int call_id = PJSUA_INVALID_ID)
: Call(acc, call_id)
{
myAcc = (MyAccount *)&acc;
} virtual void onCallState(OnCallStateParam &prm);
}; class MyAccount : public Account
{
public:
std::vector<Call *> calls; public:
MyAccount()
{} ~MyAccount()
{
std::cout << "*** Account is being deleted: No of calls="
<< calls.size() << std::endl;
} void removeCall(Call *call)
{
for (std::vector<Call *>::iterator it = calls.begin();
it != calls.end(); ++it)
{
if (*it == call) {
calls.erase(it);
break;
}
}
} virtual void onRegState(OnRegStateParam &prm)
{
AccountInfo ai = getInfo();
std::cout << (ai.regIsActive? "*** Register: code=" : "*** Unregister: code=")
<< prm.code << std::endl;
} virtual void onIncomingCall(OnIncomingCallParam &iprm)
{
Call *call = new MyCall(*this, iprm.callId);
CallInfo ci = call->getInfo();
CallOpParam prm; std::cout << "*** Incoming Call: " << ci.remoteUri << " ["
<< ci.stateText << "]" << std::endl; calls.push_back(call);
prm.statusCode = (pjsip_status_code);
call->answer(prm);
}
}; void MyCall::onCallState(OnCallStateParam &prm)
{
PJ_UNUSED_ARG(prm); CallInfo ci = getInfo();
std::cout << "*** Call: " << ci.remoteUri << " [" << ci.stateText
<< "]" << std::endl; if (ci.state == PJSIP_INV_STATE_DISCONNECTED) {
myAcc->removeCall(this);
/* Delete the call */
delete this;
}
} static void mainProg1(Endpoint &ep) throw(Error)
{
// Init library
EpConfig ep_cfg;
ep_cfg.logConfig.level = ;
ep.libInit( ep_cfg ); // Transport
TransportConfig tcfg;
tcfg.port = ;
ep.transportCreate(PJSIP_TRANSPORT_UDP, tcfg); // Start library
ep.libStart();
std::cout << "*** PJSUA2 STARTED ***" << std::endl; // Add account
AccountConfig acc_cfg;
acc_cfg.idUri = "sip:test1@pjsip.org";
acc_cfg.regConfig.registrarUri = "sip:sip.pjsip.org";
acc_cfg.sipConfig.authCreds.push_back( AuthCredInfo("digest", "*",
"test1", , "test1") );
std::auto_ptr<MyAccount> acc(new MyAccount);
acc->create(acc_cfg); pj_thread_sleep(); // Make outgoing call
Call *call = new MyCall(*acc);
acc->calls.push_back(call);
CallOpParam prm(true);
prm.opt.audioCount = ;
prm.opt.videoCount = ;
call->makeCall("sip:test1@pjsip.org", prm); // Hangup all calls
pj_thread_sleep();
ep.hangupAllCalls();
pj_thread_sleep(); // Destroy library
std::cout << "*** PJSUA2 SHUTTING DOWN ***" << std::endl;
} static void mainProg2() throw(Error)
{
string json_str;
{
EpConfig epCfg;
JsonDocument jDoc; epCfg.uaConfig.maxCalls = ;
epCfg.uaConfig.userAgent = "Just JSON Test";
epCfg.uaConfig.stunServer.push_back("stun1.pjsip.org");
epCfg.uaConfig.stunServer.push_back("stun2.pjsip.org");
epCfg.logConfig.filename = "THE.LOG"; jDoc.writeObject(epCfg);
json_str = jDoc.saveString();
std::cout << json_str << std::endl << std::endl;
} {
EpConfig epCfg;
JsonDocument rDoc;
string output; rDoc.loadString(json_str);
rDoc.readObject(epCfg); JsonDocument wDoc; wDoc.writeObject(epCfg);
json_str = wDoc.saveString();
std::cout << json_str << std::endl << std::endl; wDoc.saveFile("jsontest.js");
} {
EpConfig epCfg;
JsonDocument rDoc; rDoc.loadFile("jsontest.js");
rDoc.readObject(epCfg);
pj_file_delete("jsontest.js");
}
} static void mainProg3(Endpoint &ep) throw(Error)
{
const char *paths[] = { "../../../../tests/pjsua/wavs/input.16.wav",
"../../tests/pjsua/wavs/input.16.wav",
"input.16.wav"};
unsigned i;
const char *filename = NULL; // Init library
EpConfig ep_cfg;
ep.libInit( ep_cfg ); for (i=; i<PJ_ARRAY_SIZE(paths); ++i) {
if (pj_file_exists(paths[i])) {
filename = paths[i];
break;
}
} if (!filename) {
PJSUA2_RAISE_ERROR3(PJ_ENOTFOUND, "mainProg3()",
"Could not locate input.16.wav");
} // Start library
ep.libStart();
std::cout << "*** PJSUA2 STARTED ***" << std::endl; // Create player and recorder
{
AudioMediaPlayer amp;
amp.createPlayer(filename); AudioMediaRecorder amr;
amr.createRecorder("recorder_test_output.wav"); amp.startTransmit(ep.audDevManager().getPlaybackDevMedia());
amp.startTransmit(amr); pj_thread_sleep();
}
} static void mainProg() throw(Error)
{
string json_str; {
JsonDocument jdoc;
AccountConfig accCfg; accCfg.idUri = "\"Just Test\" <sip:test@pjsip.org>";
accCfg.regConfig.registrarUri = "sip:sip.pjsip.org";
SipHeader h;
h.hName = "X-Header";
h.hValue = "User header";
accCfg.regConfig.headers.push_back(h); accCfg.sipConfig.proxies.push_back("<sip:sip.pjsip.org;transport=tcp>");
accCfg.sipConfig.proxies.push_back("<sip:sip.pjsip.org;transport=tls>"); accCfg.mediaConfig.transportConfig.tlsConfig.ciphers.push_back();
accCfg.mediaConfig.transportConfig.tlsConfig.ciphers.push_back();
accCfg.mediaConfig.transportConfig.tlsConfig.ciphers.push_back(); AuthCredInfo aci;
aci.scheme = "digest";
aci.username = "test";
aci.data = "passwd";
aci.realm = "*";
accCfg.sipConfig.authCreds.push_back(aci); jdoc.writeObject(accCfg);
json_str = jdoc.saveString();
std::cout << "Original:" << std::endl;
std::cout << json_str << std::endl << std::endl;
} {
JsonDocument rdoc; rdoc.loadString(json_str);
AccountConfig accCfg;
rdoc.readObject(accCfg); JsonDocument wdoc;
wdoc.writeObject(accCfg);
json_str = wdoc.saveString(); std::cout << "Parsed:" << std::endl;
std::cout << json_str << std::endl << std::endl;
}
} static void mainProg4(Endpoint &ep) throw(Error)
{
// Init library
EpConfig ep_cfg;
ep.libInit( ep_cfg ); // Create transport
TransportConfig tcfg;
tcfg.port = ;
ep.transportCreate(PJSIP_TRANSPORT_UDP, tcfg);
ep.transportCreate(PJSIP_TRANSPORT_TCP, tcfg); // Add account
AccountConfig acc_cfg;
acc_cfg.idUri = "sip:localhost";
std::auto_ptr<MyAccount> acc(new MyAccount);
acc->create(acc_cfg); // Start library
ep.libStart();
std::cout << "*** PJSUA2 STARTED ***" << std::endl; // Just wait for ENTER key
std::cout << "Press ENTER to quit..." << std::endl;
std::cin.get();
} int main()
{
int ret = ;
Endpoint ep; try {
ep.libCreate(); mainProg4(ep);
ret = PJ_SUCCESS;
} catch (Error & err) {
std::cout << "Exception: " << err.info() << std::endl;
ret = ;
} try {
ep.libDestroy();
} catch(Error &err) {
std::cout << "Exception: " << err.info() << std::endl;
ret = ;
} if (ret == PJ_SUCCESS) {
std::cout << "Success" << std::endl;
} else {
std::cout << "Error Found" << std::endl;
} return ret;
}

二进制文件位于 pjsip-apps/bin/samples 目录下

9.1.2 Python GUI

有一个相当完整的Python GUI示例程序,位于 pjsip-apps/src/pygui目录

 # $Id: application.py 4798 2014-03-19 21:20:17Z bennylp $
#
# pjsua Python GUI Demo
#
# Copyright (C)2013 Teluu Inc. (http://www.teluu.com)
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
import sys
if sys.version_info[0] >= 3: # Python 3
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox as msgbox
else:
import Tkinter as tk
import tkMessageBox as msgbox
import ttk import pjsua2 as pj
import log
import accountsetting
import account
import buddy
import endpoint
import settings import os
import traceback # You may try to enable pjsua worker thread by setting USE_THREADS below to True *and*
# recreate the swig module with adding -threads option to swig (uncomment USE_THREADS
# in swig/python/Makefile). In my experiment this would crash Python as reported in:
# http://lists.pjsip.org/pipermail/pjsip_lists.pjsip.org/2014-March/017223.html
USE_THREADS = False class Application(ttk.Frame):
"""
The Application main frame.
"""
def __init__(self):
global USE_THREADS
ttk.Frame.__init__(self, name='application', width=300, height=500)
self.pack(expand='yes', fill='both')
self.master.title('pjsua2 Demo')
self.master.geometry('500x500+100+100') # Logger
self.logger = log.Logger() # Accounts
self.accList = [] # GUI variables
self.showLogWindow = tk.IntVar(value=0)
self.quitting = False # Construct GUI
self._createWidgets() # Log window
self.logWindow = log.LogWindow(self)
self._onMenuShowHideLogWindow() # Instantiate endpoint
self.ep = endpoint.Endpoint()
self.ep.libCreate() # Default config
self.appConfig = settings.AppConfig()
if USE_THREADS:
self.appConfig.epConfig.uaConfig.threadCnt = 1
self.appConfig.epConfig.uaConfig.mainThreadOnly = False
else:
self.appConfig.epConfig.uaConfig.threadCnt = 0
self.appConfig.epConfig.uaConfig.mainThreadOnly = True
self.appConfig.epConfig.logConfig.writer = self.logger
self.appConfig.epConfig.logConfig.filename = "pygui.log"
self.appConfig.epConfig.logConfig.fileFlags = pj.PJ_O_APPEND
self.appConfig.epConfig.logConfig.level = 5
self.appConfig.epConfig.logConfig.consoleLevel = 5 def saveConfig(self, filename='pygui.js'):
# Save disabled accounts since they are not listed in self.accList
disabled_accs = [ac for ac in self.appConfig.accounts if not ac.enabled]
self.appConfig.accounts = [] # Get account configs from active accounts
for acc in self.accList:
acfg = settings.AccConfig()
acfg.enabled = True
acfg.config = acc.cfg
for bud in acc.buddyList:
acfg.buddyConfigs.append(bud.cfg)
self.appConfig.accounts.append(acfg) # Put back disabled accounts
self.appConfig.accounts.extend(disabled_accs)
# Save
self.appConfig.saveFile(filename) def start(self, cfg_file='pygui.js'):
global USE_THREADS
# Load config
if cfg_file and os.path.exists(cfg_file):
self.appConfig.loadFile(cfg_file) if USE_THREADS:
self.appConfig.epConfig.uaConfig.threadCnt = 1
self.appConfig.epConfig.uaConfig.mainThreadOnly = False
else:
self.appConfig.epConfig.uaConfig.threadCnt = 0
self.appConfig.epConfig.uaConfig.mainThreadOnly = True
self.appConfig.epConfig.uaConfig.threadCnt = 0
self.appConfig.epConfig.uaConfig.mainThreadOnly = True
self.appConfig.epConfig.logConfig.writer = self.logger
self.appConfig.epConfig.logConfig.level = 5
self.appConfig.epConfig.logConfig.consoleLevel = 5 # Initialize library
self.appConfig.epConfig.uaConfig.userAgent = "pygui-" + self.ep.libVersion().full;
self.ep.libInit(self.appConfig.epConfig)
self.master.title('pjsua2 Demo version ' + self.ep.libVersion().full) # Create transports
if self.appConfig.udp.enabled:
self.ep.transportCreate(self.appConfig.udp.type, self.appConfig.udp.config)
if self.appConfig.tcp.enabled:
self.ep.transportCreate(self.appConfig.tcp.type, self.appConfig.tcp.config)
if self.appConfig.tls.enabled:
self.ep.transportCreate(self.appConfig.tls.type, self.appConfig.tls.config) # Add accounts
for cfg in self.appConfig.accounts:
if cfg.enabled:
self._createAcc(cfg.config)
acc = self.accList[-1]
for buddy_cfg in cfg.buddyConfigs:
self._createBuddy(acc, buddy_cfg) # Start library
self.ep.libStart() # Start polling
if not USE_THREADS:
self._onTimer() def updateAccount(self, acc):
if acc.deleting:
return # ignore
iid = str(acc.randId)
text = acc.cfg.idUri
status = acc.statusText() values = (status,)
if self.tv.exists(iid):
self.tv.item(iid, text=text, values=values)
else:
self.tv.insert('', 'end', iid, open=True, text=text, values=values) def updateBuddy(self, bud):
iid = 'buddy' + str(bud.randId)
text = bud.cfg.uri
status = bud.statusText() values = (status,)
if self.tv.exists(iid):
self.tv.item(iid, text=text, values=values)
else:
self.tv.insert(str(bud.account.randId), 'end', iid, open=True, text=text, values=values) def _createAcc(self, acc_cfg):
acc = account.Account(self)
acc.cfg = acc_cfg
self.accList.append(acc)
self.updateAccount(acc)
acc.create(acc.cfg)
acc.cfgChanged = False
self.updateAccount(acc) def _createBuddy(self, acc, buddy_cfg):
bud = buddy.Buddy(self)
bud.cfg = buddy_cfg
bud.account = acc
bud.create(acc, bud.cfg)
self.updateBuddy(bud)
acc.buddyList.append(bud) def _createWidgets(self):
self._createAppMenu() # Main pane, a Treeview
self.tv = ttk.Treeview(self, columns=('Status'), show='tree')
self.tv.pack(side='top', fill='both', expand='yes', padx=5, pady=5) self._createContextMenu() # Handle close event
self.master.protocol("WM_DELETE_WINDOW", self._onClose) def _createAppMenu(self):
# Main menu bar
top = self.winfo_toplevel()
self.menubar = tk.Menu()
top.configure(menu=self.menubar) # File menu
file_menu = tk.Menu(self.menubar, tearoff=False)
self.menubar.add_cascade(label="File", menu=file_menu)
file_menu.add_command(label="Add account..", command=self._onMenuAddAccount)
file_menu.add_checkbutton(label="Show/hide log window", command=self._onMenuShowHideLogWindow, variable=self.showLogWindow)
file_menu.add_separator()
file_menu.add_command(label="Settings...", command=self._onMenuSettings)
file_menu.add_command(label="Save Settings", command=self._onMenuSaveSettings)
file_menu.add_separator()
file_menu.add_command(label="Quit", command=self._onMenuQuit) # Window menu
self.window_menu = tk.Menu(self.menubar, tearoff=False)
self.menubar.add_cascade(label="Window", menu=self.window_menu) # Help menu
help_menu = tk.Menu(self.menubar, tearoff=False)
self.menubar.add_cascade(label="Help", menu=help_menu)
help_menu.add_command(label="About", underline=2, command=self._onMenuAbout) def _showChatWindow(self, chat_inst):
chat_inst.showWindow() def updateWindowMenu(self):
# Chat windows
self.window_menu.delete(0, tk.END)
for acc in self.accList:
for c in acc.chatList:
cmd = lambda arg=c: self._showChatWindow(arg)
self.window_menu.add_command(label=c.title, command=cmd) def _createContextMenu(self):
top = self.winfo_toplevel() # Create Account context menu
self.accMenu = tk.Menu(top, tearoff=False)
# Labels, must match with _onAccContextMenu()
labels = ['Unregister', 'Reregister', 'Add buddy...', '-',
'Online', 'Invisible', 'Away', 'Busy', '-',
'Settings...', '-',
'Delete...']
for label in labels:
if label=='-':
self.accMenu.add_separator()
else:
cmd = lambda arg=label: self._onAccContextMenu(arg)
self.accMenu.add_command(label=label, command=cmd) # Create Buddy context menu
# Labels, must match with _onBuddyContextMenu()
self.buddyMenu = tk.Menu(top, tearoff=False)
labels = ['Audio call', 'Send instant message', '-',
'Subscribe', 'Unsubscribe', '-',
'Settings...', '-',
'Delete...'] for label in labels:
if label=='-':
self.buddyMenu.add_separator()
else:
cmd = lambda arg=label: self._onBuddyContextMenu(arg)
self.buddyMenu.add_command(label=label, command=cmd) if (top.tk.call('tk', 'windowingsystem')=='aqua'):
self.tv.bind('<2>', self._onTvRightClick)
self.tv.bind('<Control-1>', self._onTvRightClick)
else:
self.tv.bind('<3>', self._onTvRightClick)
self.tv.bind('<Double-Button-1>', self._onTvDoubleClick) def _getSelectedAccount(self):
items = self.tv.selection()
if not items:
return None
try:
iid = int(items[0])
except:
return None
accs = [acc for acc in self.accList if acc.randId==iid]
if not accs:
return None
return accs[0] def _getSelectedBuddy(self):
items = self.tv.selection()
if not items:
return None
try:
iid = int(items[0][5:])
iid_parent = int(self.tv.parent(items[0]))
except:
return None accs = [acc for acc in self.accList if acc.randId==iid_parent]
if not accs:
return None buds = [b for b in accs[0].buddyList if b.randId==iid]
if not buds:
return None return buds[0] def _onTvRightClick(self, event):
iid = self.tv.identify_row(event.y)
#iid = self.tv.identify('item', event.x, event.y)
if iid:
self.tv.selection_set( (iid,) )
acc = self._getSelectedAccount()
if acc:
self.accMenu.post(event.x_root, event.y_root)
else:
# A buddy is selected
self.buddyMenu.post(event.x_root, event.y_root) def _onTvDoubleClick(self, event):
iid = self.tv.identify_row(event.y)
if iid:
self.tv.selection_set( (iid,) )
acc = self._getSelectedAccount()
if acc:
self.cfgChanged = False
dlg = accountsetting.Dialog(self.master, acc.cfg)
if dlg.doModal():
self.updateAccount(acc)
acc.modify(acc.cfg)
else:
bud = self._getSelectedBuddy()
acc = bud.account
chat = acc.findChat(bud.cfg.uri)
if not chat:
chat = acc.newChat(bud.cfg.uri)
chat.showWindow() def _onAccContextMenu(self, label):
acc = self._getSelectedAccount()
if not acc:
return if label=='Unregister':
acc.setRegistration(False)
elif label=='Reregister':
acc.setRegistration(True)
elif label=='Online':
ps = pj.PresenceStatus()
ps.status = pj.PJSUA_BUDDY_STATUS_ONLINE
acc.setOnlineStatus(ps)
elif label=='Invisible':
ps = pj.PresenceStatus()
ps.status = pj.PJSUA_BUDDY_STATUS_OFFLINE
acc.setOnlineStatus(ps)
elif label=='Away':
ps = pj.PresenceStatus()
ps.status = pj.PJSUA_BUDDY_STATUS_ONLINE
ps.activity = pj.PJRPID_ACTIVITY_AWAY
ps.note = "Away"
acc.setOnlineStatus(ps)
elif label=='Busy':
ps = pj.PresenceStatus()
ps.status = pj.PJSUA_BUDDY_STATUS_ONLINE
ps.activity = pj.PJRPID_ACTIVITY_BUSY
ps.note = "Busy"
acc.setOnlineStatus(ps)
elif label=='Settings...':
self.cfgChanged = False
dlg = accountsetting.Dialog(self.master, acc.cfg)
if dlg.doModal():
self.updateAccount(acc)
acc.modify(acc.cfg)
elif label=='Delete...':
msg = "Do you really want to delete account '%s'?" % acc.cfg.idUri
if msgbox.askquestion('Delete account?', msg, default=msgbox.NO) != u'yes':
return
iid = str(acc.randId)
self.accList.remove(acc)
acc.setRegistration(False)
acc.deleting = True
del acc
self.tv.delete( (iid,) )
elif label=='Add buddy...':
cfg = pj.BuddyConfig()
dlg = buddy.SettingDialog(self.master, cfg)
if dlg.doModal():
self._createBuddy(acc, cfg)
else:
assert not ("Unknown menu " + label) def _onBuddyContextMenu(self, label):
bud = self._getSelectedBuddy()
if not bud:
return
acc = bud.account if label=='Audio call':
chat = acc.findChat(bud.cfg.uri)
if not chat: chat = acc.newChat(bud.cfg.uri)
chat.showWindow()
chat.startCall()
elif label=='Send instant message':
chat = acc.findChat(bud.cfg.uri)
if not chat: chat = acc.newChat(bud.cfg.uri)
chat.showWindow(True)
elif label=='Subscribe':
bud.subscribePresence(True)
elif label=='Unsubscribe':
bud.subscribePresence(False)
elif label=='Settings...':
subs = bud.cfg.subscribe
uri = bud.cfg.uri
dlg = buddy.SettingDialog(self.master, bud.cfg)
if dlg.doModal():
self.updateBuddy(bud)
# URI updated?
if uri != bud.cfg.uri:
cfg = bud.cfg
# del old
iid = 'buddy' + str(bud.randId)
acc.buddyList.remove(bud)
del bud
self.tv.delete( (iid,) )
# add new
self._createBuddy(acc, cfg)
# presence subscribe setting updated
elif subs != bud.cfg.subscribe:
bud.subscribePresence(bud.cfg.subscribe)
elif label=='Delete...':
msg = "Do you really want to delete buddy '%s'?" % bud.cfg.uri
if msgbox.askquestion('Delete buddy?', msg, default=msgbox.NO) != u'yes':
return
iid = 'buddy' + str(bud.randId)
acc.buddyList.remove(bud)
del bud
self.tv.delete( (iid,) )
else:
assert not ("Unknown menu " + label) def _onTimer(self):
if not self.quitting:
self.ep.libHandleEvents(10)
if not self.quitting:
self.master.after(50, self._onTimer) def _onClose(self):
self.saveConfig()
self.quitting = True
self.ep.libDestroy()
self.ep = None
self.update()
self.quit() def _onMenuAddAccount(self):
cfg = pj.AccountConfig()
dlg = accountsetting.Dialog(self.master, cfg)
if dlg.doModal():
self._createAcc(cfg) def _onMenuShowHideLogWindow(self):
if self.showLogWindow.get():
self.logWindow.deiconify()
else:
self.logWindow.withdraw() def _onMenuSettings(self):
dlg = settings.Dialog(self, self.appConfig)
if dlg.doModal():
msgbox.showinfo(self.master.title(), 'You need to restart for new settings to take effect') def _onMenuSaveSettings(self):
self.saveConfig() def _onMenuQuit(self):
self._onClose() def _onMenuAbout(self):
msgbox.showinfo(self.master.title(), 'About') class ExceptionCatcher:
"""Custom Tk exception catcher, mainly to display more information
from pj.Error exception
"""
def __init__(self, func, subst, widget):
self.func = func
self.subst = subst
self.widget = widget
def __call__(self, *args):
try:
if self.subst:
args = apply(self.subst, args)
return apply(self.func, args)
except pj.Error, error:
print 'Exception:'
print ' ', error.info()
print 'Traceback:'
print traceback.print_stack()
log.writeLog2(1, 'Exception: ' + error.info() + '\n')
except Exception, error:
print 'Exception:'
print ' ', str(error)
print 'Traceback:'
print traceback.print_stack()
log.writeLog2(1, 'Exception: ' + str(error) + '\n') def main():
#tk.CallWrapper = ExceptionCatcher
app = Application()
app.start()
app.mainloop() if __name__ == '__main__':
main()

需要Python 2.7及以上版本,以及Python SWIG模块。要使用应用程序,只需运行:

python application.py

9.1.3 安卓

请参考 https://trac.pjsip.org/repos/wiki/Getting-Started/Android#pjsua2 的示例应用程序。

9.1.4 Java

在目录 pjsip-apps/src/swig/java 下有一个Hello World类型的应用程序。

需要Java SWIG模块。构建SWIG模块后,从该目录运行该应用程序。

make test

9.1.5 iOS

可拷贝 pjsip-apps/src/samples/pjsua2_demo.cpp 的代码(即本页第一个代码段中的代码)到 .mm文件中,然后加入到 iOS XCode项目中。

注意:必须使用Obj-C ++(.mm)文件,而不是默认的Obj-C(.m)文件

PJSUA2开发文档--第九章 PJSUA2应用程序示例的更多相关文章

  1. PJSUA2开发文档--第十二章 PJSUA2 API 参考手册

    12 PJSUA2 API 参考手册 12.1 endpoint.hpp PJSUA2基本代理操作.  namespace pj PJSUA2 API在pj命名空间内. 12.1.1 class En ...

  2. PJSUA2开发文档--第三章 PJSUA2高级API

    3. PJSUA2高级API PJSUA2是PJSUA API以上的面向对象抽象.它为构建会话发起协议(SIP)多媒体用户代理应用程序(也称为IP / VoIP软电话)提供高级API.它将信令,媒体和 ...

  3. PJSUA2开发文档--第五章 帐户(号)Accounts

    第五章 帐户(号) 帐户提供正在使用该应用程序的用户的身份(或身份).一个帐户有一个与之相关的SIP统一资源标识符(URI).在SIP术语中,该URI用作该人的记录地址( Address of Rec ...

  4. PJSUA2开发文档--第四章 端点ENDPOINT

    4.端点ENDPOINT Endpoint类是一个单例类,应用程序必须在此类实例之前创建一个并且最多只能创建一个,然后才能执行任何操作.同样,一旦这个类被销毁,应用程序就不能调用该库的任何API.这个 ...

  5. PJSUA2开发文档--第六章 媒体 Media类

    6. 媒体(Media) 媒体对象是能够产生媒体或接受媒体的对象. Media的重要子类是AudioMedia,它代表音频媒体.PJSUA2支持多种类型的音频媒体对象: 捕获设备的AudioMedia ...

  6. PJSUA2开发文档--第七章 呼叫 Calls类

    7   呼叫Calls 呼叫由Call类处理 7.1 子类化Call类 要使用Call类,应用程序应创建子类,如: class MyCall : public Call { public: MyCal ...

  7. PJSUA2开发文档--第十一章 网络问题

    11 网络问题 11.1 IP地址更改 请参阅wiki 处理IP地址更改.请注意,本指南使用PJSUA API作为参考. 11.2 被阻止/过滤的网络 请参阅维基百科 通过阻止或过滤的VoIP网络

  8. PJSUA2开发文档--第十章 媒体质量(MEDIA QUALITY)

    10 媒体质量(Media Quality) 10.1 音频质量 如果遇到音频质量问题,可尝试以下步骤: 遵循指南:使用pjsystest测试声音设备. 识别声音问题并使用以下步骤进行故障排除:检查声 ...

  9. PJSUA2开发文档--第八章 好友(Buddy)类

    8  好友(存在)Buddy PJSUA2的功能是围绕Buddy类为中心展开的.该类表示一个远端好友(伙伴,一个人或一个SIP端点). 8.1 子类化Buddy类 要使用Buddy类,通常应创建子类, ...

随机推荐

  1. 调用链Cat介绍

    1. 调用链Cat 1.1. 调用链演进 1.2. 开源产品比较 1.3. 监控场景 1.4. cat的增值作用 1.5. cat典型报表 1.5.1. 应用报错大盘 1.5.2. 业务大盘 1.5. ...

  2. python编译pyc工程--导包问题解决

    利用python 编译工程,生产pyc文件 pyc文件好处:是一种二进制机器码,并且隐藏了源文件代码,但是有和py文件一样的功能(可以理解为效果一样) 所以可以将代码隐藏,便于商业价值,保护代码隐私还 ...

  3. 【Spark篇】---Spark中资源和任务调度源码分析与资源配置参数应用

    一.前述 Spark中资源调度是一个非常核心的模块,尤其对于我们提交参数来说,需要具体到某些配置,所以提交配置的参数于源码一一对应,掌握此节对于Spark在任务执行过程中的资源分配会更上一层楼.由于源 ...

  4. Python内置函数(57)——setattr

    英文文档: setattr(object, name, value) This is the counterpart of getattr(). The arguments are an object ...

  5. pytorch: 准备、训练和测试自己的图片数据

    大部分的pytorch入门教程,都是使用torchvision里面的数据进行训练和测试.如果我们是自己的图片数据,又该怎么做呢? 一.我的数据 我在学习的时候,使用的是fashion-mnist.这个 ...

  6. 前端(各种demo)二:左侧导航栏的折叠和打开(不使用js)基础版和升级版

    1.给div设置定位. 复习一下—— css中position有五种属性: static:默认值,没有定位 absolute:绝对定位,相对于父级元素进行定位 relative:相对定位 fixed: ...

  7. 祝雷(依乐祝):一份来自29岁.NET老程序员的自白

    潘老师的文笔果然了得,经过潘老师的妙笔生花后,文章的阅读体验果然好了很多!这里再次感谢潘老师的支持! 祝雷(合肥.NET 俱乐部发起人) [个人介绍] 博客园知名博主(依乐祝).6年以上.NET技术栈 ...

  8. redis 系列21 复制Replication (上)

    一.   概述 使用和配置主从复制非常简单,每次当 slave 和 master 之间的连接断开时, slave 会自动重连到 master 上,并且无论这期间 master 发生了什么, slave ...

  9. Python多环境管理

    Python环境管理 什么是Anaconda Anaconda是专注于数据分析的Python发行版本,包含了conda.Python等190多个科学包及其依赖项.Anaconda通过管理工具包.开发环 ...

  10. 前端笔记之JavaScript(三)关于条件判断语句、循环语句那点事

    一.条件分支语句 条件分支语句,也叫作条件判断语句,就是根据某种条件执行某些语句,不执行某些语句. JS中有三种语法是可以表示条件分支的 1.1 if……else…… 条件分支的主力语法,这个主力语法 ...