[PATCH 1/5] winepulse: Add initial stub for pulseaudio support
Andrew Eikum
aeikum at codeweavers.com
Thu Oct 29 12:04:11 CDT 2015
From: Maarten Lankhorst <m.b.lankhorst at gmail.com>
Includes API compatibility patch by Juergen Tretthahn <orson at orson.at>.
Synchronous static data initialization by Andrew Eikum <aeikum at codeweavers.com>
Signed-off-by: Andrew Eikum <aeikum at codeweavers.com>
---
configure.ac | 31 +++-
dlls/mmdevapi/main.c | 2 +-
dlls/winepulse.drv/Makefile.in | 7 +
dlls/winepulse.drv/mmdevdrv.c | 284 ++++++++++++++++++++++++++++++++++
dlls/winepulse.drv/winepulse.drv.spec | 5 +
include/config.h.in | 6 +
6 files changed, 332 insertions(+), 3 deletions(-)
create mode 100644 dlls/winepulse.drv/Makefile.in
create mode 100644 dlls/winepulse.drv/mmdevdrv.c
create mode 100644 dlls/winepulse.drv/winepulse.drv.spec
diff --git a/configure.ac b/configure.ac
index 3d48b67..23a9613 100644
--- a/configure.ac
+++ b/configure.ac
@@ -72,6 +72,7 @@ AC_ARG_WITH(pcap, AS_HELP_STRING([--without-pcap],[do not use the Packet Ca
AC_ARG_WITH(png, AS_HELP_STRING([--without-png],[do not use PNG]))
AC_ARG_WITH(pthread, AS_HELP_STRING([--without-pthread],[do not use the pthread library]),
[if test "x$withval" = "xno"; then ac_cv_header_pthread_h=no; fi])
+AC_ARG_WITH(pulse, AC_HELP_STRING([--without-pulse],[do not use PulseAudio sound support]))
AC_ARG_WITH(sane, AS_HELP_STRING([--without-sane],[do not use SANE (scanner support)]))
AC_ARG_WITH(tiff, AS_HELP_STRING([--without-tiff],[do not use TIFF]))
AC_ARG_WITH(v4l, AS_HELP_STRING([--without-v4l],[do not use v4l1 (v4l support)]))
@@ -1547,6 +1548,30 @@ then
[GetText ${notice_platform}development files not found (or too old), po files can't be rebuilt.])
fi
+dnl **** Check for PulseAudio ****
+AC_SUBST(PULSE_LIBS,"")
+AC_SUBST(PULSE_CFLAGS,"")
+if test "x$with_pulse" != "xno";
+then
+ ac_save_CPPFLAGS="$CPPFLAGS"
+ if test "$PKG_CONFIG" != "false";
+ then
+ ac_pulse_libs="`$PKG_CONFIG --libs libpulse 2>/dev/null`"
+ ac_pulse_cflags="`$PKG_CONFIG --cflags-only-I libpulse 2>/dev/null`"
+
+ CPPFLAGS="$CPPFLAGS $ac_pulse_cflags"
+ AC_CHECK_HEADERS(pulse/pulseaudio.h,
+ [AC_CHECK_LIB(pulse, pa_stream_is_corked,
+ [AC_DEFINE(HAVE_PULSEAUDIO, 1, [Define if you have pulseaudio])
+ PULSE_LIBS="$ac_pulse_libs"
+ PULSE_CFLAGS="$ac_pulse_cflags"],,$ac_pulse_libs)
+ ])
+ fi
+ CPPFLAGS="$ac_save_CPPFLAGS"
+fi
+WINE_WARNING_WITH(pulse, [test "$ac_cv_lib_pulse_pa_stream_is_corked" != "yes"],
+ [libpulse ${notice_platform}development files not found or too old, Pulse won't be supported.])
+
dnl **** Check for gstreamer ****
if test "x$with_gstreamer" != "xno"
then
@@ -1779,13 +1804,14 @@ fi
dnl **** Disable unsupported winmm drivers ****
test -n "$ALSA_LIBS" || enable_winealsa_drv=${enable_winealsa_drv:-no}
test -n "$COREAUDIO_LIBS" || enable_winecoreaudio_drv=${enable_winecoreaudio_drv:-no}
+test -n "$PULSE_LIBS" || enable_winepulse_drv=${enable_winepulse_drv:-no}
test "x$ac_cv_member_oss_sysinfo_numaudioengines" = xyes || enable_wineoss_drv=${enable_wineoss_drv:-no}
test "$ac_cv_header_linux_joystick_h" = "yes" -o "$ac_cv_header_IOKit_hid_IOHIDLib_h" = "yes" || enable_winejoystick_drv=${enable_winejoystick_drv:-no}
dnl **** Check for any sound system ****
-if test "x$ALSA_LIBS$COREAUDIO_LIBS" = "x" -a \
+if test "x$ALSA_LIBS$COREAUDIO_LIBS$PULSE_LIBS" = "x" -a \
"x$ac_cv_member_oss_sysinfo_numaudioengines" != xyes -a \
- "x$with_alsa$with_coreaudio$with_oss" != xnonono
+ "x$with_alsa$with_coreaudio$with_oss$with_pulse" != xnononono
then
WINE_WARNING([No sound system was found. Windows applications will be silent.])
fi
@@ -3401,6 +3427,7 @@ WINE_CONFIG_DLL(winemp3.acm)
WINE_CONFIG_DLL(wineoss.drv)
WINE_CONFIG_DLL(wineps.drv,,[clean,po])
WINE_CONFIG_DLL(wineps16.drv16,enable_win16)
+WINE_CONFIG_DLL(winepulse.drv)
WINE_CONFIG_DLL(wineqtdecoder)
WINE_CONFIG_DLL(winex11.drv)
WINE_CONFIG_DLL(wing.dll16,enable_win16)
diff --git a/dlls/mmdevapi/main.c b/dlls/mmdevapi/main.c
index 52cf6f1..aa4baa5 100644
--- a/dlls/mmdevapi/main.c
+++ b/dlls/mmdevapi/main.c
@@ -113,7 +113,7 @@ static BOOL init_driver(void)
{
static const WCHAR drv_value[] = {'A','u','d','i','o',0};
- static WCHAR default_list[] = {'a','l','s','a',',','o','s','s',',',
+ static WCHAR default_list[] = {'p','u','l','s','e',',','a','l','s','a',',','o','s','s',',',
'c','o','r','e','a','u','d','i','o',0};
DriverFuncs driver;
diff --git a/dlls/winepulse.drv/Makefile.in b/dlls/winepulse.drv/Makefile.in
new file mode 100644
index 0000000..d660063
--- /dev/null
+++ b/dlls/winepulse.drv/Makefile.in
@@ -0,0 +1,7 @@
+MODULE = winepulse.drv
+IMPORTS = dxguid uuid winmm user32 advapi32 ole32
+EXTRALIBS = $(PULSE_LIBS) $(PTHREAD_LIBS)
+EXTRAINCL = $(PULSE_CFLAGS)
+
+C_SRCS = \
+ mmdevdrv.c
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
new file mode 100644
index 0000000..04d9f19
--- /dev/null
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -0,0 +1,284 @@
+/*
+ * Copyright 2011-2012 Maarten Lankhorst
+ * Copyright 2010-2011 Maarten Lankhorst for CodeWeavers
+ * Copyright 2011 Andrew Eikum for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#define NONAMELESSUNION
+#define COBJMACROS
+#define _GNU_SOURCE
+
+#include "config.h"
+#include <poll.h>
+#include <pthread.h>
+
+#include <stdarg.h>
+#include <unistd.h>
+#include <math.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <pulse/pulseaudio.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winnls.h"
+#include "winreg.h"
+#include "wine/debug.h"
+#include "wine/unicode.h"
+#include "wine/list.h"
+
+#include "ole2.h"
+#include "dshow.h"
+#include "dsound.h"
+#include "propsys.h"
+
+#include "initguid.h"
+#include "ks.h"
+#include "ksmedia.h"
+#include "mmdeviceapi.h"
+#include "audioclient.h"
+#include "endpointvolume.h"
+#include "audiopolicy.h"
+
+#include "wine/list.h"
+
+#define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER)
+
+WINE_DEFAULT_DEBUG_CHANNEL(pulse);
+
+/* From <dlls/mmdevapi/mmdevapi.h> */
+enum DriverPriority {
+ Priority_Unavailable = 0,
+ Priority_Low,
+ Priority_Neutral,
+ Priority_Preferred
+};
+
+static const REFERENCE_TIME MinimumPeriod = 30000;
+static const REFERENCE_TIME DefaultPeriod = 100000;
+
+static pa_context *pulse_ctx;
+static pa_mainloop *pulse_ml;
+
+static pthread_mutex_t pulse_lock;
+
+static GUID pulse_render_guid =
+{ 0xfd47d9cc, 0x4218, 0x4135, { 0x9c, 0xe2, 0x0c, 0x19, 0x5c, 0x87, 0x40, 0x5b } };
+static GUID pulse_capture_guid =
+{ 0x25da76d0, 0x033c, 0x4235, { 0x90, 0x02, 0x19, 0xf4, 0x88, 0x94, 0xac, 0x6f } };
+
+BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
+{
+ if (reason == DLL_PROCESS_ATTACH) {
+ pthread_mutexattr_t attr;
+
+ DisableThreadLibraryCalls(dll);
+
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT);
+
+ if (pthread_mutex_init(&pulse_lock, &attr) != 0)
+ pthread_mutex_init(&pulse_lock, NULL);
+ } else if (reason == DLL_PROCESS_DETACH) {
+ if (pulse_ctx) {
+ pa_context_disconnect(pulse_ctx);
+ pa_context_unref(pulse_ctx);
+ }
+ if (pulse_ml)
+ pa_mainloop_quit(pulse_ml, 0);
+ }
+ return TRUE;
+}
+
+
+static const WCHAR defaultW[] = {'P','u','l','s','e','a','u','d','i','o',0};
+
+/* Following pulseaudio design here, mainloop has the lock taken whenever
+ * it is handling something for pulse, and the lock is required whenever
+ * doing any pa_* call that can affect the state in any way
+ *
+ * pa_cond_wait is used when waiting on results, because the mainloop needs
+ * the same lock taken to affect the state
+ *
+ * This is basically the same as the pa_threaded_mainloop implementation,
+ * but that cannot be used because it uses pthread_create directly
+ *
+ * pa_threaded_mainloop_(un)lock -> pthread_mutex_(un)lock
+ * pa_threaded_mainloop_signal -> pthread_cond_signal
+ * pa_threaded_mainloop_wait -> pthread_cond_wait
+ */
+
+static int pulse_poll_func(struct pollfd *ufds, unsigned long nfds, int timeout, void *userdata) {
+ int r;
+ pthread_mutex_unlock(&pulse_lock);
+ r = poll(ufds, nfds, timeout);
+ pthread_mutex_lock(&pulse_lock);
+ return r;
+}
+
+static void pulse_contextcallback(pa_context *c, void *userdata)
+{
+ switch (pa_context_get_state(c)) {
+ default:
+ FIXME("Unhandled state: %i\n", pa_context_get_state(c));
+ case PA_CONTEXT_CONNECTING:
+ case PA_CONTEXT_UNCONNECTED:
+ case PA_CONTEXT_AUTHORIZING:
+ case PA_CONTEXT_SETTING_NAME:
+ case PA_CONTEXT_TERMINATED:
+ TRACE("State change to %i\n", pa_context_get_state(c));
+ return;
+
+ case PA_CONTEXT_READY:
+ TRACE("Ready\n");
+ break;
+
+ case PA_CONTEXT_FAILED:
+ ERR("Context failed: %s\n", pa_strerror(pa_context_errno(c)));
+ break;
+ }
+}
+
+
+/* some poorly-behaved applications call audio functions during DllMain, so we
+ * have to do as much as possible without creating a new thread. this function
+ * sets up a synchronous connection to verify the server is running and query
+ * static data. */
+static HRESULT pulse_test_connect(void)
+{
+ int len, ret;
+ WCHAR path[PATH_MAX], *name;
+ char *str;
+
+ pulse_ml = pa_mainloop_new();
+
+ pa_mainloop_set_poll_func(pulse_ml, pulse_poll_func, NULL);
+
+ GetModuleFileNameW(NULL, path, sizeof(path)/sizeof(*path));
+ name = strrchrW(path, '\\');
+ if (!name)
+ name = path;
+ else
+ name++;
+ len = WideCharToMultiByte(CP_UNIXCP, 0, name, -1, NULL, 0, NULL, NULL);
+ str = pa_xmalloc(len);
+ WideCharToMultiByte(CP_UNIXCP, 0, name, -1, str, len, NULL, NULL);
+ TRACE("Name: %s\n", str);
+ pulse_ctx = pa_context_new(pa_mainloop_get_api(pulse_ml), str);
+ pa_xfree(str);
+ if (!pulse_ctx) {
+ ERR("Failed to create context\n");
+ pa_mainloop_free(pulse_ml);
+ pulse_ml = NULL;
+ return E_FAIL;
+ }
+
+ pa_context_set_state_callback(pulse_ctx, pulse_contextcallback, NULL);
+
+ TRACE("libpulse protocol version: %u. API Version %u\n", pa_context_get_protocol_version(pulse_ctx), PA_API_VERSION);
+ if (pa_context_connect(pulse_ctx, NULL, 0, NULL) < 0)
+ goto fail;
+
+ /* Wait for connection */
+ while (pa_mainloop_iterate(pulse_ml, 1, &ret) >= 0) {
+ pa_context_state_t state = pa_context_get_state(pulse_ctx);
+
+ if (state == PA_CONTEXT_FAILED || state == PA_CONTEXT_TERMINATED)
+ goto fail;
+
+ if (state == PA_CONTEXT_READY)
+ break;
+ }
+
+ TRACE("Test-connected to server %s with protocol version: %i.\n",
+ pa_context_get_server(pulse_ctx),
+ pa_context_get_server_protocol_version(pulse_ctx));
+
+ pa_context_unref(pulse_ctx);
+ pulse_ctx = NULL;
+ pa_mainloop_free(pulse_ml);
+ pulse_ml = NULL;
+
+ return S_OK;
+
+fail:
+ pa_context_unref(pulse_ctx);
+ pulse_ctx = NULL;
+ pa_mainloop_free(pulse_ml);
+ pulse_ml = NULL;
+
+ return E_FAIL;
+}
+
+HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, const WCHAR ***ids, GUID **keys,
+ UINT *num, UINT *def_index)
+{
+ WCHAR *id;
+
+ TRACE("%d %p %p %p\n", flow, ids, num, def_index);
+
+ *num = 1;
+ *def_index = 0;
+
+ *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(**ids));
+ *keys = NULL;
+ if (!*ids)
+ return E_OUTOFMEMORY;
+
+ (*ids)[0] = id = HeapAlloc(GetProcessHeap(), 0, sizeof(defaultW));
+ *keys = HeapAlloc(GetProcessHeap(), 0, sizeof(**keys));
+ if (!*keys || !id) {
+ HeapFree(GetProcessHeap(), 0, id);
+ HeapFree(GetProcessHeap(), 0, *keys);
+ HeapFree(GetProcessHeap(), 0, *ids);
+ *ids = NULL;
+ *keys = NULL;
+ return E_OUTOFMEMORY;
+ }
+ memcpy(id, defaultW, sizeof(defaultW));
+
+ if (flow == eRender)
+ (*keys)[0] = pulse_render_guid;
+ else
+ (*keys)[0] = pulse_capture_guid;
+
+ return S_OK;
+}
+
+int WINAPI AUDDRV_GetPriority(void)
+{
+ HRESULT hr;
+ pthread_mutex_lock(&pulse_lock);
+ hr = pulse_test_connect();
+ pthread_mutex_unlock(&pulse_lock);
+ return SUCCEEDED(hr) ? Priority_Low : Priority_Unavailable;
+}
+
+HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient **out)
+{
+ TRACE("%s %p %p\n", debugstr_guid(guid), dev, out);
+ *out = NULL;
+ return E_NOTIMPL;
+}
+
+HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
+ IAudioSessionManager2 **out)
+{
+ *out = NULL;
+ return E_NOTIMPL;
+}
diff --git a/dlls/winepulse.drv/winepulse.drv.spec b/dlls/winepulse.drv/winepulse.drv.spec
new file mode 100644
index 0000000..612bf46
--- /dev/null
+++ b/dlls/winepulse.drv/winepulse.drv.spec
@@ -0,0 +1,5 @@
+# MMDevAPI driver functions
+@ stdcall -private GetPriority() AUDDRV_GetPriority
+@ stdcall -private GetEndpointIDs(long ptr ptr ptr ptr) AUDDRV_GetEndpointIDs
+@ stdcall -private GetAudioEndpoint(ptr ptr ptr) AUDDRV_GetAudioEndpoint
+@ stdcall -private GetAudioSessionManager(ptr ptr) AUDDRV_GetAudioSessionManager
diff --git a/include/config.h.in b/include/config.h.in
index eb61a94..817b5f2 100644
--- a/include/config.h.in
+++ b/include/config.h.in
@@ -696,6 +696,12 @@
/* Define to 1 if you have the <pthread_np.h> header file. */
#undef HAVE_PTHREAD_NP_H
+/* Define if you have pulseaudio */
+#undef HAVE_PULSEAUDIO
+
+/* Define to 1 if you have the <pulse/pulseaudio.h> header file. */
+#undef HAVE_PULSE_PULSEAUDIO_H
+
/* Define to 1 if you have the <pwd.h> header file. */
#undef HAVE_PWD_H
--
2.6.2
More information about the wine-patches
mailing list