You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
On the packaged OpenScreen 1.9.4-rc.3 app on Windows 10, ending a native Windows screen recording can fail to save the recording. The user sees a save/recording error after clicking Stop, the editor does not open with a usable recording, and the MP4 may never be finalized.
I reproduced and debugged this from the openscreen-release-v1.9.4 source archive on the affected machine. There were three distinct problems in the native helper path:
Windows 10 startup crash:session_.IsBorderRequired(false) was called through the projected GraphicsCaptureSession object without first querying the optional IGraphicsCaptureSession3 interface. On Windows 10 19045 this could terminate wgc-capture.exe with 0xC0000005; the surrounding C++ exception handler cannot catch a call through a missing ABI interface.
Stop/save hang: the DXGI input path created a second D3D11 encoding device and copied every frame through a shared texture guarded by IDXGIKeyedMutex::AcquireSync. Under the full recording load on this machine, the display driver could fail to return from that cross-device synchronization. At the same time, application frame-state locking covered GPU copy/readback work. WGC callbacks then could not quiesce, the video thread could not join, and IMFSinkWriter::Finalize was never reached, leaving the MP4 unsaved.
Packaged-app DPI compatibility: the installed 1.9.4-rc.3 main process supplied the selected display as Electron DIP bounds (2560x1440 at 150%), while the rebuilt per-monitor-v2-aware helper enumerated the physical monitor as 3840x2160. Strict matching rejected the only monitor and the app fell back from native capture. This is closely related to wgc-capture.exe is DPI-unaware, and its monitor lookup silently depends on it #346.
The virtual display adapter and the discrete GPU may make the D3D synchronization failure more likely, but the blocking cross-device bridge and lock ownership make a driver stall fatal regardless of the trigger.
Expected behavior
Clicking Stop should quiesce WGC callbacks, join the writer threads, finalize the fragmented MP4, emit recording-stopped, and open the editor promptly. Optional Windows capture interfaces must not crash older Windows versions, and a single-monitor scaled setup must resolve the selected display without weakening multi-monitor correctness.
To Reproduce
Run packaged OpenScreen 1.9.4-rc.3 on Windows 10 22H2.
Select the 3840x2160 display at 150% Windows scaling.
Enable system audio, microphone, webcam, and editable cursor capture.
Record for several seconds.
Click Stop.
Observe that native capture can fail to stop/finalize and the application reports that the recording could not be saved.
The native-helper failure is below the React UI. Chromium MediaRecorder/WebM fallback saved correctly, but that only avoids the affected WGC helper path.
Root cause details
The relevant code is under electron/native/wgc-capture/src/.
wgc_session.cpp: direct use of IsBorderRequired assumes IGraphicsCaptureSession3 is present.
mf_encoder.cpp / .h: the DXGI pipeline owns a second D3D11 device plus a shared keyed-mutex bridge between capture and encoding devices.
main.cpp: the WGC callback and video writer hold the frame-state mutex across D3D CopyResource, conversion, or staging readback work. A driver call that does not return prevents shutdown from draining the producer and joining the writer.
monitor_utils.cpp: physical bounds are correct for the current helper, but an already packaged 1.9.4-rc.3 main process can still provide legacy DIP bounds.
The finite keyed-mutex timeout was not a sufficient safeguard on this machine: the driver-level synchronization could still fail to return. Because the writer was stranded before finalization, the user-visible result was a save failure rather than a recoverable dropped frame.
Fix implemented and tested locally
I rebuilt wgc-capture.exe with the following changes:
Query IGraphicsCaptureSession3 with try_as before setting IsBorderRequired; skip the cosmetic border setting when unavailable.
Remove the second D3D11 encoding device, the shared bridge textures, and both IDXGIKeyedMutex handshakes.
Reuse the WGC capture device and its multithread-protected immediate context for Media Foundation, BGRA-to-NV12 video processing, sample allocation, and hardware H.264 input.
Create one stable BGRA frame texture before callbacks start. On the DXGI path it has D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET.
Keep the frame-state mutex limited to timestamps and small state updates. Do CopyResource, GPU conversion, CPU readback, and synchronous encoder submission outside that application mutex. D3D's multithread protection serializes the shared immediate context.
Prefer the repaired dxgi-nv12 path by default; retain OPENSCREEN_WGC_ENABLE_DXGI_INPUT=0 as a diagnostic CPU fallback.
Preserve strict physical-bounds matching on multi-monitor systems. If and only if Windows enumerates exactly one monitor, accept legacy mismatched DIP bounds and select that sole monitor. This allows the rebuilt helper to work with the already packaged 1.9.4-rc.3 main process without reintroducing wgc-capture.exe is DPI-unaware, and its monitor lookup silently depends on it #346's wrong-monitor guessing.
Test results
All tests below were performed on the affected machine with the rebuilt helper.
Native helper, default configuration, display + system audio + microphone
Separate webcam file: H.264 1280x720, 8.0667 s, 7,860,646 bytes
Both files were non-empty and readable by ffprobe
No native-capture fallback and no save error
npm run build-vite also passed. The native helper and cursor sampler rebuilt successfully with Visual Studio 2022 Build Tools.
Screenshots
The original user-facing error was displayed immediately after ending the recording and stated that the recording could not be saved. I can provide the screenshot separately if useful; it contains no additional native stack trace.
OS
Windows
OS Version
Windows 10 Pro 22H2, version 10.0.19045, build 19045, 64-bit
Search existing issues
Related: #252, #336, #346 (and the earlier #34 / #115 stop-path reports).
Describe the bug
On the packaged OpenScreen
1.9.4-rc.3app on Windows 10, ending a native Windows screen recording can fail to save the recording. The user sees a save/recording error after clicking Stop, the editor does not open with a usable recording, and the MP4 may never be finalized.I reproduced and debugged this from the
openscreen-release-v1.9.4source archive on the affected machine. There were three distinct problems in the native helper path:session_.IsBorderRequired(false)was called through the projectedGraphicsCaptureSessionobject without first querying the optionalIGraphicsCaptureSession3interface. On Windows 10 19045 this could terminatewgc-capture.exewith0xC0000005; the surrounding C++ exception handler cannot catch a call through a missing ABI interface.IDXGIKeyedMutex::AcquireSync. Under the full recording load on this machine, the display driver could fail to return from that cross-device synchronization. At the same time, application frame-state locking covered GPU copy/readback work. WGC callbacks then could not quiesce, the video thread could not join, andIMFSinkWriter::Finalizewas never reached, leaving the MP4 unsaved.1.9.4-rc.3main process supplied the selected display as Electron DIP bounds (2560x1440at 150%), while the rebuilt per-monitor-v2-aware helper enumerated the physical monitor as3840x2160. Strict matching rejected the only monitor and the app fell back from native capture. This is closely related to wgc-capture.exe is DPI-unaware, and its monitor lookup silently depends on it #346.The virtual display adapter and the discrete GPU may make the D3D synchronization failure more likely, but the blocking cross-device bridge and lock ownership make a driver stall fatal regardless of the trigger.
Expected behavior
Clicking Stop should quiesce WGC callbacks, join the writer threads, finalize the fragmented MP4, emit
recording-stopped, and open the editor promptly. Optional Windows capture interfaces must not crash older Windows versions, and a single-monitor scaled setup must resolve the selected display without weakening multi-monitor correctness.To Reproduce
1.9.4-rc.3on Windows 10 22H2.The native-helper failure is below the React UI. Chromium
MediaRecorder/WebM fallback saved correctly, but that only avoids the affected WGC helper path.Root cause details
The relevant code is under
electron/native/wgc-capture/src/.wgc_session.cpp: direct use ofIsBorderRequiredassumesIGraphicsCaptureSession3is present.mf_encoder.cpp/.h: the DXGI pipeline owns a second D3D11 device plus a shared keyed-mutex bridge between capture and encoding devices.main.cpp: the WGC callback and video writer hold the frame-state mutex across D3DCopyResource, conversion, or staging readback work. A driver call that does not return prevents shutdown from draining the producer and joining the writer.monitor_utils.cpp: physical bounds are correct for the current helper, but an already packaged1.9.4-rc.3main process can still provide legacy DIP bounds.The finite keyed-mutex timeout was not a sufficient safeguard on this machine: the driver-level synchronization could still fail to return. Because the writer was stranded before finalization, the user-visible result was a save failure rather than a recoverable dropped frame.
Fix implemented and tested locally
I rebuilt
wgc-capture.exewith the following changes:IGraphicsCaptureSession3withtry_asbefore settingIsBorderRequired; skip the cosmetic border setting when unavailable.IDXGIKeyedMutexhandshakes.D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET.CopyResource, GPU conversion, CPU readback, and synchronous encoder submission outside that application mutex. D3D's multithread protection serializes the shared immediate context.dxgi-nv12path by default; retainOPENSCREEN_WGC_ENABLE_DXGI_INPUT=0as a diagnostic CPU fallback.1.9.4-rc.3main process without reintroducing wgc-capture.exe is DPI-unaware, and its monitor lookup silently depends on it #346's wrong-monitor guessing.Test results
All tests below were performed on the affected machine with the rebuilt helper.
Native helper, default configuration, display + system audio + microphone
videoInput: dxgi-nv12wgc-quiesce drained=truerecording-stoppedemittedRepository full Windows helper test
Command:
node scripts/test-windows-wgc-helper.mjs --webcam --system-audio --microphoneEnd-to-end development app test, forced native helper
[native-wgc] starting Windows captureand[native-wgc] capture starteddefault,videoInput: dxgi-nv12,container: fragmented-mp4End-to-end test of the actually installed packaged
1.9.4-rc.3app2560x1440DIP bounds safely resolved to the sole physical3840x2160monitordefault,videoInput: dxgi-nv12,container: fragmented-mp4ffprobenpm run build-vitealso passed. The native helper and cursor sampler rebuilt successfully with Visual Studio 2022 Build Tools.Screenshots
The original user-facing error was displayed immediately after ending the recording and stated that the recording could not be saved. I can provide the screenshot separately if useful; it contains no additional native stack trace.
OS
Windows
OS Version
Windows 10 Pro 22H2, version
10.0.19045, build19045, 64-bitBrowser
Other
Browser Version
Packaged Electron app (
Electron 41.2.1)Device Type
Desktop
Additional context
1.9.4-rc.3(1.9.4.0product version)31.0.15.515217.50.19.949AppliedDPI=144)1234FA69D894E6AEFB805BFD0CC4E4D9AE042806FF95DE3784DB94AE226893C2No username, machine name, local paths, browser media device IDs, or recording content are included in this report.