diff options
Diffstat (limited to 'indra')
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()); | 
