fix(embedded-mpv): enable private Snap shared memory

This commit is contained in:
4gray committed 2026-07-17 22:42:37 +02:00
1 parent cc18cae320
commit 08e92d4f27
5 files changed
+120 -11

No files matched your search

@@ -653,10 +653,20 @@ std::string libmpvClientApiVersion() {
std::to_string(version & 0xffff);
}
std::string runtimeProbeShmName() {
#if defined(_WIN32)
const uint64_t processId = (uint64_t)GetCurrentProcessId();
#else
const uint64_t processId = (uint64_t)getpid();
#endif
return "/impv-fc-runtime-probe-" + std::to_string(processId);
}
/*
* Bounded startup capability probe: initialize an idle libmpv client and
* create the platform GL + mpv OpenGL render contexts. It deliberately does
* not create an FBO/shm ring, open media, or enter either command loop.
* create the platform GL + mpv OpenGL render contexts, then create, validate,
* and destroy a minimal shared-memory ring. It deliberately does not create
* an FBO, open media, or enter either command loop.
*/
int runRuntimeProbe() {
mpv_handle* mpv = mpv_create();
@@ -711,6 +721,31 @@ int runRuntimeProbe() {
return runtimeProbeFailure("mpv-render-context-failed", renderError);
}
frame_helper::ShmRing runtimeProbeRing;
const std::string shmName = runtimeProbeShmName();
if (!runtimeProbeRing.create(shmName, 16, 16, 1)) {
runtimeProbeRing.destroy();
mpv_render_context_free(renderContext);
gl.destroy();
mpv_terminate_destroy(mpv);
return runtimeProbeFailure("shared-memory-create-failed");
}
const bool sharedMemoryInitialized =
runtimeProbeRing.base != nullptr &&
runtimeProbeRing.header != nullptr &&
runtimeProbeRing.header->magic == FRAME_SHM_MAGIC &&
runtimeProbeRing.header->version == FRAME_SHM_VERSION &&
runtimeProbeRing.header->width == 16 &&
runtimeProbeRing.header->height == 16 &&
runtimeProbeRing.header->generation == 1;
runtimeProbeRing.destroy();
if (!sharedMemoryInitialized) {
mpv_render_context_free(renderContext);
gl.destroy();
mpv_terminate_destroy(mpv);
return runtimeProbeFailure("shared-memory-initialize-failed");
}
const std::string libmpvVersion = libmpvClientApiVersion();
mpv_render_context_free(renderContext);
gl.destroy();
@@ -32,6 +32,16 @@ describe('Embedded MPV native source recording invariants', () => {
),
'utf8'
);
const electronBuilderConfig = JSON.parse(
readFileSync(
path.resolve(__dirname, '../../../../../electron-builder.json'),
'utf8'
)
) as {
snap?: {
plugs?: unknown;
};
};
const stageRuntimeSource = readFileSync(
path.resolve(
__dirname,
@@ -778,12 +788,29 @@ describe('Embedded MPV native source recording invariants', () => {
);
});
it('runs the helper runtime probe without shared memory, media, or command loops', () => {
it('keeps Electron Builder defaults and requests private Snap shared memory', () => {
expect(electronBuilderConfig.snap?.plugs).toEqual([
'default',
{
'shared-memory': {
interface: 'shared-memory',
private: true,
},
},
]);
});
it('runs the helper runtime probe through shared memory without media or command loops', () => {
const runtimeProbe = sourceFunctionBody(
frameHelperSource,
'int runRuntimeProbe(',
'runRuntimeProbe'
);
const runtimeProbeShmName = sourceFunctionBody(
frameHelperSource,
'std::string runtimeProbeShmName(',
'runtimeProbeShmName'
);
const main = sourceFunctionBody(frameHelperSource, 'int main(', 'main');
const runtimeProbeFailure = sourceFunctionBody(
frameHelperSource,
@@ -804,12 +831,30 @@ describe('Embedded MPV native source recording invariants', () => {
expect(runtimeProbe).toContain('mpv_render_context_free(');
expect(runtimeProbe).toContain('gl.destroy()');
expect(runtimeProbe).toContain('mpv_terminate_destroy(mpv)');
expect(runtimeProbe).toContain(
'frame_helper::ShmRing runtimeProbeRing;'
);
expect(runtimeProbe).toContain(
'const std::string shmName = runtimeProbeShmName();'
);
expect(runtimeProbe).toContain(
'runtimeProbeRing.create(shmName, 16, 16, 1)'
);
expect(runtimeProbe).toContain(
'runtimeProbeRing.header->magic == FRAME_SHM_MAGIC'
);
expect(runtimeProbe).toContain('runtimeProbeRing.destroy();');
expect(runtimeProbe).toContain('"shared-memory-create-failed"');
expect(runtimeProbe).toContain('"shared-memory-initialize-failed"');
expect(runtimeProbeShmName).toContain('"/impv-fc-runtime-probe-"');
expect(runtimeProbeShmName).toContain('getpid()');
expect(runtimeProbeShmName).toContain('GetCurrentProcessId()');
expect(runtimeProbeShmName).toContain('std::to_string(processId)');
expect(runtimeProbe).toContain('.num("protocol", 1)');
expect(runtimeProbe).toContain('.boolean("usable", true)');
expect(runtimeProbe).toContain('.str("libmpv",');
expect(runtimeProbe).toContain('.str("renderApi", gl.renderApiName())');
expect(runtimeProbe).not.toContain('pipeline');
expect(runtimeProbe).not.toContain('shm');
expect(runtimeProbe).not.toContain('runStdinLoop');
expect(runtimeProbe).not.toContain('runMpvEventLoop');
expect(runtimeProbe).not.toContain('loadfile');
+16 -4
View File
@@ -241,10 +241,22 @@ has succeeded. On Linux x64 that decision validates the profile manifest,
regular-file/access modes, the complete declared bundled closure and hashes,
then runs `iptvnator_mpv_helper --runtime-probe` with a three-second timeout.
The probe loads dependencies through the normal ELF loader, initializes an
idle libmpv client, and creates EGL/OpenGL plus mpv render contexts without
opening media or shared memory. It must emit exactly one protocol-v1 JSON line
and return zero. Bundled profiles prepend only their packaged `native/lib` to
`LD_LIBRARY_PATH`; the system profile never injects a private loader path.
idle libmpv client, creates EGL/OpenGL plus mpv render contexts, then
creates, maps, validates, and destroys a minimal `16x16` shared-memory ring
named `/impv-fc-runtime-probe-<pid>`. It never opens media or enters the media
or command loops. Shared-memory creation/mapping and header-initialization
failures emit the stable helper reasons `shared-memory-create-failed` and
`shared-memory-initialize-failed`. The probe must emit exactly one protocol-v1
JSON line and return zero. Bundled profiles prepend only their packaged
`native/lib` to `LD_LIBRARY_PATH`; the system profile never injects a private
loader path.
The strict Snap keeps Electron Builder's default plugs and adds an
auto-connected private `shared-memory` plug. Private shared memory gives the
app a confined, snap-specific POSIX shm namespace rather than global
cross-snap access. Consequently the installed-Snap `--runtime-probe` validates
the actual confinement and shm lifecycle required by frame-copy, not only the
loader and graphics contexts.
Packaged discovery is limited to packaged resource locations and never falls
through to writable cwd/dist development paths. A disabled base experiment or
any failed capability check keeps the renderer sandbox enabled and falls back
+9
View File
@@ -139,6 +139,15 @@
"grade": "stable",
"summary": "IPTV application for M3U playlists, Xtream Codes API, and Stalker portals",
"executableArgs": ["--ozone-platform=x11"],
"plugs": [
"default",
{
"shared-memory": {
"interface": "shared-memory",
"private": true
}
}
],
"environment": {
"DISABLE_WAYLAND": "1"
}
+11 -3
View File
@@ -114,6 +114,10 @@ The DEB metadata requires `libmpv2` and is release-tested on Ubuntu 24.04
(Noble). Ubuntu 22.04 (Jammy) only provides `libmpv1`; use the x64 AppImage on
that distribution rather than relaxing the runtime contract.
The strict Snap retains Electron Builder's default plugs and adds an
auto-connected private `shared-memory` plug. This supplies a snap-specific
POSIX shm namespace without granting global cross-snap shared-memory access.
Profiles cannot share one Electron Builder pass because its targets reuse the
same unpacked application directory. A missing or unsupported profile, or a
target from another profile, fails packaging.
@@ -147,9 +151,13 @@ iptvnator_mpv_helper --runtime-probe
```
The bounded probe initializes idle libmpv plus EGL/OpenGL and mpv render
contexts without media or shared memory. A timeout, loader failure, malformed
protocol, missing file, hash mismatch, or unusable graphics path returns a
stable reason and keeps the BrowserWindow sandbox enabled.
contexts, then creates, maps, validates, and destroys a minimal `16x16`
shared-memory ring named `/impv-fc-runtime-probe-<pid>`. It does not open media
or enter media/command loops. A timeout, loader failure, malformed protocol,
missing file, hash mismatch, unusable graphics path, or shm lifecycle failure
returns a stable reason and keeps the BrowserWindow sandbox enabled. The
installed-Snap probe therefore tests the private shared-memory confinement
needed by playback rather than only loader and graphics startup.
## CI And Source Distribution