send messages to the gui, wait for codecs, and other things

JingleSession now sends messages about errors or session terminating to the GUI.
Another thing is that it'll wait for all transports candidates and all codecs to be ready before
starting or accepting a session. This is required by video, which is only missing a GUI. :)
This commit is contained in:
Thibaut GIRKA 2009-09-17 23:36:26 +02:00
parent f5b1c2dca7
commit bd9d793ad8
2 changed files with 88 additions and 23 deletions

View File

@ -1478,7 +1478,6 @@ class ChatControl(ChatControlBase):
if self.audio_state in (self.AUDIO_STATE_NOT_AVAILABLE, if self.audio_state in (self.AUDIO_STATE_NOT_AVAILABLE,
self.AUDIO_STATE_AVAILABLE): self.AUDIO_STATE_AVAILABLE):
self._audio_image.hide() self._audio_image.hide()
return
elif self.audio_state == self.AUDIO_STATE_CONNECTING: elif self.audio_state == self.AUDIO_STATE_CONNECTING:
self._audio_image.set_from_stock(gtk.STOCK_CONVERT, 1) self._audio_image.set_from_stock(gtk.STOCK_CONVERT, 1)
elif self.audio_state == self.AUDIO_STATE_CONNECTION_RECEIVED: elif self.audio_state == self.AUDIO_STATE_CONNECTION_RECEIVED:

View File

@ -105,7 +105,6 @@ class JingleSession(object):
self.sid = sid # sessionid self.sid = sid # sessionid
self.accepted = True # is this session accepted by user self.accepted = True # is this session accepted by user
self.candidates_ready = False # True when local candidates are prepared
# callbacks to call on proper contents # callbacks to call on proper contents
# use .prepend() to add new callbacks, especially when you're going # use .prepend() to add new callbacks, especially when you're going
@ -130,7 +129,7 @@ class JingleSession(object):
'transport-accept': [self.__defaultCB], #TODO 'transport-accept': [self.__defaultCB], #TODO
'transport-reject': [self.__defaultCB], #TODO 'transport-reject': [self.__defaultCB], #TODO
'iq-result': [], 'iq-result': [],
'iq-error': [], 'iq-error': [self.__errorCB],
} }
''' Interaction with user ''' ''' Interaction with user '''
@ -152,7 +151,7 @@ class JingleSession(object):
if self.state == JingleStates.active: if self.state == JingleStates.active:
reason.addChild('success') reason.addChild('success')
else: else:
reason.addChild('abort') reason.addChild('cancel')
self.__sessionTerminate(reason) self.__sessionTerminate(reason)
''' Middle-level functions to manage contents. Handle local content ''' Middle-level functions to manage contents. Handle local content
@ -190,13 +189,17 @@ class JingleSession(object):
def acceptSession(self): def acceptSession(self):
''' Check if all contents and user agreed to start session. ''' ''' Check if all contents and user agreed to start session. '''
if not self.weinitiate and self.accepted and self.candidates_ready: if not self.weinitiate and self.accepted and \
all((i.candidates_ready for i in self.contents.itervalues())) and \
all((i.p2psession.get_property('codecs-ready') for i in self.contents.itervalues())):
self.__sessionAccept() self.__sessionAccept()
''' Middle-level function to do stanza exchange. ''' ''' Middle-level function to do stanza exchange. '''
def startSession(self): def startSession(self):
''' Start session. ''' ''' Start session. '''
if self.weinitiate and self.candidates_ready: if self.weinitiate and \
all((i.candidates_ready for i in self.contents.itervalues())) and \
all((i.p2psession.get_property('codecs-ready') for i in self.contents.itervalues())):
self.__sessionInitiate() self.__sessionInitiate()
def sendSessionInfo(self): pass def sendSessionInfo(self): pass
@ -227,15 +230,11 @@ class JingleSession(object):
# it's a jingle action # it's a jingle action
action = jingle.getAttr('action') action = jingle.getAttr('action')
if action not in self.callbacks: if action not in self.callbacks:
err = xmpp.Error(stanza, xmpp.NS_STANZAS + ' bad_request') self.__send_error('bad_request')
self.connection.connection.send(err)
return return
#FIXME: If we aren't initiated and it's not a session-initiate... #FIXME: If we aren't initiated and it's not a session-initiate...
if action != 'session-initiate' and self.state == JingleStates.ended: if action != 'session-initiate' and self.state == JingleStates.ended:
err = xmpp.Error(stanza, xmpp.NS_STANZAS + ' item-not-found') self.__send_error('item-not-found', 'unknown-session')
err.setTag('unknown-session', namespace=xmpp.NS_JINGLE_ERRORS)
self.connection.connection.send(err)
self.connection.deleteJingle(self)
return return
else: else:
# it's an iq-result (ack) stanza # it's an iq-result (ack) stanza
@ -249,9 +248,7 @@ class JingleSession(object):
except xmpp.NodeProcessed: except xmpp.NodeProcessed:
pass pass
except OutOfOrder: except OutOfOrder:
err = xmpp.Error(stanza, xmpp.NS_STANZAS + ' unexpected-request') self.__send_error('unexpected-request', 'out-of-order')#FIXME
err.setTag('out-of-order', namespace=xmpp.NS_JINGLE_ERRORS)
self.connection.connection.send(err)
def __defaultCB(self, stanza, jingle, error, action): def __defaultCB(self, stanza, jingle, error, action):
''' Default callback for action stanzas -- simple ack ''' Default callback for action stanzas -- simple ack
@ -259,6 +256,20 @@ class JingleSession(object):
response = stanza.buildReply('result') response = stanza.buildReply('result')
self.connection.connection.send(response) self.connection.connection.send(response)
def __errorCB(self, stanza, jingle, error, action):
#FIXME
text = error.getTagData('text')
jingle_error = None
xmpp_error = None
for child in error.getChildren():
if child.getNamespace() == xmpp.NS_JINGLE_ERRORS:
jingle_error = child.getName()
elif child.getNamespace() == xmpp.NS_STANZAS:
xmpp_error = child.getName()
self.__dispatch_error(xmpp_error, jingle_error, text)
#FIXME: Not sure if we would want to do that... not yet...
#self.connection.deleteJingle(self)
def __transportReplaceCB(self, stanza, jingle, error, action): def __transportReplaceCB(self, stanza, jingle, error, action):
for content in jingle.iterTags('content'): for content in jingle.iterTags('content'):
creator = content['creator'] creator = content['creator']
@ -290,9 +301,7 @@ class JingleSession(object):
def __sessionInfoCB(self, stanza, jingle, error, action): def __sessionInfoCB(self, stanza, jingle, error, action):
payload = jingle.getPayload() payload = jingle.getPayload()
if len(payload) > 0: if len(payload) > 0:
err = xmpp.Error(stanza, xmpp.NS_STANZAS + ' feature-not-implemented') self.__send_error('feature-not-implemented', 'unsupported-info')
err.setTag('unsupported-info', namespace=xmpp.NS_JINGLE_ERRORS)
self.connection.connection.send(err)
raise xmpp.NodeProcessed raise xmpp.NodeProcessed
def __contentRemoveCB(self, stanza, jingle, error, action): def __contentRemoveCB(self, stanza, jingle, error, action):
@ -309,6 +318,7 @@ class JingleSession(object):
def __sessionAcceptCB(self, stanza, jingle, error, action): def __sessionAcceptCB(self, stanza, jingle, error, action):
if self.state != JingleStates.pending: #FIXME if self.state != JingleStates.pending: #FIXME
raise OutOfOrder raise OutOfOrder
self.state = JingleStates.active
def __contentAcceptCB(self, stanza, jingle, error, action): def __contentAcceptCB(self, stanza, jingle, error, action):
''' Called when we get content-accept stanza or equivalent one ''' Called when we get content-accept stanza or equivalent one
@ -388,13 +398,55 @@ class JingleSession(object):
def __sessionTerminateCB(self, stanza, jingle, error, action): def __sessionTerminateCB(self, stanza, jingle, error, action):
self.connection.deleteJingle(self) self.connection.deleteJingle(self)
self.connection.dispatch('JINGLE_DISCONNECTED', (self.peerjid, self.sid)) reason, text = self.__reason_from_stanza(jingle)
if reason not in ('success', 'cancel', 'decline'):
self.__dispatch_error(reason, reason, text)
if text:
text = '%s (%s)' % (reason, text)
else:
text = reason#TODO
self.connection.dispatch('JINGLE_DISCONNECTED', (self.peerjid, self.sid, text))
def __send_error(self, stanza, error, jingle_error=None, text=None):
err = xmpp.Error(stanza, error)
err.setNamespace(xmpp.NS_STANZAS)
if jingle_error:
err.setTag(jingle_error, namespace=xmpp.NS_JINGLE_ERRORS)
if text:
err.setTagData('text', text)
self.connection.connection.send(err)
self.__dispatch_error(error, jingle_error, text)
def __dispatch_error(error, jingle_error=None, text=None):
if jingle_error:
error = jingle_error
if text:
text = '%s (%s)' % (error, text)
else:
text = error
self.connection.dispatch('JINGLE_ERROR', (self.peerjid, self.sid, text))
def __broadcastAllCB(self, stanza, jingle, error, action): def __broadcastAllCB(self, stanza, jingle, error, action):
''' Broadcast the stanza to all content handlers. ''' ''' Broadcast the stanza to all content handlers. '''
for content in self.contents.itervalues(): for content in self.contents.itervalues():
content.stanzaCB(stanza, None, error, action) content.stanzaCB(stanza, None, error, action)
def __reason_from_stanza(self, stanza):
reason = 'success'
reasons = ['success', 'busy', 'cancel', 'connectivity-error',
'decline', 'expired', 'failed-application', 'failed-transport',
'general-error', 'gone', 'incompatible-parameters', 'media-error',
'security-error', 'timeout', 'unsupported-applications',
'unsupported-transports']
tag = stanza.getTag('reason')
if tag:
text = tag.getTagData('text')
for r in reasons:
if tag.getTag(r):
reason = r
break
return (reason, text)
''' Methods that make/send proper pieces of XML. They check if the session ''' Methods that make/send proper pieces of XML. They check if the session
is in appropriate state. ''' is in appropriate state. '''
def __makeJingle(self, action): def __makeJingle(self, action):
@ -452,6 +504,14 @@ class JingleSession(object):
jingle.addChild(node=reason) jingle.addChild(node=reason)
self.__broadcastAllCB(stanza, jingle, None, 'session-terminate-sent') self.__broadcastAllCB(stanza, jingle, None, 'session-terminate-sent')
self.connection.connection.send(stanza) self.connection.connection.send(stanza)
reason, text = self.__reason_from_stanza(jingle)
if reason not in ('success', 'cancel', 'decline'):
self.__dispatch_error(reason, reason, text)
if text:
text = '%s (%s)' % (reason, text)
else:
text = reason
self.connection.dispatch('JINGLE_DISCONNECTED', (self.peerjid, self.sid, text))
self.connection.deleteJingle(self) self.connection.deleteJingle(self)
def __contentAdd(self): def __contentAdd(self):
@ -618,6 +678,8 @@ class JingleRTPContent(JingleContent):
'video': farsight.MEDIA_TYPE_VIDEO}[media] 'video': farsight.MEDIA_TYPE_VIDEO}[media]
self.got_codecs = False self.got_codecs = False
self.candidates_ready = False # True when local candidates are prepared
self.callbacks['content-accept'] += [self.__getRemoteCodecsCB] self.callbacks['content-accept'] += [self.__getRemoteCodecsCB]
self.callbacks['session-accept'] += [self.__getRemoteCodecsCB] self.callbacks['session-accept'] += [self.__getRemoteCodecsCB]
self.callbacks['session-initiate'] += [self.__getRemoteCodecsCB] self.callbacks['session-initiate'] += [self.__getRemoteCodecsCB]
@ -658,14 +720,17 @@ class JingleRTPContent(JingleContent):
pass pass
elif name == 'farsight-recv-codecs-changed': elif name == 'farsight-recv-codecs-changed':
pass pass
elif name == 'farsight-codecs-changed':
self.session.acceptSession()
self.session.startSession()
elif name == 'farsight-local-candidates-prepared': elif name == 'farsight-local-candidates-prepared':
self.session.candidates_ready = True self.candidates_ready = True
self.session.acceptSession() self.session.acceptSession()
self.session.startSession() self.session.startSession()
elif name == 'farsight-new-local-candidate': elif name == 'farsight-new-local-candidate':
candidate = message.structure['candidate'] candidate = message.structure['candidate']
self.candidates.append(candidate) self.candidates.append(candidate)
if self.session.candidates_ready: if self.candidates_ready:
#FIXME: Is this case even possible? #FIXME: Is this case even possible?
self.send_candidate(candidate) self.send_candidate(candidate)
elif name == 'farsight-component-state-changed': elif name == 'farsight-component-state-changed':
@ -734,7 +799,6 @@ class JingleVoIP(JingleRTPContent):
over an ICE UDP protocol. ''' over an ICE UDP protocol. '''
def __init__(self, session, node=None): def __init__(self, session, node=None):
JingleRTPContent.__init__(self, session, 'audio', node) JingleRTPContent.__init__(self, session, 'audio', node)
self.setupStream() self.setupStream()
@ -786,6 +850,7 @@ class JingleVoIP(JingleRTPContent):
# The following is needed for farsight to process ICE requests: # The following is needed for farsight to process ICE requests:
self.pipeline.set_state(gst.STATE_PLAYING) self.pipeline.set_state(gst.STATE_PLAYING)
class JingleVideo(JingleRTPContent): class JingleVideo(JingleRTPContent):
def __init__(self, session, node=None): def __init__(self, session, node=None):
JingleRTPContent.__init__(self, session, 'video', node) JingleRTPContent.__init__(self, session, 'video', node)
@ -824,6 +889,7 @@ class JingleVideo(JingleRTPContent):
# The following is needed for farsight to process ICE requests: # The following is needed for farsight to process ICE requests:
self.pipeline.set_state(gst.STATE_PLAYING) self.pipeline.set_state(gst.STATE_PLAYING)
class ConnectionJingle(object): class ConnectionJingle(object):
''' This object depends on that it is a part of Connection class. ''' ''' This object depends on that it is a part of Connection class. '''
def __init__(self): def __init__(self):