Commit 1d15de79 authored by Emmanuel Gil Peyrot's avatar Emmanuel Gil Peyrot Committed by Philipp Hörist

Jingle video: Replace X11 specifics with GTK+

This uses the same mechanism originally added for preferences, so that
video display is handled by a GTK+ widget instead of some X11 dark
magic.

This makes the self video display properly (!) during a Jingle call, but
still with random freezes and no remote video being displayed, those
fixes will be for a latter commit.
parent be6e4b3a
Pipeline #4752 passed with stages
in 3 minutes and 20 seconds
......@@ -699,7 +699,7 @@ class ChatControl(ChatControlBase):
# update MessageWindow._controls
self.parent_win.change_jid(self.account, old_full_jid, new_full_jid)
def stop_jingle(self, sid=None, _reason=None):
def stop_jingle(self, sid=None, reason=None):
audio_sid = self.jingle['audio'].sid
video_sid = self.jingle['video'].sid
if audio_sid and sid in (audio_sid, None):
......@@ -909,20 +909,9 @@ class ChatControl(ChatControlBase):
fixed = self.xml.outgoing_fixed
fixed.set_no_show_all(False)
video_hbox.show_all()
out_da = self.xml.outgoing_drawingarea
out_da.realize()
if os.name == 'nt':
out_xid = out_da.get_window().handle
else:
out_xid = out_da.get_window().get_xid()
else:
out_xid = None
video_hbox.show_all()
in_da = self.xml.incoming_drawingarea
in_da.realize()
in_xid = in_da.get_window().get_xid()
sid = con.get_module('Jingle').start_video(
self.contact.get_full_jid(), in_xid, out_xid)
self.contact.get_full_jid())
else:
sid = con.start_audio(self.contact.get_full_jid())
self.jingle[jingle_type].set_state('connecting', sid)
......
......@@ -412,11 +412,8 @@ class JingleAudio(JingleRTPContent):
class JingleVideo(JingleRTPContent):
def __init__(self, session, transport=None, in_xid=0, out_xid=0):
def __init__(self, session, transport=None):
JingleRTPContent.__init__(self, session, 'video', transport)
self.in_xid = in_xid
self.out_xid = out_xid
self.out_xid_set = False
self.setup_stream()
def setup_stream(self):
......@@ -426,42 +423,24 @@ class JingleVideo(JingleRTPContent):
JingleRTPContent.setup_stream(self, self._on_src_pad_added)
bus = self.pipeline.get_bus()
bus.enable_sync_message_emission()
bus.connect('sync-message::element', self._on_sync_message)
# the local parts
if app.config.get('video_framerate'):
framerate = 'videorate ! video/x-raw,framerate=%s ! ' % \
app.config.get('video_framerate')
else:
framerate = ''
try:
w, h = app.config.get('video_size').split('x')
except Exception:
w = h = None
if w and h:
video_size = 'video/x-raw,width=%s,height=%s ! ' % (w, h)
else:
video_size = ''
def do_setup(self, self_display_sink, other_sink):
if app.config.get('video_see_self'):
tee = '! tee name=t ! queue ! videoscale ! ' + \
'video/x-raw,width=160,height=120 ! videoconvert ! ' + \
'%s t. ! queue ' % app.config.get(
'video_output_device')
tee = '! tee name=split ! queue name=self-display-queue split. ! queue name=network-queue'
else:
tee = ''
self.src_bin = self.make_bin_from_config('video_input_device',
'%%s %s! %svideoscale ! %svideoconvert' %
(tee, framerate, video_size),
'%%s %s' % tee,
_("video input"))
self.pipeline.add(self.src_bin)
if app.config.get('video_see_self'):
self.pipeline.add(self_display_sink)
self_display_queue = self.src_bin.get_by_name('self-display-queue')
self_display_queue.get_static_pad('src').link_maybe_ghosting(self_display_sink.get_static_pad('sink'))
self.pipeline.set_state(Gst.State.PLAYING)
self.sink = self.make_bin_from_config('video_output_device',
'videoscale ! videoconvert ! %s',
_("video output"))
self.sink = other_sink
self.pipeline.add(self.sink)
self.src_bin.get_static_pad('src').link(self.p2psession.get_property(
......@@ -469,28 +448,13 @@ class JingleVideo(JingleRTPContent):
# The following is needed for farstream to process ICE requests:
self.pipeline.set_state(Gst.State.PLAYING)
def _on_sync_message(self, bus, message):
if message.get_structure() is None:
return False
if message.get_structure().get_name() == 'prepare-window-handle':
message.src.set_property('force-aspect-ratio', True)
imagesink = message.src
if app.config.get('video_see_self') and not self.out_xid_set:
imagesink.set_window_handle(self.out_xid)
self.out_xid_set = True
else:
imagesink.set_window_handle(self.in_xid)
Gst.debug_bin_to_dot_file(self.pipeline, Gst.DebugGraphDetails.ALL, 'video-graph')
def get_fallback_src(self):
# TODO: Use avatar?
pipeline = 'videotestsrc is-live=true ! video/x-raw,framerate=10/1 ! videoconvert'
return Gst.parse_bin_from_description(pipeline, True)
def destroy(self):
JingleRTPContent.destroy(self)
self.pipeline.get_bus().disconnect_by_func(self._on_sync_message)
def get_content(desc):
if desc['media'] == 'audio':
return JingleAudio
......
......@@ -165,18 +165,18 @@ class Jingle(BaseModule):
jingle.start_session()
return jingle.sid
def start_video(self, jid, in_xid, out_xid):
def start_video(self, jid):
if self.get_jingle_session(jid, media='video'):
return self.get_jingle_session(jid, media='video').sid
jingle = self.get_jingle_session(jid, media='audio')
if jingle:
jingle.add_content('video', JingleVideo(jingle, in_xid=in_xid,
out_xid=out_xid))
video = JingleVideo(jingle)
jingle.add_content('video', video)
else:
jingle = JingleSession(self._con, weinitiate=True, jid=jid)
self._sessions[jingle.sid] = jingle
jingle.add_content('video', JingleVideo(jingle, in_xid=in_xid,
out_xid=out_xid))
video = JingleVideo(jingle)
jingle.add_content('video', video)
jingle.start_session()
return jingle.sid
......
......@@ -945,16 +945,13 @@ microphone-sensitivity-high-symbolic</property>
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<child>
<object class="GtkViewport" id="viewport1">
<object class="GtkViewport" id="outgoing_viewport">
<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>
</object>
<placeholder/>
</child>
</object>
</child>
......@@ -971,16 +968,11 @@ microphone-sensitivity-high-symbolic</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkViewport" id="viewport2">
<object class="GtkViewport" id="incoming_viewport">
<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>
</object>
<placeholder/>
</child>
</object>
<packing>
......
......@@ -29,7 +29,6 @@ from typing import Dict # pylint: disable=unused-import
from typing import List # pylint: disable=unused-import
from typing import Tuple # pylint: disable=unused-import
import os
import uuid
import logging
......@@ -54,6 +53,7 @@ from gajim.gtk.util import get_icon_name
from gajim.gtk.util import get_builder
from gajim.gtk.util import get_activity_icon_name
from gajim.gtk.util import get_app_window
from gajim.gtk import gstreamer
if app.is_installed('GSPELL'):
from gi.repository import Gspell # pylint: disable=ungrouped-imports
......@@ -1100,34 +1100,12 @@ class VoIPCallReceivedDialog:
fixed = ctrl.xml.get_object('outgoing_fixed')
fixed.set_no_show_all(False)
video_hbox.show_all()
ctrl.xml.get_object('incoming_drawingarea').realize()
if os.name == 'nt':
in_xid = ctrl.xml.get_object('incoming_drawingarea').\
get_window().handle
else:
in_xid = ctrl.xml.get_object('incoming_drawingarea').\
get_property('window').get_xid()
content = session.get_content('video')
# move outgoing stream to chat window
if app.config.get('video_see_self'):
ctrl.xml.get_object('outgoing_drawingarea').realize()
if os.name == 'nt':
out_xid = ctrl.xml.get_object('outgoing_drawingarea').\
get_window().handle
else:
out_xid = ctrl.xml.get_object('outgoing_drawingarea').\
get_property('window').get_xid()
b = content.src_bin
for e in b.children:
if not e.get_name().startswith('autovideosink'):
continue
for f in e.children:
if f.get_name().startswith('autovideosink'):
f.set_window_handle(out_xid)
content.out_xid = out_xid
break
break
content.in_xid = in_xid
sink_other, widget_other, _ = gstreamer.create_gtk_widget()
sink_self, widget_self, _ = gstreamer.create_gtk_widget()
ctrl.xml.incoming_viewport.add(widget_other)
ctrl.xml.outgoing_viewport.add(widget_self)
content.do_setup(sink_self, sink_other)
ctrl.set_video_state('connecting', self.sid)
# Now, accept the content/sessions.
# This should be done after the chat control is running
......
from typing import Tuple, Optional
from gi.repository import Gst
from gi.repository import Gtk
def create_gtk_widget() -> Tuple[Optional[Gst.Element], Optional[Gtk.Widget], Optional[str]]:
gtkglsink = Gst.ElementFactory.make('gtkglsink', None)
if gtkglsink is not None:
glsinkbin = Gst.ElementFactory.make('glsinkbin', None)
if glsinkbin is None:
return None, None, None
glsinkbin.set_property('sink', gtkglsink)
sink = glsinkbin
widget = gtkglsink.get_property('widget')
name = 'gtkglsink'
else:
sink = Gst.ElementFactory.make('gtksink', None)
if sink is None:
return None, None, None
widget = sink.get_property('widget')
name = 'gtksink'
widget.set_visible(True)
widget.set_property('expand', True)
return sink, widget, name
......@@ -38,6 +38,7 @@ from gajim.gtk.util import open_window
from gajim.gtk.dialogs import AspellDictError
from gajim.gtk.sounds import ManageSounds
from gajim.gtk.const import ControlType
from gajim.gtk import gstreamer
try:
from gajim.common.multimedia_helpers import AudioInputManager, AudioOutputManager
......@@ -384,7 +385,7 @@ class Preferences(Gtk.ApplicationWindow):
def on_av_map(tab):
label = self._ui.selected_video_output
sink, widget, name = self.setup_video_output()
sink, widget, name = gstreamer.create_gtk_widget()
if sink is None:
log.error('Failed to obtain a working Gstreamer GTK+ sink, '
'video support will be disabled')
......@@ -508,27 +509,6 @@ class Preferences(Gtk.ApplicationWindow):
if event.keyval == Gdk.KEY_Escape:
self.destroy()
@staticmethod
def setup_video_output():
gtkglsink = Gst.ElementFactory.make('gtkglsink', None)
if gtkglsink is not None:
glsinkbin = Gst.ElementFactory.make('glsinkbin', None)
if glsinkbin is None:
return None, None, None
glsinkbin.set_property('sink', gtkglsink)
sink = glsinkbin
widget = gtkglsink.get_property('widget')
name = 'gtkglsink'
else:
sink = Gst.ElementFactory.make('gtksink', None)
if sink is None:
return None, None, None
widget = sink.get_property('widget')
name = 'gtksink'
widget.set_visible(True)
widget.set_property('expand', True)
return sink, widget, name
def get_per_account_option(self, opt):
"""
Return the value of the option opt if it's the same in all accounts else
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment