Check the result of elf_aux_info() on FreeBSD and OpenBSD ARM and fall
back to no AES support if the call fails, rather than relying on the
output variable being left untouched.
The Qt UI continuously redrawing prevents libdecor-gtk's glib dispatch loop
from ever returning since it constantly posts new messages to draw again.
The result is that Moonlight randomly hangs (with the spinner still animating)
at the "Starting app..." phase of connection establishment and never proceeds
until the livelock is broken by loss of focus, hiding the window, or similar.
For posterity, the hanging stack:
QtWaylandClient::QWaylandWindow::waitForFrameSync (this=this@entry=..., timeout=timeout@entry=...) at qwaylandwindow.cpp:891
QtWaylandClient::QWaylandGLContext::swapBuffers (this=..., surface=...) at qwaylandglcontext.cpp:494
QRhiGles2::endFrame (this=..., swapChain=..., flags=...) at qrhigles2.cpp:2390
QRhi::endFrame (this=..., swapChain=..., flags=...) at qrhi.cpp:11600
QSGGuiThreadRenderLoop::renderWindow (this=..., window=...) at qsgrenderloop.cpp:714
QQuickWindow::event (this=..., event=...) at qquickwindow.cpp:1663
QCoreApplication::notifyInternal2 (receiver=..., event=...) at qcoreapplication.cpp:1126
QCoreApplication::sendEvent (receiver=..., event=...) at qcoreapplication.cpp:1571
QPlatformWindow::deliverUpdateRequest (this=...) at qplatformwindow.cpp:831
QObject::event (this=..., e=...) at qobject.cpp:1480
QCoreApplication::notifyInternal2 (receiver=..., event=...) at qcoreapplication.cpp:1126
QCoreApplication::sendEvent (receiver=..., event=...) at qcoreapplication.cpp:1571
QCoreApplicationPrivate::sendPostedEvents (receiver=..., event_type=..., data=...) at qcoreapplication.cpp:1926
postEventSourceDispatch (s=...) at qeventdispatcher_glib.cpp:248
g_main_dispatch (context=...) at gmain.c:3591
g_main_context_dispatch_unlocked (context=...) at gmain.c:4451
g_main_context_iterate_unlocked (context=context@entry=..., block=block@entry=..., dispatch=dispatch@entry=..., self=...) at gmain.c:4516
g_main_context_iteration (context=..., context@entry=..., may_block=may_block@entry=...) at gmain.c:4582
libdecor_plugin_gtk_dispatch (plugin=..., timeout=...) at libdecor-gtk.c:509
Wayland_PumpEvents (_this=...) at SDL_waylandevents.c:610
Wayland_GLES_LoadLibrary (_this=..., path=...) at SDL_waylandopengles.c:51
SDL_GL_LoadLibrary_REAL (path=...) at SDL_video.c:4735
SDL_RecreateWindow (window=..., flags=...) at SDL_video.c:2796
GLES2_CreateRenderer (renderer=..., window=..., create_props=...) at SDL_render_gles2.c:2329
SDL_CreateRendererWithProperties_REAL (props=props@entry=...) at SDL_render.c:1126
SDL_CreateRenderer_REAL (window=..., name=...) at SDL_render.c:1287
SDL_CreateRenderer_REAL (window=..., idx=..., flags=...) at sdl2_compat.c:5986
EGLRenderer::initialize (this=..., params=...) at eglvid.cpp:445
FFmpegVideoDecoder::initializeRendererInternal (this=..., renderer=..., params=...) at ffmpeg.cpp:323
FFmpegVideoDecoder::createFrontendRenderer (this=..., params=..., useAlternateFrontend=...) at ffmpeg.cpp:426
FFmpegVideoDecoder::completeInitialization (this=..., decoder=..., requiredFormat=..., params=..., testMode=..., useAlternateFrontend=...) at ffmpeg.cpp:486
FFmpegVideoDecoder::tryInitializeRenderer(AVCodec const*, AVPixelFormat, _DECODER_PARAMETERS*, AVCodecHWConfig const*, IFFmpegRenderer::InitFailureReason*, std::function<IFFmpegRenderer* ()>)
(this=..., decoder=..., requiredFormat=..., params=..., hwConfig=..., failureReason=..., createRendererFunc=...) at ffmpeg.cpp:1200
FFmpegVideoDecoder::tryInitializeHwAccelDecoder (this=..., params=..., pass=..., terminallyFailedHardwareDecoders=...) at ffmpeg.cpp:1544
FFmpegVideoDecoder::initialize (this=..., params=...) at ffmpeg.cpp:1701
Session::chooseDecoder (vds=..., renderer=..., window=..., videoFormat=..., width=..., height=..., frameRate=..., enableVsync=..., enableFramePacing=..., testOnly=..., chosenDecoder=...) at session.cpp:322
Session::getDecoderAvailability (window=..., vds=..., videoFormat=..., width=..., height=..., frameRate=...) at session.cpp:498
Session::initialize (this=..., qtWindow=...) at session.cpp:755
The AppImage bundles libva from the build environment (Ubuntu 22.04,
VA-API 1.20). VA-API driver modules (e.g. Mesa's radeonsi_drv_video.so)
are dlopen()ed by libva and export an entrypoint named after the libva
minor version they were built against (e.g. __vaDriverInit_1_23). An
older libva cannot load newer drivers, so on any host more recent than
the build container va_openDriver() fails:
Trying .../radeonsi_drv_video.so
libva error: has no function __vaDriverInit_1_0
va_openDriver() returns -1
and Moonlight silently falls back to software decoding (reproduced on
Fedora 44 / Mesa 26.1 / RX 6500 XT; overriding the bundled libva with
LD_PRELOAD of the host libva immediately yields HW decode). Refreshing
the bundled libva at each release only postpones the breakage: the host
drivers always eventually outrun it (see #1398).
Exclude libva and its backends from linuxdeploy so Moonlight and the
bundled FFmpeg resolve them through the loader's normal search path and
pick up the host libva, which the distro's package manager keeps in step
with the host drivers. This mirrors how Mesa/GL/EGL are already
host-provided.
Hosts without a usable libva (e.g. bare NVIDIA systems) must still not
fail to launch, since libva.so.2 is a direct DT_NEEDED of Moonlight and
the bundled FFmpeg. Stage the build-environment libva in
opt/libva-fallback (outside every loader search path) together with
libva-probe, a tiny program whose only job is to carry the same
DT_NEEDED entries and VA_API_* versioned symbol references as the
binaries shipped in the AppImage. The generated AppRun runs the probe
first: if the dynamic loader can start it against the host, the host
libva is used (probe passing is, by construction, exactly Moonlight
being loadable); if not, it prepends the fallback directory and launches
with the bundled libva, matching pre-existing behavior. The build script
verifies that the probe references every version node required by the
shipped binaries, so the two cannot silently drift apart.
We should be using the EGL renderer's colorspace and color range
rather than the backend renderer's for fallbacks. The former is
what actually gets passed to the host PC during stream init.