Fixed web browser thanks to ArminW/Imprudence
This commit is contained in:
@@ -7,14 +7,15 @@ if (STANDALONE)
|
||||
pkg_check_modules(DBUSGLIB REQUIRED dbus-glib-1)
|
||||
|
||||
elseif (LINUX)
|
||||
use_prebuilt_binary(glib) # dbusglib needs glib
|
||||
use_prebuilt_binary(dbusglib)
|
||||
set(DBUSGLIB_FOUND ON FORCE BOOL)
|
||||
set(DBUSGLIB_INCLUDE_DIRS
|
||||
${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include/glib-2.0
|
||||
)
|
||||
# We don't need to explicitly link against dbus-glib itself, because
|
||||
# the viewer probes for the system's copy at runtime.
|
||||
|
||||
set(DBUSGLIB_LIBRARIES
|
||||
dbus-glib-1
|
||||
gobject-2.0
|
||||
glib-2.0
|
||||
)
|
||||
|
||||
@@ -7,6 +7,7 @@ if (STANDALONE)
|
||||
pkg_check_modules(GSTREAMER010 REQUIRED gstreamer-0.10)
|
||||
pkg_check_modules(GSTREAMER010_PLUGINS_BASE REQUIRED gstreamer-plugins-base-0.10)
|
||||
elseif (LINUX)
|
||||
use_prebuilt_binary(glib) # gstreamer needs glib
|
||||
use_prebuilt_binary(gstreamer)
|
||||
# possible libxml should have its own .cmake file instead
|
||||
use_prebuilt_binary(libxml)
|
||||
|
||||
@@ -32,6 +32,7 @@ if (STANDALONE)
|
||||
endforeach(pkg)
|
||||
else (STANDALONE)
|
||||
if (NOT DARWIN)
|
||||
use_prebuilt_binary(glib) # gtk-etc needs glib
|
||||
use_prebuilt_binary(gtk-atk-pango-glib)
|
||||
endif (NOT DARWIN)
|
||||
if (LINUX)
|
||||
@@ -49,11 +50,14 @@ else (STANDALONE)
|
||||
pangoft2-1.0
|
||||
pangox-1.0
|
||||
pangoxft-1.0
|
||||
pangocairo-1.0
|
||||
)
|
||||
endif (LINUX)
|
||||
|
||||
include_directories (
|
||||
${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include
|
||||
${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include/cairo
|
||||
${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include/pixman-1
|
||||
${LIBS_PREBUILT_DIR}/include
|
||||
)
|
||||
foreach(include ${${LL_ARCH}_INCLUDES})
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
project(llplugin)
|
||||
|
||||
include(00-Common)
|
||||
include(CURL)
|
||||
include(LLCommon)
|
||||
include(LLImage)
|
||||
include(LLMath)
|
||||
@@ -19,10 +20,12 @@ include_directories(
|
||||
${LLRENDER_INCLUDE_DIRS}
|
||||
${LLXML_INCLUDE_DIRS}
|
||||
${LLWINDOW_INCLUDE_DIRS}
|
||||
${LLQTWEBKIT_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
set(llplugin_SOURCE_FILES
|
||||
llpluginclassmedia.cpp
|
||||
llplugincookiestore.cpp
|
||||
llplugininstance.cpp
|
||||
llpluginmessage.cpp
|
||||
llpluginmessagepipe.cpp
|
||||
@@ -36,6 +39,7 @@ set(llplugin_HEADER_FILES
|
||||
|
||||
llpluginclassmedia.h
|
||||
llpluginclassmediaowner.h
|
||||
llplugincookiestore.h
|
||||
llplugininstance.h
|
||||
llpluginmessage.h
|
||||
llpluginmessageclasses.h
|
||||
@@ -48,20 +52,32 @@ set(llplugin_HEADER_FILES
|
||||
set_source_files_properties(${llplugin_HEADER_FILES}
|
||||
PROPERTIES HEADER_FILE_ONLY TRUE)
|
||||
|
||||
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)
|
||||
|
||||
list(APPEND llplugin_SOURCE_FILES ${llplugin_HEADER_FILES})
|
||||
|
||||
add_library (llplugin ${llplugin_SOURCE_FILES})
|
||||
|
||||
add_dependencies(llplugin
|
||||
prepare
|
||||
)
|
||||
|
||||
add_subdirectory(slplugin)
|
||||
|
||||
# # Add tests
|
||||
# include(LLAddBuildTest)
|
||||
# # UNIT TESTS
|
||||
# SET(llplugin_TEST_SOURCE_FILES
|
||||
# llplugincookiestore.cpp
|
||||
# )
|
||||
#
|
||||
# # llplugincookiestore has a dependency on curl, so we need to link the curl library into the test.
|
||||
# set_source_files_properties(
|
||||
# llplugincookiestore.cpp
|
||||
# PROPERTIES
|
||||
# LL_TEST_ADDITIONAL_LIBRARIES "${CURL_LIBRARIES}"
|
||||
# )
|
||||
#
|
||||
# LL_ADD_PROJECT_UNIT_TESTS(llplugin "${llplugin_TEST_SOURCE_FILES}")
|
||||
|
||||
93
indra/llplugin/llpluginclassmedia.cpp
Normal file → Executable file
93
indra/llplugin/llpluginclassmedia.cpp
Normal file → Executable file
@@ -2,9 +2,10 @@
|
||||
* @file llpluginclassmedia.cpp
|
||||
* @brief LLPluginClassMedia handles a plugin which knows about the "media" message class.
|
||||
*
|
||||
* @cond
|
||||
* $LicenseInfo:firstyear=2008&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2008-2009, Linden Research, Inc.
|
||||
* Copyright (c) 2008-2010, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
@@ -12,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,
|
||||
@@ -28,6 +29,8 @@
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
#include "linden_common.h"
|
||||
@@ -36,6 +39,8 @@
|
||||
#include "llpluginclassmedia.h"
|
||||
#include "llpluginmessageclasses.h"
|
||||
|
||||
#include "llqtwebkit.h"
|
||||
|
||||
static int LOW_PRIORITY_TEXTURE_SIZE_DEFAULT = 256;
|
||||
|
||||
static int nextPowerOf2( int value )
|
||||
@@ -54,23 +59,31 @@ LLPluginClassMedia::LLPluginClassMedia(LLPluginClassMediaOwner *owner)
|
||||
mOwner = owner;
|
||||
mPlugin = NULL;
|
||||
reset();
|
||||
|
||||
//debug use
|
||||
mDeleteOK = true ;
|
||||
}
|
||||
|
||||
|
||||
LLPluginClassMedia::~LLPluginClassMedia()
|
||||
{
|
||||
llassert_always(mDeleteOK) ;
|
||||
reset();
|
||||
}
|
||||
|
||||
bool LLPluginClassMedia::init(const std::string &launcher_filename, const std::string &plugin_filename, bool debug, const std::string &user_data_path)
|
||||
bool LLPluginClassMedia::init(const std::string &launcher_filename, const std::string &plugin_filename, bool debug)
|
||||
{
|
||||
LL_DEBUGS("Plugin") << "launcher: " << launcher_filename << LL_ENDL;
|
||||
LL_DEBUGS("Plugin") << "plugin: " << plugin_filename << LL_ENDL;
|
||||
LL_DEBUGS("Plugin") << "user_data_path: " << user_data_path << LL_ENDL;
|
||||
|
||||
mPlugin = new LLPluginProcessParent(this);
|
||||
mPlugin->setSleepTime(mSleepTime);
|
||||
mPlugin->init(launcher_filename, plugin_filename, debug, user_data_path);
|
||||
|
||||
// Queue up the media init message -- it will be sent after all the currently queued messages.
|
||||
LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "init");
|
||||
sendMessage(message);
|
||||
|
||||
mPlugin->init(launcher_filename, plugin_filename, debug);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -101,6 +114,8 @@ void LLPluginClassMedia::reset()
|
||||
mSetMediaHeight = -1;
|
||||
mRequestedMediaWidth = 0;
|
||||
mRequestedMediaHeight = 0;
|
||||
mRequestedTextureWidth = 0;
|
||||
mRequestedTextureHeight = 0;
|
||||
mFullMediaWidth = 0;
|
||||
mFullMediaHeight = 0;
|
||||
mTextureWidth = 0;
|
||||
@@ -123,7 +138,8 @@ void LLPluginClassMedia::reset()
|
||||
mCanPaste = false;
|
||||
mMediaName.clear();
|
||||
mMediaDescription.clear();
|
||||
|
||||
mBackgroundColor = LLColor4(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
|
||||
// media_browser class
|
||||
mNavigateURI.clear();
|
||||
mNavigateResultCode = -1;
|
||||
@@ -132,6 +148,8 @@ void LLPluginClassMedia::reset()
|
||||
mHistoryForwardAvailable = false;
|
||||
mStatusText.clear();
|
||||
mProgressPercent = 0;
|
||||
mClickURL.clear();
|
||||
mClickTarget.clear();
|
||||
|
||||
// media_time class
|
||||
mCurrentTime = 0.0f;
|
||||
@@ -147,7 +165,7 @@ void LLPluginClassMedia::idle(void)
|
||||
mPlugin->idle();
|
||||
}
|
||||
|
||||
if((mMediaWidth == -1) || (!mTextureParamsReceived) || (mPlugin == NULL))
|
||||
if((mMediaWidth == -1) || (!mTextureParamsReceived) || (mPlugin == NULL) || (mPlugin->isBlocked()))
|
||||
{
|
||||
// Can't process a size change at this time
|
||||
}
|
||||
@@ -233,6 +251,10 @@ void LLPluginClassMedia::idle(void)
|
||||
message.setValueS32("height", mRequestedMediaHeight);
|
||||
message.setValueS32("texture_width", mRequestedTextureWidth);
|
||||
message.setValueS32("texture_height", mRequestedTextureHeight);
|
||||
message.setValueReal("background_r", mBackgroundColor.mV[VX]);
|
||||
message.setValueReal("background_g", mBackgroundColor.mV[VY]);
|
||||
message.setValueReal("background_b", mBackgroundColor.mV[VZ]);
|
||||
message.setValueReal("background_a", mBackgroundColor.mV[VW]);
|
||||
mPlugin->sendMessage(message); // DO NOT just use sendMessage() here -- we want this to jump ahead of the queue.
|
||||
|
||||
LL_DEBUGS("Plugin") << "Sending size_change" << LL_ENDL;
|
||||
@@ -420,6 +442,12 @@ void LLPluginClassMedia::mouseEvent(EMouseEventType type, int button, int x, int
|
||||
{
|
||||
if(type == MOUSE_EVENT_MOVE)
|
||||
{
|
||||
if(!mPlugin || !mPlugin->isRunning() || mPlugin->isBlocked())
|
||||
{
|
||||
// Don't queue up mouse move events that can't be delivered.
|
||||
return;
|
||||
}
|
||||
|
||||
if((x == mLastMouseX) && (y == mLastMouseY))
|
||||
{
|
||||
// Don't spam unnecessary mouse move events.
|
||||
@@ -458,7 +486,7 @@ void LLPluginClassMedia::mouseEvent(EMouseEventType type, int button, int x, int
|
||||
sendMessage(message);
|
||||
}
|
||||
|
||||
bool LLPluginClassMedia::keyEvent(EKeyEventType type, int key_code, MASK modifiers)
|
||||
bool LLPluginClassMedia::keyEvent(EKeyEventType type, int key_code, MASK modifiers, LLSD native_key_data)
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
@@ -515,6 +543,7 @@ bool LLPluginClassMedia::keyEvent(EKeyEventType type, int key_code, MASK modifie
|
||||
message.setValueS32("key", key_code);
|
||||
|
||||
message.setValue("modifiers", translateModifiers(modifiers));
|
||||
message.setValueLLSD("native_key_data", native_key_data);
|
||||
|
||||
sendMessage(message);
|
||||
}
|
||||
@@ -533,12 +562,13 @@ void LLPluginClassMedia::scrollEvent(int x, int y, MASK modifiers)
|
||||
sendMessage(message);
|
||||
}
|
||||
|
||||
bool LLPluginClassMedia::textInput(const std::string &text, MASK modifiers)
|
||||
bool LLPluginClassMedia::textInput(const std::string &text, MASK modifiers, LLSD native_key_data)
|
||||
{
|
||||
LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "text_event");
|
||||
|
||||
message.setValue("text", text);
|
||||
message.setValue("modifiers", translateModifiers(modifiers));
|
||||
message.setValueLLSD("native_key_data", native_key_data);
|
||||
|
||||
sendMessage(message);
|
||||
|
||||
@@ -663,6 +693,34 @@ void LLPluginClassMedia::paste()
|
||||
sendMessage(message);
|
||||
}
|
||||
|
||||
void LLPluginClassMedia::setUserDataPath(const std::string &user_data_path)
|
||||
{
|
||||
LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "set_user_data_path");
|
||||
message.setValue("path", user_data_path);
|
||||
sendMessage(message);
|
||||
}
|
||||
|
||||
void LLPluginClassMedia::setLanguageCode(const std::string &language_code)
|
||||
{
|
||||
LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "set_language_code");
|
||||
message.setValue("language", language_code);
|
||||
sendMessage(message);
|
||||
}
|
||||
|
||||
void LLPluginClassMedia::setPluginsEnabled(const bool enabled)
|
||||
{
|
||||
LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "plugins_enabled");
|
||||
message.setValueBoolean("enable", enabled);
|
||||
sendMessage(message);
|
||||
}
|
||||
|
||||
void LLPluginClassMedia::setJavascriptEnabled(const bool enabled)
|
||||
{
|
||||
LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "javascript_enabled");
|
||||
message.setValueBoolean("enable", enabled);
|
||||
sendMessage(message);
|
||||
}
|
||||
|
||||
/* virtual */
|
||||
void LLPluginClassMedia::receivePluginMessage(const LLPluginMessage &message)
|
||||
{
|
||||
@@ -923,6 +981,13 @@ void LLPluginClassMedia::receivePluginMessage(const LLPluginMessage &message)
|
||||
mClickTarget.clear();
|
||||
mediaEvent(LLPluginClassMediaOwner::MEDIA_EVENT_CLICK_LINK_NOFOLLOW);
|
||||
}
|
||||
else if(message_name == "cookie_set")
|
||||
{
|
||||
if(mOwner)
|
||||
{
|
||||
mOwner->handleCookieSet(this, message.getValue("cookie"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS("Plugin") << "Unknown " << message_name << " class message: " << message_name << LL_ENDL;
|
||||
@@ -1006,9 +1071,17 @@ void LLPluginClassMedia::clear_cookies()
|
||||
sendMessage(message);
|
||||
}
|
||||
|
||||
void LLPluginClassMedia::set_cookies(const std::string &cookies)
|
||||
{
|
||||
LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "set_cookies");
|
||||
message.setValue("cookies", cookies);
|
||||
sendMessage(message);
|
||||
}
|
||||
|
||||
void LLPluginClassMedia::enable_cookies(bool enable)
|
||||
{
|
||||
LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "enable_cookies");
|
||||
message.setValueBoolean("enable", enable);
|
||||
sendMessage(message);
|
||||
}
|
||||
|
||||
|
||||
39
indra/llplugin/llpluginclassmedia.h
Normal file → Executable file
39
indra/llplugin/llpluginclassmedia.h
Normal file → Executable file
@@ -2,9 +2,10 @@
|
||||
* @file llpluginclassmedia.h
|
||||
* @brief LLPluginClassMedia handles interaction with a plugin which knows about the "media" message class.
|
||||
*
|
||||
* @cond
|
||||
* $LicenseInfo:firstyear=2008&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2008-2009, Linden Research, Inc.
|
||||
* Copyright (c) 2008-2010, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
@@ -12,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,
|
||||
@@ -28,6 +29,8 @@
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
#ifndef LL_LLPLUGINCLASSMEDIA_H
|
||||
@@ -38,7 +41,7 @@
|
||||
#include "llrect.h"
|
||||
#include "llpluginclassmediaowner.h"
|
||||
#include <queue>
|
||||
|
||||
#include "v4color.h"
|
||||
|
||||
class LLPluginClassMedia : public LLPluginProcessParentOwner
|
||||
{
|
||||
@@ -48,7 +51,9 @@ public:
|
||||
virtual ~LLPluginClassMedia();
|
||||
|
||||
// local initialization, called by the media manager when creating a source
|
||||
virtual bool init(const std::string &launcher_filename, const std::string &plugin_filename, bool debug, const std::string &user_data_path);
|
||||
virtual bool init(const std::string &launcher_filename,
|
||||
const std::string &plugin_filename,
|
||||
bool debug);
|
||||
|
||||
// undoes everything init() didm called by the media manager when destroying a source
|
||||
virtual void reset();
|
||||
@@ -85,6 +90,8 @@ public:
|
||||
void setSize(int width, int height);
|
||||
void setAutoScale(bool auto_scale);
|
||||
|
||||
void setBackgroundColor(LLColor4 color) { mBackgroundColor = color; };
|
||||
|
||||
// Returns true if all of the texture parameters (depth, format, size, and texture size) are set up and consistent.
|
||||
// This will initially be false, and will also be false for some time after setSize while the resize is processed.
|
||||
// Note that if this returns true, it is safe to use all the get() functions above without checking for invalid return values
|
||||
@@ -111,12 +118,12 @@ public:
|
||||
KEY_EVENT_REPEAT
|
||||
}EKeyEventType;
|
||||
|
||||
bool keyEvent(EKeyEventType type, int key_code, MASK modifiers);
|
||||
bool keyEvent(EKeyEventType type, int key_code, MASK modifiers, LLSD native_key_data);
|
||||
|
||||
void scrollEvent(int x, int y, MASK modifiers);
|
||||
|
||||
// Text may be unicode (utf8 encoded)
|
||||
bool textInput(const std::string &text, MASK modifiers);
|
||||
bool textInput(const std::string &text, MASK modifiers, LLSD native_key_data);
|
||||
|
||||
void loadURI(const std::string &uri);
|
||||
|
||||
@@ -170,6 +177,12 @@ public:
|
||||
|
||||
void paste();
|
||||
bool canPaste() const { return mCanPaste; };
|
||||
|
||||
// These can be called before init(), and they will be queued and sent before the media init message.
|
||||
void setUserDataPath(const std::string &user_data_path);
|
||||
void setLanguageCode(const std::string &language_code);
|
||||
void setPluginsEnabled(const bool enabled);
|
||||
void setJavascriptEnabled(const bool enabled);
|
||||
|
||||
///////////////////////////////////
|
||||
// media browser class functions
|
||||
@@ -178,6 +191,7 @@ public:
|
||||
void focus(bool focused);
|
||||
void clear_cache();
|
||||
void clear_cookies();
|
||||
void set_cookies(const std::string &cookies);
|
||||
void enable_cookies(bool enable);
|
||||
void proxy_setup(bool enable, const std::string &host = LLStringUtil::null, int port = 0);
|
||||
void browse_stop();
|
||||
@@ -211,6 +225,7 @@ public:
|
||||
// This is valid after MEDIA_EVENT_CLICK_LINK_HREF
|
||||
std::string getClickTarget() const { return mClickTarget; };
|
||||
|
||||
|
||||
std::string getMediaName() const { return mMediaName; };
|
||||
std::string getMediaDescription() const { return mMediaDescription; };
|
||||
|
||||
@@ -327,6 +342,8 @@ protected:
|
||||
std::string mMediaName;
|
||||
std::string mMediaDescription;
|
||||
|
||||
LLColor4 mBackgroundColor;
|
||||
|
||||
/////////////////////////////////////////
|
||||
// media_browser class
|
||||
std::string mNavigateURI;
|
||||
@@ -347,6 +364,14 @@ protected:
|
||||
F64 mCurrentRate;
|
||||
F64 mLoadedDuration;
|
||||
|
||||
//--------------------------------------
|
||||
//debug use only
|
||||
//
|
||||
private:
|
||||
bool mDeleteOK ;
|
||||
public:
|
||||
void setDeleteOK(bool flag) { mDeleteOK = flag ;}
|
||||
//--------------------------------------
|
||||
};
|
||||
|
||||
#endif // LL_LLPLUGINCLASSMEDIA_H
|
||||
|
||||
11
indra/llplugin/llpluginclassmediaowner.h
Normal file → Executable file
11
indra/llplugin/llpluginclassmediaowner.h
Normal file → Executable file
@@ -2,9 +2,10 @@
|
||||
* @file llpluginclassmediaowner.h
|
||||
* @brief LLPluginClassMedia handles interaction with a plugin which knows about the "media" message class.
|
||||
*
|
||||
* @cond
|
||||
* $LicenseInfo:firstyear=2008&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2008-2009, Linden Research, Inc.
|
||||
* Copyright (c) 2008-2010, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
@@ -12,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,
|
||||
@@ -28,6 +29,8 @@
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
#ifndef LL_LLPLUGINCLASSMEDIAOWNER_H
|
||||
@@ -38,6 +41,7 @@
|
||||
#include <queue>
|
||||
|
||||
class LLPluginClassMedia;
|
||||
class LLPluginCookieStore;
|
||||
|
||||
class LLPluginClassMediaOwner
|
||||
{
|
||||
@@ -77,6 +81,7 @@ public:
|
||||
|
||||
virtual ~LLPluginClassMediaOwner() {};
|
||||
virtual void handleMediaEvent(LLPluginClassMedia* /*self*/, EMediaEvent /*event*/) {};
|
||||
virtual void handleCookieSet(LLPluginClassMedia* /*self*/, const std::string &/*cookie*/) {};
|
||||
};
|
||||
|
||||
#endif // LL_LLPLUGINCLASSMEDIAOWNER_H
|
||||
|
||||
671
indra/llplugin/llplugincookiestore.cpp
Normal file
671
indra/llplugin/llplugincookiestore.cpp
Normal file
@@ -0,0 +1,671 @@
|
||||
/**
|
||||
* @file llplugincookiestore.cpp
|
||||
* @brief LLPluginCookieStore provides central storage for http cookies used by plugins
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
|
||||
#include "linden_common.h"
|
||||
#include "indra_constants.h"
|
||||
|
||||
#include "llplugincookiestore.h"
|
||||
#include <iostream>
|
||||
|
||||
// for curl_getdate() (apparently parsing RFC 1123 dates is hard)
|
||||
#include <curl/curl.h>
|
||||
|
||||
LLPluginCookieStore::LLPluginCookieStore():
|
||||
mHasChangedCookies(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
LLPluginCookieStore::~LLPluginCookieStore()
|
||||
{
|
||||
clearCookies();
|
||||
}
|
||||
|
||||
|
||||
LLPluginCookieStore::Cookie::Cookie(const std::string &s, std::string::size_type cookie_start, std::string::size_type cookie_end):
|
||||
mCookie(s, cookie_start, cookie_end - cookie_start),
|
||||
mNameStart(0), mNameEnd(0),
|
||||
mValueStart(0), mValueEnd(0),
|
||||
mDomainStart(0), mDomainEnd(0),
|
||||
mPathStart(0), mPathEnd(0),
|
||||
mDead(false), mChanged(true)
|
||||
{
|
||||
}
|
||||
|
||||
LLPluginCookieStore::Cookie *LLPluginCookieStore::Cookie::createFromString(const std::string &s, std::string::size_type cookie_start, std::string::size_type cookie_end, const std::string &host)
|
||||
{
|
||||
Cookie *result = new Cookie(s, cookie_start, cookie_end);
|
||||
|
||||
if(!result->parse(host))
|
||||
{
|
||||
delete result;
|
||||
result = NULL;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string LLPluginCookieStore::Cookie::getKey() const
|
||||
{
|
||||
std::string result;
|
||||
if(mDomainEnd > mDomainStart)
|
||||
{
|
||||
result += mCookie.substr(mDomainStart, mDomainEnd - mDomainStart);
|
||||
}
|
||||
result += ';';
|
||||
if(mPathEnd > mPathStart)
|
||||
{
|
||||
result += mCookie.substr(mPathStart, mPathEnd - mPathStart);
|
||||
}
|
||||
result += ';';
|
||||
result += mCookie.substr(mNameStart, mNameEnd - mNameStart);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool LLPluginCookieStore::Cookie::parse(const std::string &host)
|
||||
{
|
||||
bool first_field = true;
|
||||
|
||||
std::string::size_type cookie_end = mCookie.size();
|
||||
std::string::size_type field_start = 0;
|
||||
|
||||
LL_DEBUGS("CookieStoreParse") << "parsing cookie: " << mCookie << LL_ENDL;
|
||||
while(field_start < cookie_end)
|
||||
{
|
||||
// Finding the start of the next field requires honoring special quoting rules
|
||||
// see the definition of 'quoted-string' in rfc2616 for details
|
||||
std::string::size_type next_field_start = findFieldEnd(field_start);
|
||||
|
||||
// The end of this field should not include the terminating ';' or any trailing whitespace
|
||||
std::string::size_type field_end = mCookie.find_last_not_of("; ", next_field_start);
|
||||
if(field_end == std::string::npos || field_end < field_start)
|
||||
{
|
||||
// This field was empty or all whitespace. Set end = start so it shows as empty.
|
||||
field_end = field_start;
|
||||
}
|
||||
else if (field_end < next_field_start)
|
||||
{
|
||||
// we actually want the index of the char _after_ what 'last not of' found
|
||||
++field_end;
|
||||
}
|
||||
|
||||
// find the start of the actual name (skip separator and possible whitespace)
|
||||
std::string::size_type name_start = mCookie.find_first_not_of("; ", field_start);
|
||||
if(name_start == std::string::npos || name_start > next_field_start)
|
||||
{
|
||||
// Again, nothing but whitespace.
|
||||
name_start = field_start;
|
||||
}
|
||||
|
||||
// the name and value are separated by the first equals sign
|
||||
std::string::size_type name_value_sep = mCookie.find_first_of("=", name_start);
|
||||
if(name_value_sep == std::string::npos || name_value_sep > field_end)
|
||||
{
|
||||
// No separator found, so this is a field without an =
|
||||
name_value_sep = field_end;
|
||||
}
|
||||
|
||||
// the name end is before the name-value separator
|
||||
std::string::size_type name_end = mCookie.find_last_not_of("= ", name_value_sep);
|
||||
if(name_end == std::string::npos || name_end < name_start)
|
||||
{
|
||||
// I'm not sure how we'd hit this case... it seems like it would have to be an empty name.
|
||||
name_end = name_start;
|
||||
}
|
||||
else if (name_end < name_value_sep)
|
||||
{
|
||||
// we actually want the index of the char _after_ what 'last not of' found
|
||||
++name_end;
|
||||
}
|
||||
|
||||
// Value is between the name-value sep and the end of the field.
|
||||
std::string::size_type value_start = mCookie.find_first_not_of("= ", name_value_sep);
|
||||
if(value_start == std::string::npos || value_start > field_end)
|
||||
{
|
||||
// All whitespace or empty value
|
||||
value_start = field_end;
|
||||
}
|
||||
std::string::size_type value_end = mCookie.find_last_not_of("; ", field_end);
|
||||
if(value_end == std::string::npos || value_end < value_start)
|
||||
{
|
||||
// All whitespace or empty value
|
||||
value_end = value_start;
|
||||
}
|
||||
else if (value_end < field_end)
|
||||
{
|
||||
// we actually want the index of the char _after_ what 'last not of' found
|
||||
++value_end;
|
||||
}
|
||||
|
||||
LL_DEBUGS("CookieStoreParse")
|
||||
<< " field name: \"" << mCookie.substr(name_start, name_end - name_start)
|
||||
<< "\", value: \"" << mCookie.substr(value_start, value_end - value_start) << "\""
|
||||
<< LL_ENDL;
|
||||
|
||||
// See whether this field is one we know
|
||||
if(first_field)
|
||||
{
|
||||
// The first field is the name=value pair
|
||||
mNameStart = name_start;
|
||||
mNameEnd = name_end;
|
||||
mValueStart = value_start;
|
||||
mValueEnd = value_end;
|
||||
first_field = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Subsequent fields must come from the set in rfc2109
|
||||
if(matchName(name_start, name_end, "expires"))
|
||||
{
|
||||
std::string date_string(mCookie, value_start, value_end - value_start);
|
||||
// If the cookie contains an "expires" field, it MUST contain a parsable date.
|
||||
|
||||
// HACK: LLDate apparently can't PARSE an rfc1123-format date, even though it can GENERATE one.
|
||||
// The curl function curl_getdate can do this, but I'm hesitant to unilaterally introduce a curl dependency in LLDate.
|
||||
#if 1
|
||||
time_t date = curl_getdate(date_string.c_str(), NULL );
|
||||
mDate.secondsSinceEpoch((F64)date);
|
||||
LL_DEBUGS("CookieStoreParse") << " expire date parsed to: " << mDate.asRFC1123() << LL_ENDL;
|
||||
#else
|
||||
// This doesn't work (rfc1123-format dates cause it to fail)
|
||||
if(!mDate.fromString(date_string))
|
||||
{
|
||||
// Date failed to parse.
|
||||
LL_WARNS("CookieStoreParse") << "failed to parse cookie's expire date: " << date << LL_ENDL;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else if(matchName(name_start, name_end, "domain"))
|
||||
{
|
||||
mDomainStart = value_start;
|
||||
mDomainEnd = value_end;
|
||||
}
|
||||
else if(matchName(name_start, name_end, "path"))
|
||||
{
|
||||
mPathStart = value_start;
|
||||
mPathEnd = value_end;
|
||||
}
|
||||
else if(matchName(name_start, name_end, "max-age"))
|
||||
{
|
||||
// TODO: how should we handle this?
|
||||
}
|
||||
else if(matchName(name_start, name_end, "secure"))
|
||||
{
|
||||
// We don't care about the value of this field (yet)
|
||||
}
|
||||
else if(matchName(name_start, name_end, "version"))
|
||||
{
|
||||
// We don't care about the value of this field (yet)
|
||||
}
|
||||
else if(matchName(name_start, name_end, "comment"))
|
||||
{
|
||||
// We don't care about the value of this field (yet)
|
||||
}
|
||||
else if(matchName(name_start, name_end, "httponly"))
|
||||
{
|
||||
// We don't care about the value of this field (yet)
|
||||
}
|
||||
else
|
||||
{
|
||||
// An unknown field is a parse failure
|
||||
LL_WARNS("CookieStoreParse") << "unexpected field name: " << mCookie.substr(name_start, name_end - name_start) << LL_ENDL;
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// move on to the next field, skipping this field's separator and any leading whitespace
|
||||
field_start = mCookie.find_first_not_of("; ", next_field_start);
|
||||
}
|
||||
|
||||
// The cookie MUST have a name
|
||||
if(mNameEnd <= mNameStart)
|
||||
return false;
|
||||
|
||||
// If the cookie doesn't have a domain, add the current host as the domain.
|
||||
if(mDomainEnd <= mDomainStart)
|
||||
{
|
||||
if(host.empty())
|
||||
{
|
||||
// no domain and no current host -- this is a parse failure.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Figure out whether this cookie ended with a ";" or not...
|
||||
std::string::size_type last_char = mCookie.find_last_not_of(" ");
|
||||
if((last_char != std::string::npos) && (mCookie[last_char] != ';'))
|
||||
{
|
||||
mCookie += ";";
|
||||
}
|
||||
|
||||
mCookie += " domain=";
|
||||
mDomainStart = mCookie.size();
|
||||
mCookie += host;
|
||||
mDomainEnd = mCookie.size();
|
||||
|
||||
LL_DEBUGS("CookieStoreParse") << "added domain (" << mDomainStart << " to " << mDomainEnd << "), new cookie is: " << mCookie << LL_ENDL;
|
||||
}
|
||||
|
||||
// If the cookie doesn't have a path, add "/".
|
||||
if(mPathEnd <= mPathStart)
|
||||
{
|
||||
// Figure out whether this cookie ended with a ";" or not...
|
||||
std::string::size_type last_char = mCookie.find_last_not_of(" ");
|
||||
if((last_char != std::string::npos) && (mCookie[last_char] != ';'))
|
||||
{
|
||||
mCookie += ";";
|
||||
}
|
||||
|
||||
mCookie += " path=";
|
||||
mPathStart = mCookie.size();
|
||||
mCookie += "/";
|
||||
mPathEnd = mCookie.size();
|
||||
|
||||
LL_DEBUGS("CookieStoreParse") << "added path (" << mPathStart << " to " << mPathEnd << "), new cookie is: " << mCookie << LL_ENDL;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string::size_type LLPluginCookieStore::Cookie::findFieldEnd(std::string::size_type start, std::string::size_type end)
|
||||
{
|
||||
std::string::size_type result = start;
|
||||
|
||||
if(end == std::string::npos)
|
||||
end = mCookie.size();
|
||||
|
||||
bool in_quotes = false;
|
||||
for(; (result < end); result++)
|
||||
{
|
||||
switch(mCookie[result])
|
||||
{
|
||||
case '\\':
|
||||
if(in_quotes)
|
||||
result++; // The next character is backslash-quoted. Skip over it.
|
||||
break;
|
||||
case '"':
|
||||
in_quotes = !in_quotes;
|
||||
break;
|
||||
case ';':
|
||||
if(!in_quotes)
|
||||
return result;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If we got here, no ';' was found.
|
||||
return end;
|
||||
}
|
||||
|
||||
bool LLPluginCookieStore::Cookie::matchName(std::string::size_type start, std::string::size_type end, const char *name)
|
||||
{
|
||||
// NOTE: this assumes 'name' is already in lowercase. The code which uses it should be able to arrange this...
|
||||
|
||||
while((start < end) && (*name != '\0'))
|
||||
{
|
||||
if(tolower(mCookie[start]) != *name)
|
||||
return false;
|
||||
|
||||
start++;
|
||||
name++;
|
||||
}
|
||||
|
||||
// iff both strings hit the end at the same time, they're equal.
|
||||
return ((start == end) && (*name == '\0'));
|
||||
}
|
||||
|
||||
std::string LLPluginCookieStore::getAllCookies()
|
||||
{
|
||||
std::stringstream result;
|
||||
writeAllCookies(result);
|
||||
return result.str();
|
||||
}
|
||||
|
||||
void LLPluginCookieStore::writeAllCookies(std::ostream& s)
|
||||
{
|
||||
cookie_map_t::iterator iter;
|
||||
for(iter = mCookies.begin(); iter != mCookies.end(); iter++)
|
||||
{
|
||||
// Don't return expired cookies
|
||||
if(!iter->second->isDead())
|
||||
{
|
||||
s << (iter->second->getCookie()) << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::string LLPluginCookieStore::getPersistentCookies()
|
||||
{
|
||||
std::stringstream result;
|
||||
writePersistentCookies(result);
|
||||
return result.str();
|
||||
}
|
||||
|
||||
void LLPluginCookieStore::writePersistentCookies(std::ostream& s)
|
||||
{
|
||||
cookie_map_t::iterator iter;
|
||||
for(iter = mCookies.begin(); iter != mCookies.end(); iter++)
|
||||
{
|
||||
// Don't return expired cookies or session cookies
|
||||
if(!iter->second->isDead() && !iter->second->isSessionCookie())
|
||||
{
|
||||
s << iter->second->getCookie() << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string LLPluginCookieStore::getChangedCookies(bool clear_changed)
|
||||
{
|
||||
std::stringstream result;
|
||||
writeChangedCookies(result, clear_changed);
|
||||
|
||||
return result.str();
|
||||
}
|
||||
|
||||
void LLPluginCookieStore::writeChangedCookies(std::ostream& s, bool clear_changed)
|
||||
{
|
||||
if(mHasChangedCookies)
|
||||
{
|
||||
lldebugs << "returning changed cookies: " << llendl;
|
||||
cookie_map_t::iterator iter;
|
||||
for(iter = mCookies.begin(); iter != mCookies.end(); )
|
||||
{
|
||||
cookie_map_t::iterator next = iter;
|
||||
next++;
|
||||
|
||||
// Only return cookies marked as "changed"
|
||||
if(iter->second->isChanged())
|
||||
{
|
||||
s << iter->second->getCookie() << "\n";
|
||||
|
||||
lldebugs << " " << iter->second->getCookie() << llendl;
|
||||
|
||||
// If requested, clear the changed mark
|
||||
if(clear_changed)
|
||||
{
|
||||
if(iter->second->isDead())
|
||||
{
|
||||
// If this cookie was previously marked dead, it needs to be removed entirely.
|
||||
delete iter->second;
|
||||
mCookies.erase(iter);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not dead, just mark as not changed.
|
||||
iter->second->setChanged(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
iter = next;
|
||||
}
|
||||
}
|
||||
|
||||
if(clear_changed)
|
||||
mHasChangedCookies = false;
|
||||
}
|
||||
|
||||
void LLPluginCookieStore::setAllCookies(const std::string &cookies, bool mark_changed)
|
||||
{
|
||||
clearCookies();
|
||||
setCookies(cookies, mark_changed);
|
||||
}
|
||||
|
||||
void LLPluginCookieStore::readAllCookies(std::istream& s, bool mark_changed)
|
||||
{
|
||||
clearCookies();
|
||||
readCookies(s, mark_changed);
|
||||
}
|
||||
|
||||
void LLPluginCookieStore::setCookies(const std::string &cookies, bool mark_changed)
|
||||
{
|
||||
std::string::size_type start = 0;
|
||||
|
||||
while(start != std::string::npos)
|
||||
{
|
||||
std::string::size_type end = cookies.find_first_of("\r\n", start);
|
||||
if(end > start)
|
||||
{
|
||||
// The line is non-empty. Try to create a cookie from it.
|
||||
setOneCookie(cookies, start, end, mark_changed);
|
||||
}
|
||||
start = cookies.find_first_not_of("\r\n ", end);
|
||||
}
|
||||
}
|
||||
|
||||
void LLPluginCookieStore::setCookiesFromHost(const std::string &cookies, const std::string &host, bool mark_changed)
|
||||
{
|
||||
std::string::size_type start = 0;
|
||||
|
||||
while(start != std::string::npos)
|
||||
{
|
||||
std::string::size_type end = cookies.find_first_of("\r\n", start);
|
||||
if(end > start)
|
||||
{
|
||||
// The line is non-empty. Try to create a cookie from it.
|
||||
setOneCookie(cookies, start, end, mark_changed, host);
|
||||
}
|
||||
start = cookies.find_first_not_of("\r\n ", end);
|
||||
}
|
||||
}
|
||||
|
||||
void LLPluginCookieStore::readCookies(std::istream& s, bool mark_changed)
|
||||
{
|
||||
std::string line;
|
||||
while(s.good() && !s.eof())
|
||||
{
|
||||
std::getline(s, line);
|
||||
if(!line.empty())
|
||||
{
|
||||
// Try to create a cookie from this line.
|
||||
setOneCookie(line, 0, std::string::npos, mark_changed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string LLPluginCookieStore::quoteString(const std::string &s)
|
||||
{
|
||||
std::stringstream result;
|
||||
|
||||
result << '"';
|
||||
|
||||
for(std::string::size_type i = 0; i < s.size(); ++i)
|
||||
{
|
||||
char c = s[i];
|
||||
switch(c)
|
||||
{
|
||||
// All these separators need to be quoted in HTTP headers, according to section 2.2 of rfc 2616:
|
||||
case '(': case ')': case '<': case '>': case '@':
|
||||
case ',': case ';': case ':': case '\\': case '"':
|
||||
case '/': case '[': case ']': case '?': case '=':
|
||||
case '{': case '}': case ' ': case '\t':
|
||||
result << '\\';
|
||||
break;
|
||||
}
|
||||
|
||||
result << c;
|
||||
}
|
||||
|
||||
result << '"';
|
||||
|
||||
return result.str();
|
||||
}
|
||||
|
||||
std::string LLPluginCookieStore::unquoteString(const std::string &s)
|
||||
{
|
||||
std::stringstream result;
|
||||
|
||||
bool in_quotes = false;
|
||||
|
||||
for(std::string::size_type i = 0; i < s.size(); ++i)
|
||||
{
|
||||
char c = s[i];
|
||||
switch(c)
|
||||
{
|
||||
case '\\':
|
||||
if(in_quotes)
|
||||
{
|
||||
// The next character is backslash-quoted. Pass it through untouched.
|
||||
++i;
|
||||
if(i < s.size())
|
||||
{
|
||||
result << s[i];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case '"':
|
||||
in_quotes = !in_quotes;
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
|
||||
result << c;
|
||||
}
|
||||
|
||||
return result.str();
|
||||
}
|
||||
|
||||
// The flow for deleting a cookie is non-obvious enough that I should call it out here...
|
||||
// Deleting a cookie is done by setting a cookie with the same name, path, and domain, but with an expire timestamp in the past.
|
||||
// (This is exactly how a web server tells a browser to delete a cookie.)
|
||||
// When deleting with mark_changed set to true, this replaces the existing cookie in the list with an entry that's marked both dead and changed.
|
||||
// Some time later when writeChangedCookies() is called with clear_changed set to true, the dead cookie is deleted from the list after being returned, so that the
|
||||
// delete operation (in the form of the expired cookie) is passed along.
|
||||
void LLPluginCookieStore::setOneCookie(const std::string &s, std::string::size_type cookie_start, std::string::size_type cookie_end, bool mark_changed, const std::string &host)
|
||||
{
|
||||
Cookie *cookie = Cookie::createFromString(s, cookie_start, cookie_end, host);
|
||||
if(cookie)
|
||||
{
|
||||
LL_DEBUGS("CookieStoreUpdate") << "setting cookie: " << cookie->getCookie() << LL_ENDL;
|
||||
|
||||
// Create a key for this cookie
|
||||
std::string key = cookie->getKey();
|
||||
|
||||
// Check to see whether this cookie should have expired
|
||||
if(!cookie->isSessionCookie() && (cookie->getDate() < LLDate::now()))
|
||||
{
|
||||
// This cookie has expired.
|
||||
if(mark_changed)
|
||||
{
|
||||
// If we're marking cookies as changed, we should keep it anyway since we'll need to send it out with deltas.
|
||||
cookie->setDead(true);
|
||||
LL_DEBUGS("CookieStoreUpdate") << " marking dead" << LL_ENDL;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we're not marking cookies as changed, we don't need to keep this cookie at all.
|
||||
// If the cookie was already in the list, delete it.
|
||||
removeCookie(key);
|
||||
|
||||
delete cookie;
|
||||
cookie = NULL;
|
||||
|
||||
LL_DEBUGS("CookieStoreUpdate") << " removing" << LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
||||
if(cookie)
|
||||
{
|
||||
// If it already exists in the map, replace it.
|
||||
cookie_map_t::iterator iter = mCookies.find(key);
|
||||
if(iter != mCookies.end())
|
||||
{
|
||||
if(iter->second->getCookie() == cookie->getCookie())
|
||||
{
|
||||
// The new cookie is identical to the old -- don't mark as changed.
|
||||
// Just leave the old one in the map.
|
||||
delete cookie;
|
||||
cookie = NULL;
|
||||
|
||||
LL_DEBUGS("CookieStoreUpdate") << " unchanged" << LL_ENDL;
|
||||
}
|
||||
else
|
||||
{
|
||||
// A matching cookie was already in the map. Replace it.
|
||||
delete iter->second;
|
||||
iter->second = cookie;
|
||||
|
||||
cookie->setChanged(mark_changed);
|
||||
if(mark_changed)
|
||||
mHasChangedCookies = true;
|
||||
|
||||
LL_DEBUGS("CookieStoreUpdate") << " replacing" << LL_ENDL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The cookie wasn't in the map. Insert it.
|
||||
mCookies.insert(std::make_pair(key, cookie));
|
||||
|
||||
cookie->setChanged(mark_changed);
|
||||
if(mark_changed)
|
||||
mHasChangedCookies = true;
|
||||
|
||||
LL_DEBUGS("CookieStoreUpdate") << " adding" << LL_ENDL;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS("CookieStoreUpdate") << "failed to parse cookie: " << s.substr(cookie_start, cookie_end - cookie_start) << LL_ENDL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void LLPluginCookieStore::clearCookies()
|
||||
{
|
||||
while(!mCookies.empty())
|
||||
{
|
||||
cookie_map_t::iterator iter = mCookies.begin();
|
||||
delete iter->second;
|
||||
mCookies.erase(iter);
|
||||
}
|
||||
}
|
||||
|
||||
void LLPluginCookieStore::removeCookie(const std::string &key)
|
||||
{
|
||||
cookie_map_t::iterator iter = mCookies.find(key);
|
||||
if(iter != mCookies.end())
|
||||
{
|
||||
delete iter->second;
|
||||
mCookies.erase(iter);
|
||||
}
|
||||
}
|
||||
|
||||
127
indra/llplugin/llplugincookiestore.h
Normal file
127
indra/llplugin/llplugincookiestore.h
Normal file
@@ -0,0 +1,127 @@
|
||||
/**
|
||||
* @file llplugincookiestore.h
|
||||
* @brief LLPluginCookieStore provides central storage for http cookies used by plugins
|
||||
*
|
||||
* @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 LL_LLPLUGINCOOKIESTORE_H
|
||||
#define LL_LLPLUGINCOOKIESTORE_H
|
||||
|
||||
#include "lldate.h"
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
class LLPluginCookieStore
|
||||
{
|
||||
LOG_CLASS(LLPluginCookieStore);
|
||||
public:
|
||||
LLPluginCookieStore();
|
||||
~LLPluginCookieStore();
|
||||
|
||||
// gets all cookies currently in storage -- use when initializing a plugin
|
||||
std::string getAllCookies();
|
||||
void writeAllCookies(std::ostream& s);
|
||||
|
||||
// gets only persistent cookies (i.e. not session cookies) -- use when writing cookies to a file
|
||||
std::string getPersistentCookies();
|
||||
void writePersistentCookies(std::ostream& s);
|
||||
|
||||
// gets cookies which are marked as "changed" -- use when sending periodic updates to plugins
|
||||
std::string getChangedCookies(bool clear_changed = true);
|
||||
void writeChangedCookies(std::ostream& s, bool clear_changed = true);
|
||||
|
||||
// (re)initializes internal data structures and bulk-sets cookies -- use when reading cookies from a file
|
||||
void setAllCookies(const std::string &cookies, bool mark_changed = false);
|
||||
void readAllCookies(std::istream& s, bool mark_changed = false);
|
||||
|
||||
// sets one or more cookies (without reinitializing anything) -- use when receiving cookies from a plugin
|
||||
void setCookies(const std::string &cookies, bool mark_changed = true);
|
||||
void readCookies(std::istream& s, bool mark_changed = true);
|
||||
|
||||
// sets one or more cookies (without reinitializing anything), supplying a hostname the cookies came from -- use when setting a cookie manually
|
||||
void setCookiesFromHost(const std::string &cookies, const std::string &host, bool mark_changed = true);
|
||||
|
||||
// quote or unquote a string as per the definition of 'quoted-string' in rfc2616
|
||||
static std::string quoteString(const std::string &s);
|
||||
static std::string unquoteString(const std::string &s);
|
||||
|
||||
private:
|
||||
|
||||
void setOneCookie(const std::string &s, std::string::size_type cookie_start, std::string::size_type cookie_end, bool mark_changed, const std::string &host = LLStringUtil::null);
|
||||
|
||||
class Cookie
|
||||
{
|
||||
public:
|
||||
static Cookie *createFromString(const std::string &s, std::string::size_type cookie_start = 0, std::string::size_type cookie_end = std::string::npos, const std::string &host = LLStringUtil::null);
|
||||
|
||||
// Construct a string from the cookie that uniquely represents it, to be used as a key in a std::map.
|
||||
std::string getKey() const;
|
||||
|
||||
const std::string &getCookie() const { return mCookie; };
|
||||
bool isSessionCookie() const { return mDate.isNull(); };
|
||||
|
||||
bool isDead() const { return mDead; };
|
||||
void setDead(bool dead) { mDead = dead; };
|
||||
|
||||
bool isChanged() const { return mChanged; };
|
||||
void setChanged(bool changed) { mChanged = changed; };
|
||||
|
||||
const LLDate &getDate() const { return mDate; };
|
||||
|
||||
private:
|
||||
Cookie(const std::string &s, std::string::size_type cookie_start = 0, std::string::size_type cookie_end = std::string::npos);
|
||||
bool parse(const std::string &host);
|
||||
std::string::size_type findFieldEnd(std::string::size_type start = 0, std::string::size_type end = std::string::npos);
|
||||
bool matchName(std::string::size_type start, std::string::size_type end, const char *name);
|
||||
|
||||
std::string mCookie; // The full cookie, in RFC 2109 string format
|
||||
LLDate mDate; // The expiration date of the cookie. For session cookies, this will be a null date (mDate.isNull() is true).
|
||||
// Start/end indices of various parts of the cookie string. Stored as indices into the string to save space and time.
|
||||
std::string::size_type mNameStart, mNameEnd;
|
||||
std::string::size_type mValueStart, mValueEnd;
|
||||
std::string::size_type mDomainStart, mDomainEnd;
|
||||
std::string::size_type mPathStart, mPathEnd;
|
||||
bool mDead;
|
||||
bool mChanged;
|
||||
};
|
||||
|
||||
typedef std::map<std::string, Cookie*> cookie_map_t;
|
||||
|
||||
cookie_map_t mCookies;
|
||||
bool mHasChangedCookies;
|
||||
|
||||
void clearCookies();
|
||||
void removeCookie(const std::string &key);
|
||||
};
|
||||
|
||||
#endif // LL_LLPLUGINCOOKIESTORE_H
|
||||
9
indra/llplugin/llplugininstance.cpp
Normal file → Executable file
9
indra/llplugin/llplugininstance.cpp
Normal file → Executable file
@@ -2,9 +2,10 @@
|
||||
* @file llplugininstance.cpp
|
||||
* @brief LLPluginInstance handles loading the dynamic library of a plugin and setting up its entry points for message passing.
|
||||
*
|
||||
* @cond
|
||||
* $LicenseInfo:firstyear=2008&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2008-2009, Linden Research, Inc.
|
||||
* Copyright (c) 2008-2010, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
@@ -12,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,
|
||||
@@ -28,6 +29,8 @@
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
#include "linden_common.h"
|
||||
|
||||
10
indra/llplugin/llplugininstance.h
Normal file → Executable file
10
indra/llplugin/llplugininstance.h
Normal file → Executable file
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
* @file llplugininstance.h
|
||||
* @brief LLPluginInstance handles loading the dynamic library of a plugin and setting up its entry points for message passing.
|
||||
*
|
||||
* @cond
|
||||
* $LicenseInfo:firstyear=2008&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2008-2009, Linden Research, Inc.
|
||||
* Copyright (c) 2008-2010, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
@@ -12,13 +12,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,
|
||||
@@ -28,6 +28,8 @@
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
#ifndef LL_LLPLUGININSTANCE_H
|
||||
|
||||
9
indra/llplugin/llpluginmessage.cpp
Normal file → Executable file
9
indra/llplugin/llpluginmessage.cpp
Normal file → Executable file
@@ -2,9 +2,10 @@
|
||||
* @file llpluginmessage.cpp
|
||||
* @brief LLPluginMessage encapsulates the serialization/deserialization of messages passed to and from plugins.
|
||||
*
|
||||
* @cond
|
||||
* $LicenseInfo:firstyear=2008&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2008-2009, Linden Research, Inc.
|
||||
* Copyright (c) 2008-2010, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
@@ -12,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,
|
||||
@@ -28,6 +29,8 @@
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
#include "linden_common.h"
|
||||
|
||||
10
indra/llplugin/llpluginmessage.h
Normal file → Executable file
10
indra/llplugin/llpluginmessage.h
Normal file → Executable file
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
* @file llpluginmessage.h
|
||||
* @brief LLPluginMessage encapsulates the serialization/deserialization of messages passed to and from plugins.
|
||||
*
|
||||
* @cond
|
||||
* $LicenseInfo:firstyear=2008&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2008-2009, Linden Research, Inc.
|
||||
* Copyright (c) 2008-2010, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
@@ -12,13 +12,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,
|
||||
@@ -28,6 +28,8 @@
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
#ifndef LL_LLPLUGINMESSAGE_H
|
||||
|
||||
9
indra/llplugin/llpluginmessageclasses.h
Normal file → Executable file
9
indra/llplugin/llpluginmessageclasses.h
Normal file → Executable file
@@ -2,9 +2,10 @@
|
||||
* @file llpluginmessageclasses.h
|
||||
* @brief This file defines the versions of existing message classes for LLPluginMessage.
|
||||
*
|
||||
* @cond
|
||||
* $LicenseInfo:firstyear=2008&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2008-2009, Linden Research, Inc.
|
||||
* Copyright (c) 2008-2010, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
@@ -12,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,
|
||||
@@ -28,6 +29,8 @@
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
#ifndef LL_LLPLUGINMESSAGECLASSES_H
|
||||
|
||||
129
indra/llplugin/llpluginmessagepipe.cpp
Normal file → Executable file
129
indra/llplugin/llpluginmessagepipe.cpp
Normal file → Executable file
@@ -2,9 +2,10 @@
|
||||
* @file llpluginmessagepipe.cpp
|
||||
* @brief Classes that implement connections from the plugin system to pipes/pumps.
|
||||
*
|
||||
* @cond
|
||||
* $LicenseInfo:firstyear=2008&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2008-2009, Linden Research, Inc.
|
||||
* Copyright (c) 2008-2010, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
@@ -12,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,
|
||||
@@ -28,6 +29,8 @@
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
#include "linden_common.h"
|
||||
@@ -95,11 +98,14 @@ void LLPluginMessagePipeOwner::killMessagePipe(void)
|
||||
}
|
||||
}
|
||||
|
||||
LLPluginMessagePipe::LLPluginMessagePipe(LLPluginMessagePipeOwner *owner, LLSocket::ptr_t socket)
|
||||
LLPluginMessagePipe::LLPluginMessagePipe(LLPluginMessagePipeOwner *owner, LLSocket::ptr_t socket):
|
||||
mInputMutex(gAPRPoolp),
|
||||
mOutputMutex(gAPRPoolp),
|
||||
mOwner(owner),
|
||||
mSocket(socket)
|
||||
{
|
||||
mOwner = owner;
|
||||
|
||||
mOwner->setMessagePipe(this);
|
||||
mSocket = socket;
|
||||
}
|
||||
|
||||
LLPluginMessagePipe::~LLPluginMessagePipe()
|
||||
@@ -113,6 +119,7 @@ LLPluginMessagePipe::~LLPluginMessagePipe()
|
||||
bool LLPluginMessagePipe::addMessage(const std::string &message)
|
||||
{
|
||||
// queue the message for later output
|
||||
LLMutexLock lock(&mOutputMutex);
|
||||
mOutput += message;
|
||||
mOutput += MESSAGE_DELIMITER; // message separator
|
||||
|
||||
@@ -147,6 +154,18 @@ void LLPluginMessagePipe::setSocketTimeout(apr_interval_time_t timeout_usec)
|
||||
}
|
||||
|
||||
bool LLPluginMessagePipe::pump(F64 timeout)
|
||||
{
|
||||
bool result = pumpOutput();
|
||||
|
||||
if(result)
|
||||
{
|
||||
result = pumpInput(timeout);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool LLPluginMessagePipe::pumpOutput()
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
@@ -155,6 +174,7 @@ bool LLPluginMessagePipe::pump(F64 timeout)
|
||||
apr_status_t status;
|
||||
apr_size_t size;
|
||||
|
||||
LLMutexLock lock(&mOutputMutex);
|
||||
if(!mOutput.empty())
|
||||
{
|
||||
// write any outgoing messages
|
||||
@@ -182,6 +202,17 @@ bool LLPluginMessagePipe::pump(F64 timeout)
|
||||
// remove the written part from the buffer and try again later.
|
||||
mOutput = mOutput.substr(size);
|
||||
}
|
||||
else if(APR_STATUS_IS_EOF(status))
|
||||
{
|
||||
// This is what we normally expect when a plugin exits.
|
||||
llinfos << "Got EOF from plugin socket. " << llendl;
|
||||
|
||||
if(mOwner)
|
||||
{
|
||||
mOwner->socketError(status);
|
||||
}
|
||||
result = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// some other error
|
||||
@@ -195,6 +226,19 @@ bool LLPluginMessagePipe::pump(F64 timeout)
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool LLPluginMessagePipe::pumpInput(F64 timeout)
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
if(mSocket)
|
||||
{
|
||||
apr_status_t status;
|
||||
apr_size_t size;
|
||||
|
||||
// FIXME: For some reason, the apr timeout stuff isn't working properly on windows.
|
||||
// Until such time as we figure out why, don't try to use the socket timeout -- just sleep here instead.
|
||||
@@ -215,8 +259,16 @@ bool LLPluginMessagePipe::pump(F64 timeout)
|
||||
char input_buf[1024];
|
||||
apr_size_t request_size;
|
||||
|
||||
// Start out by reading one byte, so that any data received will wake us up.
|
||||
request_size = 1;
|
||||
if(timeout == 0.0f)
|
||||
{
|
||||
// If we have no timeout, start out with a full read.
|
||||
request_size = sizeof(input_buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Start out by reading one byte, so that any data received will wake us up.
|
||||
request_size = 1;
|
||||
}
|
||||
|
||||
// and use the timeout so we'll sleep if no data is available.
|
||||
setSocketTimeout((apr_interval_time_t)(timeout * 1000000));
|
||||
@@ -235,11 +287,14 @@ bool LLPluginMessagePipe::pump(F64 timeout)
|
||||
// LL_INFOS("Plugin") << "after apr_socket_recv, size = " << size << LL_ENDL;
|
||||
|
||||
if(size > 0)
|
||||
{
|
||||
LLMutexLock lock(&mInputMutex);
|
||||
mInput.append(input_buf, size);
|
||||
}
|
||||
|
||||
if(status == APR_SUCCESS)
|
||||
{
|
||||
// llinfos << "success, read " << size << llendl;
|
||||
LL_DEBUGS("PluginSocket") << "success, read " << size << LL_ENDL;
|
||||
|
||||
if(size != request_size)
|
||||
{
|
||||
@@ -249,16 +304,28 @@ bool LLPluginMessagePipe::pump(F64 timeout)
|
||||
}
|
||||
else if(APR_STATUS_IS_TIMEUP(status))
|
||||
{
|
||||
// llinfos << "TIMEUP, read " << size << llendl;
|
||||
LL_DEBUGS("PluginSocket") << "TIMEUP, read " << size << LL_ENDL;
|
||||
|
||||
// Timeout was hit. Since the initial read is 1 byte, this should never be a partial read.
|
||||
break;
|
||||
}
|
||||
else if(APR_STATUS_IS_EAGAIN(status))
|
||||
{
|
||||
// llinfos << "EAGAIN, read " << size << llendl;
|
||||
LL_DEBUGS("PluginSocket") << "EAGAIN, read " << size << LL_ENDL;
|
||||
|
||||
// We've been doing partial reads, and we're done now.
|
||||
// Non-blocking read returned immediately.
|
||||
break;
|
||||
}
|
||||
else if(APR_STATUS_IS_EOF(status))
|
||||
{
|
||||
// This is what we normally expect when a plugin exits.
|
||||
LL_INFOS("PluginSocket") << "Got EOF from plugin socket. " << LL_ENDL;
|
||||
|
||||
if(mOwner)
|
||||
{
|
||||
mOwner->socketError(status);
|
||||
}
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
else
|
||||
@@ -275,22 +342,18 @@ bool LLPluginMessagePipe::pump(F64 timeout)
|
||||
break;
|
||||
}
|
||||
|
||||
// Second and subsequent reads should not use the timeout
|
||||
setSocketTimeout(0);
|
||||
// and should try to fill the input buffer
|
||||
request_size = sizeof(input_buf);
|
||||
if(timeout != 0.0f)
|
||||
{
|
||||
// Second and subsequent reads should not use the timeout
|
||||
setSocketTimeout(0);
|
||||
// and should try to fill the input buffer
|
||||
request_size = sizeof(input_buf);
|
||||
}
|
||||
}
|
||||
|
||||
processInput();
|
||||
}
|
||||
}
|
||||
|
||||
if(!result)
|
||||
{
|
||||
// If we got an error, we're done.
|
||||
LL_INFOS("Plugin") << "Error from socket, cleaning up." << LL_ENDL;
|
||||
delete this;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -298,25 +361,27 @@ bool LLPluginMessagePipe::pump(F64 timeout)
|
||||
void LLPluginMessagePipe::processInput(void)
|
||||
{
|
||||
// Look for input delimiter(s) in the input buffer.
|
||||
int start = 0;
|
||||
int delim;
|
||||
while((delim = mInput.find(MESSAGE_DELIMITER, start)) != std::string::npos)
|
||||
mInputMutex.lock();
|
||||
while((delim = mInput.find(MESSAGE_DELIMITER)) != std::string::npos)
|
||||
{
|
||||
// Let the owner process this message
|
||||
if (mOwner)
|
||||
{
|
||||
mOwner->receiveMessageRaw(mInput.substr(start, delim - start));
|
||||
// Pull the message out of the input buffer before calling receiveMessageRaw.
|
||||
// It's now possible for this function to get called recursively (in the case where the plugin makes a blocking request)
|
||||
// and this guarantees that the messages will get dequeued correctly.
|
||||
std::string message(mInput, 0, delim);
|
||||
mInput.erase(0, delim + 1);
|
||||
mInputMutex.unlock();
|
||||
mOwner->receiveMessageRaw(message);
|
||||
mInputMutex.lock();
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS("Plugin") << "!mOwner" << LL_ENDL;
|
||||
}
|
||||
start = delim + 1;
|
||||
}
|
||||
|
||||
// Remove delivered messages from the input buffer.
|
||||
if(start != 0)
|
||||
mInput = mInput.substr(start);
|
||||
|
||||
mInputMutex.unlock();
|
||||
}
|
||||
|
||||
|
||||
18
indra/llplugin/llpluginmessagepipe.h
Normal file → Executable file
18
indra/llplugin/llpluginmessagepipe.h
Normal file → Executable file
@@ -2,9 +2,10 @@
|
||||
* @file llpluginmessagepipe.h
|
||||
* @brief Classes that implement connections from the plugin system to pipes/pumps.
|
||||
*
|
||||
* @cond
|
||||
* $LicenseInfo:firstyear=2008&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2008-2009, Linden Research, Inc.
|
||||
* Copyright (c) 2008-2010, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
@@ -12,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,
|
||||
@@ -28,12 +29,15 @@
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
#ifndef LL_LLPLUGINMESSAGEPIPE_H
|
||||
#define LL_LLPLUGINMESSAGEPIPE_H
|
||||
|
||||
#include "lliosocket.h"
|
||||
#include "llthread.h"
|
||||
|
||||
class LLPluginMessagePipe;
|
||||
|
||||
@@ -50,7 +54,7 @@ public:
|
||||
virtual apr_status_t socketError(apr_status_t error);
|
||||
|
||||
// called from LLPluginMessagePipe to manage the connection with LLPluginMessagePipeOwner -- do not use!
|
||||
virtual void setMessagePipe(LLPluginMessagePipe *message_pipe) ;
|
||||
virtual void setMessagePipe(LLPluginMessagePipe *message_pipe);
|
||||
|
||||
protected:
|
||||
// returns false if writeMessageRaw() would drop the message
|
||||
@@ -75,14 +79,18 @@ public:
|
||||
void clearOwner(void);
|
||||
|
||||
bool pump(F64 timeout = 0.0f);
|
||||
|
||||
bool pumpOutput();
|
||||
bool pumpInput(F64 timeout = 0.0f);
|
||||
|
||||
protected:
|
||||
void processInput(void);
|
||||
|
||||
// used internally by pump()
|
||||
void setSocketTimeout(apr_interval_time_t timeout_usec);
|
||||
|
||||
LLMutex mInputMutex;
|
||||
std::string mInput;
|
||||
LLMutex mOutputMutex;
|
||||
std::string mOutput;
|
||||
|
||||
LLPluginMessagePipeOwner *mOwner;
|
||||
|
||||
107
indra/llplugin/llpluginprocesschild.cpp
Normal file → Executable file
107
indra/llplugin/llpluginprocesschild.cpp
Normal file → Executable file
@@ -2,9 +2,10 @@
|
||||
* @file llpluginprocesschild.cpp
|
||||
* @brief LLPluginProcessChild handles the child side of the external-process plugin API.
|
||||
*
|
||||
* @cond
|
||||
* $LicenseInfo:firstyear=2008&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2008-2009, Linden Research, Inc.
|
||||
* Copyright (c) 2008-2010, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
@@ -12,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,
|
||||
@@ -28,6 +29,8 @@
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
#include "linden_common.h"
|
||||
@@ -42,10 +45,13 @@ static const F32 PLUGIN_IDLE_SECONDS = 1.0f / 100.0f; // Each call to idle will
|
||||
|
||||
LLPluginProcessChild::LLPluginProcessChild()
|
||||
{
|
||||
mState = STATE_UNINITIALIZED;
|
||||
mInstance = NULL;
|
||||
mSocket = LLSocket::create(gAPRPoolp, LLSocket::STREAM_TCP);
|
||||
mSleepTime = PLUGIN_IDLE_SECONDS; // default: send idle messages at 100Hz
|
||||
mCPUElapsed = 0.0f;
|
||||
mBlockingRequest = false;
|
||||
mBlockingResponseReceived = false;
|
||||
}
|
||||
|
||||
LLPluginProcessChild::~LLPluginProcessChild()
|
||||
@@ -58,7 +64,7 @@ LLPluginProcessChild::~LLPluginProcessChild()
|
||||
// appears to fail and lock up which means that a given instance of the slplugin process never exits.
|
||||
// This is bad, especially when users try to update their version of SL - it fails because the slplugin
|
||||
// process as well as a bunch of plugin specific files are locked and cannot be overwritten.
|
||||
exit(0);
|
||||
exit( 0 );
|
||||
//delete mInstance;
|
||||
//mInstance = NULL;
|
||||
}
|
||||
@@ -81,9 +87,14 @@ void LLPluginProcessChild::idle(void)
|
||||
bool idle_again;
|
||||
do
|
||||
{
|
||||
if(mSocketError != APR_SUCCESS)
|
||||
if(APR_STATUS_IS_EOF(mSocketError))
|
||||
{
|
||||
LL_INFOS("Plugin") << "message pipe is in error state, moving to STATE_ERROR"<< LL_ENDL;
|
||||
// Plugin socket was closed. This covers both normal plugin termination and host crashes.
|
||||
setState(STATE_ERROR);
|
||||
}
|
||||
else if(mSocketError != APR_SUCCESS)
|
||||
{
|
||||
LL_INFOS("Plugin") << "message pipe is in error state (" << mSocketError << "), moving to STATE_ERROR"<< LL_ENDL;
|
||||
setState(STATE_ERROR);
|
||||
}
|
||||
|
||||
@@ -153,7 +164,6 @@ void LLPluginProcessChild::idle(void)
|
||||
{
|
||||
setState(STATE_PLUGIN_INITIALIZING);
|
||||
LLPluginMessage message("base", "init");
|
||||
message.setValue("user_data_path", mUserDataPath);
|
||||
sendMessageToPlugin(message);
|
||||
}
|
||||
break;
|
||||
@@ -225,6 +235,7 @@ void LLPluginProcessChild::idle(void)
|
||||
|
||||
void LLPluginProcessChild::sleep(F64 seconds)
|
||||
{
|
||||
deliverQueuedMessages();
|
||||
if(mMessagePipe)
|
||||
{
|
||||
mMessagePipe->pump(seconds);
|
||||
@@ -237,6 +248,7 @@ void LLPluginProcessChild::sleep(F64 seconds)
|
||||
|
||||
void LLPluginProcessChild::pump(void)
|
||||
{
|
||||
deliverQueuedMessages();
|
||||
if(mMessagePipe)
|
||||
{
|
||||
mMessagePipe->pump(0.0f);
|
||||
@@ -278,14 +290,14 @@ void LLPluginProcessChild::sendMessageToPlugin(const LLPluginMessage &message)
|
||||
{
|
||||
if (mInstance)
|
||||
{
|
||||
std::string buffer = message.generate();
|
||||
|
||||
LL_DEBUGS("Plugin") << "Sending to plugin: " << buffer << LL_ENDL;
|
||||
LLTimer elapsed;
|
||||
|
||||
mInstance->sendMessage(buffer);
|
||||
|
||||
mCPUElapsed += elapsed.getElapsedTimeF64();
|
||||
std::string buffer = message.generate();
|
||||
|
||||
LL_DEBUGS("Plugin") << "Sending to plugin: " << buffer << LL_ENDL;
|
||||
LLTimer elapsed;
|
||||
|
||||
mInstance->sendMessage(buffer);
|
||||
|
||||
mCPUElapsed += elapsed.getElapsedTimeF64();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -308,15 +320,32 @@ void LLPluginProcessChild::receiveMessageRaw(const std::string &message)
|
||||
|
||||
LL_DEBUGS("Plugin") << "Received from parent: " << message << LL_ENDL;
|
||||
|
||||
// Decode this message
|
||||
LLPluginMessage parsed;
|
||||
parsed.parse(message);
|
||||
|
||||
if(mBlockingRequest)
|
||||
{
|
||||
// We're blocking the plugin waiting for a response.
|
||||
|
||||
if(parsed.hasValue("blocking_response"))
|
||||
{
|
||||
// This is the message we've been waiting for -- fall through and send it immediately.
|
||||
mBlockingResponseReceived = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Still waiting. Queue this message and don't process it yet.
|
||||
mMessageQueue.push(message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool passMessage = true;
|
||||
|
||||
// FIXME: how should we handle queueing here?
|
||||
|
||||
{
|
||||
// Decode this message
|
||||
LLPluginMessage parsed;
|
||||
parsed.parse(message);
|
||||
|
||||
std::string message_class = parsed.getClass();
|
||||
if(message_class == LLPLUGIN_MESSAGE_CLASS_INTERNAL)
|
||||
{
|
||||
@@ -326,7 +355,6 @@ void LLPluginProcessChild::receiveMessageRaw(const std::string &message)
|
||||
if(message_name == "load_plugin")
|
||||
{
|
||||
mPluginFile = parsed.getValue("file");
|
||||
mUserDataPath = parsed.getValue("user_data_path");
|
||||
}
|
||||
else if(message_name == "shm_add")
|
||||
{
|
||||
@@ -425,7 +453,13 @@ void LLPluginProcessChild::receiveMessageRaw(const std::string &message)
|
||||
void LLPluginProcessChild::receivePluginMessage(const std::string &message)
|
||||
{
|
||||
LL_DEBUGS("Plugin") << "Received from plugin: " << message << LL_ENDL;
|
||||
|
||||
|
||||
if(mBlockingRequest)
|
||||
{
|
||||
//
|
||||
LL_ERRS("Plugin") << "Can't send a message while already waiting on a blocking request -- aborting!" << LL_ENDL;
|
||||
}
|
||||
|
||||
// Incoming message from the plugin instance
|
||||
bool passMessage = true;
|
||||
|
||||
@@ -436,6 +470,12 @@ void LLPluginProcessChild::receivePluginMessage(const std::string &message)
|
||||
// Decode this message
|
||||
LLPluginMessage parsed;
|
||||
parsed.parse(message);
|
||||
|
||||
if(parsed.hasValue("blocking_request"))
|
||||
{
|
||||
mBlockingRequest = true;
|
||||
}
|
||||
|
||||
std::string message_class = parsed.getClass();
|
||||
if(message_class == "base")
|
||||
{
|
||||
@@ -494,6 +534,19 @@ void LLPluginProcessChild::receivePluginMessage(const std::string &message)
|
||||
LL_DEBUGS("Plugin") << "Passing through to parent: " << message << LL_ENDL;
|
||||
writeMessageRaw(message);
|
||||
}
|
||||
|
||||
while(mBlockingRequest)
|
||||
{
|
||||
// The plugin wants to block and wait for a response to this message.
|
||||
sleep(mSleepTime); // this will pump the message pipe and process messages
|
||||
|
||||
if(mBlockingResponseReceived || mSocketError != APR_SUCCESS || (mMessagePipe == NULL))
|
||||
{
|
||||
// Response has been received, or we've hit an error state. Stop waiting.
|
||||
mBlockingRequest = false;
|
||||
mBlockingResponseReceived = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -502,3 +555,15 @@ void LLPluginProcessChild::setState(EState state)
|
||||
LL_DEBUGS("Plugin") << "setting state to " << state << LL_ENDL;
|
||||
mState = state;
|
||||
};
|
||||
|
||||
void LLPluginProcessChild::deliverQueuedMessages()
|
||||
{
|
||||
if(!mBlockingRequest)
|
||||
{
|
||||
while(!mMessageQueue.empty())
|
||||
{
|
||||
receiveMessageRaw(mMessageQueue.front());
|
||||
mMessageQueue.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
21
indra/llplugin/llpluginprocesschild.h
Normal file → Executable file
21
indra/llplugin/llpluginprocesschild.h
Normal file → Executable file
@@ -2,9 +2,10 @@
|
||||
* @file llpluginprocesschild.h
|
||||
* @brief LLPluginProcessChild handles the child side of the external-process plugin API.
|
||||
*
|
||||
* @cond
|
||||
* $LicenseInfo:firstyear=2008&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2008-2009, Linden Research, Inc.
|
||||
* Copyright (c) 2008-2010, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
@@ -12,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,
|
||||
@@ -28,11 +29,15 @@
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
#ifndef LL_LLPLUGINPROCESSCHILD_H
|
||||
#define LL_LLPLUGINPROCESSCHILD_H
|
||||
|
||||
#include <queue> //imprudence
|
||||
|
||||
#include "llpluginmessage.h"
|
||||
#include "llpluginmessagepipe.h"
|
||||
#include "llplugininstance.h"
|
||||
@@ -88,16 +93,15 @@ private:
|
||||
STATE_ERROR, // generic bailout state
|
||||
STATE_DONE // state machine will sit in this state after either error or normal termination.
|
||||
};
|
||||
EState mState;
|
||||
void setState(EState state);
|
||||
|
||||
EState mState;
|
||||
|
||||
LLHost mLauncherHost;
|
||||
LLSocket::ptr_t mSocket;
|
||||
|
||||
std::string mPluginFile;
|
||||
|
||||
std::string mUserDataPath;
|
||||
|
||||
LLPluginInstance *mInstance;
|
||||
|
||||
typedef std::map<std::string, LLPluginSharedMemory*> sharedMemoryRegionsType;
|
||||
@@ -106,6 +110,11 @@ private:
|
||||
LLTimer mHeartbeat;
|
||||
F64 mSleepTime;
|
||||
F64 mCPUElapsed;
|
||||
bool mBlockingRequest;
|
||||
bool mBlockingResponseReceived;
|
||||
std::queue<std::string> mMessageQueue;
|
||||
|
||||
void deliverQueuedMessages();
|
||||
|
||||
};
|
||||
|
||||
|
||||
536
indra/llplugin/llpluginprocessparent.cpp
Normal file → Executable file
536
indra/llplugin/llpluginprocessparent.cpp
Normal file → Executable file
@@ -2,9 +2,10 @@
|
||||
* @file llpluginprocessparent.cpp
|
||||
* @brief LLPluginProcessParent handles the parent side of the external-process plugin API.
|
||||
*
|
||||
* @cond
|
||||
* $LicenseInfo:firstyear=2008&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2008-2009, Linden Research, Inc.
|
||||
* Copyright (c) 2008-2010, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
@@ -12,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,
|
||||
@@ -28,6 +29,8 @@
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
#include "linden_common.h"
|
||||
@@ -44,25 +47,90 @@ LLPluginProcessParentOwner::~LLPluginProcessParentOwner()
|
||||
|
||||
}
|
||||
|
||||
LLPluginProcessParent::LLPluginProcessParent(LLPluginProcessParentOwner *owner)
|
||||
bool LLPluginProcessParent::sUseReadThread = false;
|
||||
apr_pollset_t *LLPluginProcessParent::sPollSet = NULL;
|
||||
bool LLPluginProcessParent::sPollsetNeedsRebuild = false;
|
||||
LLMutex *LLPluginProcessParent::sInstancesMutex;
|
||||
std::list<LLPluginProcessParent*> LLPluginProcessParent::sInstances;
|
||||
LLThread *LLPluginProcessParent::sReadThread = NULL;
|
||||
|
||||
|
||||
class LLPluginProcessParentPollThread: public LLThread
|
||||
{
|
||||
public:
|
||||
LLPluginProcessParentPollThread() :
|
||||
LLThread("LLPluginProcessParentPollThread", gAPRPoolp)
|
||||
{
|
||||
}
|
||||
protected:
|
||||
// Inherited from LLThread
|
||||
/*virtual*/ void run(void)
|
||||
{
|
||||
while(!isQuitting() && LLPluginProcessParent::getUseReadThread())
|
||||
{
|
||||
LLPluginProcessParent::poll(0.1f);
|
||||
checkPause();
|
||||
}
|
||||
|
||||
// Final poll to clean up the pollset, etc.
|
||||
LLPluginProcessParent::poll(0.0f);
|
||||
}
|
||||
|
||||
// Inherited from LLThread
|
||||
/*virtual*/ bool runCondition(void)
|
||||
{
|
||||
return(LLPluginProcessParent::canPollThreadRun());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
LLPluginProcessParent::LLPluginProcessParent(LLPluginProcessParentOwner *owner):
|
||||
mIncomingQueueMutex(gAPRPoolp)
|
||||
{
|
||||
if(!sInstancesMutex)
|
||||
{
|
||||
sInstancesMutex = new LLMutex(gAPRPoolp);
|
||||
}
|
||||
|
||||
mOwner = owner;
|
||||
mBoundPort = 0;
|
||||
mState = STATE_UNINITIALIZED;
|
||||
mSleepTime = 0.0;
|
||||
mCPUUsage = 0.0;
|
||||
mDisableTimeout = false;
|
||||
mDebug = false;
|
||||
mBlocked = false;
|
||||
mPolledInput = false;
|
||||
mPollFD.client_data = NULL;
|
||||
|
||||
mPluginLaunchTimeout = 60.0f;
|
||||
mPluginLockupTimeout = 15.0f;
|
||||
|
||||
// Don't start the timer here -- start it when we actually launch the plugin process.
|
||||
mHeartbeat.stop();
|
||||
|
||||
|
||||
// Don't add to the global list until fully constructed.
|
||||
{
|
||||
LLMutexLock lock(sInstancesMutex);
|
||||
sInstances.push_back(this);
|
||||
}
|
||||
}
|
||||
|
||||
LLPluginProcessParent::~LLPluginProcessParent()
|
||||
{
|
||||
LL_DEBUGS("Plugin") << "destructor" << LL_ENDL;
|
||||
|
||||
// Remove from the global list before beginning destruction.
|
||||
{
|
||||
// Make sure to get the global mutex _first_ here, to avoid a possible deadlock against LLPluginProcessParent::poll()
|
||||
LLMutexLock lock(sInstancesMutex);
|
||||
{
|
||||
LLMutexLock lock2(&mIncomingQueueMutex);
|
||||
sInstances.remove(this);
|
||||
}
|
||||
}
|
||||
|
||||
// Destroy any remaining shared memory regions
|
||||
sharedMemoryRegionsType::iterator iter;
|
||||
while((iter = mSharedMemoryRegions.begin()) != mSharedMemoryRegions.end())
|
||||
@@ -74,15 +142,17 @@ LLPluginProcessParent::~LLPluginProcessParent()
|
||||
mSharedMemoryRegions.erase(iter);
|
||||
}
|
||||
|
||||
// orphaning the process means it won't be killed when the LLProcessLauncher is destructed.
|
||||
// This is what we want -- it should exit cleanly once it notices the sockets have been closed.
|
||||
mProcess.orphan();
|
||||
mProcess.kill();
|
||||
killSockets();
|
||||
}
|
||||
|
||||
void LLPluginProcessParent::killSockets(void)
|
||||
{
|
||||
killMessagePipe();
|
||||
{
|
||||
LLMutexLock lock(&mIncomingQueueMutex);
|
||||
killMessagePipe();
|
||||
}
|
||||
|
||||
mListenSocket.reset();
|
||||
mSocket.reset();
|
||||
}
|
||||
@@ -95,14 +165,12 @@ void LLPluginProcessParent::errorState(void)
|
||||
setState(STATE_ERROR);
|
||||
}
|
||||
|
||||
void LLPluginProcessParent::init(const std::string &launcher_filename, const std::string &plugin_filename, bool debug, const std::string &user_data_path)
|
||||
void LLPluginProcessParent::init(const std::string &launcher_filename, const std::string &plugin_filename, bool debug)
|
||||
{
|
||||
mProcess.setExecutable(launcher_filename);
|
||||
mPluginFile = plugin_filename;
|
||||
mCPUUsage = 0.0f;
|
||||
mDebug = debug;
|
||||
mUserDataPath = user_data_path;
|
||||
|
||||
mDebug = debug;
|
||||
setState(STATE_INITIALIZED);
|
||||
}
|
||||
|
||||
@@ -158,21 +226,47 @@ void LLPluginProcessParent::idle(void)
|
||||
|
||||
do
|
||||
{
|
||||
// process queued messages
|
||||
mIncomingQueueMutex.lock();
|
||||
while(!mIncomingQueue.empty())
|
||||
{
|
||||
LLPluginMessage message = mIncomingQueue.front();
|
||||
mIncomingQueue.pop();
|
||||
mIncomingQueueMutex.unlock();
|
||||
|
||||
receiveMessage(message);
|
||||
|
||||
mIncomingQueueMutex.lock();
|
||||
}
|
||||
|
||||
mIncomingQueueMutex.unlock();
|
||||
|
||||
// Give time to network processing
|
||||
if(mMessagePipe)
|
||||
{
|
||||
if(!mMessagePipe->pump())
|
||||
// Drain any queued outgoing messages
|
||||
mMessagePipe->pumpOutput();
|
||||
|
||||
// Only do input processing here if this instance isn't in a pollset.
|
||||
if(!mPolledInput)
|
||||
{
|
||||
// LL_WARNS("Plugin") << "Message pipe hit an error state" << LL_ENDL;
|
||||
errorState();
|
||||
mMessagePipe->pumpInput();
|
||||
}
|
||||
}
|
||||
|
||||
if((mSocketError != APR_SUCCESS) && (mState <= STATE_RUNNING))
|
||||
|
||||
if(mState <= STATE_RUNNING)
|
||||
{
|
||||
// The socket is in an error state -- the plugin is gone.
|
||||
LL_WARNS("Plugin") << "Socket hit an error state (" << mSocketError << ")" << LL_ENDL;
|
||||
errorState();
|
||||
if(APR_STATUS_IS_EOF(mSocketError))
|
||||
{
|
||||
// Plugin socket was closed. This covers both normal plugin termination and plugin crashes.
|
||||
errorState();
|
||||
}
|
||||
else if(mSocketError != APR_SUCCESS)
|
||||
{
|
||||
// The socket is in an error state -- the plugin is gone.
|
||||
LL_WARNS("Plugin") << "Socket hit an error state (" << mSocketError << ")" << LL_ENDL;
|
||||
errorState();
|
||||
}
|
||||
}
|
||||
|
||||
// If a state needs to go directly to another state (as a performance enhancement), it can set idle_again to true after calling setState().
|
||||
@@ -314,6 +408,17 @@ void LLPluginProcessParent::idle(void)
|
||||
mDebugger.addArgument("end tell");
|
||||
mDebugger.launch();
|
||||
|
||||
#elif LL_LINUX
|
||||
|
||||
std::stringstream cmd;
|
||||
|
||||
mDebugger.setExecutable("/usr/bin/gnome-terminal");
|
||||
mDebugger.addArgument("--geometry=165x24-0+0");
|
||||
mDebugger.addArgument("-e");
|
||||
cmd << "/usr/bin/gdb -n /proc/" << mProcess.getProcessID() << "/exe " << mProcess.getProcessID();
|
||||
mDebugger.addArgument(cmd.str());
|
||||
mDebugger.launch();
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -353,13 +458,12 @@ void LLPluginProcessParent::idle(void)
|
||||
break;
|
||||
|
||||
case STATE_HELLO:
|
||||
LL_DEBUGS("Plugin") << "received hello message" << llendl;
|
||||
LL_DEBUGS("Plugin") << "received hello message" << LL_ENDL;
|
||||
|
||||
// Send the message to load the plugin
|
||||
{
|
||||
LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "load_plugin");
|
||||
message.setValue("file", mPluginFile);
|
||||
message.setValue("user_data_path", mUserDataPath);
|
||||
sendMessage(message);
|
||||
}
|
||||
|
||||
@@ -388,7 +492,7 @@ void LLPluginProcessParent::idle(void)
|
||||
}
|
||||
else if(pluginLockedUp())
|
||||
{
|
||||
LL_WARNS("Plugin") << "timeout in exiting state, bailing out" << llendl;
|
||||
LL_WARNS("Plugin") << "timeout in exiting state, bailing out" << LL_ENDL;
|
||||
errorState();
|
||||
}
|
||||
break;
|
||||
@@ -410,8 +514,7 @@ void LLPluginProcessParent::idle(void)
|
||||
break;
|
||||
|
||||
case STATE_CLEANUP:
|
||||
// Don't do a kill here anymore -- closing the sockets is the new 'kill'.
|
||||
mProcess.orphan();
|
||||
mProcess.kill();
|
||||
killSockets();
|
||||
setState(STATE_DONE);
|
||||
break;
|
||||
@@ -479,23 +582,323 @@ void LLPluginProcessParent::setSleepTime(F64 sleep_time, bool force_send)
|
||||
|
||||
void LLPluginProcessParent::sendMessage(const LLPluginMessage &message)
|
||||
{
|
||||
if(message.hasValue("blocking_response"))
|
||||
{
|
||||
mBlocked = false;
|
||||
|
||||
// reset the heartbeat timer, since there will have been no heartbeats while the plugin was blocked.
|
||||
mHeartbeat.setTimerExpirySec(mPluginLockupTimeout);
|
||||
}
|
||||
|
||||
std::string buffer = message.generate();
|
||||
LL_DEBUGS("Plugin") << "Sending: " << buffer << LL_ENDL;
|
||||
writeMessageRaw(buffer);
|
||||
|
||||
// Try to send message immediately.
|
||||
if(mMessagePipe)
|
||||
{
|
||||
mMessagePipe->pumpOutput();
|
||||
}
|
||||
}
|
||||
|
||||
//virtual
|
||||
void LLPluginProcessParent::setMessagePipe(LLPluginMessagePipe *message_pipe)
|
||||
{
|
||||
bool update_pollset = false;
|
||||
|
||||
if(mMessagePipe)
|
||||
{
|
||||
// Unsetting an existing message pipe -- remove from the pollset
|
||||
mPollFD.client_data = NULL;
|
||||
|
||||
// pollset needs an update
|
||||
update_pollset = true;
|
||||
}
|
||||
if(message_pipe != NULL)
|
||||
{
|
||||
// Set up the apr_pollfd_t
|
||||
mPollFD.p = gAPRPoolp;
|
||||
mPollFD.desc_type = APR_POLL_SOCKET;
|
||||
mPollFD.reqevents = APR_POLLIN|APR_POLLERR|APR_POLLHUP;
|
||||
mPollFD.rtnevents = 0;
|
||||
mPollFD.desc.s = mSocket->getSocket();
|
||||
mPollFD.client_data = (void*)this;
|
||||
|
||||
// pollset needs an update
|
||||
update_pollset = true;
|
||||
}
|
||||
|
||||
mMessagePipe = message_pipe;
|
||||
|
||||
if(update_pollset)
|
||||
{
|
||||
dirtyPollSet();
|
||||
}
|
||||
}
|
||||
|
||||
//static
|
||||
void LLPluginProcessParent::dirtyPollSet()
|
||||
{
|
||||
sPollsetNeedsRebuild = true;
|
||||
|
||||
if(sReadThread)
|
||||
{
|
||||
LL_DEBUGS("PluginPoll") << "unpausing read thread " << LL_ENDL;
|
||||
sReadThread->unpause();
|
||||
}
|
||||
}
|
||||
|
||||
void LLPluginProcessParent::updatePollset()
|
||||
{
|
||||
if(!sInstancesMutex)
|
||||
{
|
||||
// No instances have been created yet. There's no work to do.
|
||||
return;
|
||||
}
|
||||
|
||||
LLMutexLock lock(sInstancesMutex);
|
||||
|
||||
if(sPollSet)
|
||||
{
|
||||
LL_DEBUGS("PluginPoll") << "destroying pollset " << sPollSet << LL_ENDL;
|
||||
// delete the existing pollset.
|
||||
apr_pollset_destroy(sPollSet);
|
||||
sPollSet = NULL;
|
||||
}
|
||||
|
||||
std::list<LLPluginProcessParent*>::iterator iter;
|
||||
int count = 0;
|
||||
|
||||
// Count the number of instances that want to be in the pollset
|
||||
for(iter = sInstances.begin(); iter != sInstances.end(); iter++)
|
||||
{
|
||||
(*iter)->mPolledInput = false;
|
||||
if((*iter)->mPollFD.client_data)
|
||||
{
|
||||
// This instance has a socket that needs to be polled.
|
||||
++count;
|
||||
}
|
||||
}
|
||||
|
||||
if(sUseReadThread && sReadThread && !sReadThread->isQuitting())
|
||||
{
|
||||
if(!sPollSet && (count > 0))
|
||||
{
|
||||
#ifdef APR_POLLSET_NOCOPY
|
||||
// The pollset doesn't exist yet. Create it now.
|
||||
apr_status_t status = apr_pollset_create(&sPollSet, count, gAPRPoolp, APR_POLLSET_NOCOPY);
|
||||
if(status != APR_SUCCESS)
|
||||
{
|
||||
#endif // APR_POLLSET_NOCOPY
|
||||
LL_WARNS("PluginPoll") << "Couldn't create pollset. Falling back to non-pollset mode." << LL_ENDL;
|
||||
sPollSet = NULL;
|
||||
#ifdef APR_POLLSET_NOCOPY
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_DEBUGS("PluginPoll") << "created pollset " << sPollSet << LL_ENDL;
|
||||
|
||||
// Pollset was created, add all instances to it.
|
||||
for(iter = sInstances.begin(); iter != sInstances.end(); iter++)
|
||||
{
|
||||
if((*iter)->mPollFD.client_data)
|
||||
{
|
||||
status = apr_pollset_add(sPollSet, &((*iter)->mPollFD));
|
||||
if(status == APR_SUCCESS)
|
||||
{
|
||||
(*iter)->mPolledInput = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS("PluginPoll") << "apr_pollset_add failed with status " << status << LL_ENDL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // APR_POLLSET_NOCOPY
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LLPluginProcessParent::setUseReadThread(bool use_read_thread)
|
||||
{
|
||||
if(sUseReadThread != use_read_thread)
|
||||
{
|
||||
sUseReadThread = use_read_thread;
|
||||
|
||||
if(sUseReadThread)
|
||||
{
|
||||
if(!sReadThread)
|
||||
{
|
||||
// start up the read thread
|
||||
LL_INFOS("PluginPoll") << "creating read thread " << LL_ENDL;
|
||||
|
||||
// make sure the pollset gets rebuilt.
|
||||
sPollsetNeedsRebuild = true;
|
||||
|
||||
sReadThread = new LLPluginProcessParentPollThread;
|
||||
sReadThread->start();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(sReadThread)
|
||||
{
|
||||
// shut down the read thread
|
||||
LL_INFOS("PluginPoll") << "destroying read thread " << LL_ENDL;
|
||||
delete sReadThread;
|
||||
sReadThread = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void LLPluginProcessParent::poll(F64 timeout)
|
||||
{
|
||||
if(sPollsetNeedsRebuild || !sUseReadThread)
|
||||
{
|
||||
sPollsetNeedsRebuild = false;
|
||||
updatePollset();
|
||||
}
|
||||
|
||||
if(sPollSet)
|
||||
{
|
||||
apr_status_t status;
|
||||
apr_int32_t count;
|
||||
const apr_pollfd_t *descriptors;
|
||||
status = apr_pollset_poll(sPollSet, (apr_interval_time_t)(timeout * 1000000), &count, &descriptors);
|
||||
if(status == APR_SUCCESS)
|
||||
{
|
||||
// One or more of the descriptors signalled. Call them.
|
||||
for(int i = 0; i < count; i++)
|
||||
{
|
||||
LLPluginProcessParent *self = (LLPluginProcessParent *)(descriptors[i].client_data);
|
||||
// NOTE: the descriptor returned here is actually a COPY of the original (even though we create the pollset with APR_POLLSET_NOCOPY).
|
||||
// This means that even if the parent has set its mPollFD.client_data to NULL, the old pointer may still there in this descriptor.
|
||||
// It's even possible that the old pointer no longer points to a valid LLPluginProcessParent.
|
||||
// This means that we can't safely dereference the 'self' pointer here without some extra steps...
|
||||
if(self)
|
||||
{
|
||||
// Make sure this pointer is still in the instances list
|
||||
bool valid = false;
|
||||
{
|
||||
LLMutexLock lock(sInstancesMutex);
|
||||
for(std::list<LLPluginProcessParent*>::iterator iter = sInstances.begin(); iter != sInstances.end(); ++iter)
|
||||
{
|
||||
if(*iter == self)
|
||||
{
|
||||
// Lock the instance's mutex before unlocking the global mutex.
|
||||
// This avoids a possible race condition where the instance gets deleted between this check and the servicePoll() call.
|
||||
self->mIncomingQueueMutex.lock();
|
||||
valid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(valid)
|
||||
{
|
||||
// The instance is still valid.
|
||||
// Pull incoming messages off the socket
|
||||
self->servicePoll();
|
||||
self->mIncomingQueueMutex.unlock();
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_DEBUGS("PluginPoll") << "detected deleted instance " << self << LL_ENDL;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(APR_STATUS_IS_TIMEUP(status))
|
||||
{
|
||||
// timed out with no incoming data. Just return.
|
||||
}
|
||||
else if(status == EBADF)
|
||||
{
|
||||
// This happens when one of the file descriptors in the pollset is destroyed, which happens whenever a plugin's socket is closed.
|
||||
// The pollset has been or will be recreated, so just return.
|
||||
LL_DEBUGS("PluginPoll") << "apr_pollset_poll returned EBADF" << LL_ENDL;
|
||||
}
|
||||
else if(status != APR_SUCCESS)
|
||||
{
|
||||
LL_WARNS("PluginPoll") << "apr_pollset_poll failed with status " << status << LL_ENDL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LLPluginProcessParent::servicePoll()
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
// poll signalled on this object's socket. Try to process incoming messages.
|
||||
if(mMessagePipe)
|
||||
{
|
||||
result = mMessagePipe->pumpInput(0.0f);
|
||||
}
|
||||
|
||||
if(!result)
|
||||
{
|
||||
// If we got a read error on input, remove this pipe from the pollset
|
||||
apr_pollset_remove(sPollSet, &mPollFD);
|
||||
|
||||
// and tell the code not to re-add it
|
||||
mPollFD.client_data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void LLPluginProcessParent::receiveMessageRaw(const std::string &message)
|
||||
{
|
||||
LL_DEBUGS("Plugin") << "Received: " << message << LL_ENDL;
|
||||
|
||||
// FIXME: should this go into a queue instead?
|
||||
|
||||
LLPluginMessage parsed;
|
||||
if(parsed.parse(message) != -1)
|
||||
{
|
||||
receiveMessage(parsed);
|
||||
if(parsed.hasValue("blocking_request"))
|
||||
{
|
||||
mBlocked = true;
|
||||
}
|
||||
|
||||
if(mPolledInput)
|
||||
{
|
||||
// This is being called on the polling thread -- only do minimal processing/queueing.
|
||||
receiveMessageEarly(parsed);
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is not being called on the polling thread -- do full message processing at this time.
|
||||
receiveMessage(parsed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LLPluginProcessParent::receiveMessageEarly(const LLPluginMessage &message)
|
||||
{
|
||||
// NOTE: this function will be called from the polling thread. It will be called with mIncomingQueueMutex _already locked_.
|
||||
|
||||
bool handled = false;
|
||||
|
||||
std::string message_class = message.getClass();
|
||||
if(message_class == LLPLUGIN_MESSAGE_CLASS_INTERNAL)
|
||||
{
|
||||
// no internal messages need to be handled early.
|
||||
}
|
||||
else
|
||||
{
|
||||
// Call out to the owner and see if they to reply
|
||||
// TODO: Should this only happen when blocked?
|
||||
if(mOwner != NULL)
|
||||
{
|
||||
handled = mOwner->receivePluginMessageEarly(message);
|
||||
}
|
||||
}
|
||||
|
||||
if(!handled)
|
||||
{
|
||||
// any message that wasn't handled early needs to be queued.
|
||||
mIncomingQueue.push(message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -681,7 +1084,7 @@ std::string LLPluginProcessParent::getPluginVersion(void)
|
||||
|
||||
void LLPluginProcessParent::setState(EState state)
|
||||
{
|
||||
LL_DEBUGS("Plugin") << "setting state to " << state << LL_ENDL;
|
||||
LL_DEBUGS("Plugin") << "setting state to " << stateToString(state) << LL_ENDL;
|
||||
mState = state;
|
||||
};
|
||||
|
||||
@@ -689,18 +1092,15 @@ bool LLPluginProcessParent::pluginLockedUpOrQuit()
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
if(!mDisableTimeout && !mDebug)
|
||||
if(!mProcess.isRunning())
|
||||
{
|
||||
if(!mProcess.isRunning())
|
||||
{
|
||||
LL_WARNS("Plugin") << "child exited" << llendl;
|
||||
result = true;
|
||||
}
|
||||
else if(pluginLockedUp())
|
||||
{
|
||||
LL_WARNS("Plugin") << "timeout" << llendl;
|
||||
result = true;
|
||||
}
|
||||
LL_WARNS("Plugin") << "child exited" << LL_ENDL;
|
||||
result = true;
|
||||
}
|
||||
else if(pluginLockedUp())
|
||||
{
|
||||
LL_WARNS("Plugin") << "timeout" << LL_ENDL;
|
||||
result = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -708,7 +1108,63 @@ bool LLPluginProcessParent::pluginLockedUpOrQuit()
|
||||
|
||||
bool LLPluginProcessParent::pluginLockedUp()
|
||||
{
|
||||
if(mDisableTimeout || mDebug || mBlocked)
|
||||
{
|
||||
// Never time out a plugin process in these cases.
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the timer is running and has expired, the plugin has locked up.
|
||||
return (mHeartbeat.getStarted() && mHeartbeat.hasExpired());
|
||||
}
|
||||
|
||||
std::string LLPluginProcessParent::stateToString(EState state)
|
||||
{
|
||||
std::string eng = "unknown plugin state";
|
||||
switch (state)
|
||||
{
|
||||
case STATE_UNINITIALIZED:
|
||||
eng = "STATE_UNINITIALIZED";
|
||||
break;
|
||||
case STATE_INITIALIZED:
|
||||
eng = "STATE_INITIALIZED - init() has been called";
|
||||
break;
|
||||
case STATE_LISTENING:
|
||||
eng = "STATE_LISTENING - listening for incoming connection";
|
||||
break;
|
||||
case STATE_LAUNCHED:
|
||||
eng = "STATE_LAUNCHED - process has been launched";
|
||||
break;
|
||||
case STATE_CONNECTED:
|
||||
eng = "STATE_CONNECTED - process has connected";
|
||||
break;
|
||||
case STATE_HELLO:
|
||||
eng = "STATE_HELLO - first message from the plugin process has been received";
|
||||
break;
|
||||
case STATE_LOADING:
|
||||
eng = "STATE_LOADING - process has been asked to load the plugin";
|
||||
break;
|
||||
case STATE_RUNNING:
|
||||
eng = "STATE_RUNNING - plugin running";
|
||||
break;
|
||||
case STATE_LAUNCH_FAILURE:
|
||||
eng = "STATE_LAUNCH_FAILURE - failure before plugin loaded";
|
||||
break;
|
||||
case STATE_ERROR:
|
||||
eng = "STATE_ERROR - generic bailout state";
|
||||
break;
|
||||
case STATE_CLEANUP:
|
||||
eng = "STATE_CLEANUP - clean everything up";
|
||||
break;
|
||||
case STATE_EXITING:
|
||||
eng = "STATE_EXITING - tried to kill process, waiting for it to exit";
|
||||
break;
|
||||
case STATE_DONE:
|
||||
eng = "STATE_DONE - plugin done";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return llformat("(%d) ", (S32)state) + eng;
|
||||
}
|
||||
|
||||
49
indra/llplugin/llpluginprocessparent.h
Normal file → Executable file
49
indra/llplugin/llpluginprocessparent.h
Normal file → Executable file
@@ -2,9 +2,10 @@
|
||||
* @file llpluginprocessparent.h
|
||||
* @brief LLPluginProcessParent handles the parent side of the external-process plugin API.
|
||||
*
|
||||
* @cond
|
||||
* $LicenseInfo:firstyear=2008&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2008-2009, Linden Research, Inc.
|
||||
* Copyright (c) 2008-2010, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
@@ -12,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,
|
||||
@@ -28,11 +29,15 @@
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
#ifndef LL_LLPLUGINPROCESSPARENT_H
|
||||
#define LL_LLPLUGINPROCESSPARENT_H
|
||||
|
||||
#include <queue> //imprudence
|
||||
|
||||
#include "llapr.h"
|
||||
#include "llprocesslauncher.h"
|
||||
#include "llpluginmessage.h"
|
||||
@@ -40,12 +45,14 @@
|
||||
#include "llpluginsharedmemory.h"
|
||||
|
||||
#include "lliosocket.h"
|
||||
#include "llthread.h"
|
||||
|
||||
class LLPluginProcessParentOwner
|
||||
{
|
||||
public:
|
||||
virtual ~LLPluginProcessParentOwner();
|
||||
virtual void receivePluginMessage(const LLPluginMessage &message) = 0;
|
||||
virtual bool receivePluginMessageEarly(const LLPluginMessage &message) {return false;};
|
||||
// This will only be called when the plugin has died unexpectedly
|
||||
virtual void pluginLaunchFailed() {};
|
||||
virtual void pluginDied() {};
|
||||
@@ -58,7 +65,10 @@ public:
|
||||
LLPluginProcessParent(LLPluginProcessParentOwner *owner);
|
||||
~LLPluginProcessParent();
|
||||
|
||||
void init(const std::string &launcher_filename, const std::string &plugin_filename, bool debug, const std::string &user_data_path);
|
||||
void init(const std::string &launcher_filename,
|
||||
const std::string &plugin_filename,
|
||||
bool debug);
|
||||
|
||||
void idle(void);
|
||||
|
||||
// returns true if the plugin is on its way to steady state
|
||||
@@ -70,6 +80,9 @@ public:
|
||||
// returns true if the process has exited or we've had a fatal error
|
||||
bool isDone(void);
|
||||
|
||||
// returns true if the process is currently waiting on a blocking request
|
||||
bool isBlocked(void) { return mBlocked; };
|
||||
|
||||
void killSockets(void);
|
||||
|
||||
// Go to the proper error state
|
||||
@@ -83,7 +96,9 @@ public:
|
||||
void receiveMessage(const LLPluginMessage &message);
|
||||
|
||||
// Inherited from LLPluginMessagePipeOwner
|
||||
void receiveMessageRaw(const std::string &message);
|
||||
/*virtual*/ void receiveMessageRaw(const std::string &message);
|
||||
/*virtual*/ void receiveMessageEarly(const LLPluginMessage &message);
|
||||
/*virtual*/ void setMessagePipe(LLPluginMessagePipe *message_pipe) ;
|
||||
|
||||
// This adds a memory segment shared with the client, generating a name for the segment. The name generated is guaranteed to be unique on the host.
|
||||
// The caller must call removeSharedMemory first (and wait until getSharedMemorySize returns 0 for the indicated name) before re-adding a segment with the same name.
|
||||
@@ -106,7 +121,11 @@ public:
|
||||
void setLockupTimeout(F32 timeout) { mPluginLockupTimeout = timeout; };
|
||||
|
||||
F64 getCPUUsage() { return mCPUUsage; };
|
||||
|
||||
|
||||
static void poll(F64 timeout);
|
||||
static bool canPollThreadRun() { return (sPollSet || sPollsetNeedsRebuild || sUseReadThread); };
|
||||
static void setUseReadThread(bool use_read_thread);
|
||||
static bool getUseReadThread() { return sUseReadThread; };
|
||||
private:
|
||||
|
||||
enum EState
|
||||
@@ -128,6 +147,7 @@ private:
|
||||
};
|
||||
EState mState;
|
||||
void setState(EState state);
|
||||
std::string stateToString(EState state);
|
||||
|
||||
bool pluginLockedUp();
|
||||
bool pluginLockedUpOrQuit();
|
||||
@@ -142,8 +162,6 @@ private:
|
||||
|
||||
std::string mPluginFile;
|
||||
|
||||
std::string mUserDataPath;
|
||||
|
||||
LLPluginProcessParentOwner *mOwner;
|
||||
|
||||
typedef std::map<std::string, LLPluginSharedMemory*> sharedMemoryRegionsType;
|
||||
@@ -158,12 +176,27 @@ private:
|
||||
|
||||
bool mDisableTimeout;
|
||||
bool mDebug;
|
||||
bool mBlocked;
|
||||
bool mPolledInput;
|
||||
|
||||
LLProcessLauncher mDebugger;
|
||||
|
||||
F32 mPluginLaunchTimeout; // Somewhat longer timeout for initial launch.
|
||||
F32 mPluginLockupTimeout; // If we don't receive a heartbeat in this many seconds, we declare the plugin locked up.
|
||||
|
||||
static bool sUseReadThread;
|
||||
apr_pollfd_t mPollFD;
|
||||
static apr_pollset_t *sPollSet;
|
||||
static bool sPollsetNeedsRebuild;
|
||||
static LLMutex *sInstancesMutex;
|
||||
static std::list<LLPluginProcessParent*> sInstances;
|
||||
static void dirtyPollSet();
|
||||
static void updatePollset();
|
||||
void servicePoll();
|
||||
static LLThread *sReadThread;
|
||||
|
||||
LLMutex mIncomingQueueMutex;
|
||||
std::queue<LLPluginMessage> mIncomingQueue;
|
||||
};
|
||||
|
||||
#endif // LL_LLPLUGINPROCESSPARENT_H
|
||||
|
||||
11
indra/llplugin/llpluginsharedmemory.cpp
Normal file → Executable file
11
indra/llplugin/llpluginsharedmemory.cpp
Normal file → Executable file
@@ -1,10 +1,11 @@
|
||||
/**
|
||||
* @file llpluginsharedmemory.cpp
|
||||
* @brief LLPluginSharedMemory manages a shared memory segment for use by the LLPlugin API.
|
||||
* LLPluginSharedMemory manages a shared memory segment for use by the LLPlugin API.
|
||||
*
|
||||
* @cond
|
||||
* $LicenseInfo:firstyear=2008&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2008-2009, Linden Research, Inc.
|
||||
* Copyright (c) 2008-2010, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
@@ -12,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,
|
||||
@@ -28,6 +29,8 @@
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
#include "linden_common.h"
|
||||
|
||||
10
indra/llplugin/llpluginsharedmemory.h
Normal file → Executable file
10
indra/llplugin/llpluginsharedmemory.h
Normal file → Executable file
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
* @file llpluginsharedmemory.h
|
||||
* @brief LLPluginSharedMemory manages a shared memory segment for use by the LLPlugin API.
|
||||
*
|
||||
* @cond
|
||||
* $LicenseInfo:firstyear=2008&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2008-2009, Linden Research, Inc.
|
||||
* Copyright (c) 2008-2010, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
@@ -12,13 +12,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,
|
||||
@@ -28,6 +28,8 @@
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
#ifndef LL_LLPLUGINSHAREDMEMORY_H
|
||||
|
||||
42
indra/llplugin/slplugin/CMakeLists.txt
Normal file → Executable file
42
indra/llplugin/slplugin/CMakeLists.txt
Normal file → Executable file
@@ -16,6 +16,7 @@ include_directories(
|
||||
if (DARWIN)
|
||||
include(CMakeFindFrameworks)
|
||||
find_library(CARBON_LIBRARY Carbon)
|
||||
find_library(COCOA_LIBRARY Cocoa)
|
||||
endif (DARWIN)
|
||||
|
||||
|
||||
@@ -25,11 +26,33 @@ set(SLPlugin_SOURCE_FILES
|
||||
slplugin.cpp
|
||||
)
|
||||
|
||||
if (DARWIN)
|
||||
list(APPEND SLPlugin_SOURCE_FILES
|
||||
slplugin-objc.mm
|
||||
)
|
||||
list(APPEND SLPlugin_HEADER_FILES
|
||||
slplugin-objc.h
|
||||
)
|
||||
endif (DARWIN)
|
||||
|
||||
set_source_files_properties(${SLPlugin_HEADER_FILES}
|
||||
PROPERTIES HEADER_FILE_ONLY TRUE)
|
||||
|
||||
if (SLPlugin_HEADER_FILES)
|
||||
list(APPEND SLPlugin_SOURCE_FILES ${SLPlugin_HEADER_FILES})
|
||||
endif (SLPlugin_HEADER_FILES)
|
||||
|
||||
add_executable(SLPlugin
|
||||
WIN32
|
||||
MACOSX_BUNDLE
|
||||
${SLPlugin_SOURCE_FILES}
|
||||
)
|
||||
|
||||
set_target_properties(SLPlugin
|
||||
PROPERTIES
|
||||
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/slplugin_info.plist
|
||||
)
|
||||
|
||||
target_link_libraries(SLPlugin
|
||||
${LLPLUGIN_LIBRARIES}
|
||||
${LLMESSAGE_LIBRARIES}
|
||||
@@ -44,15 +67,16 @@ add_dependencies(SLPlugin
|
||||
)
|
||||
|
||||
if (DARWIN)
|
||||
# Mac version needs to link against carbon, and also needs an embedded plist (to set LSBackgroundOnly)
|
||||
target_link_libraries(SLPlugin ${CARBON_LIBRARY})
|
||||
set_target_properties(
|
||||
SLPlugin
|
||||
PROPERTIES
|
||||
LINK_FLAGS "-Wl,-sectcreate,__TEXT,__info_plist,${CMAKE_CURRENT_SOURCE_DIR}/slplugin_info.plist"
|
||||
# Mac version needs to link against Carbon
|
||||
target_link_libraries(SLPlugin ${CARBON_LIBRARY} ${COCOA_LIBRARY})
|
||||
# Make sure the app bundle has a Resources directory (it will get populated by viewer-manifest.py later)
|
||||
add_custom_command(
|
||||
TARGET SLPlugin POST_BUILD
|
||||
COMMAND mkdir
|
||||
ARGS
|
||||
-p
|
||||
${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/SLPlugin.app/Contents/Resources
|
||||
)
|
||||
endif (DARWIN)
|
||||
|
||||
if (LINUX)
|
||||
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lrt")
|
||||
endif (LINUX)
|
||||
#ll_deploy_sharedlibs_command(SLPlugin)
|
||||
|
||||
42
indra/llplugin/slplugin/slplugin-objc.h
Normal file
42
indra/llplugin/slplugin/slplugin-objc.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* @file slplugin-objc.h
|
||||
* @brief Header file for slplugin-objc.mm.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
|
||||
|
||||
/* Defined in slplugin-objc.mm: */
|
||||
void setupCocoa();
|
||||
void createAutoReleasePool();
|
||||
void deleteAutoReleasePool();
|
||||
89
indra/llplugin/slplugin/slplugin-objc.mm
Normal file
89
indra/llplugin/slplugin/slplugin-objc.mm
Normal file
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* @file slplugin-objc.mm
|
||||
* @brief Objective-C++ file for use with the loader shell, so we can use a couple of Cocoa APIs.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
|
||||
|
||||
#include <AppKit/AppKit.h>
|
||||
|
||||
#include "slplugin-objc.h"
|
||||
|
||||
|
||||
void setupCocoa()
|
||||
{
|
||||
static bool inited = false;
|
||||
|
||||
if(!inited)
|
||||
{
|
||||
createAutoReleasePool();
|
||||
|
||||
// The following prevents the Cocoa command line parser from trying to open 'unknown' arguements as documents.
|
||||
// ie. running './secondlife -set Language fr' would cause a pop-up saying can't open document 'fr'
|
||||
// when init'ing the Cocoa App window.
|
||||
[[NSUserDefaults standardUserDefaults] setObject:@"NO" forKey:@"NSTreatUnknownArgumentsAsOpen"];
|
||||
|
||||
// This is a bit of voodoo taken from the Apple sample code "CarbonCocoa_PictureCursor":
|
||||
// http://developer.apple.com/samplecode/CarbonCocoa_PictureCursor/index.html
|
||||
|
||||
// Needed for Carbon based applications which call into Cocoa
|
||||
NSApplicationLoad();
|
||||
|
||||
// Must first call [[[NSWindow alloc] init] release] to get the NSWindow machinery set up so that NSCursor can use a window to cache the cursor image
|
||||
[[[NSWindow alloc] init] release];
|
||||
|
||||
deleteAutoReleasePool();
|
||||
|
||||
inited = true;
|
||||
}
|
||||
}
|
||||
|
||||
static NSAutoreleasePool *sPool = NULL;
|
||||
|
||||
void createAutoReleasePool()
|
||||
{
|
||||
if(!sPool)
|
||||
{
|
||||
sPool = [[NSAutoreleasePool alloc] init];
|
||||
}
|
||||
}
|
||||
|
||||
void deleteAutoReleasePool()
|
||||
{
|
||||
if(sPool)
|
||||
{
|
||||
[sPool release];
|
||||
sPool = NULL;
|
||||
}
|
||||
}
|
||||
170
indra/llplugin/slplugin/slplugin.cpp
Normal file → Executable file
170
indra/llplugin/slplugin/slplugin.cpp
Normal file → Executable file
@@ -1,10 +1,12 @@
|
||||
/**
|
||||
/**
|
||||
* @file slplugin.cpp
|
||||
* @brief Loader shell for plugins, intended to be launched by the plugin host application, which directly loads a plugin dynamic library.
|
||||
*
|
||||
* @cond
|
||||
*
|
||||
* $LicenseInfo:firstyear=2008&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2008-2009, Linden Research, Inc.
|
||||
* Copyright (c) 2008-2010, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
@@ -12,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,
|
||||
@@ -28,6 +30,9 @@
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*
|
||||
*
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
|
||||
@@ -41,6 +46,7 @@
|
||||
|
||||
#if LL_DARWIN
|
||||
#include <Carbon/Carbon.h>
|
||||
#include "slplugin-objc.h"
|
||||
#endif
|
||||
|
||||
#if LL_DARWIN || LL_LINUX
|
||||
@@ -48,16 +54,17 @@
|
||||
#endif
|
||||
|
||||
/*
|
||||
On Mac OS, since we call WaitNextEvent, this process will show up in the dock unless we set the LSBackgroundOnly flag in the Info.plist.
|
||||
|
||||
On Mac OS, since we call WaitNextEvent, this process will show up in the dock unless we set the LSBackgroundOnly or LSUIElement flag in the Info.plist.
|
||||
|
||||
Normally non-bundled binaries don't have an info.plist file, but it's possible to embed one in the binary by adding this to the linker flags:
|
||||
|
||||
|
||||
-sectcreate __TEXT __info_plist /path/to/slplugin_info.plist
|
||||
|
||||
|
||||
which means adding this to the gcc flags:
|
||||
|
||||
|
||||
-Wl,-sectcreate,__TEXT,__info_plist,/path/to/slplugin_info.plist
|
||||
|
||||
Now that SLPlugin is a bundled app on the Mac, this is no longer necessary (it can just use a regular Info.plist file), but I'm leaving this comment in for posterity.
|
||||
*/
|
||||
|
||||
#if LL_DARWIN || LL_LINUX
|
||||
@@ -68,7 +75,7 @@ static void crash_handler(int sig)
|
||||
// TODO: add our own crash reporting
|
||||
_exit(1);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if LL_WINDOWS
|
||||
#include <windows.h>
|
||||
@@ -81,7 +88,7 @@ LONG WINAPI myWin32ExceptionHandler( struct _EXCEPTION_POINTERS* exception_infop
|
||||
//std::cerr << "intercepted an unhandled exception and will exit immediately." << std::endl;
|
||||
|
||||
// TODO: replace exception handler before we exit?
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
}
|
||||
|
||||
// Taken from : http://blog.kalmbachnet.de/?postid=75
|
||||
@@ -153,7 +160,7 @@ bool checkExceptionHandler()
|
||||
if (prev_filter == NULL)
|
||||
{
|
||||
ok = FALSE;
|
||||
if (myWin32ExceptionHandler == NULL)
|
||||
if (NULL == myWin32ExceptionHandler)
|
||||
{
|
||||
LL_WARNS("AppInit") << "Exception handler uninitialized." << LL_ENDL;
|
||||
}
|
||||
@@ -167,7 +174,7 @@ bool checkExceptionHandler()
|
||||
}
|
||||
#endif
|
||||
|
||||
// If this application on Windows platform is a console application, a console is always
|
||||
// If this application on Windows platform is a console application, a console is always
|
||||
// created which is bad. Making it a Windows "application" via CMake settings but not
|
||||
// adding any code to explicitly create windows does the right thing.
|
||||
#if LL_WINDOWS
|
||||
@@ -178,7 +185,7 @@ int main(int argc, char **argv)
|
||||
{
|
||||
ll_init_apr();
|
||||
|
||||
// Set up llerror logging
|
||||
// Set up llerror logging
|
||||
{
|
||||
LLError::initForApplication(".");
|
||||
LLError::setDefaultLevel(LLError::LEVEL_INFO);
|
||||
@@ -191,25 +198,23 @@ int main(int argc, char **argv)
|
||||
{
|
||||
LL_ERRS("slplugin") << "usage: " << "SLPlugin" << " launcher_port" << LL_ENDL;
|
||||
};
|
||||
|
||||
|
||||
U32 port = 0;
|
||||
if(!LLStringUtil::convertToU32(lpCmdLine, port))
|
||||
{
|
||||
LL_ERRS("slplugin") << "port number must be numeric" << LL_ENDL;
|
||||
};
|
||||
|
||||
// Insert our exception handler into the system so this plugin doesn't
|
||||
// Insert our exception handler into the system so this plugin doesn't
|
||||
// display a crash message if something bad happens. The host app will
|
||||
// see the missing heartbeat and log appropriately.
|
||||
initExceptionHandler();
|
||||
#elif LL_DARWIN || LL_LINUX
|
||||
setpriority(PRIO_PROCESS, getpid(), 19);
|
||||
|
||||
if(argc < 2)
|
||||
{
|
||||
LL_ERRS("slplugin") << "usage: " << argv[0] << " launcher_port" << LL_ENDL;
|
||||
}
|
||||
|
||||
|
||||
U32 port = 0;
|
||||
if(!LLStringUtil::convertToU32(argv[1], port))
|
||||
{
|
||||
@@ -227,23 +232,50 @@ int main(int argc, char **argv)
|
||||
signal(SIGSYS, &crash_handler); // non-existent system call invoked
|
||||
#endif
|
||||
|
||||
#if LL_DARWIN
|
||||
setupCocoa();
|
||||
createAutoReleasePool();
|
||||
#endif
|
||||
|
||||
LLPluginProcessChild *plugin = new LLPluginProcessChild();
|
||||
|
||||
plugin->init(port);
|
||||
|
||||
|
||||
#if LL_DARWIN
|
||||
deleteAutoReleasePool();
|
||||
#endif
|
||||
|
||||
LLTimer timer;
|
||||
timer.start();
|
||||
|
||||
#if LL_WINDOWS
|
||||
checkExceptionHandler();
|
||||
#endif
|
||||
|
||||
|
||||
#if LL_DARWIN
|
||||
// If the plugin opens a new window (such as the Flash plugin's fullscreen player), we may need to bring this plugin process to the foreground.
|
||||
// Use this to track the current frontmost window and bring this process to the front if it changes.
|
||||
WindowRef front_window = NULL;
|
||||
WindowGroupRef layer_group = NULL;
|
||||
int window_hack_state = 0;
|
||||
CreateWindowGroup(kWindowGroupAttrFixedLevel, &layer_group);
|
||||
if(layer_group)
|
||||
{
|
||||
// Start out with a window layer that's way out in front (fixes the problem with the menubar not getting hidden on first switch to fullscreen youtube)
|
||||
SetWindowGroupName(layer_group, CFSTR("SLPlugin Layer"));
|
||||
SetWindowGroupLevel(layer_group, kCGOverlayWindowLevel);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if LL_DARWIN
|
||||
EventTargetRef event_target = GetEventDispatcherTarget();
|
||||
#endif
|
||||
while(!plugin->isDone())
|
||||
{
|
||||
timer.reset();
|
||||
#if LL_DARWIN
|
||||
createAutoReleasePool();
|
||||
#endif
|
||||
timer.reset();
|
||||
plugin->idle();
|
||||
#if LL_DARWIN
|
||||
{
|
||||
@@ -254,11 +286,85 @@ int main(int argc, char **argv)
|
||||
SendEventToEventTarget (event, event_target);
|
||||
ReleaseEvent(event);
|
||||
}
|
||||
|
||||
// Check for a change in this process's frontmost window.
|
||||
if(FrontWindow() != front_window)
|
||||
{
|
||||
ProcessSerialNumber self = { 0, kCurrentProcess };
|
||||
ProcessSerialNumber parent = { 0, kNoProcess };
|
||||
ProcessSerialNumber front = { 0, kNoProcess };
|
||||
Boolean this_is_front_process = false;
|
||||
Boolean parent_is_front_process = false;
|
||||
{
|
||||
// Get this process's parent
|
||||
ProcessInfoRec info;
|
||||
info.processInfoLength = sizeof(ProcessInfoRec);
|
||||
info.processName = NULL;
|
||||
info.processAppSpec = NULL;
|
||||
if(GetProcessInformation( &self, &info ) == noErr)
|
||||
{
|
||||
parent = info.processLauncher;
|
||||
}
|
||||
|
||||
// and figure out whether this process or its parent are currently frontmost
|
||||
if(GetFrontProcess(&front) == noErr)
|
||||
{
|
||||
(void) SameProcess(&self, &front, &this_is_front_process);
|
||||
(void) SameProcess(&parent, &front, &parent_is_front_process);
|
||||
}
|
||||
}
|
||||
|
||||
if((FrontWindow() != NULL) && (front_window == NULL))
|
||||
{
|
||||
// Opening the first window
|
||||
|
||||
if(window_hack_state == 0)
|
||||
{
|
||||
// Next time through the event loop, lower the window group layer
|
||||
window_hack_state = 1;
|
||||
}
|
||||
|
||||
if(layer_group)
|
||||
{
|
||||
SetWindowGroup(FrontWindow(), layer_group);
|
||||
}
|
||||
|
||||
if(parent_is_front_process)
|
||||
{
|
||||
// Bring this process's windows to the front.
|
||||
(void) SetFrontProcess( &self );
|
||||
}
|
||||
|
||||
ActivateWindow(FrontWindow(), true);
|
||||
}
|
||||
else if((FrontWindow() == NULL) && (front_window != NULL))
|
||||
{
|
||||
// Closing the last window
|
||||
|
||||
if(this_is_front_process)
|
||||
{
|
||||
// Try to bring this process's parent to the front
|
||||
(void) SetFrontProcess(&parent);
|
||||
}
|
||||
}
|
||||
else if(window_hack_state == 1)
|
||||
{
|
||||
if(layer_group)
|
||||
{
|
||||
// Set the window group level back to something less extreme
|
||||
SetWindowGroupLevel(layer_group, kCGNormalWindowLevel);
|
||||
}
|
||||
window_hack_state = 2;
|
||||
}
|
||||
|
||||
front_window = FrontWindow();
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
F64 elapsed = timer.getElapsedTimeF64();
|
||||
F64 remaining = plugin->getSleepTime() - elapsed;
|
||||
|
||||
|
||||
if(remaining <= 0.0f)
|
||||
{
|
||||
// We've already used our full allotment.
|
||||
@@ -271,26 +377,30 @@ int main(int argc, char **argv)
|
||||
{
|
||||
|
||||
// LL_INFOS("slplugin") << "elapsed = " << elapsed * 1000.0f << " ms, remaining = " << remaining * 1000.0f << " ms, sleeping for " << remaining * 1000.0f << " ms" << LL_ENDL;
|
||||
// timer.reset();
|
||||
|
||||
// timer.reset();
|
||||
|
||||
// This also services the network as needed.
|
||||
plugin->sleep(remaining);
|
||||
|
||||
|
||||
// LL_INFOS("slplugin") << "slept for "<< timer.getElapsedTimeF64() * 1000.0f << " ms" << LL_ENDL;
|
||||
}
|
||||
|
||||
#if LL_WINDOWS
|
||||
// More agressive checking of interfering exception handlers.
|
||||
// Doesn't appear to be required so far - even for plugins
|
||||
// that do crash with a single call to the intercept
|
||||
// Doesn't appear to be required so far - even for plugins
|
||||
// that do crash with a single call to the intercept
|
||||
// exception handler such as QuickTime.
|
||||
//checkExceptionHandler();
|
||||
#endif
|
||||
|
||||
#if LL_DARWIN
|
||||
deleteAutoReleasePool();
|
||||
#endif
|
||||
}
|
||||
|
||||
delete plugin;
|
||||
|
||||
ll_cleanup_apr();
|
||||
|
||||
ll_cleanup_apr();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
4
indra/llplugin/slplugin/slplugin_info.plist
Normal file → Executable file
4
indra/llplugin/slplugin/slplugin_info.plist
Normal file → Executable file
@@ -6,7 +6,7 @@
|
||||
<string>English</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>LSBackgroundOnly</key>
|
||||
<true/>
|
||||
<key>LSUIElement</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
8
indra/media_plugins/CMakeLists.txt
Normal file → Executable file
8
indra/media_plugins/CMakeLists.txt
Normal file → Executable file
@@ -4,10 +4,12 @@ add_subdirectory(base)
|
||||
|
||||
add_subdirectory(webkit)
|
||||
|
||||
add_subdirectory(gstreamer010)
|
||||
if (LINUX OR DARWIN)
|
||||
add_subdirectory(gstreamer010)
|
||||
endif (LINUX OR DARWIN)
|
||||
|
||||
if (WINDOWS OR DARWIN)
|
||||
if (WINDOWS)
|
||||
add_subdirectory(quicktime)
|
||||
endif (WINDOWS OR DARWIN)
|
||||
endif (WINDOWS)
|
||||
|
||||
add_subdirectory(example)
|
||||
|
||||
@@ -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
|
||||
@@ -47,7 +47,3 @@ add_library(media_plugin_base
|
||||
${media_plugin_base_SOURCE_FILES}
|
||||
)
|
||||
|
||||
add_dependencies(media_plugin_base
|
||||
prepare
|
||||
)
|
||||
|
||||
|
||||
9
indra/media_plugins/base/media_plugin_base.cpp
Normal file → Executable file
9
indra/media_plugins/base/media_plugin_base.cpp
Normal file → Executable file
@@ -4,9 +4,10 @@
|
||||
*
|
||||
* All plugins should be a subclass of MediaPluginBase.
|
||||
*
|
||||
* @cond
|
||||
* $LicenseInfo:firstyear=2008&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2008-2009, Linden Research, Inc.
|
||||
* Copyright (c) 2008-2010, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
@@ -14,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,
|
||||
@@ -30,6 +31,8 @@
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
#include "linden_common.h"
|
||||
|
||||
0
indra/media_plugins/base/media_plugin_base.exp
Normal file → Executable file
0
indra/media_plugins/base/media_plugin_base.exp
Normal file → Executable file
9
indra/media_plugins/base/media_plugin_base.h
Normal file → Executable file
9
indra/media_plugins/base/media_plugin_base.h
Normal file → Executable file
@@ -2,9 +2,10 @@
|
||||
* @file media_plugin_base.h
|
||||
* @brief Media plugin base class for LLMedia API plugin system
|
||||
*
|
||||
* @cond
|
||||
* $LicenseInfo:firstyear=2008&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2008-2009, Linden Research, Inc.
|
||||
* Copyright (c) 2008-2010, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
@@ -12,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,
|
||||
@@ -28,6 +29,8 @@
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
#include "linden_common.h"
|
||||
|
||||
@@ -14,7 +14,7 @@ include(PluginAPI)
|
||||
include(MediaPluginBase)
|
||||
include(FindOpenGL)
|
||||
|
||||
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
|
||||
|
||||
44
indra/media_plugins/example/media_plugin_example.cpp
Normal file → Executable file
44
indra/media_plugins/example/media_plugin_example.cpp
Normal file → Executable file
@@ -2,10 +2,11 @@
|
||||
* @file media_plugin_example.cpp
|
||||
* @brief Example plugin for LLMedia API plugin system
|
||||
*
|
||||
* @cond
|
||||
* $LicenseInfo:firstyear=2008&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2009, Linden Research, Inc.
|
||||
*
|
||||
*
|
||||
* Copyright (c) 2008-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
|
||||
@@ -13,20 +14,23 @@
|
||||
* ("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
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include "linden_common.h"
|
||||
@@ -117,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" )
|
||||
@@ -189,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" );
|
||||
|
||||
@@ -32,16 +32,14 @@ include_directories(
|
||||
|
||||
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}
|
||||
|
||||
9
indra/media_plugins/gstreamer010/llmediaimplgstreamer.h
Normal file → Executable file
9
indra/media_plugins/gstreamer010/llmediaimplgstreamer.h
Normal file → Executable file
@@ -3,9 +3,10 @@
|
||||
* @author Tofu Linden
|
||||
* @brief implementation that supports media playback via GStreamer.
|
||||
*
|
||||
* @cond
|
||||
* $LicenseInfo:firstyear=2007&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2007-2009, Linden Research, Inc.
|
||||
* Copyright (c) 2007-2010, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
@@ -13,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,
|
||||
@@ -29,6 +30,8 @@
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
// header guard
|
||||
|
||||
@@ -1,171 +0,0 @@
|
||||
/**
|
||||
* @file llmediaimplgstreamer_syms.cpp
|
||||
* @brief dynamic GStreamer symbol-grabbing code
|
||||
*
|
||||
* $LicenseInfo:firstyear=2007&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2007-2009, 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://secondlifegrid.net/programs/open_source/licensing/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
|
||||
*
|
||||
* 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$
|
||||
*/
|
||||
|
||||
#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
|
||||
@@ -1,78 +0,0 @@
|
||||
/**
|
||||
* @file llmediaimplgstreamer_syms.h
|
||||
* @brief dynamic GStreamer symbol-grabbing code
|
||||
*
|
||||
* $LicenseInfo:firstyear=2007&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2007-2009, 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://secondlifegrid.net/programs/open_source/licensing/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
|
||||
*
|
||||
* 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$
|
||||
*/
|
||||
|
||||
#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
|
||||
@@ -1,51 +0,0 @@
|
||||
|
||||
// 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);
|
||||
@@ -1,5 +0,0 @@
|
||||
|
||||
// required symbols to grab
|
||||
LL_GST_SYM(true, gst_video_sink_get_type, GType, void);
|
||||
|
||||
// optional symbols to grab
|
||||
9
indra/media_plugins/gstreamer010/llmediaimplgstreamertriviallogging.h
Normal file → Executable file
9
indra/media_plugins/gstreamer010/llmediaimplgstreamertriviallogging.h
Normal file → Executable file
@@ -2,9 +2,10 @@
|
||||
* @file llmediaimplgstreamertriviallogging.h
|
||||
* @brief minimal logging utilities.
|
||||
*
|
||||
* @cond
|
||||
* $LicenseInfo:firstyear=2009&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2009, Linden Research, Inc.
|
||||
* Copyright (c) 2009-2010, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
@@ -12,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,
|
||||
@@ -28,6 +29,8 @@
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
#ifndef __LLMEDIAIMPLGSTREAMERTRIVIALLOGGING_H__
|
||||
|
||||
173
indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.cpp
Normal file → Executable file
173
indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.cpp
Normal file → Executable file
@@ -1,10 +1,11 @@
|
||||
/**
|
||||
* @file llmediaimplgstreamervidplug.cpp
|
||||
* @file llmediaimplgstreamervidplug.h
|
||||
* @brief Video-consuming static GStreamer plugin for gst-to-LLMediaImpl
|
||||
*
|
||||
* @cond
|
||||
* $LicenseInfo:firstyear=2007&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2007-2009, Linden Research, Inc.
|
||||
* Copyright (c) 2007-2010, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
@@ -12,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,
|
||||
@@ -28,6 +29,8 @@
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
#if LL_GSTREAMER010_ENABLED
|
||||
@@ -38,15 +41,29 @@
|
||||
#include <gst/video/video.h>
|
||||
#include <gst/video/gstvideosink.h>
|
||||
|
||||
#include "llmediaimplgstreamer_syms.h"
|
||||
#include "llmediaimplgstreamertriviallogging.h"
|
||||
// #include "llthread.h"
|
||||
|
||||
#include "llmediaimplgstreamervidplug.h"
|
||||
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC (gst_slvideo_debug);
|
||||
#define GST_CAT_DEFAULT gst_slvideo_debug
|
||||
|
||||
/* Filter signals and args *//*
|
||||
enum
|
||||
{
|
||||
*//* FILL ME *//*
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
ARG_0
|
||||
};
|
||||
|
||||
#define SLV_SIZECAPS ", width=(int){1,2,4,8,16,32,64,128,256,512,1024}, height=(int){1,2,4,8,16,32,64,128,256,512,1024} "
|
||||
#define SLV_ALLCAPS GST_VIDEO_CAPS_RGBx SLV_SIZECAPS ";" GST_VIDEO_CAPS_BGRx SLV_SIZECAPS
|
||||
*/
|
||||
|
||||
#define SLV_SIZECAPS ", width=(int)[1,2048], height=(int)[1,2048] "
|
||||
#define SLV_ALLCAPS GST_VIDEO_CAPS_RGBx SLV_SIZECAPS
|
||||
@@ -78,9 +95,9 @@ gst_slvideo_base_init (gpointer gclass)
|
||||
};
|
||||
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);
|
||||
gst_element_class_add_pad_template (element_class,
|
||||
gst_static_pad_template_get (&sink_factory));
|
||||
gst_element_class_set_details (element_class, &element_details);
|
||||
}
|
||||
|
||||
|
||||
@@ -91,7 +108,7 @@ gst_slvideo_finalize (GObject * object)
|
||||
slvideo = GST_SLVIDEO (object);
|
||||
if (slvideo->caps)
|
||||
{
|
||||
llgst_caps_unref(slvideo->caps);
|
||||
gst_caps_unref(slvideo->caps);
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS(parent_class)->finalize (object);
|
||||
@@ -102,7 +119,7 @@ static GstFlowReturn
|
||||
gst_slvideo_show_frame (GstBaseSink * bsink, GstBuffer * buf)
|
||||
{
|
||||
GstSLVideo *slvideo;
|
||||
llg_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
|
||||
g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
|
||||
|
||||
slvideo = GST_SLVIDEO(bsink);
|
||||
|
||||
@@ -194,7 +211,7 @@ gst_slvideo_get_caps (GstBaseSink * bsink)
|
||||
GstSLVideo *slvideo;
|
||||
slvideo = GST_SLVIDEO(bsink);
|
||||
|
||||
return llgst_caps_ref (slvideo->caps);
|
||||
return gst_caps_ref (slvideo->caps);
|
||||
}
|
||||
|
||||
|
||||
@@ -204,21 +221,32 @@ gst_slvideo_set_caps (GstBaseSink * bsink, GstCaps * caps)
|
||||
{
|
||||
GstSLVideo *filter;
|
||||
GstStructure *structure;
|
||||
// GstCaps *intersection;
|
||||
|
||||
GST_DEBUG ("set caps with %" GST_PTR_FORMAT, caps);
|
||||
|
||||
filter = GST_SLVIDEO(bsink);
|
||||
|
||||
int width, height;
|
||||
/*
|
||||
intersection = gst_caps_intersect (filter->caps, caps);
|
||||
if (gst_caps_is_empty (intersection))
|
||||
{
|
||||
// no overlap between our caps and requested caps
|
||||
return FALSE;
|
||||
}
|
||||
gst_caps_unref(intersection);
|
||||
*/
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
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");
|
||||
structure = gst_caps_get_structure (caps, 0);
|
||||
ret = gst_structure_get_int (structure, "width", &width);
|
||||
ret = ret && gst_structure_get_int (structure, "height", &height);
|
||||
fps = gst_structure_get_value (structure, "framerate");
|
||||
ret = ret && (fps != NULL);
|
||||
par = llgst_structure_get_value (structure, "pixel-aspect-ratio");
|
||||
par = gst_structure_get_value (structure, "pixel-aspect-ratio");
|
||||
if (!ret)
|
||||
return FALSE;
|
||||
|
||||
@@ -228,34 +256,35 @@ gst_slvideo_set_caps (GstBaseSink * bsink, GstCaps * caps)
|
||||
|
||||
filter->width = width;
|
||||
filter->height = height;
|
||||
|
||||
filter->fps_n = llgst_value_get_fraction_numerator(fps);
|
||||
filter->fps_d = llgst_value_get_fraction_denominator(fps);
|
||||
filter->fps_n = gst_value_get_fraction_numerator(fps);
|
||||
filter->fps_d = gst_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);
|
||||
filter->par_n = gst_value_get_fraction_numerator(par);
|
||||
filter->par_d = gst_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),
|
||||
if (0 == strcmp(gst_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);
|
||||
gst_structure_get_int(structure, "red_mask", &red_mask);
|
||||
gst_structure_get_int(structure, "green_mask", &green_mask);
|
||||
gst_structure_get_int(structure, "blue_mask", &blue_mask);
|
||||
if ((unsigned int)red_mask == 0xFF000000 &&
|
||||
(unsigned int)green_mask == 0x00FF0000 &&
|
||||
(unsigned int)blue_mask == 0x0000FF00)
|
||||
@@ -269,12 +298,13 @@ gst_slvideo_set_caps (GstBaseSink * bsink, GstCaps * caps)
|
||||
filter->format = SLV_PF_BGRX;
|
||||
//fprintf(stderr, "\n\nPIXEL FORMAT BGR\n\n");
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
}*/
|
||||
|
||||
filter->format = SLV_PF_RGBX;
|
||||
|
||||
GST_OBJECT_UNLOCK(filter);
|
||||
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -321,15 +351,15 @@ gst_slvideo_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size,
|
||||
// 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))
|
||||
structure = gst_caps_get_structure (caps, 0);
|
||||
if (!gst_structure_get_int(structure, "width", &width) ||
|
||||
!gst_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();
|
||||
GstBuffer *newbuf = gst_buffer_new();
|
||||
bool made_bufferdata_ptr = false;
|
||||
#define MAXDEPTHHACK 4
|
||||
|
||||
@@ -349,19 +379,19 @@ gst_slvideo_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size,
|
||||
|
||||
GstCaps *desired_caps;
|
||||
GstStructure *desired_struct;
|
||||
desired_caps = llgst_caps_copy (caps);
|
||||
desired_struct = llgst_caps_get_structure (desired_caps, 0);
|
||||
desired_caps = gst_caps_copy (caps);
|
||||
desired_struct = gst_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);
|
||||
gst_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);
|
||||
gst_structure_set_value (desired_struct, "height", &value);
|
||||
|
||||
if (llgst_pad_peer_accept_caps (GST_VIDEO_SINK_PAD (slvideo),
|
||||
if (gst_pad_peer_accept_caps (GST_VIDEO_SINK_PAD (slvideo),
|
||||
desired_caps))
|
||||
{
|
||||
// todo: re-use buffers from a pool?
|
||||
@@ -372,13 +402,13 @@ gst_slvideo_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size,
|
||||
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);
|
||||
gst_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_caps_unref(desired_caps);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -390,7 +420,7 @@ gst_slvideo_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint 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);
|
||||
gst_buffer_set_caps (GST_BUFFER_CAST(newbuf), caps);
|
||||
}
|
||||
|
||||
*buf = GST_BUFFER_CAST(newbuf);
|
||||
@@ -432,6 +462,20 @@ gst_slvideo_class_init (GstSLVideoClass * klass)
|
||||
#undef LLGST_DEBUG_FUNCPTR
|
||||
}
|
||||
|
||||
/*
|
||||
static void
|
||||
gst_slvideo_update_caps (GstSLVideo * slvideo)
|
||||
{
|
||||
GstCaps *caps;
|
||||
|
||||
// GStreamer will automatically convert colourspace if necessary.
|
||||
// GStreamer will automatically resize media to one of these enumerated
|
||||
// powers-of-two that we ask for (yay GStreamer!)
|
||||
caps = gst_caps_from_string (SLV_ALLCAPS);
|
||||
|
||||
gst_caps_replace (&slvideo->caps, caps);
|
||||
}
|
||||
*/
|
||||
|
||||
/* initialize the new element
|
||||
* instantiate pads and add them to element
|
||||
@@ -454,24 +498,24 @@ gst_slvideo_init (GstSLVideo * filter,
|
||||
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);
|
||||
GstCaps *caps = gst_caps_from_string (SLV_ALLCAPS);
|
||||
gst_caps_replace (&filter->caps, caps);
|
||||
filter->resize_forced_always = false;
|
||||
filter->resize_try_width = -1;
|
||||
filter->resize_try_height = -1;
|
||||
GST_OBJECT_UNLOCK(filter);
|
||||
|
||||
//gst_slvideo_update_caps(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));
|
||||
g_return_if_fail (GST_IS_SLVIDEO (object));
|
||||
|
||||
switch (prop_id) {
|
||||
default:
|
||||
if (prop_id) {
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -479,12 +523,10 @@ static void
|
||||
gst_slvideo_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
llg_return_if_fail (GST_IS_SLVIDEO (object));
|
||||
g_return_if_fail (GST_IS_SLVIDEO (object));
|
||||
|
||||
switch (prop_id) {
|
||||
default:
|
||||
if (prop_id) {
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -502,7 +544,7 @@ plugin_init (GstPlugin * plugin)
|
||||
GST_DEBUG_CATEGORY_INIT (gst_slvideo_debug, (gchar*)"private-slvideo-plugin",
|
||||
0, (gchar*)"Second Life Video Sink");
|
||||
|
||||
return llgst_element_register (plugin, "private-slvideo",
|
||||
return gst_element_register (plugin, "private-slvideo",
|
||||
GST_RANK_NONE, GST_TYPE_SLVIDEO);
|
||||
}
|
||||
|
||||
@@ -512,20 +554,19 @@ plugin_init (GstPlugin * plugin)
|
||||
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*)"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");
|
||||
gst_plugin_register_static( GST_VERSION_MAJOR,
|
||||
GST_VERSION_MINOR,
|
||||
(const gchar *)"private-slvideoplugin",
|
||||
(gchar *)"SL Video sink plugin",
|
||||
plugin_init,
|
||||
(const gchar *)"0.1",
|
||||
GST_LICENSE_UNKNOWN,
|
||||
(const gchar *)"Second Life",
|
||||
(const gchar *)"Second Life",
|
||||
(const gchar *)"http://www.secondlife.com/" );
|
||||
}
|
||||
|
||||
#endif // LL_GSTREAMER010_ENABLED
|
||||
|
||||
10
indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.h
Normal file → Executable file
10
indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.h
Normal file → Executable file
@@ -2,9 +2,10 @@
|
||||
* @file llmediaimplgstreamervidplug.h
|
||||
* @brief Video-consuming static GStreamer plugin for gst-to-LLMediaImpl
|
||||
*
|
||||
* @cond
|
||||
* $LicenseInfo:firstyear=2007&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2007-2009, Linden Research, Inc.
|
||||
* Copyright (c) 2007-2010, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
@@ -12,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,
|
||||
@@ -28,6 +29,8 @@
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
#ifndef __GST_SLVIDEO_H__
|
||||
@@ -39,6 +42,7 @@ extern "C" {
|
||||
#include <gst/gst.h>
|
||||
#include <gst/video/video.h>
|
||||
#include <gst/video/gstvideosink.h>
|
||||
// #include <glib/gthread.h>
|
||||
}
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
540
indra/media_plugins/gstreamer010/media_plugin_gstreamer010.cpp
Normal file → Executable file
540
indra/media_plugins/gstreamer010/media_plugin_gstreamer010.cpp
Normal file → Executable file
@@ -2,9 +2,10 @@
|
||||
* @file media_plugin_gstreamer010.cpp
|
||||
* @brief GStreamer-0.10 plugin for LLMedia API plugin system
|
||||
*
|
||||
* @cond
|
||||
* $LicenseInfo:firstyear=2007&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2007-2009, Linden Research, Inc.
|
||||
* Copyright (c) 2007-2010, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
@@ -12,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,
|
||||
@@ -28,10 +29,19 @@
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
#include "linden_common.h"
|
||||
|
||||
// Needed for _getcwd() RC
|
||||
#ifdef LL_WINDOWS
|
||||
#include <direct.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
#include "llgl.h"
|
||||
|
||||
#include "llplugininstance.h"
|
||||
@@ -43,6 +53,7 @@
|
||||
|
||||
extern "C" {
|
||||
#include <gst/gst.h>
|
||||
#include <gst/gstelement.h>
|
||||
}
|
||||
|
||||
#include "llmediaimplgstreamer.h"
|
||||
@@ -50,8 +61,6 @@ extern "C" {
|
||||
|
||||
#include "llmediaimplgstreamervidplug.h"
|
||||
|
||||
#include "llmediaimplgstreamer_syms.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
class MediaPluginGStreamer010 : public MediaPluginBase
|
||||
@@ -65,6 +74,8 @@ public:
|
||||
static bool startup();
|
||||
static bool closedown();
|
||||
|
||||
static void set_gst_plugin_path();
|
||||
|
||||
gboolean processGSTEvents(GstBus *bus,
|
||||
GstMessage *message);
|
||||
|
||||
@@ -80,7 +91,7 @@ private:
|
||||
bool play(double rate);
|
||||
bool getTimePos(double &sec_out);
|
||||
|
||||
static const double MIN_LOOP_SEC = 1.0F;
|
||||
#define MIN_LOOP_SEC 1.0F
|
||||
|
||||
bool mIsLooping;
|
||||
|
||||
@@ -136,6 +147,8 @@ private:
|
||||
|
||||
bool mSeekWanted;
|
||||
double mSeekDestination;
|
||||
|
||||
std::string mLastTitle;
|
||||
|
||||
// Very GStreamer-specific
|
||||
GMainLoop *mPump; // event pump for this media
|
||||
@@ -193,149 +206,179 @@ MediaPluginGStreamer010::processGSTEvents(GstBus *bus,
|
||||
GST_MESSAGE_TYPE(message) != GST_MESSAGE_BUFFERING)
|
||||
{
|
||||
DEBUGMSG("Got GST message type: %s",
|
||||
LLGST_MESSAGE_TYPE_NAME (message));
|
||||
GST_MESSAGE_TYPE_NAME (message));
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: grok 'duration' message type
|
||||
DEBUGMSG("Got GST message type: %s",
|
||||
LLGST_MESSAGE_TYPE_NAME (message));
|
||||
GST_MESSAGE_TYPE_NAME (message));
|
||||
}
|
||||
|
||||
switch (GST_MESSAGE_TYPE (message)) {
|
||||
case GST_MESSAGE_BUFFERING: {
|
||||
// NEEDS GST 0.10.11+
|
||||
if (llgst_message_parse_buffering)
|
||||
switch (GST_MESSAGE_TYPE (message))
|
||||
{
|
||||
case GST_MESSAGE_BUFFERING:
|
||||
{
|
||||
// NEEDS GST 0.10.11+ and America discovered by C.Columbus
|
||||
gint percent = 0;
|
||||
llgst_message_parse_buffering(message, &percent);
|
||||
gst_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;
|
||||
case GST_MESSAGE_STATE_CHANGED: {
|
||||
GstState old_state;
|
||||
GstState new_state;
|
||||
GstState pending_state;
|
||||
gst_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
|
||||
|
||||
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)
|
||||
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;
|
||||
|
||||
gst_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:
|
||||
{
|
||||
GError *err = NULL;
|
||||
gchar *debug = NULL;
|
||||
|
||||
llgst_message_parse_info (message, &err, &debug);
|
||||
gst_message_parse_info (message, &err, &debug);
|
||||
INFOMSG("GST info: %s", err?err->message:"(unknown)");
|
||||
if (err)
|
||||
g_error_free (err);
|
||||
g_free (debug);
|
||||
|
||||
break;
|
||||
}
|
||||
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)
|
||||
case GST_MESSAGE_WARNING:
|
||||
{
|
||||
DEBUGMSG("looping media...");
|
||||
double eos_pos_sec = 0.0F;
|
||||
bool got_eos_position = getTimePos(eos_pos_sec);
|
||||
GError *err = NULL;
|
||||
gchar *debug = NULL;
|
||||
|
||||
gst_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_TAG:
|
||||
{
|
||||
GstTagList *new_tags;
|
||||
|
||||
if (got_eos_position && eos_pos_sec < MIN_LOOP_SEC)
|
||||
gst_message_parse_tag( message, &new_tags );
|
||||
|
||||
gchar *title = NULL;
|
||||
|
||||
if ( gst_tag_list_get_string(new_tags, GST_TAG_TITLE, &title) )
|
||||
{
|
||||
// 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)
|
||||
//WARMING("Title: %s", title);
|
||||
std::string newtitle(title);
|
||||
gst_tag_list_free(new_tags);
|
||||
|
||||
if ( newtitle != mLastTitle && !newtitle.empty() )
|
||||
{
|
||||
play(1.0);
|
||||
LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "name_text");
|
||||
message.setValue("name", newtitle );
|
||||
sendMessage( message );
|
||||
mLastTitle = newtitle;
|
||||
}
|
||||
g_free(title);
|
||||
}
|
||||
|
||||
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
|
||||
#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);
|
||||
{
|
||||
#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;
|
||||
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
|
||||
@@ -541,7 +584,7 @@ MediaPluginGStreamer010::pause()
|
||||
{
|
||||
DEBUGMSG("pausing media...");
|
||||
// todo: error-check this?
|
||||
llgst_element_set_state(mPlaybin, GST_STATE_PAUSED);
|
||||
gst_element_set_state(mPlaybin, GST_STATE_PAUSED);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -550,7 +593,7 @@ MediaPluginGStreamer010::stop()
|
||||
{
|
||||
DEBUGMSG("stopping media...");
|
||||
// todo: error-check this?
|
||||
llgst_element_set_state(mPlaybin, GST_STATE_READY);
|
||||
gst_element_set_state(mPlaybin, GST_STATE_READY);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -561,7 +604,7 @@ MediaPluginGStreamer010::play(double rate)
|
||||
|
||||
DEBUGMSG("playing media... rate=%f", rate);
|
||||
// todo: error-check this?
|
||||
llgst_element_set_state(mPlaybin, GST_STATE_PLAYING);
|
||||
gst_element_set_state(mPlaybin, GST_STATE_PLAYING);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -590,7 +633,7 @@ MediaPluginGStreamer010::seek(double time_sec)
|
||||
bool success = false;
|
||||
if (mDoneInit && mPlaybin)
|
||||
{
|
||||
success = llgst_element_seek(mPlaybin, 1.0F, GST_FORMAT_TIME,
|
||||
success = gst_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),
|
||||
@@ -609,11 +652,9 @@ MediaPluginGStreamer010::getTimePos(double &sec_out)
|
||||
{
|
||||
gint64 pos;
|
||||
GstFormat timefmt = GST_FORMAT_TIME;
|
||||
got_position =
|
||||
llgst_element_query_position &&
|
||||
llgst_element_query_position(mPlaybin,
|
||||
&timefmt,
|
||||
&pos);
|
||||
got_position = gst_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
|
||||
@@ -655,7 +696,7 @@ MediaPluginGStreamer010::load()
|
||||
DEBUGMSG("setting up media...");
|
||||
|
||||
mIsLooping = false;
|
||||
mVolume = 0.1234567; // minor hack to force an initial volume update
|
||||
mVolume = (float) 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);
|
||||
@@ -666,7 +707,7 @@ MediaPluginGStreamer010::load()
|
||||
}
|
||||
|
||||
// instantiate a playbin element to do the hard work
|
||||
mPlaybin = llgst_element_factory_make ("playbin", "play");
|
||||
mPlaybin = gst_element_factory_make ("playbin", "play");
|
||||
if (!mPlaybin)
|
||||
{
|
||||
setStatus(STATUS_ERROR);
|
||||
@@ -674,21 +715,21 @@ MediaPluginGStreamer010::load()
|
||||
}
|
||||
|
||||
// get playbin's bus
|
||||
GstBus *bus = llgst_pipeline_get_bus (GST_PIPELINE (mPlaybin));
|
||||
GstBus *bus = gst_pipeline_get_bus (GST_PIPELINE (mPlaybin));
|
||||
if (!bus)
|
||||
{
|
||||
setStatus(STATUS_ERROR);
|
||||
return false; // error
|
||||
}
|
||||
mBusWatchID = llgst_bus_add_watch (bus,
|
||||
mBusWatchID = gst_bus_add_watch (bus,
|
||||
llmediaimplgstreamer_bus_callback,
|
||||
this);
|
||||
llgst_object_unref (bus);
|
||||
gst_object_unref (bus);
|
||||
|
||||
if (NULL == getenv("LL_GSTREAMER_EXTERNAL")) {
|
||||
// instantiate a custom video sink
|
||||
mVideoSink =
|
||||
GST_SLVIDEO(llgst_element_factory_make ("private-slvideo", "slvideo"));
|
||||
GST_SLVIDEO(gst_element_factory_make ("private-slvideo", "slvideo"));
|
||||
if (!mVideoSink)
|
||||
{
|
||||
WARNMSG("Could not instantiate private-slvideo element.");
|
||||
@@ -718,8 +759,8 @@ MediaPluginGStreamer010::unload ()
|
||||
|
||||
if (mPlaybin)
|
||||
{
|
||||
llgst_element_set_state (mPlaybin, GST_STATE_NULL);
|
||||
llgst_object_unref (GST_OBJECT (mPlaybin));
|
||||
gst_element_set_state (mPlaybin, GST_STATE_NULL);
|
||||
gst_object_unref (GST_OBJECT (mPlaybin));
|
||||
mPlaybin = NULL;
|
||||
}
|
||||
|
||||
@@ -752,7 +793,10 @@ MediaPluginGStreamer010::startup()
|
||||
|
||||
// Init the glib type system - we need it.
|
||||
g_type_init();
|
||||
set_gst_plugin_path();
|
||||
|
||||
|
||||
/*
|
||||
// Get symbols!
|
||||
#if LL_DARWIN
|
||||
if (! grab_gst_syms("libgstreamer-0.10.dylib",
|
||||
@@ -768,24 +812,24 @@ MediaPluginGStreamer010::startup()
|
||||
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 (gst_segtrap_set_enabled)
|
||||
// {
|
||||
gst_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 (gst_registry_fork_set_enabled) {
|
||||
// if we can disable SIGCHLD-using forking behaviour,
|
||||
// do it.
|
||||
llgst_registry_fork_set_enabled(false);
|
||||
gst_registry_fork_set_enabled(false);
|
||||
}
|
||||
else {
|
||||
// else temporarily install default SIGCHLD handler
|
||||
@@ -796,24 +840,24 @@ MediaPluginGStreamer010::startup()
|
||||
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);
|
||||
gboolean init_gst_success = gst_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)
|
||||
if (!gst_registry_fork_set_enabled)
|
||||
sigaction(SIGCHLD, &oldact, NULL);
|
||||
#endif // LL_LINUX
|
||||
|
||||
*/
|
||||
if (!init_gst_success) // fail
|
||||
{
|
||||
if (err)
|
||||
@@ -827,16 +871,139 @@ MediaPluginGStreamer010::startup()
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Set up logging facilities
|
||||
gst_debug_remove_log_function( gst_debug_log_default );
|
||||
// gst_debug_add_log_function( gstreamer_log, NULL );
|
||||
|
||||
// Init our custom plugins - only really need do this once.
|
||||
gst_slvideo_init_class();
|
||||
|
||||
/*
|
||||
// List the plugins GStreamer can find
|
||||
LL_DEBUGS("MediaImpl") << "Found GStreamer plugins:" << LL_ENDL;
|
||||
GList *list;
|
||||
GstRegistry *registry = gst_registry_get_default();
|
||||
std::string loaded = "";
|
||||
for (list = gst_registry_get_plugin_list(registry);
|
||||
list != NULL;
|
||||
list = g_list_next(list))
|
||||
{
|
||||
GstPlugin *list_plugin = (GstPlugin *)list->data;
|
||||
(bool)gst_plugin_is_loaded(list_plugin) ? loaded = "Yes" : loaded = "No";
|
||||
LL_DEBUGS("MediaImpl") << gst_plugin_get_name(list_plugin) << ", loaded? " << loaded << LL_ENDL;
|
||||
}
|
||||
gst_plugin_list_free(list);
|
||||
*/
|
||||
mDoneInit = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MediaPluginGStreamer010::set_gst_plugin_path()
|
||||
{
|
||||
// Linux sets GST_PLUGIN_PATH in wrapper.sh, not here.
|
||||
#if LL_WINDOWS || LL_DARWIN
|
||||
|
||||
std::string imp_dir = "";
|
||||
|
||||
// Get the current working directory:
|
||||
#if LL_WINDOWS
|
||||
char* raw_dir;
|
||||
raw_dir = _getcwd(NULL,0);
|
||||
if( raw_dir != NULL )
|
||||
{
|
||||
imp_dir = std::string( raw_dir );
|
||||
}
|
||||
#elif LL_DARWIN
|
||||
CFBundleRef main_bundle = CFBundleGetMainBundle();
|
||||
if( main_bundle != NULL )
|
||||
{
|
||||
CFURLRef bundle_url = CFBundleCopyBundleURL( main_bundle );
|
||||
if( bundle_url != NULL )
|
||||
{
|
||||
#ifndef MAXPATHLEN
|
||||
#define MAXPATHLEN 1024
|
||||
#endif
|
||||
char raw_dir[MAXPATHLEN];
|
||||
if( CFURLGetFileSystemRepresentation( bundle_url, true, (UInt8 *)raw_dir, MAXPATHLEN) )
|
||||
{
|
||||
imp_dir = std::string( raw_dir ) + "/Contents/MacOS/";
|
||||
}
|
||||
CFRelease(bundle_url);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if( imp_dir == "" )
|
||||
{
|
||||
WARNMSG("Could not get application directory, not setting GST_PLUGIN_PATH.");
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUGMSG("Imprudence is installed at %s", imp_dir);
|
||||
|
||||
// ":" on Mac and 'Nix, ";" on Windows
|
||||
std::string separator = G_SEARCHPATH_SEPARATOR_S;
|
||||
|
||||
// Grab the current path, if it's set.
|
||||
std::string old_plugin_path = "";
|
||||
char *old_path = getenv("GST_PLUGIN_PATH");
|
||||
if(old_path == NULL)
|
||||
{
|
||||
DEBUGMSG("Did not find user-set GST_PLUGIN_PATH.");
|
||||
}
|
||||
else
|
||||
{
|
||||
old_plugin_path = separator + std::string( old_path );
|
||||
}
|
||||
|
||||
|
||||
// Search both Imprudence and Imprudence\lib\gstreamer-plugins.
|
||||
// But we also want to search the path the user has set, if any.
|
||||
std::string plugin_path =
|
||||
"GST_PLUGIN_PATH=" +
|
||||
#if LL_WINDOWS
|
||||
imp_dir + "\\lib\\gstreamer-plugins" +
|
||||
#elif LL_DARWIN
|
||||
imp_dir + separator +
|
||||
imp_dir + "/../Resources/lib/gstreamer-plugins" +
|
||||
#endif
|
||||
old_plugin_path;
|
||||
|
||||
int put_result;
|
||||
|
||||
// Place GST_PLUGIN_PATH in the environment settings
|
||||
#if LL_WINDOWS
|
||||
put_result = _putenv( (char*)plugin_path.c_str() );
|
||||
#elif LL_DARWIN
|
||||
put_result = putenv( (char*)plugin_path.c_str() );
|
||||
#endif
|
||||
|
||||
if( put_result == -1 )
|
||||
{
|
||||
WARNMSG("Setting GST_PLUGIN_PATH failed!");
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUGMSG("GST_PLUGIN_PATH set to %s", getenv("GST_PLUGIN_PATH"));
|
||||
}
|
||||
|
||||
// Don't load system plugins. We only want to use ours, to avoid conflicts.
|
||||
#if LL_WINDOWS
|
||||
put_result = _putenv( "GST_PLUGIN_SYSTEM_PATH=\"\"" );
|
||||
#elif LL_DARWIN
|
||||
put_result = putenv( "GST_PLUGIN_SYSTEM_PATH=\"\"" );
|
||||
#endif
|
||||
|
||||
if( put_result == -1 )
|
||||
{
|
||||
WARNMSG("Setting GST_PLUGIN_SYSTEM_PATH=\"\" failed!");
|
||||
}
|
||||
|
||||
#endif // LL_WINDOWS || LL_DARWIN
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
MediaPluginGStreamer010::sizeChanged()
|
||||
@@ -878,7 +1045,7 @@ MediaPluginGStreamer010::closedown()
|
||||
if (!mDoneInit)
|
||||
return false; // error
|
||||
|
||||
ungrab_gst_syms();
|
||||
// ungrab_gst_syms();
|
||||
|
||||
mDoneInit = false;
|
||||
|
||||
@@ -899,11 +1066,10 @@ std::string
|
||||
MediaPluginGStreamer010::getVersion()
|
||||
{
|
||||
std::string plugin_version = "GStreamer010 media plugin, GStreamer version ";
|
||||
if (mDoneInit &&
|
||||
llgst_version)
|
||||
if (mDoneInit) // && gst_version)
|
||||
{
|
||||
guint major, minor, micro, nano;
|
||||
llgst_version(&major, &minor, µ, &nano);
|
||||
gst_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
|
||||
@@ -945,33 +1111,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")
|
||||
{
|
||||
@@ -1036,7 +1175,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");
|
||||
|
||||
0
indra/media_plugins/quicktime/CMakeLists.txt
Normal file → Executable file
0
indra/media_plugins/quicktime/CMakeLists.txt
Normal file → Executable file
180
indra/media_plugins/quicktime/media_plugin_quicktime.cpp
Normal file → Executable file
180
indra/media_plugins/quicktime/media_plugin_quicktime.cpp
Normal file → Executable file
@@ -1,33 +1,29 @@
|
||||
/**
|
||||
/**
|
||||
* @file media_plugin_quicktime.cpp
|
||||
* @brief QuickTime plugin for LLMedia API plugin system
|
||||
*
|
||||
* $LicenseInfo:firstyear=2008&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2008-2009, Linden Research, Inc.
|
||||
*
|
||||
* @cond
|
||||
* $LicenseInfo:firstyear=2008&license=viewerlgpl$
|
||||
* 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://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
*
|
||||
* 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
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
* 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.
|
||||
*
|
||||
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* 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"
|
||||
@@ -102,14 +98,14 @@ private:
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -117,16 +113,16 @@ private:
|
||||
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;
|
||||
@@ -145,25 +141,27 @@ private:
|
||||
{
|
||||
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);
|
||||
@@ -202,7 +200,7 @@ private:
|
||||
SetMovieDrawingCompleteProc( mMovieHandle, movieDrawingCallWhenChanged, movieDrawingCompleteCallback, ( long )this );
|
||||
|
||||
setStatus(STATUS_LOADED);
|
||||
|
||||
|
||||
sizeChanged();
|
||||
};
|
||||
|
||||
@@ -239,7 +237,7 @@ private:
|
||||
DisposeGWorld( mGWorldHandle );
|
||||
mGWorldHandle = NULL;
|
||||
};
|
||||
|
||||
|
||||
setStatus(STATUS_NONE);
|
||||
|
||||
return true;
|
||||
@@ -249,7 +247,7 @@ private:
|
||||
{
|
||||
unload();
|
||||
load( url );
|
||||
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
@@ -257,7 +255,7 @@ private:
|
||||
{
|
||||
if ( ! mMovieHandle )
|
||||
return false;
|
||||
|
||||
|
||||
// Check to see whether the movie's natural size has updated
|
||||
{
|
||||
int width, height;
|
||||
@@ -275,14 +273,14 @@ private:
|
||||
//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)
|
||||
@@ -314,7 +312,7 @@ private:
|
||||
{
|
||||
DisposeGWorld( old_gworld_handle );
|
||||
}
|
||||
|
||||
|
||||
// Set up the movie display matrix
|
||||
{
|
||||
// scale movie to fit rect and invert vertically to match opengl image format
|
||||
@@ -327,7 +325,7 @@ private:
|
||||
ScaleMatrix( &transform, X2Fix( scaleX ), X2Fix( scaleY ), X2Fix( centerX ), X2Fix( centerY ) );
|
||||
SetMovieMatrix( mMovieHandle, &transform );
|
||||
}
|
||||
|
||||
|
||||
// update movie controller
|
||||
if ( mMovieController )
|
||||
{
|
||||
@@ -345,7 +343,6 @@ private:
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static Boolean mcActionFilterCallBack( MovieController mc, short action, void *params, long ref )
|
||||
{
|
||||
Boolean result = false;
|
||||
@@ -355,9 +352,9 @@ private:
|
||||
switch( action )
|
||||
{
|
||||
// handle window resizing
|
||||
case mcActionControllerSizeChanged:
|
||||
case mcActionControllerSizeChanged:
|
||||
// Ensure that the movie draws correctly at the new size
|
||||
self->sizeChanged();
|
||||
self->sizeChanged();
|
||||
break;
|
||||
|
||||
// Block any movie controller actions that open URLs.
|
||||
@@ -386,6 +383,7 @@ private:
|
||||
// self->updateQuickTime();
|
||||
// TODO ^^^
|
||||
|
||||
|
||||
if ( self->mWidth > 0 && self->mHeight > 0 )
|
||||
self->setDirty( 0, 0, self->mWidth, self->mHeight );
|
||||
|
||||
@@ -434,7 +432,7 @@ private:
|
||||
MCDoAction( mMovieController, mcActionPlay, (void*)rate );
|
||||
rewind();
|
||||
};
|
||||
|
||||
|
||||
MCDoAction( mMovieController, mcActionPrerollAndPlay, (void*)getPlayRate() );
|
||||
MCDoAction( mMovieController, mcActionSetVolume, (void*)mCurVolume );
|
||||
setStatus(STATUS_PLAYING);
|
||||
@@ -462,7 +460,7 @@ private:
|
||||
if ( mCommand == COMMAND_PAUSE )
|
||||
{
|
||||
if ( mStatus == STATUS_PLAYING )
|
||||
{
|
||||
{
|
||||
if ( GetMovieLoadState( mMovieHandle ) >= kMovieLoadStatePlaythroughOK )
|
||||
{
|
||||
Fixed rate = X2Fix( 0.0 );
|
||||
@@ -495,7 +493,7 @@ private:
|
||||
void getMovieNaturalSize(int *movie_width, int *movie_height)
|
||||
{
|
||||
Rect rect;
|
||||
|
||||
|
||||
GetMovieNaturalBoundsRect( mMovieHandle, &rect );
|
||||
|
||||
int width = ( rect.right - rect.left );
|
||||
@@ -518,7 +516,7 @@ private:
|
||||
*movie_width = width;
|
||||
*movie_height = height;
|
||||
}
|
||||
|
||||
|
||||
void updateQuickTime(int milliseconds)
|
||||
{
|
||||
if ( ! mMovieHandle )
|
||||
@@ -721,8 +719,8 @@ private:
|
||||
return false;
|
||||
|
||||
// allocate some space and grab it
|
||||
UInt8* item_data = new UInt8[size + 1];
|
||||
memset(item_data, 0, (size + 1) * sizeof(UInt8));
|
||||
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 )
|
||||
{
|
||||
@@ -856,42 +854,12 @@ 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")
|
||||
{
|
||||
// no response is necessary here.
|
||||
F64 time = message_in.getValueReal("time");
|
||||
|
||||
|
||||
// Convert time to milliseconds for update()
|
||||
update((int)(time * 1000.0f));
|
||||
}
|
||||
@@ -905,8 +873,6 @@ void MediaPluginQuickTime::receiveMessage(const char *message_string)
|
||||
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
|
||||
@@ -929,9 +895,9 @@ void MediaPluginQuickTime::receiveMessage(const char *message_string)
|
||||
// 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();
|
||||
sizeChanged();
|
||||
}
|
||||
mSharedSegments.erase(iter);
|
||||
}
|
||||
@@ -952,7 +918,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");
|
||||
@@ -988,9 +988,9 @@ void MediaPluginQuickTime::receiveMessage(const char *message_string)
|
||||
mTextureHeight = texture_height;
|
||||
|
||||
mMediaSizeChanging = false;
|
||||
|
||||
|
||||
sizeChanged();
|
||||
|
||||
|
||||
update();
|
||||
};
|
||||
};
|
||||
@@ -999,14 +999,14 @@ void MediaPluginQuickTime::receiveMessage(const char *message_string)
|
||||
{
|
||||
std::string uri = message_in.getValue("uri");
|
||||
load( uri );
|
||||
sendStatus();
|
||||
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);
|
||||
@@ -1099,7 +1099,7 @@ MediaPluginQuickTime::~MediaPluginQuickTime()
|
||||
|
||||
void MediaPluginQuickTime::receiveMessage(const char *message_string)
|
||||
{
|
||||
// no-op
|
||||
// no-op
|
||||
}
|
||||
|
||||
// We're building without quicktime enabled. Just refuse to initialize.
|
||||
|
||||
@@ -27,19 +27,11 @@ include_directories(
|
||||
${LLIMAGE_INCLUDE_DIRS}
|
||||
${LLRENDER_INCLUDE_DIRS}
|
||||
${LLWINDOW_INCLUDE_DIRS}
|
||||
${LLQTWEBKIT_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
|
||||
### media_plugin_webkit
|
||||
|
||||
if(NOT WORD_SIZE EQUAL 32)
|
||||
if(WINDOWS)
|
||||
add_definitions(/FIXED:NO)
|
||||
else(WINDOWS) # not windows therefore gcc LINUX and DARWIN
|
||||
add_definitions(-fPIC)
|
||||
endif(WINDOWS)
|
||||
endif(NOT WORD_SIZE EQUAL 32)
|
||||
|
||||
set(media_plugin_webkit_SOURCE_FILES
|
||||
media_plugin_webkit.cpp
|
||||
)
|
||||
@@ -59,11 +51,9 @@ set(media_plugin_webkit_LINK_LIBRARIES
|
||||
|
||||
# Select which VolumeCatcher implementation to use
|
||||
if (LINUX)
|
||||
if (PULSEAUDIO_FOUND)
|
||||
if (PULSEAUDIO)
|
||||
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)
|
||||
endif (PULSEAUDIO)
|
||||
list(APPEND media_plugin_webkit_LINK_LIBRARIES
|
||||
${UI_LIBRARIES} # for glib/GTK
|
||||
)
|
||||
@@ -77,6 +67,9 @@ elseif (DARWIN)
|
||||
)
|
||||
elseif (WINDOWS)
|
||||
list(APPEND media_plugin_webkit_SOURCE_FILES windows_volume_catcher.cpp)
|
||||
else (LINUX)
|
||||
# All other platforms use the dummy volume catcher for now.
|
||||
list(APPEND media_plugin_webkit_SOURCE_FILES dummy_volume_catcher.cpp)
|
||||
endif (LINUX)
|
||||
|
||||
set_source_files_properties(${media_plugin_webkit_HEADER_FILES}
|
||||
|
||||
37
indra/media_plugins/webkit/dummy_volume_catcher.cpp
Executable file → Normal file
37
indra/media_plugins/webkit/dummy_volume_catcher.cpp
Executable file → Normal file
@@ -3,26 +3,33 @@
|
||||
* @brief A null implementation of the "VolumeCatcher" class for platforms where it's not implemented yet.
|
||||
*
|
||||
* @cond
|
||||
* $LicenseInfo:firstyear=2010&license=viewerlgpl$
|
||||
* $LicenseInfo:firstyear=2010&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2010, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
* 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
|
||||
*
|
||||
* 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.
|
||||
* 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
|
||||
*
|
||||
* 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.
|
||||
* 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.
|
||||
*
|
||||
* 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
|
||||
* 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
|
||||
*/
|
||||
|
||||
|
||||
40
indra/media_plugins/webkit/linux_volume_catcher.cpp
Executable file → Normal file
40
indra/media_plugins/webkit/linux_volume_catcher.cpp
Executable file → Normal file
@@ -3,26 +3,33 @@
|
||||
* @brief A Linux-specific, PulseAudio-specific hack to detect and volume-adjust new audio sources
|
||||
*
|
||||
* @cond
|
||||
* $LicenseInfo:firstyear=2010&license=viewerlgpl$
|
||||
* $LicenseInfo:firstyear=2010&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2010, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
* 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
|
||||
*
|
||||
* 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.
|
||||
* 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
|
||||
*
|
||||
* 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.
|
||||
* 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.
|
||||
*
|
||||
* 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
|
||||
* 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
|
||||
*/
|
||||
|
||||
@@ -35,8 +42,9 @@
|
||||
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 <set> //imprudence
|
||||
|
||||
#include "linden_common.h"
|
||||
#include <set>
|
||||
|
||||
#include "volume_catcher.h"
|
||||
|
||||
|
||||
56
indra/media_plugins/webkit/linux_volume_catcher.h
Executable file
56
indra/media_plugins/webkit/linux_volume_catcher.h
Executable file
@@ -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
|
||||
0
indra/media_plugins/webkit/linux_volume_catcher_pa_syms.inc
Executable file → Normal file
0
indra/media_plugins/webkit/linux_volume_catcher_pa_syms.inc
Executable file → Normal file
0
indra/media_plugins/webkit/linux_volume_catcher_paglib_syms.inc
Executable file → Normal file
0
indra/media_plugins/webkit/linux_volume_catcher_paglib_syms.inc
Executable file → Normal file
37
indra/media_plugins/webkit/mac_volume_catcher.cpp
Executable file → Normal file
37
indra/media_plugins/webkit/mac_volume_catcher.cpp
Executable file → Normal file
@@ -3,26 +3,33 @@
|
||||
* @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$
|
||||
* $LicenseInfo:firstyear=2010&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2010, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
* 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
|
||||
*
|
||||
* 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.
|
||||
* 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
|
||||
*
|
||||
* 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.
|
||||
* 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.
|
||||
*
|
||||
* 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
|
||||
* 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
|
||||
*/
|
||||
|
||||
|
||||
119
indra/media_plugins/webkit/media_plugin_webkit.cpp
Normal file → Executable file
119
indra/media_plugins/webkit/media_plugin_webkit.cpp
Normal file → Executable file
@@ -44,6 +44,7 @@
|
||||
#include "llpluginmessage.h"
|
||||
#include "llpluginmessageclasses.h"
|
||||
#include "media_plugin_base.h"
|
||||
#include <iomanip>
|
||||
|
||||
// set to 1 if you're using the version of llqtwebkit that's QPixmap-ified
|
||||
#if LL_LINUX
|
||||
@@ -64,7 +65,6 @@ extern "C" {
|
||||
# include <unistd.h>
|
||||
# include <stdlib.h>
|
||||
#endif
|
||||
#include <iomanip>
|
||||
|
||||
#if LL_WINDOWS
|
||||
// *NOTE:Mani - This captures the module handle for the dll. This is used below
|
||||
@@ -78,6 +78,20 @@ extern "C" {
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef LL_STANDALONE
|
||||
#include <qglobal.h>
|
||||
#elif defined(LL_LINUX)
|
||||
// We don't provide Qt headers for non-standalone, therefore define this here.
|
||||
// Our prebuilt is built with QT_NAMESPACE undefined.
|
||||
#define QT_MANGLE_NAMESPACE(name) name
|
||||
#define Q_INIT_RESOURCE(name) \
|
||||
do { extern int QT_MANGLE_NAMESPACE(qInitResources_ ## name) (); \
|
||||
QT_MANGLE_NAMESPACE(qInitResources_ ## name) (); } while (0)
|
||||
#else
|
||||
// Apparently this symbol doesn't exist in the windows and Mac tar balls provided by LL.
|
||||
#define Q_INIT_RESOURCE(name) /*nothing*/
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
class MediaPluginWebKit :
|
||||
@@ -123,7 +137,6 @@ private:
|
||||
F32 mBackgroundR;
|
||||
F32 mBackgroundG;
|
||||
F32 mBackgroundB;
|
||||
std::string mTarget;
|
||||
|
||||
VolumeCatcher mVolumeCatcher;
|
||||
|
||||
@@ -312,7 +325,11 @@ private:
|
||||
LLQtWebKit::getInstance()->enableJavascript( mJavascriptEnabled );
|
||||
|
||||
// create single browser window
|
||||
mBrowserWindowId = LLQtWebKit::getInstance()->createBrowserWindow( mWidth, mHeight, mTarget);
|
||||
#if LLQTWEBKIT_API_VERSION >= 2
|
||||
mBrowserWindowId = LLQtWebKit::getInstance()->createBrowserWindow(mWidth, mHeight /*, mTarget*/ ); // We don't have mTarget yet.
|
||||
#else
|
||||
mBrowserWindowId = LLQtWebKit::getInstance()->createBrowserWindow( mWidth, mHeight );
|
||||
#endif
|
||||
|
||||
// tell LLQtWebKit about the size of the browser window
|
||||
LLQtWebKit::getInstance()->setSize( mBrowserWindowId, mWidth, mHeight );
|
||||
@@ -322,6 +339,12 @@ private:
|
||||
|
||||
// append details to agent string
|
||||
LLQtWebKit::getInstance()->setBrowserAgentId( mUserAgent );
|
||||
|
||||
// Viewer 2+ -- MC
|
||||
#if LL_WINDOWS
|
||||
// Set up window open behavior
|
||||
LLQtWebKit::getInstance()->setWindowOpenBehavior(mBrowserWindowId, LLQtWebKit::WOB_SIMULATE_BLANK_HREF_CLICK);
|
||||
#endif
|
||||
|
||||
#if !LL_QTWEBKIT_USES_PIXMAPS
|
||||
// don't flip bitmap
|
||||
@@ -513,9 +536,16 @@ private:
|
||||
void onClickLinkHref(const EventType& event)
|
||||
{
|
||||
LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "click_href");
|
||||
#if LLQTWEBKIT_API_VERSION >= 2
|
||||
message.setValue("uri", event.getEventUri());
|
||||
message.setValue("target", event.getStringValue());
|
||||
message.setValue("uuid", event.getStringValue2());
|
||||
#else
|
||||
// This will work as long as we don't need "uuid", which will be needed for MoaP.
|
||||
message.setValue("uri", event.getStringValue());
|
||||
message.setValue("target", event.getStringValue2());
|
||||
message.setValueU32("target_type", event.getLinkType());
|
||||
#endif
|
||||
sendMessage(message);
|
||||
}
|
||||
|
||||
@@ -524,10 +554,13 @@ private:
|
||||
void onClickLinkNoFollow(const EventType& event)
|
||||
{
|
||||
LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "click_nofollow");
|
||||
#if LLQTWEBKIT_API_VERSION >= 2
|
||||
message.setValue("uri", event.getEventUri());
|
||||
#else
|
||||
message.setValue("uri", event.getStringValue());
|
||||
#endif
|
||||
sendMessage(message);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// virtual
|
||||
@@ -540,42 +573,6 @@ private:
|
||||
// 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)
|
||||
{
|
||||
@@ -721,26 +718,6 @@ private:
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -766,6 +743,9 @@ MediaPluginWebKit::MediaPluginWebKit(LLPluginInstance::sendMessageFunction host_
|
||||
mJavascriptEnabled = true; // default to on
|
||||
mPluginsEnabled = true; // default to on
|
||||
mUserAgent = "LLPluginMedia Web Browser";
|
||||
|
||||
// Initialize WebCore resource.
|
||||
Q_INIT_RESOURCE(WebCore);
|
||||
}
|
||||
|
||||
MediaPluginWebKit::~MediaPluginWebKit()
|
||||
@@ -879,8 +859,6 @@ void MediaPluginWebKit::receiveMessage(const char *message_string)
|
||||
{
|
||||
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())
|
||||
{
|
||||
@@ -1100,14 +1078,10 @@ void MediaPluginWebKit::receiveMessage(const char *message_string)
|
||||
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)
|
||||
{
|
||||
@@ -1207,17 +1181,6 @@ void MediaPluginWebKit::receiveMessage(const char *message_string)
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
||||
37
indra/media_plugins/webkit/volume_catcher.h
Executable file → Normal file
37
indra/media_plugins/webkit/volume_catcher.h
Executable file → Normal file
@@ -3,26 +3,33 @@
|
||||
* @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$
|
||||
* $LicenseInfo:firstyear=2010&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2010, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
* 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
|
||||
*
|
||||
* 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.
|
||||
* 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
|
||||
*
|
||||
* 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.
|
||||
* 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.
|
||||
*
|
||||
* 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
|
||||
* 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
|
||||
*/
|
||||
|
||||
|
||||
37
indra/media_plugins/webkit/windows_volume_catcher.cpp
Executable file → Normal file
37
indra/media_plugins/webkit/windows_volume_catcher.cpp
Executable file → Normal file
@@ -3,26 +3,33 @@
|
||||
* @brief A Windows implementation of volume level control of all audio channels opened by a process.
|
||||
*
|
||||
* @cond
|
||||
* $LicenseInfo:firstyear=2010&license=viewerlgpl$
|
||||
* $LicenseInfo:firstyear=2010&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2010, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
* 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
|
||||
*
|
||||
* 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.
|
||||
* 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
|
||||
*
|
||||
* 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.
|
||||
* 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.
|
||||
*
|
||||
* 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
|
||||
* 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
|
||||
*/
|
||||
|
||||
|
||||
@@ -39,8 +39,9 @@
|
||||
#include "llviewercontrol.h"
|
||||
#include "llviewerimage.h"
|
||||
#include "llviewerwindow.h"
|
||||
#include "llversionviewer.h"
|
||||
#include "llviewerimagelist.h"
|
||||
//#include "viewerversion.h"
|
||||
|
||||
#include "llpluginclassmedia.h"
|
||||
|
||||
#include "llevent.h" // LLSimpleListener
|
||||
@@ -241,13 +242,15 @@ LLViewerMediaImpl* LLViewerMedia::getMediaImplFromTextureID(const LLUUID& textur
|
||||
// static
|
||||
std::string LLViewerMedia::getCurrentUserAgent()
|
||||
{
|
||||
// Don't include version, channel, or skin -- MC
|
||||
|
||||
// Don't use user-visible string to avoid
|
||||
// punctuation and strange characters.
|
||||
std::string skin_name = gSavedSettings.getString("SkinCurrent");
|
||||
//std::string skin_name = gSavedSettings.getString("SkinCurrent");
|
||||
|
||||
// Just in case we need to check browser differences in A/B test
|
||||
// builds.
|
||||
std::string channel = gSavedSettings.getString("VersionChannelName");
|
||||
//std::string channel = gSavedSettings.getString("VersionChannelName");
|
||||
|
||||
// append our magic version number string to the browser user agent id
|
||||
// See the HTTP 1.0 and 1.1 specifications for allowed formats:
|
||||
@@ -257,9 +260,10 @@ std::string LLViewerMedia::getCurrentUserAgent()
|
||||
// http://www.mozilla.org/build/revised-user-agent-strings.html
|
||||
std::ostringstream codec;
|
||||
codec << "SecondLife/";
|
||||
codec << LL_VERSION_MAJOR << "." << LL_VERSION_MINOR << "." << LL_VERSION_PATCH << "." << LL_VERSION_BUILD;
|
||||
codec << " (" << channel << "; " << skin_name << " skin)";
|
||||
llinfos << codec.str() << llendl;
|
||||
codec << "C64 Basic V2";
|
||||
//codec << ViewerVersion::getImpMajorVersion() << "." << ViewerVersion::getImpMinorVersion() << "." << ViewerVersion::getImpPatchVersion() << " " << ViewerVersion::getImpTestVersion();
|
||||
//codec << " (" << channel << "; " << skin_name << " skin)";
|
||||
// llinfos << codec.str() << llendl;
|
||||
|
||||
return codec.str();
|
||||
}
|
||||
@@ -468,7 +472,7 @@ LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_
|
||||
// In this case we just use whatever gDirUtilp->getOSUserAppDir() gives us (this
|
||||
// is what we always used before this change)
|
||||
std::string linden_user_dir = gDirUtilp->getLindenUserDir();
|
||||
if (!linden_user_dir.empty())
|
||||
if ( ! linden_user_dir.empty() )
|
||||
{
|
||||
// gDirUtilp->getLindenUserDir() is whole path, not just Linden name
|
||||
user_data_path = linden_user_dir;
|
||||
@@ -489,7 +493,22 @@ LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_
|
||||
{
|
||||
LLPluginClassMedia* media_source = new LLPluginClassMedia(owner);
|
||||
media_source->setSize(default_width, default_height);
|
||||
if (media_source->init(launcher_name, plugin_name, false, user_data_path))
|
||||
media_source->setUserDataPath(user_data_path);
|
||||
media_source->setLanguageCode(LLUI::getLanguage());
|
||||
|
||||
// collect 'cookies enabled' setting from prefs and send to embedded browser
|
||||
bool cookies_enabled = gSavedSettings.getBOOL( "BrowserCookiesEnabled" );
|
||||
media_source->enable_cookies( cookies_enabled );
|
||||
|
||||
// collect 'plugins enabled' setting from prefs and send to embedded browser
|
||||
bool plugins_enabled = gSavedSettings.getBOOL( "BrowserPluginsEnabled" );
|
||||
media_source->setPluginsEnabled( plugins_enabled );
|
||||
|
||||
// collect 'javascript enabled' setting from prefs and send to embedded browser
|
||||
bool javascript_enabled = gSavedSettings.getBOOL( "BrowserJavascriptEnabled" );
|
||||
media_source->setJavascriptEnabled( javascript_enabled );
|
||||
|
||||
if (media_source->init(launcher_name, plugin_name, gSavedSettings.getBOOL("PluginAttachDebuggerToPlugins")))
|
||||
{
|
||||
return media_source;
|
||||
}
|
||||
@@ -500,7 +519,12 @@ LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
LL_WARNS("Plugin") << "plugin intialization failed for mime type: " << media_type << LL_ENDL;
|
||||
LLSD args;
|
||||
args["MIME_TYPE"] = media_type;
|
||||
LLNotifications::instance().add("NoPlugin", args);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -715,12 +739,8 @@ void LLViewerMediaImpl::navigateHome()
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
void LLViewerMediaImpl::navigateTo(const std::string& _url, const std::string& mime_type, bool rediscover_type)
|
||||
void LLViewerMediaImpl::navigateTo(const std::string& url, const std::string& mime_type, bool rediscover_type)
|
||||
{
|
||||
// trim whitespace from front and back of URL - fixes EXT-5363
|
||||
std::string url(_url);
|
||||
LLStringUtil::trim(url);
|
||||
|
||||
if(rediscover_type)
|
||||
{
|
||||
|
||||
@@ -729,12 +749,7 @@ void LLViewerMediaImpl::navigateTo(const std::string& _url, const std::string& m
|
||||
|
||||
if(scheme.empty() || "http" == scheme || "https" == scheme)
|
||||
{
|
||||
// If we don't set an Accept header, LLHTTPClient will add one like this:
|
||||
// Accept: application/llsd+xml
|
||||
// which is really not what we want.
|
||||
LLSD headers = LLSD::emptyMap();
|
||||
headers["Accept"] = "*/*";
|
||||
LLHTTPClient::getHeaderOnly(url, new LLMimeDiscoveryResponder(this), headers, 10.0f);
|
||||
LLHTTPClient::getHeaderOnly( url, new LLMimeDiscoveryResponder(this));
|
||||
}
|
||||
else if("data" == scheme || "file" == scheme || "about" == scheme)
|
||||
{
|
||||
@@ -788,7 +803,38 @@ bool LLViewerMediaImpl::handleKeyHere(KEY key, MASK mask)
|
||||
|
||||
if (mMediaSource)
|
||||
{
|
||||
result = mMediaSource->keyEvent(LLPluginClassMedia::KEY_EVENT_DOWN ,key, mask);
|
||||
// FIXME: THIS IS SO WRONG.
|
||||
// Menu keys should be handled by the menu system and not passed to UI elements, but this is how LLTextEditor and LLLineEditor do it...
|
||||
if( MASK_CONTROL & mask )
|
||||
{
|
||||
if( 'C' == key )
|
||||
{
|
||||
mMediaSource->copy();
|
||||
result = true;
|
||||
}
|
||||
else
|
||||
if( 'V' == key )
|
||||
{
|
||||
mMediaSource->paste();
|
||||
result = true;
|
||||
}
|
||||
else
|
||||
if( 'X' == key )
|
||||
{
|
||||
mMediaSource->cut();
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(!result)
|
||||
{
|
||||
|
||||
LLSD native_key_data = LLSD::emptyMap();
|
||||
|
||||
result = mMediaSource->keyEvent(LLPluginClassMedia::KEY_EVENT_DOWN ,key, mask, native_key_data);
|
||||
// Since the viewer internal event dispatching doesn't give us key-up events, simulate one here.
|
||||
(void)mMediaSource->keyEvent(LLPluginClassMedia::KEY_EVENT_UP ,key, mask, native_key_data);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -805,7 +851,9 @@ bool LLViewerMediaImpl::handleUnicodeCharHere(llwchar uni_char)
|
||||
if (uni_char >= 32 // discard 'control' characters
|
||||
&& uni_char != 127) // SDL thinks this is 'delete' - yuck.
|
||||
{
|
||||
mMediaSource->textInput(wstring_to_utf8str(LLWString(1, uni_char)), gKeyboard->currentMask(FALSE));
|
||||
LLSD native_key_data = LLSD::emptyMap();
|
||||
|
||||
mMediaSource->textInput(wstring_to_utf8str(LLWString(1, uni_char)), gKeyboard->currentMask(FALSE), native_key_data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user