Changeset 4686


Ignore:
Timestamp:
Dec 5, 2013 11:04:36 AM (6 years ago)
Author:
nanang
Message:

Re #1708:

  • Changed participant managements in Chat, e.g: changed Buddy based to URI based, create temporary Buddy for incoming call/IM from non-buddy.
  • Implemented simple media features in chat window (mute, volume, codec info).
  • Misc: added window menu listing chat windows, changed behaviour on closing chat window, etc.
Location:
pjproject/branches/projects/pjsua2/pjsip-apps/src/pygui
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • pjproject/branches/projects/pjsua2/pjsip-apps/src/pygui/account.py

    r4677 r4686  
    5252                self.deleting = False 
    5353 
    54         def findBuddy(self, uri): 
     54        """ 
     55        def findBuddy2(self, uri): 
    5556                # TODO: proper URI comparison 
    5657                for bud in self.buddyList: 
     
    5859                                return bud 
    5960                return None 
    60                  
    61         def findChat(self, buddy, call_inst = None): 
     61        """ 
     62         
     63        def findChat(self, uri_str, call_inst = None): 
     64                uri = ch.ParseSipUri(uri_str) 
     65                if not uri: return None 
     66                         
    6267                for chat in self.chatList: 
    63                         if chat.isBuddyParticipant(buddy): 
     68                        if chat.isUriParticipant(uri): 
    6469                                if call_inst and chat.isCallRegistered(call_inst): 
    6570                                        return chat 
     
    6873                return None 
    6974         
    70         def newChat(self, buddy): 
    71                 chat = ch.Chat(self.app, self, buddy) 
     75        def newChat(self, uri_str): 
     76                uri = ch.ParseSipUri(uri_str) 
     77                if not uri: return None 
     78                         
     79                chat = ch.Chat(self.app, self, uri) 
    7280                self.chatList.append(chat) 
     81                self.app.updateWindowMenu() 
    7382                return chat 
    7483         
     
    113122                        c.answer(call_prm) 
    114123                         
    115                         # create chat instance 
    116                         bud = self.findBuddy(ci.remoteUri) 
    117                         if not bud: 
    118                                 print "=== Incoming call from '%s': cannot find buddy" % ci.remoteUri 
    119                                 return 
    120                         chat = self.findChat(bud) 
    121                         if not chat: chat = self.newChat(bud) 
     124                        # find/create chat instance 
     125                        chat = self.findChat(ci.remoteUri) 
     126                        if not chat: chat = self.newChat(ci.remoteUri) 
    122127                         
    123128                        chat.showWindow() 
    124                         chat.registerCall(bud, c) 
     129                        chat.registerCall(ci.remoteUri, c) 
    125130                        chat.updateCallState(c, ci) 
    126131                else: 
     
    128133                         
    129134        def onInstantMessage(self, prm): 
    130                 bud = self.findBuddy(prm.fromUri) 
    131                 if not bud: 
    132                         print "=== Incoming IM from '%s': cannot find buddy" % prm.fromUri 
    133                         return 
    134                 chat = self.findChat(bud) 
    135                 if not chat: chat = self.newChat(bud) 
    136                          
     135                chat = self.findChat(prm.fromUri) 
     136                if not chat: chat = self.newChat(prm.fromUri) 
     137                 
    137138                chat.showWindow() 
    138                 chat.addMessage(bud.cfg.uri, prm.msgBody) 
     139                chat.addMessage(prm.fromUri, prm.msgBody) 
    139140                 
    140141        def onInstantMessageStatus(self, prm): 
    141142                if prm.code/100 == 2: return 
    142143                 
    143                 bud = self.findBuddy(prm.toUri) 
    144                 if not bud: return 
    145                 chat = self.findChat(bud) 
    146                 if not chat: return 
    147                  
    148                 chat.addMessage(None, "Failed sending message to '%s': %s" % (bud.cfg.uri, prm.reason)) 
     144                chat = self.findChat(prm.toUri) 
     145                if not chat: 
     146                        print "=== IM status to '%s' cannot find chat" % prm.toUri 
     147                        return 
     148                 
     149                chat.addMessage(None, "Failed sending message to '%s': %s" % (prm.toUri, prm.reason)) 
    149150                 
    150151        def onTypingIndication(self, prm): 
    151                 bud = self.findBuddy(prm.fromUri) 
    152                 if not bud: 
    153                         print "=== Incoming typing indication from '%s': cannot find buddy" % prm.fromUri 
    154                         return 
    155                 chat = self.findChat(bud) 
    156                 if not chat: return 
    157                  
    158                 chat.setTypingIndication(bud.cfg.uri, prm.isTyping) 
     152                chat = self.findChat(prm.fromUri) 
     153                if not chat: 
     154                        print "=== Incoming typing indication from '%s' cannot find chat" % prm.fromUri 
     155                        return 
     156                 
     157                chat.setTypingIndication(prm.fromUri, prm.isTyping) 
    159158 
    160159                 
  • pjproject/branches/projects/pjsua2/pjsip-apps/src/pygui/application.py

    r4679 r4686  
    209209                file_menu.add_command(label="Quit", command=self._onMenuQuit) 
    210210 
     211                # Window menu 
     212                self.window_menu = tk.Menu(self.menubar, tearoff=False) 
     213                self.menubar.add_cascade(label="Window", menu=self.window_menu) 
     214                 
    211215                # Help menu 
    212216                help_menu = tk.Menu(self.menubar, tearoff=False) 
    213217                self.menubar.add_cascade(label="Help", menu=help_menu) 
    214218                help_menu.add_command(label="About", underline=2, command=self._onMenuAbout) 
     219         
     220        def _showChatWindow(self, chat_inst): 
     221                chat_inst.showWindow() 
     222                 
     223        def updateWindowMenu(self): 
     224                # Chat windows 
     225                self.window_menu.delete(0, tk.END) 
     226                for acc in self.accList: 
     227                        for c in acc.chatList: 
     228                                cmd = lambda arg=c: self._showChatWindow(arg) 
     229                                self.window_menu.add_command(label=c.title, command=cmd) 
    215230                 
    216231        def _createContextMenu(self): 
     
    312327                                bud = self._getSelectedBuddy() 
    313328                                acc = bud.account 
    314                                 chat = acc.findChat(bud) 
     329                                chat = acc.findChat(bud.cfg.uri) 
    315330                                if not chat: 
    316                                         chat = acc.newChat(bud) 
     331                                        chat = acc.newChat(bud.cfg.uri) 
    317332                                chat.showWindow() 
    318333         
     
    377392                         
    378393                if label=='Audio call': 
    379                         chat = acc.findChat(bud) 
    380                         if not chat: chat = acc.newChat(bud) 
     394                        chat = acc.findChat(bud.cfg.uri) 
     395                        if not chat: chat = acc.newChat(bud.cfg.uri) 
    381396                        chat.showWindow() 
    382397                        chat.startCall() 
    383398                elif label=='Send instant message': 
    384                         chat = acc.findChat(bud) 
    385                         if not chat: chat = acc.newChat(bud) 
     399                        chat = acc.findChat(bud.cfg.uri) 
     400                        if not chat: chat = acc.newChat(bud.cfg.uri) 
    386401                        chat.showWindow() 
    387402                elif label=='Subscribe': 
     
    493508                 
    494509if __name__ == '__main__': 
    495         print pj 
    496510        main() 
  • pjproject/branches/projects/pjsua2/pjsip-apps/src/pygui/call.py

    r4679 r4686  
    3232import pjsua2 as pj 
    3333import application 
     34import endpoint as ep 
    3435 
    3536# Call class 
     
    4445                self.chat = chat 
    4546                self.connected = False 
     47                self.onhold = False 
    4648 
    4749        def onCallState(self, prm): 
     
    5860                           mi.status == pj.PJSUA_CALL_MEDIA_REMOTE_HOLD): 
    5961                                m = self.getMedia(mi.index) 
    60                                 print m 
     62                                am = pj.AudioMedia.typecastFromMedia(m) 
     63                                # connect ports 
     64                                ep.Endpoint.instance.audDevManager().getCaptureDevMedia().startTransmit(am) 
     65                                am.startTransmit(ep.Endpoint.instance.audDevManager().getPlaybackDevMedia()) 
     66 
     67                                if mi.status == pj.PJSUA_CALL_MEDIA_REMOTE_HOLD and not self.onhold: 
     68                                        self.chat.addMessage(None, "'%s' sets call onhold" % (self.peerUri)) 
     69                                        self.onhold = True 
     70                                elif mi.status == pj.PJSUA_CALL_MEDIA_ACTIVE and self.onhold: 
     71                                        self.chat.addMessage(None, "'%s' sets call active" % (self.peerUri)) 
     72                                        self.onhold = False 
    6173                         
    6274        def onInstantMessage(self, prm): 
  • pjproject/branches/projects/pjsua2/pjsip-apps/src/pygui/chat.py

    r4677 r4686  
    2727        import ttk 
    2828 
     29import buddy 
    2930import call 
    3031import chatgui as gui 
     32import endpoint as ep 
    3133import pjsua2 as pj 
     34import re 
     35 
     36SipUriRegex = re.compile('(sip|sips):([^:;>\@]*)@?([^:;>]*):?([^:;>]*)') 
     37ConfIdx = 1 
     38 
     39# Simple SIP uri parser, input URI must have been validated 
     40def ParseSipUri(sip_uri_str): 
     41        m = SipUriRegex.search(sip_uri_str) 
     42        if not m: 
     43                assert(0) 
     44                return None 
     45         
     46        scheme = m.group(1) 
     47        user = m.group(2) 
     48        host = m.group(3) 
     49        port = m.group(4) 
     50        if host == '': 
     51                host = user 
     52                user = '' 
     53                 
     54        return SipUri(scheme.lower(), user, host.lower(), port) 
     55         
     56class SipUri: 
     57        def __init__(self, scheme, user, host, port): 
     58                self.scheme = scheme 
     59                self.user = user 
     60                self.host = host 
     61                self.port = port 
     62                 
     63        def __cmp__(self, sip_uri): 
     64                if self.scheme == sip_uri.scheme and self.user == sip_uri.user and self.host == sip_uri.host: 
     65                        # don't check port, at least for now 
     66                        return 0 
     67                return -1 
     68         
     69        def __str__(self): 
     70                s = self.scheme + ':' 
     71                if self.user: s += self.user + '@' 
     72                s += self.host 
     73                if self.port: s+= ':' + self.port 
     74                return s 
    3275         
    3376class Chat(gui.ChatObserver): 
    34         def __init__(self, app, acc, bud, call_inst=None): 
     77        def __init__(self, app, acc, uri, call_inst=None): 
    3578                self._app = app 
    3679                self._acc = acc 
    37                 self._participantList = [] 
    38                 self._callList = [] 
     80                self.title = '' 
     81                 
     82                global ConfIdx 
     83                self.confIdx = ConfIdx 
     84                ConfIdx += 1 
     85                 
     86                # each participant call/buddy instances are stored in call list 
     87                # and buddy list with same index as in particpant list 
     88                self._participantList = []      # list of SipUri 
     89                self._callList = []             # list of Call 
     90                self._buddyList = []            # list of Buddy 
     91                 
    3992                self._gui = gui.ChatFrame(self) 
    40                 self.addParticipant(bud, call_inst) 
     93                self.addParticipant(uri, call_inst) 
    4194         
    4295        def _updateGui(self): 
    4396                if self.isPrivate(): 
    44                         bud = self._participantList[0] 
    45                         self._gui.title(bud.cfg.uri) 
    46                 else: 
    47                         self._gui.title('Conference (%d participants)' % (len(self._participantList))) 
    48  
    49         def _getCallFromBuddy(self, bud): 
    50                 try: 
    51                         idx = self._participantList.index(bud) 
    52                         the_call = self._callList[idx] 
    53                 except: 
     97                        self.title = str(self._participantList[0]) 
     98                else: 
     99                        self.title = 'Conference #%d (%d participants)' % (self.confIdx, len(self._participantList)) 
     100                self._gui.title(self.title) 
     101                self._app.updateWindowMenu() 
     102                 
     103        def _getCallFromUriStr(self, uri_str, op = ''): 
     104                uri = ParseSipUri(uri_str) 
     105                if uri not in self._participantList: 
     106                        print "=== %s cannot find participant with URI '%s'" % (op, uri_str) 
    54107                        return None 
    55                 return the_call 
    56                  
    57         def _getBuddyFromUri(self, uri): 
    58                 for bud in self._participantList: 
    59                         if uri == bud.cfg.uri: 
    60                                 return bud 
     108                idx = self._participantList.index(uri) 
     109                if idx < len(self._callList): 
     110                        return self._callList[idx] 
    61111                return None 
    62                  
    63         def _getCallFromUri(self, uri, op = ''): 
    64                 for idx, bud in enumerate(self._participantList): 
    65                         if uri == bud.cfg.uri: 
    66                                 if idx < len(self._callList): 
    67                                         return self._callList[idx] 
    68                                 return None 
    69                 print "=== %s cannot find buddy URI '%s'" % (op, uri) 
    70                 return None 
    71                  
    72         def _sendTypingIndication(self, is_typing): 
     112         
     113        def _getActiveMediaIdx(self, thecall): 
     114                ci = thecall.getInfo() 
     115                for mi in ci.media: 
     116                        if mi.type == pj.PJMEDIA_TYPE_AUDIO and \ 
     117                          (mi.status != pj.PJSUA_CALL_MEDIA_NONE and \ 
     118                           mi.status != pj.PJSUA_CALL_MEDIA_ERROR): 
     119                                return mi.index 
     120                return -1 
     121                 
     122        def _getAudioMediaFromUriStr(self, uri_str): 
     123                c = self._getCallFromUriStr(uri_str) 
     124                if not c: return None 
     125 
     126                idx = self._getActiveMediaIdx(c) 
     127                if idx < 0: return None 
     128 
     129                m = c.getMedia(idx) 
     130                am = pj.AudioMedia.typecastFromMedia(m) 
     131                return am 
     132                 
     133        def _sendTypingIndication(self, is_typing, sender_uri_str=''): 
     134                sender_uri = ParseSipUri(sender_uri_str) if sender_uri_str else None 
    73135                type_ind_param = pj.SendTypingIndicationParam() 
    74136                type_ind_param.isTyping = is_typing 
    75                 for bud in self._participantList: 
    76                         c = self._getCallFromBuddy(bud) 
    77                         try: 
    78                                 if c and c.connected: 
    79                                         c.sendTypingIndication(type_ind_param) 
    80                                 else: 
    81                                         bud.sendTypingIndication(type_ind_param) 
    82                         except: 
    83                                 pass 
    84  
    85         def _sendInstantMessage(self, msg, sender_uri=''): 
    86                 send_im_param = pj.SendInstantMessageParam() 
    87                 send_im_param.content = str(msg) 
    88                 for bud in self._participantList: 
     137                for idx, p in enumerate(self._participantList): 
    89138                        # don't echo back to the original sender 
    90                         if sender_uri and bud.cfg.uri == sender_uri: 
     139                        if sender_uri and p == sender_uri: 
    91140                                continue 
    92141                                 
    93142                        # send via call, if any, or buddy 
    94                         c = self._getCallFromBuddy(bud) 
     143                        sender = None 
     144                        if self._callList[idx] and self._callList[idx].connected: 
     145                                sender = self._callList[idx] 
     146                        else: 
     147                                sender = self._buddyList[idx] 
     148                        assert(sender) 
     149                                 
    95150                        try: 
    96                                 if c and c.connected: 
    97                                         c.sendInstantMessage(send_im_param) 
    98                                 else: 
    99                                         bud.sendInstantMessage(send_im_param) 
     151                                sender.sendTypingIndication(type_ind_param) 
     152                        except: 
     153                                pass 
     154 
     155        def _sendInstantMessage(self, msg, sender_uri_str=''): 
     156                sender_uri = ParseSipUri(sender_uri_str) if sender_uri_str else None 
     157                send_im_param = pj.SendInstantMessageParam() 
     158                send_im_param.content = str(msg) 
     159                for idx, p in enumerate(self._participantList): 
     160                        # don't echo back to the original sender 
     161                        if sender_uri and p == sender_uri: 
     162                                continue 
     163                                 
     164                        # send via call, if any, or buddy 
     165                        sender = None 
     166                        if self._callList[idx] and self._callList[idx].connected: 
     167                                sender = self._callList[idx] 
     168                        else: 
     169                                sender = self._buddyList[idx] 
     170                        assert(sender) 
     171                         
     172                        try: 
     173                                sender.sendInstantMessage(send_im_param) 
    100174                        except: 
    101175                                # error will be handled via Account::onInstantMessageStatus() 
     
    105179                return len(self._participantList) <= 1 
    106180                 
    107         def isBuddyParticipant(self, bud): 
    108                 return bud in self._participantList 
     181        def isUriParticipant(self, uri): 
     182                return uri in self._participantList 
    109183                 
    110184        def isCallRegistered(self, call_inst): 
    111185                return call_inst in self._callList 
    112186                 
    113         def registerCall(self, bud, call_inst): 
     187        def registerCall(self, uri_str, call_inst): 
     188                uri = ParseSipUri(uri_str) 
    114189                try: 
    115                         idx = self._participantList.index(bud) 
    116                         if len(self._callList) < idx+1: 
    117                                 self._callList.append(call_inst) 
    118                         else: 
    119                                 self._callList[idx] = call_inst 
    120  
     190                        idx = self._participantList.index(uri) 
     191                        bud = self._buddyList[idx] 
     192                        self._callList[idx] = call_inst 
    121193                        call_inst.chat = self 
    122194                        call_inst.peerUri = bud.cfg.uri 
    123195                except: 
    124                         pass 
     196                        assert(0) # idx must be found! 
    125197                 
    126198        def showWindow(self): 
     
    133205                        print b.cfg.uri 
    134206                 
    135         def addParticipant(self, bud, call_inst=None): 
     207        def addParticipant(self, uri, call_inst=None): 
    136208                # avoid duplication 
    137                 if self.isBuddyParticipant(bud): return 
    138                 for b in self._participantList: 
    139                         if bud.cfg.uri == b.cfg.uri: return 
    140                          
     209                if self.isUriParticipant(uri): return 
     210                 
     211                uri_str = str(uri) 
     212                 
     213                # find buddy, create one if not found (e.g: for IM/typing ind), 
     214                # it is a temporary one and not really registered to acc 
     215                bud = None 
     216                try: 
     217                        bud = self._acc.findBuddy(uri_str) 
     218                except: 
     219                        bud = buddy.Buddy(None) 
     220                        bud_cfg = pj.BuddyConfig() 
     221                        bud_cfg.uri = uri_str 
     222                        bud_cfg.subscribe = False 
     223                        bud.create(self._acc, bud_cfg) 
     224                        bud.cfg = bud_cfg 
     225                        bud.account = self._acc 
     226                         
     227                # update URI from buddy URI 
     228                uri = ParseSipUri(bud.cfg.uri) 
     229                 
    141230                # add it 
    142                 self._participantList.append(bud) 
    143                 if call_inst: 
    144                         self._callList.append(call_inst) 
    145                 self._gui.addParticipant(bud.cfg.uri) 
    146  
     231                self._participantList.append(uri) 
     232                self._callList.append(call_inst) 
     233                self._buddyList.append(bud) 
     234                self._gui.addParticipant(str(uri)) 
    147235                self._updateGui() 
    148236         
    149         def kickParticipant(self, bud): 
    150                 if bud in self._participantList: 
    151                         idx = self._participantList.index(bud) 
    152                         self._participantList.remove(bud) 
    153                         self._gui.delParticipant(bud.cfg.uri) 
    154                          
    155                         # also clear call, if any 
    156                         if self._callList: del self._callList[idx] 
    157                          
     237        def kickParticipant(self, uri): 
     238                if (not uri) or (uri not in self._participantList): 
     239                        assert(0) 
     240                        return 
     241                 
     242                idx = self._participantList.index(uri) 
     243                del self._participantList[idx] 
     244                del self._callList[idx] 
     245                del self._buddyList[idx] 
     246                self._gui.delParticipant(str(uri)) 
     247                 
    158248                if self._participantList: 
    159249                        self._updateGui() 
    160250                else: 
    161                         # will remove entry from list eventually destroy this chat? 
    162                         self._acc.chatList.remove(self) 
    163                          
    164                         # let's destroy GUI manually 
    165                         self._gui.destroy() 
    166                         #self.destroy() 
    167                          
    168         def addMessage(self, from_uri, msg): 
    169                 if from_uri: 
    170                         msg = from_uri + ': ' + msg 
     251                        self.onCloseWindow() 
     252                         
     253        def addMessage(self, from_uri_str, msg): 
     254                if from_uri_str: 
     255                        # print message on GUI 
     256                        msg = from_uri_str + ': ' + msg 
    171257                        self._gui.textAddMessage(msg) 
    172                         self._sendInstantMessage(msg, from_uri) 
     258                        # now relay to all participants 
     259                        self._sendInstantMessage(msg, from_uri_str) 
    173260                else: 
    174261                        self._gui.textAddMessage(msg, False) 
    175262                         
    176         def setTypingIndication(self, from_uri, is_typing): 
    177                 self._gui.textSetTypingIndication(from_uri, is_typing) 
     263        def setTypingIndication(self, from_uri_str, is_typing): 
     264                        # notify GUI 
     265                        self._gui.textSetTypingIndication(from_uri_str, is_typing) 
     266                        # now relay to all participants 
     267                        self._sendTypingIndication(is_typing, from_uri_str) 
    178268                 
    179269        def startCall(self): 
     
    182272                call_param.opt.audioCount = 1 
    183273                call_param.opt.videoCount = 0 
    184                 for idx, bud in enumerate(self._participantList): 
     274                for idx, p in enumerate(self._participantList): 
    185275                        # just skip if call is instantiated 
    186276                        if len(self._callList)>=idx+1 and self._callList[idx]: 
    187277                                continue 
     278                         
     279                        uri_str = str(p) 
     280                        c = call.Call(self._acc, uri_str, self) 
     281                        self._callList[idx] = c 
     282                        self._gui.audioUpdateState(uri_str, gui.AudioState.INITIALIZING) 
     283                         
     284                        try: 
     285                                c.makeCall(uri_str, call_param) 
     286                        except: 
     287                                self._callList[idx] = None 
     288                                self._gui.audioUpdateState(bud.cfg.uri, gui.AudioState.FAILED) 
    188289                                 
    189                         c = call.Call(self._acc, bud.cfg.uri, self) 
    190                         if len(self._callList) < idx+1: 
    191                                 self._callList.append(c) 
    192                         else: 
    193                                 self._callList[idx] = c 
    194  
    195                         self._gui.audioUpdateState(bud.cfg.uri, gui.AudioState.INITIALIZING) 
    196                          
    197                         try: 
    198                                 c.makeCall(bud.cfg.uri, call_param) 
    199                         except: 
    200                                 #self._callList[idx] = None 
    201                                 #self._gui.audioUpdateState(bud.cfg.uri, gui.AudioState.FAILED) 
    202                                 self.kickParticipant(bud) 
     290                                # kick the disconnected participant, but the last (avoid zombie chat) 
     291                                if not self.isPrivate(): 
     292                                        self.kickParticipant(p) 
    203293                         
    204294        def stopCall(self): 
    205                 for bud in self._participantList: 
    206                         self._gui.audioUpdateState(bud.cfg.uri, gui.AudioState.DISCONNECTED) 
     295                for p in self._participantList: 
     296                        self._gui.audioUpdateState(str(p), gui.AudioState.DISCONNECTED) 
     297                        if True or not self.isPrivate(): 
     298                                self.kickParticipant(p) 
    207299                 
    208300                # clear call list, calls should be auto-destroyed by GC (and hungup by destructor) 
    209                 del self._callList[:] 
     301                #for idx, c in enumerate(self._callList): 
     302                #       self._callList[idx] = None 
    210303                 
    211304        def updateCallState(self, thecall, info = None): 
    212305                # info is optional here, just to avoid calling getInfo() twice (in the caller and here) 
    213306                if not info: info = thecall.getInfo() 
     307                 
    214308                if info.state < pj.PJSIP_INV_STATE_CONFIRMED: 
    215309                        self._gui.audioUpdateState(thecall.peerUri, gui.AudioState.INITIALIZING) 
    216310                elif info.state == pj.PJSIP_INV_STATE_CONFIRMED: 
    217311                        self._gui.audioUpdateState(thecall.peerUri, gui.AudioState.CONNECTED) 
     312                        med_idx = self._getActiveMediaIdx(thecall) 
     313                        si = thecall.getStreamInfo(med_idx) 
     314                        stats_str = "Audio codec: %s/%s\n..." % (si.codecName, si.codecClockRate) 
     315                        self._gui.audioSetStatsText(thecall.peerUri, stats_str) 
    218316                elif info.state == pj.PJSIP_INV_STATE_DISCONNECTED: 
    219317                        if info.lastStatusCode/100 != 2: 
     
    233331                        # kick the disconnected participant, but the last (avoid zombie chat) 
    234332                        if not self.isPrivate(): 
    235                                 bud = self._getBuddyFromUri(thecall.peerUri) 
    236                                 if bud: self.kickParticipant(bud) 
     333                                self.kickParticipant(ParseSipUri(thecall.peerUri)) 
    237334 
    238335                         
     
    250347                 
    251348        # Audio 
    252         def onHangup(self, peer_uri): 
    253                 c = self._getCallFromUri(peer_uri, "onHangup()") 
     349        def onHangup(self, peer_uri_str): 
     350                c = self._getCallFromUriStr(peer_uri_str, "onHangup()") 
    254351                if not c: return 
    255352                call_param = pj.CallOpParam() 
    256353                c.hangup(call_param) 
    257354 
    258         def onHold(self, peer_uri): 
    259                 c = self._getCallFromUri(peer_uri, "onHold()") 
     355        def onHold(self, peer_uri_str): 
     356                c = self._getCallFromUriStr(peer_uri_str, "onHold()") 
    260357                if not c: return 
    261358                call_param = pj.CallOpParam() 
    262359                c.setHold(call_param) 
    263360 
    264         def onUnhold(self, peer_uri): 
    265                 c = self._getCallFromUri(peer_uri, "onUnhold()") 
     361        def onUnhold(self, peer_uri_str): 
     362                c = self._getCallFromUriStr(peer_uri_str, "onUnhold()") 
    266363                if not c: return 
     364                 
    267365                call_param = pj.CallOpParam() 
     366                call_param.opt.audioCount = 1 
     367                call_param.opt.videoCount = 0 
     368                call_param.opt.flag |= pj.PJSUA_CALL_UNHOLD 
    268369                c.reinvite(call_param) 
    269370                 
    270         def onRxMute(self, peer_uri, is_muted): 
    271                 pass 
    272         def onRxVol(self, peer_uri, vol_pct): 
    273                 pass 
    274         def onTxMute(self, peer_uri, is_muted): 
    275                 pass 
     371        def onRxMute(self, peer_uri_str, mute): 
     372                am = self._getAudioMediaFromUriStr(peer_uri_str) 
     373                if not am: return 
     374                if mute: 
     375                        am.stopTransmit(ep.Endpoint.instance.audDevManager().getPlaybackDevMedia()) 
     376                        self.addMessage(None, "Muted audio from '%s'" % (peer_uri_str)) 
     377                else: 
     378                        am.startTransmit(ep.Endpoint.instance.audDevManager().getPlaybackDevMedia()) 
     379                        self.addMessage(None, "Unmuted audio from '%s'" % (peer_uri_str)) 
     380                 
     381        def onRxVol(self, peer_uri_str, vol_pct): 
     382                am = self._getAudioMediaFromUriStr(peer_uri_str) 
     383                if not am: return 
     384                # pjsua volume range = 0:mute, 1:no adjustment, 2:100% louder 
     385                am.adjustRxLevel(vol_pct/50.0) 
     386                self.addMessage(None, "Adjusted volume level audio from '%s'" % (peer_uri_str)) 
     387                         
     388        def onTxMute(self, peer_uri_str, mute): 
     389                am = self._getAudioMediaFromUriStr(peer_uri_str) 
     390                if not am: return 
     391                if mute: 
     392                        ep.Endpoint.instance.audDevManager().getCaptureDevMedia().stopTransmit(am) 
     393                        self.addMessage(None, "Muted audio to '%s'" % (peer_uri_str)) 
     394                else: 
     395                        ep.Endpoint.instance.audDevManager().getCaptureDevMedia().startTransmit(am) 
     396                        self.addMessage(None, "Unmuted audio to '%s'" % (peer_uri_str)) 
    276397 
    277398        # Chat room 
     
    281402                if dlg.doModal(): 
    282403                        for bud in buds: 
    283                                 self.addParticipant(bud) 
    284                         self.startCall() 
     404                                uri = ParseSipUri(bud.cfg.uri) 
     405                                self.addParticipant(uri) 
     406                        if not self.isPrivate(): 
     407                                self.startCall() 
    285408                                 
    286409        def onStartAudio(self): 
     
    289412        def onStopAudio(self): 
    290413                self.stopCall() 
     414                 
     415        def onCloseWindow(self): 
     416                self.stopCall() 
     417                # will remove entry from list eventually destroy this chat? 
     418                if self in self._acc.chatList: self._acc.chatList.remove(self) 
     419                self._app.updateWindowMenu() 
     420                # destroy GUI 
     421                self._gui.destroy() 
    291422 
    292423 
  • pjproject/branches/projects/pjsua2/pjsip-apps/src/pygui/chatgui.py

    r4677 r4686  
    164164                        self._btnHold['text'] = 'Hold' 
    165165                        self._btnHold.config(state=tk.NORMAL) 
     166                        self._rxMute = False 
     167                        self._txMute = False 
     168                        self.btnRxMute['text'] = 'Mute' 
     169                        self.btnTxMute['text'] = 'Mute' 
     170                        self.rxVol.set(5.0) 
    166171                 
    167172                # save last state 
    168173                self._state = state 
     174                 
     175        def setStatsText(self, stats_str): 
     176                self.stat.config(state=tk.NORMAL) 
     177                self.stat.delete("0.0", tk.END) 
     178                self.stat.insert(tk.END, stats_str) 
     179                self.stat.config(state=tk.DISABLED) 
    169180                 
    170181        def _onHold(self): 
     
    187198                self._rxMute = not self._rxMute 
    188199                self._observer.onRxMute(self.peerUri, self._rxMute) 
     200                self.btnRxMute['text'] = 'Unmute' if self._rxMute else 'Mute' 
    189201                 
    190202        def _onRxVol(self, event): 
    191203                # notify app 
    192204                vol = self.rxVol.get() 
    193                 self._observer.onRxVol(self.peerUri, vol) 
     205                self._observer.onRxVol(self.peerUri, vol*10.0) 
    194206 
    195207        def _onTxMute(self): 
     
    197209                self._txMute = not self._txMute 
    198210                self._observer.onTxMute(self.peerUri, self._txMute) 
     211                self.btnTxMute['text'] = 'Unmute' if self._txMute else 'Mute' 
    199212 
    200213        def _createInitWidgets(self): 
     
    231244                self.rxVolFrm.pack(side=tk.LEFT, fill=tk.Y) 
    232245                 
    233                 self.btnRxMute = ttk.Button(self.rxVolFrm, width=5, text='Mute', command=self._onRxMute) 
     246                self.btnRxMute = ttk.Button(self.rxVolFrm, width=8, text='Mute', command=self._onRxMute) 
    234247                self.btnRxMute.pack(side=tk.LEFT) 
    235248                self.rxVol = tk.Scale(self.rxVolFrm, orient=tk.HORIZONTAL, from_=0.0, to=10.0, showvalue=0) #, tickinterval=10.0, showvalue=1) 
     
    241254                self.txVolFrm.pack(side=tk.RIGHT, fill=tk.Y) 
    242255                 
    243                 self.btnTxMute = ttk.Button(self.txVolFrm, width=5, text='Mute', command=self._onTxMute) 
     256                self.btnTxMute = ttk.Button(self.txVolFrm, width=8, text='Mute', command=self._onTxMute) 
    244257                self.btnTxMute.pack(side=tk.LEFT) 
    245258                 
    246259                # stat 
    247                 self.stat = tk.Text(self._callFrame, width=20, height=5, font=("Arial", "10")) 
     260                self.stat = tk.Text(self._callFrame, width=10, height=2, bg='lightgray', relief=tk.FLAT, font=("Arial", "9")) 
    248261                self.stat.insert(tk.END, 'stat here') 
    249262                self.stat.pack(side=tk.BOTTOM, fill=tk.BOTH, expand=1) 
     
    256269                pass 
    257270        def onStopAudio(self): 
     271                pass 
     272        def onCloseWindow(self): 
    258273                pass 
    259274                 
     
    264279        def __init__(self, observer): 
    265280                tk.Toplevel.__init__(self) 
    266                 self.protocol("WM_DELETE_WINDOW", lambda: self.withdraw()) 
     281                self.protocol("WM_DELETE_WINDOW", self._onClose) 
    267282                self._observer = observer 
    268283 
     
    331346                        self._observer.onStopAudio() 
    332347                self.enableAudio(self._audioEnabled) 
     348                 
     349        def _onClose(self): 
     350                self._observer.onCloseWindow() 
    333351                         
    334352        # APIs 
     
    381399                else: 
    382400                        self.enableAudio(True) 
    383                                                  
     401                         
     402        def audioSetStatsText(self, participant_uri, stats_str): 
     403                for aud_frm in self._audioFrames: 
     404                        if participant_uri == aud_frm.peerUri: 
     405                                aud_frm.setStatsText(stats_str) 
     406                                break 
     407                                 
    384408if __name__ == '__main__': 
    385409        root = tk.Tk() 
Note: See TracChangeset for help on using the changeset viewer.