From a44a46b9f5a8136d06450ba20fb42cd25600d35c Mon Sep 17 00:00:00 2001 From: deeplakes Date: Thu, 20 Aug 2026 12:11:21 -0300 Subject: [PATCH] screen_recorder: add encoder setting with CPU fallback GPUs with no hardware video encoder at all (Asahi/Apple Silicon, some VMs) fail vaInitialize during the encoder probe and the recording aborts with no output file. Adds an "encoder" setting (gpu/cpu, default gpu) so users on such hardware can force software encoding, and always passes -fallback-cpu-encoding yes so a hardware encoder that fails at runtime degrades gracefully instead of aborting the recording for anyone. --- screen_recorder/README.md | 1 + screen_recorder/plugin.toml | 14 +++++++++++++- screen_recorder/recorder_service.luau | 17 +++++++++++++++-- screen_recorder/translations/en.json | 8 ++++++++ 4 files changed, 37 insertions(+), 3 deletions(-) diff --git a/screen_recorder/README.md b/screen_recorder/README.md index aea5b55..ca116f1 100644 --- a/screen_recorder/README.md +++ b/screen_recorder/README.md @@ -67,6 +67,7 @@ Replay controls are available only when `replay_enabled` is true. | `replay_duration` | `int` | `30` | Replay buffer duration in seconds. | | `replay_storage` | `select` | `ram` | Stores replay data in RAM or on disk. | | `restore_portal` | `bool` | `false` | Asks GPU Screen Recorder to restore the portal session. | +| `encoder` | `select` | `gpu` | `gpu` uses hardware encoding with a CPU fallback if it fails at runtime; `cpu` skips the hardware probe entirely, for GPUs with no video encoder at all (e.g. Asahi/Apple Silicon, some VMs). | ## IPC diff --git a/screen_recorder/plugin.toml b/screen_recorder/plugin.toml index 5dd326f..9ffabed 100644 --- a/screen_recorder/plugin.toml +++ b/screen_recorder/plugin.toml @@ -12,7 +12,7 @@ id = "noctalia/screen_recorder" name = "Screen Recorder" -version = "1.2.2" +version = "1.3.0" plugin_api = 3 author = "noctalia" license = "MIT" @@ -185,6 +185,18 @@ label_key = "settings.restore_portal.label" default = false advanced = true +[[setting]] +key = "encoder" +type = "select" +label_key = "settings.encoder.label" +description_key = "settings.encoder.description" +default = "gpu" +advanced = true +options = [ + { value = "gpu", label_key = "settings.encoder.options.gpu" }, + { value = "cpu", label_key = "settings.encoder.options.cpu" }, +] + # Headless background service: owns the recording / replay logic, runs for the # whole session, and bridges the "command" / "status" state channels. [[service]] diff --git a/screen_recorder/recorder_service.luau b/screen_recorder/recorder_service.luau index a7a866e..78562b3 100644 --- a/screen_recorder/recorder_service.luau +++ b/screen_recorder/recorder_service.luau @@ -197,6 +197,17 @@ local function buildResolutionFlag() return if res ~= "original" then `-s {res}` else "" end +-- "-fallback-cpu-encoding yes" is always included so a hardware encoder that +-- fails at runtime (e.g. unsupported codec, driver bug) degrades to software +-- encoding instead of aborting the recording. "-encoder cpu" additionally +-- skips the hardware probe entirely, which is required on GPUs with no VAAPI/ +-- NVENC encoder at all (Asahi/Apple Silicon, some VMs) — probing there always +-- fails and just delays startup. +local function buildEncoderFlags() + local forceCpu = if cfg("encoder") == "cpu" then " -encoder cpu" else "" + return `-fallback-cpu-encoding yes{forceCpu}` +end + -- Capture modes a caller may request. An IPC override outside this set is -- discarded so an arbitrary payload can never reach the gpu-screen-recorder -- command line. @@ -270,8 +281,9 @@ local function buildRecordCommand(focusedOutputName, sourceOverride) local restore = if cfg("restore_portal") then "-restore-portal-session yes" else "" local audioFlags = buildAudioFlags() local resFlag = buildResolutionFlag() + local encoderFlags = buildEncoderFlags() - local flags = `-w {source} -f {fps} -k {codec} {audioFlags} -bm qp -ffmpeg-video-opts qp={qp} -cursor {cursor} -cr {cr} {resFlag} {restore} -v no -o "{outputPath}"` + local flags = `-w {source} -f {fps} -k {codec} {audioFlags} -bm qp -ffmpeg-video-opts qp={qp} -cursor {cursor} -cr {cr} {resFlag} {restore} {encoderFlags} -v no -o "{outputPath}"` log(`starting recording: mode={selectedVideoSource(sourceOverride)} target={source} output={outputPath} flags=[{flags}]`) @@ -297,8 +309,9 @@ local function buildReplayCommand(focusedOutputName, sourceOverride) local restore = if cfg("restore_portal") then "-restore-portal-session yes" else "" local audioFlags = buildAudioFlags() local resFlag = buildResolutionFlag() + local encoderFlags = buildEncoderFlags() - local flags = `-w {source} -c mp4 -f {fps} -k {codec} {audioFlags} -bm qp -ffmpeg-video-opts qp={qp} -cursor {cursor} -cr {cr} {resFlag} -r {duration} -replay-storage {storage} {restore} -v no -o "{dir}"` + local flags = `-w {source} -c mp4 -f {fps} -k {codec} {audioFlags} -bm qp -ffmpeg-video-opts qp={qp} -cursor {cursor} -cr {cr} {resFlag} -r {duration} -replay-storage {storage} {restore} {encoderFlags} -v no -o "{dir}"` log(`starting replay buffer: mode={selectedVideoSource(sourceOverride)} target={source} dir={dir} flags=[{flags}]`) diff --git a/screen_recorder/translations/en.json b/screen_recorder/translations/en.json index d42a7a3..504cbd9 100644 --- a/screen_recorder/translations/en.json +++ b/screen_recorder/translations/en.json @@ -47,6 +47,14 @@ "description": "Defaults to ~/Videos/Recordings when empty", "label": "Output Directory" }, + "encoder": { + "description": "CPU encoding always works but uses more power; GPU encoding requires a working hardware video encoder (VAAPI/NVENC) and isn't available on some systems (e.g. Asahi/Apple Silicon GPUs, some VMs)", + "label": "Encoder", + "options": { + "cpu": "CPU (software)", + "gpu": "GPU (hardware)" + } + }, "filename_pattern": { "description": "Date-format pattern without extension; use %s for Unix timestamp", "label": "Filename Pattern"