diff --git a/electron/native/bin/win32-x64/helpers-manifest.json b/electron/native/bin/win32-x64/helpers-manifest.json index 234719bf..8713d1c3 100644 --- a/electron/native/bin/win32-x64/helpers-manifest.json +++ b/electron/native/bin/win32-x64/helpers-manifest.json @@ -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", diff --git a/electron/native/bin/win32-x64/wgc-capture.exe b/electron/native/bin/win32-x64/wgc-capture.exe index 2973f783..ac92a80b 100644 Binary files a/electron/native/bin/win32-x64/wgc-capture.exe and b/electron/native/bin/win32-x64/wgc-capture.exe differ diff --git a/electron/native/wgc-capture/src/main.cpp b/electron/native/wgc-capture/src/main.cpp index 2f61d046..c949e8a2 100644 --- a/electron/native/wgc-capture/src/main.cpp +++ b/electron/native/wgc-capture/src/main.cpp @@ -13,6 +13,7 @@ #include #include #include +#include static std::atomic g_stopRequested{false}; static std::atomic g_pauseRequested{false}; @@ -249,6 +250,7 @@ int main(int argc, char* argv[]) { // Set up frame callback std::atomic frameCount{0}; + std::atomic 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) { diff --git a/electron/native/wgc-capture/src/mf_encoder.cpp b/electron/native/wgc-capture/src/mf_encoder.cpp index a1474c20..00eea688 100644 --- a/electron/native/wgc-capture/src/mf_encoder.cpp +++ b/electron/native/wgc-capture/src/mf_encoder.cpp @@ -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 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 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 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); }