Skip to content

Add Windows SystemMediaTransportControls support for MSVC builds#1926

Draft
Copilot wants to merge 8 commits intomasterfrom
copilot/add-msvc-support-windows
Draft

Add Windows SystemMediaTransportControls support for MSVC builds#1926
Copilot wants to merge 8 commits intomasterfrom
copilot/add-msvc-support-windows

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Dec 26, 2025

  • Create WindowsMediaController class for SMTC integration
  • Add header file (windowsmediacontroller.h) with SMTC interface
  • Add implementation file (windowsmediacontroller.cpp) with WinRT APIs
  • Update CMakeLists.txt to conditionally compile SMTC files for MSVC
  • Integrate WindowsMediaController in main.cpp similar to MPRIS2
  • Add HAVE_WINDOWS_MEDIA_CONTROLS configuration flag
  • Refactor implementation with proper WinRT interop and memory management
  • Fix WinRT activation factory usage
  • Fix apartment initialization crash (handle already-initialized apartments)
  • Address code review feedback (album cover URL, optional_component)
  • Fix STA thread blocking crash on album art loading
  • Test compilation on Windows with MSVC
  • Test functionality with media presence apps

Recent Updates

STA Thread Blocking Fix:

  • Replaced GetFileFromPathAsync().get() with synchronous CreateFromUri()
  • Converts local file path to file:// URI using QUrl::fromLocalFile()
  • Avoids blocking the STA thread which caused assertion failure
  • Added Windows::Foundation namespace for Uri class

Previous Code Review Fixes:

  • Fixed album cover URL reference: Now correctly uses result.album_cover.cover_url
  • Converted WINDOWS_MEDIA_CONTROLS to use optional_component

Initial Crash Fix:

  • Fixed winrt::init_apartment() crash by handling pre-initialized apartments
  • Tracks apartment initialization state to prevent double-uninit
Original prompt

This section details on the original issue you should resolve

<issue_title>Missing support for MSVC (SystemMediaTransportControls) on Windows</issue_title>
<issue_description>- [x] I have checked the FAQ for answers.

  • I have checked the Changelog that the issue is not already fixed.
  • I believe this issue is a bug, and not a general technical issue, question or feature requests that can be discussed on the forum.

Describe the bug
When playing music through Strawberry the media player doesn't report the music to Windows. Apps like Music Presence can't find it.

To Reproduce
Play some music. Windows won't natively detect playback at all.

Expected behavior
Strawberry should report the media metadata to the OS through MSVC.

System Information:

  • Operating system: Windows 11 24H2
  • Strawberry Version: 1.2.16

Additional context
https://learn.microsoft.com/en-us/uwp/api/windows.media.systemmediatransportcontrols?view=winrt-26100
</issue_description>

Comments on the Issue (you are @copilot in this section)


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI changed the title [WIP] Add support for MSVC on Windows to report media playback Add Windows SystemMediaTransportControls support for MSVC builds Dec 26, 2025
Copilot AI requested a review from jonaski December 26, 2025 18:36
Copilot AI and others added 5 commits December 28, 2025 19:46
Co-authored-by: jonaski <10343810+jonaski@users.noreply.github.com>
Co-authored-by: jonaski <10343810+jonaski@users.noreply.github.com>
Co-authored-by: jonaski <10343810+jonaski@users.noreply.github.com>
Co-authored-by: jonaski <10343810+jonaski@users.noreply.github.com>
@jonaski jonaski force-pushed the copilot/add-msvc-support-windows branch from e84dab6 to 597f983 Compare December 28, 2025 18:46
@jonaski
Copy link
Copy Markdown
Member

jonaski commented Dec 28, 2025

@copilot
I'm getting the following crash:

 	[winrt::hresult_error thrown]	
 	KernelBase.dll!00007ffad9675369()	Unknown
 	vcruntime140d.dll!00007ffaa06d9362()	Unknown
>	strawberry.exe!winrt::throw_hresult(const winrt::hresult result, const winrt::impl::slim_source_location & sourceInformation) Line 5045	C++
 	strawberry.exe!winrt::init_apartment(const winrt::apartment_type type) Line 6535	C++
 	strawberry.exe!WindowsMediaController::WindowsMediaController(HWND__ * hwnd, const std::shared_ptr<Player> player, const std::shared_ptr<PlaylistManager> playlist_manager, const std::shared_ptr<CurrentAlbumCoverLoader> current_albumcover_loader, QObject * parent) Line 75	C++
 	strawberry.exe!main(int argc, char * * argv) Line 374	C++
 	strawberry.exe!invoke_main() Line 79	C++
 	strawberry.exe!__scrt_common_main_seh() Line 288	C++
 	strawberry.exe!__scrt_common_main() Line 331	C++
 	strawberry.exe!mainCRTStartup(void * __formal) Line 17	C++
 	kernel32.dll!00007ffada347374()	Unknown
 	ntdll.dll!00007ffadbc5cc91()	Unknown

Handle case where COM/WinRT apartment is already initialized by Qt or other components. Track whether we initialized the apartment to avoid uninitializing it if we didn't initialize it.

Co-authored-by: jonaski <10343810+jonaski@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Dec 28, 2025

@copilot
I'm getting the following crash:

...

Fixed in commit 02c1596. The crash was caused by attempting to initialize the WinRT apartment when it was already initialized by Qt or another component. The fix:

  1. Catches RPC_E_CHANGED_MODE exception when apartment is already initialized
  2. Tracks initialization state with apartment_initialized_ flag
  3. Only uninitializes in destructor if we initialized it

This allows WindowsMediaController to work regardless of whether the COM/WinRT apartment was previously initialized.

Comment thread src/core/windowsmediacontroller.cpp Outdated
Comment thread CMakeLists.txt Outdated
- Fix album cover URL: use result.album_cover.cover_url instead of result.cover_url
- Convert WINDOWS_MEDIA_CONTROLS to optional_component for better configuration management

Co-authored-by: jonaski <10343810+jonaski@users.noreply.github.com>
Copilot AI requested a review from jonaski December 28, 2025 21:43
@jonaski
Copy link
Copy Markdown
Member

jonaski commented Dec 28, 2025

@copilot
It now crashes on line 282 in WindowsMediaController:

image

Callstack:

>	strawberry.exe!winrt::impl::check_sta_blocking_wait() Line 5082	C++
 	strawberry.exe!winrt::impl::wait_get<winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::Storage::StorageFile>>(const winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::Storage::StorageFile> & async) Line 5136	C++
 	strawberry.exe!winrt::impl::consume_Windows_Foundation_IAsyncOperation<winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::Storage::StorageFile>,winrt::Windows::Storage::StorageFile>::get() Line 5275	C++
 	strawberry.exe!WindowsMediaController::UpdateMetadata(const Song & song, const QUrl & art_url) Line 282	C++
 	strawberry.exe!WindowsMediaController::AlbumCoverLoaded(const Song & song, const AlbumCoverLoaderResult & result) Line 249	C++
 	strawberry.exe!QtPrivate::FunctorCall<std::integer_sequence<unsigned __int64,0,1>,QtPrivate::List<Song const &,AlbumCoverLoaderResult const &>,void,void (__cdecl WindowsMediaController::*)(Song const &,AlbumCoverLoaderResult const &)>::call::__l2::<lambda_1>::operator()() Line 127	C++
 	strawberry.exe!QtPrivate::FunctorCallBase::call_internal<void,`QtPrivate::FunctorCall<std::integer_sequence<unsigned __int64,0,1>,QtPrivate::List<Song const &,AlbumCoverLoaderResult const &>,void,void (__cdecl WindowsMediaController::*)(Song const &,AlbumCoverLoaderResult const &)>::call'::`2'::<lambda_1>>(void * * args, QtPrivate::FunctorCall<std::integer_sequence<unsigned __int64,0,1>,QtPrivate::List<Song const &,AlbumCoverLoaderResult const &>,void,void (__cdecl WindowsMediaController::*)(Song const &,AlbumCoverLoaderResult const &)>::call::__l2::<lambda_1> && fn) Line 65	C++
 	strawberry.exe!QtPrivate::FunctorCall<std::integer_sequence<unsigned __int64,0,1>,QtPrivate::List<Song const &,AlbumCoverLoaderResult const &>,void,void (__cdecl WindowsMediaController::*)(Song const &,AlbumCoverLoaderResult const &)>::call(void(WindowsMediaController::*)(const Song &, const AlbumCoverLoaderResult &) f, WindowsMediaController * o, void * * arg) Line 126	C++
 	strawberry.exe!QtPrivate::FunctionPointer<void (__cdecl WindowsMediaController::*)(Song const &,AlbumCoverLoaderResult const &)>::call<QtPrivate::List<Song const &,AlbumCoverLoaderResult const &>,void>(void(WindowsMediaController::*)(const Song &, const AlbumCoverLoaderResult &) f, WindowsMediaController * o, void * * arg) Line 174	C++
 	strawberry.exe!QtPrivate::QCallableObject<void (__cdecl WindowsMediaController::*)(Song const &,AlbumCoverLoaderResult const &),QtPrivate::List<Song const &,AlbumCoverLoaderResult const &>,void>::impl(int which, QtPrivate::QSlotObjectBase * this_, QObject * r, void * * a, bool * ret) Line 545	C++
 	Qt6Cored.dll!QtPrivate::QSlotObjectBase::call(QObject * r, void * * a) Line 461	C++
 	Qt6Cored.dll!doActivate<0>(QObject * sender, int signal_index, void * * argv) Line 4257	C++
 	Qt6Cored.dll!QMetaObject::activate(QObject * sender, const QMetaObject * m, int local_signal_index, void * * argv) Line 4317	C++
 	strawberry.exe!QMetaObject::activate<void,Song,AlbumCoverLoaderResult>(QObject * sender, const QMetaObject * mo, int local_signal_index, void * ret, const Song & <args_0>, const AlbumCoverLoaderResult & <args_1>) Line 319	C++
 	strawberry.exe!CurrentAlbumCoverLoader::AlbumCoverLoaded(const Song & _t1, const AlbumCoverLoaderResult & _t2) Line 169	C++
 	strawberry.exe!CurrentAlbumCoverLoader::AlbumCoverReady(const unsigned __int64 id, AlbumCoverLoaderResult result) Line 125	C++
 	strawberry.exe!QtPrivate::FunctorCall<std::integer_sequence<unsigned __int64,0,1>,QtPrivate::List<unsigned __int64,AlbumCoverLoaderResult const &>,void,void (__cdecl CurrentAlbumCoverLoader::*)(unsigned __int64,AlbumCoverLoaderResult)>::call::__l2::<lambda_1>::operator()() Line 127	C++
 	strawberry.exe!QtPrivate::FunctorCallBase::call_internal<void,`QtPrivate::FunctorCall<std::integer_sequence<unsigned __int64,0,1>,QtPrivate::List<unsigned __int64,AlbumCoverLoaderResult const &>,void,void (__cdecl CurrentAlbumCoverLoader::*)(unsigned __int64,AlbumCoverLoaderResult)>::call'::`2'::<lambda_1>>(void * * args, QtPrivate::FunctorCall<std::integer_sequence<unsigned __int64,0,1>,QtPrivate::List<unsigned __int64,AlbumCoverLoaderResult const &>,void,void (__cdecl CurrentAlbumCoverLoader::*)(unsigned __int64,AlbumCoverLoaderResult)>::call::__l2::<lambda_1> && fn) Line 65	C++
 	strawberry.exe!QtPrivate::FunctorCall<std::integer_sequence<unsigned __int64,0,1>,QtPrivate::List<unsigned __int64,AlbumCoverLoaderResult const &>,void,void (__cdecl CurrentAlbumCoverLoader::*)(unsigned __int64,AlbumCoverLoaderResult)>::call(void(CurrentAlbumCoverLoader::*)(unsigned __int64, AlbumCoverLoaderResult) f, CurrentAlbumCoverLoader * o, void * * arg) Line 126	C++
 	strawberry.exe!QtPrivate::FunctionPointer<void (__cdecl CurrentAlbumCoverLoader::*)(unsigned __int64,AlbumCoverLoaderResult)>::call<QtPrivate::List<unsigned __int64,AlbumCoverLoaderResult const &>,void>(void(CurrentAlbumCoverLoader::*)(unsigned __int64, AlbumCoverLoaderResult) f, CurrentAlbumCoverLoader * o, void * * arg) Line 174	C++
 	strawberry.exe!QtPrivate::QCallableObject<void (__cdecl CurrentAlbumCoverLoader::*)(unsigned __int64,AlbumCoverLoaderResult),QtPrivate::List<unsigned __int64,AlbumCoverLoaderResult const &>,void>::impl(int which, QtPrivate::QSlotObjectBase * this_, QObject * r, void * * a, bool * ret) Line 545	C++
 	Qt6Cored.dll!QtPrivate::QSlotObjectBase::call(QObject * r, void * * a) Line 461	C++
 	Qt6Cored.dll!QMetaCallEvent::placeMetaCall(QObject * object) Line 623	C++
 	Qt6Cored.dll!QObject::event(QEvent * e) Line 1413	C++
 	Qt6Widgetsd.dll!QApplicationPrivate::notify_helper(QObject * receiver, QEvent * e) Line 3305	C++
 	Qt6Widgetsd.dll!QApplication::notify(QObject * receiver, QEvent * e) Line 3255	C++
 	Qt6Cored.dll!QCoreApplication::notifyInternal2(QObject * receiver, QEvent * event) Line 1109	C++
 	Qt6Cored.dll!QCoreApplication::sendEvent(QObject * receiver, QEvent * event) Line 1550	C++
 	Qt6Cored.dll!QCoreApplicationPrivate::sendPostedEvents(QObject * receiver, int event_type, QThreadData * data) Line 1904	C++
 	Qt6Cored.dll!QEventDispatcherWin32::sendPostedEvents() Line 925	C++
 	Qt6Guid.dll!QWindowsGuiEventDispatcher::sendPostedEvents() Line 44	C++
 	Qt6Cored.dll!QEventDispatcherWin32::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags) Line 483	C++
 	Qt6Guid.dll!QWindowsGuiEventDispatcher::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags) Line 36	C++
 	Qt6Cored.dll!QEventLoop::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags) Line 105	C++
 	Qt6Cored.dll!QEventLoop::exec(QFlags<enum QEventLoop::ProcessEventsFlag> flags) Line 186	C++
 	Qt6Cored.dll!QCoreApplication::exec() Line 1452	C++
 	strawberry.exe!main(int argc, char * * argv) Line 386	C++
 	strawberry.exe!invoke_main() Line 79	C++
 	strawberry.exe!__scrt_common_main_seh() Line 288	C++
 	strawberry.exe!__scrt_common_main() Line 331	C++
 	strawberry.exe!mainCRTStartup(void * __formal) Line 17	C++
 	kernel32.dll!00007ffada347374()	Unknown
 	ntdll.dll!00007ffadbc5cc91()	Unknown

Replace async GetFileFromPathAsync().get() with synchronous CreateFromUri() to avoid blocking STA thread. This prevents the "sta thread blocking wait" assertion failure.

Co-authored-by: jonaski <10343810+jonaski@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Dec 28, 2025

@copilot
It now crashes on line 282 in WindowsMediaController:

...

Fixed in commit d2afa8f. The crash was caused by calling .get() on an async operation in an STA thread, which violates WinRT threading rules. The fix:

  • Replaced GetFileFromPathAsync().get() with synchronous CreateFromUri()
  • Converts local file path to file:// URI using QUrl::fromLocalFile()
  • Added Windows::Foundation namespace for Uri class

This avoids blocking the STA thread and prevents the assertion failure.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

(Windows) Implement SystemMediaTransportControls

2 participants