[fix] release idle audio playback devices to allow Windows sleep, refs #131

This commit is contained in:
dijunkun
2026-10-29 15:08:56 +08:00
parent dd774de4f8
commit 8e007b0130
2 changed files with 23 additions and 4 deletions
@@ -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<int>(size))) {
LOG_ERROR("Failed to push audio data: {}", SDL_GetError());
ClearAudio(remote_id);
}
}
@@ -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<HostDisplay> 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<std::string, SDL_AudioStream*> 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_;