From e746ca9b1a28399f7db30d1307d483ce578de182 Mon Sep 17 00:00:00 2001 From: Yann Leboulanger Date: Tue, 25 Mar 2014 21:26:57 +0100 Subject: [PATCH] start havine audio / video. Doesn't work correctly yet --- data/gui/chat_control.ui | 93 +++++++++++++++++++-- data/gui/preferences_window.ui | 19 ++++- src/chat_control.py | 2 +- src/common/gajim.py | 17 ++-- src/common/jingle.py | 2 +- src/common/jingle_rtp.py | 139 ++++++++++++++++--------------- src/common/jingle_transport.py | 81 +++++++++--------- src/common/multimedia_helpers.py | 43 +++++----- src/config.py | 1 - src/features_window.py | 2 +- src/gui_interface.py | 2 +- 11 files changed, 250 insertions(+), 151 deletions(-) diff --git a/data/gui/chat_control.ui b/data/gui/chat_control.ui index b9eae0f67..b8d85353d 100644 --- a/data/gui/chat_control.ui +++ b/data/gui/chat_control.ui @@ -17,10 +17,9 @@ True 1 - + + True True - 3 - 1 True @@ -329,6 +328,7 @@ True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True + Show a list of emoticons (Alt+M) Show a list of emoticons (Alt+M) none False @@ -391,6 +391,7 @@ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True True + Add this contact to roster (Ctrl+D) Add this contact to roster (Ctrl+D) none @@ -481,6 +482,7 @@ True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True + Invite contacts to the conversation (Ctrl+G) Invite contacts to the conversation (Ctrl+G) none False @@ -507,6 +509,7 @@ True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True + Show the contact's profile (Ctrl+I) Show the contact's profile (Ctrl+I) none False @@ -533,6 +536,7 @@ True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True + Browse the chat history (Ctrl+H) Browse the chat history (Ctrl+H) none False @@ -540,7 +544,6 @@ True False - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-justify-fill 1 @@ -571,6 +574,7 @@ True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True + Show advanced functions (Alt+D) Show advanced functions (Alt+D) none False @@ -610,6 +614,7 @@ True True True + True none False vertical @@ -631,6 +636,7 @@ audio-volume-medium True True True + True none False 0.4699999988079071 @@ -701,6 +707,8 @@ audio-mic-volume-low True True True + True + Send message Send message image4 True @@ -720,9 +728,80 @@ audio-mic-volume-low - True - True - 0 + False + True + + + + + False + True + 6 + + + False + True + + + 160 + 120 + True + False + + + True + False + False + + + + + + + False + True + 0 + + + + + True + False + vertical + + + True + False + + + 320 + 240 + True + False + False + + + + + True + True + 0 + + + + + + + + True + True + 1 + + + + + True + True diff --git a/data/gui/preferences_window.ui b/data/gui/preferences_window.ui index 43068b213..23c44c067 100644 --- a/data/gui/preferences_window.ui +++ b/data/gui/preferences_window.ui @@ -2363,7 +2363,7 @@ $T will be replaced by auto-not-available timeout False - True + False 0 @@ -2386,7 +2386,7 @@ $T will be replaced by auto-not-available timeout True False - 4 + 5 2 6 6 @@ -2494,6 +2494,21 @@ $T will be replaced by auto-not-available timeout + + + View own video source + True + True + False + 0 + True + + + 2 + 4 + 5 + + diff --git a/src/chat_control.py b/src/chat_control.py index 4d133a390..cc7280daa 100644 --- a/src/chat_control.py +++ b/src/chat_control.py @@ -1798,7 +1798,7 @@ class ChatControl(ChatControlBase): # Video buttons self._video_button.set_sensitive(self.video_available) - # change tooltip text for audio and video buttons if python-farstream is + # change tooltip text for audio and video buttons if farstream is # not installed audio_tooltip_text = _('Toggle audio session') + '\n' video_tooltip_text = _('Toggle video session') + '\n' diff --git a/src/common/gajim.py b/src/common/gajim.py index 65c70e4b2..ea4014947 100644 --- a/src/common/gajim.py +++ b/src/common/gajim.py @@ -184,16 +184,19 @@ except Exception: HAVE_FARSTREAM = True try: - raise ImportError - farstream = __import__('farstream') - import gst - import glib + import gi + gi.require_version('Farstream', '0.2') + from gi.repository import Farstream + gi.require_version('Gst', '1.0') + from gi.repository import Gst + from gi.repository import GLib try: - conference = gst.element_factory_make('fsrtpconference') - session = conference.new_session(farstream.MEDIA_TYPE_AUDIO) + Gst.init(None) + conference = Gst.ElementFactory.make('fsrtpconference', None) + session = conference.new_session(Farstream.MediaType.AUDIO) del session del conference - except: + except GLib.GError: HAVE_FARSTREAM = False except ImportError: diff --git a/src/common/jingle.py b/src/common/jingle.py index c9d64fe70..95b6d6b1e 100644 --- a/src/common/jingle.py +++ b/src/common/jingle.py @@ -61,7 +61,7 @@ class ConnectionJingle(object): """ if sid in self._sessions: #FIXME: Move this elsewhere? - for content in self._sessions[sid].contents.values(): + for content in list(self._sessions[sid].contents.values()): content.destroy() self._sessions[sid].callbacks = [] del self._sessions[sid] diff --git a/src/common/jingle_rtp.py b/src/common/jingle_rtp.py index 86bd35b32..fe3195bd4 100644 --- a/src/common/jingle_rtp.py +++ b/src/common/jingle_rtp.py @@ -23,9 +23,11 @@ from gi.repository import GLib import socket import nbxmpp -import farstream -import gst -from glib import GError +from gi.repository import Farstream +import gi +gi.require_version('Gst', '1.0') +from gi.repository import Gst +from gi.repository import GLib from common import gajim @@ -45,8 +47,8 @@ class JingleRTPContent(JingleContent): JingleContent.__init__(self, session, transport) self.media = media self._dtmf_running = False - self.farstream_media = {'audio': farstream.MEDIA_TYPE_AUDIO, - 'video': farstream.MEDIA_TYPE_VIDEO}[media] + self.farstream_media = {'audio': Farstream.MediaType.AUDIO, + 'video': Farstream.MediaType.VIDEO}[media] self.pipeline = None self.src_bin = None @@ -64,13 +66,13 @@ class JingleRTPContent(JingleContent): def setup_stream(self, on_src_pad_added): # pipeline and bus - self.pipeline = gst.Pipeline() + self.pipeline = Gst.Pipeline() bus = self.pipeline.get_bus() bus.add_signal_watch() bus.connect('message', self._on_gst_message) # conference - self.conference = gst.element_factory_make('fsrtpconference') + self.conference = Gst.ElementFactory.make('fsrtpconference', None) self.pipeline.add(self.conference) self.funnel = None @@ -80,6 +82,7 @@ class JingleRTPContent(JingleContent): # FIXME: Consider a workaround, here... # pidgin and telepathy-gabble don't follow the XEP, and it won't work # due to bad controlling-mode + params = {'controlling-mode': self.session.weinitiate, 'debug': False} if gajim.config.get('use_stun_server'): stun_server = gajim.config.get('stun_server') @@ -88,16 +91,16 @@ class JingleRTPContent(JingleContent): if stun_server: try: ip = socket.getaddrinfo(stun_server, 0, socket.AF_UNSPEC, - socket.SOCK_STREAM)[0][4][0] + socket.SOCK_STREAM)[0][4][0] except socket.gaierror as e: log.warning('Lookup of stun ip failed: %s' % str(e)) else: params['stun-ip'] = ip self.p2pstream = self.p2psession.new_stream(participant, - farstream.DIRECTION_RECV) + Farstream.StreamDirection.RECV) self.p2pstream.connect('src-pad-added', on_src_pad_added) - self.p2pstream.set_transmitter('nice', params) + self.p2pstream.set_transmitter_ht('nice', params) def is_ready(self): return (JingleContent.is_ready(self) and self.candidates_ready) @@ -105,9 +108,9 @@ class JingleRTPContent(JingleContent): def make_bin_from_config(self, config_key, pipeline, text): pipeline = pipeline % gajim.config.get(config_key) try: - bin = gst.parse_bin_from_description(pipeline, True) + bin = Gst.parse_bin_from_description(pipeline, True) return bin - except GError as e: + except GLib.GError as e: gajim.nec.push_incoming_event(InformationEvent(None, conn=self.session.connection, level='error', pri_txt=_('%s configuration error') % text.capitalize(), @@ -144,8 +147,8 @@ class JingleRTPContent(JingleContent): def _start_dtmf(self, event): if event in ('*', '#'): - event = {'*': farstream.DTMF_EVENT_STAR, - '#': farstream.DTMF_EVENT_POUND}[event] + event = {'*': Farstream.DTMFEvent.STAR, + '#': Farstream.DTMFEvent.POUND}[event] else: event = int(event) self.p2psession.start_telephony_event(event, 2) @@ -155,30 +158,30 @@ class JingleRTPContent(JingleContent): def _fill_content(self, content): content.addChild(nbxmpp.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.funnel = Gst.ElementFactory.make('funnel', None) self.pipeline.add(self.funnel) - self.funnel.set_state(gst.STATE_PLAYING) - self.sink.set_state(gst.STATE_PLAYING) self.funnel.link(self.sink) + self.sink.set_state(Gst.State.PLAYING) + self.funnel.set_state(Gst.State.PLAYING) def _on_src_pad_added(self, stream, pad, codec): if not self.funnel: self._setup_funnel() - pad.link(self.funnel.get_pad('sink%d')) + pad.link(self.funnel.get_request_pad('sink_%u')) def _on_gst_message(self, bus, message): - if message.type == gst.MESSAGE_ELEMENT: - name = message.structure.get_name() + if message.type == Gst.MessageType.ELEMENT: + name = message.get_structure().get_name() log.debug('gst element message: %s: %s' % (name, message)) if name == 'farstream-new-active-candidate-pair': pass elif name == 'farstream-recv-codecs-changed': pass elif name == 'farstream-codecs-changed': - if self.sent and self.p2psession.get_property('codecs'): + if self.sent and self.p2psession.props.codecs_without_config: self.send_description_info() if self.transport.remote_candidates: # those lines MUST be done after we get info on our @@ -187,29 +190,30 @@ class JingleRTPContent(JingleContent): self.transport.remote_candidates) self.transport.remote_candidates = [] self.p2pstream.set_property('direction', - farstream.DIRECTION_BOTH) + Farstream.StreamDirection.BOTH) elif name == 'farstream-local-candidates-prepared': self.candidates_ready = True if self.is_ready(): self.session.on_session_state_changed(self) elif name == 'farstream-new-local-candidate': - candidate = message.structure['candidate'] + candidate = message.get_structure().get_value('candidate').copy( + ) self.transport.candidates.append(candidate) if self.sent: # FIXME: Is this case even possible? self.send_candidate(candidate) elif name == 'farstream-component-state-changed': - state = message.structure['state'] - if state == farstream.STREAM_STATE_FAILED: + state = message.get_structure().get_value('state') + if state == Farstream.StreamState.FAILED: reason = nbxmpp.Node('reason') reason.setTag('failed-transport') self.session.remove_content(self.creator, self.name, reason) elif name == 'farstream-error': log.error('Farstream error #%d!\nMessage: %s' % ( - message.structure['error-no'], - message.structure['error-msg'])) - elif message.type == gst.MESSAGE_ERROR: + message.get_structure().get_value('error-no'), + message.get_structure().get_value('error-msg'))) + elif message.type == Gst.MessageType.ERROR: # TODO: Fix it to fallback to videotestsrc anytime an error occur, # or raise an error, Jingle way # or maybe one-sided stream? @@ -217,21 +221,21 @@ class JingleRTPContent(JingleContent): gajim.nec.push_incoming_event(InformationEvent(None, conn=self.session.connection, level='error', pri_txt=_('GStreamer error'), sec_txt=_('Error: %s\nDebug: ' - '%s' % (message.structure['gerror'], - message.structure['debug'])))) + '%s' % (message.get_structure().get_value('gerror'), + message.get_structure().get_value('debug'))))) sink_pad = self.p2psession.get_property('sink-pad') # Remove old source - self.src_bin.get_pad('src').unlink(sink_pad) - self.src_bin.set_state(gst.STATE_NULL) + self.src_bin.get_static_pad('src').unlink(sink_pad) + self.src_bin.set_state(Gst.State.NULL) self.pipeline.remove(self.src_bin) if not self.stream_failed_once: # Add fallback source self.src_bin = self.get_fallback_src() self.pipeline.add(self.src_bin) - self.src_bin.get_pad('src').link(sink_pad) + self.src_bin.get_static_pad('src').link(sink_pad) self.stream_failed_once = True else: reason = nbxmpp.Node('reason') @@ -239,10 +243,10 @@ class JingleRTPContent(JingleContent): self.session.remove_content(self.creator, self.name, reason) # Start playing again - self.pipeline.set_state(gst.STATE_PLAYING) + self.pipeline.set_state(Gst.State.PLAYING) def get_fallback_src(self): - return gst.element_factory_make('fakesrc') + return Gst.ElementFactory.make('fakesrc', None) def on_negotiated(self): if self.accepted: @@ -252,9 +256,9 @@ class JingleRTPContent(JingleContent): self.p2pstream.add_remote_candidates( self.transport.remote_candidates) self.transport.remote_candidates = [] - # TODO: farstream.DIRECTION_BOTH only if senders='both' - self.p2pstream.set_property('direction', - farstream.DIRECTION_BOTH) + # TODO: Farstream.StreamDirection.BOTH only if senders='both' +# self.p2pstream.set_property('direction', +# Farstream.StreamDirection.BOTH) JingleContent.on_negotiated(self) def __on_remote_codecs(self, stanza, content, error, action): @@ -267,39 +271,39 @@ class JingleRTPContent(JingleContent): if not codec['id'] or not codec['name'] or not codec['clockrate']: # ignore invalid payload-types continue - c = farstream.Codec(int(codec['id']), codec['name'], + c = Farstream.Codec.new(int(codec['id']), codec['name'], self.farstream_media, int(codec['clockrate'])) if 'channels' in codec: c.channels = int(codec['channels']) else: c.channels = 1 - c.optional_params = [(str(p['name']), str(p['value'])) for p in \ - codec.iterTags('parameter')] + for p in codec.iterTags('parameter'): + c.add_optional_parameter(p['name'], str(p['value'])) codecs.append(c) if codecs: # FIXME: Handle this case: - # glib.GError: There was no intersection between the remote codecs and + # GLib.GError: There was no intersection between the remote codecs and # the local ones self.p2pstream.set_remote_codecs(codecs) def iter_codecs(self): - codecs = self.p2psession.get_property('codecs') + codecs = self.p2psession.props.codecs_without_config for codec in codecs: attrs = {'name': codec.encoding_name, - 'id': codec.id, - 'channels': codec.channels} + 'id': codec.id, + 'channels': codec.channels} if codec.clock_rate: attrs['clockrate'] = codec.clock_rate if codec.optional_params: - payload = (nbxmpp.Node('parameter', {'name': name, - 'value': value}) for name, value in codec.optional_params) + payload = (nbxmpp.Node('parameter', {'name': p.name, + 'value': p.value}) for p in codec.optional_params) else: payload = () yield nbxmpp.Node('payload-type', attrs, payload) def __stop(self, *things): - self.pipeline.set_state(gst.STATE_NULL) + self.pipeline.set_state(Gst.State.NULL) def __del__(self): self.__stop() @@ -341,31 +345,32 @@ class JingleAudio(JingleRTPContent): # place 16kHz before 8kHz, as buggy psi versions will take in # account only the first codec - codecs = [farstream.Codec(farstream.CODEC_ID_ANY, 'SPEEX', - farstream.MEDIA_TYPE_AUDIO, 16000), - farstream.Codec(farstream.CODEC_ID_ANY, 'SPEEX', - farstream.MEDIA_TYPE_AUDIO, 8000)] + codecs = [Farstream.Codec.new(Farstream.CODEC_ID_ANY, 'SPEEX', + Farstream.MediaType.AUDIO, 16000), + Farstream.Codec.new(Farstream.CODEC_ID_ANY, 'SPEEX', + Farstream.MediaType.AUDIO, 8000)] self.p2psession.set_codec_preferences(codecs) # the local parts # TODO: Add queues? self.src_bin = self.make_bin_from_config('audio_input_device', - '%s ! audioconvert', _("audio input")) + '%s ! audioconvert', _("audio input")) self.sink = self.make_bin_from_config('audio_output_device', - 'audioconvert ! volume name=gajim_out_vol ! %s', _("audio output")) + 'audioconvert ! volume name=gajim_out_vol ! %s', _("audio output")) self.mic_volume = self.src_bin.get_by_name('gajim_vol') self.out_volume = self.sink.get_by_name('gajim_out_vol') # link gst elements - self.pipeline.add(self.sink, self.src_bin) + self.pipeline.add(self.sink) + self.pipeline.add(self.src_bin) - self.src_bin.get_pad('src').link(self.p2psession.get_property( - 'sink-pad')) + self.src_bin.get_static_pad('src').link(self.p2psession.get_property( + 'sink-pad')) # The following is needed for farstream to process ICE requests: - self.pipeline.set_state(gst.STATE_PLAYING) + self.pipeline.set_state(Gst.State.PLAYING) class JingleVideo(JingleRTPContent): @@ -381,7 +386,7 @@ class JingleVideo(JingleRTPContent): # the local parts if gajim.config.get('video_framerate'): - framerate = 'videorate ! video/x-raw-yuv,framerate=%s ! ' % \ + framerate = 'videorate ! video/x-raw,framerate=%s ! ' % \ gajim.config.get('video_framerate') else: framerate = '' @@ -390,11 +395,11 @@ class JingleVideo(JingleRTPContent): except: w = h = None if w and h: - video_size = 'video/x-raw-yuv,width=%s,height=%s ! ' % (w, h) + video_size = 'video/x-raw,width=%s,height=%s ! ' % (w, h) else: video_size = '' self.src_bin = self.make_bin_from_config('video_input_device', - '%%s ! %svideoscale ! %sffmpegcolorspace' % (framerate, video_size), + '%%s ! %svideoscale ! %svideoconvert' % (framerate, video_size), _("video input")) #caps = gst.element_factory_make('capsfilter') #caps.set_property('caps', gst.caps_from_string('video/x-raw-yuv, width=320, height=240')) @@ -403,20 +408,20 @@ class JingleVideo(JingleRTPContent): #src_bin.link(caps) self.sink = self.make_bin_from_config('video_output_device', - 'videoscale ! ffmpegcolorspace ! %s force-aspect-ratio=True', + 'videoscale ! videoconvert ! %s force-aspect-ratio=True', _("video output")) self.pipeline.add(self.sink) - self.src_bin.get_pad('src').link(self.p2psession.get_property( + self.src_bin.get_static_pad('src').link(self.p2psession.get_property( 'sink-pad')) # The following is needed for farstream to process ICE requests: - self.pipeline.set_state(gst.STATE_PLAYING) + self.pipeline.set_state(Gst.State.PLAYING) def get_fallback_src(self): # TODO: Use avatar? - pipeline = 'videotestsrc is-live=true ! video/x-raw-yuv,framerate=10/1 ! ffmpegcolorspace' - return gst.parse_bin_from_description(pipeline, True) + pipeline = 'videotestsrc is-live=true ! video/x-raw,framerate=10/1 ! videoconvert' + return Gst.parse_bin_from_description(pipeline, True) def get_content(desc): if desc['media'] == 'audio': diff --git a/src/common/jingle_transport.py b/src/common/jingle_transport.py index 5f6f8b7c0..7ff7b91fc 100644 --- a/src/common/jingle_transport.py +++ b/src/common/jingle_transport.py @@ -343,8 +343,7 @@ class JingleTransportIBB(JingleTransport): return transport try: - raise ImportError - import farstream + from gi.repository import Farstream except Exception: pass @@ -353,24 +352,24 @@ class JingleTransportICEUDP(JingleTransport): JingleTransport.__init__(self, TransportType.ICEUDP) def make_candidate(self, candidate): - types = {farstream.CANDIDATE_TYPE_HOST: 'host', - farstream.CANDIDATE_TYPE_SRFLX: 'srflx', - farstream.CANDIDATE_TYPE_PRFLX: 'prflx', - farstream.CANDIDATE_TYPE_RELAY: 'relay', - farstream.CANDIDATE_TYPE_MULTICAST: 'multicast'} + types = {Farstream.CandidateType.HOST: 'host', + Farstream.CandidateType.SRFLX: 'srflx', + Farstream.CandidateType.PRFLX: 'prflx', + Farstream.CandidateType.RELAY: 'relay', + Farstream.CandidateType.MULTICAST: 'multicast'} attrs = { - 'component': candidate.component_id, - 'foundation': '1', # hack - 'generation': '0', - 'ip': candidate.ip, - 'network': '0', - 'port': candidate.port, - 'priority': int(candidate.priority), # hack - 'id': gajim.get_an_id() + 'component': candidate.component_id, + 'foundation': '1', # hack + 'generation': '0', + 'ip': candidate.ip, + 'network': '0', + 'port': candidate.port, + 'priority': int(candidate.priority), # hack + 'id': gajim.get_an_id() } if candidate.type in types: attrs['type'] = types[candidate.type] - if candidate.proto == farstream.NETWORK_PROTOCOL_UDP: + if candidate.proto == Farstream.NetworkProtocol.UDP: attrs['protocol'] = 'udp' else: # we actually don't handle properly different tcp options in jingle @@ -389,33 +388,37 @@ class JingleTransportICEUDP(JingleTransport): def parse_transport_stanza(self, transport): candidates = [] for candidate in transport.iterTags('candidate'): - cand = farstream.Candidate() - cand.component_id = int(candidate['component']) - cand.ip = str(candidate['ip']) - cand.port = int(candidate['port']) - cand.foundation = str(candidate['foundation']) - #cand.type = farstream.CANDIDATE_TYPE_LOCAL - cand.priority = int(candidate['priority']) - + foundation = str(candidate['foundation']) + component_id = int(candidate['component']) + ip = str(candidate['ip']) + port = int(candidate['port']) + base_ip = None + base_port = 0 if candidate['protocol'] == 'udp': - cand.proto = farstream.NETWORK_PROTOCOL_UDP + proto = Farstream.NetworkProtocol.UDP else: - # we actually don't handle properly different tcp options in jingle - cand.proto = farstream.NETWORK_PROTOCOL_TCP - - cand.username = str(transport['ufrag']) - cand.password = str(transport['pwd']) - - #FIXME: huh? - types = {'host': farstream.CANDIDATE_TYPE_HOST, - 'srflx': farstream.CANDIDATE_TYPE_SRFLX, - 'prflx': farstream.CANDIDATE_TYPE_PRFLX, - 'relay': farstream.CANDIDATE_TYPE_RELAY, - 'multicast': farstream.CANDIDATE_TYPE_MULTICAST} + # we actually don't handle properly different tcp options in + # jingle + proto = Farstream.NetworkProtocol.TCP + priority = int(candidate['priority']) + types = {'host': Farstream.CandidateType.HOST, + 'srflx': Farstream.CandidateType.SRFLX, + 'prflx': Farstream.CandidateType.PRFLX, + 'relay': Farstream.CandidateType.RELAY, + 'multicast': Farstream.CandidateType.MULTICAST} if 'type' in candidate and candidate['type'] in types: - cand.type = types[candidate['type']] + type_ = types[candidate['type']] else: - print('Unknown type %s' % candidate['type']) + log.warning('Unknown type %s' % candidate['type']) + type_ = Farstream.CandidateType.HOST + username = str(transport['ufrag']) + password = str(transport['pwd']) + ttl = 0 + + cand = Farstream.Candidate.new_full(foundation, component_id, ip, + port, base_ip, base_port, proto, priority, type_, username, + password, ttl) + candidates.append(cand) self.remote_candidates.extend(candidates) return candidates diff --git a/src/common/multimedia_helpers.py b/src/common/multimedia_helpers.py index 5968b84a7..9df0408b3 100644 --- a/src/common/multimedia_helpers.py +++ b/src/common/multimedia_helpers.py @@ -13,7 +13,7 @@ ## You should have received a copy of the GNU General Public License ## along with Gajim. If not, see . -import gst +from gi.repository import Gst class DeviceManager(object): @@ -29,29 +29,24 @@ class DeviceManager(object): return self.devices def detect_element(self, name, text, pipe='%s'): - try: - if gst.element_factory_find(name): - element = gst.element_factory_make(name, - '%spresencetest' % name) - if isinstance(element, gst.interfaces.PropertyProbe): - element.set_state(gst.STATE_READY) - element.probe_property_name('device') - devices = element.probe_get_values_name('device') - if devices: - self.devices[text % _(' Default device')] = pipe % name - for device in devices: - element.set_state(gst.STATE_NULL) - element.set_property('device', device) - element.set_state(gst.STATE_READY) - device_name = element.get_property('device-name') - self.devices[text % device_name] = pipe % \ - '%s device=%s' % (name, device) - element.set_state(gst.STATE_NULL) - else: - self.devices[text] = pipe % name - except ImportError: - pass - except gst.ElementNotFoundError: + if Gst.ElementFactory.find(name): + element = Gst.ElementFactory.make(name, '%spresencetest' % name) + if hasattr(element.props, 'device'): + element.set_state(Gst.State.READY) + devices = element.get_properties('device') + if devices: + self.devices[text % _(' Default device')] = pipe % name + for device in devices: + element.set_state(Gst.State.NULL) + element.set_property('device', device) + element.set_state(Gst.State.READY) + device_name = element.get_property('device-name') + self.devices[text % device_name] = pipe % \ + '%s device=%s' % (name, device) + element.set_state(Gst.State.NULL) + else: + self.devices[text] = pipe % name + else: print('element \'%s\' not found' % name) diff --git a/src/config.py b/src/config.py index 588c7a5c3..234019c24 100644 --- a/src/config.py +++ b/src/config.py @@ -65,7 +65,6 @@ from common import gpg from common import ged try: - raise ImportError from common.multimedia_helpers import AudioInputManager, AudioOutputManager from common.multimedia_helpers import VideoInputManager, VideoOutputManager HAS_GST = True diff --git a/src/features_window.py b/src/features_window.py index 80f6f9764..00407b347 100644 --- a/src/features_window.py +++ b/src/features_window.py @@ -97,7 +97,7 @@ class FeaturesWindow: _('Requires python-docutils.')), _('Audio / Video'): (self.farstream_available, _('Ability to start audio and video chat.'), - _('Requires python-farstream and gstreamer-plugins-bad.'), + _('Requires gir1.2-farstream-0.2, gir1.2-gstreamer-1.0, gstreamer1.0-libav and gstreamer1.0-plugins-ugly.'), _('Feature not available under Windows.')), _('UPnP-IGD'): (self.gupnp_igd_available, _('Ability to request your router to forward port for file transfer.'), diff --git a/src/gui_interface.py b/src/gui_interface.py index 63f051f19..3a808311e 100644 --- a/src/gui_interface.py +++ b/src/gui_interface.py @@ -129,7 +129,7 @@ class Interface: else: return - cls(obj.pri_txt, obj.sec_txt) + cls(obj.pri_txt, GLib.markup_escape_text(obj.sec_txt)) def handle_ask_new_nick(self, account, room_jid): title = _('Unable to join group chat')