[vlc-devel] [PATCH 4/4] aout: winstore: select the audio output using ActivateAudioInterfaceAsync
Steve Lhomme
robux4 at ycbcr.xyz
Wed Jun 24 14:26:57 CEST 2020
See the API https://docs.microsoft.com/en-us/windows/win32/api/mmdeviceapi/nf-mmdeviceapi-activateaudiointerfaceasync
It requires a recent mingw-w64 with the added API.
We request the IAudioClient asynchronously and return the found client or NULL
once the async call as completed.
The code originates from the vlc-winrt project with some modifications.
Do not rely anymore on the local "winstore-client" variable to cache the
IAudioClient. A client is queried/used between each Start/Stop calls.
---
modules/audio_output/Makefile.am | 2 +-
modules/audio_output/winstore.cpp | 212 +++++++++++++++++++++++++++---
2 files changed, 196 insertions(+), 18 deletions(-)
diff --git a/modules/audio_output/Makefile.am b/modules/audio_output/Makefile.am
index e1812b9bc91..a72ba649551 100644
--- a/modules/audio_output/Makefile.am
+++ b/modules/audio_output/Makefile.am
@@ -62,7 +62,7 @@ endif
libmmdevice_plugin_la_SOURCES = audio_output/mmdevice.c audio_output/mmdevice.h
libmmdevice_plugin_la_LIBADD = $(LIBCOM) $(LIBM)
libwinstore_plugin_la_SOURCES = audio_output/winstore.cpp audio_output/mmdevice.h
-libwinstore_plugin_la_LIBADD = $(LIBCOM)
+libwinstore_plugin_la_LIBADD = $(LIBCOM) -lmmdevapi
libwasapi_plugin_la_SOURCES = audio_output/wasapi.c
libwasapi_plugin_la_LIBADD = $(LIBCOM) -lksuser
if HAVE_WASAPI
diff --git a/modules/audio_output/winstore.cpp b/modules/audio_output/winstore.cpp
index 66ce3721dfb..5061600d1cb 100644
--- a/modules/audio_output/winstore.cpp
+++ b/modules/audio_output/winstore.cpp
@@ -24,7 +24,6 @@
#define INITGUID
-#include <cassert>
#include <audiopolicy.h>
#include <vlc_common.h>
@@ -33,6 +32,9 @@
#include <vlc_modules.h>
#include "audio_output/mmdevice.h"
+#include <audioclient.h>
+#include <mmdeviceapi.h>
+
#include <wrl.h>
#include <wrl/client.h>
using namespace Microsoft::WRL;
@@ -57,8 +59,141 @@ typedef struct
aout_stream_t *stream; /**< Underlying audio output stream */
module_t *module;
IAudioClient *client;
+ wchar_t* default_device; // read once on open
+ wchar_t* requested_device;
} aout_sys_t;
+
+class MMDeviceLocator :
+ public IActivateAudioInterfaceCompletionHandler
+{
+public:
+ MMDeviceLocator();
+ virtual ~MMDeviceLocator();
+
+ IAudioClient* WaitForAudioClient(const wchar_t* devId);
+
+ // IUnknown methods
+ STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
+ STDMETHODIMP_(ULONG) AddRef();
+ STDMETHODIMP_(ULONG) Release();
+
+ // IActivateAudioInterfaceCompletionHandler
+ HRESULT ActivateCompleted(IActivateAudioInterfaceAsyncOperation *operation);
+
+private:
+ IAudioClient* m_AudioClient;
+ vlc_sem_t async_completed;
+ LONG m_cRef;
+};
+
+MMDeviceLocator::MMDeviceLocator()
+ : m_cRef(0)
+{
+ vlc_sem_init(&async_completed, 0);
+}
+
+MMDeviceLocator::~MMDeviceLocator()
+{
+}
+
+HRESULT MMDeviceLocator::ActivateCompleted(IActivateAudioInterfaceAsyncOperation *operation)
+{
+ HRESULT hr;
+ HRESULT hrActivateResult;
+ ComPtr<IUnknown> audioInterface;
+
+ hr = operation->GetActivateResult(&hrActivateResult, audioInterface.GetAddressOf());
+ if (SUCCEEDED(hr) && SUCCEEDED(hrActivateResult) && audioInterface.Get() != nullptr)
+ {
+ hr = audioInterface->QueryInterface(&m_AudioClient);
+ if (SUCCEEDED(hr))
+ {
+ // "BackgroundCapableMedia" does not work in UWP
+ AudioClientProperties props = AudioClientProperties {
+ .cbSize = sizeof(props),
+ .bIsOffload = FALSE,
+ .eCategory = AudioCategory_Movie,
+ .Options = AUDCLNT_STREAMOPTIONS_NONE
+ };
+ ComPtr<IAudioClient2> audioClient2;
+ if (SUCCEEDED(audioInterface->QueryInterface(audioClient2.GetAddressOf()))
+ && audioClient2.Get())
+ {
+ if (FAILED(audioClient2->SetClientProperties(&props))) {
+ OutputDebugString(TEXT("Failed to set audio client properties"));
+ }
+ }
+ }
+ }
+ vlc_sem_post( &async_completed );
+ return hr;
+}
+
+
+/* IUnknown methods */
+STDMETHODIMP MMDeviceLocator::QueryInterface(REFIID riid, void **ppv)
+{
+ if( riid == IID_IUnknown ||
+ riid == IID_IActivateAudioInterfaceCompletionHandler )
+ {
+ AddRef();
+ *ppv = this;
+ return S_OK;
+ }
+ if( riid == IID_IAgileObject )
+ {
+ *ppv = nullptr;
+ return S_OK;
+ }
+ *ppv = nullptr;
+ return ResultFromScode( E_NOINTERFACE );
+}
+
+STDMETHODIMP_(ULONG) MMDeviceLocator::AddRef()
+{
+ return InterlockedIncrement(&m_cRef);
+}
+
+STDMETHODIMP_(ULONG) MMDeviceLocator::Release()
+{
+ if( !InterlockedDecrement(&m_cRef) )
+ delete this;
+
+ return m_cRef;
+}
+
+IAudioClient* MMDeviceLocator::WaitForAudioClient(const wchar_t* devId)
+{
+ ComPtr<IActivateAudioInterfaceAsyncOperation> asyncOp;
+
+ m_AudioClient = nullptr;
+ if (FAILED(ActivateAudioInterfaceAsync(devId, __uuidof(IAudioClient), nullptr, this, asyncOp.GetAddressOf())))
+ return nullptr;
+
+ vlc_sem_wait( &async_completed );
+
+ return m_AudioClient;
+}
+
+static IAudioClient* GetAudioClient(const wchar_t* devId)
+{
+ assert(devId != nullptr);
+ ComPtr<MMDeviceLocator> audioReg(new MMDeviceLocator());
+ return audioReg->WaitForAudioClient(devId);
+}
+
+static void SetRequestedDevice(audio_output_t *aout, wchar_t *psz_device)
+{
+ aout_sys_t* sys = reinterpret_cast<aout_sys_t*>(aout->sys);
+ if (sys->requested_device != psz_device)
+ {
+ if (sys->requested_device != sys->default_device)
+ free(sys->requested_device);
+ sys->requested_device = psz_device;
+ }
+}
+
static void ResetInvalidated(audio_output_t *aout, HRESULT hr)
{
/* Select the default device (and restart) on unplug */
@@ -67,6 +202,8 @@ static void ResetInvalidated(audio_output_t *aout, HRESULT hr)
{
aout_sys_t* sys = reinterpret_cast<aout_sys_t*>(aout->sys);
sys->client = nullptr;
+ SetRequestedDevice(aout, sys->default_device);
+ aout_RestartRequest(aout, AOUT_RESTART_OUTPUT);
}
}
@@ -226,12 +363,36 @@ static int Start(audio_output_t *aout, audio_sample_format_t *restrict fmt)
if (unlikely(s == nullptr))
return -1;
- s->owner.device = sys->client;
+ if (sys->requested_device != nullptr)
+ {
+ sys->client = GetAudioClient(sys->requested_device);
+ if (sys->client == nullptr)
+ {
+ vlc_object_delete(&s->obj);
+ return -1;
+ }
+ }
s->owner.activate = ActivateDevice;
EnterMTA();
- sys->module = vlc_module_load(vlc_object_logger(&s->obj), "aout stream", nullptr, false,
- aout_stream_Start, s, fmt, &hr);
+
+ for (;;)
+ {
+ hr = S_OK;
+ s->owner.device = sys->client;
+ sys->module = vlc_module_load(vlc_object_logger(&s->obj), "aout stream", nullptr, false,
+ aout_stream_Start, s, fmt, &hr);
+ if (hr == AUDCLNT_E_ALREADY_INITIALIZED)
+ sys->client = GetAudioClient(sys->requested_device ? sys->requested_device : sys->default_device);
+ else if (hr == AUDCLNT_E_DEVICE_INVALIDATED)
+ sys->client = GetAudioClient(sys->default_device);
+ else
+ break;
+ if (sys->client == nullptr || sys->module != nullptr)
+ break;
+ }
+ SetRequestedDevice(aout, nullptr);
+
LeaveMTA();
if (sys->module == nullptr)
@@ -240,6 +401,7 @@ static int Start(audio_output_t *aout, audio_sample_format_t *restrict fmt)
return -1;
}
+ sys->client->AddRef();
assert (sys->stream == nullptr);
sys->stream = s;
return 0;
@@ -257,21 +419,30 @@ static void Stop(audio_output_t *aout)
vlc_object_delete(&sys->stream->obj);
sys->stream = nullptr;
+ if(sys->client != nullptr)
+ {
+ sys->client->Release();
+ }
}
static int DeviceSelect(audio_output_t *aout, const char* psz_device)
{
+ aout_sys_t *sys = reinterpret_cast<aout_sys_t*>(aout->sys);
if( psz_device == nullptr )
- return VLC_EGENERIC;
- char* psz_end;
- aout_sys_t* sys = reinterpret_cast<aout_sys_t*>(aout->sys);
- intptr_t ptr = strtoll( psz_device, &psz_end, 16 );
- if ( *psz_end != 0 )
- return VLC_EGENERIC;
- if (sys->client == reinterpret_cast<IAudioClient*>(ptr))
- return VLC_SUCCESS;
- sys->client = reinterpret_cast<IAudioClient*>(ptr);
- var_SetAddress( vlc_object_parent(aout), "winstore-client", sys->client );
+ {
+ SetRequestedDevice(aout, sys->default_device);
+ }
+ else
+ {
+ size_t len = MultiByteToWideChar(CP_UTF8, 0, psz_device, -1, nullptr, 0);
+ if (len == 0)
+ return VLC_ENOMEM;
+ wchar_t *requested_device = new wchar_t[len];
+ if (unlikely(requested_device == nullptr))
+ return VLC_ENOMEM;
+ MultiByteToWideChar(CP_UTF8, 0, psz_device, -1, requested_device, len);
+ SetRequestedDevice(aout, requested_device);
+ }
aout_RestartRequest( aout, AOUT_RESTART_OUTPUT );
return VLC_SUCCESS;
}
@@ -284,11 +455,17 @@ static int Open(vlc_object_t *obj)
if (unlikely(sys == nullptr))
return VLC_ENOMEM;
+ if (unlikely(FAILED(StringFromIID(DEVINTERFACE_AUDIO_RENDER, &sys->default_device))))
+ {
+ msg_Dbg(obj, "Failed to get the default renderer string");
+ free(sys);
+ return VLC_EGENERIC;
+ }
+
aout->sys = sys;
sys->stream = nullptr;
- sys->client = reinterpret_cast<IAudioClient*>(var_CreateGetAddress( vlc_object_parent(aout), "winstore-client" ));
- if (sys->client != nullptr)
- msg_Dbg( aout, "Reusing previous client: %p", sys->client );
+ sys->client = nullptr;
+ sys->requested_device = sys->default_device;
aout->start = Start;
aout->stop = Stop;
aout->time_get = TimeGet;
@@ -306,6 +483,7 @@ static void Close(vlc_object_t *obj)
audio_output_t *aout = (audio_output_t *)obj;
aout_sys_t *sys = reinterpret_cast<aout_sys_t*>(aout->sys);
+ CoTaskMemFree(sys->default_device);
free(sys);
}
--
2.26.2
More information about the vlc-devel
mailing list