PJSUA2应用程序示例

示例应用程序

9.1.1 C++ 

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

复制代码
  1 /* $Id: pjsua2_demo.cpp 5467 2016-10-21 07:55:41Z nanang $ */
  2 /*
  3  * Copyright (C) 2008-2013 Teluu Inc. (http://www.teluu.com)
  4  *
  5  * This program is free software; you can redistribute it and/or modify
  6  * it under the terms of the GNU General Public License as published by
  7  * the Free Software Foundation; either version 2 of the License, or
  8  * (at your option) any later version.
  9  *
 10  * This program is distributed in the hope that it will be useful,
 11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 13  * GNU General Public License for more details.
 14  *
 15  * You should have received a copy of the GNU General Public License
 16  * along with this program; if not, write to the Free Software
 17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 18  */
 19 #include <pjsua2.hpp>
 20 #include <iostream>
 21 #include <memory>
 22 #include <pj/file_access.h>
 23 
 24 #define THIS_FILE     "pjsua2_demo.cpp"
 25 
 26 using namespace pj;
 27 
 28 class MyAccount;
 29 
 30 class MyCall : public Call
 31 {
 32 private:
 33     MyAccount *myAcc;
 34 
 35 public:
 36     MyCall(Account &acc, int call_id = PJSUA_INVALID_ID)
 37     : Call(acc, call_id)
 38     {
 39         myAcc = (MyAccount *)&acc;
 40     }
 41     
 42     virtual void onCallState(OnCallStateParam &prm);
 43 };
 44 
 45 class MyAccount : public Account
 46 {
 47 public:
 48     std::vector<Call *> calls;
 49     
 50 public:
 51     MyAccount()
 52     {}
 53 
 54     ~MyAccount()
 55     {
 56         std::cout << "*** Account is being deleted: No of calls="
 57                   << calls.size() << std::endl;
 58     }
 59     
 60     void removeCall(Call *call)
 61     {
 62         for (std::vector<Call *>::iterator it = calls.begin();
 63              it != calls.end(); ++it)
 64         {
 65             if (*it == call) {
 66                 calls.erase(it);
 67                 break;
 68             }
 69         }
 70     }
 71 
 72     virtual void onRegState(OnRegStateParam &prm)
 73     {
 74     AccountInfo ai = getInfo();
 75     std::cout << (ai.regIsActive? "*** Register: code=" : "*** Unregister: code=")
 76           << prm.code << std::endl;
 77     }
 78     
 79     virtual void onIncomingCall(OnIncomingCallParam &iprm)
 80     {
 81         Call *call = new MyCall(*this, iprm.callId);
 82         CallInfo ci = call->getInfo();
 83         CallOpParam prm;
 84         
 85         std::cout << "*** Incoming Call: " <<  ci.remoteUri << " ["
 86                   << ci.stateText << "]" << std::endl;
 87         
 88         calls.push_back(call);
 89         prm.statusCode = (pjsip_status_code)200;
 90         call->answer(prm);
 91     }
 92 };
 93 
 94 void MyCall::onCallState(OnCallStateParam &prm)
 95 {
 96     PJ_UNUSED_ARG(prm);
 97 
 98     CallInfo ci = getInfo();
 99     std::cout << "*** Call: " <<  ci.remoteUri << " [" << ci.stateText
100               << "]" << std::endl;
101     
102     if (ci.state == PJSIP_INV_STATE_DISCONNECTED) {
103         myAcc->removeCall(this);
104         /* Delete the call */
105         delete this;
106     }
107 }
108 
109 static void mainProg1(Endpoint &ep) throw(Error)
110 {
111     // Init library
112     EpConfig ep_cfg;
113     ep_cfg.logConfig.level = 4;
114     ep.libInit( ep_cfg );
115 
116     // Transport
117     TransportConfig tcfg;
118     tcfg.port = 5060;
119     ep.transportCreate(PJSIP_TRANSPORT_UDP, tcfg);
120 
121     // Start library
122     ep.libStart();
123     std::cout << "*** PJSUA2 STARTED ***" << std::endl;
124 
125     // Add account
126     AccountConfig acc_cfg;
127     acc_cfg.idUri = "sip:[email protected]";
128     acc_cfg.regConfig.registrarUri = "sip:sip.pjsip.org";
129     acc_cfg.sipConfig.authCreds.push_back( AuthCredInfo("digest", "*",
130                                                         "test1", 0, "test1") );
131     std::auto_ptr<MyAccount> acc(new MyAccount);
132     acc->create(acc_cfg);
133     
134     pj_thread_sleep(2000);
135     
136     // Make outgoing call
137     Call *call = new MyCall(*acc);
138     acc->calls.push_back(call);
139     CallOpParam prm(true);
140     prm.opt.audioCount = 1;
141     prm.opt.videoCount = 0;
142     call->makeCall("sip:[email protected]", prm);
143     
144     // Hangup all calls
145     pj_thread_sleep(8000);
146     ep.hangupAllCalls();
147     pj_thread_sleep(4000);
148     
149     // Destroy library
150     std::cout << "*** PJSUA2 SHUTTING DOWN ***" << std::endl;
151 }
152 
153 static void mainProg2() throw(Error)
154 {
155     string json_str;
156     {
157     EpConfig epCfg;
158     JsonDocument jDoc;
159 
160     epCfg.uaConfig.maxCalls = 61;
161     epCfg.uaConfig.userAgent = "Just JSON Test";
162     epCfg.uaConfig.stunServer.push_back("stun1.pjsip.org");
163     epCfg.uaConfig.stunServer.push_back("stun2.pjsip.org");
164     epCfg.logConfig.filename = "THE.LOG";
165 
166     jDoc.writeObject(epCfg);
167     json_str = jDoc.saveString();
168     std::cout << json_str << std::endl << std::endl;
169     }
170 
171     {
172     EpConfig epCfg;
173     JsonDocument rDoc;
174     string output;
175 
176     rDoc.loadString(json_str);
177     rDoc.readObject(epCfg);
178 
179     JsonDocument wDoc;
180 
181     wDoc.writeObject(epCfg);
182     json_str = wDoc.saveString();
183     std::cout << json_str << std::endl << std::endl;
184 
185     wDoc.saveFile("jsontest.js");
186     }
187 
188     {
189     EpConfig epCfg;
190     JsonDocument rDoc;
191 
192     rDoc.loadFile("jsontest.js");
193     rDoc.readObject(epCfg);
194     pj_file_delete("jsontest.js");
195     }
196 }
197 
198 
199 static void mainProg3(Endpoint &ep) throw(Error)
200 {
201     const char *paths[] = { "../../../../tests/pjsua/wavs/input.16.wav",
202                 "../../tests/pjsua/wavs/input.16.wav",
203                 "input.16.wav"};
204     unsigned i;
205     const char *filename = NULL;
206 
207     // Init library
208     EpConfig ep_cfg;
209     ep.libInit( ep_cfg );
210 
211     for (i=0; i<PJ_ARRAY_SIZE(paths); ++i) {
212        if (pj_file_exists(paths[i])) {
213           filename = paths[i];
214           break;
215        }
216     }
217 
218     if (!filename) {
219     PJSUA2_RAISE_ERROR3(PJ_ENOTFOUND, "mainProg3()",
220                "Could not locate input.16.wav");
221     }
222 
223     // Start library
224     ep.libStart();
225     std::cout << "*** PJSUA2 STARTED ***" << std::endl;
226 
227     // Create player and recorder
228     {
229     AudioMediaPlayer amp;
230     amp.createPlayer(filename);
231 
232     AudioMediaRecorder amr;
233     amr.createRecorder("recorder_test_output.wav");
234 
235     amp.startTransmit(ep.audDevManager().getPlaybackDevMedia());
236     amp.startTransmit(amr);
237 
238     pj_thread_sleep(5000);
239     }
240 }
241 
242 
243 static void mainProg() throw(Error)
244 {
245     string json_str;
246 
247     {
248     JsonDocument jdoc;
249     AccountConfig accCfg;
250 
251     accCfg.idUri = "\"Just Test\" <sip:[email protected]>";
252     accCfg.regConfig.registrarUri = "sip:sip.pjsip.org";
253     SipHeader h;
254     h.hName = "X-Header";
255     h.hValue = "User header";
256     accCfg.regConfig.headers.push_back(h);
257 
258     accCfg.sipConfig.proxies.push_back("<sip:sip.pjsip.org;transport=tcp>");
259     accCfg.sipConfig.proxies.push_back("<sip:sip.pjsip.org;transport=tls>");
260 
261     accCfg.mediaConfig.transportConfig.tlsConfig.ciphers.push_back(1);
262     accCfg.mediaConfig.transportConfig.tlsConfig.ciphers.push_back(2);
263     accCfg.mediaConfig.transportConfig.tlsConfig.ciphers.push_back(3);
264 
265     AuthCredInfo aci;
266     aci.scheme = "digest";
267     aci.username = "test";
268     aci.data = "passwd";
269     aci.realm = "*";
270     accCfg.sipConfig.authCreds.push_back(aci);
271 
272     jdoc.writeObject(accCfg);
273     json_str = jdoc.saveString();
274     std::cout << "Original:" << std::endl;
275     std::cout << json_str << std::endl << std::endl;
276     }
277 
278     {
279     JsonDocument rdoc;
280 
281     rdoc.loadString(json_str);
282     AccountConfig accCfg;
283     rdoc.readObject(accCfg);
284 
285     JsonDocument wdoc;
286     wdoc.writeObject(accCfg);
287     json_str = wdoc.saveString();
288 
289     std::cout << "Parsed:" << std::endl;
290     std::cout << json_str << std::endl << std::endl;
291     }
292 }
293 
294 
295 static void mainProg4(Endpoint &ep) throw(Error)
296 {
297     // Init library
298     EpConfig ep_cfg;
299     ep.libInit( ep_cfg );
300 
301     // Create transport
302     TransportConfig tcfg;
303     tcfg.port = 5060;
304     ep.transportCreate(PJSIP_TRANSPORT_UDP, tcfg);
305     ep.transportCreate(PJSIP_TRANSPORT_TCP, tcfg);
306 
307     // Add account
308     AccountConfig acc_cfg;
309     acc_cfg.idUri = "sip:localhost";
310     std::auto_ptr<MyAccount> acc(new MyAccount);
311     acc->create(acc_cfg);
312 
313     // Start library
314     ep.libStart();
315     std::cout << "*** PJSUA2 STARTED ***" << std::endl;
316 
317     // Just wait for ENTER key
318     std::cout << "Press ENTER to quit..." << std::endl;
319     std::cin.get();
320 }
321 
322 
323 int main()
324 {
325     int ret = 0;
326     Endpoint ep;
327 
328     try {
329     ep.libCreate();
330 
331     mainProg4(ep);
332     ret = PJ_SUCCESS;
333     } catch (Error & err) {
334     std::cout << "Exception: " << err.info() << std::endl;
335     ret = 1;
336     }
337 
338     try {
339     ep.libDestroy();
340     } catch(Error &err) {
341     std::cout << "Exception: " << err.info() << std::endl;
342     ret = 1;
343     }
344 
345     if (ret == PJ_SUCCESS) {
346     std::cout << "Success" << std::endl;
347     } else {
348     std::cout << "Error Found" << std::endl;
349     }
350 
351     return ret;
352 }
复制代码

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

9.1.2 Python GUI

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

复制代码
  1 # $Id: application.py 4798 2014-03-19 21:20:17Z bennylp $
  2 #
  3 # pjsua Python GUI Demo
  4 #
  5 # Copyright (C)2013 Teluu Inc. (http://www.teluu.com)
  6 #
  7 # This program is free software; you can redistribute it and/or modify
  8 # it under the terms of the GNU General Public License as published by
  9 # the Free Software Foundation; either version 2 of the License, or
 10 # (at your option) any later version.
 11 #
 12 # This program is distributed in the hope that it will be useful,
 13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 15 # GNU General Public License for more details.
 16 #
 17 # You should have received a copy of the GNU General Public License
 18 # along with this program; if not, write to the Free Software
 19 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 20 #
 21 import sys
 22 if sys.version_info[0] >= 3: # Python 3
 23     import tkinter as tk
 24     from tkinter import ttk
 25     from tkinter import messagebox as msgbox
 26 else:
 27     import Tkinter as tk
 28     import tkMessageBox as msgbox
 29     import ttk
 30 
 31 import pjsua2 as pj
 32 import log
 33 import accountsetting
 34 import account
 35 import buddy
 36 import endpoint
 37 import settings
 38 
 39 import os
 40 import traceback
 41 
 42 # You may try to enable pjsua worker thread by setting USE_THREADS below to True *and*
 43 # recreate the swig module with adding -threads option to swig (uncomment USE_THREADS 
 44 # in swig/python/Makefile). In my experiment this would crash Python as reported in:
 45 # http://lists.pjsip.org/pipermail/pjsip_lists.pjsip.org/2014-March/017223.html
 46 USE_THREADS = False
 47     
 48 class Application(ttk.Frame):
 49     """
 50     The Application main frame.
 51     """
 52     def __init__(self):
 53         global USE_THREADS
 54         ttk.Frame.__init__(self, name='application', width=300, height=500)
 55         self.pack(expand='yes', fill='both')
 56         self.master.title('pjsua2 Demo')
 57         self.master.geometry('500x500+100+100')
 58         
 59         # Logger
 60         self.logger = log.Logger()
 61         
 62         # Accounts
 63         self.accList = []
 64         
 65         # GUI variables
 66         self.showLogWindow = tk.IntVar(value=0)
 67         self.quitting = False 
 68         
 69         # Construct GUI
 70         self._createWidgets()
 71         
 72         # Log window
 73         self.logWindow = log.LogWindow(self)
 74         self._onMenuShowHideLogWindow()
 75         
 76         # Instantiate endpoint
 77         self.ep = endpoint.Endpoint()
 78         self.ep.libCreate()
 79         
 80         # Default config
 81         self.appConfig = settings.AppConfig()
 82         if USE_THREADS:
 83             self.appConfig.epConfig.uaConfig.threadCnt = 1
 84             self.appConfig.epConfig.uaConfig.mainThreadOnly = False
 85         else:
 86             self.appConfig.epConfig.uaConfig.threadCnt = 0
 87             self.appConfig.epConfig.uaConfig.mainThreadOnly = True
 88         self.appConfig.epConfig.logConfig.writer = self.logger
 89         self.appConfig.epConfig.logConfig.filename = "pygui.log"
 90         self.appConfig.epConfig.logConfig.fileFlags = pj.PJ_O_APPEND
 91         self.appConfig.epConfig.logConfig.level = 5
 92         self.appConfig.epConfig.logConfig.consoleLevel = 5
 93         
 94     def saveConfig(self, filename='pygui.js'):
 95         # Save disabled accounts since they are not listed in self.accList
 96         disabled_accs = [ac for ac in self.appConfig.accounts if not ac.enabled]
 97         self.appConfig.accounts = []
 98         
 99         # Get account configs from active accounts
100         for acc in self.accList:
101             acfg = settings.AccConfig()
102             acfg.enabled = True
103             acfg.config = acc.cfg
104             for bud in acc.buddyList:
105                 acfg.buddyConfigs.append(bud.cfg)
106             self.appConfig.accounts.append(acfg)
107         
108         # Put back disabled accounts
109         self.appConfig.accounts.extend(disabled_accs)
110         # Save
111         self.appConfig.saveFile(filename)
112     
113     def start(self, cfg_file='pygui.js'):
114         global USE_THREADS
115         # Load config
116         if cfg_file and os.path.exists(cfg_file):
117             self.appConfig.loadFile(cfg_file)
118 
119         if USE_THREADS:
120             self.appConfig.epConfig.uaConfig.threadCnt = 1
121             self.appConfig.epConfig.uaConfig.mainThreadOnly = False
122         else:
123             self.appConfig.epConfig.uaConfig.threadCnt = 0
124             self.appConfig.epConfig.uaConfig.mainThreadOnly = True
125         self.appConfig.epConfig.uaConfig.threadCnt = 0
126         self.appConfig.epConfig.uaConfig.mainThreadOnly = True
127         self.appConfig.epConfig.logConfig.writer = self.logger
128         self.appConfig.epConfig.logConfig.level = 5
129         self.appConfig.epConfig.logConfig.consoleLevel = 5
130                 
131         # Initialize library
132         self.appConfig.epConfig.uaConfig.userAgent = "pygui-" + self.ep.libVersion().full;
133         self.ep.libInit(self.appConfig.epConfig)
134         self.master.title('pjsua2 Demo version ' + self.ep.libVersion().full)
135         
136         # Create transports
137         if self.appConfig.udp.enabled:
138             self.ep.transportCreate(self.appConfig.udp.type, self.appConfig.udp.config)
139         if self.appConfig.tcp.enabled:
140             self.ep.transportCreate(self.appConfig.tcp.type, self.appConfig.tcp.config)
141         if self.appConfig.tls.enabled:
142             self.ep.transportCreate(self.appConfig.tls.type, self.appConfig.tls.config)
143             
144         # Add accounts
145         for cfg in self.appConfig.accounts:
146             if cfg.enabled:
147                 self._createAcc(cfg.config)
148                 acc = self.accList[-1]
149                 for buddy_cfg in cfg.buddyConfigs:
150                     self._createBuddy(acc, buddy_cfg)
151                 
152         # Start library
153         self.ep.libStart()
154         
155         # Start polling
156         if not USE_THREADS:
157             self._onTimer()
158 
159     def updateAccount(self, acc):
160         if acc.deleting:
161             return    # ignore
162         iid = str(acc.randId)
163         text = acc.cfg.idUri
164         status = acc.statusText()
165         
166         values = (status,)
167         if self.tv.exists(iid):
168             self.tv.item(iid, text=text, values=values)
169         else:
170             self.tv.insert('', 'end',  iid, open=True, text=text, values=values)
171         
172     def updateBuddy(self, bud):
173         iid = 'buddy' + str(bud.randId)
174         text = bud.cfg.uri
175         status = bud.statusText()
176         
177         values = (status,)
178         if self.tv.exists(iid):
179             self.tv.item(iid, text=text, values=values)
180         else:
181             self.tv.insert(str(bud.account.randId), 'end',  iid, open=True, text=text, values=values)
182         
183     def _createAcc(self, acc_cfg):
184         acc = account.Account(self)
185         acc.cfg = acc_cfg
186         self.accList.append(acc)
187         self.updateAccount(acc)
188         acc.create(acc.cfg)
189         acc.cfgChanged = False
190         self.updateAccount(acc)
191                 
192     def _createBuddy(self, acc, buddy_cfg):
193         bud = buddy.Buddy(self)
194         bud.cfg = buddy_cfg
195         bud.account = acc
196         bud.create(acc, bud.cfg)
197         self.updateBuddy(bud)
198         acc.buddyList.append(bud)
199 
200     def _createWidgets(self):
201         self._createAppMenu()
202         
203         # Main pane, a Treeview
204         self.tv = ttk.Treeview(self, columns=('Status'), show='tree')
205         self.tv.pack(side='top', fill='both', expand='yes', padx=5, pady=5)
206 
207         self._createContextMenu()
208         
209         # Handle close event
210         self.master.protocol("WM_DELETE_WINDOW", self._onClose)
211     
212     def _createAppMenu(self):
213         # Main menu bar
214         top = self.winfo_toplevel()
215         self.menubar = tk.Menu()
216         top.configure(menu=self.menubar)
217         
218         # File menu
219         file_menu = tk.Menu(self.menubar, tearoff=False)
220         self.menubar.add_cascade(label="File", menu=file_menu)
221         file_menu.add_command(label="Add account..", command=self._onMenuAddAccount)
222         file_menu.add_checkbutton(label="Show/hide log window", command=self._onMenuShowHideLogWindow, variable=self.showLogWindow)
223         file_menu.add_separator()
224         file_menu.add_command(label="Settings...", command=self._onMenuSettings)
225         file_menu.add_command(label="Save Settings", command=self._onMenuSaveSettings)
226         file_menu.add_separator()
227         file_menu.add_command(label="Quit", command=self._onMenuQuit)
228 
229         # Window menu
230         self.window_menu = tk.Menu(self.menubar, tearoff=False)
231         self.menubar.add_cascade(label="Window", menu=self.window_menu)
232         
233         # Help menu
234         help_menu = tk.Menu(self.menubar, tearoff=False)
235         self.menubar.add_cascade(label="Help", menu=help_menu)
236         help_menu.add_command(label="About", underline=2, command=self._onMenuAbout)
237     
238     def _showChatWindow(self, chat_inst):
239         chat_inst.showWindow()
240         
241     def updateWindowMenu(self):
242         # Chat windows
243         self.window_menu.delete(0, tk.END)
244         for acc in self.accList:
245             for c in acc.chatList:
246                 cmd = lambda arg=c: self._showChatWindow(arg)
247                 self.window_menu.add_command(label=c.title, command=cmd)
248         
249     def _createContextMenu(self):
250         top = self.winfo_toplevel()
251 
252         # Create Account context menu
253         self.accMenu = tk.Menu(top, tearoff=False)
254         # Labels, must match with _onAccContextMenu()
255         labels = ['Unregister', 'Reregister', 'Add buddy...', '-',
256               'Online', 'Invisible', 'Away', 'Busy', '-',
257               'Settings...', '-',
258               'Delete...']
259         for label in labels:
260             if label=='-':
261                 self.accMenu.add_separator()
262             else:
263                 cmd = lambda arg=label: self._onAccContextMenu(arg)
264                 self.accMenu.add_command(label=label, command=cmd)
265         
266         # Create Buddy context menu
267         # Labels, must match with _onBuddyContextMenu()
268         self.buddyMenu = tk.Menu(top, tearoff=False)
269         labels = ['Audio call', 'Send instant message', '-',
270               'Subscribe', 'Unsubscribe', '-',
271               'Settings...', '-',
272               'Delete...']
273         
274         for label in labels:
275             if label=='-':
276                 self.buddyMenu.add_separator()
277             else:
278                 cmd = lambda arg=label: self._onBuddyContextMenu(arg)
279                 self.buddyMenu.add_command(label=label, command=cmd)
280         
281         if (top.tk.call('tk', 'windowingsystem')=='aqua'):
282             self.tv.bind('<2>', self._onTvRightClick)
283             self.tv.bind('<Control-1>', self._onTvRightClick)
284         else:
285             self.tv.bind('<3>', self._onTvRightClick)
286         self.tv.bind('<Double-Button-1>', self._onTvDoubleClick)
287 
288     def _getSelectedAccount(self):
289         items = self.tv.selection()
290         if not items:
291             return None
292         try:
293             iid = int(items[0])
294         except:
295             return None
296         accs = [acc for acc in self.accList if acc.randId==iid]
297         if not accs:
298             return None
299         return accs[0]
300     
301     def _getSelectedBuddy(self):
302         items = self.tv.selection()
303         if not items:
304             return None
305         try:
306             iid = int(items[0][5:])
307             iid_parent = int(self.tv.parent(items[0]))
308         except:
309             return None
310             
311         accs = [acc for acc in self.accList if acc.randId==iid_parent]
312         if not accs:
313             return None
314             
315         buds = [b for b in accs[0].buddyList if b.randId==iid]
316         if not buds:
317             return None
318             
319         return buds[0]
320     
321     def _onTvRightClick(self, event):
322         iid = self.tv.identify_row(event.y)
323         #iid = self.tv.identify('item', event.x, event.y)
324         if iid:
325             self.tv.selection_set( (iid,) )
326             acc = self._getSelectedAccount()
327             if acc:
328                 self.accMenu.post(event.x_root, event.y_root)
329             else:
330                 # A buddy is selected
331                 self.buddyMenu.post(event.x_root, event.y_root)
332     
333     def _onTvDoubleClick(self, event):
334         iid = self.tv.identify_row(event.y)
335         if iid:
336             self.tv.selection_set( (iid,) )
337             acc = self._getSelectedAccount()
338             if acc:
339                 self.cfgChanged = False
340                 dlg = accountsetting.Dialog(self.master, acc.cfg)
341                 if dlg.doModal():
342                     self.updateAccount(acc)
343                     acc.modify(acc.cfg)
344             else:
345                 bud = self._getSelectedBuddy()
346                 acc = bud.account
347                 chat = acc.findChat(bud.cfg.uri)
348                 if not chat:
349                     chat = acc.newChat(bud.cfg.uri)
350                 chat.showWindow()
351     
352     def _onAccContextMenu(self, label):
353         acc = self._getSelectedAccount()
354         if not acc:
355             return
356         
357         if label=='Unregister':
358             acc.setRegistration(False)
359         elif label=='Reregister':
360             acc.setRegistration(True)
361         elif label=='Online':
362             ps = pj.PresenceStatus()
363             ps.status = pj.PJSUA_BUDDY_STATUS_ONLINE
364             acc.setOnlineStatus(ps)
365         elif label=='Invisible':
366             ps = pj.PresenceStatus()
367             ps.status = pj.PJSUA_BUDDY_STATUS_OFFLINE
368             acc.setOnlineStatus(ps)
369         elif label=='Away':
370             ps = pj.PresenceStatus()
371             ps.status = pj.PJSUA_BUDDY_STATUS_ONLINE
372             ps.activity = pj.PJRPID_ACTIVITY_AWAY
373             ps.note = "Away"
374             acc.setOnlineStatus(ps)
375         elif label=='Busy':
376             ps = pj.PresenceStatus()
377             ps.status = pj.PJSUA_BUDDY_STATUS_ONLINE
378             ps.activity = pj.PJRPID_ACTIVITY_BUSY
379             ps.note = "Busy"
380             acc.setOnlineStatus(ps)
381         elif label=='Settings...':
382             self.cfgChanged = False
383             dlg = accountsetting.Dialog(self.master, acc.cfg)
384             if dlg.doModal():
385                 self.updateAccount(acc)
386                 acc.modify(acc.cfg)
387         elif label=='Delete...':
388             msg = "Do you really want to delete account '%s'?" % acc.cfg.idUri
389             if msgbox.askquestion('Delete account?', msg, default=msgbox.NO) != u'yes':
390                 return
391             iid = str(acc.randId)
392             self.accList.remove(acc)
393             acc.setRegistration(False)
394             acc.deleting = True
395             del acc
396             self.tv.delete( (iid,) )
397         elif label=='Add buddy...':
398             cfg = pj.BuddyConfig()
399             dlg = buddy.SettingDialog(self.master, cfg)
400             if dlg.doModal():
401                 self._createBuddy(acc, cfg)
402         else:
403             assert not ("Unknown menu " + label)
404     
405     def _onBuddyContextMenu(self, label):
406         bud = self._getSelectedBuddy()
407         if not bud:
408             return
409         acc = bud.account
410             
411         if label=='Audio call':
412             chat = acc.findChat(bud.cfg.uri)
413             if not chat: chat = acc.newChat(bud.cfg.uri)
414             chat.showWindow()
415             chat.startCall()
416         elif label=='Send instant message':
417             chat = acc.findChat(bud.cfg.uri)
418             if not chat: chat = acc.newChat(bud.cfg.uri)
419             chat.showWindow(True)
420         elif label=='Subscribe':
421             bud.subscribePresence(True)
422         elif label=='Unsubscribe':
423             bud.subscribePresence(False)
424         elif label=='Settings...':
425             subs = bud.cfg.subscribe
426             uri  = bud.cfg.uri
427             dlg = buddy.SettingDialog(self.master, bud.cfg)
428             if dlg.doModal():
429                 self.updateBuddy(bud)
430                 # URI updated?
431                 if uri != bud.cfg.uri:
432                     cfg = bud.cfg
433                     # del old
434                     iid = 'buddy' + str(bud.randId)
435                     acc.buddyList.remove(bud)
436                     del bud
437                     self.tv.delete( (iid,) )
438                     # add new
439                     self._createBuddy(acc, cfg)
440                 # presence subscribe setting updated
441                 elif subs != bud.cfg.subscribe:
442                     bud.subscribePresence(bud.cfg.subscribe)
443         elif label=='Delete...':
444             msg = "Do you really want to delete buddy '%s'?" % bud.cfg.uri
445             if msgbox.askquestion('Delete buddy?', msg, default=msgbox.NO) != u'yes':
446                 return
447             iid = 'buddy' + str(bud.randId)
448             acc.buddyList.remove(bud)
449             del bud
450             self.tv.delete( (iid,) )
451         else:
452             assert not ("Unknown menu " + label)
453             
454     def _onTimer(self):
455         if not self.quitting:
456             self.ep.libHandleEvents(10)
457             if not self.quitting:
458                 self.master.after(50, self._onTimer)
459             
460     def _onClose(self):
461         self.saveConfig()
462         self.quitting = True
463         self.ep.libDestroy()
464         self.ep = None
465         self.update()
466         self.quit()
467         
468     def _onMenuAddAccount(self):
469         cfg = pj.AccountConfig()
470         dlg = accountsetting.Dialog(self.master, cfg)
471         if dlg.doModal():
472             self._createAcc(cfg)
473             
474     def _onMenuShowHideLogWindow(self):
475         if self.showLogWindow.get():
476             self.logWindow.deiconify()
477         else:
478             self.logWindow.withdraw()
479     
480     def _onMenuSettings(self):
481         dlg = settings.Dialog(self, self.appConfig)
482         if dlg.doModal():
483             msgbox.showinfo(self.master.title(), 'You need to restart for new settings to take effect')
484     
485     def _onMenuSaveSettings(self):
486         self.saveConfig()
487         
488     def _onMenuQuit(self):
489         self._onClose()
490 
491     def _onMenuAbout(self):
492         msgbox.showinfo(self.master.title(), 'About')
493         
494 
495 class ExceptionCatcher:
496     """Custom Tk exception catcher, mainly to display more information 
497        from pj.Error exception
498     """ 
499     def __init__(self, func, subst, widget):
500         self.func = func 
501         self.subst = subst
502         self.widget = widget
503     def __call__(self, *args):
504         try:
505             if self.subst:
506                 args = apply(self.subst, args)
507             return apply(self.func, args)
508         except pj.Error, error:
509             print 'Exception:'
510             print '  ', error.info()
511             print 'Traceback:'
512             print traceback.print_stack()
513             log.writeLog2(1, 'Exception: ' + error.info() + '\n')
514         except Exception, error:
515             print 'Exception:'
516             print '  ', str(error)
517             print 'Traceback:'
518             print traceback.print_stack()
519             log.writeLog2(1, 'Exception: ' + str(error) + '\n')
520 
521 def main():
522     #tk.CallWrapper = ExceptionCatcher
523     app = Application()
524     app.start()
525     app.mainloop()
526         
527 if __name__ == '__main__':
528     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)文件

发表评论

您的电子邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部