• PJSUA2开发文档--第九章 PJSUA2应用程序示例


    9. PJSUA2示例应用程序

    9.1 示例应用程序

    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:test1@pjsip.org";
    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:test1@pjsip.org", 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:test@pjsip.org>";
    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 }
    View Code

     二进制文件位于 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() + '
    ')
    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) + '
    ')
    520 
    521 def main():
    522     #tk.CallWrapper = ExceptionCatcher
    523     app = Application()
    524     app.start()
    525     app.mainloop()
    526         
    527 if __name__ == '__main__':
    528     main()
    View Code

    需要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)文件

  • 相关阅读:
    jvm内存溢出问题
    表单提交后台正则表达式验证
    java项目改为web项目
    图片按像素压缩
    系统和压缩内存占用空间大
    teamview修改id
    新浪天气api
    SpringBoot+MySql+Mybatis+ thymeleaf 搭建个人博客
    FFmpeg的简单使用
    禁止f12及浏览器右键查看
  • 原文地址:https://www.cnblogs.com/mobilecard/p/6733452.html
Copyright © 2020-2023  润新知