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:
parent
f5b1c2dca7
commit
bd9d793ad8
|
@ -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:
|
||||||
|
|
|
@ -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):
|
||||||
|
|
Loading…
Reference in New Issue