feat(embedded-mpv): add Windows frame-copy support (#1175)

Port the embedded mpv frame-copy pipeline to Windows with WGL rendering and named shared memory. Includes packaging validation, platform gates, tests, and architecture documentation.
This commit is contained in:
4gray authored and GitHub committed 2026-07-15 21:27:56 +02:00
1 parent 7d75d989e8
commit 59e08fd2d6
41 files changed
+1284 -157

No files matched your search

@@ -7,13 +7,30 @@
* memory cage forbids external ArrayBuffers over foreign memory.
*
* Plain C N-API (no node-addon-api) so it stays ABI-stable and trivial.
* macOS and Linux (POSIX shm) — other platforms export an empty object;
* the TypeScript side gates on platform before requiring it.
* macOS and Linux (POSIX shm) plus Windows (named file mapping; binding.gyp
* compiles this file as C++ there because MSVC's C mode has no
* <stdatomic.h>) — other platforms export an empty object; the TypeScript
* side gates on platform before requiring it.
*/
#define NAPI_VERSION 8
#include <node_api.h>
#if defined(__APPLE__) || defined(__linux__)
#if defined(__APPLE__) || defined(__linux__) || defined(_WIN32)
#if defined(_WIN32)
#include <stdint.h>
#include <string.h>
#include "../helper/frame_shm.h"
/* frame_shm.h typed the fields as std::atomic in C++ mode; keep the shared
* atomic_load_explicit call sites below spelling-compatible. */
using std::atomic_load_explicit;
using std::memory_order_acquire;
using std::memory_order_relaxed;
#else
#include <fcntl.h>
#include <stdatomic.h>
@@ -26,6 +43,8 @@
#include "../helper/frame_shm.h"
#endif
static FrameShmHeader* g_header = NULL;
static uint8_t* g_base = NULL;
static size_t g_size = 0;
@@ -35,7 +54,11 @@ static uint64_t now_ns(void) { return frame_shm_now_ns(); }
static void unmap_current(void) {
if (g_base) {
#if defined(_WIN32)
UnmapViewOfFile(g_base);
#else
munmap(g_base, g_size);
#endif
}
g_base = NULL;
g_header = NULL;
@@ -68,6 +91,23 @@ static napi_value Open(napi_env env, napi_callback_info info) {
return throw_error(env, "shm name must be a string");
}
#if defined(_WIN32)
char mapping_name[280];
frame_shm_windows_name(name, mapping_name, sizeof(mapping_name));
HANDLE mapping = OpenFileMappingA(FILE_MAP_READ, FALSE, mapping_name);
if (!mapping) return throw_error(env, "shm open failed (helper gone?)");
void* mapped = MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0);
/* The view holds its own reference to the section object. */
CloseHandle(mapping);
if (!mapped) return throw_error(env, "shm map failed");
MEMORY_BASIC_INFORMATION region;
if (VirtualQuery(mapped, &region, sizeof(region)) == 0 ||
region.RegionSize < sizeof(FrameShmHeader)) {
UnmapViewOfFile(mapped);
return throw_error(env, "shm segment too small");
}
const size_t segment_size = (size_t)region.RegionSize;
#else
const int fd = shm_open(name, O_RDONLY, 0);
if (fd < 0) return throw_error(env, "shm_open failed (helper gone?)");
struct stat segment;
@@ -80,17 +120,23 @@ static napi_value Open(napi_env env, napi_callback_info info) {
mmap(NULL, (size_t)segment.st_size, PROT_READ, MAP_SHARED, fd, 0);
close(fd);
if (mapped == MAP_FAILED) return throw_error(env, "mmap failed");
const size_t segment_size = (size_t)segment.st_size;
#endif
FrameShmHeader* header = (FrameShmHeader*)mapped;
if (header->magic != FRAME_SHM_MAGIC ||
header->version != FRAME_SHM_VERSION) {
munmap(mapped, (size_t)segment.st_size);
#if defined(_WIN32)
UnmapViewOfFile(mapped);
#else
munmap(mapped, segment_size);
#endif
return throw_error(env, "frame ring not initialized yet");
}
unmap_current();
g_base = (uint8_t*)mapped;
g_size = (size_t)segment.st_size;
g_size = segment_size;
g_header = header;
napi_value result;
@@ -204,7 +250,7 @@ static napi_value Init(napi_env env, napi_value exports) {
return exports;
}
#else /* neither __APPLE__ nor __linux__ */
#else /* no frame-copy port for this platform */
static napi_value Init(napi_env env, napi_value exports) {
/* Frame-copy is not ported to this platform yet; loading this module