summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--indra/cmake/PulseAudio.cmake28
-rw-r--r--indra/media_plugins/webkit/CMakeLists.txt14
-rw-r--r--indra/media_plugins/webkit/linux_volume_catcher.cpp483
-rw-r--r--indra/media_plugins/webkit/linux_volume_catcher_pa_syms.inc21
-rw-r--r--indra/media_plugins/webkit/linux_volume_catcher_paglib_syms.inc6
-rw-r--r--indra/media_plugins/webkit/media_plugin_webkit.cpp34
-rw-r--r--indra/newview/linux_tools/client-readme.txt9
-rw-r--r--install.xml19
8 files changed, 603 insertions, 11 deletions
diff --git a/indra/cmake/PulseAudio.cmake b/indra/cmake/PulseAudio.cmake
new file mode 100644
index 0000000000..f8087a8083
--- /dev/null
+++ b/indra/cmake/PulseAudio.cmake
@@ -0,0 +1,28 @@
+# -*- cmake -*-
+include(Prebuilt)
+
+if (STANDALONE)
+ include(FindPkgConfig)
+
+ pkg_check_modules(PULSEAUDIO REQUIRED libpulse-mainloop-glib)
+
+elseif (LINUX)
+ use_prebuilt_binary(pulseaudio)
+ set(PULSEAUDIO_FOUND ON FORCE BOOL)
+ set(PULSEAUDIO_INCLUDE_DIRS
+ ${LIBS_PREBUILT_DIR}/include
+ )
+ # We don't need to explicitly link against pulseaudio itself, because
+ # the viewer probes for the system's copy at runtime.
+ set(PULSEAUDIO_LIBRARIES
+ # none needed!
+ )
+endif (STANDALONE)
+
+if (PULSEAUDIO_FOUND)
+ set(PULSEAUDIO ON CACHE BOOL "Build with PulseAudio support, if available.")
+endif (PULSEAUDIO_FOUND)
+
+if (PULSEAUDIO)
+ add_definitions(-DLL_PULSEAUDIO_ENABLED=1)
+endif (PULSEAUDIO)
diff --git a/indra/media_plugins/webkit/CMakeLists.txt b/indra/media_plugins/webkit/CMakeLists.txt
index 4512c22b5d..9f66a77c64 100644
--- a/indra/media_plugins/webkit/CMakeLists.txt
+++ b/indra/media_plugins/webkit/CMakeLists.txt
@@ -14,10 +14,12 @@ include(Linking)
include(PluginAPI)
include(MediaPluginBase)
include(FindOpenGL)
+include(PulseAudio)
include(WebKitLibPlugin)
include_directories(
+ ${PULSEAUDIO_INCLUDE_DIRS}
${LLPLUGIN_INCLUDE_DIRS}
${MEDIA_PLUGIN_BASE_INCLUDE_DIRS}
${LLCOMMON_INCLUDE_DIRS}
@@ -34,25 +36,27 @@ set(media_plugin_webkit_SOURCE_FILES
media_plugin_webkit.cpp
)
-add_library(media_plugin_webkit
- SHARED
- ${media_plugin_webkit_SOURCE_FILES}
-)
-
set(media_plugin_webkit_LINK_LIBRARIES
${LLPLUGIN_LIBRARIES}
${MEDIA_PLUGIN_BASE_LIBRARIES}
${LLCOMMON_LIBRARIES}
${WEBKIT_PLUGIN_LIBRARIES}
${PLUGIN_API_WINDOWS_LIBRARIES}
+ ${PULSEAUDIO_LIBRARIES}
)
if (LINUX)
+ list(APPEND media_plugin_webkit_SOURCE_FILES linux_volume_catcher.cpp)
list(APPEND media_plugin_webkit_LINK_LIBRARIES
${UI_LIBRARIES} # for glib/GTK
)
endif (LINUX)
+add_library(media_plugin_webkit
+ SHARED
+ ${media_plugin_webkit_SOURCE_FILES}
+)
+
target_link_libraries(media_plugin_webkit ${media_plugin_webkit_LINK_LIBRARIES})
add_dependencies(media_plugin_webkit
diff --git a/indra/media_plugins/webkit/linux_volume_catcher.cpp b/indra/media_plugins/webkit/linux_volume_catcher.cpp
new file mode 100644
index 0000000000..2ba28bd4bf
--- /dev/null
+++ b/indra/media_plugins/webkit/linux_volume_catcher.cpp
@@ -0,0 +1,483 @@
+/**
+ * @file linux_volume_catcher.cpp
+ * @brief A Linux-specific, PulseAudio-specific hack to detect and volume-adjust new audio sources
+ *
+ * @cond
+ * $LicenseInfo:firstyear=2010&license=viewergpl$
+ *
+ * Copyright (c) 2010, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlife.com/developers/opensource/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at http://secondlife.com/developers/opensource/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/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 "linux_volume_catcher.h"
+
+
+#if LL_PULSEAUDIO_ENABLED
+
+extern "C" {
+#include <glib.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"
+}
+
+////////////////////////////////////////////////////
+
+#define DEBUGMSG(...) do {} while(0)
+#define INFOMSG(...) do {} while(0)
+#define WARNMSG(...) do {} while(0)
+
+#define LL_PA_SYM(REQUIRED, PASYM, RTN, ...) RTN (*ll##PASYM)(__VA_ARGS__) = NULL
+#include "linux_volume_catcher_pa_syms.inc"
+#include "linux_volume_catcher_paglib_syms.inc"
+#undef LL_PA_SYM
+
+static bool sSymsGrabbed = false;
+static apr_pool_t *sSymPADSOMemoryPool = NULL;
+static apr_dso_handle_t *sSymPADSOHandleG = NULL;
+
+bool grab_pa_syms(std::string pulse_dso_name)
+{
+ if (sSymsGrabbed)
+ {
+ // already have grabbed good syms
+ return true;
+ }
+
+ bool sym_error = false;
+ bool rtn = false;
+ apr_status_t rv;
+ apr_dso_handle_t *sSymPADSOHandle = NULL;
+
+#define LL_PA_SYM(REQUIRED, PASYM, RTN, ...) do{rv = apr_dso_sym((apr_dso_handle_sym_t*)&ll##PASYM, sSymPADSOHandle, #PASYM); if (rv != APR_SUCCESS) {INFOMSG("Failed to grab symbol: %s", #PASYM); if (REQUIRED) sym_error = true;} else DEBUGMSG("grabbed symbol: %s from %p", #PASYM, (void*)ll##PASYM);}while(0)
+
+ //attempt to load the shared library
+ apr_pool_create(&sSymPADSOMemoryPool, NULL);
+
+ if ( APR_SUCCESS == (rv = apr_dso_load(&sSymPADSOHandle,
+ pulse_dso_name.c_str(),
+ sSymPADSOMemoryPool) ))
+ {
+ INFOMSG("Found DSO: %s", pulse_dso_name.c_str());
+
+#include "linux_volume_catcher_pa_syms.inc"
+#include "linux_volume_catcher_paglib_syms.inc"
+
+ if ( sSymPADSOHandle )
+ {
+ sSymPADSOHandleG = sSymPADSOHandle;
+ sSymPADSOHandle = NULL;
+ }
+
+ rtn = !sym_error;
+ }
+ else
+ {
+ INFOMSG("Couldn't load DSO: %s", pulse_dso_name.c_str());
+ rtn = false; // failure
+ }
+
+ if (sym_error)
+ {
+ WARNMSG("Failed to find necessary symbols in PulseAudio libraries.");
+ }
+#undef LL_PA_SYM
+
+ sSymsGrabbed = rtn;
+ return rtn;
+}
+
+
+void ungrab_pa_syms()
+{
+ // should be safe to call regardless of whether we've
+ // actually grabbed syms.
+
+ if ( sSymPADSOHandleG )
+ {
+ apr_dso_unload(sSymPADSOHandleG);
+ sSymPADSOHandleG = NULL;
+ }
+
+ if ( sSymPADSOMemoryPool )
+ {
+ apr_pool_destroy(sSymPADSOMemoryPool);
+ sSymPADSOMemoryPool = NULL;
+ }
+
+ // NULL-out all of the symbols we'd grabbed
+#define LL_PA_SYM(REQUIRED, PASYM, RTN, ...) do{ll##PASYM = NULL;}while(0)
+#include "linux_volume_catcher_pa_syms.inc"
+#include "linux_volume_catcher_paglib_syms.inc"
+#undef LL_PA_SYM
+
+ sSymsGrabbed = false;
+}
+////////////////////////////////////////////////////
+
+// 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);
+}
+
+
+class LinuxVolumeCatcherImpl
+{
+public:
+ LinuxVolumeCatcherImpl();
+ ~LinuxVolumeCatcherImpl();
+
+ 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;
+};
+
+LinuxVolumeCatcherImpl::LinuxVolumeCatcherImpl()
+ : mDesiredVolume(0.0f),
+ mMainloop(NULL),
+ mPAContext(NULL),
+ mConnected(false),
+ mGotSyms(false)
+{
+ init();
+}
+
+LinuxVolumeCatcherImpl::~LinuxVolumeCatcherImpl()
+{
+ cleanup();
+}
+
+bool LinuxVolumeCatcherImpl::loadsyms(std::string pulse_dso_name)
+{
+ return grab_pa_syms(pulse_dso_name);
+}
+
+void LinuxVolumeCatcherImpl::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) 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, NULL, 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, NULL, cflags, NULL) >= 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 LinuxVolumeCatcherImpl::cleanup()
+{
+ mConnected = false;
+
+ if (mGotSyms && mPAContext)
+ {
+ llpa_context_disconnect(mPAContext);
+ llpa_context_unref(mPAContext);
+ }
+ mPAContext = NULL;
+
+ if (mGotSyms && mMainloop)
+ {
+ llpa_glib_mainloop_free(mMainloop);
+ }
+ mMainloop = NULL;
+}
+
+void LinuxVolumeCatcherImpl::setVolume(F32 volume)
+{
+ mDesiredVolume = volume;
+
+ if (!mGotSyms) return;
+
+ if (mConnected && mPAContext)
+ {
+ update_all_volumes(mDesiredVolume);
+ }
+
+ pump();
+}
+
+void LinuxVolumeCatcherImpl::pump()
+{
+ gboolean may_block = FALSE;
+ g_main_context_iteration(g_main_context_default(), may_block);
+}
+
+void LinuxVolumeCatcherImpl::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),
+ NULL, NULL)))
+ {
+ llpa_operation_unref(op);
+ }
+}
+
+void LinuxVolumeCatcherImpl::update_all_volumes(F32 volume)
+{
+ for (std::set<U32>::iterator it = mSinkInputIndices.begin();
+ it != mSinkInputIndices.end(); ++it)
+ {
+ update_index_volume(*it, volume);
+ }
+}
+
+void LinuxVolumeCatcherImpl::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 = NULL; // okay as null
+ void *userdata = NULL; // 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)
+{
+ LinuxVolumeCatcherImpl *impl = dynamic_cast<LinuxVolumeCatcherImpl*>((LinuxVolumeCatcherImpl*)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 (sinkpid == getpid()) // 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)
+{
+ LinuxVolumeCatcherImpl *impl = dynamic_cast<LinuxVolumeCatcherImpl*>((LinuxVolumeCatcherImpl*)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
+ {
+ // 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);
+ }
+ }
+ break;
+
+ default:;
+ }
+}
+
+void callback_context_state(pa_context *context, void *userdata)
+{
+ LinuxVolumeCatcherImpl *impl = dynamic_cast<LinuxVolumeCatcherImpl*>((LinuxVolumeCatcherImpl*)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:;
+ }
+}
+
+/////////////////////////////////////////////////////
+
+LinuxVolumeCatcher::LinuxVolumeCatcher()
+{
+ pimpl = new LinuxVolumeCatcherImpl();
+}
+
+LinuxVolumeCatcher::~LinuxVolumeCatcher()
+{
+ delete pimpl;
+ pimpl = NULL;
+}
+
+void LinuxVolumeCatcher::setVolume(F32 volume)
+{
+ llassert(pimpl);
+ pimpl->setVolume(volume);
+}
+
+void LinuxVolumeCatcher::pump()
+{
+ llassert(pimpl);
+ pimpl->pump();
+}
+
+#else // !LL_PULSEAUDIO_ENABLED
+
+// stub.
+
+LinuxVolumeCatcher::LinuxVolumeCatcher()
+{
+ pimpl = NULL;
+}
+
+LinuxVolumeCatcher::~LinuxVolumeCatcher()
+{
+}
+
+void LinuxVolumeCatcher::setVolume(F32 volume)
+{
+}
+
+void LinuxVolumeCatcher::pump()
+{
+}
+
+#endif // LL_PULSEAUDIO_ENABLED
diff --git a/indra/media_plugins/webkit/linux_volume_catcher_pa_syms.inc b/indra/media_plugins/webkit/linux_volume_catcher_pa_syms.inc
new file mode 100644
index 0000000000..d806b48428
--- /dev/null
+++ b/indra/media_plugins/webkit/linux_volume_catcher_pa_syms.inc
@@ -0,0 +1,21 @@
+// required symbols to grab
+LL_PA_SYM(true, pa_context_connect, int, pa_context *c, const char *server, pa_context_flags_t flags, const pa_spawn_api *api);
+LL_PA_SYM(true, pa_context_disconnect, void, pa_context *c);
+LL_PA_SYM(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_PA_SYM(true, pa_context_get_sink_input_info_list, pa_operation*, pa_context *c, pa_sink_input_info_cb_t cb, void *userdata);
+LL_PA_SYM(true, pa_context_get_state, pa_context_state_t, pa_context *c);
+LL_PA_SYM(true, pa_context_new_with_proplist, pa_context*, pa_mainloop_api *mainloop, const char *name, pa_proplist *proplist);
+LL_PA_SYM(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_PA_SYM(true, pa_context_set_state_callback, void, pa_context *c, pa_context_notify_cb_t cb, void *userdata);
+LL_PA_SYM(true, pa_context_set_subscribe_callback, void, pa_context *c, pa_context_subscribe_cb_t cb, void *userdata);
+LL_PA_SYM(true, pa_context_subscribe, pa_operation*, pa_context *c, pa_subscription_mask_t m, pa_context_success_cb_t cb, void *userdata);
+LL_PA_SYM(true, pa_context_unref, void, pa_context *c);
+LL_PA_SYM(true, pa_cvolume_set, pa_cvolume*, pa_cvolume *a, unsigned channels, pa_volume_t v);
+LL_PA_SYM(true, pa_operation_unref, void, pa_operation *o);
+LL_PA_SYM(true, pa_proplist_free, void, pa_proplist* p);
+LL_PA_SYM(true, pa_proplist_gets, const char*, pa_proplist *p, const char *key);
+LL_PA_SYM(true, pa_proplist_new, pa_proplist*, void);
+LL_PA_SYM(true, pa_proplist_sets, int, pa_proplist *p, const char *key, const char *value);
+LL_PA_SYM(true, pa_sw_volume_from_linear, pa_volume_t, double v);
+
+// optional symbols to grab
diff --git a/indra/media_plugins/webkit/linux_volume_catcher_paglib_syms.inc b/indra/media_plugins/webkit/linux_volume_catcher_paglib_syms.inc
new file mode 100644
index 0000000000..abf628c96c
--- /dev/null
+++ b/indra/media_plugins/webkit/linux_volume_catcher_paglib_syms.inc
@@ -0,0 +1,6 @@
+// required symbols to grab
+LL_PA_SYM(true, pa_glib_mainloop_free, void, pa_glib_mainloop* g);
+LL_PA_SYM(true, pa_glib_mainloop_get_api, pa_mainloop_api*, pa_glib_mainloop* g);
+LL_PA_SYM(true, pa_glib_mainloop_new, pa_glib_mainloop *, GMainContext *c);
+
+// optional symbols to grab
diff --git a/indra/media_plugins/webkit/media_plugin_webkit.cpp b/indra/media_plugins/webkit/media_plugin_webkit.cpp
index 688d3bcd3d..c7aba04492 100644
--- a/indra/media_plugins/webkit/media_plugin_webkit.cpp
+++ b/indra/media_plugins/webkit/media_plugin_webkit.cpp
@@ -43,11 +43,15 @@
#include "llpluginmessageclasses.h"
#include "media_plugin_base.h"
+#if LL_LINUX
+# include "linux_volume_catcher.h"
+#endif // LL_LINUX
+
#if LL_WINDOWS
-#include <direct.h>
+# include <direct.h>
#else
-#include <unistd.h>
-#include <stdlib.h>
+# include <unistd.h>
+# include <stdlib.h>
#endif
#if LL_WINDOWS
@@ -102,6 +106,10 @@ private:
F32 mBackgroundG;
F32 mBackgroundB;
+#if LL_LINUX
+ LinuxVolumeCatcher mLinuxVolumeCatcher;
+#endif // LL_LINUX
+
void setInitState(int state)
{
// std::cerr << "changing init state to " << state << std::endl;
@@ -114,6 +122,10 @@ private:
{
LLQtWebKit::getInstance()->pump( milliseconds );
+#if LL_LINUX
+ mLinuxVolumeCatcher.pump();
+#endif // LL_LINUX
+
checkEditState();
if(mInitState == INIT_STATE_NAVIGATE_COMPLETE)
@@ -281,6 +293,7 @@ private:
return false;
};
+ void setVolume(F32 vol);
////////////////////////////////////////////////////////////////////////////////
// virtual
@@ -732,6 +745,14 @@ void MediaPluginWebKit::receiveMessage(const char *message_string)
// std::cerr << "MediaPluginWebKit::receiveMessage: unknown base message: " << message_name << std::endl;
}
}
+ else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME)
+ {
+ if(message_name == "set_volume")
+ {
+ F32 volume = message_in.getValueReal("volume");
+ setVolume(volume);
+ }
+ }
else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA)
{
if(message_name == "size_change")
@@ -998,6 +1019,13 @@ void MediaPluginWebKit::receiveMessage(const char *message_string)
}
}
+void MediaPluginWebKit::setVolume(F32 volume)
+{
+#if LL_LINUX
+ mLinuxVolumeCatcher.setVolume(volume);
+#endif // LL_LINUX
+}
+
int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data)
{
MediaPluginWebKit *self = new MediaPluginWebKit(host_send_func, host_user_data);
diff --git a/indra/newview/linux_tools/client-readme.txt b/indra/newview/linux_tools/client-readme.txt
index 92d321d8c0..e01b9e4bc6 100644
--- a/indra/newview/linux_tools/client-readme.txt
+++ b/indra/newview/linux_tools/client-readme.txt
@@ -15,7 +15,7 @@ Life itself - please see <http://www.secondlife.com/whatis/>.
5.3. Blank window after minimizing it
5.4. Audio
5.5. 'Alt' key for camera controls doesn't work
- 5.6. In-world streaming movie/music playback
+ 5.6. In-world streaming movie, music and Flash playback
6. Advanced Troubleshooting
6.1. Audio
6.2. OpenGL
@@ -169,12 +169,15 @@ SOLUTION:- Some window managers eat the Alt key for their own purposes; you
example, the 'Windows' key!) which will allow the Alt key to function
properly with mouse actions in Second Life and other applications.
-PROBLEM 6:- In-world movie and/or music playback doesn't work for me.
+PROBLEM 6:- In-world movie, music, or Flash playback doesn't work for me.
SOLUTION:- You need to have a working installation of GStreamer 0.10; this
is usually an optional package for most versions of Linux. If you have
installed GStreamer 0.10 and you can play some music/movies but not others
then you need to install a wider selection of GStreamer plugins, either
- from your vendor or an appropriate third party.
+ from your vendor (i.e. the 'Ugly' plugins) or an appropriate third party.
+ For Flash playback, you need to have Flash 10 installed for your normal
+ web browser (for example, Firefox). PulseAudio is required for Flash
+ volume control / muting to fully function inside Second Life.
6. ADVANCED TROUBLESHOOTING
diff --git a/install.xml b/install.xml
index 4c377a9f3e..f02a1663e3 100644
--- a/install.xml
+++ b/install.xml
@@ -298,6 +298,25 @@
</map>
</map>
</map>
+ <key>pulseaudio</key>
+ <map>
+ <key>copyright</key>
+ <string>Copyright 2004-2006 Lennart Poettering, Copyright 2006 Pierre Ossman (ossman@cendio.se) for Cendio AB</string>
+ <key>description</key>
+ <string>pulseaudio: headers only</string>
+ <key>license</key>
+ <string>lgpl</string>
+ <key>packages</key>
+ <map>
+ <key>linux</key>
+ <map>
+ <key>md5sum</key>
+ <string>30cb00069fe2a545fbf7be1070386236</string>
+ <key>url</key>
+ <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/linux-pulse-headers-0.9.14.tar.bz2</uri>
+ </map>
+ </map>
+ </map>
<key>elfio</key>
<map>
<key>license</key>