fix(windows): finalize WGC recordings safely

This commit is contained in:
webadderall
2026-03-28 17:03:41 +11:00
parent 58153679dd
commit 4e8ab9cf45
4 changed files with 39 additions and 14 deletions
@@ -5,10 +5,10 @@
"helpers": {
"wgc-capture": {
"binaryName": "wgc-capture.exe",
"binarySha256": "72d8f0703a5360a0e911a95bf8839354c877df5fa51294af75ded83a17c0c32e",
"binarySha256": "5f364b07b016c597288f90a9b61c581e6559d6d32b79ffadceb37859bbea8dd2",
"sourceDir": "electron/native/wgc-capture",
"sourceFingerprint": "f30dee6b4956a5b57f4fc4d8d586f3470c793c8f7e3242f4160394debbb3f3be",
"updatedAt": "2026-03-28T05:17:10.449Z"
"sourceFingerprint": "9e9bce082266ca5968cf5f0b535b469d47c4ec3a775a93726171c0dfdbcdaa44",
"updatedAt": "2026-03-28T05:48:46.587Z"
},
"cursor-monitor": {
"binaryName": "cursor-monitor.exe",
Binary file not shown.
+18 -5
View File
@@ -13,6 +13,7 @@
#include <mutex>
#include <condition_variable>
#include <chrono>
#include <cstdio>
static std::atomic<bool> g_stopRequested{false};
static std::atomic<bool> g_pauseRequested{false};
@@ -249,6 +250,7 @@ int main(int argc, char* argv[]) {
// Set up frame callback
std::atomic<int64_t> frameCount{0};
std::atomic<bool> recordingStartedAnnounced{false};
session.setFrameCallback([&](ID3D11Texture2D* texture, int64_t timestampHns) {
g_lastFrameTimestampHns = timestampHns;
if (g_stopRequested) return;
@@ -269,7 +271,11 @@ int main(int argc, char* argv[]) {
}
if (encoder.writeFrame(texture, adjustedTimestampHns)) {
frameCount++;
const int64_t writtenFrames = frameCount.fetch_add(1) + 1;
if (writtenFrames == 1 && !recordingStartedAnnounced.exchange(true)) {
std::cout << "Recording started" << std::endl;
std::cout.flush();
}
}
});
@@ -312,9 +318,6 @@ int main(int argc, char* argv[]) {
micActive = micCapture.start();
}
std::cout << "Recording started" << std::endl;
std::cout.flush();
// Wait for stop signal while pausing/resuming audio tracks in lockstep.
while (!g_stopRequested) {
if (g_pauseRequested) {
@@ -333,7 +336,17 @@ int main(int argc, char* argv[]) {
session.stopCapture();
if (audioActive) loopback.stop();
if (micActive) micCapture.stop();
encoder.finalize();
if (frameCount.load() <= 0) {
std::cerr << "ERROR: No video frames were captured before stop" << std::endl;
DeleteFileW(outputPathW.c_str());
return 1;
}
if (!encoder.finalize()) {
std::cerr << "ERROR: Failed to finalize Media Foundation encoder" << std::endl;
return 1;
}
std::cout << "Recording stopped. Output path: " << config.outputPath << std::endl;
if (audioActive) {
+18 -6
View File
@@ -22,6 +22,8 @@ MFEncoder::~MFEncoder() {
bool MFEncoder::initialize(const std::wstring& outputPath, int width, int height, int fps,
ID3D11Device* device, ID3D11DeviceContext* context) {
std::lock_guard<std::mutex> lock(mutex_);
if (initialized_) return false;
if (width % 2 != 0 || height % 2 != 0) {
@@ -124,6 +126,8 @@ bool MFEncoder::initialize(const std::wstring& outputPath, int width, int height
}
bool MFEncoder::writeFrame(ID3D11Texture2D* texture, int64_t timestampHns) {
std::lock_guard<std::mutex> lock(mutex_);
if (!initialized_ || !sinkWriter_) return false;
context_->CopyResource(stagingTexture_.Get(), texture);
@@ -186,20 +190,28 @@ bool MFEncoder::writeFrame(ID3D11Texture2D* texture, int64_t timestampHns) {
sample->SetSampleDuration(10000000LL / fps_);
hr = sinkWriter_->WriteSample(streamIndex_, sample.Get());
if (FAILED(hr)) {
std::cerr << "ERROR: WriteSample failed: 0x" << std::hex << hr << std::endl;
}
return SUCCEEDED(hr);
}
bool MFEncoder::finalize() {
if (!initialized_) return false;
initialized_ = false;
std::lock_guard<std::mutex> lock(mutex_);
if (!initialized_) return false;
if (!sinkWriter_) return false;
HRESULT hr = sinkWriter_->Finalize();
if (FAILED(hr)) {
std::cerr << "ERROR: SinkWriter Finalize failed: 0x" << std::hex << hr << std::endl;
}
initialized_ = false;
sinkWriter_.Reset();
stagingTexture_.Reset();
nv12Buffer_.clear();
nv12Buffer_.shrink_to_fit();
if (!sinkWriter_) return false;
HRESULT hr = sinkWriter_->Finalize();
sinkWriter_.Reset();
MFShutdown();
return SUCCEEDED(hr);
}