Skip to content

Instantly share code, notes, and snippets.

@seahawk1986
Created March 26, 2024 12:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save seahawk1986/18753df5296f4175ca85eda4bc6cd449 to your computer and use it in GitHub Desktop.
Save seahawk1986/18753df5296f4175ca85eda4bc6cd449 to your computer and use it in GitHub Desktop.
yavdr-frontend.org

yaVDR-Frontend

Die graphische User-Session

Der X-Server wird über die Systemd-Unit x@vt7.service gestartet, die als Abhängigkeit der Unit xlogin@vdr.service aufgerufen wird. xlogin startet eine Systemd-User Session (die nötigen Units stammen aus dem Paket dbus-user-session). Im Autostat des Window Manager/Desktop-Manager wird die Unit yavdr-frontend.service gestartet. Das Skript yavdr-frontend dient dazu den Start des VDR-Frontend, einem Mediacenter wie KODI und weitere Anwendungen zu koordinieren, und den Wechsel zwischen diesen zu ermöglichen. Es kann über DBus gesteuert werden, so dass seine Aktionen leicht durch Skripte, irexec, Hotkeys des Window-Manager usw. ausgelöst werden können.

yavdr-frontend startet beim Start das konfigurierte Frontend. Das kann ein VDR-Frontend, ein Mediacenter wie KODI oder eine andere Systemd-Unit bzw. Anwendung sein.

main:
    # start with the choosen frontend type: vdr|mediacenter|app
    primary_frontend = vdr
    secondary_frontend = mediacenter

Umgebungsvariablen im Systemd-Environment

Folgende Umgebungsvariablen werden genutz, um Optionen für die Frontends festzulegen:

VariableVorgabewertBeschreibung
DISPLAY:0Bildschirm für das primäre Frontend
ALSA_DEVICEhw:0,0Gerät für die PCM-Ausgabe über ALSA
ALSA_AC3_DEVICEGerät für die Passthrough-Ausgabe über ALSA
AE_SINKSoundausgabe für KODI (ALSA oder PULSE)

DBus-API

yavdr-frontend legt auf dem SystemBus unter dem Namen de.yavdr.frontend ein Objekt an, das unter dem Pfad **/frontend** die folgenden Funktionen bietet:

start
starte das momentan gewählte Frontend
stop
stoppe das momentan aktive Frontend
toggle
starte das Frontend, wenn es inaktiv ist, stoppe es, wenn es aktiv ist
switch
wechlse zum anderen Frontend
switchTo string:”FRONTEND NAME”
wechsle zum Frontend mit dem angegebenen Namen
setDisplay string:”DISPLAY”
setze das Display, auf dem das primäre Frontend angezeigt werden soll, also z.B. “:0” oder “:0.1”
setNext string:”FRONTEND NAME”
setze das nächste Frontend, aber wechsle nicht sofort
switchBetween string:”FRONTEND A” string:”FRONTEND B”
wechsle zu Frontend A, wenn dieses aktuell nicht gewählt ist, sonst zu Frontend B

Mit dem Hilfsskript frontend-dbus-send können diese Befehle leicht in einem Skript genutzt werden, z.B. um zum Mediacenter (oder zum zuletzt genutzten Frontend zurück) zu wechseln:

frontend-dbus-send switchBetween string:vdr string:mediacenter

Start des VDR-Frontends

yavdr-frontend initialisiert ein Objekt der Klasse VDRFrontend. Dieses wartet auf den Start des VDR und fragt über das dbus2vdr-Plugin die vom VDR geladenen Plugins ab. Dann sucht es unter Berücksichtigung der Reihenfolge der Einträge im Abschnitt [VDR-Frontends] in seiner Konfigurationsdatei ein passendes Frontend-Objekt heraus. Das Frontend kann im einfachsten Fall eine .desktop-Datei sein, alternativ kann auch eine Systemd-Unit oder ein Python-Modul genutzt werden. In den beiden erstgenannten Fällen wird ein Python-Wrapper genutzt, um die benötigten Methoden (start(), stop(), on_stopped()) zu implementieren.

In der Konfigurationsdatei wird einem VDR-Ausgabeplugin ein entsprechendes Frontend zugewiesen:

vdr:
  frontends:
    # <plugin name> = <type> <path> [<object>]
    # type can be one of:
    # module (Python-Module)
    # unit (Systemd Unit)
    # app (.desktop file)
      softhddevice:
        module_name: frontends.softhddevice
        class_name: Softhddevice
      xineliboutput:
        app: vdr-sxfe.desktop
      xine:
        unit xine.service
      rpihddevice:
        module_name: frontends.rpihddevice
        class_name: RPIHDDevice

Ob das Frontend beim Start sofort gestartet wird oder in dem Fall, dass der VDR für eine Aufnahme gestartet wurde erst nachträglich durch den Benutzer (z.B. per Tastendruck auf der Fernbedienung) aktiviert wird, kann über die Konfigurationsdatei festgelegt werden:

vdr:
# allowed values for attach_frontend:
# auto: attach only on manual start
# never: never start the frontend autmatically
# always: always start frontend
     attach_frontend = auto

Start des Mediacenter

Anstatt wie bei früheren yaVDR-Version fest auf XBMC/KODI zu setzen, soll das bei yaVDR 0.7 generischer gelöst werden, so dass es möglich wird z.B. Plex oder andere Programme als Mediacenter zu nutzen. Vor dem Wechsel zum Mediacenter wird ein laufendes VDR-Frontend oder eine andere Standalone-Anwendung automatisch gestoppt.

Die Syntax entspricht der für die VDR-Frontends. Der Abschnitt in der Konfigurationsdatei sieht z.B. so aus:

applications:
    mediacenter:
        name: kodi.service

Für KODI genügt es nicht den normalen Starter aus der KODI.desktop zu nutzen, da es nicht sauber auf ein SIGTERM-Signal reagiert (bleibt z.B. mitunter beim Beenden hängen). Daher wird es in der Unit kodi.service gezielt über seine RPC-API beendet und dann wird darauf gewartet, dass der Prozess tatsächlich beendet wurde (und damit alle Ressourcen wieder freigegeben wurden), da es sonst zu Folgeproblemen wie noch belegten Soundkarten für das nachfolgende Frontend kommen kann.

Starten von Programmen über das VDR OSD

Das vdr-plugin-desktop stellt die vorhandenen .desktop-Dateien in einer Menü-Struktur dar und ruft ein externes Skript mit dem Pfad der .desktop-Datei auf, wenn der Benutzer eine Anwendung ausgewählt hat. Das Skript wird vom Plugin in /var/lib/vdr/plugins/desktop/starter erwartet. Ein Beispielskript ist im Quellcode des Plugins zu finden: [starter][]

[starter]: https://github.com/flensrocker/vdr-plugin-desktop/blob/master/examples/starter

Das starter-Skript gibt die .desktop-Datei über die DBus-API an yavdr-frontend weiter.

Starten von Programmen über den Desktop

Daneben könnte man z.B. eine angepasste Version von adeskbar oder einen anderen Programmstarter nutzen, der sich ähnlich verhält.

beim Start eines Programms

Um Programme in der Session aufzurufen, benötigen wir ein Hilfsskript, dem die .desktop-Dateien übergeben werden und das den Pfad an yavdr-frontend weiterreicht, das den Start der Anwendung übernimmt.

Audioausgabe

In der User-Session kann pulseaudio genutzt werden, so dass auch moderne Programme ohne Alsa-Unterstützung Ton ausgeben können. Wenn ein Programm nur alsa-Unterstützung bietet oder direkten Zugriff auf die Hardware benötigt (z.B. für bestimmte Passthrough-Formate), kann es mittels pasuspender oder yavdr-pasuspend pulseaudio dazu veranlassen seine Ausgabe stummzuschalten und die Audiogeräte freizugeben. pasuspender nimm ein Programm als Argument, das direkten Zugriff auf die Audio-Hardware haben soll, die Ausgabe über pulseaudio wird so lange unterbrochen, bis das Programm beendet wird. yavdr-pasuspend ermöglicht es die pulseaudio-Ausgabe an- und abzuschalten, was z.B. für softhddevice praktisch ist, da das kein eigenständiges Programm ist:

# schalte pulseaudio für ein bestimmtes Programm ab:
pasuspender -- audacity

# schalte pulseaudio bis auf weiteres ab:
yavdr-pasuspend -s
svdrpsend plug softhddevice atta
# und wenn es nicht mehr gebraucht wird:
svdrpsend plug softhddevice deta
yavdr-pasuspend -r

Der direkte Zugriff auf die Audio-Hardware funktioniert nur für den Nutzer, unter dem Pulseaudio läuft (und root).

Unterstützung für zusätzliche Frontends ergänzen

Frontend-Klassen

Ein neues Frontend sollte von der Basis-Klasse BasicFrontend abgeleitet werden. Wird eine eigene __init__() Funtion genutzt, muss die Controller-Instanz als Argument akzeptiert werden (alle anderen Argumente müssen optional sein):

controller
die Klasseninstanz, die das Frontend verwaltet
name
der Name des Frontend
fe_type
der Typ des Frontends

Diese müssen dann an die __init__() Methode der Basisklasse weitergegeben werden:

from yavdr_frontend.basicfrontend import BasicFrontend

class MyFrontend(BasicFrontend)
    name = "MyFrontend"
    fe_type = "CustomFrontend"
def __init__(self, controller):
    super().__init__(controller, name, fe_type)
    # do other stuff

Darüber hinaus sollte es die folgenden Methoden implementieren (die Basis-Klasse BasicFrontend fungiert wie ein Dummy-Frontend, das das Interface abbildet, ohne etwas zu tun):

start(self)
starte das Frontend
started(self)
setzt die Status-Variable is_running auf aktiv, wenn der Start erfolgreich war
stop(self)
stoppt das Frontend
stopped(self)
wenn das Frontend gestoppt wurde, muss es die on_stopped() Methode des Controller aufrufen und den Status is_running zu false ändern
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment