fix: recover audio after Windows sleep via ao-null-reload
This commit is contained in:
@@ -124,11 +124,6 @@ std::optional<HRESULT> 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<HRESULT> 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> MpvCore::instance_ = nullptr;
|
||||
|
||||
} // namespace mpv
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include <Windows.h>
|
||||
|
||||
#include <cmath>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
@@ -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<MpvCore> instance);
|
||||
@@ -45,10 +42,6 @@ class MpvCore {
|
||||
std::optional<HRESULT> WindowProc(HWND hwnd, UINT message, WPARAM wparam,
|
||||
LPARAM lparam);
|
||||
|
||||
// Callback invoked when Windows resumes from sleep/hibernate.
|
||||
using PowerResumeCallback = std::function<void()>;
|
||||
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<MpvCore> instance_;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user