[vlc-devel] [PATCHv2 2/3] mmdevice: handle AUDCLNT_E_ALREADY_INITIALIZED
Thomas Guillem
thomas at gllm.fr
Thu Jul 6 11:25:00 CEST 2017
Sadly, this error can happen even if the "aout stream" was well stopped. See
MSDN comments in the commit.
Add DeviceRestartLocked() that fetch a new instance of the current IMMDevice.
---
modules/audio_output/mmdevice.c | 77 ++++++++++++++++++++++++++++++++---------
1 file changed, 61 insertions(+), 16 deletions(-)
diff --git a/modules/audio_output/mmdevice.c b/modules/audio_output/mmdevice.c
index 8446c1cab3..194a8bf91b 100644
--- a/modules/audio_output/mmdevice.c
+++ b/modules/audio_output/mmdevice.c
@@ -94,6 +94,7 @@ struct aout_sys_t
float gain; /**< Current software gain volume */
wchar_t *selecting_device; /**< Requesting device identifier, NULL if none */
+ wchar_t *selected_device; /**< Requested device identifier, NULL if none */
float volume; /**< Requested volume, negative if none */
signed char mute; /**< Requested mute, negative if none */
CRITICAL_SECTION lock;
@@ -718,22 +719,10 @@ static int DevicesEnum(audio_output_t *aout, IMMDeviceEnumerator *it)
return n;
}
-static int DeviceSelectLocked(audio_output_t *aout, const char *id)
+static int DeviceRequestLocked(audio_output_t *aout)
{
aout_sys_t *sys = aout->sys;
- wchar_t *device;
-
- if (id != NULL)
- {
- device = ToWide(id);
- if (unlikely(device == NULL))
- return -1;
- }
- else
- device = default_device;
-
- assert(sys->selecting_device == NULL);
- sys->selecting_device = device;
+ assert(sys->selecting_device);
WakeConditionVariable(&sys->work);
while (sys->selecting_device != NULL)
@@ -745,6 +734,32 @@ static int DeviceSelectLocked(audio_output_t *aout, const char *id)
return (sys->dev != NULL) ? 0 : -1;
}
+static int DeviceSelectLocked(audio_output_t *aout, const char *id)
+{
+ aout_sys_t *sys = aout->sys;
+ assert(sys->selecting_device == NULL);
+
+ if (id != NULL)
+ {
+ sys->selecting_device = ToWide(id);
+ if (unlikely(sys->selecting_device == NULL))
+ return -1;
+ }
+ else
+ sys->selecting_device = default_device;
+
+ return DeviceRequestLocked(aout);
+}
+
+static int DeviceRestartLocked(audio_output_t *aout)
+{
+ aout_sys_t *sys = aout->sys;
+ assert(sys->selecting_device == NULL);
+ assert(sys->selected_device != NULL);
+ sys->selecting_device = sys->selected_device;
+ return DeviceRequestLocked(aout);
+}
+
static int DeviceSelect(audio_output_t *aout, const char *id)
{
EnterCriticalSection(&aout->sys->lock);
@@ -790,6 +805,11 @@ static HRESULT MMSession(audio_output_t *aout, IMMDeviceEnumerator *it)
assert(sys->selecting_device != NULL);
assert(sys->dev == NULL);
+ /* Yes, it's perfectly valid to request the same device, see Start()
+ * comments. */
+ if (sys->selected_device != sys->selecting_device
+ && sys->selected_device != default_device)
+ free(sys->selected_device);
if (sys->selecting_device != default_device) /* Device selected explicitly */
{
msg_Dbg(aout, "using selected device %ls", sys->selecting_device);
@@ -797,7 +817,7 @@ static HRESULT MMSession(audio_output_t *aout, IMMDeviceEnumerator *it)
if (FAILED(hr))
msg_Err(aout, "cannot get selected device %ls (error 0x%lx)",
sys->selecting_device, hr);
- free(sys->selecting_device);
+ sys->selected_device = sys->selecting_device;
}
else
hr = AUDCLNT_E_DEVICE_INVALIDATED;
@@ -810,6 +830,8 @@ static HRESULT MMSession(audio_output_t *aout, IMMDeviceEnumerator *it)
eConsole, &sys->dev);
if (FAILED(hr))
msg_Err(aout, "cannot get default device (error 0x%lx)", hr);
+ else
+ sys->selected_device = default_device;
}
sys->selecting_device = NULL;
@@ -1083,7 +1105,29 @@ static int Start(audio_output_t *aout, audio_sample_format_t *restrict fmt)
sys->module = vlc_module_load(s, "aout stream", "$mmdevice-backend",
false, aout_stream_Start, s, fmt, &hr);
- if (hr != AUDCLNT_E_DEVICE_INVALIDATED || DeviceSelectLocked(aout, NULL))
+
+ int ret = -1;
+ if (hr == AUDCLNT_E_ALREADY_INITIALIZED)
+ {
+ /* From MSDN: "If the initial call to Initialize fails, subsequent
+ * Initialize calls might fail and return error code
+ * E_ALREADY_INITIALIZED, even though the interface has not been
+ * initialized. If this occurs, release the IAudioClient interface
+ * and obtain a new IAudioClient interface from the MMDevice API
+ * before calling Initialize again."
+ *
+ * Therefore, request to MMThread the same device and try again. */
+
+ ret = DeviceRestartLocked(aout);
+ }
+ else if (hr == AUDCLNT_E_DEVICE_INVALIDATED)
+ {
+ /* The audio endpoint device has been unplugged, request to
+ * MMThread the default device and try again. */
+
+ ret = DeviceSelectLocked(aout, NULL);
+ }
+ if (ret != 0)
break;
}
LeaveCriticalSection(&sys->lock);
@@ -1135,6 +1179,7 @@ static int Open(vlc_object_t *obj)
sys->ducks = 0;
sys->selecting_device = default_device;
+ sys->selected_device = NULL;
sys->gain = 1.f;
sys->volume = -1.f;
sys->mute = -1;
--
2.11.0
More information about the vlc-devel
mailing list