Merge branch 'lan-direct' of https://github.com/kunkundi/crossdesk into lan-direct

This commit is contained in:
dijunkun
2026-10-29 20:19:21 +08:00
2 changed files with 23 additions and 4 deletions
@@ -123,7 +123,6 @@ void SessionDeviceManager::RecordCaptureCadence(
} }
void SessionDeviceManager::Initialize() { void SessionDeviceManager::Initialize() {
if (!owner_.unattended_host_) InitializeAudioOutput();
screen_capturer_factory_ = new ScreenCapturerFactory(); screen_capturer_factory_ = new ScreenCapturerFactory();
device_controller_factory_ = new DeviceControllerFactory(); device_controller_factory_ = new DeviceControllerFactory();
keyboard_capturer_ = keyboard_capturer_ =
@@ -573,6 +572,12 @@ int SessionDeviceManager::StopKeyboardCapturer() {
} }
int SessionDeviceManager::InitializeAudioOutput() { 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{}; SDL_AudioSpec desired_out{};
desired_out.freq = 48000; desired_out.freq = 48000;
desired_out.format = SDL_AUDIO_S16; desired_out.format = SDL_AUDIO_S16;
@@ -610,7 +615,12 @@ int SessionDeviceManager::InitializeAudioOutput() {
#endif #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; return 0;
} }
@@ -630,11 +640,15 @@ void SessionDeviceManager::ClearAudio(const std::string& remote_id) {
if (found == output_streams_.end()) return; if (found == output_streams_.end()) return;
SDL_DestroyAudioStream(found->second); SDL_DestroyAudioStream(found->second);
output_streams_.erase(found); 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, void SessionDeviceManager::PushAudio(const std::string& remote_id,
const char *data, size_t size) { 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); auto found = output_streams_.find(remote_id);
if (found == output_streams_.end()) { if (found == output_streams_.end()) {
SDL_AudioSpec spec{}; SDL_AudioSpec spec{};
@@ -644,17 +658,20 @@ void SessionDeviceManager::PushAudio(const std::string& remote_id,
auto* stream = SDL_CreateAudioStream(&spec, &spec); auto* stream = SDL_CreateAudioStream(&spec, &spec);
if (!stream) { if (!stream) {
LOG_ERROR("Failed to create output stream: {}", SDL_GetError()); LOG_ERROR("Failed to create output stream: {}", SDL_GetError());
if (output_streams_.empty()) DestroyAudioOutput();
return; return;
} }
if (!SDL_BindAudioStream(output_device_, stream)) { if (!SDL_BindAudioStream(output_device_, stream)) {
LOG_ERROR("Failed to bind output stream: {}", SDL_GetError()); LOG_ERROR("Failed to bind output stream: {}", SDL_GetError());
SDL_DestroyAudioStream(stream); SDL_DestroyAudioStream(stream);
if (output_streams_.empty()) DestroyAudioOutput();
return; return;
} }
found = output_streams_.emplace(remote_id, stream).first; found = output_streams_.emplace(remote_id, stream).first;
} }
if (!SDL_PutAudioStreamData(found->second, data, static_cast<int>(size))) { if (!SDL_PutAudioStreamData(found->second, data, static_cast<int>(size))) {
LOG_ERROR("Failed to push audio data: {}", SDL_GetError()); LOG_ERROR("Failed to push audio data: {}", SDL_GetError());
ClearAudio(remote_id);
} }
} }
@@ -79,10 +79,10 @@ public:
int StartKeyboardCapturer(); int StartKeyboardCapturer();
int StopKeyboardCapturer(); int StopKeyboardCapturer();
int InitializeAudioOutput();
int DestroyAudioOutput(); int DestroyAudioOutput();
// Serialized by GuiRuntime::audio_delivery_mutex_. Each peer has its own // Serialized by GuiRuntime::audio_delivery_mutex_. Each peer has its own
// stream so SDL mixes simultaneous playback and muting clears only that peer. // 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 ClearAudio(const std::string& remote_id);
void PushAudio(const std::string& remote_id, const char *data, size_t size); 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; std::vector<HostDisplay> host_display_list() const;
private: private:
int InitializeAudioOutput();
unsigned ReleaseMouseButtonMask(unsigned mask); unsigned ReleaseMouseButtonMask(unsigned mask);
void DrainCapturedKeyboardInput(); void DrainCapturedKeyboardInput();
bool ShouldSendCapturedFrame(std::chrono::steady_clock::time_point now, bool ShouldSendCapturedFrame(std::chrono::steady_clock::time_point now,
@@ -117,6 +118,7 @@ private:
GuiRuntime &owner_; GuiRuntime &owner_;
uint32_t output_device_ = 0; uint32_t output_device_ = 0;
std::unordered_map<std::string, SDL_AudioStream*> output_streams_; std::unordered_map<std::string, SDL_AudioStream*> output_streams_;
std::chrono::steady_clock::time_point next_audio_output_retry_{};
ScreenCapturerFactory *screen_capturer_factory_ = nullptr; ScreenCapturerFactory *screen_capturer_factory_ = nullptr;
ScreenCapturer *screen_capturer_ = nullptr; ScreenCapturer *screen_capturer_ = nullptr;
ScreenSharingPower screen_sharing_power_; ScreenSharingPower screen_sharing_power_;