support for content-{add,reject,accept}, new helpers, and other few things

This commit is contained in:
Thibaut GIRKA 2009-09-25 19:32:13 +02:00
parent a051d1ec95
commit 77541f3e7f
4 changed files with 196 additions and 81 deletions

View File

@ -1579,14 +1579,17 @@ class ChatControl(ChatControlBase, ChatCommands):
elif state == 'connecting': elif state == 'connecting':
self.audio_state = self.JINGLE_STATE_CONNECTING self.audio_state = self.JINGLE_STATE_CONNECTING
self.audio_sid = sid self.audio_sid = sid
self._audio_button.set_active(True)
elif state == 'connection_received': elif state == 'connection_received':
self.audio_state = self.JINGLE_STATE_CONNECTION_RECEIVED self.audio_state = self.JINGLE_STATE_CONNECTION_RECEIVED
self.audio_sid = sid self.audio_sid = sid
self._audio_button.set_active(True)
elif state == 'connected': elif state == 'connected':
self.audio_state = self.JINGLE_STATE_CONNECTED self.audio_state = self.JINGLE_STATE_CONNECTED
elif state == 'stop': elif state == 'stop':
self.audio_state = self.JINGLE_STATE_AVAILABLE self.audio_state = self.JINGLE_STATE_AVAILABLE
self.audio_sid = None self.audio_sid = None
self._audio_button.set_active(False)
elif state == 'error': elif state == 'error':
self.audio_state = self.JINGLE_STATE_ERROR self.audio_state = self.JINGLE_STATE_ERROR
self.update_audio() self.update_audio()
@ -1605,15 +1608,18 @@ class ChatControl(ChatControlBase, ChatCommands):
self.video_sid = None self.video_sid = None
elif state == 'connecting': elif state == 'connecting':
self.video_state = self.JINGLE_STATE_CONNECTING self.video_state = self.JINGLE_STATE_CONNECTING
self._video_button.set_active(True)
self.video_sid = sid self.video_sid = sid
elif state == 'connection_received': elif state == 'connection_received':
self.video_state = self.JINGLE_STATE_CONNECTION_RECEIVED self.video_state = self.JINGLE_STATE_CONNECTION_RECEIVED
self._video_button.set_active(True)
self.video_sid = sid self.video_sid = sid
elif state == 'connected': elif state == 'connected':
self.video_state = self.JINGLE_STATE_CONNECTED self.video_state = self.JINGLE_STATE_CONNECTED
elif state == 'stop': elif state == 'stop':
self.video_state = self.JINGLE_STATE_AVAILABLE self.video_state = self.JINGLE_STATE_AVAILABLE
self.video_sid = None self.video_sid = None
self._video_button.set_active(False)
elif state == 'error': elif state == 'error':
self.video_state = self.JINGLE_STATE_ERROR self.video_state = self.JINGLE_STATE_ERROR
self.update_video() self.update_video()
@ -1825,10 +1831,11 @@ class ChatControl(ChatControlBase, ChatCommands):
self.set_audio_state('connecting', sid) self.set_audio_state('connecting', sid)
else: else:
session = gajim.connections[self.account].get_jingle_session( session = gajim.connections[self.account].get_jingle_session(
self.contact.get_full_jid(), self.audio_sid) self.contact.get_full_jid(), self.video_sid)
if session: if session:
# TODO: end only audio content = session.get_content('audio')
session.end_session() if content:
session.remove_content(content.creator, content.name)
def on_video_button_toggled(self, widget): def on_video_button_toggled(self, widget):
if widget.get_active(): if widget.get_active():
@ -1839,8 +1846,9 @@ class ChatControl(ChatControlBase, ChatCommands):
session = gajim.connections[self.account].get_jingle_session( session = gajim.connections[self.account].get_jingle_session(
self.contact.get_full_jid(), self.video_sid) self.contact.get_full_jid(), self.video_sid)
if session: if session:
# TODO: end only video content = session.get_content('video')
session.end_session() if content:
session.remove_content(content.creator, content.name)
def _toggle_gpg(self): def _toggle_gpg(self):
if not self.gpg_is_active and not self.contact.keyID: if not self.gpg_is_active and not self.contact.keyID:

View File

@ -17,7 +17,10 @@
# - 'senders' attribute of 'content' element # - 'senders' attribute of 'content' element
# - security preconditions # - security preconditions
# * actions: # * actions:
# - content-accept, content-reject, content-add, content-modify # - content-accept: see content-add
# - content-reject: sending it ; receiving is ok
# - content-add: handling ; sending is ok
# - content-modify: both
# - description-info, session-info # - description-info, session-info
# - security-info # - security-info
# - transport-accept, transport-reject # - transport-accept, transport-reject
@ -88,7 +91,7 @@ class JingleSession(object):
# our full jid # our full jid
self.ourjid = gajim.get_jid_from_account(self.connection.name) + '/' + \ self.ourjid = gajim.get_jid_from_account(self.connection.name) + '/' + \
con.server_resource con.server_resource
self.peerjid = jid # jid we connect to self.peerjid = str(jid) # jid we connect to
# jid we use as the initiator # jid we use as the initiator
self.initiator = weinitiate and self.ourjid or self.peerjid self.initiator = weinitiate and self.ourjid or self.peerjid
# jid we use as the responder # jid we use as the responder
@ -107,10 +110,12 @@ class JingleSession(object):
# use .prepend() to add new callbacks, especially when you're going # use .prepend() to add new callbacks, especially when you're going
# to send error instead of ack # to send error instead of ack
self.callbacks = { self.callbacks = {
'content-accept': [self.__contentAcceptCB, self.__defaultCB], 'content-accept': [self.__contentAcceptCB, self.__broadcastCB,
'content-add': [self.__defaultCB], #TODO self.__defaultCB],
'content-add': [self.__contentAddCB, self.__broadcastCB,
self.__defaultCB], #TODO
'content-modify': [self.__defaultCB], #TODO 'content-modify': [self.__defaultCB], #TODO
'content-reject': [self.__defaultCB], #TODO 'content-reject': [self.__defaultCB, self.__contentRemoveCB], #TODO
'content-remove': [self.__defaultCB, self.__contentRemoveCB], 'content-remove': [self.__defaultCB, self.__contentRemoveCB],
'description-info': [self.__defaultCB], #TODO 'description-info': [self.__defaultCB], #TODO
'security-info': [self.__defaultCB], #TODO 'security-info': [self.__defaultCB], #TODO
@ -133,7 +138,6 @@ class JingleSession(object):
def approve_session(self): def approve_session(self):
''' Called when user accepts session in UI (when we aren't the initiator). ''' Called when user accepts session in UI (when we aren't the initiator).
''' '''
self.accepted = True
self.accept_session() self.accept_session()
def decline_session(self): def decline_session(self):
@ -154,10 +158,23 @@ class JingleSession(object):
''' Middle-level functions to manage contents. Handle local content ''' Middle-level functions to manage contents. Handle local content
cache and send change notifications. ''' cache and send change notifications. '''
def get_content(self, media=None):
if media == 'audio':
cls = JingleVoIP
elif media == 'video':
cls = JingleVideo
#elif media == None:
# cls = JingleContent
else:
return None
for content in self.contents.values():
if isinstance(content, cls):
return content
def add_content(self, name, content, creator='we'): def add_content(self, name, content, creator='we'):
''' Add new content to session. If the session is active, ''' Add new content to session. If the session is active,
this will send proper stanza to update session. this will send proper stanza to update session.
The protocol prohibits changing that when pending.
Creator must be one of ('we', 'peer', 'initiator', 'responder')''' Creator must be one of ('we', 'peer', 'initiator', 'responder')'''
assert creator in ('we', 'peer', 'initiator', 'responder') assert creator in ('we', 'peer', 'initiator', 'responder')
@ -171,34 +188,53 @@ class JingleSession(object):
content.name = name content.name = name
self.contents[(creator, name)] = content self.contents[(creator, name)] = content
if self.state == JingleStates.active:
pass # TODO: send proper stanza, shouldn't be needed now
def remove_content(self, creator, name): def remove_content(self, creator, name):
''' We do not need this now ''' ''' We do not need this now '''
pass #TODO:
if (creator, name) in self.contents:
content = self.contents[(creator, name)]
if len(self.contents) > 1:
self.__content_remove(content)
self.contents[(creator, name)].destroy()
if len(self.contents) == 0:
self.end_session()
def modify_content(self, creator, name, *someother): def modify_content(self, creator, name, *someother):
''' We do not need this now ''' ''' We do not need this now '''
pass pass
def accept_session(self): def on_session_state_changed(self, content=None):
''' Check if all contents and user agreed to start session. ''' if self.state == JingleStates.active and self.accepted:
if not self.weinitiate and self.accepted and \ if not content:
self.state == JingleStates.pending and self.is_ready(): return
if (content.creator == 'initiator') == self.weinitiate:
# We initiated this content. It's a pending content-add.
self.__content_add(content)
else:
# The other side created this content, we accept it.
self.__content_accept(content)
elif self.is_ready():
if not self.weinitiate and self.state == JingleStates.pending:
self.__session_accept() self.__session_accept()
elif self.weinitiate and self.state == JingleStates.ended:
self.__session_initiate()
def is_ready(self): def is_ready(self):
''' Returns True when all codecs and candidates are ready ''' Returns True when all codecs and candidates are ready
(for all contents). ''' (for all contents). '''
return all((content.is_ready() for content in self.contents.itervalues())) return (all((content.is_ready() for content in self.contents.itervalues()))
and self.accepted)
''' Middle-level function to do stanza exchange. ''' ''' Middle-level function to do stanza exchange. '''
def accept_session(self):
''' Mark the session as accepted. '''
self.accepted = True
self.on_session_state_changed()
def start_session(self): def start_session(self):
''' Start session. ''' ''' Mark the session as ready to be started. '''
#FIXME: Start only once self.accepted = True
if self.weinitiate and self.state == JingleStates.ended and self.is_ready(): self.on_session_state_changed()
self.__session_initiate()
def send_session_info(self): def send_session_info(self):
pass pass
@ -309,10 +345,10 @@ class JingleSession(object):
creator = content['creator'] creator = content['creator']
name = content['name'] name = content['name']
if (creator, name) in self.contents: if (creator, name) in self.contents:
del self.contents[(creator, name)] self.contents[(creator, name)].destroy()
if len(self.contents) == 0: if len(self.contents) == 0:
reason = xmpp.Node('reason') reason = xmpp.Node('reason')
reason.setTag('success') #FIXME: Is it the good one? reason.setTag('success')
self._session_terminate(reason) self._session_terminate(reason)
def __sessionAcceptCB(self, stanza, jingle, error, action): def __sessionAcceptCB(self, stanza, jingle, error, action):
@ -328,6 +364,27 @@ class JingleSession(object):
creator = content['creator'] creator = content['creator']
name = content['name']#TODO... name = content['name']#TODO...
def __contentAddCB(self, stanza, jingle, error, action):
#TODO: Needs to be rewritten
if self.state == JingleStates.ended:
raise OutOfOrder
for element in jingle.iterTags('content'):
# checking what kind of session this will be
desc_ns = element.getTag('description').getNamespace()
media = element.getTag('description')['media']
tran_ns = element.getTag('transport').getNamespace()
if desc_ns == xmpp.NS_JINGLE_RTP and media in ('audio', 'video') \
and tran_ns == xmpp.NS_JINGLE_ICE_UDP:
if media == 'audio':
self.add_content(element['name'], JingleVoIP(self), 'peer')
else:
self.add_content(element['name'], JingleVideo(self), 'peer')
else:
content = JingleContent()
self.add_content(element['name'], content, 'peer')
self.__content_reject(content)
self.contents[(content.creator, content.name)].destroy()
def __sessionInitiateCB(self, stanza, jingle, error, action): def __sessionInitiateCB(self, stanza, jingle, error, action):
''' We got a jingle session request from other entity, ''' We got a jingle session request from other entity,
therefore we are the receiver... Unpack the data, therefore we are the receiver... Unpack the data,
@ -511,20 +568,40 @@ class JingleSession(object):
text = '%s (%s)' % (reason, text) text = '%s (%s)' % (reason, text)
else: else:
text = reason text = reason
self.connection.dispatch('JINGLE_DISCONNECTED', (self.peerjid, self.sid, text))
self.connection.delete_jingle(self) self.connection.delete_jingle(self)
self.connection.dispatch('JINGLE_DISCONNECTED', (self.peerjid, self.sid, text))
def __content_add(self): def __content_add(self, content):
assert self.state == JingleStates.active #TODO: test
def __content_accept(self):
assert self.state != JingleStates.ended assert self.state != JingleStates.ended
stanza, jingle = self.__make_jingle('content-add')
self.__append_content(jingle, content)
self.__broadcastCB(stanza, jingle, None, 'content-add-sent')
self.connection.connection.send(stanza)
def __content_accept(self, content):
#TODO: test
assert self.state != JingleStates.ended
stanza, jingle = self.__make_jingle('content-accept')
self.__append_content(jingle, content)
self.__broadcastCB(stanza, jingle, None, 'content-accept-sent')
self.connection.connection.send(stanza)
def __content_reject(self, content):
assert self.state != JingleStates.ended
stanza, jingle = self.__make_jingle('content-reject')
self.__append_content(jingle, content)
self.connection.connection.send(stanza)
def __content_modify(self): def __content_modify(self):
assert self.state != JingleStates.ended assert self.state != JingleStates.ended
def __content_remove(self): def __content_remove(self, content):
assert self.state != JingleStates.ended assert self.state != JingleStates.ended
stanza, jingle = self.__make_jingle('content-remove')
self.__append_content(jingle, content)
self.connection.connection.send(stanza)
#TODO: dispatch something?
def content_negociated(self, media): def content_negociated(self, media):
self.connection.dispatch('JINGLE_CONNECTED', (self.peerjid, self.sid, self.connection.dispatch('JINGLE_CONNECTED', (self.peerjid, self.sid,
@ -554,8 +631,8 @@ class JingleContent(object):
self.callbacks = { self.callbacks = {
# these are called when *we* get stanzas # these are called when *we* get stanzas
'content-accept': [], 'content-accept': [self.__transportInfoCB],
'content-add': [], 'content-add': [self.__transportInfoCB],
'content-modify': [], 'content-modify': [],
'content-remove': [], 'content-remove': [],
'session-accept': [self.__transportInfoCB], 'session-accept': [self.__transportInfoCB],
@ -566,6 +643,8 @@ class JingleContent(object):
'iq-result': [], 'iq-result': [],
'iq-error': [], 'iq-error': [],
# these are called when *we* sent these stanzas # these are called when *we* sent these stanzas
'content-accept-sent': [self.__fillJingleStanza],
'content-add-sent': [self.__fillJingleStanza],
'session-initiate-sent': [self.__fillJingleStanza], 'session-initiate-sent': [self.__fillJingleStanza],
'session-accept-sent': [self.__fillJingleStanza], 'session-accept-sent': [self.__fillJingleStanza],
'session-terminate-sent': [], 'session-terminate-sent': [],
@ -676,6 +755,11 @@ class JingleContent(object):
content.addChild(xmpp.NS_JINGLE_ICE_UDP + ' transport', attrs=attrs, content.addChild(xmpp.NS_JINGLE_ICE_UDP + ' transport', attrs=attrs,
payload=self.iter_candidates()) payload=self.iter_candidates())
def destroy(self):
self.callbacks = None
del self.session.contents[(self.creator, self.name)]
class JingleRTPContent(JingleContent): class JingleRTPContent(JingleContent):
def __init__(self, session, media, node=None): def __init__(self, session, media, node=None):
JingleContent.__init__(self, session, node) JingleContent.__init__(self, session, node)
@ -687,6 +771,7 @@ class JingleRTPContent(JingleContent):
self.candidates_ready = False # True when local candidates are prepared self.candidates_ready = False # True when local candidates are prepared
self.callbacks['content-accept'] += [self.__getRemoteCodecsCB] self.callbacks['content-accept'] += [self.__getRemoteCodecsCB]
self.callbacks['content-add'] += [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]
self.callbacks['session-terminate'] += [self.__stop] self.callbacks['session-terminate'] += [self.__stop]
@ -718,21 +803,32 @@ class JingleRTPContent(JingleContent):
content.addChild(xmpp.NS_JINGLE_RTP + ' description', content.addChild(xmpp.NS_JINGLE_RTP + ' description',
attrs={'media': self.media}, payload=self.iter_codecs()) attrs={'media': self.media}, payload=self.iter_codecs())
def _setup_funnel(self):
self.funnel = gst.element_factory_make('fsfunnel')
self.pipeline.add(self.funnel)
self.funnel.set_state(gst.STATE_PLAYING)
self.sink.set_state(gst.STATE_PLAYING)
self.funnel.link(self.sink)
def _on_src_pad_added(self, stream, pad, codec):
if not self.funnel:
self._setup_funnel()
pad.link(self.funnel.get_pad('sink%d'))
def _on_gst_message(self, bus, message): def _on_gst_message(self, bus, message):
if message.type == gst.MESSAGE_ELEMENT: if message.type == gst.MESSAGE_ELEMENT:
name = message.structure.get_name() name = message.structure.get_name()
#print name
if name == 'farsight-new-active-candidate-pair': if name == 'farsight-new-active-candidate-pair':
pass pass
elif name == 'farsight-recv-codecs-changed': elif name == 'farsight-recv-codecs-changed':
pass pass
elif name == 'farsight-codecs-changed': elif name == 'farsight-codecs-changed':
self.session.accept_session() if self.is_ready():
self.session.start_session() self.session.on_session_state_changed(self)
elif name == 'farsight-local-candidates-prepared': elif name == 'farsight-local-candidates-prepared':
self.candidates_ready = True self.candidates_ready = True
self.session.accept_session() if self.is_ready():
self.session.start_session() self.session.on_session_state_changed(self)
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)
@ -751,9 +847,6 @@ class JingleRTPContent(JingleContent):
#TODO: farsight.DIRECTION_BOTH only if senders='both' #TODO: farsight.DIRECTION_BOTH only if senders='both'
self.p2pstream.set_property('direction', farsight.DIRECTION_BOTH) self.p2pstream.set_property('direction', farsight.DIRECTION_BOTH)
self.session.content_negociated(self.media) self.session.content_negociated(self.media)
#if not self.session.weinitiate: #FIXME: one more FIXME...
# self.session.send_content_accept(self.__content((xmpp.Node(
# 'description', payload=self.iter_codecs()),)))
elif name == 'farsight-error': elif name == 'farsight-error':
print 'Farsight error #%d!' % message.structure['error-no'] print 'Farsight error #%d!' % message.structure['error-no']
print 'Message: %s' % message.structure['error-msg'] print 'Message: %s' % message.structure['error-msg']
@ -805,6 +898,11 @@ class JingleRTPContent(JingleContent):
def __del__(self): def __del__(self):
self.__stop() self.__stop()
def destroy(self):
JingleContent.destroy(self)
self.p2pstream.disconnect_by_func(self._on_src_pad_added)
self.pipeline.get_bus().disconnect_by_func(self._on_gst_message)
class JingleVoIP(JingleRTPContent): class JingleVoIP(JingleRTPContent):
''' Jingle VoIP sessions consist of audio content transported ''' Jingle VoIP sessions consist of audio content transported
@ -830,8 +928,8 @@ class JingleVoIP(JingleRTPContent):
# the local parts # the local parts
# TODO: use gconfaudiosink? # TODO: use gconfaudiosink?
# sink = get_first_gst_element(['alsasink', 'osssink', 'autoaudiosink']) # sink = get_first_gst_element(['alsasink', 'osssink', 'autoaudiosink'])
sink = gst.element_factory_make('alsasink') self.sink = gst.element_factory_make('alsasink')
sink.set_property('sync', False) self.sink.set_property('sync', False)
#sink.set_property('latency-time', 20000) #sink.set_property('latency-time', 20000)
#sink.set_property('buffer-time', 80000) #sink.set_property('buffer-time', 80000)
@ -843,21 +941,12 @@ class JingleVoIP(JingleRTPContent):
self.mic_volume.set_property('volume', 1) self.mic_volume.set_property('volume', 1)
# link gst elements # link gst elements
self.pipeline.add(sink, src_mic, self.mic_volume) self.pipeline.add(self.sink, src_mic, self.mic_volume)
src_mic.link(self.mic_volume) src_mic.link(self.mic_volume)
def src_pad_added (stream, pad, codec):
if not self.funnel:
self.funnel = gst.element_factory_make('fsfunnel')
self.pipeline.add(self.funnel)
self.funnel.set_state (gst.STATE_PLAYING)
sink.set_state (gst.STATE_PLAYING)
self.funnel.link(sink)
pad.link(self.funnel.get_pad('sink%d'))
self.mic_volume.get_pad('src').link(self.p2psession.get_property( self.mic_volume.get_pad('src').link(self.p2psession.get_property(
'sink-pad')) 'sink-pad'))
self.p2pstream.connect('src-pad-added', src_pad_added) self.p2pstream.connect('src-pad-added', self._on_src_pad_added)
# 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)
@ -875,7 +964,7 @@ class JingleVideo(JingleRTPContent):
# sometimes it'll freeze... # sometimes it'll freeze...
JingleRTPContent.setup_stream(self) JingleRTPContent.setup_stream(self)
# the local parts # the local parts
src_vid = gst.element_factory_make('v4l2src') src_vid = gst.element_factory_make('videotestsrc')
videoscale = gst.element_factory_make('videoscale') videoscale = gst.element_factory_make('videoscale')
caps = gst.element_factory_make('capsfilter') caps = gst.element_factory_make('capsfilter')
caps.set_property('caps', gst.caps_from_string('video/x-raw-yuv, width=320, height=240')) caps.set_property('caps', gst.caps_from_string('video/x-raw-yuv, width=320, height=240'))
@ -884,19 +973,11 @@ class JingleVideo(JingleRTPContent):
self.pipeline.add(src_vid, videoscale, caps, colorspace) self.pipeline.add(src_vid, videoscale, caps, colorspace)
gst.element_link_many(src_vid, videoscale, caps, colorspace) gst.element_link_many(src_vid, videoscale, caps, colorspace)
def src_pad_added (stream, pad, codec): self.sink = gst.element_factory_make('xvimagesink')
if not self.funnel: self.pipeline.add(self.sink)
self.funnel = gst.element_factory_make('fsfunnel')
self.pipeline.add(self.funnel)
videosink = gst.element_factory_make('xvimagesink')
self.pipeline.add(videosink)
self.funnel.set_state (gst.STATE_PLAYING)
videosink.set_state(gst.STATE_PLAYING)
self.funnel.link(videosink)
pad.link(self.funnel.get_pad('sink%d'))
colorspace.get_pad('src').link(self.p2psession.get_property('sink-pad')) colorspace.get_pad('src').link(self.p2psession.get_property('sink-pad'))
self.p2pstream.connect('src-pad-added', src_pad_added) self.p2pstream.connect('src-pad-added', self._on_src_pad_added)
# 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)
@ -953,6 +1034,8 @@ class ConnectionJingle(object):
raise xmpp.NodeProcessed raise xmpp.NodeProcessed
def startVoIP(self, jid): def startVoIP(self, jid):
if self.get_jingle_session(jid, media='audio'):
return
jingle = self.get_jingle_session(jid, media='video') jingle = self.get_jingle_session(jid, media='video')
if jingle: if jingle:
jingle.add_content('voice', JingleVoIP(jingle)) jingle.add_content('voice', JingleVoIP(jingle))
@ -964,6 +1047,8 @@ class ConnectionJingle(object):
return jingle.sid return jingle.sid
def startVideoIP(self, jid): def startVideoIP(self, jid):
if self.get_jingle_session(jid, media='video'):
return
jingle = self.get_jingle_session(jid, media='audio') jingle = self.get_jingle_session(jid, media='audio')
if jingle: if jingle:
jingle.add_content('video', JingleVideo(jingle)) jingle.add_content('video', JingleVideo(jingle))
@ -981,14 +1066,10 @@ class ConnectionJingle(object):
else: else:
return None return None
elif media: elif media:
if media == 'audio': if media not in ('audio', 'video'):
cls = JingleVoIP
elif media == 'video':
cls = JingleVideo
else:
return None return None
for session in self.__sessions.values(): for session in self.__sessions.values():
for content in session.contents.values(): if session.peerjid == jid and session.get_content(media):
if isinstance(content, cls):
return session return session
return None return None

View File

@ -32,6 +32,7 @@
import gtk import gtk
import gobject import gobject
import os import os
from weakref import WeakValueDictionary
import gtkgui_helpers import gtkgui_helpers
import vcard import vcard
@ -4442,11 +4443,15 @@ class GPGInfoWindow:
self.window.destroy() self.window.destroy()
class VoIPCallReceivedDialog(object): class VoIPCallReceivedDialog(object):
instances = WeakValueDictionary()
def __init__(self, account, contact_jid, sid): def __init__(self, account, contact_jid, sid):
self.account = account self.account = account
self.fjid = contact_jid self.fjid = contact_jid
self.sid = sid self.sid = sid
self.instances[(contact_jid, sid)] = self
xml = gtkgui_helpers.get_glade('voip_call_received_dialog.glade') xml = gtkgui_helpers.get_glade('voip_call_received_dialog.glade')
xml.signal_autoconnect(self) xml.signal_autoconnect(self)
@ -4461,9 +4466,17 @@ class VoIPCallReceivedDialog(object):
dialog = xml.get_widget('voip_call_received_messagedialog') dialog = xml.get_widget('voip_call_received_messagedialog')
dialog.set_property('secondary-text', dialog.set_property('secondary-text',
dialog.get_property('secondary-text') % {'contact': contact_text}) dialog.get_property('secondary-text') % {'contact': contact_text})
self._dialog = dialog
dialog.show_all() dialog.show_all()
@classmethod
def get_dialog(cls, jid, sid):
if (jid, sid) in cls.instances:
return cls.instances[(jid, sid)]
else:
return None
def on_voip_call_received_messagedialog_close(self, dialog): def on_voip_call_received_messagedialog_close(self, dialog):
return self.on_voip_call_received_messagedialog_response(dialog, return self.on_voip_call_received_messagedialog_response(dialog,
gtk.RESPONSE_NO) gtk.RESPONSE_NO)
@ -4487,7 +4500,10 @@ class VoIPCallReceivedDialog(object):
if not contact: if not contact:
return return
ctrl = gajim.interface.new_chat(contact, self.account) ctrl = gajim.interface.new_chat(contact, self.account)
if session.get_content('audio'):
ctrl.set_audio_state('connecting', self.sid) ctrl.set_audio_state('connecting', self.sid)
if session.get_content('video'):
ctrl.set_video_state('connecting', self.sid)
else: # response==gtk.RESPONSE_NO else: # response==gtk.RESPONSE_NO
session.decline_session() session.decline_session()

View File

@ -2119,7 +2119,10 @@ class Interface:
if not ctrl: if not ctrl:
ctrl = self.msg_win_mgr.get_control(jid, account) ctrl = self.msg_win_mgr.get_control(jid, account)
if ctrl: if ctrl:
if 'audio' in content_types:
ctrl.set_audio_state('connection_received', sid) ctrl.set_audio_state('connection_received', sid)
if 'video' in content_types:
ctrl.set_video_state('connection_received', sid)
if helpers.allow_popup_window(account): if helpers.allow_popup_window(account):
dialogs.VoIPCallReceivedDialog(account, peerjid, sid) dialogs.VoIPCallReceivedDialog(account, peerjid, sid)
@ -2141,14 +2144,17 @@ class Interface:
def handle_event_jingle_connected(self, account, data): def handle_event_jingle_connected(self, account, data):
# ('JINGLE_CONNECTED', account, (peerjid, sid, media)) # ('JINGLE_CONNECTED', account, (peerjid, sid, media))
peerjid, sid, media = data peerjid, sid, media = data
if media == 'audio': if media in ('audio', 'video'):
jid = gajim.get_jid_without_resource(peerjid) jid = gajim.get_jid_without_resource(peerjid)
resource = gajim.get_resource_from_jid(peerjid) resource = gajim.get_resource_from_jid(peerjid)
ctrl = self.msg_win_mgr.get_control(peerjid, account) ctrl = self.msg_win_mgr.get_control(peerjid, account)
if not ctrl: if not ctrl:
ctrl = self.msg_win_mgr.get_control(jid, account) ctrl = self.msg_win_mgr.get_control(jid, account)
if ctrl: if ctrl:
if media == 'audio':
ctrl.set_audio_state('connected', sid) ctrl.set_audio_state('connected', sid)
else:
ctrl.set_video_state('connected', sid)
def handle_event_jingle_disconnected(self, account, data): def handle_event_jingle_disconnected(self, account, data):
# ('JINGLE_DISCONNECTED', account, (peerjid, sid, reason)) # ('JINGLE_DISCONNECTED', account, (peerjid, sid, reason))
@ -2160,6 +2166,10 @@ class Interface:
ctrl = self.msg_win_mgr.get_control(jid, account) ctrl = self.msg_win_mgr.get_control(jid, account)
if ctrl: if ctrl:
ctrl.set_audio_state('stop', sid=sid, reason=reason) ctrl.set_audio_state('stop', sid=sid, reason=reason)
ctrl.set_video_state('stop', sid=sid, reason=reason)
dialog = dialogs.VoIPCallReceivedDialog.get_dialog(peerjid, sid)
if dialog:
dialog._dialog.destroy()
def handle_event_jingle_error(self, account, data): def handle_event_jingle_error(self, account, data):
# ('JINGLE_ERROR', account, (peerjid, sid, reason)) # ('JINGLE_ERROR', account, (peerjid, sid, reason))