diff options
Diffstat (limited to 'indra/media_plugins')
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, µ, &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; + } +} |