summaryrefslogtreecommitdiff
path: root/indra/media_plugins
diff options
context:
space:
mode:
Diffstat (limited to 'indra/media_plugins')
-rw-r--r--indra/media_plugins/CMakeLists.txt17
-rw-r--r--indra/media_plugins/base/CMakeLists.txt41
-rw-r--r--indra/media_plugins/base/media_plugin_base.cpp206
-rw-r--r--indra/media_plugins/base/media_plugin_base.exp2
-rw-r--r--indra/media_plugins/base/media_plugin_base.h129
-rw-r--r--indra/media_plugins/example/CMakeLists.txt74
-rw-r--r--indra/media_plugins/example/media_plugin_example.cpp487
-rw-r--r--indra/media_plugins/gstreamer010/CMakeLists.txt64
-rw-r--r--indra/media_plugins/gstreamer010/llmediaimplgstreamer.h53
-rw-r--r--indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms.cpp167
-rw-r--r--indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms.h74
-rw-r--r--indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms_raw.inc51
-rw-r--r--indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms_rawv.inc5
-rw-r--r--indra/media_plugins/gstreamer010/llmediaimplgstreamertriviallogging.h55
-rw-r--r--indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.cpp527
-rw-r--r--indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.h105
-rw-r--r--indra/media_plugins/gstreamer010/media_plugin_gstreamer010.cpp1266
-rw-r--r--indra/media_plugins/quicktime/CMakeLists.txt91
-rw-r--r--indra/media_plugins/quicktime/media_plugin_quicktime.cpp1111
-rw-r--r--indra/media_plugins/webkit/CMakeLists.txt120
-rw-r--r--indra/media_plugins/webkit/dummy_volume_catcher.cpp58
-rw-r--r--indra/media_plugins/webkit/linux_volume_catcher.cpp468
-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/mac_volume_catcher.cpp268
-rw-r--r--indra/media_plugins/webkit/media_plugin_webkit.cpp1238
-rw-r--r--indra/media_plugins/webkit/volume_catcher.h54
-rw-r--r--indra/media_plugins/webkit/windows_volume_catcher.cpp117
-rw-r--r--indra/media_plugins/winmmshim/CMakeLists.txt34
-rw-r--r--indra/media_plugins/winmmshim/forwarding_api.cpp1454
-rw-r--r--indra/media_plugins/winmmshim/forwarding_api.h389
-rw-r--r--indra/media_plugins/winmmshim/winmm.def218
-rw-r--r--indra/media_plugins/winmmshim/winmm_shim.cpp188
33 files changed, 9158 insertions, 0 deletions
diff --git a/indra/media_plugins/CMakeLists.txt b/indra/media_plugins/CMakeLists.txt
new file mode 100644
index 0000000000..85318aea3b
--- /dev/null
+++ b/indra/media_plugins/CMakeLists.txt
@@ -0,0 +1,17 @@
+# -*- cmake -*-
+
+add_subdirectory(base)
+
+add_subdirectory(webkit)
+
+add_subdirectory(gstreamer010)
+
+if (WINDOWS OR DARWIN)
+ add_subdirectory(quicktime)
+endif (WINDOWS OR DARWIN)
+
+if (WINDOWS)
+ add_subdirectory(winmmshim)
+endif (WINDOWS)
+
+add_subdirectory(example)
diff --git a/indra/media_plugins/base/CMakeLists.txt b/indra/media_plugins/base/CMakeLists.txt
new file mode 100644
index 0000000000..f8d2dabc6c
--- /dev/null
+++ b/indra/media_plugins/base/CMakeLists.txt
@@ -0,0 +1,41 @@
+# -*- cmake -*-
+
+project(media_plugin_base)
+
+include(00-Common)
+include(LLCommon)
+include(LLImage)
+include(LLPlugin)
+include(LLMath)
+include(LLRender)
+include(LLWindow)
+include(Linking)
+include(PluginAPI)
+include(FindOpenGL)
+
+include_directories(
+ ${LLPLUGIN_INCLUDE_DIRS}
+ ${LLCOMMON_INCLUDE_DIRS}
+ ${LLMATH_INCLUDE_DIRS}
+ ${LLIMAGE_INCLUDE_DIRS}
+ ${LLRENDER_INCLUDE_DIRS}
+ ${LLWINDOW_INCLUDE_DIRS}
+)
+
+
+### media_plugin_base
+
+set(media_plugin_base_SOURCE_FILES
+ media_plugin_base.cpp
+)
+
+set(media_plugin_base_HEADER_FILES
+ CMakeLists.txt
+
+ media_plugin_base.h
+)
+
+add_library(media_plugin_base
+ ${media_plugin_base_SOURCE_FILES}
+)
+
diff --git a/indra/media_plugins/base/media_plugin_base.cpp b/indra/media_plugins/base/media_plugin_base.cpp
new file mode 100644
index 0000000000..37c498664a
--- /dev/null
+++ b/indra/media_plugins/base/media_plugin_base.cpp
@@ -0,0 +1,206 @@
+/**
+ * @file media_plugin_base.cpp
+ * @brief Media plugin base class for LLMedia API plugin system
+ *
+ * All plugins should be a subclass of MediaPluginBase.
+ *
+ * @cond
+ * $LicenseInfo:firstyear=2008&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 "linden_common.h"
+#include "media_plugin_base.h"
+
+
+// TODO: Make sure that the only symbol exported from this library is LLPluginInitEntryPoint
+////////////////////////////////////////////////////////////////////////////////
+/// Media plugin constructor.
+///
+/// @param[in] host_send_func Function for sending messages from plugin to plugin loader shell
+/// @param[in] host_user_data Message data for messages from plugin to plugin loader shell
+
+MediaPluginBase::MediaPluginBase(
+ LLPluginInstance::sendMessageFunction host_send_func,
+ void *host_user_data )
+{
+ mHostSendFunction = host_send_func;
+ mHostUserData = host_user_data;
+ mDeleteMe = false;
+ mPixels = 0;
+ mWidth = 0;
+ mHeight = 0;
+ mTextureWidth = 0;
+ mTextureHeight = 0;
+ mDepth = 0;
+ mStatus = STATUS_NONE;
+}
+
+/**
+ * Converts current media status enum value into string (STATUS_LOADING into "loading", etc.)
+ *
+ * @return Media status string ("loading", "playing", "paused", etc)
+ *
+ */
+std::string MediaPluginBase::statusString()
+{
+ std::string result;
+
+ switch(mStatus)
+ {
+ case STATUS_LOADING: result = "loading"; break;
+ case STATUS_LOADED: result = "loaded"; break;
+ case STATUS_ERROR: result = "error"; break;
+ case STATUS_PLAYING: result = "playing"; break;
+ case STATUS_PAUSED: result = "paused"; break;
+ case STATUS_DONE: result = "done"; break;
+ default:
+ // keep the empty string
+ break;
+ }
+
+ return result;
+}
+
+/**
+ * Set media status.
+ *
+ * @param[in] status Media status (STATUS_LOADING, STATUS_PLAYING, STATUS_PAUSED, etc)
+ *
+ */
+void MediaPluginBase::setStatus(EStatus status)
+{
+ if(mStatus != status)
+ {
+ mStatus = status;
+ sendStatus();
+ }
+}
+
+
+/**
+ * Receive message from plugin loader shell.
+ *
+ * @param[in] message_string Message string
+ * @param[in] user_data Message data
+ *
+ */
+void MediaPluginBase::staticReceiveMessage(const char *message_string, void **user_data)
+{
+ MediaPluginBase *self = (MediaPluginBase*)*user_data;
+
+ if(self != NULL)
+ {
+ self->receiveMessage(message_string);
+
+ // If the plugin has processed the delete message, delete it.
+ if(self->mDeleteMe)
+ {
+ delete self;
+ *user_data = NULL;
+ }
+ }
+}
+
+/**
+ * Send message to plugin loader shell.
+ *
+ * @param[in] message Message data being sent to plugin loader shell
+ *
+ */
+void MediaPluginBase::sendMessage(const LLPluginMessage &message)
+{
+ std::string output = message.generate();
+ mHostSendFunction(output.c_str(), &mHostUserData);
+}
+
+/**
+ * Notifies plugin loader shell that part of display area needs to be redrawn.
+ *
+ * @param[in] left Left X coordinate of area to redraw (0,0 is at top left corner)
+ * @param[in] top Top Y coordinate of area to redraw (0,0 is at top left corner)
+ * @param[in] right Right X-coordinate of area to redraw (0,0 is at top left corner)
+ * @param[in] bottom Bottom Y-coordinate of area to redraw (0,0 is at top left corner)
+ *
+ */
+void MediaPluginBase::setDirty(int left, int top, int right, int bottom)
+{
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "updated");
+
+ message.setValueS32("left", left);
+ message.setValueS32("top", top);
+ message.setValueS32("right", right);
+ message.setValueS32("bottom", bottom);
+
+ sendMessage(message);
+}
+
+/**
+ * Sends "media_status" message to plugin loader shell ("loading", "playing", "paused", etc.)
+ *
+ */
+void MediaPluginBase::sendStatus()
+{
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "media_status");
+
+ message.setValue("status", statusString());
+
+ sendMessage(message);
+}
+
+
+#if LL_WINDOWS
+# define LLSYMEXPORT __declspec(dllexport)
+#elif LL_LINUX
+# define LLSYMEXPORT __attribute__ ((visibility("default")))
+#else
+# define LLSYMEXPORT /**/
+#endif
+
+extern "C"
+{
+ LLSYMEXPORT int LLPluginInitEntryPoint(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data);
+}
+
+/**
+ * Plugin initialization and entry point. Establishes communication channel for messages between plugin and plugin loader shell. TODO:DOC - Please check!
+ *
+ * @param[in] host_send_func Function for sending messages from plugin to plugin loader shell
+ * @param[in] host_user_data Message data for messages from plugin to plugin loader shell
+ * @param[out] plugin_send_func Function for plugin to receive messages from plugin loader shell
+ * @param[out] plugin_user_data Pointer to plugin instance
+ *
+ * @return int, where 0=success
+ *
+ */
+LLSYMEXPORT int
+LLPluginInitEntryPoint(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data)
+{
+ return init_media_plugin(host_send_func, host_user_data, plugin_send_func, plugin_user_data);
+}
+
+#ifdef WIN32
+int WINAPI DllEntryPoint( HINSTANCE hInstance, unsigned long reason, void* params )
+{
+ return 1;
+}
+#endif
diff --git a/indra/media_plugins/base/media_plugin_base.exp b/indra/media_plugins/base/media_plugin_base.exp
new file mode 100644
index 0000000000..d8c7bb712a
--- /dev/null
+++ b/indra/media_plugins/base/media_plugin_base.exp
@@ -0,0 +1,2 @@
+_LLPluginInitEntryPoint
+
diff --git a/indra/media_plugins/base/media_plugin_base.h b/indra/media_plugins/base/media_plugin_base.h
new file mode 100644
index 0000000000..38b8226bb3
--- /dev/null
+++ b/indra/media_plugins/base/media_plugin_base.h
@@ -0,0 +1,129 @@
+/**
+ * @file media_plugin_base.h
+ * @brief Media plugin base class for LLMedia API plugin system
+ *
+ * @cond
+ * $LicenseInfo:firstyear=2008&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 "linden_common.h"
+
+#include "llplugininstance.h"
+#include "llpluginmessage.h"
+#include "llpluginmessageclasses.h"
+
+
+class MediaPluginBase
+{
+public:
+ MediaPluginBase(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data);
+ /** Media plugin destructor. */
+ virtual ~MediaPluginBase() {}
+
+ /** Handle received message from plugin loader shell. */
+ virtual void receiveMessage(const char *message_string) = 0;
+
+ static void staticReceiveMessage(const char *message_string, void **user_data);
+
+protected:
+
+ /** Plugin status. */
+ typedef enum
+ {
+ STATUS_NONE,
+ STATUS_LOADING,
+ STATUS_LOADED,
+ STATUS_ERROR,
+ STATUS_PLAYING,
+ STATUS_PAUSED,
+ STATUS_DONE
+ } EStatus;
+
+ /** Plugin shared memory. */
+ class SharedSegmentInfo
+ {
+ public:
+ /** Shared memory address. */
+ void *mAddress;
+ /** Shared memory size. */
+ size_t mSize;
+ };
+
+ void sendMessage(const LLPluginMessage &message);
+ void sendStatus();
+ std::string statusString();
+ void setStatus(EStatus status);
+
+ /// Note: The quicktime plugin overrides this to add current time and duration to the message.
+ virtual void setDirty(int left, int top, int right, int bottom);
+
+ /** Map of shared memory names to shared memory. */
+ typedef std::map<std::string, SharedSegmentInfo> SharedSegmentMap;
+
+
+ /** Function to send message from plugin to plugin loader shell. */
+ LLPluginInstance::sendMessageFunction mHostSendFunction;
+ /** Message data being sent to plugin loader shell by mHostSendFunction. */
+ void *mHostUserData;
+ /** Flag to delete plugin instance (self). */
+ bool mDeleteMe;
+ /** Pixel array to display. TODO:DOC are pixels always 24-bit RGB format, aligned on 32-bit boundary? Also: calling this a pixel array may be misleading since 1 pixel > 1 char. */
+ unsigned char* mPixels;
+ /** TODO:DOC what's this for -- does a texture have its own piece of shared memory? updated on size_change_request, cleared on shm_remove */
+ std::string mTextureSegmentName;
+ /** Width of plugin display in pixels. */
+ int mWidth;
+ /** Height of plugin display in pixels. */
+ int mHeight;
+ /** Width of plugin texture. */
+ int mTextureWidth;
+ /** Height of plugin texture. */
+ int mTextureHeight;
+ /** Pixel depth (pixel size in bytes). */
+ int mDepth;
+ /** Current status of plugin. */
+ EStatus mStatus;
+ /** Map of shared memory segments. */
+ SharedSegmentMap mSharedSegments;
+
+};
+
+/** The plugin <b>must</b> define this function to create its instance.
+ * It should look something like this:
+ * @code
+ * {
+ * MediaPluginFoo *self = new MediaPluginFoo(host_send_func, host_user_data);
+ * *plugin_send_func = MediaPluginFoo::staticReceiveMessage;
+ * *plugin_user_data = (void*)self;
+ *
+ * return 0;
+ * }
+ * @endcode
+ */
+int init_media_plugin(
+ LLPluginInstance::sendMessageFunction host_send_func,
+ void *host_user_data,
+ LLPluginInstance::sendMessageFunction *plugin_send_func,
+ void **plugin_user_data);
+
+
diff --git a/indra/media_plugins/example/CMakeLists.txt b/indra/media_plugins/example/CMakeLists.txt
new file mode 100644
index 0000000000..4d82f2747c
--- /dev/null
+++ b/indra/media_plugins/example/CMakeLists.txt
@@ -0,0 +1,74 @@
+# -*- cmake -*-
+
+project(media_plugin_example)
+
+include(00-Common)
+include(LLCommon)
+include(LLImage)
+include(LLPlugin)
+include(LLMath)
+include(LLRender)
+include(LLWindow)
+include(Linking)
+include(PluginAPI)
+include(MediaPluginBase)
+include(FindOpenGL)
+
+include(ExamplePlugin)
+
+include_directories(
+ ${LLPLUGIN_INCLUDE_DIRS}
+ ${MEDIA_PLUGIN_BASE_INCLUDE_DIRS}
+ ${LLCOMMON_INCLUDE_DIRS}
+ ${LLMATH_INCLUDE_DIRS}
+ ${LLIMAGE_INCLUDE_DIRS}
+ ${LLRENDER_INCLUDE_DIRS}
+ ${LLWINDOW_INCLUDE_DIRS}
+)
+
+
+### media_plugin_example
+
+set(media_plugin_example_SOURCE_FILES
+ media_plugin_example.cpp
+ )
+
+add_library(media_plugin_example
+ SHARED
+ ${media_plugin_example_SOURCE_FILES}
+)
+
+target_link_libraries(media_plugin_example
+ ${LLPLUGIN_LIBRARIES}
+ ${MEDIA_PLUGIN_BASE_LIBRARIES}
+ ${LLCOMMON_LIBRARIES}
+ ${EXAMPLE_PLUGIN_LIBRARIES}
+ ${PLUGIN_API_WINDOWS_LIBRARIES}
+)
+
+add_dependencies(media_plugin_example
+ ${LLPLUGIN_LIBRARIES}
+ ${MEDIA_PLUGIN_BASE_LIBRARIES}
+ ${LLCOMMON_LIBRARIES}
+)
+
+if (WINDOWS)
+ set_target_properties(
+ media_plugin_example
+ PROPERTIES
+ LINK_FLAGS "/MANIFEST:NO"
+ )
+endif (WINDOWS)
+
+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_example
+ PROPERTIES
+ PREFIX ""
+ BUILD_WITH_INSTALL_RPATH 1
+ INSTALL_NAME_DIR "@executable_path"
+ LINK_FLAGS "-exported_symbols_list ${CMAKE_CURRENT_SOURCE_DIR}/../base/media_plugin_base.exp"
+ )
+
+endif (DARWIN) \ No newline at end of file
diff --git a/indra/media_plugins/example/media_plugin_example.cpp b/indra/media_plugins/example/media_plugin_example.cpp
new file mode 100644
index 0000000000..f8a871930e
--- /dev/null
+++ b/indra/media_plugins/example/media_plugin_example.cpp
@@ -0,0 +1,487 @@
+/**
+ * @file media_plugin_example.cpp
+ * @brief Example plugin for LLMedia API plugin system
+ *
+ * @cond
+ * $LicenseInfo:firstyear=2008&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 "linden_common.h"
+
+#include "llgl.h"
+#include "llplugininstance.h"
+#include "llpluginmessage.h"
+#include "llpluginmessageclasses.h"
+#include "media_plugin_base.h"
+
+#include <time.h>
+
+////////////////////////////////////////////////////////////////////////////////
+//
+class MediaPluginExample :
+ public MediaPluginBase
+{
+ public:
+ MediaPluginExample( LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data );
+ ~MediaPluginExample();
+
+ /*virtual*/ void receiveMessage( const char* message_string );
+
+ private:
+ bool init();
+ void update( F64 milliseconds );
+ void write_pixel( int x, int y, unsigned char r, unsigned char g, unsigned char b );
+ bool mFirstTime;
+
+ time_t mLastUpdateTime;
+ enum Constants { ENumObjects = 10 };
+ unsigned char* mBackgroundPixels;
+ int mColorR[ ENumObjects ];
+ int mColorG[ ENumObjects ];
+ int mColorB[ ENumObjects ];
+ int mXpos[ ENumObjects ];
+ int mYpos[ ENumObjects ];
+ int mXInc[ ENumObjects ];
+ int mYInc[ ENumObjects ];
+ int mBlockSize[ ENumObjects ];
+ bool mMouseButtonDown;
+ bool mStopAction;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//
+MediaPluginExample::MediaPluginExample( LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data ) :
+ MediaPluginBase( host_send_func, host_user_data )
+{
+ mFirstTime = true;
+ mWidth = 0;
+ mHeight = 0;
+ mDepth = 4;
+ mPixels = 0;
+ mMouseButtonDown = false;
+ mStopAction = false;
+ mLastUpdateTime = 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+MediaPluginExample::~MediaPluginExample()
+{
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void MediaPluginExample::receiveMessage( const char* message_string )
+{
+ LLPluginMessage message_in;
+
+ if ( message_in.parse( message_string ) >= 0 )
+ {
+ std::string message_class = message_in.getClass();
+ std::string message_name = message_in.getName();
+
+ if ( message_class == LLPLUGIN_MESSAGE_CLASS_BASE )
+ {
+ if ( message_name == "init" )
+ {
+ LLPluginMessage message( "base", "init_response" );
+ LLSD versions = LLSD::emptyMap();
+ versions[ LLPLUGIN_MESSAGE_CLASS_BASE ] = LLPLUGIN_MESSAGE_CLASS_BASE_VERSION;
+ versions[ LLPLUGIN_MESSAGE_CLASS_MEDIA ] = LLPLUGIN_MESSAGE_CLASS_MEDIA_VERSION;
+ versions[ LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER ] = LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER_VERSION;
+ message.setValueLLSD( "versions", versions );
+
+ std::string plugin_version = "Example media plugin, Example Version 1.0.0.0";
+ message.setValue( "plugin_version", plugin_version );
+ sendMessage( message );
+ }
+ else
+ if ( message_name == "idle" )
+ {
+ // no response is necessary here.
+ F64 time = message_in.getValueReal( "time" );
+
+ // Convert time to milliseconds for update()
+ update( time );
+ }
+ else
+ if ( message_name == "cleanup" )
+ {
+ // clean up here
+ }
+ else
+ if ( message_name == "shm_added" )
+ {
+ SharedSegmentInfo info;
+ info.mAddress = message_in.getValuePointer( "address" );
+ info.mSize = ( size_t )message_in.getValueS32( "size" );
+ std::string name = message_in.getValue( "name" );
+
+ mSharedSegments.insert( SharedSegmentMap::value_type( name, info ) );
+
+ }
+ else
+ if ( message_name == "shm_remove" )
+ {
+ std::string name = message_in.getValue( "name" );
+
+ SharedSegmentMap::iterator iter = mSharedSegments.find( name );
+ if( iter != mSharedSegments.end() )
+ {
+ if ( mPixels == iter->second.mAddress )
+ {
+ // This is the currently active pixel buffer.
+ // Make sure we stop drawing to it.
+ mPixels = NULL;
+ mTextureSegmentName.clear();
+ };
+ mSharedSegments.erase( iter );
+ }
+ else
+ {
+ //std::cerr << "MediaPluginExample::receiveMessage: unknown shared memory region!" << std::endl;
+ };
+
+ // Send the response so it can be cleaned up.
+ LLPluginMessage message( "base", "shm_remove_response" );
+ message.setValue( "name", name );
+ sendMessage( message );
+ }
+ else
+ {
+ //std::cerr << "MediaPluginExample::receiveMessage: unknown base message: " << message_name << std::endl;
+ };
+ }
+ else
+ if ( message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA )
+ {
+ if ( message_name == "init" )
+ {
+ // Plugin gets to decide the texture parameters to use.
+ LLPluginMessage message( LLPLUGIN_MESSAGE_CLASS_MEDIA, "texture_params" );
+ message.setValueS32( "default_width", mWidth );
+ message.setValueS32( "default_height", mHeight );
+ message.setValueS32( "depth", mDepth );
+ message.setValueU32( "internalformat", GL_RGBA );
+ message.setValueU32( "format", GL_RGBA );
+ message.setValueU32( "type", GL_UNSIGNED_BYTE );
+ message.setValueBoolean( "coords_opengl", false );
+ sendMessage( message );
+ }
+ else if ( message_name == "size_change" )
+ {
+ std::string name = message_in.getValue( "name" );
+ S32 width = message_in.getValueS32( "width" );
+ S32 height = message_in.getValueS32( "height" );
+ S32 texture_width = message_in.getValueS32( "texture_width" );
+ S32 texture_height = message_in.getValueS32( "texture_height" );
+
+ if ( ! name.empty() )
+ {
+ // Find the shared memory region with this name
+ SharedSegmentMap::iterator iter = mSharedSegments.find( name );
+ if ( iter != mSharedSegments.end() )
+ {
+ mPixels = ( unsigned char* )iter->second.mAddress;
+ mWidth = width;
+ mHeight = height;
+
+ mTextureWidth = texture_width;
+ mTextureHeight = texture_height;
+
+ init();
+ };
+ };
+
+ LLPluginMessage message( LLPLUGIN_MESSAGE_CLASS_MEDIA, "size_change_response" );
+ message.setValue( "name", name );
+ message.setValueS32( "width", width );
+ message.setValueS32( "height", height );
+ message.setValueS32( "texture_width", texture_width );
+ message.setValueS32( "texture_height", texture_height );
+ sendMessage( message );
+ }
+ else
+ if ( message_name == "load_uri" )
+ {
+ std::string uri = message_in.getValue( "uri" );
+ if ( ! uri.empty() )
+ {
+ };
+ }
+ else
+ if ( message_name == "mouse_event" )
+ {
+ std::string event = message_in.getValue( "event" );
+ S32 button = message_in.getValueS32( "button" );
+
+ // left mouse button
+ if ( button == 0 )
+ {
+ int mouse_x = message_in.getValueS32( "x" );
+ int mouse_y = message_in.getValueS32( "y" );
+ std::string modifiers = message_in.getValue( "modifiers" );
+
+ if ( event == "move" )
+ {
+ if ( mMouseButtonDown )
+ write_pixel( mouse_x, mouse_y, rand() % 0x80 + 0x80, rand() % 0x80 + 0x80, rand() % 0x80 + 0x80 );
+ }
+ else
+ if ( event == "down" )
+ {
+ mMouseButtonDown = true;
+ }
+ else
+ if ( event == "up" )
+ {
+ mMouseButtonDown = false;
+ }
+ else
+ if ( event == "double_click" )
+ {
+ };
+ };
+ }
+ else
+ if ( message_name == "key_event" )
+ {
+ std::string event = message_in.getValue( "event" );
+ S32 key = message_in.getValueS32( "key" );
+ std::string modifiers = message_in.getValue( "modifiers" );
+
+ if ( event == "down" )
+ {
+ if ( key == ' ')
+ {
+ mLastUpdateTime = 0;
+ update( 0.0f );
+ };
+ };
+ }
+ else
+ {
+ //std::cerr << "MediaPluginExample::receiveMessage: unknown media message: " << message_string << std::endl;
+ };
+ }
+ else
+ if ( message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER )
+ {
+ if ( message_name == "browse_reload" )
+ {
+ mLastUpdateTime = 0;
+ mFirstTime = true;
+ mStopAction = false;
+ update( 0.0f );
+ }
+ else
+ if ( message_name == "browse_stop" )
+ {
+ for( int n = 0; n < ENumObjects; ++n )
+ mXInc[ n ] = mYInc[ n ] = 0;
+
+ mStopAction = true;
+ update( 0.0f );
+ }
+ else
+ {
+ //std::cerr << "MediaPluginExample::receiveMessage: unknown media_browser message: " << message_string << std::endl;
+ };
+ }
+ else
+ {
+ //std::cerr << "MediaPluginExample::receiveMessage: unknown message class: " << message_class << std::endl;
+ };
+ };
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void MediaPluginExample::write_pixel( int x, int y, unsigned char r, unsigned char g, unsigned char b )
+{
+ // make sure we don't write outside the buffer
+ if ( ( x < 0 ) || ( x >= mWidth ) || ( y < 0 ) || ( y >= mHeight ) )
+ return;
+
+ if ( mBackgroundPixels != NULL )
+ {
+ unsigned char *pixel = mBackgroundPixels;
+ pixel += y * mWidth * mDepth;
+ pixel += ( x * mDepth );
+ pixel[ 0 ] = b;
+ pixel[ 1 ] = g;
+ pixel[ 2 ] = r;
+
+ setDirty( x, y, x + 1, y + 1 );
+ };
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+void MediaPluginExample::update( F64 milliseconds )
+{
+ if ( mWidth < 1 || mWidth > 2048 || mHeight < 1 || mHeight > 2048 )
+ return;
+
+ if ( mPixels == 0 )
+ return;
+
+ if ( mFirstTime )
+ {
+ for( int n = 0; n < ENumObjects; ++n )
+ {
+ mXpos[ n ] = ( mWidth / 2 ) + rand() % ( mWidth / 16 ) - ( mWidth / 32 );
+ mYpos[ n ] = ( mHeight / 2 ) + rand() % ( mHeight / 16 ) - ( mHeight / 32 );
+
+ mColorR[ n ] = rand() % 0x60 + 0x60;
+ mColorG[ n ] = rand() % 0x60 + 0x60;
+ mColorB[ n ] = rand() % 0x60 + 0x60;
+
+ mXInc[ n ] = 0;
+ while ( mXInc[ n ] == 0 )
+ mXInc[ n ] = rand() % 7 - 3;
+
+ mYInc[ n ] = 0;
+ while ( mYInc[ n ] == 0 )
+ mYInc[ n ] = rand() % 9 - 4;
+
+ mBlockSize[ n ] = rand() % 0x30 + 0x10;
+ };
+
+ delete [] mBackgroundPixels;
+
+ mBackgroundPixels = new unsigned char[ mWidth * mHeight * mDepth ];
+
+ mFirstTime = false;
+ };
+
+ if ( mStopAction )
+ return;
+
+ if ( time( NULL ) > mLastUpdateTime + 3 )
+ {
+ const int num_squares = rand() % 20 + 4;
+ int sqr1_r = rand() % 0x80 + 0x20;
+ int sqr1_g = rand() % 0x80 + 0x20;
+ int sqr1_b = rand() % 0x80 + 0x20;
+ int sqr2_r = rand() % 0x80 + 0x20;
+ int sqr2_g = rand() % 0x80 + 0x20;
+ int sqr2_b = rand() % 0x80 + 0x20;
+
+ for ( int y1 = 0; y1 < num_squares; ++y1 )
+ {
+ for ( int x1 = 0; x1 < num_squares; ++x1 )
+ {
+ int px_start = mWidth * x1 / num_squares;
+ int px_end = ( mWidth * ( x1 + 1 ) ) / num_squares;
+ int py_start = mHeight * y1 / num_squares;
+ int py_end = ( mHeight * ( y1 + 1 ) ) / num_squares;
+
+ for( int y2 = py_start; y2 < py_end; ++y2 )
+ {
+ for( int x2 = px_start; x2 < px_end; ++x2 )
+ {
+ int rowspan = mWidth * mDepth;
+
+ if ( ( y1 % 2 ) ^ ( x1 % 2 ) )
+ {
+ mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 0 ] = sqr1_r;
+ mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 1 ] = sqr1_g;
+ mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 2 ] = sqr1_b;
+ }
+ else
+ {
+ mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 0 ] = sqr2_r;
+ mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 1 ] = sqr2_g;
+ mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 2 ] = sqr2_b;
+ };
+ };
+ };
+ };
+ };
+
+ time( &mLastUpdateTime );
+ };
+
+ memcpy( mPixels, mBackgroundPixels, mWidth * mHeight * mDepth );
+
+ for( int n = 0; n < ENumObjects; ++n )
+ {
+ if ( rand() % 50 == 0 )
+ {
+ mXInc[ n ] = 0;
+ while ( mXInc[ n ] == 0 )
+ mXInc[ n ] = rand() % 7 - 3;
+
+ mYInc[ n ] = 0;
+ while ( mYInc[ n ] == 0 )
+ mYInc[ n ] = rand() % 9 - 4;
+ };
+
+ if ( mXpos[ n ] + mXInc[ n ] < 0 || mXpos[ n ] + mXInc[ n ] >= mWidth - mBlockSize[ n ] )
+ mXInc[ n ] =- mXInc[ n ];
+
+ if ( mYpos[ n ] + mYInc[ n ] < 0 || mYpos[ n ] + mYInc[ n ] >= mHeight - mBlockSize[ n ] )
+ mYInc[ n ] =- mYInc[ n ];
+
+ mXpos[ n ] += mXInc[ n ];
+ mYpos[ n ] += mYInc[ n ];
+
+ for( int y = 0; y < mBlockSize[ n ]; ++y )
+ {
+ for( int x = 0; x < mBlockSize[ n ]; ++x )
+ {
+ mPixels[ ( mXpos[ n ] + x ) * mDepth + ( mYpos[ n ] + y ) * mDepth * mWidth + 0 ] = mColorR[ n ];
+ mPixels[ ( mXpos[ n ] + x ) * mDepth + ( mYpos[ n ] + y ) * mDepth * mWidth + 1 ] = mColorG[ n ];
+ mPixels[ ( mXpos[ n ] + x ) * mDepth + ( mYpos[ n ] + y ) * mDepth * mWidth + 2 ] = mColorB[ n ];
+ };
+ };
+ };
+
+ setDirty( 0, 0, mWidth, mHeight );
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//
+bool MediaPluginExample::init()
+{
+ LLPluginMessage message( LLPLUGIN_MESSAGE_CLASS_MEDIA, "name_text" );
+ message.setValue( "name", "Example Plugin" );
+ sendMessage( message );
+
+ return true;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//
+int init_media_plugin( LLPluginInstance::sendMessageFunction host_send_func,
+ void* host_user_data,
+ LLPluginInstance::sendMessageFunction *plugin_send_func,
+ void **plugin_user_data )
+{
+ MediaPluginExample* self = new MediaPluginExample( host_send_func, host_user_data );
+ *plugin_send_func = MediaPluginExample::staticReceiveMessage;
+ *plugin_user_data = ( void* )self;
+
+ return 0;
+}
diff --git a/indra/media_plugins/gstreamer010/CMakeLists.txt b/indra/media_plugins/gstreamer010/CMakeLists.txt
new file mode 100644
index 0000000000..a5127ae5f4
--- /dev/null
+++ b/indra/media_plugins/gstreamer010/CMakeLists.txt
@@ -0,0 +1,64 @@
+# -*- cmake -*-
+
+project(media_plugin_gstreamer010)
+
+include(00-Common)
+include(LLCommon)
+include(LLImage)
+include(LLPlugin)
+include(LLMath)
+include(LLRender)
+include(LLWindow)
+include(Linking)
+include(PluginAPI)
+include(MediaPluginBase)
+include(FindOpenGL)
+
+include(GStreamer010Plugin)
+
+include_directories(
+ ${LLPLUGIN_INCLUDE_DIRS}
+ ${MEDIA_PLUGIN_BASE_INCLUDE_DIRS}
+ ${LLCOMMON_INCLUDE_DIRS}
+ ${LLMATH_INCLUDE_DIRS}
+ ${LLIMAGE_INCLUDE_DIRS}
+ ${LLRENDER_INCLUDE_DIRS}
+ ${LLWINDOW_INCLUDE_DIRS}
+ ${GSTREAMER010_INCLUDE_DIRS}
+ ${GSTREAMER010_PLUGINS_BASE_INCLUDE_DIRS}
+)
+
+### media_plugin_gstreamer010
+
+set(media_plugin_gstreamer010_SOURCE_FILES
+ media_plugin_gstreamer010.cpp
+ llmediaimplgstreamer_syms.cpp
+ llmediaimplgstreamervidplug.cpp
+ )
+
+set(media_plugin_gstreamer010_HEADER_FILES
+ llmediaimplgstreamervidplug.h
+ llmediaimplgstreamer_syms.h
+ llmediaimplgstreamertriviallogging.h
+ )
+
+add_library(media_plugin_gstreamer010
+ SHARED
+ ${media_plugin_gstreamer010_SOURCE_FILES}
+)
+
+target_link_libraries(media_plugin_gstreamer010
+ ${LLPLUGIN_LIBRARIES}
+ ${MEDIA_PLUGIN_BASE_LIBRARIES}
+ ${LLCOMMON_LIBRARIES}
+ ${PLUGIN_API_WINDOWS_LIBRARIES}
+ ${GSTREAMER010_LIBRARIES}
+)
+
+add_dependencies(media_plugin_gstreamer010
+ ${LLPLUGIN_LIBRARIES}
+ ${MEDIA_PLUGIN_BASE_LIBRARIES}
+ ${LLCOMMON_LIBRARIES}
+)
+
+
diff --git a/indra/media_plugins/gstreamer010/llmediaimplgstreamer.h b/indra/media_plugins/gstreamer010/llmediaimplgstreamer.h
new file mode 100644
index 0000000000..6bc272c009
--- /dev/null
+++ b/indra/media_plugins/gstreamer010/llmediaimplgstreamer.h
@@ -0,0 +1,53 @@
+/**
+ * @file llmediaimplgstreamer.h
+ * @author Tofu Linden
+ * @brief implementation that supports media playback via GStreamer.
+ *
+ * @cond
+ * $LicenseInfo:firstyear=2007&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
+ */
+
+// header guard
+#ifndef llmediaimplgstreamer_h
+#define llmediaimplgstreamer_h
+
+#if LL_GSTREAMER010_ENABLED
+
+extern "C" {
+#include <stdio.h>
+#include <gst/gst.h>
+
+#include "apr_pools.h"
+#include "apr_dso.h"
+}
+
+
+extern "C" {
+gboolean llmediaimplgstreamer_bus_callback (GstBus *bus,
+ GstMessage *message,
+ gpointer data);
+}
+
+#endif // LL_GSTREAMER010_ENABLED
+
+#endif // llmediaimplgstreamer_h
diff --git a/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms.cpp b/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms.cpp
new file mode 100644
index 0000000000..2e4baaa9eb
--- /dev/null
+++ b/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms.cpp
@@ -0,0 +1,167 @@
+/**
+ * @file llmediaimplgstreamer_syms.cpp
+ * @brief dynamic GStreamer symbol-grabbing code
+ *
+ * @cond
+ * $LicenseInfo:firstyear=2007&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
+ */
+
+#if LL_GSTREAMER010_ENABLED
+
+#include <string>
+
+extern "C" {
+#include <gst/gst.h>
+
+#include "apr_pools.h"
+#include "apr_dso.h"
+}
+
+#include "llmediaimplgstreamertriviallogging.h"
+
+#define LL_GST_SYM(REQ, GSTSYM, RTN, ...) RTN (*ll##GSTSYM)(__VA_ARGS__) = NULL
+#include "llmediaimplgstreamer_syms_raw.inc"
+#include "llmediaimplgstreamer_syms_rawv.inc"
+#undef LL_GST_SYM
+
+// a couple of stubs for disgusting reasons
+GstDebugCategory*
+ll_gst_debug_category_new(gchar *name, guint color, gchar *description)
+{
+ static GstDebugCategory dummy;
+ return &dummy;
+}
+void ll_gst_debug_register_funcptr(GstDebugFuncPtr func, gchar* ptrname)
+{
+}
+
+static bool sSymsGrabbed = false;
+static apr_pool_t *sSymGSTDSOMemoryPool = NULL;
+static apr_dso_handle_t *sSymGSTDSOHandleG = NULL;
+static apr_dso_handle_t *sSymGSTDSOHandleV = NULL;
+
+
+bool grab_gst_syms(std::string gst_dso_name,
+ std::string gst_dso_name_vid)
+{
+ if (sSymsGrabbed)
+ {
+ // already have grabbed good syms
+ return TRUE;
+ }
+
+ bool sym_error = false;
+ bool rtn = false;
+ apr_status_t rv;
+ apr_dso_handle_t *sSymGSTDSOHandle = NULL;
+
+#define LL_GST_SYM(REQ, GSTSYM, RTN, ...) do{rv = apr_dso_sym((apr_dso_handle_sym_t*)&ll##GSTSYM, sSymGSTDSOHandle, #GSTSYM); if (rv != APR_SUCCESS) {INFOMSG("Failed to grab symbol: %s", #GSTSYM); if (REQ) sym_error = true;} else DEBUGMSG("grabbed symbol: %s from %p", #GSTSYM, (void*)ll##GSTSYM);}while(0)
+
+ //attempt to load the shared libraries
+ apr_pool_create(&sSymGSTDSOMemoryPool, NULL);
+
+ if ( APR_SUCCESS == (rv = apr_dso_load(&sSymGSTDSOHandle,
+ gst_dso_name.c_str(),
+ sSymGSTDSOMemoryPool) ))
+ {
+ INFOMSG("Found DSO: %s", gst_dso_name.c_str());
+#include "llmediaimplgstreamer_syms_raw.inc"
+
+ if ( sSymGSTDSOHandle )
+ {
+ sSymGSTDSOHandleG = sSymGSTDSOHandle;
+ sSymGSTDSOHandle = NULL;
+ }
+
+ if ( APR_SUCCESS ==
+ (rv = apr_dso_load(&sSymGSTDSOHandle,
+ gst_dso_name_vid.c_str(),
+ sSymGSTDSOMemoryPool) ))
+ {
+ INFOMSG("Found DSO: %s", gst_dso_name_vid.c_str());
+#include "llmediaimplgstreamer_syms_rawv.inc"
+ rtn = !sym_error;
+ }
+ else
+ {
+ INFOMSG("Couldn't load DSO: %s", gst_dso_name_vid.c_str());
+ rtn = false; // failure
+ }
+ }
+ else
+ {
+ INFOMSG("Couldn't load DSO: %s", gst_dso_name.c_str());
+ rtn = false; // failure
+ }
+
+ if (sym_error)
+ {
+ WARNMSG("Failed to find necessary symbols in GStreamer libraries.");
+ }
+
+ if ( sSymGSTDSOHandle )
+ {
+ sSymGSTDSOHandleV = sSymGSTDSOHandle;
+ sSymGSTDSOHandle = NULL;
+ }
+#undef LL_GST_SYM
+
+ sSymsGrabbed = !!rtn;
+ return rtn;
+}
+
+
+void ungrab_gst_syms()
+{
+ // should be safe to call regardless of whether we've
+ // actually grabbed syms.
+
+ if ( sSymGSTDSOHandleG )
+ {
+ apr_dso_unload(sSymGSTDSOHandleG);
+ sSymGSTDSOHandleG = NULL;
+ }
+
+ if ( sSymGSTDSOHandleV )
+ {
+ apr_dso_unload(sSymGSTDSOHandleV);
+ sSymGSTDSOHandleV = NULL;
+ }
+
+ if ( sSymGSTDSOMemoryPool )
+ {
+ apr_pool_destroy(sSymGSTDSOMemoryPool);
+ sSymGSTDSOMemoryPool = NULL;
+ }
+
+ // NULL-out all of the symbols we'd grabbed
+#define LL_GST_SYM(REQ, GSTSYM, RTN, ...) do{ll##GSTSYM = NULL;}while(0)
+#include "llmediaimplgstreamer_syms_raw.inc"
+#include "llmediaimplgstreamer_syms_rawv.inc"
+#undef LL_GST_SYM
+
+ sSymsGrabbed = false;
+}
+
+
+#endif // LL_GSTREAMER010_ENABLED
diff --git a/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms.h b/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms.h
new file mode 100644
index 0000000000..d1559089c8
--- /dev/null
+++ b/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms.h
@@ -0,0 +1,74 @@
+/**
+ * @file llmediaimplgstreamer_syms.h
+ * @brief dynamic GStreamer symbol-grabbing code
+ *
+ * @cond
+ * $LicenseInfo:firstyear=2007&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 "linden_common.h"
+
+#if LL_GSTREAMER010_ENABLED
+
+extern "C" {
+#include <gst/gst.h>
+}
+
+bool grab_gst_syms(std::string gst_dso_name,
+ std::string gst_dso_name_vid);
+void ungrab_gst_syms();
+
+#define LL_GST_SYM(REQ, GSTSYM, RTN, ...) extern RTN (*ll##GSTSYM)(__VA_ARGS__)
+#include "llmediaimplgstreamer_syms_raw.inc"
+#include "llmediaimplgstreamer_syms_rawv.inc"
+#undef LL_GST_SYM
+
+// regrettable hacks to give us better runtime compatibility with older systems
+#define llg_return_if_fail(COND) do{if (!(COND)) return;}while(0)
+#define llg_return_val_if_fail(COND,V) do{if (!(COND)) return V;}while(0)
+
+// regrettable hacks because GStreamer was not designed for runtime loading
+#undef GST_TYPE_MESSAGE
+#define GST_TYPE_MESSAGE (llgst_message_get_type())
+#undef GST_TYPE_OBJECT
+#define GST_TYPE_OBJECT (llgst_object_get_type())
+#undef GST_TYPE_PIPELINE
+#define GST_TYPE_PIPELINE (llgst_pipeline_get_type())
+#undef GST_TYPE_ELEMENT
+#define GST_TYPE_ELEMENT (llgst_element_get_type())
+#undef GST_TYPE_VIDEO_SINK
+#define GST_TYPE_VIDEO_SINK (llgst_video_sink_get_type())
+// more regrettable hacks to stub-out these .h-exposed GStreamer internals
+void ll_gst_debug_register_funcptr(GstDebugFuncPtr func, gchar* ptrname);
+#undef _gst_debug_register_funcptr
+#define _gst_debug_register_funcptr ll_gst_debug_register_funcptr
+GstDebugCategory* ll_gst_debug_category_new(gchar *name, guint color, gchar *description);
+#undef _gst_debug_category_new
+#define _gst_debug_category_new ll_gst_debug_category_new
+#undef __gst_debug_enabled
+#define __gst_debug_enabled (0)
+
+// more hacks
+#define LLGST_MESSAGE_TYPE_NAME(M) (llgst_message_type_get_name(GST_MESSAGE_TYPE(M)))
+
+#endif // LL_GSTREAMER010_ENABLED
diff --git a/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms_raw.inc b/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms_raw.inc
new file mode 100644
index 0000000000..b33e59363d
--- /dev/null
+++ b/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms_raw.inc
@@ -0,0 +1,51 @@
+
+// required symbols to grab
+LL_GST_SYM(true, gst_pad_peer_accept_caps, gboolean, GstPad *pad, GstCaps *caps);
+LL_GST_SYM(true, gst_buffer_new, GstBuffer*, void);
+LL_GST_SYM(true, gst_buffer_set_caps, void, GstBuffer*, GstCaps *);
+LL_GST_SYM(true, gst_structure_set_value, void, GstStructure *, const gchar *, const GValue*);
+LL_GST_SYM(true, gst_init_check, gboolean, int *argc, char **argv[], GError ** err);
+LL_GST_SYM(true, gst_message_get_type, GType, void);
+LL_GST_SYM(true, gst_message_type_get_name, const gchar*, GstMessageType type);
+LL_GST_SYM(true, gst_message_parse_error, void, GstMessage *message, GError **gerror, gchar **debug);
+LL_GST_SYM(true, gst_message_parse_warning, void, GstMessage *message, GError **gerror, gchar **debug);
+LL_GST_SYM(true, gst_message_parse_state_changed, void, GstMessage *message, GstState *oldstate, GstState *newstate, GstState *pending);
+LL_GST_SYM(true, gst_element_set_state, GstStateChangeReturn, GstElement *element, GstState state);
+LL_GST_SYM(true, gst_object_unref, void, gpointer object);
+LL_GST_SYM(true, gst_object_get_type, GType, void);
+LL_GST_SYM(true, gst_pipeline_get_type, GType, void);
+LL_GST_SYM(true, gst_pipeline_get_bus, GstBus*, GstPipeline *pipeline);
+LL_GST_SYM(true, gst_bus_add_watch, guint, GstBus * bus, GstBusFunc func, gpointer user_data);
+LL_GST_SYM(true, gst_element_factory_make, GstElement*, const gchar *factoryname, const gchar *name);
+LL_GST_SYM(true, gst_element_get_type, GType, void);
+LL_GST_SYM(true, gst_static_pad_template_get, GstPadTemplate*, GstStaticPadTemplate *pad_template);
+LL_GST_SYM(true, gst_element_class_add_pad_template, void, GstElementClass *klass, GstPadTemplate *temp);
+LL_GST_SYM(true, gst_element_class_set_details, void, GstElementClass *klass, const GstElementDetails *details);
+LL_GST_SYM(true, gst_caps_unref, void, GstCaps* caps);
+LL_GST_SYM(true, gst_caps_ref, GstCaps *, GstCaps* caps);
+//LL_GST_SYM(true, gst_caps_is_empty, gboolean, const GstCaps *caps);
+LL_GST_SYM(true, gst_caps_from_string, GstCaps *, const gchar *string);
+LL_GST_SYM(true, gst_caps_replace, void, GstCaps **caps, GstCaps *newcaps);
+LL_GST_SYM(true, gst_caps_get_structure, GstStructure *, const GstCaps *caps, guint index);
+LL_GST_SYM(true, gst_caps_copy, GstCaps *, const GstCaps * caps);
+//LL_GST_SYM(true, gst_caps_intersect, GstCaps *, const GstCaps *caps1, const GstCaps *caps2);
+LL_GST_SYM(true, gst_element_register, gboolean, GstPlugin *plugin, const gchar *name, guint rank, GType type);
+LL_GST_SYM(true, _gst_plugin_register_static, void, GstPluginDesc *desc);
+LL_GST_SYM(true, gst_structure_get_int, gboolean, const GstStructure *structure, const gchar *fieldname, gint *value);
+LL_GST_SYM(true, gst_structure_get_value, G_CONST_RETURN GValue *, const GstStructure *structure, const gchar *fieldname);
+LL_GST_SYM(true, gst_value_get_fraction_numerator, gint, const GValue *value);
+LL_GST_SYM(true, gst_value_get_fraction_denominator, gint, const GValue *value);
+LL_GST_SYM(true, gst_structure_get_name, G_CONST_RETURN gchar *, const GstStructure *structure);
+LL_GST_SYM(true, gst_element_seek, bool, GstElement *, gdouble, GstFormat, GstSeekFlags, GstSeekType, gint64, GstSeekType, gint64);
+
+// optional symbols to grab
+LL_GST_SYM(false, gst_registry_fork_set_enabled, void, gboolean enabled);
+LL_GST_SYM(false, gst_segtrap_set_enabled, void, gboolean enabled);
+LL_GST_SYM(false, gst_message_parse_buffering, void, GstMessage *message, gint *percent);
+LL_GST_SYM(false, gst_message_parse_info, void, GstMessage *message, GError **gerror, gchar **debug);
+LL_GST_SYM(false, gst_element_query_position, gboolean, GstElement *element, GstFormat *format, gint64 *cur);
+LL_GST_SYM(false, gst_version, void, guint *major, guint *minor, guint *micro, guint *nano);
+
+// GStreamer 'internal' symbols which may not be visible in some runtimes but are still used in expanded GStreamer header macros - yuck! We'll substitute our own stubs for these.
+//LL_GST_SYM(true, _gst_debug_register_funcptr, void, GstDebugFuncPtr func, gchar* ptrname);
+//LL_GST_SYM(true, _gst_debug_category_new, GstDebugCategory *, gchar *name, guint color, gchar *description);
diff --git a/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms_rawv.inc b/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms_rawv.inc
new file mode 100644
index 0000000000..14fbcb48b9
--- /dev/null
+++ b/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms_rawv.inc
@@ -0,0 +1,5 @@
+
+// required symbols to grab
+LL_GST_SYM(true, gst_video_sink_get_type, GType, void);
+
+// optional symbols to grab
diff --git a/indra/media_plugins/gstreamer010/llmediaimplgstreamertriviallogging.h b/indra/media_plugins/gstreamer010/llmediaimplgstreamertriviallogging.h
new file mode 100644
index 0000000000..e7b31bec94
--- /dev/null
+++ b/indra/media_plugins/gstreamer010/llmediaimplgstreamertriviallogging.h
@@ -0,0 +1,55 @@
+/**
+ * @file llmediaimplgstreamertriviallogging.h
+ * @brief minimal logging utilities.
+ *
+ * @cond
+ * $LicenseInfo:firstyear=2009&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 __LLMEDIAIMPLGSTREAMERTRIVIALLOGGING_H__
+#define __LLMEDIAIMPLGSTREAMERTRIVIALLOGGING_H__
+
+#include <cstdio>
+
+extern "C" {
+#include <sys/types.h>
+#include <unistd.h>
+}
+
+/////////////////////////////////////////////////////////////////////////
+// Debug/Info/Warning macros.
+#define MSGMODULEFOO "(media plugin)"
+#define STDERRMSG(...) do{\
+ fprintf(stderr, " pid:%d: ", (int)getpid());\
+ fprintf(stderr, MSGMODULEFOO " %s:%d: ", __FUNCTION__, __LINE__);\
+ fprintf(stderr, __VA_ARGS__);\
+ fputc('\n',stderr);\
+ }while(0)
+#define NULLMSG(...) do{}while(0)
+
+#define DEBUGMSG NULLMSG
+#define INFOMSG STDERRMSG
+#define WARNMSG STDERRMSG
+/////////////////////////////////////////////////////////////////////////
+
+#endif /* __LLMEDIAIMPLGSTREAMERTRIVIALLOGGING_H__ */
diff --git a/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.cpp b/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.cpp
new file mode 100644
index 0000000000..cdb7f4faeb
--- /dev/null
+++ b/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.cpp
@@ -0,0 +1,527 @@
+/**
+ * @file llmediaimplgstreamervidplug.h
+ * @brief Video-consuming static GStreamer plugin for gst-to-LLMediaImpl
+ *
+ * @cond
+ * $LicenseInfo:firstyear=2007&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
+ */
+
+#if LL_GSTREAMER010_ENABLED
+
+#include "linden_common.h"
+
+#include <gst/gst.h>
+#include <gst/video/video.h>
+#include <gst/video/gstvideosink.h>
+
+#include "llmediaimplgstreamer_syms.h"
+#include "llmediaimplgstreamertriviallogging.h"
+
+#include "llmediaimplgstreamervidplug.h"
+
+
+GST_DEBUG_CATEGORY_STATIC (gst_slvideo_debug);
+#define GST_CAT_DEFAULT gst_slvideo_debug
+
+
+#define SLV_SIZECAPS ", width=(int)[1,2048], height=(int)[1,2048] "
+#define SLV_ALLCAPS GST_VIDEO_CAPS_RGBx SLV_SIZECAPS
+
+static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE (
+ (gchar*)"sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (SLV_ALLCAPS)
+ );
+
+GST_BOILERPLATE (GstSLVideo, gst_slvideo, GstVideoSink,
+ GST_TYPE_VIDEO_SINK);
+
+static void gst_slvideo_set_property (GObject * object, guint prop_id,
+ const GValue * value,
+ GParamSpec * pspec);
+static void gst_slvideo_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+
+static void
+gst_slvideo_base_init (gpointer gclass)
+{
+ static GstElementDetails element_details = {
+ (gchar*)"PluginTemplate",
+ (gchar*)"Generic/PluginTemplate",
+ (gchar*)"Generic Template Element",
+ (gchar*)"Linden Lab"
+ };
+ GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
+
+ llgst_element_class_add_pad_template (element_class,
+ llgst_static_pad_template_get (&sink_factory));
+ llgst_element_class_set_details (element_class, &element_details);
+}
+
+
+static void
+gst_slvideo_finalize (GObject * object)
+{
+ GstSLVideo *slvideo;
+ slvideo = GST_SLVIDEO (object);
+ if (slvideo->caps)
+ {
+ llgst_caps_unref(slvideo->caps);
+ }
+
+ G_OBJECT_CLASS(parent_class)->finalize (object);
+}
+
+
+static GstFlowReturn
+gst_slvideo_show_frame (GstBaseSink * bsink, GstBuffer * buf)
+{
+ GstSLVideo *slvideo;
+ llg_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
+
+ slvideo = GST_SLVIDEO(bsink);
+
+ DEBUGMSG("transferring a frame of %dx%d <- %p (%d)",
+ slvideo->width, slvideo->height, GST_BUFFER_DATA(buf),
+ slvideo->format);
+
+ if (GST_BUFFER_DATA(buf))
+ {
+ // copy frame and frame info into neutral territory
+ GST_OBJECT_LOCK(slvideo);
+ slvideo->retained_frame_ready = TRUE;
+ slvideo->retained_frame_width = slvideo->width;
+ slvideo->retained_frame_height = slvideo->height;
+ slvideo->retained_frame_format = slvideo->format;
+ int rowbytes =
+ SLVPixelFormatBytes[slvideo->retained_frame_format] *
+ slvideo->retained_frame_width;
+ int needbytes = rowbytes * slvideo->retained_frame_width;
+ // resize retained frame hunk only if necessary
+ if (needbytes != slvideo->retained_frame_allocbytes)
+ {
+ delete[] slvideo->retained_frame_data;
+ slvideo->retained_frame_data = new unsigned char[needbytes];
+ slvideo->retained_frame_allocbytes = needbytes;
+
+ }
+ // copy the actual frame data to neutral territory -
+ // flipped, for GL reasons
+ for (int ypos=0; ypos<slvideo->height; ++ypos)
+ {
+ memcpy(&slvideo->retained_frame_data[(slvideo->height-1-ypos)*rowbytes],
+ &(((unsigned char*)GST_BUFFER_DATA(buf))[ypos*rowbytes]),
+ rowbytes);
+ }
+ // done with the shared data
+ GST_OBJECT_UNLOCK(slvideo);
+ }
+
+ return GST_FLOW_OK;
+}
+
+
+static GstStateChangeReturn
+gst_slvideo_change_state(GstElement * element, GstStateChange transition)
+{
+ GstSLVideo *slvideo;
+ GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
+
+ slvideo = GST_SLVIDEO (element);
+
+ switch (transition) {
+ case GST_STATE_CHANGE_NULL_TO_READY:
+ break;
+ case GST_STATE_CHANGE_READY_TO_PAUSED:
+ break;
+ case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+ break;
+ default:
+ break;
+ }
+
+ ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+ if (ret == GST_STATE_CHANGE_FAILURE)
+ return ret;
+
+ switch (transition) {
+ case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+ break;
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ slvideo->fps_n = 0;
+ slvideo->fps_d = 1;
+ GST_VIDEO_SINK_WIDTH(slvideo) = 0;
+ GST_VIDEO_SINK_HEIGHT(slvideo) = 0;
+ break;
+ case GST_STATE_CHANGE_READY_TO_NULL:
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+
+static GstCaps *
+gst_slvideo_get_caps (GstBaseSink * bsink)
+{
+ GstSLVideo *slvideo;
+ slvideo = GST_SLVIDEO(bsink);
+
+ return llgst_caps_ref (slvideo->caps);
+}
+
+
+/* this function handles the link with other elements */
+static gboolean
+gst_slvideo_set_caps (GstBaseSink * bsink, GstCaps * caps)
+{
+ GstSLVideo *filter;
+ GstStructure *structure;
+
+ GST_DEBUG ("set caps with %" GST_PTR_FORMAT, caps);
+
+ filter = GST_SLVIDEO(bsink);
+
+ int width, height;
+ gboolean ret;
+ const GValue *fps;
+ const GValue *par;
+ structure = llgst_caps_get_structure (caps, 0);
+ ret = llgst_structure_get_int (structure, "width", &width);
+ ret = ret && llgst_structure_get_int (structure, "height", &height);
+ fps = llgst_structure_get_value (structure, "framerate");
+ ret = ret && (fps != NULL);
+ par = llgst_structure_get_value (structure, "pixel-aspect-ratio");
+ if (!ret)
+ return FALSE;
+
+ INFOMSG("** filter caps set with width=%d, height=%d", width, height);
+
+ GST_OBJECT_LOCK(filter);
+
+ filter->width = width;
+ filter->height = height;
+
+ filter->fps_n = llgst_value_get_fraction_numerator(fps);
+ filter->fps_d = llgst_value_get_fraction_denominator(fps);
+ if (par)
+ {
+ filter->par_n = llgst_value_get_fraction_numerator(par);
+ filter->par_d = llgst_value_get_fraction_denominator(par);
+ }
+ else
+ {
+ filter->par_n = 1;
+ filter->par_d = 1;
+ }
+ GST_VIDEO_SINK_WIDTH(filter) = width;
+ GST_VIDEO_SINK_HEIGHT(filter) = height;
+
+ // crufty lump - we *always* accept *only* RGBX now.
+ /*
+ filter->format = SLV_PF_UNKNOWN;
+ if (0 == strcmp(llgst_structure_get_name(structure),
+ "video/x-raw-rgb"))
+ {
+ int red_mask;
+ int green_mask;
+ int blue_mask;
+ llgst_structure_get_int(structure, "red_mask", &red_mask);
+ llgst_structure_get_int(structure, "green_mask", &green_mask);
+ llgst_structure_get_int(structure, "blue_mask", &blue_mask);
+ if ((unsigned int)red_mask == 0xFF000000 &&
+ (unsigned int)green_mask == 0x00FF0000 &&
+ (unsigned int)blue_mask == 0x0000FF00)
+ {
+ filter->format = SLV_PF_RGBX;
+ //fprintf(stderr, "\n\nPIXEL FORMAT RGB\n\n");
+ } else if ((unsigned int)red_mask == 0x0000FF00 &&
+ (unsigned int)green_mask == 0x00FF0000 &&
+ (unsigned int)blue_mask == 0xFF000000)
+ {
+ filter->format = SLV_PF_BGRX;
+ //fprintf(stderr, "\n\nPIXEL FORMAT BGR\n\n");
+ }
+ }*/
+
+ filter->format = SLV_PF_RGBX;
+
+ GST_OBJECT_UNLOCK(filter);
+
+ return TRUE;
+}
+
+
+static gboolean
+gst_slvideo_start (GstBaseSink * bsink)
+{
+ GstSLVideo *slvideo;
+ gboolean ret = TRUE;
+
+ slvideo = GST_SLVIDEO(bsink);
+
+ return ret;
+}
+
+static gboolean
+gst_slvideo_stop (GstBaseSink * bsink)
+{
+ GstSLVideo *slvideo;
+ slvideo = GST_SLVIDEO(bsink);
+
+ // free-up retained frame buffer
+ GST_OBJECT_LOCK(slvideo);
+ slvideo->retained_frame_ready = FALSE;
+ delete[] slvideo->retained_frame_data;
+ slvideo->retained_frame_data = NULL;
+ slvideo->retained_frame_allocbytes = 0;
+ GST_OBJECT_UNLOCK(slvideo);
+
+ return TRUE;
+}
+
+
+static GstFlowReturn
+gst_slvideo_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size,
+ GstCaps * caps, GstBuffer ** buf)
+{
+ gint width, height;
+ GstStructure *structure = NULL;
+ GstSLVideo *slvideo;
+ slvideo = GST_SLVIDEO(bsink);
+
+ // caps == requested caps
+ // we can ignore these and reverse-negotiate our preferred dimensions with
+ // the peer if we like - we need to do this to obey dynamic resize requests
+ // flowing in from the app.
+ structure = llgst_caps_get_structure (caps, 0);
+ if (!llgst_structure_get_int(structure, "width", &width) ||
+ !llgst_structure_get_int(structure, "height", &height))
+ {
+ GST_WARNING_OBJECT (slvideo, "no width/height in caps %" GST_PTR_FORMAT, caps);
+ return GST_FLOW_NOT_NEGOTIATED;
+ }
+
+ GstBuffer *newbuf = llgst_buffer_new();
+ bool made_bufferdata_ptr = false;
+#define MAXDEPTHHACK 4
+
+ GST_OBJECT_LOCK(slvideo);
+ if (slvideo->resize_forced_always) // app is giving us a fixed size to work with
+ {
+ gint slwantwidth, slwantheight;
+ slwantwidth = slvideo->resize_try_width;
+ slwantheight = slvideo->resize_try_height;
+
+ if (slwantwidth != width ||
+ slwantheight != height)
+ {
+ // don't like requested caps, we will issue our own suggestion - copy
+ // the requested caps but substitute our own width and height and see
+ // if our peer is happy with that.
+
+ GstCaps *desired_caps;
+ GstStructure *desired_struct;
+ desired_caps = llgst_caps_copy (caps);
+ desired_struct = llgst_caps_get_structure (desired_caps, 0);
+
+ GValue value = {0};
+ g_value_init(&value, G_TYPE_INT);
+ g_value_set_int(&value, slwantwidth);
+ llgst_structure_set_value (desired_struct, "width", &value);
+ g_value_unset(&value);
+ g_value_init(&value, G_TYPE_INT);
+ g_value_set_int(&value, slwantheight);
+ llgst_structure_set_value (desired_struct, "height", &value);
+
+ if (llgst_pad_peer_accept_caps (GST_VIDEO_SINK_PAD (slvideo),
+ desired_caps))
+ {
+ // todo: re-use buffers from a pool?
+ // todo: set MALLOCDATA to null, set DATA to point straight to shm?
+
+ // peer likes our cap suggestion
+ DEBUGMSG("peer loves us :)");
+ GST_BUFFER_SIZE(newbuf) = slwantwidth * slwantheight * MAXDEPTHHACK;
+ GST_BUFFER_MALLOCDATA(newbuf) = (guint8*)g_malloc(GST_BUFFER_SIZE(newbuf));
+ GST_BUFFER_DATA(newbuf) = GST_BUFFER_MALLOCDATA(newbuf);
+ llgst_buffer_set_caps (GST_BUFFER_CAST(newbuf), desired_caps);
+
+ made_bufferdata_ptr = true;
+ } else {
+ // peer hates our cap suggestion
+ INFOMSG("peer hates us :(");
+ llgst_caps_unref(desired_caps);
+ }
+ }
+ }
+
+ GST_OBJECT_UNLOCK(slvideo);
+
+ if (!made_bufferdata_ptr) // need to fallback to malloc at original size
+ {
+ GST_BUFFER_SIZE(newbuf) = width * height * MAXDEPTHHACK;
+ GST_BUFFER_MALLOCDATA(newbuf) = (guint8*)g_malloc(GST_BUFFER_SIZE(newbuf));
+ GST_BUFFER_DATA(newbuf) = GST_BUFFER_MALLOCDATA(newbuf);
+ llgst_buffer_set_caps (GST_BUFFER_CAST(newbuf), caps);
+ }
+
+ *buf = GST_BUFFER_CAST(newbuf);
+
+ return GST_FLOW_OK;
+}
+
+
+/* initialize the plugin's class */
+static void
+gst_slvideo_class_init (GstSLVideoClass * klass)
+{
+ GObjectClass *gobject_class;
+ GstElementClass *gstelement_class;
+ GstBaseSinkClass *gstbasesink_class;
+
+ gobject_class = (GObjectClass *) klass;
+ gstelement_class = (GstElementClass *) klass;
+ gstbasesink_class = (GstBaseSinkClass *) klass;
+
+ gobject_class->finalize = gst_slvideo_finalize;
+ gobject_class->set_property = gst_slvideo_set_property;
+ gobject_class->get_property = gst_slvideo_get_property;
+
+ gstelement_class->change_state = gst_slvideo_change_state;
+
+#define LLGST_DEBUG_FUNCPTR(p) (p)
+ gstbasesink_class->get_caps = LLGST_DEBUG_FUNCPTR (gst_slvideo_get_caps);
+ gstbasesink_class->set_caps = LLGST_DEBUG_FUNCPTR( gst_slvideo_set_caps);
+ gstbasesink_class->buffer_alloc=LLGST_DEBUG_FUNCPTR(gst_slvideo_buffer_alloc);
+ //gstbasesink_class->get_times = LLGST_DEBUG_FUNCPTR (gst_slvideo_get_times);
+ gstbasesink_class->preroll = LLGST_DEBUG_FUNCPTR (gst_slvideo_show_frame);
+ gstbasesink_class->render = LLGST_DEBUG_FUNCPTR (gst_slvideo_show_frame);
+
+ gstbasesink_class->start = LLGST_DEBUG_FUNCPTR (gst_slvideo_start);
+ gstbasesink_class->stop = LLGST_DEBUG_FUNCPTR (gst_slvideo_stop);
+
+ // gstbasesink_class->unlock = LLGST_DEBUG_FUNCPTR (gst_slvideo_unlock);
+#undef LLGST_DEBUG_FUNCPTR
+}
+
+
+/* initialize the new element
+ * instantiate pads and add them to element
+ * set functions
+ * initialize structure
+ */
+static void
+gst_slvideo_init (GstSLVideo * filter,
+ GstSLVideoClass * gclass)
+{
+ filter->caps = NULL;
+ filter->width = -1;
+ filter->height = -1;
+
+ // this is the info we share with the client app
+ GST_OBJECT_LOCK(filter);
+ filter->retained_frame_ready = FALSE;
+ filter->retained_frame_data = NULL;
+ filter->retained_frame_allocbytes = 0;
+ filter->retained_frame_width = filter->width;
+ filter->retained_frame_height = filter->height;
+ filter->retained_frame_format = SLV_PF_UNKNOWN;
+ GstCaps *caps = llgst_caps_from_string (SLV_ALLCAPS);
+ llgst_caps_replace (&filter->caps, caps);
+ filter->resize_forced_always = false;
+ filter->resize_try_width = -1;
+ filter->resize_try_height = -1;
+ GST_OBJECT_UNLOCK(filter);
+}
+
+static void
+gst_slvideo_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ llg_return_if_fail (GST_IS_SLVIDEO (object));
+
+ switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_slvideo_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ llg_return_if_fail (GST_IS_SLVIDEO (object));
+
+ switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+
+/* entry point to initialize the plug-in
+ * initialize the plug-in itself
+ * register the element factories and pad templates
+ * register the features
+ */
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+ DEBUGMSG("PLUGIN INIT");
+
+ GST_DEBUG_CATEGORY_INIT (gst_slvideo_debug, (gchar*)"private-slvideo-plugin",
+ 0, (gchar*)"Second Life Video Sink");
+
+ return llgst_element_register (plugin, "private-slvideo",
+ GST_RANK_NONE, GST_TYPE_SLVIDEO);
+}
+
+/* this is the structure that gstreamer looks for to register plugins
+ */
+/* NOTE: Can't rely upon GST_PLUGIN_DEFINE_STATIC to self-register, since
+ some g++ versions buggily avoid __attribute__((constructor)) functions -
+ so we provide an explicit plugin init function.
+ */
+#define PACKAGE (gchar*)"packagehack"
+// this macro quietly refers to PACKAGE internally
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+ GST_VERSION_MINOR,
+ (gchar*)"private-slvideoplugin",
+ (gchar*)"SL Video sink plugin",
+ plugin_init, (gchar*)"1.0", (gchar*)"LGPL",
+ (gchar*)"Second Life",
+ (gchar*)"http://www.secondlife.com/");
+#undef PACKAGE
+void gst_slvideo_init_class (void)
+{
+ ll_gst_plugin_register_static (&gst_plugin_desc);
+ DEBUGMSG("CLASS INIT");
+}
+
+#endif // LL_GSTREAMER010_ENABLED
diff --git a/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.h b/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.h
new file mode 100644
index 0000000000..29d65fa4e9
--- /dev/null
+++ b/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.h
@@ -0,0 +1,105 @@
+/**
+ * @file llmediaimplgstreamervidplug.h
+ * @brief Video-consuming static GStreamer plugin for gst-to-LLMediaImpl
+ *
+ * @cond
+ * $LicenseInfo:firstyear=2007&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 __GST_SLVIDEO_H__
+#define __GST_SLVIDEO_H__
+
+#if LL_GSTREAMER010_ENABLED
+
+extern "C" {
+#include <gst/gst.h>
+#include <gst/video/video.h>
+#include <gst/video/gstvideosink.h>
+}
+
+G_BEGIN_DECLS
+
+/* #defines don't like whitespacey bits */
+#define GST_TYPE_SLVIDEO \
+ (gst_slvideo_get_type())
+#define GST_SLVIDEO(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SLVIDEO,GstSLVideo))
+#define GST_SLVIDEO_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SLVIDEO,GstSLVideoClass))
+#define GST_IS_SLVIDEO(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SLVIDEO))
+#define GST_IS_SLVIDEO_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SLVIDEO))
+
+typedef struct _GstSLVideo GstSLVideo;
+typedef struct _GstSLVideoClass GstSLVideoClass;
+
+typedef enum {
+ SLV_PF_UNKNOWN = 0,
+ SLV_PF_RGBX = 1,
+ SLV_PF_BGRX = 2,
+ SLV__END = 3
+} SLVPixelFormat;
+const int SLVPixelFormatBytes[SLV__END] = {1, 4, 4};
+
+struct _GstSLVideo
+{
+ GstVideoSink video_sink;
+
+ GstCaps *caps;
+
+ int fps_n, fps_d;
+ int par_n, par_d;
+ int height, width;
+ SLVPixelFormat format;
+
+ // SHARED WITH APPLICATION:
+ // Access to the following should be protected by GST_OBJECT_LOCK() on
+ // the GstSLVideo object, and should be totally consistent upon UNLOCK
+ // (i.e. all written at once to reflect the current retained frame info
+ // when the retained frame is updated.)
+ bool retained_frame_ready; // new frame ready since flag last reset. (*TODO: could get the writer to wait on a semaphore instead of having the reader poll, potentially making dropped frames somewhat cheaper.)
+ unsigned char* retained_frame_data;
+ int retained_frame_allocbytes;
+ int retained_frame_width, retained_frame_height;
+ SLVPixelFormat retained_frame_format;
+ // sticky resize info
+ bool resize_forced_always;
+ int resize_try_width;
+ int resize_try_height;
+};
+
+struct _GstSLVideoClass
+{
+ GstVideoSinkClass parent_class;
+};
+
+GType gst_slvideo_get_type (void);
+
+void gst_slvideo_init_class (void);
+
+G_END_DECLS
+
+#endif // LL_GSTREAMER010_ENABLED
+
+#endif /* __GST_SLVIDEO_H__ */
diff --git a/indra/media_plugins/gstreamer010/media_plugin_gstreamer010.cpp b/indra/media_plugins/gstreamer010/media_plugin_gstreamer010.cpp
new file mode 100644
index 0000000000..352b63583e
--- /dev/null
+++ b/indra/media_plugins/gstreamer010/media_plugin_gstreamer010.cpp
@@ -0,0 +1,1266 @@
+/**
+ * @file media_plugin_gstreamer010.cpp
+ * @brief GStreamer-0.10 plugin for LLMedia API plugin system
+ *
+ * @cond
+ * $LicenseInfo:firstyear=2007&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 "linden_common.h"
+
+#include "llgl.h"
+
+#include "llplugininstance.h"
+#include "llpluginmessage.h"
+#include "llpluginmessageclasses.h"
+#include "media_plugin_base.h"
+
+#if LL_GSTREAMER010_ENABLED
+
+extern "C" {
+#include <gst/gst.h>
+}
+
+#include "llmediaimplgstreamer.h"
+#include "llmediaimplgstreamertriviallogging.h"
+
+#include "llmediaimplgstreamervidplug.h"
+
+#include "llmediaimplgstreamer_syms.h"
+
+//////////////////////////////////////////////////////////////////////////////
+//
+class MediaPluginGStreamer010 : public MediaPluginBase
+{
+public:
+ MediaPluginGStreamer010(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data);
+ ~MediaPluginGStreamer010();
+
+ /* virtual */ void receiveMessage(const char *message_string);
+
+ static bool startup();
+ static bool closedown();
+
+ gboolean processGSTEvents(GstBus *bus,
+ GstMessage *message);
+
+private:
+ std::string getVersion();
+ bool navigateTo( const std::string urlIn );
+ bool seek( double time_sec );
+ bool setVolume( float volume );
+
+ // misc
+ bool pause();
+ bool stop();
+ bool play(double rate);
+ bool getTimePos(double &sec_out);
+
+ static const double MIN_LOOP_SEC = 1.0F;
+
+ bool mIsLooping;
+
+ enum ECommand {
+ COMMAND_NONE,
+ COMMAND_STOP,
+ COMMAND_PLAY,
+ COMMAND_FAST_FORWARD,
+ COMMAND_FAST_REWIND,
+ COMMAND_PAUSE,
+ COMMAND_SEEK,
+ };
+ ECommand mCommand;
+
+private:
+ bool unload();
+ bool load();
+
+ bool update(int milliseconds);
+ void mouseDown( int x, int y );
+ void mouseUp( int x, int y );
+ void mouseMove( int x, int y );
+
+ void sizeChanged();
+
+ static bool mDoneInit;
+
+ guint mBusWatchID;
+
+ float mVolume;
+
+ int mDepth;
+
+ // media NATURAL size
+ int mNaturalWidth;
+ int mNaturalHeight;
+ // media current size
+ int mCurrentWidth;
+ int mCurrentHeight;
+ int mCurrentRowbytes;
+ // previous media size so we can detect changes
+ int mPreviousWidth;
+ int mPreviousHeight;
+ // desired render size from host
+ int mWidth;
+ int mHeight;
+ // padded texture size we need to write into
+ int mTextureWidth;
+ int mTextureHeight;
+
+ int mTextureFormatPrimary;
+ int mTextureFormatType;
+
+ bool mSeekWanted;
+ double mSeekDestination;
+
+ // Very GStreamer-specific
+ GMainLoop *mPump; // event pump for this media
+ GstElement *mPlaybin;
+ GstElement *mVisualizer;
+ GstSLVideo *mVideoSink;
+};
+
+//static
+bool MediaPluginGStreamer010::mDoneInit = false;
+
+MediaPluginGStreamer010::MediaPluginGStreamer010(
+ LLPluginInstance::sendMessageFunction host_send_func,
+ void *host_user_data ) :
+ MediaPluginBase(host_send_func, host_user_data),
+ mBusWatchID ( 0 ),
+ mCurrentRowbytes ( 4 ),
+ mTextureFormatPrimary ( GL_RGBA ),
+ mTextureFormatType ( GL_UNSIGNED_INT_8_8_8_8_REV ),
+ mSeekWanted(false),
+ mSeekDestination(0.0),
+ mPump ( NULL ),
+ mPlaybin ( NULL ),
+ mVisualizer ( NULL ),
+ mVideoSink ( NULL ),
+ mCommand ( COMMAND_NONE )
+{
+ std::ostringstream str;
+ INFOMSG("MediaPluginGStreamer010 constructor - my PID=%u", U32(getpid()));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//
+//#define LL_GST_REPORT_STATE_CHANGES
+#ifdef LL_GST_REPORT_STATE_CHANGES
+static char* get_gst_state_name(GstState state)
+{
+ switch (state) {
+ case GST_STATE_VOID_PENDING: return "VOID_PENDING";
+ case GST_STATE_NULL: return "NULL";
+ case GST_STATE_READY: return "READY";
+ case GST_STATE_PAUSED: return "PAUSED";
+ case GST_STATE_PLAYING: return "PLAYING";
+ }
+ return "(unknown)";
+}
+#endif // LL_GST_REPORT_STATE_CHANGES
+
+gboolean
+MediaPluginGStreamer010::processGSTEvents(GstBus *bus,
+ GstMessage *message)
+{
+ if (!message)
+ return TRUE; // shield against GStreamer bug
+
+ if (GST_MESSAGE_TYPE(message) != GST_MESSAGE_STATE_CHANGED &&
+ GST_MESSAGE_TYPE(message) != GST_MESSAGE_BUFFERING)
+ {
+ DEBUGMSG("Got GST message type: %s",
+ LLGST_MESSAGE_TYPE_NAME (message));
+ }
+ else
+ {
+ // TODO: grok 'duration' message type
+ DEBUGMSG("Got GST message type: %s",
+ LLGST_MESSAGE_TYPE_NAME (message));
+ }
+
+ switch (GST_MESSAGE_TYPE (message)) {
+ case GST_MESSAGE_BUFFERING: {
+ // NEEDS GST 0.10.11+
+ if (llgst_message_parse_buffering)
+ {
+ gint percent = 0;
+ llgst_message_parse_buffering(message, &percent);
+ DEBUGMSG("GST buffering: %d%%", percent);
+ }
+ break;
+ }
+ case GST_MESSAGE_STATE_CHANGED: {
+ GstState old_state;
+ GstState new_state;
+ GstState pending_state;
+ llgst_message_parse_state_changed(message,
+ &old_state,
+ &new_state,
+ &pending_state);
+#ifdef LL_GST_REPORT_STATE_CHANGES
+ // not generally very useful, and rather spammy.
+ DEBUGMSG("state change (old,<new>,pending): %s,<%s>,%s",
+ get_gst_state_name(old_state),
+ get_gst_state_name(new_state),
+ get_gst_state_name(pending_state));
+#endif // LL_GST_REPORT_STATE_CHANGES
+
+ switch (new_state) {
+ case GST_STATE_VOID_PENDING:
+ break;
+ case GST_STATE_NULL:
+ break;
+ case GST_STATE_READY:
+ setStatus(STATUS_LOADED);
+ break;
+ case GST_STATE_PAUSED:
+ setStatus(STATUS_PAUSED);
+ break;
+ case GST_STATE_PLAYING:
+ setStatus(STATUS_PLAYING);
+ break;
+ }
+ break;
+ }
+ case GST_MESSAGE_ERROR: {
+ GError *err = NULL;
+ gchar *debug = NULL;
+
+ llgst_message_parse_error (message, &err, &debug);
+ WARNMSG("GST error: %s", err?err->message:"(unknown)");
+ if (err)
+ g_error_free (err);
+ g_free (debug);
+
+ mCommand = COMMAND_STOP;
+
+ setStatus(STATUS_ERROR);
+
+ break;
+ }
+ case GST_MESSAGE_INFO: {
+ if (llgst_message_parse_info)
+ {
+ GError *err = NULL;
+ gchar *debug = NULL;
+
+ llgst_message_parse_info (message, &err, &debug);
+ INFOMSG("GST info: %s", err?err->message:"(unknown)");
+ if (err)
+ g_error_free (err);
+ g_free (debug);
+ }
+ break;
+ }
+ case GST_MESSAGE_WARNING: {
+ GError *err = NULL;
+ gchar *debug = NULL;
+
+ llgst_message_parse_warning (message, &err, &debug);
+ WARNMSG("GST warning: %s", err?err->message:"(unknown)");
+ if (err)
+ g_error_free (err);
+ g_free (debug);
+
+ break;
+ }
+ case GST_MESSAGE_EOS:
+ /* end-of-stream */
+ DEBUGMSG("GST end-of-stream.");
+ if (mIsLooping)
+ {
+ DEBUGMSG("looping media...");
+ double eos_pos_sec = 0.0F;
+ bool got_eos_position = getTimePos(eos_pos_sec);
+
+ if (got_eos_position && eos_pos_sec < MIN_LOOP_SEC)
+ {
+ // if we know that the movie is really short, don't
+ // loop it else it can easily become a time-hog
+ // because of GStreamer spin-up overhead
+ DEBUGMSG("really short movie (%0.3fsec) - not gonna loop this, pausing instead.", eos_pos_sec);
+ // inject a COMMAND_PAUSE
+ mCommand = COMMAND_PAUSE;
+ }
+ else
+ {
+#undef LLGST_LOOP_BY_SEEKING
+// loop with a stop-start instead of a seek, because it actually seems rather
+// faster than seeking on remote streams.
+#ifdef LLGST_LOOP_BY_SEEKING
+ // first, try looping by an explicit rewind
+ bool seeksuccess = seek(0.0);
+ if (seeksuccess)
+ {
+ play(1.0);
+ }
+ else
+#endif // LLGST_LOOP_BY_SEEKING
+ { // use clumsy stop-start to loop
+ DEBUGMSG("didn't loop by rewinding - stopping and starting instead...");
+ stop();
+ play(1.0);
+ }
+ }
+ }
+ else // not a looping media
+ {
+ // inject a COMMAND_STOP
+ mCommand = COMMAND_STOP;
+ }
+ break;
+ default:
+ /* unhandled message */
+ break;
+ }
+
+ /* we want to be notified again the next time there is a message
+ * on the bus, so return true (false means we want to stop watching
+ * for messages on the bus and our callback should not be called again)
+ */
+ return TRUE;
+}
+
+extern "C" {
+gboolean
+llmediaimplgstreamer_bus_callback (GstBus *bus,
+ GstMessage *message,
+ gpointer data)
+{
+ MediaPluginGStreamer010 *impl = (MediaPluginGStreamer010*)data;
+ return impl->processGSTEvents(bus, message);
+}
+} // extern "C"
+
+
+
+bool
+MediaPluginGStreamer010::navigateTo ( const std::string urlIn )
+{
+ if (!mDoneInit)
+ return false; // error
+
+ setStatus(STATUS_LOADING);
+
+ DEBUGMSG("Setting media URI: %s", urlIn.c_str());
+
+ mSeekWanted = false;
+
+ if (NULL == mPump ||
+ NULL == mPlaybin)
+ {
+ setStatus(STATUS_ERROR);
+ return false; // error
+ }
+
+ // set URI
+ g_object_set (G_OBJECT (mPlaybin), "uri", urlIn.c_str(), NULL);
+ //g_object_set (G_OBJECT (mPlaybin), "uri", "file:///tmp/movie", NULL);
+
+ // navigateTo implicitly plays, too.
+ play(1.0);
+
+ return true;
+}
+
+
+bool
+MediaPluginGStreamer010::update(int milliseconds)
+{
+ if (!mDoneInit)
+ return false; // error
+
+ DEBUGMSG("updating media...");
+
+ // sanity check
+ if (NULL == mPump ||
+ NULL == mPlaybin)
+ {
+ DEBUGMSG("dead media...");
+ return false;
+ }
+
+ // see if there's an outstanding seek wanted
+ if (mSeekWanted &&
+ // bleh, GST has to be happy that the movie is really truly playing
+ // or it may quietly ignore the seek (with rtsp:// at least).
+ (GST_STATE(mPlaybin) == GST_STATE_PLAYING))
+ {
+ seek(mSeekDestination);
+ mSeekWanted = false;
+ }
+
+ // *TODO: time-limit - but there isn't a lot we can do here, most
+ // time is spent in gstreamer's own opaque worker-threads. maybe
+ // we can do something sneaky like only unlock the video object
+ // for 'milliseconds' and otherwise hold the lock.
+ while (g_main_context_pending(g_main_loop_get_context(mPump)))
+ {
+ g_main_context_iteration(g_main_loop_get_context(mPump), FALSE);
+ }
+
+ // check for availability of a new frame
+
+ if (mVideoSink)
+ {
+ GST_OBJECT_LOCK(mVideoSink);
+ if (mVideoSink->retained_frame_ready)
+ {
+ DEBUGMSG("NEW FRAME READY");
+
+ if (mVideoSink->retained_frame_width != mCurrentWidth ||
+ mVideoSink->retained_frame_height != mCurrentHeight)
+ // *TODO: also check for change in format
+ {
+ // just resize container, don't consume frame
+ int neww = mVideoSink->retained_frame_width;
+ int newh = mVideoSink->retained_frame_height;
+
+ int newd = 4;
+ mTextureFormatPrimary = GL_RGBA;
+ mTextureFormatType = GL_UNSIGNED_INT_8_8_8_8_REV;
+
+ /*
+ int newd = SLVPixelFormatBytes[mVideoSink->retained_frame_format];
+ if (SLV_PF_BGRX == mVideoSink->retained_frame_format)
+ {
+ mTextureFormatPrimary = GL_BGRA;
+ mTextureFormatType = GL_UNSIGNED_INT_8_8_8_8_REV;
+ }
+ else
+ {
+ mTextureFormatPrimary = GL_RGBA;
+ mTextureFormatType = GL_UNSIGNED_INT_8_8_8_8_REV;
+ }
+ */
+
+ GST_OBJECT_UNLOCK(mVideoSink);
+
+ mCurrentRowbytes = neww * newd;
+ DEBUGMSG("video container resized to %dx%d",
+ neww, newh);
+
+ mDepth = newd;
+ mCurrentWidth = neww;
+ mCurrentHeight = newh;
+ sizeChanged();
+ return true;
+ }
+
+ if (mPixels &&
+ mCurrentHeight <= mHeight &&
+ mCurrentWidth <= mWidth &&
+ !mTextureSegmentName.empty())
+ {
+ // we're gonna totally consume this frame - reset 'ready' flag
+ mVideoSink->retained_frame_ready = FALSE;
+ int destination_rowbytes = mWidth * mDepth;
+ for (int row=0; row<mCurrentHeight; ++row)
+ {
+ memcpy(&mPixels
+ [destination_rowbytes * row],
+ &mVideoSink->retained_frame_data
+ [mCurrentRowbytes * row],
+ mCurrentRowbytes);
+ }
+
+ GST_OBJECT_UNLOCK(mVideoSink);
+ DEBUGMSG("NEW FRAME REALLY TRULY CONSUMED, TELLING HOST");
+
+ setDirty(0,0,mCurrentWidth,mCurrentHeight);
+ }
+ else
+ {
+ // new frame ready, but we're not ready to
+ // consume it.
+
+ GST_OBJECT_UNLOCK(mVideoSink);
+
+ DEBUGMSG("NEW FRAME not consumed, still waiting for a shm segment and/or shm resize");
+ }
+
+ return true;
+ }
+ else
+ {
+ // nothing to do yet.
+ GST_OBJECT_UNLOCK(mVideoSink);
+ return true;
+ }
+ }
+
+ return true;
+}
+
+
+void
+MediaPluginGStreamer010::mouseDown( int x, int y )
+{
+ // do nothing
+}
+
+void
+MediaPluginGStreamer010::mouseUp( int x, int y )
+{
+ // do nothing
+}
+
+void
+MediaPluginGStreamer010::mouseMove( int x, int y )
+{
+ // do nothing
+}
+
+
+bool
+MediaPluginGStreamer010::pause()
+{
+ DEBUGMSG("pausing media...");
+ // todo: error-check this?
+ if (mDoneInit && mPlaybin)
+ {
+ llgst_element_set_state(mPlaybin, GST_STATE_PAUSED);
+ return true;
+ }
+ return false;
+}
+
+bool
+MediaPluginGStreamer010::stop()
+{
+ DEBUGMSG("stopping media...");
+ // todo: error-check this?
+ if (mDoneInit && mPlaybin)
+ {
+ llgst_element_set_state(mPlaybin, GST_STATE_READY);
+ return true;
+ }
+ return false;
+}
+
+bool
+MediaPluginGStreamer010::play(double rate)
+{
+ // NOTE: we don't actually support non-natural rate.
+
+ DEBUGMSG("playing media... rate=%f", rate);
+ // todo: error-check this?
+ if (mDoneInit && mPlaybin)
+ {
+ llgst_element_set_state(mPlaybin, GST_STATE_PLAYING);
+ return true;
+ }
+ return false;
+}
+
+bool
+MediaPluginGStreamer010::setVolume( float volume )
+{
+ // we try to only update volume as conservatively as
+ // possible, as many gst-plugins-base versions up to at least
+ // November 2008 have critical race-conditions in setting volume - sigh
+ if (mVolume == volume)
+ return true; // nothing to do, everything's fine
+
+ mVolume = volume;
+ if (mDoneInit && mPlaybin)
+ {
+ g_object_set(mPlaybin, "volume", mVolume, NULL);
+ return true;
+ }
+
+ return false;
+}
+
+bool
+MediaPluginGStreamer010::seek(double time_sec)
+{
+ bool success = false;
+ if (mDoneInit && mPlaybin)
+ {
+ success = llgst_element_seek(mPlaybin, 1.0F, GST_FORMAT_TIME,
+ GstSeekFlags(GST_SEEK_FLAG_FLUSH |
+ GST_SEEK_FLAG_KEY_UNIT),
+ GST_SEEK_TYPE_SET, gint64(time_sec*GST_SECOND),
+ GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
+ }
+ DEBUGMSG("MEDIA SEEK REQUEST to %fsec result was %d",
+ float(time_sec), int(success));
+ return success;
+}
+
+bool
+MediaPluginGStreamer010::getTimePos(double &sec_out)
+{
+ bool got_position = false;
+ if (mDoneInit && mPlaybin)
+ {
+ gint64 pos;
+ GstFormat timefmt = GST_FORMAT_TIME;
+ got_position =
+ llgst_element_query_position &&
+ llgst_element_query_position(mPlaybin,
+ &timefmt,
+ &pos);
+ got_position = got_position
+ && (timefmt == GST_FORMAT_TIME);
+ // GStreamer may have other ideas, but we consider the current position
+ // undefined if not PLAYING or PAUSED
+ got_position = got_position &&
+ (GST_STATE(mPlaybin) == GST_STATE_PLAYING ||
+ GST_STATE(mPlaybin) == GST_STATE_PAUSED);
+ if (got_position && !GST_CLOCK_TIME_IS_VALID(pos))
+ {
+ if (GST_STATE(mPlaybin) == GST_STATE_PLAYING)
+ {
+ // if we're playing then we treat an invalid clock time
+ // as 0, for complicated reasons (insert reason here)
+ pos = 0;
+ }
+ else
+ {
+ got_position = false;
+ }
+
+ }
+ // If all the preconditions succeeded... we can trust the result.
+ if (got_position)
+ {
+ sec_out = double(pos) / double(GST_SECOND); // gst to sec
+ }
+ }
+ return got_position;
+}
+
+bool
+MediaPluginGStreamer010::load()
+{
+ if (!mDoneInit)
+ return false; // error
+
+ setStatus(STATUS_LOADING);
+
+ DEBUGMSG("setting up media...");
+
+ mIsLooping = false;
+ mVolume = 0.1234567; // minor hack to force an initial volume update
+
+ // Create a pumpable main-loop for this media
+ mPump = g_main_loop_new (NULL, FALSE);
+ if (!mPump)
+ {
+ setStatus(STATUS_ERROR);
+ return false; // error
+ }
+
+ // instantiate a playbin element to do the hard work
+ mPlaybin = llgst_element_factory_make ("playbin", "play");
+ if (!mPlaybin)
+ {
+ setStatus(STATUS_ERROR);
+ return false; // error
+ }
+
+ // get playbin's bus
+ GstBus *bus = llgst_pipeline_get_bus (GST_PIPELINE (mPlaybin));
+ if (!bus)
+ {
+ setStatus(STATUS_ERROR);
+ return false; // error
+ }
+ mBusWatchID = llgst_bus_add_watch (bus,
+ llmediaimplgstreamer_bus_callback,
+ this);
+ llgst_object_unref (bus);
+
+#if 0 // not quite stable/correct yet
+ // get a visualizer element (bonus feature!)
+ char* vis_name = getenv("LL_GST_VIS_NAME");
+ if (!vis_name ||
+ (vis_name && std::string(vis_name)!="none"))
+ {
+ if (vis_name)
+ {
+ mVisualizer = llgst_element_factory_make (vis_name, "vis");
+ }
+ if (!mVisualizer)
+ {
+ mVisualizer = llgst_element_factory_make ("libvisual_jess", "vis");
+ if (!mVisualizer)
+ {
+ mVisualizer = llgst_element_factory_make ("goom", "vis");
+ if (!mVisualizer)
+ {
+ mVisualizer = llgst_element_factory_make ("libvisual_lv_scope", "vis");
+ if (!mVisualizer)
+ {
+ // That's okay, we don't NEED this.
+ }
+ }
+ }
+ }
+ }
+#endif
+
+ if (NULL == getenv("LL_GSTREAMER_EXTERNAL")) {
+ // instantiate a custom video sink
+ mVideoSink =
+ GST_SLVIDEO(llgst_element_factory_make ("private-slvideo", "slvideo"));
+ if (!mVideoSink)
+ {
+ WARNMSG("Could not instantiate private-slvideo element.");
+ // todo: cleanup.
+ setStatus(STATUS_ERROR);
+ return false; // error
+ }
+
+ // connect the pieces
+ g_object_set(mPlaybin, "video-sink", mVideoSink, NULL);
+ }
+
+ if (mVisualizer)
+ {
+ g_object_set(mPlaybin, "vis-plugin", mVisualizer, NULL);
+ }
+
+ return true;
+}
+
+bool
+MediaPluginGStreamer010::unload ()
+{
+ if (!mDoneInit)
+ return false; // error
+
+ DEBUGMSG("unloading media...");
+
+ // stop getting callbacks for this bus
+ g_source_remove(mBusWatchID);
+ mBusWatchID = 0;
+
+ if (mPlaybin)
+ {
+ llgst_element_set_state (mPlaybin, GST_STATE_NULL);
+ llgst_object_unref (GST_OBJECT (mPlaybin));
+ mPlaybin = NULL;
+ }
+
+ if (mVisualizer)
+ {
+ llgst_object_unref (GST_OBJECT (mVisualizer));
+ mVisualizer = NULL;
+ }
+
+ if (mPump)
+ {
+ g_main_loop_quit(mPump);
+ mPump = NULL;
+ }
+
+ mVideoSink = NULL;
+
+ setStatus(STATUS_NONE);
+
+ return true;
+}
+
+
+//static
+bool
+MediaPluginGStreamer010::startup()
+{
+ // first - check if GStreamer is explicitly disabled
+ if (NULL != getenv("LL_DISABLE_GSTREAMER"))
+ return false;
+
+ // only do global GStreamer initialization once.
+ if (!mDoneInit)
+ {
+ g_thread_init(NULL);
+
+ // Init the glib type system - we need it.
+ g_type_init();
+
+ // Get symbols!
+#if LL_DARWIN
+ if (! grab_gst_syms("libgstreamer-0.10.dylib",
+ "libgstvideo-0.10.dylib") )
+#elseif LL_WINDOWS
+ if (! grab_gst_syms("libgstreamer-0.10.dll",
+ "libgstvideo-0.10.dll") )
+#else // linux or other ELFy unixoid
+ if (! grab_gst_syms("libgstreamer-0.10.so.0",
+ "libgstvideo-0.10.so.0") )
+#endif
+ {
+ WARNMSG("Couldn't find suitable GStreamer 0.10 support on this system - video playback disabled.");
+ return false;
+ }
+
+ if (llgst_segtrap_set_enabled)
+ {
+ llgst_segtrap_set_enabled(FALSE);
+ }
+ else
+ {
+ WARNMSG("gst_segtrap_set_enabled() is not available; plugin crashes won't be caught.");
+ }
+
+#if LL_LINUX
+ // Gstreamer tries a fork during init, waitpid-ing on it,
+ // which conflicts with any installed SIGCHLD handler...
+ struct sigaction tmpact, oldact;
+ if (llgst_registry_fork_set_enabled) {
+ // if we can disable SIGCHLD-using forking behaviour,
+ // do it.
+ llgst_registry_fork_set_enabled(false);
+ }
+ else {
+ // else temporarily install default SIGCHLD handler
+ // while GStreamer initialises
+ tmpact.sa_handler = SIG_DFL;
+ sigemptyset( &tmpact.sa_mask );
+ tmpact.sa_flags = SA_SIGINFO;
+ sigaction(SIGCHLD, &tmpact, &oldact);
+ }
+#endif // LL_LINUX
+
+ // Protect against GStreamer resetting the locale, yuck.
+ static std::string saved_locale;
+ saved_locale = setlocale(LC_ALL, NULL);
+
+ // finally, try to initialize GStreamer!
+ GError *err = NULL;
+ gboolean init_gst_success = llgst_init_check(NULL, NULL, &err);
+
+ // restore old locale
+ setlocale(LC_ALL, saved_locale.c_str() );
+
+#if LL_LINUX
+ // restore old SIGCHLD handler
+ if (!llgst_registry_fork_set_enabled)
+ sigaction(SIGCHLD, &oldact, NULL);
+#endif // LL_LINUX
+
+ if (!init_gst_success) // fail
+ {
+ if (err)
+ {
+ WARNMSG("GST init failed: %s", err->message);
+ g_error_free(err);
+ }
+ else
+ {
+ WARNMSG("GST init failed for unspecified reason.");
+ }
+ return false;
+ }
+
+ // Init our custom plugins - only really need do this once.
+ gst_slvideo_init_class();
+
+ mDoneInit = true;
+ }
+
+ return true;
+}
+
+
+void
+MediaPluginGStreamer010::sizeChanged()
+{
+ // the shared writing space has possibly changed size/location/whatever
+
+ // Check to see whether the movie's NATURAL size has been set yet
+ if (1 == mNaturalWidth &&
+ 1 == mNaturalHeight)
+ {
+ mNaturalWidth = mCurrentWidth;
+ mNaturalHeight = mCurrentHeight;
+ DEBUGMSG("Media NATURAL size better detected as %dx%d",
+ mNaturalWidth, mNaturalHeight);
+ }
+
+ // if the size has changed then the shm has changed and the app needs telling
+ if (mCurrentWidth != mPreviousWidth ||
+ mCurrentHeight != mPreviousHeight)
+ {
+ mPreviousWidth = mCurrentWidth;
+ mPreviousHeight = mCurrentHeight;
+
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "size_change_request");
+ message.setValue("name", mTextureSegmentName);
+ message.setValueS32("width", mNaturalWidth);
+ message.setValueS32("height", mNaturalHeight);
+ DEBUGMSG("<--- Sending size change request to application with name: '%s' - natural size is %d x %d", mTextureSegmentName.c_str(), mNaturalWidth, mNaturalHeight);
+ sendMessage(message);
+ }
+}
+
+
+
+//static
+bool
+MediaPluginGStreamer010::closedown()
+{
+ if (!mDoneInit)
+ return false; // error
+
+ ungrab_gst_syms();
+
+ mDoneInit = false;
+
+ return true;
+}
+
+MediaPluginGStreamer010::~MediaPluginGStreamer010()
+{
+ DEBUGMSG("MediaPluginGStreamer010 destructor");
+
+ closedown();
+
+ DEBUGMSG("GStreamer010 closing down");
+}
+
+
+std::string
+MediaPluginGStreamer010::getVersion()
+{
+ std::string plugin_version = "GStreamer010 media plugin, GStreamer version ";
+ if (mDoneInit &&
+ llgst_version)
+ {
+ guint major, minor, micro, nano;
+ llgst_version(&major, &minor, &micro, &nano);
+ plugin_version += llformat("%u.%u.%u.%u (runtime), %u.%u.%u.%u (headers)", (unsigned int)major, (unsigned int)minor, (unsigned int)micro, (unsigned int)nano, (unsigned int)GST_VERSION_MAJOR, (unsigned int)GST_VERSION_MINOR, (unsigned int)GST_VERSION_MICRO, (unsigned int)GST_VERSION_NANO);
+ }
+ else
+ {
+ plugin_version += "(unknown)";
+ }
+ return plugin_version;
+}
+
+void MediaPluginGStreamer010::receiveMessage(const char *message_string)
+{
+ //std::cerr << "MediaPluginGStreamer010::receiveMessage: received message: \"" << message_string << "\"" << std::endl;
+
+ LLPluginMessage message_in;
+
+ if(message_in.parse(message_string) >= 0)
+ {
+ std::string message_class = message_in.getClass();
+ std::string message_name = message_in.getName();
+ if(message_class == LLPLUGIN_MESSAGE_CLASS_BASE)
+ {
+ if(message_name == "init")
+ {
+ LLPluginMessage message("base", "init_response");
+ LLSD versions = LLSD::emptyMap();
+ versions[LLPLUGIN_MESSAGE_CLASS_BASE] = LLPLUGIN_MESSAGE_CLASS_BASE_VERSION;
+ versions[LLPLUGIN_MESSAGE_CLASS_MEDIA] = LLPLUGIN_MESSAGE_CLASS_MEDIA_VERSION;
+ versions[LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME] = LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME_VERSION;
+ message.setValueLLSD("versions", versions);
+
+ if ( load() )
+ {
+ DEBUGMSG("GStreamer010 media instance set up");
+ }
+ else
+ {
+ WARNMSG("GStreamer010 media instance failed to set up");
+ }
+
+ message.setValue("plugin_version", getVersion());
+ sendMessage(message);
+ }
+ else if(message_name == "idle")
+ {
+ // no response is necessary here.
+ double time = message_in.getValueReal("time");
+
+ // Convert time to milliseconds for update()
+ update((int)(time * 1000.0f));
+ }
+ else if(message_name == "cleanup")
+ {
+ unload();
+ closedown();
+ }
+ else if(message_name == "shm_added")
+ {
+ SharedSegmentInfo info;
+ info.mAddress = message_in.getValuePointer("address");
+ info.mSize = (size_t)message_in.getValueS32("size");
+ std::string name = message_in.getValue("name");
+
+ std::ostringstream str;
+ INFOMSG("MediaPluginGStreamer010::receiveMessage: shared memory added, name: %s, size: %d, address: %p", name.c_str(), int(info.mSize), info.mAddress);
+
+ mSharedSegments.insert(SharedSegmentMap::value_type(name, info));
+ }
+ else if(message_name == "shm_remove")
+ {
+ std::string name = message_in.getValue("name");
+
+ DEBUGMSG("MediaPluginGStreamer010::receiveMessage: shared memory remove, name = %s", name.c_str());
+
+ SharedSegmentMap::iterator iter = mSharedSegments.find(name);
+ if(iter != mSharedSegments.end())
+ {
+ if(mPixels == iter->second.mAddress)
+ {
+ // This is the currently active pixel buffer. Make sure we stop drawing to it.
+ mPixels = NULL;
+ mTextureSegmentName.clear();
+
+ // Make sure the movie decoder is no longer pointed at the shared segment.
+ sizeChanged();
+ }
+ mSharedSegments.erase(iter);
+ }
+ else
+ {
+ WARNMSG("MediaPluginGStreamer010::receiveMessage: unknown shared memory region!");
+ }
+
+ // Send the response so it can be cleaned up.
+ LLPluginMessage message("base", "shm_remove_response");
+ message.setValue("name", name);
+ sendMessage(message);
+ }
+ else
+ {
+ std::ostringstream str;
+ INFOMSG("MediaPluginGStreamer010::receiveMessage: unknown base message: %s", message_name.c_str());
+ }
+ }
+ else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA)
+ {
+ if(message_name == "init")
+ {
+ // Plugin gets to decide the texture parameters to use.
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "texture_params");
+ // lame to have to decide this now, it depends on the movie. Oh well.
+ mDepth = 4;
+
+ mCurrentWidth = 1;
+ mCurrentHeight = 1;
+ mPreviousWidth = 1;
+ mPreviousHeight = 1;
+ mNaturalWidth = 1;
+ mNaturalHeight = 1;
+ mWidth = 1;
+ mHeight = 1;
+ mTextureWidth = 1;
+ mTextureHeight = 1;
+
+ message.setValueU32("format", GL_RGBA);
+ message.setValueU32("type", GL_UNSIGNED_INT_8_8_8_8_REV);
+
+ message.setValueS32("depth", mDepth);
+ message.setValueS32("default_width", mWidth);
+ message.setValueS32("default_height", mHeight);
+ message.setValueU32("internalformat", GL_RGBA8);
+ message.setValueBoolean("coords_opengl", true); // true == use OpenGL-style coordinates, false == (0,0) is upper left.
+ message.setValueBoolean("allow_downsample", true); // we respond with grace and performance if asked to downscale
+ sendMessage(message);
+ }
+ else if(message_name == "size_change")
+ {
+ std::string name = message_in.getValue("name");
+ S32 width = message_in.getValueS32("width");
+ S32 height = message_in.getValueS32("height");
+ S32 texture_width = message_in.getValueS32("texture_width");
+ S32 texture_height = message_in.getValueS32("texture_height");
+
+ std::ostringstream str;
+ INFOMSG("---->Got size change instruction from application with shm name: %s - size is %d x %d", name.c_str(), width, height);
+
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "size_change_response");
+ message.setValue("name", name);
+ message.setValueS32("width", width);
+ message.setValueS32("height", height);
+ message.setValueS32("texture_width", texture_width);
+ message.setValueS32("texture_height", texture_height);
+ sendMessage(message);
+
+ if(!name.empty())
+ {
+ // Find the shared memory region with this name
+ SharedSegmentMap::iterator iter = mSharedSegments.find(name);
+ if(iter != mSharedSegments.end())
+ {
+ INFOMSG("*** Got size change with matching shm, new size is %d x %d", width, height);
+ INFOMSG("*** Got size change with matching shm, texture size size is %d x %d", texture_width, texture_height);
+
+ mPixels = (unsigned char*)iter->second.mAddress;
+ mTextureSegmentName = name;
+ mWidth = width;
+ mHeight = height;
+
+ if (texture_width > 1 ||
+ texture_height > 1) // not a dummy size from the app, a real explicit forced size
+ {
+ INFOMSG("**** = REAL RESIZE REQUEST FROM APP");
+
+ GST_OBJECT_LOCK(mVideoSink);
+ mVideoSink->resize_forced_always = true;
+ mVideoSink->resize_try_width = texture_width;
+ mVideoSink->resize_try_height = texture_height;
+ GST_OBJECT_UNLOCK(mVideoSink);
+ }
+
+ mTextureWidth = texture_width;
+ mTextureHeight = texture_height;
+ }
+ }
+ }
+ else if(message_name == "load_uri")
+ {
+ std::string uri = message_in.getValue("uri");
+ navigateTo( uri );
+ sendStatus();
+ }
+ else if(message_name == "mouse_event")
+ {
+ std::string event = message_in.getValue("event");
+ S32 x = message_in.getValueS32("x");
+ S32 y = message_in.getValueS32("y");
+
+ if(event == "down")
+ {
+ mouseDown(x, y);
+ }
+ else if(event == "up")
+ {
+ mouseUp(x, y);
+ }
+ else if(event == "move")
+ {
+ mouseMove(x, y);
+ };
+ };
+ }
+ else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME)
+ {
+ if(message_name == "stop")
+ {
+ stop();
+ }
+ else if(message_name == "start")
+ {
+ double rate = 0.0;
+ if(message_in.hasValue("rate"))
+ {
+ rate = message_in.getValueReal("rate");
+ }
+ // NOTE: we don't actually support rate.
+ play(rate);
+ }
+ else if(message_name == "pause")
+ {
+ pause();
+ }
+ else if(message_name == "seek")
+ {
+ double time = message_in.getValueReal("time");
+ // defer the actual seek in case we haven't
+ // really truly started yet in which case there
+ // is nothing to seek upon
+ mSeekWanted = true;
+ mSeekDestination = time;
+ }
+ else if(message_name == "set_loop")
+ {
+ bool loop = message_in.getValueBoolean("loop");
+ mIsLooping = loop;
+ }
+ else if(message_name == "set_volume")
+ {
+ double volume = message_in.getValueReal("volume");
+ setVolume(volume);
+ }
+ }
+ else
+ {
+ INFOMSG("MediaPluginGStreamer010::receiveMessage: unknown message class: %s", message_class.c_str());
+ }
+ }
+}
+
+int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data)
+{
+ if (MediaPluginGStreamer010::startup())
+ {
+ MediaPluginGStreamer010 *self = new MediaPluginGStreamer010(host_send_func, host_user_data);
+ *plugin_send_func = MediaPluginGStreamer010::staticReceiveMessage;
+ *plugin_user_data = (void*)self;
+
+ return 0; // okay
+ }
+ else
+ {
+ return -1; // failed to init
+ }
+}
+
+#else // LL_GSTREAMER010_ENABLED
+
+// Stubbed-out class with constructor/destructor (necessary or windows linker
+// will just think its dead code and optimize it all out)
+class MediaPluginGStreamer010 : public MediaPluginBase
+{
+public:
+ MediaPluginGStreamer010(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data);
+ ~MediaPluginGStreamer010();
+ /* virtual */ void receiveMessage(const char *message_string);
+};
+
+MediaPluginGStreamer010::MediaPluginGStreamer010(
+ LLPluginInstance::sendMessageFunction host_send_func,
+ void *host_user_data ) :
+ MediaPluginBase(host_send_func, host_user_data)
+{
+ // no-op
+}
+
+MediaPluginGStreamer010::~MediaPluginGStreamer010()
+{
+ // no-op
+}
+
+void MediaPluginGStreamer010::receiveMessage(const char *message_string)
+{
+ // no-op
+}
+
+// We're building without GStreamer enabled. Just refuse to initialize.
+int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data)
+{
+ return -1;
+}
+
+#endif // LL_GSTREAMER010_ENABLED
diff --git a/indra/media_plugins/quicktime/CMakeLists.txt b/indra/media_plugins/quicktime/CMakeLists.txt
new file mode 100644
index 0000000000..f0b8f0d167
--- /dev/null
+++ b/indra/media_plugins/quicktime/CMakeLists.txt
@@ -0,0 +1,91 @@
+# -*- cmake -*-
+
+project(media_plugin_quicktime)
+
+include(00-Common)
+include(LLCommon)
+include(LLImage)
+include(LLPlugin)
+include(LLMath)
+include(LLRender)
+include(LLWindow)
+include(Linking)
+include(PluginAPI)
+include(MediaPluginBase)
+include(FindOpenGL)
+include(QuickTimePlugin)
+
+include_directories(
+ ${LLPLUGIN_INCLUDE_DIRS}
+ ${MEDIA_PLUGIN_BASE_INCLUDE_DIRS}
+ ${LLCOMMON_INCLUDE_DIRS}
+ ${LLMATH_INCLUDE_DIRS}
+ ${LLIMAGE_INCLUDE_DIRS}
+ ${LLRENDER_INCLUDE_DIRS}
+ ${LLWINDOW_INCLUDE_DIRS}
+)
+
+if (DARWIN)
+ include(CMakeFindFrameworks)
+ find_library(CARBON_LIBRARY Carbon)
+endif (DARWIN)
+
+
+### media_plugin_quicktime
+
+set(media_plugin_quicktime_SOURCE_FILES
+ media_plugin_quicktime.cpp
+ )
+
+add_library(media_plugin_quicktime
+ SHARED
+ ${media_plugin_quicktime_SOURCE_FILES}
+)
+
+target_link_libraries(media_plugin_quicktime
+ ${LLPLUGIN_LIBRARIES}
+ ${MEDIA_PLUGIN_BASE_LIBRARIES}
+ ${LLCOMMON_LIBRARIES}
+ ${QUICKTIME_LIBRARY}
+ ${PLUGIN_API_WINDOWS_LIBRARIES}
+)
+
+add_dependencies(media_plugin_quicktime
+ ${LLPLUGIN_LIBRARIES}
+ ${MEDIA_PLUGIN_BASE_LIBRARIES}
+ ${LLCOMMON_LIBRARIES}
+)
+
+if (WINDOWS)
+ set_target_properties(
+ media_plugin_quicktime
+ PROPERTIES
+ LINK_FLAGS "/MANIFEST:NO"
+ )
+endif (WINDOWS)
+
+if (QUICKTIME)
+
+ add_definitions(-DLL_QUICKTIME_ENABLED=1)
+
+ 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_quicktime
+ PROPERTIES
+ PREFIX ""
+ BUILD_WITH_INSTALL_RPATH 1
+ INSTALL_NAME_DIR "@executable_path"
+ LINK_FLAGS "-exported_symbols_list ${CMAKE_CURRENT_SOURCE_DIR}/../base/media_plugin_base.exp"
+ )
+
+# We use a bunch of deprecated system APIs.
+ set_source_files_properties(
+ media_plugin_quicktime.cpp PROPERTIES
+ COMPILE_FLAGS -Wno-deprecated-declarations
+ )
+ find_library(CARBON_LIBRARY Carbon)
+ target_link_libraries(media_plugin_quicktime ${CARBON_LIBRARY})
+ endif (DARWIN)
+endif (QUICKTIME)
+
diff --git a/indra/media_plugins/quicktime/media_plugin_quicktime.cpp b/indra/media_plugins/quicktime/media_plugin_quicktime.cpp
new file mode 100644
index 0000000000..999f754dcf
--- /dev/null
+++ b/indra/media_plugins/quicktime/media_plugin_quicktime.cpp
@@ -0,0 +1,1111 @@
+/**
+ * @file media_plugin_quicktime.cpp
+ * @brief QuickTime plugin for LLMedia API plugin system
+ *
+ * @cond
+ * $LicenseInfo:firstyear=2008&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 "linden_common.h"
+
+#include "llgl.h"
+
+#include "llplugininstance.h"
+#include "llpluginmessage.h"
+#include "llpluginmessageclasses.h"
+#include "media_plugin_base.h"
+
+#if LL_QUICKTIME_ENABLED
+
+#if defined(LL_DARWIN)
+ #include <QuickTime/QuickTime.h>
+#elif defined(LL_WINDOWS)
+ #include "MacTypes.h"
+ #include "QTML.h"
+ #include "Movies.h"
+ #include "QDoffscreen.h"
+ #include "FixMath.h"
+ #include "QTLoadLibraryUtils.h"
+#endif
+
+// TODO: Make sure that the only symbol exported from this library is LLPluginInitEntryPoint
+////////////////////////////////////////////////////////////////////////////////
+//
+class MediaPluginQuickTime : public MediaPluginBase
+{
+public:
+ MediaPluginQuickTime(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data);
+ ~MediaPluginQuickTime();
+
+ /* virtual */ void receiveMessage(const char *message_string);
+
+private:
+
+ int mNaturalWidth;
+ int mNaturalHeight;
+ Movie mMovieHandle;
+ GWorldPtr mGWorldHandle;
+ ComponentInstance mMovieController;
+ int mCurVolume;
+ bool mMediaSizeChanging;
+ bool mIsLooping;
+ std::string mMovieTitle;
+ bool mReceivedTitle;
+ const int mMinWidth;
+ const int mMaxWidth;
+ const int mMinHeight;
+ const int mMaxHeight;
+ F64 mPlayRate;
+ std::string mNavigateURL;
+
+ enum ECommand {
+ COMMAND_NONE,
+ COMMAND_STOP,
+ COMMAND_PLAY,
+ COMMAND_FAST_FORWARD,
+ COMMAND_FAST_REWIND,
+ COMMAND_PAUSE,
+ COMMAND_SEEK,
+ };
+ ECommand mCommand;
+
+ // Override this to add current time and duration to the message
+ /*virtual*/ void setDirty(int left, int top, int right, int bottom)
+ {
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "updated");
+
+ message.setValueS32("left", left);
+ message.setValueS32("top", top);
+ message.setValueS32("right", right);
+ message.setValueS32("bottom", bottom);
+
+ if(mMovieHandle)
+ {
+ message.setValueReal("current_time", getCurrentTime());
+ message.setValueReal("duration", getDuration());
+ message.setValueReal("current_rate", Fix2X(GetMovieRate(mMovieHandle)));
+ }
+
+ sendMessage(message);
+ }
+
+
+ static Rect rectFromSize(int width, int height)
+ {
+ Rect result;
+
+
+ result.left = 0;
+ result.top = 0;
+ result.right = width;
+ result.bottom = height;
+
+ return result;
+ }
+
+ Fixed getPlayRate(void)
+ {
+ Fixed result;
+ if(mPlayRate == 0.0f)
+ {
+ // Default to the movie's preferred rate
+ result = GetMoviePreferredRate(mMovieHandle);
+ if(result == 0)
+ {
+ // Don't return a 0 play rate, ever.
+ std::cerr << "Movie's preferred rate is 0, forcing to 1.0." << std::endl;
+ result = X2Fix(1.0f);
+ }
+ }
+ else
+ {
+ result = X2Fix(mPlayRate);
+ }
+
+ return result;
+ }
+
+ void load( const std::string url )
+ {
+
+ if ( url.empty() )
+ return;
+
+ // Stop and unload any existing movie before starting another one.
+ unload();
+
+ setStatus(STATUS_LOADING);
+
+ //In case std::string::c_str() makes a copy of the url data,
+ //make sure there is memory to hold it before allocating memory for handle.
+ //if fails, NewHandleClear(...) should return NULL.
+ const char* url_string = url.c_str() ;
+ Handle handle = NewHandleClear( ( Size )( url.length() + 1 ) );
+
+ if ( NULL == handle || noErr != MemError() || NULL == *handle )
+ {
+ setStatus(STATUS_ERROR);
+ return;
+ }
+
+ BlockMove( url_string, *handle, ( Size )( url.length() + 1 ) );
+
+ OSErr err = NewMovieFromDataRef( &mMovieHandle, newMovieActive | newMovieDontInteractWithUser | newMovieAsyncOK | newMovieIdleImportOK, nil, handle, URLDataHandlerSubType );
+ DisposeHandle( handle );
+ if ( noErr != err )
+ {
+ setStatus(STATUS_ERROR);
+ return;
+ };
+
+ mNavigateURL = url;
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "navigate_begin");
+ message.setValue("uri", mNavigateURL);
+ sendMessage(message);
+
+ // do pre-roll actions (typically fired for streaming movies but not always)
+ PrePrerollMovie( mMovieHandle, 0, getPlayRate(), moviePrePrerollCompleteCallback, ( void * )this );
+
+ Rect movie_rect = rectFromSize(mWidth, mHeight);
+
+ // make a new movie controller
+ mMovieController = NewMovieController( mMovieHandle, &movie_rect, mcNotVisible | mcTopLeftMovie );
+
+ // movie controller
+ MCSetActionFilterWithRefCon( mMovieController, mcActionFilterCallBack, ( long )this );
+
+ SetMoviePlayHints( mMovieHandle, hintsAllowDynamicResize, hintsAllowDynamicResize );
+
+ // function that gets called when a frame is drawn
+ SetMovieDrawingCompleteProc( mMovieHandle, movieDrawingCallWhenChanged, movieDrawingCompleteCallback, ( long )this );
+
+ setStatus(STATUS_LOADED);
+
+ sizeChanged();
+ };
+
+ bool unload()
+ {
+ // new movie and have to get title again
+ mReceivedTitle = false;
+
+ if ( mMovieHandle )
+ {
+ StopMovie( mMovieHandle );
+ if ( mMovieController )
+ {
+ MCMovieChanged( mMovieController, mMovieHandle );
+ };
+ };
+
+ if ( mMovieController )
+ {
+ MCSetActionFilterWithRefCon( mMovieController, NULL, (long)this );
+ DisposeMovieController( mMovieController );
+ mMovieController = NULL;
+ };
+
+ if ( mMovieHandle )
+ {
+ SetMovieDrawingCompleteProc( mMovieHandle, movieDrawingCallWhenChanged, nil, ( long )this );
+ DisposeMovie( mMovieHandle );
+ mMovieHandle = NULL;
+ };
+
+ if ( mGWorldHandle )
+ {
+ DisposeGWorld( mGWorldHandle );
+ mGWorldHandle = NULL;
+ };
+
+ setStatus(STATUS_NONE);
+
+ return true;
+ }
+
+ bool navigateTo( const std::string url )
+ {
+ unload();
+ load( url );
+
+ return true;
+ };
+
+ bool sizeChanged()
+ {
+ if ( ! mMovieHandle )
+ return false;
+
+ // Check to see whether the movie's natural size has updated
+ {
+ int width, height;
+ getMovieNaturalSize(&width, &height);
+ if((width != 0) && (height != 0) && ((width != mNaturalWidth) || (height != mNaturalHeight)))
+ {
+ mNaturalWidth = width;
+ mNaturalHeight = height;
+
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "size_change_request");
+ message.setValue("name", mTextureSegmentName);
+ message.setValueS32("width", width);
+ message.setValueS32("height", height);
+ sendMessage(message);
+ //std::cerr << "<--- Sending size change request to application with name: " << mTextureSegmentName << " - size is " << width << " x " << height << std::endl;
+ }
+ }
+
+ // sanitize destination size
+ Rect dest_rect = rectFromSize(mWidth, mHeight);
+
+ // media depth won't change
+ int depth_bits = mDepth * 8;
+ long rowbytes = mDepth * mTextureWidth;
+
+ GWorldPtr old_gworld_handle = mGWorldHandle;
+
+ if(mPixels != NULL)
+ {
+ // We have pixels. Set up a GWorld pointing at the texture.
+ OSErr result = NewGWorldFromPtr( &mGWorldHandle, depth_bits, &dest_rect, NULL, NULL, 0, (Ptr)mPixels, rowbytes);
+ if ( noErr != result )
+ {
+ // TODO: unrecoverable?? throw exception? return something?
+ return false;
+ }
+ }
+ else
+ {
+ // We don't have pixels. Create a fake GWorld we can point the movie at when it's not safe to render normally.
+ Rect tempRect = rectFromSize(1, 1);
+ OSErr result = NewGWorld( &mGWorldHandle, depth_bits, &tempRect, NULL, NULL, 0);
+ if ( noErr != result )
+ {
+ // TODO: unrecoverable?? throw exception? return something?
+ return false;
+ }
+ }
+
+ SetMovieGWorld( mMovieHandle, mGWorldHandle, GetGWorldDevice( mGWorldHandle ) );
+
+ // If the GWorld was already set up, delete it.
+ if(old_gworld_handle != NULL)
+ {
+ DisposeGWorld( old_gworld_handle );
+ }
+
+ // Set up the movie display matrix
+ {
+ // scale movie to fit rect and invert vertically to match opengl image format
+ MatrixRecord transform;
+ SetIdentityMatrix( &transform ); // transforms are additive so start from identify matrix
+ double scaleX = (double) mWidth / mNaturalWidth;
+ double scaleY = -1.0 * (double) mHeight / mNaturalHeight;
+ double centerX = mWidth / 2.0;
+ double centerY = mHeight / 2.0;
+ ScaleMatrix( &transform, X2Fix( scaleX ), X2Fix( scaleY ), X2Fix( centerX ), X2Fix( centerY ) );
+ SetMovieMatrix( mMovieHandle, &transform );
+ }
+
+ // update movie controller
+ if ( mMovieController )
+ {
+ MCSetControllerPort( mMovieController, mGWorldHandle );
+ MCPositionController( mMovieController, &dest_rect, &dest_rect,
+ mcTopLeftMovie | mcPositionDontInvalidate );
+ MCMovieChanged( mMovieController, mMovieHandle );
+ }
+
+
+ // Emit event with size change so the calling app knows about it too
+ // TODO:
+ //LLMediaEvent event( this );
+ //mEventEmitter.update( &LLMediaObserver::onMediaSizeChange, event );
+
+ return true;
+ }
+ static Boolean mcActionFilterCallBack( MovieController mc, short action, void *params, long ref )
+ {
+ Boolean result = false;
+
+ MediaPluginQuickTime* self = ( MediaPluginQuickTime* )ref;
+
+ switch( action )
+ {
+ // handle window resizing
+ case mcActionControllerSizeChanged:
+ // Ensure that the movie draws correctly at the new size
+ self->sizeChanged();
+ break;
+
+ // Block any movie controller actions that open URLs.
+ case mcActionLinkToURL:
+ case mcActionGetNextURL:
+ case mcActionLinkToURLExtended:
+ // Prevent the movie controller from handling the message
+ result = true;
+ break;
+
+ default:
+ break;
+ };
+
+ return result;
+ };
+
+ static OSErr movieDrawingCompleteCallback( Movie call_back_movie, long ref )
+ {
+ MediaPluginQuickTime* self = ( MediaPluginQuickTime* )ref;
+
+ // IMPORTANT: typically, a consumer who is observing this event will set a flag
+ // when this event is fired then render later. Be aware that the media stream
+ // can change during this period - dimensions, depth, format etc.
+ //LLMediaEvent event( self );
+// self->updateQuickTime();
+ // TODO ^^^
+
+
+ if ( self->mWidth > 0 && self->mHeight > 0 )
+ self->setDirty( 0, 0, self->mWidth, self->mHeight );
+
+ return noErr;
+ };
+
+ static void moviePrePrerollCompleteCallback( Movie movie, OSErr preroll_err, void *ref )
+ {
+ MediaPluginQuickTime* self = ( MediaPluginQuickTime* )ref;
+
+ // TODO:
+ //LLMediaEvent event( self );
+ //self->mEventEmitter.update( &LLMediaObserver::onMediaPreroll, event );
+
+ // Send a "navigate complete" event.
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "navigate_complete");
+ message.setValue("uri", self->mNavigateURL);
+ message.setValueS32("result_code", 200);
+ message.setValue("result_string", "OK");
+ self->sendMessage(message);
+ };
+
+
+ void rewind()
+ {
+ GoToBeginningOfMovie( mMovieHandle );
+ MCMovieChanged( mMovieController, mMovieHandle );
+ };
+
+ bool processState()
+ {
+ if ( mCommand == COMMAND_PLAY )
+ {
+ if ( mStatus == STATUS_LOADED || mStatus == STATUS_PAUSED || mStatus == STATUS_PLAYING || mStatus == STATUS_DONE )
+ {
+ long state = GetMovieLoadState( mMovieHandle );
+
+ if ( state >= kMovieLoadStatePlaythroughOK )
+ {
+ // if the movie is at the end (generally because it reached it naturally)
+ // and we play is requested, jump back to the start of the movie.
+ // note: this is different from having loop flag set.
+ if ( IsMovieDone( mMovieHandle ) )
+ {
+ Fixed rate = X2Fix( 0.0 );
+ MCDoAction( mMovieController, mcActionPlay, (void*)rate );
+ rewind();
+ };
+
+ MCDoAction( mMovieController, mcActionPrerollAndPlay, (void*)getPlayRate() );
+ MCDoAction( mMovieController, mcActionSetVolume, (void*)mCurVolume );
+ setStatus(STATUS_PLAYING);
+ mCommand = COMMAND_NONE;
+ };
+ };
+ }
+ else
+ if ( mCommand == COMMAND_STOP )
+ {
+ if ( mStatus == STATUS_PLAYING || mStatus == STATUS_PAUSED || mStatus == STATUS_DONE )
+ {
+ if ( GetMovieLoadState( mMovieHandle ) >= kMovieLoadStatePlaythroughOK )
+ {
+ Fixed rate = X2Fix( 0.0 );
+ MCDoAction( mMovieController, mcActionPlay, (void*)rate );
+ rewind();
+
+ setStatus(STATUS_LOADED);
+ mCommand = COMMAND_NONE;
+ };
+ };
+ }
+ else
+ if ( mCommand == COMMAND_PAUSE )
+ {
+ if ( mStatus == STATUS_PLAYING )
+ {
+ if ( GetMovieLoadState( mMovieHandle ) >= kMovieLoadStatePlaythroughOK )
+ {
+ Fixed rate = X2Fix( 0.0 );
+ MCDoAction( mMovieController, mcActionPlay, (void*)rate );
+ setStatus(STATUS_PAUSED);
+ mCommand = COMMAND_NONE;
+ };
+ };
+ };
+
+ return true;
+ };
+
+ void play(F64 rate)
+ {
+ mPlayRate = rate;
+ mCommand = COMMAND_PLAY;
+ };
+
+ void stop()
+ {
+ mCommand = COMMAND_STOP;
+ };
+
+ void pause()
+ {
+ mCommand = COMMAND_PAUSE;
+ };
+
+ void getMovieNaturalSize(int *movie_width, int *movie_height)
+ {
+ Rect rect;
+
+ GetMovieNaturalBoundsRect( mMovieHandle, &rect );
+
+ int width = ( rect.right - rect.left );
+ int height = ( rect.bottom - rect.top );
+
+ // make sure width and height fall in valid range
+ if ( width < mMinWidth )
+ width = mMinWidth;
+
+ if ( width > mMaxWidth )
+ width = mMaxWidth;
+
+ if ( height < mMinHeight )
+ height = mMinHeight;
+
+ if ( height > mMaxHeight )
+ height = mMaxHeight;
+
+ // return the new rect
+ *movie_width = width;
+ *movie_height = height;
+ }
+
+ void updateQuickTime(int milliseconds)
+ {
+ if ( ! mMovieHandle )
+ return;
+
+ if ( ! mMovieController )
+ return;
+
+ // this wasn't required in 1.xx viewer but we have to manually
+ // work the Windows message pump now
+ #if defined( LL_WINDOWS )
+ MSG msg;
+ while ( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) )
+ {
+ GetMessage( &msg, NULL, 0, 0 );
+ TranslateMessage( &msg );
+ DispatchMessage( &msg );
+ };
+ #endif
+
+ MCIdle( mMovieController );
+
+ if ( ! mGWorldHandle )
+ return;
+
+ if ( mMediaSizeChanging )
+ return;
+
+ // update state machine
+ processState();
+
+ // see if title arrived and if so, update member variable with contents
+ checkTitle();
+
+ // QT call to see if we are at the end - can't do with controller
+ if ( IsMovieDone( mMovieHandle ) )
+ {
+ // special code for looping - need to rewind at the end of the movie
+ if ( mIsLooping )
+ {
+ // go back to start
+ rewind();
+
+ if ( mMovieController )
+ {
+ // kick off new play
+ MCDoAction( mMovieController, mcActionPrerollAndPlay, (void*)getPlayRate() );
+
+ // set the volume
+ MCDoAction( mMovieController, mcActionSetVolume, (void*)mCurVolume );
+ };
+ }
+ else
+ {
+ if(mStatus == STATUS_PLAYING)
+ {
+ setStatus(STATUS_DONE);
+ }
+ }
+ }
+
+ };
+
+ int getDataWidth() const
+ {
+ if ( mGWorldHandle )
+ {
+ int depth = mDepth;
+
+ if (depth < 1)
+ depth = 1;
+
+ // ALWAYS use the row bytes from the PixMap if we have a GWorld because
+ // sometimes it's not the same as mMediaDepth * mMediaWidth !
+ PixMapHandle pix_map_handle = GetGWorldPixMap( mGWorldHandle );
+ return QTGetPixMapHandleRowBytes( pix_map_handle ) / depth;
+ }
+ else
+ {
+ // TODO : return LLMediaImplCommon::getaDataWidth();
+ return 0;
+ }
+ };
+
+ void seek( F64 time )
+ {
+ if ( mMovieController )
+ {
+ TimeRecord when;
+ when.scale = GetMovieTimeScale( mMovieHandle );
+ when.base = 0;
+
+ // 'time' is in (floating point) seconds. The timebase time will be in 'units', where
+ // there are 'scale' units per second.
+ SInt64 raw_time = ( SInt64 )( time * (double)( when.scale ) );
+
+ when.value.hi = ( SInt32 )( raw_time >> 32 );
+ when.value.lo = ( SInt32 )( ( raw_time & 0x00000000FFFFFFFF ) );
+
+ MCDoAction( mMovieController, mcActionGoToTime, &when );
+ };
+ };
+
+ F64 getLoadedDuration()
+ {
+ TimeValue duration;
+ if(GetMaxLoadedTimeInMovie( mMovieHandle, &duration ) != noErr)
+ {
+ // If GetMaxLoadedTimeInMovie returns an error, return the full duration of the movie.
+ duration = GetMovieDuration( mMovieHandle );
+ }
+ TimeValue scale = GetMovieTimeScale( mMovieHandle );
+
+ return (F64)duration / (F64)scale;
+ };
+
+ F64 getDuration()
+ {
+ TimeValue duration = GetMovieDuration( mMovieHandle );
+ TimeValue scale = GetMovieTimeScale( mMovieHandle );
+
+ return (F64)duration / (F64)scale;
+ };
+
+ F64 getCurrentTime()
+ {
+ TimeValue curr_time = GetMovieTime( mMovieHandle, 0 );
+ TimeValue scale = GetMovieTimeScale( mMovieHandle );
+
+ return (F64)curr_time / (F64)scale;
+ };
+
+ void setVolume( F64 volume )
+ {
+ mCurVolume = (short)(volume * ( double ) 0x100 );
+
+ if ( mMovieController )
+ {
+ MCDoAction( mMovieController, mcActionSetVolume, (void*)mCurVolume );
+ };
+ };
+
+ ////////////////////////////////////////////////////////////////////////////////
+ //
+ void update(int milliseconds = 0)
+ {
+ updateQuickTime(milliseconds);
+ };
+
+ ////////////////////////////////////////////////////////////////////////////////
+ //
+ void mouseDown( int x, int y )
+ {
+ };
+
+ ////////////////////////////////////////////////////////////////////////////////
+ //
+ void mouseUp( int x, int y )
+ {
+ };
+
+ ////////////////////////////////////////////////////////////////////////////////
+ //
+ void mouseMove( int x, int y )
+ {
+ };
+
+ ////////////////////////////////////////////////////////////////////////////////
+ //
+ void keyPress( unsigned char key )
+ {
+ };
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // Grab movie title into mMovieTitle - should be called repeatedly
+ // until it returns true since movie title takes a while to become
+ // available.
+ const bool getMovieTitle()
+ {
+ // grab meta data from movie
+ QTMetaDataRef media_data_ref;
+ OSErr result = QTCopyMovieMetaData( mMovieHandle, &media_data_ref );
+ if ( noErr != result )
+ return false;
+
+ // look up "Display Name" in meta data
+ OSType meta_data_key = kQTMetaDataCommonKeyDisplayName;
+ QTMetaDataItem item = kQTMetaDataItemUninitialized;
+ result = QTMetaDataGetNextItem( media_data_ref, kQTMetaDataStorageFormatWildcard,
+ 0, kQTMetaDataKeyFormatCommon,
+ (const UInt8 *)&meta_data_key,
+ sizeof( meta_data_key ), &item );
+ if ( noErr != result )
+ return false;
+
+ // find the size of the title
+ ByteCount size;
+ result = QTMetaDataGetItemValue( media_data_ref, item, NULL, 0, &size );
+ if ( noErr != result || size <= 0 /*|| size > 1024 FIXME: arbitrary limit */ )
+ return false;
+
+ // allocate some space and grab it
+ UInt8* item_data = new UInt8[ size + 1 ];
+ memset( item_data, 0, ( size + 1 ) * sizeof( UInt8 ) );
+ result = QTMetaDataGetItemValue( media_data_ref, item, item_data, size, NULL );
+ if ( noErr != result )
+ {
+ delete [] item_data;
+ return false;
+ };
+
+ // save it
+ if ( strlen( (char*)item_data ) )
+ mMovieTitle = std::string( (char* )item_data );
+ else
+ mMovieTitle = "";
+
+ // clean up
+ delete [] item_data;
+
+ return true;
+ };
+
+ // called regularly to see if title changed
+ void checkTitle()
+ {
+ // we did already receive title so keep checking
+ if ( ! mReceivedTitle )
+ {
+ // grab title from movie meta data
+ if ( getMovieTitle() )
+ {
+ // pass back to host application
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "name_text");
+ message.setValue("name", mMovieTitle );
+ sendMessage( message );
+
+ // stop looking once we find a title for this movie.
+ // TODO: this may to be reset if movie title changes
+ // during playback but this is okay for now
+ mReceivedTitle = true;
+ };
+ };
+ };
+};
+
+MediaPluginQuickTime::MediaPluginQuickTime(
+ LLPluginInstance::sendMessageFunction host_send_func,
+ void *host_user_data ) :
+ MediaPluginBase(host_send_func, host_user_data),
+ mMinWidth( 0 ),
+ mMaxWidth( 2048 ),
+ mMinHeight( 0 ),
+ mMaxHeight( 2048 )
+{
+// std::cerr << "MediaPluginQuickTime constructor" << std::endl;
+
+ mNaturalWidth = -1;
+ mNaturalHeight = -1;
+ mMovieHandle = 0;
+ mGWorldHandle = 0;
+ mMovieController = 0;
+ mCurVolume = 0x99;
+ mMediaSizeChanging = false;
+ mIsLooping = false;
+ mMovieTitle = std::string();
+ mReceivedTitle = false;
+ mCommand = COMMAND_NONE;
+ mPlayRate = 0.0f;
+ mStatus = STATUS_NONE;
+}
+
+MediaPluginQuickTime::~MediaPluginQuickTime()
+{
+// std::cerr << "MediaPluginQuickTime destructor" << std::endl;
+
+ ExitMovies();
+
+#ifdef LL_WINDOWS
+ TerminateQTML();
+// std::cerr << "QuickTime closing down" << std::endl;
+#endif
+}
+
+
+void MediaPluginQuickTime::receiveMessage(const char *message_string)
+{
+// std::cerr << "MediaPluginQuickTime::receiveMessage: received message: \"" << message_string << "\"" << std::endl;
+ LLPluginMessage message_in;
+
+ if(message_in.parse(message_string) >= 0)
+ {
+ std::string message_class = message_in.getClass();
+ std::string message_name = message_in.getName();
+ if(message_class == LLPLUGIN_MESSAGE_CLASS_BASE)
+ {
+ if(message_name == "init")
+ {
+ LLPluginMessage message("base", "init_response");
+ LLSD versions = LLSD::emptyMap();
+ versions[LLPLUGIN_MESSAGE_CLASS_BASE] = LLPLUGIN_MESSAGE_CLASS_BASE_VERSION;
+ versions[LLPLUGIN_MESSAGE_CLASS_MEDIA] = LLPLUGIN_MESSAGE_CLASS_MEDIA_VERSION;
+ // Normally a plugin would only specify one of these two subclasses, but this is a demo...
+ versions[LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME] = LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME_VERSION;
+ message.setValueLLSD("versions", versions);
+
+ #ifdef LL_WINDOWS
+
+ // QuickTime 7.6.4 has an issue (that was not present in 7.6.2) with initializing QuickTime
+ // according to this article: http://lists.apple.com/archives/QuickTime-API/2009/Sep/msg00097.html
+ // The solution presented there appears to work.
+ QTLoadLibrary("qtcf.dll");
+
+ // main initialization for QuickTime - only required on Windows
+ OSErr result = InitializeQTML( 0L );
+ if ( result != noErr )
+ {
+ //TODO: If no QT on Windows, this fails - respond accordingly.
+ }
+ else
+ {
+ //std::cerr << "QuickTime initialized" << std::endl;
+ };
+ #endif
+
+ // required for both Windows and Mac
+ EnterMovies();
+
+ std::string plugin_version = "QuickTime media plugin, QuickTime version ";
+
+ long version = 0;
+ Gestalt( gestaltQuickTimeVersion, &version );
+ std::ostringstream codec( "" );
+ codec << std::hex << version << std::dec;
+ plugin_version += codec.str();
+ message.setValue("plugin_version", plugin_version);
+ sendMessage(message);
+ }
+ else if(message_name == "idle")
+ {
+ // no response is necessary here.
+ F64 time = message_in.getValueReal("time");
+
+ // Convert time to milliseconds for update()
+ update((int)(time * 1000.0f));
+ }
+ else if(message_name == "cleanup")
+ {
+ // TODO: clean up here
+ }
+ else if(message_name == "shm_added")
+ {
+ SharedSegmentInfo info;
+ info.mAddress = message_in.getValuePointer("address");
+ info.mSize = (size_t)message_in.getValueS32("size");
+ std::string name = message_in.getValue("name");
+// std::cerr << "MediaPluginQuickTime::receiveMessage: shared memory added, name: " << name
+// << ", size: " << info.mSize
+// << ", address: " << info.mAddress
+// << std::endl;
+
+ mSharedSegments.insert(SharedSegmentMap::value_type(name, info));
+
+ }
+ else if(message_name == "shm_remove")
+ {
+ std::string name = message_in.getValue("name");
+
+// std::cerr << "MediaPluginQuickTime::receiveMessage: shared memory remove, name = " << name << std::endl;
+
+ SharedSegmentMap::iterator iter = mSharedSegments.find(name);
+ if(iter != mSharedSegments.end())
+ {
+ if(mPixels == iter->second.mAddress)
+ {
+ // This is the currently active pixel buffer. Make sure we stop drawing to it.
+ mPixels = NULL;
+ mTextureSegmentName.clear();
+
+ // Make sure the movie GWorld is no longer pointed at the shared segment.
+ sizeChanged();
+ }
+ mSharedSegments.erase(iter);
+ }
+ else
+ {
+// std::cerr << "MediaPluginQuickTime::receiveMessage: unknown shared memory region!" << std::endl;
+ }
+
+ // Send the response so it can be cleaned up.
+ LLPluginMessage message("base", "shm_remove_response");
+ message.setValue("name", name);
+ sendMessage(message);
+ }
+ else
+ {
+// std::cerr << "MediaPluginQuickTime::receiveMessage: unknown base message: " << message_name << std::endl;
+ }
+ }
+ else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA)
+ {
+ if(message_name == "init")
+ {
+ // This is the media init message -- all necessary data for initialization should have been received.
+
+ // Plugin gets to decide the texture parameters to use.
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "texture_params");
+ #if defined(LL_WINDOWS)
+ // Values for Windows
+ mDepth = 3;
+ message.setValueU32("format", GL_RGB);
+ message.setValueU32("type", GL_UNSIGNED_BYTE);
+
+ // We really want to pad the texture width to a multiple of 32 bytes, but since we're using 3-byte pixels, it doesn't come out even.
+ // Padding to a multiple of 3*32 guarantees it'll divide out properly.
+ message.setValueU32("padding", 32 * 3);
+ #else
+ // Values for Mac
+ mDepth = 4;
+ message.setValueU32("format", GL_BGRA_EXT);
+ #ifdef __BIG_ENDIAN__
+ message.setValueU32("type", GL_UNSIGNED_INT_8_8_8_8_REV );
+ #else
+ message.setValueU32("type", GL_UNSIGNED_INT_8_8_8_8);
+ #endif
+
+ // Pad texture width to a multiple of 32 bytes, to line up with cache lines.
+ message.setValueU32("padding", 32);
+ #endif
+ message.setValueS32("depth", mDepth);
+ message.setValueU32("internalformat", GL_RGB);
+ message.setValueBoolean("coords_opengl", true); // true == use OpenGL-style coordinates, false == (0,0) is upper left.
+ message.setValueBoolean("allow_downsample", true);
+ sendMessage(message);
+ }
+ else if(message_name == "size_change")
+ {
+ std::string name = message_in.getValue("name");
+ S32 width = message_in.getValueS32("width");
+ S32 height = message_in.getValueS32("height");
+ S32 texture_width = message_in.getValueS32("texture_width");
+ S32 texture_height = message_in.getValueS32("texture_height");
+
+ //std::cerr << "---->Got size change instruction from application with name: " << name << " - size is " << width << " x " << height << std::endl;
+
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "size_change_response");
+ message.setValue("name", name);
+ message.setValueS32("width", width);
+ message.setValueS32("height", height);
+ message.setValueS32("texture_width", texture_width);
+ message.setValueS32("texture_height", texture_height);
+ sendMessage(message);
+
+ if(!name.empty())
+ {
+ // Find the shared memory region with this name
+ SharedSegmentMap::iterator iter = mSharedSegments.find(name);
+ if(iter != mSharedSegments.end())
+ {
+// std::cerr << "%%% Got size change, new size is " << width << " by " << height << std::endl;
+// std::cerr << "%%%% texture size is " << texture_width << " by " << texture_height << std::endl;
+
+ mPixels = (unsigned char*)iter->second.mAddress;
+ mTextureSegmentName = name;
+ mWidth = width;
+ mHeight = height;
+
+ mTextureWidth = texture_width;
+ mTextureHeight = texture_height;
+
+ mMediaSizeChanging = false;
+
+ sizeChanged();
+
+ update();
+ };
+ };
+ }
+ else if(message_name == "load_uri")
+ {
+ std::string uri = message_in.getValue("uri");
+ load( uri );
+ sendStatus();
+ }
+ else if(message_name == "mouse_event")
+ {
+ std::string event = message_in.getValue("event");
+ S32 x = message_in.getValueS32("x");
+ S32 y = message_in.getValueS32("y");
+
+ if(event == "down")
+ {
+ mouseDown(x, y);
+ }
+ else if(event == "up")
+ {
+ mouseUp(x, y);
+ }
+ else if(event == "move")
+ {
+ mouseMove(x, y);
+ };
+ };
+ }
+ else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME)
+ {
+ if(message_name == "stop")
+ {
+ stop();
+ }
+ else if(message_name == "start")
+ {
+ F64 rate = 0.0;
+ if(message_in.hasValue("rate"))
+ {
+ rate = message_in.getValueReal("rate");
+ }
+ play(rate);
+ }
+ else if(message_name == "pause")
+ {
+ pause();
+ }
+ else if(message_name == "seek")
+ {
+ F64 time = message_in.getValueReal("time");
+ seek(time);
+ }
+ else if(message_name == "set_loop")
+ {
+ bool loop = message_in.getValueBoolean("loop");
+ mIsLooping = loop;
+ }
+ else if(message_name == "set_volume")
+ {
+ F64 volume = message_in.getValueReal("volume");
+ setVolume(volume);
+ }
+ }
+ else
+ {
+// std::cerr << "MediaPluginQuickTime::receiveMessage: unknown message class: " << message_class << std::endl;
+ };
+ };
+}
+
+int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data)
+{
+ MediaPluginQuickTime *self = new MediaPluginQuickTime(host_send_func, host_user_data);
+ *plugin_send_func = MediaPluginQuickTime::staticReceiveMessage;
+ *plugin_user_data = (void*)self;
+
+ return 0;
+}
+
+#else // LL_QUICKTIME_ENABLED
+
+// Stubbed-out class with constructor/destructor (necessary or windows linker
+// will just think its dead code and optimize it all out)
+class MediaPluginQuickTime : public MediaPluginBase
+{
+public:
+ MediaPluginQuickTime(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data);
+ ~MediaPluginQuickTime();
+ /* virtual */ void receiveMessage(const char *message_string);
+};
+
+MediaPluginQuickTime::MediaPluginQuickTime(
+ LLPluginInstance::sendMessageFunction host_send_func,
+ void *host_user_data ) :
+ MediaPluginBase(host_send_func, host_user_data)
+{
+ // no-op
+}
+
+MediaPluginQuickTime::~MediaPluginQuickTime()
+{
+ // no-op
+}
+
+void MediaPluginQuickTime::receiveMessage(const char *message_string)
+{
+ // no-op
+}
+
+// We're building without quicktime enabled. Just refuse to initialize.
+int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data)
+{
+ return -1;
+}
+
+#endif // LL_QUICKTIME_ENABLED
diff --git a/indra/media_plugins/webkit/CMakeLists.txt b/indra/media_plugins/webkit/CMakeLists.txt
new file mode 100644
index 0000000000..57478ddf27
--- /dev/null
+++ b/indra/media_plugins/webkit/CMakeLists.txt
@@ -0,0 +1,120 @@
+# -*- cmake -*-
+
+project(media_plugin_webkit)
+
+include(00-Common)
+include(LLCommon)
+include(LLImage)
+include(LLPlugin)
+include(LLMath)
+include(LLRender)
+include(LLWindow)
+include(UI)
+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}
+ ${LLMATH_INCLUDE_DIRS}
+ ${LLIMAGE_INCLUDE_DIRS}
+ ${LLRENDER_INCLUDE_DIRS}
+ ${LLWINDOW_INCLUDE_DIRS}
+)
+
+
+### media_plugin_webkit
+
+set(media_plugin_webkit_SOURCE_FILES
+ media_plugin_webkit.cpp
+ )
+
+set(media_plugin_webkit_HEADER_FILES
+ volume_catcher.h
+ )
+
+set(media_plugin_webkit_LINK_LIBRARIES
+ ${LLPLUGIN_LIBRARIES}
+ ${MEDIA_PLUGIN_BASE_LIBRARIES}
+ ${LLCOMMON_LIBRARIES}
+ ${WEBKIT_PLUGIN_LIBRARIES}
+ ${PLUGIN_API_WINDOWS_LIBRARIES}
+ ${PULSEAUDIO_LIBRARIES}
+)
+
+# Select which VolumeCatcher implementation to use
+if (LINUX)
+ if (PULSEAUDIO_FOUND)
+ list(APPEND media_plugin_webkit_SOURCE_FILES linux_volume_catcher.cpp)
+ else (PULSEAUDIO_FOUND)
+ list(APPEND media_plugin_webkit_SOURCE_FILES dummy_volume_catcher.cpp)
+ endif (PULSEAUDIO_FOUND)
+ list(APPEND media_plugin_webkit_LINK_LIBRARIES
+ ${UI_LIBRARIES} # for glib/GTK
+ )
+elseif (DARWIN)
+ list(APPEND media_plugin_webkit_SOURCE_FILES mac_volume_catcher.cpp)
+ find_library(CORESERVICES_LIBRARY CoreServices)
+ find_library(AUDIOUNIT_LIBRARY AudioUnit)
+ list(APPEND media_plugin_webkit_LINK_LIBRARIES
+ ${CORESERVICES_LIBRARY} # for Component Manager calls
+ ${AUDIOUNIT_LIBRARY} # for AudioUnit calls
+ )
+elseif (WINDOWS)
+ list(APPEND media_plugin_webkit_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_webkit_SOURCE_FILES ${media_plugin_webkit_HEADER_FILES})
+
+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
+ ${LLPLUGIN_LIBRARIES}
+ ${MEDIA_PLUGIN_BASE_LIBRARIES}
+ ${LLCOMMON_LIBRARIES}
+)
+
+if (WINDOWS)
+ set_target_properties(
+ media_plugin_webkit
+ PROPERTIES
+ LINK_FLAGS "/MANIFEST:NO"
+ )
+endif (WINDOWS)
+
+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_webkit
+ PROPERTIES
+ PREFIX ""
+ BUILD_WITH_INSTALL_RPATH 1
+ INSTALL_NAME_DIR "@executable_path"
+ LINK_FLAGS "-exported_symbols_list ${CMAKE_CURRENT_SOURCE_DIR}/../base/media_plugin_base.exp"
+ )
+
+ # copy the webkit dylib to the build directory
+ add_custom_command(
+ TARGET media_plugin_webkit POST_BUILD
+# OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/libllqtwebkit.dylib
+ COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/../libraries/universal-darwin/lib_release/libllqtwebkit.dylib ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/
+ DEPENDS media_plugin_webkit ${CMAKE_SOURCE_DIR}/../libraries/universal-darwin/lib_release/libllqtwebkit.dylib
+ )
+
+endif (DARWIN)
+
diff --git a/indra/media_plugins/webkit/dummy_volume_catcher.cpp b/indra/media_plugins/webkit/dummy_volume_catcher.cpp
new file mode 100644
index 0000000000..d54b31b2ae
--- /dev/null
+++ b/indra/media_plugins/webkit/dummy_volume_catcher.cpp
@@ -0,0 +1,58 @@
+/**
+ * @file dummy_volume_catcher.cpp
+ * @brief A null implementation of the "VolumeCatcher" class for platforms where it's not implemented yet.
+ *
+ * @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"
+
+
+class VolumeCatcherImpl
+{
+};
+
+/////////////////////////////////////////////////////
+
+VolumeCatcher::VolumeCatcher()
+{
+ pimpl = NULL;
+}
+
+VolumeCatcher::~VolumeCatcher()
+{
+}
+
+void VolumeCatcher::setVolume(F32 volume)
+{
+}
+
+void VolumeCatcher::setPan(F32 pan)
+{
+}
+
+void VolumeCatcher::pump()
+{
+}
+
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..91be3a89e9
--- /dev/null
+++ b/indra/media_plugins/webkit/linux_volume_catcher.cpp
@@ -0,0 +1,468 @@
+/**
+ * @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=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.h"
+
+
+extern "C" {
+#include <glib.h>
+#include <glib-object.h>
+
+#include <pulse/introspect.h>
+#include <pulse/context.h>
+#include <pulse/subscribe.h>
+#include <pulse/glib-mainloop.h> // There's no special reason why we want the *glib* PA mainloop, but the generic polling implementation seems broken.
+
+#include "apr_pools.h"
+#include "apr_dso.h"
+}
+
+////////////////////////////////////////////////////
+
+#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 VolumeCatcherImpl
+{
+public:
+ VolumeCatcherImpl();
+ ~VolumeCatcherImpl();
+
+ void setVolume(F32 volume);
+ void pump(void);
+
+ // for internal use - can't be private because used from our C callbacks
+
+ bool loadsyms(std::string pulse_dso_name);
+ void init();
+ void cleanup();
+
+ void update_all_volumes(F32 volume);
+ void update_index_volume(U32 index, F32 volume);
+ void connected_okay();
+
+ std::set<U32> mSinkInputIndices;
+ std::map<U32,U32> mSinkInputNumChannels;
+ F32 mDesiredVolume;
+ pa_glib_mainloop *mMainloop;
+ pa_context *mPAContext;
+ bool mConnected;
+ bool mGotSyms;
+};
+
+VolumeCatcherImpl::VolumeCatcherImpl()
+ : mDesiredVolume(0.0f),
+ mMainloop(NULL),
+ mPAContext(NULL),
+ mConnected(false),
+ mGotSyms(false)
+{
+ init();
+}
+
+VolumeCatcherImpl::~VolumeCatcherImpl()
+{
+ cleanup();
+}
+
+bool VolumeCatcherImpl::loadsyms(std::string pulse_dso_name)
+{
+ return grab_pa_syms(pulse_dso_name);
+}
+
+void VolumeCatcherImpl::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;
+
+ // better make double-sure glib itself is initialized properly.
+ if (!g_thread_supported ()) g_thread_init (NULL);
+ g_type_init();
+
+ 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 VolumeCatcherImpl::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 VolumeCatcherImpl::setVolume(F32 volume)
+{
+ mDesiredVolume = volume;
+
+ if (!mGotSyms) return;
+
+ if (mConnected && mPAContext)
+ {
+ update_all_volumes(mDesiredVolume);
+ }
+
+ pump();
+}
+
+void VolumeCatcherImpl::pump()
+{
+ gboolean may_block = FALSE;
+ g_main_context_iteration(g_main_context_default(), may_block);
+}
+
+void VolumeCatcherImpl::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 VolumeCatcherImpl::update_all_volumes(F32 volume)
+{
+ for (std::set<U32>::iterator it = mSinkInputIndices.begin();
+ it != mSinkInputIndices.end(); ++it)
+ {
+ update_index_volume(*it, volume);
+ }
+}
+
+void VolumeCatcherImpl::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)
+{
+ VolumeCatcherImpl *impl = dynamic_cast<VolumeCatcherImpl*>((VolumeCatcherImpl*)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)
+{
+ VolumeCatcherImpl *impl = dynamic_cast<VolumeCatcherImpl*>((VolumeCatcherImpl*)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)
+{
+ VolumeCatcherImpl *impl = dynamic_cast<VolumeCatcherImpl*>((VolumeCatcherImpl*)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:;
+ }
+}
+
+/////////////////////////////////////////////////////
+
+VolumeCatcher::VolumeCatcher()
+{
+ pimpl = new VolumeCatcherImpl();
+}
+
+VolumeCatcher::~VolumeCatcher()
+{
+ delete pimpl;
+ pimpl = NULL;
+}
+
+void VolumeCatcher::setVolume(F32 volume)
+{
+ llassert(pimpl);
+ pimpl->setVolume(volume);
+}
+
+void VolumeCatcher::setPan(F32 pan)
+{
+ // TODO: implement this (if possible)
+}
+
+void VolumeCatcher::pump()
+{
+ llassert(pimpl);
+ pimpl->pump();
+}
diff --git a/indra/media_plugins/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/mac_volume_catcher.cpp b/indra/media_plugins/webkit/mac_volume_catcher.cpp
new file mode 100644
index 0000000000..8a06bb8487
--- /dev/null
+++ b/indra/media_plugins/webkit/mac_volume_catcher.cpp
@@ -0,0 +1,268 @@
+/**
+ * @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 <Carbon/Carbon.h>
+#include <QuickTime/QuickTime.h>
+#include <AudioUnit/AudioUnit.h>
+
+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.
+}
+
diff --git a/indra/media_plugins/webkit/media_plugin_webkit.cpp b/indra/media_plugins/webkit/media_plugin_webkit.cpp
new file mode 100644
index 0000000000..bd1a44a930
--- /dev/null
+++ b/indra/media_plugins/webkit/media_plugin_webkit.cpp
@@ -0,0 +1,1238 @@
+/**
+ * @file media_plugin_webkit.cpp
+ * @brief Webkit plugin for LLMedia API plugin system
+ *
+ * @cond
+ * $LicenseInfo:firstyear=2008&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 "llqtwebkit.h"
+
+#include "linden_common.h"
+#include "indra_constants.h" // for indra keyboard codes
+
+#include "llgl.h"
+
+#include "llplugininstance.h"
+#include "llpluginmessage.h"
+#include "llpluginmessageclasses.h"
+#include "media_plugin_base.h"
+
+// set to 1 if you're using the version of llqtwebkit that's QPixmap-ified
+#if LL_LINUX
+# define LL_QTWEBKIT_USES_PIXMAPS 0
+extern "C" {
+# include <glib.h>
+# include <glib-object.h>
+}
+#else
+# define LL_QTWEBKIT_USES_PIXMAPS 0
+#endif // LL_LINUX
+
+# include "volume_catcher.h"
+
+#if LL_WINDOWS
+# include <direct.h>
+#else
+# include <unistd.h>
+# include <stdlib.h>
+#endif
+
+#if LL_WINDOWS
+ // *NOTE:Mani - This captures the module handle for the dll. This is used below
+ // to get the path to this dll for webkit initialization.
+ // I don't know how/if this can be done with apr...
+ namespace { HMODULE gModuleHandle;};
+ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+ {
+ gModuleHandle = (HMODULE) hinstDLL;
+ return TRUE;
+ }
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+//
+class MediaPluginWebKit :
+ public MediaPluginBase,
+ public LLEmbeddedBrowserWindowObserver
+{
+public:
+ MediaPluginWebKit(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data);
+ ~MediaPluginWebKit();
+
+ /*virtual*/ void receiveMessage(const char *message_string);
+
+private:
+
+ std::string mProfileDir;
+ std::string mHostLanguage;
+ std::string mUserAgent;
+ bool mCookiesEnabled;
+ bool mJavascriptEnabled;
+ bool mPluginsEnabled;
+
+ enum
+ {
+ INIT_STATE_UNINITIALIZED, // LLQtWebkit hasn't been set up yet
+ INIT_STATE_INITIALIZED, // LLQtWebkit has been set up, but no browser window has been created yet.
+ INIT_STATE_NAVIGATING, // Browser instance has been set up and initial navigate to about:blank has been issued
+ INIT_STATE_NAVIGATE_COMPLETE, // initial navigate to about:blank has completed
+ INIT_STATE_WAIT_REDRAW, // First real navigate begin has been received, waiting for page changed event to start handling redraws
+ INIT_STATE_WAIT_COMPLETE, // Waiting for first real navigate complete event
+ INIT_STATE_RUNNING // All initialization gymnastics are complete.
+ };
+ int mBrowserWindowId;
+ int mInitState;
+ std::string mInitialNavigateURL;
+ bool mNeedsUpdate;
+
+ bool mCanCut;
+ bool mCanCopy;
+ bool mCanPaste;
+ int mLastMouseX;
+ int mLastMouseY;
+ bool mFirstFocus;
+ F32 mBackgroundR;
+ F32 mBackgroundG;
+ F32 mBackgroundB;
+ std::string mTarget;
+
+ VolumeCatcher mVolumeCatcher;
+
+ void setInitState(int state)
+ {
+// std::cerr << "changing init state to " << state << std::endl;
+ mInitState = state;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+ //
+ void update(int milliseconds)
+ {
+#if LL_QTLINUX_DOESNT_HAVE_GLIB
+ // pump glib generously, as Linux browser plugins are on the
+ // glib main loop, even if the browser itself isn't - ugh
+ // This is NOT NEEDED if Qt itself was built with glib
+ // mainloop integration.
+ GMainContext *mainc = g_main_context_default();
+ while(g_main_context_iteration(mainc, FALSE));
+#endif // LL_QTLINUX_DOESNT_HAVE_GLIB
+
+ // pump qt
+ LLQtWebKit::getInstance()->pump( milliseconds );
+
+ mVolumeCatcher.pump();
+
+ checkEditState();
+
+ if(mInitState == INIT_STATE_NAVIGATE_COMPLETE)
+ {
+ if(!mInitialNavigateURL.empty())
+ {
+ // We already have the initial navigate URL -- kick off the navigate.
+ LLQtWebKit::getInstance()->navigateTo( mBrowserWindowId, mInitialNavigateURL );
+ mInitialNavigateURL.clear();
+ }
+ }
+
+ if ( (mInitState > INIT_STATE_WAIT_REDRAW) && mNeedsUpdate )
+ {
+ const unsigned char* browser_pixels = LLQtWebKit::getInstance()->grabBrowserWindow( mBrowserWindowId );
+
+ unsigned int rowspan = LLQtWebKit::getInstance()->getBrowserRowSpan( mBrowserWindowId );
+ unsigned int height = LLQtWebKit::getInstance()->getBrowserHeight( mBrowserWindowId );
+#if !LL_QTWEBKIT_USES_PIXMAPS
+ unsigned int buffer_size = rowspan * height;
+#endif // !LL_QTWEBKIT_USES_PIXMAPS
+
+// std::cerr << "webkit plugin: updating" << std::endl;
+
+ // TODO: should get rid of this memcpy if possible
+ if ( mPixels && browser_pixels )
+ {
+// std::cerr << " memcopy of " << buffer_size << " bytes" << std::endl;
+
+#if LL_QTWEBKIT_USES_PIXMAPS
+ // copy the pixel data upside-down because of the co-ord system
+ for (int y=0; y<height; ++y)
+ {
+ memcpy( &mPixels[(height-y-1)*rowspan], &browser_pixels[y*rowspan], rowspan );
+ }
+#else
+ memcpy( mPixels, browser_pixels, buffer_size );
+#endif // LL_QTWEBKIT_USES_PIXMAPS
+ }
+
+ if ( mWidth > 0 && mHeight > 0 )
+ {
+// std::cerr << "Setting dirty, " << mWidth << " x " << mHeight << std::endl;
+ setDirty( 0, 0, mWidth, mHeight );
+ }
+
+ mNeedsUpdate = false;
+ };
+ };
+
+ ////////////////////////////////////////////////////////////////////////////////
+ //
+ bool initBrowser()
+ {
+ // already initialized
+ if ( mInitState > INIT_STATE_UNINITIALIZED )
+ return true;
+
+ // set up directories
+ char cwd[ FILENAME_MAX ]; // I *think* this is defined on all platforms we use
+ if (NULL == getcwd( cwd, FILENAME_MAX - 1 ))
+ {
+ llwarns << "Couldn't get cwd - probably too long - failing to init." << llendl;
+ return false;
+ }
+ std::string application_dir = std::string( cwd );
+
+#if LL_LINUX
+ // take care to initialize glib properly, because some
+ // versions of Qt don't, and we indirectly need it for (some
+ // versions of) Flash to not crash the browser.
+ if (!g_thread_supported ()) g_thread_init (NULL);
+ g_type_init();
+#endif
+
+#if LL_DARWIN
+ // When running under the Xcode debugger, there's a setting called "Break on Debugger()/DebugStr()" which defaults to being turned on.
+ // This causes the environment variable USERBREAK to be set to 1, which causes these legacy calls to break into the debugger.
+ // This wouldn't cause any problems except for the fact that the current release version of the Flash plugin has a call to Debugger() in it
+ // which gets hit when the plugin is probed by webkit.
+ // Unsetting the environment variable here works around this issue.
+ unsetenv("USERBREAK");
+#endif
+
+#if LL_WINDOWS
+ //*NOTE:Mani - On windows, at least, the component path is the
+ // location of this dll's image file.
+ std::string component_dir;
+ char dll_path[_MAX_PATH];
+ DWORD len = GetModuleFileNameA(gModuleHandle, (LPCH)&dll_path, _MAX_PATH);
+ while(len && dll_path[ len ] != ('\\') )
+ {
+ len--;
+ }
+ if(len >= 0)
+ {
+ dll_path[len] = 0;
+ component_dir = dll_path;
+ }
+ else
+ {
+ // *NOTE:Mani - This case should be an rare exception.
+ // GetModuleFileNameA should always give you a full path, no?
+ component_dir = application_dir;
+ }
+#else
+ std::string component_dir = application_dir;
+#endif
+
+ // window handle - needed on Windows and must be app window.
+#if LL_WINDOWS
+ char window_title[ MAX_PATH ];
+ GetConsoleTitleA( window_title, MAX_PATH );
+ void* native_window_handle = (void*)FindWindowA( NULL, window_title );
+#else
+ void* native_window_handle = 0;
+#endif
+
+ // main browser initialization
+ bool result = LLQtWebKit::getInstance()->init( application_dir, component_dir, mProfileDir, native_window_handle );
+ if ( result )
+ {
+ mInitState = INIT_STATE_INITIALIZED;
+
+ return true;
+ };
+
+ return false;
+ };
+
+ ////////////////////////////////////////////////////////////////////////////////
+ //
+ bool initBrowserWindow()
+ {
+ // already initialized
+ if ( mInitState > INIT_STATE_INITIALIZED )
+ return true;
+
+ // not enough information to initialize the browser yet.
+ if ( mWidth < 0 || mHeight < 0 || mDepth < 0 ||
+ mTextureWidth < 0 || mTextureHeight < 0 )
+ {
+ return false;
+ };
+
+ // Set up host language before creating browser window
+ if(!mHostLanguage.empty())
+ {
+ LLQtWebKit::getInstance()->setHostLanguage(mHostLanguage);
+ }
+
+ // turn on/off cookies based on what host app tells us
+ LLQtWebKit::getInstance()->enableCookies( mCookiesEnabled );
+
+ // turn on/off plugins based on what host app tells us
+ LLQtWebKit::getInstance()->enablePlugins( mPluginsEnabled );
+
+ // turn on/off Javascript based on what host app tells us
+ LLQtWebKit::getInstance()->enableJavascript( mJavascriptEnabled );
+
+ // create single browser window
+ mBrowserWindowId = LLQtWebKit::getInstance()->createBrowserWindow( mWidth, mHeight, mTarget);
+
+ // tell LLQtWebKit about the size of the browser window
+ LLQtWebKit::getInstance()->setSize( mBrowserWindowId, mWidth, mHeight );
+
+ // observer events that LLQtWebKit emits
+ LLQtWebKit::getInstance()->addObserver( mBrowserWindowId, this );
+
+ // append details to agent string
+ LLQtWebKit::getInstance()->setBrowserAgentId( mUserAgent );
+
+#if !LL_QTWEBKIT_USES_PIXMAPS
+ // don't flip bitmap
+ LLQtWebKit::getInstance()->flipWindow( mBrowserWindowId, true );
+#endif // !LL_QTWEBKIT_USES_PIXMAPS
+
+ // set background color
+ // convert background color channels from [0.0, 1.0] to [0, 255];
+ LLQtWebKit::getInstance()->setBackgroundColor( mBrowserWindowId, int(mBackgroundR * 255.0f), int(mBackgroundG * 255.0f), int(mBackgroundB * 255.0f) );
+
+ // Set state _before_ starting the navigate, since onNavigateBegin might get called before this call returns.
+ setInitState(INIT_STATE_NAVIGATING);
+
+ // Don't do this here -- it causes the dreaded "white flash" when loading a browser instance.
+ // FIXME: Re-added this because navigating to a "page" initializes things correctly - especially
+ // for the HTTP AUTH dialog issues (DEV-41731). Will fix at a later date.
+ // Build a data URL like this: "data:text/html,%3Chtml%3E%3Cbody bgcolor=%22#RRGGBB%22%3E%3C/body%3E%3C/html%3E"
+ // where RRGGBB is the background color in HTML style
+ std::stringstream url;
+
+ url << "data:text/html,%3Chtml%3E%3Cbody%20bgcolor=%22#";
+ // convert background color channels from [0.0, 1.0] to [0, 255];
+ url << std::setfill('0') << std::setw(2) << std::hex << int(mBackgroundR * 255.0f);
+ url << std::setfill('0') << std::setw(2) << std::hex << int(mBackgroundG * 255.0f);
+ url << std::setfill('0') << std::setw(2) << std::hex << int(mBackgroundB * 255.0f);
+ url << "%22%3E%3C/body%3E%3C/html%3E";
+
+ lldebugs << "data url is: " << url.str() << llendl;
+
+ LLQtWebKit::getInstance()->navigateTo( mBrowserWindowId, url.str() );
+// LLQtWebKit::getInstance()->navigateTo( mBrowserWindowId, "about:blank" );
+
+ return true;
+ }
+
+ void setVolume(F32 vol);
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // virtual
+ void onCursorChanged(const EventType& event)
+ {
+ LLQtWebKit::ECursor llqt_cursor = (LLQtWebKit::ECursor)event.getIntValue();
+ std::string name;
+
+ switch(llqt_cursor)
+ {
+ case LLQtWebKit::C_ARROW:
+ name = "arrow";
+ break;
+ case LLQtWebKit::C_IBEAM:
+ name = "ibeam";
+ break;
+ case LLQtWebKit::C_SPLITV:
+ name = "splitv";
+ break;
+ case LLQtWebKit::C_SPLITH:
+ name = "splith";
+ break;
+ case LLQtWebKit::C_POINTINGHAND:
+ name = "hand";
+ break;
+
+ default:
+ llwarns << "Unknown cursor ID: " << (int)llqt_cursor << llendl;
+ break;
+ }
+
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "cursor_changed");
+ message.setValue("name", name);
+ sendMessage(message);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // virtual
+ void onPageChanged( const EventType& event )
+ {
+ if(mInitState == INIT_STATE_WAIT_REDRAW)
+ {
+ setInitState(INIT_STATE_WAIT_COMPLETE);
+ }
+
+ // flag that an update is required
+ mNeedsUpdate = true;
+ };
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // virtual
+ void onNavigateBegin(const EventType& event)
+ {
+ if(mInitState >= INIT_STATE_NAVIGATE_COMPLETE)
+ {
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "navigate_begin");
+ message.setValue("uri", event.getEventUri());
+ sendMessage(message);
+
+ setStatus(STATUS_LOADING);
+ }
+
+ if(mInitState == INIT_STATE_NAVIGATE_COMPLETE)
+ {
+ // Skip the WAIT_REDRAW state now -- with the right background color set, it should no longer be necessary.
+// setInitState(INIT_STATE_WAIT_REDRAW);
+ setInitState(INIT_STATE_WAIT_COMPLETE);
+ }
+
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // virtual
+ void onNavigateComplete(const EventType& event)
+ {
+ if(mInitState >= INIT_STATE_NAVIGATE_COMPLETE)
+ {
+ if(mInitState < INIT_STATE_RUNNING)
+ {
+ setInitState(INIT_STATE_RUNNING);
+
+ // Clear the history, so the "back" button doesn't take you back to "about:blank".
+ LLQtWebKit::getInstance()->clearHistory(mBrowserWindowId);
+ }
+
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "navigate_complete");
+ message.setValue("uri", event.getEventUri());
+ message.setValueS32("result_code", event.getIntValue());
+ message.setValue("result_string", event.getStringValue());
+ message.setValueBoolean("history_back_available", LLQtWebKit::getInstance()->userActionIsEnabled( mBrowserWindowId, LLQtWebKit::UA_NAVIGATE_BACK));
+ message.setValueBoolean("history_forward_available", LLQtWebKit::getInstance()->userActionIsEnabled( mBrowserWindowId, LLQtWebKit::UA_NAVIGATE_FORWARD));
+ sendMessage(message);
+
+ setStatus(STATUS_LOADED);
+ }
+ else if(mInitState == INIT_STATE_NAVIGATING)
+ {
+ setInitState(INIT_STATE_NAVIGATE_COMPLETE);
+ }
+
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // virtual
+ void onUpdateProgress(const EventType& event)
+ {
+ if(mInitState >= INIT_STATE_NAVIGATE_COMPLETE)
+ {
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "progress");
+ message.setValueS32("percent", event.getIntValue());
+ sendMessage(message);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // virtual
+ void onStatusTextChange(const EventType& event)
+ {
+ if(mInitState >= INIT_STATE_NAVIGATE_COMPLETE)
+ {
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "status_text");
+ message.setValue("status", event.getStringValue());
+ sendMessage(message);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // virtual
+ void onTitleChange(const EventType& event)
+ {
+ if(mInitState >= INIT_STATE_NAVIGATE_COMPLETE)
+ {
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "name_text");
+ message.setValue("name", event.getStringValue());
+ sendMessage(message);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // virtual
+ void onLocationChange(const EventType& event)
+ {
+ if(mInitState >= INIT_STATE_NAVIGATE_COMPLETE)
+ {
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "location_changed");
+ message.setValue("uri", event.getEventUri());
+ sendMessage(message);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // virtual
+ void onClickLinkHref(const EventType& event)
+ {
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "click_href");
+ message.setValue("uri", event.getEventUri());
+ message.setValue("target", event.getStringValue());
+ message.setValue("uuid", event.getStringValue2());
+ sendMessage(message);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // virtual
+ void onClickLinkNoFollow(const EventType& event)
+ {
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "click_nofollow");
+ message.setValue("uri", event.getEventUri());
+ sendMessage(message);
+ }
+
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // virtual
+ void onCookieChanged(const EventType& event)
+ {
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "cookie_set");
+ message.setValue("cookie", event.getStringValue());
+ // These could be passed through as well, but aren't really needed.
+// message.setValue("uri", event.getEventUri());
+// message.setValueBoolean("dead", (event.getIntValue() != 0))
+ sendMessage(message);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // virtual
+ void onWindowCloseRequested(const EventType& event)
+ {
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "close_request");
+ message.setValue("uuid", event.getStringValue());
+ sendMessage(message);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // virtual
+ void onWindowGeometryChangeRequested(const EventType& event)
+ {
+ int x, y, width, height;
+ event.getRectValue(x, y, width, height);
+
+ // This sometimes gets called with a zero-size request. Don't pass these along.
+ if(width > 0 && height > 0)
+ {
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "geometry_change");
+ message.setValue("uuid", event.getStringValue());
+ message.setValueS32("x", x);
+ message.setValueS32("y", y);
+ message.setValueS32("width", width);
+ message.setValueS32("height", height);
+ sendMessage(message);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // virtual
+ std::string onRequestFilePicker( const EventType& eventIn )
+ {
+ return blockingPickFile();
+ }
+
+ LLQtWebKit::EKeyboardModifier decodeModifiers(std::string &modifiers)
+ {
+ int result = 0;
+
+ if(modifiers.find("shift") != std::string::npos)
+ result |= LLQtWebKit::KM_MODIFIER_SHIFT;
+
+ if(modifiers.find("alt") != std::string::npos)
+ result |= LLQtWebKit::KM_MODIFIER_ALT;
+
+ if(modifiers.find("control") != std::string::npos)
+ result |= LLQtWebKit::KM_MODIFIER_CONTROL;
+
+ if(modifiers.find("meta") != std::string::npos)
+ result |= LLQtWebKit::KM_MODIFIER_META;
+
+ return (LLQtWebKit::EKeyboardModifier)result;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+ //
+ void deserializeKeyboardData( LLSD native_key_data, uint32_t& native_scan_code, uint32_t& native_virtual_key, uint32_t& native_modifiers )
+ {
+ native_scan_code = 0;
+ native_virtual_key = 0;
+ native_modifiers = 0;
+
+ if( native_key_data.isMap() )
+ {
+#if LL_DARWIN
+ native_scan_code = (uint32_t)(native_key_data["char_code"].asInteger());
+ native_virtual_key = (uint32_t)(native_key_data["key_code"].asInteger());
+ native_modifiers = (uint32_t)(native_key_data["modifiers"].asInteger());
+#elif LL_WINDOWS
+ native_scan_code = (uint32_t)(native_key_data["scan_code"].asInteger());
+ native_virtual_key = (uint32_t)(native_key_data["virtual_key"].asInteger());
+ // TODO: I don't think we need to do anything with native modifiers here -- please verify
+#elif LL_LINUX
+ native_scan_code = (uint32_t)(native_key_data["scan_code"].asInteger());
+ native_virtual_key = (uint32_t)(native_key_data["virtual_key"].asInteger());
+ native_modifiers = (uint32_t)(native_key_data["modifiers"].asInteger());
+#else
+ // Add other platforms here as needed
+#endif
+ };
+ };
+
+ ////////////////////////////////////////////////////////////////////////////////
+ //
+ void keyEvent(LLQtWebKit::EKeyEvent key_event, int key, LLQtWebKit::EKeyboardModifier modifiers, LLSD native_key_data = LLSD::emptyMap())
+ {
+ // The incoming values for 'key' will be the ones from indra_constants.h
+ std::string utf8_text;
+
+ if(key < KEY_SPECIAL)
+ {
+ // Low-ascii characters need to get passed through.
+ utf8_text = (char)key;
+ }
+
+ // Any special-case handling we want to do for particular keys...
+ switch((KEY)key)
+ {
+ // ASCII codes for some standard keys
+ case LLQtWebKit::KEY_BACKSPACE: utf8_text = (char)8; break;
+ case LLQtWebKit::KEY_TAB: utf8_text = (char)9; break;
+ case LLQtWebKit::KEY_RETURN: utf8_text = (char)13; break;
+ case LLQtWebKit::KEY_PAD_RETURN: utf8_text = (char)13; break;
+ case LLQtWebKit::KEY_ESCAPE: utf8_text = (char)27; break;
+
+ default:
+ break;
+ }
+
+// std::cerr << "key event " << (int)key_event << ", native_key_data = " << native_key_data << std::endl;
+
+ uint32_t native_scan_code = 0;
+ uint32_t native_virtual_key = 0;
+ uint32_t native_modifiers = 0;
+ deserializeKeyboardData( native_key_data, native_scan_code, native_virtual_key, native_modifiers );
+
+ LLQtWebKit::getInstance()->keyboardEvent( mBrowserWindowId, key_event, (uint32_t)key, utf8_text.c_str(), modifiers, native_scan_code, native_virtual_key, native_modifiers);
+
+ checkEditState();
+ };
+
+ ////////////////////////////////////////////////////////////////////////////////
+ //
+ void unicodeInput( const std::string &utf8str, LLQtWebKit::EKeyboardModifier modifiers, LLSD native_key_data = LLSD::emptyMap())
+ {
+ uint32_t key = LLQtWebKit::KEY_NONE;
+
+// std::cerr << "unicode input, native_key_data = " << native_key_data << std::endl;
+
+ if(utf8str.size() == 1)
+ {
+ // The only way a utf8 string can be one byte long is if it's actually a single 7-bit ascii character.
+ // In this case, use it as the key value.
+ key = utf8str[0];
+ }
+
+ uint32_t native_scan_code = 0;
+ uint32_t native_virtual_key = 0;
+ uint32_t native_modifiers = 0;
+ deserializeKeyboardData( native_key_data, native_scan_code, native_virtual_key, native_modifiers );
+
+ LLQtWebKit::getInstance()->keyboardEvent( mBrowserWindowId, LLQtWebKit::KE_KEY_DOWN, (uint32_t)key, utf8str.c_str(), modifiers, native_scan_code, native_virtual_key, native_modifiers);
+ LLQtWebKit::getInstance()->keyboardEvent( mBrowserWindowId, LLQtWebKit::KE_KEY_UP, (uint32_t)key, utf8str.c_str(), modifiers, native_scan_code, native_virtual_key, native_modifiers);
+
+ checkEditState();
+ };
+
+ void checkEditState(void)
+ {
+ bool can_cut = LLQtWebKit::getInstance()->userActionIsEnabled( mBrowserWindowId, LLQtWebKit::UA_EDIT_CUT);
+ bool can_copy = LLQtWebKit::getInstance()->userActionIsEnabled( mBrowserWindowId, LLQtWebKit::UA_EDIT_COPY);
+ bool can_paste = LLQtWebKit::getInstance()->userActionIsEnabled( mBrowserWindowId, LLQtWebKit::UA_EDIT_PASTE);
+
+ if((can_cut != mCanCut) || (can_copy != mCanCopy) || (can_paste != mCanPaste))
+ {
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "edit_state");
+
+ if(can_cut != mCanCut)
+ {
+ mCanCut = can_cut;
+ message.setValueBoolean("cut", can_cut);
+ }
+
+ if(can_copy != mCanCopy)
+ {
+ mCanCopy = can_copy;
+ message.setValueBoolean("copy", can_copy);
+ }
+
+ if(can_paste != mCanPaste)
+ {
+ mCanPaste = can_paste;
+ message.setValueBoolean("paste", can_paste);
+ }
+
+ sendMessage(message);
+
+ }
+ }
+
+ std::string mPickedFile;
+
+ std::string blockingPickFile(void)
+ {
+ mPickedFile.clear();
+
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "pick_file");
+ message.setValueBoolean("blocking_request", true);
+
+ // The "blocking_request" key in the message means this sendMessage call will block until a response is received.
+ sendMessage(message);
+
+ return mPickedFile;
+ }
+
+ void onPickFileResponse(const std::string &file)
+ {
+ mPickedFile = file;
+ }
+
+};
+
+MediaPluginWebKit::MediaPluginWebKit(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data) :
+ MediaPluginBase(host_send_func, host_user_data)
+{
+// std::cerr << "MediaPluginWebKit constructor" << std::endl;
+
+ mBrowserWindowId = 0;
+ mInitState = INIT_STATE_UNINITIALIZED;
+ mNeedsUpdate = true;
+ mCanCut = false;
+ mCanCopy = false;
+ mCanPaste = false;
+ mLastMouseX = 0;
+ mLastMouseY = 0;
+ mFirstFocus = true;
+ mBackgroundR = 0.0f;
+ mBackgroundG = 0.0f;
+ mBackgroundB = 0.0f;
+
+ mHostLanguage = "en"; // default to english
+ mJavascriptEnabled = true; // default to on
+ mPluginsEnabled = true; // default to on
+ mUserAgent = "LLPluginMedia Web Browser";
+}
+
+MediaPluginWebKit::~MediaPluginWebKit()
+{
+ // unhook observer
+ LLQtWebKit::getInstance()->remObserver( mBrowserWindowId, this );
+
+ // clean up
+ LLQtWebKit::getInstance()->reset();
+
+// std::cerr << "MediaPluginWebKit destructor" << std::endl;
+}
+
+void MediaPluginWebKit::receiveMessage(const char *message_string)
+{
+// std::cerr << "MediaPluginWebKit::receiveMessage: received message: \"" << message_string << "\"" << std::endl;
+ LLPluginMessage message_in;
+
+ if(message_in.parse(message_string) >= 0)
+ {
+ std::string message_class = message_in.getClass();
+ std::string message_name = message_in.getName();
+ if(message_class == LLPLUGIN_MESSAGE_CLASS_BASE)
+ {
+ if(message_name == "init")
+ {
+ LLPluginMessage message("base", "init_response");
+ LLSD versions = LLSD::emptyMap();
+ versions[LLPLUGIN_MESSAGE_CLASS_BASE] = LLPLUGIN_MESSAGE_CLASS_BASE_VERSION;
+ versions[LLPLUGIN_MESSAGE_CLASS_MEDIA] = LLPLUGIN_MESSAGE_CLASS_MEDIA_VERSION;
+ versions[LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER] = LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER_VERSION;
+ message.setValueLLSD("versions", versions);
+
+ std::string plugin_version = "Webkit media plugin, Webkit version ";
+ plugin_version += LLQtWebKit::getInstance()->getVersion();
+ message.setValue("plugin_version", plugin_version);
+ sendMessage(message);
+ }
+ else if(message_name == "idle")
+ {
+ // no response is necessary here.
+ F64 time = message_in.getValueReal("time");
+
+ // Convert time to milliseconds for update()
+ update((int)(time * 1000.0f));
+ }
+ else if(message_name == "cleanup")
+ {
+ // DTOR most likely won't be called but the recent change to the way this process
+ // is (not) killed means we see this message and can do what we need to here.
+ // Note: this cleanup is ultimately what writes cookies to the disk
+ LLQtWebKit::getInstance()->remObserver( mBrowserWindowId, this );
+ LLQtWebKit::getInstance()->reset();
+ }
+ else if(message_name == "shm_added")
+ {
+ SharedSegmentInfo info;
+ info.mAddress = message_in.getValuePointer("address");
+ info.mSize = (size_t)message_in.getValueS32("size");
+ std::string name = message_in.getValue("name");
+
+// std::cerr << "MediaPluginWebKit::receiveMessage: shared memory added, name: " << name
+// << ", size: " << info.mSize
+// << ", address: " << info.mAddress
+// << std::endl;
+
+ mSharedSegments.insert(SharedSegmentMap::value_type(name, info));
+
+ }
+ else if(message_name == "shm_remove")
+ {
+ std::string name = message_in.getValue("name");
+
+// std::cerr << "MediaPluginWebKit::receiveMessage: shared memory remove, name = " << name << std::endl;
+
+ SharedSegmentMap::iterator iter = mSharedSegments.find(name);
+ if(iter != mSharedSegments.end())
+ {
+ if(mPixels == iter->second.mAddress)
+ {
+ // This is the currently active pixel buffer. Make sure we stop drawing to it.
+ mPixels = NULL;
+ mTextureSegmentName.clear();
+ }
+ mSharedSegments.erase(iter);
+ }
+ else
+ {
+// std::cerr << "MediaPluginWebKit::receiveMessage: unknown shared memory region!" << std::endl;
+ }
+
+ // Send the response so it can be cleaned up.
+ LLPluginMessage message("base", "shm_remove_response");
+ message.setValue("name", name);
+ sendMessage(message);
+ }
+ else
+ {
+// 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 == "init")
+ {
+ mTarget = message_in.getValue("target");
+
+ // This is the media init message -- all necessary data for initialization should have been received.
+ if(initBrowser())
+ {
+
+ // Plugin gets to decide the texture parameters to use.
+ mDepth = 4;
+
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "texture_params");
+ message.setValueS32("default_width", 1024);
+ message.setValueS32("default_height", 1024);
+ message.setValueS32("depth", mDepth);
+ message.setValueU32("internalformat", GL_RGBA);
+ #if LL_QTWEBKIT_USES_PIXMAPS
+ message.setValueU32("format", GL_BGRA_EXT); // I hope this isn't system-dependant... is it? If so, we'll have to check the root window's pixel layout or something... yuck.
+ #else
+ message.setValueU32("format", GL_RGBA);
+ #endif // LL_QTWEBKIT_USES_PIXMAPS
+ message.setValueU32("type", GL_UNSIGNED_BYTE);
+ message.setValueBoolean("coords_opengl", true);
+ sendMessage(message);
+ }
+ else
+ {
+ // if initialization failed, we're done.
+ mDeleteMe = true;
+ }
+
+ }
+ else if(message_name == "set_user_data_path")
+ {
+ std::string user_data_path = message_in.getValue("path"); // n.b. always has trailing platform-specific dir-delimiter
+ mProfileDir = user_data_path + "browser_profile";
+
+ // FIXME: Should we do anything with this if it comes in after the browser has been initialized?
+ }
+ else if(message_name == "set_language_code")
+ {
+ mHostLanguage = message_in.getValue("language");
+
+ // FIXME: Should we do anything with this if it comes in after the browser has been initialized?
+ }
+ else if(message_name == "plugins_enabled")
+ {
+ mPluginsEnabled = message_in.getValueBoolean("enable");
+ }
+ else if(message_name == "javascript_enabled")
+ {
+ mJavascriptEnabled = message_in.getValueBoolean("enable");
+ }
+ else if(message_name == "size_change")
+ {
+ std::string name = message_in.getValue("name");
+ S32 width = message_in.getValueS32("width");
+ S32 height = message_in.getValueS32("height");
+ S32 texture_width = message_in.getValueS32("texture_width");
+ S32 texture_height = message_in.getValueS32("texture_height");
+ mBackgroundR = message_in.getValueReal("background_r");
+ mBackgroundG = message_in.getValueReal("background_g");
+ mBackgroundB = message_in.getValueReal("background_b");
+// mBackgroundA = message_in.setValueReal("background_a"); // Ignore any alpha
+
+ if(!name.empty())
+ {
+ // Find the shared memory region with this name
+ SharedSegmentMap::iterator iter = mSharedSegments.find(name);
+ if(iter != mSharedSegments.end())
+ {
+ mPixels = (unsigned char*)iter->second.mAddress;
+ mWidth = width;
+ mHeight = height;
+
+ if(initBrowserWindow())
+ {
+
+ // size changed so tell the browser
+ LLQtWebKit::getInstance()->setSize( mBrowserWindowId, mWidth, mHeight );
+
+ // std::cerr << "webkit plugin: set size to " << mWidth << " x " << mHeight
+ // << ", rowspan is " << LLQtWebKit::getInstance()->getBrowserRowSpan(mBrowserWindowId) << std::endl;
+
+ S32 real_width = LLQtWebKit::getInstance()->getBrowserRowSpan(mBrowserWindowId) / LLQtWebKit::getInstance()->getBrowserDepth(mBrowserWindowId);
+
+ // The actual width the browser will be drawing to is probably smaller... let the host know by modifying texture_width in the response.
+ if(real_width <= texture_width)
+ {
+ texture_width = real_width;
+ }
+ else
+ {
+ // This won't work -- it'll be bigger than the allocated memory. This is a fatal error.
+ // std::cerr << "Fatal error: browser rowbytes greater than texture width" << std::endl;
+ mDeleteMe = true;
+ return;
+ }
+ }
+ else
+ {
+ // Setting up the browser window failed. This is a fatal error.
+ mDeleteMe = true;
+ }
+
+
+ mTextureWidth = texture_width;
+ mTextureHeight = texture_height;
+
+ };
+ };
+
+ LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "size_change_response");
+ message.setValue("name", name);
+ message.setValueS32("width", width);
+ message.setValueS32("height", height);
+ message.setValueS32("texture_width", texture_width);
+ message.setValueS32("texture_height", texture_height);
+ sendMessage(message);
+
+ }
+ else if(message_name == "load_uri")
+ {
+ std::string uri = message_in.getValue("uri");
+
+// std::cout << "loading URI: " << uri << std::endl;
+
+ if(!uri.empty())
+ {
+ if(mInitState >= INIT_STATE_NAVIGATE_COMPLETE)
+ {
+ LLQtWebKit::getInstance()->navigateTo( mBrowserWindowId, uri );
+ }
+ else
+ {
+ mInitialNavigateURL = uri;
+ }
+ }
+ }
+ else if(message_name == "mouse_event")
+ {
+ std::string event = message_in.getValue("event");
+ S32 button = message_in.getValueS32("button");
+ mLastMouseX = message_in.getValueS32("x");
+ mLastMouseY = message_in.getValueS32("y");
+ std::string modifiers = message_in.getValue("modifiers");
+
+ // Treat unknown mouse events as mouse-moves.
+ LLQtWebKit::EMouseEvent mouse_event = LLQtWebKit::ME_MOUSE_MOVE;
+ if(event == "down")
+ {
+ mouse_event = LLQtWebKit::ME_MOUSE_DOWN;
+ }
+ else if(event == "up")
+ {
+ mouse_event = LLQtWebKit::ME_MOUSE_UP;
+ }
+ else if(event == "double_click")
+ {
+ mouse_event = LLQtWebKit::ME_MOUSE_DOUBLE_CLICK;
+ }
+
+ LLQtWebKit::getInstance()->mouseEvent( mBrowserWindowId, mouse_event, button, mLastMouseX, mLastMouseY, decodeModifiers(modifiers));
+ checkEditState();
+ }
+ else if(message_name == "scroll_event")
+ {
+ S32 x = message_in.getValueS32("x");
+ S32 y = message_in.getValueS32("y");
+ std::string modifiers = message_in.getValue("modifiers");
+
+ // Incoming scroll events are adjusted so that 1 detent is approximately 1 unit.
+ // Qt expects 1 detent to be 120 units.
+ // It also seems that our y scroll direction is inverted vs. what Qt expects.
+
+ x *= 120;
+ y *= -120;
+
+ LLQtWebKit::getInstance()->scrollWheelEvent(mBrowserWindowId, mLastMouseX, mLastMouseY, x, y, decodeModifiers(modifiers));
+ }
+ else if(message_name == "key_event")
+ {
+ std::string event = message_in.getValue("event");
+ S32 key = message_in.getValueS32("key");
+ std::string modifiers = message_in.getValue("modifiers");
+ LLSD native_key_data = message_in.getValueLLSD("native_key_data");
+
+ // Treat unknown events as key-up for safety.
+ LLQtWebKit::EKeyEvent key_event = LLQtWebKit::KE_KEY_UP;
+ if(event == "down")
+ {
+ key_event = LLQtWebKit::KE_KEY_DOWN;
+ }
+ else if(event == "repeat")
+ {
+ key_event = LLQtWebKit::KE_KEY_REPEAT;
+ }
+
+ keyEvent(key_event, key, decodeModifiers(modifiers), native_key_data);
+ }
+ else if(message_name == "text_event")
+ {
+ std::string text = message_in.getValue("text");
+ std::string modifiers = message_in.getValue("modifiers");
+ LLSD native_key_data = message_in.getValueLLSD("native_key_data");
+
+ unicodeInput(text, decodeModifiers(modifiers), native_key_data);
+ }
+ if(message_name == "edit_cut")
+ {
+ LLQtWebKit::getInstance()->userAction( mBrowserWindowId, LLQtWebKit::UA_EDIT_CUT );
+ checkEditState();
+ }
+ if(message_name == "edit_copy")
+ {
+ LLQtWebKit::getInstance()->userAction( mBrowserWindowId, LLQtWebKit::UA_EDIT_COPY );
+ checkEditState();
+ }
+ if(message_name == "edit_paste")
+ {
+ LLQtWebKit::getInstance()->userAction( mBrowserWindowId, LLQtWebKit::UA_EDIT_PASTE );
+ checkEditState();
+ }
+ if(message_name == "pick_file_response")
+ {
+ onPickFileResponse(message_in.getValue("file"));
+ }
+ else
+ {
+// std::cerr << "MediaPluginWebKit::receiveMessage: unknown media message: " << message_string << std::endl;
+ }
+ }
+ else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER)
+ {
+ if(message_name == "focus")
+ {
+ bool val = message_in.getValueBoolean("focused");
+ LLQtWebKit::getInstance()->focusBrowser( mBrowserWindowId, val );
+
+ if(mFirstFocus && val)
+ {
+ // On the first focus, post a tab key event. This fixes a problem with initial focus.
+ std::string empty;
+ keyEvent(LLQtWebKit::KE_KEY_DOWN, KEY_TAB, decodeModifiers(empty));
+ keyEvent(LLQtWebKit::KE_KEY_UP, KEY_TAB, decodeModifiers(empty));
+ mFirstFocus = false;
+ }
+ }
+ else if(message_name == "clear_cache")
+ {
+ LLQtWebKit::getInstance()->clearCache();
+ }
+ else if(message_name == "clear_cookies")
+ {
+ LLQtWebKit::getInstance()->clearAllCookies();
+ }
+ else if(message_name == "enable_cookies")
+ {
+ mCookiesEnabled = message_in.getValueBoolean("enable");
+ LLQtWebKit::getInstance()->enableCookies( mCookiesEnabled );
+ }
+ else if(message_name == "enable_plugins")
+ {
+ mPluginsEnabled = message_in.getValueBoolean("enable");
+ LLQtWebKit::getInstance()->enablePlugins( mPluginsEnabled );
+ }
+ else if(message_name == "enable_javascript")
+ {
+ mJavascriptEnabled = message_in.getValueBoolean("enable");
+ //LLQtWebKit::getInstance()->enableJavascript( mJavascriptEnabled );
+ }
+ else if(message_name == "set_cookies")
+ {
+ LLQtWebKit::getInstance()->setCookies(message_in.getValue("cookies"));
+ }
+ else if(message_name == "proxy_setup")
+ {
+ bool val = message_in.getValueBoolean("enable");
+ std::string host = message_in.getValue("host");
+ int port = message_in.getValueS32("port");
+ LLQtWebKit::getInstance()->enableProxy( val, host, port );
+ }
+ else if(message_name == "browse_stop")
+ {
+ LLQtWebKit::getInstance()->userAction( mBrowserWindowId, LLQtWebKit::UA_NAVIGATE_STOP );
+ }
+ else if(message_name == "browse_reload")
+ {
+ // foo = message_in.getValueBoolean("ignore_cache");
+ LLQtWebKit::getInstance()->userAction( mBrowserWindowId, LLQtWebKit::UA_NAVIGATE_RELOAD );
+ }
+ else if(message_name == "browse_forward")
+ {
+ LLQtWebKit::getInstance()->userAction( mBrowserWindowId, LLQtWebKit::UA_NAVIGATE_FORWARD );
+ }
+ else if(message_name == "browse_back")
+ {
+ LLQtWebKit::getInstance()->userAction( mBrowserWindowId, LLQtWebKit::UA_NAVIGATE_BACK );
+ }
+ else if(message_name == "set_status_redirect")
+ {
+ int code = message_in.getValueS32("code");
+ std::string url = message_in.getValue("url");
+ if ( 404 == code ) // browser lib only supports 404 right now
+ {
+ LLQtWebKit::getInstance()->set404RedirectUrl( mBrowserWindowId, url );
+ };
+ }
+ else if(message_name == "set_user_agent")
+ {
+ mUserAgent = message_in.getValue("user_agent");
+ LLQtWebKit::getInstance()->setBrowserAgentId( mUserAgent );
+ }
+ else if(message_name == "init_history")
+ {
+ // Initialize browser history
+ LLSD history = message_in.getValueLLSD("history");
+ // First, clear the URL history
+ LLQtWebKit::getInstance()->clearHistory(mBrowserWindowId);
+ // Then, add the history items in order
+ LLSD::array_iterator iter_history = history.beginArray();
+ LLSD::array_iterator end_history = history.endArray();
+ for(; iter_history != end_history; ++iter_history)
+ {
+ std::string url = (*iter_history).asString();
+ if(! url.empty()) {
+ LLQtWebKit::getInstance()->prependHistoryUrl(mBrowserWindowId, url);
+ }
+ }
+ }
+ else if(message_name == "proxy_window_opened")
+ {
+ std::string target = message_in.getValue("target");
+ std::string uuid = message_in.getValue("uuid");
+ LLQtWebKit::getInstance()->proxyWindowOpened(mBrowserWindowId, target, uuid);
+ }
+ else if(message_name == "proxy_window_closed")
+ {
+ std::string uuid = message_in.getValue("uuid");
+ LLQtWebKit::getInstance()->proxyWindowClosed(mBrowserWindowId, uuid);
+ }
+ else
+ {
+// std::cerr << "MediaPluginWebKit::receiveMessage: unknown media_browser message: " << message_string << std::endl;
+ };
+ }
+ else
+ {
+// std::cerr << "MediaPluginWebKit::receiveMessage: unknown message class: " << message_class << std::endl;
+ };
+ }
+}
+
+void MediaPluginWebKit::setVolume(F32 volume)
+{
+ mVolumeCatcher.setVolume(volume);
+}
+
+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);
+ *plugin_send_func = MediaPluginWebKit::staticReceiveMessage;
+ *plugin_user_data = (void*)self;
+
+ return 0;
+}
+
diff --git a/indra/media_plugins/webkit/volume_catcher.h b/indra/media_plugins/webkit/volume_catcher.h
new file mode 100644
index 0000000000..337f2913d3
--- /dev/null
+++ b/indra/media_plugins/webkit/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/webkit/windows_volume_catcher.cpp b/indra/media_plugins/webkit/windows_volume_catcher.cpp
new file mode 100644
index 0000000000..5fb84756ee
--- /dev/null
+++ b/indra/media_plugins/webkit/windows_volume_catcher.cpp
@@ -0,0 +1,117 @@
+/**
+ * @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;
+
+ F32 mVolume;
+ F32 mPan;
+};
+VolumeCatcherImpl::VolumeCatcherImpl()
+: mVolume(1.0f), // default volume is max
+ mPan(0.f) // default pan is centered
+{
+ 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 (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.
+}
+
+
diff --git a/indra/media_plugins/winmmshim/CMakeLists.txt b/indra/media_plugins/winmmshim/CMakeLists.txt
new file mode 100644
index 0000000000..387214088f
--- /dev/null
+++ b/indra/media_plugins/winmmshim/CMakeLists.txt
@@ -0,0 +1,34 @@
+# -*- cmake -*-
+
+project(winmm_shim)
+
+### winmm_shim
+
+set(winmm_shim_SOURCE_FILES
+ forwarding_api.cpp
+ winmm_shim.cpp
+ )
+
+set(winmm_shim_HEADER_FILES
+ forwarding_api.h
+ winmm.def
+ )
+
+list(APPEND winmm_shim_SOURCE_FILES ${winmm_shim_HEADER_FILES})
+
+set_source_files_properties(${media_plugin_webkit_HEADER_FILES}
+ PROPERTIES HEADER_FILE_ONLY TRUE)
+
+add_library(winmm_shim
+ SHARED
+ ${winmm_shim_SOURCE_FILES}
+)
+
+if (WINDOWS)
+ set_target_properties(
+ winmm_shim
+ PROPERTIES
+ LINK_FLAGS "/MANIFEST:NO"
+ OUTPUT_NAME "winmm"
+ )
+endif (WINDOWS)
diff --git a/indra/media_plugins/winmmshim/forwarding_api.cpp b/indra/media_plugins/winmmshim/forwarding_api.cpp
new file mode 100644
index 0000000000..eff7e20451
--- /dev/null
+++ b/indra/media_plugins/winmmshim/forwarding_api.cpp
@@ -0,0 +1,1454 @@
+/**
+ * @file forwarding_api.cpp
+ * @brief forwards winmm API calls to real winmm.dll
+ *
+ * $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$
+ */
+
+#include "forwarding_api.h"
+
+CloseDriver_type CloseDriver_orig;
+OpenDriver_type OpenDriver_orig;
+SendDriverMessage_type SendDriverMessage_orig;
+DrvGetModuleHandle_type DrvGetModuleHandle_orig;
+GetDriverModuleHandle_type GetDriverModuleHandle_orig;
+DefDriverProc_type DefDriverProc_orig;
+DriverCallback_type DriverCallback_orig;
+mmsystemGetVersion_type mmsystemGetVersion_orig;
+sndPlaySoundA_type sndPlaySoundA_orig;
+sndPlaySoundW_type sndPlaySoundW_orig;
+PlaySoundA_type PlaySoundA_orig;
+PlaySoundW_type PlaySoundW_orig;
+waveOutGetNumDevs_type waveOutGetNumDevs_orig;
+waveOutGetDevCapsA_type waveOutGetDevCapsA_orig;
+waveOutGetDevCapsW_type waveOutGetDevCapsW_orig;
+waveOutGetVolume_type waveOutGetVolume_orig;
+waveOutSetVolume_type waveOutSetVolume_orig;
+waveOutGetErrorTextA_type waveOutGetErrorTextA_orig;
+waveOutGetErrorTextW_type waveOutGetErrorTextW_orig;
+waveOutOpen_type waveOutOpen_orig;
+waveOutClose_type waveOutClose_orig;
+waveOutPrepareHeader_type waveOutPrepareHeader_orig;
+waveOutUnprepareHeader_type waveOutUnprepareHeader_orig;
+waveOutWrite_type waveOutWrite_orig;
+waveOutPause_type waveOutPause_orig;
+waveOutRestart_type waveOutRestart_orig;
+waveOutReset_type waveOutReset_orig;
+waveOutBreakLoop_type waveOutBreakLoop_orig;
+waveOutGetPosition_type waveOutGetPosition_orig;
+waveOutGetPitch_type waveOutGetPitch_orig;
+waveOutSetPitch_type waveOutSetPitch_orig;
+waveOutGetPlaybackRate_type waveOutGetPlaybackRate_orig;
+waveOutSetPlaybackRate_type waveOutSetPlaybackRate_orig;
+waveOutGetID_type waveOutGetID_orig;
+waveOutMessage_type waveOutMessage_orig;
+waveInGetNumDevs_type waveInGetNumDevs_orig;
+waveInGetDevCapsA_type waveInGetDevCapsA_orig;
+waveInGetDevCapsW_type waveInGetDevCapsW_orig;
+waveInGetErrorTextA_type waveInGetErrorTextA_orig;
+waveInGetErrorTextW_type waveInGetErrorTextW_orig;
+waveInOpen_type waveInOpen_orig;
+waveInClose_type waveInClose_orig;
+waveInPrepareHeader_type waveInPrepareHeader_orig;
+waveInUnprepareHeader_type waveInUnprepareHeader_orig;
+waveInAddBuffer_type waveInAddBuffer_orig;
+waveInStart_type waveInStart_orig;
+waveInStop_type waveInStop_orig;
+waveInReset_type waveInReset_orig;
+waveInGetPosition_type waveInGetPosition_orig;
+waveInGetID_type waveInGetID_orig;
+waveInMessage_type waveInMessage_orig;
+midiOutGetNumDevs_type midiOutGetNumDevs_orig;
+midiStreamOpen_type midiStreamOpen_orig;
+midiStreamClose_type midiStreamClose_orig;
+midiStreamProperty_type midiStreamProperty_orig;
+midiStreamPosition_type midiStreamPosition_orig;
+midiStreamOut_type midiStreamOut_orig;
+midiStreamPause_type midiStreamPause_orig;
+midiStreamRestart_type midiStreamRestart_orig;
+midiStreamStop_type midiStreamStop_orig;
+midiConnect_type midiConnect_orig;
+midiDisconnect_type midiDisconnect_orig;
+midiOutGetDevCapsA_type midiOutGetDevCapsA_orig;
+midiOutGetDevCapsW_type midiOutGetDevCapsW_orig;
+midiOutGetVolume_type midiOutGetVolume_orig;
+midiOutSetVolume_type midiOutSetVolume_orig;
+midiOutGetErrorTextA_type midiOutGetErrorTextA_orig;
+midiOutGetErrorTextW_type midiOutGetErrorTextW_orig;
+midiOutOpen_type midiOutOpen_orig;
+midiOutClose_type midiOutClose_orig;
+midiOutPrepareHeader_type midiOutPrepareHeader_orig;
+midiOutUnprepareHeader_type midiOutUnprepareHeader_orig;
+midiOutShortMsg_type midiOutShortMsg_orig;
+midiOutLongMsg_type midiOutLongMsg_orig;
+midiOutReset_type midiOutReset_orig;
+midiOutCachePatches_type midiOutCachePatches_orig;
+midiOutCacheDrumPatches_type midiOutCacheDrumPatches_orig;
+midiOutGetID_type midiOutGetID_orig;
+midiOutMessage_type midiOutMessage_orig;
+midiInGetNumDevs_type midiInGetNumDevs_orig;
+midiInGetDevCapsA_type midiInGetDevCapsA_orig;
+midiInGetDevCapsW_type midiInGetDevCapsW_orig;
+midiInGetErrorTextA_type midiInGetErrorTextA_orig;
+midiInGetErrorTextW_type midiInGetErrorTextW_orig;
+midiInOpen_type midiInOpen_orig;
+midiInClose_type midiInClose_orig;
+midiInPrepareHeader_type midiInPrepareHeader_orig;
+midiInUnprepareHeader_type midiInUnprepareHeader_orig;
+midiInAddBuffer_type midiInAddBuffer_orig;
+midiInStart_type midiInStart_orig;
+midiInStop_type midiInStop_orig;
+midiInReset_type midiInReset_orig;
+midiInGetID_type midiInGetID_orig;
+midiInMessage_type midiInMessage_orig;
+auxGetNumDevs_type auxGetNumDevs_orig;
+auxGetDevCapsA_type auxGetDevCapsA_orig;
+auxGetDevCapsW_type auxGetDevCapsW_orig;
+auxSetVolume_type auxSetVolume_orig;
+auxGetVolume_type auxGetVolume_orig;
+auxOutMessage_type auxOutMessage_orig;
+mixerGetNumDevs_type mixerGetNumDevs_orig;
+mixerGetDevCapsA_type mixerGetDevCapsA_orig;
+mixerGetDevCapsW_type mixerGetDevCapsW_orig;
+mixerOpen_type mixerOpen_orig;
+mixerClose_type mixerClose_orig;
+mixerMessage_type mixerMessage_orig;
+mixerGetLineInfoA_type mixerGetLineInfoA_orig;
+mixerGetLineInfoW_type mixerGetLineInfoW_orig;
+mixerGetID_type mixerGetID_orig;
+mixerGetLineControlsA_type mixerGetLineControlsA_orig;
+mixerGetLineControlsW_type mixerGetLineControlsW_orig;
+mixerGetControlDetailsA_type mixerGetControlDetailsA_orig;
+mixerGetControlDetailsW_type mixerGetControlDetailsW_orig;
+mixerSetControlDetails_type mixerSetControlDetails_orig;
+mmGetCurrentTask_type mmGetCurrentTask_orig;
+mmTaskBlock_type mmTaskBlock_orig;
+mmTaskCreate_type mmTaskCreate_orig;
+mmTaskSignal_type mmTaskSignal_orig;
+mmTaskYield_type mmTaskYield_orig;
+timeGetSystemTime_type timeGetSystemTime_orig;
+timeGetTime_type timeGetTime_orig;
+timeSetEvent_type timeSetEvent_orig;
+timeKillEvent_type timeKillEvent_orig;
+timeGetDevCaps_type timeGetDevCaps_orig;
+timeBeginPeriod_type timeBeginPeriod_orig;
+timeEndPeriod_type timeEndPeriod_orig;
+joyGetNumDevs_type joyGetNumDevs_orig;
+joyConfigChanged_type joyConfigChanged_orig;
+joyGetDevCapsA_type joyGetDevCapsA_orig;
+joyGetDevCapsW_type joyGetDevCapsW_orig;
+joyGetPos_type joyGetPos_orig;
+joyGetPosEx_type joyGetPosEx_orig;
+joyGetThreshold_type joyGetThreshold_orig;
+joyReleaseCapture_type joyReleaseCapture_orig;
+joySetCapture_type joySetCapture_orig;
+joySetThreshold_type joySetThreshold_orig;
+mmioStringToFOURCCA_type mmioStringToFOURCCA_orig;
+mmioStringToFOURCCW_type mmioStringToFOURCCW_orig;
+mmioInstallIOProcA_type mmioInstallIOProcA_orig;
+mmioInstallIOProcW_type mmioInstallIOProcW_orig;
+mmioOpenA_type mmioOpenA_orig;
+mmioOpenW_type mmioOpenW_orig;
+mmioRenameA_type mmioRenameA_orig;
+mmioRenameW_type mmioRenameW_orig;
+mmioClose_type mmioClose_orig;
+mmioRead_type mmioRead_orig;
+mmioWrite_type mmioWrite_orig;
+mmioSeek_type mmioSeek_orig;
+mmioGetInfo_type mmioGetInfo_orig;
+mmioSetInfo_type mmioSetInfo_orig;
+mmioSetBuffer_type mmioSetBuffer_orig;
+mmioFlush_type mmioFlush_orig;
+mmioAdvance_type mmioAdvance_orig;
+mmioSendMessage_type mmioSendMessage_orig;
+mmioDescend_type mmioDescend_orig;
+mmioAscend_type mmioAscend_orig;
+mmioCreateChunk_type mmioCreateChunk_orig;
+mciSendCommandA_type mciSendCommandA_orig;
+mciSendCommandW_type mciSendCommandW_orig;
+mciSendStringA_type mciSendStringA_orig;
+mciSendStringW_type mciSendStringW_orig;
+mciGetDeviceIDA_type mciGetDeviceIDA_orig;
+mciGetDeviceIDW_type mciGetDeviceIDW_orig;
+mciGetDeviceIDFromElementIDA_type mciGetDeviceIDFromElementIDA_orig;
+mciGetDeviceIDFromElementIDW_type mciGetDeviceIDFromElementIDW_orig;
+mciGetDriverData_type mciGetDriverData_orig;
+mciGetErrorStringA_type mciGetErrorStringA_orig;
+mciGetErrorStringW_type mciGetErrorStringW_orig;
+mciSetDriverData_type mciSetDriverData_orig;
+mciDriverNotify_type mciDriverNotify_orig;
+mciDriverYield_type mciDriverYield_orig;
+mciSetYieldProc_type mciSetYieldProc_orig;
+mciFreeCommandResource_type mciFreeCommandResource_orig;
+mciGetCreatorTask_type mciGetCreatorTask_orig;
+mciGetYieldProc_type mciGetYieldProc_orig;
+mciLoadCommandResource_type mciLoadCommandResource_orig;
+mciExecute_type mciExecute_orig;
+
+// grab pointers to function calls in the real DLL
+void init_function_pointers(HMODULE winmm_handle)
+{
+ CloseDriver_orig = (CloseDriver_type)::GetProcAddress(winmm_handle, "CloseDriver");
+ OpenDriver_orig = (OpenDriver_type)::GetProcAddress(winmm_handle, "OpenDriver");
+ SendDriverMessage_orig = (SendDriverMessage_type)::GetProcAddress(winmm_handle, "SendDriverMessage");
+ DrvGetModuleHandle_orig = (DrvGetModuleHandle_type)::GetProcAddress(winmm_handle, "DrvGetModuleHandle");
+ GetDriverModuleHandle_orig = (GetDriverModuleHandle_type)::GetProcAddress(winmm_handle, "GetDriverModuleHandle");
+ DefDriverProc_orig = (DefDriverProc_type)::GetProcAddress(winmm_handle, "DefDriverProc");
+ DriverCallback_orig = (DriverCallback_type)::GetProcAddress(winmm_handle, "DriverCallback");
+ mmsystemGetVersion_orig = (mmsystemGetVersion_type)::GetProcAddress(winmm_handle, "mmsystemGetVersion");
+ sndPlaySoundA_orig = (sndPlaySoundA_type)::GetProcAddress(winmm_handle, "sndPlaySoundA");
+ sndPlaySoundW_orig = (sndPlaySoundW_type)::GetProcAddress(winmm_handle, "sndPlaySoundW");
+ PlaySoundA_orig = (PlaySoundA_type)::GetProcAddress(winmm_handle, "PlaySoundA");
+ PlaySoundW_orig = (PlaySoundW_type)::GetProcAddress(winmm_handle, "PlaySoundW");
+ waveOutGetNumDevs_orig = (waveOutGetNumDevs_type)::GetProcAddress(winmm_handle, "waveOutGetNumDevs");
+ waveOutGetDevCapsA_orig = (waveOutGetDevCapsA_type)::GetProcAddress(winmm_handle, "waveOutGetDevCapsA");
+ waveOutGetDevCapsW_orig = (waveOutGetDevCapsW_type)::GetProcAddress(winmm_handle, "waveOutGetDevCapsW");
+ waveOutGetVolume_orig = (waveOutGetVolume_type)::GetProcAddress(winmm_handle, "waveOutGetVolume");
+ waveOutSetVolume_orig = (waveOutSetVolume_type)::GetProcAddress(winmm_handle, "waveOutSetVolume");
+ waveOutGetErrorTextA_orig = (waveOutGetErrorTextA_type)::GetProcAddress(winmm_handle, "waveOutGetErrorTextA");
+ waveOutGetErrorTextW_orig = (waveOutGetErrorTextW_type)::GetProcAddress(winmm_handle, "waveOutGetErrorTextW");
+ waveOutOpen_orig = (waveOutOpen_type)::GetProcAddress(winmm_handle, "waveOutOpen");
+ waveOutClose_orig = (waveOutClose_type)::GetProcAddress(winmm_handle, "waveOutClose");
+ waveOutPrepareHeader_orig = (waveOutPrepareHeader_type)::GetProcAddress(winmm_handle, "waveOutPrepareHeader");
+ waveOutUnprepareHeader_orig = (waveOutUnprepareHeader_type)::GetProcAddress(winmm_handle, "waveOutUnprepareHeader");
+ waveOutWrite_orig = (waveOutWrite_type)::GetProcAddress(winmm_handle, "waveOutWrite");
+ waveOutPause_orig = (waveOutPause_type)::GetProcAddress(winmm_handle, "waveOutPause");
+ waveOutRestart_orig = (waveOutRestart_type)::GetProcAddress(winmm_handle, "waveOutRestart");
+ waveOutReset_orig = (waveOutReset_type)::GetProcAddress(winmm_handle, "waveOutReset");
+ waveOutBreakLoop_orig = (waveOutBreakLoop_type)::GetProcAddress(winmm_handle, "waveOutBreakLoop");
+ waveOutGetPosition_orig = (waveOutGetPosition_type)::GetProcAddress(winmm_handle, "waveOutGetPosition");
+ waveOutGetPitch_orig = (waveOutGetPitch_type)::GetProcAddress(winmm_handle, "waveOutGetPitch");
+ waveOutSetPitch_orig = (waveOutSetPitch_type)::GetProcAddress(winmm_handle, "waveOutSetPitch");
+ waveOutGetPlaybackRate_orig = (waveOutGetPlaybackRate_type)::GetProcAddress(winmm_handle, "waveOutGetPlaybackRate");
+ waveOutSetPlaybackRate_orig = (waveOutSetPlaybackRate_type)::GetProcAddress(winmm_handle, "waveOutSetPlaybackRate");
+ waveOutGetID_orig = (waveOutGetID_type)::GetProcAddress(winmm_handle, "waveOutGetID");
+ waveOutMessage_orig = (waveOutMessage_type)::GetProcAddress(winmm_handle, "waveOutMessage");
+ waveInGetNumDevs_orig = (waveInGetNumDevs_type)::GetProcAddress(winmm_handle, "waveInGetNumDevs");
+ waveInGetDevCapsA_orig = (waveInGetDevCapsA_type)::GetProcAddress(winmm_handle, "waveInGetDevCapsA");
+ waveInGetDevCapsW_orig = (waveInGetDevCapsW_type)::GetProcAddress(winmm_handle, "waveInGetDevCapsW");
+ waveInGetErrorTextA_orig = (waveInGetErrorTextA_type)::GetProcAddress(winmm_handle, "waveInGetErrorTextA");
+ waveInGetErrorTextW_orig = (waveInGetErrorTextW_type)::GetProcAddress(winmm_handle, "waveInGetErrorTextW");
+ waveInOpen_orig = (waveInOpen_type)::GetProcAddress(winmm_handle, "waveInOpen");
+ waveInClose_orig = (waveInClose_type)::GetProcAddress(winmm_handle, "waveInClose");
+ waveInPrepareHeader_orig = (waveInPrepareHeader_type)::GetProcAddress(winmm_handle, "waveInPrepareHeader");
+ waveInUnprepareHeader_orig = (waveInUnprepareHeader_type)::GetProcAddress(winmm_handle, "waveInUnprepareHeader");
+ waveInAddBuffer_orig = (waveInAddBuffer_type)::GetProcAddress(winmm_handle, "waveInAddBuffer");
+ waveInStart_orig = (waveInStart_type)::GetProcAddress(winmm_handle, "waveInStart");
+ waveInStop_orig = (waveInStop_type)::GetProcAddress(winmm_handle, "waveInStop");
+ waveInReset_orig = (waveInReset_type)::GetProcAddress(winmm_handle, "waveInReset");
+ waveInGetPosition_orig = (waveInGetPosition_type)::GetProcAddress(winmm_handle, "waveInGetPosition");
+ waveInGetID_orig = (waveInGetID_type)::GetProcAddress(winmm_handle, "waveInGetID");
+ waveInMessage_orig = (waveInMessage_type)::GetProcAddress(winmm_handle, "waveInMessage");
+ midiOutGetNumDevs_orig = (midiOutGetNumDevs_type)::GetProcAddress(winmm_handle, "midiOutGetNumDevs");
+ midiStreamOpen_orig = (midiStreamOpen_type)::GetProcAddress(winmm_handle, "midiStreamOpen");
+ midiStreamClose_orig = (midiStreamClose_type)::GetProcAddress(winmm_handle, "midiStreamClose");
+ midiStreamProperty_orig = (midiStreamProperty_type)::GetProcAddress(winmm_handle, "midiStreamProperty");
+ midiStreamPosition_orig = (midiStreamPosition_type)::GetProcAddress(winmm_handle, "midiStreamPosition");
+ midiStreamOut_orig = (midiStreamOut_type)::GetProcAddress(winmm_handle, "midiStreamOut");
+ midiStreamPause_orig = (midiStreamPause_type)::GetProcAddress(winmm_handle, "midiStreamPause");
+ midiStreamRestart_orig = (midiStreamRestart_type)::GetProcAddress(winmm_handle, "midiStreamRestart");
+ midiStreamStop_orig = (midiStreamStop_type)::GetProcAddress(winmm_handle, "midiStreamStop");
+ midiConnect_orig = (midiConnect_type)::GetProcAddress(winmm_handle, "midiConnect");
+ midiDisconnect_orig = (midiDisconnect_type)::GetProcAddress(winmm_handle, "midiDisconnect");
+ midiOutGetDevCapsA_orig = (midiOutGetDevCapsA_type)::GetProcAddress(winmm_handle, "midiOutGetDevCapsA");
+ midiOutGetDevCapsW_orig = (midiOutGetDevCapsW_type)::GetProcAddress(winmm_handle, "midiOutGetDevCapsW");
+ midiOutGetVolume_orig = (midiOutGetVolume_type)::GetProcAddress(winmm_handle, "midiOutGetVolume");
+ midiOutSetVolume_orig = (midiOutSetVolume_type)::GetProcAddress(winmm_handle, "midiOutSetVolume");
+ midiOutGetErrorTextA_orig = (midiOutGetErrorTextA_type)::GetProcAddress(winmm_handle, "midiOutGetErrorTextA");
+ midiOutGetErrorTextW_orig = (midiOutGetErrorTextW_type)::GetProcAddress(winmm_handle, "midiOutGetErrorTextW");
+ midiOutOpen_orig = (midiOutOpen_type)::GetProcAddress(winmm_handle, "midiOutOpen");
+ midiOutClose_orig = (midiOutClose_type)::GetProcAddress(winmm_handle, "midiOutClose");
+ midiOutPrepareHeader_orig = (midiOutPrepareHeader_type)::GetProcAddress(winmm_handle, "midiOutPrepareHeader");
+ midiOutUnprepareHeader_orig = (midiOutUnprepareHeader_type)::GetProcAddress(winmm_handle, "midiOutUnprepareHeader");
+ midiOutShortMsg_orig = (midiOutShortMsg_type)::GetProcAddress(winmm_handle, "midiOutShortMsg");
+ midiOutLongMsg_orig = (midiOutLongMsg_type)::GetProcAddress(winmm_handle, "midiOutLongMsg");
+ midiOutReset_orig = (midiOutReset_type)::GetProcAddress(winmm_handle, "midiOutReset");
+ midiOutCachePatches_orig = (midiOutCachePatches_type)::GetProcAddress(winmm_handle, "midiOutCachePatches");
+ midiOutCacheDrumPatches_orig = (midiOutCacheDrumPatches_type)::GetProcAddress(winmm_handle, "midiOutCacheDrumPatches");
+ midiOutGetID_orig = (midiOutGetID_type)::GetProcAddress(winmm_handle, "midiOutGetID");
+ midiOutMessage_orig = (midiOutMessage_type)::GetProcAddress(winmm_handle, "midiOutMessage");
+ midiInGetNumDevs_orig = (midiInGetNumDevs_type)::GetProcAddress(winmm_handle, "midiInGetNumDevs");
+ midiInGetDevCapsA_orig = (midiInGetDevCapsA_type)::GetProcAddress(winmm_handle, "midiInGetDevCapsA");
+ midiInGetDevCapsW_orig = (midiInGetDevCapsW_type)::GetProcAddress(winmm_handle, "midiInGetDevCapsW");
+ midiInGetErrorTextA_orig = (midiInGetErrorTextA_type)::GetProcAddress(winmm_handle, "midiInGetErrorTextA");
+ midiInGetErrorTextW_orig = (midiInGetErrorTextW_type)::GetProcAddress(winmm_handle, "midiInGetErrorTextW");
+ midiInOpen_orig = (midiInOpen_type)::GetProcAddress(winmm_handle, "midiInOpen");
+ midiInClose_orig = (midiInClose_type)::GetProcAddress(winmm_handle, "midiInClose");
+ midiInPrepareHeader_orig = (midiInPrepareHeader_type)::GetProcAddress(winmm_handle, "midiInPrepareHeader");
+ midiInUnprepareHeader_orig = (midiInUnprepareHeader_type)::GetProcAddress(winmm_handle, "midiInUnprepareHeader");
+ midiInAddBuffer_orig = (midiInAddBuffer_type)::GetProcAddress(winmm_handle, "midiInAddBuffer");
+ midiInStart_orig = (midiInStart_type)::GetProcAddress(winmm_handle, "midiInStart");
+ midiInStop_orig = (midiInStop_type)::GetProcAddress(winmm_handle, "midiInStop");
+ midiInReset_orig = (midiInReset_type)::GetProcAddress(winmm_handle, "midiInReset");
+ midiInGetID_orig = (midiInGetID_type)::GetProcAddress(winmm_handle, "midiInGetID");
+ midiInMessage_orig = (midiInMessage_type)::GetProcAddress(winmm_handle, "midiInMessage");
+ auxGetNumDevs_orig = (auxGetNumDevs_type)::GetProcAddress(winmm_handle, "auxGetNumDevs");
+ auxGetDevCapsA_orig = (auxGetDevCapsA_type)::GetProcAddress(winmm_handle, "auxGetDevCapsA");
+ auxGetDevCapsW_orig = (auxGetDevCapsW_type)::GetProcAddress(winmm_handle, "auxGetDevCapsW");
+ auxSetVolume_orig = (auxSetVolume_type)::GetProcAddress(winmm_handle, "auxSetVolume");
+ auxGetVolume_orig = (auxGetVolume_type)::GetProcAddress(winmm_handle, "auxGetVolume");
+ auxOutMessage_orig = (auxOutMessage_type)::GetProcAddress(winmm_handle, "auxOutMessage");
+ mixerGetNumDevs_orig = (mixerGetNumDevs_type)::GetProcAddress(winmm_handle, "mixerGetNumDevs");
+ mixerGetDevCapsA_orig = (mixerGetDevCapsA_type)::GetProcAddress(winmm_handle, "mixerGetDevCapsA");
+ mixerGetDevCapsW_orig = (mixerGetDevCapsW_type)::GetProcAddress(winmm_handle, "mixerGetDevCapsW");
+ mixerOpen_orig = (mixerOpen_type)::GetProcAddress(winmm_handle, "mixerOpen");
+ mixerClose_orig = (mixerClose_type)::GetProcAddress(winmm_handle, "mixerClose");
+ mixerMessage_orig = (mixerMessage_type)::GetProcAddress(winmm_handle, "mixerMessage");
+ mixerGetLineInfoA_orig = (mixerGetLineInfoA_type)::GetProcAddress(winmm_handle, "mixerGetLineInfoA");
+ mixerGetLineInfoW_orig = (mixerGetLineInfoW_type)::GetProcAddress(winmm_handle, "mixerGetLineInfoW");
+ mixerGetID_orig = (mixerGetID_type)::GetProcAddress(winmm_handle, "mixerGetID");
+ mixerGetLineControlsA_orig = (mixerGetLineControlsA_type)::GetProcAddress(winmm_handle, "mixerGetLineControlsA");
+ mixerGetLineControlsW_orig = (mixerGetLineControlsW_type)::GetProcAddress(winmm_handle, "mixerGetLineControlsW");
+ mixerGetControlDetailsA_orig = (mixerGetControlDetailsA_type)::GetProcAddress(winmm_handle, "mixerGetControlDetailsA");
+ mixerGetControlDetailsW_orig = (mixerGetControlDetailsW_type)::GetProcAddress(winmm_handle, "mixerGetControlDetailsW");
+ mixerSetControlDetails_orig = (mixerSetControlDetails_type)::GetProcAddress(winmm_handle, "mixerSetControlDetails");
+ mmGetCurrentTask_orig = (mmGetCurrentTask_type)::GetProcAddress(winmm_handle, "mmGetCurrentTask");
+ mmTaskBlock_orig = (mmTaskBlock_type)::GetProcAddress(winmm_handle, "mmTaskBlock");
+ mmTaskCreate_orig = (mmTaskCreate_type)::GetProcAddress(winmm_handle, "mmTaskCreate");
+ mmTaskSignal_orig = (mmTaskSignal_type)::GetProcAddress(winmm_handle, "mmTaskSignal");
+ mmTaskYield_orig = (mmTaskYield_type)::GetProcAddress(winmm_handle, "mmTaskYield");
+ timeGetSystemTime_orig = (timeGetSystemTime_type)::GetProcAddress(winmm_handle, "timeGetSystemTime");
+ timeGetTime_orig = (timeGetTime_type)::GetProcAddress(winmm_handle, "timeGetTime");
+ timeSetEvent_orig = (timeSetEvent_type)::GetProcAddress(winmm_handle, "timeSetEvent");
+ timeKillEvent_orig = (timeKillEvent_type)::GetProcAddress(winmm_handle, "timeKillEvent");
+ timeGetDevCaps_orig = (timeGetDevCaps_type)::GetProcAddress(winmm_handle, "timeGetDevCaps");
+ timeBeginPeriod_orig = (timeBeginPeriod_type)::GetProcAddress(winmm_handle, "timeBeginPeriod");
+ timeEndPeriod_orig = (timeEndPeriod_type)::GetProcAddress(winmm_handle, "timeEndPeriod");
+ joyGetNumDevs_orig = (joyGetNumDevs_type)::GetProcAddress(winmm_handle, "joyGetNumDevs");
+ joyConfigChanged_orig = (joyConfigChanged_type)::GetProcAddress(winmm_handle, "joyConfigChanged");
+ joyGetDevCapsA_orig = (joyGetDevCapsA_type)::GetProcAddress(winmm_handle, "joyGetDevCapsA");
+ joyGetDevCapsW_orig = (joyGetDevCapsW_type)::GetProcAddress(winmm_handle, "joyGetDevCapsW");
+ joyGetPos_orig = (joyGetPos_type)::GetProcAddress(winmm_handle, "joyGetPos");
+ joyGetPosEx_orig = (joyGetPosEx_type)::GetProcAddress(winmm_handle, "joyGetPosEx");
+ joyGetThreshold_orig = (joyGetThreshold_type)::GetProcAddress(winmm_handle, "joyGetThreshold");
+ joyReleaseCapture_orig = (joyReleaseCapture_type)::GetProcAddress(winmm_handle, "joyReleaseCapture");
+ joySetCapture_orig = (joySetCapture_type)::GetProcAddress(winmm_handle, "joySetCapture");
+ joySetThreshold_orig = (joySetThreshold_type)::GetProcAddress(winmm_handle, "joySetThreshold");
+ mciDriverNotify_orig = (mciDriverNotify_type)::GetProcAddress(winmm_handle, "mciDriverNotify");
+ mciDriverYield_orig = (mciDriverYield_type)::GetProcAddress(winmm_handle, "mciDriverYield");
+ mmioStringToFOURCCA_orig = (mmioStringToFOURCCA_type)::GetProcAddress(winmm_handle, "mmioStringToFOURCCA");
+ mmioStringToFOURCCW_orig = (mmioStringToFOURCCW_type)::GetProcAddress(winmm_handle, "mmioStringToFOURCCW");
+ mmioInstallIOProcA_orig = (mmioInstallIOProcA_type)::GetProcAddress(winmm_handle, "mmioInstallIOProcA");
+ mmioInstallIOProcW_orig = (mmioInstallIOProcW_type)::GetProcAddress(winmm_handle, "mmioInstallIOProcW");
+ mmioOpenA_orig = (mmioOpenA_type)::GetProcAddress(winmm_handle, "mmioOpenA");
+ mmioOpenW_orig = (mmioOpenW_type)::GetProcAddress(winmm_handle, "mmioOpenW");
+ mmioRenameA_orig = (mmioRenameA_type)::GetProcAddress(winmm_handle, "mmioRenameA");
+ mmioRenameW_orig = (mmioRenameW_type)::GetProcAddress(winmm_handle, "mmioRenameW");
+ mmioClose_orig = (mmioClose_type)::GetProcAddress(winmm_handle, "mmioClose");
+ mmioRead_orig = (mmioRead_type)::GetProcAddress(winmm_handle, "mmioRead");
+ mmioWrite_orig = (mmioWrite_type)::GetProcAddress(winmm_handle, "mmioWrite");
+ mmioSeek_orig = (mmioSeek_type)::GetProcAddress(winmm_handle, "mmioSeek");
+ mmioGetInfo_orig = (mmioGetInfo_type)::GetProcAddress(winmm_handle, "mmioGetInfo");
+ mmioSetInfo_orig = (mmioSetInfo_type)::GetProcAddress(winmm_handle, "mmioSetInfo");
+ mmioSetBuffer_orig = (mmioSetBuffer_type)::GetProcAddress(winmm_handle, "mmioSetBuffer");
+ mmioFlush_orig = (mmioFlush_type)::GetProcAddress(winmm_handle, "mmioFlush");
+ mmioAdvance_orig = (mmioAdvance_type)::GetProcAddress(winmm_handle, "mmioAdvance");
+ mmioSendMessage_orig = (mmioSendMessage_type)::GetProcAddress(winmm_handle, "mmioSendMessage");
+ mmioDescend_orig = (mmioDescend_type)::GetProcAddress(winmm_handle, "mmioDescend");
+ mmioAscend_orig = (mmioAscend_type)::GetProcAddress(winmm_handle, "mmioAscend");
+ mmioCreateChunk_orig = (mmioCreateChunk_type)::GetProcAddress(winmm_handle, "mmioCreateChunk");
+ mciSendCommandA_orig = (mciSendCommandA_type)::GetProcAddress(winmm_handle, "mciSendCommandA");
+ mciSendCommandW_orig = (mciSendCommandW_type)::GetProcAddress(winmm_handle, "mciSendCommandW");
+ mciSendStringA_orig = (mciSendStringA_type)::GetProcAddress(winmm_handle, "mciSendStringA");
+ mciSendStringW_orig = (mciSendStringW_type)::GetProcAddress(winmm_handle, "mciSendStringW");
+ mciGetDeviceIDA_orig = (mciGetDeviceIDA_type)::GetProcAddress(winmm_handle, "mciGetDeviceIDA");
+ mciGetDeviceIDW_orig = (mciGetDeviceIDW_type)::GetProcAddress(winmm_handle, "mciGetDeviceIDW");
+ mciGetDeviceIDFromElementIDA_orig = (mciGetDeviceIDFromElementIDA_type)::GetProcAddress(winmm_handle, "mciGetDeviceIDFromElementIDA");
+ mciGetDeviceIDFromElementIDW_orig = (mciGetDeviceIDFromElementIDW_type)::GetProcAddress(winmm_handle, "mciGetDeviceIDFromElementIDW");
+ mciGetDriverData_orig = (mciGetDriverData_type)::GetProcAddress(winmm_handle, "mciGetDriverData");
+ mciGetErrorStringA_orig = (mciGetErrorStringA_type)::GetProcAddress(winmm_handle, "mciGetErrorStringA");
+ mciGetErrorStringW_orig = (mciGetErrorStringW_type)::GetProcAddress(winmm_handle, "mciGetErrorStringW");
+ mciSetDriverData_orig = (mciSetDriverData_type)::GetProcAddress(winmm_handle, "mciSetDriverData");
+ mciSetYieldProc_orig = (mciSetYieldProc_type)::GetProcAddress(winmm_handle, "mciSetYieldProc");
+ mciFreeCommandResource_orig = (mciFreeCommandResource_type)::GetProcAddress(winmm_handle, "mciFreeCommandResource");
+ mciGetCreatorTask_orig = (mciGetCreatorTask_type)::GetProcAddress(winmm_handle, "mciGetCreatorTask");
+ mciGetYieldProc_orig = (mciGetYieldProc_type)::GetProcAddress(winmm_handle, "mciGetYieldProc");
+ mciLoadCommandResource_orig = (mciLoadCommandResource_type)::GetProcAddress(winmm_handle, "mciLoadCommandResource");
+ mciExecute_orig = (mciExecute_type)::GetProcAddress(winmm_handle, "mciExecute");
+}
+
+extern "C" {
+ LRESULT WINAPI CloseDriver( HDRVR hDriver, LPARAM lParam1, LPARAM lParam2)
+ {
+ //OutputDebugString(L"CloseDriver\n");
+ return CloseDriver_orig( hDriver, lParam1, lParam2);
+ }
+
+ HDRVR WINAPI OpenDriver( LPCWSTR szDriverName, LPCWSTR szSectionName, LPARAM lParam2)
+ {
+ //OutputDebugString(L"OpenDriver\n");
+ return OpenDriver_orig( szDriverName, szSectionName, lParam2);
+ }
+
+ LRESULT WINAPI SendDriverMessage( HDRVR hDriver, UINT message, LPARAM lParam1, LPARAM lParam2)
+ {
+ //OutputDebugString(L"SendDriverMessage\n");
+ return SendDriverMessage_orig( hDriver, message, lParam1, lParam2);
+ }
+
+ HMODULE WINAPI DrvGetModuleHandle( HDRVR hDriver)
+ {
+ //OutputDebugString(L"DrvGetModuleHandle\n");
+ return DrvGetModuleHandle_orig( hDriver);
+ }
+
+ HMODULE WINAPI GetDriverModuleHandle( HDRVR hDriver)
+ {
+ //OutputDebugString(L"GetDriverModuleHandle\n");
+ return GetDriverModuleHandle_orig( hDriver);
+ }
+
+ LRESULT WINAPI DefDriverProc( DWORD_PTR dwDriverIdentifier, HDRVR hdrvr, UINT uMsg, LPARAM lParam1, LPARAM lParam2)
+ {
+ //OutputDebugString(L"DefDriverProc\n");
+ return DefDriverProc_orig( dwDriverIdentifier, hdrvr, uMsg, lParam1, lParam2);
+ }
+
+ BOOL WINAPI DriverCallback( DWORD dwCallBack, DWORD dwFlags, HDRVR hdrvr, DWORD msg, DWORD dwUser, DWORD dwParam1, DWORD dwParam2)
+ {
+ //OutputDebugString(L"DriverCallback\n");
+ return DriverCallback_orig(dwCallBack, dwFlags, hdrvr, msg, dwUser, dwParam1, dwParam2);
+ }
+
+ UINT WINAPI mmsystemGetVersion(void)
+ {
+ //OutputDebugString(L"mmsystemGetVersion\n");
+ return mmsystemGetVersion_orig();
+ }
+
+ BOOL WINAPI sndPlaySoundA( LPCSTR pszSound, UINT fuSound)
+ {
+ //OutputDebugString(L"sndPlaySoundA\n");
+ return sndPlaySoundA_orig( pszSound, fuSound);
+ }
+
+ BOOL WINAPI sndPlaySoundW( LPCWSTR pszSound, UINT fuSound)
+ {
+ //OutputDebugString(L"sndPlaySoundW\n");
+ return sndPlaySoundW_orig( pszSound, fuSound);
+ }
+
+ BOOL WINAPI PlaySoundA( LPCSTR pszSound, HMODULE hmod, DWORD fdwSound)
+ {
+ //OutputDebugString(L"PlaySoundA\n");
+ return PlaySoundA_orig( pszSound, hmod, fdwSound);
+ }
+
+ BOOL WINAPI PlaySoundW( LPCWSTR pszSound, HMODULE hmod, DWORD fdwSound)
+ {
+ //OutputDebugString(L"PlaySoundW\n");
+ return PlaySoundW_orig( pszSound, hmod, fdwSound);
+ }
+
+ UINT WINAPI waveOutGetNumDevs(void)
+ {
+ //OutputDebugString(L"waveOutGetNumDevs\n");
+ return waveOutGetNumDevs_orig();
+ }
+
+ MMRESULT WINAPI waveOutGetDevCapsA( UINT_PTR uDeviceID, LPWAVEOUTCAPSA pwoc, UINT cbwoc)
+ {
+ //OutputDebugString(L"waveOutGetDevCapsA\n");
+ return waveOutGetDevCapsA_orig( uDeviceID, pwoc, cbwoc);
+ }
+
+ MMRESULT WINAPI waveOutGetDevCapsW( UINT_PTR uDeviceID, LPWAVEOUTCAPSW pwoc, UINT cbwoc)
+ {
+ //OutputDebugString(L"waveOutGetDevCapsW\n");
+ return waveOutGetDevCapsW_orig( uDeviceID, pwoc, cbwoc);
+ }
+
+
+ MMRESULT WINAPI waveOutGetVolume( HWAVEOUT hwo, LPDWORD pdwVolume)
+ {
+ //OutputDebugString(L"waveOutGetVolume\n");
+ return waveOutGetVolume_orig( hwo, pdwVolume);
+ }
+
+ MMRESULT WINAPI waveOutSetVolume( HWAVEOUT hwo, DWORD dwVolume)
+ {
+ //OutputDebugString(L"waveOutSetVolume\n");
+ return waveOutSetVolume_orig( hwo, dwVolume);
+ }
+
+ MMRESULT WINAPI waveOutGetErrorTextA( MMRESULT mmrError, LPSTR pszText, UINT cchText)
+ {
+ //OutputDebugString(L"waveOutGetErrorTextA\n");
+ return waveOutGetErrorTextA_orig( mmrError, pszText, cchText);
+ }
+
+ MMRESULT WINAPI waveOutGetErrorTextW( MMRESULT mmrError, LPWSTR pszText, UINT cchText)
+ {
+ //OutputDebugString(L"waveOutGetErrorTextW\n");
+ return waveOutGetErrorTextW_orig( mmrError, pszText, cchText);
+ }
+
+ //MMRESULT WINAPI waveOutOpen( LPHWAVEOUT phwo, UINT uDeviceID, LPCWAVEFORMATEX pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen)
+ //{
+ // //OutputDebugString(L"waveOutGetErrorTextW\n");
+ // return waveOutOpen_orig( phwo, uDeviceID, pwfx, dwCallback, dwInstance, fdwOpen);
+ //}
+
+ //MMRESULT WINAPI waveOutClose( HWAVEOUT hwo)
+ //{
+ // //OutputDebugString(L"waveOutGetErrorTextW\n");
+ // return waveOutClose_orig( hwo );
+ //}
+
+ MMRESULT WINAPI waveOutPrepareHeader( HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh)
+ {
+ //OutputDebugString(L"waveOutPrepareHeader\n");
+ return waveOutPrepareHeader_orig( hwo, pwh, cbwh);
+ }
+
+ MMRESULT WINAPI waveOutUnprepareHeader( HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh)
+ {
+ //OutputDebugString(L"waveOutUnprepareHeader\n");
+ return waveOutUnprepareHeader_orig( hwo, pwh, cbwh);
+ }
+
+
+ //MMRESULT WINAPI waveOutWrite( HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh)
+ //{
+ // //OutputDebugString(L"waveOutUnprepareHeader\n");
+ // return waveOutWrite_orig( hwo, pwh, cbwh);
+ //}
+
+ MMRESULT WINAPI waveOutPause( HWAVEOUT hwo)
+ {
+ //OutputDebugString(L"waveOutPause\n");
+ return waveOutPause_orig( hwo);
+ }
+
+ MMRESULT WINAPI waveOutRestart( HWAVEOUT hwo)
+ {
+ //OutputDebugString(L"waveOutRestart\n");
+ return waveOutRestart_orig( hwo);
+ }
+
+ MMRESULT WINAPI waveOutReset( HWAVEOUT hwo)
+ {
+ //OutputDebugString(L"waveOutReset\n");
+ return waveOutReset_orig( hwo);
+ }
+
+ MMRESULT WINAPI waveOutBreakLoop( HWAVEOUT hwo)
+ {
+ //OutputDebugString(L"waveOutBreakLoop\n");
+ return waveOutBreakLoop_orig( hwo);
+ }
+
+ MMRESULT WINAPI waveOutGetPosition( HWAVEOUT hwo, LPMMTIME pmmt, UINT cbmmt)
+ {
+ //OutputDebugString(L"waveOutGetPosition\n");
+ return waveOutGetPosition_orig( hwo, pmmt, cbmmt);
+ }
+
+ MMRESULT WINAPI waveOutGetPitch( HWAVEOUT hwo, LPDWORD pdwPitch)
+ {
+ //OutputDebugString(L"waveOutGetPitch\n");
+ return waveOutGetPitch_orig( hwo, pdwPitch);
+ }
+
+ MMRESULT WINAPI waveOutSetPitch( HWAVEOUT hwo, DWORD dwPitch)
+ {
+ //OutputDebugString(L"waveOutSetPitch\n");
+ return waveOutSetPitch_orig( hwo, dwPitch);
+ }
+
+ MMRESULT WINAPI waveOutGetPlaybackRate( HWAVEOUT hwo, LPDWORD pdwRate)
+ {
+ //OutputDebugString(L"waveOutGetPlaybackRate\n");
+ return waveOutGetPlaybackRate_orig( hwo, pdwRate);
+ }
+
+ MMRESULT WINAPI waveOutSetPlaybackRate( HWAVEOUT hwo, DWORD dwRate)
+ {
+ //OutputDebugString(L"waveOutSetPlaybackRate\n");
+ return waveOutSetPlaybackRate_orig( hwo, dwRate);
+ }
+
+ MMRESULT WINAPI waveOutGetID( HWAVEOUT hwo, LPUINT puDeviceID)
+ {
+ //OutputDebugString(L"waveOutGetID\n");
+ return waveOutGetID_orig( hwo, puDeviceID);
+ }
+
+ MMRESULT WINAPI waveOutMessage( HWAVEOUT hwo, UINT uMsg, DWORD_PTR dw1, DWORD_PTR dw2)
+ {
+ //OutputDebugString(L"waveOutMessage\n");
+ return waveOutMessage_orig( hwo, uMsg, dw1, dw2);
+ }
+
+ UINT WINAPI waveInGetNumDevs(void)
+ {
+ //OutputDebugString(L"waveInGetNumDevs\n");
+ return waveInGetNumDevs_orig();
+ }
+
+ MMRESULT WINAPI waveInGetDevCapsA( UINT_PTR uDeviceID, LPWAVEINCAPSA pwic, UINT cbwic)
+ {
+ //OutputDebugString(L"waveInGetDevCapsA\n");
+ return waveInGetDevCapsA_orig( uDeviceID, pwic, cbwic);
+ }
+
+ MMRESULT WINAPI waveInGetDevCapsW( UINT_PTR uDeviceID, LPWAVEINCAPSW pwic, UINT cbwic)
+ {
+ //OutputDebugString(L"waveInGetDevCapsW\n");
+ return waveInGetDevCapsW_orig( uDeviceID, pwic, cbwic);
+ }
+
+ MMRESULT WINAPI waveInGetErrorTextA(MMRESULT mmrError, LPSTR pszText, UINT cchText)
+ {
+ //OutputDebugString(L"waveInGetErrorTextA\n");
+ return waveInGetErrorTextA_orig(mmrError, pszText, cchText);
+ }
+
+ MMRESULT WINAPI waveInGetErrorTextW(MMRESULT mmrError, LPWSTR pszText, UINT cchText)
+ {
+ //OutputDebugString(L"waveInGetErrorTextW\n");
+ return waveInGetErrorTextW_orig(mmrError, pszText, cchText);
+ }
+
+ MMRESULT WINAPI waveInOpen( LPHWAVEIN phwi, UINT uDeviceID, LPCWAVEFORMATEX pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen)
+ {
+ //OutputDebugString(L"waveInOpen\n");
+ return waveInOpen_orig(phwi, uDeviceID, pwfx, dwCallback, dwInstance, fdwOpen);
+ }
+
+ MMRESULT WINAPI waveInClose( HWAVEIN hwi)
+ {
+ //OutputDebugString(L"waveInClose\n");
+ return waveInClose_orig( hwi);
+ }
+
+ MMRESULT WINAPI waveInPrepareHeader( HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh)
+ {
+ //OutputDebugString(L"waveInPrepareHeader\n");
+ return waveInPrepareHeader_orig( hwi, pwh, cbwh);
+ }
+
+ MMRESULT WINAPI waveInUnprepareHeader( HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh)
+ {
+ //OutputDebugString(L"waveInUnprepareHeader\n");
+ return waveInUnprepareHeader_orig( hwi, pwh, cbwh);
+ }
+
+ MMRESULT WINAPI waveInAddBuffer( HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh)
+ {
+ //OutputDebugString(L"waveInAddBuffer\n");
+ return waveInAddBuffer_orig( hwi, pwh, cbwh);
+ }
+
+ MMRESULT WINAPI waveInStart( HWAVEIN hwi)
+ {
+ //OutputDebugString(L"waveInStart\n");
+ return waveInStart_orig( hwi);
+ }
+
+ MMRESULT WINAPI waveInStop( HWAVEIN hwi)
+ {
+ //OutputDebugString(L"waveInStop\n");
+ return waveInStop_orig(hwi);
+ }
+
+ MMRESULT WINAPI waveInReset( HWAVEIN hwi)
+ {
+ //OutputDebugString(L"waveInReset\n");
+ return waveInReset_orig(hwi);
+ }
+
+ MMRESULT WINAPI waveInGetPosition( HWAVEIN hwi, LPMMTIME pmmt, UINT cbmmt)
+ {
+ //OutputDebugString(L"waveInGetPosition\n");
+ return waveInGetPosition_orig( hwi, pmmt, cbmmt);
+ }
+
+ MMRESULT WINAPI waveInGetID( HWAVEIN hwi, LPUINT puDeviceID)
+ {
+ //OutputDebugString(L"waveInGetID\n");
+ return waveInGetID_orig( hwi, puDeviceID);
+ }
+
+ MMRESULT WINAPI waveInMessage( HWAVEIN hwi, UINT uMsg, DWORD_PTR dw1, DWORD_PTR dw2)
+ {
+ //OutputDebugString(L"waveInMessage\n");
+ return waveInMessage_orig( hwi, uMsg, dw1, dw2);
+ }
+
+ UINT WINAPI midiOutGetNumDevs(void)
+ {
+ //OutputDebugString(L"midiOutGetNumDevs\n");
+ return midiOutGetNumDevs_orig();
+ }
+
+ MMRESULT WINAPI midiStreamOpen( LPHMIDISTRM phms, LPUINT puDeviceID, DWORD cMidi, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen)
+ {
+ //OutputDebugString(L"midiStreamOpen\n");
+ return midiStreamOpen_orig( phms, puDeviceID, cMidi, dwCallback, dwInstance, fdwOpen);
+ }
+
+ MMRESULT WINAPI midiStreamClose( HMIDISTRM hms)
+ {
+ //OutputDebugString(L"midiStreamClose\n");
+ return midiStreamClose_orig( hms);
+ }
+
+ MMRESULT WINAPI midiStreamProperty( HMIDISTRM hms, LPBYTE lppropdata, DWORD dwProperty)
+ {
+ //OutputDebugString(L"midiStreamProperty\n");
+ return midiStreamProperty_orig( hms, lppropdata, dwProperty);
+ }
+
+ MMRESULT WINAPI midiStreamPosition( HMIDISTRM hms, LPMMTIME lpmmt, UINT cbmmt)
+ {
+ //OutputDebugString(L"midiStreamPosition\n");
+ return midiStreamPosition_orig( hms, lpmmt, cbmmt);
+ }
+
+ MMRESULT WINAPI midiStreamOut( HMIDISTRM hms, LPMIDIHDR pmh, UINT cbmh)
+ {
+ //OutputDebugString(L"midiStreamOut\n");
+ return midiStreamOut_orig( hms, pmh, cbmh);
+ }
+
+ MMRESULT WINAPI midiStreamPause( HMIDISTRM hms)
+ {
+ //OutputDebugString(L"midiStreamPause\n");
+ return midiStreamPause_orig( hms);
+ }
+
+ MMRESULT WINAPI midiStreamRestart( HMIDISTRM hms)
+ {
+ //OutputDebugString(L"midiStreamRestart\n");
+ return midiStreamRestart_orig( hms);
+ }
+
+ MMRESULT WINAPI midiStreamStop( HMIDISTRM hms)
+ {
+ //OutputDebugString(L"midiStreamStop\n");
+ return midiStreamStop_orig( hms);
+ }
+
+ MMRESULT WINAPI midiConnect( HMIDI hmi, HMIDIOUT hmo, LPVOID pReserved)
+ {
+ //OutputDebugString(L"midiConnect\n");
+ return midiConnect_orig( hmi, hmo, pReserved);
+ }
+
+ MMRESULT WINAPI midiDisconnect( HMIDI hmi, HMIDIOUT hmo, LPVOID pReserved)
+ {
+ //OutputDebugString(L"midiDisconnect\n");
+ return midiDisconnect_orig( hmi, hmo, pReserved);
+ }
+
+ MMRESULT WINAPI midiOutGetDevCapsA( UINT_PTR uDeviceID, LPMIDIOUTCAPSA pmoc, UINT cbmoc)
+ {
+ //OutputDebugString(L"midiOutGetDevCapsA\n");
+ return midiOutGetDevCapsA_orig( uDeviceID, pmoc, cbmoc);
+ }
+
+ MMRESULT WINAPI midiOutGetDevCapsW( UINT_PTR uDeviceID, LPMIDIOUTCAPSW pmoc, UINT cbmoc)
+ {
+ //OutputDebugString(L"midiOutGetDevCapsW\n");
+ return midiOutGetDevCapsW_orig( uDeviceID, pmoc, cbmoc);
+ }
+
+ MMRESULT WINAPI midiOutGetVolume( HMIDIOUT hmo, LPDWORD pdwVolume)
+ {
+ //OutputDebugString(L"midiOutGetVolume\n");
+ return midiOutGetVolume_orig( hmo, pdwVolume);
+ }
+
+ MMRESULT WINAPI midiOutSetVolume( HMIDIOUT hmo, DWORD dwVolume)
+ {
+ //OutputDebugString(L"midiOutSetVolume\n");
+ return midiOutSetVolume_orig( hmo, dwVolume);
+ }
+
+ MMRESULT WINAPI midiOutGetErrorTextA( MMRESULT mmrError, LPSTR pszText, UINT cchText)
+ {
+ //OutputDebugString(L"midiOutGetErrorTextA\n");
+ return midiOutGetErrorTextA_orig( mmrError, pszText, cchText);
+ }
+
+ MMRESULT WINAPI midiOutGetErrorTextW( MMRESULT mmrError, LPWSTR pszText, UINT cchText)
+ {
+ //OutputDebugString(L"midiOutGetErrorTextW\n");
+ return midiOutGetErrorTextW_orig( mmrError, pszText, cchText);
+ }
+
+ MMRESULT WINAPI midiOutOpen( LPHMIDIOUT phmo, UINT uDeviceID, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen)
+ {
+ //OutputDebugString(L"midiOutOpen\n");
+ return midiOutOpen_orig(phmo, uDeviceID, dwCallback, dwInstance, fdwOpen);
+ }
+
+ MMRESULT WINAPI midiOutClose( HMIDIOUT hmo)
+ {
+ //OutputDebugString(L"midiOutClose\n");
+ return midiOutClose_orig( hmo);
+ }
+
+ MMRESULT WINAPI midiOutPrepareHeader( HMIDIOUT hmo, LPMIDIHDR pmh, UINT cbmh)
+ {
+ //OutputDebugString(L"midiOutPrepareHeader\n");
+ return midiOutPrepareHeader_orig( hmo, pmh, cbmh);
+ }
+
+ MMRESULT WINAPI midiOutUnprepareHeader(HMIDIOUT hmo, LPMIDIHDR pmh, UINT cbmh)
+ {
+ //OutputDebugString(L"midiOutUnprepareHeader\n");
+ return midiOutUnprepareHeader_orig(hmo, pmh, cbmh);
+ }
+
+ MMRESULT WINAPI midiOutShortMsg( HMIDIOUT hmo, DWORD dwMsg)
+ {
+ //OutputDebugString(L"midiOutShortMsg\n");
+ return midiOutShortMsg_orig( hmo, dwMsg);
+ }
+
+ MMRESULT WINAPI midiOutLongMsg(HMIDIOUT hmo, LPMIDIHDR pmh, UINT cbmh)
+ {
+ //OutputDebugString(L"midiOutLongMsg\n");
+ return midiOutLongMsg_orig(hmo, pmh, cbmh);
+ }
+
+ MMRESULT WINAPI midiOutReset( HMIDIOUT hmo)
+ {
+ //OutputDebugString(L"midiOutReset\n");
+ return midiOutReset_orig( hmo);
+ }
+
+ MMRESULT WINAPI midiOutCachePatches( HMIDIOUT hmo, UINT uBank, LPWORD pwpa, UINT fuCache)
+ {
+ //OutputDebugString(L"midiOutCachePatches\n");
+ return midiOutCachePatches_orig( hmo, uBank, pwpa, fuCache);
+ }
+
+ MMRESULT WINAPI midiOutCacheDrumPatches( HMIDIOUT hmo, UINT uPatch, LPWORD pwkya, UINT fuCache)
+ {
+ //OutputDebugString(L"midiOutCacheDrumPatches\n");
+ return midiOutCacheDrumPatches_orig( hmo, uPatch, pwkya, fuCache);
+ }
+
+ MMRESULT WINAPI midiOutGetID( HMIDIOUT hmo, LPUINT puDeviceID)
+ {
+ //OutputDebugString(L"midiOutGetID\n");
+ return midiOutGetID_orig( hmo, puDeviceID);
+ }
+
+ MMRESULT WINAPI midiOutMessage( HMIDIOUT hmo, UINT uMsg, DWORD_PTR dw1, DWORD_PTR dw2)
+ {
+ //OutputDebugString(L"midiOutMessage\n");
+ return midiOutMessage_orig( hmo, uMsg, dw1, dw2);
+ }
+
+ UINT WINAPI midiInGetNumDevs(void)
+ {
+ //OutputDebugString(L"midiInGetNumDevs\n");
+ return midiInGetNumDevs_orig();
+ }
+
+ MMRESULT WINAPI midiInGetDevCapsA( UINT_PTR uDeviceID, LPMIDIINCAPSA pmic, UINT cbmic)
+ {
+ //OutputDebugString(L"midiInGetDevCapsA\n");
+ return midiInGetDevCapsA_orig( uDeviceID, pmic, cbmic);
+ }
+
+ MMRESULT WINAPI midiInGetDevCapsW( UINT_PTR uDeviceID, LPMIDIINCAPSW pmic, UINT cbmic)
+ {
+ //OutputDebugString(L"midiInGetDevCapsW\n");
+ return midiInGetDevCapsW_orig( uDeviceID, pmic, cbmic);
+ }
+
+ MMRESULT WINAPI midiInGetErrorTextA( MMRESULT mmrError, LPSTR pszText, UINT cchText)
+ {
+ //OutputDebugString(L"midiInGetErrorTextA\n");
+ return midiInGetErrorTextA_orig( mmrError, pszText, cchText);
+ }
+
+ MMRESULT WINAPI midiInGetErrorTextW( MMRESULT mmrError, LPWSTR pszText, UINT cchText)
+ {
+ //OutputDebugString(L"midiInGetErrorTextW\n");
+ return midiInGetErrorTextW_orig( mmrError, pszText, cchText);
+ }
+
+ MMRESULT WINAPI midiInOpen( LPHMIDIIN phmi, UINT uDeviceID, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen)
+ {
+ //OutputDebugString(L"midiInOpen\n");
+ return midiInOpen_orig(phmi, uDeviceID, dwCallback, dwInstance, fdwOpen);
+ }
+
+ MMRESULT WINAPI midiInClose( HMIDIIN hmi)
+ {
+ //OutputDebugString(L"midiInClose\n");
+ return midiInClose_orig( hmi);
+ }
+
+ MMRESULT WINAPI midiInPrepareHeader( HMIDIIN hmi, LPMIDIHDR pmh, UINT cbmh)
+ {
+ //OutputDebugString(L"midiInPrepareHeader\n");
+ return midiInPrepareHeader_orig( hmi, pmh, cbmh);
+ }
+
+ MMRESULT WINAPI midiInUnprepareHeader( HMIDIIN hmi, LPMIDIHDR pmh, UINT cbmh)
+ {
+ //OutputDebugString(L"midiInUnprepareHeader\n");
+ return midiInUnprepareHeader_orig( hmi, pmh, cbmh);
+ }
+
+ MMRESULT WINAPI midiInAddBuffer( HMIDIIN hmi, LPMIDIHDR pmh, UINT cbmh)
+ {
+ //OutputDebugString(L"midiInAddBuffer\n");
+ return midiInAddBuffer_orig( hmi, pmh, cbmh);
+ }
+
+ MMRESULT WINAPI midiInStart( HMIDIIN hmi)
+ {
+ //OutputDebugString(L"midiInStart\n");
+ return midiInStart_orig( hmi);
+ }
+
+ MMRESULT WINAPI midiInStop( HMIDIIN hmi)
+ {
+ //OutputDebugString(L"midiInStop\n");
+ return midiInStop_orig(hmi);
+ }
+
+ MMRESULT WINAPI midiInReset( HMIDIIN hmi)
+ {
+ //OutputDebugString(L"midiInReset\n");
+ return midiInReset_orig( hmi);
+ }
+
+ MMRESULT WINAPI midiInGetID( HMIDIIN hmi, LPUINT puDeviceID)
+ {
+ //OutputDebugString(L"midiInGetID\n");
+ return midiInGetID_orig( hmi, puDeviceID);
+ }
+
+ MMRESULT WINAPI midiInMessage( HMIDIIN hmi, UINT uMsg, DWORD_PTR dw1, DWORD_PTR dw2)
+ {
+ //OutputDebugString(L"midiInMessage\n");
+ return midiInMessage_orig( hmi, uMsg, dw1, dw2);
+ }
+
+ UINT WINAPI auxGetNumDevs(void)
+ {
+ //OutputDebugString(L"auxGetNumDevs\n");
+ return auxGetNumDevs_orig();
+ }
+
+ MMRESULT WINAPI auxGetDevCapsA( UINT_PTR uDeviceID, LPAUXCAPSA pac, UINT cbac)
+ {
+ //OutputDebugString(L"auxGetDevCapsA\n");
+ return auxGetDevCapsA_orig( uDeviceID, pac, cbac);
+ }
+
+ MMRESULT WINAPI auxGetDevCapsW( UINT_PTR uDeviceID, LPAUXCAPSW pac, UINT cbac)
+ {
+ //OutputDebugString(L"auxGetDevCapsW\n");
+ return auxGetDevCapsW_orig( uDeviceID, pac, cbac);
+ }
+
+ MMRESULT WINAPI auxSetVolume( UINT uDeviceID, DWORD dwVolume)
+ {
+ //OutputDebugString(L"auxSetVolume\n");
+ return auxSetVolume_orig( uDeviceID, dwVolume);
+ }
+
+ MMRESULT WINAPI auxGetVolume( UINT uDeviceID, LPDWORD pdwVolume)
+ {
+ //OutputDebugString(L"auxGetVolume\n");
+ return auxGetVolume_orig( uDeviceID, pdwVolume);
+ }
+
+ MMRESULT WINAPI auxOutMessage( UINT uDeviceID, UINT uMsg, DWORD_PTR dw1, DWORD_PTR dw2)
+ {
+ //OutputDebugString(L"auxOutMessage\n");
+ return auxOutMessage_orig( uDeviceID, uMsg, dw1, dw2);
+ }
+
+ UINT WINAPI mixerGetNumDevs(void)
+ {
+ //OutputDebugString(L"mixerGetNumDevs\n");
+ return mixerGetNumDevs_orig();
+ }
+
+ MMRESULT WINAPI mixerGetDevCapsA( UINT_PTR uMxId, LPMIXERCAPSA pmxcaps, UINT cbmxcaps)
+ {
+ //OutputDebugString(L"mixerGetDevCapsA\n");
+ return mixerGetDevCapsA_orig( uMxId, pmxcaps, cbmxcaps);
+ }
+
+ MMRESULT WINAPI mixerGetDevCapsW( UINT_PTR uMxId, LPMIXERCAPSW pmxcaps, UINT cbmxcaps)
+ {
+ //OutputDebugString(L"mixerGetDevCapsW\n");
+ return mixerGetDevCapsW_orig( uMxId, pmxcaps, cbmxcaps);
+ }
+
+ MMRESULT WINAPI mixerOpen( LPHMIXER phmx, UINT uMxId, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen)
+ {
+ //OutputDebugString(L"mixerOpen\n");
+ return mixerOpen_orig( phmx, uMxId, dwCallback, dwInstance, fdwOpen);
+ }
+
+ MMRESULT WINAPI mixerClose( HMIXER hmx)
+ {
+ //OutputDebugString(L"mixerClose\n");
+ return mixerClose_orig( hmx);
+ }
+
+ DWORD WINAPI mixerMessage( HMIXER hmx, UINT uMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
+ {
+ //OutputDebugString(L"mixerMessage\n");
+ return mixerMessage_orig( hmx, uMsg, dwParam1, dwParam2);
+ }
+
+ MMRESULT WINAPI mixerGetLineInfoA( HMIXEROBJ hmxobj, LPMIXERLINEA pmxl, DWORD fdwInfo)
+ {
+ //OutputDebugString(L"mixerGetLineInfoA\n");
+ return mixerGetLineInfoA_orig( hmxobj, pmxl, fdwInfo);
+ }
+
+ MMRESULT WINAPI mixerGetLineInfoW( HMIXEROBJ hmxobj, LPMIXERLINEW pmxl, DWORD fdwInfo)
+ {
+ //OutputDebugString(L"mixerGetLineInfoW\n");
+ return mixerGetLineInfoW_orig( hmxobj, pmxl, fdwInfo);
+ }
+
+ MMRESULT WINAPI mixerGetID( HMIXEROBJ hmxobj, UINT FAR *puMxId, DWORD fdwId)
+ {
+ //OutputDebugString(L"mixerGetID\n");
+ return mixerGetID_orig( hmxobj, puMxId, fdwId);
+ }
+
+ MMRESULT WINAPI mixerGetLineControlsA( HMIXEROBJ hmxobj, LPMIXERLINECONTROLSA pmxlc, DWORD fdwControls)
+ {
+ //OutputDebugString(L"mixerGetLineControlsA\n");
+ return mixerGetLineControlsA_orig( hmxobj, pmxlc, fdwControls);
+ }
+
+ MMRESULT WINAPI mixerGetLineControlsW( HMIXEROBJ hmxobj, LPMIXERLINECONTROLSW pmxlc, DWORD fdwControls)
+ {
+ //OutputDebugString(L"mixerGetLineControlsW\n");
+ return mixerGetLineControlsW_orig( hmxobj, pmxlc, fdwControls);
+ }
+
+ MMRESULT WINAPI mixerGetControlDetailsA( HMIXEROBJ hmxobj, LPMIXERCONTROLDETAILS pmxcd, DWORD fdwDetails)
+ {
+ //OutputDebugString(L"mixerGetControlDetailsA\n");
+ return mixerGetControlDetailsA_orig( hmxobj, pmxcd, fdwDetails);
+ }
+
+ MMRESULT WINAPI mixerGetControlDetailsW( HMIXEROBJ hmxobj, LPMIXERCONTROLDETAILS pmxcd, DWORD fdwDetails)
+ {
+ //OutputDebugString(L"mixerGetControlDetailsW\n");
+ return mixerGetControlDetailsW_orig( hmxobj, pmxcd, fdwDetails);
+ }
+
+ MMRESULT WINAPI mixerSetControlDetails( HMIXEROBJ hmxobj, LPMIXERCONTROLDETAILS pmxcd, DWORD fdwDetails)
+ {
+ //OutputDebugString(L"mixerSetControlDetails\n");
+ return mixerSetControlDetails_orig( hmxobj, pmxcd, fdwDetails);
+ }
+
+ DWORD WINAPI mmGetCurrentTask(void)
+ {
+ //OutputDebugString(L"mmGetCurrentTask\n");
+ return mmGetCurrentTask_orig();
+ }
+
+ void WINAPI mmTaskBlock(DWORD val)
+ {
+ //OutputDebugString(L"mmTaskBlock\n");
+ return mmTaskBlock_orig(val);
+ }
+
+ UINT WINAPI mmTaskCreate(LPTASKCALLBACK a, HANDLE* b, DWORD_PTR c)
+ {
+ //OutputDebugString(L"mmTaskCreate\n");
+ return mmTaskCreate_orig(a, b, c);
+ }
+
+ BOOL WINAPI mmTaskSignal(DWORD a)
+ {
+ //OutputDebugString(L"mmTaskSignal\n");
+ return mmTaskSignal_orig(a);
+ }
+
+ VOID WINAPI mmTaskYield()
+ {
+ //OutputDebugString(L"mmTaskYield\n");
+ mmTaskYield_orig();
+ }
+
+ MMRESULT WINAPI timeGetSystemTime( LPMMTIME pmmt, UINT cbmmt)
+ {
+ //OutputDebugString(L"timeGetSystemTime\n");
+ return timeGetSystemTime_orig( pmmt, cbmmt);
+ }
+
+ DWORD WINAPI timeGetTime(void)
+ {
+ //OutputDebugString(L"timeGetTime\n");
+ return timeGetTime_orig();
+ }
+
+ MMRESULT WINAPI timeSetEvent( UINT uDelay, UINT uResolution, LPTIMECALLBACK fptc, DWORD_PTR dwUser, UINT fuEvent)
+ {
+ //OutputDebugString(L"timeSetEvent\n");
+ return timeSetEvent_orig(uDelay, uResolution, fptc, dwUser, fuEvent);
+ }
+
+ MMRESULT WINAPI timeKillEvent( UINT uTimerID)
+ {
+ //OutputDebugString(L"timeKillEvent\n");
+ return timeKillEvent_orig( uTimerID);
+ }
+
+ MMRESULT WINAPI timeGetDevCaps( LPTIMECAPS ptc, UINT cbtc)
+ {
+ //OutputDebugString(L"timeGetDevCaps\n");
+ return timeGetDevCaps_orig( ptc, cbtc);
+ }
+
+ MMRESULT WINAPI timeBeginPeriod( UINT uPeriod)
+ {
+ //OutputDebugString(L"timeBeginPeriod\n");
+ return timeBeginPeriod_orig( uPeriod);
+ }
+
+ MMRESULT WINAPI timeEndPeriod( UINT uPeriod)
+ {
+ //OutputDebugString(L"timeEndPeriod\n");
+ return timeEndPeriod_orig( uPeriod);
+ }
+
+ UINT WINAPI joyGetNumDevs(void)
+ {
+ //OutputDebugString(L"joyGetNumDevs\n");
+ return joyGetNumDevs_orig();
+ }
+
+ MMRESULT WINAPI joyConfigChanged(DWORD dwFlags)
+ {
+ //OutputDebugString(L"joyConfigChanged\n");
+ return joyConfigChanged_orig(dwFlags);
+ }
+
+ MMRESULT WINAPI joyGetDevCapsA( UINT_PTR uJoyID, LPJOYCAPSA pjc, UINT cbjc)
+ {
+ //OutputDebugString(L"joyGetDevCapsA\n");
+ return joyGetDevCapsA_orig( uJoyID, pjc, cbjc);
+ }
+
+ MMRESULT WINAPI joyGetDevCapsW( UINT_PTR uJoyID, LPJOYCAPSW pjc, UINT cbjc)
+ {
+ //OutputDebugString(L"joyGetDevCapsW\n");
+ return joyGetDevCapsW_orig( uJoyID, pjc, cbjc);
+ }
+
+ MMRESULT WINAPI joyGetPos( UINT uJoyID, LPJOYINFO pji)
+ {
+ //OutputDebugString(L"joyGetPos\n");
+ return joyGetPos_orig( uJoyID, pji);
+ }
+
+ MMRESULT WINAPI joyGetPosEx( UINT uJoyID, LPJOYINFOEX pji)
+ {
+ //OutputDebugString(L"joyGetPosEx\n");
+ return joyGetPosEx_orig( uJoyID, pji);
+ }
+
+ MMRESULT WINAPI joyGetThreshold( UINT uJoyID, LPUINT puThreshold)
+ {
+ //OutputDebugString(L"joyGetThreshold\n");
+ return joyGetThreshold_orig( uJoyID, puThreshold);
+ }
+
+ MMRESULT WINAPI joyReleaseCapture( UINT uJoyID)
+ {
+ //OutputDebugString(L"joyReleaseCapture\n");
+ return joyReleaseCapture_orig( uJoyID);
+ }
+
+ MMRESULT WINAPI joySetCapture( HWND hwnd, UINT uJoyID, UINT uPeriod, BOOL fChanged)
+ {
+ //OutputDebugString(L"joySetCapture\n");
+ return joySetCapture_orig(hwnd, uJoyID, uPeriod, fChanged);
+ }
+
+ MMRESULT WINAPI joySetThreshold( UINT uJoyID, UINT uThreshold)
+ {
+ //OutputDebugString(L"joySetThreshold\n");
+ return joySetThreshold_orig( uJoyID, uThreshold);
+ }
+
+ BOOL WINAPI mciDriverNotify(HWND hwndCallback, UINT uDeviceID, UINT uStatus)
+ {
+ //OutputDebugString(L"mciDriverNotify\n");
+ return mciDriverNotify_orig(hwndCallback, uDeviceID, uStatus);
+ }
+
+ UINT WINAPI mciDriverYield(UINT uDeviceID)
+ {
+ //OutputDebugString(L"mciDriverYield\n");
+ return mciDriverYield_orig(uDeviceID);
+ }
+
+ FOURCC WINAPI mmioStringToFOURCCA( LPCSTR sz, UINT uFlags)
+ {
+ //OutputDebugString(L"mmioStringToFOURCCA\n");
+ return mmioStringToFOURCCA_orig( sz, uFlags);
+ }
+
+ FOURCC WINAPI mmioStringToFOURCCW( LPCWSTR sz, UINT uFlags)
+ {
+ //OutputDebugString(L"mmioStringToFOURCCW\n");
+ return mmioStringToFOURCCW_orig( sz, uFlags);
+ }
+
+ LPMMIOPROC WINAPI mmioInstallIOProcA( FOURCC fccIOProc, LPMMIOPROC pIOProc, DWORD dwFlags)
+ {
+ //OutputDebugString(L"mmioInstallIOProcA\n");
+ return mmioInstallIOProcA_orig( fccIOProc, pIOProc, dwFlags);
+ }
+
+ LPMMIOPROC WINAPI mmioInstallIOProcW( FOURCC fccIOProc, LPMMIOPROC pIOProc, DWORD dwFlags)
+ {
+ //OutputDebugString(L"mmioInstallIOProcW\n");
+ return mmioInstallIOProcW_orig( fccIOProc, pIOProc, dwFlags);
+ }
+
+ HMMIO WINAPI mmioOpenA( LPSTR pszFileName, LPMMIOINFO pmmioinfo, DWORD fdwOpen)
+ {
+ //OutputDebugString(L"mmioOpenA\n");
+ return mmioOpenA_orig( pszFileName, pmmioinfo, fdwOpen);
+ }
+
+ HMMIO WINAPI mmioOpenW( LPWSTR pszFileName, LPMMIOINFO pmmioinfo, DWORD fdwOpen)
+ {
+ //OutputDebugString(L"mmioOpenW\n");
+ return mmioOpenW_orig( pszFileName, pmmioinfo, fdwOpen);
+ }
+
+ MMRESULT WINAPI mmioRenameA( LPCSTR pszFileName, LPCSTR pszNewFileName, LPCMMIOINFO pmmioinfo, DWORD fdwRename)
+ {
+ //OutputDebugString(L"mmioRenameA\n");
+ return mmioRenameA_orig( pszFileName, pszNewFileName, pmmioinfo, fdwRename);
+ }
+
+ MMRESULT WINAPI mmioRenameW( LPCWSTR pszFileName, LPCWSTR pszNewFileName, LPCMMIOINFO pmmioinfo, DWORD fdwRename)
+ {
+ //OutputDebugString(L"mmioRenameW\n");
+ return mmioRenameW_orig( pszFileName, pszNewFileName, pmmioinfo, fdwRename);
+ }
+
+ MMRESULT WINAPI mmioClose( HMMIO hmmio, UINT fuClose)
+ {
+ //OutputDebugString(L"mmioClose\n");
+ return mmioClose_orig( hmmio, fuClose);
+ }
+
+ LONG WINAPI mmioRead( HMMIO hmmio, HPSTR pch, LONG cch)
+ {
+ //OutputDebugString(L"mmioRead\n");
+ return mmioRead_orig( hmmio, pch, cch);
+ }
+
+ LONG WINAPI mmioWrite( HMMIO hmmio, const char _huge* pch, LONG cch)
+ {
+ //OutputDebugString(L"mmioWrite\n");
+ return mmioWrite_orig( hmmio, pch, cch);
+ }
+
+ LONG WINAPI mmioSeek( HMMIO hmmio, LONG lOffset, int iOrigin)
+ {
+ //OutputDebugString(L"mmioSeek\n");
+ return mmioSeek_orig(hmmio, lOffset, iOrigin);
+ }
+
+ MMRESULT WINAPI mmioGetInfo( HMMIO hmmio, LPMMIOINFO pmmioinfo, UINT fuInfo)
+ {
+ //OutputDebugString(L"mmioGetInfo\n");
+ return mmioGetInfo_orig( hmmio, pmmioinfo, fuInfo);
+ }
+
+ MMRESULT WINAPI mmioSetInfo( HMMIO hmmio, LPCMMIOINFO pmmioinfo, UINT fuInfo)
+ {
+ //OutputDebugString(L"mmioSetInfo\n");
+ return mmioSetInfo_orig( hmmio, pmmioinfo, fuInfo);
+ }
+
+ MMRESULT WINAPI mmioSetBuffer( HMMIO hmmio, LPSTR pchBuffer, LONG cchBuffer, UINT fuBuffer)
+ {
+ //OutputDebugString(L"mmioSetBuffer\n");
+ return mmioSetBuffer_orig(hmmio, pchBuffer, cchBuffer, fuBuffer);
+ }
+
+ MMRESULT WINAPI mmioFlush( HMMIO hmmio, UINT fuFlush)
+ {
+ //OutputDebugString(L"mmioFlush\n");
+ return mmioFlush_orig( hmmio, fuFlush);
+ }
+
+ MMRESULT WINAPI mmioAdvance( HMMIO hmmio, LPMMIOINFO pmmioinfo, UINT fuAdvance)
+ {
+ //OutputDebugString(L"mmioAdvance\n");
+ return mmioAdvance_orig( hmmio, pmmioinfo, fuAdvance);
+ }
+
+ LRESULT WINAPI mmioSendMessage( HMMIO hmmio, UINT uMsg, LPARAM lParam1, LPARAM lParam2)
+ {
+ //OutputDebugString(L"mmioSendMessage\n");
+ return mmioSendMessage_orig(hmmio, uMsg, lParam1, lParam2);
+ }
+
+ MMRESULT WINAPI mmioDescend( HMMIO hmmio, LPMMCKINFO pmmcki, const MMCKINFO FAR* pmmckiParent, UINT fuDescend)
+ {
+ //OutputDebugString(L"mmioDescend\n");
+ return mmioDescend_orig(hmmio, pmmcki, pmmckiParent, fuDescend);
+ }
+
+ MMRESULT WINAPI mmioAscend( HMMIO hmmio, LPMMCKINFO pmmcki, UINT fuAscend)
+ {
+ //OutputDebugString(L"mmioAscend\n");
+ return mmioAscend_orig( hmmio, pmmcki, fuAscend);
+ }
+
+ MMRESULT WINAPI mmioCreateChunk(HMMIO hmmio, LPMMCKINFO pmmcki, UINT fuCreate)
+ {
+ //OutputDebugString(L"mmioCreateChunk\n");
+ return mmioCreateChunk_orig(hmmio, pmmcki, fuCreate);
+ }
+
+ MCIERROR WINAPI mciSendCommandA( MCIDEVICEID mciId, UINT uMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
+ {
+ //OutputDebugString(L"mciSendCommandA\n");
+ return mciSendCommandA_orig( mciId, uMsg, dwParam1, dwParam2);
+ }
+
+ MCIERROR WINAPI mciSendCommandW( MCIDEVICEID mciId, UINT uMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
+ {
+ //OutputDebugString(L"mciSendCommandW\n");
+ return mciSendCommandW_orig( mciId, uMsg, dwParam1, dwParam2);
+ }
+
+ MCIERROR WINAPI mciSendStringA( LPCSTR lpstrCommand, LPSTR lpstrReturnString, UINT uReturnLength, HWND hwndCallback)
+ {
+ //OutputDebugString(L"mciSendStringA\n");
+ return mciSendStringA_orig( lpstrCommand, lpstrReturnString, uReturnLength, hwndCallback);
+ }
+
+ MCIERROR WINAPI mciSendStringW( LPCWSTR lpstrCommand, LPWSTR lpstrReturnString, UINT uReturnLength, HWND hwndCallback)
+ {
+ //OutputDebugString(L"mciSendStringW\n");
+ return mciSendStringW_orig( lpstrCommand, lpstrReturnString, uReturnLength, hwndCallback);
+ }
+
+ MCIDEVICEID WINAPI mciGetDeviceIDA( LPCSTR pszDevice)
+ {
+ //OutputDebugString(L"mciGetDeviceIDA\n");
+ return mciGetDeviceIDA_orig( pszDevice);
+ }
+
+ MCIDEVICEID WINAPI mciGetDeviceIDW( LPCWSTR pszDevice)
+ {
+ //OutputDebugString(L"mciGetDeviceIDW\n");
+ return mciGetDeviceIDW_orig( pszDevice);
+ }
+
+ MCIDEVICEID WINAPI mciGetDeviceIDFromElementIDA( DWORD dwElementID, LPCSTR lpstrType )
+ {
+ //OutputDebugString(L"mciGetDeviceIDFromElementIDA\n");
+ return mciGetDeviceIDFromElementIDA_orig( dwElementID, lpstrType );
+ }
+
+ MCIDEVICEID WINAPI mciGetDeviceIDFromElementIDW( DWORD dwElementID, LPCWSTR lpstrType )
+ {
+ //OutputDebugString(L"mciGetDeviceIDFromElementIDW\n");
+ return mciGetDeviceIDFromElementIDW_orig( dwElementID, lpstrType );
+ }
+
+ DWORD_PTR WINAPI mciGetDriverData(UINT uDeviceID)
+ {
+ //OutputDebugString(L"mciGetDriverData\n");
+ return mciGetDriverData_orig(uDeviceID);
+ }
+
+ BOOL WINAPI mciGetErrorStringA( MCIERROR mcierr, LPSTR pszText, UINT cchText)
+ {
+ //OutputDebugString(L"mciGetErrorStringA\n");
+ return mciGetErrorStringA_orig( mcierr, pszText, cchText);
+ }
+
+ BOOL WINAPI mciGetErrorStringW( MCIERROR mcierr, LPWSTR pszText, UINT cchText)
+ {
+ //OutputDebugString(L"mciGetErrorStringW\n");
+ return mciGetErrorStringW_orig( mcierr, pszText, cchText);
+ }
+
+ BOOL WINAPI mciSetDriverData(UINT uDeviceID, DWORD_PTR dwData)
+ {
+ //OutputDebugString(L"mciSetDriverData_type\n");
+ return mciSetDriverData_orig( uDeviceID, dwData );
+ }
+
+ BOOL WINAPI mciSetYieldProc( MCIDEVICEID mciId, YIELDPROC fpYieldProc, DWORD dwYieldData)
+ {
+ //OutputDebugString(L"mciSetYieldProc\n");
+ return mciSetYieldProc_orig(mciId, fpYieldProc, dwYieldData);
+ }
+
+ BOOL WINAPI mciFreeCommandResource(UINT uTable)
+ {
+ //OutputDebugString(L"mciFreeCommandResource\n");
+ return mciFreeCommandResource_orig(uTable);
+ }
+
+ HTASK WINAPI mciGetCreatorTask( MCIDEVICEID mciId)
+ {
+ //OutputDebugString(L"mciGetCreatorTask\n");
+ return mciGetCreatorTask_orig( mciId);
+ }
+
+ YIELDPROC WINAPI mciGetYieldProc( MCIDEVICEID mciId, LPDWORD pdwYieldData)
+ {
+ //OutputDebugString(L"mciGetYieldProc\n");
+ return mciGetYieldProc_orig( mciId, pdwYieldData);
+ }
+
+ UINT WINAPI mciLoadCommandResource(HINSTANCE hInstance, LPCWSTR lpResName, UINT uType)
+ {
+ //OutputDebugString(L"mciLoadCommandResource");
+ return mciLoadCommandResource_orig(hInstance, lpResName, uType);
+ }
+
+
+ BOOL WINAPI mciExecute(LPCSTR pszCommand)
+ {
+ //OutputDebugString(L"mciExecute\n");
+ return mciExecute_orig(pszCommand);
+ }
+}
diff --git a/indra/media_plugins/winmmshim/forwarding_api.h b/indra/media_plugins/winmmshim/forwarding_api.h
new file mode 100644
index 0000000000..89a6b347f3
--- /dev/null
+++ b/indra/media_plugins/winmmshim/forwarding_api.h
@@ -0,0 +1,389 @@
+/**
+ * @file forwarding_api.h
+ * @brief forwards winmm API calls to real winmm.dll
+ *
+ * $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$
+ */
+
+// this turns off __declspec(dllimport) for the functions declared in mmsystem.h
+#define _WINMM_
+#include <windows.h>
+#include <mmsystem.h>
+
+void init_function_pointers(HMODULE winmm_handle);
+
+typedef VOID (*LPTASKCALLBACK)(DWORD_PTR dwInst);
+
+typedef LRESULT (WINAPI *CloseDriver_type)( HDRVR hDriver, LPARAM lParam1, LPARAM lParam2);
+extern CloseDriver_type CloseDriver_orig;
+typedef HDRVR (WINAPI *OpenDriver_type)( LPCWSTR szDriverName, LPCWSTR szSectionName, LPARAM lParam2);
+extern OpenDriver_type OpenDriver_orig;
+typedef LRESULT (WINAPI *SendDriverMessage_type)( HDRVR hDriver, UINT message, LPARAM lParam1, LPARAM lParam2);
+extern SendDriverMessage_type SendDriverMessage_orig;
+typedef HMODULE (WINAPI *DrvGetModuleHandle_type)( HDRVR hDriver);
+extern DrvGetModuleHandle_type DrvGetModuleHandle_orig;
+typedef HMODULE (WINAPI *GetDriverModuleHandle_type)( HDRVR hDriver);
+extern GetDriverModuleHandle_type GetDriverModuleHandle_orig;
+typedef LRESULT (WINAPI *DefDriverProc_type)( DWORD_PTR dwDriverIdentifier, HDRVR hdrvr, UINT uMsg, LPARAM lParam1, LPARAM lParam2);
+extern DefDriverProc_type DefDriverProc_orig;
+typedef BOOL (WINAPI *DriverCallback_type)(DWORD dwCallBack, DWORD dwFlags, HDRVR hdrvr, DWORD msg, DWORD dwUser, DWORD dwParam1, DWORD dwParam2);
+extern DriverCallback_type DriverCallback_orig;
+typedef UINT (WINAPI *mmsystemGetVersion_type)(void);
+extern mmsystemGetVersion_type mmsystemGetVersion_orig;
+typedef BOOL (WINAPI *sndPlaySoundA_type)( LPCSTR pszSound, UINT fuSound);
+extern sndPlaySoundA_type sndPlaySoundA_orig;
+typedef BOOL (WINAPI *sndPlaySoundW_type)( LPCWSTR pszSound, UINT fuSound);
+extern sndPlaySoundW_type sndPlaySoundW_orig;
+typedef BOOL (WINAPI *PlaySoundA_type)( LPCSTR pszSound, HMODULE hmod, DWORD fdwSound);
+extern PlaySoundA_type PlaySoundA_orig;
+typedef BOOL (WINAPI *PlaySoundW_type)( LPCWSTR pszSound, HMODULE hmod, DWORD fdwSound);
+extern PlaySoundW_type PlaySoundW_orig;
+typedef UINT (WINAPI *waveOutGetNumDevs_type)(void);
+extern waveOutGetNumDevs_type waveOutGetNumDevs_orig;
+typedef MMRESULT (WINAPI *waveOutGetDevCapsA_type)( UINT_PTR uDeviceID, LPWAVEOUTCAPSA pwoc, UINT cbwoc);
+extern waveOutGetDevCapsA_type waveOutGetDevCapsA_orig;
+typedef MMRESULT (WINAPI *waveOutGetDevCapsW_type)( UINT_PTR uDeviceID, LPWAVEOUTCAPSW pwoc, UINT cbwoc);
+extern waveOutGetDevCapsW_type waveOutGetDevCapsW_orig;
+typedef MMRESULT (WINAPI *waveOutGetVolume_type)( HWAVEOUT hwo, LPDWORD pdwVolume);
+extern waveOutGetVolume_type waveOutGetVolume_orig;
+typedef MMRESULT (WINAPI *waveOutSetVolume_type)( HWAVEOUT hwo, DWORD dwVolume);
+extern waveOutSetVolume_type waveOutSetVolume_orig;
+typedef MMRESULT (WINAPI *waveOutGetErrorTextA_type)( MMRESULT mmrError, LPSTR pszText, UINT cchText);
+extern waveOutGetErrorTextA_type waveOutGetErrorTextA_orig;
+typedef MMRESULT (WINAPI *waveOutGetErrorTextW_type)( MMRESULT mmrError, LPWSTR pszText, UINT cchText);
+extern waveOutGetErrorTextW_type waveOutGetErrorTextW_orig;
+typedef MMRESULT (WINAPI *waveOutOpen_type)( LPHWAVEOUT phwo, UINT uDeviceID, LPCWAVEFORMATEX pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen);
+extern waveOutOpen_type waveOutOpen_orig;
+typedef MMRESULT (WINAPI *waveOutClose_type)( HWAVEOUT hwo);
+extern waveOutClose_type waveOutClose_orig;
+typedef MMRESULT (WINAPI *waveOutPrepareHeader_type)( HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh);
+extern waveOutPrepareHeader_type waveOutPrepareHeader_orig;
+typedef MMRESULT (WINAPI *waveOutUnprepareHeader_type)( HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh);
+extern waveOutUnprepareHeader_type waveOutUnprepareHeader_orig;
+typedef MMRESULT (WINAPI *waveOutWrite_type)( HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh);
+extern waveOutWrite_type waveOutWrite_orig;
+typedef MMRESULT (WINAPI *waveOutPause_type)( HWAVEOUT hwo);
+extern waveOutPause_type waveOutPause_orig;
+typedef MMRESULT (WINAPI *waveOutRestart_type)( HWAVEOUT hwo);
+extern waveOutRestart_type waveOutRestart_orig;
+typedef MMRESULT (WINAPI *waveOutReset_type)( HWAVEOUT hwo);
+extern waveOutReset_type waveOutReset_orig;
+typedef MMRESULT (WINAPI *waveOutBreakLoop_type)( HWAVEOUT hwo);
+extern waveOutBreakLoop_type waveOutBreakLoop_orig;
+typedef MMRESULT (WINAPI *waveOutGetPosition_type)( HWAVEOUT hwo, LPMMTIME pmmt, UINT cbmmt);
+extern waveOutGetPosition_type waveOutGetPosition_orig;
+typedef MMRESULT (WINAPI *waveOutGetPitch_type)( HWAVEOUT hwo, LPDWORD pdwPitch);
+extern waveOutGetPitch_type waveOutGetPitch_orig;
+typedef MMRESULT (WINAPI *waveOutSetPitch_type)( HWAVEOUT hwo, DWORD dwPitch);
+extern waveOutSetPitch_type waveOutSetPitch_orig;
+typedef MMRESULT (WINAPI *waveOutGetPlaybackRate_type)( HWAVEOUT hwo, LPDWORD pdwRate);
+extern waveOutGetPlaybackRate_type waveOutGetPlaybackRate_orig;
+typedef MMRESULT (WINAPI *waveOutSetPlaybackRate_type)( HWAVEOUT hwo, DWORD dwRate);
+extern waveOutSetPlaybackRate_type waveOutSetPlaybackRate_orig;
+typedef MMRESULT (WINAPI *waveOutGetID_type)( HWAVEOUT hwo, LPUINT puDeviceID);
+extern waveOutGetID_type waveOutGetID_orig;
+typedef MMRESULT (WINAPI *waveOutMessage_type)( HWAVEOUT hwo, UINT uMsg, DWORD_PTR dw1, DWORD_PTR dw2);
+extern waveOutMessage_type waveOutMessage_orig;
+typedef UINT (WINAPI *waveInGetNumDevs_type)(void);
+extern waveInGetNumDevs_type waveInGetNumDevs_orig;
+typedef MMRESULT (WINAPI *waveInGetDevCapsA_type)( UINT_PTR uDeviceID, LPWAVEINCAPSA pwic, UINT cbwic);
+extern waveInGetDevCapsA_type waveInGetDevCapsA_orig;
+typedef MMRESULT (WINAPI *waveInGetDevCapsW_type)( UINT_PTR uDeviceID, LPWAVEINCAPSW pwic, UINT cbwic);
+extern waveInGetDevCapsW_type waveInGetDevCapsW_orig;
+typedef MMRESULT (WINAPI *waveInGetErrorTextA_type)(MMRESULT mmrError, LPSTR pszText, UINT cchText);
+extern waveInGetErrorTextA_type waveInGetErrorTextA_orig;
+typedef MMRESULT (WINAPI *waveInGetErrorTextW_type)(MMRESULT mmrError, LPWSTR pszText, UINT cchText);
+extern waveInGetErrorTextW_type waveInGetErrorTextW_orig;
+typedef MMRESULT (WINAPI *waveInOpen_type)( LPHWAVEIN phwi, UINT uDeviceID, LPCWAVEFORMATEX pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen);
+extern waveInOpen_type waveInOpen_orig;
+typedef MMRESULT (WINAPI *waveInClose_type)( HWAVEIN hwi);
+extern waveInClose_type waveInClose_orig;
+typedef MMRESULT (WINAPI *waveInPrepareHeader_type)( HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh);
+extern waveInPrepareHeader_type waveInPrepareHeader_orig;
+typedef MMRESULT (WINAPI *waveInUnprepareHeader_type)( HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh);
+extern waveInUnprepareHeader_type waveInUnprepareHeader_orig;
+typedef MMRESULT (WINAPI *waveInAddBuffer_type)( HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh);
+extern waveInAddBuffer_type waveInAddBuffer_orig;
+typedef MMRESULT (WINAPI *waveInStart_type)( HWAVEIN hwi);
+extern waveInStart_type waveInStart_orig;
+typedef MMRESULT (WINAPI *waveInStop_type)( HWAVEIN hwi);
+extern waveInStop_type waveInStop_orig;
+typedef MMRESULT (WINAPI *waveInReset_type)( HWAVEIN hwi);
+extern waveInReset_type waveInReset_orig;
+typedef MMRESULT (WINAPI *waveInGetPosition_type)( HWAVEIN hwi, LPMMTIME pmmt, UINT cbmmt);
+extern waveInGetPosition_type waveInGetPosition_orig;
+typedef MMRESULT (WINAPI *waveInGetID_type)( HWAVEIN hwi, LPUINT puDeviceID);
+extern waveInGetID_type waveInGetID_orig;
+typedef MMRESULT (WINAPI *waveInMessage_type)( HWAVEIN hwi, UINT uMsg, DWORD_PTR dw1, DWORD_PTR dw2);
+extern waveInMessage_type waveInMessage_orig;
+typedef UINT (WINAPI *midiOutGetNumDevs_type)(void);
+extern midiOutGetNumDevs_type midiOutGetNumDevs_orig;
+typedef MMRESULT (WINAPI *midiStreamOpen_type)( LPHMIDISTRM phms, LPUINT puDeviceID, DWORD cMidi, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen);
+extern midiStreamOpen_type midiStreamOpen_orig;
+typedef MMRESULT (WINAPI *midiStreamClose_type)( HMIDISTRM hms);
+extern midiStreamClose_type midiStreamClose_orig;
+typedef MMRESULT (WINAPI *midiStreamProperty_type)( HMIDISTRM hms, LPBYTE lppropdata, DWORD dwProperty);
+extern midiStreamProperty_type midiStreamProperty_orig;
+typedef MMRESULT (WINAPI *midiStreamPosition_type)( HMIDISTRM hms, LPMMTIME lpmmt, UINT cbmmt);
+extern midiStreamPosition_type midiStreamPosition_orig;
+typedef MMRESULT (WINAPI *midiStreamOut_type)( HMIDISTRM hms, LPMIDIHDR pmh, UINT cbmh);
+extern midiStreamOut_type midiStreamOut_orig;
+typedef MMRESULT (WINAPI *midiStreamPause_type)( HMIDISTRM hms);
+extern midiStreamPause_type midiStreamPause_orig;
+typedef MMRESULT (WINAPI *midiStreamRestart_type)( HMIDISTRM hms);
+extern midiStreamRestart_type midiStreamRestart_orig;
+typedef MMRESULT (WINAPI *midiStreamStop_type)( HMIDISTRM hms);
+extern midiStreamStop_type midiStreamStop_orig;
+typedef MMRESULT (WINAPI *midiConnect_type)( HMIDI hmi, HMIDIOUT hmo, LPVOID pReserved);
+extern midiConnect_type midiConnect_orig;
+typedef MMRESULT (WINAPI *midiDisconnect_type)( HMIDI hmi, HMIDIOUT hmo, LPVOID pReserved);
+extern midiDisconnect_type midiDisconnect_orig;
+typedef MMRESULT (WINAPI *midiOutGetDevCapsA_type)( UINT_PTR uDeviceID, LPMIDIOUTCAPSA pmoc, UINT cbmoc);
+extern midiOutGetDevCapsA_type midiOutGetDevCapsA_orig;
+typedef MMRESULT (WINAPI *midiOutGetDevCapsW_type)( UINT_PTR uDeviceID, LPMIDIOUTCAPSW pmoc, UINT cbmoc);
+extern midiOutGetDevCapsW_type midiOutGetDevCapsW_orig;
+typedef MMRESULT (WINAPI *midiOutGetVolume_type)( HMIDIOUT hmo, LPDWORD pdwVolume);
+extern midiOutGetVolume_type midiOutGetVolume_orig;
+typedef MMRESULT (WINAPI *midiOutSetVolume_type)( HMIDIOUT hmo, DWORD dwVolume);
+extern midiOutSetVolume_type midiOutSetVolume_orig;
+typedef MMRESULT (WINAPI *midiOutGetErrorTextA_type)( MMRESULT mmrError, LPSTR pszText, UINT cchText);
+extern midiOutGetErrorTextA_type midiOutGetErrorTextA_orig;
+typedef MMRESULT (WINAPI *midiOutGetErrorTextW_type)( MMRESULT mmrError, LPWSTR pszText, UINT cchText);
+extern midiOutGetErrorTextW_type midiOutGetErrorTextW_orig;
+typedef MMRESULT (WINAPI *midiOutOpen_type)( LPHMIDIOUT phmo, UINT uDeviceID, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen);
+extern midiOutOpen_type midiOutOpen_orig;
+typedef MMRESULT (WINAPI *midiOutClose_type)( HMIDIOUT hmo);
+extern midiOutClose_type midiOutClose_orig;
+typedef MMRESULT (WINAPI *midiOutPrepareHeader_type)( HMIDIOUT hmo, LPMIDIHDR pmh, UINT cbmh);
+extern midiOutPrepareHeader_type midiOutPrepareHeader_orig;
+typedef MMRESULT (WINAPI *midiOutUnprepareHeader_type)(HMIDIOUT hmo, LPMIDIHDR pmh, UINT cbmh);
+extern midiOutUnprepareHeader_type midiOutUnprepareHeader_orig;
+typedef MMRESULT (WINAPI *midiOutShortMsg_type)( HMIDIOUT hmo, DWORD dwMsg);
+extern midiOutShortMsg_type midiOutShortMsg_orig;
+typedef MMRESULT (WINAPI *midiOutLongMsg_type)(HMIDIOUT hmo, LPMIDIHDR pmh, UINT cbmh);
+extern midiOutLongMsg_type midiOutLongMsg_orig;
+typedef MMRESULT (WINAPI *midiOutReset_type)( HMIDIOUT hmo);
+extern midiOutReset_type midiOutReset_orig;
+typedef MMRESULT (WINAPI *midiOutCachePatches_type)( HMIDIOUT hmo, UINT uBank, LPWORD pwpa, UINT fuCache);
+extern midiOutCachePatches_type midiOutCachePatches_orig;
+typedef MMRESULT (WINAPI *midiOutCacheDrumPatches_type)( HMIDIOUT hmo, UINT uPatch, LPWORD pwkya, UINT fuCache);
+extern midiOutCacheDrumPatches_type midiOutCacheDrumPatches_orig;
+typedef MMRESULT (WINAPI *midiOutGetID_type)( HMIDIOUT hmo, LPUINT puDeviceID);
+extern midiOutGetID_type midiOutGetID_orig;
+typedef MMRESULT (WINAPI *midiOutMessage_type)( HMIDIOUT hmo, UINT uMsg, DWORD_PTR dw1, DWORD_PTR dw2);
+extern midiOutMessage_type midiOutMessage_orig;
+typedef UINT (WINAPI *midiInGetNumDevs_type)(void);
+extern midiInGetNumDevs_type midiInGetNumDevs_orig;
+typedef MMRESULT (WINAPI *midiInGetDevCapsA_type)( UINT_PTR uDeviceID, LPMIDIINCAPSA pmic, UINT cbmic);
+extern midiInGetDevCapsA_type midiInGetDevCapsA_orig;
+typedef MMRESULT (WINAPI *midiInGetDevCapsW_type)( UINT_PTR uDeviceID, LPMIDIINCAPSW pmic, UINT cbmic);
+extern midiInGetDevCapsW_type midiInGetDevCapsW_orig;
+typedef MMRESULT (WINAPI *midiInGetErrorTextA_type)( MMRESULT mmrError, LPSTR pszText, UINT cchText);
+extern midiInGetErrorTextA_type midiInGetErrorTextA_orig;
+typedef MMRESULT (WINAPI *midiInGetErrorTextW_type)( MMRESULT mmrError, LPWSTR pszText, UINT cchText);
+extern midiInGetErrorTextW_type midiInGetErrorTextW_orig;
+typedef MMRESULT (WINAPI *midiInOpen_type)( LPHMIDIIN phmi, UINT uDeviceID, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen);
+extern midiInOpen_type midiInOpen_orig;
+typedef MMRESULT (WINAPI *midiInClose_type)( HMIDIIN hmi);
+extern midiInClose_type midiInClose_orig;
+typedef MMRESULT (WINAPI *midiInPrepareHeader_type)( HMIDIIN hmi, LPMIDIHDR pmh, UINT cbmh);
+extern midiInPrepareHeader_type midiInPrepareHeader_orig;
+typedef MMRESULT (WINAPI *midiInUnprepareHeader_type)( HMIDIIN hmi, LPMIDIHDR pmh, UINT cbmh);
+extern midiInUnprepareHeader_type midiInUnprepareHeader_orig;
+typedef MMRESULT (WINAPI *midiInAddBuffer_type)( HMIDIIN hmi, LPMIDIHDR pmh, UINT cbmh);
+extern midiInAddBuffer_type midiInAddBuffer_orig;
+typedef MMRESULT (WINAPI *midiInStart_type)( HMIDIIN hmi);
+extern midiInStart_type midiInStart_orig;
+typedef MMRESULT (WINAPI *midiInStop_type)( HMIDIIN hmi);
+extern midiInStop_type midiInStop_orig;
+typedef MMRESULT (WINAPI *midiInReset_type)( HMIDIIN hmi);
+extern midiInReset_type midiInReset_orig;
+typedef MMRESULT (WINAPI *midiInGetID_type)( HMIDIIN hmi, LPUINT puDeviceID);
+extern midiInGetID_type midiInGetID_orig;
+typedef MMRESULT (WINAPI *midiInMessage_type)( HMIDIIN hmi, UINT uMsg, DWORD_PTR dw1, DWORD_PTR dw2);
+extern midiInMessage_type midiInMessage_orig;
+typedef UINT (WINAPI *auxGetNumDevs_type)(void);
+extern auxGetNumDevs_type auxGetNumDevs_orig;
+typedef MMRESULT (WINAPI *auxGetDevCapsA_type)( UINT_PTR uDeviceID, LPAUXCAPSA pac, UINT cbac);
+extern auxGetDevCapsA_type auxGetDevCapsA_orig;
+typedef MMRESULT (WINAPI *auxGetDevCapsW_type)( UINT_PTR uDeviceID, LPAUXCAPSW pac, UINT cbac);
+extern auxGetDevCapsW_type auxGetDevCapsW_orig;
+typedef MMRESULT (WINAPI *auxSetVolume_type)( UINT uDeviceID, DWORD dwVolume);
+extern auxSetVolume_type auxSetVolume_orig;
+typedef MMRESULT (WINAPI *auxGetVolume_type)( UINT uDeviceID, LPDWORD pdwVolume);
+extern auxGetVolume_type auxGetVolume_orig;
+typedef MMRESULT (WINAPI *auxOutMessage_type)( UINT uDeviceID, UINT uMsg, DWORD_PTR dw1, DWORD_PTR dw2);
+extern auxOutMessage_type auxOutMessage_orig;
+typedef UINT (WINAPI *mixerGetNumDevs_type)(void);
+extern mixerGetNumDevs_type mixerGetNumDevs_orig;
+typedef MMRESULT (WINAPI *mixerGetDevCapsA_type)( UINT_PTR uMxId, LPMIXERCAPSA pmxcaps, UINT cbmxcaps);
+extern mixerGetDevCapsA_type mixerGetDevCapsA_orig;
+typedef MMRESULT (WINAPI *mixerGetDevCapsW_type)( UINT_PTR uMxId, LPMIXERCAPSW pmxcaps, UINT cbmxcaps);
+extern mixerGetDevCapsW_type mixerGetDevCapsW_orig;
+typedef MMRESULT (WINAPI *mixerOpen_type)( LPHMIXER phmx, UINT uMxId, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen);
+extern mixerOpen_type mixerOpen_orig;
+typedef MMRESULT (WINAPI *mixerClose_type)( HMIXER hmx);
+extern mixerClose_type mixerClose_orig;
+typedef DWORD (WINAPI *mixerMessage_type)( HMIXER hmx, UINT uMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2);
+extern mixerMessage_type mixerMessage_orig;
+typedef MMRESULT (WINAPI *mixerGetLineInfoA_type)( HMIXEROBJ hmxobj, LPMIXERLINEA pmxl, DWORD fdwInfo);
+extern mixerGetLineInfoA_type mixerGetLineInfoA_orig;
+typedef MMRESULT (WINAPI *mixerGetLineInfoW_type)( HMIXEROBJ hmxobj, LPMIXERLINEW pmxl, DWORD fdwInfo);
+extern mixerGetLineInfoW_type mixerGetLineInfoW_orig;
+typedef MMRESULT (WINAPI *mixerGetID_type)( HMIXEROBJ hmxobj, UINT FAR *puMxId, DWORD fdwId);
+extern mixerGetID_type mixerGetID_orig;
+typedef MMRESULT (WINAPI *mixerGetLineControlsA_type)( HMIXEROBJ hmxobj, LPMIXERLINECONTROLSA pmxlc, DWORD fdwControls);
+extern mixerGetLineControlsA_type mixerGetLineControlsA_orig;
+typedef MMRESULT (WINAPI *mixerGetLineControlsW_type)( HMIXEROBJ hmxobj, LPMIXERLINECONTROLSW pmxlc, DWORD fdwControls);
+extern mixerGetLineControlsW_type mixerGetLineControlsW_orig;
+typedef MMRESULT (WINAPI *mixerGetControlDetailsA_type)( HMIXEROBJ hmxobj, LPMIXERCONTROLDETAILS pmxcd, DWORD fdwDetails);
+extern mixerGetControlDetailsA_type mixerGetControlDetailsA_orig;
+typedef MMRESULT (WINAPI *mixerGetControlDetailsW_type)( HMIXEROBJ hmxobj, LPMIXERCONTROLDETAILS pmxcd, DWORD fdwDetails);
+extern mixerGetControlDetailsW_type mixerGetControlDetailsW_orig;
+typedef MMRESULT (WINAPI *mixerSetControlDetails_type)( HMIXEROBJ hmxobj, LPMIXERCONTROLDETAILS pmxcd, DWORD fdwDetails);
+extern mixerSetControlDetails_type mixerSetControlDetails_orig;
+typedef DWORD (WINAPI *mmGetCurrentTask_type)(void);
+extern mmGetCurrentTask_type mmGetCurrentTask_orig;
+typedef void (WINAPI *mmTaskBlock_type)(DWORD);
+extern mmTaskBlock_type mmTaskBlock_orig;
+typedef UINT (WINAPI *mmTaskCreate_type)(LPTASKCALLBACK, HANDLE*, DWORD_PTR);
+extern mmTaskCreate_type mmTaskCreate_orig;
+typedef BOOL (WINAPI *mmTaskSignal_type)(DWORD);
+extern mmTaskSignal_type mmTaskSignal_orig;
+typedef VOID (WINAPI *mmTaskYield_type)(VOID);
+extern mmTaskYield_type mmTaskYield_orig;
+typedef MMRESULT (WINAPI *timeGetSystemTime_type)( LPMMTIME pmmt, UINT cbmmt);
+extern timeGetSystemTime_type timeGetSystemTime_orig;
+typedef DWORD (WINAPI *timeGetTime_type)(void);
+extern timeGetTime_type timeGetTime_orig;
+typedef MMRESULT (WINAPI *timeSetEvent_type)( UINT uDelay, UINT uResolution, LPTIMECALLBACK fptc, DWORD_PTR dwUser, UINT fuEvent);
+extern timeSetEvent_type timeSetEvent_orig;
+typedef MMRESULT (WINAPI *timeKillEvent_type)( UINT uTimerID);
+extern timeKillEvent_type timeKillEvent_orig;
+typedef MMRESULT (WINAPI *timeGetDevCaps_type)( LPTIMECAPS ptc, UINT cbtc);
+extern timeGetDevCaps_type timeGetDevCaps_orig;
+typedef MMRESULT (WINAPI *timeBeginPeriod_type)( UINT uPeriod);
+extern timeBeginPeriod_type timeBeginPeriod_orig;
+typedef MMRESULT (WINAPI *timeEndPeriod_type)( UINT uPeriod);
+extern timeEndPeriod_type timeEndPeriod_orig;
+typedef UINT (WINAPI *joyGetNumDevs_type)(void);
+extern joyGetNumDevs_type joyGetNumDevs_orig;
+typedef MMRESULT (WINAPI *joyConfigChanged_type)(DWORD dwFlags);
+extern joyConfigChanged_type joyConfigChanged_orig;
+typedef MMRESULT (WINAPI *joyGetDevCapsA_type)( UINT_PTR uJoyID, LPJOYCAPSA pjc, UINT cbjc);
+extern joyGetDevCapsA_type joyGetDevCapsA_orig;
+typedef MMRESULT (WINAPI *joyGetDevCapsW_type)( UINT_PTR uJoyID, LPJOYCAPSW pjc, UINT cbjc);
+extern joyGetDevCapsW_type joyGetDevCapsW_orig;
+typedef MMRESULT (WINAPI *joyGetPos_type)( UINT uJoyID, LPJOYINFO pji);
+extern joyGetPos_type joyGetPos_orig;
+typedef MMRESULT (WINAPI *joyGetPosEx_type)( UINT uJoyID, LPJOYINFOEX pji);
+extern joyGetPosEx_type joyGetPosEx_orig;
+typedef MMRESULT (WINAPI *joyGetThreshold_type)( UINT uJoyID, LPUINT puThreshold);
+extern joyGetThreshold_type joyGetThreshold_orig;
+typedef MMRESULT (WINAPI *joyReleaseCapture_type)( UINT uJoyID);
+extern joyReleaseCapture_type joyReleaseCapture_orig;
+typedef MMRESULT (WINAPI *joySetCapture_type)( HWND hwnd, UINT uJoyID, UINT uPeriod, BOOL fChanged);
+extern joySetCapture_type joySetCapture_orig;
+typedef MMRESULT (WINAPI *joySetThreshold_type)( UINT uJoyID, UINT uThreshold);
+extern joySetThreshold_type joySetThreshold_orig;
+typedef BOOL (WINAPI *mciDriverNotify_type)(HWND hwndCallback, UINT uDeviceID, UINT uStatus);
+extern mciDriverNotify_type mciDriverNotify_orig;
+typedef UINT (WINAPI *mciDriverYield_type)(UINT uDeviceID);
+extern mciDriverYield_type mciDriverYield_orig;
+typedef FOURCC (WINAPI *mmioStringToFOURCCA_type)( LPCSTR sz, UINT uFlags);
+extern mmioStringToFOURCCA_type mmioStringToFOURCCA_orig;
+typedef FOURCC (WINAPI *mmioStringToFOURCCW_type)( LPCWSTR sz, UINT uFlags);
+extern mmioStringToFOURCCW_type mmioStringToFOURCCW_orig;
+typedef LPMMIOPROC (WINAPI *mmioInstallIOProcA_type)( FOURCC fccIOProc, LPMMIOPROC pIOProc, DWORD dwFlags);
+extern mmioInstallIOProcA_type mmioInstallIOProcA_orig;
+typedef LPMMIOPROC (WINAPI *mmioInstallIOProcW_type)( FOURCC fccIOProc, LPMMIOPROC pIOProc, DWORD dwFlags);
+extern mmioInstallIOProcW_type mmioInstallIOProcW_orig;
+typedef HMMIO (WINAPI *mmioOpenA_type)( LPSTR pszFileName, LPMMIOINFO pmmioinfo, DWORD fdwOpen);
+extern mmioOpenA_type mmioOpenA_orig;
+typedef HMMIO (WINAPI *mmioOpenW_type)( LPWSTR pszFileName, LPMMIOINFO pmmioinfo, DWORD fdwOpen);
+extern mmioOpenW_type mmioOpenW_orig;
+typedef MMRESULT (WINAPI *mmioRenameA_type)( LPCSTR pszFileName, LPCSTR pszNewFileName, LPCMMIOINFO pmmioinfo, DWORD fdwRename);
+extern mmioRenameA_type mmioRenameA_orig;
+typedef MMRESULT (WINAPI *mmioRenameW_type)( LPCWSTR pszFileName, LPCWSTR pszNewFileName, LPCMMIOINFO pmmioinfo, DWORD fdwRename);
+extern mmioRenameW_type mmioRenameW_orig;
+typedef MMRESULT (WINAPI *mmioClose_type)( HMMIO hmmio, UINT fuClose);
+extern mmioClose_type mmioClose_orig;
+typedef LONG (WINAPI *mmioRead_type)( HMMIO hmmio, HPSTR pch, LONG cch);
+extern mmioRead_type mmioRead_orig;
+typedef LONG (WINAPI *mmioWrite_type)( HMMIO hmmio, const char _huge* pch, LONG cch);
+extern mmioWrite_type mmioWrite_orig;
+typedef LONG (WINAPI *mmioSeek_type)( HMMIO hmmio, LONG lOffset, int iOrigin);
+extern mmioSeek_type mmioSeek_orig;
+typedef MMRESULT (WINAPI *mmioGetInfo_type)( HMMIO hmmio, LPMMIOINFO pmmioinfo, UINT fuInfo);
+extern mmioGetInfo_type mmioGetInfo_orig;
+typedef MMRESULT (WINAPI *mmioSetInfo_type)( HMMIO hmmio, LPCMMIOINFO pmmioinfo, UINT fuInfo);
+extern mmioSetInfo_type mmioSetInfo_orig;
+typedef MMRESULT (WINAPI *mmioSetBuffer_type)( HMMIO hmmio, LPSTR pchBuffer, LONG cchBuffer, UINT fuBuffer);
+extern mmioSetBuffer_type mmioSetBuffer_orig;
+typedef MMRESULT (WINAPI *mmioFlush_type)( HMMIO hmmio, UINT fuFlush);
+extern mmioFlush_type mmioFlush_orig;
+typedef MMRESULT (WINAPI *mmioAdvance_type)( HMMIO hmmio, LPMMIOINFO pmmioinfo, UINT fuAdvance);
+extern mmioAdvance_type mmioAdvance_orig;
+typedef LRESULT (WINAPI *mmioSendMessage_type)( HMMIO hmmio, UINT uMsg, LPARAM lParam1, LPARAM lParam2);
+extern mmioSendMessage_type mmioSendMessage_orig;
+typedef MMRESULT (WINAPI *mmioDescend_type)( HMMIO hmmio, LPMMCKINFO pmmcki, const MMCKINFO FAR* pmmckiParent, UINT fuDescend);
+extern mmioDescend_type mmioDescend_orig;
+typedef MMRESULT (WINAPI *mmioAscend_type)( HMMIO hmmio, LPMMCKINFO pmmcki, UINT fuAscend);
+extern mmioAscend_type mmioAscend_orig;
+typedef MMRESULT (WINAPI *mmioCreateChunk_type)(HMMIO hmmio, LPMMCKINFO pmmcki, UINT fuCreate);
+extern mmioCreateChunk_type mmioCreateChunk_orig;
+typedef MCIERROR (WINAPI *mciSendCommandA_type)( MCIDEVICEID mciId, UINT uMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2);
+extern mciSendCommandA_type mciSendCommandA_orig;
+typedef MCIERROR (WINAPI *mciSendCommandW_type)( MCIDEVICEID mciId, UINT uMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2);
+extern mciSendCommandW_type mciSendCommandW_orig;
+typedef MCIERROR (WINAPI *mciSendStringA_type)( LPCSTR lpstrCommand, LPSTR lpstrReturnString, UINT uReturnLength, HWND hwndCallback);
+extern mciSendStringA_type mciSendStringA_orig;
+typedef MCIERROR (WINAPI *mciSendStringW_type)( LPCWSTR lpstrCommand, LPWSTR lpstrReturnString, UINT uReturnLength, HWND hwndCallback);
+extern mciSendStringW_type mciSendStringW_orig;
+typedef MCIDEVICEID (WINAPI *mciGetDeviceIDA_type)( LPCSTR pszDevice);
+extern mciGetDeviceIDA_type mciGetDeviceIDA_orig;
+typedef MCIDEVICEID (WINAPI *mciGetDeviceIDW_type)( LPCWSTR pszDevice);
+extern mciGetDeviceIDW_type mciGetDeviceIDW_orig;
+typedef MCIDEVICEID (WINAPI *mciGetDeviceIDFromElementIDA_type)( DWORD dwElementID, LPCSTR lpstrType );
+extern mciGetDeviceIDFromElementIDA_type mciGetDeviceIDFromElementIDA_orig;
+typedef MCIDEVICEID (WINAPI *mciGetDeviceIDFromElementIDW_type)( DWORD dwElementID, LPCWSTR lpstrType );
+extern mciGetDeviceIDFromElementIDW_type mciGetDeviceIDFromElementIDW_orig;
+typedef DWORD_PTR (WINAPI *mciGetDriverData_type)(UINT uDeviceID);
+extern mciGetDriverData_type mciGetDriverData_orig;
+typedef BOOL (WINAPI *mciGetErrorStringA_type)( MCIERROR mcierr, LPSTR pszText, UINT cchText);
+extern mciGetErrorStringA_type mciGetErrorStringA_orig;
+typedef BOOL (WINAPI *mciGetErrorStringW_type)( MCIERROR mcierr, LPWSTR pszText, UINT cchText);
+extern mciGetErrorStringW_type mciGetErrorStringW_orig;
+typedef BOOL (WINAPI *mciSetDriverData_type)(UINT uDeviceID, DWORD_PTR dwData);
+extern mciSetDriverData_type mciSetDriverData_orig;
+typedef BOOL (WINAPI *mciSetYieldProc_type)( MCIDEVICEID mciId, YIELDPROC fpYieldProc, DWORD dwYieldData);
+extern mciSetYieldProc_type mciSetYieldProc_orig;
+typedef BOOL (WINAPI *mciFreeCommandResource_type)(UINT uTable);
+extern mciFreeCommandResource_type mciFreeCommandResource_orig;
+typedef HTASK (WINAPI *mciGetCreatorTask_type)( MCIDEVICEID mciId);
+extern mciGetCreatorTask_type mciGetCreatorTask_orig;
+typedef YIELDPROC (WINAPI *mciGetYieldProc_type)( MCIDEVICEID mciId, LPDWORD pdwYieldData);
+extern mciGetYieldProc_type mciGetYieldProc_orig;
+typedef UINT (WINAPI *mciLoadCommandResource_type)(HINSTANCE hInstance, LPCWSTR lpResName, UINT uType);
+extern mciLoadCommandResource_type mciLoadCommandResource_orig;
+typedef BOOL (WINAPI *mciExecute_type)(LPCSTR pszCommand);
+extern mciExecute_type mciExecute_orig;
diff --git a/indra/media_plugins/winmmshim/winmm.def b/indra/media_plugins/winmmshim/winmm.def
new file mode 100644
index 0000000000..c55a2845f8
--- /dev/null
+++ b/indra/media_plugins/winmmshim/winmm.def
@@ -0,0 +1,218 @@
+;
+; winmm.def
+;
+; Exports for WINMM DLL
+;
+
+LIBRARY WINMM
+EXPORTS
+CloseDriver
+DefDriverProc
+DriverCallback
+DrvGetModuleHandle
+GetDriverModuleHandle
+;MigrateAllDrivers
+;MigrateSoundEvents
+;NotifyCallbackData
+OpenDriver
+;PlaySound
+PlaySoundA
+PlaySoundW
+SendDriverMessage
+;WOW32DriverCallback
+;WOW32ResolveMultiMediaHandle
+;WOWAppExit
+;WinmmLogoff
+;WinmmLogon
+;aux32Message
+auxGetDevCapsA
+auxGetDevCapsW
+auxGetNumDevs
+auxGetVolume
+auxOutMessage
+auxSetVolume
+;gfxAddGfx
+;gfxBatchChange
+;gfxCreateGfxFactoriesList
+;gfxCreateZoneFactoriesList
+;gfxDestroyDeviceInterfaceList
+;gfxEnumerateGfxs
+;gfxLogoff
+;gfxLogon
+;gfxModifyGfx
+;gfxOpenGfx
+;gfxRemoveGfx
+;joy32Message
+joyConfigChanged
+joyGetDevCapsA
+joyGetDevCapsW
+joyGetNumDevs
+joyGetPos
+joyGetPosEx
+joyGetThreshold
+joyReleaseCapture
+joySetCapture
+joySetThreshold
+;mci32Message
+mciDriverNotify
+mciDriverYield
+mciExecute
+mciFreeCommandResource
+mciGetCreatorTask
+mciGetDeviceIDA
+mciGetDeviceIDFromElementIDA
+mciGetDeviceIDFromElementIDW
+mciGetDeviceIDW
+mciGetDriverData
+mciGetErrorStringA
+mciGetErrorStringW
+mciGetYieldProc
+mciLoadCommandResource
+mciSendCommandA
+mciSendCommandW
+mciSendStringA
+mciSendStringW
+mciSetDriverData
+mciSetYieldProc
+;mid32Message
+midiConnect
+midiDisconnect
+midiInAddBuffer
+midiInClose
+midiInGetDevCapsA
+midiInGetDevCapsW
+midiInGetErrorTextA
+midiInGetErrorTextW
+midiInGetID
+midiInGetNumDevs
+midiInMessage
+midiInOpen
+midiInPrepareHeader
+midiInReset
+midiInStart
+midiInStop
+midiInUnprepareHeader
+midiOutCacheDrumPatches
+midiOutCachePatches
+midiOutClose
+midiOutGetDevCapsA
+midiOutGetDevCapsW
+midiOutGetErrorTextA
+midiOutGetErrorTextW
+midiOutGetID
+midiOutGetNumDevs
+midiOutGetVolume
+midiOutLongMsg
+midiOutMessage
+midiOutOpen
+midiOutPrepareHeader
+midiOutReset
+midiOutSetVolume
+midiOutShortMsg
+midiOutUnprepareHeader
+midiStreamClose
+midiStreamOpen
+midiStreamOut
+midiStreamPause
+midiStreamPosition
+midiStreamProperty
+midiStreamRestart
+midiStreamStop
+mixerClose
+mixerGetControlDetailsA
+mixerGetControlDetailsW
+mixerGetDevCapsA
+mixerGetDevCapsW
+mixerGetID
+mixerGetLineControlsA
+mixerGetLineControlsW
+mixerGetLineInfoA
+mixerGetLineInfoW
+mixerGetNumDevs
+mixerMessage
+mixerOpen
+mixerSetControlDetails
+;mmDrvInstall
+mmGetCurrentTask
+mmTaskBlock
+mmTaskCreate
+mmTaskSignal
+mmTaskYield
+mmioAdvance
+mmioAscend
+mmioClose
+mmioCreateChunk
+mmioDescend
+mmioFlush
+mmioGetInfo
+mmioInstallIOProcA
+mmioInstallIOProcW
+mmioOpenA
+mmioOpenW
+mmioRead
+mmioRenameA
+mmioRenameW
+mmioSeek
+mmioSendMessage
+mmioSetBuffer
+mmioSetInfo
+mmioStringToFOURCCA
+mmioStringToFOURCCW
+mmioWrite
+mmsystemGetVersion
+;mod32Message
+;mxd32Message
+sndPlaySoundA
+sndPlaySoundW
+;tid32Message
+timeBeginPeriod
+timeEndPeriod
+timeGetDevCaps
+timeGetSystemTime
+timeGetTime
+timeKillEvent
+timeSetEvent
+waveInAddBuffer
+waveInClose
+waveInGetDevCapsA
+waveInGetDevCapsW
+waveInGetErrorTextA
+waveInGetErrorTextW
+waveInGetID
+waveInGetNumDevs
+waveInGetPosition
+waveInMessage
+waveInOpen
+waveInPrepareHeader
+waveInReset
+waveInStart
+waveInStop
+waveInUnprepareHeader
+waveOutBreakLoop
+waveOutClose
+waveOutGetDevCapsA
+waveOutGetDevCapsW
+waveOutGetErrorTextA
+waveOutGetErrorTextW
+waveOutGetID
+waveOutGetNumDevs
+waveOutGetPitch
+waveOutGetPlaybackRate
+waveOutGetPosition
+waveOutGetVolume
+waveOutMessage
+waveOutOpen
+waveOutPause
+waveOutPrepareHeader
+waveOutReset
+waveOutRestart
+waveOutSetPitch
+waveOutSetPlaybackRate
+waveOutSetVolume
+waveOutUnprepareHeader
+waveOutWrite
+;wid32Message
+;winmmSetDebugLevel
+;wod32Message
+setPluginVolume
+setPluginMute \ No newline at end of file
diff --git a/indra/media_plugins/winmmshim/winmm_shim.cpp b/indra/media_plugins/winmmshim/winmm_shim.cpp
new file mode 100644
index 0000000000..9563a3b664
--- /dev/null
+++ b/indra/media_plugins/winmmshim/winmm_shim.cpp
@@ -0,0 +1,188 @@
+/**
+ * @file winmmshim.cpp
+ * @brief controls volume level of process by intercepting calls to winmm.dll
+ *
+ * $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$
+ */
+#include "forwarding_api.h"
+#include <xmmintrin.h>
+#include <map>
+#include <math.h>
+
+using std::wstring;
+
+static float sVolumeLevel = 1.f;
+static bool sMute = false;
+
+BOOL APIENTRY DllMain( HMODULE hModule,
+ DWORD ul_reason_for_call,
+ LPVOID lpReserved
+ )
+{
+ static bool initialized = false;
+ // do this only once
+ if (!initialized)
+ { // bind to original winmm.dll
+ TCHAR system_path[MAX_PATH];
+ TCHAR dll_path[MAX_PATH];
+ ::GetSystemDirectory(system_path, MAX_PATH);
+
+ // grab winmm.dll from system path, where it should live
+ wsprintf(dll_path, "%s\\winmm.dll", system_path);
+ HMODULE winmm_handle = ::LoadLibrary(dll_path);
+
+ if (winmm_handle != NULL)
+ { // we have a dll, let's get out pointers!
+ initialized = true;
+ init_function_pointers(winmm_handle);
+ return true;
+ }
+
+ // failed to initialize real winmm.dll
+ return false;
+ }
+ return true;
+}
+
+
+extern "C"
+{
+ // tracks the requested format for a given waveout buffer
+ struct WaveOutFormat
+ {
+ WaveOutFormat(int bits_per_sample)
+ : mBitsPerSample(bits_per_sample)
+ {}
+ int mBitsPerSample;
+ };
+ typedef std::map<HWAVEOUT, WaveOutFormat*> wave_out_map_t;
+ static wave_out_map_t sWaveOuts;
+
+ MMRESULT WINAPI waveOutOpen( LPHWAVEOUT phwo, UINT uDeviceID, LPCWAVEFORMATEX pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen)
+ {
+ if (pwfx->wFormatTag != WAVE_FORMAT_PCM
+ || (pwfx->wBitsPerSample != 8 && pwfx->wBitsPerSample != 16))
+ { // uncompressed 8 and 16 bit sound are the only types we support
+ return WAVERR_BADFORMAT;
+ }
+
+ MMRESULT result = waveOutOpen_orig(phwo, uDeviceID, pwfx, dwCallback, dwInstance, fdwOpen);
+ if (result == MMSYSERR_NOERROR
+ && ((fdwOpen & WAVE_FORMAT_QUERY) == 0)) // not just querying for format support
+ { // remember the requested bits per sample, and associate with the given handle
+ WaveOutFormat* wave_outp = new WaveOutFormat(pwfx->wBitsPerSample);
+ sWaveOuts.insert(std::make_pair(*phwo, wave_outp));
+ }
+ return result;
+ }
+
+ MMRESULT WINAPI waveOutClose( HWAVEOUT hwo)
+ {
+ wave_out_map_t::iterator found_it = sWaveOuts.find(hwo);
+ if (found_it != sWaveOuts.end())
+ { // forget what we know about this handle
+ delete found_it->second;
+ sWaveOuts.erase(found_it);
+ }
+ return waveOutClose_orig( hwo);
+ }
+
+ MMRESULT WINAPI waveOutWrite( HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh)
+ {
+ MMRESULT result = MMSYSERR_NOERROR;
+
+ if (sMute)
+ { // zero out the audio buffer when muted
+ memset(pwh->lpData, 0, pwh->dwBufferLength);
+ }
+ else if (sVolumeLevel != 1.f)
+ { // need to apply volume level
+ wave_out_map_t::iterator found_it = sWaveOuts.find(hwo);
+ if (found_it != sWaveOuts.end())
+ {
+ WaveOutFormat* formatp = found_it->second;
+ switch (formatp->mBitsPerSample){
+ case 8:
+ {
+ char volume = (char)(sVolumeLevel * 127.f);
+ for (unsigned int i = 0; i < pwh->dwBufferLength; i++)
+ {
+ // unsigned multiply doesn't use most significant bit, so shift by 7 bits
+ // to get resulting value back into 8 bits
+ pwh->lpData[i] = (pwh->lpData[i] * volume) >> 7;
+ }
+ break;
+ }
+ case 16:
+ {
+ short volume_16 = (short)(sVolumeLevel * 32767.f);
+
+ // copy volume level 4 times into 64 bit MMX register
+ __m64 volume_64 = _mm_set_pi16(volume_16, volume_16, volume_16, volume_16);
+ __m64* sample_64;
+ __m64* last_sample_64 = (__m64*)(pwh->lpData + pwh->dwBufferLength - sizeof(__m64));
+ // for everything that can be addressed in 64 bit multiples...
+ for (sample_64 = (__m64*)pwh->lpData;
+ sample_64 <= last_sample_64;
+ ++sample_64)
+ {
+ //...multiply the samples by the volume...
+ __m64 scaled_sample = _mm_mulhi_pi16(*sample_64, volume_64);
+ // ...and shift left 1 bit since an unsigned multiple loses the most significant bit
+ // 0x7FFF * 0x7FFF = 0x3fff0001
+ // 0x3fff0001 << 1 = 0x7ffe0002
+ // notice that the LSB is always 0...should consider dithering
+ *sample_64 = _mm_slli_pi16(scaled_sample, 1);
+ }
+
+ // the captain has turned off the MMX sign, you are now free to use floating point registers
+ _mm_empty();
+
+ // finish remaining samples that didn't fit into 64 bit register
+ for (short* sample_16 = (short*)sample_64;
+ sample_16 < (short*)(pwh->lpData + pwh->dwBufferLength);
+ ++sample_16)
+ {
+ *sample_16 = (*sample_16 * volume_16) >> 15;
+ }
+
+ break;
+ }
+ default:
+ // don't do anything
+ break;
+ }
+ }
+ }
+ return waveOutWrite_orig( hwo, pwh, cbwh);
+ }
+
+ void WINAPI setPluginVolume(float volume)
+ {
+ sVolumeLevel = volume;
+ }
+
+ void WINAPI setPluginMute(bool mute)
+ {
+ sMute = mute;
+ }
+}