Changeset 8498 for branches/jingle/src

Show
Ignore:
Timestamp:
08/15/07 11:47:29 (17 months ago)
Author:
liori
Message:

Jingle: more farsight in jingle.py

Files:
1 modified

Legend:

Unmodified
Added
Removed
  • branches/jingle/src/common/jingle.py

    r8493 r8498  
    6262                # to send error instead of ack 
    6363                self.callbacks={ 
    64                         'content-accept':       [self.__contentAcceptCB, self.__defaultCB], 
     64                        'content-accept':       [self.__contentAcceptCB, self.__broadcastCB, self.__defaultCB], 
    6565                        'content-add':          [self.__defaultCB], 
    6666                        'content-modify':       [self.__defaultCB], 
    6767                        'content-remove':       [self.__defaultCB], 
    68                         'session-accept':       [self.__contentAcceptCB, self.__defaultCB], 
     68                        'session-accept':       [self.__contentAcceptCB, self.__broadcastCB, self.__defaultCB], 
    6969                        'session-info':         [self.__defaultCB], 
    70                         'session-initiate':     [self.__sessionInitiateCB, self.__defaultCB], 
     70                        'session-initiate':     [self.__sessionInitiateCB, self.__broadcastCB, self.__defaultCB], 
    7171                        'session-terminate':    [self.__defaultCB], 
    72                         'transport-info':       [self.__defaultCB], 
     72                        'transport-info':       [self.__broadcastCB, self.__defaultCB], 
    7373                        'iq-result':            [], 
    7474                        'iq-error':             [], 
     
    7676 
    7777                # for making streams using farsight 
     78                import gc 
     79                gc.disable() 
     80                print self.weinitiate, "#farsight_session_factory_make" 
    7881                self.p2psession = farsight.farsight_session_factory_make('rtp') 
    7982                self.p2psession.connect('error', self.on_p2psession_error) 
     
    124127                if error: 
    125128                        # it's an iq-error stanza 
    126                         callables = 'iq-error' 
     129                        action = 'iq-error' 
    127130                elif jingle: 
    128131                        # it's a jingle action 
    129132                        action = jingle.getAttr('action') 
    130                         callables = action 
    131133                else: 
    132134                        # it's an iq-result (ack) stanza 
    133                         callables = 'iq-result' 
    134  
    135                 callables = self.callbacks[callables] 
     135                        action = 'iq-result' 
     136 
     137                callables = self.callbacks[action] 
    136138 
    137139                try: 
    138140                        for callable in callables: 
    139                                 callable(stanza=stanza, jingle=jingle, error=error) 
     141                                callable(stanza=stanza, jingle=jingle, error=error, action=action) 
    140142                except xmpp.NodeProcessed: 
    141143                        pass 
    142144 
    143         def __defaultCB(self, stanza, jingle, error): 
     145        def __defaultCB(self, stanza, jingle, error, action): 
    144146                ''' Default callback for action stanzas -- simple ack 
    145147                and stop processing. ''' 
     
    147149                self.connection.connection.send(response) 
    148150 
    149         def __contentAcceptCB(self, stanza, jingle, error): 
     151        def __contentAcceptCB(self, stanza, jingle, error, action): 
    150152                ''' Called when we get content-accept stanza or equivalent one 
    151153                (like session-accept).''' 
    152                 # check which contents are accepted, call their callbacks 
     154                # check which contents are accepted 
    153155                for content in jingle.iterTags('content'): 
    154156                        creator = content['creator'] 
    155157                        name = content['name'] 
    156                          
    157  
    158         def __sessionInitiateCB(self, stanza, jingle, error): 
     158 
     159        def __sessionInitiateCB(self, stanza, jingle, error, action): 
    159160                ''' We got a jingle session request from other entity, 
    160161                therefore we are the receiver... Unpack the data. ''' 
     
    170171                        if desc_ns==xmpp.NS_JINGLE_AUDIO and tran_ns==xmpp.NS_JINGLE_ICE_UDP: 
    171172                                # we've got voip content 
    172                                 self.addContent(element['name'], JingleVoiP(self, node=element), 'peer') 
     173                                self.addContent(element['name'], JingleVoiP(self), 'peer') 
    173174                                fail = False 
    174175 
     
    182183                self.state = JingleStates.pending 
    183184 
     185        def __broadcastCB(self, stanza, jingle, error, action): 
     186                ''' Broadcast the stanza contents to proper content handlers. ''' 
     187                for content in jingle.iterTags('content'): 
     188                        name = content['name'] 
     189                        creator = content['creator'] 
     190                        cn = self.contents[(creator, name)] 
     191                        cn.stanzaCB(stanza, content, error, action) 
     192 
    184193        def on_p2psession_error(self, *anything): 
    185                 print "Farsight session error!" 
     194                print self.weinitiate, "Farsight session error!" 
    186195 
    187196        ''' Methods that make/send proper pieces of XML. They check if the session 
     
    281290 
    282291        def toXML(self): 
    283                 return xmpp.Node('payload', 
     292                return xmpp.Node('payload-type', 
    284293                        attrs=self.attrs, 
    285294                        payload=(xmpp.Node('parameter', {'name': k, 'value': v}) for k,v in self.params)) 
    286295 
    287296class JingleAudioSession(object): 
    288         __metaclass__=meta.VerboseClassType 
     297#       __metaclass__=meta.VerboseClassType 
    289298        def __init__(self, content, fromNode): 
    290299                self.content = content 
     
    354363                        return self.__codecsList(self.responder_codecs) 
    355364 
    356 class JingleICEUDPSession(object): 
    357         __metaclass__=meta.VerboseClassType 
    358         def __init__(self, content): 
    359                 self.content = content 
    360  
    361         def _sessionInitiateCB(self): 
    362                 ''' Called when we initiate the session. ''' 
    363                 pass 
    364  
    365         def toXML(self): 
    366                 ''' ICE-UDP doesn't send much in its transport stanza... ''' 
    367                 return xmpp.Node(xmpp.NS_JINGLE_ICE_UDP+' transport') 
    368  
    369365class JingleContent(object): 
    370366        ''' An abstraction of content in Jingle sessions. ''' 
     
    380376        ''' Jingle VoiP sessions consist of audio content transported 
    381377        over an ICE UDP protocol. ''' 
    382         __metaclass__=meta.VerboseClassType 
     378#       __metaclass__=meta.VerboseClassType 
    383379        def __init__(self, session, node=None): 
    384380                JingleContent.__init__(self, session, node) 
    385                 self.codecs = None 
     381                self.got_codecs = False 
    386382 
    387383                #if node is None: 
     
    392388                self.setupStream() 
    393389 
     390        def stanzaCB(self, stanza, content, error, action): 
     391                ''' Called when something related to our content was sent by peer. ''' 
     392                callbacks = { 
     393                        'content-accept': [self.__getRemoteCodecsCB], 
     394                        'content-add': [], 
     395                        'content-modify': [], 
     396                        'content-remove': [], 
     397                        'session-accept': [self.__getRemoteCodecsCB], 
     398                        'session-info': [], 
     399                        'session-initiate': [self.__getRemoteCodecsCB], 
     400                        'session-terminate': [], 
     401                        'transport-info': [self.__transportInfoCB], 
     402                        'iq-result': [], 
     403                        'iq-error': [], 
     404                }[action] 
     405                for callback in callbacks: 
     406                        callback(stanza, content, error, action) 
     407 
     408        def __getRemoteCodecsCB(self, stanza, content, error, action): 
     409                if self.got_codecs: return 
     410 
     411                codecs = [] 
     412                for codec in content.getTag('description').iterTags('payload-type'): 
     413                        c = {'id': int(codec['id']), 
     414                             'encoding_name': codec['name'], 
     415                             'media_type': farsight.MEDIA_TYPE_AUDIO, 
     416                             'channels': 1, 
     417                             'params': dict((p['name'], p['value']) for p in codec.iterTags('parameter'))} 
     418                        if 'channels' in codec: c['channels']=codec['channels'] 
     419                        codecs.append(c) 
     420                if len(codecs)==0: return 
     421 
     422                print self.session.weinitiate, "#farsight_stream_set_remote_codecs" 
     423                self.p2pstream.set_remote_codecs(codecs) 
     424                self.got_codecs=True 
     425 
     426        def __transportInfoCB(self, stanza, content, error, action): 
     427                ''' Got a new transport candidate. ''' 
     428                candidates = [] 
     429                for candidate in content.getTag('transport').iterTags('candidate'): 
     430                        cand={ 
     431                        #       'candidate_id': str(self.session.connection.connection.getAnID()), 
     432                                'candidate_id': candidate['cid'], 
     433                                'component':    int(candidate['component']), 
     434                                'ip':           candidate['ip'], 
     435                                'port':         int(candidate['port']), 
     436                                'proto':        candidate['protocol']=='udp' and farsight.NETWORK_PROTOCOL_UDP \ 
     437                                                or farsight.NETWORK_PROTOCOL_TCP, 
     438                                'proto_subtype':'RTP', 
     439                                'proto_profile':'AVP', 
     440                                'preference':   float(candidate['priority'])/100000, 
     441                        #       'type':         farsight.CANDIDATE_TYPE_LOCAL, 
     442                                'type':         int(candidate['type']), 
     443                        } 
     444                        if 'ufrag' in candidate: cand['username']=candidate['ufrag'] 
     445                        if 'pwd' in candidate: cand['password']=candidate['pwd'] 
     446 
     447                        candidates.append(cand) 
     448                print self.session.weinitiate, "#add_remote_candidate" 
     449                self.p2pstream.add_remote_candidate(candidates) 
     450 
    394451        def toXML(self): 
    395452                ''' Return proper XML for <content/> element. ''' 
     
    397454                        attrs={'name': self.name, 'creator': self.creator, 'profile': 'RTP/AVP'}, 
    398455                        payload=[ 
    399                                 xmpp.Node(xmpp.NS_JINGLE_AUDIO+' description', payload=self.getCodecs()), 
     456                                xmpp.Node(xmpp.NS_JINGLE_AUDIO+' description', payload=self.iterCodecs()), 
    400457                                xmpp.Node(xmpp.NS_JINGLE_ICE_UDP+' transport') 
    401458                        ]) 
     
    408465 
    409466        def setupStream(self): 
     467                print self.session.weinitiate, "#farsight_session_create_stream" 
    410468                self.p2pstream = self.session.p2psession.create_stream( 
    411469                        farsight.MEDIA_TYPE_AUDIO, farsight.STREAM_DIRECTION_BOTH) 
     
    417475                self.p2pstream.connect('state-changed', self.on_p2pstream_state_changed) 
    418476                self.p2pstream.connect('new-native-candidate', self.on_p2pstream_new_native_candidate) 
     477 
     478                self.p2pstream.set_remote_codecs(self.p2pstream.get_local_codecs()) 
     479 
     480                print self.session.weinitiate, "#farsight_stream_prepare_transports" 
    419481                self.p2pstream.prepare_transports() 
    420482 
     483                print self.session.weinitiate, "#farsight_stream_set_active_codec" 
     484                self.p2pstream.set_active_codec(8)      #??? 
     485 
     486                sink = gst.element_factory_make('alsasink') 
     487                sink.set_property('sync', False) 
     488                sink.set_property('latency-time', 20000) 
     489                sink.set_property('buffer-time', 80000) 
     490 
     491                src = gst.element_factory_make('audiotestsrc') 
     492                src.set_property('blocksize', 320) 
     493                #src.set_property('latency-time', 20000) 
     494                src.set_property('is-live', True) 
     495 
     496                print self.session.weinitiate, "#farsight_stream_set_sink" 
     497                self.p2pstream.set_sink(sink) 
     498                print self.session.weinitiate, "#farsight_stream_set_source" 
     499                self.p2pstream.set_source(src) 
     500 
    421501        def on_p2pstream_error(self, *whatever): pass 
    422         def on_p2pstream_new_active_candidate_pair(self, *whatever): pass 
    423         def on_p2pstream_codec_changed(self, *whatever): pass 
    424         def on_p2pstream_native_candidates_prepared(self, *whatever): pass 
    425         def on_p2pstream_state_changed(self, *whatever): pass 
     502        def on_p2pstream_new_active_candidate_pair(self, stream, native, remote): 
     503                print self.session.weinitiate, "##new_active_candidate_pair" 
     504                #print "New native candidate pair: %s, %s" % (native, remote) 
     505        def on_p2pstream_codec_changed(self, stream, codecid): 
     506                print self.session.weinitiate, "##codec_changed" 
     507                #print "Codec changed: %d" % codecid 
     508        def on_p2pstream_native_candidates_prepared(self, *whatever): 
     509                print self.session.weinitiate, "##native_candidates_prepared" 
     510                #print "Native candidates prepared: %r" % whatever 
     511                for candidate in self.p2pstream.get_native_candidate_list(): 
     512                        self.send_candidate(candidate) 
     513        def on_p2pstream_state_changed(self, stream, state, dir): 
     514                print self.session.weinitiate, "##state_changed" 
     515                #print "State: %d, Dir: %d" % (state, dir) 
     516                if state==farsight.STREAM_STATE_CONNECTED: 
     517                        print self.session.weinitiate, "#farsight_stream_signal_native_candidates_prepared" 
     518                        stream.signal_native_candidates_prepared() 
     519                        print self.session.weinitiate, "#farsight_stream_start" 
     520                        stream.start() 
    426521        def on_p2pstream_new_native_candidate(self, p2pstream, candidate_id): 
     522                print self.session.weinitiate, "##new_native_candidate" 
     523                print self.session.weinitiate, "#get_native_candidate" 
    427524                candidates = p2pstream.get_native_candidate(candidate_id) 
     525                print self.session.weinitiate, "#!", repr(candidates) 
    428526 
    429527                for candidate in candidates: 
    430                         attrs={ 
    431                                 'component': candidate['component'], 
    432                                 'foundation': '1', # hack 
    433                                 'generation': '0', 
    434                                 'ip': candidate['ip'], 
    435                                 'network': '0', 
    436                                 'port': candidate['port'], 
    437                                 'priority': int(100000*candidate['preference']), # hack 
    438                                 'protocol': candidate['proto']==farsight.NETWORK_PROTOCOL_UDP and 'udp' or 'tcp', 
    439                         } 
    440                         if 'username' in candidate: attrs['ufrag']=candidate['username'] 
    441                         if 'password' in candidate: attrs['pwd']=candidate['password'] 
    442                         c=self.__content() 
    443                         t=c.addChild(xmpp.NS_JINGLE_ICE_UDP+' transport') 
    444                         t.addChild('candidate', attrs=attrs) 
    445                         self.session.sendTransportInfo(c) 
    446  
    447         def getCodecs(self): 
     528                        self.send_candidate(candidate) 
     529        def send_candidate(self, candidate): 
     530                attrs={ 
     531                        'cid': candidate['candidate_id'], 
     532                        'component': candidate['component'], 
     533                        'foundation': '1', # hack 
     534                        'generation': '0', 
     535                        'type': candidate['type'], 
     536                        'ip': candidate['ip'], 
     537                        'network': '0', 
     538                        'port': candidate['port'], 
     539                        'priority': int(100000*candidate['preference']), # hack 
     540                        'protocol': candidate['proto']==farsight.NETWORK_PROTOCOL_UDP and 'udp' or 'tcp', 
     541                } 
     542                if 'username' in candidate: attrs['ufrag']=candidate['username'] 
     543                if 'password' in candidate: attrs['pwd']=candidate['password'] 
     544                c=self.__content() 
     545                t=c.addChild(xmpp.NS_JINGLE_ICE_UDP+' transport') 
     546                t.addChild('candidate', attrs=attrs) 
     547                self.session.sendTransportInfo(c) 
     548 
     549        def iterCodecs(self): 
     550                print self.session.weinitiate, "#farsight_stream_get_local_codecs" 
    448551                codecs=self.p2pstream.get_local_codecs() 
    449                 return (xmpp.Node('payload', attrs=a) for a in codecs) 
     552                for codec in codecs: 
     553                        a = {'name': codec['encoding_name'], 
     554                             'id': codec['id'], 
     555                             'channels': 1} 
     556                        if 'clock_rate' in codec: a['clockrate']=codec['clock_rate'] 
     557                        if 'optional_params' in codec: 
     558                                p = (xmpp.Node('parameter', {'name': name, 'value': value}) 
     559                                     for name, value in codec['optional_params'].iteritems()) 
     560                        else:   p = () 
     561                        yield xmpp.Node('payload-type', a, p) 
    450562 
    451563class ConnectionJingle(object):