From 062fd82de11b12c39926dcaeced35bce777cfe92 Mon Sep 17 00:00:00 2001 From: edde746 <86283021+edde746@users.noreply.github.com> Date: Fri, 3 Apr 2026 12:19:36 +0200 Subject: [PATCH] fix: recover audio after Windows sleep via ao-null-reload --- windows/runner/mpv/mpv_core.cpp | 17 ----------------- windows/runner/mpv/mpv_core.h | 8 -------- windows/runner/mpv/mpv_player.cpp | 17 +++++++++++++++++ windows/runner/mpv/mpv_plugin.cpp | 21 --------------------- 4 files changed, 17 insertions(+), 46 deletions(-) diff --git a/windows/runner/mpv/mpv_core.cpp b/windows/runner/mpv/mpv_core.cpp index b146048e..6e75eed6 100644 --- a/windows/runner/mpv/mpv_core.cpp +++ b/windows/runner/mpv/mpv_core.cpp @@ -124,11 +124,6 @@ std::optional MpvCore::WindowProc(HWND hwnd, UINT message, ::RedrawWindow(flutter_window_, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN); } - } else if (wparam == kPowerResumeTimerId) { - ::KillTimer(flutter_window_, kPowerResumeTimerId); - if (power_resume_cb_) { - power_resume_cb_(); - } } break; } @@ -149,14 +144,6 @@ std::optional MpvCore::WindowProc(HWND hwnd, UINT message, } break; } - case WM_POWERBROADCAST: { - if (wparam == PBT_APMRESUMEAUTOMATIC && power_resume_cb_) { - // Delay audio reload to let WASAPI fully reinitialize after wake. - ::KillTimer(flutter_window_, kPowerResumeTimerId); - ::SetTimer(flutter_window_, kPowerResumeTimerId, kPowerResumeAudioDelay, nullptr); - } - break; - } case WM_CLOSE: { ::SendMessage(container_, WM_CLOSE, 0, 0); for (const auto& [mpv_view, rect] : mpv_views_) { @@ -207,10 +194,6 @@ void MpvCore::DisableComposition() { ::DwmFlush(); } -void MpvCore::SetPowerResumeCallback(PowerResumeCallback callback) { - power_resume_cb_ = std::move(callback); -} - std::unique_ptr MpvCore::instance_ = nullptr; } // namespace mpv diff --git a/windows/runner/mpv/mpv_core.h b/windows/runner/mpv/mpv_core.h index 06b68198..49233d1c 100644 --- a/windows/runner/mpv/mpv_core.h +++ b/windows/runner/mpv/mpv_core.h @@ -4,7 +4,6 @@ #include #include -#include #include #include #include @@ -16,9 +15,7 @@ namespace mpv { class MpvCore { public: static constexpr auto kPositionAndShowDelay = 300; - static constexpr auto kPowerResumeAudioDelay = 500; static constexpr UINT_PTR kCompositionRestoreTimerId = 1001; - static constexpr UINT_PTR kPowerResumeTimerId = 1002; static MpvCore* GetInstance(); static void SetInstance(std::unique_ptr instance); @@ -45,10 +42,6 @@ class MpvCore { std::optional WindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam); - // Callback invoked when Windows resumes from sleep/hibernate. - using PowerResumeCallback = std::function; - void SetPowerResumeCallback(PowerResumeCallback callback); - private: RECT GetGlobalRect(int32_t left, int32_t top, int32_t right, int32_t bottom); void EnableComposition(); @@ -62,7 +55,6 @@ class MpvCore { bool was_window_hidden_due_to_minimize_ = false; bool visible_ = true; bool composition_enabled_ = false; - PowerResumeCallback power_resume_cb_; static std::unique_ptr instance_; }; diff --git a/windows/runner/mpv/mpv_player.cpp b/windows/runner/mpv/mpv_player.cpp index 15428653..cb6804c5 100644 --- a/windows/runner/mpv/mpv_player.cpp +++ b/windows/runner/mpv/mpv_player.cpp @@ -53,6 +53,11 @@ bool MpvPlayer::Initialize(HWND container, HWND flutter_window) { mpv_set_option_string(mpv_, "tone-mapping", "auto"); mpv_set_option_string(mpv_, "hdr-compute-peak", "auto"); + // When WASAPI becomes unavailable (sleep, device unplug), fall back to null + // audio output instead of permanently dropping the audio track. Recovery is + // handled in the event loop when audio-device-list changes. + mpv_set_option_string(mpv_, "audio-fallback-to-null", "yes"); + // Default to warn-level logging; Dart side can raise to "v" if debug logging is enabled. mpv_request_log_messages(mpv_, "warn"); @@ -355,6 +360,18 @@ void MpvPlayer::HandleMpvEvent(mpv_event* event) { UpdateHDRMode(sigPeak); } + // Audio recovery: when the device list changes and audio has fallen back + // to null output (e.g. after sleep/wake or device unplug), re-set + // audio-device to switch back to the real output. + // Mirrors mpv's TOOLS/lua/ao-null-reload.lua for embedded libmpv. + if (strcmp(prop->name, "audio-device-list") == 0 && + GetProperty("current-ao") == "null") { + auto device = GetProperty("audio-device"); + if (!device.empty()) { + mpv_set_property_string(mpv_, "audio-device", device.c_str()); + } + } + SendPropertyChange(prop->name, &node); break; } diff --git a/windows/runner/mpv/mpv_plugin.cpp b/windows/runner/mpv/mpv_plugin.cpp index 2031f306..48585f71 100644 --- a/windows/runner/mpv/mpv_plugin.cpp +++ b/windows/runner/mpv/mpv_plugin.cpp @@ -65,12 +65,6 @@ MpvPlayerPlugin::MpvPlayerPlugin(flutter::PluginRegistrarWindows* registrar) } MpvPlayerPlugin::~MpvPlayerPlugin() { - // Clear power-resume callback and kill pending timer so they cannot - // invoke a dangling `this`. - if (MpvCore::GetInstance()) { - MpvCore::GetInstance()->SetPowerResumeCallback(nullptr); - ::KillTimer(GetWindow(), MpvCore::kPowerResumeTimerId); - } // Unregister window proc delegate. if (proc_id_) { registrar_->UnregisterTopLevelWindowProcDelegate(proc_id_.value()); @@ -135,16 +129,6 @@ void MpvPlayerPlugin::HandleMethodCall( RECT rect = {0, 0, 100, 100}; MpvCore::GetInstance()->CreateMpvView(player_->GetHwnd(), rect, 1.0); - // Reload audio output on wake from sleep to recover stale WASAPI sessions. - MpvCore::GetInstance()->SetPowerResumeCallback([this]() { - if (player_ && player_->IsInitialized()) { - auto device = player_->GetProperty("audio-device"); - if (!device.empty()) { - player_->SetProperty("audio-device", device); - } - } - }); - // Start hidden. MpvCore::GetInstance()->SetVisible(false); result->Success(flutter::EncodableValue(true)); @@ -153,11 +137,6 @@ void MpvPlayerPlugin::HandleMethodCall( result->Error("INIT_FAILED", "Failed to initialize MPV player"); } } else if (method == "dispose") { - // Clear power-resume callback and kill pending timer before disposing. - if (MpvCore::GetInstance()) { - MpvCore::GetInstance()->SetPowerResumeCallback(nullptr); - ::KillTimer(GetWindow(), MpvCore::kPowerResumeTimerId); - } if (player_) { auto hwnd = player_->GetHwnd(); player_->Dispose();