86 lines
3.0 KiB
Python
86 lines
3.0 KiB
Python
"""Frozen-process gate for the Qt multimedia runtime.
|
|
|
|
PyInstaller executes this file as a runtime hook. Normal application starts
|
|
are untouched; ``--media-smoke-test`` exits before the application entry point
|
|
after proving that the frozen Qt multimedia modules and a decoder backend can
|
|
be loaded in an offscreen Qt event loop.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
|
|
MEDIA_SMOKE_ARGUMENT = "--media-smoke-test"
|
|
|
|
|
|
def _emit(message: str, *, error: bool = False) -> None:
|
|
# Windowed PyInstaller executables set stdout/stderr to None on Windows.
|
|
# The exit status is the gate contract; diagnostics are best-effort.
|
|
stream = sys.stderr if error else sys.stdout
|
|
if stream is not None:
|
|
print(message, file=stream, flush=True)
|
|
|
|
|
|
def _run_media_smoke_gate() -> None:
|
|
# Keep these imports inside the gate. Missing frozen extension modules or
|
|
# linked Qt multimedia libraries must make this process fail, while normal
|
|
# launches retain the application's existing fallback behaviour.
|
|
from PySide6.QtCore import QTimer
|
|
from PySide6.QtMultimedia import QAudioOutput, QMediaFormat, QMediaPlayer
|
|
from PySide6.QtMultimediaWidgets import QVideoWidget
|
|
from PySide6.QtWidgets import QApplication
|
|
|
|
application = QApplication.instance()
|
|
owns_application = application is None
|
|
if application is None:
|
|
application = QApplication(["DoctorWorkstationMediaSmoke"])
|
|
|
|
player = QMediaPlayer()
|
|
audio_output = QAudioOutput()
|
|
video_widget = QVideoWidget()
|
|
player.setAudioOutput(audio_output)
|
|
player.setVideoOutput(video_widget)
|
|
|
|
if not player.isAvailable():
|
|
raise RuntimeError("Qt reports that no multimedia backend is available")
|
|
|
|
decode_formats = QMediaFormat().supportedFileFormats(QMediaFormat.ConversionMode.Decode)
|
|
if not decode_formats:
|
|
raise RuntimeError("Qt multimedia loaded without a supported decoder format")
|
|
|
|
video_widget.resize(16, 16)
|
|
video_widget.show()
|
|
QTimer.singleShot(0, application.quit)
|
|
event_status = application.exec()
|
|
video_widget.close()
|
|
player.setVideoOutput(None)
|
|
player.setAudioOutput(None)
|
|
if event_status != 0:
|
|
raise RuntimeError(f"Qt multimedia offscreen event loop exited with {event_status}")
|
|
|
|
# QApplication cannot be recreated safely in-process. The runtime hook is
|
|
# a one-shot frozen gate, but retaining this distinction makes direct test
|
|
# imports predictable.
|
|
if owns_application:
|
|
application.processEvents()
|
|
|
|
|
|
def _dispatch() -> int | None:
|
|
if MEDIA_SMOKE_ARGUMENT not in sys.argv[1:]:
|
|
return None
|
|
try:
|
|
_run_media_smoke_gate()
|
|
except Exception as exc:
|
|
_emit(
|
|
f"Frozen Qt multimedia smoke gate failed: {type(exc).__name__}: {exc}",
|
|
error=True,
|
|
)
|
|
return 70
|
|
_emit("Frozen Qt multimedia smoke gate passed.")
|
|
return 0
|
|
|
|
|
|
_media_smoke_status = _dispatch()
|
|
if _media_smoke_status is not None:
|
|
raise SystemExit(_media_smoke_status)
|