更新
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
"""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)
|
||||
Reference in New Issue
Block a user