Skip to content

fix(recording): use hardware H.264 on Windows by default - #489

Merged
EtienneLescot merged 2 commits into
mainfrom
claude/openscreen-issue-460-674726
Aug 23, 2026
Merged

fix(recording): use hardware H.264 on Windows by default#489
EtienneLescot merged 2 commits into
mainfrom
claude/openscreen-issue-460-674726

Conversation

@EtienneLescot

@EtienneLescot EtienneLescot commented Aug 23, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fixes the root cause behind #460 ("Timed out waiting for native Windows capture to stop" on Windows machines without a dedicated GPU).

MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS defaults to FALSE in Media Foundation, and the "default" sink-writer configuration path (no preferSoftwareEncoder, no OPENSCREEN_WGC_ENABLE_DXGI_INPUT) never set it. So every plain Windows recording ran on the software H.264 encoder regardless of what GPU the machine had — only the opt-in DXGI path ever asked for hardware. That's invisible on a fast CPU, but on the older machines reported in #460 (a 6th-gen i3, an i5-4590 with HD 4600) it's slow enough that shutdown blows the 50-60s stop budget and the recording is lost.

  • createSinkWriter now requests hardware transforms whenever software isn't explicitly forced, DXGI device manager or not.
  • Extended the existing VBR rate-control fix (previously DXGI-only) to the same condition — hardware MFTs default to constant bitrate, which would otherwise spend the full configured budget on a static screen now that hardware is reachable from the default path too.
  • Added a videoEncoderRuntime field ("hardware" / "software" / "unknown") to the helper's encoder-selection event, so a future bug report can tell "a real hardware encoder stalled on a bad driver" apart from "this machine is still silently on software" — it introspects the sink writer's actual resolved transform pipeline rather than trusting which path was configured.

Verification

Compiled with the real MSVC/CMake toolchain and ran the actual helper end to end (not just tsc/lint):

  • Default path: videoEncoderRuntime flipped from "software" to "hardware" on this machine, with no other flags set.
  • Full test matrix passed with no regressions: default, --software-encoder, OPENSCREEN_WGC_ENABLE_DXGI_INPUT=1, --window, --system-audio, --microphone, test:wgc-audio-timeline:win, test:wgc-mic-selection:win.
  • tsc --noEmit and biome check clean.

One accepted trade-off, confirmed back-to-back on this machine: hardware output ran roughly 5x larger than software for the same content even with VBR correctly engaged (8.7 Mbps vs 1.7 Mbps) — a real rate-distortion difference between the two encoder implementations, not a rate-control bug. Worth it for the CPU relief and stop-reliability on the weak machines this is meant to fix.

Test plan

  • On a machine that previously hit the [Bug]: I tested version 1.10.0. #460 timeout (weak/no dedicated GPU), confirm a recording now stops and saves without the timeout
  • Confirm Save Diagnostics now includes videoEncoderRuntime in the encoder-selection line for a fresh recording
  • Spot-check recorded file size/quality on a couple of GPU vendors (Intel/AMD/NVIDIA) since only one machine (Intel Quick Sync class) was available for this change

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Windows recording diagnostics now report whether hardware or software encoding was selected at runtime.
    • Recording results distinguish the configured encoder preference from the encoder actually used.
  • Bug Fixes

    • Improved hardware encoder support for CPU-readback recordings and fallback scenarios.
  • Documentation

    • Updated recording documentation to explain encoder selection, runtime detection, and hardware encoding behavior.
  • Tests

    • Added validation to ensure runtime encoder reporting is accurate for hardware, software, and fallback recording modes.

…en asked

MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS defaults to FALSE, and the "default"
sink-writer path (no preferSoftwareEncoder, no OPENSCREEN_WGC_ENABLE_DXGI_INPUT)
never set it. So every plain recording ran on the software H.264 encoder
regardless of what GPU the machine had -- the DXGI path was the only one that
ever asked for hardware. On a fast CPU that's invisible; on the older machines
in #460 (a 6th-gen i3, an i5-4590 with HD 4600) it's slow enough to blow the
50-60s stop-shutdown budget and lose the whole recording to a "Timed out
waiting for native Windows capture to stop" failure.

createSinkWriter now asks for hardware transforms whenever software is not
forced, DXGI device manager or not. Verified against the real compiled helper:
the default path went from videoEncoderRuntime "software" to "hardware" on this
machine, with no other flags set.

That uncovered a second, known issue the DXGI path had already fixed once:
hardware MFTs default to constant bitrate, which spends the full configured
budget doing nothing on a static screen. applyHardwareRateControl's VBR fix was
gated on the DXGI path alone; it now runs whenever hardware transforms were
requested, matching the wider condition above.

Added videoEncoderRuntime ("hardware"/"software"/"unknown") to the
encoder-selection event so a bug report can tell these two failure shapes
apart going forward: a real hardware encoder stalling on a bad driver, versus
every recording quietly running through software regardless of what hardware
is on the machine. It introspects the sink writer's own resolved transform
pipeline (IMFSinkWriterEx::GetTransformForStream) rather than trusting which
path was configured, since MF is free to hand back software even when hardware
was requested.

Verified end to end on real hardware: compiled with MSVC/CMake, ran the actual
helper through the full test matrix (default, software-encoder, DXGI, window,
system-audio, microphone, audio-timeline, mic-selection) with no regressions.
One accepted trade-off, confirmed back-to-back on this machine: hardware output
ran roughly 5x larger than software for the same content even with VBR
correctly engaged (8.7 Mbps vs 1.7 Mbps) -- a real rate-distortion difference
between the two encoders, not a rate-control bug, and worth the CPU relief and
stop-reliability it buys on weak machines.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 21c121f6-b1a3-438b-ad14-07a14a336669

📥 Commits

Reviewing files that changed from the base of the PR and between c6faf50 and 871b284.

📒 Files selected for processing (7)
  • electron/ipc/handlers.ts
  • electron/native/wgc-capture/src/main.cpp
  • electron/native/wgc-capture/src/mf_encoder.cpp
  • electron/native/wgc-capture/src/mf_encoder.h
  • scripts/test-windows-wgc-helper.mjs
  • src/lib/nativeWindowsRecording.ts
  • technical-documentation/architecture/recording.md

Included review availability: Your plan provides up to 4 included reviews per hour; 2 remain after this review.


📝 Walkthrough

Walkthrough

Windows recording now detects the encoder runtime selected by Media Foundation. The native event, IPC response, TypeScript result, validation helper, and recording documentation expose the distinction between configured encoder selection and runtime encoder selection.

Changes

Windows encoder runtime reporting

Layer / File(s) Summary
Runtime encoder detection
electron/native/wgc-capture/src/mf_encoder.h, electron/native/wgc-capture/src/mf_encoder.cpp, electron/native/wgc-capture/src/main.cpp
MFEncoder tracks hardware, software, and unknown runtime states. It detects the selected video encoder after BeginWriting() and includes the result in the encoder-selection event.
Hardware transform setup and rate control
electron/native/wgc-capture/src/mf_encoder.cpp, technical-documentation/architecture/recording.md
Non-software sink-writer attempts enable hardware transforms with or without a DXGI manager. Hardware rate-control configuration applies to all such attempts. The documentation describes runtime detection and hardware MFT configuration.
IPC propagation and helper validation
electron/ipc/handlers.ts, src/lib/nativeWindowsRecording.ts, scripts/test-windows-wgc-helper.mjs
The recording-start response exposes videoEncoderRuntime. The helper validates recognized runtime values and requires software runtime selection for software or fallback modes.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to 871b2

The change enables hardware H.264 encoding by default on Windows and reports the selected encoder runtime; no actionable merge-blocking risk remains beyond normal checks and review.

Suggested reviewers: my-denia

Sequence Diagram(s)

sequenceDiagram
  participant MFEncoder
  participant NativeEvent
  participant IPCHandler
  participant RecordingResult
  MFEncoder->>NativeEvent: emit requested encoder and videoEncoderRuntime
  NativeEvent->>IPCHandler: pass encoder-selection metadata
  IPCHandler->>RecordingResult: return videoEncoderRuntime
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.77% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 13 functions across 6 files. (1 skipped: 1 unsupported.) Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: enabling hardware H.264 encoding by default for Windows recording.
Description check ✅ Passed The description covers the issue, implementation, testing, trade-offs, and test plan, but leaves template metadata checkboxes and the issue number incomplete.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/openscreen-issue-460-674726

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@EtienneLescot
EtienneLescot merged commit 3847cf3 into main Aug 23, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant