def incoming_call(self, prm): call = Call(self.acc, call_id=prm.callId) call_prm = pj.CallOpParam() # Ringing State call_prm.statusCode = pj.PJSIP_SC_RINGING call.answer(call_prm) # Set uri call.uri = call.getInfo().remoteUri iid = call.uri.split(':')[1].split('@')[0] if msg.askquestion('Incoming Call', 'Accept call from ' + iid + ' ?', default=msg.YES): # If not exist current buddy, then create if iid not in self.buddy_list: # Initialize buddy bud = Buddy(self, iid) # Initialize configuration of buddy bud_cfg = pj.BuddyConfig() bud_cfg.uri = 'sip:' + iid + '@' + self.domain # Create buddy bud.cfg = bud_cfg bud.create(self.acc, bud.cfg) bud.subscribePresence(True) # Push into buddy_list self.buddy_list[iid] = bud self.update_buddy(bud) # If not exist current chat dialog, then create if iid not in self.chat_list: self.chat_list[iid] = Chat(self.acc, self.buddy_list[iid], self) # Inform the corresponding chat self.chat_list[iid].receive_call(call) else: call.hangup(call_prm)
class Chat(tk.Toplevel): def __init__(self, acc, bud, app): tk.Toplevel.__init__(self) self.acc = acc self.bud = bud self.app = app self.call = None self.vid_win = None self.vid_pre = None self.state = AudioState.DISCONNECT """ Initialize UI """ self.photo = tk.PhotoImage(file='image/user.png') self.title(bud.cfg.uri) self.resizable(width=False, height=False) self.geometry('+{}+{}'.format(int(self.winfo_screenwidth() / 2), int(self.winfo_screenheight() / 2))) self.canvas = tk.Canvas(self, width=400, height=400, bg=COLOR_BACKGROUND) self.canvas.grid(row=0, column=0, rowspan=10, columnspan=2, padx=10, pady=10, sticky='nsew') self.chat = tk.Frame(self.canvas) self.canvas.create_window((0, 0), window=self.chat, anchor='nw', tags='chat') self.canvas.bind('<Configure>', self._canvas_resize) self.chat.bind('<Configure>', self._chat_resize) self.scrl = ttk.Scrollbar(self, orient=tk.VERTICAL, command=self.canvas.yview) self.canvas.config(yscrollcommand=self.scrl.set) self.scrl.grid(row=0, column=2, rowspan=10, sticky='nsw') # Message Entry self.message = tk.Entry(self, font=FONT_CONTENT, width=30) self.message.bind('<Return>', self._send_message) self.message.grid(row=10, column=0, padx=10, pady=10) # Call Button tk.Button(self, text='Call', font=FONT_CONTENT, width=10, command=self._make_call).grid(row=10, column=1, padx=2, pady=2) # State Label self.state_label = tk.Label(self, font=FONT_TITLE, text='No connect') self.state_label.grid(row=5, column=3, columnspan=3, padx=10, pady=10) # Timer Label self.timer = StopWatch(self) self.timer.grid(row=6, column=3, columnspan=3, padx=10, pady=10) # RX Volume Scale tk.Label(self, text='Micphone', font=FONT_CONTENT).grid(row=7, column=3, padx=10, pady=10) self.rx_scale = tk.Scale(self, from_=0.0, to=10.0, orient=tk.HORIZONTAL, showvalue=0) self.rx_scale.set(5.0) self.rx_scale.bind('<ButtonRelease-1>', self._rx_volume) self.rx_scale.grid(row=7, column=4, columnspan=2, padx=5, pady=5) # TX Volume Scale tk.Label(self, text='Speaker', font=FONT_CONTENT).grid(row=8, column=3, padx=10, pady=10) self.tx_scale = tk.Scale(self, from_=0.0, to=10.0, orient=tk.HORIZONTAL, showvalue=0) self.tx_scale.set(5.0) self.tx_scale.bind('<ButtonRelease-1>', self._tx_volume) self.tx_scale.grid(row=8, column=4, columnspan=2, padx=5, pady=5) # Hold Button self.hold_button = tk.Button(self, text='Hold', font=FONT_CONTENT, width=10, command=self._set_hold) self.hold_button.grid(row=9, column=3, padx=10, pady=10) # Hangup Button tk.Button(self, text='Hangup', font=FONT_CONTENT, width=10, command=self._hang_up).grid(row=9, column=5, padx=10, pady=10) self.protocol('WM_DELETE_WINDOW', self._exit) self.bind('<Configure>', self._resize) def add_message(self, msg, flag): msg = msg.replace('\n', '') tk.Label(self.chat, text=time.strftime('%H:%M:%S', time.localtime()), fg=COLOR_TIME).pack(anchor='center') if flag == MessageState.RECEIVE: content = tk.Frame(self.chat) tk.Label(content, image=self.photo).pack(side=tk.LEFT, anchor='n') tk.Label(content, font=FONT_MESSAGE, text=msg, wraplength=200, justify='left', bg=COLOR_RECEIVE_BUBBLE).pack(side=tk.LEFT, ipadx=5, ipady=5) content.pack(anchor='w') else: content = tk.Frame(self.chat) tk.Label(content, image=self.photo).pack(side=tk.RIGHT, anchor='n') tk.Label(content, font=FONT_MESSAGE, text=msg, wraplength=200, justify='left', bg=COLOR_SEND_BUBBLE).pack(side=tk.RIGHT, ipadx=5, ipady=5) content.pack(anchor='e') # Important!!! Can't remove!!! self.chat.update_idletasks() self.canvas.yview_moveto(1) def show_video(self, vid_win, vid_pre): self.vid_win = vid_win self.vid_pre = vid_pre # Set video position pos = pj.MediaCoordinate() pos.x = self.winfo_rootx() + self.winfo_width() pos.y = self.winfo_rooty() self.vid_win.setPos(pos) # Set video size size = pj.MediaSize() size.w = int(self.winfo_height() * 0.8) size.h = int(self.winfo_height() * 0.8) self.vid_win.setSize(size) # Show video self.vid_win.Show(True) # Show preview pre_prm = pj.VideoPreviewOpParam() self.vid_pre.start(pre_prm) # Set preview position pos = pj.MediaCoordinate() pos.x = self.winfo_rootx() + self.winfo_width() pos.y = self.winfo_rooty() + int(self.winfo_height() * 0.8) self.vid_pre.getVideoWindow().setPos(pos) # Set preview size size = pj.MediaSize() size.w = int(self.winfo_height() * 0.2) size.h = int(self.winfo_height() * 0.2) self.vid_pre.getVideoWindow().setSize(size) def is_calling(self): self.state = AudioState.CALLING self.state_label['text'] = self.state.value def is_connect(self): self.state = AudioState.CONNECT self.state_label['text'] = self.state.value self.timer.reset() self.timer.start() def is_disconnect(self): if self.vid_win: self.vid_win = None if self.vid_pre: self.vid_pre.stop() self.vid_pre = None self.state = AudioState.DISCONNECT self.state_label['text'] = self.state.value self.timer.stop() self.add_message('Call Ended\nLast ' + self.timer.get(), MessageState.INFO) def is_hold(self): self.hold_button['text'] = 'UnHold' self.state = AudioState.HOLD self.state_label['text'] = self.state.value self.timer.stop() def is_unhold(self): self.hold_button['text'] = 'Hold' self.state = AudioState.CONNECT self.state_label['text'] = self.state.value self.timer.start() def _send_message(self, event): msg = self.message.get() if msg != '': msg_prm = pj.SendInstantMessageParam() msg_prm.content = msg self.bud.sendInstantMessage(msg_prm) self.add_message(msg, MessageState.SEND) self.message.delete(0, 'end') def receive_message(self, msg): self.add_message(msg, MessageState.RECEIVE) def _make_call(self): if self.state == AudioState.DISCONNECT: # Initialize Call self.call = Call(self.acc, self.bud.cfg.uri, self) # Create paramter call_prm = pj.CallOpParam() # Enable audio call_prm.opt.audioCount = 1 # Enable video call_prm.opt.videoCount = 1 # Make call self.call.makeCall(self.bud.cfg.uri, call_prm) def receive_call(self, call): # Update call and set chat self.call = call self.call.chat = self # Receive call call_prm = pj.CallOpParam() call_prm.statusCode = pj.PJSIP_SC_OK self.call.answer(call_prm) def _set_hold(self): if self.call: call_prm = pj.CallOpParam() if self.state == AudioState.CONNECT: self.call.setHold(call_prm) self.is_hold() elif self.state == AudioState.HOLD: # Important!!! Can't remove!!! call_prm.opt.audioCount = 1 call_prm.opt.flag = pj.PJSUA_CALL_UNHOLD self.call.reinvite(call_prm) self.is_unhold() else: print('Call isn\'t initialized') def _hang_up(self): if self.call: call_prm = pj.CallOpParam() if self.state != AudioState.DISCONNECT: self.call.hangup(call_prm) def _rx_volume(self, event): am = self.call.getAudioMedia(-1) am.adjustRxLevel(self.rx_scale.get() / 10.0) def _tx_volume(self, event): am = self.call.getAudioMedia(-1) am.adjustTxLevel(self.tx_scale.get() / 10.0) def _canvas_resize(self, event): # Important!!! Can't remove!!! self.canvas.itemconfig('chat', width=event.width) def _chat_resize(self, event): # Important!!! Can't remove!!! self.canvas.config(scrollregion=self.canvas.bbox('all')) def _resize(self, event): if self.vid_win and self.vid_pre: # Set video position pos = pj.MediaCoordinate() pos.x = self.winfo_rootx() + self.winfo_width() pos.y = self.winfo_rooty() self.vid_win.setPos(pos) # Set preview position pos = pj.MediaCoordinate() pos.x = self.winfo_rootx() + self.winfo_width() pos.y = self.winfo_rooty() + int(self.winfo_height() * 0.8) self.vid_pre.getVideoWindow().setPos(pos) def _exit(self): try: self.app.delete_chat(self.bud.iid) self._hang_up() self.destroy() except: self.destroy()