diff --git a/packages/audioplayers_windows/CHANGELOG.md b/packages/audioplayers_windows/CHANGELOG.md index 633a1e45d..71e501306 100644 --- a/packages/audioplayers_windows/CHANGELOG.md +++ b/packages/audioplayers_windows/CHANGELOG.md @@ -1,3 +1,7 @@ +## 4.2.2 + +- **FIX**: Fixed app crash on startup on Windows with missing Media Feature Pack + ## 4.2.1 - **FIX**: Migrate to Melos v7 and Pub Workspaces ([#1929](https://github.com/bluefireteam/audioplayers/issues/1929)). ([9d0bfe0b](https://github.com/bluefireteam/audioplayers/commit/9d0bfe0be5cba0ce4fb3a75912b41117a8996bfe)) diff --git a/packages/audioplayers_windows/pubspec.yaml b/packages/audioplayers_windows/pubspec.yaml index de0432291..22abdaa23 100644 --- a/packages/audioplayers_windows/pubspec.yaml +++ b/packages/audioplayers_windows/pubspec.yaml @@ -1,7 +1,7 @@ name: audioplayers_windows resolution: workspace description: Windows implementation of audioplayers, a Flutter plugin to play multiple audio files simultaneously -version: 4.2.1 +version: 4.2.2 homepage: https://github.com/bluefireteam/audioplayers repository: https://github.com/bluefireteam/audioplayers/tree/master/packages/audioplayers_windows diff --git a/packages/audioplayers_windows/windows/CMakeLists.txt b/packages/audioplayers_windows/windows/CMakeLists.txt index 0943f70ff..c0241c09d 100644 --- a/packages/audioplayers_windows/windows/CMakeLists.txt +++ b/packages/audioplayers_windows/windows/CMakeLists.txt @@ -46,13 +46,17 @@ set_target_properties(${PLUGIN_NAME} PROPERTIES CXX_VISIBILITY_PRESET hidden) target_link_libraries(${PLUGIN_NAME} PRIVATE ${CMAKE_BINARY_DIR}/packages/Microsoft.Windows.ImplementationLibrary/build/native/Microsoft.Windows.ImplementationLibrary.targets) -target_link_libraries(${PLUGIN_NAME} PRIVATE Mfplat windowsapp) +target_link_libraries(${PLUGIN_NAME} PRIVATE windowsapp) target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL) target_include_directories(${PLUGIN_NAME} INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include") target_link_libraries(${PLUGIN_NAME} PRIVATE flutter flutter_wrapper_plugin) -target_link_libraries(${PLUGIN_NAME} PRIVATE shlwapi) +target_link_libraries(${PLUGIN_NAME} PRIVATE shlwapi Mfplat.lib) + +target_link_libraries(${PLUGIN_NAME} PRIVATE delayimp.lib) +target_link_options(${PLUGIN_NAME} PRIVATE "/DELAYLOAD:Mfplat.dll") +target_link_options(${PLUGIN_NAME} PRIVATE "/DELAYLOAD:mfreadwrite.dll") # List of absolute paths to libraries that should be bundled with the plugin set(audioplayers_windows_bundled_libraries diff --git a/packages/audioplayers_windows/windows/audio_player.cpp b/packages/audioplayers_windows/windows/audio_player.cpp index 00baff8d7..b807dc69e 100644 --- a/packages/audioplayers_windows/windows/audio_player.cpp +++ b/packages/audioplayers_windows/windows/audio_player.cpp @@ -25,29 +25,58 @@ AudioPlayer::AudioPlayer( : _playerId(playerId), _methodChannel(methodChannel), _eventHandler(eventHandler) { - m_mfPlatform.Startup(); - - // Callbacks invoked by the media engine wrapper - auto onError = std::bind(&AudioPlayer::OnMediaError, this, - std::placeholders::_1, std::placeholders::_2); - auto onBufferingStateChanged = - std::bind(&AudioPlayer::OnMediaStateChange, this, std::placeholders::_1); - auto onPlaybackEndedCB = std::bind(&AudioPlayer::OnPlaybackEnded, this); - auto onSeekCompletedCB = std::bind(&AudioPlayer::OnSeekCompleted, this); - auto onLoadedCB = std::bind(&AudioPlayer::SendInitialized, this); - - // Create and initialize the MediaEngineWrapper which manages media playback - m_mediaEngineWrapper = winrt::make_self( - onLoadedCB, onError, onBufferingStateChanged, onPlaybackEndedCB, - onSeekCompletedCB); - - m_mediaEngineWrapper->Initialize(); + HMODULE hMfplat = + LoadLibraryEx(L"Mfplat.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); + HMODULE hMfreadwrite = + LoadLibraryEx(L"mfreadwrite.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); + + if (!hMfplat || !hMfreadwrite) { + m_mediaFoundationFailed = true; + if (hMfplat) + FreeLibrary(hMfplat); + if (hMfreadwrite) + FreeLibrary(hMfreadwrite); + return; + } + FreeLibrary(hMfplat); + FreeLibrary(hMfreadwrite); + + try { + m_mfPlatform.Startup(); + + // Callbacks invoked by the media engine wrapper + auto onError = std::bind(&AudioPlayer::OnMediaError, this, + std::placeholders::_1, std::placeholders::_2); + auto onBufferingStateChanged = std::bind(&AudioPlayer::OnMediaStateChange, + this, std::placeholders::_1); + auto onPlaybackEndedCB = std::bind(&AudioPlayer::OnPlaybackEnded, this); + auto onSeekCompletedCB = std::bind(&AudioPlayer::OnSeekCompleted, this); + auto onLoadedCB = std::bind(&AudioPlayer::SendInitialized, this); + + // Create and initialize the MediaEngineWrapper which manages media + // playback + m_mediaEngineWrapper = winrt::make_self( + onLoadedCB, onError, onBufferingStateChanged, onPlaybackEndedCB, + onSeekCompletedCB); + + m_mediaEngineWrapper->Initialize(); + } catch (...) { + m_mediaFoundationFailed = true; + } } AudioPlayer::~AudioPlayer() {} // This method should be called asynchronously, to avoid freezing UI void AudioPlayer::SetSourceUrl(std::string url) { + if (m_mediaFoundationFailed) { + this->OnError("WindowsAudioError", + "Media Feature Pack not found. Please install it from " + "Windows Settings > Optional Features.", + nullptr); + return; + } + if (_url != url) { _url = url; _isInitialized = false; @@ -90,6 +119,14 @@ void AudioPlayer::SetSourceUrl(std::string url) { } void AudioPlayer::SetSourceBytes(std::vector bytes) { + if (m_mediaFoundationFailed) { + this->OnError("WindowsAudioError", + "Media Feature Pack not found. Please install it from " + "Windows Settings > Optional Features.", + nullptr); + return; + } + _isInitialized = false; _url.clear(); size_t size = bytes.size(); @@ -181,6 +218,8 @@ void AudioPlayer::OnPlaybackEnded() { } void AudioPlayer::OnDurationUpdate() { + if (m_mediaFoundationFailed || !m_mediaEngineWrapper) + return; auto duration = m_mediaEngineWrapper->GetDuration(); if (this->_eventHandler) { this->_eventHandler->Success( @@ -214,6 +253,8 @@ void AudioPlayer::OnLog(const std::string& message) { } void AudioPlayer::SendInitialized() { + if (m_mediaFoundationFailed || !m_mediaEngineWrapper) + return; if (!this->_isInitialized) { this->_isInitialized = true; OnPrepared(true); @@ -222,6 +263,8 @@ void AudioPlayer::SendInitialized() { } void AudioPlayer::ReleaseMediaSource() { + if (m_mediaFoundationFailed || !m_mediaEngineWrapper) + return; if (_isInitialized) { m_mediaEngineWrapper->Pause(); } @@ -231,6 +274,12 @@ void AudioPlayer::ReleaseMediaSource() { } void AudioPlayer::Dispose() { + if (m_mediaFoundationFailed || !m_mediaEngineWrapper) { + _methodChannel = nullptr; + _eventHandler = nullptr; + return; + } + ReleaseMediaSource(); m_mediaEngineWrapper->Shutdown(); _methodChannel = nullptr; @@ -238,6 +287,8 @@ void AudioPlayer::Dispose() { } void AudioPlayer::SetReleaseMode(ReleaseMode releaseMode) { + if (m_mediaFoundationFailed || !m_mediaEngineWrapper) + return; m_mediaEngineWrapper->SetLooping(releaseMode == ReleaseMode::loop); _releaseMode = releaseMode; } @@ -247,6 +298,9 @@ ReleaseMode AudioPlayer::GetReleaseMode() { } void AudioPlayer::SetVolume(double volume) { + if (m_mediaFoundationFailed || !m_mediaEngineWrapper) + return; + if (volume > 1) { volume = 1; } else if (volume < 0) { @@ -256,23 +310,33 @@ void AudioPlayer::SetVolume(double volume) { } void AudioPlayer::SetPlaybackSpeed(double playbackSpeed) { + if (m_mediaFoundationFailed || !m_mediaEngineWrapper) + return; m_mediaEngineWrapper->SetPlaybackRate(playbackSpeed); } void AudioPlayer::SetBalance(double balance) { + if (m_mediaFoundationFailed || !m_mediaEngineWrapper) + return; m_mediaEngineWrapper->SetBalance(balance); } void AudioPlayer::Play() { + if (m_mediaFoundationFailed || !m_mediaEngineWrapper) + return; m_mediaEngineWrapper->StartPlayingFrom(m_mediaEngineWrapper->GetMediaTime()); OnDurationUpdate(); } void AudioPlayer::Pause() { + if (m_mediaFoundationFailed || !m_mediaEngineWrapper) + return; m_mediaEngineWrapper->Pause(); } void AudioPlayer::Stop() { + if (m_mediaFoundationFailed || !m_mediaEngineWrapper) + return; Pause(); if (GetReleaseMode() == ReleaseMode::release) { ReleaseMediaSource(); @@ -282,21 +346,28 @@ void AudioPlayer::Stop() { } void AudioPlayer::Resume() { + if (m_mediaFoundationFailed || !m_mediaEngineWrapper) + return; m_mediaEngineWrapper->Resume(); OnDurationUpdate(); } double AudioPlayer::GetPosition() { - if (!_isInitialized) { + if (m_mediaFoundationFailed || !_isInitialized) { return std::numeric_limits::quiet_NaN(); } return m_mediaEngineWrapper->GetMediaTime(); } double AudioPlayer::GetDuration() { + if (m_mediaFoundationFailed || !m_mediaEngineWrapper) { + return std::numeric_limits::quiet_NaN(); + } return m_mediaEngineWrapper->GetDuration(); } void AudioPlayer::SeekTo(double seek) { + if (m_mediaFoundationFailed || !m_mediaEngineWrapper) + return; m_mediaEngineWrapper->SeekTo(seek); } diff --git a/packages/audioplayers_windows/windows/audio_player.h b/packages/audioplayers_windows/windows/audio_player.h index 3254f0454..1c3eac082 100644 --- a/packages/audioplayers_windows/windows/audio_player.h +++ b/packages/audioplayers_windows/windows/audio_player.h @@ -104,6 +104,7 @@ class AudioPlayer { winrt::com_ptr m_mediaEngineWrapper; bool _isInitialized = false; + bool m_mediaFoundationFailed = false; ReleaseMode _releaseMode = ReleaseMode::release; std::string _url{};