Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=58631
-- v2: windows.media.playback.mediaplayer: Implement IMediaPlayer2::get_SystemMediaTransportControls().
From: Mohamad Al-Jaf mohamadaljaf@gmail.com
--- include/windows.media.playback.idl | 44 ++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+)
diff --git a/include/windows.media.playback.idl b/include/windows.media.playback.idl index 4f09cc90679..ed33632cb28 100644 --- a/include/windows.media.playback.idl +++ b/include/windows.media.playback.idl @@ -39,11 +39,14 @@ import "windows.storage.streams.idl"; import "windows.ui.composition.idl";
namespace Windows.Media.Playback { + typedef enum MediaPlayerAudioCategory MediaPlayerAudioCategory; + typedef enum MediaPlayerAudioDeviceType MediaPlayerAudioDeviceType; typedef enum MediaPlayerError MediaPlayerError; typedef enum MediaPlayerState MediaPlayerState;
interface IBackgroundMediaPlayerStatics; interface IMediaPlayer; + interface IMediaPlayer2; interface IMediaPlayerDataReceivedEventArgs; interface IMediaPlayerFailedEventArgs; interface IMediaPlayerRateChangedEventArgs; @@ -71,6 +74,33 @@ namespace Windows.Media.Playback { interface Windows.Foundation.TypedEventHandler<Windows.Media.Playback.MediaPlayer *, Windows.Media.Playback.PlaybackMediaMarkerReachedEventArgs *>; }
+ [ + contract(Windows.Foundation.UniversalApiContract, 1.0) + ] + enum MediaPlayerAudioCategory + { + Other = 0, + Communications = 3, + Alerts = 4, + SoundEffects = 5, + GameEffects = 6, + GameMedia = 7, + GameChat = 8, + Speech = 9, + Movie = 10, + Media = 11, + }; + + [ + contract(Windows.Foundation.UniversalApiContract, 1.0) + ] + enum MediaPlayerAudioDeviceType + { + Console = 0, + Multimedia = 1, + Communications = 2, + }; + [ contract(Windows.Foundation.UniversalApiContract, 1.0) ] @@ -227,6 +257,20 @@ namespace Windows.Media.Playback { HRESULT SetUriSource([in] Windows.Foundation.Uri *value); }
+ [ + contract(Windows.Foundation.UniversalApiContract, 1.0), + exclusiveto(Windows.Media.Playback.MediaPlayer), + uuid(3c841218-2123-4fc5-9082-2f883f77bdf5) + ] + interface IMediaPlayer2 : IInspectable + { + [propget] HRESULT SystemMediaTransportControls([out, retval] Windows.Media.SystemMediaTransportControls **value); + [propget] HRESULT AudioCategory([out, retval] Windows.Media.Playback.MediaPlayerAudioCategory *value); + [propput] HRESULT AudioCategory([in] Windows.Media.Playback.MediaPlayerAudioCategory value); + [propget] HRESULT AudioDeviceType([out, retval] Windows.Media.Playback.MediaPlayerAudioDeviceType *value); + [propput] HRESULT AudioDeviceType([in] Windows.Media.Playback.MediaPlayerAudioDeviceType value); + } + [ contract(Windows.Foundation.UniversalApiContract, 1.0), exclusiveto(Windows.Media.Playback.MediaPlayerDataReceivedEventArgs),
From: Mohamad Al-Jaf mohamadaljaf@gmail.com
--- .../private.h | 1 + .../windows.media.playback.mediaplayer/main.c | 59 +++++++++++++++++++ .../private.h | 38 ++++++++++++ .../tests/mediaplayer.c | 6 ++ 4 files changed, 104 insertions(+)
diff --git a/dlls/windows.media.playback.backgroundmediaplayer/private.h b/dlls/windows.media.playback.backgroundmediaplayer/private.h index 63115ff2a6f..6abcbff33c6 100644 --- a/dlls/windows.media.playback.backgroundmediaplayer/private.h +++ b/dlls/windows.media.playback.backgroundmediaplayer/private.h @@ -32,6 +32,7 @@ #define WIDL_using_Windows_Foundation #define WIDL_using_Windows_Foundation_Collections #include "windows.foundation.h" +#define WIDL_using_Windows_Media #define WIDL_using_Windows_Media_Playback #include "windows.media.playback.h"
diff --git a/dlls/windows.media.playback.mediaplayer/main.c b/dlls/windows.media.playback.mediaplayer/main.c index 08d170c88df..67b50ba065b 100644 --- a/dlls/windows.media.playback.mediaplayer/main.c +++ b/dlls/windows.media.playback.mediaplayer/main.c @@ -27,6 +27,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(mediaplayer); struct media_player { IMediaPlayer IMediaPlayer_iface; + IMediaPlayer2 IMediaPlayer2_iface; LONG ref; };
@@ -51,6 +52,13 @@ static HRESULT WINAPI media_player_QueryInterface( IMediaPlayer *iface, REFIID i return S_OK; }
+ if (IsEqualGUID( iid, &IID_IMediaPlayer2 )) + { + *out = &impl->IMediaPlayer2_iface; + IInspectable_AddRef( *out ); + return S_OK; + } + FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); *out = NULL; return E_NOINTERFACE; @@ -404,6 +412,56 @@ static const struct IMediaPlayerVtbl media_player_vtbl = media_player_SetUriSource, };
+DEFINE_IINSPECTABLE( media_player2, IMediaPlayer2, struct media_player, IMediaPlayer_iface ) + +static HRESULT WINAPI media_player2_get_SystemMediaTransportControls( IMediaPlayer2 *iface, ISystemMediaTransportControls **value ) +{ + FIXME( "iface %p, value %p stub!\n", iface, value ); + return E_NOTIMPL; +} + +static HRESULT WINAPI media_player2_get_AudioCategory( IMediaPlayer2 *iface, MediaPlayerAudioCategory *value ) +{ + FIXME( "iface %p, value %p stub!\n", iface, value ); + return E_NOTIMPL; +} + +static HRESULT WINAPI media_player2_put_AudioCategory( IMediaPlayer2 *iface, MediaPlayerAudioCategory value ) +{ + FIXME( "iface %p, value %#x stub!\n", iface, value ); + return E_NOTIMPL; +} + +static HRESULT WINAPI media_player2_get_AudioDeviceType( IMediaPlayer2 *iface, MediaPlayerAudioDeviceType *value ) +{ + FIXME( "iface %p, value %p stub!\n", iface, value ); + return E_NOTIMPL; +} + +static HRESULT WINAPI media_player2_put_AudioDeviceType( IMediaPlayer2 *iface, MediaPlayerAudioDeviceType value ) +{ + FIXME( "iface %p, value %#x stub!\n", iface, value ); + return E_NOTIMPL; +} + +static const struct IMediaPlayer2Vtbl media_player2_vtbl = +{ + /* IUnknown methods */ + media_player2_QueryInterface, + media_player2_AddRef, + media_player2_Release, + /* IInspectable methods */ + media_player2_GetIids, + media_player2_GetRuntimeClassName, + media_player2_GetTrustLevel, + /* IMediaPlayer2 methods */ + media_player2_get_SystemMediaTransportControls, + media_player2_get_AudioCategory, + media_player2_put_AudioCategory, + media_player2_get_AudioDeviceType, + media_player2_put_AudioDeviceType, +}; + struct media_player_statics { IActivationFactory IActivationFactory_iface; @@ -482,6 +540,7 @@ static HRESULT WINAPI factory_ActivateInstance( IActivationFactory *iface, IInsp }
impl->IMediaPlayer_iface.lpVtbl = &media_player_vtbl; + impl->IMediaPlayer2_iface.lpVtbl = &media_player2_vtbl; impl->ref = 1;
*instance = (IInspectable *)&impl->IMediaPlayer_iface; diff --git a/dlls/windows.media.playback.mediaplayer/private.h b/dlls/windows.media.playback.mediaplayer/private.h index e356abcff28..d5bbba3206a 100644 --- a/dlls/windows.media.playback.mediaplayer/private.h +++ b/dlls/windows.media.playback.mediaplayer/private.h @@ -36,4 +36,42 @@ #define WIDL_using_Windows_Media_Playback #include "windows.media.playback.h"
+#define DEFINE_IINSPECTABLE_( pfx, iface_type, impl_type, impl_from, iface_mem, expr ) \ + static inline impl_type *impl_from( iface_type *iface ) \ + { \ + return CONTAINING_RECORD( iface, impl_type, iface_mem ); \ + } \ + static HRESULT WINAPI pfx##_QueryInterface( iface_type *iface, REFIID iid, void **out ) \ + { \ + impl_type *impl = impl_from( iface ); \ + return IInspectable_QueryInterface( (IInspectable *)(expr), iid, out ); \ + } \ + static ULONG WINAPI pfx##_AddRef( iface_type *iface ) \ + { \ + impl_type *impl = impl_from( iface ); \ + return IInspectable_AddRef( (IInspectable *)(expr) ); \ + } \ + static ULONG WINAPI pfx##_Release( iface_type *iface ) \ + { \ + impl_type *impl = impl_from( iface ); \ + return IInspectable_Release( (IInspectable *)(expr) ); \ + } \ + static HRESULT WINAPI pfx##_GetIids( iface_type *iface, ULONG *iid_count, IID **iids ) \ + { \ + impl_type *impl = impl_from( iface ); \ + return IInspectable_GetIids( (IInspectable *)(expr), iid_count, iids ); \ + } \ + static HRESULT WINAPI pfx##_GetRuntimeClassName( iface_type *iface, HSTRING *class_name ) \ + { \ + impl_type *impl = impl_from( iface ); \ + return IInspectable_GetRuntimeClassName( (IInspectable *)(expr), class_name ); \ + } \ + static HRESULT WINAPI pfx##_GetTrustLevel( iface_type *iface, TrustLevel *trust_level ) \ + { \ + impl_type *impl = impl_from( iface ); \ + return IInspectable_GetTrustLevel( (IInspectable *)(expr), trust_level ); \ + } +#define DEFINE_IINSPECTABLE( pfx, iface_type, impl_type, base_iface ) \ + DEFINE_IINSPECTABLE_( pfx, iface_type, impl_type, impl_from_##iface_type, iface_type##_iface, &impl->base_iface ) + #endif diff --git a/dlls/windows.media.playback.mediaplayer/tests/mediaplayer.c b/dlls/windows.media.playback.mediaplayer/tests/mediaplayer.c index 6262190e81d..93b0a3d71a4 100644 --- a/dlls/windows.media.playback.mediaplayer/tests/mediaplayer.c +++ b/dlls/windows.media.playback.mediaplayer/tests/mediaplayer.c @@ -48,6 +48,7 @@ static void check_interface_( unsigned int line, void *obj, const IID *iid, BOOL static void test_MediaPlayer_Statics(void) { static const WCHAR *media_player_name = L"Windows.Media.Playback.MediaPlayer"; + IMediaPlayer2 *media_player2 = (void *)0xdeadbeef; IActivationFactory *factory = (void *)0xdeadbeef; IMediaPlayer *media_player = (void *)0xdeadbeef; IInspectable *inspectable = (void *)0xdeadbeef; @@ -81,6 +82,11 @@ static void test_MediaPlayer_Statics(void)
check_interface( media_player, &IID_IAgileObject, TRUE );
+ hr = IMediaPlayer_QueryInterface( media_player, &IID_IMediaPlayer2, (void **)&media_player2 ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + + ref = IMediaPlayer2_Release( media_player2 ); + ok( ref == 2, "got ref %ld.\n", ref ); ref = IMediaPlayer_Release( media_player ); ok( ref == 1, "got ref %ld.\n", ref ); ref = IInspectable_Release( inspectable );
From: Mohamad Al-Jaf mohamadaljaf@gmail.com
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=58631 --- .../Makefile.in | 2 +- .../windows.media.playback.mediaplayer/main.c | 70 ++++++++++++++++++- .../private.h | 2 + .../tests/mediaplayer.c | 13 ++++ 4 files changed, 83 insertions(+), 4 deletions(-)
diff --git a/dlls/windows.media.playback.mediaplayer/Makefile.in b/dlls/windows.media.playback.mediaplayer/Makefile.in index 9fa6895c9ea..f37a609b848 100644 --- a/dlls/windows.media.playback.mediaplayer/Makefile.in +++ b/dlls/windows.media.playback.mediaplayer/Makefile.in @@ -1,5 +1,5 @@ MODULE = windows.media.playback.mediaplayer.dll -IMPORTS = combase +IMPORTS = combase user32
SOURCES = \ classes.idl \ diff --git a/dlls/windows.media.playback.mediaplayer/main.c b/dlls/windows.media.playback.mediaplayer/main.c index 67b50ba065b..a486a58be5e 100644 --- a/dlls/windows.media.playback.mediaplayer/main.c +++ b/dlls/windows.media.playback.mediaplayer/main.c @@ -29,6 +29,10 @@ struct media_player IMediaPlayer IMediaPlayer_iface; IMediaPlayer2 IMediaPlayer2_iface; LONG ref; + + CRITICAL_SECTION cs; + ISystemMediaTransportControls *controls; + HWND window; };
static inline struct media_player *impl_from_IMediaPlayer( IMediaPlayer *iface ) @@ -79,7 +83,20 @@ static ULONG WINAPI media_player_Release( IMediaPlayer *iface )
TRACE( "iface %p decreasing refcount to %lu.\n", iface, ref );
- if (!ref) free( impl ); + if (!ref) + { + EnterCriticalSection( &impl->cs ); + if (!impl->ref && impl->controls) + { + ISystemMediaTransportControls_Release( impl->controls ); + DestroyWindow( impl->window ); + } + LeaveCriticalSection( &impl->cs ); + + impl->cs.DebugInfo->Spare[0] = 0; + DeleteCriticalSection( &impl->cs ); + free( impl ); + } return ref; }
@@ -414,10 +431,54 @@ static const struct IMediaPlayerVtbl media_player_vtbl =
DEFINE_IINSPECTABLE( media_player2, IMediaPlayer2, struct media_player, IMediaPlayer_iface )
+static HRESULT get_system_media_transport_controls( HWND *window, ISystemMediaTransportControls **controls ) +{ + static const WCHAR *media_control_statics_name = L"Windows.Media.SystemMediaTransportControls"; + ISystemMediaTransportControlsInterop *media_control_interop_statics = NULL; + IActivationFactory *factory = NULL; + HSTRING str = NULL; + HRESULT hr; + + FIXME( "shell integration not implemented.\n" ); + + if (!(*window = CreateWindowExA( 0, "static", NULL, WS_POPUP, 0, 0, 0, 0, NULL, NULL, GetModuleHandleA( NULL ), NULL ))) return HRESULT_FROM_WIN32( GetLastError() ); + + if (FAILED(hr = WindowsCreateString( media_control_statics_name, wcslen( media_control_statics_name ), &str ))) return hr; + if (SUCCEEDED(hr)) hr = RoGetActivationFactory( str, &IID_IActivationFactory, (void **)&factory ); + if (SUCCEEDED(hr)) hr = IActivationFactory_QueryInterface( factory, &IID_ISystemMediaTransportControlsInterop, (void **)&media_control_interop_statics ); + if (SUCCEEDED(hr)) hr = ISystemMediaTransportControlsInterop_GetForWindow( media_control_interop_statics, *window, &IID_ISystemMediaTransportControls, (void **)controls ); + + if (media_control_interop_statics) ISystemMediaTransportControlsInterop_Release( media_control_interop_statics ); + if (factory) IActivationFactory_Release( factory ); + WindowsDeleteString( str ); + return hr; +} + static HRESULT WINAPI media_player2_get_SystemMediaTransportControls( IMediaPlayer2 *iface, ISystemMediaTransportControls **value ) { - FIXME( "iface %p, value %p stub!\n", iface, value ); - return E_NOTIMPL; + struct media_player *impl = impl_from_IMediaPlayer2( iface ); + HRESULT hr; + + TRACE( "iface %p, value %p\n", iface, value ); + + EnterCriticalSection( &impl->cs ); + + if (!impl->controls && FAILED(hr = get_system_media_transport_controls( &impl->window, &impl->controls ))) + { + if (impl->window) + { + DestroyWindow( impl->window ); + impl->window = NULL; + } + *value = NULL; + LeaveCriticalSection( &impl->cs ); + return hr; + } + + *value = impl->controls; + ISystemMediaTransportControls_AddRef( *value ); + LeaveCriticalSection( &impl->cs ); + return S_OK; }
static HRESULT WINAPI media_player2_get_AudioCategory( IMediaPlayer2 *iface, MediaPlayerAudioCategory *value ) @@ -543,6 +604,9 @@ static HRESULT WINAPI factory_ActivateInstance( IActivationFactory *iface, IInsp impl->IMediaPlayer2_iface.lpVtbl = &media_player2_vtbl; impl->ref = 1;
+ InitializeCriticalSectionEx( &impl->cs, 0, RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO ); + impl->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": media_controls.cs"); + *instance = (IInspectable *)&impl->IMediaPlayer_iface; return S_OK; } diff --git a/dlls/windows.media.playback.mediaplayer/private.h b/dlls/windows.media.playback.mediaplayer/private.h index d5bbba3206a..f1bc402a3b2 100644 --- a/dlls/windows.media.playback.mediaplayer/private.h +++ b/dlls/windows.media.playback.mediaplayer/private.h @@ -28,6 +28,7 @@ #include "winstring.h"
#include "activation.h" +#include "roapi.h"
#define WIDL_using_Windows_Foundation #define WIDL_using_Windows_Foundation_Collections @@ -35,6 +36,7 @@ #define WIDL_using_Windows_Media #define WIDL_using_Windows_Media_Playback #include "windows.media.playback.h" +#include "systemmediatransportcontrolsinterop.h"
#define DEFINE_IINSPECTABLE_( pfx, iface_type, impl_type, impl_from, iface_mem, expr ) \ static inline impl_type *impl_from( iface_type *iface ) \ diff --git a/dlls/windows.media.playback.mediaplayer/tests/mediaplayer.c b/dlls/windows.media.playback.mediaplayer/tests/mediaplayer.c index 93b0a3d71a4..8b3af42ae8d 100644 --- a/dlls/windows.media.playback.mediaplayer/tests/mediaplayer.c +++ b/dlls/windows.media.playback.mediaplayer/tests/mediaplayer.c @@ -28,6 +28,7 @@ #define WIDL_using_Windows_Foundation #define WIDL_using_Windows_Foundation_Collections #include "windows.foundation.h" +#define WIDL_using_Windows_Media #define WIDL_using_Windows_Media_Playback #include "windows.media.playback.h"
@@ -48,6 +49,8 @@ static void check_interface_( unsigned int line, void *obj, const IID *iid, BOOL static void test_MediaPlayer_Statics(void) { static const WCHAR *media_player_name = L"Windows.Media.Playback.MediaPlayer"; + ISystemMediaTransportControls *media_transport_controls2 = (void *)0xdeadbeef; + ISystemMediaTransportControls *media_transport_controls = (void *)0xdeadbeef; IMediaPlayer2 *media_player2 = (void *)0xdeadbeef; IActivationFactory *factory = (void *)0xdeadbeef; IMediaPlayer *media_player = (void *)0xdeadbeef; @@ -85,6 +88,16 @@ static void test_MediaPlayer_Statics(void) hr = IMediaPlayer_QueryInterface( media_player, &IID_IMediaPlayer2, (void **)&media_player2 ); ok( hr == S_OK, "got hr %#lx.\n", hr );
+ hr = IMediaPlayer2_get_SystemMediaTransportControls( media_player2, &media_transport_controls ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + hr = IMediaPlayer2_get_SystemMediaTransportControls( media_player2, &media_transport_controls2 ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + ok( media_transport_controls == media_transport_controls2, "got media_transport_controls %p, media_transport_controls2 %p.\n", media_transport_controls, media_transport_controls2 ); + ref = ISystemMediaTransportControls_Release( media_transport_controls2 ); + ok( ref == 3, "got ref %ld.\n", ref ); + + ref = ISystemMediaTransportControls_Release( media_transport_controls ); + ok( ref == 2, "got ref %ld.\n", ref ); ref = IMediaPlayer2_Release( media_player2 ); ok( ref == 2, "got ref %ld.\n", ref ); ref = IMediaPlayer_Release( media_player );
**v2** - Remove unnecessary pointer clearing
Rémi Bernon (@rbernon) commented about dlls/windows.media.playback.mediaplayer/main.c:
- HSTRING str = NULL;
- HRESULT hr;
- FIXME( "shell integration not implemented.\n" );
- if (!(*window = CreateWindowExA( 0, "static", NULL, WS_POPUP, 0, 0, 0, 0, NULL, NULL, GetModuleHandleA( NULL ), NULL ))) return HRESULT_FROM_WIN32( GetLastError() );
- if (FAILED(hr = WindowsCreateString( media_control_statics_name, wcslen( media_control_statics_name ), &str ))) return hr;
- if (SUCCEEDED(hr)) hr = RoGetActivationFactory( str, &IID_IActivationFactory, (void **)&factory );
- if (SUCCEEDED(hr)) hr = IActivationFactory_QueryInterface( factory, &IID_ISystemMediaTransportControlsInterop, (void **)&media_control_interop_statics );
- if (SUCCEEDED(hr)) hr = ISystemMediaTransportControlsInterop_GetForWindow( media_control_interop_statics, *window, &IID_ISystemMediaTransportControls, (void **)controls );
- if (media_control_interop_statics) ISystemMediaTransportControlsInterop_Release( media_control_interop_statics );
- if (factory) IActivationFactory_Release( factory );
- WindowsDeleteString( str );
- return hr;
In general it is better for any function to either succeed, or fail completely without anything for the caller to cleanup. This should destroy the window on failure if it was created:
```suggestion:-10+0 HWND hwnd;
if (!(hwnd = CreateWindowExW( 0, L"static", NULL, WS_POPUP, 0, 0, 0, 0, NULL, NULL, GetModuleHandleA( NULL ), NULL ))) return HRESULT_FROM_WIN32( GetLastError() );
if (FAILED(hr = WindowsCreateString( media_control_statics_name, wcslen( media_control_statics_name ), &str ))) goto done; if (SUCCEEDED(hr = RoGetActivationFactory( str, &IID_IActivationFactory, (void **)&factory ))) { hr = IActivationFactory_QueryInterface( factory, &IID_ISystemMediaTransportControlsInterop, (void **)&media_control_interop_statics ); IActivationFactory_Release( factory ); } if (SUCCEEDED(hr)) { hr = ISystemMediaTransportControlsInterop_GetForWindow( media_control_interop_statics, *window, &IID_ISystemMediaTransportControls, (void **)controls ); ISystemMediaTransportControlsInterop_Release( media_control_interop_statics ); } WindowsDeleteString( str );
done: if (FAILED(hr)) DestroyWindow( hwnd ); else *window = hwnd; return hr; ```
Also, as I changed it here as well, we usually prefer to use the unicode version of any user32 function. It avoids any unnecessary locale conversion.
Rémi Bernon (@rbernon) commented about dlls/windows.media.playback.mediaplayer/main.c:
- FIXME( "iface %p, value %p stub!\n", iface, value );
- return E_NOTIMPL;
- struct media_player *impl = impl_from_IMediaPlayer2( iface );
- HRESULT hr;
- TRACE( "iface %p, value %p\n", iface, value );
- EnterCriticalSection( &impl->cs );
- if (!impl->controls && FAILED(hr = get_system_media_transport_controls( &impl->window, &impl->controls )))
- {
if (impl->window)
{
DestroyWindow( impl->window );
impl->window = NULL;
}
```suggestion:-4+0 ```
Then you wouldn't need this.
Rémi Bernon (@rbernon) commented about dlls/windows.media.playback.mediaplayer/main.c:
impl->IMediaPlayer2_iface.lpVtbl = &media_player2_vtbl; impl->ref = 1;
- InitializeCriticalSectionEx( &impl->cs, 0, RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO );
- impl->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": media_controls.cs");
Could we avoid the critical section here? Could the control be created right on object activation?