summaryrefslogtreecommitdiff
path: root/indra
diff options
context:
space:
mode:
authorRye <rye@alchemyviewer.org>2025-10-23 05:47:55 -0400
committerRye <rye@alchemyviewer.org>2025-10-27 04:59:13 -0400
commit348974089c6f5f441deda9cec028210b5a298d50 (patch)
tree3083d4feeaa8bf5fc5dd009801aab0086a837b94 /indra
parentc950cb3b49e1810adec0b7f7a9afadce71ce49a7 (diff)
Restore linux CEF build support including changes from archive/develop
Fix various files missing linden_common Signed-off-by: Rye <rye@alchemyviewer.org>
Diffstat (limited to 'indra')
-rw-r--r--indra/cmake/FindPipeWire.cmake100
-rw-r--r--indra/cmake/LibVLCPlugin.cmake35
-rw-r--r--indra/llcommon/llatomic.cpp2
-rw-r--r--indra/llcommon/llcallbacklist.cpp2
-rw-r--r--indra/llcommon/lldeadmantimer.cpp1
-rw-r--r--indra/llcommon/llprocinfo.cpp1
-rw-r--r--indra/llcommon/llprofiler.cpp2
-rw-r--r--indra/llcommon/llprofiler.h9
-rw-r--r--indra/llfilesystem/lldiriterator.cpp2
-rw-r--r--indra/llplugin/llpluginclassmedia.cpp9
-rw-r--r--indra/llplugin/llpluginclassmedia.h4
-rw-r--r--indra/media_plugins/cef/CMakeLists.txt63
-rw-r--r--indra/media_plugins/cef/linux/volume_catcher_linux.cpp78
-rw-r--r--indra/media_plugins/cef/linux/volume_catcher_linux.h149
-rwxr-xr-xindra/media_plugins/cef/linux/volume_catcher_pipewire.cpp333
-rw-r--r--indra/media_plugins/cef/linux/volume_catcher_pipewire_syms.inc26
-rwxr-xr-xindra/media_plugins/cef/linux/volume_catcher_pulseaudio.cpp322
-rwxr-xr-xindra/media_plugins/cef/linux/volume_catcher_pulseaudio_glib_syms.inc10
-rwxr-xr-xindra/media_plugins/cef/linux/volume_catcher_pulseaudio_syms.inc29
-rw-r--r--indra/media_plugins/cef/media_plugin_cef.cpp41
-rw-r--r--indra/media_plugins/cef/volume_catcher.h10
-rw-r--r--indra/media_plugins/cef/volume_catcher_null.cpp (renamed from indra/media_plugins/cef/mac_volume_catcher_null.cpp)50
-rw-r--r--indra/media_plugins/cef/windows_volume_catcher.cpp1
-rw-r--r--indra/newview/app_settings/settings.xml11
-rw-r--r--indra/newview/llviewermedia.cpp9
-rw-r--r--indra/newview/skins/default/xui/en/mime_types_linux.xml70
-rwxr-xr-xindra/newview/viewer_manifest.py62
27 files changed, 1300 insertions, 131 deletions
diff --git a/indra/cmake/FindPipeWire.cmake b/indra/cmake/FindPipeWire.cmake
new file mode 100644
index 0000000000..868acf5ec1
--- /dev/null
+++ b/indra/cmake/FindPipeWire.cmake
@@ -0,0 +1,100 @@
+# cmake-format: off
+# .rst: FindPipeWire
+# -------
+#
+# Try to find PipeWire on a Unix system.
+#
+# This will define the following variables:
+#
+# ``PIPEWIRE_FOUND`` True if (the requested version of) PipeWire is available
+# ``PIPEWIRE_VERSION`` The version of PipeWire ``PIPEWIRE_LIBRARIES`` This can
+# be passed to target_link_libraries() instead of the ``PipeWire::PipeWire``
+# target ``PIPEWIRE_INCLUDE_DIRS`` This should be passed to
+# target_include_directories() if the target is not used for linking
+# ``PIPEWIRE_COMPILE_FLAGS`` This should be passed to target_compile_options()
+# if the target is not used for linking
+#
+# If ``PIPEWIRE_FOUND`` is TRUE, it will also define the following imported
+# target:
+#
+# ``PipeWire::PipeWire`` The PipeWire library
+#
+# In general we recommend using the imported target, as it is easier to use.
+# Bear in mind, however, that if the target is in the link interface of an
+# exported library, it must be made available by the package config file.
+
+# =============================================================================
+# Copyright 2014 Alex Merry <alex.merry@kde.org> Copyright 2014 Martin Gräßlin
+# <mgraesslin@kde.org> Copyright 2018-2020 Jan Grulich <jgrulich@redhat.com>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the copyright notice, this list
+# of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the copyright notice, this
+# list of conditions and the following disclaimer in the documentation and/or
+# other materials provided with the distribution.
+# 3. The name of the author may not be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+# EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+# =============================================================================
+# cmake-format: on
+
+# Use pkg-config to get the directories and then use these values in the FIND_PATH() and FIND_LIBRARY() calls
+find_package(PkgConfig QUIET)
+
+pkg_search_module(PKG_PIPEWIRE QUIET libpipewire-0.3)
+pkg_search_module(PKG_SPA QUIET libspa-0.2)
+
+set(PIPEWIRE_COMPILE_FLAGS "${PKG_PIPEWIRE_CFLAGS}" "${PKG_SPA_CFLAGS}")
+set(PIPEWIRE_VERSION "${PKG_PIPEWIRE_VERSION}")
+
+find_path(
+ PIPEWIRE_INCLUDE_DIRS
+ NAMES pipewire/pipewire.h
+ HINTS ${PKG_PIPEWIRE_INCLUDE_DIRS} ${PKG_PIPEWIRE_INCLUDE_DIRS}/pipewire-0.3)
+
+find_path(
+ SPA_INCLUDE_DIRS
+ NAMES spa/param/props.h
+ HINTS ${PKG_SPA_INCLUDE_DIRS} ${PKG_SPA_INCLUDE_DIRS}/spa-0.2)
+
+find_library(
+ PIPEWIRE_LIBRARIES
+ NAMES pipewire-0.3
+ HINTS ${PKG_PIPEWIRE_LIBRARY_DIRS})
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(
+ PipeWire
+ FOUND_VAR PIPEWIRE_FOUND
+ REQUIRED_VARS PIPEWIRE_LIBRARIES PIPEWIRE_INCLUDE_DIRS SPA_INCLUDE_DIRS
+ VERSION_VAR PIPEWIRE_VERSION)
+
+if(PIPEWIRE_FOUND AND NOT TARGET PipeWire::PipeWire)
+ add_library(PipeWire::PipeWire UNKNOWN IMPORTED)
+ set_target_properties(
+ PipeWire::PipeWire
+ PROPERTIES IMPORTED_LOCATION "${PIPEWIRE_LIBRARIES}"
+ INTERFACE_COMPILE_OPTIONS "${PIPEWIRE_COMPILE_FLAGS}"
+ INTERFACE_INCLUDE_DIRECTORIES "${PIPEWIRE_INCLUDE_DIRS};${SPA_INCLUDE_DIRS}")
+endif()
+
+mark_as_advanced(PIPEWIRE_LIBRARIES PIPEWIRE_INCLUDE_DIRS)
+
+include(FeatureSummary)
+set_package_properties(
+ PipeWire PROPERTIES
+ URL "https://www.pipewire.org"
+ DESCRIPTION "PipeWire - multimedia processing")
diff --git a/indra/cmake/LibVLCPlugin.cmake b/indra/cmake/LibVLCPlugin.cmake
index 6361028c0c..a38c660a33 100644
--- a/indra/cmake/LibVLCPlugin.cmake
+++ b/indra/cmake/LibVLCPlugin.cmake
@@ -5,20 +5,31 @@ include(Prebuilt)
include_guard()
add_library( ll::libvlc INTERFACE IMPORTED )
-use_prebuilt_binary(vlc-bin)
set(LIBVLCPLUGIN ON CACHE BOOL
"LIBVLCPLUGIN support for the llplugin/llmedia test apps.")
-find_library(VLC_LIBRARY
- NAMES
- libvlc.lib
- libvlc.dylib
- PATHS "${ARCH_PREBUILT_DIRS_RELEASE}" REQUIRED NO_DEFAULT_PATH)
+if (LIBVLCPLUGIN)
+ if(LINUX)
+ find_package(PkgConfig REQUIRED)
-find_library(VLCCORE_LIBRARY
- NAMES
- libvlccore.lib
- libvlccore.dylib
- PATHS "${ARCH_PREBUILT_DIRS_RELEASE}" REQUIRED NO_DEFAULT_PATH)
+ pkg_check_modules(libvlc REQUIRED IMPORTED_TARGET libvlc)
+ target_link_libraries( ll::libvlc INTERFACE PkgConfig::libvlc)
+ return()
+ endif()
-target_link_libraries(ll::libvlc INTERFACE ${VLC_LIBRARY} ${VLCCORE_LIBRARY})
+ use_prebuilt_binary(vlc-bin)
+
+ find_library(VLC_LIBRARY
+ NAMES
+ libvlc.lib
+ libvlc.dylib
+ PATHS "${ARCH_PREBUILT_DIRS_RELEASE}" REQUIRED NO_DEFAULT_PATH)
+
+ find_library(VLCCORE_LIBRARY
+ NAMES
+ libvlccore.lib
+ libvlccore.dylib
+ PATHS "${ARCH_PREBUILT_DIRS_RELEASE}" REQUIRED NO_DEFAULT_PATH)
+
+ target_link_libraries(ll::libvlc INTERFACE ${VLC_LIBRARY} ${VLCCORE_LIBRARY})
+endif()
diff --git a/indra/llcommon/llatomic.cpp b/indra/llcommon/llatomic.cpp
index d200bb0406..cff5e01225 100644
--- a/indra/llcommon/llatomic.cpp
+++ b/indra/llcommon/llatomic.cpp
@@ -23,6 +23,8 @@
* $/LicenseInfo$
*/
+#include "linden_common.h"
+
#include "llatomic.h"
//============================================================================
diff --git a/indra/llcommon/llcallbacklist.cpp b/indra/llcommon/llcallbacklist.cpp
index 3d5d30bd90..8fb7823f04 100644
--- a/indra/llcommon/llcallbacklist.cpp
+++ b/indra/llcommon/llcallbacklist.cpp
@@ -24,6 +24,8 @@
* $/LicenseInfo$
*/
+#include "linden_common.h"
+
#include "llcallbacklist.h"
#include "lleventtimer.h"
#include "llerrorlegacy.h"
diff --git a/indra/llcommon/lldeadmantimer.cpp b/indra/llcommon/lldeadmantimer.cpp
index f9c14d7c24..45c01eb84a 100644
--- a/indra/llcommon/lldeadmantimer.cpp
+++ b/indra/llcommon/lldeadmantimer.cpp
@@ -25,6 +25,7 @@
* $/LicenseInfo$
*/
+#include "linden_common.h"
#include "lldeadmantimer.h"
diff --git a/indra/llcommon/llprocinfo.cpp b/indra/llcommon/llprocinfo.cpp
index 69be00e14a..9fe4af2103 100644
--- a/indra/llcommon/llprocinfo.cpp
+++ b/indra/llcommon/llprocinfo.cpp
@@ -25,6 +25,7 @@
* $/LicenseInfo$
*/
+#include "linden_common.h"
#include "llprocinfo.h"
diff --git a/indra/llcommon/llprofiler.cpp b/indra/llcommon/llprofiler.cpp
index bdddabf977..b2ac7723d4 100644
--- a/indra/llcommon/llprofiler.cpp
+++ b/indra/llcommon/llprofiler.cpp
@@ -25,6 +25,6 @@
* $/LicenseInfo$
*/
-#include "linden_common.h"
+#include "llpreprocessor.h"
#include "TracyClient.cpp"
diff --git a/indra/llcommon/llprofiler.h b/indra/llcommon/llprofiler.h
index 5fb32d6280..7c77d94475 100644
--- a/indra/llcommon/llprofiler.h
+++ b/indra/llcommon/llprofiler.h
@@ -76,7 +76,16 @@
#if defined(LL_PROFILER_CONFIGURATION) && (LL_PROFILER_CONFIGURATION > LL_PROFILER_CONFIG_NONE)
#if LL_PROFILER_CONFIGURATION == LL_PROFILER_CONFIG_TRACY || LL_PROFILER_CONFIGURATION == LL_PROFILER_CONFIG_TRACY_FAST_TIMER
+ #include "llpreprocessor.h"
+
+#if defined(LL_GNUC) && GCC_VERSION >= 130000
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wnonnull"
+#endif
#include "tracy/Tracy.hpp"
+#if defined(LL_GNUC) && GCC_VERSION >= 130000
+# pragma GCC diagnostic push
+#endif
// Enable RenderDoc labeling
//#define LL_PROFILER_ENABLE_RENDER_DOC 0
diff --git a/indra/llfilesystem/lldiriterator.cpp b/indra/llfilesystem/lldiriterator.cpp
index e8c37389d2..b736a577bd 100644
--- a/indra/llfilesystem/lldiriterator.cpp
+++ b/indra/llfilesystem/lldiriterator.cpp
@@ -24,6 +24,8 @@
* $/LicenseInfo$
*/
+#include "linden_common.h"
+
#include "lldiriterator.h"
#include "fix_macros.h"
diff --git a/indra/llplugin/llpluginclassmedia.cpp b/indra/llplugin/llpluginclassmedia.cpp
index 77a4b08af5..68c8854a12 100644
--- a/indra/llplugin/llpluginclassmedia.cpp
+++ b/indra/llplugin/llpluginclassmedia.cpp
@@ -1015,6 +1015,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 71522bcd7d..bfede4e9c1 100644
--- a/indra/llplugin/llpluginclassmedia.h
+++ b/indra/llplugin/llpluginclassmedia.h
@@ -134,6 +134,10 @@ public:
// Text may be unicode (utf8 encoded)
bool textInput(const std::string &text, MASK modifiers, LLSD native_key_data);
+#if LL_LINUX
+ void enablePipeWireVolumeCatcher( bool enable );
+#endif
+
static std::string sOIDcookieUrl;
static std::string sOIDcookieName;
static std::string sOIDcookieValue;
diff --git a/indra/media_plugins/cef/CMakeLists.txt b/indra/media_plugins/cef/CMakeLists.txt
index dc2d82017d..3a91e398d5 100644
--- a/indra/media_plugins/cef/CMakeLists.txt
+++ b/indra/media_plugins/cef/CMakeLists.txt
@@ -10,7 +10,7 @@ include(Linking)
include(PluginAPI)
include(CEFPlugin)
-
+include(GLIB)
### media_plugin_cef
@@ -23,20 +23,38 @@ set(media_plugin_cef_HEADER_FILES
)
# Select which VolumeCatcher implementation to use
-if (LINUX)
- message(FATAL_ERROR "CEF plugin has been enabled for a Linux compile.\n"
- " Please create a volume_catcher implementation for this platform.")
-elseif (DARWIN)
- list(APPEND media_plugin_cef_SOURCE_FILES mac_volume_catcher_null.cpp)
- find_library(CORESERVICES_LIBRARY CoreServices)
- find_library(AUDIOUNIT_LIBRARY AudioUnit)
- set( media_plugin_cef_LINK_LIBRARIES
- ${CORESERVICES_LIBRARY} # for Component Manager calls
- ${AUDIOUNIT_LIBRARY} # for AudioUnit calls
- )
+if (FALSE) # Disable linux volume catcher due to issues dlopening with CEF 139
+ 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})
elseif (WINDOWS)
list(APPEND media_plugin_cef_SOURCE_FILES windows_volume_catcher.cpp)
-endif (LINUX)
+else()
+ list(APPEND media_plugin_cef_SOURCE_FILES volume_catcher_null.cpp)
+endif ()
list(APPEND media_plugin_cef_SOURCE_FILES ${media_plugin_cef_HEADER_FILES})
@@ -45,13 +63,10 @@ add_library(media_plugin_cef
${media_plugin_cef_SOURCE_FILES}
)
-#add_dependencies(media_plugin_cef
-# ${MEDIA_PLUGIN_BASE_LIBRARIES}
-#)
-
target_link_libraries(media_plugin_cef
media_plugin_base
ll::cef
+ ll::glib_headers
)
if (WINDOWS)
@@ -60,9 +75,15 @@ if (WINDOWS)
PROPERTIES
LINK_FLAGS "/MANIFEST:NO /NODEFAULTLIB:LIBCMT /IGNORE:4099"
)
-endif (WINDOWS)
+elseif (DARWIN)
+ find_library(CORESERVICES_LIBRARY CoreServices)
+ find_library(AUDIOUNIT_LIBRARY AudioUnit)
+
+ target_link_libraries(media_plugin_cef
+ ${CORESERVICES_LIBRARY} # for Component Manager calls
+ ${AUDIOUNIT_LIBRARY} # for AudioUnit calls
+ )
-if (DARWIN)
# Don't prepend 'lib' to the executable name, and don't embed a full path in the library's install name
set_target_properties(
media_plugin_cef
@@ -73,4 +94,6 @@ if (DARWIN)
LINK_FLAGS "-exported_symbols_list ${CMAKE_CURRENT_SOURCE_DIR}/../base/media_plugin_base.exp"
)
-endif (DARWIN)
+elseif (LINUX)
+ target_link_options(media_plugin_cef PRIVATE "LINKER:--build-id" "LINKER:-rpath,'$ORIGIN:$ORIGIN/../../lib'")
+endif ()
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..7d33242063
--- /dev/null
+++ b/indra/media_plugins/cef/linux/volume_catcher_linux.cpp
@@ -0,0 +1,78 @@
+/**
+ * @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"
+
+VolumeCatcher::VolumeCatcher()
+{
+}
+
+void VolumeCatcher::onEnablePipeWireVolumeCatcher(bool enable)
+{
+ if (pimpl != nullptr)
+ return;
+
+ if (enable)
+ {
+ LL_DEBUGS() << "volume catcher using pipewire" << LL_ENDL;
+ pimpl = new VolumeCatcherPipeWire();
+ }
+ else
+ {
+ LL_DEBUGS() << "volume catcher using pulseaudio" << LL_ENDL;
+ pimpl = new VolumeCatcherPulseAudio();
+ }
+}
+
+VolumeCatcher::~VolumeCatcher()
+{
+ if (pimpl != nullptr)
+ {
+ delete pimpl;
+ pimpl = nullptr;
+ }
+}
+
+void VolumeCatcher::setVolume(F32 volume)
+{
+ if (pimpl != nullptr) {
+ pimpl->setVolume(volume);
+ }
+}
+
+void VolumeCatcher::setPan(F32 pan)
+{
+ if (pimpl != nullptr)
+ pimpl->setPan(pan);
+}
+
+void VolumeCatcher::pump()
+{
+ if (pimpl != nullptr)
+ pimpl->pump();
+}
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..505f9ffb31
--- /dev/null
+++ b/indra/media_plugins/cef/linux/volume_catcher_linux.h
@@ -0,0 +1,149 @@
+/**
+ * @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 VolumeCatcherImpl
+{
+public:
+ virtual ~VolumeCatcherImpl() = default;
+
+ 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 VolumeCatcherPulseAudio : public 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 VolumeCatcherImpl
+{
+public:
+ VolumeCatcherPipeWire();
+ ~VolumeCatcherPipeWire();
+
+ bool loadsyms(std::string pw_dso_name);
+ void init();
+ void cleanup();
+
+ // some of these should be private
+
+ void lock();
+ void unlock();
+
+ 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 = nullptr;
+ pw_context* mContext = nullptr;
+ pw_core* mCore = nullptr;
+ pw_registry* mRegistry = nullptr;
+ spa_hook mRegistryListener;
+
+ std::unordered_set<ChildNode*> mChildNodes;
+ std::mutex mChildNodesMutex;
+ std::mutex mCleanupMutex;
+};
+
+#endif // VOLUME_CATCHER_LINUX_H
diff --git a/indra/media_plugins/cef/linux/volume_catcher_pipewire.cpp b/indra/media_plugins/cef/linux/volume_catcher_pipewire.cpp
new file mode 100755
index 0000000000..73ef7fc18c
--- /dev/null
+++ b/indra/media_plugins/cef/linux/volume_catcher_pipewire.cpp
@@ -0,0 +1,333 @@
+/**
+ * @file volume_catcher_pipewire.cpp
+ * @brief A Linux-specific, PipeWire-specific hack to detect and volume-adjust new audio sources
+ *
+ * @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
+ */
+
+/*
+ The high-level design is as follows:
+ 1) Connect to the PipeWire daemon
+ 2) Find all existing and new audio nodes
+ 3) Examine PID and parent PID's to see if it belongs to our process
+ 4) If so, tell PipeWire to adjust the volume of that node
+ 5) Keep a list of all audio nodes and adjust when we setVolume()
+ */
+
+#include "linden_common.h"
+
+#include "volume_catcher_linux.h"
+
+extern "C" {
+#include <spa/pod/builder.h>
+#include <spa/param/props.h>
+}
+
+SymbolGrabber pwSymbolGrabber;
+
+#include "volume_catcher_pipewire_syms.inc"
+
+////////////////////////////////////////////////////
+
+VolumeCatcherPipeWire::VolumeCatcherPipeWire()
+{
+ init();
+}
+
+VolumeCatcherPipeWire::~VolumeCatcherPipeWire()
+{
+ cleanup();
+}
+
+static void registryEventGlobal(
+ void *data, uint32_t id, uint32_t permissions, const char *type,
+ uint32_t version, const struct spa_dict *props)
+{
+ static_cast<VolumeCatcherPipeWire*>(data)->handleRegistryEventGlobal(
+ id, permissions, type, version, props
+ );
+}
+
+static const struct pw_registry_events REGISTRY_EVENTS = {
+ .version = PW_VERSION_REGISTRY_EVENTS,
+ .global = registryEventGlobal,
+};
+
+bool VolumeCatcherPipeWire::loadsyms(std::string pw_dso_name)
+{
+ return pwSymbolGrabber.grabSymbols({ pw_dso_name });
+}
+
+void VolumeCatcherPipeWire::init()
+{
+ LL_DEBUGS() << "init" << LL_ENDL;
+
+ mGotSyms = loadsyms("libpipewire-0.3.so.0");
+
+ if (!mGotSyms)
+ return;
+
+ LL_DEBUGS() << "successfully got symbols" << LL_ENDL;
+
+ llpw_init(nullptr, nullptr);
+
+ mThreadLoop = llpw_thread_loop_new("SL Plugin Volume Adjuster", nullptr);
+
+ if (!mThreadLoop)
+ return;
+
+ // i dont think we need to lock this early
+ // std::lock_guard pwLock(*this);
+
+ mContext = llpw_context_new(
+ llpw_thread_loop_get_loop(mThreadLoop), nullptr, 0
+ );
+
+ if (!mContext)
+ return;
+
+ mCore = llpw_context_connect(mContext, nullptr, 0);
+
+ if (!mCore)
+ return;
+
+ mRegistry = pw_core_get_registry(mCore, PW_VERSION_REGISTRY, 0);
+
+ LL_DEBUGS() << "pw_core_get_registry: " << (mRegistry?"success":"nullptr") << LL_ENDL;
+
+ spa_zero(mRegistryListener);
+
+ pw_registry_add_listener(
+ mRegistry, &mRegistryListener, &REGISTRY_EVENTS, this
+ );
+
+ llpw_thread_loop_start(mThreadLoop);
+
+ LL_DEBUGS() << "thread loop started" << LL_ENDL;
+}
+
+void VolumeCatcherPipeWire::cleanup()
+{
+ {
+ std::unique_lock childNodesLock(mChildNodesMutex);
+ for (auto *childNode: mChildNodes)
+ childNode->destroy();
+
+ mChildNodes.clear();
+ }
+
+ {
+ std::unique_lock pwLock(mCleanupMutex);
+ if (mRegistry)
+ llpw_proxy_destroy((struct pw_proxy *) mRegistry);
+
+ spa_zero(mRegistryListener);
+
+ if (mCore)
+ llpw_core_disconnect(mCore);
+ if (mContext)
+ llpw_context_destroy(mContext);
+ }
+
+ if (!mThreadLoop)
+ return;
+
+ llpw_thread_loop_stop(mThreadLoop);
+ llpw_thread_loop_destroy(mThreadLoop);
+
+ LL_DEBUGS() << "cleanup done" << LL_ENDL;
+}
+
+void VolumeCatcherPipeWire::lock()
+{
+ if (!mThreadLoop)
+ return;
+
+ llpw_thread_loop_lock(mThreadLoop);
+}
+
+void VolumeCatcherPipeWire::unlock()
+{
+ if (!mThreadLoop)
+ return;
+
+ llpw_thread_loop_unlock(mThreadLoop);
+}
+
+const uint32_t channels = 1;
+const float resetVolumes[channels] = { 1.0f };
+
+void VolumeCatcherPipeWire::ChildNode::updateVolume()
+{
+ if (!mActive)
+ return;
+
+ F32 volume = std::clamp(mImpl->mVolume, 0.0f, 1.0f);
+
+ const float volumes[channels] = { volume };
+
+ uint8_t buffer[512];
+
+ spa_pod_builder builder;
+ spa_pod_builder_init(&builder, buffer, sizeof(buffer));
+
+ spa_pod_frame frame;
+ spa_pod_builder_push_object(&builder, &frame, SPA_TYPE_OBJECT_Props, SPA_PARAM_Props);
+
+ // resets system-wide memorized volume for chromium (not google chrome) to 100%
+ spa_pod_builder_prop(&builder, SPA_PROP_channelVolumes, 0);
+ spa_pod_builder_array(&builder, sizeof(float), SPA_TYPE_Float, channels, resetVolumes);
+
+ // sets temporary volume
+ spa_pod_builder_prop(&builder, SPA_PROP_softVolumes, 0);
+ spa_pod_builder_array(&builder, sizeof(float), SPA_TYPE_Float, channels, volumes);
+
+ spa_pod* pod = static_cast<spa_pod*>(spa_pod_builder_pop(&builder, &frame));
+
+ {
+ std::lock_guard pwLock(*mImpl);
+ pw_node_set_param((pw_node*)mProxy, SPA_PARAM_Props, 0, pod);
+ }
+}
+
+void VolumeCatcherPipeWire::ChildNode::destroy()
+{
+ if (!mActive)
+ return;
+
+ mActive = false;
+
+ {
+ std::unique_lock childNodesLock(mImpl->mChildNodesMutex);
+ mImpl->mChildNodes.erase(this);
+ }
+
+ spa_hook_remove(&mNodeListener);
+ spa_hook_remove(&mProxyListener);
+
+ {
+ std::lock_guard pwLock(*mImpl);
+ llpw_proxy_destroy(mProxy);
+ }
+}
+
+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;
+
+ pid_t pid = atoll(processId);
+
+ if (!isPluginPid(pid))
+ return;
+
+ const char* appName = spa_dict_lookup(info->props, PW_KEY_APP_NAME);
+ LL_DEBUGS() << "got app: " << appName << LL_ENDL;
+
+ auto* const childNode = static_cast<VolumeCatcherPipeWire::ChildNode*>(data);
+ LL_DEBUGS() << "init volume: " << childNode->mImpl->mVolume << LL_ENDL;
+
+ childNode->updateVolume();
+
+ {
+ std::lock_guard childNodesLock(childNode->mImpl->mChildNodesMutex);
+ childNode->mImpl->mChildNodes.insert(childNode);
+ }
+}
+
+static const struct pw_node_events NODE_EVENTS = {
+ .version = PW_VERSION_CLIENT_EVENTS,
+ .info = nodeEventInfo,
+};
+
+static void proxyEventDestroy(void* data)
+{
+ auto* const childNode = static_cast<VolumeCatcherPipeWire::ChildNode*>(data);
+ childNode->destroy();
+}
+
+static void proxyEventRemoved(void* data)
+{
+ auto* const childNode = static_cast<VolumeCatcherPipeWire::ChildNode*>(data);
+ childNode->destroy();
+}
+
+static const struct pw_proxy_events PROXY_EVENTS = {
+ .version = PW_VERSION_PROXY_EVENTS,
+ .destroy = proxyEventDestroy,
+ .removed = proxyEventRemoved,
+};
+
+void VolumeCatcherPipeWire::handleRegistryEventGlobal(
+ uint32_t id, uint32_t permissions, const char *type, uint32_t version,
+ const struct spa_dict *props)
+{
+ if (props == nullptr || type == 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;
+
+ pw_proxy* proxy = static_cast<pw_proxy*>(
+ pw_registry_bind(mRegistry, id, type, PW_VERSION_CLIENT, sizeof(ChildNode))
+ );
+
+ auto* const childNode = static_cast<ChildNode*>(llpw_proxy_get_user_data(proxy));
+
+ childNode->mActive = true;
+ childNode->mProxy = proxy;
+ childNode->mImpl = this;
+
+ pw_node_add_listener((pw_node*)proxy, &childNode->mNodeListener, &NODE_EVENTS, childNode);
+ llpw_proxy_add_listener(proxy, &childNode->mProxyListener, &PROXY_EVENTS, childNode);
+}
+
+void VolumeCatcherPipeWire::setVolume(F32 volume)
+{
+ LL_DEBUGS() << "setting volume to: " << volume << LL_ENDL;
+
+ mVolume = volume;
+
+ {
+ std::unique_lock childNodeslock(mChildNodesMutex);
+ std::unordered_set<ChildNode *> copyOfChildNodes(mChildNodes);
+
+ LL_DEBUGS() << "found " << copyOfChildNodes.size() << " child nodes" << LL_ENDL;
+
+ for (auto* childNode : copyOfChildNodes)
+ childNode->updateVolume();
+ }
+}
+
+void VolumeCatcherPipeWire::setPan(F32 pan)
+{
+}
+
+void VolumeCatcherPipeWire::pump()
+{
+}
diff --git a/indra/media_plugins/cef/linux/volume_catcher_pipewire_syms.inc b/indra/media_plugins/cef/linux/volume_catcher_pipewire_syms.inc
new file mode 100644
index 0000000000..dbc0f5f169
--- /dev/null
+++ b/indra/media_plugins/cef/linux/volume_catcher_pipewire_syms.inc
@@ -0,0 +1,26 @@
+#define G pwSymbolGrabber
+
+// required symbols to grab
+LL_GRAB_SYM(G, true, pw_init, void, int *argc, char **argv[]);
+// LL_GRAB_SYM(G, true, pw_main_loop_new, struct pw_main_loop *, const struct spa_dict *props);
+// LL_GRAB_SYM(G, true, pw_main_loop_get_loop, struct pw_loop *, struct pw_main_loop *loop);
+// LL_GRAB_SYM(G, true, pw_main_loop_destroy, void, struct pw_main_loop *loop);
+// LL_GRAB_SYM(G, true, pw_main_loop_run, void, struct pw_main_loop *loop);
+LL_GRAB_SYM(G, true, pw_context_new, struct pw_context *, struct pw_loop *main_loop, struct pw_properties *props, size_t user_data_size);
+LL_GRAB_SYM(G, true, pw_context_destroy, void, struct pw_context *context);
+LL_GRAB_SYM(G, true, pw_context_connect, struct pw_core *, struct pw_context *context, struct pw_properties *properties, size_t user_data_size);
+LL_GRAB_SYM(G, true, pw_thread_loop_new, struct pw_thread_loop *, const char *name, const struct spa_dict *props);
+LL_GRAB_SYM(G, true, pw_thread_loop_destroy, void, struct pw_thread_loop *loop);
+LL_GRAB_SYM(G, true, pw_thread_loop_get_loop, struct pw_loop *, struct pw_thread_loop *loop);
+LL_GRAB_SYM(G, true, pw_thread_loop_start, int, struct pw_thread_loop *loop);
+LL_GRAB_SYM(G, true, pw_thread_loop_stop, void, struct pw_thread_loop *loop);
+LL_GRAB_SYM(G, true, pw_thread_loop_lock, void, struct pw_thread_loop *loop);
+LL_GRAB_SYM(G, true, pw_thread_loop_unlock, void, struct pw_thread_loop *loop);
+LL_GRAB_SYM(G, true, pw_proxy_add_listener, void, struct pw_proxy *proxy, struct spa_hook *listener, const struct pw_proxy_events *events, void *data);
+LL_GRAB_SYM(G, true, pw_proxy_destroy, void, struct pw_proxy *proxy);
+LL_GRAB_SYM(G, true, pw_proxy_get_user_data, void *, struct pw_proxy *proxy);
+LL_GRAB_SYM(G, true, pw_core_disconnect, int, struct pw_core *core);
+
+// optional symbols to grab
+
+#undef G
diff --git a/indra/media_plugins/cef/linux/volume_catcher_pulseaudio.cpp b/indra/media_plugins/cef/linux/volume_catcher_pulseaudio.cpp
new file mode 100755
index 0000000000..f8a48a91fd
--- /dev/null
+++ b/indra/media_plugins/cef/linux/volume_catcher_pulseaudio.cpp
@@ -0,0 +1,322 @@
+/**
+ * @file volume_catcher_pulseaudio.cpp
+ * @brief A Linux-specific, PulseAudio-specific hack to detect and volume-adjust new audio sources
+ *
+ * @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
+ */
+
+/*
+ The high-level design is as follows:
+ 1) Connect to the PulseAudio daemon
+ 2) Watch for the creation of new audio players connecting to the daemon (this includes ALSA clients running on the PulseAudio emulation layer, such as Flash plugins)
+ 3) Examine any new audio player's PID to see if it belongs to our own process
+ 4) If so, tell PA to adjust the volume of that audio player ('sink input' in PA parlance)
+ 5) Keep a list of all living audio players that we care about, adjust the volumes of all of them when we get a new setVolume() call
+ */
+
+#include "linden_common.h"
+
+#include "volume_catcher_linux.h"
+
+extern "C" {
+#include <glib.h>
+#include <glib-object.h>
+
+#include <pulse/introspect.h>
+
+#include <pulse/subscribe.h>
+}
+
+SymbolGrabber paSymbolGrabber;
+
+#include "volume_catcher_pulseaudio_syms.inc"
+#include "volume_catcher_pulseaudio_glib_syms.inc"
+
+////////////////////////////////////////////////////
+
+// PulseAudio requires a chain of callbacks with C linkage
+extern "C" {
+ void callback_discovered_sinkinput(pa_context *context, const pa_sink_input_info *i, int eol, void *userdata);
+ void callback_subscription_alert(pa_context *context, pa_subscription_event_type_t t, uint32_t index, void *userdata);
+ void callback_context_state(pa_context *context, void *userdata);
+}
+
+VolumeCatcherPulseAudio::VolumeCatcherPulseAudio()
+ : mDesiredVolume(0.0f),
+ mMainloop(nullptr),
+ mPAContext(nullptr),
+ mConnected(false),
+ mGotSyms(false)
+{
+ init();
+}
+
+VolumeCatcherPulseAudio::~VolumeCatcherPulseAudio()
+{
+ cleanup();
+}
+
+bool VolumeCatcherPulseAudio::loadsyms(std::string pulse_dso_name)
+{
+ return paSymbolGrabber.grabSymbols({ pulse_dso_name });
+}
+
+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
+ // than crash
+
+ // we cheat and rely upon libpulse-mainloop-glib.so.0 to pull-in
+ // libpulse.so.0 - this isn't a great assumption, and the two DSOs should
+ // probably be loaded separately. Our Linux DSO framework needs refactoring,
+ // 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;
+
+ mMainloop = llpa_glib_mainloop_new(g_main_context_default());
+
+ if (mMainloop)
+ {
+ pa_mainloop_api *api = llpa_glib_mainloop_get_api(mMainloop);
+
+ if (api)
+ {
+ pa_proplist *proplist = llpa_proplist_new();
+
+ if (proplist)
+ {
+ llpa_proplist_sets(proplist, PA_PROP_APPLICATION_ICON_NAME, "multimedia-player");
+ llpa_proplist_sets(proplist, PA_PROP_APPLICATION_ID, "com.secondlife.viewer.mediaplugvoladjust");
+ llpa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, "SL Plugin Volume Adjuster");
+ llpa_proplist_sets(proplist, PA_PROP_APPLICATION_VERSION, "1");
+
+ // plain old pa_context_new() is broken!
+ mPAContext = llpa_context_new_with_proplist(api, nullptr, proplist);
+
+ llpa_proplist_free(proplist);
+ }
+ }
+ }
+
+ // Now we've set up a PA context and mainloop, try connecting the
+ // PA context to a PA daemon.
+ if (mPAContext)
+ {
+ llpa_context_set_state_callback(mPAContext, callback_context_state, this);
+ pa_context_flags_t cflags = (pa_context_flags)0; // maybe add PA_CONTEXT_NOAUTOSPAWN?
+ if (llpa_context_connect(mPAContext, nullptr, cflags, nullptr) >= 0)
+ {
+ // Okay! We haven't definitely connected, but we
+ // haven't definitely failed yet.
+ }
+ else
+ {
+ // Failed to connect to PA manager... we'll leave
+ // things like that. Perhaps we should try again later.
+ }
+ }
+}
+
+void VolumeCatcherPulseAudio::cleanup()
+{
+ mConnected = false;
+
+ if (mGotSyms && mPAContext)
+ {
+ llpa_context_disconnect(mPAContext);
+ llpa_context_unref(mPAContext);
+ }
+
+ mPAContext = nullptr;
+
+ if (mGotSyms && mMainloop)
+ llpa_glib_mainloop_free(mMainloop);
+
+ mMainloop = nullptr;
+}
+
+void VolumeCatcherPulseAudio::setVolume(F32 volume)
+{
+ mDesiredVolume = volume;
+
+ if (!mGotSyms)
+ return;
+
+ if (mConnected && mPAContext)
+ {
+ update_all_volumes(mDesiredVolume);
+ }
+
+ pump();
+}
+
+void VolumeCatcherPulseAudio::setPan(F32 pan)
+{
+}
+
+void VolumeCatcherPulseAudio::pump()
+{
+ gboolean may_block = FALSE;
+ g_main_context_iteration(g_main_context_default(), may_block);
+}
+
+void VolumeCatcherPulseAudio::connected_okay()
+{
+ pa_operation *op;
+
+ // fetch global list of existing sinkinputs
+ if ((op = llpa_context_get_sink_input_info_list(mPAContext,
+ callback_discovered_sinkinput,
+ this)))
+ {
+ llpa_operation_unref(op);
+ }
+
+ // subscribe to future global sinkinput changes
+ llpa_context_set_subscribe_callback(mPAContext,
+ callback_subscription_alert,
+ this);
+ if ((op = llpa_context_subscribe(mPAContext, (pa_subscription_mask_t)
+ (PA_SUBSCRIPTION_MASK_SINK_INPUT),
+ nullptr, nullptr)))
+ {
+ llpa_operation_unref(op);
+ }
+}
+
+void VolumeCatcherPulseAudio::update_all_volumes(F32 volume)
+{
+ for (std::set<U32>::iterator it = mSinkInputIndices.begin();
+ it != mSinkInputIndices.end(); ++it)
+ {
+ update_index_volume(*it, volume);
+ }
+}
+
+void VolumeCatcherPulseAudio::update_index_volume(U32 index, F32 volume)
+{
+ static pa_cvolume cvol;
+ llpa_cvolume_set(&cvol, mSinkInputNumChannels[index],
+ llpa_sw_volume_from_linear(volume));
+
+ pa_context *c = mPAContext;
+ uint32_t idx = index;
+ const pa_cvolume *cvolumep = &cvol;
+ pa_context_success_cb_t cb = nullptr; // okay as null
+ void *userdata = nullptr; // okay as null
+
+ pa_operation *op;
+ if ((op = llpa_context_set_sink_input_volume(c, idx, cvolumep, cb, userdata)))
+ llpa_operation_unref(op);
+}
+
+void callback_discovered_sinkinput(pa_context *context, const pa_sink_input_info *sii, int eol, void *userdata)
+{
+ VolumeCatcherPulseAudio *impl = dynamic_cast<VolumeCatcherPulseAudio*>((VolumeCatcherPulseAudio*)userdata);
+ llassert(impl);
+
+ if (0 == eol)
+ {
+ pa_proplist *proplist = sii->proplist;
+ pid_t sinkpid = atoll(llpa_proplist_gets(proplist, PA_PROP_APPLICATION_PROCESS_ID));
+
+ if (isPluginPid( sinkpid )) // does the discovered sinkinput belong to this process?
+ {
+ bool is_new = (impl->mSinkInputIndices.find(sii->index) == impl->mSinkInputIndices.end());
+
+ impl->mSinkInputIndices.insert(sii->index);
+ impl->mSinkInputNumChannels[sii->index] = sii->channel_map.channels;
+
+ if (is_new)
+ {
+ // new!
+ impl->update_index_volume(sii->index, impl->mDesiredVolume);
+ }
+ else
+ {
+ // seen it already, do nothing.
+ }
+ }
+ }
+}
+
+void callback_subscription_alert(pa_context *context, pa_subscription_event_type_t t, uint32_t index, void *userdata)
+{
+ VolumeCatcherPulseAudio *impl = dynamic_cast<VolumeCatcherPulseAudio*>((VolumeCatcherPulseAudio*)userdata);
+ llassert(impl);
+
+ switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK)
+ {
+ case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
+ if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE)
+ {
+ // forget this sinkinput, if we were caring about it
+ impl->mSinkInputIndices.erase(index);
+ impl->mSinkInputNumChannels.erase(index);
+ }
+ else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW)
+ {
+ // ask for more info about this new sinkinput
+ pa_operation *op;
+ if ((op = llpa_context_get_sink_input_info(impl->mPAContext, index, callback_discovered_sinkinput, impl)))
+ {
+ llpa_operation_unref(op);
+ }
+ }
+ else
+ {
+ // property change on this sinkinput - we don't care.
+ }
+ break;
+
+ default:;
+ }
+}
+
+void callback_context_state(pa_context *context, void *userdata)
+{
+ VolumeCatcherPulseAudio *impl = dynamic_cast<VolumeCatcherPulseAudio*>((VolumeCatcherPulseAudio*)userdata);
+ llassert(impl);
+
+ switch (llpa_context_get_state(context))
+ {
+ case PA_CONTEXT_READY:
+ impl->mConnected = true;
+ impl->connected_okay();
+ break;
+ case PA_CONTEXT_TERMINATED:
+ impl->mConnected = false;
+ break;
+ case PA_CONTEXT_FAILED:
+ impl->mConnected = false;
+ break;
+ default:;
+ }
+}
diff --git a/indra/media_plugins/cef/linux/volume_catcher_pulseaudio_glib_syms.inc b/indra/media_plugins/cef/linux/volume_catcher_pulseaudio_glib_syms.inc
new file mode 100755
index 0000000000..e9b7196e51
--- /dev/null
+++ b/indra/media_plugins/cef/linux/volume_catcher_pulseaudio_glib_syms.inc
@@ -0,0 +1,10 @@
+#define G paSymbolGrabber
+
+// required symbols to grab
+LL_GRAB_SYM(G, true, pa_glib_mainloop_free, void, pa_glib_mainloop* g)
+LL_GRAB_SYM(G, true, pa_glib_mainloop_get_api, pa_mainloop_api*, pa_glib_mainloop* g)
+LL_GRAB_SYM(G, true, pa_glib_mainloop_new, pa_glib_mainloop *, GMainContext *c)
+
+// optional symbols to grab
+
+#undef G
diff --git a/indra/media_plugins/cef/linux/volume_catcher_pulseaudio_syms.inc b/indra/media_plugins/cef/linux/volume_catcher_pulseaudio_syms.inc
new file mode 100755
index 0000000000..4859a34405
--- /dev/null
+++ b/indra/media_plugins/cef/linux/volume_catcher_pulseaudio_syms.inc
@@ -0,0 +1,29 @@
+#define G paSymbolGrabber
+
+// required symbols to grab
+LL_GRAB_SYM(G, true, pa_context_connect, int, pa_context *c, const char *server, pa_context_flags_t flags, const pa_spawn_api *api)
+LL_GRAB_SYM(G, true, pa_context_disconnect, void, pa_context *c)
+LL_GRAB_SYM(G, true, pa_context_get_sink_input_info, pa_operation*, pa_context *c, uint32_t idx, pa_sink_input_info_cb_t cb, void *userdata)
+LL_GRAB_SYM(G, true, pa_context_get_sink_input_info_list, pa_operation*, pa_context *c, pa_sink_input_info_cb_t cb, void *userdata)
+LL_GRAB_SYM(G, true, pa_context_get_state, pa_context_state_t, pa_context *c)
+LL_GRAB_SYM(G, true, pa_context_new_with_proplist, pa_context*, pa_mainloop_api *mainloop, const char *name, pa_proplist *proplist)
+LL_GRAB_SYM(G, true, pa_context_set_sink_input_volume, pa_operation*, pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata)
+LL_GRAB_SYM(G, true, pa_context_set_state_callback, void, pa_context *c, pa_context_notify_cb_t cb, void *userdata)
+LL_GRAB_SYM(G, true, pa_context_set_subscribe_callback, void, pa_context *c, pa_context_subscribe_cb_t cb, void *userdata)
+LL_GRAB_SYM(G, true, pa_context_subscribe, pa_operation*, pa_context *c, pa_subscription_mask_t m, pa_context_success_cb_t cb, void *userdata)
+LL_GRAB_SYM(G, true, pa_context_unref, void, pa_context *c)
+LL_GRAB_SYM(G, true, pa_cvolume_set, pa_cvolume*, pa_cvolume *a, unsigned channels, pa_volume_t v)
+LL_GRAB_SYM(G, true, pa_operation_unref, void, pa_operation *o)
+LL_GRAB_SYM(G, true, pa_proplist_free, void, pa_proplist* p)
+LL_GRAB_SYM(G, true, pa_proplist_gets, const char*, pa_proplist *p, const char *key)
+LL_GRAB_SYM(G, true, pa_proplist_new, pa_proplist*, void)
+LL_GRAB_SYM(G, true, pa_proplist_sets, int, pa_proplist *p, const char *key, const char *value)
+LL_GRAB_SYM(G, true, pa_sw_volume_from_linear, pa_volume_t, double v)
+// LL_GRAB_SYM(G, true, pa_mainloop_free, void, pa_mainloop *m)
+// LL_GRAB_SYM(G, true, pa_mainloop_get_api, pa_mainloop_api *, pa_mainloop *m)
+// LL_GRAB_SYM(G, true, pa_mainloop_iterate, int, pa_mainloop *m, int block, int *retval)
+// LL_GRAB_SYM(G, true, pa_mainloop_new, pa_mainloop *, void)
+
+// optional symbols to grab
+
+#undef G
diff --git a/indra/media_plugins/cef/media_plugin_cef.cpp b/indra/media_plugins/cef/media_plugin_cef.cpp
index caf804f915..5693420bb6 100644
--- a/indra/media_plugins/cef/media_plugin_cef.cpp
+++ b/indra/media_plugins/cef/media_plugin_cef.cpp
@@ -911,7 +911,7 @@ void MediaPluginCEF::receiveMessage(const char* message_string)
keyEvent(key_event, native_key_data);
-#elif LL_WINDOWS
+#else
std::string event = message_in.getValue("event");
LLSD native_key_data = message_in.getValueLLSD("native_key_data");
@@ -933,6 +933,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");
@@ -1095,6 +1102,28 @@ void MediaPluginCEF::keyEvent(dullahan::EKeyEvent key_event, LLSD native_key_dat
mCEFLib->nativeKeyboardEventWin(msg, wparam, lparam);
#endif
+
+#if LL_LINUX
+
+ uint32_t native_virtual_key = (uint32_t)(native_key_data["virtual_key"].asInteger()); // this is actually the SDL event.key.keysym.sym;
+ uint32_t native_virtual_key_win = (uint32_t)(native_key_data["virtual_key_win"].asInteger());
+ uint32_t native_modifiers = (uint32_t)(native_key_data["modifiers"].asInteger());
+
+ // only for non-printable keysyms, the actual text input is done in unicodeInput() below
+ if (native_virtual_key <= 0x1b || native_virtual_key >= 0x7f)
+ {
+ // set keypad flag, not sure if this even does anything
+ bool keypad = false;
+ if (native_virtual_key_win >= 0x60 && native_virtual_key_win <= 0x6f)
+ {
+ keypad = true;
+ }
+
+ // yes, we send native_virtual_key_win twice because native_virtual_key breaks it
+ mCEFLib->nativeKeyboardEventSDL2(key_event, native_virtual_key, native_modifiers, keypad);
+ }
+
+#endif // LL_LINUX
};
void MediaPluginCEF::unicodeInput(std::string event, LLSD native_key_data = LLSD::emptyMap())
@@ -1125,6 +1154,16 @@ void MediaPluginCEF::unicodeInput(std::string event, LLSD native_key_data = LLSD
U64 lparam = ll_U32_from_sd(native_key_data["l_param"]);
mCEFLib->nativeKeyboardEventWin(msg, wparam, lparam);
#endif
+
+#if LL_LINUX
+
+ uint32_t native_scan_code = (uint32_t)(native_key_data["sdl_sym"].asInteger());
+ uint32_t native_virtual_key = (uint32_t)(native_key_data["virtual_key"].asInteger());
+ uint32_t native_modifiers = (uint32_t)(native_key_data["modifiers"].asInteger());
+
+ mCEFLib->nativeKeyboardEvent(dullahan::KE_KEY_DOWN, native_scan_code, native_virtual_key, native_modifiers);
+
+#endif // LL_LINUX
};
////////////////////////////////////////////////////////////////////////////////
diff --git a/indra/media_plugins/cef/volume_catcher.h b/indra/media_plugins/cef/volume_catcher.h
index ea97a24947..806f2a008a 100644
--- a/indra/media_plugins/cef/volume_catcher.h
+++ b/indra/media_plugins/cef/volume_catcher.h
@@ -35,7 +35,7 @@ class VolumeCatcherImpl;
class VolumeCatcher
{
- public:
+public:
VolumeCatcher();
~VolumeCatcher();
@@ -47,8 +47,14 @@ class VolumeCatcher
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:
+#if LL_LINUX
+ void onEnablePipeWireVolumeCatcher(bool enable);
+#endif
+
+private:
+#if LL_LINUX || LL_WINDOWS
VolumeCatcherImpl *pimpl;
+#endif
};
#endif // VOLUME_CATCHER_H
diff --git a/indra/media_plugins/cef/mac_volume_catcher_null.cpp b/indra/media_plugins/cef/volume_catcher_null.cpp
index c479e24a95..cd5eccca38 100644
--- a/indra/media_plugins/cef/mac_volume_catcher_null.cpp
+++ b/indra/media_plugins/cef/volume_catcher_null.cpp
@@ -1,5 +1,5 @@
/**
- * @file windows_volume_catcher.cpp
+ * @file volume_catcher_null.cpp
* @brief A null implementation of volume level control of all audio channels opened by a process.
* We are using this for the macOS version for now until we can understand how to make the
* exitising mac_volume_catcher.cpp work without the (now, non-existant) QuickTime dependency
@@ -29,67 +29,31 @@
*/
#include "volume_catcher.h"
-#include "llsingleton.h"
-class VolumeCatcherImpl : public LLSingleton<VolumeCatcherImpl>
-{
- LLSINGLETON(VolumeCatcherImpl);
- // This is a singleton class -- both callers and the component implementation should use getInstance() to find the instance.
- ~VolumeCatcherImpl();
-
-public:
-
- void setVolume(F32 volume);
- void setPan(F32 pan);
-
-private:
- F32 mVolume;
- F32 mPan;
- bool mSystemIsVistaOrHigher;
-};
-
-VolumeCatcherImpl::VolumeCatcherImpl()
-: mVolume(1.0f), // default volume is max
- mPan(0.f) // default pan is centered
-{
-}
-
-VolumeCatcherImpl::~VolumeCatcherImpl()
-{
-}
-
-void VolumeCatcherImpl::setVolume(F32 volume)
-{
- mVolume = volume;
-}
-
-void VolumeCatcherImpl::setPan(F32 pan)
-{ // remember pan for calculating individual channel levels later
- mPan = pan;
-}
/////////////////////////////////////////////////////
VolumeCatcher::VolumeCatcher()
{
- pimpl = VolumeCatcherImpl::getInstance();
}
VolumeCatcher::~VolumeCatcher()
{
- // Let the instance persist until exit.
}
void VolumeCatcher::setVolume(F32 volume)
{
- pimpl->setVolume(volume);
}
void VolumeCatcher::setPan(F32 pan)
{
- pimpl->setPan(pan);
}
void VolumeCatcher::pump()
{
- // No periodic tasks are necessary for this implementation.
}
+
+#if LL_LINUX
+void VolumeCatcher::onEnablePipeWireVolumeCatcher(bool enable)
+{
+}
+#endif
diff --git a/indra/media_plugins/cef/windows_volume_catcher.cpp b/indra/media_plugins/cef/windows_volume_catcher.cpp
index e7daeb5f74..1e52fee9de 100644
--- a/indra/media_plugins/cef/windows_volume_catcher.cpp
+++ b/indra/media_plugins/cef/windows_volume_catcher.cpp
@@ -44,7 +44,6 @@ public:
private:
F32 mVolume;
F32 mPan;
- bool mSystemIsVistaOrHigher;
};
VolumeCatcherImpl::VolumeCatcherImpl()
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index d6a1fbd124..a7a52182c1 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -5053,6 +5053,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>1</integer>
+ </map>
<key>MediaControlFadeTime</key>
<map>
<key>Comment</key>
diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp
index 89861d74bc..bb3ea74589 100644
--- a/indra/newview/llviewermedia.cpp
+++ b/indra/newview/llviewermedia.cpp
@@ -1749,9 +1749,7 @@ LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_
}
else if(LLFile::stat(plugin_name, &s))
{
-#if !LL_LINUX
LL_WARNS_ONCE("Media") << "Couldn't find plugin at " << plugin_name << LL_ENDL;
-#endif
}
else
{
@@ -1783,6 +1781,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());
@@ -1804,9 +1807,7 @@ LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_
}
}
}
-#if !LL_LINUX
LL_WARNS_ONCE("Plugin") << "plugin initialization failed for mime type: " << media_type << LL_ENDL;
-#endif
if(gAgent.isInitialized())
{
diff --git a/indra/newview/skins/default/xui/en/mime_types_linux.xml b/indra/newview/skins/default/xui/en/mime_types_linux.xml
index d0ecd0a11c..28e63c82f1 100644
--- a/indra/newview/skins/default/xui/en/mime_types_linux.xml
+++ b/indra/newview/skins/default/xui/en/mime_types_linux.xml
@@ -7,7 +7,7 @@
none
</defaultwidget>
<defaultimpl>
- media_plugin_webkit
+ media_plugin_cef
</defaultimpl>
<widgetset name="web">
<label name="web_label">
@@ -130,7 +130,7 @@
movie
</widgettype>
<impl>
- media_plugin_libvlc
+ media_plugin_gstreamer10
</impl>
</scheme>
<mimetype name="blank">
@@ -141,7 +141,7 @@
none
</widgettype>
<impl>
- media_plugin_webkit
+ media_plugin_cef
</impl>
</mimetype>
<mimetype name="none/none">
@@ -152,7 +152,7 @@
none
</widgettype>
<impl>
- media_plugin_webkit
+ media_plugin_cef
</impl>
</mimetype>
<mimetype name="audio/*">
@@ -163,7 +163,7 @@
audio
</widgettype>
<impl>
- media_plugin_libvlc
+ media_plugin_gstreamer10
</impl>
</mimetype>
<mimetype name="video/*">
@@ -174,7 +174,7 @@
movie
</widgettype>
<impl>
- media_plugin_libvlc
+ media_plugin_gstreamer10
</impl>
</mimetype>
<mimetype name="image/*">
@@ -185,7 +185,7 @@
image
</widgettype>
<impl>
- media_plugin_webkit
+ media_plugin_cef
</impl>
</mimetype>
<mimetype menu="1" name="video/vnd.secondlife.qt.legacy">
@@ -196,7 +196,7 @@
movie
</widgettype>
<impl>
- media_plugin_libvlc
+ media_plugin_gstreamer10
</impl>
</mimetype>
<mimetype name="application/javascript">
@@ -207,7 +207,7 @@
web
</widgettype>
<impl>
- media_plugin_webkit
+ media_plugin_cef
</impl>
</mimetype>
<mimetype name="application/ogg">
@@ -218,7 +218,7 @@
audio
</widgettype>
<impl>
- media_plugin_libvlc
+ media_plugin_gstreamer10
</impl>
</mimetype>
<mimetype name="application/pdf">
@@ -229,7 +229,7 @@
image
</widgettype>
<impl>
- media_plugin_webkit
+ media_plugin_cef
</impl>
</mimetype>
<mimetype name="application/postscript">
@@ -240,7 +240,7 @@
image
</widgettype>
<impl>
- media_plugin_webkit
+ media_plugin_cef
</impl>
</mimetype>
<mimetype name="application/rtf">
@@ -251,7 +251,7 @@
image
</widgettype>
<impl>
- media_plugin_webkit
+ media_plugin_cef
</impl>
</mimetype>
<mimetype name="application/smil">
@@ -262,7 +262,7 @@
movie
</widgettype>
<impl>
- media_plugin_webkit
+ media_plugin_cef
</impl>
</mimetype>
<mimetype name="application/xhtml+xml">
@@ -273,7 +273,7 @@
web
</widgettype>
<impl>
- media_plugin_webkit
+ media_plugin_cef
</impl>
</mimetype>
<mimetype name="application/x-director">
@@ -284,7 +284,7 @@
image
</widgettype>
<impl>
- media_plugin_webkit
+ media_plugin_cef
</impl>
</mimetype>
<mimetype name="audio/mid">
@@ -295,7 +295,7 @@
audio
</widgettype>
<impl>
- media_plugin_libvlc
+ media_plugin_gstreamer10
</impl>
</mimetype>
<mimetype name="audio/mpeg">
@@ -306,7 +306,7 @@
audio
</widgettype>
<impl>
- media_plugin_libvlc
+ media_plugin_gstreamer10
</impl>
</mimetype>
<mimetype name="audio/x-aiff">
@@ -317,7 +317,7 @@
audio
</widgettype>
<impl>
- media_plugin_libvlc
+ media_plugin_gstreamer10
</impl>
</mimetype>
<mimetype name="audio/x-wav">
@@ -328,7 +328,7 @@
audio
</widgettype>
<impl>
- media_plugin_libvlc
+ media_plugin_gstreamer10
</impl>
</mimetype>
<mimetype menu="1" name="image/bmp">
@@ -339,7 +339,7 @@
image
</widgettype>
<impl>
- media_plugin_webkit
+ media_plugin_cef
</impl>
</mimetype>
<mimetype menu="1" name="image/gif">
@@ -350,7 +350,7 @@
image
</widgettype>
<impl>
- media_plugin_webkit
+ media_plugin_cef
</impl>
</mimetype>
<mimetype menu="1" name="image/jpeg">
@@ -361,7 +361,7 @@
image
</widgettype>
<impl>
- media_plugin_webkit
+ media_plugin_cef
</impl>
</mimetype>
<mimetype menu="1" name="image/png">
@@ -372,7 +372,7 @@
image
</widgettype>
<impl>
- media_plugin_webkit
+ media_plugin_cef
</impl>
</mimetype>
<mimetype name="image/svg+xml">
@@ -383,7 +383,7 @@
image
</widgettype>
<impl>
- media_plugin_webkit
+ media_plugin_cef
</impl>
</mimetype>
<mimetype menu="1" name="image/tiff">
@@ -394,7 +394,7 @@
image
</widgettype>
<impl>
- media_plugin_webkit
+ media_plugin_cef
</impl>
</mimetype>
<mimetype menu="1" name="text/html">
@@ -405,7 +405,7 @@
web
</widgettype>
<impl>
- media_plugin_webkit
+ media_plugin_cef
</impl>
</mimetype>
<mimetype menu="1" name="text/plain">
@@ -416,7 +416,7 @@
text
</widgettype>
<impl>
- media_plugin_webkit
+ media_plugin_cef
</impl>
</mimetype>
<mimetype name="text/xml">
@@ -427,7 +427,7 @@
text
</widgettype>
<impl>
- media_plugin_webkit
+ media_plugin_cef
</impl>
</mimetype>
<mimetype menu="1" name="video/mpeg">
@@ -438,7 +438,7 @@
movie
</widgettype>
<impl>
- media_plugin_libvlc
+ media_plugin_gstreamer10
</impl>
</mimetype>
<mimetype name="video/mp4">
@@ -449,7 +449,7 @@
movie
</widgettype>
<impl>
- media_plugin_libvlc
+ media_plugin_gstreamer10
</impl>
</mimetype>
<mimetype menu="1" name="video/quicktime">
@@ -460,7 +460,7 @@
movie
</widgettype>
<impl>
- media_plugin_libvlc
+ media_plugin_gstreamer10
</impl>
</mimetype>
<mimetype name="video/x-ms-asf">
@@ -471,7 +471,7 @@
movie
</widgettype>
<impl>
- media_plugin_libvlc
+ media_plugin_gstreamer10
</impl>
</mimetype>
<mimetype name="video/x-ms-wmv">
@@ -482,7 +482,7 @@
movie
</widgettype>
<impl>
- media_plugin_libvlc
+ media_plugin_gstreamer10
</impl>
</mimetype>
<mimetype menu="1" name="video/x-msvideo">
@@ -493,7 +493,7 @@
movie
</widgettype>
<impl>
- media_plugin_libvlc
+ media_plugin_gstreamer10
</impl>
</mimetype>
</mimetypes>
diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py
index 0e2c9d177e..6f13466d75 100755
--- a/indra/newview/viewer_manifest.py
+++ b/indra/newview/viewer_manifest.py
@@ -1152,21 +1152,52 @@ class LinuxManifest(ViewerManifest):
self.path("secondlife_256.BMP","ll_icon.BMP")
# plugins
- with self.prefix(src="../media_plugins", dst="bin/llplugin"):
- self.path("gstreamer010/libmedia_plugin_gstreamer010.so",
- "libmedia_plugin_gstreamer.so")
- self.path2basename("libvlc", "libmedia_plugin_libvlc.so")
+ with self.prefix(dst="bin/llplugin"):
+ with self.prefix(src=os.path.join(self.args['build'], os.pardir, 'media_plugins')):
+ with self.prefix(src='cef'):
+ self.path("libmedia_plugin_cef.so")
+
+ # Media plugins - LibVLC
+ with self.prefix(src='libvlc'):
+ self.path("libmedia_plugin_libvlc.so")
+
+ # GStreamer 1.0 Media Plugin
+ with self.prefix(src='gstreamer10'):
+ self.path("libmedia_plugin_gstreamer10.so")
+
+ # Media plugins - Example (useful for debugging - not shipped with release viewer)
+ if self.channel_type() != 'release':
+ with self.prefix(src='example'):
+ self.path("libmedia_plugin_example.so")
+
+
+ with self.prefix(src=os.path.join(pkgdir, 'lib', 'release'), dst="lib"):
+ self.path( "libcef.so" )
+ self.path( "libEGL*" )
+ self.path( "libvulkan*" )
+ self.path( "libvk_swiftshader*" )
+ self.path( "libGLESv2*" )
+
+ with self.prefix(src=os.path.join(pkgdir, 'bin', 'release'), dst="bin"):
+ self.path( "chrome-sandbox" )
+ self.path( "dullahan_host" )
+
+ with self.prefix(src=os.path.join(pkgdir, 'lib', 'release'), dst="bin"):
+ self.path( "v8_context_snapshot.bin" )
+ self.path( "vk_swiftshader_icd.json")
- with self.prefix(src=os.path.join(pkgdir, 'lib', 'vlc', 'plugins'), dst="bin/llplugin/vlc/plugins"):
- self.path( "plugins.dat" )
- self.path( "*/*.so" )
+ with self.prefix(src=os.path.join(pkgdir, 'lib', 'release'), dst="lib"):
+ self.path( "v8_context_snapshot.bin" )
+ self.path( "vk_swiftshader_icd.json")
- with self.prefix(src=os.path.join(pkgdir, 'lib' ), dst="lib"):
- self.path( "libvlc*.so*" )
+ with self.prefix(src=os.path.join(pkgdir, 'resources'), dst="lib"):
+ self.path( "chrome_100_percent.pak" )
+ self.path( "chrome_200_percent.pak" )
+ self.path( "resources.pak" )
+ self.path( "icudtl.dat" )
- # llcommon
- if not self.path("../llcommon/libllcommon.so", "lib/libllcommon.so"):
- print("Skipping llcommon.so (assuming llcommon was linked statically)")
+ with self.prefix(src=os.path.join(pkgdir, 'resources', 'locales'), dst=os.path.join('lib', 'locales')):
+ self.path("*.pak")
self.path("featuretable_linux.txt")
self.path("cube.dae")
@@ -1215,6 +1246,13 @@ class LinuxManifest(ViewerManifest):
["find"] +
[os.path.join(self.get_dst_prefix(), dir) for dir in ('bin', 'lib')] +
['-type', 'f', '!', '-name', '*.py',
+ '!', '-name', '*.pak',
+ '!', '-name', '*.bin',
+ '!', '-name', '*.dat',
+ '!', '-name', '*.crt',
+ '!', '-name', '*.dll',
+ '!', '-name', '*.lib',
+ '!', '-name', '*.json',
'!', '-name', 'update_install', '-exec', 'strip', '-S', '{}', ';'])
class Linux_i686_Manifest(LinuxManifest):