Commit f4a986cc authored by Rémi Denis-Courmont's avatar Rémi Denis-Courmont

mmdevice: simplify thread synchronization

parent a3926236
...@@ -82,18 +82,21 @@ struct aout_sys_t ...@@ -82,18 +82,21 @@ struct aout_sys_t
struct IAudioSessionEvents session_events; struct IAudioSessionEvents session_events;
LONG refs; LONG refs;
CRITICAL_SECTION lock; /**< Lock to protect Core Audio API state */
HANDLE device_changed; /**< Event to reset thread */ HANDLE device_changed; /**< Event to reset thread */
HANDLE device_ready; /**< Event when thread is reset */
vlc_thread_t thread; /**< Thread for audio session control */ vlc_thread_t thread; /**< Thread for audio session control */
}; };
/* NOTE: The Core Audio API documentation totally fails to specify the thread /* NOTE: The Core Audio API documentation totally fails to specify the thread
* safety (or lack thereof) of the interfaces. This code is most pessimistic * safety (or lack thereof) of the interfaces. This code takes the most
* and assumes that the API is not thread-safe at all. * restrictive assumption, no thread safety: The background thread (MMThread)
* only runs at specified times, namely between the device_ready and
* device_changed events (effectively, a thread barrier but only Windows 8
* provides thread barriers natively).
* *
* The audio output owner (i.e. the audio output core) is responsible for * The audio output owner (i.e. the audio output core) is responsible for
* serializing callbacks. This code only needs to be concerned with * serializing callbacks. This code only needs to be concerned with
* synchronization between the set of audio output callbacks, the thread * synchronization between the set of audio output callbacks, MMThread()
* and (trivially) the device and session notifications. */ * and (trivially) the device and session notifications. */
static int vlc_FromHR(audio_output_t *aout, HRESULT hr) static int vlc_FromHR(audio_output_t *aout, HRESULT hr)
...@@ -111,9 +114,7 @@ static int TimeGet(audio_output_t *aout, mtime_t *restrict delay) ...@@ -111,9 +114,7 @@ static int TimeGet(audio_output_t *aout, mtime_t *restrict delay)
HRESULT hr; HRESULT hr;
EnterMTA(); EnterMTA();
EnterCriticalSection(&sys->lock);
hr = aout_stream_TimeGet(sys->stream, delay); hr = aout_stream_TimeGet(sys->stream, delay);
LeaveCriticalSection(&sys->lock);
LeaveMTA(); LeaveMTA();
return SUCCEEDED(hr) ? 0 : -1; return SUCCEEDED(hr) ? 0 : -1;
...@@ -125,9 +126,7 @@ static void Play(audio_output_t *aout, block_t *block) ...@@ -125,9 +126,7 @@ static void Play(audio_output_t *aout, block_t *block)
HRESULT hr; HRESULT hr;
EnterMTA(); EnterMTA();
EnterCriticalSection(&sys->lock);
hr = aout_stream_Play(sys->stream, block); hr = aout_stream_Play(sys->stream, block);
LeaveCriticalSection(&sys->lock);
LeaveMTA(); LeaveMTA();
vlc_FromHR(aout, hr); vlc_FromHR(aout, hr);
...@@ -139,9 +138,7 @@ static void Pause(audio_output_t *aout, bool paused, mtime_t date) ...@@ -139,9 +138,7 @@ static void Pause(audio_output_t *aout, bool paused, mtime_t date)
HRESULT hr; HRESULT hr;
EnterMTA(); EnterMTA();
EnterCriticalSection(&sys->lock);
hr = aout_stream_Pause(sys->stream, paused); hr = aout_stream_Pause(sys->stream, paused);
LeaveCriticalSection(&sys->lock);
LeaveMTA(); LeaveMTA();
vlc_FromHR(aout, hr); vlc_FromHR(aout, hr);
...@@ -151,24 +148,21 @@ static void Pause(audio_output_t *aout, bool paused, mtime_t date) ...@@ -151,24 +148,21 @@ static void Pause(audio_output_t *aout, bool paused, mtime_t date)
static void Flush(audio_output_t *aout, bool wait) static void Flush(audio_output_t *aout, bool wait)
{ {
aout_sys_t *sys = aout->sys; aout_sys_t *sys = aout->sys;
mtime_t delay = VLC_TS_INVALID;
EnterMTA(); EnterMTA();
EnterCriticalSection(&sys->lock);
if (wait) if (wait)
{ /* Loosy drain emulation */ { /* Loosy drain emulation */
if (FAILED(aout_stream_TimeGet(sys->stream, &delay))) mtime_t delay;
delay = VLC_TS_INVALID;
if (SUCCEEDED(aout_stream_TimeGet(sys->stream, &delay)))
Sleep((delay / (CLOCK_FREQ / 1000)) + 1);
} }
else else
aout_stream_Flush(sys->stream); aout_stream_Flush(sys->stream);
LeaveCriticalSection(&sys->lock);
LeaveMTA(); LeaveMTA();
if (delay != VLC_TS_INVALID)
Sleep((delay / (CLOCK_FREQ / 1000)) + 1);
} }
static ISimpleAudioVolume *GetSimpleVolume(audio_output_t *aout) static ISimpleAudioVolume *GetSimpleVolume(audio_output_t *aout)
...@@ -182,26 +176,21 @@ static ISimpleAudioVolume *GetSimpleVolume(audio_output_t *aout) ...@@ -182,26 +176,21 @@ static ISimpleAudioVolume *GetSimpleVolume(audio_output_t *aout)
if (TryEnterMTA(aout)) if (TryEnterMTA(aout))
return NULL; return NULL;
EnterCriticalSection(&sys->lock);
hr = IAudioSessionManager_GetSimpleAudioVolume(sys->manager, hr = IAudioSessionManager_GetSimpleAudioVolume(sys->manager,
&GUID_VLC_AUD_OUT, &GUID_VLC_AUD_OUT,
FALSE, &volume); FALSE, &volume);
if (FAILED(hr)) if (FAILED(hr))
{ {
LeaveCriticalSection(&sys->lock);
LeaveMTA(); LeaveMTA();
msg_Err(aout, "cannot get simple volume (error 0x%lx)", hr); msg_Err(aout, "cannot get simple volume (error 0x%lx)", hr);
return NULL; assert(volume == NULL);
} }
return volume; return volume;
} }
static void PutSimpleVolume(audio_output_t *aout, ISimpleAudioVolume *volume) static void PutSimpleVolume(ISimpleAudioVolume *volume)
{ {
aout_sys_t *sys = aout->sys;
ISimpleAudioVolume_Release(volume); ISimpleAudioVolume_Release(volume);
LeaveCriticalSection(&sys->lock);
LeaveMTA(); LeaveMTA();
} }
...@@ -214,7 +203,7 @@ static int VolumeSet(audio_output_t *aout, float vol) ...@@ -214,7 +203,7 @@ static int VolumeSet(audio_output_t *aout, float vol)
HRESULT hr = ISimpleAudioVolume_SetMasterVolume(volume, vol, NULL); HRESULT hr = ISimpleAudioVolume_SetMasterVolume(volume, vol, NULL);
if (FAILED(hr)) if (FAILED(hr))
msg_Err(aout, "cannot set volume (error 0x%lx)", hr); msg_Err(aout, "cannot set volume (error 0x%lx)", hr);
PutSimpleVolume(aout, volume); PutSimpleVolume(volume);
return FAILED(hr) ? -1 : 0; return FAILED(hr) ? -1 : 0;
} }
...@@ -228,7 +217,7 @@ static int MuteSet(audio_output_t *aout, bool mute) ...@@ -228,7 +217,7 @@ static int MuteSet(audio_output_t *aout, bool mute)
HRESULT hr = ISimpleAudioVolume_SetMute(volume, mute ? TRUE : FALSE, NULL); HRESULT hr = ISimpleAudioVolume_SetMute(volume, mute ? TRUE : FALSE, NULL);
if (FAILED(hr)) if (FAILED(hr))
msg_Err(aout, "cannot set volume (error 0x%lx)", hr); msg_Err(aout, "cannot set volume (error 0x%lx)", hr);
PutSimpleVolume(aout, volume); PutSimpleVolume(volume);
return FAILED(hr) ? -1 : 0; return FAILED(hr) ? -1 : 0;
} }
...@@ -483,7 +472,6 @@ static void MMSession(audio_output_t *aout, aout_sys_t *sys) ...@@ -483,7 +472,6 @@ static void MMSession(audio_output_t *aout, aout_sys_t *sys)
HRESULT hr; HRESULT hr;
/* Register session control */ /* Register session control */
msg_Err(aout, "HERE: %p", sys->manager);
if (sys->manager != NULL) if (sys->manager != NULL)
{ {
hr = IAudioSessionManager_GetAudioSessionControl(sys->manager, hr = IAudioSessionManager_GetAudioSessionControl(sys->manager,
...@@ -495,7 +483,6 @@ static void MMSession(audio_output_t *aout, aout_sys_t *sys) ...@@ -495,7 +483,6 @@ static void MMSession(audio_output_t *aout, aout_sys_t *sys)
else else
control = NULL; control = NULL;
msg_Err(aout, "THERE");
if (control != NULL) if (control != NULL)
{ {
wchar_t *ua = var_InheritWide(aout, "user-agent"); wchar_t *ua = var_InheritWide(aout, "user-agent");
...@@ -506,9 +493,8 @@ static void MMSession(audio_output_t *aout, aout_sys_t *sys) ...@@ -506,9 +493,8 @@ static void MMSession(audio_output_t *aout, aout_sys_t *sys)
&sys->session_events); &sys->session_events);
} }
LeaveCriticalSection(&sys->lock); SetEvent(sys->device_ready);
WaitForSingleObject(sys->device_changed, INFINITE); WaitForSingleObject(sys->device_changed, INFINITE);
EnterCriticalSection(&sys->lock);
/* Deregister session control */ /* Deregister session control */
if (control != NULL) if (control != NULL)
...@@ -534,10 +520,8 @@ static void *MMThread(void *data) ...@@ -534,10 +520,8 @@ static void *MMThread(void *data)
aout_sys_t *sys = aout->sys; aout_sys_t *sys = aout->sys;
EnterMTA(); EnterMTA();
EnterCriticalSection(&sys->lock);
while (sys->it != NULL) while (sys->it != NULL)
MMSession(aout, sys); MMSession(aout, sys);
LeaveCriticalSection(&sys->lock);
LeaveMTA(); LeaveMTA();
return NULL; return NULL;
} }
...@@ -550,7 +534,8 @@ static HRESULT OpenDevice(audio_output_t *aout, const char *devid) ...@@ -550,7 +534,8 @@ static HRESULT OpenDevice(audio_output_t *aout, const char *devid)
aout_sys_t *sys = aout->sys; aout_sys_t *sys = aout->sys;
HRESULT hr; HRESULT hr;
EnterCriticalSection(&sys->lock); assert(sys->dev == NULL);
if (devid != NULL) /* Device selected explicitly */ if (devid != NULL) /* Device selected explicitly */
{ {
msg_Dbg(aout, "using selected device %s", devid); msg_Dbg(aout, "using selected device %s", devid);
...@@ -584,9 +569,9 @@ static HRESULT OpenDevice(audio_output_t *aout, const char *devid) ...@@ -584,9 +569,9 @@ static HRESULT OpenDevice(audio_output_t *aout, const char *devid)
else else
sys->manager = pv; sys->manager = pv;
} }
LeaveCriticalSection(&sys->lock);
SetEvent(sys->device_changed); SetEvent(sys->device_changed);
WaitForSingleObject(sys->device_ready, INFINITE);
return hr; return hr;
} }
...@@ -599,7 +584,6 @@ static void CloseDevice(audio_output_t *aout) ...@@ -599,7 +584,6 @@ static void CloseDevice(audio_output_t *aout)
assert(sys->dev != NULL); assert(sys->dev != NULL);
EnterCriticalSection(&sys->lock);
if (sys->manager != NULL) if (sys->manager != NULL)
{ {
IAudioSessionManager_Release(sys->manager); IAudioSessionManager_Release(sys->manager);
...@@ -608,7 +592,6 @@ static void CloseDevice(audio_output_t *aout) ...@@ -608,7 +592,6 @@ static void CloseDevice(audio_output_t *aout)
IMMDevice_Release(sys->dev); IMMDevice_Release(sys->dev);
sys->dev = NULL; sys->dev = NULL;
LeaveCriticalSection(&sys->lock);
} }
static int Start(audio_output_t *aout, audio_sample_format_t *restrict fmt) static int Start(audio_output_t *aout, audio_sample_format_t *restrict fmt)
...@@ -620,9 +603,7 @@ static int Start(audio_output_t *aout, audio_sample_format_t *restrict fmt) ...@@ -620,9 +603,7 @@ static int Start(audio_output_t *aout, audio_sample_format_t *restrict fmt)
return -1; return -1;
EnterMTA(); EnterMTA();
EnterCriticalSection(&sys->lock);
sys->stream = aout_stream_Start(aout, fmt, sys->dev, &GUID_VLC_AUD_OUT); sys->stream = aout_stream_Start(aout, fmt, sys->dev, &GUID_VLC_AUD_OUT);
LeaveCriticalSection(&sys->lock);
LeaveMTA(); LeaveMTA();
return (sys->stream != NULL) ? 0 : -1; return (sys->stream != NULL) ? 0 : -1;
...@@ -635,9 +616,7 @@ static void Stop(audio_output_t *aout) ...@@ -635,9 +616,7 @@ static void Stop(audio_output_t *aout)
assert (sys->stream != NULL); assert (sys->stream != NULL);
EnterMTA(); EnterMTA();
EnterCriticalSection(&sys->lock);
aout_stream_Stop(sys->stream); aout_stream_Stop(sys->stream);
LeaveCriticalSection(&sys->lock);
LeaveMTA(); LeaveMTA();
sys->stream = NULL; sys->stream = NULL;
...@@ -661,12 +640,14 @@ static int Open(vlc_object_t *obj) ...@@ -661,12 +640,14 @@ static int Open(vlc_object_t *obj)
sys->aout = aout; sys->aout = aout;
sys->stream = NULL; sys->stream = NULL;
sys->it = NULL; sys->it = NULL;
sys->dev = NULL;
sys->manager = NULL;
sys->session_events.lpVtbl = &vlc_AudioSessionEvents; sys->session_events.lpVtbl = &vlc_AudioSessionEvents;
sys->refs = 1; sys->refs = 1;
InitializeCriticalSection(&sys->lock);
sys->device_changed = CreateEvent(NULL, FALSE, FALSE, NULL); sys->device_changed = CreateEvent(NULL, FALSE, FALSE, NULL);
if (unlikely(sys->device_changed == NULL)) sys->device_ready = CreateEvent(NULL, FALSE, FALSE, NULL);
if (unlikely(sys->device_changed == NULL || sys->device_ready == NULL))
goto error; goto error;
/* Initialize MMDevice API */ /* Initialize MMDevice API */
...@@ -686,6 +667,7 @@ static int Open(vlc_object_t *obj) ...@@ -686,6 +667,7 @@ static int Open(vlc_object_t *obj)
if (vlc_clone(&sys->thread, MMThread, aout, VLC_THREAD_PRIORITY_LOW)) if (vlc_clone(&sys->thread, MMThread, aout, VLC_THREAD_PRIORITY_LOW))
goto error; goto error;
WaitForSingleObject(sys->device_ready, INFINITE);
/* Get a device to start with */ /* Get a device to start with */
do do
...@@ -710,9 +692,10 @@ error: ...@@ -710,9 +692,10 @@ error:
IMMDeviceEnumerator_Release(sys->it); IMMDeviceEnumerator_Release(sys->it);
LeaveMTA(); LeaveMTA();
} }
if (sys->device_ready != NULL)
CloseHandle(sys->device_ready);
if (sys->device_changed != NULL) if (sys->device_changed != NULL)
CloseHandle(sys->device_changed); CloseHandle(sys->device_changed);
DeleteCriticalSection(&sys->lock);
free(sys); free(sys);
return VLC_EGENERIC; return VLC_EGENERIC;
} }
...@@ -726,20 +709,18 @@ static void Close(vlc_object_t *obj) ...@@ -726,20 +709,18 @@ static void Close(vlc_object_t *obj)
var_Destroy (aout, "audio-device"); var_Destroy (aout, "audio-device");
EnterMTA(); /* enter MTA before thread leaves MTA */ EnterMTA(); /* enter MTA before thread leaves MTA */
EnterCriticalSection(&sys->lock);
if (sys->dev != NULL) if (sys->dev != NULL)
CloseDevice(aout); CloseDevice(aout);
IMMDeviceEnumerator_Release(sys->it); IMMDeviceEnumerator_Release(sys->it);
sys->it = NULL; sys->it = NULL;
LeaveCriticalSection(&sys->lock);
SetEvent(sys->device_changed); SetEvent(sys->device_changed);
vlc_join(sys->thread, NULL); vlc_join(sys->thread, NULL);
LeaveMTA(); LeaveMTA();
CloseHandle(sys->device_ready);
CloseHandle(sys->device_changed); CloseHandle(sys->device_changed);
DeleteCriticalSection(&sys->lock);
free(sys); free(sys);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment