diff --git a/data/gui/chat_control.ui b/data/gui/chat_control.ui
index b9eae0f67e78cd5f6dc040e28399bb401f3b191e..b8d85353d6d13f94a8798761278131b5699ba5f6 100644
--- a/data/gui/chat_control.ui
+++ b/data/gui/chat_control.ui
@@ -17,10 +17,9 @@
     <property name="can_focus">True</property>
     <property name="spacing">1</property>
     <child>
-      <object class="GtkVBox" id="chat_control_vbox">
+      <object class="GtkPaned" id="paned1">
+        <property name="visible">True</property>
         <property name="can_focus">True</property>
-        <property name="border_width">3</property>
-        <property name="spacing">1</property>
         <child>
           <object class="GtkVBox" id="vbox2">
             <property name="visible">True</property>
@@ -329,6 +328,7 @@
                     <property name="receives_default">True</property>
                     <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
                     <property name="has_tooltip">True</property>
+                    <property name="tooltip_markup" translatable="yes">Show a list of emoticons (Alt+M)</property>
                     <property name="tooltip_text" translatable="yes">Show a list of emoticons (Alt+M)</property>
                     <property name="relief">none</property>
                     <property name="focus_on_click">False</property>
@@ -391,6 +391,7 @@
                     <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
                     <property name="no_show_all">True</property>
                     <property name="has_tooltip">True</property>
+                    <property name="tooltip_markup" translatable="yes">Add this contact to roster (Ctrl+D)</property>
                     <property name="tooltip_text" translatable="yes">Add this contact to roster (Ctrl+D)</property>
                     <property name="relief">none</property>
                     <child>
@@ -481,6 +482,7 @@
                     <property name="receives_default">True</property>
                     <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
                     <property name="has_tooltip">True</property>
+                    <property name="tooltip_markup" translatable="yes">Invite contacts to the conversation (Ctrl+G)</property>
                     <property name="tooltip_text" translatable="yes">Invite contacts to the conversation (Ctrl+G)</property>
                     <property name="relief">none</property>
                     <property name="focus_on_click">False</property>
@@ -507,6 +509,7 @@
                     <property name="receives_default">True</property>
                     <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
                     <property name="has_tooltip">True</property>
+                    <property name="tooltip_markup" translatable="yes">Show the contact&amp;apos;s profile (Ctrl+I)</property>
                     <property name="tooltip_text" translatable="yes">Show the contact's profile (Ctrl+I)</property>
                     <property name="relief">none</property>
                     <property name="focus_on_click">False</property>
@@ -533,6 +536,7 @@
                     <property name="receives_default">True</property>
                     <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
                     <property name="has_tooltip">True</property>
+                    <property name="tooltip_markup" translatable="yes">Browse the chat history (Ctrl+H)</property>
                     <property name="tooltip_text" translatable="yes">Browse the chat history (Ctrl+H)</property>
                     <property name="relief">none</property>
                     <property name="focus_on_click">False</property>
@@ -540,7 +544,6 @@
                       <object class="GtkImage" id="history_image">
                         <property name="visible">True</property>
                         <property name="can_focus">False</property>
-                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
                         <property name="stock">gtk-justify-fill</property>
                         <property name="icon-size">1</property>
                       </object>
@@ -571,6 +574,7 @@
                     <property name="receives_default">True</property>
                     <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
                     <property name="has_tooltip">True</property>
+                    <property name="tooltip_markup" translatable="yes">Show advanced functions (Alt+D)</property>
                     <property name="tooltip_text" translatable="yes">Show advanced functions (Alt+D)</property>
                     <property name="relief">none</property>
                     <property name="focus_on_click">False</property>
@@ -610,6 +614,7 @@
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
                         <property name="receives_default">True</property>
+                        <property name="has_tooltip">True</property>
                         <property name="relief">none</property>
                         <property name="focus_on_click">False</property>
                         <property name="orientation">vertical</property>
@@ -631,6 +636,7 @@ audio-volume-medium</property>
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
                         <property name="receives_default">True</property>
+                        <property name="has_tooltip">True</property>
                         <property name="relief">none</property>
                         <property name="focus_on_click">False</property>
                         <property name="yalign">0.4699999988079071</property>
@@ -701,6 +707,8 @@ audio-mic-volume-low</property>
                     <property name="visible">True</property>
                     <property name="can_focus">True</property>
                     <property name="receives_default">True</property>
+                    <property name="has_tooltip">True</property>
+                    <property name="tooltip_markup" translatable="yes">Send message</property>
                     <property name="tooltip_text" translatable="yes">Send message</property>
                     <property name="image">image4</property>
                     <property name="use_underline">True</property>
@@ -720,9 +728,80 @@ audio-mic-volume-low</property>
             </child>
           </object>
           <packing>
-            <property name="expand">True</property>
-            <property name="fill">True</property>
-            <property name="position">0</property>
+            <property name="resize">False</property>
+            <property name="shrink">True</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkBox" id="video_hbox">
+            <property name="can_focus">False</property>
+            <property name="no_show_all">True</property>
+            <property name="spacing">6</property>
+            <child>
+              <object class="GtkFixed" id="outgoing_fixed">
+                <property name="can_focus">False</property>
+                <property name="no_show_all">True</property>
+                <child>
+                  <object class="GtkViewport" id="viewport1">
+                    <property name="width_request">160</property>
+                    <property name="height_request">120</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <child>
+                      <object class="GtkDrawingArea" id="outgoing_drawingarea">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="double_buffered">False</property>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkBox" id="vbox1">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="orientation">vertical</property>
+                <child>
+                  <object class="GtkViewport" id="viewport2">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <child>
+                      <object class="GtkDrawingArea" id="incoming_drawingarea">
+                        <property name="width_request">320</property>
+                        <property name="height_request">240</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="double_buffered">False</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">True</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="resize">True</property>
+            <property name="shrink">True</property>
           </packing>
         </child>
       </object>
diff --git a/data/gui/preferences_window.ui b/data/gui/preferences_window.ui
index 43068b2136a73c9de67d7ed4d03af01521b55a0f..23c44c067838f79c34b824b7ebd0ecc915573e5c 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</property>
                   </object>
                   <packing>
                     <property name="expand">False</property>
-                    <property name="fill">True</property>
+                    <property name="fill">False</property>
                     <property name="position">0</property>
                   </packing>
                 </child>
@@ -2386,7 +2386,7 @@ $T will be replaced by auto-not-available timeout</property>
                           <object class="GtkTable" id="table8">
                             <property name="visible">True</property>
                             <property name="can_focus">False</property>
-                            <property name="n_rows">4</property>
+                            <property name="n_rows">5</property>
                             <property name="n_columns">2</property>
                             <property name="column_spacing">6</property>
                             <property name="row_spacing">6</property>
@@ -2494,6 +2494,21 @@ $T will be replaced by auto-not-available timeout</property>
                                 <property name="y_options"/>
                               </packing>
                             </child>
+                            <child>
+                              <object class="GtkCheckButton" id="video_see_self_checkbutton">
+                                <property name="label" translatable="yes">View own video source</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">False</property>
+                                <property name="xalign">0</property>
+                                <property name="draw_indicator">True</property>
+                              </object>
+                              <packing>
+                                <property name="right_attach">2</property>
+                                <property name="top_attach">4</property>
+                                <property name="bottom_attach">5</property>
+                              </packing>
+                            </child>
                           </object>
                         </child>
                       </object>
diff --git a/src/chat_control.py b/src/chat_control.py
index 4d133a3900ed402026e0c05b48d9f8f4ebfa952b..cc7280daadb286de63705bb77edc534f34f539f7 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 65c70e4b24e471e4abb5c1bbc38204298a7169b8..ea4014947d6a61134bd0ca55bff5104ec9eb7adf 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 c9d64fe7057427ec5fe8464eb1751b8dc769e035..95b6d6b1eb588115a8e37bef1bb5f9463a528cbd 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 86bd35b325b2cad4ace964e74a0375cabd9994b0..fe3195bd4a04703557c7aa103fa986f1f2dbe4b9 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 5f6f8b7c0c1de4caecec365e233d0c442c087167..7ff7b91fcdb62e0f8ea96f4de2112131ab59c3e1 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 5968b84a7f1b3ee44f41eb18761a95d141e3b9b7..9df0408b35353242c6aca955aa1bb8acc2f80f5a 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 <http://www.gnu.org/licenses/>.
 
-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 588c7a5c3de6759caa03fdbc5b9690322ffe33f8..234019c24707b5e887f1292fa6cfb9608081f5a8 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 80f6f976451810b3122359804244d3a7995a4342..00407b3473d710ab2c08e9c8aec9043e5a80046e 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 63f051f19ee81acc6473391f15cd43e7adda57e5..3a808311e6f0b9a6e16fe3d59f8338a1331f7771 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')