def stop(self): if not self.started: return notification_center = NotificationCenter() notification_center.discard_observer(self) self.notification_map.clear() for logger in self.loggers: logger.stop() self.started = False
def notificationsCheckboxClicked_(self, sender): settings = SIPSimpleSettings() settings.logs.trace_notifications_in_gui = bool(sender.state()) settings.logs.trace_notifications = settings.logs.trace_notifications_in_gui or settings.logs.trace_notifications_to_file settings.save() notification_center = NotificationCenter() if settings.logs.trace_notifications_in_gui: notification_center.add_observer(self) else: notification_center.discard_observer(self)
def stop(self): if self._stopped: return self._stopped = True self.janus_logger.stop() self.factory.stopTrying() notification_center = NotificationCenter() notification_center.discard_observer(self, name='JanusBackendConnected') notification_center.discard_observer(self, name='JanusBackendDisconnected') if self.connector is not None: self.connector.disconnect() self.connector = None if self.connection is not None: self.connection.disconnect() self.connection = Null
def stop(self): if self._stopped: return self._stopped = True self.factory.stopTrying() notification_center = NotificationCenter() notification_center.discard_observer(self, name='JanusBackendConnected') notification_center.discard_observer(self, name='JanusBackendDisconnected') if self.connector is not None: self.connector.disconnect() self.connector = None if self.protocol is not None: self.protocol.disconnect() self.protocol = Null
def sipRadioClicked_(self, sender): notification_center = NotificationCenter() trace = sender.selectedCell().tag() settings = SIPSimpleSettings() settings.logs.trace_sip_in_gui = trace if trace == Disabled: notification_center.discard_observer(self, name="DNSLookupTrace") settings.logs.trace_sip = settings.logs.trace_sip_to_file elif trace == Simplified: notification_center.add_observer(self, name="DNSLookupTrace") settings.logs.trace_sip = True elif trace == Full: notification_center.add_observer(self, name="DNSLookupTrace") settings.logs.trace_sip = True settings.save()
def end(self): if self._done: return self._done = True notification_center = NotificationCenter() if not self._initialize_done: # we are in the middle of initialize() try: msrp_connector = self.msrp_connector if self.greenlet is not None: api.kill(self.greenlet) if msrp_connector is not None: msrp_connector.cleanup() finally: notification_center.post_notification( 'MediaStreamDidNotInitialize', sender=self, data=NotificationData(reason='Interrupted')) notification_center.discard_observer(self, sender=self) self.msrp_connector = None self.greenlet = None else: notification_center.post_notification('MediaStreamWillEnd', sender=self) msrp = self.msrp msrp_session = self.msrp_session msrp_connector = self.msrp_connector try: if self.greenlet is not None: api.kill(self.greenlet) if msrp_session is not None: msrp_session.shutdown() elif msrp is not None: msrp.loseConnection(wait=False) if msrp_connector is not None: msrp_connector.cleanup() finally: notification_center.post_notification( 'MediaStreamDidEnd', sender=self, data=NotificationData(error=self._failure_reason)) notification_center.remove_observer(self, sender=self) self.msrp = None self.msrp_session = None self.msrp_connector = None self.session = None self.greenlet = None
def _cleanup(self): notification_center = NotificationCenter() notification_center.remove_observer(self, sender=self.session) self.session = None if self.audio_stream is not None: notification_center.discard_observer(self, sender=self.audio_stream) self.audio_stream = None if self.chat_stream is not None: notification_center.discard_observer(self, sender=self.chat_stream) self.chat_stream = None if self.end_timer is not None and self.end_timer.active(): self.end_timer.cancel() self.end_timer = None
def _cleanup(self): notification_center = NotificationCenter() notification_center.remove_observer(self, sender=self.session) self.session = None if self.audio_stream is not None: notification_center.discard_observer(self, sender=self.audio_stream) self.audio_stream = None if self.chat_stream is not None: notification_center.discard_observer(self, sender=self.chat_stream) self.chat_stream = None if self.end_timer is not None and self.end_timer.active(): self.end_timer.cancel() self.end_timer = None
def xcapRadioClicked_(self, sender): notification_center = NotificationCenter() trace = sender.selectedCell().tag() settings = SIPSimpleSettings() settings.logs.trace_xcap_in_gui = trace if trace == Disabled: notification_center.discard_observer(self, name="XCAPManagerDidDiscoverServerCapabilities") notification_center.discard_observer(self, name="XCAPSubscriptionGotNotify") notification_center.discard_observer(self, name="XCAPManagerDidChangeState") settings.logs.trace_xcap = settings.logs.trace_xcap_to_file elif trace == Simplified: notification_center.add_observer(self, name="XCAPManagerDidDiscoverServerCapabilities") notification_center.add_observer(self, name="XCAPManagerDidChangeState") settings.logs.trace_xcap = True elif trace == Full: notification_center.add_observer(self, name="XCAPManagerDidDiscoverServerCapabilities") notification_center.add_observer(self, name="XCAPManagerDidChangeState") notification_center.add_observer(self, name="XCAPSubscriptionGotNotify") settings.logs.trace_xcap = True settings.save()
def dealloc(self): # Observers added in init NSNotificationCenter.defaultCenter().removeObserver_(self) notification_center = NotificationCenter() notification_center.add_observer(self, name="SIPSessionDidStart") notification_center.add_observer( self, name="SIPSessionDidRenegotiateStreams") # Observers added when settings change notification_center.discard_observer(self, name="SIPEngineSIPTrace") notification_center.discard_observer(self, name="DNSLookupTrace") notification_center.discard_observer(self, name="MSRPLibraryLog") notification_center.discard_observer(self, name="MSRPTransportTrace") notification_center.discard_observer( self, name="XCAPManagerDidDiscoverServerCapabilities") notification_center.discard_observer(self, name="XCAPSubscriptionGotNotify") notification_center.discard_observer(self, name="XCAPManagerDidChangeState") notification_center.discard_observer(self, name="SIPEngineLog") notification_center.discard_observer(self) super(DebugWindow, self).dealloc()
def dealloc(self): # Observers added in init NSNotificationCenter.defaultCenter().removeObserver_(self) notification_center = NotificationCenter() notification_center.discard_observer(self, name="SIPSessionDidStart") notification_center.discard_observer(self, name="SIPSessionDidRenegotiateStreams") notification_center.discard_observer(self, name="AudioSessionHasQualityIssues") notification_center.discard_observer(self, name="AudioStreamICENegotiationDidSucceed") notification_center.discard_observer(self, name="AudioStreamICENegotiationDidFail") # Observers added when settings change notification_center.discard_observer(self, name="SIPEngineSIPTrace") notification_center.discard_observer(self, name="DNSLookupTrace") notification_center.discard_observer(self, name="MSRPLibraryLog") notification_center.discard_observer(self, name="MSRPTransportTrace") notification_center.discard_observer(self, name="XCAPManagerDidDiscoverServerCapabilities") notification_center.discard_observer(self, name="XCAPSubscriptionGotNotify") notification_center.discard_observer(self, name="XCAPManagerDidChangeState") notification_center.discard_observer(self, name="SIPEngineLog") notification_center.discard_observer(self) super(DebugWindow, self).dealloc()
class SessionInfoController(NSObject): window = objc.IBOutlet() sessionBox = objc.IBOutlet() audioBox = objc.IBOutlet() videoBox = objc.IBOutlet() chatBox = objc.IBOutlet() remote_party = objc.IBOutlet() account = objc.IBOutlet() duration = objc.IBOutlet() remote_ua = objc.IBOutlet() status = objc.IBOutlet() remote_endpoint = objc.IBOutlet() tls_lock = objc.IBOutlet() audio_status = objc.IBOutlet() audio_codec = objc.IBOutlet() audio_remote_endpoint = objc.IBOutlet() audio_ice_negotiation = objc.IBOutlet() audio_rtt = objc.IBOutlet() audio_rtt_graph = objc.IBOutlet() audio_packet_loss_rx = objc.IBOutlet() audio_packet_loss_tx = objc.IBOutlet() audio_packet_loss_rx_graph = objc.IBOutlet() audio_packet_loss_tx_graph = objc.IBOutlet() audio_srtp_lock = objc.IBOutlet() rx_speed_graph = objc.IBOutlet() rx_speed = objc.IBOutlet() tx_speed_graph = objc.IBOutlet() tx_speed = objc.IBOutlet() video_status = objc.IBOutlet() video_codec = objc.IBOutlet() video_remote_endpoint = objc.IBOutlet() video_ice_negotiation = objc.IBOutlet() video_srtp_lock = objc.IBOutlet() video_rx_speed_graph = objc.IBOutlet() video_rx_speed = objc.IBOutlet() video_tx_speed_graph = objc.IBOutlet() video_tx_speed = objc.IBOutlet() chat_remote_endpoint = objc.IBOutlet() chat_connection_mode = objc.IBOutlet() chat_tls_lock = objc.IBOutlet() def __new__(cls, *args, **kwargs): return cls.alloc().init() def __init__(self, sessionController): self.notification_center = NotificationCenter() self.notification_center.add_observer( self, name='CFGSettingsObjectDidChange') self.sessionController = None self.audio_stream = None self.video_stream = None self.chat_stream = None self.add_session(sessionController) self.add_audio_stream() self.add_video_stream() self.add_chat_stream() self.timer = NSTimer.timerWithTimeInterval_target_selector_userInfo_repeats_( 1.0, self, "updateTimer:", None, True) NSRunLoop.currentRunLoop().addTimer_forMode_(self.timer, NSModalPanelRunLoopMode) NSRunLoop.currentRunLoop().addTimer_forMode_(self.timer, NSDefaultRunLoopMode) NSBundle.loadNibNamed_owner_("SessionInfoPanel", self) sessionBoxTitle = NSAttributedString.alloc( ).initWithString_attributes_( NSLocalizedString("SIP Session", "Label"), NSDictionary.dictionaryWithObject_forKey_( NSColor.orangeColor(), NSForegroundColorAttributeName)) self.sessionBox.setTitle_(sessionBoxTitle) audioBoxTitle = NSAttributedString.alloc().initWithString_attributes_( NSLocalizedString("Audio Stream", "Label"), NSDictionary.dictionaryWithObject_forKey_( NSColor.orangeColor(), NSForegroundColorAttributeName)) self.audioBox.setTitle_(audioBoxTitle) videoBoxTitle = NSAttributedString.alloc().initWithString_attributes_( NSLocalizedString("Video Stream", "Label"), NSDictionary.dictionaryWithObject_forKey_( NSColor.orangeColor(), NSForegroundColorAttributeName)) self.videoBox.setTitle_(videoBoxTitle) chatBoxTitle = NSAttributedString.alloc().initWithString_attributes_( NSLocalizedString("Chat Stream", "Label"), NSDictionary.dictionaryWithObject_forKey_( NSColor.orangeColor(), NSForegroundColorAttributeName)) self.chatBox.setTitle_(chatBoxTitle) settings = SIPSimpleSettings() self.audio_rtt_graph.setLineWidth_(1.0) self.audio_rtt_graph.setLineSpacing_(1.0) self.audio_rtt_graph.setAboveLimit_( settings.gui.rtt_threshold) # if higher show red color self.audio_rtt_graph.setMinimumHeigth_(settings.gui.rtt_threshold) self.audio_packet_loss_rx_graph.setLineWidth_(1.0) self.audio_packet_loss_rx_graph.setLineSpacing_(1.0) self.audio_packet_loss_rx_graph.setAboveLimit_( 3) # if higher than 3% show red color self.audio_packet_loss_rx_graph.setLineColor_(NSColor.greenColor()) self.audio_packet_loss_rx_graph.setMinimumHeigth_(5) self.audio_packet_loss_tx_graph.setLineWidth_(1.0) self.audio_packet_loss_tx_graph.setLineSpacing_(1.0) self.audio_packet_loss_tx_graph.setAboveLimit_( 3) # if higher than 3% show red color self.audio_packet_loss_tx_graph.setLineColor_(NSColor.greenColor()) self.audio_packet_loss_tx_graph.setMinimumHeigth_(5) self.rx_speed_graph.setLineWidth_(1.0) self.rx_speed_graph.setLineSpacing_(0.0) self.rx_speed_graph.setLineColor_(NSColor.greenColor()) self.rx_speed_graph.setMinimumHeigth_(100000) self.rx_speed_graph.setAboveLimit_(120000) self.tx_speed_graph.setLineWidth_(1.0) self.tx_speed_graph.setLineSpacing_(0.0) self.tx_speed_graph.setLineColor_(NSColor.blueColor()) self.tx_speed_graph.setMinimumHeigth_(100000) self.tx_speed_graph.setAboveLimit_(120000) self.video_rx_speed_graph.setLineWidth_(1.0) self.video_rx_speed_graph.setLineSpacing_(0.0) self.video_rx_speed_graph.setLineColor_(NSColor.greenColor()) self.video_rx_speed_graph.setMinimumHeigth_(100000) self.video_rx_speed_graph.setAboveLimit_(99999999) self.video_tx_speed_graph.setLineWidth_(1.0) self.video_tx_speed_graph.setLineSpacing_(0.0) self.video_tx_speed_graph.setLineColor_(NSColor.blueColor()) self.video_tx_speed_graph.setMinimumHeigth_(100000) self.video_tx_speed_graph.setAboveLimit_(99999999) self.resetSession() self.updatePanelValues() @objc.python_method def add_session(self, sessionController): if self.sessionController is None: self.sessionController = sessionController self.notification_center.add_observer( self, sender=self.sessionController) @objc.python_method def remove_session(self): if self.sessionController is not None: self.notification_center.remove_observer( self, sender=self.sessionController) self.notification_center.remove_observer( self, name='CFGSettingsObjectDidChange') self.sessionController = None self.remove_audio_stream() self.remove_video_stream() self.remove_chat_stream() @objc.python_method def add_audio_stream(self): if self.sessionController is not None and self.sessionController.hasStreamOfType( "audio") and self.audio_stream is None: self.audio_stream = self.sessionController.streamHandlerOfType( "audio") self.notification_center.add_observer(self, sender=self.audio_stream) self.notification_center.add_observer( self, sender=self.audio_stream.stream) @objc.python_method def remove_audio_stream(self): if self.audio_stream is not None: self.notification_center.discard_observer(self, sender=self.audio_stream) self.notification_center.discard_observer( self, sender=self.audio_stream.stream) self.audio_stream = None self.updateAudioStatus() @objc.python_method def add_video_stream(self): if self.sessionController is not None and self.sessionController.hasStreamOfType( "video") and self.video_stream is None: self.video_stream = self.sessionController.streamHandlerOfType( "video") self.notification_center.add_observer(self, sender=self.video_stream) self.notification_center.add_observer( self, sender=self.video_stream.stream) @objc.python_method def remove_video_stream(self): if self.video_stream is not None: self.notification_center.discard_observer(self, sender=self.video_stream) self.notification_center.discard_observer( self, sender=self.video_stream.stream) self.video_stream = None self.resetVideo() self.updateVideoStatus() @objc.python_method def add_chat_stream(self): if self.sessionController is not None and self.sessionController.hasStreamOfType( "chat") and self.chat_stream is None: self.chat_stream = self.sessionController.streamHandlerOfType( "chat") @objc.python_method def remove_chat_stream(self): if self.chat_stream is not None: self.chat_stream = None @objc.python_method def resetSession(self): self.remote_endpoint.setStringValue_('') self.remote_ua.setStringValue_('') self.status.setStringValue_('') self.duration.setStringValue_('') self.resetAudio() self.resetVideo() self.resetChat() @objc.python_method def resetAudio(self): self.audio_status.setStringValue_('') self.audio_codec.setStringValue_('') self.audio_remote_endpoint.setStringValue_('') self.audio_ice_negotiation.setStringValue_('') self.audio_rtt.setStringValue_('') self.audio_packet_loss_rx.setStringValue_('') self.audio_packet_loss_tx.setStringValue_('') self.rx_speed.setStringValue_('') self.tx_speed.setStringValue_('') @objc.python_method def resetVideo(self): self.video_status.setStringValue_('') self.video_codec.setStringValue_('') self.video_remote_endpoint.setStringValue_('') self.video_ice_negotiation.setStringValue_('') self.video_rx_speed.setStringValue_('') self.video_tx_speed.setStringValue_('') @objc.python_method def resetChat(self): self.chat_remote_endpoint.setStringValue_('') self.chat_connection_mode.setStringValue_('') @objc.python_method def updatePanelValues(self): self.updateSession() @objc.python_method def updateSession(self): if self.sessionController is None: self.resetSession() else: self.updateSessionStatus() self.remote_party.setStringValue_(self.sessionController.titleLong) self.account.setStringValue_(str( self.sessionController.account.id)) if self.sessionController.conference_info is not None and self.sessionController.remote_focus: pass if hasattr( self.sessionController.session, 'remote_user_agent' ) and self.sessionController.session.remote_user_agent is not None: self.remote_ua.setStringValue_( self.sessionController.session.remote_user_agent) if self.sessionController.session is not None: if self.sessionController.session.transport is not None: transport = self.sessionController.session.transport if self.sessionController.session.peer_address is not None: self.remote_endpoint.setStringValue_( '%s:%s' % (transport, self.sessionController.session.peer_address)) self.tls_lock.setHidden_(False if transport == 'tls' else True) elif self.sessionController.routes: route = self.sessionController.routes[0] self.remote_endpoint.setStringValue_( '%s:%s:%s' % (route.transport, route.address, route.port)) self.tls_lock.setHidden_(False if route.transport == 'tls' else True) elif self.sessionController.routes: route = self.sessionController.routes[0] self.remote_endpoint.setStringValue_( '%s:%s:%s' % (route.transport, route.address, route.port)) self.tls_lock.setHidden_(False if route.transport == 'tls' else True) self.updateAudio() self.updateVideo() self.updateChat() @objc.python_method def updateSessionStatus(self, sub_state=None): if self.sessionController.state is None: self.status.setStringValue_("") return if sub_state is None: sub_state = self.sessionController.session.state if self.sessionController.session is not None else 'none' sub_state = re.sub("_", " ", sub_state.title()) if sub_state is not None else '' state = self.sessionController.state.title() self.status.setStringValue_('%s (%s)' % (state, sub_state) if state != sub_state and sub_state != 'None' else state) @objc.python_method def updateAudio(self): if self.audio_status.stringValue() and ( self.sessionController is None or self.audio_stream is None or self.audio_stream.stream is None): self.resetAudio() elif (self.sessionController is not None and self.audio_stream is not None and self.audio_stream.stream is not None): self.updateAudioStatus() self.audio_rtt_graph.setDataQueue_needsDisplay_( self.audio_stream.rtt_history, True if self.window.isVisible() else False) self.rx_speed_graph.setDataQueue_needsDisplay_( self.audio_stream.rx_speed_history, True if self.window.isVisible() else False) self.tx_speed_graph.setDataQueue_needsDisplay_( self.audio_stream.tx_speed_history, True if self.window.isVisible() else False) self.audio_packet_loss_rx_graph.setDataQueue_needsDisplay_( self.audio_stream.loss_rx_history, True if self.window.isVisible() else False) self.audio_packet_loss_tx_graph.setDataQueue_needsDisplay_( self.audio_stream.loss_tx_history, True if self.window.isVisible() else False) rtt = self.audio_stream.statistics['rtt'] if rtt > 1000: text = '%.1f s' % (float(rtt) / 1000.0) elif rtt > 100: text = '%d ms' % rtt elif rtt: text = '%d ms' % rtt else: text = '' self.rx_speed.setStringValue_('Rx %s/s' % format_size( self.audio_stream.statistics['rx_bytes'], bits=True)) self.tx_speed.setStringValue_('Tx %s/s' % format_size( self.audio_stream.statistics['tx_bytes'], bits=True)) self.audio_rtt.setStringValue_(text) if self.audio_stream.statistics['loss_rx'] > 3: self.audio_packet_loss_rx.setStringValue_( 'Local: %.1f %%' % self.audio_stream.statistics['loss_rx']) else: self.audio_packet_loss_rx.setStringValue_('') if self.audio_stream.statistics['loss_tx'] > 3: self.audio_packet_loss_tx.setStringValue_( 'Remote: %.1f %%' % self.audio_stream.statistics['loss_tx']) else: self.audio_packet_loss_tx.setStringValue_('') if self.audio_stream.stream.codec and self.audio_stream.stream.sample_rate: codec = beautify_audio_codec(self.audio_stream.stream.codec) try: settings = SIPSimpleSettings() sample_rate = self.audio_stream.stream.sample_rate / 1000 codec = codec + " %0.fkHz" % sample_rate except TypeError: pass self.audio_codec.setStringValue_(codec) self.audio_srtp_lock.setHidden_( False if self.audio_stream.encryption_active else True) if self.audio_stream.encryption_active: if self.audio_stream.zrtp_active: self.audio_srtp_lock.setImage_( NSImage.imageNamed_("locked-green") if self. audio_stream.zrtp_verified else NSImage. imageNamed_("locked-red")) else: self.audio_srtp_lock.setImage_( NSImage.imageNamed_("srtp")) else: self.audio_codec.setStringValue_('') self.audio_srtp_lock.setHidden_(True) self.audio_remote_endpoint.setStringValue_( '%s:%s' % (self.audio_stream.stream.remote_rtp_address, self.audio_stream.stream.remote_rtp_port) if self. audio_stream.stream.remote_rtp_address else '') if self.audio_stream.stream.ice_active: ice_status = self.audio_stream.ice_negotiation_status if self.audio_stream.ice_negotiation_status is not None else '' if self.audio_stream.stream.ice_active: if self.audio_stream.stream.local_rtp_candidate and self.audio_stream.stream.remote_rtp_candidate: if self.audio_stream.stream.local_rtp_candidate.type.lower( ) != 'relay' and self.audio_stream.stream.remote_rtp_candidate.type.lower( ) != 'relay': if self.audio_stream.stream.local_rtp_candidate.type.lower( ) == 'host' and self.audio_stream.stream.remote_rtp_candidate.type.lower( ) == 'host': ice_status = NSLocalizedString( "Host to Host", "Label") else: ice_status = NSLocalizedString( "Peer to Peer", "Label") else: ice_status = NSLocalizedString( "Server Relayed", "Label") else: ice_status = self.audio_stream.ice_negotiation_status if self.audio_stream.ice_negotiation_status is not None else '' if ice_status == b"All ICE checklists failed (PJNATH_EICEFAILED)": ice_status = NSLocalizedString("Probing Failed", "Label") elif ice_status == b"Remote answer doesn't support ICE": ice_status = NSLocalizedString("Not Supported", "Label") self.audio_ice_negotiation.setStringValue_(ice_status) @objc.python_method def updateVideo(self): if self.video_status.stringValue() and ( self.sessionController is None or self.video_stream is None or self.video_stream.stream is None): self.resetVideo() elif (self.sessionController is not None and self.video_stream is not None and self.video_stream.stream is not None): self.updateVideoStatus() self.video_rx_speed_graph.setDataQueue_needsDisplay_( self.video_stream.rx_speed_history, True if self.window.isVisible() else False) self.video_tx_speed_graph.setDataQueue_needsDisplay_( self.video_stream.tx_speed_history, True if self.window.isVisible() else False) rtt = self.video_stream.statistics['rtt'] if rtt > 1000: text = '%.1f s' % (float(rtt) / 1000.0) elif rtt > 100: text = '%d ms' % rtt elif rtt: text = '%d ms' % rtt else: text = '' self.video_rx_speed.setStringValue_('Rx %s/s' % format_size( self.video_stream.statistics['rx_bytes'], bits=True)) self.video_tx_speed.setStringValue_('Tx %s/s' % format_size( self.video_stream.statistics['tx_bytes'], bits=True)) if self.video_stream.stream.codec and self.video_stream.stream.sample_rate: codec = beautify_video_codec(self.video_stream.stream.codec) try: settings = SIPSimpleSettings() sample_rate = self.video_stream.stream.sample_rate / 1000 codec = codec + " @%d fps" % self.video_stream.statistics[ 'fps'] except TypeError: pass self.video_codec.setStringValue_(codec) self.video_srtp_lock.setHidden_( False if self.video_stream.encryption_active else True) if self.video_stream.encryption_active: if self.video_stream.zrtp_active: self.video_srtp_lock.setImage_( NSImage.imageNamed_("locked-green") if self. video_stream.zrtp_verified else NSImage. imageNamed_("locked-red")) else: self.video_srtp_lock.setImage_( NSImage.imageNamed_("srtp")) else: self.video_codec.setStringValue_('') self.video_srtp_lock.setHidden_(True) self.video_remote_endpoint.setStringValue_( '%s:%s' % (self.video_stream.stream.remote_rtp_address, self.video_stream.stream.remote_rtp_port) if self. video_stream.stream.remote_rtp_address else '') if self.video_stream.stream.ice_active: ice_status = self.video_stream.ice_negotiation_status if self.video_stream.ice_negotiation_status is not None else '' if self.video_stream.stream.ice_active: if self.video_stream.stream.local_rtp_candidate and self.video_stream.stream.remote_rtp_candidate: if self.video_stream.stream.local_rtp_candidate.type.lower( ) != 'relay' and self.video_stream.stream.remote_rtp_candidate.type.lower( ) != 'relay': if self.video_stream.stream.local_rtp_candidate.type.lower( ) == 'host' and self.video_stream.stream.remote_rtp_candidate.type.lower( ) == 'host': ice_status = NSLocalizedString( "Host to Host", "Label") else: ice_status = NSLocalizedString( "Peer to Peer", "Label") else: ice_status = NSLocalizedString( "Server Relayed", "Label") else: ice_status = self.video_stream.ice_negotiation_status if self.video_stream.ice_negotiation_status is not None else '' if ice_status == "All ICE checklists failed (PJNATH_EICEFAILED)": ice_status = NSLocalizedString("Probing Failed", "Label") elif ice_status == "Remote answer doesn't support ICE": ice_status = NSLocalizedString("Not Supported", "Label") self.video_ice_negotiation.setStringValue_(ice_status) @objc.python_method def updateChat(self): if self.sessionController is None or self.chat_stream is None or self.chat_stream.stream is None or self.chat_stream.stream.msrp is None: self.resetChat() else: if self.chat_stream and self.chat_stream.stream and self.chat_stream.stream.msrp: if len(self.chat_stream.stream.msrp.full_local_path) > 1: self.chat_remote_endpoint.setStringValue_( str(self.chat_stream.stream.msrp.full_local_path[0])) else: self.chat_remote_endpoint.setStringValue_( str(self.chat_stream.stream.msrp.full_remote_path[0])) if self.chat_stream and self.chat_stream.stream: self.chat_connection_mode.setStringValue_( self.chat_stream.stream.local_role.title()) def updateTimer_(self, timer): if self.sessionController is not None: self.updateDuration() self.updateAudio() self.updateVideo() @objc.python_method def updateDuration(self): if self.sessionController is not None and self.sessionController.session is not None: if self.sessionController.session.end_time: now = self.sessionController.session.end_time else: now = ISOTimestamp.now() if self.sessionController.session.start_time and now >= self.sessionController.session.start_time: elapsed = now - self.sessionController.session.start_time h = elapsed.days * 24 + elapsed.seconds / (60 * 60) m = (elapsed.seconds / 60) % 60 s = elapsed.seconds % 60 text = "%02i:%02i:%02i" % (h, m, s) self.duration.setStringValue_(text) else: self.duration.setStringValue_('') @objc.python_method def updateAudioStatus(self): if self.sessionController is None or self.audio_stream is None: self.audio_status.setStringValue_("") else: if self.audio_stream.holdByLocal: self.audio_status.setStringValue_( NSLocalizedString("On Hold", "Label")) elif self.audio_stream.holdByRemote: self.audio_status.setStringValue_( NSLocalizedString("Hold by Remote", "Label")) elif self.audio_stream.status == STREAM_CONNECTED: if self.audio_stream.encryption_active: title = '%s (%s)' % ( self.audio_stream.stream.encryption.type, self.audio_stream.stream.encryption.cipher) else: title = NSLocalizedString("Not Encrypted", "Label") self.audio_status.setStringValue_(title) else: self.audio_status.setStringValue_("") @objc.python_method def updateVideoStatus(self): if self.sessionController is None or self.video_stream is None: self.video_status.setStringValue_("") else: if self.audio_stream and self.audio_stream.holdByLocal: self.video_status.setStringValue_( NSLocalizedString("On Hold", "Label")) elif self.audio_stream and self.audio_stream.holdByRemote: self.video_status.setStringValue_( NSLocalizedString("Hold by Remote", "Label")) elif self.video_stream.status == STREAM_CONNECTED: if self.video_stream.encryption_active: title = '%s (%s)' % ( self.video_stream.stream.encryption.type, self.video_stream.stream.encryption.cipher) else: title = NSLocalizedString("Not Encrypted", "Label") self.video_status.setStringValue_(title) else: self.video_status.setStringValue_("") @objc.python_method @run_in_gui_thread def handle_notification(self, notification): handler = getattr(self, '_NH_%s' % notification.name, Null) handler(notification) @objc.python_method def _NH_BlinkDidRenegotiateStreams(self, notification): for stream in notification.data.removed_streams: if stream.type == 'audio': self.remove_audio_stream() elif stream.type == 'chat': self.remove_chat_stream() elif stream.type == 'video': self.remove_video_stream() for stream in notification.data.added_streams: if stream.type == 'audio': self.add_audio_stream() elif stream.type == 'chat': self.add_chat_stream() elif stream.type == 'video': self.add_video_stream() self.updatePanelValues() @objc.python_method def _NH_CFGSettingsObjectDidChange(self, notification): settings = SIPSimpleSettings() if "gui.rtt_threshold" in notification.data.modified: self.audio_rtt_graph.setAboveLimit_(settings.gui.rtt_threshold) self.audio_rtt_graph.setMinimumHeigth_(settings.gui.rtt_threshold) @objc.python_method def _NH_BlinkSessionGotRingIndication(self, notification): self.updateSessionStatus( sub_state=NSLocalizedString("Ringing...", "Label")) @objc.python_method def _NH_BlinkSessionGotProvisionalResponse(self, notification): self.updateSessionStatus(sub_state=notification.data.reason) @objc.python_method def _NH_BlinkSessionDidProcessTransaction(self, notification): self.updateSessionStatus() @objc.python_method def _NH_BlinkSentAddProposal(self, notification): self.updateSessionStatus() @objc.python_method def _NH_BlinkSentRemoveProposal(self, notification): self.updateSessionStatus() @objc.python_method def _NH_BlinkGotProposal(self, notification): self.updateSessionStatus() @objc.python_method def _NH_BlinkProposalGotRejected(self, notification): self.updateSessionStatus() @objc.python_method def _NH_BlinkStreamHandlersChanged(self, notification): self.updatePanelValues() @objc.python_method def _NH_BlinkSessionWillStart(self, notification): self.updatePanelValues() @objc.python_method def _NH_BlinkSessionDidStart(self, notification): self.add_audio_stream() self.add_video_stream() self.add_chat_stream() self.updatePanelValues() @objc.python_method def _NH_BlinkSessionDidEnd(self, notification): self.stopTimer() @objc.python_method def _NH_BlinkConferenceGotUpdate(self, notification): if self.sessionController is not None and self.sessionController.session is not None and hasattr( notification.data, 'conference_info'): pass # todo: the ICE negotiation notification handlers below are never executed (check observers), yet the values in the GUI are updated (from somewhere else?) -Dan @objc.python_method def _NH_RTPStreamICENegotiationDidFail(self, notification): if notification.sender.type == 'audio' and self.audio_stream is not None: self.audio_ice_negotiation.setStringValue_( self.audio_stream.ice_negotiation_status if self.audio_stream. ice_negotiation_status is not None else '') elif notification.sender.type == 'video' and self.video_stream is not None: self.video_ice_negotiation.setStringValue_( self.video_stream.ice_negotiation_status if self.video_stream. ice_negotiation_status is not None else '') @objc.python_method def _NH_RTPStreamICENegotiationDidSucceed(self, notification): if notification.sender.type == 'audio' and self.audio_stream is not None: self.audio_ice_negotiation.setStringValue_( self.audio_stream.ice_negotiation_status if self.audio_stream. ice_negotiation_status is not None else '') elif notification.sender.type == 'video' and self.video_stream is not None: self.video_ice_negotiation.setStringValue_( self.video_stream.ice_negotiation_status if self.video_stream. ice_negotiation_status is not None else '') @objc.python_method def _NH_RTPStreamDidChangeHoldState(self, notification): self.updateAudioStatus() @objc.python_method def show(self): self.window.orderFront_(None) @objc.python_method def hide(self): self.window.orderOut_(None) @objc.python_method def toggle(self): if self.window.isVisible(): self.hide() else: self.show() def windowShouldClose_(self, sender): self.window.orderOut_(None) @objc.python_method def close(self): self.stopTimer() self.remove_session() self.window.orderOut_(None) @objc.python_method def stopTimer(self): if self.timer: self.timer.invalidate() self.timer = None def dealloc(self): self.audio_packet_loss_rx_graph.removeFromSuperview() self.audio_packet_loss_tx_graph.removeFromSuperview() self.audio_rtt_graph.removeFromSuperview() self.rx_speed_graph.removeFromSuperview() self.tx_speed_graph.removeFromSuperview() self.video_rx_speed_graph.removeFromSuperview() self.video_tx_speed_graph.removeFromSuperview() objc.super(SessionInfoController, self).dealloc()
def _CH_publish(self, command): if command.state is None or self._publication is None and command.state is SameState: command.signal() return notification_center = NotificationCenter() settings = SIPSimpleSettings() if self._publication_timer is not None and self._publication_timer.active( ): self._publication_timer.cancel() self._publication_timer = None if self._publication is None: duration = command.refresh_interval or self.account.sip.publish_interval from_header = FromHeader(self.account.uri, self.account.display_name) self._publication = Publication( from_header, self.event, self.payload_type.content_type, credentials=self.account.credentials, duration=duration, extra_headers=self.extra_headers) notification_center.add_observer(self, sender=self._publication) notification_center.post_notification( self.__class__.__name__ + 'WillPublish', sender=self, data=NotificationData(state=command.state, duration=duration)) else: notification_center.post_notification( self.__class__.__name__ + 'WillRefresh', sender=self, data=NotificationData(state=command.state)) try: if Host.default_ip is None: raise PublicationError('No IP address', retry_after=60) # Lookup routes valid_transports = self.__transports__.intersection( settings.sip.transport_list) if self.account.sip.outbound_proxy is not None and self.account.sip.outbound_proxy.transport in valid_transports: uri = SIPURI(host=self.account.sip.outbound_proxy.host, port=self.account.sip.outbound_proxy.port, parameters={ 'transport': self.account.sip.outbound_proxy.transport }) else: uri = SIPURI(host=self.account.id.domain) lookup = DNSLookup() try: routes = lookup.lookup_sip_proxy( uri, valid_transports, tls_name=self.account.sip.tls_name).wait() except DNSLookupError as e: retry_after = random.uniform(self._dns_wait, 2 * self._dns_wait) self._dns_wait = limit(2 * self._dns_wait, max=30) raise PublicationError('DNS lookup failed: %s' % e, retry_after=retry_after) else: self._dns_wait = 1 body = None if command.state is SameState else command.state.toxml( ) # Publish by trying each route in turn publish_timeout = time() + 30 for route in routes: if Host.default_ip is None: raise PublicationError('No IP address', retry_after=60) remaining_time = publish_timeout - time() if remaining_time > 0: try: try: self._publication.publish(body, RouteHeader(route.uri), timeout=limit( remaining_time, min=1, max=10)) except ( ValueError, AttributeError ) as e: # this happens for an initial PUBLISH with body=None raise PublicationError(str(e), retry_after=0) except PublicationETagError: state = self.state # access self.state only once to avoid race conditions if state is not None: self._publication.publish( state.toxml(), RouteHeader(route.uri), timeout=limit(remaining_time, min=1, max=10)) else: command.signal() return except SIPCoreError: raise PublicationError('Internal error', retry_after=5) try: while True: notification = self._data_channel.wait() if notification.name == 'SIPPublicationDidSucceed': break if notification.name == 'SIPPublicationDidEnd': raise PublicationError( 'Publication expired', retry_after=random.uniform(60, 120) ) # publication expired while we were trying to re-publish except SIPPublicationDidFail as e: if e.data.code == 407: # Authentication failed, so retry the publication in some time raise PublicationError('Authentication failed', retry_after=random.uniform( 60, 120)) elif e.data.code == 412: raise PublicationError( 'Conditional request failed', retry_after=0) elif e.data.code == 423: # Get the value of the Min-Expires header if e.data.min_expires is not None and e.data.min_expires > self.account.sip.publish_interval: refresh_interval = e.data.min_expires else: refresh_interval = None raise PublicationError( 'Interval too short', retry_after=random.uniform(60, 120), refresh_interval=refresh_interval) elif e.data.code in (405, 406, 489): raise PublicationError( 'Method or event not supported', retry_after=3600) else: # Otherwise just try the next route continue else: self.publishing = True self._publish_wait = 1 command.signal() break else: # There are no more routes to try, reschedule the publication retry_after = random.uniform(self._publish_wait, 2 * self._publish_wait) self._publish_wait = limit(self._publish_wait * 2, max=30) raise PublicationError('No more routes to try', retry_after=retry_after) except PublicationError as e: self.publishing = False notification_center.discard_observer(self, sender=self._publication) def publish(e): if self.active: self._command_channel.send( Command('publish', event=command.event, state=self.state, refresh_interval=e.refresh_interval)) else: command.signal() self._publication_timer = None self._publication_timer = reactor.callLater( e.retry_after, publish, e) self._publication = None notification_center.post_notification( self.__nickname__ + 'PublicationDidFail', sender=self, data=NotificationData(reason=e.error)) else: notification_center.post_notification(self.__nickname__ + 'PublicationDidSucceed', sender=self)
class SessionInfoController(NSObject): implements(IObserver) window = objc.IBOutlet() sessionBox = objc.IBOutlet() audioBox = objc.IBOutlet() videoBox = objc.IBOutlet() chatBox = objc.IBOutlet() remote_party = objc.IBOutlet() account = objc.IBOutlet() duration = objc.IBOutlet() remote_ua = objc.IBOutlet() status = objc.IBOutlet() remote_endpoint = objc.IBOutlet() tls_lock = objc.IBOutlet() audio_status = objc.IBOutlet() audio_codec = objc.IBOutlet() audio_remote_endpoint = objc.IBOutlet() audio_ice_negotiation = objc.IBOutlet() audio_ice_local_candidate = objc.IBOutlet() audio_ice_remote_candidate = objc.IBOutlet() audio_rtt = objc.IBOutlet() audio_packet_loss = objc.IBOutlet() audio_rtt_graph = objc.IBOutlet() audio_packet_loss_graph = objc.IBOutlet() audio_srtp_lock = objc.IBOutlet() rx_speed_graph = objc.IBOutlet() rx_speed = objc.IBOutlet() tx_speed_graph = objc.IBOutlet() tx_speed = objc.IBOutlet() video_status = objc.IBOutlet() video_codec = objc.IBOutlet() video_remote_endpoint = objc.IBOutlet() video_ice_negotiation = objc.IBOutlet() video_ice_local_candidate = objc.IBOutlet() video_ice_remote_candidate = objc.IBOutlet() video_srtp_lock = objc.IBOutlet() video_rx_speed_graph = objc.IBOutlet() video_rx_speed = objc.IBOutlet() video_tx_speed_graph = objc.IBOutlet() video_tx_speed = objc.IBOutlet() chat_remote_endpoint = objc.IBOutlet() chat_connection_mode = objc.IBOutlet() chat_tls_lock = objc.IBOutlet() def __new__(cls, *args, **kwargs): return cls.alloc().init() def __init__(self, sessionController): self.notification_center = NotificationCenter() self.notification_center.add_observer(self, name='CFGSettingsObjectDidChange') self.sessionController = None self.audio_stream = None self.video_stream = None self.chat_stream = None self.add_session(sessionController) self.add_audio_stream() self.add_video_stream() self.add_chat_stream() self.timer = NSTimer.timerWithTimeInterval_target_selector_userInfo_repeats_(1.0, self, "updateTimer:", None, True) NSRunLoop.currentRunLoop().addTimer_forMode_(self.timer, NSModalPanelRunLoopMode) NSRunLoop.currentRunLoop().addTimer_forMode_(self.timer, NSDefaultRunLoopMode) NSBundle.loadNibNamed_owner_("SessionInfoPanel", self) sessionBoxTitle = NSAttributedString.alloc().initWithString_attributes_(NSLocalizedString("SIP Session", "Label"), NSDictionary.dictionaryWithObject_forKey_(NSColor.orangeColor(), NSForegroundColorAttributeName)) self.sessionBox.setTitle_(sessionBoxTitle) audioBoxTitle = NSAttributedString.alloc().initWithString_attributes_(NSLocalizedString("Audio Stream", "Label"), NSDictionary.dictionaryWithObject_forKey_(NSColor.orangeColor(), NSForegroundColorAttributeName)) self.audioBox.setTitle_(audioBoxTitle) videoBoxTitle = NSAttributedString.alloc().initWithString_attributes_(NSLocalizedString("Video Stream", "Label"), NSDictionary.dictionaryWithObject_forKey_(NSColor.orangeColor(), NSForegroundColorAttributeName)) self.videoBox.setTitle_(videoBoxTitle) chatBoxTitle = NSAttributedString.alloc().initWithString_attributes_(NSLocalizedString("Chat Stream", "Label"), NSDictionary.dictionaryWithObject_forKey_(NSColor.orangeColor(), NSForegroundColorAttributeName)) self.chatBox.setTitle_(chatBoxTitle) settings = SIPSimpleSettings() self.audio_rtt_graph.setLineWidth_(1.0) self.audio_rtt_graph.setLineSpacing_(1.0) self.audio_rtt_graph.setAboveLimit_(settings.gui.rtt_threshold) # if higher show red color self.audio_rtt_graph.setMinimumHeigth_(settings.gui.rtt_threshold) self.audio_packet_loss_graph.setLineWidth_(1.0) self.audio_packet_loss_graph.setLineSpacing_(1.0) self.audio_packet_loss_graph.setAboveLimit_(3) # if higher than 3% show red color self.audio_packet_loss_graph.setLineColor_(NSColor.greenColor()) self.audio_packet_loss_graph.setMinimumHeigth_(5) self.rx_speed_graph.setLineWidth_(1.0) self.rx_speed_graph.setLineSpacing_(0.0) self.rx_speed_graph.setLineColor_(NSColor.greenColor()) self.rx_speed_graph.setMinimumHeigth_(100000) self.rx_speed_graph.setAboveLimit_(120000) self.tx_speed_graph.setLineWidth_(1.0) self.tx_speed_graph.setLineSpacing_(0.0) self.tx_speed_graph.setLineColor_(NSColor.blueColor()) self.tx_speed_graph.setMinimumHeigth_(100000) self.tx_speed_graph.setAboveLimit_(120000) self.video_rx_speed_graph.setLineWidth_(1.0) self.video_rx_speed_graph.setLineSpacing_(0.0) self.video_rx_speed_graph.setLineColor_(NSColor.greenColor()) self.video_rx_speed_graph.setMinimumHeigth_(100000) self.video_rx_speed_graph.setAboveLimit_(1200000) self.video_tx_speed_graph.setLineWidth_(1.0) self.video_tx_speed_graph.setLineSpacing_(0.0) self.video_tx_speed_graph.setLineColor_(NSColor.blueColor()) self.video_tx_speed_graph.setMinimumHeigth_(100000) self.video_tx_speed_graph.setAboveLimit_(1200000) self.resetSession() self.updatePanelValues() def add_session(self, sessionController): if self.sessionController is None: self.sessionController = sessionController self.notification_center.add_observer(self, sender=self.sessionController) def remove_session(self): if self.sessionController is not None: self.notification_center.remove_observer(self, sender=self.sessionController) self.notification_center.remove_observer(self, name='CFGSettingsObjectDidChange') self.sessionController = None self.remove_audio_stream() self.remove_video_stream() self.remove_chat_stream() def add_audio_stream(self): if self.sessionController is not None and self.sessionController.hasStreamOfType("audio") and self.audio_stream is None: self.audio_stream = self.sessionController.streamHandlerOfType("audio") self.notification_center.add_observer(self, sender=self.audio_stream) self.notification_center.add_observer(self, sender=self.audio_stream.stream) def remove_audio_stream(self): if self.audio_stream is not None: self.notification_center.discard_observer(self, sender=self.audio_stream) self.notification_center.discard_observer(self, sender=self.audio_stream.stream) self.audio_stream = None self.updateAudioStatus() def add_video_stream(self): if self.sessionController is not None and self.sessionController.hasStreamOfType("video") and self.video_stream is None: self.video_stream = self.sessionController.streamHandlerOfType("video") self.notification_center.add_observer(self, sender=self.video_stream) self.notification_center.add_observer(self, sender=self.video_stream.stream) def remove_video_stream(self): if self.video_stream is not None: self.notification_center.discard_observer(self, sender=self.video_stream) self.notification_center.discard_observer(self, sender=self.video_stream.stream) self.video_stream = None self.resetVideo() self.updateVideoStatus() def add_chat_stream(self): if self.sessionController is not None and self.sessionController.hasStreamOfType("chat") and self.chat_stream is None: self.chat_stream = self.sessionController.streamHandlerOfType("chat") def remove_chat_stream(self): if self.chat_stream is not None: self.chat_stream = None def _NH_BlinkDidRenegotiateStreams(self, notification): for stream in notification.data.removed_streams: if stream.type == 'audio': self.remove_audio_stream() elif stream.type == 'chat': self.remove_chat_stream() elif stream.type == 'video': self.remove_video_stream() for stream in notification.data.added_streams: if stream.type == 'audio': self.add_audio_stream() elif stream.type == 'chat': self.add_chat_stream() elif stream.type == 'video': self.add_video_stream() self.updatePanelValues() def _NH_CFGSettingsObjectDidChange(self, notification): settings = SIPSimpleSettings() if notification.data.modified.has_key("gui.rtt_threshold"): self.audio_rtt_graph.setAboveLimit_(settings.gui.rtt_threshold) self.audio_rtt_graph.setMinimumHeigth_(settings.gui.rtt_threshold) @allocate_autorelease_pool def handle_notification(self, notification): handler = getattr(self, '_NH_%s' % notification.name, Null) handler(notification) def resetSession(self): self.remote_endpoint.setStringValue_('') self.remote_ua.setStringValue_('') self.status.setStringValue_('') self.duration.setStringValue_('') self.resetAudio() self.resetVideo() self.resetChat() def resetAudio(self): self.audio_status.setStringValue_('') self.audio_codec.setStringValue_('') self.audio_remote_endpoint.setStringValue_('') self.audio_ice_negotiation.setStringValue_('') self.audio_ice_local_candidate.setStringValue_('') self.audio_ice_remote_candidate.setStringValue_('') self.audio_rtt.setStringValue_('') self.audio_packet_loss.setStringValue_('') self.rx_speed.setStringValue_('') self.tx_speed.setStringValue_('') def resetVideo(self): self.video_status.setStringValue_('') self.video_codec.setStringValue_('') self.video_remote_endpoint.setStringValue_('') self.video_ice_negotiation.setStringValue_('') self.video_ice_local_candidate.setStringValue_('') self.video_ice_remote_candidate.setStringValue_('') self.video_rx_speed.setStringValue_('') self.video_tx_speed.setStringValue_('') def resetChat(self): self.chat_remote_endpoint.setStringValue_('') self.chat_connection_mode.setStringValue_('') def updatePanelValues(self): self.updateSession() def updateSession(self): if self.sessionController is None: self.resetSession() else: self.updateSessionStatus() self.remote_party.setStringValue_(self.sessionController.getTitleFull()) self.account.setStringValue_(str(self.sessionController.account.id)) if self.sessionController.conference_info is not None and self.sessionController.remote_focus: pass if hasattr(self.sessionController.session, 'remote_user_agent') and self.sessionController.session.remote_user_agent is not None: self.remote_ua.setStringValue_(self.sessionController.session.remote_user_agent) if self.sessionController.session is not None: if self.sessionController.session.transport is not None: transport = self.sessionController.session.transport if self.sessionController.session.peer_address is not None: self.remote_endpoint.setStringValue_('%s:%s' % (transport, str(self.sessionController.session.peer_address))) self.tls_lock.setHidden_(False if transport == 'tls' else True) elif self.sessionController.routes: route = self.sessionController.routes[0] self.remote_endpoint.setStringValue_('%s:%s:%s' % (route.transport, route.address, route.port)) self.tls_lock.setHidden_(False if route.transport == 'tls' else True) elif self.sessionController.routes: route = self.sessionController.routes[0] self.remote_endpoint.setStringValue_('%s:%s:%s' % (route.transport, route.address, route.port)) self.tls_lock.setHidden_(False if route.transport == 'tls' else True) self.updateAudio() self.updateVideo() self.updateChat() def updateSessionStatus(self, sub_state=None): if self.sessionController.state is None: self.status.setStringValue_("") return if sub_state is None: sub_state = self.sessionController.session.state if self.sessionController.session is not None else 'none' sub_state = re.sub("_", " ", sub_state.encode('utf-8').title()) if sub_state is not None else '' state = self.sessionController.state.title() self.status.setStringValue_('%s (%s)' % (state, sub_state) if state != sub_state and sub_state != 'None' else state) def updateAudio(self): if self.audio_status.stringValue() and (self.sessionController is None or self.audio_stream is None or self.audio_stream.stream is None): self.resetAudio() elif (self.sessionController is not None and self.audio_stream is not None and self.audio_stream.stream is not None): self.updateAudioStatus() self.audio_rtt_graph.setDataQueue_needsDisplay_(self.audio_stream.rtt_history, True if self.window.isVisible() else False) self.rx_speed_graph.setDataQueue_needsDisplay_(self.audio_stream.rx_speed_history, True if self.window.isVisible() else False) self.tx_speed_graph.setDataQueue_needsDisplay_(self.audio_stream.tx_speed_history, True if self.window.isVisible() else False) self.audio_packet_loss_graph.setDataQueue_needsDisplay_(self.audio_stream.loss_history, True if self.window.isVisible() else False) rtt = self.audio_stream.statistics['rtt'] if rtt > 1000: text = '%.1f s' % (float(rtt)/1000.0) elif rtt > 100: text = '%d ms' % rtt elif rtt: text = '%d ms' % rtt else: text = '' self.rx_speed.setStringValue_('Rx %s/s' % format_size(self.audio_stream.statistics['rx_bytes'], bits=True)) self.tx_speed.setStringValue_('Tx %s/s' % format_size(self.audio_stream.statistics['tx_bytes'], bits=True)) self.audio_rtt.setStringValue_(text) self.audio_packet_loss.setStringValue_('%.1f %%' % self.audio_stream.statistics['loss'] if self.audio_stream.statistics['loss'] else '') if self.audio_stream.stream.codec and self.audio_stream.stream.sample_rate: codec = beautify_audio_codec(self.audio_stream.stream.codec) try: settings = SIPSimpleSettings() sample_rate = self.audio_stream.stream.sample_rate/1000 codec = codec + " %0.fkHz" % sample_rate except TypeError: pass self.audio_codec.setStringValue_(codec) self.audio_srtp_lock.setHidden_(False if self.audio_stream.stream.srtp_active else True) else: self.audio_codec.setStringValue_('') self.audio_srtp_lock.setHidden_(True) self.audio_remote_endpoint.setStringValue_('%s:%s' % (self.audio_stream.stream.remote_rtp_address, self.audio_stream.stream.remote_rtp_port) if self.audio_stream.stream.remote_rtp_address else '') if self.audio_stream.stream.ice_active: if self.audio_stream.stream.local_rtp_candidate is not None: try: candidate = ice_candidates[self.audio_stream.stream.local_rtp_candidate.type.lower()] except KeyError: candidate = self.audio_stream.stream.local_rtp_candidate.type.capitalize() else: candidate = '' self.audio_ice_local_candidate.setStringValue_(candidate) if self.audio_stream.stream.remote_rtp_candidate is not None: try: candidate = ice_candidates[self.audio_stream.stream.remote_rtp_candidate.type.lower()] except KeyError: candidate = self.audio_stream.stream.remote_rtp_candidate.type.capitalize() else: candidate = '' self.audio_ice_remote_candidate.setStringValue_(candidate) ice_status = self.audio_stream.ice_negotiation_status if self.audio_stream.ice_negotiation_status is not None else '' if self.audio_stream.stream.ice_active: if self.audio_stream.stream.local_rtp_candidate and self.audio_stream.stream.remote_rtp_candidate: if self.audio_stream.stream.local_rtp_candidate.type.lower() != 'relay' and self.audio_stream.stream.remote_rtp_candidate.type.lower() != 'relay': ice_status += ' ('+ NSLocalizedString("Peer to Peer", "Label")+')' else: ice_status += ' ('+ NSLocalizedString("Server Relayed", "Label") + ')' else: self.audio_ice_local_candidate.setStringValue_('') self.audio_ice_remote_candidate.setStringValue_('') ice_status = self.audio_stream.ice_negotiation_status if self.audio_stream.ice_negotiation_status is not None else '' self.audio_ice_negotiation.setStringValue_(ice_status) def updateVideo(self): if self.video_status.stringValue() and (self.sessionController is None or self.video_stream is None or self.video_stream.stream is None): self.resetVideo() elif (self.sessionController is not None and self.video_stream is not None and self.video_stream.stream is not None): self.updateVideoStatus() self.video_rx_speed_graph.setDataQueue_needsDisplay_(self.video_stream.rx_speed_history, True if self.window.isVisible() else False) self.video_tx_speed_graph.setDataQueue_needsDisplay_(self.video_stream.tx_speed_history, True if self.window.isVisible() else False) rtt = self.video_stream.statistics['rtt'] if rtt > 1000: text = '%.1f s' % (float(rtt)/1000.0) elif rtt > 100: text = '%d ms' % rtt elif rtt: text = '%d ms' % rtt else: text = '' self.video_rx_speed.setStringValue_('Rx %s/s' % format_size(self.video_stream.statistics['rx_bytes'], bits=True)) self.video_tx_speed.setStringValue_('Tx %s/s' % format_size(self.video_stream.statistics['tx_bytes'], bits=True)) if self.video_stream.stream.codec and self.video_stream.stream.clock_rate: codec = beautify_video_codec(self.video_stream.stream.codec) try: settings = SIPSimpleSettings() sample_rate = self.video_stream.stream.clock_rate/1000 codec = codec + " %0.fkHz" % sample_rate except TypeError: pass self.video_codec.setStringValue_(codec) self.video_srtp_lock.setHidden_(False if self.video_stream.stream.srtp_active else True) else: self.video_codec.setStringValue_('') self.video_srtp_lock.setHidden_(True) self.video_remote_endpoint.setStringValue_('%s:%s' % (self.video_stream.stream.remote_rtp_address, self.video_stream.stream.remote_rtp_port) if self.video_stream.stream.remote_rtp_address else '') if self.video_stream.stream.ice_active: if self.video_stream.stream.local_rtp_candidate is not None: try: candidate = ice_candidates[self.video_stream.stream.local_rtp_candidate.type.lower()] except KeyError: candidate = self.video_stream.stream.local_rtp_candidate.type.capitalize() else: candidate = '' self.video_ice_local_candidate.setStringValue_(candidate) if self.video_stream.stream.remote_rtp_candidate is not None: try: candidate = ice_candidates[self.video_stream.stream.remote_rtp_candidate.type.lower()] except KeyError: candidate = self.video_stream.stream.remote_rtp_candidate.type.capitalize() else: candidate = '' self.video_ice_remote_candidate.setStringValue_(candidate) ice_status = self.video_stream.ice_negotiation_status if self.video_stream.ice_negotiation_status is not None else '' if self.video_stream.stream.ice_active: if self.video_stream.stream.local_rtp_candidate and self.video_stream.stream.remote_rtp_candidate: if self.video_stream.stream.local_rtp_candidate.type.lower() != 'relay' and self.video_stream.stream.remote_rtp_candidate.type.lower() != 'relay': ice_status += ' ('+ NSLocalizedString("Peer to Peer", "Label")+')' else: ice_status += ' ('+ NSLocalizedString("Server Relayed", "Label") + ')' else: self.video_ice_local_candidate.setStringValue_('') self.video_ice_remote_candidate.setStringValue_('') ice_status = self.video_stream.ice_negotiation_status if self.video_stream.ice_negotiation_status is not None else '' self.video_ice_negotiation.setStringValue_(ice_status) def updateChat(self): if self.sessionController is None or self.chat_stream is None or self.chat_stream.stream is None or self.chat_stream.stream.msrp is None: self.resetChat() else: if self.chat_stream and self.chat_stream.stream and self.chat_stream.stream.msrp: if len(self.chat_stream.stream.msrp.full_local_path) > 1: self.chat_remote_endpoint.setStringValue_(str(self.chat_stream.stream.msrp.full_local_path[0])) else: self.chat_remote_endpoint.setStringValue_(str(self.chat_stream.stream.msrp.full_remote_path[0])) if self.chat_stream and self.chat_stream.stream: self.chat_connection_mode.setStringValue_(self.chat_stream.stream.local_role.title()) def updateTimer_(self, timer): if self.sessionController is not None: self.updateDuration() self.updateAudio() self.updateVideo() def updateDuration(self): if self.sessionController is not None and self.sessionController.session is not None: if self.sessionController.session.end_time: now = self.sessionController.session.end_time else: now = datetime.datetime(*time.localtime()[:6]) if self.sessionController.session.start_time and now >= self.sessionController.session.start_time: elapsed = now - self.sessionController.session.start_time h = elapsed.days * 24 + elapsed.seconds / (60*60) m = (elapsed.seconds / 60) % 60 s = elapsed.seconds % 60 text = u"%02i:%02i:%02i"%(h,m,s) self.duration.setStringValue_(text) else: self.duration.setStringValue_('') def updateAudioStatus(self): if self.sessionController is None or self.audio_stream is None: self.audio_status.setStringValue_("") else: if self.audio_stream.holdByLocal: self.audio_status.setStringValue_(NSLocalizedString("On Hold", "Label")) elif self.audio_stream.holdByRemote: self.audio_status.setStringValue_(NSLocalizedString("Hold by Remote", "Label")) elif self.audio_stream.status == STREAM_CONNECTED: title = NSLocalizedString("Active", "Label") if self.audio_stream.stream.srtp_active: title = title + ' ' + NSLocalizedString("Encrypted", "Label") title = title + ' (SDES)' self.audio_status.setStringValue_(title) else: self.audio_status.setStringValue_("") def updateVideoStatus(self): if self.sessionController is None or self.video_stream is None: self.video_status.setStringValue_("") else: if self.audio_stream and self.audio_stream.holdByLocal: self.video_status.setStringValue_(NSLocalizedString("On Hold", "Label")) elif self.audio_stream and self.audio_stream.holdByRemote: self.video_status.setStringValue_(NSLocalizedString("Hold by Remote", "Label")) elif self.video_stream.status == STREAM_CONNECTED: title = NSLocalizedString("Active", "Label") if self.video_stream.stream.srtp_active: title = title + ' ' + NSLocalizedString("Encrypted", "Label") title = title + ' (SDES)' self.video_status.setStringValue_(title) else: self.video_status.setStringValue_("") def _NH_BlinkSessionGotRingIndication(self, notification): self.updateSessionStatus(sub_state=NSLocalizedString("Ringing...", "Label")) def _NH_BlinkSessionGotProvisionalResponse(self, notification): self.updateSessionStatus(sub_state=notification.data.reason) def _NH_BlinkSessionDidProcessTransaction(self, notification): self.updateSessionStatus() def _NH_BlinkSentAddProposal(self, notification): self.updateSessionStatus() def _NH_BlinkSentRemoveProposal(self, notification): self.updateSessionStatus() def _NH_BlinkGotProposal(self, notification): self.updateSessionStatus() def _NH_BlinkProposalGotRejected(self, notification): self.updateSessionStatus() def _NH_BlinkStreamHandlersChanged(self, notification): self.updatePanelValues() def _NH_BlinkSessionWillStart(self, notification): self.updatePanelValues() def _NH_BlinkSessionDidStart(self, notification): self.add_audio_stream() self.add_video_stream() self.add_chat_stream() self.updatePanelValues() def _NH_BlinkSessionDidEnd(self, notification): self.stopTimer() def _NH_BlinkConferenceGotUpdate(self, notification): if self.sessionController is not None and self.sessionController.session is not None and hasattr(notification.data, 'conference_info'): pass @run_in_gui_thread def _NH_AudioStreamICENegotiationDidFail(self, notification): if self.audio_stream is not None: self.audio_ice_negotiation.setStringValue_(self.audio_stream.ice_negotiation_status if self.audio_stream.ice_negotiation_status is not None else '') @run_in_gui_thread def _NH_AudioStreamICENegotiationDidSucceed(self, notification): if self.audio_stream is not None: self.audio_ice_negotiation.setStringValue_(self.audio_stream.ice_negotiation_status if self.audio_stream.ice_negotiation_status is not None else '') @run_in_gui_thread def _NH_VideoStreamICENegotiationDidFail(self, notification): if self.video_stream is not None: self.video_ice_negotiation.setStringValue_(self.video_stream.ice_negotiation_status if self.video_stream.ice_negotiation_status is not None else '') @run_in_gui_thread def _NH_VideoStreamICENegotiationDidSucceed(self, notification): if self.video_stream is not None: self.video_ice_negotiation.setStringValue_(self.video_stream.ice_negotiation_status if self.video_stream.ice_negotiation_status is not None else '') def _NH_AudioStreamDidChangeHoldState(self, notification): self.updateAudioStatus() def show(self): self.window.orderFront_(None) def hide(self): self.window.orderOut_(None) def toggle(self): if self.window.isVisible(): self.hide() else: self.show() def windowShouldClose_(self, sender): self.window.orderOut_(None) def close(self): self.stopTimer() self.remove_session() self.window.orderOut_(None) def stopTimer(self): if self.timer: self.timer.invalidate() self.timer = None def dealloc(self): self.audio_packet_loss_graph.removeFromSuperview() self.audio_rtt_graph.removeFromSuperview() self.rx_speed_graph.removeFromSuperview() self.tx_speed_graph.removeFromSuperview() self.video_rx_speed_graph.removeFromSuperview() self.video_tx_speed_graph.removeFromSuperview() super(SessionInfoController, self).dealloc()
def userDefaultsDidChange_(self, notification): userdef = NSUserDefaults.standardUserDefaults() notification_center = NotificationCenter() trace = userdef.integerForKey_("SIPTrace") if trace == Disabled: notification_center.discard_observer(self, name="SIPEngineSIPTrace") notification_center.discard_observer(self, name="DNSLookupTrace") self.sipTraceType = None elif trace == Simplified: notification_center.add_observer(self, name="SIPEngineSIPTrace") notification_center.add_observer(self, name="DNSLookupTrace") self.sipTraceType = "simple" elif trace == Full: notification_center.add_observer(self, name="SIPEngineSIPTrace") notification_center.add_observer(self, name="DNSLookupTrace") self.sipTraceType = "full" trace = userdef.integerForKey_("MSRPTrace") if trace == Disabled: notification_center.discard_observer(self, name="MSRPLibraryLog") notification_center.discard_observer(self, name="MSRPTransportTrace") self.msrpTraceType = None elif trace == Simplified: notification_center.add_observer(self, name="MSRPLibraryLog") notification_center.add_observer(self, name="MSRPTransportTrace") self.msrpTraceType = "simple" elif trace == Full: notification_center.add_observer(self, name="MSRPLibraryLog") notification_center.add_observer(self, name="MSRPTransportTrace") self.msrpTraceType = "full" trace = userdef.integerForKey_("XCAPTrace") if trace == Disabled: notification_center.discard_observer( self, name="XCAPManagerDidDiscoverServerCapabilities") notification_center.discard_observer( self, name="XCAPSubscriptionGotNotify") notification_center.discard_observer( self, name="XCAPManagerDidChangeState") self.xcapTraceType = None elif trace == Simplified: notification_center.add_observer( self, name="XCAPManagerDidDiscoverServerCapabilities") notification_center.add_observer(self, name="XCAPManagerDidChangeState") self.xcapTraceType = "simple" elif trace == Full: notification_center.add_observer( self, name="XCAPManagerDidDiscoverServerCapabilities") notification_center.add_observer(self, name="XCAPManagerDidChangeState") notification_center.add_observer(self, name="XCAPSubscriptionGotNotify") self.xcapTraceType = "full" trace = userdef.boolForKey_("EnablePJSIPTrace") if trace: notification_center.add_observer(self, name="SIPEngineLog") else: notification_center.discard_observer(self, name="SIPEngineLog") trace = userdef.boolForKey_("EnableNotificationsTrace") if trace: notification_center.add_observer(self) else: notification_center.discard_observer(self)
def _CH_register(self, command): notification_center = NotificationCenter() settings = SIPSimpleSettings() if self._registration_timer is not None and self._registration_timer.active(): self._registration_timer.cancel() self._registration_timer = None try: if Host.default_ip is None: raise RegistrationError('No IP address', retry_after=60) # Initialize the registration if self._registration is None: duration = command.refresh_interval or self.account.sip.register_interval try: self._registration = Registration(FromHeader(self.account.uri, self.account.display_name), credentials=self.account.credentials, duration=duration, extra_headers=[Header('Supported', 'gruu')]) except Exception as e: raise RegistrationError('Cannot create registration: %s' % str(e), retry_after=120) notification_center.add_observer(self, sender=self._registration) notification_center.post_notification('SIPAccountWillRegister', sender=self.account) else: notification_center.post_notification('SIPAccountRegistrationWillRefresh', sender=self.account) # Lookup routes if self.account.sip.outbound_proxy is not None and self.account.sip.outbound_proxy.transport in settings.sip.transport_list: uri = SIPURI(host=self.account.sip.outbound_proxy.host, port=self.account.sip.outbound_proxy.port, parameters={'transport': self.account.sip.outbound_proxy.transport}) else: uri = SIPURI(host=self.account.id.domain) lookup = DNSLookup() try: routes = lookup.lookup_sip_proxy(uri, settings.sip.transport_list, tls_name=self.account.sip.tls_name).wait() except DNSLookupError as e: retry_after = int(random.uniform(self._dns_wait, 2*self._dns_wait)) self._dns_wait = limit(2*self._dns_wait, max=30) raise RegistrationError('DNS lookup failed: %s' % e, retry_after=retry_after) else: self._dns_wait = 1 # Register by trying each route in turn register_timeout = time() + 30 i = 0 for route in routes: i += 1 remaining_time = register_timeout-time() if remaining_time > 0: try: contact_uri = self.account.contact[NoGRUU, route] except KeyError: continue contact_header = ContactHeader(contact_uri) instance_id = '"<%s>"' % settings.instance_id contact_header.parameters[b"+sip.instance"] = instance_id.encode() if self.account.nat_traversal.use_ice: contact_header.parameters[b"+sip.ice"] = None route_header = RouteHeader(route.uri) try: self._registration.register(contact_header, route_header, timeout=limit(remaining_time, min=1, max=10)) except SIPCoreError: raise RegistrationError('Internal error', retry_after=5) try: while True: notification = self._data_channel.wait() if notification.name == 'SIPRegistrationDidSucceed': break if notification.name == 'SIPRegistrationDidEnd': raise RegistrationError('Registration expired', retry_after=int(random.uniform(60, 120))) # registration expired while we were trying to re-register except SIPRegistrationDidFail as e: notification_data = NotificationData(code=e.data.code, reason=e.data.reason, registration=self._registration, registrar=route) notification_center.post_notification('SIPAccountRegistrationGotAnswer', sender=self.account, data=notification_data) if e.data.code == 401: # Authentication failed, so retry the registration in some time raise RegistrationError('Authentication failed', retry_after=int(random.uniform(60, 120))) elif e.data.code == 408: # Timeout raise RegistrationError('Request timeout', retry_after=int(random.uniform(15, 40))) elif e.data.code == 423: # Get the value of the Min-Expires header if e.data.min_expires is not None and e.data.min_expires > self.account.sip.register_interval: refresh_interval = e.data.min_expires else: refresh_interval = None raise RegistrationError('Interval too short', retry_after=int(random.uniform(60, 120)), refresh_interval=refresh_interval) else: if i == len(routes): raise RegistrationError(e.data.reason, retry_after=int(random.uniform(15, 40))) else: # Otherwise just try the next route continue else: notification_data = NotificationData(code=notification.data.code, reason=notification.data.reason, registration=self._registration, registrar=route) notification_center.post_notification('SIPAccountRegistrationGotAnswer', sender=self.account, data=notification_data) self.registered = True # Save GRUU try: header = next(header for header in notification.data.contact_header_list if header.parameters.get('+sip.instance', '').strip('"<>') == settings.instance_id) except StopIteration: self.account.contact.public_gruu = None self.account.contact.temporary_gruu = None else: public_gruu = header.parameters.get('pub-gruu', None) temporary_gruu = header.parameters.get('temp-gruu', None) try: self.account.contact.public_gruu = SIPURI.parse(public_gruu.strip('"')) except (AttributeError, SIPCoreError): self.account.contact.public_gruu = None try: self.account.contact.temporary_gruu = SIPURI.parse(temporary_gruu.strip('"')) except (AttributeError, SIPCoreError): self.account.contact.temporary_gruu = None notification_data = NotificationData(contact_header=notification.data.contact_header, contact_header_list=notification.data.contact_header_list, expires=notification.data.expires_in, registrar=route) notification_center.post_notification('SIPAccountRegistrationDidSucceed', sender=self.account, data=notification_data) self._register_wait = 1 command.signal() break else: # There are no more routes to try, reschedule the registration retry_after = int(random.uniform(self._register_wait, 2*self._register_wait)) self._register_wait = limit(self._register_wait*2, max=30) raise RegistrationError('No more routes to try', retry_after=retry_after) except RegistrationError as e: self.registered = False notification_center.discard_observer(self, sender=self._registration) notification_center.post_notification('SIPAccountRegistrationDidFail', sender=self.account, data=NotificationData(error=e.error, retry_after=e.retry_after)) def register(e): if self.active: self._command_channel.send(Command('register', command.event, refresh_interval=e.refresh_interval)) self._registration_timer = None self._registration_timer = reactor.callLater(e.retry_after, register, e) self._registration = None self.account.contact.public_gruu = None self.account.contact.temporary_gruu = None
class VideoController(MediaStream): implements(IObserver) type = "video" ended = False started = False previous_rx_bytes = 0 previous_tx_bytes = 0 previous_tx_packets = 0 previous_rx_packets = 0 all_rx_bytes = 0 statistics_timer = None last_stats = None initial_full_screen = False paused = False # TODO: set zrtp_supported from a Media notification to enable zRTP UI elements -adi zrtp_supported = False # stream supports zRTP zrtp_active = False # stream is engaging zRTP zrtp_verified = False # zRTP peer has been verified zrtp_is_ok = True # zRTP is encrypted ok zrtp_show_verify_phrase = False # show verify phrase @classmethod def createStream(self): return VideoStream() def resetStream(self): self.sessionController.log_debug(u"Reset stream %s" % self) self.notification_center.discard_observer(self, sender=self.stream) self.stream = VideoStream() self.started = False self.previous_rx_bytes = 0 self.previous_tx_bytes = 0 self.all_rx_bytes = 0 self.initial_full_screen = False self.paused = False self.notification_center.add_observer(self, sender=self.stream) @allocate_autorelease_pool @run_in_gui_thread def handle_notification(self, notification): handler = getattr(self, '_NH_%s' % notification.name, Null) handler(notification.sender, notification.data) def initWithOwner_stream_(self, sessionController, stream): self = super(VideoController, self).initWithOwner_stream_(sessionController, stream) self.notification_center = NotificationCenter() sessionController.log_debug(u"Init %s" % self) self.videoWindowController = VideoWindowController(self) self.statistics = {'loss': 0, 'rtt':0 , 'jitter':0 , 'rx_bytes': 0, 'tx_bytes': 0} # 5 minutes of history data for Session Info graphs self.loss_history = deque(maxlen=300) self.rtt_history = deque(maxlen=300) self.jitter_history = deque(maxlen=300) self.rx_speed_history = deque(maxlen=300) self.tx_speed_history = deque(maxlen=300) self.ice_negotiation_status = u'Disabled' if not self.sessionController.account.nat_traversal.use_ice else None return self def updateStatisticsTimer_(self, timer): if not self.stream: return stats = self.stream.statistics if stats is not None and self.last_stats is not None: jitter = stats['rx']['jitter']['last'] / 1000.0 + stats['tx']['jitter']['last'] / 1000.0 rtt = stats['rtt']['last'] / 1000 / 2 rx_packets = stats['rx']['packets'] - self.last_stats['rx']['packets'] self.all_rx_bytes =+ stats['rx']['bytes'] rx_lost_packets = stats['rx']['packets_lost'] - self.last_stats['rx']['packets_lost'] loss = 100.0 * rx_lost_packets / rx_packets if rx_packets else 0 self.statistics['loss'] = loss self.statistics['jitter'] = jitter self.statistics['rtt'] = rtt rx_overhead = (stats['rx']['packets'] - self.previous_rx_packets) * RTP_PACKET_OVERHEAD tx_overhead = (stats['tx']['packets'] - self.previous_tx_packets) * RTP_PACKET_OVERHEAD if self.previous_rx_packets: self.statistics['rx_bytes'] = stats['rx']['bytes']/STATISTICS_INTERVAL - self.previous_rx_bytes + rx_overhead if self.previous_tx_packets: self.statistics['tx_bytes'] = stats['tx']['bytes']/STATISTICS_INTERVAL - self.previous_tx_bytes + tx_overhead if self.statistics['rx_bytes'] < 0: self.statistics['rx_bytes'] = 0 if self.statistics['tx_bytes'] < 0: self.statistics['tx_bytes'] = 0 self.previous_rx_bytes = stats['rx']['bytes'] if stats['rx']['bytes'] >=0 else 0 self.previous_tx_bytes = stats['tx']['bytes'] if stats['tx']['bytes'] >=0 else 0 self.previous_rx_packets = stats['rx']['packets'] self.previous_tx_packets = stats['tx']['packets'] # summarize statistics jitter = self.statistics['jitter'] rtt = self.statistics['rtt'] loss = self.statistics['loss'] if self.jitter_history is not None: self.jitter_history.append(jitter) if self.rtt_history is not None: self.rtt_history.append(rtt) if self.loss_history is not None: self.loss_history.append(loss) if self.rx_speed_history is not None: self.rx_speed_history.append(self.statistics['rx_bytes'] * 8) if self.tx_speed_history is not None: self.tx_speed_history.append(self.statistics['tx_bytes'] * 8) self.last_stats = stats if self.all_rx_bytes > 200000 and not self.initial_full_screen: settings = SIPSimpleSettings() if settings.video.full_screen_after_connect: self.initial_full_screen = True self.videoWindowController.goToFullScreen() @run_in_green_thread def togglePause(self): if self.stream is None: return if self.status != STREAM_CONNECTED: return if self.paused: self.sessionController.log_debug("Resume Video") self.paused = False self.stream.resume() else: self.paused = True self.sessionController.log_debug("Pause Video") self.stream.pause() def show(self): self.videoWindowController.show() def hide(self): self.videoWindowController.hide() def goToFullScreen(self): self.videoWindowController.goToFullScreen() def startOutgoing(self, is_update): self.ended = False self.notification_center.add_observer(self, sender=self.stream) self.notification_center.add_observer(self, sender=self.sessionController) if is_update and self.sessionController.canProposeMediaStreamChanges(): self.changeStatus(STREAM_PROPOSING) else: self.changeStatus(STREAM_WAITING_DNS_LOOKUP) def startIncoming(self, is_update): self.ended = False self.notification_center.add_observer(self, sender=self.stream) self.notification_center.add_observer(self, sender=self.sessionController) self.changeStatus(STREAM_PROPOSING if is_update else STREAM_INCOMING) def dealloc(self): self.sessionController.log_debug(u"Dealloc %s" % self) self.videoWindowController = None self.stream = None self.notification_center = None self.sessionController = None super(VideoController, self).dealloc() def deallocTimer_(self, timer): self.release() def end(self): if self.ended: return self.sessionController.log_debug(u"End %s" % self) self.ended = True NSApp.delegate().contactsWindowController.hideLocalVideoWindow() status = self.status if status in [STREAM_IDLE, STREAM_FAILED]: self.changeStatus(STREAM_IDLE) elif status == STREAM_PROPOSING: self.sessionController.cancelProposal(self.stream) self.changeStatus(STREAM_CANCELLING) else: self.sessionController.endStream(self) self.changeStatus(STREAM_IDLE) self.removeFromSession() self.videoWindowController.close() self.notification_center.discard_observer(self, sender=self.sessionController) dealloc_timer = NSTimer.timerWithTimeInterval_target_selector_userInfo_repeats_(5.0, self, "deallocTimer:", None, False) NSRunLoop.currentRunLoop().addTimer_forMode_(dealloc_timer, NSRunLoopCommonModes) NSRunLoop.currentRunLoop().addTimer_forMode_(dealloc_timer, NSEventTrackingRunLoopMode) @run_in_gui_thread def changeStatus(self, newstate, fail_reason=None): self.status = newstate MediaStream.changeStatus(self, newstate, fail_reason) if newstate in (STREAM_IDLE, STREAM_FAILED): self.end() @run_in_gui_thread def _NH_MediaStreamDidInitialize(self, sender, data): if self.sessionController.session.direction == 'outgoing': self.videoWindowController.initLocalVideoWindow() def _NH_VideoStreamICENegotiationDidFail(self, sender, data): self.sessionController.log_info(u'Video ICE negotiation failed: %s' % data.reason) self.ice_negotiation_status = data.reason self.stopTimers() def _NH_VideoStreamICENegotiationDidSucceed(self, sender, data): self.sessionController.log_info(u'Video ICE negotiation succeeded') self.sessionController.log_info(u'Video RTP endpoints: %s:%d (%s) <-> %s:%d (%s)' % (self.stream.local_rtp_address, self.stream.local_rtp_port, ice_candidates[self.stream.local_rtp_candidate.type.lower()], self.stream.remote_rtp_address, self.stream.remote_rtp_port, ice_candidates[self.stream.remote_rtp_candidate.type.lower()])) if self.stream.local_rtp_candidate.type.lower() != 'relay' and self.stream.remote_rtp_candidate.type.lower() != 'relay': self.sessionController.log_info(u'Video stream is peer to peer') else: self.sessionController.log_info(u'Video stream is relayed by server') self.ice_negotiation_status = 'Success' def _NH_MediaStreamDidStart(self, sender, data): super(VideoController, self)._NH_MediaStreamDidStart(sender, data) self.started = True sample_rate = self.stream.clock_rate/1000 codec = beautify_video_codec(self.stream.codec) self.sessionController.log_info("Video stream established to %s:%s using %s %0.fkHz codec" % (self.stream.remote_rtp_address, self.stream.remote_rtp_port, codec, sample_rate)) self.videoWindowController.show() self.changeStatus(STREAM_CONNECTED) if self.sessionController.hasStreamOfType("chat") and self.videoWindowController.always_on_top: self.videoWindowController.toogleAlwaysOnTop() self.statistics_timer = NSTimer.timerWithTimeInterval_target_selector_userInfo_repeats_(STATISTICS_INTERVAL, self, "updateStatisticsTimer:", None, True) NSRunLoop.currentRunLoop().addTimer_forMode_(self.statistics_timer, NSRunLoopCommonModes) NSRunLoop.currentRunLoop().addTimer_forMode_(self.statistics_timer, NSEventTrackingRunLoopMode) def _NH_MediaStreamDidFail(self, sender, data): super(VideoController, self)._NH_MediaStreamDidFail(sender, data) self.sessionController.log_info(u"Video call failed: %s" % data.reason) self.stopTimers() self.changeStatus(STREAM_FAILED, data.reason) self.ice_negotiation_status = None self.rtt_history = None self.loss_history = None self.jitter_history = None self.rx_speed_history = None self.tx_speed_history = None def _NH_MediaStreamDidEnd(self, sender, data): super(VideoController, self)._NH_MediaStreamDidEnd(sender, data) self.stopTimers() self.ice_negotiation_status = None self.rtt_history = None self.loss_history = None self.jitter_history = None self.rx_speed_history = None self.tx_speed_history = None if self.started: self.sessionController.log_info(u"Video stream ended") else: self.sessionController.log_info(u"Video stream canceled") self.changeStatus(STREAM_IDLE, self.sessionController.endingBy) if not self.started and self.sessionController.failureReason != "Session Cancelled": self.videoWindowController.showDisconnectedPanel() def _NH_BlinkSessionDidFail(self, sender, data): self.stopTimers() def _NH_BlinkSessionDidEnd(self, sender, data): pass def stopTimers(self): if self.statistics_timer is not None: if self.statistics_timer.isValid(): self.statistics_timer.invalidate() self.statistics_timer = None
def userDefaultsDidChange_(self, notification): userdef = NSUserDefaults.standardUserDefaults() notification_center = NotificationCenter() trace = userdef.integerForKey_("SIPTrace") if trace == Disabled: notification_center.discard_observer(self, name="SIPEngineSIPTrace") notification_center.discard_observer(self, name="DNSLookupTrace") self.sipTraceType = None elif trace == Simplified: notification_center.add_observer(self, name="SIPEngineSIPTrace") notification_center.add_observer(self, name="DNSLookupTrace") self.sipTraceType = "simple" elif trace == Full: notification_center.add_observer(self, name="SIPEngineSIPTrace") notification_center.add_observer(self, name="DNSLookupTrace") self.sipTraceType = "full" trace = userdef.integerForKey_("MSRPTrace") if trace == Disabled: notification_center.discard_observer(self, name="MSRPLibraryLog") notification_center.discard_observer(self, name="MSRPTransportTrace") self.msrpTraceType = None elif trace == Simplified: notification_center.add_observer(self, name="MSRPLibraryLog") notification_center.add_observer(self, name="MSRPTransportTrace") self.msrpTraceType = "simple" elif trace == Full: notification_center.add_observer(self, name="MSRPLibraryLog") notification_center.add_observer(self, name="MSRPTransportTrace") self.msrpTraceType = "full" trace = userdef.integerForKey_("XCAPTrace") if trace == Disabled: notification_center.discard_observer(self, name="XCAPManagerDidDiscoverServerCapabilities") notification_center.discard_observer(self, name="XCAPSubscriptionGotNotify") notification_center.discard_observer(self, name="XCAPManagerDidChangeState") self.xcapTraceType = None elif trace == Simplified: notification_center.add_observer(self, name="XCAPManagerDidDiscoverServerCapabilities") notification_center.add_observer(self, name="XCAPManagerDidChangeState") self.xcapTraceType = "simple" elif trace == Full: notification_center.add_observer(self, name="XCAPManagerDidDiscoverServerCapabilities") notification_center.add_observer(self, name="XCAPManagerDidChangeState") notification_center.add_observer(self, name="XCAPSubscriptionGotNotify") self.xcapTraceType = "full" trace = userdef.boolForKey_("EnablePJSIPTrace") if trace: notification_center.add_observer(self, name="SIPEngineLog") else: notification_center.discard_observer(self, name="SIPEngineLog") trace = userdef.boolForKey_("EnableNotificationsTrace") if trace: notification_center.add_observer(self) else: notification_center.discard_observer(self)
class VideoController(MediaStream): type = "video" ended = False started = False previous_rx_bytes = 0 previous_tx_bytes = 0 previous_tx_packets = 0 previous_rx_packets = 0 all_rx_bytes = 0 statistics_timer = None last_stats = None initial_full_screen = False media_received = False waiting_label = NSLocalizedString("Waiting For Media...", "Audio status label") paused = False @objc.python_method @classmethod def createStream(self): return MediaStreamRegistry.VideoStream() @objc.python_method def resetStream(self): self.sessionController.log_debug("Reset stream %s" % self) self.notification_center.discard_observer(self, sender=self.stream) self.stream = MediaStreamRegistry.VideoStream() self.started = False self.previous_rx_bytes = 0 self.previous_tx_bytes = 0 self.all_rx_bytes = 0 self.initial_full_screen = False self.media_received = False self.paused = False self.notification_center.add_observer(self, sender=self.stream) @property def zrtp_verified(self): if not self.zrtp_active: return False return self.stream.encryption.zrtp.verified @property def zrtp_sas(self): if not self.zrtp_active: return None return self.stream.encryption.zrtp.sas @property def zrtp_active(self): return self.stream.encryption.type == 'ZRTP' and self.stream.encryption.active @property def encryption_active(self): return self.stream.encryption.active @property def srtp_active(self): return self.stream.encryption.type == 'SRTP/SDES' and self.stream.encryption.active def confirm_sas(self): if not self.zrtp_active: return try: self.stream.encryption.zrtp.verified = True except Exception: pass @objc.python_method def decline_sas(self): if not self.zrtp_active: return try: self.stream.encryption.zrtp.verified = False except Exception: pass @objc.python_method @run_in_gui_thread def handle_notification(self, notification): handler = getattr(self, '_NH_%s' % notification.name, Null) handler(notification.sender, notification.data) def initWithOwner_stream_(self, sessionController, stream): self = objc.super(VideoController, self).initWithOwner_stream_(sessionController, stream) self.notification_center = NotificationCenter() sessionController.log_debug("Init %s" % self) self.videoRecorder = VideoRecorder(self) self.videoWindowController = VideoWindowController(self) self.statistics = { 'loss_rx': 0, 'rtt': 0, 'jitter': 0, 'rx_bytes': 0, 'tx_bytes': 0, 'fps': 0 } # 5 minutes of history data for Session Info graphs self.loss_rx_history = deque(maxlen=300) self.rtt_history = deque(maxlen=300) self.jitter_history = deque(maxlen=300) self.rx_speed_history = deque(maxlen=300) self.tx_speed_history = deque(maxlen=300) self.ice_negotiation_status = NSLocalizedString( "Disabled", "Label" ) if not self.sessionController.account.nat_traversal.use_ice else None if self.sessionController.video_consumer != "standalone": self.initial_full_screen = True return self @run_in_thread('video-io') def updateStatisticsTimer_(self, timer): if not self.stream: return stats = self.stream.statistics if stats is not None and self.last_stats is not None: jitter = stats['rx']['jitter']['last'] / 1000.0 + stats['tx'][ 'jitter']['last'] / 1000.0 rtt = stats['rtt']['last'] / 1000 / 2 rx_packets = stats['rx']['packets'] - self.last_stats['rx'][ 'packets'] self.all_rx_bytes = +stats['rx']['bytes'] rx_lost_packets = stats['rx']['packets_lost'] - self.last_stats[ 'rx']['packets_lost'] loss_rx = 100.0 * rx_lost_packets / rx_packets if rx_packets else 0 self.statistics['loss_rx'] = loss_rx self.statistics['jitter'] = jitter self.statistics['rtt'] = rtt try: self.statistics['fps'] = self.stream.producer.framerate except AttributeError: self.statistics['fps'] = 0 rx_overhead = (stats['rx']['packets'] - self.previous_rx_packets) * RTP_PACKET_OVERHEAD tx_overhead = (stats['tx']['packets'] - self.previous_tx_packets) * RTP_PACKET_OVERHEAD if self.previous_rx_packets: self.statistics['rx_bytes'] = stats['rx'][ 'bytes'] / STATISTICS_INTERVAL - self.previous_rx_bytes + rx_overhead if self.previous_tx_packets: self.statistics['tx_bytes'] = stats['tx'][ 'bytes'] / STATISTICS_INTERVAL - self.previous_tx_bytes + tx_overhead if self.statistics['rx_bytes'] < 0: self.statistics['rx_bytes'] = 0 if self.statistics['tx_bytes'] < 0: self.statistics['tx_bytes'] = 0 self.previous_rx_bytes = stats['rx'][ 'bytes'] if stats['rx']['bytes'] >= 0 else 0 self.previous_tx_bytes = stats['tx'][ 'bytes'] if stats['tx']['bytes'] >= 0 else 0 self.previous_rx_packets = stats['rx']['packets'] self.previous_tx_packets = stats['tx']['packets'] # summarize statistics jitter = self.statistics['jitter'] rtt = self.statistics['rtt'] loss_rx = self.statistics['loss_rx'] if self.jitter_history is not None: self.jitter_history.append(jitter) if self.rtt_history is not None: self.rtt_history.append(rtt) if self.loss_rx_history is not None: self.loss_rx_history.append(loss_rx) if self.rx_speed_history is not None: self.rx_speed_history.append(self.statistics['rx_bytes'] * 8) if self.tx_speed_history is not None: self.tx_speed_history.append(self.statistics['tx_bytes'] * 8) self.last_stats = stats if self.all_rx_bytes > 200000 and not self.initial_full_screen and self.sessionController.video_consumer == "standalone": settings = SIPSimpleSettings() if settings.video.full_screen_after_connect: self.initial_full_screen = True if self.videoWindowController: self.videoWindowController.goToFullScreen() if self.all_rx_bytes > 200000 and not self.media_received: self.sessionController.log_info('Video channel received data') self.markMediaReceived() @objc.python_method def markMediaReceived(self): self.media_received = True if self.videoWindowController and self.videoWindowController.disconnectLabel and self.videoWindowController.disconnectLabel.stringValue( ) == self.waiting_label: self.videoWindowController.hideStatusLabel() @objc.python_method def togglePause(self): if self.stream is None: return if self.status != STREAM_CONNECTED: return if self.paused: self.sessionController.log_debug("Resume Video") self.paused = False self.stream.resume() else: self.paused = True self.sessionController.log_debug("Pause Video") self.stream.pause() @objc.python_method def showVideoWindow(self): if self.videoWindowController: self.videoWindowController.show() @objc.python_method def hideVideoWindow(self): if self.videoWindowController: if self.videoWindowController.window(): self.videoWindowController.videoView.setProducer(None) if self.videoWindowController.full_screen or self.videoWindowController.full_screen_in_progress: self.videoWindowController.must_hide_after_exit_full_screen = True self.videoWindowController.goToWindowMode() else: self.videoWindowController.window().orderOut_(None) @objc.python_method def hide(self): if self.videoWindowController: self.videoWindowController.hide() @objc.python_method def goToFullScreen(self): if self.videoWindowController: self.videoWindowController.goToFullScreen() @objc.python_method def startOutgoing(self, is_update): if self.videoWindowController: self.videoWindowController.initLocalVideoWindow() self.ended = False self.notification_center.add_observer(self, sender=self.stream) self.notification_center.add_observer(self, sender=self.sessionController) self.notification_center.add_observer(self, sender=self.sessionController, name='VideoRemovedByRemoteParty') if is_update and self.sessionController.canProposeMediaStreamChanges(): self.changeStatus(STREAM_PROPOSING) else: self.changeStatus(STREAM_WAITING_DNS_LOOKUP) self.wait_for_camera_timer = NSTimer.timerWithTimeInterval_target_selector_userInfo_repeats_( 5.0, self, "localVideoReadyTimer:", None, False) NSRunLoop.currentRunLoop().addTimer_forMode_( self.wait_for_camera_timer, NSRunLoopCommonModes) NSRunLoop.currentRunLoop().addTimer_forMode_( self.wait_for_camera_timer, NSEventTrackingRunLoopMode) def localVideoReadyTimer_(self, timer): self.notification_center.post_notification( "BlinkLocalVideoReady", sender=self.sessionController) self.wait_for_camera_timer = None @objc.python_method def startIncoming(self, is_update): self.ended = False self.notification_center.add_observer(self, sender=self.stream) self.notification_center.add_observer(self, sender=self.sessionController) self.notification_center.add_observer(self, sender=self.sessionController, name='VideoRemovedByRemoteParty') self.changeStatus(STREAM_PROPOSING if is_update else STREAM_INCOMING) def dealloc(self): self.sessionController.log_debug("Dealloc %s" % self) self.notification_center.discard_observer( self, sender=self.sessionController) self.notification_center.discard_observer(self, sender=self.stream) self.videoWindowController.release() self.videoWindowController = None self.videoRecorder = None self.stream = None self.sessionController = None self.notification_center = None objc.super(VideoController, self).dealloc() def deallocTimer_(self, timer): self.release() @objc.python_method def end(self): if self.ended: return self.sessionController.log_debug("End %s" % self) self.ended = True if self.sessionController.waitingForLocalVideo: self.stop_wait_for_camera_timer() self.sessionController.cancelBeforeDNSLookup() if self.sessionController.video_consumer == "audio": NSApp.delegate().contactsWindowController.detachVideo( self.sessionController) elif self.sessionController.video_consumer == "chat": NSApp.delegate().chatWindowController.detachVideo( self.sessionController) status = self.status if status in [STREAM_IDLE, STREAM_FAILED]: self.changeStatus(STREAM_IDLE) elif status == STREAM_PROPOSING: self.sessionController.cancelProposal(self) self.changeStatus(STREAM_CANCELLING) else: self.sessionController.endStream(self) self.changeStatus(STREAM_IDLE) self.removeFromSession() self.videoRecorder.stop() self.videoWindowController.close() self.notification_center.remove_observer( self, sender=self.sessionController, name='VideoRemovedByRemoteParty') dealloc_timer = NSTimer.timerWithTimeInterval_target_selector_userInfo_repeats_( 5.0, self, "deallocTimer:", None, False) NSRunLoop.currentRunLoop().addTimer_forMode_(dealloc_timer, NSRunLoopCommonModes) NSRunLoop.currentRunLoop().addTimer_forMode_( dealloc_timer, NSEventTrackingRunLoopMode) @objc.python_method def sessionStateChanged(self, state, detail): if state == STATE_CONNECTING: self.changeStatus(STREAM_CONNECTING) elif state in (STATE_FAILED, STATE_DNS_FAILED): if detail.startswith("DNS Lookup"): if self.videoWindowController: self.videoWindowController.showStatusLabel( NSLocalizedString("DNS Lookup failed", "Audio status label")) self.changeStatus( STREAM_FAILED, NSLocalizedString("DNS Lookup failed", "Audio status label")) else: self.videoWindowController.showStatusLabel(detail) self.changeStatus(STREAM_FAILED, detail) @objc.python_method @run_in_gui_thread def changeStatus(self, newstate, fail_reason=None): if self.status == newstate: return self.status = newstate MediaStream.changeStatus(self, newstate, fail_reason) if newstate in (STREAM_IDLE, STREAM_FAILED): self.end() if self.videoWindowController and self.videoWindowController.localVideoWindow: self.videoWindowController.localVideoWindow.cancelButton.setHidden_( True) if self.videoWindowController: if newstate == STREAM_WAITING_DNS_LOOKUP: self.videoWindowController.showStatusLabel( NSLocalizedString("Finding Destination...", "Audio status label")) elif newstate == STREAM_RINGING: self.videoWindowController.showStatusLabel( NSLocalizedString("Ringing...", "Audio status label")) elif newstate == STREAM_CONNECTING: self.videoWindowController.showStatusLabel( NSLocalizedString("Connecting...", "Audio status label")) elif newstate == STREAM_CONNECTED: if not self.media_received: self.videoWindowController.showStatusLabel( self.waiting_label) elif newstate == STREAM_PROPOSING: self.videoWindowController.showStatusLabel( NSLocalizedString("Adding Video...", "Audio status label")) @objc.python_method def _NH_MediaStreamDidInitialize(self, sender, data): pass @objc.python_method def _NH_RTPStreamICENegotiationDidFail(self, sender, data): self.sessionController.log_info('Video ICE negotiation failed: %s' % data.reason) self.ice_negotiation_status = data.reason @objc.python_method @run_in_gui_thread def _NH_RTPStreamICENegotiationStateDidChange(self, sender, data): if self.videoWindowController: if data.state == 'GATHERING': self.videoWindowController.showStatusLabel( NSLocalizedString("Gathering ICE Candidates...", "Audio status label")) elif data.state == 'NEGOTIATION_START': self.videoWindowController.showStatusLabel( NSLocalizedString("Connecting...", "Audio status label")) elif data.state == 'NEGOTIATING': self.videoWindowController.showStatusLabel( NSLocalizedString("Negotiating ICE...", "Audio status label")) elif data.state == 'GATHERING_COMPLETE': self.videoWindowController.showStatusLabel( NSLocalizedString("Gathering Complete", "Audio status label")) elif data.state == 'RUNNING': self.videoWindowController.showStatusLabel( NSLocalizedString("ICE Negotiation Succeeded", "Audio status label")) elif data.state == 'FAILED': self.videoWindowController.showStatusLabel( NSLocalizedString("ICE Negotiation Failed", "Audio status label")) @objc.python_method def _NH_RTPStreamICENegotiationDidSucceed(self, sender, data): self.sessionController.log_info('Video ICE negotiation succeeded') self.sessionController.log_info( 'Video RTP endpoints: %s:%d (%s) <-> %s:%d (%s)' % (self.stream.local_rtp_address, self.stream.local_rtp_port, ice_candidates[self.stream.local_rtp_candidate.type.lower()], self.stream.remote_rtp_address, self.stream.remote_rtp_port, ice_candidates[self.stream.remote_rtp_candidate.type.lower()])) self.ice_negotiation_status = 'Success' @objc.python_method def _NH_VideoStreamReceivedKeyFrame(self, sender, data): if not self.media_received: self.sessionController.log_info('Video channel received key frame') self.markMediaReceived() @objc.python_method @run_in_gui_thread def _NH_BlinkSessionChangedDisplayName(self, sender, data): if self.videoWindowController: self.videoWindowController.title = NSLocalizedString( "Video with %s", "Window title") % self.sessionController.titleShort if self.videoWindowController.window(): self.videoWindowController.window().setTitle_( self.videoWindowController.title) @objc.python_method def _NH_MediaStreamDidStart(self, sender, data): self.started = True sample_rate = self.stream.sample_rate / 1000 codec = beautify_video_codec(self.stream.codec) self.sessionController.log_info( "Video stream established to %s:%s using %s codec" % (self.stream.remote_rtp_address, self.stream.remote_rtp_port, codec)) self.changeStatus(STREAM_CONNECTED) self.sessionController.setVideoConsumer( self.sessionController.video_consumer) self.statistics_timer = NSTimer.timerWithTimeInterval_target_selector_userInfo_repeats_( STATISTICS_INTERVAL, self, "updateStatisticsTimer:", None, True) NSRunLoop.currentRunLoop().addTimer_forMode_(self.statistics_timer, NSRunLoopCommonModes) NSRunLoop.currentRunLoop().addTimer_forMode_( self.statistics_timer, NSEventTrackingRunLoopMode) @objc.python_method def _NH_MediaStreamDidNotInitialize(self, sender, data): self.sessionController.log_info("Video call failed: %s" % data.reason) self.stopTimers() self.changeStatus(STREAM_FAILED, data.reason) self.ice_negotiation_status = None self.rtt_history = None self.loss_rx_history = None self.jitter_history = None self.rx_speed_history = None self.tx_speed_history = None @objc.python_method def _NH_MediaStreamDidFail(self, sender, data): pass @objc.python_method def _NH_MediaStreamWillEnd(self, sender, data): self.stopTimers() if self.videoWindowController: self.videoWindowController.goToWindowMode() self.ice_negotiation_status = None self.rtt_history = None self.loss_rx_history = None self.jitter_history = None self.rx_speed_history = None self.tx_speed_history = None @objc.python_method def _NH_MediaStreamDidEnd(self, sender, data): if data.error is not None: self.sessionController.log_info("Video call failed: %s" % data.error) self.changeStatus(STREAM_FAILED, data.reason) elif self.started: self.sessionController.log_info("Video stream ended") else: self.sessionController.log_info("Video stream canceled") self.changeStatus(STREAM_IDLE, self.sessionController.endingBy) @objc.python_method def _NH_BlinkSessionGotRingIndication(self, sender, data): self.changeStatus(STREAM_RINGING) @objc.python_method def _NH_VideoRemovedByRemoteParty(self, sender, data): if self.videoWindowController: self.videoWindowController.showStatusLabel( NSLocalizedString("Video Ended", "Label")) @objc.python_method def _NH_BlinkProposalGotRejected(self, sender, data): if self.stream in data.proposed_streams: if self.videoWindowController: self.videoWindowController.showStatusLabel( NSLocalizedString("Proposal rejected", "Label")) @objc.python_method def _NH_BlinkWillCancelProposal(self, sender, data): self.sessionController.log_info("Video proposal cancelled") self.changeStatus(STREAM_FAILED, "Proposal Cancelled") @objc.python_method def _NH_BlinkSessionDidStart(self, sender, data): if self.status != STREAM_CONNECTED: if self.videoWindowController: if not self.media_received: self.videoWindowController.showStatusLabel( NSLocalizedString("Waiting for Media...", "Label")) audio_stream = self.sessionController.streamHandlerOfType("audio") if audio_stream and audio_stream.status in ( STREAM_CONNECTING, STREAM_CONNECTED ) and self.sessionController.video_consumer == 'audio': NSApp.delegate().contactsWindowController.showAudioDrawer() @objc.python_method def _NH_BlinkSessionDidFail(self, sender, data): if host is None or host.default_ip is None: reason = NSLocalizedString("No Internet connection", "Label") else: reason = "%s (%s)" % (data.failure_reason.title(), data.code) if data.code is not None: if data.code == 486: reason = NSLocalizedString("Busy Here", "Label") elif data.code == 487: reason = NSLocalizedString("Session Cancelled", "Label") elif data.code == 603: reason = NSLocalizedString("Call Declined", "Label") elif data.code == 408: if data.originator == 'local': reason = NSLocalizedString("Network Timeout", "Label") else: reason = NSLocalizedString("User Unreachable", "Label") elif data.code == 480: reason = NSLocalizedString("User Not Online", "Label") elif data.code == 482: reason = NSLocalizedString("User Unreachable", "Label") elif data.code >= 500 and data.code < 600: reason = NSLocalizedString( "Server Failure (%s)" % data.code, "Label") if self.videoWindowController: self.videoWindowController.showStatusLabel(reason) self.stopTimers() self.changeStatus(STREAM_FAILED) @objc.python_method def _NH_BlinkSessionWillEnd(self, sender, data): if self.videoWindowController: self.videoWindowController.showStatusLabel( NSLocalizedString("Video Ended", "Label")) @objc.python_method def stopTimers(self): if self.statistics_timer is not None: if self.statistics_timer.isValid(): self.statistics_timer.invalidate() self.statistics_timer = None @objc.python_method def stop_wait_for_camera_timer(self): if self.wait_for_camera_timer is not None: if self.wait_for_camera_timer.isValid(): self.wait_for_camera_timer.invalidate() self.wait_for_camera_timer = None @objc.python_method def _NH_RTPStreamDidEnableEncryption(self, sender, data): self.sessionController.log_info( "%s video encryption active using %s" % (sender.encryption.type, sender.encryption.cipher)) try: otr = self.sessionController.encryption['video'] except KeyError: self.sessionController.encryption['video'] = {} self.sessionController.encryption['video'][ 'type'] = sender.encryption.type if self.videoWindowController: self.videoWindowController.update_encryption_icon() @objc.python_method def _NH_RTPStreamDidNotEncryption(self, sender, data): self.sessionController.log_info("Video encryption not enabled: %s" % data.reason) if sender.encryption.type != 'ZRTP': return if self.videoWindowController: self.videoWindowController.update_encryption_icon() @objc.python_method def _NH_RTPStreamZRTPReceivedSAS(self, sender, data): if self.videoWindowController: self.videoWindowController.update_encryption_icon() @objc.python_method def _NH_RTPStreamZRTPVerifiedStateChanged(self, sender, data): if self.videoWindowController: try: otr = self.sessionController.encryption['video'] except KeyError: self.sessionController.encryption['video'] = {} self.sessionController.encryption['video']['type'] = 'ZRTP' self.sessionController.encryption['video'][ 'verified'] = 'yes' if self.stream.encryption.zrtp.verified else 'no' self.videoWindowController.update_encryption_icon()