Skip to content

Commit 13d2b50

Browse files
authored
Merge pull request #366 from justjanne/wayland-ui
Add Wayland support
2 parents 3b7e466 + 446fb5b commit 13d2b50

File tree

9 files changed

+90
-118
lines changed

9 files changed

+90
-118
lines changed

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ description = "voctomix is a video mixing software, written in python."
55
readme = "README.md"
66
requires-python = ">=3.11"
77
dependencies = [
8+
"numpy<2.0",
89
"scipy>=1.15.2",
910
"scipy-stubs>=1.15.2.1",
1011
"sdnotify>=0.3.2",

uv.lock

Lines changed: 20 additions & 42 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

voctogui/__main__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
gi.require_version('Gst', '1.0')
66
gi.require_version('GstVideo', '1.0')
77
gi.require_version('GstNet', '1.0')
8-
from gi.repository import Gtk, Gdk, Gst, GstVideo
8+
from gi.repository import Gtk, Gdk, Gst, GstVideo, GLib
99

1010
import signal
1111
import logging
@@ -30,6 +30,9 @@
3030
Gdk.init([]) # type: ignore
3131
Gtk.init([]) # type: ignore
3232

33+
# select window icon on wayland
34+
GLib.set_prgname("voctogui.desktop")
35+
3336
# select Awaita:Dark theme
3437
settings = Gtk.Settings.get_default()
3538
if settings is not None:

voctogui/lib/ui.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import logging
2+
from typing import cast
3+
24
from gi.repository import Gtk, Gdk
35

46
from voctogui.lib.config import Config
@@ -79,12 +81,14 @@ def setup(self):
7981
self.mix_audio_display = AudioDisplay(audio_box, "mix", uibuilder=self)
8082

8183
# Create Main-Video Display
84+
video_main = cast(Gtk.Box, self.find_widget_recursive(self.win, 'video_main'))
8285
self.mix_video_display = VideoDisplay(
83-
self.find_widget_recursive(self.win, 'video_main'),
8486
self.mix_audio_display,
8587
port=Port.MIX_PREVIEW if Config.getPreviewsEnabled() else Port.MIX_OUT,
8688
name="MIX"
8789
)
90+
video_main.pack_start(self.mix_video_display.widget, fill=True, expand=True, padding=0)
91+
self.mix_video_display.play()
8892

8993
for idx, livepreview in enumerate(Config.getLivePreviews()):
9094
if Config.getPreviewsEnabled():

voctogui/lib/videodisplay.py

Lines changed: 21 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import logging
22
import sys
3+
from typing import cast
34

4-
from gi.repository import Gst, Gdk
5+
from gi.repository import Gst, Gdk, Gtk
56

67
from voctogui.lib.args import Args
78
from voctogui.lib.config import Config
@@ -15,12 +16,14 @@
1516

1617
class VideoDisplay(object):
1718
"""Displays a Voctomix-Video-Stream into a GtkWidget"""
19+
imagesink: Gst.Element
20+
widget: Gtk.Widget
21+
pipeline: Gst.Pipeline
1822

19-
def __init__(self, video_drawing_area, audio_display, port, name, width=None, height=None,
23+
def __init__(self, audio_display, port, name, width=None, height=None,
2024
has_audio=True, play_audio=False):
2125
self.log = logging.getLogger('VideoDisplay:%s' % name)
2226
self.name = name
23-
self.video_drawing_area = video_drawing_area
2427
self.level_callback = None if audio_display is None else audio_display.callback
2528
video_decoder = None
2629

@@ -78,28 +81,16 @@ def __init__(self, video_drawing_area, audio_display, port, name, width=None, he
7881
# Video Display
7982
videosystem = Config.getVideoSystem()
8083
self.log.debug('Configuring for Video-System %s', videosystem)
84+
if videosystem == 'gtk':
85+
pipe += """ ! gtksink
86+
name=imagesink-{name} sync=false
87+
""".format(name=name)
8188

82-
if videosystem == 'gl':
89+
elif videosystem == 'gtkgl':
8390
pipe += """ ! glupload
84-
! glcolorconvert
85-
! glimagesinkelement
86-
name=imagesink-{name}
87-
""".format(name=name)
88-
89-
elif videosystem == 'xv':
90-
pipe += """ ! xvimagesink
91-
name=imagesink-{name}
92-
""".format(name=name)
93-
94-
elif videosystem == 'x':
95-
pipe += """ ! ximagesink
96-
name=imagesink-{name}
97-
""".format(name=name)
98-
99-
elif videosystem == 'vaapi':
100-
pipe += """ ! vaapisink
101-
name=imagesink-{name}
102-
""".format(name=name)
91+
! gtkglsink
92+
name=imagesink-{name} sync=false
93+
""".format(name=name)
10394

10495
else:
10596
raise Exception(
@@ -134,48 +125,27 @@ def __init__(self, video_drawing_area, audio_display, port, name, width=None, he
134125
self.log.info("Creating Display-Pipeline:\n%s", pretty(pipe))
135126
try:
136127
# launch gstreamer pipeline
137-
self.pipeline = Gst.parse_launch(pipe)
128+
self.pipeline = cast(Gst.Pipeline, Gst.parse_launch(pipe))
138129
self.log.info("pipeline launched successfuly")
139130
except:
140131
self.log.error("Can not launch pipeline")
141132
sys.exit(-1)
142133

134+
self.imagesink = cast(Gst.Element, self.pipeline.get_by_name('imagesink-{name}'.format(name=self.name)))
135+
self.widget = cast(Gtk.Widget, self.imagesink.get_property("widget"))
136+
143137
if Args.dot:
144138
self.log.debug("Generating DOT image of videodisplay pipeline")
145139
gst_generate_dot(self.pipeline, "gui.videodisplay.{}".format(name), Args.gst_debug_details)
146140

147-
self.pipeline.use_clock(Clock)
148-
149-
self.video_drawing_area.add_events(
150-
Gdk.EventMask.KEY_PRESS_MASK | Gdk.EventMask.KEY_RELEASE_MASK)
151-
self.video_drawing_area.connect("realize", self.on_realize)
141+
self.pipeline.set_clock(Clock)
152142
bus = self.pipeline.get_bus()
153143
bus.add_signal_watch()
154144
bus.enable_sync_message_emission()
155145

156146
bus.connect('message::error', self.on_error)
157-
bus.connect('sync-message::element', self.on_syncmsg)
158-
bus.connect('message::state-changed', self.on_state_changed)
159147
bus.connect("message::element", self.on_level)
160148

161-
def on_realize(self, win):
162-
self.imagesink = self.pipeline.get_by_name(
163-
'imagesink-{name}'.format(name=self.name))
164-
self.xid = self.video_drawing_area.get_property('window').get_xid()
165-
166-
self.log.debug('Realized Drawing-Area with xid %u', self.xid)
167-
self.video_drawing_area.realize()
168-
169-
self.log.info("Launching Display-Pipeline")
170-
self.pipeline.set_state(Gst.State.PLAYING)
171-
172-
def on_syncmsg(self, bus, msg):
173-
if type(msg) == Gst.Message and self.imagesink:
174-
if msg.get_structure().get_name() == "prepare-window-handle":
175-
self.log.info(
176-
'Setting imagesink window-handle to 0x%x', self.xid)
177-
self.imagesink.set_window_handle(self.xid)
178-
179149
def on_error(self, bus, message):
180150
(error, debug) = message.parse_error()
181151
self.log.error(
@@ -192,6 +162,5 @@ def on_level(self, bus, msg):
192162
decay = msg.get_structure().get_value('decay')
193163
self.level_callback(rms, peak, decay)
194164

195-
def on_state_changed(self, bus, message):
196-
if message.parse_state_changed().newstate == Gst.State.PLAYING:
197-
self.video_drawing_area.show()
165+
def play(self):
166+
self.pipeline.set_state(Gst.State.PLAYING)

voctogui/lib/videopreviewframe.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from gi.repository import Gtk
2+
from gi.repository.Gtk import Requisition
3+
4+
class VideoPreviewFrame(Gtk.Bin):
5+
"""
6+
Custom helper class to force a specific size for a child widget
7+
"""
8+
natural_width: int
9+
natural_height: int
10+
11+
def __init__(self, natural_width: int, natural_height: int):
12+
super().__init__()
13+
self.natural_width = natural_width
14+
self.natural_height = natural_height
15+
16+
def do_get_preferred_size(self) -> tuple[Requisition, Requisition]:
17+
natural_size = Requisition.new()
18+
natural_size.width = self.natural_width
19+
natural_size.height = self.natural_height
20+
return natural_size, natural_size
21+
22+
def do_get_preferred_height(self) -> tuple[int, int]:
23+
return self.natural_height, self.natural_height
24+
25+
def do_get_preferred_width(self) -> tuple[int, int]:
26+
return self.natural_width, self.natural_width

voctogui/lib/videopreviews.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
import math
55
import os
66
from configparser import NoOptionError
7+
from typing import cast
78

8-
from gi.repository import Gtk, Gdk, GObject
9+
from gi.repository import Gst, Gtk, Gdk, GObject
10+
from voctogui.lib.videopreviewframe import VideoPreviewFrame
911
from voctogui.lib.videodisplay import VideoDisplay
1012
from voctogui.lib.audioonlydisplay import AudioOnlyDisplay
1113
from voctogui.lib.audiodisplay import AudioDisplay
@@ -60,18 +62,16 @@ def addPreview(self, uibuilder, source, port, has_volume=True):
6062
if has_audio and Config.getAudioStreams().get_source_streams(source):
6163
mix_audio_display = AudioDisplay(self.audio_box, source, uibuilder, has_volume)
6264
if source in Config.getVideoSources(internal=True):
63-
video = uibuilder.load_check_widget('video',
64-
os.path.dirname(uibuilder.uifile) +
65-
"/widgetpreview.ui")
66-
video.set_size_request(*self.previewSize)
67-
self.video_box.pack_start(video, fill=False,
68-
expand=False, padding=0)
69-
70-
player = VideoDisplay(video, mix_audio_display, port=port,
65+
player = VideoDisplay(audio_display=mix_audio_display, port=port,
7166
width=self.previewSize[0],
7267
height=self.previewSize[1],
7368
name=source.upper(),
7469
has_audio=has_audio,
7570
)
71+
video = cast(Gtk.Widget, player.widget)
72+
frame = VideoPreviewFrame(*self.previewSize)
73+
frame.add(video)
74+
self.video_box.pack_start(frame, False, False, 0)
75+
player.play()
7676
elif has_audio and Config.getAudioStreams().get_source_streams(source):
7777
player = AudioOnlyDisplay(mix_audio_display, port=port, name=source.upper())

voctogui/ui/voctogui.ui

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,13 +113,16 @@
113113
<property name="shadow-type">none</property>
114114
<property name="obey-child">False</property>
115115
<child>
116-
<object class="GtkDrawingArea" id="video_main">
116+
<object class="GtkBox" id="video_main">
117117
<property name="width-request">100</property>
118118
<property name="visible">True</property>
119119
<property name="can-focus">False</property>
120120
<property name="double-buffered">False</property>
121121
<property name="hexpand">True</property>
122122
<property name="vexpand">True</property>
123+
<child>
124+
<placeholder/>
125+
</child>
123126
</object>
124127
</child>
125128
</object>

voctogui/ui/widgetpreview.ui

Lines changed: 0 additions & 12 deletions
This file was deleted.

0 commit comments

Comments
 (0)