diff --git a/feature_parity_table.md b/feature_parity_table.md index 0f55fdd71..b1a605d25 100644 --- a/feature_parity_table.md +++ b/feature_parity_table.md @@ -31,7 +31,7 @@ Note: LLM means Low Latency Mode. local assetyesyesyesyesyesyes external URL fileyesyesyesyesyesyes external URL streamyesyesyesyesyesyes - byte arraySDK >=23not yetnot yetnot yetnot yetnot yet + byte arraySDK >=23not yetnot yetnot yetyesnot yet Audio Config set urlyesyesyesyesyesyes audio cache (pre-load)yesyesyesyesyesyes diff --git a/packages/audioplayers/example/integration_test/platform_features.dart b/packages/audioplayers/example/integration_test/platform_features.dart index 9ae8659a0..ff9a83aea 100644 --- a/packages/audioplayers/example/integration_test/platform_features.dart +++ b/packages/audioplayers/example/integration_test/platform_features.dart @@ -58,7 +58,6 @@ class PlatformFeatures { ); static const windowsPlatformFeatures = PlatformFeatures( - hasBytesSource: false, hasPlaylistSourceType: false, hasLowLatency: false, hasReleaseModeRelease: false, diff --git a/packages/audioplayers/lib/src/audio_cache.dart b/packages/audioplayers/lib/src/audio_cache.dart index e95c6a29f..98c76b599 100644 --- a/packages/audioplayers/lib/src/audio_cache.dart +++ b/packages/audioplayers/lib/src/audio_cache.dart @@ -125,7 +125,8 @@ class AudioCache { throw 'This method cannot be used on web!'; } final uri = await load(fileName); - return fileSystem.file(uri.toFilePath(windows: false)); + return fileSystem.file(uri.toFilePath( + windows: defaultTargetPlatform == TargetPlatform.windows)); } /// Loads a single [fileName] to the cache but returns it as a list of bytes. diff --git a/packages/audioplayers_windows/windows/CMakeLists.txt b/packages/audioplayers_windows/windows/CMakeLists.txt index 5b269bebb..0943f70ff 100644 --- a/packages/audioplayers_windows/windows/CMakeLists.txt +++ b/packages/audioplayers_windows/windows/CMakeLists.txt @@ -52,6 +52,7 @@ 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) # 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 a0a8934d8..d9bd6cd41 100644 --- a/packages/audioplayers_windows/windows/audio_player.cpp +++ b/packages/audioplayers_windows/windows/audio_player.cpp @@ -6,6 +6,7 @@ #include #include #include +#include // for SHCreateMemStream #include #include @@ -42,6 +43,8 @@ AudioPlayer::AudioPlayer( m_mediaEngineWrapper->Initialize(); } +AudioPlayer::~AudioPlayer() {} + // This method should be called asynchronously, to avoid freezing UI void AudioPlayer::SetSourceUrl(std::string url) { if (_url != url) { @@ -77,7 +80,36 @@ void AudioPlayer::SetSourceUrl(std::string url) { } } -AudioPlayer::~AudioPlayer() {} +void AudioPlayer::SetSourceBytes(std::vector bytes) { + _isInitialized = false; + _url.clear(); + size_t size = bytes.size(); + + try { + winrt::com_ptr sourceResolver; + THROW_IF_FAILED(MFCreateSourceResolver(sourceResolver.put())); + constexpr uint32_t sourceResolutionFlags = + MF_RESOLUTION_MEDIASOURCE | + MF_RESOLUTION_CONTENT_DOES_NOT_HAVE_TO_MATCH_EXTENSION_OR_MIME_TYPE | + MF_RESOLUTION_READ; + MF_OBJECT_TYPE objectType = {}; + + winrt::com_ptr mediaSource; + + IStream* pstm = + SHCreateMemStream(bytes.data(), static_cast(size)); + IMFByteStream* stream = NULL; + MFCreateMFByteStreamOnStream(pstm, &stream); + + sourceResolver->CreateObjectFromByteStream( + stream, nullptr, sourceResolutionFlags, nullptr, &objectType, + reinterpret_cast(mediaSource.put_void())); + m_mediaEngineWrapper->SetMediaSource(mediaSource.get()); + } catch (...) { + // Forward errors to event stream, as this is called asynchronously + this->OnError("WindowsAudioError", "Error setting bytes", nullptr); + } +} void AudioPlayer::OnMediaError(MF_MEDIA_ENGINE_ERR error, HRESULT hr) { LOG_HR_MSG(hr, "MediaEngine error (%d)", error); @@ -199,6 +231,7 @@ void AudioPlayer::ReleaseMediaSource() { m_mediaEngineWrapper->Pause(); } m_mediaEngineWrapper->ReleaseMediaSource(); + _url.clear(); _isInitialized = false; } diff --git a/packages/audioplayers_windows/windows/audio_player.h b/packages/audioplayers_windows/windows/audio_player.h index f9f7137e3..771cad7c7 100644 --- a/packages/audioplayers_windows/windows/audio_player.h +++ b/packages/audioplayers_windows/windows/audio_player.h @@ -77,6 +77,8 @@ class AudioPlayer { void SeekTo(double seek); + void SetSourceBytes(std::vector bytes); + void SetSourceUrl(std::string url); void OnLog(const std::string& message); diff --git a/packages/audioplayers_windows/windows/audioplayers_windows_plugin.cpp b/packages/audioplayers_windows/windows/audioplayers_windows_plugin.cpp index 8cbd80894..24e6b9b4c 100644 --- a/packages/audioplayers_windows/windows/audioplayers_windows_plugin.cpp +++ b/packages/audioplayers_windows/windows/audioplayers_windows_plugin.cpp @@ -178,6 +178,17 @@ void AudioplayersWindowsPlugin::HandleMethodCall( } std::thread(&AudioPlayer::SetSourceUrl, player, url).detach(); + } else if (method_call.method_name().compare("setSourceBytes") == 0) { + auto data = GetArgument>("bytes", args, + std::vector{}); + + if (data.empty()) { + result->Error("WindowsAudioError", + "Null bytes received on setSourceBytes", nullptr); + return; + } + + std::thread(&AudioPlayer::SetSourceBytes, player, data).detach(); } else if (method_call.method_name().compare("getDuration") == 0) { auto duration = player->GetDuration(); result->Success(isnan(duration)