From 3069d8886241b2d9deaadfbf8823d463b469ec6b Mon Sep 17 00:00:00 2001 From: Armin Weatherwax Date: Mon, 14 Jun 2010 16:27:25 +0200 Subject: update to latest SG2 media_plugins --- linden/indra/media_plugins/base/CMakeLists.txt | 4 +- .../indra/media_plugins/base/media_plugin_base.cpp | 5 +- .../indra/media_plugins/base/media_plugin_base.h | 5 +- linden/indra/media_plugins/example/CMakeLists.txt | 6 +- .../media_plugins/example/media_plugin_example.cpp | 31 +- .../media_plugins/gstreamer010/CMakeLists.txt | 15 +- .../gstreamer010/llmediaimplgstreamer.h | 5 +- .../gstreamer010/llmediaimplgstreamer_syms.cpp | 5 +- .../gstreamer010/llmediaimplgstreamer_syms.h | 5 +- .../llmediaimplgstreamertriviallogging.h | 5 +- .../gstreamer010/llmediaimplgstreamervidplug.cpp | 25 +- .../gstreamer010/llmediaimplgstreamervidplug.h | 5 +- .../gstreamer010/media_plugin_gstreamer010.cpp | 63 +-- .../quicktime/media_plugin_quicktime.cpp | 71 +-- linden/indra/media_plugins/webkit/CMakeLists.txt | 10 +- .../media_plugins/webkit/linux_volume_catcher.cpp | 490 +++++++++++++++++++++ .../media_plugins/webkit/linux_volume_catcher.h | 56 +++ .../webkit/linux_volume_catcher_pa_syms.inc | 21 + .../webkit/linux_volume_catcher_paglib_syms.inc | 6 + .../media_plugins/webkit/media_plugin_webkit.cpp | 352 ++++++++++----- 20 files changed, 956 insertions(+), 229 deletions(-) create mode 100755 linden/indra/media_plugins/webkit/linux_volume_catcher.cpp create mode 100755 linden/indra/media_plugins/webkit/linux_volume_catcher.h create mode 100755 linden/indra/media_plugins/webkit/linux_volume_catcher_pa_syms.inc create mode 100755 linden/indra/media_plugins/webkit/linux_volume_catcher_paglib_syms.inc (limited to 'linden') diff --git a/linden/indra/media_plugins/base/CMakeLists.txt b/linden/indra/media_plugins/base/CMakeLists.txt index a3ee02e..8d62043 100755 --- a/linden/indra/media_plugins/base/CMakeLists.txt +++ b/linden/indra/media_plugins/base/CMakeLists.txt @@ -25,13 +25,13 @@ include_directories( ### media_plugin_base -if(WORD_SIZE EQUAL 64) +if(NOT CMAKE_SIZEOF_VOID_P MATCHES 4) if(WINDOWS) add_definitions(/FIXED:NO) else(WINDOWS) # not windows therefore gcc LINUX and DARWIN add_definitions(-fPIC) endif(WINDOWS) -endif (WORD_SIZE EQUAL 64) +endif (NOT CMAKE_SIZEOF_VOID_P MATCHES 4) set(media_plugin_base_SOURCE_FILES media_plugin_base.cpp diff --git a/linden/indra/media_plugins/base/media_plugin_base.cpp b/linden/indra/media_plugins/base/media_plugin_base.cpp index baae68c..47acdfd 100755 --- a/linden/indra/media_plugins/base/media_plugin_base.cpp +++ b/linden/indra/media_plugins/base/media_plugin_base.cpp @@ -15,13 +15,13 @@ * ("GPL"), unless you have obtained a separate licensing agreement * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * online at http://secondlife.com/developers/opensource/gplv2 * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * http://secondlife.com/developers/opensource/flossexception * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, @@ -31,6 +31,7 @@ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ + * * @endcond */ diff --git a/linden/indra/media_plugins/base/media_plugin_base.h b/linden/indra/media_plugins/base/media_plugin_base.h index 8311e66..efb0629 100755 --- a/linden/indra/media_plugins/base/media_plugin_base.h +++ b/linden/indra/media_plugins/base/media_plugin_base.h @@ -13,13 +13,13 @@ * ("GPL"), unless you have obtained a separate licensing agreement * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * online at http://secondlife.com/developers/opensource/gplv2 * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * http://secondlife.com/developers/opensource/flossexception * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, @@ -29,6 +29,7 @@ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ + * * @endcond */ diff --git a/linden/indra/media_plugins/example/CMakeLists.txt b/linden/indra/media_plugins/example/CMakeLists.txt index 6d14c1b..7822300 100755 --- a/linden/indra/media_plugins/example/CMakeLists.txt +++ b/linden/indra/media_plugins/example/CMakeLists.txt @@ -14,7 +14,7 @@ include(PluginAPI) include(MediaPluginBase) include(FindOpenGL) -#awfixme include(ExamplePlugin) +#include(ExamplePlugin) include_directories( ${LLPLUGIN_INCLUDE_DIRS} @@ -29,13 +29,13 @@ include_directories( ### media_plugin_example -if(WORD_SIZE EQUAL 64) +if(NOT CMAKE_SIZEOF_VOID_P MATCHES 4) if(WINDOWS) add_definitions(/FIXED:NO) else(WINDOWS) # not windows therefore gcc LINUX and DARWIN add_definitions(-fPIC) endif(WINDOWS) -endif (WORD_SIZE EQUAL 64) +endif (NOT CMAKE_SIZEOF_VOID_P MATCHES 4) set(media_plugin_example_SOURCE_FILES media_plugin_example.cpp diff --git a/linden/indra/media_plugins/example/media_plugin_example.cpp b/linden/indra/media_plugins/example/media_plugin_example.cpp index 83abae0..1eabe6c 100755 --- a/linden/indra/media_plugins/example/media_plugin_example.cpp +++ b/linden/indra/media_plugins/example/media_plugin_example.cpp @@ -13,13 +13,13 @@ * ("GPL"), unless you have obtained a separate licensing agreement * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * online at http://secondlife.com/developers/opensource/gplv2 * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * http://secondlife.com/developers/opensource/flossexception * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, @@ -29,6 +29,7 @@ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ + * * @endcond */ @@ -120,17 +121,6 @@ void MediaPluginExample::receiveMessage( const char* message_string ) std::string plugin_version = "Example media plugin, Example Version 1.0.0.0"; message.setValue( "plugin_version", plugin_version ); sendMessage( message ); - - // Plugin gets to decide the texture parameters to use. - message.setMessage( 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 == "idle" ) @@ -192,7 +182,20 @@ void MediaPluginExample::receiveMessage( const char* message_string ) else if ( message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA ) { - if ( message_name == "size_change" ) + 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" ); diff --git a/linden/indra/media_plugins/gstreamer010/CMakeLists.txt b/linden/indra/media_plugins/gstreamer010/CMakeLists.txt index 3b73e04..ee16849 100755 --- a/linden/indra/media_plugins/gstreamer010/CMakeLists.txt +++ b/linden/indra/media_plugins/gstreamer010/CMakeLists.txt @@ -30,6 +30,14 @@ include_directories( ### media_plugin_gstreamer010 +if(NOT CMAKE_SIZEOF_VOID_P MATCHES 4) + if(WINDOWS) + add_definitions(/FIXED:NO) + else(WINDOWS) # not windows therefore gcc LINUX and DARWIN + add_definitions(-fPIC) + endif(WINDOWS) +endif (NOT CMAKE_SIZEOF_VOID_P MATCHES 4) + set(media_plugin_gstreamer010_SOURCE_FILES media_plugin_gstreamer010.cpp llmediaimplgstreamer_syms.cpp @@ -42,13 +50,6 @@ set(media_plugin_gstreamer010_HEADER_FILES llmediaimplgstreamertriviallogging.h ) -if (${CXX_VERSION_NUMBER} MATCHES "4[23].") - # Work around a bad interaction between broken gstreamer headers and - # g++ 4.3's increased strictness. - set_source_files_properties(llmediaimplgstreamervidplug.cpp PROPERTIES - COMPILE_FLAGS -Wno-write-strings) -endif (${CXX_VERSION_NUMBER} MATCHES "4[23].") - add_library(media_plugin_gstreamer010 SHARED ${media_plugin_gstreamer010_SOURCE_FILES} diff --git a/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamer.h b/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamer.h index 6920c3b..bfc443b 100755 --- a/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamer.h +++ b/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamer.h @@ -14,13 +14,13 @@ * ("GPL"), unless you have obtained a separate licensing agreement * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * online at http://secondlife.com/developers/opensource/gplv2 * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * http://secondlife.com/developers/opensource/flossexception * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, @@ -30,6 +30,7 @@ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ + * * @endcond */ diff --git a/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms.cpp b/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms.cpp index 28960ac..82978ad 100755 --- a/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms.cpp +++ b/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms.cpp @@ -13,13 +13,13 @@ * ("GPL"), unless you have obtained a separate licensing agreement * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * online at http://secondlife.com/developers/opensource/gplv2 * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * http://secondlife.com/developers/opensource/flossexception * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, @@ -29,6 +29,7 @@ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ + * * @endcond */ diff --git a/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms.h b/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms.h index 7955898..c309043 100755 --- a/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms.h +++ b/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms.h @@ -13,13 +13,13 @@ * ("GPL"), unless you have obtained a separate licensing agreement * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * online at http://secondlife.com/developers/opensource/gplv2 * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * http://secondlife.com/developers/opensource/flossexception * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, @@ -29,6 +29,7 @@ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ + * * @endcond */ diff --git a/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamertriviallogging.h b/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamertriviallogging.h index 27f0eed..f5da637 100755 --- a/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamertriviallogging.h +++ b/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamertriviallogging.h @@ -13,13 +13,13 @@ * ("GPL"), unless you have obtained a separate licensing agreement * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * online at http://secondlife.com/developers/opensource/gplv2 * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * http://secondlife.com/developers/opensource/flossexception * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, @@ -29,6 +29,7 @@ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ + * * @endcond */ diff --git a/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.cpp b/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.cpp index 2b10a2a..9242830 100755 --- a/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.cpp +++ b/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.cpp @@ -13,13 +13,13 @@ * ("GPL"), unless you have obtained a separate licensing agreement * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * online at http://secondlife.com/developers/opensource/gplv2 * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * http://secondlife.com/developers/opensource/flossexception * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, @@ -29,6 +29,7 @@ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ + * * @endcond */ @@ -54,7 +55,7 @@ GST_DEBUG_CATEGORY_STATIC (gst_slvideo_debug); #define SLV_ALLCAPS GST_VIDEO_CAPS_RGBx SLV_SIZECAPS static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ( - "sink", + (gchar*)"sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS (SLV_ALLCAPS) @@ -514,18 +515,18 @@ plugin_init (GstPlugin * plugin) some g++ versions buggily avoid __attribute__((constructor)) functions - so we provide an explicit plugin init function. */ -void gst_slvideo_init_class (void) -{ -#define PACKAGE "packagehack" +#define PACKAGE (gchar*)"packagehack" // this macro quietly refers to PACKAGE internally - static GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, - "private-slvideoplugin", - "SL Video sink plugin", - plugin_init, "0.1", GST_LICENSE_UNKNOWN, - "Second Life", - "http://www.secondlife.com/"); + (gchar*)"private-slvideoplugin", + (gchar*)"SL Video sink plugin", + plugin_init, (gchar*)"0.1", (gchar*)GST_LICENSE_UNKNOWN, + (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"); } diff --git a/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.h b/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.h index fad80cb..56f6db9 100755 --- a/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.h +++ b/linden/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.h @@ -13,13 +13,13 @@ * ("GPL"), unless you have obtained a separate licensing agreement * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * online at http://secondlife.com/developers/opensource/gplv2 * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * http://secondlife.com/developers/opensource/flossexception * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, @@ -29,6 +29,7 @@ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ + * * @endcond */ diff --git a/linden/indra/media_plugins/gstreamer010/media_plugin_gstreamer010.cpp b/linden/indra/media_plugins/gstreamer010/media_plugin_gstreamer010.cpp index 82e6fdc..7d34a1e 100755 --- a/linden/indra/media_plugins/gstreamer010/media_plugin_gstreamer010.cpp +++ b/linden/indra/media_plugins/gstreamer010/media_plugin_gstreamer010.cpp @@ -13,13 +13,13 @@ * ("GPL"), unless you have obtained a separate licensing agreement * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * online at http://secondlife.com/developers/opensource/gplv2 * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * http://secondlife.com/developers/opensource/flossexception * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, @@ -29,6 +29,7 @@ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ + * * @endcond */ @@ -947,33 +948,6 @@ void MediaPluginGStreamer010::receiveMessage(const char *message_string) message.setValue("plugin_version", getVersion()); sendMessage(message); - - // Plugin gets to decide the texture parameters to use. - message.setMessage(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 == "idle") { @@ -1038,7 +1012,36 @@ void MediaPluginGStreamer010::receiveMessage(const char *message_string) } else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA) { - if(message_name == "size_change") + 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"); diff --git a/linden/indra/media_plugins/quicktime/media_plugin_quicktime.cpp b/linden/indra/media_plugins/quicktime/media_plugin_quicktime.cpp index 9b4c531..68b9679 100755 --- a/linden/indra/media_plugins/quicktime/media_plugin_quicktime.cpp +++ b/linden/indra/media_plugins/quicktime/media_plugin_quicktime.cpp @@ -13,13 +13,13 @@ * ("GPL"), unless you have obtained a separate licensing agreement * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * online at http://secondlife.com/developers/opensource/gplv2 * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * http://secondlife.com/developers/opensource/flossexception * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, @@ -29,6 +29,7 @@ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ + * * @endcond */ @@ -860,36 +861,6 @@ void MediaPluginQuickTime::receiveMessage(const char *message_string) plugin_version += codec.str(); message.setValue("plugin_version", plugin_version); sendMessage(message); - - // Plugin gets to decide the texture parameters to use. - message.setMessage(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 == "idle") { @@ -954,7 +925,41 @@ void MediaPluginQuickTime::receiveMessage(const char *message_string) } else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA) { - if(message_name == "size_change") + 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"); diff --git a/linden/indra/media_plugins/webkit/CMakeLists.txt b/linden/indra/media_plugins/webkit/CMakeLists.txt index 1c999ba..be02781 100644 --- a/linden/indra/media_plugins/webkit/CMakeLists.txt +++ b/linden/indra/media_plugins/webkit/CMakeLists.txt @@ -14,10 +14,12 @@ include(Linking) include(PluginAPI) include(MediaPluginBase) include(FindOpenGL) +#include(PulseAudio) include(WebKitLibPlugin) include_directories( + ${PULSEAUDIO_INCLUDE_DIRS} ${LLPLUGIN_INCLUDE_DIRS} ${MEDIA_PLUGIN_BASE_INCLUDE_DIRS} ${LLCOMMON_INCLUDE_DIRS} @@ -35,13 +37,13 @@ set(media_plugin_webkit_SOURCE_FILES media_plugin_webkit.cpp ) -if(WORD_SIZE EQUAL 64) +if(NOT CMAKE_SIZEOF_VOID_P MATCHES 4) if(WINDOWS) add_definitions(/FIXED:NO) else(WINDOWS) # not windows therefore gcc LINUX and DARWIN add_definitions(-fPIC) endif(WINDOWS) -endif (WORD_SIZE EQUAL 64) +endif (NOT CMAKE_SIZEOF_VOID_P MATCHES 4) set(media_plugin_webkit_LINK_LIBRARIES ${LLPLUGIN_LIBRARIES} @@ -49,9 +51,11 @@ set(media_plugin_webkit_LINK_LIBRARIES ${LLCOMMON_LIBRARIES} ${WEBKIT_PLUGIN_LIBRARIES} ${PLUGIN_API_WINDOWS_LIBRARIES} + ${PULSEAUDIO_LIBRARIES} ) -if(LINUX) +if (LINUX) + list(APPEND media_plugin_webkit_SOURCE_FILES linux_volume_catcher.cpp) list(APPEND media_plugin_webkit_LINK_LIBRARIES ${UI_LIBRARIES} # for glib/GTK ) diff --git a/linden/indra/media_plugins/webkit/linux_volume_catcher.cpp b/linden/indra/media_plugins/webkit/linux_volume_catcher.cpp new file mode 100755 index 0000000..15a2dfb --- /dev/null +++ b/linden/indra/media_plugins/webkit/linux_volume_catcher.cpp @@ -0,0 +1,490 @@ +/** + * @file linux_volume_catcher.cpp + * @brief A Linux-specific, PulseAudio-specific hack to detect and volume-adjust new audio sources + * + * @cond + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 2010, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + * + * @endcond + */ + +/* + The high-level design is as follows: + 1) Connect to the PulseAudio daemon + 2) Watch for the creation of new audio players connecting to the daemon (this includes ALSA clients running on the PulseAudio emulation layer, such as Flash plugins) + 3) Examine any new audio player's PID to see if it belongs to our own process + 4) If so, tell PA to adjust the volume of that audio player ('sink input' in PA parlance) + 5) Keep a list of all living audio players that we care about, adjust the volumes of all of them when we get a new setVolume() call + */ + +#include "linden_common.h" + +#include "linux_volume_catcher.h" + + +#if LL_PULSEAUDIO_ENABLED + +extern "C" { +#include + +#include +#include +#include +#include // There's no special reason why we want the *glib* PA mainloop, but the generic polling implementation seems broken. + +#include "apr_pools.h" +#include "apr_dso.h" +} + +//////////////////////////////////////////////////// + +#define DEBUGMSG(...) do {} while(0) +#define INFOMSG(...) do {} while(0) +#define WARNMSG(...) do {} while(0) + +#define LL_PA_SYM(REQUIRED, PASYM, RTN, ...) RTN (*ll##PASYM)(__VA_ARGS__) = NULL +#include "linux_volume_catcher_pa_syms.inc" +#include "linux_volume_catcher_paglib_syms.inc" +#undef LL_PA_SYM + +static bool sSymsGrabbed = false; +static apr_pool_t *sSymPADSOMemoryPool = NULL; +static apr_dso_handle_t *sSymPADSOHandleG = NULL; + +bool grab_pa_syms(std::string pulse_dso_name) +{ + if (sSymsGrabbed) + { + // already have grabbed good syms + return true; + } + + bool sym_error = false; + bool rtn = false; + apr_status_t rv; + apr_dso_handle_t *sSymPADSOHandle = NULL; + +#define LL_PA_SYM(REQUIRED, PASYM, RTN, ...) do{rv = apr_dso_sym((apr_dso_handle_sym_t*)&ll##PASYM, sSymPADSOHandle, #PASYM); if (rv != APR_SUCCESS) {INFOMSG("Failed to grab symbol: %s", #PASYM); if (REQUIRED) sym_error = true;} else DEBUGMSG("grabbed symbol: %s from %p", #PASYM, (void*)ll##PASYM);}while(0) + + //attempt to load the shared library + apr_pool_create(&sSymPADSOMemoryPool, NULL); + + if ( APR_SUCCESS == (rv = apr_dso_load(&sSymPADSOHandle, + pulse_dso_name.c_str(), + sSymPADSOMemoryPool) )) + { + INFOMSG("Found DSO: %s", pulse_dso_name.c_str()); + +#include "linux_volume_catcher_pa_syms.inc" +#include "linux_volume_catcher_paglib_syms.inc" + + if ( sSymPADSOHandle ) + { + sSymPADSOHandleG = sSymPADSOHandle; + sSymPADSOHandle = NULL; + } + + rtn = !sym_error; + } + else + { + INFOMSG("Couldn't load DSO: %s", pulse_dso_name.c_str()); + rtn = false; // failure + } + + if (sym_error) + { + WARNMSG("Failed to find necessary symbols in PulseAudio libraries."); + } +#undef LL_PA_SYM + + sSymsGrabbed = rtn; + return rtn; +} + + +void ungrab_pa_syms() +{ + // should be safe to call regardless of whether we've + // actually grabbed syms. + + if ( sSymPADSOHandleG ) + { + apr_dso_unload(sSymPADSOHandleG); + sSymPADSOHandleG = NULL; + } + + if ( sSymPADSOMemoryPool ) + { + apr_pool_destroy(sSymPADSOMemoryPool); + sSymPADSOMemoryPool = NULL; + } + + // NULL-out all of the symbols we'd grabbed +#define LL_PA_SYM(REQUIRED, PASYM, RTN, ...) do{ll##PASYM = NULL;}while(0) +#include "linux_volume_catcher_pa_syms.inc" +#include "linux_volume_catcher_paglib_syms.inc" +#undef LL_PA_SYM + + sSymsGrabbed = false; +} +//////////////////////////////////////////////////// + +// PulseAudio requires a chain of callbacks with C linkage +extern "C" { + void callback_discovered_sinkinput(pa_context *context, const pa_sink_input_info *i, int eol, void *userdata); + void callback_subscription_alert(pa_context *context, pa_subscription_event_type_t t, uint32_t index, void *userdata); + void callback_context_state(pa_context *context, void *userdata); +} + + +class LinuxVolumeCatcherImpl +{ +public: + LinuxVolumeCatcherImpl(); + ~LinuxVolumeCatcherImpl(); + + void setVolume(F32 volume); + void pump(void); + + // for internal use - can't be private because used from our C callbacks + + bool loadsyms(std::string pulse_dso_name); + void init(); + void cleanup(); + + void update_all_volumes(F32 volume); + void update_index_volume(U32 index, F32 volume); + void connected_okay(); + + std::set mSinkInputIndices; + std::map mSinkInputNumChannels; + F32 mDesiredVolume; + pa_glib_mainloop *mMainloop; + pa_context *mPAContext; + bool mConnected; + bool mGotSyms; +}; + +LinuxVolumeCatcherImpl::LinuxVolumeCatcherImpl() + : mDesiredVolume(0.0f), + mMainloop(NULL), + mPAContext(NULL), + mConnected(false), + mGotSyms(false) +{ + init(); +} + +LinuxVolumeCatcherImpl::~LinuxVolumeCatcherImpl() +{ + cleanup(); +} + +bool LinuxVolumeCatcherImpl::loadsyms(std::string pulse_dso_name) +{ + return grab_pa_syms(pulse_dso_name); +} + +void LinuxVolumeCatcherImpl::init() +{ + // try to be as defensive as possible because PA's interface is a + // bit fragile and (for our purposes) we'd rather simply not function + // than crash + + // we cheat and rely upon libpulse-mainloop-glib.so.0 to pull-in + // libpulse.so.0 - this isn't a great assumption, and the two DSOs should + // probably be loaded separately. Our Linux DSO framework needs refactoring, + // we do this sort of thing a lot with practically identical logic... + mGotSyms = loadsyms("libpulse-mainloop-glib.so.0"); + if (!mGotSyms) return; + + mMainloop = llpa_glib_mainloop_new(g_main_context_default()); + if (mMainloop) + { + pa_mainloop_api *api = llpa_glib_mainloop_get_api(mMainloop); + if (api) + { + pa_proplist *proplist = llpa_proplist_new(); + if (proplist) + { + llpa_proplist_sets(proplist, PA_PROP_APPLICATION_ICON_NAME, "multimedia-player"); + llpa_proplist_sets(proplist, PA_PROP_APPLICATION_ID, "com.secondlife.viewer.mediaplugvoladjust"); + llpa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, "SL Plugin Volume Adjuster"); + llpa_proplist_sets(proplist, PA_PROP_APPLICATION_VERSION, "1"); + + // plain old pa_context_new() is broken! + mPAContext = llpa_context_new_with_proplist(api, NULL, proplist); + llpa_proplist_free(proplist); + } + } + } + + // Now we've set up a PA context and mainloop, try connecting the + // PA context to a PA daemon. + if (mPAContext) + { + llpa_context_set_state_callback(mPAContext, callback_context_state, this); + pa_context_flags_t cflags = (pa_context_flags)0; // maybe add PA_CONTEXT_NOAUTOSPAWN? + if (llpa_context_connect(mPAContext, NULL, cflags, NULL) >= 0) + { + // Okay! We haven't definitely connected, but we + // haven't definitely failed yet. + } + else + { + // Failed to connect to PA manager... we'll leave + // things like that. Perhaps we should try again later. + } + } +} + +void LinuxVolumeCatcherImpl::cleanup() +{ + mConnected = false; + + if (mGotSyms && mPAContext) + { + llpa_context_disconnect(mPAContext); + llpa_context_unref(mPAContext); + } + mPAContext = NULL; + + if (mGotSyms && mMainloop) + { + llpa_glib_mainloop_free(mMainloop); + } + mMainloop = NULL; +} + +void LinuxVolumeCatcherImpl::setVolume(F32 volume) +{ + mDesiredVolume = volume; + + if (!mGotSyms) return; + + if (mConnected && mPAContext) + { + update_all_volumes(mDesiredVolume); + } + + pump(); +} + +void LinuxVolumeCatcherImpl::pump() +{ + gboolean may_block = FALSE; + g_main_context_iteration(g_main_context_default(), may_block); +} + +void LinuxVolumeCatcherImpl::connected_okay() +{ + pa_operation *op; + + // fetch global list of existing sinkinputs + if ((op = llpa_context_get_sink_input_info_list(mPAContext, + callback_discovered_sinkinput, + this))) + { + llpa_operation_unref(op); + } + + // subscribe to future global sinkinput changes + llpa_context_set_subscribe_callback(mPAContext, + callback_subscription_alert, + this); + if ((op = llpa_context_subscribe(mPAContext, (pa_subscription_mask_t) + (PA_SUBSCRIPTION_MASK_SINK_INPUT), + NULL, NULL))) + { + llpa_operation_unref(op); + } +} + +void LinuxVolumeCatcherImpl::update_all_volumes(F32 volume) +{ + for (std::set::iterator it = mSinkInputIndices.begin(); + it != mSinkInputIndices.end(); ++it) + { + update_index_volume(*it, volume); + } +} + +void LinuxVolumeCatcherImpl::update_index_volume(U32 index, F32 volume) +{ + static pa_cvolume cvol; + llpa_cvolume_set(&cvol, mSinkInputNumChannels[index], + llpa_sw_volume_from_linear(volume)); + + pa_context *c = mPAContext; + uint32_t idx = index; + const pa_cvolume *cvolumep = &cvol; + pa_context_success_cb_t cb = NULL; // okay as null + void *userdata = NULL; // okay as null + + pa_operation *op; + if ((op = llpa_context_set_sink_input_volume(c, idx, cvolumep, cb, userdata))) + { + llpa_operation_unref(op); + } +} + + +void callback_discovered_sinkinput(pa_context *context, const pa_sink_input_info *sii, int eol, void *userdata) +{ + LinuxVolumeCatcherImpl *impl = dynamic_cast((LinuxVolumeCatcherImpl*)userdata); + llassert(impl); + + if (0 == eol) + { + pa_proplist *proplist = sii->proplist; + pid_t sinkpid = atoll(llpa_proplist_gets(proplist, PA_PROP_APPLICATION_PROCESS_ID)); + + if (sinkpid == getpid()) // does the discovered sinkinput belong to this process? + { + bool is_new = (impl->mSinkInputIndices.find(sii->index) == + impl->mSinkInputIndices.end()); + + impl->mSinkInputIndices.insert(sii->index); + impl->mSinkInputNumChannels[sii->index] = sii->channel_map.channels; + + if (is_new) + { + // new! + impl->update_index_volume(sii->index, impl->mDesiredVolume); + } + else + { + // seen it already, do nothing. + } + } + } +} + +void callback_subscription_alert(pa_context *context, pa_subscription_event_type_t t, uint32_t index, void *userdata) +{ + LinuxVolumeCatcherImpl *impl = dynamic_cast((LinuxVolumeCatcherImpl*)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) +{ + LinuxVolumeCatcherImpl *impl = dynamic_cast((LinuxVolumeCatcherImpl*)userdata); + llassert(impl); + + switch (llpa_context_get_state(context)) + { + case PA_CONTEXT_READY: + impl->mConnected = true; + impl->connected_okay(); + break; + case PA_CONTEXT_TERMINATED: + impl->mConnected = false; + break; + case PA_CONTEXT_FAILED: + impl->mConnected = false; + break; + default:; + } +} + +///////////////////////////////////////////////////// + +LinuxVolumeCatcher::LinuxVolumeCatcher() +{ + pimpl = new LinuxVolumeCatcherImpl(); +} + +LinuxVolumeCatcher::~LinuxVolumeCatcher() +{ + delete pimpl; + pimpl = NULL; +} + +void LinuxVolumeCatcher::setVolume(F32 volume) +{ + llassert(pimpl); + pimpl->setVolume(volume); +} + +void LinuxVolumeCatcher::pump() +{ + llassert(pimpl); + pimpl->pump(); +} + +#else // !LL_PULSEAUDIO_ENABLED + +// stub. + +LinuxVolumeCatcher::LinuxVolumeCatcher() +{ + pimpl = NULL; +} + +LinuxVolumeCatcher::~LinuxVolumeCatcher() +{ +} + +void LinuxVolumeCatcher::setVolume(F32 volume) +{ +} + +void LinuxVolumeCatcher::pump() +{ +} + +#endif // LL_PULSEAUDIO_ENABLED diff --git a/linden/indra/media_plugins/webkit/linux_volume_catcher.h b/linden/indra/media_plugins/webkit/linux_volume_catcher.h new file mode 100755 index 0000000..d4a1b38 --- /dev/null +++ b/linden/indra/media_plugins/webkit/linux_volume_catcher.h @@ -0,0 +1,56 @@ +/** + * @file linux_volume_catcher.h + * @brief A Linux-specific, PulseAudio-specific hack to detect and volume-adjust new audio sources + * + * @cond + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 2010, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + * + * @endcond + */ + +#ifndef LINUX_VOLUME_CATCHER_H +#define LINUX_VOLUME_CATCHER_H + +#include "linden_common.h" + +class LinuxVolumeCatcherImpl; + +class LinuxVolumeCatcher +{ + public: + LinuxVolumeCatcher(); + ~LinuxVolumeCatcher(); + + void setVolume(F32 volume); // 0.0 - 1.0 + 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: + LinuxVolumeCatcherImpl *pimpl; +}; + +#endif // LINUX_VOLUME_CATCHER_H diff --git a/linden/indra/media_plugins/webkit/linux_volume_catcher_pa_syms.inc b/linden/indra/media_plugins/webkit/linux_volume_catcher_pa_syms.inc new file mode 100755 index 0000000..d806b48 --- /dev/null +++ b/linden/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/linden/indra/media_plugins/webkit/linux_volume_catcher_paglib_syms.inc b/linden/indra/media_plugins/webkit/linux_volume_catcher_paglib_syms.inc new file mode 100755 index 0000000..abf628c --- /dev/null +++ b/linden/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/linden/indra/media_plugins/webkit/media_plugin_webkit.cpp b/linden/indra/media_plugins/webkit/media_plugin_webkit.cpp index 2e3f06d..28c6e98 100755 --- a/linden/indra/media_plugins/webkit/media_plugin_webkit.cpp +++ b/linden/indra/media_plugins/webkit/media_plugin_webkit.cpp @@ -13,13 +13,13 @@ * ("GPL"), unless you have obtained a separate licensing agreement * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * online at http://secondlife.com/developers/opensource/gplv2 * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * http://secondlife.com/developers/opensource/flossexception * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, @@ -29,9 +29,10 @@ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ + * * @endcond */ -#include //FIXME: This is included from elsewhere in SG2.0 + #include "llqtwebkit.h" #include "linden_common.h" @@ -44,11 +45,22 @@ #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 +#else +# define LL_QTWEBKIT_USES_PIXMAPS 0 +#endif // LL_LINUX + +#if LL_LINUX +# include "linux_volume_catcher.h" +#endif // LL_LINUX + #if LL_WINDOWS -#include +# include #else -#include -#include +# include +# include #endif #if LL_WINDOWS @@ -78,10 +90,16 @@ public: private: std::string mProfileDir; + std::string mHostLanguage; + std::string mUserAgent; + bool mCookiesEnabled; + bool mJavascriptEnabled; + bool mPluginsEnabled; enum { - INIT_STATE_UNINITIALIZED, // Browser instance hasn't been set up yet + 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 @@ -103,6 +121,10 @@ private: F32 mBackgroundG; F32 mBackgroundB; +#if LL_LINUX + LinuxVolumeCatcher mLinuxVolumeCatcher; +#endif // LL_LINUX + void setInitState(int state) { // std::cerr << "changing init state to " << state << std::endl; @@ -115,6 +137,10 @@ private: { LLQtWebKit::getInstance()->pump( milliseconds ); +#if LL_LINUX + mLinuxVolumeCatcher.pump(); +#endif // LL_LINUX + checkEditState(); if(mInitState == INIT_STATE_NAVIGATE_COMPLETE) @@ -131,7 +157,11 @@ private: { const unsigned char* browser_pixels = LLQtWebKit::getInstance()->grabBrowserWindow( mBrowserWindowId ); - unsigned int buffer_size = LLQtWebKit::getInstance()->getBrowserRowSpan( mBrowserWindowId ) * LLQtWebKit::getInstance()->getBrowserHeight( 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; @@ -139,7 +169,16 @@ private: 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 0 && mHeight > 0 ) @@ -160,13 +199,6 @@ private: if ( mInitState > INIT_STATE_UNINITIALIZED ) 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 directories char cwd[ FILENAME_MAX ]; // I *think* this is defined on all platforms we use if (NULL == getcwd( cwd, FILENAME_MAX - 1 )) @@ -177,12 +209,12 @@ private: std::string application_dir = std::string( cwd ); #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"); + // 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 @@ -223,65 +255,91 @@ private: bool result = LLQtWebKit::getInstance()->init( application_dir, component_dir, mProfileDir, native_window_handle ); if ( result ) { - // create single browser window - mBrowserWindowId = LLQtWebKit::getInstance()->createBrowserWindow( mWidth, mHeight ); -#if LL_WINDOWS - // Enable plugins - LLQtWebKit::getInstance()->enablePlugins(true); -#elif LL_DARWIN - // Enable plugins - LLQtWebKit::getInstance()->enablePlugins(true); -#elif LL_LINUX - // Enable plugins - LLQtWebKit::getInstance()->enablePlugins(true); -#endif - // Enable cookies - LLQtWebKit::getInstance()->enableCookies( true ); - - // 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( "LLPluginMedia Web Browser" ); - - // don't flip bitmap - LLQtWebKit::getInstance()->flipWindow( mBrowserWindowId, true ); - - // 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; + mInitState = INIT_STATE_INITIALIZED; - 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; }; 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 ); + + // 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 @@ -616,6 +674,11 @@ MediaPluginWebKit::MediaPluginWebKit(LLPluginInstance::sendMessageFunction host_ 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() @@ -642,9 +705,6 @@ void MediaPluginWebKit::receiveMessage(const char *message_string) { if(message_name == "init") { - std::string user_data_path = message_in.getValue("user_data_path"); // n.b. always has trailing platform-specific dir-delimiter - mProfileDir = user_data_path + "browser_profile"; - LLPluginMessage message("base", "init_response"); LLSD versions = LLSD::emptyMap(); versions[LLPLUGIN_MESSAGE_CLASS_BASE] = LLPLUGIN_MESSAGE_CLASS_BASE_VERSION; @@ -656,19 +716,6 @@ void MediaPluginWebKit::receiveMessage(const char *message_string) plugin_version += LLQtWebKit::getInstance()->getVersion(); message.setValue("plugin_version", plugin_version); sendMessage(message); - - // Plugin gets to decide the texture parameters to use. - mDepth = 4; - - message.setMessage(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); - message.setValueU32("format", GL_RGBA); - message.setValueU32("type", GL_UNSIGNED_BYTE); - message.setValueBoolean("coords_opengl", true); - sendMessage(message); } else if(message_name == "idle") { @@ -733,9 +780,68 @@ void MediaPluginWebKit::receiveMessage(const char *message_string) // std::cerr << "MediaPluginWebKit::receiveMessage: unknown base message: " << message_name << std::endl; } } + else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME) + { + if(message_name == "set_volume") + { + F32 volume = message_in.getValueReal("volume"); + setVolume(volume); + } + } else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA) { - if(message_name == "size_change") + if(message_name == "init") + { + // 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"); @@ -757,29 +863,36 @@ void MediaPluginWebKit::receiveMessage(const char *message_string) mWidth = width; mHeight = height; - // initialize (only gets called once) - initBrowser(); - - // 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) + if(initBrowserWindow()) { - texture_width = real_width; + + // 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 { - // 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; + // Setting up the browser window failed. This is a fatal error. mDeleteMe = true; - return; } + mTextureWidth = texture_width; mTextureHeight = texture_height; @@ -929,8 +1042,18 @@ void MediaPluginWebKit::receiveMessage(const char *message_string) } else if(message_name == "enable_cookies") { - bool val = message_in.getValueBoolean("enable"); - LLQtWebKit::getInstance()->enableCookies( val ); + 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 == "proxy_setup") { @@ -967,8 +1090,8 @@ void MediaPluginWebKit::receiveMessage(const char *message_string) } else if(message_name == "set_user_agent") { - std::string user_agent = message_in.getValue("user_agent"); - LLQtWebKit::getInstance()->setBrowserAgentId( user_agent ); + mUserAgent = message_in.getValue("user_agent"); + LLQtWebKit::getInstance()->setBrowserAgentId( mUserAgent ); } else if(message_name == "init_history") { @@ -999,6 +1122,13 @@ void MediaPluginWebKit::receiveMessage(const char *message_string) } } +void MediaPluginWebKit::setVolume(F32 volume) +{ +#if LL_LINUX + mLinuxVolumeCatcher.setVolume(volume); +#endif // LL_LINUX +} + int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data) { MediaPluginWebKit *self = new MediaPluginWebKit(host_send_func, host_user_data); -- cgit v1.1