From 8e007b01306a1647e5bfe84c2695f882652c8fd4 Mon Sep 17 00:00:00 2001 From: dijunkun Date: Thu, 29 Oct 2026 15:08:56 +0800 Subject: [PATCH] [fix] release idle audio playback devices to allow Windows sleep, refs #131 --- .../devices/session_device_manager.cpp | 23 ++++++++++++++++--- .../features/devices/session_device_manager.h | 4 +++- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/apps/desktop/src/gui/features/devices/session_device_manager.cpp b/apps/desktop/src/gui/features/devices/session_device_manager.cpp index 0e16c2e7..7040088c 100644 --- a/apps/desktop/src/gui/features/devices/session_device_manager.cpp +++ b/apps/desktop/src/gui/features/devices/session_device_manager.cpp @@ -123,7 +123,6 @@ void SessionDeviceManager::RecordCaptureCadence( } void SessionDeviceManager::Initialize() { - if (!owner_.unattended_host_) InitializeAudioOutput(); screen_capturer_factory_ = new ScreenCapturerFactory(); device_controller_factory_ = new DeviceControllerFactory(); keyboard_capturer_ = @@ -573,6 +572,12 @@ int SessionDeviceManager::StopKeyboardCapturer() { } int SessionDeviceManager::InitializeAudioOutput() { + if (output_device_) return 0; + const auto now = std::chrono::steady_clock::now(); + if (now < next_audio_output_retry_) return -1; + // A missing/unavailable playback device must not be reopened for every frame. + next_audio_output_retry_ = now + std::chrono::seconds(1); + SDL_AudioSpec desired_out{}; desired_out.freq = 48000; desired_out.format = SDL_AUDIO_S16; @@ -610,7 +615,12 @@ int SessionDeviceManager::InitializeAudioOutput() { #endif } - SDL_ResumeAudioDevice(output_device_); + if (!SDL_ResumeAudioDevice(output_device_)) { + LOG_ERROR("Failed to resume output device: {}", SDL_GetError()); + DestroyAudioOutput(); + return -1; + } + next_audio_output_retry_ = {}; return 0; } @@ -630,11 +640,15 @@ void SessionDeviceManager::ClearAudio(const std::string& remote_id) { if (found == output_streams_.end()) return; SDL_DestroyAudioStream(found->second); output_streams_.erase(found); + // An open SDL playback device can keep the Windows audio driver active even + // without queued samples. Release it when no peer needs playback anymore. + if (output_streams_.empty()) DestroyAudioOutput(); } void SessionDeviceManager::PushAudio(const std::string& remote_id, const char *data, size_t size) { - if (!output_device_) return; + if (!data || size == 0 || owner_.unattended_host_) return; + if (InitializeAudioOutput() != 0) return; auto found = output_streams_.find(remote_id); if (found == output_streams_.end()) { SDL_AudioSpec spec{}; @@ -644,17 +658,20 @@ void SessionDeviceManager::PushAudio(const std::string& remote_id, auto* stream = SDL_CreateAudioStream(&spec, &spec); if (!stream) { LOG_ERROR("Failed to create output stream: {}", SDL_GetError()); + if (output_streams_.empty()) DestroyAudioOutput(); return; } if (!SDL_BindAudioStream(output_device_, stream)) { LOG_ERROR("Failed to bind output stream: {}", SDL_GetError()); SDL_DestroyAudioStream(stream); + if (output_streams_.empty()) DestroyAudioOutput(); return; } found = output_streams_.emplace(remote_id, stream).first; } if (!SDL_PutAudioStreamData(found->second, data, static_cast(size))) { LOG_ERROR("Failed to push audio data: {}", SDL_GetError()); + ClearAudio(remote_id); } } diff --git a/apps/desktop/src/gui/features/devices/session_device_manager.h b/apps/desktop/src/gui/features/devices/session_device_manager.h index 114864d1..faa42b75 100644 --- a/apps/desktop/src/gui/features/devices/session_device_manager.h +++ b/apps/desktop/src/gui/features/devices/session_device_manager.h @@ -79,10 +79,10 @@ public: int StartKeyboardCapturer(); int StopKeyboardCapturer(); - int InitializeAudioOutput(); int DestroyAudioOutput(); // Serialized by GuiRuntime::audio_delivery_mutex_. Each peer has its own // stream so SDL mixes simultaneous playback and muting clears only that peer. + // Open the output on the first frame and close it after the last stream ends. void ClearAudio(const std::string& remote_id); void PushAudio(const std::string& remote_id, const char *data, size_t size); @@ -102,6 +102,7 @@ public: std::vector host_display_list() const; private: + int InitializeAudioOutput(); unsigned ReleaseMouseButtonMask(unsigned mask); void DrainCapturedKeyboardInput(); bool ShouldSendCapturedFrame(std::chrono::steady_clock::time_point now, @@ -117,6 +118,7 @@ private: GuiRuntime &owner_; uint32_t output_device_ = 0; std::unordered_map output_streams_; + std::chrono::steady_clock::time_point next_audio_output_retry_{}; ScreenCapturerFactory *screen_capturer_factory_ = nullptr; ScreenCapturer *screen_capturer_ = nullptr; ScreenSharingPower screen_sharing_power_;