DEFINE_GUID (GUID_VLC_AUD_OUT, 0x4533f59d, 0x59ee, 0x00c6,
0xad, 0xb2, 0xc6, 0x8b, 0x50, 0x1a, 0x66, 0x55);
-static int TryEnter(vlc_object_t *obj)
+static int TryEnterMTA(vlc_object_t *obj)
{
HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if (unlikely(FAILED(hr)))
}
return 0;
}
-#define TryEnter(o) TryEnter(VLC_OBJECT(o))
+#define TryEnterMTA(o) TryEnterMTA(VLC_OBJECT(o))
-static void Enter(void)
+static void EnterMTA(void)
{
HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if (unlikely(FAILED(hr)))
abort();
}
-static void Leave(void)
+static void LeaveMTA(void)
{
CoUninitialize();
}
struct aout_sys_t
{
audio_output_t *aout;
- aout_api_t *api; /**< Audio output back-end API */
- IMMDeviceEnumerator *it;
- IMMDevice *dev; /**< Selected output device */
- IAudioSessionManager *manager; /**< Session for the output device */
+ aout_stream_t *stream; /**< Underlying audio output stream */
+ IMMDeviceEnumerator *it; /**< Device enumerator, NULL when exiting */
/*TODO: IMMNotificationClient*/
+
+ IMMDevice *dev; /**< Selected output device, NULL if none */
+ IAudioSessionManager *manager; /**< Session for the output device */
struct IAudioSessionEvents session_events;
+ ISimpleAudioVolume *volume; /**< Volume setter */
LONG refs;
- CRITICAL_SECTION lock;
- CONDITION_VARIABLE request_wait;
- CONDITION_VARIABLE reply_wait;
-
- bool active; /**< Flag to request thread to keep running */
- bool running; /**< Whether the thread is still running */
- int8_t mute; /**< Requested mute state or negative value */
- float volume; /**< Requested volume or negative value */
+ HANDLE device_changed; /**< Event to reset thread */
+ HANDLE device_ready; /**< Event when thread is reset */
+ vlc_thread_t thread; /**< Thread for audio session control */
};
+/* NOTE: The Core Audio API documentation totally fails to specify the thread
+ * safety (or lack thereof) of the interfaces. This code takes the most
+ * 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
+ * serializing callbacks. This code only needs to be concerned with
+ * synchronization between the set of audio output callbacks, MMThread()
+ * and (trivially) the device and session notifications. */
+
static int vlc_FromHR(audio_output_t *aout, HRESULT hr)
{
/* Restart on unplug */
if (unlikely(hr == AUDCLNT_E_DEVICE_INVALIDATED))
- var_TriggerCallback(aout, "audio-device");
+ aout_RestartRequest(aout, AOUT_RESTART_OUTPUT);
return SUCCEEDED(hr) ? 0 : -1;
}
static int TimeGet(audio_output_t *aout, mtime_t *restrict delay)
{
aout_sys_t *sys = aout->sys;
- HRESULT hr = aout_api_TimeGet(sys->api, delay);
+ HRESULT hr;
+
+ EnterMTA();
+ hr = aout_stream_TimeGet(sys->stream, delay);
+ LeaveMTA();
return SUCCEEDED(hr) ? 0 : -1;
}
static void Play(audio_output_t *aout, block_t *block)
{
aout_sys_t *sys = aout->sys;
- HRESULT hr = aout_api_Play(sys->api, block);
+ HRESULT hr;
+
+ EnterMTA();
+ hr = aout_stream_Play(sys->stream, block);
+ LeaveMTA();
vlc_FromHR(aout, hr);
}
static void Pause(audio_output_t *aout, bool paused, mtime_t date)
{
aout_sys_t *sys = aout->sys;
- HRESULT hr = aout_api_Pause(sys->api, paused);
+ HRESULT hr;
+
+ EnterMTA();
+ hr = aout_stream_Pause(sys->stream, paused);
+ LeaveMTA();
vlc_FromHR(aout, hr);
(void) date;
{
aout_sys_t *sys = aout->sys;
- if (wait)
- return; /* Drain not implemented */
+ EnterMTA();
- aout_api_Flush(sys->api);
-}
+ if (wait)
+ { /* Loosy drain emulation */
+ mtime_t delay;
-static int VolumeSet(audio_output_t *aout, float vol)
-{
- aout_sys_t *sys = aout->sys;
+ if (SUCCEEDED(aout_stream_TimeGet(sys->stream, &delay)))
+ Sleep((delay / (CLOCK_FREQ / 1000)) + 1);
+ }
+ else
+ aout_stream_Flush(sys->stream);
- EnterCriticalSection(&sys->lock);
- sys->volume = vol;
- LeaveCriticalSection(&sys->lock);
+ LeaveMTA();
- WakeConditionVariable(&sys->request_wait);
- return 0;
}
-static int MuteSet(audio_output_t *aout, bool mute)
+static int VolumeSet(audio_output_t *aout, float vol)
{
- aout_sys_t *sys = aout->sys;
+ ISimpleAudioVolume *volume = aout->sys->volume;
+ if (volume == NULL)
+ return -1;
- EnterCriticalSection(&sys->lock);
- sys->mute = mute;
- LeaveCriticalSection(&sys->lock);
+ if (TryEnterMTA(aout))
+ return -1;
- WakeConditionVariable(&sys->request_wait);
- return 0;
-}
+ HRESULT hr = ISimpleAudioVolume_SetMasterVolume(volume, vol, NULL);
+ if (FAILED(hr))
+ msg_Err(aout, "cannot set volume (error 0x%lx)", hr);
+ LeaveMTA();
-/*** Audio devices ***/
-static int DeviceChanged(vlc_object_t *obj, const char *varname,
- vlc_value_t prev, vlc_value_t cur, void *data)
-{
- /* FIXME: This does not work. sys->dev, sys->manager and sys->api must be
- * recreated. Those pointers are protected by the aout lock, which
- * serializes accesses to the audio_output_t. Unfortunately,
- * aout lock cannot be taken from a variable callback.
- * Solution: add device_change callback to audio_output_t. */
- aout_ChannelsRestart(obj, varname, prev, cur, data);
- return VLC_SUCCESS;
+ return FAILED(hr) ? -1 : 0;
}
-static void GetDevices(vlc_object_t *obj, IMMDeviceEnumerator *it)
+static int MuteSet(audio_output_t *aout, bool mute)
{
- HRESULT hr;
- vlc_value_t val, text;
-
- var_Create (obj, "audio-device", VLC_VAR_STRING | VLC_VAR_HASCHOICE);
- text.psz_string = _("Audio Device");
- var_Change (obj, "audio-device", VLC_VAR_SETTEXT, &text, NULL);
+ ISimpleAudioVolume *volume = aout->sys->volume;
+ if (volume == NULL)
+ return -1;
- /* TODO: implement IMMNotificationClient for hotplug devices */
- IMMDeviceCollection *devs;
- hr = IMMDeviceEnumerator_EnumAudioEndpoints(it, eRender,
- DEVICE_STATE_ACTIVE, &devs);
- if (FAILED(hr))
- {
- msg_Warn (obj, "cannot enumerate audio endpoints (error 0x%lx)", hr);
- return;
- }
+ if (TryEnterMTA(aout))
+ return -1;
- UINT n;
- hr = IMMDeviceCollection_GetCount(devs, &n);
+ HRESULT hr = ISimpleAudioVolume_SetMute(volume, mute ? TRUE : FALSE, NULL);
if (FAILED(hr))
- {
- msg_Warn (obj, "cannot count audio endpoints (error 0x%lx)", hr);
- n = 0;
- }
- else
- msg_Dbg(obj, "Available Windows Audio devices:");
+ msg_Err(aout, "cannot set volume (error 0x%lx)", hr);
+ LeaveMTA();
- while (n > 0)
- {
- IMMDevice *dev;
-
- hr = IMMDeviceCollection_Item(devs, --n, &dev);
- if (FAILED(hr))
- continue;
-
- /* Unique device ID */
- LPWSTR devid;
- hr = IMMDevice_GetId(dev, &devid);
- if (FAILED(hr))
- {
- IMMDevice_Release(dev);
- continue;
- }
- val.psz_string = FromWide(devid);
- CoTaskMemFree(devid);
- text.psz_string = val.psz_string;
-
- /* User-readable device name */
- IPropertyStore *props;
- hr = IMMDevice_OpenPropertyStore(dev, STGM_READ, &props);
- if (SUCCEEDED(hr))
- {
- PROPVARIANT v;
-
- PropVariantInit(&v);
- hr = IPropertyStore_GetValue(props, &PKEY_Device_FriendlyName, &v);
- if (SUCCEEDED(hr))
- text.psz_string = FromWide(v.pwszVal);
- PropVariantClear(&v);
- IPropertyStore_Release(props);
- }
- IMMDevice_Release(dev);
-
- msg_Dbg(obj, "%s (%s)", val.psz_string, text.psz_string);
- var_Change(obj, "audio-device", VLC_VAR_ADDCHOICE, &val, &text);
- if (likely(text.psz_string != val.psz_string))
- free(text.psz_string);
- free(val.psz_string);
- }
- IMMDeviceCollection_Release(devs);
+ return FAILED(hr) ? -1 : 0;
}
/*** Audio session events ***/
static STDMETHODIMP
vlc_AudioSessionEvents_OnSessionDisconnected(IAudioSessionEvents *this,
- AudioSessionDisconnectReason reason)
+ AudioSessionDisconnectReason reason)
{
aout_sys_t *sys = vlc_AudioSessionEvents_sys(this);
audio_output_t *aout = sys->aout;
- msg_Dbg(aout, "session disconnected: reason %d", reason);
+ switch (reason)
+ {
+ case DisconnectReasonDeviceRemoval:
+ msg_Warn(aout, "session disconnected: %s", "device removed");
+ break;
+ case DisconnectReasonServerShutdown:
+ msg_Err(aout, "session disconnected: %s", "service stopped");
+ return S_OK;
+ case DisconnectReasonFormatChanged:
+ msg_Warn(aout, "session disconnected: %s", "format changed");
+ break;
+ case DisconnectReasonSessionLogoff:
+ msg_Err(aout, "session disconnected: %s", "user logged off");
+ return S_OK;
+ case DisconnectReasonSessionDisconnected:
+ msg_Err(aout, "session disconnected: %s", "session disconnected");
+ return S_OK;
+ case DisconnectReasonExclusiveModeOverride:
+ msg_Err(aout, "session disconnected: %s", "stream overriden");
+ return S_OK;
+ default:
+ msg_Warn(aout, "session disconnected: unknown reason %d", reason);
+ return S_OK;
+ }
+ /* NOTE: audio decoder thread should get invalidated device and restart */
return S_OK;
}
}
#define var_InheritWide(o,n) var_InheritWide(VLC_OBJECT(o),n)
-/** MMDevice audio output thread.
- * A number of Core Audio Interfaces must be deleted from the same thread than
- * they were created from... */
-static void MMThread(void *data)
+static void MMSession(audio_output_t *aout, aout_sys_t *sys)
{
- audio_output_t *aout = data;
- aout_sys_t *sys = aout->sys;
IAudioSessionControl *control;
- ISimpleAudioVolume *volume;
HRESULT hr;
- Enter();
- /* Instantiate thread-invariable interfaces */
- hr = IAudioSessionManager_GetAudioSessionControl(sys->manager,
- &GUID_VLC_AUD_OUT, 0,
- &control);
- if (FAILED(hr))
- msg_Warn(aout, "cannot get session control (error 0x%lx)", hr);
+ /* Register session control */
+ if (sys->manager != NULL)
+ {
+ hr = IAudioSessionManager_GetSimpleAudioVolume(sys->manager,
+ &GUID_VLC_AUD_OUT,
+ FALSE, &sys->volume);
+ if (FAILED(hr))
+ msg_Err(aout, "cannot get simple volume (error 0x%lx)", hr);
+
+ hr = IAudioSessionManager_GetAudioSessionControl(sys->manager,
+ &GUID_VLC_AUD_OUT, 0,
+ &control);
+ if (FAILED(hr))
+ msg_Err(aout, "cannot get session control (error 0x%lx)", hr);
+ }
else
+ {
+ sys->volume = NULL;
+ control = NULL;
+ }
+
+ if (control != NULL)
{
wchar_t *ua = var_InheritWide(aout, "user-agent");
IAudioSessionControl_SetDisplayName(control, ua, NULL);
free(ua);
- sys->session_events.lpVtbl = &vlc_AudioSessionEvents;
IAudioSessionControl_RegisterAudioSessionNotification(control,
&sys->session_events);
}
- hr = IAudioSessionManager_GetSimpleAudioVolume(sys->manager,
- &GUID_VLC_AUD_OUT, FALSE,
- &volume);
+ if (sys->volume != NULL)
+ { /* Get current values (_after_ changes notification registration) */
+ BOOL mute;
+ float level;
+
+ hr = ISimpleAudioVolume_GetMute(sys->volume, &mute);
+ if (FAILED(hr))
+ msg_Err(aout, "cannot get mute (error 0x%lx)", hr);
+ else
+ aout_MuteReport(aout, mute != FALSE);
+
+ hr = ISimpleAudioVolume_GetMasterVolume(sys->volume, &level);
+ if (FAILED(hr))
+ msg_Err(aout, "cannot get mute (error 0x%lx)", hr);
+ else
+ aout_VolumeReport(aout, level);
+ }
+
+ SetEvent(sys->device_ready);
+ /* Wait until device change or exit */
+ WaitForSingleObject(sys->device_changed, INFINITE);
+
+ /* Deregister session control */
+ if (control != NULL)
+ {
+ IAudioSessionControl_UnregisterAudioSessionNotification(control,
+ &sys->session_events);
+ IAudioSessionControl_Release(control);
+ }
+
+ if (sys->volume != NULL)
+ ISimpleAudioVolume_Release(sys->volume);
+}
+
+/** MMDevice audio output thread.
+ * This thread takes cares of the audio session control. Inconveniently enough,
+ * the audio session control interface must:
+ * - be created and destroyed from the same thread, and
+ * - survive across VLC audio output calls.
+ * The only way to reconcile both requirements is a custom thread.
+ * The thread also ensure that the COM Multi-Thread Apartment is continuously
+ * referenced so that MMDevice objects are not destroyed early.
+ */
+static void *MMThread(void *data)
+{
+ audio_output_t *aout = data;
+ aout_sys_t *sys = aout->sys;
+
+ EnterMTA();
+ while (sys->it != NULL)
+ MMSession(aout, sys);
+ LeaveMTA();
+ return NULL;
+}
+
+/*** Audio devices ***/
+static int DevicesEnum(audio_output_t *aout, char ***idp, char ***namep)
+{
+ aout_sys_t *sys = aout->sys;
+ HRESULT hr;
+ IMMDeviceCollection *devs;
+
+ hr = IMMDeviceEnumerator_EnumAudioEndpoints(sys->it, eRender,
+ DEVICE_STATE_ACTIVE, &devs);
+ if (FAILED(hr))
+ {
+ msg_Warn(aout, "cannot enumerate audio endpoints (error 0x%lx)", hr);
+ return -1;
+ }
+
+ UINT count;
+ hr = IMMDeviceCollection_GetCount(devs, &count);
if (FAILED(hr))
- msg_Err(aout, "cannot get simple volume (error 0x%lx)", hr);
+ {
+ msg_Warn(aout, "cannot count audio endpoints (error 0x%lx)", hr);
+ count = 0;
+ }
+ else
+ msg_Dbg(aout, "Available Windows Audio devices:");
+
+ char **ids = xmalloc (count * sizeof (*ids));
+ char **names = xmalloc (count * sizeof (*names));
+ unsigned n = 0;
- EnterCriticalSection(&sys->lock);
- while (sys->active)
+ for (UINT i = 0; i < count; i++)
{
- /* Update volume */
- if (sys->volume >= 0.f)
+ IMMDevice *dev;
+
+ hr = IMMDeviceCollection_Item(devs, i, &dev);
+ if (FAILED(hr))
+ continue;
+
+ /* Unique device ID */
+ LPWSTR devid;
+ hr = IMMDevice_GetId(dev, &devid);
+ if (FAILED(hr))
{
- hr = ISimpleAudioVolume_SetMasterVolume(volume, sys->volume, NULL);
- if (FAILED(hr))
- msg_Err(aout, "cannot set volume (error 0x%lx)", hr);
- sys->volume = -1.f;
+ IMMDevice_Release(dev);
+ continue;
}
+ ids[n] = FromWide(devid);
+ names[n] = NULL;
+ CoTaskMemFree(devid);
- /* Update mute state */
- if (sys->mute >= 0)
+ /* User-readable device name */
+ IPropertyStore *props;
+ hr = IMMDevice_OpenPropertyStore(dev, STGM_READ, &props);
+ if (SUCCEEDED(hr))
{
- hr = ISimpleAudioVolume_SetMute(volume, sys->mute, NULL);
- if (FAILED(hr))
- msg_Err(aout, "cannot set mute (error 0x%lx)", hr);
- sys->mute = -1;
+ PROPVARIANT v;
+
+ PropVariantInit(&v);
+ hr = IPropertyStore_GetValue(props, &PKEY_Device_FriendlyName, &v);
+ if (SUCCEEDED(hr))
+ names[n] = FromWide(v.pwszVal);
+ PropVariantClear(&v);
+ IPropertyStore_Release(props);
}
+ IMMDevice_Release(dev);
+ if (names[n] == NULL)
+ names[n] = xstrdup(ids[n]);
- SleepConditionVariableCS(&sys->request_wait, &sys->lock, INFINITE);
+ msg_Dbg(aout, "%s (%s)", ids[n], names[n]);
+ n++;
}
- LeaveCriticalSection(&sys->lock);
+ IMMDeviceCollection_Release(devs);
- if (volume != NULL)
- ISimpleAudioVolume_Release(volume);
- if (control != NULL)
+ *idp = ids;
+ *namep = names;
+ return n;
+}
+
+/**
+ * Opens the selected audio output device.
+ */
+static HRESULT OpenDevice(audio_output_t *aout, const char *devid)
+{
+ aout_sys_t *sys = aout->sys;
+ HRESULT hr;
+
+ assert(sys->dev == NULL);
+
+ if (devid != NULL) /* Device selected explicitly */
{
- IAudioSessionControl_UnregisterAudioSessionNotification(control,
- &sys->session_events);
- IAudioSessionControl_Release(control);
+ msg_Dbg(aout, "using selected device %s", devid);
+
+ wchar_t *wdevid = ToWide(devid);
+ if (likely(wdevid != NULL))
+ {
+ hr = IMMDeviceEnumerator_GetDevice(sys->it, wdevid, &sys->dev);
+ free (wdevid);
+ }
+ else
+ hr = E_OUTOFMEMORY;
+ }
+ else /* Default device selected by policy */
+ {
+ msg_Dbg(aout, "using default device");
+ hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(sys->it, eRender,
+ eConsole, &sys->dev);
+ }
+ assert(sys->manager == NULL);
+ if (FAILED(hr))
+ {
+ msg_Err(aout, "cannot get device (error 0x%lx)", hr);
+ goto out;
+ }
+
+ /* Create session manager (for controls even w/o active audio client) */
+ void *pv;
+ hr = IMMDevice_Activate(sys->dev, &IID_IAudioSessionManager,
+ CLSCTX_ALL, NULL, &pv);
+ if (FAILED(hr))
+ msg_Err(aout, "cannot activate session manager (error 0x%lx)", hr);
+ else
+ sys->manager = pv;
+
+ /* Report actual device */
+ LPWSTR wdevid;
+ hr = IMMDevice_GetId(sys->dev, &wdevid);
+ if (SUCCEEDED(hr))
+ {
+ char *id = FromWide(wdevid);
+ CoTaskMemFree(wdevid);
+ if (likely(id != NULL))
+ {
+ aout_DeviceReport(aout, id);
+ free(id);
+ }
+ }
+out:
+ SetEvent(sys->device_changed);
+ WaitForSingleObject(sys->device_ready, INFINITE);
+ return hr;
+}
+
+/**
+ * Closes the opened audio output device (if any).
+ */
+static void CloseDevice(audio_output_t *aout)
+{
+ aout_sys_t *sys = aout->sys;
+
+ assert(sys->dev != NULL);
+
+ if (sys->manager != NULL)
+ {
+ IAudioSessionManager_Release(sys->manager);
+ sys->manager = NULL;
}
- EnterCriticalSection(&sys->lock);
- sys->running = false;
- LeaveCriticalSection(&sys->lock);
- WakeConditionVariable(&sys->reply_wait);
+ IMMDevice_Release(sys->dev);
+ sys->dev = NULL;
+}
+
+static int DeviceSelect(audio_output_t *aout, const char *id)
+{
+ aout_sys_t *sys = aout->sys;
+ HRESULT hr;
+
+ if (TryEnterMTA(aout))
+ return -1;
+
+ if (sys->dev != NULL)
+ CloseDevice(aout);
+
+ hr = OpenDevice(aout, id);
+ while (hr == AUDCLNT_E_DEVICE_INVALIDATED)
+ hr = OpenDevice(aout, NULL); /* Fallback to default device */
+ LeaveMTA();
+
+ if (sys->stream != NULL)
+ /* Request restart of stream with the new device */
+ aout_RestartRequest(aout, AOUT_RESTART_OUTPUT);
+ return FAILED(hr) ? -1 : 0;
+}
+
+/**
+ * Callback for aout_stream_t to create a stream on the device.
+ * This can instantiate an IAudioClient or IDirectSound(8) object.
+ */
+static HRESULT ActivateDevice(void *opaque, REFIID iid, PROPVARIANT *actparms,
+ void **restrict pv)
+{
+ IMMDevice *dev = opaque;
+
+ return IMMDevice_Activate(dev, iid, CLSCTX_ALL, actparms, pv);
}
static int Start(audio_output_t *aout, audio_sample_format_t *restrict fmt)
{
aout_sys_t *sys = aout->sys;
+ HRESULT hr;
+
+ assert (sys->stream == NULL);
+ /* Open the default device if required (to deal with restarts) */
+ if (sys->dev == NULL && FAILED(DeviceSelect(aout, NULL)))
+ return -1;
- assert (sys->api == NULL);
- if (sys->dev == NULL)
+ aout_stream_t *s = vlc_object_create(aout, sizeof (*s));
+ if (unlikely(s == NULL))
return -1;
- sys->api = aout_api_Start(aout, fmt, sys->dev, &GUID_VLC_AUD_OUT);
- return (sys->api != NULL) ? 0 : -1;
+ s->owner.device = sys->dev;
+ s->owner.activate = ActivateDevice;
+
+ EnterMTA();
+ hr = aout_stream_Start(s, fmt, &GUID_VLC_AUD_OUT);
+ if (SUCCEEDED(hr))
+ sys->stream = s;
+ else
+ vlc_object_release(s);
+ LeaveMTA();
+
+ return vlc_FromHR(aout, hr);
}
static void Stop(audio_output_t *aout)
{
aout_sys_t *sys = aout->sys;
- assert (sys->api != NULL);
- aout_api_Stop(sys->api);
- sys->api = NULL;
+ assert (sys->stream != NULL);
+
+ EnterMTA();
+ aout_stream_Stop(sys->stream);
+ LeaveMTA();
+
+ vlc_object_release(sys->stream);
+ sys->stream = NULL;
}
static int Open(vlc_object_t *obj)
/* Fallback to other plugin until pass-through is implemented */
return VLC_EGENERIC;
- /* Initialize MMDevice API */
- if (TryEnter(aout))
- return VLC_EGENERIC;
-
aout_sys_t *sys = malloc(sizeof (*sys));
if (unlikely(sys == NULL))
return VLC_ENOMEM;
+ aout->sys = sys;
sys->aout = aout;
- sys->api = NULL;
+ sys->stream = NULL;
sys->it = NULL;
sys->dev = NULL;
sys->manager = NULL;
+ sys->session_events.lpVtbl = &vlc_AudioSessionEvents;
sys->refs = 1;
- InitializeCriticalSection(&sys->lock);
- InitializeConditionVariable(&sys->request_wait);
- InitializeConditionVariable(&sys->reply_wait);
- sys->active = true;
- sys->running = true;
- sys->volume = -1.f;
- sys->mute = -1;
+
+ sys->device_changed = CreateEvent(NULL, FALSE, FALSE, NULL);
+ sys->device_ready = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (unlikely(sys->device_changed == NULL || sys->device_ready == NULL))
+ goto error;
+
+ /* Initialize MMDevice API */
+ if (TryEnterMTA(aout))
+ goto error;
hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL,
&IID_IMMDeviceEnumerator, &pv);
if (FAILED(hr))
{
+ LeaveMTA();
msg_Dbg(aout, "cannot create device enumerator (error 0x%lx)", hr);
- Leave();
goto error;
}
sys->it = pv;
- GetDevices(obj, sys->it);
- /* Get audio device according to policy */
- wchar_t *devid = var_InheritWide(aout, "audio-device");
- if (devid != NULL)
- {
- msg_Dbg (aout, "using selected device %ls", devid);
- hr = IMMDeviceEnumerator_GetDevice (sys->it, devid, &sys->dev);
- if (FAILED(hr))
- msg_Err(aout, "cannot get device %ls (error 0x%lx)", devid, hr);
- free (devid);
- }
- if (sys->dev == NULL)
- {
- msg_Dbg (aout, "using default device");
- hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(sys->it, eRender,
- eConsole, &sys->dev);
- if (FAILED(hr))
- msg_Err(aout, "cannot get default device (error 0x%lx)", hr);
- }
- if (sys->dev == NULL)
- /* TODO: VLC should be able to start without devices, so long as
- * a device becomes available before Start() is called. */
+ if (vlc_clone(&sys->thread, MMThread, aout, VLC_THREAD_PRIORITY_LOW))
goto error;
+ WaitForSingleObject(sys->device_ready, INFINITE);
- hr = IMMDevice_Activate(sys->dev, &IID_IAudioSessionManager,
- CLSCTX_ALL, NULL, &pv);
- if (FAILED(hr))
- msg_Err(aout, "cannot activate session manager (error 0x%lx)", hr);
- else
- sys->manager = pv;
+ DeviceSelect(aout, NULL); /* Get a device to start with */
+ LeaveMTA(); /* leave MTA after thread has entered MTA */
- /* Note: thread handle released by CRT, ignore it. */
- if (_beginthread(MMThread, 0, aout) == (uintptr_t)-1)
- goto error;
- Leave();
-
- aout->sys = sys;
aout->start = Start;
aout->stop = Stop;
aout->time_get = TimeGet;
aout->flush = Flush;
aout->volume_set = VolumeSet;
aout->mute_set = MuteSet;
- var_AddCallback (aout, "audio-device", DeviceChanged, NULL);
+ aout->device_enum = DevicesEnum;
+ aout->device_select = DeviceSelect;
return VLC_SUCCESS;
+
error:
- if (sys->manager != NULL)
- IAudioSessionManager_Release(sys->manager);
- if (sys->dev != NULL)
- IMMDevice_Release(sys->dev);
if (sys->it != NULL)
+ {
IMMDeviceEnumerator_Release(sys->it);
- DeleteCriticalSection(&sys->lock);
+ LeaveMTA();
+ }
+ if (sys->device_ready != NULL)
+ CloseHandle(sys->device_ready);
+ if (sys->device_changed != NULL)
+ CloseHandle(sys->device_changed);
free(sys);
return VLC_EGENERIC;
}
audio_output_t *aout = (audio_output_t *)obj;
aout_sys_t *sys = aout->sys;
- EnterCriticalSection(&sys->lock);
- sys->active = false;
- WakeConditionVariable(&sys->request_wait);
- while (sys->running)
- SleepConditionVariableCS(&sys->reply_wait, &sys->lock, INFINITE);
- LeaveCriticalSection(&sys->lock);
-
- var_DelCallback (aout, "audio-device", DeviceChanged, NULL);
- var_Destroy (aout, "audio-device");
-
- Enter();
+ EnterMTA(); /* enter MTA before thread leaves MTA */
+ if (sys->dev != NULL)
+ CloseDevice(aout);
- if (sys->manager != NULL)
- IAudioSessionManager_Release(sys->manager);
- IMMDevice_Release(sys->dev);
IMMDeviceEnumerator_Release(sys->it);
- Leave();
+ sys->it = NULL;
+
+ SetEvent(sys->device_changed);
+ vlc_join(sys->thread, NULL);
+ LeaveMTA();
+ CloseHandle(sys->device_ready);
+ CloseHandle(sys->device_changed);
free(sys);
}