[1] MLD-6.x / General / Welche Video-Ausgabe für Hyperion?
 

Offline videopix

  • Member
  • **
  • Posts: 85
    • View Profile
Welche Video-Ausgabe für Hyperion?
« on: September 07, 2026, 22:18:16 »
Guten Abend,

ich möchte MLD 6.5 auf einem ASRock J3455M (x86 mit interner Intel HD Graphics 500) installieren und auch Hyperion nutzen. Dabei gibt es Probleme, weil Hyperion keinen Zugriff auf den Bildinhalt bekommt.

Beim Intel-Image endet die Hyperion-ng-Installation in einem podman-Fehler, weil card0 nicht existiert. Ändert man das im compose.yml in card1, startet Hyperion zwar, bekommt aber kein Bild.

Bei der X86-Installation werden diese Ausgabe-Optionen angeboten:
- DvbHdDevice
- DvbSdDevice
- gStreamerDevice
- SoftHdVaapi (empfohlen)
- SoftHdCuvid
- SoftHddrm
- VaapiVideo
- XineLibOutput
Die Hilfetexte wiederholen leider nur die Namen.

Die ersten beiden Optionen entfallen hier.
SoftHdVaapi (=softhddevice?) läuft bei Hyperion-ng wieder auf einen Installationsfehler. Das alte Hyperion läuft zwar, bekommt aber kein Bild.
Bei SoftHddrm bleibt schon die MLD-Installation hängen.

Bevor ich weiter rumprobiere:
Welche Option eignet sich? Sind eventuell irgendwelche Anpassungen nötig?

Danke!
Uwe
Test-VDR: ASRock J3455M (Intel-Grafik), 4GB RAM, 60GB SSD, Cine S2 PCIe twin tuner + DuoFlex an Gigablue Ultra SCR LNB

Online clausmuus

  • Administrator
  • Expert Member
  • ********
  • Posts: 21531
    • View Profile
    • ClausMuus.de
Welche Video-Ausgabe für Hyperion?
« Reply #1 on: September 07, 2026, 22:28:20 »
In unserem Wiki https://www.minidvblinux.de/wiki/ gibt es eine ausführliche Beschreibung zu dem Thema. Ich selber setzte das nicht ein, weshalb ich dazu nicht mehr sagen kann.
MLD 5.5 - Raspberry PI - 7" Touch TFT - Squeeze Play
MLD 6.5 - lirc yaUsbIR - 4 x DD-Sat - Intel N100M - 4GB RAM - 64GB SSD + 12TB HDD + 22TB HDD - Lian Li PC-C37B - Samsung LE40A559

Offline franky

  • MLD-Developer
  • Expert Member
  • ******
  • Posts: 602
    • View Profile
Welche Video-Ausgabe für Hyperion?
« Reply #2 on: September 07, 2026, 23:01:24 »

Bevor ich weiter rumprobiere:
Welche Option eignet sich? Sind eventuell irgendwelche Anpassungen nötig?

Hi Uwe,

für deine GPU funktionieren nur SoftHdVaapi (ist softhddevice mit va-api HW-Beschleunigung für Intel GPUs) oder das alte XineLibOutput, wobei das letztere nicht wirklich zu empfehlen ist.
Eigentlich ist für deine  GPU nur SoftHdVaapi sinnvoll.
Da ich wie Claus auch kein Hyperion nutze, kann ich da auch nicht weiterhelfen.

Gruß
Klaus

Offline videopix

  • Member
  • **
  • Posts: 85
    • View Profile
Welche Video-Ausgabe für Hyperion?
« Reply #3 on: Today at 00:45:57 »
Hallo Claus & Klaus,

danke für eure schnellen Antworten. Das Wiki bezieht sich auf Hardware-Grabber. Ich habe aber inzwischen eine schlanke Software-Lösung für mein System gefunden:

Bei mir läuft MLD 6.5 auf einem ASRock J3455M mit interner Intel-Grafik 500 und Ausgabe über softhddevice mit va-api HW-Beschleunigung, installiert über das Image "X86 PC mit Intel GPU".
Das Paket hyperion-ng setzt einen Podman-Container auf, hyperiond startete aber erst, nachdem ich in /usr/share/hyperion/docker-compose.yml ganz unten card0 durch card1 ersetzt hatte, also:
Code: [Select]
- /dev/dri/card1:/dev/dri/card1Bitte auch gleich das # vor privileged: true entfernen.

Das Hyperion UI war dann nach einem Reboot unter mld.fritz.box:8090 erreichbar.
Eigentlich sollte man dort unter "Aufnahme Hardware" die Grafik-Schnittstelle auswählen können. Das hat aber nicht funktioniert. Daher habe ich ein Python Script erstellt, das den Bildschirminhalt in einer niedrigen Auflösung abgreift und im Flatbuffer-Format an Hyperion schickt. Das funktioniert hier tadellos. Im Hyperion UI unter "Netzwerk" muss "Flatbuffer Server" aktiviert und der Port 19400 eingetragen sein. Speichern nicht vergessen!

Das Script habe ich als /usr/share/hyperion/vdr-kmsgrab-flatbuf.py abgelegt.

Inhalt:
Code: [Select]
#!/usr/bin/env python3

import subprocess
import socket
import struct
import time
import signal
import select

import flatbuffers


# ============================================================
# Konfiguration
# ============================================================

WIDTH = 160
HEIGHT = 90
FPS = 25

HYPERION_HOST = "127.0.0.1"
HYPERION_PORT = 19400

PRIORITY = 100
ORIGIN = "VDR-KMSgrab"

FRAME_SIZE = WIDTH * HEIGHT * 3

running = True


# ============================================================
# Signal handling
# ============================================================

def signal_handler(signum, frame):
    global running
    running = False


signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)


# ============================================================
# FlatBuffer
# ============================================================

def create_register(builder):
    origin = builder.CreateString(ORIGIN)

    builder.StartObject(2)

    builder.PrependUOffsetTRelativeSlot(
        0, origin, 0
    )

    builder.PrependInt32Slot(
        1, PRIORITY, 0
    )

    return builder.EndObject()


def create_raw_image(builder, frame):
    data = builder.CreateByteVector(frame)

    builder.StartObject(3)

    builder.PrependUOffsetTRelativeSlot(
        0, data, 0
    )

    builder.PrependInt32Slot(
        1, WIDTH, -1
    )

    builder.PrependInt32Slot(
        2, HEIGHT, -1
    )

    return builder.EndObject()


def create_image(builder, raw_image):
    builder.StartObject(3)

    # RawImage
    builder.PrependUint8Slot(
        0, 1, 0
    )

    builder.PrependUOffsetTRelativeSlot(
        1, raw_image, 0
    )

    builder.PrependInt32Slot(
        2, -1, -1
    )

    return builder.EndObject()


def create_request(builder, command_type, command):
    builder.StartObject(2)

    builder.PrependUint8Slot(
        0, command_type, 0
    )

    builder.PrependUOffsetTRelativeSlot(
        1, command, 0
    )

    return builder.EndObject()


def build_register():
    builder = flatbuffers.Builder(1024)

    register = create_register(builder)

    # Command.Register
    request = create_request(
        builder,
        4,
        register
    )

    builder.Finish(request)

    return builder.Output()


def build_image(frame):
    builder = flatbuffers.Builder(
        FRAME_SIZE + 1024
    )

    raw_image = create_raw_image(
        builder,
        frame
    )

    image = create_image(
        builder,
        raw_image
    )

    # Command.Image
    request = create_request(
        builder,
        2,
        image
    )

    builder.Finish(request)

    return builder.Output()


def build_clear():
    builder = flatbuffers.Builder(256)

    builder.StartObject(1)

    builder.PrependInt32Slot(
        0,
        PRIORITY,
        0
    )

    clear = builder.EndObject()

    # Command.Clear
    request = create_request(
        builder,
        3,
        clear
    )

    builder.Finish(request)

    return builder.Output()


# ============================================================
# TCP
# ============================================================

def send_message(sock, payload):
    header = struct.pack(
        ">I",
        len(payload)
    )

    sock.sendall(header)
    sock.sendall(payload)


def read_reply_nonblocking(sock, reply_buffer):
    """
    Hyperion Replies nebenbei abholen.

    Die Funktion blockiert nicht.
    """

    while True:

        try:
            data = sock.recv(65536)

            if not data:
                raise ConnectionError(
                    "Hyperion hat Verbindung geschlossen"
                )

            reply_buffer.extend(data)

        except BlockingIOError:
            break

    # Replies aus dem Buffer entfernen.
    #
    # Format:
    #   4 Byte Laenge
    #   Payload

    while True:

        if len(reply_buffer) < 4:
            break

        size = struct.unpack(
            ">I",
            reply_buffer[:4]
        )[0]

        if size > 1024 * 1024:
            raise ConnectionError(
                f"Ungueltige Reply-Groesse: {size}"
            )

        if len(reply_buffer) < 4 + size:
            break

        del reply_buffer[:4 + size]


# ============================================================
# Hyperion Verbindung
# ============================================================

def connect_hyperion():

    while running:

        sock = None

        try:

            print(
                f"Verbinde mit Hyperion "
                f"{HYPERION_HOST}:{HYPERION_PORT} ..."
            )

            sock = socket.create_connection(
                (
                    HYPERION_HOST,
                    HYPERION_PORT
                ),
                timeout=3
            )

            # Wichtig:
            # Socket fuer den laufenden Betrieb
            # nicht mehr blockierend betreiben.

            sock.setblocking(False)

            print(
                "Hyperion FlatBuffer verbunden."
            )

            # Register senden
            register = build_register()

            send_message(
                sock,
                register
            )

            # Kurz Zeit geben, damit Hyperion
            # die Register-Reply erzeugen kann.
            time.sleep(0.01)

            reply_buffer = bytearray()

            try:
                read_reply_nonblocking(
                    sock,
                    reply_buffer
                )
            except BlockingIOError:
                pass

            print(
                f"Hyperion registriert: "
                f"priority={PRIORITY}, "
                f"origin={ORIGIN}"
            )

            return sock, reply_buffer

        except (
            OSError,
            ConnectionError
        ) as e:

            print(
                f"Hyperion nicht erreichbar: {e}"
            )

            if sock is not None:

                try:
                    sock.close()
                except Exception:
                    pass

            if running:
                time.sleep(1)

    return None, None


# ============================================================
# FFmpeg
# ============================================================

def start_ffmpeg():

    cmd = [
        "ffmpeg",

        "-hide_banner",
        "-loglevel", "error",

        "-f", "kmsgrab",
        "-device", "/dev/dri/card1",

        "-framerate", str(FPS),

        "-i", "-",

        "-vf",
        (
            "hwdownload,"
            "format=bgr0,"
            f"scale={WIDTH}:{HEIGHT},"
            "format=rgb24"
        ),

        "-f", "rawvideo",
        "-pix_fmt", "rgb24",

        "pipe:1",
    ]

    print("Starte FFmpeg:")
    print(" ".join(cmd))
    print()

    return subprocess.Popen(
        cmd,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        bufsize=FRAME_SIZE * 4,
    )


# ============================================================
# Main
# ============================================================

def main():

    global running

    print()
    print(
        "VDR KMSgrab -> Hyperion FlatBuffer"
    )
    print("-----------------------------------")
    print("/dev/dri/card1")
    print(f"{WIDTH}x{HEIGHT}")
    print(f"{FPS} fps")
    print(f"{FRAME_SIZE} Bytes RGB24")
    print(
        f"{HYPERION_HOST}:{HYPERION_PORT}"
    )
    print()

    ffmpeg = start_ffmpeg()

    sock = None
    reply_buffer = bytearray()

    frame_count = 0

    start_time = time.monotonic()
    last_report = start_time

    try:

        while running:

            # ------------------------------------------------
            # Verbindung herstellen
            # ------------------------------------------------

            if sock is None:

                sock, reply_buffer = (
                    connect_hyperion()
                )

                if sock is None:
                    break

            # ------------------------------------------------
            # Hyperion Replies nebenbei abholen
            # ------------------------------------------------

            try:

                read_reply_nonblocking(
                    sock,
                    reply_buffer
                )

            except (
                ConnectionError,
                OSError
            ) as e:

                print(
                    f"Hyperion-Verbindung verloren: {e}"
                )

                try:
                    sock.close()
                except Exception:
                    pass

                sock = None
                continue

            # ------------------------------------------------
            # Einen Frame lesen
            # ------------------------------------------------

            frame = ffmpeg.stdout.read(
                FRAME_SIZE
            )

            if len(frame) != FRAME_SIZE:

                print(
                    f"FFmpeg: erwartet "
                    f"{FRAME_SIZE} Bytes, "
                    f"erhalten {len(frame)}"
                )

                break

            # ------------------------------------------------
            # FlatBuffer bauen
            # ------------------------------------------------

            payload = build_image(frame)

            # ------------------------------------------------
            # Frame senden
            # ------------------------------------------------

            try:

                send_message(
                    sock,
                    payload
                )

                frame_count += 1

            except (
                BrokenPipeError,
                ConnectionResetError,
                ConnectionAbortedError,
                OSError
            ) as e:

                print(
                    f"Hyperion-Verbindung verloren: {e}"
                )

                try:
                    sock.close()
                except Exception:
                    pass

                sock = None

                continue

            # ------------------------------------------------
            # Statistik
            # ------------------------------------------------

            now = time.monotonic()

            if now - last_report >= 5:

                elapsed = (
                    now - start_time
                )

                real_fps = (
                    frame_count / elapsed
                    if elapsed > 0
                    else 0
                )

                print(
                    f"{frame_count} Frames | "
                    f"{real_fps:.1f} fps | "
                    f"{len(payload)} Byte FlatBuffer | "
                    f"Replies im Puffer: "
                    f"{len(reply_buffer)}"
                )

                last_report = now

    finally:

        running = False

        # ----------------------------------------------------
        # Priority freigeben
        # ----------------------------------------------------

        if sock is not None:

            try:

                clear = build_clear()

                send_message(
                    sock,
                    clear
                )

            except Exception:
                pass

            try:
                sock.close()
            except Exception:
                pass

        # ----------------------------------------------------
        # FFmpeg beenden
        # ----------------------------------------------------

        try:

            ffmpeg.terminate()
            ffmpeg.wait(timeout=2)

        except Exception:

            try:
                ffmpeg.kill()
            except Exception:
                pass

        print()
        print("Grabber beendet.")


if __name__ == "__main__":
    main()

Außer hyperion-ng müssen folgende MLD-Pakete installiert sein:
  • ffmpeg
  • python3-pip
dann im Terminal:
Code: [Select]
python3 -m pip install flatbuffers
python3 /usr/share/hyperion/vdr-kmsgrab-flatbuf.py

Antwort etwa:
Code: [Select]
VDR KMSgrab -> Hyperion FlatBuffer
-----------------------------------
/dev/dri/card1
160x90
25 fps
43200 Bytes RGB24
127.0.0.1:19400

Starte FFmpeg:
...

Verbinde mit Hyperion 127.0.0.1:19400 ...
Hyperion FlatBuffer verbunden.
Hyperion registriert: priority=100, origin=VDR-KMSgrab
124 Frames | 24.7 fps | 43272 Byte FlatBuffer | Replies im Puffer: 0
249 Frames | 24.8 fps | 43272 Byte FlatBuffer | Replies im Puffer: 0
...

Die in Hyperion eingestellten LEDs sollten je nach Bildschirminhalt leuchten.

Wenn alles funktioniert am besten einen systemd-Service anlegen, der das Script beim Booten startet:
Code: [Select]
nano /etc/systemd/system/vdr-kmsgrab.service
Inhalt:

Code: [Select]
[Unit]
Description=VDR KMSgrab to Hyperion FlatBuffer
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=/usr/bin/python3 -u /usr/share/hyperion/vdr-kmsgrab-flatbuf.py
Restart=always
RestartSec=3

[Install]
WantedBy=multi-user.target

systemd neu laden
Code: [Select]
systemctl daemon-reload
Dienst testweise starten
Code: [Select]
systemctl start vdr-kmsgrab.service
Dann:
Code: [Select]
systemctl status vdr-kmsgrab.service
Ausgabe etwa:
Code: [Select]
Active: active (running)
Live-Log ansehen:
Code: [Select]
journalctl -u vdr-kmsgrab.service -f
Es sollten die bisherigen Meldungen erscheinen:
Code: [Select]
Hyperion FlatBuffer verbunden.
Hyperion registriert: priority=100, origin=VDR-KMSgrab
124 Frames | 24.7 fps | 43272 Byte FlatBuffer
249 Frames | 24.8 fps | 43272 Byte FlatBuffer
...

Wenn das funktioniert und die LEDs leuchten, automatischen Start aktivieren:
Code: [Select]
systemctl enable vdr-kmsgrab.service
Oder direkt aktivieren und starten:
Code: [Select]
systemctl enable --now vdr-kmsgrab.service
Damit läuft das Grabber-Script nach einem Neustart automatisch.

Ich wollte diesen Text eigentlich im Wiki ergänzen, aber die Syntax dort schreckt mich ab!

Schöne Grüße
Uwe
Test-VDR: ASRock J3455M (Intel-Grafik), 4GB RAM, 60GB SSD, Cine S2 PCIe twin tuner + DuoFlex an Gigablue Ultra SCR LNB

Online clausmuus

  • Administrator
  • Expert Member
  • ********
  • Posts: 21531
    • View Profile
    • ClausMuus.de
Welche Video-Ausgabe für Hyperion?
« Reply #4 on: Today at 08:37:30 »
Gute Anleitung!
Magst Du auf der Hyperion Wiki Seite einen Link zu diesem Forum Post hinzufügen (oder die ganze Anleitung dort einfügen), damit die nicht verloren geht.
MLD 5.5 - Raspberry PI - 7" Touch TFT - Squeeze Play
MLD 6.5 - lirc yaUsbIR - 4 x DD-Sat - Intel N100M - 4GB RAM - 64GB SSD + 12TB HDD + 22TB HDD - Lian Li PC-C37B - Samsung LE40A559

Offline videopix

  • Member
  • **
  • Posts: 85
    • View Profile
Welche Video-Ausgabe für Hyperion?
« Reply #5 on: Today at 15:12:21 »
Inzwischen musste ich feststellen, dass die GPU nach dem Booten mal als card0 und mal als card1 firmiert.
Code: [Select]
ls -l /dev/dri/by-pathliefert
Code: [Select]
pci-0000:00:02.0-card -> ../card0
Damit ist der stabile Pfad zur Karte
Code: [Select]
/dev/dri/by-path/pci-0000:00:02.0-card
Die letzte Zeile in meinem .yml habe ich also ersetzt durch
Code: [Select]
- /dev/dri/by-path/pci-0000:00:02.0-card:/dev/dri/card0und im Script bei dem FFmpeg-Kommando steht jetzt ebenfalls
Code: [Select]
"-device", "/dev/dri/by-path/pci-0000:00:02.0-card",
Das sollte die GPU wirklich bei jedem Boot finden.
Test-VDR: ASRock J3455M (Intel-Grafik), 4GB RAM, 60GB SSD, Cine S2 PCIe twin tuner + DuoFlex an Gigablue Ultra SCR LNB

[1] MLD-6.x / General / Welche Video-Ausgabe für Hyperion?
 



Users Online Users Online

0 Members and 1 Guest are viewing this topic.