summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--indra/llplugin/llpluginclassmedia.cpp9
-rw-r--r--indra/llplugin/llpluginclassmedia.h3
-rw-r--r--indra/media_plugins/base/media_plugin_base.cpp51
-rw-r--r--indra/media_plugins/base/media_plugin_base.h18
-rw-r--r--indra/media_plugins/cef/CMakeLists.txt45
-rw-r--r--indra/media_plugins/cef/linux/volume_catcher_linux.cpp103
-rw-r--r--indra/media_plugins/cef/linux/volume_catcher_linux.h151
-rwxr-xr-xindra/media_plugins/cef/linux/volume_catcher_pipewire.cpp (renamed from indra/media_plugins/cef/linux_volume_catcher_pw.cpp)247
-rw-r--r--indra/media_plugins/cef/linux/volume_catcher_pipewire_syms.inc (renamed from indra/media_plugins/cef/linux_volume_catcher_pw_syms.inc)0
-rwxr-xr-xindra/media_plugins/cef/linux/volume_catcher_pulseaudio.cpp (renamed from indra/media_plugins/cef/linux_volume_catcher_pa.cpp)167
-rwxr-xr-xindra/media_plugins/cef/linux/volume_catcher_pulseaudio_glib_syms.inc (renamed from indra/media_plugins/cef/linux_volume_catcher_paglib_syms.inc)0
-rwxr-xr-xindra/media_plugins/cef/linux/volume_catcher_pulseaudio_syms.inc (renamed from indra/media_plugins/cef/linux_volume_catcher_pa_syms.inc)4
-rw-r--r--indra/media_plugins/cef/media_plugin_cef.cpp7
-rw-r--r--indra/media_plugins/cef/volume_catcher.h32
-rw-r--r--indra/newview/app_settings/settings.xml11
-rw-r--r--indra/newview/llviewermedia.cpp5
16 files changed, 500 insertions, 353 deletions
diff --git a/indra/llplugin/llpluginclassmedia.cpp b/indra/llplugin/llpluginclassmedia.cpp
index 3e72710366..3d87e2be7c 100644
--- a/indra/llplugin/llpluginclassmedia.cpp
+++ b/indra/llplugin/llpluginclassmedia.cpp
@@ -981,6 +981,15 @@ void LLPluginClassMedia::enableMediaPluginDebugging( bool enable )
sendMessage( message );
}
+#if LL_LINUX
+void LLPluginClassMedia::enablePipeWireVolumeCatcher( bool enable )
+{
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "enable_pipewire_volume_catcher");
+ message.setValueBoolean( "enable", enable );
+ sendMessage( message );
+}
+#endif
+
void LLPluginClassMedia::setTarget(const std::string &target)
{
mTarget = target;
diff --git a/indra/llplugin/llpluginclassmedia.h b/indra/llplugin/llpluginclassmedia.h
index ba76ae4e37..4b5ec2b76c 100644
--- a/indra/llplugin/llpluginclassmedia.h
+++ b/indra/llplugin/llpluginclassmedia.h
@@ -122,6 +122,9 @@ public:
// enable/disable media plugin debugging messages and info spam
void enableMediaPluginDebugging( bool enable );
+#if LL_LINUX
+ void enablePipeWireVolumeCatcher( bool enable );
+#endif
// Javascript <-> viewer events
void jsEnableObject( bool enable );
diff --git a/indra/media_plugins/base/media_plugin_base.cpp b/indra/media_plugins/base/media_plugin_base.cpp
index dbbc973f00..3514ff428f 100644
--- a/indra/media_plugins/base/media_plugin_base.cpp
+++ b/indra/media_plugins/base/media_plugin_base.cpp
@@ -183,13 +183,13 @@ bool SymbolGrabber::grabSymbols(std::vector< std::string > const &aDSONames)
return true;
//attempt to load the shared libraries
- apr_pool_create(&sSymPADSOMemoryPool, nullptr);
+ apr_pool_create(&sSymDSOMemoryPool, nullptr);
for( std::vector< std::string >::const_iterator itr = aDSONames.begin(); itr != aDSONames.end(); ++itr )
{
apr_dso_handle_t *pDSO(NULL);
std::string strDSO{ *itr };
- if( APR_SUCCESS == apr_dso_load( &pDSO, strDSO.c_str(), sSymPADSOMemoryPool ))
+ if( APR_SUCCESS == apr_dso_load( &pDSO, strDSO.c_str(), sSymDSOMemoryPool ))
sLoadedLibraries.push_back( pDSO );
for( auto i = 0; i < gSymbolsToGrab.size(); ++i )
@@ -254,3 +254,50 @@ int WINAPI DllEntryPoint( HINSTANCE hInstance, unsigned long reason, void* param
return 1;
}
#endif
+
+#if LL_LINUX
+pid_t getParentPid( pid_t aPid )
+{
+ std::stringstream strm;
+ strm << "/proc/" << aPid << "/status";
+ std::ifstream in{ strm.str() };
+
+ if( !in.is_open() )
+ return 0;
+
+ pid_t res {0};
+ while( !in.eof() && res == 0 )
+ {
+ std::string line;
+ line.resize( 1024, 0 );
+ in.getline( &line[0], line.length() );
+
+ auto i = line.find( "PPid:" );
+
+ if( i == std::string::npos )
+ continue;
+
+ char const *pIn = line.c_str() + 5; // Skip over pid;
+ while( *pIn != 0 && isspace( *pIn ) )
+ ++pIn;
+
+ if( *pIn )
+ res = atoll( pIn );
+ }
+ return res;
+}
+
+bool isPluginPid( pid_t aPid )
+{
+ auto myPid = getpid();
+
+ do
+ {
+ if( aPid == myPid )
+ return true;
+ aPid = getParentPid( aPid );
+ } while( aPid > 1 );
+
+ return false;
+}
+#endif \ No newline at end of file
diff --git a/indra/media_plugins/base/media_plugin_base.h b/indra/media_plugins/base/media_plugin_base.h
index 2e975a5bf2..571dd3bd1b 100644
--- a/indra/media_plugins/base/media_plugin_base.h
+++ b/indra/media_plugins/base/media_plugin_base.h
@@ -52,12 +52,21 @@ private:
std::vector< SymbolToGrab > gSymbolsToGrab;
bool sSymsGrabbed = false;
- apr_pool_t *sSymPADSOMemoryPool = nullptr;
+ apr_pool_t *sSymDSOMemoryPool = nullptr;
std::vector<apr_dso_handle_t *> sLoadedLibraries;
};
extern SymbolGrabber gSymbolGrabber;
-#define LL_GRAB_SYM(REQUIRED, SYMBOL_NAME, RETURN, ...) RETURN (*ll##SYMBOL_NAME)(__VA_ARGS__) = nullptr; size_t gRegistered##SYMBOL_NAME = gSymbolGrabber.registerSymbol( { REQUIRED, #SYMBOL_NAME , (apr_dso_handle_sym_t*)&ll##SYMBOL_NAME} );
+
+// extern SymbolGrabber gSymbolGrabber;
+
+#define LL_SYMBOL_GRABBER gSymbolGrabber
+
+#define LL_GRAB_SYM(REQUIRED, SYMBOL_NAME, RETURN, ...) \
+ RETURN (*ll##SYMBOL_NAME)(__VA_ARGS__) = nullptr; \
+ size_t gRegistered##SYMBOL_NAME = LL_SYMBOL_GRABBER.registerSymbol( \
+ { REQUIRED, #SYMBOL_NAME , (apr_dso_handle_sym_t*)&ll##SYMBOL_NAME} \
+ );
#endif
@@ -153,4 +162,7 @@ int init_media_plugin(
LLPluginInstance::sendMessageFunction *plugin_send_func,
void **plugin_user_data);
-
+#if LL_LINUX
+pid_t getParentPid(pid_t aPid);
+bool isPluginPid(pid_t aPid);
+#endif \ No newline at end of file
diff --git a/indra/media_plugins/cef/CMakeLists.txt b/indra/media_plugins/cef/CMakeLists.txt
index 4ef2b0957e..065aa4605a 100644
--- a/indra/media_plugins/cef/CMakeLists.txt
+++ b/indra/media_plugins/cef/CMakeLists.txt
@@ -24,27 +24,30 @@ set(media_plugin_cef_HEADER_FILES
# Select which VolumeCatcher implementation to use
if (LINUX)
- # off by default. we should keep using pulse for now
- set(USE_VOLUME_CATCHER_PW OFF CACHE BOOL "Use PipeWire for CEF volume catching")
-
- if (USE_VOLUME_CATCHER_PW)
- include(FindPipeWire)
- include_directories(SYSTEM ${PIPEWIRE_INCLUDE_DIRS} ${SPA_INCLUDE_DIRS})
- message( "Building with Linux volume catcher using PipeWire" )
- set(LINUX_VOLUME_CATCHER linux_volume_catcher_pw.cpp)
- else (USE_VOLUME_CATCHER_PW)
- foreach( PULSE_FILE pulse/introspect.h pulse/context.h pulse/subscribe.h pulse/glib-mainloop.h )
- find_path( PULSE_FILE_${PULSE_FILE}_FOUND ${PULSE_FILE} NO_CACHE)
- if( NOT PULSE_FILE_${PULSE_FILE}_FOUND )
- message( "Looking for ${PULSE_FILE} ... not found")
- message( FATAL_ERROR "Pulse header not found" )
- else()
- message( "Looking for ${PULSE_FILE} ... found")
- endif()
- endforeach()
- message( "Building with Linux volume catcher using PulseAudio" )
- set(LINUX_VOLUME_CATCHER linux_volume_catcher_pa.cpp)
- endif (USE_VOLUME_CATCHER_PW)
+ foreach( PULSE_FILE pulse/introspect.h pulse/context.h pulse/subscribe.h pulse/glib-mainloop.h )
+ find_path( PULSE_FILE_${PULSE_FILE}_FOUND ${PULSE_FILE} NO_CACHE)
+ if( NOT PULSE_FILE_${PULSE_FILE}_FOUND )
+ message( "Looking for ${PULSE_FILE} ... not found")
+ message( FATAL_ERROR "Pulse header not found" )
+ else()
+ message( "Looking for ${PULSE_FILE} ... found")
+ endif()
+ endforeach()
+
+ include(FindPipeWire)
+ include_directories(SYSTEM ${PIPEWIRE_INCLUDE_DIRS} ${SPA_INCLUDE_DIRS})
+
+ message( "Building with Linux volume catcher for PipeWire and PulseAudio" )
+
+ list(APPEND media_plugin_cef_HEADER_FILES
+ linux/volume_catcher_linux.h
+ )
+
+ set(LINUX_VOLUME_CATCHER
+ linux/volume_catcher_linux.cpp
+ linux/volume_catcher_pulseaudio.cpp
+ linux/volume_catcher_pipewire.cpp
+ )
list(APPEND media_plugin_cef_SOURCE_FILES ${LINUX_VOLUME_CATCHER})
set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--build-id -Wl,-rpath,'$ORIGIN:$ORIGIN/../../lib'")
diff --git a/indra/media_plugins/cef/linux/volume_catcher_linux.cpp b/indra/media_plugins/cef/linux/volume_catcher_linux.cpp
new file mode 100644
index 0000000000..86cc329e66
--- /dev/null
+++ b/indra/media_plugins/cef/linux/volume_catcher_linux.cpp
@@ -0,0 +1,103 @@
+/**
+ * @file volume_catcher.cpp
+ * @brief Linux volume catcher which will pick an implementation to use
+ *
+ * @cond
+ * $LicenseInfo:firstyear=2010&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * 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;
+ * version 2.1 of the License only.
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ * @endcond
+ */
+
+#include "volume_catcher_linux.h"
+
+#define PULSEAUDIO pimpl
+#define PIPEWIRE pimpl2
+
+////////////////////////////////////////////////////
+
+VolumeCatcher::VolumeCatcher()
+{
+ // only init once we receive which implementation to use
+
+ // debugClear();
+ // debugPrint("init volume catcher\n");
+}
+
+void VolumeCatcher::onEnablePipeWireVolumeCatcher(bool enable) {
+ if (enable) {
+ // load pipewire
+
+ if (PULSEAUDIO != nullptr) {
+ delete PULSEAUDIO;
+ PULSEAUDIO = nullptr;
+ }
+
+ if (PIPEWIRE == nullptr) {
+ // debugPrint("volume catcher using pipewire\n");
+ PIPEWIRE = new VolumeCatcherPipeWire();
+ }
+ } else {
+ // load pulseaudio
+
+ if (PIPEWIRE != nullptr) {
+ delete PIPEWIRE;
+ PIPEWIRE = nullptr;
+ }
+
+ if (PULSEAUDIO == nullptr) {
+ // debugPrint("volume catcher using pulseaudio\n");
+ PULSEAUDIO = new VolumeCatcherPulseAudio();
+ }
+ }
+}
+
+VolumeCatcher::~VolumeCatcher()
+{
+ if (PULSEAUDIO != nullptr) {
+ delete PULSEAUDIO;
+ PULSEAUDIO = nullptr;
+ }
+
+ if (PIPEWIRE != nullptr) {
+ delete PIPEWIRE;
+ PIPEWIRE = nullptr;
+ }
+}
+
+void VolumeCatcher::setVolume(F32 volume)
+{
+ if (PULSEAUDIO != nullptr) PULSEAUDIO->setVolume(volume);
+ if (PIPEWIRE != nullptr) PIPEWIRE->setVolume(volume);
+}
+
+void VolumeCatcher::setPan(F32 pan)
+{
+ // not implemented for both
+ // if (PULSEAUDIO) PULSEAUDIO->setPan(pan);
+ // if (PIPEWIRE) PIPEWIRE->setPan(pan);
+}
+
+void VolumeCatcher::pump()
+{
+ if (PULSEAUDIO != nullptr) PULSEAUDIO->pump();
+ if (PIPEWIRE != nullptr) PIPEWIRE->pump(); // doesn't need it
+}
+
diff --git a/indra/media_plugins/cef/linux/volume_catcher_linux.h b/indra/media_plugins/cef/linux/volume_catcher_linux.h
new file mode 100644
index 0000000000..5149dd62e0
--- /dev/null
+++ b/indra/media_plugins/cef/linux/volume_catcher_linux.h
@@ -0,0 +1,151 @@
+/**
+ * @file volume_catcher_impl.h
+ * @brief
+ *
+ * @cond
+ * $LicenseInfo:firstyear=2010&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * 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;
+ * version 2.1 of the License only.
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ * @endcond
+ */
+
+#ifndef VOLUME_CATCHER_LINUX_H
+#define VOLUME_CATCHER_LINUX_H
+
+#include "linden_common.h"
+
+#include "../volume_catcher.h"
+
+#include <unordered_set>
+#include <mutex>
+
+extern "C" {
+// There's no special reason why we want the *glib* PA mainloop, but the generic polling implementation seems broken.
+#include <pulse/glib-mainloop.h>
+#include <pulse/context.h>
+
+#include <pipewire/pipewire.h>
+
+#include "apr_pools.h"
+#include "apr_dso.h"
+}
+
+#include "media_plugin_base.h"
+
+class VolumeCatcherPulseAudio : public virtual VolumeCatcherImpl
+{
+public:
+ VolumeCatcherPulseAudio();
+ ~VolumeCatcherPulseAudio();
+
+ void setVolume(F32 volume);
+ void setPan(F32 pan);
+ void pump();
+
+ // for internal use - can't be private because used from our C callbacks
+
+ bool loadsyms(std::string pa_dso_name);
+ void init();
+ void cleanup();
+
+ void update_all_volumes(F32 volume);
+ void update_index_volume(U32 index, F32 volume);
+ void connected_okay();
+
+ std::set<U32> mSinkInputIndices;
+ std::map<U32,U32> mSinkInputNumChannels;
+ F32 mDesiredVolume;
+ pa_glib_mainloop *mMainloop;
+ pa_context *mPAContext;
+ bool mConnected;
+ bool mGotSyms;
+};
+
+class VolumeCatcherPipeWire : public virtual VolumeCatcherImpl
+{
+public:
+ VolumeCatcherPipeWire();
+ ~VolumeCatcherPipeWire();
+
+ bool loadsyms(std::string pw_dso_name);
+ void init();
+ void cleanup();
+
+ // some of these should be private
+
+ void pwLock();
+ void pwUnlock();
+
+ void setVolume(F32 volume);
+ void setPan(F32 pan);
+ void pump();
+
+ void handleRegistryEventGlobal(
+ uint32_t id, uint32_t permissions, const char* type,
+ uint32_t version, const struct spa_dict* props
+ );
+
+ class ChildNode
+ {
+ public:
+ bool mActive = false;
+
+ pw_proxy* mProxy = nullptr;
+ spa_hook mNodeListener {};
+ spa_hook mProxyListener {};
+ VolumeCatcherPipeWire* mImpl = nullptr;
+
+ void updateVolume();
+ void destroy();
+ };
+
+ bool mGotSyms = false;
+
+ F32 mVolume = 1.0f; // max by default
+ // F32 mPan = 0.0f; // center
+
+ pw_thread_loop* mThreadLoop;
+ pw_context* mContext;
+ pw_core* mCore;
+ pw_registry* mRegistry;
+ spa_hook mRegistryListener;
+
+ std::unordered_set<ChildNode*> mChildNodes;
+ std::mutex mChildNodesMutex;
+};
+
+// static void debugClear()
+// {
+// auto file = fopen("volume-catcher-log.txt", "w");
+// fprintf(file, "\n");
+// fclose(file);
+// }
+
+// static void debugPrint(const char* format, ...)
+// {
+// va_list args;
+// va_start(args, format);
+// auto file = fopen("volume-catcher-log.txt", "a");
+// vfprintf(file, format, args);
+// fclose(file);
+// va_end(args);
+// }
+
+#endif // VOLUME_CATCHER_LINUX_H
diff --git a/indra/media_plugins/cef/linux_volume_catcher_pw.cpp b/indra/media_plugins/cef/linux/volume_catcher_pipewire.cpp
index b7171d10f9..a779cf868e 100755
--- a/indra/media_plugins/cef/linux_volume_catcher_pw.cpp
+++ b/indra/media_plugins/cef/linux/volume_catcher_pipewire.cpp
@@ -1,5 +1,5 @@
/**
- * @file linux_volume_catcher_pw.cpp
+ * @file volume_catcher_pipewire.cpp
* @brief A Linux-specific, PipeWire-specific hack to detect and volume-adjust new audio sources
*
* @cond
@@ -37,111 +37,37 @@
#include "linden_common.h"
-#include "volume_catcher.h"
-
-#include <unordered_set>
-#include <mutex>
+#include "volume_catcher_linux.h"
extern "C" {
-#include <pipewire/pipewire.h>
#include <spa/pod/builder.h>
#include <spa/param/props.h>
-
-#include "apr_pools.h"
-#include "apr_dso.h"
}
-#include "media_plugin_base.h"
+SymbolGrabber pwSymbolGrabber;
+#undef LL_SYMBOL_GRABBER
+#define LL_SYMBOL_GRABBER pwSymbolGrabber
-SymbolGrabber gSymbolGrabber;
-
-#include "linux_volume_catcher_pw_syms.inc"
+#include "volume_catcher_pipewire_syms.inc"
////////////////////////////////////////////////////
-class VolumeCatcherImpl
-{
-public:
- VolumeCatcherImpl();
- ~VolumeCatcherImpl();
-
- bool loadsyms(std::string pw_dso_name);
- void init();
- void cleanup();
-
- void pwLock();
- void pwUnlock();
-
- void setVolume(F32 volume);
- // void setPan(F32 pan);
-
- void handleRegistryEventGlobal(
- uint32_t id, uint32_t permissions, const char* type,
- uint32_t version, const struct spa_dict* props
- );
-
- class ChildNode
- {
- public:
- bool mActive = false;
-
- pw_proxy* mProxy = nullptr;
- spa_hook mNodeListener {};
- spa_hook mProxyListener {};
- VolumeCatcherImpl* mImpl = nullptr;
-
- void updateVolume();
- void destroy();
- };
-
- bool mGotSyms = false;
-
- F32 mVolume = 1.0f; // max by default
- // F32 mPan = 0.0f; // center
-
- pw_thread_loop* mThreadLoop;
- pw_context* mContext;
- pw_core* mCore;
- pw_registry* mRegistry;
- spa_hook mRegistryListener;
-
- std::unordered_set<ChildNode*> mChildNodes;
- std::mutex mChildNodesMutex;
-};
-
-VolumeCatcherImpl::VolumeCatcherImpl()
+VolumeCatcherPipeWire::VolumeCatcherPipeWire()
{
init();
}
-VolumeCatcherImpl::~VolumeCatcherImpl()
+VolumeCatcherPipeWire::~VolumeCatcherPipeWire()
{
cleanup();
}
-// static void debugClear()
-// {
-// auto file = fopen("/home/maki/git/firestorm-viewer/log.txt", "w");
-// fprintf(file, "\n");
-// fclose(file);
-// }
-
-// static void debugPrint(const char* format, ...)
-// {
-// va_list args;
-// va_start(args, format);
-// auto file = fopen("/home/maki/git/firestorm-viewer/log.txt", "a");
-// vfprintf(file, format, args);
-// fclose(file);
-// va_end(args);
-// }
-
static void registryEventGlobal(
void *data, uint32_t id, uint32_t permissions, const char *type,
uint32_t version, const struct spa_dict *props
)
{
- static_cast<VolumeCatcherImpl*>(data)->handleRegistryEventGlobal(
+ static_cast<VolumeCatcherPipeWire*>(data)->handleRegistryEventGlobal(
id, permissions, type, version, props
);
}
@@ -151,33 +77,42 @@ static const struct pw_registry_events REGISTRY_EVENTS = {
.global = registryEventGlobal,
};
-bool VolumeCatcherImpl::loadsyms(std::string pw_dso_name)
+bool VolumeCatcherPipeWire::loadsyms(std::string pw_dso_name)
{
- return gSymbolGrabber.grabSymbols({ pw_dso_name });
+ return pwSymbolGrabber.grabSymbols({ pw_dso_name });
}
-void VolumeCatcherImpl::init()
+void VolumeCatcherPipeWire::init()
{
- // debugClear();
// debugPrint("init\n");
mGotSyms = loadsyms("libpipewire-0.3.so.0");
- if (!mGotSyms) return;
+
+ if (!mGotSyms)
+ return;
+
+ // debugPrint("got syms\n");
llpw_init(NULL, NULL);
mThreadLoop = llpw_thread_loop_new("SL Plugin Volume Adjuster", NULL);
- if (!mThreadLoop) return;
+
+ if (!mThreadLoop)
+ return;
pwLock();
mContext = llpw_context_new(
llpw_thread_loop_get_loop(mThreadLoop), NULL, 0
);
- if (!mContext) return pwUnlock();
+
+ if (!mContext)
+ return pwUnlock();
mCore = llpw_context_connect(mContext, NULL, 0);
- if (!mCore) return pwUnlock();
+
+ if (!mCore)
+ return pwUnlock();
mRegistry = pw_core_get_registry(mCore, PW_VERSION_REGISTRY, 0);
@@ -196,7 +131,7 @@ void VolumeCatcherImpl::init()
// debugPrint("started thread loop\n");
}
-void VolumeCatcherImpl::cleanup()
+void VolumeCatcherPipeWire::cleanup()
{
mChildNodesMutex.lock();
for (auto* childNode : mChildNodes) {
@@ -212,7 +147,8 @@ void VolumeCatcherImpl::cleanup()
if (mContext) llpw_context_destroy(mContext);
pwUnlock();
- if (!mThreadLoop) return;
+ if (!mThreadLoop)
+ return;
llpw_thread_loop_stop(mThreadLoop);
llpw_thread_loop_destroy(mThreadLoop);
@@ -220,13 +156,17 @@ void VolumeCatcherImpl::cleanup()
// debugPrint("cleanup done\n");
}
-void VolumeCatcherImpl::pwLock() {
- if (!mThreadLoop) return;
+void VolumeCatcherPipeWire::pwLock() {
+ if (!mThreadLoop)
+ return;
+
llpw_thread_loop_lock(mThreadLoop);
}
-void VolumeCatcherImpl::pwUnlock() {
- if (!mThreadLoop) return;
+void VolumeCatcherPipeWire::pwUnlock() {
+ if (!mThreadLoop)
+ return;
+
llpw_thread_loop_unlock(mThreadLoop);
}
@@ -234,9 +174,10 @@ void VolumeCatcherImpl::pwUnlock() {
// #include <sys/time.h>
-void VolumeCatcherImpl::ChildNode::updateVolume()
+void VolumeCatcherPipeWire::ChildNode::updateVolume()
{
- if (!mActive) return;
+ if (!mActive)
+ return;
F32 volume = std::clamp(mImpl->mVolume, 0.0f, 1.0f);
// F32 pan = std::clamp(mImpl->mPan, -1.0f, 1.0f);
@@ -274,9 +215,11 @@ void VolumeCatcherImpl::ChildNode::updateVolume()
mImpl->pwUnlock();
}
-void VolumeCatcherImpl::ChildNode::destroy()
+void VolumeCatcherPipeWire::ChildNode::destroy()
{
- if (!mActive) return;
+ if (!mActive)
+ return;
+
mActive = false;
mImpl->mChildNodesMutex.lock();
@@ -291,59 +234,23 @@ void VolumeCatcherImpl::ChildNode::destroy()
mImpl->pwUnlock();
}
-static pid_t getParentPid(pid_t aPid)
-{
- std::stringstream strm;
- strm << "/proc/" << aPid << "/status";
- std::ifstream in{ strm.str() };
-
- if (!in.is_open()) return 0;
-
- pid_t res {0};
- while (!in.eof() && res == 0)
- {
- std::string line;
- line.resize( 1024, 0 );
- in.getline( &line[0], line.length() );
-
- auto i = line.find("PPid:");
-
- if (i == std::string::npos) continue;
-
- char const *pIn = line.c_str() + 5; // Skip over pid;
- while (*pIn != 0 && isspace( *pIn )) ++pIn;
-
- if (*pIn) res = atoll(pIn);
- }
- return res;
-}
-
-static bool isPluginPid(pid_t aPid)
-{
- auto myPid = getpid();
-
- do
- {
- if(aPid == myPid) return true;
- aPid = getParentPid( aPid );
- } while(aPid > 1);
-
- return false;
-}
-
static void nodeEventInfo(void* data, const struct pw_node_info* info)
{
const char* processId = spa_dict_lookup(info->props, PW_KEY_APP_PROCESS_ID);
- if (processId == nullptr) return;
+
+ if (processId == nullptr)
+ return;
pid_t pid = atoll(processId);
- if (!isPluginPid(pid)) return;
+
+ if (!isPluginPid(pid))
+ return;
// const char* appName = spa_dict_lookup(info->props, PW_KEY_APP_NAME);
// debugPrint("got app: %s\n", appName);
- auto* const childNode = static_cast<VolumeCatcherImpl::ChildNode*>(data);
- // debugPrint("init volume to: %f\n", childNode->mImpl->mDesiredVolume);
+ auto* const childNode = static_cast<VolumeCatcherPipeWire::ChildNode*>(data);
+ // debugPrint("init volume to: %f\n", childNode->mImpl->mVolume);
childNode->updateVolume();
@@ -359,13 +266,13 @@ static const struct pw_node_events NODE_EVENTS = {
static void proxyEventDestroy(void* data)
{
- auto* const childNode = static_cast<VolumeCatcherImpl::ChildNode*>(data);
+ auto* const childNode = static_cast<VolumeCatcherPipeWire::ChildNode*>(data);
childNode->destroy();
}
static void proxyEventRemoved(void* data)
{
- auto* const childNode = static_cast<VolumeCatcherImpl::ChildNode*>(data);
+ auto* const childNode = static_cast<VolumeCatcherPipeWire::ChildNode*>(data);
childNode->destroy();
}
@@ -375,14 +282,17 @@ static const struct pw_proxy_events PROXY_EVENTS = {
.removed = proxyEventRemoved,
};
-void VolumeCatcherImpl::handleRegistryEventGlobal(
+void VolumeCatcherPipeWire::handleRegistryEventGlobal(
uint32_t id, uint32_t permissions, const char *type, uint32_t version,
const struct spa_dict *props
) {
- if (props == nullptr || strcmp(type, PW_TYPE_INTERFACE_Node) != 0) return;
+ if (props == nullptr || strcmp(type, PW_TYPE_INTERFACE_Node) != 0)
+ return;
const char* mediaClass = spa_dict_lookup(props, PW_KEY_MEDIA_CLASS);
- if (mediaClass == nullptr || strcmp(mediaClass, "Stream/Output/Audio") != 0) return;
+
+ if (mediaClass == nullptr || strcmp(mediaClass, "Stream/Output/Audio") != 0)
+ return;
pw_proxy* proxy = static_cast<pw_proxy*>(
pw_registry_bind(mRegistry, id, type, PW_VERSION_CLIENT, sizeof(ChildNode))
@@ -398,7 +308,7 @@ void VolumeCatcherImpl::handleRegistryEventGlobal(
llpw_proxy_add_listener(proxy, &childNode->mProxyListener, &PROXY_EVENTS, childNode);
}
-void VolumeCatcherImpl::setVolume(F32 volume)
+void VolumeCatcherPipeWire::setVolume(F32 volume)
{
// debugPrint("setting all volume to: %f\n", volume);
@@ -415,37 +325,12 @@ void VolumeCatcherImpl::setVolume(F32 volume)
}
}
-// void VolumeCatcherImpl::setPan(F32 pan)
-// {
-// mPan = pan;
-// setVolume(mVolume);
-// }
-
-/////////////////////////////////////////////////////
-
-VolumeCatcher::VolumeCatcher()
+void VolumeCatcherPipeWire::setPan(F32 pan)
{
- pimpl = new VolumeCatcherImpl();
+ // mPan = pan;
+ // setVolume(mVolume);
}
-VolumeCatcher::~VolumeCatcher()
+void VolumeCatcherPipeWire::pump()
{
- delete pimpl;
- pimpl = nullptr;
-}
-
-void VolumeCatcher::setVolume(F32 volume)
-{
- llassert(pimpl);
- pimpl->setVolume(volume);
-}
-
-void VolumeCatcher::setPan(F32 pan)
-{
- // llassert(pimpl);
- // pimpl->setPan(pan);
-}
-
-void VolumeCatcher::pump()
-{
-}
+} \ No newline at end of file
diff --git a/indra/media_plugins/cef/linux_volume_catcher_pw_syms.inc b/indra/media_plugins/cef/linux/volume_catcher_pipewire_syms.inc
index 14e4a42f1d..14e4a42f1d 100644
--- a/indra/media_plugins/cef/linux_volume_catcher_pw_syms.inc
+++ b/indra/media_plugins/cef/linux/volume_catcher_pipewire_syms.inc
diff --git a/indra/media_plugins/cef/linux_volume_catcher_pa.cpp b/indra/media_plugins/cef/linux/volume_catcher_pulseaudio.cpp
index daad443e44..20a6458ea3 100755
--- a/indra/media_plugins/cef/linux_volume_catcher_pa.cpp
+++ b/indra/media_plugins/cef/linux/volume_catcher_pulseaudio.cpp
@@ -1,5 +1,5 @@
/**
- * @file linux_volume_catcher_pa.cpp
+ * @file volume_catcher_pulseaudio.cpp
* @brief A Linux-specific, PulseAudio-specific hack to detect and volume-adjust new audio sources
*
* @cond
@@ -37,29 +37,24 @@
#include "linden_common.h"
-#include "volume_catcher.h"
-#include <set>
-#include <map>
-#include <iostream>
+#include "volume_catcher_linux.h"
+
extern "C" {
#include <glib.h>
#include <glib-object.h>
#include <pulse/introspect.h>
-#include <pulse/context.h>
-#include <pulse/subscribe.h>
-#include <pulse/glib-mainloop.h> // There's no special reason why we want the *glib* PA mainloop, but the generic polling implementation seems broken.
-#include "apr_pools.h"
-#include "apr_dso.h"
+#include <pulse/subscribe.h>
}
-#include "media_plugin_base.h"
+SymbolGrabber paSymbolGrabber;
+#undef LL_SYMBOL_GRABBER
+#define LL_SYMBOL_GRABBER paSymbolGrabber
-SymbolGrabber gSymbolGrabber;
+#include "volume_catcher_pulseaudio_syms.inc"
+#include "volume_catcher_pulseaudio_glib_syms.inc"
-#include "linux_volume_catcher_pa_syms.inc"
-#include "linux_volume_catcher_paglib_syms.inc"
////////////////////////////////////////////////////
// PulseAudio requires a chain of callbacks with C linkage
@@ -69,35 +64,7 @@ extern "C" {
void callback_context_state(pa_context *context, void *userdata);
}
-class VolumeCatcherImpl
-{
-public:
- VolumeCatcherImpl();
- ~VolumeCatcherImpl();
-
- void setVolume(F32 volume);
- void pump(void);
-
- // for internal use - can't be private because used from our C callbacks
-
- bool loadsyms(std::string pulse_dso_name);
- void init();
- void cleanup();
-
- void update_all_volumes(F32 volume);
- void update_index_volume(U32 index, F32 volume);
- void connected_okay();
-
- std::set<U32> mSinkInputIndices;
- std::map<U32,U32> mSinkInputNumChannels;
- F32 mDesiredVolume;
- pa_glib_mainloop *mMainloop;
- pa_context *mPAContext;
- bool mConnected;
- bool mGotSyms;
-};
-
-VolumeCatcherImpl::VolumeCatcherImpl()
+VolumeCatcherPulseAudio::VolumeCatcherPulseAudio()
: mDesiredVolume(0.0f),
mMainloop(nullptr),
mPAContext(nullptr),
@@ -107,18 +74,17 @@ VolumeCatcherImpl::VolumeCatcherImpl()
init();
}
-VolumeCatcherImpl::~VolumeCatcherImpl()
+VolumeCatcherPulseAudio::~VolumeCatcherPulseAudio()
{
cleanup();
}
-bool VolumeCatcherImpl::loadsyms(std::string pulse_dso_name)
+bool VolumeCatcherPulseAudio::loadsyms(std::string pulse_dso_name)
{
- //return grab_pa_syms({pulse_dso_name});
- return gSymbolGrabber.grabSymbols( { pulse_dso_name }) ;
+ return paSymbolGrabber.grabSymbols({ pulse_dso_name });
}
-void VolumeCatcherImpl::init()
+void VolumeCatcherPulseAudio::init()
{
// try to be as defensive as possible because PA's interface is a
// bit fragile and (for our purposes) we'd rather simply not function
@@ -130,7 +96,9 @@ void VolumeCatcherImpl::init()
// we do this sort of thing a lot with practically identical logic...
mGotSyms = loadsyms("libpulse-mainloop-glib.so.0");
if (!mGotSyms) mGotSyms = loadsyms("libpulse.so.0");
- if (!mGotSyms) return;
+
+ if (!mGotSyms)
+ return;
mMainloop = llpa_glib_mainloop_new(g_main_context_default());
@@ -176,7 +144,7 @@ void VolumeCatcherImpl::init()
}
}
-void VolumeCatcherImpl::cleanup()
+void VolumeCatcherPulseAudio::cleanup()
{
mConnected = false;
@@ -194,11 +162,12 @@ void VolumeCatcherImpl::cleanup()
mMainloop = nullptr;
}
-void VolumeCatcherImpl::setVolume(F32 volume)
+void VolumeCatcherPulseAudio::setVolume(F32 volume)
{
mDesiredVolume = volume;
- if (!mGotSyms) return;
+ if (!mGotSyms)
+ return;
if (mConnected && mPAContext)
{
@@ -208,13 +177,17 @@ void VolumeCatcherImpl::setVolume(F32 volume)
pump();
}
-void VolumeCatcherImpl::pump()
+void VolumeCatcherPulseAudio::setPan(F32 pan)
+{
+}
+
+void VolumeCatcherPulseAudio::pump()
{
gboolean may_block = FALSE;
g_main_context_iteration(g_main_context_default(), may_block);
}
-void VolumeCatcherImpl::connected_okay()
+void VolumeCatcherPulseAudio::connected_okay()
{
pa_operation *op;
@@ -238,7 +211,7 @@ void VolumeCatcherImpl::connected_okay()
}
}
-void VolumeCatcherImpl::update_all_volumes(F32 volume)
+void VolumeCatcherPulseAudio::update_all_volumes(F32 volume)
{
for (std::set<U32>::iterator it = mSinkInputIndices.begin();
it != mSinkInputIndices.end(); ++it)
@@ -247,7 +220,7 @@ void VolumeCatcherImpl::update_all_volumes(F32 volume)
}
}
-void VolumeCatcherImpl::update_index_volume(U32 index, F32 volume)
+void VolumeCatcherPulseAudio::update_index_volume(U32 index, F32 volume)
{
static pa_cvolume cvol;
llpa_cvolume_set(&cvol, mSinkInputNumChannels[index],
@@ -264,55 +237,9 @@ void VolumeCatcherImpl::update_index_volume(U32 index, F32 volume)
llpa_operation_unref(op);
}
-pid_t getParentPid( pid_t aPid )
-{
- std::stringstream strm;
- strm << "/proc/" << aPid << "/status";
- std::ifstream in{ strm.str() };
-
- if( !in.is_open() )
- return 0;
-
- pid_t res {0};
- while( !in.eof() && res == 0 )
- {
- std::string line;
- line.resize( 1024, 0 );
- in.getline( &line[0], line.length() );
-
- auto i = line.find( "PPid:" );
-
- if( i == std::string::npos )
- continue;
-
- char const *pIn = line.c_str() + 5; // Skip over pid;
- while( *pIn != 0 && isspace( *pIn ) )
- ++pIn;
-
- if( *pIn )
- res = atoll( pIn );
- }
- return res;
-}
-
-
-bool isPluginPid( pid_t aPid )
-{
- auto myPid = getpid();
-
- do
- {
- if( aPid == myPid )
- return true;
- aPid = getParentPid( aPid );
- } while( aPid > 1 );
-
- return false;
-}
-
void callback_discovered_sinkinput(pa_context *context, const pa_sink_input_info *sii, int eol, void *userdata)
{
- VolumeCatcherImpl *impl = dynamic_cast<VolumeCatcherImpl*>((VolumeCatcherImpl*)userdata);
+ VolumeCatcherPulseAudio *impl = dynamic_cast<VolumeCatcherPulseAudio*>((VolumeCatcherPulseAudio*)userdata);
llassert(impl);
if (0 == eol)
@@ -342,7 +269,7 @@ void callback_discovered_sinkinput(pa_context *context, const pa_sink_input_info
void callback_subscription_alert(pa_context *context, pa_subscription_event_type_t t, uint32_t index, void *userdata)
{
- VolumeCatcherImpl *impl = dynamic_cast<VolumeCatcherImpl*>((VolumeCatcherImpl*)userdata);
+ VolumeCatcherPulseAudio *impl = dynamic_cast<VolumeCatcherPulseAudio*>((VolumeCatcherPulseAudio*)userdata);
llassert(impl);
switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK)
@@ -375,7 +302,7 @@ void callback_subscription_alert(pa_context *context, pa_subscription_event_type
void callback_context_state(pa_context *context, void *userdata)
{
- VolumeCatcherImpl *impl = dynamic_cast<VolumeCatcherImpl*>((VolumeCatcherImpl*)userdata);
+ VolumeCatcherPulseAudio *impl = dynamic_cast<VolumeCatcherPulseAudio*>((VolumeCatcherPulseAudio*)userdata);
llassert(impl);
switch (llpa_context_get_state(context))
@@ -393,33 +320,3 @@ void callback_context_state(pa_context *context, void *userdata)
default:;
}
}
-
-/////////////////////////////////////////////////////
-
-VolumeCatcher::VolumeCatcher()
-{
- pimpl = new VolumeCatcherImpl();
-}
-
-VolumeCatcher::~VolumeCatcher()
-{
- delete pimpl;
- pimpl = nullptr;
-}
-
-void VolumeCatcher::setVolume(F32 volume)
-{
- llassert(pimpl);
- pimpl->setVolume(volume);
-}
-
-void VolumeCatcher::setPan(F32 pan)
-{
- // TODO: implement this (if possible)
-}
-
-void VolumeCatcher::pump()
-{
- llassert(pimpl);
- pimpl->pump();
-}
diff --git a/indra/media_plugins/cef/linux_volume_catcher_paglib_syms.inc b/indra/media_plugins/cef/linux/volume_catcher_pulseaudio_glib_syms.inc
index 5fba60c188..5fba60c188 100755
--- a/indra/media_plugins/cef/linux_volume_catcher_paglib_syms.inc
+++ b/indra/media_plugins/cef/linux/volume_catcher_pulseaudio_glib_syms.inc
diff --git a/indra/media_plugins/cef/linux_volume_catcher_pa_syms.inc b/indra/media_plugins/cef/linux/volume_catcher_pulseaudio_syms.inc
index 4533362e61..fe4ad084b6 100755
--- a/indra/media_plugins/cef/linux_volume_catcher_pa_syms.inc
+++ b/indra/media_plugins/cef/linux/volume_catcher_pulseaudio_syms.inc
@@ -17,5 +17,9 @@ LL_GRAB_SYM(true, pa_proplist_gets, const char*, pa_proplist *p, const char *key
LL_GRAB_SYM(true, pa_proplist_new, pa_proplist*, void)
LL_GRAB_SYM(true, pa_proplist_sets, int, pa_proplist *p, const char *key, const char *value)
LL_GRAB_SYM(true, pa_sw_volume_from_linear, pa_volume_t, double v)
+// LL_GRAB_SYM(true, pa_mainloop_free, void, pa_mainloop *m)
+// LL_GRAB_SYM(true, pa_mainloop_get_api, pa_mainloop_api *, pa_mainloop *m)
+// LL_GRAB_SYM(true, pa_mainloop_iterate, int, pa_mainloop *m, int block, int *retval)
+// LL_GRAB_SYM(true, pa_mainloop_new, pa_mainloop *, void)
// optional symbols to grab
diff --git a/indra/media_plugins/cef/media_plugin_cef.cpp b/indra/media_plugins/cef/media_plugin_cef.cpp
index 60d91753d0..3cd9e620db 100644
--- a/indra/media_plugins/cef/media_plugin_cef.cpp
+++ b/indra/media_plugins/cef/media_plugin_cef.cpp
@@ -908,6 +908,13 @@ void MediaPluginCEF::receiveMessage(const char* message_string)
{
mEnableMediaPluginDebugging = message_in.getValueBoolean("enable");
}
+#if LL_LINUX
+ else if (message_name == "enable_pipewire_volume_catcher")
+ {
+ bool enable = message_in.getValueBoolean("enable");
+ mVolumeCatcher.onEnablePipeWireVolumeCatcher(enable);
+ }
+#endif
if (message_name == "pick_file_response")
{
LLSD file_list_llsd = message_in.getValueLLSD("file_list");
diff --git a/indra/media_plugins/cef/volume_catcher.h b/indra/media_plugins/cef/volume_catcher.h
index 337f2913d3..0c644b8b42 100644
--- a/indra/media_plugins/cef/volume_catcher.h
+++ b/indra/media_plugins/cef/volume_catcher.h
@@ -31,24 +31,34 @@
#include "linden_common.h"
-class VolumeCatcherImpl;
+class VolumeCatcherImpl
+{
+public:
+ virtual void setVolume(F32 volume) = 0; // 0.0 - 1.0
+
+ // Set the left-right pan of audio sources
+ // where -1.0 = left, 0 = center, and 1.0 = right
+ virtual void setPan(F32 pan) = 0;
+
+ virtual void pump() = 0; // call this at least a few times a second if you can - it affects how quickly we can 'catch' a new audio source and adjust its volume
+};
-class VolumeCatcher
+class VolumeCatcher : public virtual VolumeCatcherImpl
{
- public:
+public:
VolumeCatcher();
~VolumeCatcher();
- void setVolume(F32 volume); // 0.0 - 1.0
-
- // Set the left-right pan of audio sources
- // where -1.0 = left, 0 = center, and 1.0 = right
- void setPan(F32 pan);
+ void setVolume(F32 volume);
+ void setPan(F32 pan);
- void pump(); // call this at least a few times a second if you can - it affects how quickly we can 'catch' a new audio source and adjust its volume
-
- private:
+ void pump();
+
+ void onEnablePipeWireVolumeCatcher(bool enable);
+
+private:
VolumeCatcherImpl *pimpl;
+ VolumeCatcherImpl *pimpl2;
};
#endif // VOLUME_CATCHER_H
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 2f7c256b49..705319ee40 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -6522,6 +6522,17 @@
<key>Value</key>
<integer>0</integer>
</map>
+ <key>MediaPluginPipeWireVolumeCatcher</key>
+ <map>
+ <key>Comment</key>
+ <string>Use PipeWire instead of PulseAudio for controlling web media volume.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>0</integer>
+ </map>
<key>MediaControlFadeTime</key>
<map>
<key>Comment</key>
diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp
index 3791d1e754..601a508f9b 100644
--- a/indra/newview/llviewermedia.cpp
+++ b/indra/newview/llviewermedia.cpp
@@ -1772,6 +1772,11 @@ LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_
bool media_plugin_debugging_enabled = gSavedSettings.getBOOL("MediaPluginDebugging");
media_source->enableMediaPluginDebugging( media_plugin_debugging_enabled || clean_browser);
+#if LL_LINUX
+ bool media_plugin_pipewire_volume_catcher = gSavedSettings.getBOOL("MediaPluginPipeWireVolumeCatcher");
+ media_source->enablePipeWireVolumeCatcher( media_plugin_pipewire_volume_catcher );
+#endif
+
// need to set agent string here before instance created
media_source->setBrowserUserAgent(LLViewerMedia::getInstance()->getCurrentUserAgent());