示例#1
0
    def __init__(self, session, audio_stream):
        self.session = session
        self.stream = audio_stream
        self.start_time = None

        notification_center = NotificationCenter()
        notification_center.add_observer(self, sender=self.stream)

        self.beep = WavePlayer(SIPApplication.voice_audio_mixer,
                               Resources.get('answering_machine_tone.wav'))
        notification_center.add_observer(self, sender=self.beep)

        message_wav = SIPSimpleSettings().answering_machine.unavailable_message
        if message_wav:
            self.unavailable_message = WavePlayer(
                SIPApplication.voice_audio_mixer, message_wav.sound_file.path,
                message_wav.sound_file.volume, 1, 2, False)
            notification_center.add_observer(self,
                                             sender=self.unavailable_message)
            self.stream.bridge.add(self.unavailable_message)
        else:
            self.unavailable_message = None
            self.stream.bridge.add(self.beep)

        self.stream.device.input_muted = True
示例#2
0
 def _NH_AudioStreamGotDTMF(self, notification):
     digit_map = {'*': 'star'}
     filename = 'sounds/dtmf_%s_tone.wav' % digit_map.get(notification.data.digit, notification.data.digit)
     player = WavePlayer(SIPApplication.voice_audio_bridge.mixer, Resources.get(filename))
     notification.center.add_observer(self, sender=player)
     SIPApplication.voice_audio_bridge.add(player)
     player.start()
示例#3
0
 def capture(self):
     try:
         self.image = self.vncclient.image.copy()
     except AttributeError:
         pass
     else:
         player = WavePlayer(SIPApplication.alert_audio_bridge.mixer, Resources.get('sounds/screenshot.wav'), volume=30)
         SIPApplication.alert_audio_bridge.add(player)
         player.start()
示例#4
0
 def capture(self):
     try:
         self.image = self.vncclient.image.copy()
     except AttributeError:
         pass
     else:
         player = WavePlayer(SIPApplication.alert_audio_bridge.mixer, Resources.get('sounds/screenshot.wav'), volume=30)
         SIPApplication.alert_audio_bridge.add(player)
         player.start()
示例#5
0
 def play_audio_welcome(self, welcome_prompt):
     try:
         audio_stream = next(stream for stream in self.session.streams
                             if stream.type == 'audio')
     except StopIteration:
         return
     player = WavePlayer(audio_stream.mixer,
                         '',
                         pause_time=1,
                         initial_delay=1,
                         volume=50)
     audio_stream.bridge.add(player)
     try:
         if welcome_prompt:
             file = Resources.get('sounds/co_welcome_conference.wav')
             self.play_file_in_player(player, file, 1)
         user_count = len({
             str(s.remote_identity.uri)
             for s in self.room.sessions
             if s.remote_identity.uri != self.session.remote_identity.uri
             and any(stream
                     for stream in s.streams if stream.type == 'audio')
         })
         if user_count == 0:
             file = Resources.get('sounds/co_only_one.wav')
             self.play_file_in_player(player, file, 0.5)
         elif user_count == 1:
             file = Resources.get('sounds/co_there_is_one.wav')
             self.play_file_in_player(player, file, 0.5)
         elif user_count < 100:
             file = Resources.get('sounds/co_there_are.wav')
             self.play_file_in_player(player, file, 0.2)
             if user_count <= 24:
                 file = Resources.get('sounds/bi_%d.wav' % user_count)
                 self.play_file_in_player(player, file, 0.1)
             else:
                 file = Resources.get('sounds/bi_%d0.wav' %
                                      (user_count / 10))
                 self.play_file_in_player(player, file, 0.1)
                 file = Resources.get('sounds/bi_%d.wav' %
                                      (user_count % 10))
                 self.play_file_in_player(player, file, 0.1)
             file = Resources.get('sounds/co_more_participants.wav')
             self.play_file_in_player(player, file, 0)
         file = Resources.get('sounds/connected_tone.wav')
         self.play_file_in_player(player, file, 0.1)
     except proc.ProcExit:
         # No need to remove the bridge from the stream, it's done automatically
         pass
     else:
         audio_stream.bridge.remove(player)
         self.room.audio_conference.add(audio_stream)
         self.room.audio_conference.unhold()
     finally:
         player.stop()
示例#6
0
    def play_hangup(self):
        settings = SIPSimpleSettings()

        if settings.audio.silent:
            return

        if time.time() - self.last_hangup_tone_time > HANGUP_TONE_THROTLE_DELAY:
            hangup_tone = WavePlayer(SIPApplication.voice_audio_mixer, Resources.get('hangup_tone.wav'), volume=30)
            NotificationCenter().add_observer(self, sender=hangup_tone, name="WavePlayerDidEnd")
            SIPApplication.voice_audio_bridge.add(hangup_tone)
            hangup_tone.start()
            self.last_hangup_tone_time = time.time()
 def _NH_SIPSessionDidStart(self, notification):
     log.msg('Session started')
     session = notification.sender
     audio_stream = session.streams[0]
     prompt = os.path.realpath(os.path.join(os.path.dirname(__file__), 'jamesbond.wav'))
     player = WavePlayer(audio_stream.mixer, prompt, pause_time=1, initial_play=False)
     audio_stream.bridge.add(player)
     try:
         player.play().wait()
     except WavePlayerError:
         pass
     audio_stream.bridge.remove(player)
     session.end()
示例#8
0
 def ringtone(self):
     if 'ringtone' not in self.__dict__:
         if set(self.proposed_streams).intersection(['audio', 'video', 'desktop-sharing']):
             sound_file = self.session.account.sounds.inbound_ringtone
             if sound_file is not None and sound_file.path is DefaultPath:
                 settings = SIPSimpleSettings()
                 sound_file = settings.sounds.inbound_ringtone
             ringtone = WavePlayer(SIPApplication.alert_audio_mixer, sound_file.path, volume=sound_file.volume, loop_count=0, pause_time=2.7) if sound_file is not None else Null
             ringtone.bridge = SIPApplication.alert_audio_bridge
         else:
             ringtone = WavePlayer(SIPApplication.alert_audio_mixer, Resources.get('sounds/beeping_ringtone.wav'), volume=70, loop_count=0, pause_time=5)
             ringtone.bridge = SIPApplication.alert_audio_bridge
         self.__dict__['ringtone'] = ringtone
     return self.__dict__['ringtone']
示例#9
0
    def _cleanup(self):
        if self.timer.running:
            self.timer.stop()

        self.notification_center.remove_observer(self, sender=self.session)
        for k in self.streams.keys():
            self._set_stream(k, None)

        player = WavePlayer(SIPApplication.voice_audio_bridge.mixer, Resources.get('sounds/hangup_tone.wav'), volume=60)
        self.notification_center.add_observer(self, sender=player)
        SIPApplication.voice_audio_bridge.add(player)
        player.start()

        self.notification_center.post_notification('SessionItemDidEnd', sender=self)
示例#10
0
 def send_dtmf(self, digit):
     if self.audio_stream is not None:
         try:
             self.audio_stream.send_dtmf(digit)
         except RuntimeError:
             pass
         else:
             digit_map = {'*': 'star'}
             filename = 'sounds/dtmf_%s_tone.wav' % digit_map.get(digit, digit)
             player = WavePlayer(SIPApplication.voice_audio_bridge.mixer, Resources.get(filename))
             self.notification_center.add_observer(self, sender=player)
             if self.session.account.rtp.inband_dtmf:
                 self.audio_stream.bridge.add(player)
             SIPApplication.voice_audio_bridge.add(player)
             player.start()
示例#11
0
 def _play(self):
     config = get_config('%s@%s' % (self.session.request_uri.user, self.session.request_uri.host))
     if config is None:
         config = get_config('%s' % self.session.request_uri.user)
     try:
         audio_stream = next(stream for stream in self.session.streams if stream.type=='audio')
     except StopIteration:
         self.proc = None
         return
     player = WavePlayer(audio_stream.mixer, config.file)
     audio_stream.bridge.add(player)
     log.msg(u"Playing file %s for session %s" % (config.file, self.session.call_id))
     try:
         player.play().wait()
     except (ValueError, WavePlayerError), e:
         log.warning(u"Error playing file %s: %s" % (config.file, e))
示例#12
0
    def play_hangup(self):
        settings = SIPSimpleSettings()

        if settings.audio.silent:
            return

        if time.time(
        ) - self.last_hangup_tone_time > HANGUP_TONE_THROTLE_DELAY:
            hangup_tone = WavePlayer(SIPApplication.voice_audio_mixer,
                                     Resources.get('hangup_tone.wav'),
                                     volume=30)
            NotificationCenter().add_observer(self,
                                              sender=hangup_tone,
                                              name="WavePlayerDidEnd")
            SIPApplication.voice_audio_bridge.add(hangup_tone)
            hangup_tone.start()
            self.last_hangup_tone_time = time.time()
示例#13
0
 def _NH_SIPSessionDidStart(self, notification):
     log.msg('Session started')
     session = notification.sender
     audio_stream = session.streams[0]
     prompt = os.path.realpath(
         os.path.join(os.path.dirname(__file__), 'jamesbond.wav'))
     player = WavePlayer(audio_stream.mixer,
                         prompt,
                         pause_time=1,
                         initial_play=False)
     audio_stream.bridge.add(player)
     try:
         player.play().wait()
     except WavePlayerError:
         pass
     audio_stream.bridge.remove(player)
     session.end()
示例#14
0
 def play_audio_welcome(self, welcome_prompt):
     try:
         audio_stream = next(stream for stream in self.session.streams if stream.type == 'audio')
     except StopIteration:
         return
     player = WavePlayer(audio_stream.mixer, '', pause_time=1, initial_delay=1, volume=50)
     audio_stream.bridge.add(player)
     try:
         if welcome_prompt:
             file = Resources.get('sounds/co_welcome_conference.wav')
             self.play_file_in_player(player, file, 1)
         user_count = len({str(s.remote_identity.uri) for s in self.room.sessions if s.remote_identity.uri != self.session.remote_identity.uri and any(stream for stream in s.streams if stream.type == 'audio')})
         if user_count == 0:
             file = Resources.get('sounds/co_only_one.wav')
             self.play_file_in_player(player, file, 0.5)
         elif user_count == 1:
             file = Resources.get('sounds/co_there_is_one.wav')
             self.play_file_in_player(player, file, 0.5)
         elif user_count < 100:
             file = Resources.get('sounds/co_there_are.wav')
             self.play_file_in_player(player, file, 0.2)
             if user_count <= 24:
                 file = Resources.get('sounds/bi_%d.wav' % user_count)
                 self.play_file_in_player(player, file, 0.1)
             else:
                 file = Resources.get('sounds/bi_%d0.wav' % (user_count / 10))
                 self.play_file_in_player(player, file, 0.1)
                 file = Resources.get('sounds/bi_%d.wav' % (user_count % 10))
                 self.play_file_in_player(player, file, 0.1)
             file = Resources.get('sounds/co_more_participants.wav')
             self.play_file_in_player(player, file, 0)
         file = Resources.get('sounds/connected_tone.wav')
         self.play_file_in_player(player, file, 0.1)
     except proc.ProcExit:
         # No need to remove the bridge from the stream, it's done automatically
         pass
     else:
         audio_stream.bridge.remove(player)
         self.room.audio_conference.add(audio_stream)
         self.room.audio_conference.unhold()
     finally:
         player.stop()
示例#15
0
 def _play(self):
     config = get_config(
         '%s@%s' %
         (self.session.request_uri.user, self.session.request_uri.host))
     if config is None:
         config = get_config('%s' % self.session.request_uri.user)
     try:
         audio_stream = next(stream for stream in self.session.streams
                             if stream.type == 'audio')
     except StopIteration:
         self.proc = None
         return
     player = WavePlayer(audio_stream.mixer, config.file)
     audio_stream.bridge.add(player)
     log.msg(u"Playing file %s for session %s" %
             (config.file, self.session.call_id))
     try:
         player.play().wait()
     except (ValueError, WavePlayerError), e:
         log.warning(u"Error playing file %s: %s" % (config.file, e))
示例#16
0
 def _set_stream(self, stream_type, stream):
     old_stream = self.streams.get(stream_type, None)
     self.streams[stream_type] = stream
     if old_stream is not None:
         self.notification_center.remove_observer(self, sender=old_stream)
         if stream_type == 'audio':
             self.hold_tone = Null
     if stream is not None:
         self.notification_center.add_observer(self, sender=stream)
         if stream_type == 'audio':
             self.hold_tone = WavePlayer(stream.bridge.mixer, Resources.get('sounds/hold_tone.wav'), loop_count=0, pause_time=45, volume=30)
             stream.bridge.add(self.hold_tone)
示例#17
0
    def __init__(self, session, audio_stream):
        self.session = session
        self.stream = audio_stream
        self.start_time = None

        notification_center = NotificationCenter()
        notification_center.add_observer(self, sender=self.stream)

        self.beep = WavePlayer(SIPApplication.voice_audio_mixer, Resources.get('answering_machine_tone.wav'))
        notification_center.add_observer(self, sender=self.beep)

        message_wav = SIPSimpleSettings().answering_machine.unavailable_message
        if message_wav:
            self.unavailable_message = WavePlayer(SIPApplication.voice_audio_mixer, message_wav.sound_file.path, message_wav.sound_file.volume, 1, 2, False)
            notification_center.add_observer(self, sender=self.unavailable_message)
            self.stream.bridge.add(self.unavailable_message)
        else:
            self.unavailable_message = None
            self.stream.bridge.add(self.beep)

        self.stream.device.input_muted = True
示例#18
0
 def _play(self):
     config = get_config(
         '%s@%s' %
         (self.session.request_uri.user, self.session.request_uri.host))
     if config is None:
         config = get_config('%s' % self.session.request_uri.user)
     try:
         audio_stream = next(stream for stream in self.session.streams
                             if stream.type == 'audio')
     except StopIteration:
         self.proc = None
         return
     player = WavePlayer(audio_stream.mixer, config.file)
     audio_stream.bridge.add(player)
     log.info(u'Playing file %s for session %s' %
              (config.file, self.session.call_id))
     try:
         player.play().wait()
     except (ValueError, WavePlayerError) as e:
         log.warning(u'Error playing file %s: %s' % (config.file, e))
     except proc.ProcExit:
         pass
     finally:
         player.stop()
         self.proc = None
         audio_stream.bridge.remove(player)
         self.session.end()
         self.session = None
示例#19
0
 def _NH_DNSLookupDidSucceed(self, notification):
     settings = SIPSimpleSettings()
     notification.center.remove_observer(self, sender=notification.sender)
     if self.pending_removal:
         return
     if self.audio_stream:
         outbound_ringtone = settings.sounds.outbound_ringtone
         if outbound_ringtone:
             self.outbound_ringtone = WavePlayer(self.audio_stream.mixer, outbound_ringtone.path, outbound_ringtone.volume, loop_count=0, pause_time=5)
             self.audio_stream.bridge.add(self.outbound_ringtone)
     routes = notification.data.result
     self._tls = routes[0].transport=='tls' if routes else False
     self.status = u'Connecting...'
     self.session.connect(ToHeader(self.uri), routes, self.streams.values())
示例#20
0
	def call(self, account_name, callee, wave_file, length=None):
		logging.info("calling from: %s - to: %s", account_name, callee)
		# Setup wave playback
		self.player = WavePlayer(SIPApplication.voice_audio_mixer, wave_file, loop_count=0, initial_play=False)
		
		# configure callee and route to him/her
		callee_header = ToHeader(SIPURI.parse(callee))
		routes = [Route("62.220.31.184", 5060, "udp")]
		
		# locate caller
		account = self.accounts.get(account_name, None)
		if account is None:
			raise Exception("No account with that name found")
		
		# finally make the call
		session = Session(account)
		session.connect(callee_header, routes, [AudioStream()])
		
		# if we got a length, end the call after it
		if not length is None:
			time.sleep(length)
			session.end()
示例#21
0
class SimpleCallApplication(SIPApplication):
	def __init__(self):
		SIPApplication.__init__(self)
		
		# events used through out lifecycle
		self.ended = Event()
		self.started = Event()
		self.registering = Event()
		
		# normal properties
		self.accounts = {}
		self.active_sessions = []
		
		# configure notifications
		notification_center = NotificationCenter()
		notification_center.add_observer(self)
		
		# lets get this thing rolling
		self.start(MemoryStorage())
		
		# wait for it to finish
		self.started.wait()
	
	def add_account(self, name, username, password):
		if name in self.accounts:
			raise Exception("Already got account with that name")
		
		logging.info("adding account: %s", name)
		
		# clear the event, in case something went wrong
		self.registering.clear()
		
		# register/create the account
		new_account = Account(name)
		new_account.auth.username = username
		new_account.auth.password = password
		new_account.sip.register_interval = 30
		new_account.enabled = True
		new_account.save()
		
		# wait for it to be completly created
		self.registering.wait()
		
		# remember our account
		self.accounts[name] = new_account
	
	def call(self, account_name, callee, wave_file, length=None):
		logging.info("calling from: %s - to: %s", account_name, callee)
		# Setup wave playback
		self.player = WavePlayer(SIPApplication.voice_audio_mixer, wave_file, loop_count=0, initial_play=False)
		
		# configure callee and route to him/her
		callee_header = ToHeader(SIPURI.parse(callee))
		routes = [Route("62.220.31.184", 5060, "udp")]
		
		# locate caller
		account = self.accounts.get(account_name, None)
		if account is None:
			raise Exception("No account with that name found")
		
		# finally make the call
		session = Session(account)
		session.connect(callee_header, routes, [AudioStream()])
		
		# if we got a length, end the call after it
		if not length is None:
			time.sleep(length)
			session.end()
	
	# ----------------------------
	# Application Events
	# ----------------------------
	@run_in_green_thread
	def _NH_SIPApplicationDidStart(self, notification):
		logging.info("Application started")
		self.started.set()
	
	def _NH_SIPApplicationDidEnd(self, notification):
		logging.info("Application ended")
		self.ended.set()
	
	# ----------------------------
	# Account Events
	# ----------------------------
	def _NH_SIPAccountWillActivate(self, notification):
		logging.info("Activating!")
	
	def _NH_SIPAccountDidActivate(self, notification):
		logging.info("Activated!")
		self.registering.set()
	
	# ----------------------------
	# Session Events
	# ----------------------------
	def _NH_SIPSessionGotRingIndication(self, notification):
		logging.info('Ringing!')

	def _NH_SIPSessionDidStart(self, notification):
		logging.info('Session started!')
		session = notification.sender
		audio_stream = session.streams[0]
		audio_stream.bridge.add(self.player)
		self.player.play()

	def _NH_SIPSessionDidFail(self, notification):
		logging.info('Failed to connect')
		self.stop()

	def _NH_SIPSessionWillEnd(self, notification):
		logging.info("Session will end")
		session = notification.sender
		audio_stream = session.streams[0]
		self.player.stop()
		audio_stream.bridge.remove(self.player)

	def _NH_SIPSessionDidEnd(self, notification):
		logging.info('Session ended')
	
	def _NH_SIPSessionDidProcessTransaction(self, notification):
		logging.info('Transaction processed - method: %s - code: %s -  reason: %s' % (notification.data.method, notification.data.code, notification.data.reason))
示例#22
0
class AnsweringMachine(object):
    implements(IObserver)

    def __init__(self, session, audio_stream):
        self.session = session
        self.stream = audio_stream
        self.start_time = None

        notification_center = NotificationCenter()
        notification_center.add_observer(self, sender=self.stream)

        self.beep = WavePlayer(SIPApplication.voice_audio_mixer, Resources.get('answering_machine_tone.wav'))
        notification_center.add_observer(self, sender=self.beep)

        message_wav = SIPSimpleSettings().answering_machine.unavailable_message
        if message_wav:
            self.unavailable_message = WavePlayer(SIPApplication.voice_audio_mixer, message_wav.sound_file.path, message_wav.sound_file.volume, 1, 2, False)
            notification_center.add_observer(self, sender=self.unavailable_message)
            self.stream.bridge.add(self.unavailable_message)
        else:
            self.unavailable_message = None
            self.stream.bridge.add(self.beep)

        self.stream.device.input_muted = True

    def mute_output(self):
        BlinkLogger().log_info(u"Mute output of Answering Machine")
        self.stream.device.output_muted = True

    def unmute_output(self):
        BlinkLogger().log_info(u"Unmute output of Answering Machine")
        self.stream.device.output_muted = False

    def start(self):
        if self.unavailable_message:
            self.unavailable_message.start()
        else:
            self.beep.start()

    @property
    def duration(self):
        return (datetime.datetime.now() - self.start_time).seconds if self.start_time else None

    def stop(self):
        # Stop the answering machine and allow user to take the call
        notification_center = NotificationCenter()
        notification_center.remove_observer(self, sender=self.stream)

        notification_center.remove_observer(self, sender=self.beep)
        self.beep.stop()
        self.beep = None
        if self.unavailable_message:
            notification_center.remove_observer(self, sender=self.unavailable_message)
            self.unavailable_message.stop()
            self.unavailable_message = None

        self.stream.device.input_muted = False
        if self.stream.recording_active:
            self.stream.stop_recording()

    @allocate_autorelease_pool
    def handle_notification(self, notification):
        handler = getattr(self, '_NH_%s' % notification.name, Null)
        handler(notification)

    def _NH_WavePlayerDidEnd(self, notification):
        if notification.sender is self.unavailable_message:
            # once message is played, beep
            self.stream.bridge.remove(self.unavailable_message)
            self.stream.bridge.add(self.beep)
            self.beep.start()
        elif notification.sender is self.beep:
            # start recording after the beep
            settings = SIPSimpleSettings()
            self.stream.bridge.remove(self.beep)
            direction = self.session.direction
            remote = "%s@%s" % (self.session.remote_identity.uri.user, self.session.remote_identity.uri.host)
            filename = "%s-%s-%s.wav" % (datetime.datetime.now().strftime("%Y%m%d-%H%M%S"), remote, direction)
            path = os.path.join(settings.audio.directory.normalized, self.session.account.id)
            self.stream.start_recording(os.path.join(path, filename))
            self.start_time = datetime.datetime.now()

    def _NH_MediaStreamDidFail(self, notification):
        notification_center = NotificationCenter()
        notification_center.remove_observer(self, sender=self.stream)

        notification_center.remove_observer(self, sender=self.beep)
        self.beep.stop()
        self.beep = None
        if self.unavailable_message:
            notification_center.remove_observer(self, sender=self.unavailable_message)
            self.unavailable_message.stop()
            self.unavailable_message = None

    def _NH_MediaStreamWillEnd(self, notification):
        notification_center = NotificationCenter()
        notification_center.remove_observer(self, sender=self.beep)
        self.beep.stop()
        self.beep = None
        if self.unavailable_message:
            notification_center.remove_observer(self, sender=self.unavailable_message)
            self.unavailable_message.stop()
            self.unavailable_message = None

    def _NH_MediaStreamDidEnd(self, notification):
        notification_center = NotificationCenter()
        notification_center.remove_observer(self, sender=self.stream)

    def _NH_AudioStreamDidStartRecordingAudio(self, notification):
        BlinkLogger().log_info(u"Recording message from %s" % self.session.remote_identity)

    def _NH_AudioStreamDidStopRecordingAudio(self, notification):
        BlinkLogger().log_info(u"Message from %s finished recording (duration: %s seconds)" % (self.session.remote_identity, self.duration))
        self.addAnsweringMachineRecordingToHistory(notification.data.filename, self.duration)
        data = NotificationData(filename=notification.data.filename)
        NotificationCenter().post_notification("AnsweringMachineRecordingDidEnd", sender=self.session, data=data)

    def addAnsweringMachineRecordingToHistory(self, filename, duration):
        message = "<h3>Answering Machine Recording</h3>"
        message += "<p>%s" % filename
        message += "<br>Duration: %s seconds" % duration
        message += "<p><audio src='%s' controls='controls'>" %  urllib.quote(filename)
        media_type = 'voicemail'
        local_uri = format_identity_to_string(self.session.account)
        remote_uri = format_identity_to_string(self.session.remote_identity)
        direction = 'incoming'
        status = 'delivered'
        cpim_from = format_identity_to_string(self.session.remote_identity)
        cpim_to = format_identity_to_string(self.session.remote_identity)
        timestamp = str(ISOTimestamp.now())

        self.add_to_history(media_type, local_uri, remote_uri, direction, cpim_from, cpim_to, timestamp, message, status)

    @run_in_green_thread
    def add_to_history(self, media_type, local_uri, remote_uri, direction, cpim_from, cpim_to, timestamp, message, status):
        try:
            controller = (controller for controller in NSApp.delegate().contactsWindowController.sessionControllersManager.sessionControllers if controller.session == self.session).next()
        except StopIteration:
            history_id = str(uuid.uuid1())
        else:
            history_id = controller.history_id

        ChatHistory().add_message(history_id, media_type, local_uri, remote_uri, direction, cpim_from, cpim_to, timestamp, message, "html", "0", status)
示例#23
0
class SessionItem(object):
    implements(IObserver)

    def __init__(self):
        self.name = None
        self.uri = None
        self.session = None
        self.streams = {}
        self.initialized = False

        self.timer = LoopingCall(self._timer_fired)
        self.outbound_ringtone = Null
        self.offer_in_progress = False
        self.local_hold = False
        self.remote_hold = False

        self._active = False
        self._codec_info = u''
        self._tls = False
        self._srtp = False
        self._duration = timedelta(0)
        self._latency = 0
        self._packet_loss = 0

        self.status = None

        self.notification_center = NotificationCenter()

    def init_incoming(self, session, streams):
        self.name = session.remote_identity.display_name
        self.uri = session.remote_identity.uri
        self.session = session
        for stream in streams:
            self._set_stream(stream.type, stream)
        self.initialized = True

        self.notification_center.add_observer(self, sender=self.session)
        self.notification_center.post_notification('SessionItemNewIncoming', sender=self)

    def init_outgoing(self, name, uri, streams, account):
        self.name = name
        self.uri = uri
        self.session = Session(account)
        for stream in streams:
            self._set_stream(stream.type, stream)
        self.initialized = True

        self.notification_center.add_observer(self, sender=self.session)
        self.notification_center.post_notification('SessionItemNewOutgoing', sender=self)

    def _set_stream(self, stream_type, stream):
        old_stream = self.streams.get(stream_type, None)
        self.streams[stream_type] = stream
        if old_stream is not None:
            self.notification_center.remove_observer(self, sender=old_stream)
            if stream_type == 'audio':
                self.hold_tone = Null
        if stream is not None:
            self.notification_center.add_observer(self, sender=stream)
            if stream_type == 'audio':
                self.hold_tone = WavePlayer(stream.bridge.mixer, Resources.get('sounds/hold_tone.wav'), loop_count=0, pause_time=45, volume=30)
                stream.bridge.add(self.hold_tone)

    @property
    def audio_stream(self):
        return self.streams.get('audio', None)

    @property
    def video_stream(self):
        return self.streams.get('video', None)

    @property
    def codec_info(self):
        return self._codec_info

    @property
    def tls(self):
        return self._tls

    @property
    def srtp(self):
        return self._srtp

    @property
    def duration(self):
        return self._duration

    @property
    def latency(self):
        return self._latency

    @property
    def packet_loss(self):
        return self._packet_loss

    def _get_status(self):
        return self.__dict__.get('status', None)
    def _set_status(self, value):
        old_value = self.__dict__.get('status', None)
        if old_value == value:
            return
        self.__dict__['status'] = value
        self.notification_center.post_notification('SessionItemDidChange', sender=self)
    status = property(_get_status, _set_status)

    @property
    def pending_removal(self):
        return not bool(self.streams.values())

    def _get_active(self):
        return self._active
    def _set_active(self, value):
        value = bool(value)
        if self._active == value:
            return
        self._active = value
        if self.audio_stream:
            self.audio_stream.device.output_muted = not value
        if value:
            self.unhold()
            self.notification_center.post_notification('SessionItemDidActivate', sender=self)
        else:
            self.hold()
            self.notification_center.post_notification('SessionItemDidDeactivate', sender=self)
    active = property(_get_active, _set_active)
    del _get_active, _set_active

    def connect(self):
        self.offer_in_progress = True
        account = self.session.account
        settings = SIPSimpleSettings()
        if isinstance(account, Account):
            if account.sip.outbound_proxy is not None:
                proxy = account.sip.outbound_proxy
                uri = SIPURI(host=proxy.host, port=proxy.port, parameters={'transport': proxy.transport})
            elif account.sip.always_use_my_proxy:
                uri = SIPURI(host=account.id.domain)
            else:
                uri = self.uri
        else:
            uri = self.uri
        self.status = u'Looking up destination'
        lookup = DNSLookup()
        self.notification_center.add_observer(self, sender=lookup)
        lookup.lookup_sip_proxy(uri, settings.sip.transport_list)

    def hold(self):
        if not self.pending_removal and not self.local_hold:
            self.local_hold = True
            self.session.hold()
            self.hold_tone.start()

    def unhold(self):
        if not self.pending_removal and self.local_hold:
            self.local_hold = False
            self.session.unhold()

    def send_dtmf(self, digit):
        if self.audio_stream is not None:
            try:
                self.audio_stream.send_dtmf(digit)
            except RuntimeError:
                pass
            else:
                digit_map = {'*': 'star'}
                filename = 'sounds/dtmf_%s_tone.wav' % digit_map.get(digit, digit)
                player = WavePlayer(SIPApplication.voice_audio_bridge.mixer, Resources.get(filename))
                self.notification_center.add_observer(self, sender=player)
                if self.session.account.rtp.inband_dtmf:
                    self.audio_stream.bridge.add(player)
                SIPApplication.voice_audio_bridge.add(player)
                player.start()

    def end(self):
        if self.session.state is None:
            self.status = u'Call canceled'
            self._cleanup()
        else:
            self.session.end()

    def _cleanup(self):
        if self.timer.running:
            self.timer.stop()

        self.notification_center.remove_observer(self, sender=self.session)
        for k in self.streams.keys():
            self._set_stream(k, None)

        player = WavePlayer(SIPApplication.voice_audio_bridge.mixer, Resources.get('sounds/hangup_tone.wav'), volume=60)
        self.notification_center.add_observer(self, sender=player)
        SIPApplication.voice_audio_bridge.add(player)
        player.start()

        self.notification_center.post_notification('SessionItemDidEnd', sender=self)

    def _reset_status(self):
        if self.pending_removal or self.offer_in_progress:
            return
        if self.local_hold:
            self.status = u'On hold'
        elif self.remote_hold:
            self.status = u'Hold by remote'
        else:
            self.status = None

    def _set_codec_info(self):
        codecs = []
        if self.video_stream is not None:
            desc = 'HD Video' if self.video_stream.bit_rate/1024 >= 512 else 'Video'
            codecs.append('[%s] %s %dkbit' % (desc, self.video_stream.codec, self.video_stream.bit_rate/1024))
        if self.audio_stream is not None:
            desc = 'HD Audio' if self.audio_stream.sample_rate/1000 >= 16 else 'Audio'
            codecs.append('[%s] %s %dkHz' % (desc, self.audio_stream.codec, self.audio_stream.sample_rate/1000))
        self._codec_info = ', '.join(codecs)

    def _timer_fired(self):
        if self.audio_stream is not None:
            stats = self.audio_stream.statistics
            if stats is not None:
                self._latency = stats['rtt']['avg'] / 1000
                self._packet_loss = int(stats['rx']['packets_lost']*100.0/stats['rx']['packets']) if stats['rx']['packets'] else 0
        self._duration += timedelta(seconds=1)
        self.notification_center.post_notification('SessionItemDidChange', sender=self)

    def handle_notification(self, notification):
        handler = getattr(self, '_NH_%s' % notification.name, Null)
        handler(notification)

    def _NH_AudioStreamICENegotiationStateDidChange(self, notification):
        if notification.data.state == 'GATHERING':
            self.status = u'Gathering ICE candidates'
        elif notification.data.state == 'NEGOTIATING':
            self.status = u'Negotiating ICE'
        elif notification.data.state == 'RUNNING':
            self.status = u'Connecting...'
        elif notification.data.state == 'FAILED':
            self.status = u'ICE failed'

    def _NH_AudioStreamGotDTMF(self, notification):
        digit_map = {'*': 'star'}
        filename = 'sounds/dtmf_%s_tone.wav' % digit_map.get(notification.data.digit, notification.data.digit)
        player = WavePlayer(SIPApplication.voice_audio_bridge.mixer, Resources.get(filename))
        notification.center.add_observer(self, sender=player)
        SIPApplication.voice_audio_bridge.add(player)
        player.start()

    def _NH_DNSLookupDidSucceed(self, notification):
        settings = SIPSimpleSettings()
        notification.center.remove_observer(self, sender=notification.sender)
        if self.pending_removal:
            return
        if self.audio_stream:
            outbound_ringtone = settings.sounds.outbound_ringtone
            if outbound_ringtone:
                self.outbound_ringtone = WavePlayer(self.audio_stream.mixer, outbound_ringtone.path, outbound_ringtone.volume, loop_count=0, pause_time=5)
                self.audio_stream.bridge.add(self.outbound_ringtone)
        routes = notification.data.result
        self._tls = routes[0].transport=='tls' if routes else False
        self.status = u'Connecting...'
        self.session.connect(ToHeader(self.uri), routes, self.streams.values())

    def _NH_DNSLookupDidFail(self, notification):
        notification.center.remove_observer(self, sender=notification.sender)
        if self.pending_removal:
            return
        self.status = u'Destination not found'
        self._cleanup()

    def _NH_SIPSessionGotRingIndication(self, notification):
        self.status = u'Ringing...'
        self.outbound_ringtone.start()

    def _NH_SIPSessionWillStart(self, notification):
        self.outbound_ringtone.stop()

    def _NH_SIPSessionDidStart(self, notification):
        if self.audio_stream not in notification.data.streams:
            self._set_stream('audio', None)
        if self.video_stream not in notification.data.streams:
            self._set_stream('video', None)
        # TODO: rework the above code
        if not self.local_hold:
            self.offer_in_progress = False
        if not self.pending_removal:
            self.timer.start(1)
            self._set_codec_info()
            self.status = u'Connected'
            self._srtp = all(stream.srtp_active for stream in (self.audio_stream, self.video_stream) if stream is not None)
            self._tls = self.session.transport == 'tls'
            reactor.callLater(1, self._reset_status)
        else:
            self.status = u'Ending...'
            self._cleanup()

    def _NH_SIPSessionDidFail(self, notification):
        self.offer_in_progress = False
        if notification.data.failure_reason == 'user request':
            if notification.data.code == 487:
                reason = u'Call canceled'
            else:
                reason = unicode(notification.data.reason)
        else:
            reason = notification.data.failure_reason
        self.status = reason
        self.outbound_ringtone.stop()
        self._cleanup()

    def _NH_SIPSessionDidEnd(self, notification):
        self.offer_in_progress = False
        self.status = u'Call ended' if notification.data.originator=='local' else u'Call ended by remote'
        self._cleanup()

    def _NH_SIPSessionDidChangeHoldState(self, notification):
        if notification.data.originator == 'remote':
            self.remote_hold = notification.data.on_hold
        if self.local_hold:
            if not self.offer_in_progress:
                self.status = u'On hold'
        elif self.remote_hold:
            if not self.offer_in_progress:
                self.status = u'Hold by remote'
            self.hold_tone.start()
        else:
            self.status = None
            self.hold_tone.stop()
        self.offer_in_progress = False

    def _NH_SIPSessionGotAcceptProposal(self, notification):
        if self.audio_stream not in notification.data.proposed_streams and self.video_stream not in notification.data.proposed_streams:
            return
        if self.audio_stream in notification.data.proposed_streams and self.audio_stream not in notification.data.streams:
            self._set_stream('audio', None)
        if self.video_stream in notification.data.proposed_streams and self.video_stream not in notification.data.streams:
            self._set_stream('video', None)
        # TODO: rework the above
        self.offer_in_progress = False
        if not self.pending_removal:
            self._set_codec_info()
            self.status = u'Connected'
            reactor.callLater(1, self._reset_status)
        else:
            self.status = u'Ending...'
            self._cleanup()

    def _NH_SIPSessionGotRejectProposal(self, notification):
        if self.audio_stream not in notification.data.streams and self.video_stream not in notification.data.streams:
            return
        if self.audio_stream in notification.data.streams:
            self._set_stream('audio', None)
        if self.video_stream in notification.data.streams:
            self._set_stream('video', None)
        # TODO: rework the above
        self.offer_in_progress = False
        self.status = u'Stream refused'
        if not self.pending_removal:
            self._set_codec_info()
            reactor.callLater(1, self._reset_status)
        else:
            self._cleanup()

    def _NH_SIPSessionDidRenegotiateStreams(self, notification):
        if notification.data.action != 'remove':
            # TODO: what?
            return
        if self.audio_stream not in notification.data.streams and self.video_stream not in notification.data.streams:
            return
        if self.audio_stream in notification.data.streams:
            self._set_stream('audio', None)
        if self.video_stream in notification.data.streams:
            self._set_stream('video', None)
        # TODO: rework the above
        self.offer_in_progress = False
        self.status = u'Stream removed'
        if not self.pending_removal:
            self._set_codec_info()
            reactor.callLater(1, self._reset_status)
        else:
            self._cleanup()

    def _NH_WavePlayerDidFail(self, notification):
        notification.center.remove_observer(self, sender=notification.sender)

    def _NH_WavePlayerDidEnd(self, notification):
        notification.center.remove_observer(self, sender=notification.sender)
示例#24
0
 def beeping_ringtone(self):
     if 'beeping_ringtone' not in self.__dict__:
         ringtone = WavePlayer(SIPApplication.voice_audio_mixer, Resources.get('sounds/beeping_ringtone.wav'), volume=70, loop_count=0, pause_time=10)
         ringtone.bridge = SIPApplication.voice_audio_bridge
         self.__dict__['beeping_ringtone'] = ringtone
     return self.__dict__['beeping_ringtone']
示例#25
0
    def update_ringtones(self, account=None):
        settings = SIPSimpleSettings()

        if account is None:
            account = AccountManager().default_account

        app = SIPApplication()

        def change_tone(name, new_tone):
            current = getattr(self, name)
            if current and current.is_active:
                current.stop()
                if new_tone:
                    new_tone.start()
            setattr(self, name, new_tone)

        change_tone(
            "initial_hold_tone",
            WavePlayer(app.voice_audio_mixer,
                       Resources.get('hold_tone.wav'),
                       volume=10))
        app.voice_audio_bridge.add(self.initial_hold_tone)
        change_tone(
            "secondary_hold_tone",
            WavePlayer(app.voice_audio_mixer,
                       Resources.get('hold_tone.wav'),
                       loop_count=0,
                       pause_time=45,
                       volume=10,
                       initial_delay=45))
        app.voice_audio_bridge.add(self.secondary_hold_tone)

        if account:
            audio_primary_ringtone = account.sounds.audio_inbound.sound_file if account.sounds.audio_inbound is not None else None
        else:
            audio_primary_ringtone = None

        if audio_primary_ringtone and not settings.audio.silent:
            # Workaround not to use same device from two bridges. -Saul
            if settings.audio.alert_device is not None and app.alert_audio_mixer.real_output_device == app.voice_audio_mixer.real_output_device:
                new_tone = WavePlayer(app.voice_audio_mixer,
                                      audio_primary_ringtone.path,
                                      loop_count=0,
                                      pause_time=6)
                app.voice_audio_bridge.add(new_tone)
            else:
                new_tone = WavePlayer(app.alert_audio_mixer,
                                      audio_primary_ringtone.path,
                                      loop_count=0,
                                      pause_time=6)
                app.alert_audio_bridge.add(new_tone)
        else:
            new_tone = None
        change_tone("audio_primary_ringtone", new_tone)

        if audio_primary_ringtone and not settings.audio.silent:
            new_tone = WavePlayer(app.voice_audio_mixer,
                                  Resources.get('ring_tone.wav'),
                                  loop_count=0,
                                  pause_time=6)
            app.voice_audio_bridge.add(new_tone)
        else:
            new_tone = None
        change_tone("audio_secondary_ringtone", new_tone)

        if audio_primary_ringtone and not settings.audio.silent:
            # Workaround not to use same device from two bridges. -Saul
            if settings.audio.alert_device is not None and app.alert_audio_mixer.real_output_device == app.voice_audio_mixer.real_output_device:
                new_tone = WavePlayer(app.voice_audio_mixer,
                                      Resources.get('ring_tone.wav'),
                                      loop_count=0,
                                      pause_time=6)
                app.voice_audio_bridge.add(new_tone)
            else:
                new_tone = WavePlayer(app.alert_audio_mixer,
                                      Resources.get('ring_tone.wav'),
                                      loop_count=0,
                                      pause_time=6)
                app.alert_audio_bridge.add(new_tone)
        else:
            new_tone = None
        change_tone("chat_primary_ringtone", new_tone)

        if audio_primary_ringtone and not settings.audio.silent:
            new_tone = WavePlayer(app.voice_audio_mixer,
                                  Resources.get('ring_tone.wav'),
                                  loop_count=0,
                                  pause_time=6)
            app.voice_audio_bridge.add(new_tone)
        else:
            new_tone = None
        change_tone("chat_secondary_ringtone", new_tone)

        chat_message_outgoing_sound = settings.sounds.message_sent
        if chat_message_outgoing_sound and not settings.audio.silent:
            new_tone = WavePlayer(app.voice_audio_mixer,
                                  chat_message_outgoing_sound.path,
                                  volume=chat_message_outgoing_sound.volume)
            app.voice_audio_bridge.add(new_tone)
        else:
            new_tone = None
        change_tone("chat_message_outgoing_sound", new_tone)

        chat_message_incoming_sound = settings.sounds.message_received
        if chat_message_incoming_sound and not settings.audio.silent:
            new_tone = WavePlayer(app.voice_audio_mixer,
                                  chat_message_incoming_sound.path,
                                  volume=chat_message_incoming_sound.volume)
            app.voice_audio_bridge.add(new_tone)
        else:
            new_tone = None
        change_tone("chat_message_incoming_sound", new_tone)

        file_transfer_outgoing_sound = settings.sounds.file_sent
        if file_transfer_outgoing_sound and not settings.audio.silent:
            new_tone = WavePlayer(app.voice_audio_mixer,
                                  file_transfer_outgoing_sound.path,
                                  volume=file_transfer_outgoing_sound.volume)
            app.voice_audio_bridge.add(new_tone)
        else:
            new_tone = None
        change_tone("file_transfer_outgoing_sound", new_tone)

        file_transfer_incoming_sound = settings.sounds.file_received
        if file_transfer_incoming_sound and not settings.audio.silent:
            new_tone = WavePlayer(app.voice_audio_mixer,
                                  file_transfer_incoming_sound.path,
                                  volume=file_transfer_incoming_sound.volume)
            app.voice_audio_bridge.add(new_tone)
        else:
            new_tone = None
        change_tone("file_transfer_incoming_sound", new_tone)
示例#26
0
class AnsweringMachine(object):
    implements(IObserver)

    def __init__(self, session, audio_stream):
        self.session = session
        self.stream = audio_stream
        self.start_time = None

        notification_center = NotificationCenter()
        notification_center.add_observer(self, sender=self.stream)

        self.beep = WavePlayer(SIPApplication.voice_audio_mixer,
                               Resources.get('answering_machine_tone.wav'))
        notification_center.add_observer(self, sender=self.beep)

        message_wav = SIPSimpleSettings().answering_machine.unavailable_message
        if message_wav:
            self.unavailable_message = WavePlayer(
                SIPApplication.voice_audio_mixer, message_wav.sound_file.path,
                message_wav.sound_file.volume, 1, 2, False)
            notification_center.add_observer(self,
                                             sender=self.unavailable_message)
            self.stream.bridge.add(self.unavailable_message)
        else:
            self.unavailable_message = None
            self.stream.bridge.add(self.beep)

        self.stream.device.input_muted = True

    def start(self):
        if self.unavailable_message:
            self.unavailable_message.start()
        else:
            self.beep.start()

    @property
    def duration(self):
        return (datetime.datetime.now() -
                self.start_time).seconds if self.start_time else None

    def stop(self):
        # Stop the answering machine and allow user to take the call
        notification_center = NotificationCenter()
        notification_center.remove_observer(self, sender=self.stream)

        notification_center.remove_observer(self, sender=self.beep)
        self.beep.stop()
        self.beep = None
        if self.unavailable_message:
            notification_center.remove_observer(
                self, sender=self.unavailable_message)
            self.unavailable_message.stop()
            self.unavailable_message = None

        self.stream.device.input_muted = False
        if self.stream.recording_active:
            self.stream.stop_recording()

    @allocate_autorelease_pool
    def handle_notification(self, notification):
        handler = getattr(self, '_NH_%s' % notification.name, Null)
        handler(notification)

    def _NH_WavePlayerDidEnd(self, notification):
        if notification.sender is self.unavailable_message:
            # once message is played, beep
            self.stream.bridge.remove(self.unavailable_message)
            self.stream.bridge.add(self.beep)
            self.beep.start()
        elif notification.sender is self.beep:
            # start recording after the beep
            settings = SIPSimpleSettings()
            self.stream.bridge.remove(self.beep)
            direction = self.session.direction
            remote = "%s@%s" % (self.session.remote_identity.uri.user,
                                self.session.remote_identity.uri.host)
            filename = "%s-%s-%s.wav" % (datetime.datetime.now().strftime(
                "%Y%m%d-%H%M%S"), remote, direction)
            path = os.path.join(settings.audio.directory.normalized,
                                self.session.account.id)
            self.stream.start_recording(os.path.join(path, filename))
            self.start_time = datetime.datetime.now()

    def _NH_MediaStreamDidFail(self, notification):
        notification_center = NotificationCenter()
        notification_center.remove_observer(self, sender=self.stream)

        notification_center.remove_observer(self, sender=self.beep)
        self.beep.stop()
        self.beep = None
        if self.unavailable_message:
            notification_center.remove_observer(
                self, sender=self.unavailable_message)
            self.unavailable_message.stop()
            self.unavailable_message = None

    def _NH_MediaStreamWillEnd(self, notification):
        notification_center = NotificationCenter()
        notification_center.remove_observer(self, sender=self.beep)
        self.beep.stop()
        self.beep = None
        if self.unavailable_message:
            notification_center.remove_observer(
                self, sender=self.unavailable_message)
            self.unavailable_message.stop()
            self.unavailable_message = None

    def _NH_MediaStreamDidEnd(self, notification):
        notification_center = NotificationCenter()
        notification_center.remove_observer(self, sender=self.stream)

    def _NH_AudioStreamDidStartRecordingAudio(self, notification):
        BlinkLogger().log_info(u"Recording message from %s" %
                               self.session.remote_identity)

    def _NH_AudioStreamDidStopRecordingAudio(self, notification):
        BlinkLogger().log_info(
            u"Message from %s finished recording (duration: %s seconds)" %
            (self.session.remote_identity, self.duration))
        self.addAnsweringMachineRecordingToHistory(notification.data.filename,
                                                   self.duration)

    def addAnsweringMachineRecordingToHistory(self, filename, duration):
        message = "<h3>Answering Machine Recording</h3>"
        message += "<p>%s" % filename
        message += "<br>Duration: %s seconds" % duration
        message += "<p><audio src='%s' controls='controls'>" % urllib.quote(
            filename)
        media_type = 'voicemail'
        local_uri = format_identity_to_string(self.session.account)
        remote_uri = format_identity_to_string(self.session.remote_identity)
        direction = 'incoming'
        status = 'delivered'
        cpim_from = format_identity_to_string(self.session.remote_identity)
        cpim_to = format_identity_to_string(self.session.remote_identity)
        timestamp = str(Timestamp(datetime.datetime.now(tzlocal())))

        self.add_to_history(media_type, local_uri, remote_uri, direction,
                            cpim_from, cpim_to, timestamp, message, status)

    @run_in_green_thread
    def add_to_history(self, media_type, local_uri, remote_uri, direction,
                       cpim_from, cpim_to, timestamp, message, status):
        ChatHistory().add_message(str(uuid.uuid1()), media_type, local_uri,
                                  remote_uri, direction, cpim_from, cpim_to,
                                  timestamp, message, "html", "0", status)