summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRider Linden <rider@lindenlab.com>2015-10-09 10:09:18 -0700
committerRider Linden <rider@lindenlab.com>2015-10-09 10:09:18 -0700
commit295d392b1c1ed6182eb2dce528fd396eab2ed2b2 (patch)
treee949a892b537f062b973e4175c624b41aae4f332
parent0725bd2d5eddee94e9adc64b83f4c457b9cac725 (diff)
MAINT-5715, MAINT-5717, MAINT-5718: Replace the the volume catcher that was removed with the QT viewer. Addresses panning and fading issues, teleport and viewer mute controls.
-rw-r--r--indra/media_plugins/cef/CMakeLists.txt34
-rw-r--r--indra/media_plugins/cef/mac_volume_catcher.cpp275
-rw-r--r--indra/media_plugins/cef/media_plugin_cef.cpp20
-rw-r--r--indra/media_plugins/cef/volume_catcher.h54
-rw-r--r--indra/media_plugins/cef/windows_volume_catcher.cpp147
5 files changed, 525 insertions, 5 deletions
diff --git a/indra/media_plugins/cef/CMakeLists.txt b/indra/media_plugins/cef/CMakeLists.txt
index 7465fe727a..9bd6dbc5e9 100644
--- a/indra/media_plugins/cef/CMakeLists.txt
+++ b/indra/media_plugins/cef/CMakeLists.txt
@@ -45,23 +45,49 @@ set(media_plugin_cef_SOURCE_FILES
media_plugin_cef.cpp
)
+set(media_plugin_webkit_HEADER_FILES
+ volume_catcher.h
+ )
+
+# 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.cpp)
+ find_library(CORESERVICES_LIBRARY CoreServices)
+ find_library(AUDIOUNIT_LIBRARY AudioUnit)
+ list(APPEND media_plugin_cef_SOURCE_FILES
+ ${CORESERVICES_LIBRARY} # for Component Manager calls
+ ${AUDIOUNIT_LIBRARY} # for AudioUnit calls
+ )
+elseif (WINDOWS)
+ list(APPEND media_plugin_cef_SOURCE_FILES windows_volume_catcher.cpp)
+endif (LINUX)
+
+set_source_files_properties(${media_plugin_webkit_HEADER_FILES}
+ PROPERTIES HEADER_FILE_ONLY TRUE)
+
+list(APPEND media_plugin_cef_SOURCE_FILES ${media_plugin_cef_HEADER_FILES})
+
add_library(media_plugin_cef
SHARED
${media_plugin_cef_SOURCE_FILES}
)
-target_link_libraries(media_plugin_cef
+add_dependencies(media_plugin_cef
${LLPLUGIN_LIBRARIES}
${MEDIA_PLUGIN_BASE_LIBRARIES}
${LLCOMMON_LIBRARIES}
- ${CEF_PLUGIN_LIBRARIES}
- ${PLUGIN_API_WINDOWS_LIBRARIES}
)
-add_dependencies(media_plugin_cef
+target_link_libraries(media_plugin_cef
${LLPLUGIN_LIBRARIES}
${MEDIA_PLUGIN_BASE_LIBRARIES}
${LLCOMMON_LIBRARIES}
+ ${CEF_PLUGIN_LIBRARIES}
+ ${PLUGIN_API_WINDOWS_LIBRARIES}
)
if (WINDOWS)
diff --git a/indra/media_plugins/cef/mac_volume_catcher.cpp b/indra/media_plugins/cef/mac_volume_catcher.cpp
new file mode 100644
index 0000000000..73e5bf3da3
--- /dev/null
+++ b/indra/media_plugins/cef/mac_volume_catcher.cpp
@@ -0,0 +1,275 @@
+/**
+ * @file mac_volume_catcher.cpp
+ * @brief A Mac OS X specific hack to control the volume level of all audio channels opened by a process.
+ *
+ * @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
+ */
+
+/**************************************************************************************************************
+ This code works by using CaptureComponent to capture the "Default Output" audio component
+ (kAudioUnitType_Output/kAudioUnitSubType_DefaultOutput) and delegating all calls to the original component.
+ It does this just to keep track of all instances of the default output component, so that it can set the
+ kHALOutputParam_Volume parameter on all of them to adjust the output volume.
+**************************************************************************************************************/
+
+#include "volume_catcher.h"
+
+#include <QuickTime/QuickTime.h>
+#include <AudioUnit/AudioUnit.h>
+#include <list>
+
+#if LL_DARWIN
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+struct VolumeCatcherStorage;
+
+class VolumeCatcherImpl
+{
+public:
+
+ void setVolume(F32 volume);
+ void setPan(F32 pan);
+
+ void setInstanceVolume(VolumeCatcherStorage *instance);
+
+ std::list<VolumeCatcherStorage*> mComponentInstances;
+ Component mOriginalDefaultOutput;
+ Component mVolumeAdjuster;
+
+ static VolumeCatcherImpl *getInstance();
+private:
+ // This is a singleton class -- both callers and the component implementation should use getInstance() to find the instance.
+ VolumeCatcherImpl();
+ static VolumeCatcherImpl *sInstance;
+
+ // The singlar instance of this class is expected to last until the process exits.
+ // To ensure this, we declare the destructor here but never define it, so any code which attempts to destroy the instance will not link.
+ ~VolumeCatcherImpl();
+
+ F32 mVolume;
+ F32 mPan;
+};
+
+VolumeCatcherImpl *VolumeCatcherImpl::sInstance = NULL;;
+
+struct VolumeCatcherStorage
+{
+ ComponentInstance self;
+ ComponentInstance delegate;
+};
+
+static ComponentResult volume_catcher_component_entry(ComponentParameters *cp, Handle componentStorage);
+static ComponentResult volume_catcher_component_open(VolumeCatcherStorage *storage, ComponentInstance self);
+static ComponentResult volume_catcher_component_close(VolumeCatcherStorage *storage, ComponentInstance self);
+
+VolumeCatcherImpl *VolumeCatcherImpl::getInstance()
+{
+ if(!sInstance)
+ {
+ sInstance = new VolumeCatcherImpl;
+ }
+
+ return sInstance;
+}
+
+VolumeCatcherImpl::VolumeCatcherImpl()
+{
+ mVolume = 1.0; // default to full volume
+ mPan = 0.0; // and center pan
+
+ ComponentDescription desc;
+ desc.componentType = kAudioUnitType_Output;
+ desc.componentSubType = kAudioUnitSubType_DefaultOutput;
+ desc.componentManufacturer = kAudioUnitManufacturer_Apple;
+ desc.componentFlags = 0;
+ desc.componentFlagsMask = 0;
+
+ // Find the original default output component
+ mOriginalDefaultOutput = FindNextComponent(NULL, &desc);
+
+ // Register our own output component with the same parameters
+ mVolumeAdjuster = RegisterComponent(&desc, NewComponentRoutineUPP(volume_catcher_component_entry), 0, NULL, NULL, NULL);
+
+ // Capture the original component, so we always get found instead.
+ CaptureComponent(mOriginalDefaultOutput, mVolumeAdjuster);
+
+}
+
+static ComponentResult volume_catcher_component_entry(ComponentParameters *cp, Handle componentStorage)
+{
+ ComponentResult result = badComponentSelector;
+ VolumeCatcherStorage *storage = (VolumeCatcherStorage*)componentStorage;
+
+ switch(cp->what)
+ {
+ case kComponentOpenSelect:
+// std::cerr << "kComponentOpenSelect" << std::endl;
+ result = CallComponentFunctionWithStorageProcInfo((Handle)storage, cp, (ProcPtr)volume_catcher_component_open, uppCallComponentOpenProcInfo);
+ break;
+
+ case kComponentCloseSelect:
+// std::cerr << "kComponentCloseSelect" << std::endl;
+ result = CallComponentFunctionWithStorageProcInfo((Handle)storage, cp, (ProcPtr)volume_catcher_component_close, uppCallComponentCloseProcInfo);
+ // CallComponentFunctionWithStorageProcInfo
+ break;
+
+ default:
+// std::cerr << "Delegating selector: " << cp->what << " to component instance " << storage->delegate << std::endl;
+ result = DelegateComponentCall(cp, storage->delegate);
+ break;
+ }
+
+ return result;
+}
+
+static ComponentResult volume_catcher_component_open(VolumeCatcherStorage *storage, ComponentInstance self)
+{
+ ComponentResult result = noErr;
+ VolumeCatcherImpl *impl = VolumeCatcherImpl::getInstance();
+
+ storage = new VolumeCatcherStorage;
+
+ storage->self = self;
+ storage->delegate = NULL;
+
+ result = OpenAComponent(impl->mOriginalDefaultOutput, &(storage->delegate));
+
+ if(result != noErr)
+ {
+// std::cerr << "OpenAComponent result = " << result << ", component ref = " << storage->delegate << std::endl;
+
+ // If we failed to open the delagate component, our open is going to fail. Clean things up.
+ delete storage;
+ }
+ else
+ {
+ // Success -- set up this component's storage
+ SetComponentInstanceStorage(self, (Handle)storage);
+
+ // add this instance to the global list
+ impl->mComponentInstances.push_back(storage);
+
+ // and set up the initial volume
+ impl->setInstanceVolume(storage);
+ }
+
+ return result;
+}
+
+static ComponentResult volume_catcher_component_close(VolumeCatcherStorage *storage, ComponentInstance self)
+{
+ ComponentResult result = noErr;
+
+ if(storage)
+ {
+ if(storage->delegate)
+ {
+ CloseComponent(storage->delegate);
+ storage->delegate = NULL;
+ }
+
+ VolumeCatcherImpl *impl = VolumeCatcherImpl::getInstance();
+ impl->mComponentInstances.remove(storage);
+ delete[] storage;
+ }
+
+ return result;
+}
+
+void VolumeCatcherImpl::setVolume(F32 volume)
+{
+ VolumeCatcherImpl *impl = VolumeCatcherImpl::getInstance();
+ impl->mVolume = volume;
+
+ // Iterate through all known instances, setting the volume on each.
+ for(std::list<VolumeCatcherStorage*>::iterator iter = mComponentInstances.begin(); iter != mComponentInstances.end(); ++iter)
+ {
+ impl->setInstanceVolume(*iter);
+ }
+}
+
+void VolumeCatcherImpl::setPan(F32 pan)
+{
+ VolumeCatcherImpl *impl = VolumeCatcherImpl::getInstance();
+ impl->mPan = pan;
+
+ // TODO: implement this.
+ // This will probably require adding a "panner" audio unit to the chain somehow.
+ // There's also a "3d mixer" component that we might be able to use...
+}
+
+void VolumeCatcherImpl::setInstanceVolume(VolumeCatcherStorage *instance)
+{
+// std::cerr << "Setting volume on component instance: " << (instance->delegate) << " to " << mVolume << std::endl;
+
+ OSStatus err = noErr;
+
+ if(instance && instance->delegate)
+ {
+ err = AudioUnitSetParameter(
+ instance->delegate,
+ kHALOutputParam_Volume,
+ kAudioUnitScope_Global,
+ 0,
+ mVolume,
+ 0);
+ }
+
+ if(err)
+ {
+// std::cerr << " AudioUnitSetParameter returned " << err << std::endl;
+ }
+}
+
+/////////////////////////////////////////////////////
+
+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_DARWIN
+#pragma GCC diagnostic warning "-Wdeprecated-declarations"
+#endif
diff --git a/indra/media_plugins/cef/media_plugin_cef.cpp b/indra/media_plugins/cef/media_plugin_cef.cpp
index 5b9ff6cac8..3f3d9dc657 100644
--- a/indra/media_plugins/cef/media_plugin_cef.cpp
+++ b/indra/media_plugins/cef/media_plugin_cef.cpp
@@ -39,6 +39,7 @@
#include "boost/function.hpp"
#include "boost/bind.hpp"
#include "llCEFLib.h"
+#include "volume_catcher.h"
////////////////////////////////////////////////////////////////////////////////
//
@@ -76,6 +77,7 @@ private:
void unicodeInput(const std::string &utf8str, LLCEFLib::EKeyboardModifier modifiers, LLSD native_key_data);
void checkEditState();
+ void setVolume(F32 vol);
bool mEnableMediaPluginDebugging;
std::string mHostLanguage;
@@ -92,6 +94,8 @@ private:
std::string mCachePath;
std::string mCookiePath;
LLCEFLib* mLLCEFLib;
+
+ VolumeCatcher mVolumeCatcher;
};
////////////////////////////////////////////////////////////////////////////////
@@ -332,6 +336,7 @@ void MediaPluginCEF::receiveMessage(const char* message_string)
{
mLLCEFLib->update();
+ mVolumeCatcher.pump();
// this seems bad but unless the state changes (it won't until we figure out
// how to get CEF to tell us if copy/cut/paste is available) then this function
// will return immediately
@@ -630,7 +635,15 @@ void MediaPluginCEF::receiveMessage(const char* message_string)
mJavascriptEnabled = message_in.getValueBoolean("enable");
}
}
- else
+ else if (message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME)
+ {
+ if (message_name == "set_volume")
+ {
+ F32 volume = (F32)message_in.getValueReal("volume");
+ setVolume(volume);
+ }
+ }
+ else
{
//std::cerr << "MediaPluginWebKit::receiveMessage: unknown message class: " << message_class << std::endl;
};
@@ -763,6 +776,11 @@ void MediaPluginCEF::checkEditState()
}
}
+void MediaPluginCEF::setVolume(F32 vol)
+{
+ mVolumeCatcher.setVolume(vol);
+}
+
////////////////////////////////////////////////////////////////////////////////
//
bool MediaPluginCEF::init()
diff --git a/indra/media_plugins/cef/volume_catcher.h b/indra/media_plugins/cef/volume_catcher.h
new file mode 100644
index 0000000000..337f2913d3
--- /dev/null
+++ b/indra/media_plugins/cef/volume_catcher.h
@@ -0,0 +1,54 @@
+/**
+ * @file volume_catcher.h
+ * @brief Interface to a class with platform-specific implementations that allows control of the audio volume of all sources in the current process.
+ *
+ * @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_H
+#define VOLUME_CATCHER_H
+
+#include "linden_common.h"
+
+class VolumeCatcherImpl;
+
+class VolumeCatcher
+{
+ 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 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:
+ VolumeCatcherImpl *pimpl;
+};
+
+#endif // VOLUME_CATCHER_H
diff --git a/indra/media_plugins/cef/windows_volume_catcher.cpp b/indra/media_plugins/cef/windows_volume_catcher.cpp
new file mode 100644
index 0000000000..0cfb810906
--- /dev/null
+++ b/indra/media_plugins/cef/windows_volume_catcher.cpp
@@ -0,0 +1,147 @@
+/**
+ * @file windows_volume_catcher.cpp
+ * @brief A Windows implementation of volume level control of all audio channels opened by a process.
+ *
+ * @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.h"
+#include <windows.h>
+#include "llsingleton.h"
+class VolumeCatcherImpl : public LLSingleton<VolumeCatcherImpl>
+{
+friend LLSingleton<VolumeCatcherImpl>;
+public:
+
+ void setVolume(F32 volume);
+ void setPan(F32 pan);
+
+private:
+ // This is a singleton class -- both callers and the component implementation should use getInstance() to find the instance.
+ VolumeCatcherImpl();
+ ~VolumeCatcherImpl();
+
+ typedef void (WINAPI *set_volume_func_t)(F32);
+ typedef void (WINAPI *set_mute_func_t)(bool);
+
+ set_volume_func_t mSetVolumeFunc;
+ set_mute_func_t mSetMuteFunc;
+
+ // tests if running on Vista, 7, 8 + once in CTOR
+ bool isWindowsVistaOrHigher();
+
+ F32 mVolume;
+ F32 mPan;
+ bool mSystemIsVistaOrHigher;
+};
+
+bool VolumeCatcherImpl::isWindowsVistaOrHigher()
+{
+ OSVERSIONINFO osvi;
+ ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
+ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ GetVersionEx(&osvi);
+ return osvi.dwMajorVersion >= 6;
+}
+
+VolumeCatcherImpl::VolumeCatcherImpl()
+: mVolume(1.0f), // default volume is max
+ mPan(0.f) // default pan is centered
+{
+ mSystemIsVistaOrHigher = isWindowsVistaOrHigher();
+
+ if ( ! mSystemIsVistaOrHigher )
+ {
+ HMODULE handle = ::LoadLibrary(L"winmm.dll");
+ if(handle)
+ {
+ mSetVolumeFunc = (set_volume_func_t)::GetProcAddress(handle, "setPluginVolume");
+ mSetMuteFunc = (set_mute_func_t)::GetProcAddress(handle, "setPluginMute");
+ }
+ }
+}
+
+VolumeCatcherImpl::~VolumeCatcherImpl()
+{
+}
+
+void VolumeCatcherImpl::setVolume(F32 volume)
+{
+ mVolume = volume;
+
+ if ( mSystemIsVistaOrHigher )
+ {
+ // set both left/right to same volume
+ // TODO: use pan value to set independently
+ DWORD left_channel = (DWORD)(mVolume * 65535.0f);
+ DWORD right_channel = (DWORD)(mVolume * 65535.0f);
+ DWORD hw_volume = left_channel << 16 | right_channel;
+ ::waveOutSetVolume(NULL, hw_volume);
+ }
+ else
+ {
+ if (mSetMuteFunc)
+ {
+ mSetMuteFunc(volume == 0.f);
+ }
+ if (mSetVolumeFunc)
+ {
+ mSetVolumeFunc(mVolume);
+ }
+ }
+}
+
+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.
+}
+
+