aboutsummaryrefslogtreecommitdiff
path: root/voctogui/lib
diff options
context:
space:
mode:
Diffstat (limited to 'voctogui/lib')
-rw-r--r--voctogui/lib/args.py17
-rw-r--r--voctogui/lib/audioleveldisplay.py242
-rw-r--r--voctogui/lib/audioselector.py100
-rw-r--r--voctogui/lib/clock.py15
-rw-r--r--voctogui/lib/config.py28
-rw-r--r--voctogui/lib/connection.py187
-rw-r--r--voctogui/lib/loghandler.py86
-rw-r--r--voctogui/lib/toolbar/composition.py104
-rw-r--r--voctogui/lib/toolbar/misc.py41
-rw-r--r--voctogui/lib/toolbar/streamblank.py120
-rw-r--r--voctogui/lib/ui.py136
-rw-r--r--voctogui/lib/uibuilder.py96
-rw-r--r--voctogui/lib/videodisplay.py296
-rw-r--r--voctogui/lib/videopreviews.py195
-rw-r--r--voctogui/lib/warningoverlay.py85
15 files changed, 915 insertions, 833 deletions
diff --git a/voctogui/lib/args.py b/voctogui/lib/args.py
index 83cd40d..f50126f 100644
--- a/voctogui/lib/args.py
+++ b/voctogui/lib/args.py
@@ -4,21 +4,24 @@ __all__ = ['Args']
parser = argparse.ArgumentParser(description='Voctogui')
parser.add_argument('-v', '--verbose', action='count', default=0,
- help="Also print INFO and DEBUG messages.")
+ help="Also print INFO and DEBUG messages.")
-parser.add_argument('-c', '--color', action='store', choices=['auto', 'always', 'never'], default='auto',
- help="Control the use of colors in the Log-Output")
+parser.add_argument('-c', '--color',
+ action='store',
+ choices=['auto', 'always', 'never'],
+ default='auto',
+ help="Control the use of colors in the Log-Output")
parser.add_argument('-t', '--timestamp', action='store_true',
- help="Enable timestamps in the Log-Output")
+ help="Enable timestamps in the Log-Output")
parser.add_argument('-i', '--ini-file', action='store',
- help="Load a custom config.ini-File")
+ help="Load a custom config.ini-File")
parser.add_argument('-u', '--ui-file', action='store',
- help="Load a custom .ui-File")
+ help="Load a custom .ui-File")
parser.add_argument('-H', '--host', action='store',
- help="Connect to this host instead of the configured one.")
+ help="Connect to this host instead of the configured one.")
Args = parser.parse_args()
diff --git a/voctogui/lib/audioleveldisplay.py b/voctogui/lib/audioleveldisplay.py
index 1a4be14..bd8fb21 100644
--- a/voctogui/lib/audioleveldisplay.py
+++ b/voctogui/lib/audioleveldisplay.py
@@ -1,121 +1,127 @@
-import logging, math
+import logging
+import math
from gi.repository import Gst, Gtk
+
class AudioLevelDisplay(object):
- """ Displays a Level-Meter of another VideoDisplay into a GtkWidget """
-
- def __init__(self, drawing_area):
- self.log = logging.getLogger('AudioLevelDisplay[%s]' % drawing_area.get_name())
-
- self.drawing_area = drawing_area
-
- self.levelrms = []
- self.levelpeak = []
- self.leveldecay = []
-
- # register on_draw handler
- self.drawing_area.connect('draw', self.on_draw)
-
- def on_draw(self, widget, cr):
- # number of audio-channels
- channels = len(self.levelrms)
-
- if channels == 0:
- return
-
- width = self.drawing_area.get_allocated_width()
- height = self.drawing_area.get_allocated_height()
-
- # space between the channels in px
- margin = 2
-
- # 1 channel -> 0 margins, 2 channels -> 1 margin, 3 channels…
- channel_width = int( (width - (margin * (channels - 1))) / channels )
-
- # self.log.debug(
- # 'width: %upx filled with %u channels of each %upx '
- # 'and %ux margin of %upx',
- # width, channels, channel_width, channels-1, margin)
-
- # normalize db-value to 0…1 and multiply with the height
- rms_px = [ self.normalize_db(db) * height for db in self.levelrms ]
- peak_px = [ self.normalize_db(db) * height for db in self.levelpeak ]
- decay_px = [ self.normalize_db(db) * height for db in self.leveldecay ]
-
- # set the line-width >1, to get a nice overlap
- cr.set_line_width(2)
-
- # iterate over all pixels
- for y in range(0, height):
-
- # calculate our place in the color-gradient, clamp to 0…1
- # 0 -> green, 0.5 -> yellow, 1 -> red
- color = self.clamp(((y / height) - 0.6) / 0.42)
-
- for channel in range(0, channels):
- # start-coordinate for this channel
- x = (channel * channel_width) + (channel * margin)
-
- # calculate the brightness based on whether this line is in the
- # active region
-
- # default to 0.25, dark
- bright = 0.25
- if int(y - decay_px[channel]) in range(0, 2):
- # decay marker, 2px wide, extra bright
- bright = 1.5
- elif y < rms_px[channel]:
- # rms bar, full bright
- bright = 1
- elif y < peak_px[channel]:
- # peak bar, a little darker
- bright = 0.75
-
- # set the color with a little reduced green
- cr.set_source_rgb(
- color * bright,
- (1-color) * bright * 0.75,
- 0
- )
-
- # draw the marker
- cr.move_to(x, height-y)
- cr.line_to(x + channel_width, height-y)
- cr.stroke()
-
- # draw a black line for the margin
- cr.set_source_rgb(0,0,0)
- cr.move_to(x + channel_width, height-y)
- cr.line_to(x + channel_width + margin, height-y)
- cr.stroke()
-
- # draw db text-markers
- cr.set_source_rgb(1, 1, 1)
- for db in [-40, -20, -10, -5, -4, -3, -2, -1]:
- text = str(db)
- xbearing, ybearing, textwidth, textheight, xadvance, yadvance = (
- cr.text_extents(text))
-
- y = self.normalize_db(db) * height
- cr.move_to((width-textwidth) / 2, height - y - textheight)
- cr.show_text(text)
-
- return True
-
- def normalize_db(self, db):
- # -60db -> 1.00 (very quiet)
- # -30db -> 0.75
- # -15db -> 0.50
- # -5db -> 0.25
- # -0db -> 0.00 (very loud)
- logscale = 1 - math.log10(-0.15 * db + 1)
- return self.clamp(logscale)
-
- def clamp(self, value, min_value=0, max_value=1):
- return max(min(value, max_value), min_value)
-
- def level_callback(self, rms, peak, decay):
- self.levelrms = rms
- self.levelpeak = peak
- self.leveldecay = decay
- self.drawing_area.queue_draw()
+ """Displays a Level-Meter of another VideoDisplay into a GtkWidget"""
+
+ def __init__(self, drawing_area):
+ self.log = logging.getLogger(
+ 'AudioLevelDisplay[{}]'.format(drawing_area.get_name())
+ )
+
+ self.drawing_area = drawing_area
+
+ self.levelrms = []
+ self.levelpeak = []
+ self.leveldecay = []
+
+ # register on_draw handler
+ self.drawing_area.connect('draw', self.on_draw)
+
+ def on_draw(self, widget, cr):
+ # number of audio-channels
+ channels = len(self.levelrms)
+
+ if channels == 0:
+ return
+
+ width = self.drawing_area.get_allocated_width()
+ height = self.drawing_area.get_allocated_height()
+
+ # space between the channels in px
+ margin = 2
+
+ # 1 channel -> 0 margins, 2 channels -> 1 margin, 3 channels…
+ channel_width = int((width - (margin * (channels - 1))) / channels)
+
+ # self.log.debug(
+ # 'width: %upx filled with %u channels of each %upx '
+ # 'and %ux margin of %upx',
+ # width, channels, channel_width, channels - 1, margin
+ # )
+
+ # normalize db-value to 0…1 and multiply with the height
+ rms_px = [self.normalize_db(db) * height for db in self.levelrms]
+ peak_px = [self.normalize_db(db) * height for db in self.levelpeak]
+ decay_px = [self.normalize_db(db) * height for db in self.leveldecay]
+
+ # set the line-width >1, to get a nice overlap
+ cr.set_line_width(2)
+
+ # iterate over all pixels
+ for y in range(0, height):
+
+ # calculate our place in the color-gradient, clamp to 0…1
+ # 0 -> green, 0.5 -> yellow, 1 -> red
+ color = self.clamp(((y / height) - 0.6) / 0.42)
+
+ for channel in range(0, channels):
+ # start-coordinate for this channel
+ x = (channel * channel_width) + (channel * margin)
+
+ # calculate the brightness based on whether this line is in the
+ # active region
+
+ # default to 0.25, dark
+ bright = 0.25
+ if int(y - decay_px[channel]) in range(0, 2):
+ # decay marker, 2px wide, extra bright
+ bright = 1.5
+ elif y < rms_px[channel]:
+ # rms bar, full bright
+ bright = 1
+ elif y < peak_px[channel]:
+ # peak bar, a little darker
+ bright = 0.75
+
+ # set the color with a little reduced green
+ cr.set_source_rgb(
+ color * bright,
+ (1 - color) * bright * 0.75,
+ 0
+ )
+
+ # draw the marker
+ cr.move_to(x, height - y)
+ cr.line_to(x + channel_width, height - y)
+ cr.stroke()
+
+ # draw a black line for the margin
+ cr.set_source_rgb(0, 0, 0)
+ cr.move_to(x + channel_width, height - y)
+ cr.line_to(x + channel_width + margin, height - y)
+ cr.stroke()
+
+ # draw db text-markers
+ cr.set_source_rgb(1, 1, 1)
+ for db in [-40, -20, -10, -5, -4, -3, -2, -1]:
+ text = str(db)
+ (xbearing, ybearing,
+ textwidth, textheight,
+ xadvance, yadvance) = cr.text_extents(text)
+
+ y = self.normalize_db(db) * height
+ cr.move_to((width - textwidth) / 2, height - y - textheight)
+ cr.show_text(text)
+
+ return True
+
+ def normalize_db(self, db):
+ # -60db -> 1.00 (very quiet)
+ # -30db -> 0.75
+ # -15db -> 0.50
+ # -5db -> 0.25
+ # -0db -> 0.00 (very loud)
+ logscale = 1 - math.log10(-0.15 * db + 1)
+ return self.clamp(logscale)
+
+ def clamp(self, value, min_value=0, max_value=1):
+ return max(min(value, max_value), min_value)
+
+ def level_callback(self, rms, peak, decay):
+ self.levelrms = rms
+ self.levelpeak = peak
+ self.leveldecay = decay
+ self.drawing_area.queue_draw()
diff --git a/voctogui/lib/audioselector.py b/voctogui/lib/audioselector.py
index 4f8f9ec..c4be4d3 100644
--- a/voctogui/lib/audioselector.py
+++ b/voctogui/lib/audioselector.py
@@ -4,70 +4,72 @@ from gi.repository import Gst, Gdk, GLib
from lib.config import Config
import lib.connection as Connection
+
class AudioSelectorController(object):
- """ Displays a Level-Meter of another VideoDisplay into a GtkWidget """
+ """Displays a Level-Meter of another VideoDisplay into a GtkWidget"""
- def __init__(self, drawing_area, win, uibuilder):
- self.log = logging.getLogger('AudioSelectorController')
+ def __init__(self, drawing_area, win, uibuilder):
+ self.log = logging.getLogger('AudioSelectorController')
- self.drawing_area = drawing_area
- self.win = win
+ self.drawing_area = drawing_area
+ self.win = win
- combo = uibuilder.find_widget_recursive(win, 'combo_audio')
- combo.connect('changed', self.on_changed)
- #combo.set_sensitive(True)
- self.combo = combo
+ combo = uibuilder.find_widget_recursive(win, 'combo_audio')
+ combo.connect('changed', self.on_changed)
+ # combo.set_sensitive(True)
+ self.combo = combo
- eventbox = uibuilder.find_widget_recursive(win, 'combo_audio_events')
- eventbox.connect('button_press_event', self.on_button_press_event)
- eventbox.set_property('above_child', True)
- self.eventbox = eventbox
+ eventbox = uibuilder.find_widget_recursive(win, 'combo_audio_events')
+ eventbox.connect('button_press_event', self.on_button_press_event)
+ eventbox.set_property('above_child', True)
+ self.eventbox = eventbox
- combo.remove_all()
- for name in Config.getlist('mix', 'sources'):
- combo.append(name, name)
+ combo.remove_all()
+ for name in Config.getlist('mix', 'sources'):
+ combo.append(name, name)
- # connect event-handler and request initial state
- Connection.on('audio_status', self.on_audio_status)
- Connection.send('get_audio')
+ # connect event-handler and request initial state
+ Connection.on('audio_status', self.on_audio_status)
+ Connection.send('get_audio')
- self.timer_iteration = 0
+ self.timer_iteration = 0
- def on_audio_status(self, source):
- self.log.info('on_audio_status callback w/ source: %s', source)
- self.combo.set_active_id(source)
+ def on_audio_status(self, source):
+ self.log.info('on_audio_status callback w/ source: %s', source)
+ self.combo.set_active_id(source)
- def on_button_press_event(self, combo, event):
- if event.type != Gdk.EventType.DOUBLE_BUTTON_PRESS:
- return
+ def on_button_press_event(self, combo, event):
+ if event.type != Gdk.EventType.DOUBLE_BUTTON_PRESS:
+ return
- self.log.debug('double-clicked, unlocking')
- self.set_enabled(True)
- GLib.timeout_add_seconds(5, self.on_disabled_timer, self.timer_iteration)
+ self.log.debug('double-clicked, unlocking')
+ self.set_enabled(True)
+ GLib.timeout_add_seconds(5, self.on_disabled_timer,
+ self.timer_iteration)
- def on_disabled_timer(self, timer_iteration):
- if timer_iteration != self.timer_iteration:
- self.log.debug('lock-timer fired late, ignoring')
- return
+ def on_disabled_timer(self, timer_iteration):
+ if timer_iteration != self.timer_iteration:
+ self.log.debug('lock-timer fired late, ignoring')
+ return
- self.log.debug('lock-timer fired, locking')
- self.set_enabled(False)
- return False
+ self.log.debug('lock-timer fired, locking')
+ self.set_enabled(False)
+ return False
- def set_enabled(self, enable):
- self.combo.set_sensitive(enable)
- self.eventbox.set_property('above_child', not enable)
+ def set_enabled(self, enable):
+ self.combo.set_sensitive(enable)
+ self.eventbox.set_property('above_child', not enable)
- def is_enabled(self):
- return self.combo.get_sensitive()
+ def is_enabled(self):
+ return self.combo.get_sensitive()
- def on_changed(self, combo):
- if not self.is_enabled():
- return
+ def on_changed(self, combo):
+ if not self.is_enabled():
+ return
- self.timer_iteration += 1
+ self.timer_iteration += 1
- value = combo.get_active_text()
- self.log.info('changed to %s', value)
- self.set_enabled(False)
- Connection.send('set_audio', value)
+ value = combo.get_active_text()
+ self.log.info('changed to %s', value)
+ self.set_enabled(False)
+ Connection.send('set_audio', value)
diff --git a/voctogui/lib/clock.py b/voctogui/lib/clock.py
index 9075bdc..1a977ce 100644
--- a/voctogui/lib/clock.py
+++ b/voctogui/lib/clock.py
@@ -8,13 +8,14 @@ port = 9998
log = logging.getLogger('Clock')
Clock = None
+
def obtainClock(host):
- global log, Clock, SystemClock
+ global log, Clock, SystemClock
- log.debug('obtaining NetClientClock from host %s', host)
- Clock = GstNet.NetClientClock.new('voctocore', host, port, 0)
- log.debug('obtained NetClientClock from host %s: %s', host, Clock)
+ log.debug('obtaining NetClientClock from host %s', host)
+ Clock = GstNet.NetClientClock.new('voctocore', host, port, 0)
+ log.debug('obtained NetClientClock from host %s: %s', host, Clock)
- log.debug('waiting for NetClientClock to sync to host')
- Clock.wait_for_sync(Gst.CLOCK_TIME_NONE)
- log.info('successfully synced NetClientClock to host')
+ log.debug('waiting for NetClientClock to sync to host')
+ Clock.wait_for_sync(Gst.CLOCK_TIME_NONE)
+ log.info('successfully synced NetClientClock to host')
diff --git a/voctogui/lib/config.py b/voctogui/lib/config.py
index d4630bf..dc7c561 100644
--- a/voctogui/lib/config.py
+++ b/voctogui/lib/config.py
@@ -7,31 +7,35 @@ import lib.connection as Connection
__all__ = ['Config']
+
def getlist(self, section, option):
- return [x.strip() for x in self.get(section, option).split(',')]
+ return [x.strip() for x in self.get(section, option).split(',')]
+
def fetchServerConfig(self):
- log = logging.getLogger('Config')
- log.info("reading server-config")
+ log = logging.getLogger('Config')
+ log.info("reading server-config")
- server_config = Connection.fetchServerConfig()
+ server_config = Connection.fetchServerConfig()
- log.info("merging server-config %s", server_config)
- self.read_dict(server_config)
+ log.info("merging server-config %s", server_config)
+ self.read_dict(server_config)
SafeConfigParser.getlist = getlist
SafeConfigParser.fetchServerConfig = fetchServerConfig
files = [
- os.path.join(os.path.dirname(os.path.realpath(__file__)), '../default-config.ini'),
- os.path.join(os.path.dirname(os.path.realpath(__file__)), '../config.ini'),
- '/etc/voctomix/voctogui.ini',
- '/etc/voctogui.ini',
- os.path.expanduser('~/.voctogui.ini'),
+ os.path.join(os.path.dirname(os.path.realpath(__file__)),
+ '../default-config.ini'),
+ os.path.join(os.path.dirname(os.path.realpath(__file__)),
+ '../config.ini'),
+ '/etc/voctomix/voctogui.ini',
+ '/etc/voctogui.ini',
+ os.path.expanduser('~/.voctogui.ini'),
]
if Args.ini_file is not None:
- files.append(Args.ini_file)
+ files.append(Args.ini_file)
Config = SafeConfigParser()
Config.read(files)
diff --git a/voctogui/lib/connection.py b/voctogui/lib/connection.py
index 6f8245f..500973d 100644
--- a/voctogui/lib/connection.py
+++ b/voctogui/lib/connection.py
@@ -12,140 +12,147 @@ port = 9999
command_queue = Queue()
signal_handlers = {}
+
def establish(host):
- global conn, port, log, ip
+ global conn, port, log, ip
+
+ log.info('establishing Connection to %s', host)
+ conn = socket.create_connection((host, port))
+ log.debug('Connection successful \o/')
- log.info('establishing Connection to %s', host)
- conn = socket.create_connection( (host, port) )
- log.debug('Connection successful \o/')
+ ip = conn.getpeername()[0]
+ log.debug('Remote-IP is %s', ip)
- ip = conn.getpeername()[0]
- log.debug('Remote-IP is %s', ip)
def fetchServerConfig():
- global conn, log
+ global conn, log
- log.info('reading server-config')
- fd = conn.makefile('rw')
- fd.write("get_config\n")
- fd.flush()
+ log.info('reading server-config')
+ fd = conn.makefile('rw')
+ fd.write("get_config\n")
+ fd.flush()
- while True:
- line = fd.readline()
- words = line.split(' ')
+ while True:
+ line = fd.readline()
+ words = line.split(' ')
- signal = words[0]
- args = words[1:]
+ signal = words[0]
+ args = words[1:]
- if signal != 'server_config':
- continue
+ if signal != 'server_config':
+ continue
- server_config_json = " ".join(args)
- server_config = json.loads(server_config_json)
- return server_config
+ server_config_json = " ".join(args)
+ server_config = json.loads(server_config_json)
+ return server_config
def enterNonblockingMode():
- global conn, log
+ global conn, log
+
+ log.debug('entering nonblocking-mode')
+ conn.setblocking(False)
+ GObject.io_add_watch(conn, GObject.IO_IN, on_data, [''])
- log.debug('entering nonblocking-mode')
- conn.setblocking(False)
- GObject.io_add_watch(conn, GObject.IO_IN, on_data, [''])
def on_data(conn, _, leftovers, *args):
- global log
+ global log
+
+ '''Asynchronous connection handler. Pushes data from socket
+ into command queue linewise'''
+ try:
+ while True:
+ try:
+ leftovers.append(conn.recv(4096).decode(errors='replace'))
+ if len(leftovers[-1]) == 0:
+ log.info("Socket was closed")
- '''Asynchronous connection handler. Pushes data from socket
- into command queue linewise'''
- try:
- while True:
- try:
- leftovers.append(conn.recv(4096).decode(errors='replace'))
- if len(leftovers[-1]) == 0:
- log.info("Socket was closed")
+ # FIXME try to reconnect
+ conn.close()
+ Gtk.main_quit()
+ return False
- # FIXME try to reconnect
- conn.close()
- Gtk.main_quit()
- return False
+ except UnicodeDecodeError as e:
+ continue
+ except:
+ pass
- except UnicodeDecodeError as e:
- continue
- except:
- pass
+ data = "".join(leftovers)
+ del leftovers[:]
- data = "".join(leftovers)
- del leftovers[:]
+ lines = data.split('\n')
+ for line in lines[:-1]:
+ log.debug("got line: %r", line)
- lines = data.split('\n')
- for line in lines[:-1]:
- log.debug("got line: %r", line)
+ line = line.strip()
+ log.debug('re-starting on_loop scheduling')
+ GObject.idle_add(on_loop)
- line = line.strip()
- log.debug('re-starting on_loop scheduling')
- GObject.idle_add(on_loop)
+ command_queue.put((line, conn))
- command_queue.put((line, conn))
+ if lines[-1] != '':
+ log.debug("remaining %r", lines[-1])
- if lines[-1] != '':
- log.debug("remaining %r", lines[-1])
+ leftovers.append(lines[-1])
+ return True
- leftovers.append(lines[-1])
- return True
def on_loop():
- '''Command handler. Processes commands in the command queue whenever
- nothing else is happening (registered as GObject idle callback)'''
+ '''Command handler. Processes commands in the command queue whenever
+ nothing else is happening (registered as GObject idle callback)'''
- global command_queue
+ global command_queue
- log.debug('on_loop called')
+ log.debug('on_loop called')
- if command_queue.empty():
- log.debug('command_queue is empty again, stopping on_loop scheduling')
- return False
+ if command_queue.empty():
+ log.debug('command_queue is empty again, stopping on_loop scheduling')
+ return False
- line, requestor = command_queue.get()
+ line, requestor = command_queue.get()
- words = line.split()
- if len(words) < 1:
- log.debug('command_queue is empty again, stopping on_loop scheduling')
- return True
+ words = line.split()
+ if len(words) < 1:
+ log.debug('command_queue is empty again, stopping on_loop scheduling')
+ return True
- signal = words[0]
- args = words[1:]
+ signal = words[0]
+ args = words[1:]
- log.info('received signal %s, dispatching', signal)
- if signal not in signal_handlers:
- return True
+ log.info('received signal %s, dispatching', signal)
+ if signal not in signal_handlers:
+ return True
- for handler in signal_handlers[signal]:
- cb = handler['cb']
- if 'one' in handler and handler['one']:
- log.debug('removing one-time handler')
- del signal_handlers[signal]
+ for handler in signal_handlers[signal]:
+ cb = handler['cb']
+ if 'one' in handler and handler['one']:
+ log.debug('removing one-time handler')
+ del signal_handlers[signal]
- cb(*args)
+ cb(*args)
+
+ return True
- return True
def send(command, *args):
- global conn, log
- if len(args) > 0:
- command += ' '+(' '.join(args))
+ global conn, log
+ if len(args) > 0:
+ command += ' ' + (' '.join(args))
+
+ command += '\n'
- command += '\n'
+ conn.send(command.encode('ascii'))
- conn.send(command.encode('ascii'))
def on(signal, cb):
- if signal not in signal_handlers:
- signal_handlers[signal] = []
+ if signal not in signal_handlers:
+ signal_handlers[signal] = []
+
+ signal_handlers[signal].append({'cb': cb})
- signal_handlers[signal].append({'cb': cb})
def one(signal, cb):
- if signal not in signal_handlers:
- signal_handlers[signal] = []
+ if signal not in signal_handlers:
+ signal_handlers[signal] = []
- signal_handlers[signal].append({'cb': cb, 'one': True})
+ signal_handlers[signal].append({'cb': cb, 'one': True})
diff --git a/voctogui/lib/loghandler.py b/voctogui/lib/loghandler.py
index 2cc7ceb..6efb890 100644
--- a/voctogui/lib/loghandler.py
+++ b/voctogui/lib/loghandler.py
@@ -1,41 +1,57 @@
-import logging, time
+import logging
+import time
-class LogFormatter(logging.Formatter):
- def __init__(self, docolor, timestamps=False):
- super().__init__()
- self.docolor = docolor
- self.timestamps = timestamps
-
- def formatMessage(self, record):
- if self.docolor:
- c_lvl = 33
- c_mod = 32
- c_msg = 0
-
- if record.levelno == logging.WARNING:
- c_lvl = 31
- #c_mod = 33
- c_msg = 33
-
- elif record.levelno > logging.WARNING:
- c_lvl = 31
- c_mod = 31
- c_msg = 31
- fmt = '\x1b['+str(c_lvl)+'m%(levelname)8s\x1b[0m \x1b['+str(c_mod)+'m%(name)s\x1b['+str(c_msg)+'m: %(message)s\x1b[0m'
- else:
- fmt = '%(levelname)8s %(name)s: %(message)s'
-
- if self.timestamps:
- fmt = '%(asctime)s '+fmt
-
- if not 'asctime' in record.__dict__:
- record.__dict__['asctime']=time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(record.__dict__['created']))
+class LogFormatter(logging.Formatter):
- return fmt % record.__dict__
+ def __init__(self, docolor, timestamps=False):
+ super().__init__()
+ self.docolor = docolor
+ self.timestamps = timestamps
+
+ def formatMessage(self, record):
+ if self.docolor:
+ c_lvl = 33
+ c_mod = 32
+ c_msg = 0
+
+ if record.levelno == logging.WARNING:
+ c_lvl = 31
+ # c_mod = 33
+ c_msg = 33
+
+ elif record.levelno > logging.WARNING:
+ c_lvl = 31
+ c_mod = 31
+ c_msg = 31
+
+ fmt = ''.join([
+ '\x1b[%dm' % c_lvl, # set levelname color
+ '%(levelname)8s', # print levelname
+ '\x1b[0m', # reset formatting
+ '\x1b[%dm' % c_mod, # set name color
+ ' %(name)s', # print name
+ '\x1b[%dm' % c_msg, # set message color
+ ': %(message)s', # print message
+ '\x1b[0m' # reset formatting
+ ])
+ else:
+ fmt = '%(levelname)8s %(name)s: %(message)s'
+
+ if self.timestamps:
+ fmt = '%(asctime)s ' + fmt
+
+ if 'asctime' not in record.__dict__:
+ record.__dict__['asctime'] = time.strftime(
+ "%Y-%m-%d %H:%M:%S",
+ time.localtime(record.__dict__['created'])
+ )
+
+ return fmt % record.__dict__
class LogHandler(logging.StreamHandler):
- def __init__(self, docolor, timestamps):
- super().__init__()
- self.setFormatter(LogFormatter(docolor,timestamps))
+
+ def __init__(self, docolor, timestamps):
+ super().__init__()
+ self.setFormatter(LogFormatter(docolor, timestamps))
diff --git a/voctogui/lib/toolbar/composition.py b/voctogui/lib/toolbar/composition.py
index a2b1f29..2674260 100644
--- a/voctogui/lib/toolbar/composition.py
+++ b/voctogui/lib/toolbar/composition.py
@@ -3,54 +3,58 @@ from gi.repository import Gtk
import lib.connection as Connection
-class CompositionToolbarController(object):
- """ Manages Accelerators and Clicks on the Composition Toolbar-Buttons """
-
- def __init__(self, drawing_area, win, uibuilder):
- self.log = logging.getLogger('CompositionToolbarController')
-
- accelerators = Gtk.AccelGroup()
- win.add_accel_group(accelerators)
-
- composites = [
- 'fullscreen',
- 'picture_in_picture',
- 'side_by_side_equal',
- 'side_by_side_preview'
- ]
-
- self.composite_btns = {}
- self.current_composition = None
-
- for idx, name in enumerate(composites):
- key, mod = Gtk.accelerator_parse('F%u' % (idx+1))
- btn = uibuilder.find_widget_recursive(drawing_area, 'composite-'+name.replace('_', '-'))
- btn.set_name(name)
-
- # Thanks to http://stackoverflow.com/a/19739855/1659732
- btn.get_child().add_accelerator('clicked', accelerators, key, mod, Gtk.AccelFlags.VISIBLE)
- btn.connect('toggled', self.on_btn_toggled)
- self.composite_btns[name] = btn
-
- # connect event-handler and request initial state
- Connection.on('composite_mode', self.on_composite_mode)
- Connection.send('get_composite_mode')
-
-
- def on_btn_toggled(self, btn):
- if not btn.get_active():
- return
-
- btn_name = btn.get_name()
- if self.current_composition == btn_name:
- self.log.info('composition-mode already active: %s', btn_name)
- return
-
- self.log.info('composition-mode activated: %s', btn_name)
- Connection.send('set_composite_mode', btn_name)
-
- def on_composite_mode(self, mode):
- self.log.info('on_composite_mode callback w/ mode %s', mode)
- self.current_composition = mode
- self.composite_btns[mode].set_active(True)
+class CompositionToolbarController(object):
+ """Manages Accelerators and Clicks on the Composition Toolbar-Buttons"""
+
+ def __init__(self, drawing_area, win, uibuilder):
+ self.log = logging.getLogger('CompositionToolbarController')
+
+ accelerators = Gtk.AccelGroup()
+ win.add_accel_group(accelerators)
+
+ composites = [
+ 'fullscreen',
+ 'picture_in_picture',
+ 'side_by_side_equal',
+ 'side_by_side_preview'
+ ]
+
+ self.composite_btns = {}
+ self.current_composition = None
+
+ for idx, name in enumerate(composites):
+ key, mod = Gtk.accelerator_parse('F%u' % (idx + 1))
+ btn = uibuilder.find_widget_recursive(
+ drawing_area,
+ 'composite-' + name.replace('_', '-')
+ )
+ btn.set_name(name)
+
+ # Thanks to http://stackoverflow.com/a/19739855/1659732
+ btn.get_child().add_accelerator('clicked', accelerators,
+ key, mod, Gtk.AccelFlags.VISIBLE)
+ btn.connect('toggled', self.on_btn_toggled)
+
+ self.composite_btns[name] = btn
+
+ # connect event-handler and request initial state
+ Connection.on('composite_mode', self.on_composite_mode)
+ Connection.send('get_composite_mode')
+
+ def on_btn_toggled(self, btn):
+ if not btn.get_active():
+ return
+
+ btn_name = btn.get_name()
+ if self.current_composition == btn_name:
+ self.log.info('composition-mode already active: %s', btn_name)
+ return
+
+ self.log.info('composition-mode activated: %s', btn_name)
+ Connection.send('set_composite_mode', btn_name)
+
+ def on_composite_mode(self, mode):
+ self.log.info('on_composite_mode callback w/ mode %s', mode)
+ self.current_composition = mode
+ self.composite_btns[mode].set_active(True)
diff --git a/voctogui/lib/toolbar/misc.py b/voctogui/lib/toolbar/misc.py
index 9528b67..530bbad 100644
--- a/voctogui/lib/toolbar/misc.py
+++ b/voctogui/lib/toolbar/misc.py
@@ -6,30 +6,31 @@ import lib.connection as Connection
class MiscToolbarController(object):
- """ Manages Accelerators and Clicks Misc buttons """
+ """Manages Accelerators and Clicks Misc buttons"""
- def __init__(self, drawing_area, win, uibuilder):
- self.log = logging.getLogger('MiscToolbarController')
+ def __init__(self, drawing_area, win, uibuilder):
+ self.log = logging.getLogger('MiscToolbarController')
- # Accelerators
- accelerators = Gtk.AccelGroup()
- win.add_accel_group(accelerators)
+ # Accelerators
+ accelerators = Gtk.AccelGroup()
+ win.add_accel_group(accelerators)
- closebtn = uibuilder.find_widget_recursive(drawing_area, 'close')
- closebtn.set_visible( Config.getboolean('misc', 'close') )
- closebtn.connect('clicked', self.on_closebtn_clicked)
+ closebtn = uibuilder.find_widget_recursive(drawing_area, 'close')
+ closebtn.set_visible(Config.getboolean('misc', 'close'))
+ closebtn.connect('clicked', self.on_closebtn_clicked)
- cutbtn = uibuilder.find_widget_recursive(drawing_area, 'cut')
- cutbtn.set_visible( Config.getboolean('misc', 'cut') )
- cutbtn.connect('clicked', self.on_cutbtn_clicked)
+ cutbtn = uibuilder.find_widget_recursive(drawing_area, 'cut')
+ cutbtn.set_visible(Config.getboolean('misc', 'cut'))
+ cutbtn.connect('clicked', self.on_cutbtn_clicked)
- key, mod = Gtk.accelerator_parse('t')
- cutbtn.add_accelerator('clicked', accelerators, key, mod, Gtk.AccelFlags.VISIBLE)
+ key, mod = Gtk.accelerator_parse('t')
+ cutbtn.add_accelerator('clicked', accelerators,
+ key, mod, Gtk.AccelFlags.VISIBLE)
- def on_closebtn_clicked(self, btn):
- self.log.info('close-button clicked')
- Gtk.main_quit()
+ def on_closebtn_clicked(self, btn):
+ self.log.info('close-button clicked')
+ Gtk.main_quit()
- def on_cutbtn_clicked(self, btn):
- self.log.info('cut-button clicked')
- Connection.send('message', 'cut')
+ def on_cutbtn_clicked(self, btn):
+ self.log.info('cut-button clicked')
+ Connection.send('message', 'cut')
diff --git a/voctogui/lib/toolbar/streamblank.py b/voctogui/lib/toolbar/streamblank.py
index 717fadd..e627237 100644
--- a/voctogui/lib/toolbar/streamblank.py
+++ b/voctogui/lib/toolbar/streamblank.py
@@ -4,82 +4,88 @@ from gi.repository import Gtk
from lib.config import Config
import lib.connection as Connection
+
class StreamblankToolbarController(object):
- """ Manages Accelerators and Clicks on the Composition Toolbar-Buttons """
+ """Manages Accelerators and Clicks on the Composition Toolbar-Buttons"""
- def __init__(self, drawing_area, win, uibuilder, warning_overlay):
- self.log = logging.getLogger('StreamblankToolbarController')
+ def __init__(self, drawing_area, win, uibuilder, warning_overlay):
+ self.log = logging.getLogger('StreamblankToolbarController')
- self.warning_overlay = warning_overlay
+ self.warning_overlay = warning_overlay
- livebtn = uibuilder.find_widget_recursive(drawing_area, 'stream_live')
- blankbtn = uibuilder.find_widget_recursive(drawing_area, 'stream_blank')
+ livebtn = uibuilder.find_widget_recursive(drawing_area, 'stream_live')
+ blankbtn = uibuilder.find_widget_recursive(drawing_area,
+ 'stream_blank')
- blankbtn_pos = drawing_area.get_item_index(blankbtn)
+ blankbtn_pos = drawing_area.get_item_index(blankbtn)
- if not Config.getboolean('stream-blanker', 'enabled'):
- self.log.info('disabling stream-blanker features because the server does not support them: %s', Config.getboolean('stream-blanker', 'enabled'))
+ if not Config.getboolean('stream-blanker', 'enabled'):
+ self.log.info('disabling stream-blanker features '
+ 'because the server does not support them: %s',
+ Config.getboolean('stream-blanker', 'enabled'))
- drawing_area.remove(livebtn)
- drawing_area.remove(blankbtn)
- return
+ drawing_area.remove(livebtn)
+ drawing_area.remove(blankbtn)
+ return
- blank_sources = Config.getlist('stream-blanker', 'sources')
- self.status_btns = {}
+ blank_sources = Config.getlist('stream-blanker', 'sources')
+ self.status_btns = {}
- self.current_status = None
+ self.current_status = None
- livebtn.connect('toggled', self.on_btn_toggled)
- livebtn.set_name('live')
+ livebtn.connect('toggled', self.on_btn_toggled)
+ livebtn.set_name('live')
- self.livebtn = livebtn
- self.blank_btns = {}
+ self.livebtn = livebtn
+ self.blank_btns = {}
- for idx, name in enumerate(blank_sources):
- if idx == 0:
- new_btn = blankbtn
- else:
- new_icon = Gtk.Image.new_from_pixbuf(blankbtn.get_icon_widget().get_pixbuf())
- new_btn = Gtk.RadioToolButton(group=livebtn)
- new_btn.set_icon_widget(new_icon)
- drawing_area.insert(new_btn, blankbtn_pos+1)
+ for idx, name in enumerate(blank_sources):
+ if idx == 0:
+ new_btn = blankbtn
+ else:
+ new_icon = Gtk.Image.new_from_pixbuf(blankbtn.get_icon_widget()
+ .get_pixbuf())
+ new_btn = Gtk.RadioToolButton(group=livebtn)
+ new_btn.set_icon_widget(new_icon)
+ drawing_area.insert(new_btn, blankbtn_pos + 1)
- new_btn.set_label("Stream %s" % name)
- new_btn.connect('toggled', self.on_btn_toggled)
- new_btn.set_name(name)
+ new_btn.set_label("Stream %s" % name)
+ new_btn.connect('toggled', self.on_btn_toggled)
+ new_btn.set_name(name)
- self.blank_btns[name] = new_btn
+ self.blank_btns[name] = new_btn
- # connect event-handler and request initial state
- Connection.on('stream_status', self.on_stream_status)
- Connection.send('get_stream_status')
+ # connect event-handler and request initial state
+ Connection.on('stream_status', self.on_stream_status)
+ Connection.send('get_stream_status')
- def on_btn_toggled(self, btn):
- if not btn.get_active():
- return
+ def on_btn_toggled(self, btn):
+ if not btn.get_active():
+ return
- btn_name = btn.get_name()
- if btn_name == 'live':
- self.warning_overlay.disable()
+ btn_name = btn.get_name()
+ if btn_name == 'live':
+ self.warning_overlay.disable()
- else:
- self.warning_overlay.enable(btn_name)
+ else:
+ self.warning_overlay.enable(btn_name)
- if self.current_status == btn_name:
- self.log.info('stream-status already activate: %s', btn_name)
- return
+ if self.current_status == btn_name:
+ self.log.info('stream-status already activate: %s', btn_name)
+ return
- self.log.info('stream-status activated: %s', btn_name)
- if btn_name == 'live':
- Connection.send('set_stream_live')
- else:
- Connection.send('set_stream_blank', btn_name)
+ self.log.info('stream-status activated: %s', btn_name)
+ if btn_name == 'live':
+ Connection.send('set_stream_live')
+ else:
+ Connection.send('set_stream_blank', btn_name)
- def on_stream_status(self, status, source = None):
- self.log.info('on_stream_status callback w/ status %s and source %s', status, source)
+ def on_stream_status(self, status, source=None):
+ self.log.info('on_stream_status callback w/ status %s and source %s',
+ status, source)
- self.current_status = source if source is not None else status
- if status == 'live':
- self.livebtn.set_active(True)
- else:
- self.blank_btns[source].set_active(True)
+ self.current_status = source if source is not None else status
+ if status == 'live':
+ self.livebtn.set_active(True)
+ else:
+ self.blank_btns[source].set_active(True)
diff --git a/voctogui/lib/ui.py b/voctogui/lib/ui.py
index 553682c..92d0692 100644
--- a/voctogui/lib/ui.py
+++ b/voctogui/lib/ui.py
@@ -1,4 +1,5 @@
-import gi, logging
+import gi
+import logging
from gi.repository import Gtk, Gst, Gdk, GLib
from lib.config import Config
@@ -15,67 +16,76 @@ from lib.toolbar.composition import CompositionToolbarController
from lib.toolbar.streamblank import StreamblankToolbarController
from lib.toolbar.misc import MiscToolbarController
-class Ui(UiBuilder):
- def __init__(self, uifile):
- self.log = logging.getLogger('Ui')
- super().__init__(uifile)
-
- def setup(self):
- self.log.info('Initializing Ui')
-
- # Aquire the Main-Window from the UI-File
- self.win = self.get_check_widget('window')
-
- # Connect Close-Handler
- self.win.connect('delete-event', Gtk.main_quit)
-
-
- # Create Audio-Level Display
- drawing_area = self.find_widget_recursive(self.win, 'audiolevel_main')
- self.audio_level_display = AudioLevelDisplay(drawing_area)
-
-
- # Create Main-Video Overlay Controller
- drawing_area = self.find_widget_recursive(self.win, 'video_overlay_drawingarea')
- self.video_warning_overlay = VideoWarningOverlay(drawing_area)
-
-
- # Create Main-Video Display
- drawing_area = self.find_widget_recursive(self.win, 'video_main')
- self.main_video_display = VideoDisplay(drawing_area,
- port=11000,
- play_audio=Config.getboolean('mainvideo', 'playaudio'),
- level_callback=self.audio_level_display.level_callback)
-
-
- # Setup Preview Controller
- drawing_area = self.find_widget_recursive(self.win, 'box_left')
- self.video_previews_controller = VideoPreviewsController(drawing_area,
- win=self.win,
- uibuilder=self)
-
- drawing_area = self.find_widget_recursive(self.win, 'combo_audio')
- self.audio_selector_controller = AudioSelectorController(drawing_area,
- win=self.win,
- uibuilder=self)
-
-
- # Setup Toolbar Controllers
- toolbar = self.find_widget_recursive(self.win, 'toolbar')
- self.composition_toolbar_controller = CompositionToolbarController(toolbar,
- win=self.win,
- uibuilder=self)
-
- self.streamblank_toolbar_controller = StreamblankToolbarController(toolbar,
- win=self.win,
- uibuilder=self,
- warning_overlay=self.video_warning_overlay)
-
- self.misc_controller = MiscToolbarController(toolbar,
- win=self.win,
- uibuilder=self)
+class Ui(UiBuilder):
- def show(self):
- self.log.info('Showing Main-Window')
- self.win.show_all()
+ def __init__(self, uifile):
+ self.log = logging.getLogger('Ui')
+ super().__init__(uifile)
+
+ def setup(self):
+ self.log.info('Initializing Ui')
+
+ # Aquire the Main-Window from the UI-File
+ self.win = self.get_check_widget('window')
+
+ # Connect Close-Handler
+ self.win.connect('delete-event', Gtk.main_quit)
+
+ # Create Audio-Level Display
+ drawing_area = self.find_widget_recursive(self.win, 'audiolevel_main')
+ self.audio_level_display = AudioLevelDisplay(drawing_area)
+
+ # Create Main-Video Overlay Controller
+ drawing_area = self.find_widget_recursive(self.win,
+ 'video_overlay_drawingarea')
+ self.video_warning_overlay = VideoWarningOverlay(drawing_area)
+
+ # Create Main-Video Display
+ drawing_area = self.find_widget_recursive(self.win, 'video_main')
+ self.main_video_display = VideoDisplay(
+ drawing_area,
+ port=11000,
+ play_audio=Config.getboolean('mainvideo', 'playaudio'),
+ level_callback=self.audio_level_display.level_callback
+ )
+
+ # Setup Preview Controller
+ drawing_area = self.find_widget_recursive(self.win, 'box_left')
+ self.video_previews_controller = VideoPreviewsController(
+ drawing_area,
+ win=self.win,
+ uibuilder=self
+ )
+
+ drawing_area = self.find_widget_recursive(self.win, 'combo_audio')
+ self.audio_selector_controller = AudioSelectorController(
+ drawing_area,
+ win=self.win,
+ uibuilder=self
+ )
+
+ # Setup Toolbar Controllers
+ toolbar = self.find_widget_recursive(self.win, 'toolbar')
+ self.composition_toolbar_controller = CompositionToolbarController(
+ toolbar,
+ win=self.win,
+ uibuilder=self
+ )
+
+ self.streamblank_toolbar_controller = StreamblankToolbarController(
+ toolbar,
+ win=self.win,
+ uibuilder=self,
+ warning_overlay=self.video_warning_overlay
+ )
+
+ self.misc_controller = MiscToolbarController(
+ toolbar,
+ win=self.win,
+ uibuilder=self
+ )
+
+ def show(self):
+ self.log.info('Showing Main-Window')
+ self.win.show_all()
diff --git a/voctogui/lib/uibuilder.py b/voctogui/lib/uibuilder.py
index 2a6b00e..8776480 100644
--- a/voctogui/lib/uibuilder.py
+++ b/voctogui/lib/uibuilder.py
@@ -1,47 +1,57 @@
-import gi, logging
+import gi
+import logging
from gi.repository import Gtk, Gst
-class UiBuilder(object):
- def __init__(self, uifile):
- if not self.log:
- self.log = logging.getLogger('UiBuilder')
-
- self.uifile = uifile
-
- self.builder = Gtk.Builder()
- self.builder.add_from_file(self.uifile)
-
- def find_widget_recursive(self, widget, name):
- widget = self._find_widget_recursive(widget, name)
- if not widget:
- self.log.error('could find required widget "%s" by ID inside the parent %s', name, str(widget))
- raise Exception('Widget not found in parent')
-
- return widget
- def _find_widget_recursive(self, widget, name):
- if Gtk.Buildable.get_name(widget) == name:
- return widget
-
- if hasattr(widget, 'get_children'):
- for child in widget.get_children():
- widget = self._find_widget_recursive(child, name)
- if widget:
- return widget
-
- return None
-
- def get_check_widget(self, widget_id, clone=False):
- if clone:
- builder = Gtk.Builder()
- builder.add_from_file(self.uifile)
- else:
- builder = self.builder
-
- self.log.debug('loading widget "%s" from the .ui-File', widget_id)
- widget = builder.get_object(widget_id)
- if not widget:
- self.log.error('could not load required widget "%s" from the .ui-File', widget_id)
- raise Exception('Widget not found in .ui-File')
+class UiBuilder(object):
- return widget
+ def __init__(self, uifile):
+ if not self.log:
+ self.log = logging.getLogger('UiBuilder')
+
+ self.uifile = uifile
+
+ self.builder = Gtk.Builder()
+ self.builder.add_from_file(self.uifile)
+
+ def find_widget_recursive(self, widget, name):
+ widget = self._find_widget_recursive(widget, name)
+ if not widget:
+ self.log.error(
+ 'could find required widget "%s" by ID inside the parent %s',
+ name,
+ str(widget)
+ )
+ raise Exception('Widget not found in parent')
+
+ return widget
+
+ def _find_widget_recursive(self, widget, name):
+ if Gtk.Buildable.get_name(widget) == name:
+ return widget
+
+ if hasattr(widget, 'get_children'):
+ for child in widget.get_children():
+ widget = self._find_widget_recursive(child, name)
+ if widget:
+ return widget
+
+ return None
+
+ def get_check_widget(self, widget_id, clone=False):
+ if clone:
+ builder = Gtk.Builder()
+ builder.add_from_file(self.uifile)
+ else:
+ builder = self.builder
+
+ self.log.debug('loading widget "%s" from the .ui-File', widget_id)
+ widget = builder.get_object(widget_id)
+ if not widget:
+ self.log.error(
+ 'could not load required widget "%s" from the .ui-File',
+ widget_id
+ )
+ raise Exception('Widget not found in .ui-File')
+
+ return widget
diff --git a/voctogui/lib/videodisplay.py b/voctogui/lib/videodisplay.py
index 744e1aa..9259f6c 100644
--- a/voctogui/lib/videodisplay.py
+++ b/voctogui/lib/videodisplay.py
@@ -5,152 +5,152 @@ from lib.args import Args
from lib.config import Config
from lib.clock import Clock
+
class VideoDisplay(object):
- """ Displays a Voctomix-Video-Stream into a GtkWidget """
-
- def __init__(self, drawing_area, port, width=None, height=None, play_audio=False, level_callback=None):
- self.log = logging.getLogger('VideoDisplay[%u]' % port)
-
- self.drawing_area = drawing_area
- self.level_callback = level_callback
-
- caps = Config.get('mix', 'videocaps')
- use_previews = Config.getboolean('previews', 'enabled') and Config.getboolean('previews', 'use')
-
- # Preview-Ports are Raw-Ports + 1000
- if use_previews:
- self.log.info('using jpeg-previews instead of raw-video for gui')
- port += 1000
- else:
- self.log.info('using raw-video instead of jpeg-previews for gui')
-
- # Setup Server-Connection, Demuxing and Decoding
- pipeline = """
- tcpclientsrc host={host} port={port} blocksize=1048576 !
- queue !
- matroskademux name=demux
- """
-
- if use_previews:
- pipeline += """
- demux. !
- image/jpeg !
- jpegdec !
- {previewcaps} !
- queue !
- """
-
- else:
- pipeline += """
- demux. !
- {vcaps} !
- queue !
- """
-
- # Video Display
- videosystem = Config.get('videodisplay', 'system')
- self.log.debug('Configuring for Video-System %s', videosystem)
- if videosystem == 'gl':
- pipeline += """
- glupload !
- glcolorconvert !
- glimagesinkelement
- """
-
- elif videosystem == 'xv':
- pipeline += """
- xvimagesink
- """
-
- elif videosystem == 'x':
- prescale_caps = 'video/x-raw'
- if width and height:
- prescale_caps += ',width=%u,height=%u' % (width, height)
-
- pipeline += """
- videoconvert !
- videoscale !
- {prescale_caps} !
- ximagesink
- """.format(
- prescale_caps=prescale_caps
- )
-
- else:
- raise Exception('Invalid Videodisplay-System configured: %s' % videosystem)
-
-
-
- # If an Audio-Path is required, add an Audio-Path through a level-Element
- if self.level_callback or play_audio:
- pipeline += """
- demux. !
- {acaps} !
- queue !
- level name=lvl interval=50000000 !
- """
-
- # If Playback is requested, push fo pulseaudio
- if play_audio:
- pipeline += """
- pulsesink
- """
-
- # Otherwise just trash the Audio
- else:
- pipeline += """
- fakesink
- """
-
- pipeline = pipeline.format(
- acaps=Config.get('mix', 'audiocaps'),
- vcaps=Config.get('mix', 'videocaps'),
- previewcaps=Config.get('previews', 'videocaps'),
- host=Args.host if Args.host else Config.get('server', 'host'),
- port=port,
- )
-
- self.log.debug('Creating Display-Pipeline:\n%s', pipeline)
- self.pipeline = Gst.parse_launch(pipeline)
- self.pipeline.use_clock(Clock)
-
- self.drawing_area.realize()
- self.xid = self.drawing_area.get_property('window').get_xid()
- self.log.debug('Realized Drawing-Area with xid %u', self.xid)
-
- bus = self.pipeline.get_bus()
- bus.add_signal_watch()
- bus.enable_sync_message_emission()
-
- bus.connect('message::error', self.on_error)
- bus.connect("sync-message::element", self.on_syncmsg)
-
- if self.level_callback:
- bus.connect("message::element", self.on_level)
-
- self.log.debug('Launching Display-Pipeline')
- self.pipeline.set_state(Gst.State.PLAYING)
-
-
- def on_syncmsg(self, bus, msg):
- if msg.get_structure().get_name() == "prepare-window-handle":
- self.log.info('Setting imagesink window-handle to %s', self.xid)
- msg.src.set_window_handle(self.xid)
-
- def on_error(self, bus, message):
- self.log.debug('Received Error-Signal on Display-Pipeline')
- (error, debug) = message.parse_error()
- self.log.debug('Error-Details: #%u: %s', error.code, debug)
-
-
- def on_level(self, bus, msg):
- if msg.src.name != 'lvl':
- return
-
- if msg.type != Gst.MessageType.ELEMENT:
- return
-
- rms = msg.get_structure().get_value('rms')
- peak = msg.get_structure().get_value('peak')
- decay = msg.get_structure().get_value('decay')
- self.level_callback(rms, peak, decay)
+ """Displays a Voctomix-Video-Stream into a GtkWidget"""
+
+ def __init__(self, drawing_area, port, width=None, height=None,
+ play_audio=False, level_callback=None):
+ self.log = logging.getLogger('VideoDisplay[%u]' % port)
+
+ self.drawing_area = drawing_area
+ self.level_callback = level_callback
+
+ caps = Config.get('mix', 'videocaps')
+ use_previews = (Config.getboolean('previews', 'enabled') and
+ Config.getboolean('previews', 'use'))
+
+ # Preview-Ports are Raw-Ports + 1000
+ if use_previews:
+ self.log.info('using jpeg-previews instead of raw-video for gui')
+ port += 1000
+ else:
+ self.log.info('using raw-video instead of jpeg-previews for gui')
+
+ # Setup Server-Connection, Demuxing and Decoding
+ pipeline = """
+ tcpclientsrc host={host} port={port} blocksize=1048576 !
+ queue !
+ matroskademux name=demux
+ """
+
+ if use_previews:
+ pipeline += """
+ demux. !
+ image/jpeg !
+ jpegdec !
+ {previewcaps} !
+ queue !
+ """
+
+ else:
+ pipeline += """
+ demux. !
+ {vcaps} !
+ queue !
+ """
+
+ # Video Display
+ videosystem = Config.get('videodisplay', 'system')
+ self.log.debug('Configuring for Video-System %s', videosystem)
+ if videosystem == 'gl':
+ pipeline += """
+ glupload !
+ glcolorconvert !
+ glimagesinkelement
+ """
+
+ elif videosystem == 'xv':
+ pipeline += """
+ xvimagesink
+ """
+
+ elif videosystem == 'x':
+ prescale_caps = 'video/x-raw'
+ if width and height:
+ prescale_caps += ',width=%u,height=%u' % (width, height)
+
+ pipeline += """
+ videoconvert !
+ videoscale !
+ {prescale_caps} !
+ ximagesink
+ """.format(prescale_caps=prescale_caps)
+
+ else:
+ raise Exception(
+ 'Invalid Videodisplay-System configured: %s' % videosystem
+ )
+
+ # If an Audio-Path is required,
+ # add an Audio-Path through a level-Element
+ if self.level_callback or play_audio:
+ pipeline += """
+ demux. !
+ {acaps} !
+ queue !
+ level name=lvl interval=50000000 !
+ """
+
+ # If Playback is requested, push fo pulseaudio
+ if play_audio:
+ pipeline += """
+ pulsesink
+ """
+
+ # Otherwise just trash the Audio
+ else:
+ pipeline += """
+ fakesink
+ """
+
+ pipeline = pipeline.format(
+ acaps=Config.get('mix', 'audiocaps'),
+ vcaps=Config.get('mix', 'videocaps'),
+ previewcaps=Config.get('previews', 'videocaps'),
+ host=Args.host if Args.host else Config.get('server', 'host'),
+ port=port,
+ )
+
+ self.log.debug('Creating Display-Pipeline:\n%s', pipeline)
+ self.pipeline = Gst.parse_launch(pipeline)
+ self.pipeline.use_clock(Clock)
+
+ self.drawing_area.realize()
+ self.xid = self.drawing_area.get_property('window').get_xid()
+ self.log.debug('Realized Drawing-Area with xid %u', self.xid)
+
+ bus = self.pipeline.get_bus()
+ bus.add_signal_watch()
+ bus.enable_sync_message_emission()
+
+ bus.connect('message::error', self.on_error)
+ bus.connect("sync-message::element", self.on_syncmsg)
+
+ if self.level_callback:
+ bus.connect("message::element", self.on_level)
+
+ self.log.debug('Launching Display-Pipeline')
+ self.pipeline.set_state(Gst.State.PLAYING)
+
+ def on_syncmsg(self, bus, msg):
+ if msg.get_structure().get_name() == "prepare-window-handle":
+ self.log.info('Setting imagesink window-handle to %s', self.xid)
+ msg.src.set_window_handle(self.xid)
+
+ def on_error(self, bus, message):
+ self.log.debug('Received Error-Signal on Display-Pipeline')
+ (error, debug) = message.parse_error()
+ self.log.debug('Error-Details: #%u: %s', error.code, debug)
+
+ def on_level(self, bus, msg):
+ if msg.src.name != 'lvl':
+ return
+
+ if msg.type != Gst.MessageType.ELEMENT:
+ return
+
+ rms = msg.get_structure().get_value('rms')
+ peak = msg.get_structure().get_value('peak')
+ decay = msg.get_structure().get_value('decay')
+ self.level_callback(rms, peak, decay)
diff --git a/voctogui/lib/videopreviews.py b/voctogui/lib/videopreviews.py
index 3490a4d..9adb76f 100644
--- a/voctogui/lib/videopreviews.py
+++ b/voctogui/lib/videopreviews.py
@@ -5,133 +5,140 @@ from lib.config import Config
from lib.videodisplay import VideoDisplay
import lib.connection as Connection
-class VideoPreviewsController(object):
- """ Displays Video-Previews and selection Buttons for them """
-
- def __init__(self, drawing_area, win, uibuilder):
- self.log = logging.getLogger('VideoPreviewsController')
- self.drawing_area = drawing_area
- self.win = win
-
- self.sources = Config.getlist('mix', 'sources')
- self.preview_players = {}
- self.previews = {}
- self.a_btns = {}
- self.b_btns = {}
+class VideoPreviewsController(object):
+ """Displays Video-Previews and selection Buttons for them"""
- self.current_source = {'a': None, 'b': None}
+ def __init__(self, drawing_area, win, uibuilder):
+ self.log = logging.getLogger('VideoPreviewsController')
- try:
- width = Config.getint('previews', 'width')
- self.log.debug('Preview-Width configured to %u', width)
- except:
- width = 320
- self.log.debug('Preview-Width selected as %u', width)
+ self.drawing_area = drawing_area
+ self.win = win
- try:
- height = Config.getint('previews', 'height')
- self.log.debug('Preview-Height configured to %u', height)
- except:
- height = width*9/16
- self.log.debug('Preview-Height calculated to %u', height)
+ self.sources = Config.getlist('mix', 'sources')
+ self.preview_players = {}
+ self.previews = {}
+ self.a_btns = {}
+ self.b_btns = {}
- # Accelerators
- accelerators = Gtk.AccelGroup()
- win.add_accel_group(accelerators)
+ self.current_source = {'a': None, 'b': None}
- group_a = None
- group_b = None
+ try:
+ width = Config.getint('previews', 'width')
+ self.log.debug('Preview-Width configured to %u', width)
+ except:
+ width = 320
+ self.log.debug('Preview-Width selected as %u', width)
- for idx, source in enumerate(self.sources):
- self.log.info('Initializing Video Preview %s', source)
+ try:
+ height = Config.getint('previews', 'height')
+ self.log.debug('Preview-Height configured to %u', height)
+ except:
+ height = width * 9 / 16
+ self.log.debug('Preview-Height calculated to %u', height)
- preview = uibuilder.get_check_widget('widget_preview', clone=True)
- video = uibuilder.find_widget_recursive(preview, 'video')
+ # Accelerators
+ accelerators = Gtk.AccelGroup()
+ win.add_accel_group(accelerators)
- video.set_size_request(width, height)
- drawing_area.pack_start(preview, fill=False, expand=False, padding=0)
+ group_a = None
+ group_b = None
- player = VideoDisplay(video, port=13000 + idx, width=width, height=height)
+ for idx, source in enumerate(self.sources):
+ self.log.info('Initializing Video Preview %s', source)
- uibuilder.find_widget_recursive(preview, 'label').set_label(source)
- btn_a = uibuilder.find_widget_recursive(preview, 'btn_a')
- btn_b = uibuilder.find_widget_recursive(preview, 'btn_b')
+ preview = uibuilder.get_check_widget('widget_preview', clone=True)
+ video = uibuilder.find_widget_recursive(preview, 'video')
- btn_a.set_name("%c %u" % ('a', idx))
- btn_b.set_name("%c %u" % ('b', idx))
+ video.set_size_request(width, height)
+ drawing_area.pack_start(preview, fill=False,
+ expand=False, padding=0)
- if not group_a:
- group_a = btn_a
- else:
- btn_a.join_group(group_a)
+ player = VideoDisplay(video, port=13000 + idx,
+ width=width, height=height)
+ uibuilder.find_widget_recursive(preview, 'label').set_label(source)
+ btn_a = uibuilder.find_widget_recursive(preview, 'btn_a')
+ btn_b = uibuilder.find_widget_recursive(preview, 'btn_b')
- if not group_b:
- group_b = btn_b
- else:
- btn_b.join_group(group_b)
+ btn_a.set_name("%c %u" % ('a', idx))
+ btn_b.set_name("%c %u" % ('b', idx))
+ if not group_a:
+ group_a = btn_a
+ else:
+ btn_a.join_group(group_a)
- btn_a.connect('toggled', self.btn_toggled)
- btn_b.connect('toggled', self.btn_toggled)
+ if not group_b:
+ group_b = btn_b
+ else:
+ btn_b.join_group(group_b)
- key, mod = Gtk.accelerator_parse('%u' % (idx+1))
- btn_a.add_accelerator('activate', accelerators, key, mod, Gtk.AccelFlags.VISIBLE)
+ btn_a.connect('toggled', self.btn_toggled)
+ btn_b.connect('toggled', self.btn_toggled)
- key, mod = Gtk.accelerator_parse('<Ctrl>%u' % (idx+1))
- btn_b.add_accelerator('activate', accelerators, key, mod, Gtk.AccelFlags.VISIBLE)
+ key, mod = Gtk.accelerator_parse('%u' % (idx + 1))
+ btn_a.add_accelerator('activate', accelerators,
+ key, mod, Gtk.AccelFlags.VISIBLE)
- btn_fullscreen = uibuilder.find_widget_recursive(preview, 'btn_fullscreen')
- btn_fullscreen.set_name("%c %u" % ('f', idx))
+ key, mod = Gtk.accelerator_parse('<Ctrl>%u' % (idx + 1))
+ btn_b.add_accelerator('activate', accelerators,
+ key, mod, Gtk.AccelFlags.VISIBLE)
- btn_fullscreen.connect('clicked', self.btn_fullscreen_clicked)
+ btn_fullscreen = uibuilder.find_widget_recursive(preview,
+ 'btn_fullscreen')
+ btn_fullscreen.set_name("%c %u" % ('f', idx))
- key, mod = Gtk.accelerator_parse('<Alt>%u' % (idx+1))
- btn_fullscreen.add_accelerator('activate', accelerators, key, mod, Gtk.AccelFlags.VISIBLE)
+ btn_fullscreen.connect('clicked', self.btn_fullscreen_clicked)
- self.preview_players[source] = player
- self.previews[source] = preview
- self.a_btns[source] = btn_a
- self.b_btns[source] = btn_b
+ key, mod = Gtk.accelerator_parse('<Alt>%u' % (idx + 1))
+ btn_fullscreen.add_accelerator('activate', accelerators,
+ key, mod, Gtk.AccelFlags.VISIBLE)
+ self.preview_players[source] = player
+ self.previews[source] = preview
+ self.a_btns[source] = btn_a
+ self.b_btns[source] = btn_b
- # connect event-handler and request initial state
- Connection.on('video_status', self.on_video_status)
- Connection.send('get_video')
+ # connect event-handler and request initial state
+ Connection.on('video_status', self.on_video_status)
+ Connection.send('get_video')
- def btn_toggled(self, btn):
- if not btn.get_active():
- return
+ def btn_toggled(self, btn):
+ if not btn.get_active():
+ return
- btn_name = btn.get_name()
- self.log.debug('btn_toggled: %s', btn_name)
+ btn_name = btn.get_name()
+ self.log.debug('btn_toggled: %s', btn_name)
- channel, idx = btn_name.split(' ')[:2]
- source_name = self.sources[int(idx)]
+ channel, idx = btn_name.split(' ')[:2]
+ source_name = self.sources[int(idx)]
- if self.current_source[channel] == source_name:
- self.log.info('video-channel %s already on %s', channel, source_name)
- return
+ if self.current_source[channel] == source_name:
+ self.log.info('video-channel %s already on %s',
+ channel, source_name)
+ return
- self.log.info('video-channel %s changed to %s', channel, source_name)
- Connection.send('set_video_'+channel, source_name)
+ self.log.info('video-channel %s changed to %s', channel, source_name)
+ Connection.send('set_video_' + channel, source_name)
- def btn_fullscreen_clicked(self, btn):
- btn_name = btn.get_name()
- self.log.debug('btn_fullscreen_clicked: %s', btn_name)
+ def btn_fullscreen_clicked(self, btn):
+ btn_name = btn.get_name()
+ self.log.debug('btn_fullscreen_clicked: %s', btn_name)
- channel, idx = btn_name.split(' ')[:2]
- source_name = self.sources[int(idx)]
+ channel, idx = btn_name.split(' ')[:2]
+ source_name = self.sources[int(idx)]
- self.log.info('selcting video %s for fullscreen', source_name)
- Connection.send('set_videos_and_composite', source_name, '*', 'fullscreen')
+ self.log.info('selcting video %s for fullscreen', source_name)
+ Connection.send('set_videos_and_composite',
+ source_name, '*', 'fullscreen')
- def on_video_status(self, source_a, source_b):
- self.log.info('on_video_status callback w/ sources: %s and %s', source_a, source_b)
+ def on_video_status(self, source_a, source_b):
+ self.log.info('on_video_status callback w/ sources: %s and %s',
+ source_a, source_b)
- self.current_source['a'] = source_a
- self.current_source['b'] = source_b
+ self.current_source['a'] = source_a
+ self.current_source['b'] = source_b
- self.a_btns[source_a].set_active(True)
- self.b_btns[source_b].set_active(True)
+ self.a_btns[source_a].set_active(True)
+ self.b_btns[source_b].set_active(True)
diff --git a/voctogui/lib/warningoverlay.py b/voctogui/lib/warningoverlay.py
index bf2c2cd..f4f7f24 100644
--- a/voctogui/lib/warningoverlay.py
+++ b/voctogui/lib/warningoverlay.py
@@ -3,59 +3,64 @@ from gi.repository import GLib, Gst, cairo
from lib.config import Config
+
class VideoWarningOverlay(object):
- """ Displays a Warning-Overlay above the Video-Feed of another VideoDisplay """
+ """Displays a Warning-Overlay above the Video-Feed
+ of another VideoDisplay"""
- def __init__(self, drawing_area):
- self.log = logging.getLogger('VideoWarningOverlay')
+ def __init__(self, drawing_area):
+ self.log = logging.getLogger('VideoWarningOverlay')
- self.drawing_area = drawing_area
- self.drawing_area.connect("draw", self.draw_callback)
+ self.drawing_area = drawing_area
+ self.drawing_area.connect("draw", self.draw_callback)
- self.text = None
- self.blink_state = False
+ self.text = None
+ self.blink_state = False
- GLib.timeout_add_seconds(1, self.on_blink_callback)
+ GLib.timeout_add_seconds(1, self.on_blink_callback)
- def on_blink_callback(self):
- self.blink_state = not self.blink_state
- self.drawing_area.queue_draw()
- return True
+ def on_blink_callback(self):
+ self.blink_state = not self.blink_state
+ self.drawing_area.queue_draw()
+ return True
- def enable(self, text=None):
- self.text = text
- self.drawing_area.show()
- self.drawing_area.queue_draw()
+ def enable(self, text=None):
+ self.text = text
+ self.drawing_area.show()
+ self.drawing_area.queue_draw()
- def set_text(self, text=None):
- self.text = text
- self.drawing_area.queue_draw()
+ def set_text(self, text=None):
+ self.text = text
+ self.drawing_area.queue_draw()
- def disable(self):
- self.drawing_area.hide()
- self.drawing_area.queue_draw()
+ def disable(self):
+ self.drawing_area.hide()
+ self.drawing_area.queue_draw()
- def draw_callback(self, area, cr):
- w = self.drawing_area.get_allocated_width();
- h = self.drawing_area.get_allocated_height();
+ def draw_callback(self, area, cr):
+ w = self.drawing_area.get_allocated_width()
+ h = self.drawing_area.get_allocated_height()
- self.log.debug('draw_callback: w/h=%u/%u, blink_state=%u', w, h, self.blink_state)
+ self.log.debug('draw_callback: w/h=%u/%u, blink_state=%u',
+ w, h, self.blink_state)
- if self.blink_state:
- cr.set_source_rgba(1.0, 0.0, 0.0, 0.8)
- else:
- cr.set_source_rgba(1.0, 0.5, 0.0, 0.8)
+ if self.blink_state:
+ cr.set_source_rgba(1.0, 0.0, 0.0, 0.8)
+ else:
+ cr.set_source_rgba(1.0, 0.5, 0.0, 0.8)
- cr.rectangle(0, 0, w, h)
- cr.fill()
+ cr.rectangle(0, 0, w, h)
+ cr.fill()
- text = "Stream is Blanked"
- if self.text:
- text += ": "+self.text
+ text = "Stream is Blanked"
+ if self.text:
+ text += ": " + self.text
- cr.set_font_size(h*0.75)
- xbearing, ybearing, txtwidth, txtheight, xadvance, yadvance = cr.text_extents(text)
+ cr.set_font_size(h * 0.75)
+ (xbearing, ybearing,
+ txtwidth, txtheight,
+ xadvance, yadvance) = cr.text_extents(text)
- cr.move_to(w/2 - txtwidth/2, h*0.75)
- cr.set_source_rgba(1.0, 1.0, 1.0, 1.0)
- cr.show_text(text)
+ cr.move_to(w / 2 - txtwidth / 2, h * 0.75)
+ cr.set_source_rgba(1.0, 1.0, 1.0, 1.0)
+ cr.show_text(text)