[vlc-devel] [PATCHv2 2/3] mmdevice: handle AUDCLNT_E_ALREADY_INITIALIZED
Thomas Guillem
thomas at gllm.fr
Thu Jul 6 11:29:02 CEST 2017
On Thu, Jul 6, 2017, at 11:25, Thomas Guillem wrote:
> 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 */
Maybe a better naming would be:
wchar_t *acquired_device; /**< Acquired 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
>
> _______________________________________________
> vlc-devel mailing list
> To unsubscribe or modify your subscription options:
> https://mailman.videolan.org/listinfo/vlc-devel
More information about the vlc-devel
mailing list