retro_run() queries RETRO_ENVIRONMENT_GET_AUDIO_VIDEO_ENABLE but does not
check whether the call succeeded. That environment call is optional
(47 | RETRO_ENVIRONMENT_EXPERIMENTAL). On a frontend that does not implement
it, the callback returns false and never writes data, so
audio_video_enable keeps its initial value of 0. Both output paths are then
gated off permanently.
libretro.h states the contract explicitly:
@returns true if the environment call is available, regardless of the
value output to data.
If false, the core should assume that the frontend will not skip any
steps.
The core does the opposite. RetroArch implements the call and returns 3,
which is why this is invisible there.
Affected frontend in my case: Kodi 21.3 "Omega" via the game.libretro
bridge (21.0.7.3). grep AUDIO_VIDEO_ENABLE returns no match in that bridge's
LibretroEnvironment.cpp on the Matrix, Nexus, Omega or Piers branches, so the
call falls through to default: return false;.
Expected behavior
The core delivers video frames and audio to the frontend, as it does under
RetroArch.
Actual behavior
The ScummVM engine runs completely — it loads the game, accepts input, updates
scummvm.ini, and writes autosaves — while the frontend receives no frame and
no audio buffer at all. The screen stays black and there is no sound. No error
path is taken, so nothing is logged and the failure is silent.
The code
backends/platform/libretro/src/libretro-core.cpp, retro_run(), lines
1062–1087 at commit 7310d4e9f5d11553c6c5499911bd2f9b8ff3db3b (the commit
pinned by LibreELEC 12.0.2 and 12.2.1). Identical in current master at
line 1148.
/* Setting RA's video or audio driver to null will disable video/audio bits */
int audio_video_enable = 0;
environ_cb(RETRO_ENVIRONMENT_GET_AUDIO_VIDEO_ENABLE, &audio_video_enable);
if (g_system) {
/* Switch to ScummVM thread */
retro_switch_to_emu_thread();
...
/* Retrieve audio */
if (audio_video_enable & 2)
audio_run();
/* Retrieve video */
if (audio_video_enable & 1) {
if (video_hw_mode & VIDEO_GRAPHIC_MODE_REQUEST_SW) {
const Graphics::ManagedSurface *screen;
LIBRETRO_G_SYSTEM->getScreen(screen);
video_cb(screen->getPixels(), screen->w, screen->h, screen->pitch);
} else
video_cb(RETRO_HW_FRAME_BUFFER_VALID, ...);
}
poll_cb();
LIBRETRO_G_SYSTEM->processInputs();
}
These are the only two video_cb() call sites and the only
audio_run() call site in the core; all three are behind the gate.
retro_switch_to_emu_thread() is called before it, which is why the engine
keeps running normally.
Steps to reproduce the bug
- Use a frontend that does not implement
RETRO_ENVIRONMENT_GET_AUDIO_VIDEO_ENABLE (e.g. Kodi's RetroPlayer via
game.libretro).
- Load any ScummVM game.
- Observe: black screen, no audio, no error message. Input is accepted.
- Wait ~5 minutes and check the save directory — an autosave appears, proving
the engine is running.
Evidence that the engine runs while nothing is delivered
Controlled 8-minute run of Day of the Tentacle, sampled every 30 s:
tentacle.s00 grew 17,852 → 20,166 bytes at +330 s, gzip signature 1f 8b 08
(matching ScummVM's 300 s default autosave period).
scummvm.ini lastselectedgame changed from sky to tentacle.
- Two CPU cores at roughly 20 % throughout.
- ALSA (
/proc/asound/card*/pcm*p/sub*/status) reported no stream for the whole
run, while a control core produced RUNNING.
- Kodi never logged
RetroPlayer[RENDER]: Initializing render manager. That
line comes from CRPRenderManager::Initialize(), reached only from the
CRetroPlayerVideo constructor, which is only constructed when the game
client opens a video stream. No video_cb() call means no stream, so the
renderer is never configured. Audio is opened lazily on the first
AddFrames_S16NE, so no audio_run() means no audio stream either — both
symptoms follow from the single gate.
Control cores on the same device at the same log level — mrboom, 2048 and
dosbox — all render and play audio immediately. DOSBox is the relevant
control: it is likewise a framework core with variable resolution, it simply
calls video_cb() unconditionally.
Proposed fix — built and verified
int audio_video_enable = RETRO_AV_ENABLE_VIDEO | RETRO_AV_ENABLE_AUDIO;
if (!environ_cb(RETRO_ENVIRONMENT_GET_AUDIO_VIDEO_ENABLE, &audio_video_enable))
audio_video_enable = RETRO_AV_ENABLE_VIDEO | RETRO_AV_ENABLE_AUDIO;
I cross-compiled commit 7310d4e for aarch64 with exactly this change (using
the literal bitmask 1 | 2, since libretro.h is fetched as a submodule at
build time) and installed it on the affected device. Same 30-second test,
same method:
|
stock core |
patched core |
RENDER: Renderer configured on first frame |
absent |
present |
RENDER: Configuring format |
absent |
RGB565, 1280x720 |
AUDIO: Creating audio stream |
absent |
48000 Hz, 2 channels |
| ALSA over 30 s |
no stream |
RUNNING throughout |
| Screenshot |
black |
game renders |
Two screenshots taken at different moments show different frames, so this is
live rendering rather than a single stuck frame.
Failed to set up hardware rendering, falling back to software. still appears
with the patched core, as expected — that message comes from
retro_set_environment(), the software fallback works correctly, and it was
never related to this bug.
Not the cause (ruled out)
- Hardware rendering.
scummvm_video_hw_acceleration lives inside the
& 1 branch and can never take effect while the gate is closed.
- Core version. Commit
6fa7403b8b1b6e18e3a3d02120b38aad6a73ad26 already
contains the gate at line 1089, as do v2.8.0, v2.8.1 and v2.9.0. Downgrading
within the current range cannot help.
- Game data, file paths, addon API versions, missing libraries, reported
framerate/geometry — all verified identical or unremarkable (60.0 fps,
48000 Hz, no missing ldd entries, no core linking OpenGL).
For context: LibreELEC ≤ 11.x built this core from the now-deleted
kodi-game/scummvm fork, whose older monolithic backend called video_cb()
unconditionally. LibreELEC 12 switched to libretro/scummvm, which is when the
regression reached Kodi users — the addon version number rose smoothly and gave
no hint that the upstream source had changed.
Version/Commit
- ScummVM core:
2.10.0.47.3, reported as 8345cda-2.10.0git; also reproduced
with 2.7.0.38.3
- Frontend: not RetroArch — Kodi 21.3 "Omega" with
game.libretro 21.0.7.3
- Platform: LibreELEC 12.2.1, Raspberry Pi 4, aarch64
I am happy to test any patched build on this setup.
retro_run()queriesRETRO_ENVIRONMENT_GET_AUDIO_VIDEO_ENABLEbut does notcheck whether the call succeeded. That environment call is optional
(
47 | RETRO_ENVIRONMENT_EXPERIMENTAL). On a frontend that does not implementit, the callback returns
falseand never writesdata, soaudio_video_enablekeeps its initial value of0. Both output paths are thengated off permanently.
libretro.hstates the contract explicitly:The core does the opposite. RetroArch implements the call and returns
3,which is why this is invisible there.
Affected frontend in my case: Kodi 21.3 "Omega" via the
game.libretrobridge (21.0.7.3).
grep AUDIO_VIDEO_ENABLEreturns no match in that bridge'sLibretroEnvironment.cppon the Matrix, Nexus, Omega or Piers branches, so thecall falls through to
default: return false;.Expected behavior
The core delivers video frames and audio to the frontend, as it does under
RetroArch.
Actual behavior
The ScummVM engine runs completely — it loads the game, accepts input, updates
scummvm.ini, and writes autosaves — while the frontend receives no frame andno audio buffer at all. The screen stays black and there is no sound. No error
path is taken, so nothing is logged and the failure is silent.
The code
backends/platform/libretro/src/libretro-core.cpp,retro_run(), lines1062–1087 at commit
7310d4e9f5d11553c6c5499911bd2f9b8ff3db3b(the commitpinned by LibreELEC 12.0.2 and 12.2.1). Identical in current
masteratline 1148.
These are the only two
video_cb()call sites and the onlyaudio_run()call site in the core; all three are behind the gate.retro_switch_to_emu_thread()is called before it, which is why the enginekeeps running normally.
Steps to reproduce the bug
RETRO_ENVIRONMENT_GET_AUDIO_VIDEO_ENABLE(e.g. Kodi's RetroPlayer viagame.libretro).the engine is running.
Evidence that the engine runs while nothing is delivered
Controlled 8-minute run of Day of the Tentacle, sampled every 30 s:
tentacle.s00grew 17,852 → 20,166 bytes at +330 s, gzip signature1f 8b 08(matching ScummVM's 300 s default autosave period).
scummvm.inilastselectedgamechanged fromskytotentacle./proc/asound/card*/pcm*p/sub*/status) reported no stream for the wholerun, while a control core produced
RUNNING.RetroPlayer[RENDER]: Initializing render manager. Thatline comes from
CRPRenderManager::Initialize(), reached only from theCRetroPlayerVideoconstructor, which is only constructed when the gameclient opens a video stream. No
video_cb()call means no stream, so therenderer is never configured. Audio is opened lazily on the first
AddFrames_S16NE, so noaudio_run()means no audio stream either — bothsymptoms follow from the single gate.
Control cores on the same device at the same log level —
mrboom,2048anddosbox— all render and play audio immediately. DOSBox is the relevantcontrol: it is likewise a framework core with variable resolution, it simply
calls
video_cb()unconditionally.Proposed fix — built and verified
I cross-compiled commit
7310d4efor aarch64 with exactly this change (usingthe literal bitmask
1 | 2, sincelibretro.his fetched as a submodule atbuild time) and installed it on the affected device. Same 30-second test,
same method:
RENDER: Renderer configured on first frameRENDER: Configuring formatRGB565, 1280x720AUDIO: Creating audio stream48000 Hz, 2 channelsRUNNINGthroughoutTwo screenshots taken at different moments show different frames, so this is
live rendering rather than a single stuck frame.
Failed to set up hardware rendering, falling back to software.still appearswith the patched core, as expected — that message comes from
retro_set_environment(), the software fallback works correctly, and it wasnever related to this bug.
Not the cause (ruled out)
scummvm_video_hw_accelerationlives inside the& 1branch and can never take effect while the gate is closed.6fa7403b8b1b6e18e3a3d02120b38aad6a73ad26alreadycontains the gate at line 1089, as do v2.8.0, v2.8.1 and v2.9.0. Downgrading
within the current range cannot help.
framerate/geometry — all verified identical or unremarkable (60.0 fps,
48000 Hz, no missing
lddentries, no core linking OpenGL).For context: LibreELEC ≤ 11.x built this core from the now-deleted
kodi-game/scummvmfork, whose older monolithic backend calledvideo_cb()unconditionally. LibreELEC 12 switched to
libretro/scummvm, which is when theregression reached Kodi users — the addon version number rose smoothly and gave
no hint that the upstream source had changed.
Version/Commit
2.10.0.47.3, reported as8345cda-2.10.0git; also reproducedwith
2.7.0.38.3game.libretro21.0.7.3I am happy to test any patched build on this setup.