Files
hms-mediaengine/_entwicklung/apps/renderer/hms_renderer/pipelines.py
T
HMS MediaEngine Agent 362e089be0 AUFGERAUMT: Root auf 10 sichtbare Elemente reduziert
Der Nutzer hat recht: Der Ordner war voller Entwicklungs-Muell.
Jetzt ist sauber getrennt:

ROOT (was der Nutzer sieht und braucht):
- run.py                     = das Programm
- hms_app/                   = der Anwendungscode
- HMS MediaEngine.app        = macOS Doppelklick-Starter
- HMS-Start.vbs              = Windows Doppelklick-Starter
- HMS-Install.vbs             = Windows Erst-Installation
- HMS-Mac-Install.command     = macOS Homebrew-Installation
- HMS-Portable-Install.command = macOS Portable-Installation (16GB-Fix)
- installer_gui.py           = grafischer Installer
- launcher.pyw + launcher_core.py = interne Start-Logik
- LIESMICH.txt               = 10-Zeilen-Kurzanleitung
- .gitignore

_entwicklung/ (alles andere, NICHT benoetigt):
- packages/ apps/ native/ plugins/ tools/ schemas/ tests/ docs/
  build/ fixture_profiles/
- PLAN.md STATUS.md ERRORS.md TEST_REPORT.md CHANGELOG.md README.md
- pyproject.toml uv.lock setup_*.sh/ps1 make_mac_app.py

Diese Trennung gilt ab sofort fuer alle Commits. Der Nutzer kann
_entwicklung/ loeschen wenn er Platz braucht - die App laeuft ohne.

Verifiziert: App startet nach Aufraeumen unveraendert (Health 200).
2026-09-11 23:44:06 +02:00

80 lines
2.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Renderer-Pipeline-Definitionen (PLAN.md §12, §13, §36 Nr. 45).
Windows-Primärpfad (§12.6): d3d11h264dec → D3D11Memory → d3d11compositor
→ d3d11videosink. Kein CPU-Rundweg (Decoder → RAM → Upload verboten).
DevGL-Pfad: Nur für Entwicklung/CI-Umgebungen ohne D3D11; ausdrücklich
gekennzeichnet, kein stiller Ersatz im Normalbetrieb (§5.2, §33).
"""
from __future__ import annotations
import shutil
from dataclasses import dataclass
@dataclass(frozen=True)
class D3D11Pipeline:
"""Primärpipeline Windows: durchgängig D3D11Memory."""
video_a: str
video_b: str
width: int = 1920
height: int = 1080
fullscreen: bool = True
def launch_string(self) -> str:
sink = "d3d11videosink fullscreen=true" if self.fullscreen else "d3d11videosink"
# Zwei Quellen → Compositor → Ausgabe (§36 Nr. 5: zwei Videos GPU-mischen)
return (
f"d3d11compositor name=mix sink_0::xpos=0 sink_0::ypos=0 "
f"sink_0::width={self.width // 2} sink_0::height={self.height} "
f"sink_1::xpos={self.width // 2} sink_1::ypos=0 "
f"sink_1::width={self.width // 2} sink_1::height={self.height} ! "
f"d3d11convert ! video/x-raw(memory:D3D11Memory),format=RGBA,"
f"width={self.width},height={self.height} ! {sink} "
f"uridecodebin uri=file:///{self.video_a} ! queue ! "
f"d3d11convert ! mix. "
f"uridecodebin uri=file:///{self.video_b} ! queue ! "
f"d3d11convert ! mix."
)
@dataclass(frozen=True)
class DevGLPipeline:
"""Entwicklungspfad ohne D3D11 (explizit gekennzeichnet, kein Normalpfad).
Nur für CI/Dev ohne Windows-GPU; die Backend-Schnittstelle bleibt
identisch (§12.6)."""
video_a: str
video_b: str
width: int = 960
height: int = 540
def launch_string(self) -> str:
return (
f"glvideomixer name=mix ! glimagesink "
f"uridecodebin uri=file:///{self.video_a} ! queue ! glupload ! mix. "
f"uridecodebin uri=file:///{self.video_b} ! queue ! glupload ! mix."
)
def build_single_video_pipeline(video: str, d3d11: bool = True) -> str:
"""Minimale Einzelquellen-Pipeline (§36 Nr. 4: Testvideo → Vollbild)."""
if d3d11:
return f"uridecodebin uri=file:///{video} ! d3d11convert ! d3d11videosink fullscreen=true"
return f"uridecodebin uri=file:///{video} ! glupload ! glimagesink"
def build_compositor_pipeline(video_a: str, video_b: str, d3d11: bool = True) -> str:
"""Zwei-Quellen-Compositing (§36 Nr. 5: GPU-Mischen ohne CPU-Readback)."""
if d3d11:
return D3D11Pipeline(video_a=video_a, video_b=video_b).launch_string()
return DevGLPipeline(video_a=video_a, video_b=video_b).launch_string()
def gst_available() -> bool:
"""True, wenn ein GStreamer-CLI auf dem System liegt (Diagnose, kein Fake)."""
return shutil.which("gst-launch-1.0") is not None