winepulse: Add initial stub for pulseaudio support.

Includes API compatibility patch by Juergen Tretthahn <orson@orson.at>.
Synchronous static data initialization by Andrew Eikum <aeikum@codeweavers.com>.

Signed-off-by: Andrew Eikum <aeikum@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Maarten Lankhorst 2015-10-29 12:04:11 -05:00 committed by Alexandre Julliard
parent e4bc220802
commit 3fe0c08992
7 changed files with 425 additions and 5 deletions

112
configure vendored
View File

@ -654,6 +654,8 @@ OSS4_CFLAGS
ALSA_LIBS
GSTREAMER_LIBS
GSTREAMER_CFLAGS
PULSE_CFLAGS
PULSE_LIBS
GETTEXTPO_LIBS
Z_LIBS
FREETYPE_LIBS
@ -830,6 +832,7 @@ with_oss
with_pcap
with_png
with_pthread
with_pulse
with_sane
with_tiff
with_v4l
@ -1341,6 +1344,7 @@ enable_winemapi
enable_winemp3_acm
enable_wineoss_drv
enable_wineps_drv
enable_winepulse_drv
enable_wineqtdecoder
enable_winex11_drv
enable_wing32
@ -1523,6 +1527,8 @@ LCMS2_CFLAGS
LCMS2_LIBS
FREETYPE_CFLAGS
FREETYPE_LIBS
PULSE_CFLAGS
PULSE_LIBS
GSTREAMER_CFLAGS
GSTREAMER_LIBS
CAPI20_CFLAGS
@ -2205,6 +2211,7 @@ Optional Packages:
--without-pcap do not use the Packet Capture library
--without-png do not use PNG
--without-pthread do not use the pthread library
--without-pulse do not use PulseAudio sound support
--without-sane do not use SANE (scanner support)
--without-tiff do not use TIFF
--without-v4l do not use v4l1 (v4l support)
@ -2265,6 +2272,9 @@ Some influential environment variables:
C compiler flags for freetype2, overriding pkg-config
FREETYPE_LIBS
Linker flags for freetype2, overriding pkg-config
PULSE_CFLAGS
C compiler flags for libpulse, overriding pkg-config
PULSE_LIBS Linker flags for libpulse, overriding pkg-config
GSTREAMER_CFLAGS
C compiler flags for gstreamer-app-0.10, overriding pkg-config
GSTREAMER_LIBS
@ -3452,6 +3462,12 @@ if test "${with_pthread+set}" = set; then :
fi
# Check whether --with-pulse was given.
if test "${with_pulse+set}" = set; then :
withval=$with_pulse;
fi
# Check whether --with-sane was given.
if test "${with_sane+set}" = set; then :
withval=$with_sane;
@ -12576,6 +12592,94 @@ esac
fi
fi
PULSE_LIBS=""
PULSE_CFLAGS=""
if test "x$with_pulse" != "xno";
then
if ${PULSE_CFLAGS:+false} :; then :
if ${PKG_CONFIG+:} false; then :
PULSE_CFLAGS=`$PKG_CONFIG --cflags libpulse 2>/dev/null`
fi
fi
if ${PULSE_LIBS:+false} :; then :
if ${PKG_CONFIG+:} false; then :
PULSE_LIBS=`$PKG_CONFIG --libs libpulse 2>/dev/null`
fi
fi
$as_echo "$as_me:${as_lineno-$LINENO}: libpulse cflags: $PULSE_CFLAGS" >&5
$as_echo "$as_me:${as_lineno-$LINENO}: libpulse libs: $PULSE_LIBS" >&5
ac_save_CPPFLAGS=$CPPFLAGS
CPPFLAGS="$CPPFLAGS $PULSE_CFLAGS"
for ac_header in pulse/pulseaudio.h
do :
ac_fn_c_check_header_mongrel "$LINENO" "pulse/pulseaudio.h" "ac_cv_header_pulse_pulseaudio_h" "$ac_includes_default"
if test "x$ac_cv_header_pulse_pulseaudio_h" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_PULSE_PULSEAUDIO_H 1
_ACEOF
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for pa_stream_is_corked in -lpulse" >&5
$as_echo_n "checking for pa_stream_is_corked in -lpulse... " >&6; }
if ${ac_cv_lib_pulse_pa_stream_is_corked+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-lpulse $PULSE_LIBS $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char pa_stream_is_corked ();
int
main ()
{
return pa_stream_is_corked ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_pulse_pa_stream_is_corked=yes
else
ac_cv_lib_pulse_pa_stream_is_corked=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pulse_pa_stream_is_corked" >&5
$as_echo "$ac_cv_lib_pulse_pa_stream_is_corked" >&6; }
if test "x$ac_cv_lib_pulse_pa_stream_is_corked" = xyes; then :
:
fi
fi
done
CPPFLAGS=$ac_save_CPPFLAGS
test -z "$PULSE_CFLAGS" || PULSE_CFLAGS=`echo " $PULSE_CFLAGS" | sed 's/ -I\([^/]\)/ -I\$(top_builddir)\/\1/g'`
test -z "$PULSE_LIBS" || PULSE_LIBS=`echo " $PULSE_LIBS" | sed 's/ -L\([^/]\)/ -L\$(top_builddir)\/\1/g'`
fi
if test "$ac_cv_lib_pulse_pa_stream_is_corked" != "yes"; then :
case "x$with_pulse" in
x) as_fn_append wine_notices "|libpulse ${notice_platform}development files not found or too old, Pulse won't be supported." ;;
xno) ;;
*) as_fn_error $? "libpulse ${notice_platform}development files not found or too old, Pulse won't be supported.
This is an error since --with-pulse was requested." "$LINENO" 5 ;;
esac
fi
if test "x$with_gstreamer" != "xno"
then
if ${GSTREAMER_CFLAGS:+false} :; then :
@ -13922,12 +14026,13 @@ fi
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}
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
as_fn_append wine_warnings "|No sound system was found. Windows applications will be silent."
fi
@ -17177,6 +17282,8 @@ FREETYPE_CFLAGS = $FREETYPE_CFLAGS
FREETYPE_LIBS = $FREETYPE_LIBS
Z_LIBS = $Z_LIBS
GETTEXTPO_LIBS = $GETTEXTPO_LIBS
PULSE_LIBS = $PULSE_LIBS
PULSE_CFLAGS = $PULSE_CFLAGS
GSTREAMER_CFLAGS = $GSTREAMER_CFLAGS
GSTREAMER_LIBS = $GSTREAMER_LIBS
ALSA_LIBS = $ALSA_LIBS
@ -17954,6 +18061,7 @@ wine_fn_config_dll winemp3.acm enable_winemp3_acm
wine_fn_config_dll wineoss.drv enable_wineoss_drv
wine_fn_config_dll wineps.drv enable_wineps_drv clean,po
wine_fn_config_dll wineps16.drv16 enable_win16
wine_fn_config_dll winepulse.drv enable_winepulse_drv
wine_fn_config_dll wineqtdecoder enable_wineqtdecoder
wine_fn_config_dll winex11.drv enable_winex11_drv
wine_fn_config_dll wing.dll16 enable_win16

View File

@ -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,18 @@ 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
WINE_PACKAGE_FLAGS(PULSE,[libpulse],,,,
[AC_CHECK_HEADERS(pulse/pulseaudio.h,
[AC_CHECK_LIB(pulse, pa_stream_is_corked,[:],,[$PULSE_LIBS])])])
fi
WINE_NOTICE_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 +1792,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
@ -3402,6 +3416,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)

View File

@ -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;

View File

@ -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

View File

@ -0,0 +1,282 @@
/*
* 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"
WINE_DEFAULT_DEBUG_CHANNEL(pulse);
#define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER)
/* 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;
}

View File

@ -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

View File

@ -696,6 +696,9 @@
/* Define to 1 if you have the <pthread_np.h> header file. */
#undef HAVE_PTHREAD_NP_H
/* 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