Split plugin classes and derive AIFilePicker from BasicPluginBase (part 3).

This commit deletes all indra/media_plugins and copied one-on-one
the indra/plugins directory from my (imprudence) statemachine branch
(which contains the AIFilePicker patch).

git shows this as a lot of 'renamed' files because originally it
was a rename. However, there are a lot of changes as well: it's both
an upgrade to a newer plugin system (changes by LL) as well as an
upgrade to my refactored plugin system with a file picker as plugin.

Since this commit is a one-on-one copy, it disregards any changes
that were in Singularity and not in imprudence in indra/media_plugins
however. I will add those back in the next commit.
This commit is contained in:
Aleric Inglewood
2011-05-06 16:08:24 +02:00
parent 784fdd4f37
commit 16cd4c5c4b
44 changed files with 3666 additions and 1577 deletions

View File

@@ -0,0 +1,17 @@
# -*- cmake -*-
add_subdirectory(base_basic)
add_subdirectory(base_media)
add_subdirectory(filepicker)
add_subdirectory(webkit)
if (LINUX)
add_subdirectory(gstreamer010)
endif (LINUX)
if (WINDOWS OR DARWIN)
add_subdirectory(quicktime)
endif (WINDOWS OR DARWIN)
add_subdirectory(example_basic)
add_subdirectory(example_media)

View File

@@ -0,0 +1,39 @@
# -*- cmake -*-
project(basic_plugin_base)
include(00-Common)
include(LLCommon)
include(LLPlugin)
include(Linking)
include(PluginAPI)
include_directories(
${LLPLUGIN_INCLUDE_DIRS}
${LLCOMMON_INCLUDE_DIRS}
)
### basic_plugin_base
if(NOT CMAKE_SIZEOF_VOID_P MATCHES 4)
if(WINDOWS)
add_definitions(/FIXED:NO)
else(WINDOWS) # not windows therefore gcc LINUX and DARWIN
add_definitions(-fPIC)
endif(WINDOWS)
endif (NOT CMAKE_SIZEOF_VOID_P MATCHES 4)
set(basic_plugin_base_SOURCE_FILES
basic_plugin_base.cpp
)
set(basic_plugin_base_HEADER_FILES
CMakeLists.txt
basic_plugin_base.h
)
add_library(basic_plugin_base
${basic_plugin_base_SOURCE_FILES}
)

View File

@@ -0,0 +1,136 @@
/**
* @file basic_plugin_base.cpp
* @brief Basic plugin base class for Basic API plugin system
*
* All plugins should be a subclass of BasicPluginBase.
*
* @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 "basic_plugin_base.h"
// TODO: Make sure that the only symbol exported from this library is LLPluginInitEntryPoint
////////////////////////////////////////////////////////////////////////////////
/// Basic plugin constructor.
///
/// @param[in] send_message_function Function for sending messages from plugin to plugin loader shell
/// @param[in] plugin_instance Message data for messages from plugin to plugin loader shell
BasicPluginBase::BasicPluginBase(LLPluginInstance::sendMessageFunction send_message_function, LLPluginInstance* plugin_instance)
{
mSendMessageFunction = send_message_function;
mPluginInstance = plugin_instance;
}
/**
* Receive message from plugin loader shell.
*
* @param[in] message_string Message string
* @param[in] user_data Message data
*
*/
void BasicPluginBase::staticReceiveMessage(char const* message_string, BasicPluginBase** self_ptr)
{
BasicPluginBase* self = *self_ptr;
if(self != NULL)
{
self->receiveMessage(message_string);
// If the plugin has processed the delete message, delete it.
if(self->mDeleteMe)
{
delete self;
*self_ptr = NULL;
}
}
}
// This is the SLPlugin process.
// This is the loaded DSO.
//
// Call this function to send 'message' to the viewer.
/**
* Send message to plugin loader shell.
*
* @param[in] message Message data being sent to plugin loader shell
*
*/
void BasicPluginBase::sendMessage(const LLPluginMessage &message)
{
std::string output = message.generate();
mSendMessageFunction(output.c_str(), &mPluginInstance);
}
#if LL_WINDOWS
# define LLSYMEXPORT __declspec(dllexport)
#elif LL_LINUX
# define LLSYMEXPORT __attribute__ ((visibility("default")))
#else
# define LLSYMEXPORT /**/
#endif
extern "C"
{
LLSYMEXPORT int LLPluginInitEntryPoint(LLPluginInstance::sendMessageFunction send_message_function,
LLPluginInstance* plugin_instance,
LLPluginInstance::receiveMessageFunction* receive_message_function,
BasicPluginBase** plugin_object);
}
/**
* Plugin initialization and entry point. Establishes communication channel for messages between plugin and plugin loader shell. TODO:DOC - Please check!
*
* @param[in] send_message_function Function for sending messages from plugin to viewer
* @param[in] plugin_instance Message data for messages from plugin to plugin loader shell
* @param[out] receive_message_function Function for receiving message from viewer to plugin
* @param[out] plugin_object Pointer to plugin instance
*
* @return int, where 0=success
*
*/
LLSYMEXPORT int
LLPluginInitEntryPoint(LLPluginInstance::sendMessageFunction send_message_function,
LLPluginInstance* plugin_instance,
LLPluginInstance::receiveMessageFunction* receive_message_function,
BasicPluginBase** plugin_object)
{
*receive_message_function = BasicPluginBase::staticReceiveMessage;
return create_plugin(send_message_function, plugin_instance, plugin_object);
}
#ifdef WIN32
int WINAPI DllEntryPoint( HINSTANCE hInstance, unsigned long reason, void* params )
{
return 1;
}
#endif

View File

@@ -0,0 +1,2 @@
_LLPluginInitEntryPoint

View File

@@ -0,0 +1,88 @@
/**
* @file basic_plugin_base.h
* @brief Basic plugin base class for Basic API plugin system
*
* @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 BASIC_PLUGIN_BASE_H
#define BASIC_PLUGIN_BASE_H
#include <sstream>
#include "linden_common.h"
#include "llplugininstance.h"
#include "llpluginmessage.h"
#include "llpluginmessageclasses.h"
class BasicPluginBase
{
public:
BasicPluginBase(LLPluginInstance::sendMessageFunction send_message_function, LLPluginInstance* plugin_instance);
//! Basic plugin destructor.
virtual ~BasicPluginBase() {}
//! Handle received message from plugin loader shell.
virtual void receiveMessage(char const* message_string) = 0;
// This function is actually called and then calls the member function above.
static void staticReceiveMessage(char const* message_string, BasicPluginBase** self_ptr);
protected:
void sendMessage(LLPluginMessage const& message);
//! Message data being sent to plugin loader shell by mSendMessageFunction.
LLPluginInstance* mPluginInstance;
//! Function to send message from plugin to plugin loader shell.
LLPluginInstance::sendMessageFunction mSendMessageFunction;
//! Flag to delete plugin instance (self).
bool mDeleteMe;
};
/** The plugin <b>must</b> define this function to create its instance.
* It should look something like this:
* @code
* {
* *plugin_object = new FooPluginBar(send_message_function, plugin_instance);
* return 0;
* }
* @endcode
*/
int create_plugin(
LLPluginInstance::sendMessageFunction send_message_function,
LLPluginInstance* plugin_instance,
BasicPluginBase** plugin_object);
#endif // BASIC_PLUGIN_BASE

View File

@@ -0,0 +1,58 @@
# -*- cmake -*-
project(media_plugin_base)
include(00-Common)
include(LLCommon)
include(LLImage)
include(LLPlugin)
include(LLMath)
include(LLRender)
include(LLWindow)
include(Linking)
include(PluginAPI)
include(FindOpenGL)
include(BasicPluginBase)
include_directories(
${LLPLUGIN_INCLUDE_DIRS}
${LLCOMMON_INCLUDE_DIRS}
${LLMATH_INCLUDE_DIRS}
${LLIMAGE_INCLUDE_DIRS}
${LLRENDER_INCLUDE_DIRS}
${LLWINDOW_INCLUDE_DIRS}
${BASIC_PLUGIN_BASE_INCLUDE_DIRS}
)
### media_plugin_base
if(NOT CMAKE_SIZEOF_VOID_P MATCHES 4)
if(WINDOWS)
add_definitions(/FIXED:NO)
else(WINDOWS) # not windows therefore gcc LINUX and DARWIN
add_definitions(-fPIC)
endif(WINDOWS)
endif (NOT CMAKE_SIZEOF_VOID_P MATCHES 4)
set(media_plugin_base_SOURCE_FILES
media_plugin_base.cpp
)
set(media_plugin_base_HEADER_FILES
CMakeLists.txt
media_plugin_base.h
)
add_library(media_plugin_base
${media_plugin_base_SOURCE_FILES}
)
target_link_libraries(media_plugin_base
${BASIC_PLUGIN_BASE_LIBRARIES}
)
add_dependencies(media_plugin_base
${BASIC_PLUGIN_BASE_LIBRARIES}
)

View File

@@ -0,0 +1,151 @@
/**
* @file media_plugin_base.cpp
* @brief Media plugin base class for LLMedia API plugin system
*
* All plugins should be a subclass of MediaPluginBase.
*
* @cond
* $LicenseInfo:firstyear=2008&license=viewergpl$
*
* 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
* ("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 "media_plugin_base.h"
// TODO: Make sure that the only symbol exported from this library is LLPluginInitEntryPoint
////////////////////////////////////////////////////////////////////////////////
/// Media plugin constructor.
///
/// @param[in] send_message_function Function for sending messages from plugin to plugin loader shell
/// @param[in] host_user_data Message data for messages from plugin to plugin loader shell
MediaPluginBase::MediaPluginBase(LLPluginInstance::sendMessageFunction send_message_function, LLPluginInstance* plugin_instance)
: BasicPluginBase(send_message_function, plugin_instance)
{
mDeleteMe = false;
mPixels = 0;
mWidth = 0;
mHeight = 0;
mTextureWidth = 0;
mTextureHeight = 0;
mDepth = 0;
mStatus = STATUS_NONE;
}
/**
* Converts current media status enum value into string (STATUS_LOADING into "loading", etc.)
*
* @return Media status string ("loading", "playing", "paused", etc)
*
*/
std::string MediaPluginBase::statusString()
{
std::string result;
switch(mStatus)
{
case STATUS_LOADING: result = "loading"; break;
case STATUS_LOADED: result = "loaded"; break;
case STATUS_ERROR: result = "error"; break;
case STATUS_PLAYING: result = "playing"; break;
case STATUS_PAUSED: result = "paused"; break;
case STATUS_DONE: result = "done"; break;
default:
// keep the empty string
break;
}
return result;
}
/**
* Set media status.
*
* @param[in] status Media status (STATUS_LOADING, STATUS_PLAYING, STATUS_PAUSED, etc)
*
*/
void MediaPluginBase::setStatus(EStatus status)
{
if(mStatus != status)
{
mStatus = status;
sendStatus();
}
}
/**
* Notifies plugin loader shell that part of display area needs to be redrawn.
*
* @param[in] left Left X coordinate of area to redraw (0,0 is at top left corner)
* @param[in] top Top Y coordinate of area to redraw (0,0 is at top left corner)
* @param[in] right Right X-coordinate of area to redraw (0,0 is at top left corner)
* @param[in] bottom Bottom Y-coordinate of area to redraw (0,0 is at top left corner)
*
*/
void MediaPluginBase::setDirty(int left, int top, int right, int bottom)
{
LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "updated");
message.setValueS32("left", left);
message.setValueS32("top", top);
message.setValueS32("right", right);
message.setValueS32("bottom", bottom);
sendMessage(message);
}
/**
* Sends "media_status" message to plugin loader shell ("loading", "playing", "paused", etc.)
*
*/
void MediaPluginBase::sendStatus()
{
LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "media_status");
message.setValue("status", statusString());
sendMessage(message);
}
#if LL_WINDOWS
# define LLSYMEXPORT __declspec(dllexport)
#elif LL_LINUX
# define LLSYMEXPORT __attribute__ ((visibility("default")))
#else
# define LLSYMEXPORT /**/
#endif
#ifdef WIN32
int WINAPI DllEntryPoint( HINSTANCE hInstance, unsigned long reason, void* params )
{
return 1;
}
#endif

View File

@@ -0,0 +1,2 @@
_LLPluginInitEntryPoint

View File

@@ -0,0 +1,100 @@
/**
* @file media_plugin_base.h
* @brief Media plugin base class for LLMedia API plugin system
*
* @cond
* $LicenseInfo:firstyear=2008&license=viewergpl$
*
* 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
* ("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 MEDIA_PLUGIN_BASE_H
#define MEDIA_PLUGIN_BASE_H
#include "basic_plugin_base.h"
class MediaPluginBase : public BasicPluginBase
{
public:
MediaPluginBase(LLPluginInstance::sendMessageFunction send_message_function, LLPluginInstance* plugin_instance);
protected:
/** Plugin status. */
typedef enum
{
STATUS_NONE,
STATUS_LOADING,
STATUS_LOADED,
STATUS_ERROR,
STATUS_PLAYING,
STATUS_PAUSED,
STATUS_DONE
} EStatus;
/** Plugin shared memory. */
class SharedSegmentInfo
{
public:
/** Shared memory address. */
void *mAddress;
/** Shared memory size. */
size_t mSize;
};
void sendStatus();
std::string statusString();
void setStatus(EStatus status);
/// Note: The quicktime plugin overrides this to add current time and duration to the message.
virtual void setDirty(int left, int top, int right, int bottom);
/** Map of shared memory names to shared memory. */
typedef std::map<std::string, SharedSegmentInfo> SharedSegmentMap;
/** Pixel array to display. TODO:DOC are pixels always 24-bit RGB format, aligned on 32-bit boundary? Also: calling this a pixel array may be misleading since 1 pixel > 1 char. */
unsigned char* mPixels;
/** TODO:DOC what's this for -- does a texture have its own piece of shared memory? updated on size_change_request, cleared on shm_remove */
std::string mTextureSegmentName;
/** Width of plugin display in pixels. */
int mWidth;
/** Height of plugin display in pixels. */
int mHeight;
/** Width of plugin texture. */
int mTextureWidth;
/** Height of plugin texture. */
int mTextureHeight;
/** Pixel depth (pixel size in bytes). */
int mDepth;
/** Current status of plugin. */
EStatus mStatus;
/** Map of shared memory segments. */
SharedSegmentMap mSharedSegments;
};
#endif // MEDIA_PLUGIN_BASE_H

View File

@@ -0,0 +1,68 @@
# -*- cmake -*-
project(basic_plugin_example)
include(00-Common)
include(LLCommon)
include(LLPlugin)
include(Linking)
include(PluginAPI)
include(BasicPluginBase)
include_directories(
${LLPLUGIN_INCLUDE_DIRS}
${LLCOMMON_INCLUDE_DIRS}
${BASIC_PLUGIN_BASE_INCLUDE_DIRS}
)
### basic_plugin_example
if(NOT CMAKE_SIZEOF_VOID_P MATCHES 4)
if(WINDOWS)
add_definitions(/FIXED:NO)
else(WINDOWS) # not windows therefore gcc LINUX and DARWIN
add_definitions(-fPIC)
endif(WINDOWS)
endif (NOT CMAKE_SIZEOF_VOID_P MATCHES 4)
set(basic_plugin_example_SOURCE_FILES
basic_plugin_example.cpp
)
add_library(basic_plugin_example
SHARED
${basic_plugin_example_SOURCE_FILES}
)
target_link_libraries(basic_plugin_example
${LLPLUGIN_LIBRARIES}
${LLCOMMON_LIBRARIES}
${BASIC_PLUGIN_BASE_LIBRARIES}
)
add_dependencies(basic_plugin_example
${LLPLUGIN_LIBRARIES}
${LLCOMMON_LIBRARIES}
${BASIC_PLUGIN_BASE_LIBRARIES}
)
if (WINDOWS)
set_target_properties(
basic_plugin_example
PROPERTIES
LINK_FLAGS "/MANIFEST:NO"
)
endif (WINDOWS)
if (DARWIN)
# Don't prepend 'lib' to the executable name, and don't embed a full path in the library's install name
set_target_properties(
basic_plugin_example
PROPERTIES
PREFIX ""
BUILD_WITH_INSTALL_RPATH 1
INSTALL_NAME_DIR "@executable_path"
LINK_FLAGS "-exported_symbols_list ${CMAKE_CURRENT_SOURCE_DIR}/../base_basic/basic_plugin_base.exp"
)
endif (DARWIN)

View File

@@ -0,0 +1,123 @@
/**
* @file basic_plugin_example.cpp
* @brief Example Plugin for a basic plugin.
*
* @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 "basic_plugin_base.h"
class BasicPluginExample : public BasicPluginBase
{
public:
BasicPluginExample(LLPluginInstance::sendMessageFunction send_message_function, LLPluginInstance* plugin_instance);
~BasicPluginExample();
/*virtual*/ void receiveMessage(char const* message_string);
private:
bool init();
};
BasicPluginExample::BasicPluginExample(LLPluginInstance::sendMessageFunction send_message_function, LLPluginInstance* plugin_instance) :
BasicPluginBase(send_message_function, plugin_instance)
{
}
BasicPluginExample::~BasicPluginExample()
{
}
void BasicPluginExample::receiveMessage(char const* message_string)
{
LLPluginMessage message_in;
if (message_in.parse(message_string) >= 0)
{
std::string message_class = message_in.getClass();
std::string message_name = message_in.getName();
if (message_class == LLPLUGIN_MESSAGE_CLASS_BASE)
{
if (message_name == "init")
{
LLPluginMessage message("base", "init_response");
LLSD versions = LLSD::emptyMap();
versions[LLPLUGIN_MESSAGE_CLASS_BASE] = LLPLUGIN_MESSAGE_CLASS_BASE_VERSION;
versions[LLPLUGIN_MESSAGE_CLASS_BASIC] = LLPLUGIN_MESSAGE_CLASS_BASIC_VERSION;
message.setValueLLSD("versions", versions);
std::string plugin_version = "Basic Plugin Example, version 1.0.0.0";
message.setValue("plugin_version", plugin_version);
sendMessage(message);
}
else if (message_name == "idle")
{
// This whole message should not have existed imho -- Aleric
}
else
{
std::cerr << "BasicPluginExample::receiveMessage: unknown base message: " << message_name << std::endl;
}
}
else if (message_class == LLPLUGIN_MESSAGE_CLASS_BASIC)
{
if (message_name == "poke")
{
LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_BASIC, "pokeback");
sendMessage(message);
}
}
else
{
std::cerr << "BasicPluginExample::receiveMessage: unknown message class: " << message_class << std::endl;
}
}
}
bool BasicPluginExample::init(void)
{
LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_BASIC, "name_text");
message.setValue("name", "Basic Plugin Example");
sendMessage(message);
return true;
}
int create_plugin(LLPluginInstance::sendMessageFunction send_message_function,
LLPluginInstance* plugin_instance,
BasicPluginBase** plugin_object)
{
*plugin_object = new BasicPluginExample(send_message_function, plugin_instance);
return 0;
}

View File

@@ -0,0 +1,81 @@
# -*- cmake -*-
project(media_plugin_example)
include(00-Common)
include(LLCommon)
include(LLImage)
include(LLPlugin)
include(LLMath)
include(LLRender)
include(LLWindow)
include(Linking)
include(PluginAPI)
include(MediaPluginBase)
include(FindOpenGL)
#include(ExamplePlugin)
include_directories(
${LLPLUGIN_INCLUDE_DIRS}
${MEDIA_PLUGIN_BASE_INCLUDE_DIRS}
${LLCOMMON_INCLUDE_DIRS}
${LLMATH_INCLUDE_DIRS}
${LLIMAGE_INCLUDE_DIRS}
${LLRENDER_INCLUDE_DIRS}
${LLWINDOW_INCLUDE_DIRS}
)
### media_plugin_example
if(NOT CMAKE_SIZEOF_VOID_P MATCHES 4)
if(WINDOWS)
add_definitions(/FIXED:NO)
else(WINDOWS) # not windows therefore gcc LINUX and DARWIN
add_definitions(-fPIC)
endif(WINDOWS)
endif (NOT CMAKE_SIZEOF_VOID_P MATCHES 4)
set(media_plugin_example_SOURCE_FILES
media_plugin_example.cpp
)
add_library(media_plugin_example
SHARED
${media_plugin_example_SOURCE_FILES}
)
target_link_libraries(media_plugin_example
${LLPLUGIN_LIBRARIES}
${MEDIA_PLUGIN_BASE_LIBRARIES}
${LLCOMMON_LIBRARIES}
${EXAMPLE_PLUGIN_LIBRARIES}
${PLUGIN_API_WINDOWS_LIBRARIES}
)
add_dependencies(media_plugin_example
${LLPLUGIN_LIBRARIES}
${MEDIA_PLUGIN_BASE_LIBRARIES}
${LLCOMMON_LIBRARIES}
)
if (WINDOWS)
set_target_properties(
media_plugin_example
PROPERTIES
LINK_FLAGS "/MANIFEST:NO"
)
endif (WINDOWS)
if (DARWIN)
# Don't prepend 'lib' to the executable name, and don't embed a full path in the library's install name
set_target_properties(
media_plugin_example
PROPERTIES
PREFIX ""
BUILD_WITH_INSTALL_RPATH 1
INSTALL_NAME_DIR "@executable_path"
LINK_FLAGS "-exported_symbols_list ${CMAKE_CURRENT_SOURCE_DIR}/../base_media/media_plugin_base.exp"
)
endif (DARWIN)

View File

@@ -0,0 +1,490 @@
/**
* @file media_plugin_example.cpp
* @brief Example plugin for LLMedia API plugin system
*
* @cond
* $LicenseInfo:firstyear=2008&license=viewergpl$
*
* 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
* ("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 "llgl.h"
#include "llplugininstance.h"
#include "llpluginmessage.h"
#include "llpluginmessageclasses.h"
#include "media_plugin_base.h"
#include <time.h>
////////////////////////////////////////////////////////////////////////////////
//
class MediaPluginExample :
public MediaPluginBase
{
public:
MediaPluginExample(LLPluginInstance::sendMessageFunction send_message_function, LLPluginInstance* plugin_instance);
~MediaPluginExample();
/*virtual*/ void receiveMessage( const char* message_string );
private:
bool init();
void update( F64 milliseconds );
void write_pixel( int x, int y, unsigned char r, unsigned char g, unsigned char b );
bool mFirstTime;
time_t mLastUpdateTime;
enum Constants { ENumObjects = 10 };
unsigned char* mBackgroundPixels;
int mColorR[ ENumObjects ];
int mColorG[ ENumObjects ];
int mColorB[ ENumObjects ];
int mXpos[ ENumObjects ];
int mYpos[ ENumObjects ];
int mXInc[ ENumObjects ];
int mYInc[ ENumObjects ];
int mBlockSize[ ENumObjects ];
bool mMouseButtonDown;
bool mStopAction;
};
////////////////////////////////////////////////////////////////////////////////
//
MediaPluginExample::MediaPluginExample(LLPluginInstance::sendMessageFunction send_message_function, LLPluginInstance* plugin_instance) :
MediaPluginBase(send_message_function, plugin_instance)
{
mFirstTime = true;
mWidth = 0;
mHeight = 0;
mDepth = 4;
mPixels = 0;
mMouseButtonDown = false;
mStopAction = false;
mLastUpdateTime = 0;
}
////////////////////////////////////////////////////////////////////////////////
//
MediaPluginExample::~MediaPluginExample()
{
}
////////////////////////////////////////////////////////////////////////////////
//
void MediaPluginExample::receiveMessage( const char* message_string )
{
LLPluginMessage message_in;
if ( message_in.parse( message_string ) >= 0 )
{
std::string message_class = message_in.getClass();
std::string message_name = message_in.getName();
if ( message_class == LLPLUGIN_MESSAGE_CLASS_BASE )
{
if ( message_name == "init" )
{
LLPluginMessage message( "base", "init_response" );
LLSD versions = LLSD::emptyMap();
versions[ LLPLUGIN_MESSAGE_CLASS_BASE ] = LLPLUGIN_MESSAGE_CLASS_BASE_VERSION;
versions[ LLPLUGIN_MESSAGE_CLASS_MEDIA ] = LLPLUGIN_MESSAGE_CLASS_MEDIA_VERSION;
versions[ LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER ] = LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER_VERSION;
message.setValueLLSD( "versions", versions );
std::string plugin_version = "Example media plugin, Example Version 1.0.0.0";
message.setValue( "plugin_version", plugin_version );
sendMessage( message );
}
else
if ( message_name == "idle" )
{
// no response is necessary here.
F64 time = message_in.getValueReal( "time" );
// Convert time to milliseconds for update()
update( time );
}
else
if ( message_name == "cleanup" )
{
// clean up here
}
else
if ( message_name == "shm_added" )
{
SharedSegmentInfo info;
info.mAddress = message_in.getValuePointer( "address" );
info.mSize = ( size_t )message_in.getValueS32( "size" );
std::string name = message_in.getValue( "name" );
mSharedSegments.insert( SharedSegmentMap::value_type( name, info ) );
}
else
if ( message_name == "shm_remove" )
{
std::string name = message_in.getValue( "name" );
SharedSegmentMap::iterator iter = mSharedSegments.find( name );
if( iter != mSharedSegments.end() )
{
if ( mPixels == iter->second.mAddress )
{
// This is the currently active pixel buffer.
// Make sure we stop drawing to it.
mPixels = NULL;
mTextureSegmentName.clear();
};
mSharedSegments.erase( iter );
}
else
{
//std::cerr << "MediaPluginExample::receiveMessage: unknown shared memory region!" << std::endl;
};
// Send the response so it can be cleaned up.
LLPluginMessage message( "base", "shm_remove_response" );
message.setValue( "name", name );
sendMessage( message );
}
else
{
//std::cerr << "MediaPluginExample::receiveMessage: unknown base message: " << message_name << std::endl;
};
}
else
if ( message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA )
{
if ( message_name == "init" )
{
// Plugin gets to decide the texture parameters to use.
LLPluginMessage message( LLPLUGIN_MESSAGE_CLASS_MEDIA, "texture_params" );
message.setValueS32( "default_width", mWidth );
message.setValueS32( "default_height", mHeight );
message.setValueS32( "depth", mDepth );
message.setValueU32( "internalformat", GL_RGBA );
message.setValueU32( "format", GL_RGBA );
message.setValueU32( "type", GL_UNSIGNED_BYTE );
message.setValueBoolean( "coords_opengl", false );
sendMessage( message );
}
else if ( message_name == "size_change" )
{
std::string name = message_in.getValue( "name" );
S32 width = message_in.getValueS32( "width" );
S32 height = message_in.getValueS32( "height" );
S32 texture_width = message_in.getValueS32( "texture_width" );
S32 texture_height = message_in.getValueS32( "texture_height" );
if ( ! name.empty() )
{
// Find the shared memory region with this name
SharedSegmentMap::iterator iter = mSharedSegments.find( name );
if ( iter != mSharedSegments.end() )
{
mPixels = ( unsigned char* )iter->second.mAddress;
mWidth = width;
mHeight = height;
mTextureWidth = texture_width;
mTextureHeight = texture_height;
init();
};
};
LLPluginMessage message( LLPLUGIN_MESSAGE_CLASS_MEDIA, "size_change_response" );
message.setValue( "name", name );
message.setValueS32( "width", width );
message.setValueS32( "height", height );
message.setValueS32( "texture_width", texture_width );
message.setValueS32( "texture_height", texture_height );
sendMessage( message );
}
else
if ( message_name == "load_uri" )
{
std::string uri = message_in.getValue( "uri" );
if ( ! uri.empty() )
{
};
}
else
if ( message_name == "mouse_event" )
{
std::string event = message_in.getValue( "event" );
S32 button = message_in.getValueS32( "button" );
// left mouse button
if ( button == 0 )
{
int mouse_x = message_in.getValueS32( "x" );
int mouse_y = message_in.getValueS32( "y" );
std::string modifiers = message_in.getValue( "modifiers" );
if ( event == "move" )
{
if ( mMouseButtonDown )
write_pixel( mouse_x, mouse_y, rand() % 0x80 + 0x80, rand() % 0x80 + 0x80, rand() % 0x80 + 0x80 );
}
else
if ( event == "down" )
{
mMouseButtonDown = true;
}
else
if ( event == "up" )
{
mMouseButtonDown = false;
}
else
if ( event == "double_click" )
{
};
};
}
else
if ( message_name == "key_event" )
{
std::string event = message_in.getValue( "event" );
S32 key = message_in.getValueS32( "key" );
std::string modifiers = message_in.getValue( "modifiers" );
if ( event == "down" )
{
if ( key == ' ')
{
mLastUpdateTime = 0;
update( 0.0f );
};
};
}
else
{
//std::cerr << "MediaPluginExample::receiveMessage: unknown media message: " << message_string << std::endl;
};
}
else
if ( message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER )
{
if ( message_name == "browse_reload" )
{
mLastUpdateTime = 0;
mFirstTime = true;
mStopAction = false;
update( 0.0f );
}
else
if ( message_name == "browse_stop" )
{
for( int n = 0; n < ENumObjects; ++n )
mXInc[ n ] = mYInc[ n ] = 0;
mStopAction = true;
update( 0.0f );
}
else
{
//std::cerr << "MediaPluginExample::receiveMessage: unknown media_browser message: " << message_string << std::endl;
};
}
else
{
//std::cerr << "MediaPluginExample::receiveMessage: unknown message class: " << message_class << std::endl;
};
};
}
////////////////////////////////////////////////////////////////////////////////
//
void MediaPluginExample::write_pixel( int x, int y, unsigned char r, unsigned char g, unsigned char b )
{
// make sure we don't write outside the buffer
if ( ( x < 0 ) || ( x >= mWidth ) || ( y < 0 ) || ( y >= mHeight ) )
return;
if ( mBackgroundPixels != NULL )
{
unsigned char *pixel = mBackgroundPixels;
pixel += y * mWidth * mDepth;
pixel += ( x * mDepth );
pixel[ 0 ] = b;
pixel[ 1 ] = g;
pixel[ 2 ] = r;
setDirty( x, y, x + 1, y + 1 );
};
}
////////////////////////////////////////////////////////////////////////////////
//
void MediaPluginExample::update( F64 milliseconds )
{
if ( mWidth < 1 || mWidth > 2048 || mHeight < 1 || mHeight > 2048 )
return;
if ( mPixels == 0 )
return;
if ( mFirstTime )
{
for( int n = 0; n < ENumObjects; ++n )
{
mXpos[ n ] = ( mWidth / 2 ) + rand() % ( mWidth / 16 ) - ( mWidth / 32 );
mYpos[ n ] = ( mHeight / 2 ) + rand() % ( mHeight / 16 ) - ( mHeight / 32 );
mColorR[ n ] = rand() % 0x60 + 0x60;
mColorG[ n ] = rand() % 0x60 + 0x60;
mColorB[ n ] = rand() % 0x60 + 0x60;
mXInc[ n ] = 0;
while ( mXInc[ n ] == 0 )
mXInc[ n ] = rand() % 7 - 3;
mYInc[ n ] = 0;
while ( mYInc[ n ] == 0 )
mYInc[ n ] = rand() % 9 - 4;
mBlockSize[ n ] = rand() % 0x30 + 0x10;
};
delete [] mBackgroundPixels;
mBackgroundPixels = new unsigned char[ mWidth * mHeight * mDepth ];
mFirstTime = false;
};
if ( mStopAction )
return;
if ( time( NULL ) > mLastUpdateTime + 3 )
{
const int num_squares = rand() % 20 + 4;
int sqr1_r = rand() % 0x80 + 0x20;
int sqr1_g = rand() % 0x80 + 0x20;
int sqr1_b = rand() % 0x80 + 0x20;
int sqr2_r = rand() % 0x80 + 0x20;
int sqr2_g = rand() % 0x80 + 0x20;
int sqr2_b = rand() % 0x80 + 0x20;
for ( int y1 = 0; y1 < num_squares; ++y1 )
{
for ( int x1 = 0; x1 < num_squares; ++x1 )
{
int px_start = mWidth * x1 / num_squares;
int px_end = ( mWidth * ( x1 + 1 ) ) / num_squares;
int py_start = mHeight * y1 / num_squares;
int py_end = ( mHeight * ( y1 + 1 ) ) / num_squares;
for( int y2 = py_start; y2 < py_end; ++y2 )
{
for( int x2 = px_start; x2 < px_end; ++x2 )
{
int rowspan = mWidth * mDepth;
if ( ( y1 % 2 ) ^ ( x1 % 2 ) )
{
mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 0 ] = sqr1_r;
mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 1 ] = sqr1_g;
mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 2 ] = sqr1_b;
}
else
{
mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 0 ] = sqr2_r;
mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 1 ] = sqr2_g;
mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 2 ] = sqr2_b;
};
};
};
};
};
time( &mLastUpdateTime );
};
memcpy( mPixels, mBackgroundPixels, mWidth * mHeight * mDepth );
for( int n = 0; n < ENumObjects; ++n )
{
if ( rand() % 50 == 0 )
{
mXInc[ n ] = 0;
while ( mXInc[ n ] == 0 )
mXInc[ n ] = rand() % 7 - 3;
mYInc[ n ] = 0;
while ( mYInc[ n ] == 0 )
mYInc[ n ] = rand() % 9 - 4;
};
if ( mXpos[ n ] + mXInc[ n ] < 0 || mXpos[ n ] + mXInc[ n ] >= mWidth - mBlockSize[ n ] )
mXInc[ n ] =- mXInc[ n ];
if ( mYpos[ n ] + mYInc[ n ] < 0 || mYpos[ n ] + mYInc[ n ] >= mHeight - mBlockSize[ n ] )
mYInc[ n ] =- mYInc[ n ];
mXpos[ n ] += mXInc[ n ];
mYpos[ n ] += mYInc[ n ];
for( int y = 0; y < mBlockSize[ n ]; ++y )
{
for( int x = 0; x < mBlockSize[ n ]; ++x )
{
mPixels[ ( mXpos[ n ] + x ) * mDepth + ( mYpos[ n ] + y ) * mDepth * mWidth + 0 ] = mColorR[ n ];
mPixels[ ( mXpos[ n ] + x ) * mDepth + ( mYpos[ n ] + y ) * mDepth * mWidth + 1 ] = mColorG[ n ];
mPixels[ ( mXpos[ n ] + x ) * mDepth + ( mYpos[ n ] + y ) * mDepth * mWidth + 2 ] = mColorB[ n ];
};
};
};
setDirty( 0, 0, mWidth, mHeight );
};
////////////////////////////////////////////////////////////////////////////////
//
bool MediaPluginExample::init()
{
LLPluginMessage message( LLPLUGIN_MESSAGE_CLASS_MEDIA, "name_text" );
message.setValue( "name", "Example Plugin" );
sendMessage( message );
return true;
};
////////////////////////////////////////////////////////////////////////////////
//
int create_plugin(LLPluginInstance::sendMessageFunction send_message_function,
LLPluginInstance* plugin_instance,
BasicPluginBase** plugin_object)
{
*plugin_object = new MediaPluginExample(send_message_function, plugin_instance);
return 0;
}

View File

@@ -0,0 +1,81 @@
# -*- cmake -*-
project(basic_plugin_filepicker)
include(00-Common)
include(LLCommon)
include(LLPlugin)
include(Linking)
include(PluginAPI)
include(BasicPluginBase)
include(UI)
include_directories(
${LLPLUGIN_INCLUDE_DIRS}
${LLCOMMON_INCLUDE_DIRS}
${BASIC_PLUGIN_BASE_INCLUDE_DIRS}
${LLUI_INCLUDE_DIRS}
)
### basic_plugin_filepicker
if(NOT CMAKE_SIZEOF_VOID_P MATCHES 4)
if(WINDOWS)
add_definitions(/FIXED:NO)
else(WINDOWS) # not windows therefore gcc LINUX and DARWIN
add_definitions(-fPIC)
endif(WINDOWS)
endif (NOT CMAKE_SIZEOF_VOID_P MATCHES 4)
set(basic_plugin_filepicker_SOURCE_FILES
basic_plugin_filepicker.cpp
llfilepicker.cpp
)
set(basic_plugin_filepicker_HEADER_FILES
llfilepicker.h
)
set_source_files_properties(${basic_plugin_filepicker_HEADER_FILES}
PROPERTIES HEADER_FILE_ONLY TRUE)
list(APPEND basic_plugin_filepicker_SOURCE_FILES ${basic_plugin_filepicker_HEADER_FILES})
add_library(basic_plugin_filepicker
SHARED
${basic_plugin_filepicker_SOURCE_FILES}
)
target_link_libraries(basic_plugin_filepicker
${LLPLUGIN_LIBRARIES}
${LLCOMMON_LIBRARIES}
${BASIC_PLUGIN_BASE_LIBRARIES}
${UI_LIBRARIES}
)
add_dependencies(basic_plugin_filepicker
${LLPLUGIN_LIBRARIES}
${LLCOMMON_LIBRARIES}
${BASIC_PLUGIN_BASE_LIBRARIES}
)
if (WINDOWS)
set_target_properties(
basic_plugin_filepicker
PROPERTIES
LINK_FLAGS "/MANIFEST:NO"
)
endif (WINDOWS)
if (DARWIN)
# Don't prepend 'lib' to the executable name, and don't embed a full path in the library's install name
set_target_properties(
basic_plugin_filepicker
PROPERTIES
PREFIX ""
BUILD_WITH_INSTALL_RPATH 1
INSTALL_NAME_DIR "@executable_path"
LINK_FLAGS "-exported_symbols_list ${CMAKE_CURRENT_SOURCE_DIR}/../base_basic/basic_plugin_base.exp"
)
endif (DARWIN)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,231 @@
/**
* @file llfilepicker.h
* @brief OS-specific file picker
*
* $LicenseInfo:firstyear=2001&license=viewergpl$
*
* Copyright (c) 2001-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$
*/
// OS specific file selection dialog. This is implemented as a
// singleton class, so call the instance() method to get the working
// instance. When you call getMultipleLoadFile(), it locks the picker
// until you iterate to the end of the list of selected files with
// getNextFile() or call reset().
#ifndef LL_LLFILEPICKER_H
#define LL_LLFILEPICKER_H
#include "stdtypes.h"
#include <string>
#include <vector>
#include <map>
#if LL_DARWIN
#include <Carbon/Carbon.h>
// AssertMacros.h does bad things.
#undef verify
#undef check
#undef require
#include "llstring.h"
#endif
// Need commdlg.h for OPENFILENAMEA
#ifdef LL_WINDOWS
#include <commdlg.h>
#endif
// mostly for Linux, possible on others
#if LL_GTK
# include "gtk/gtk.h"
#endif // LL_GTK
// also mostly for Linux, for some X11-specific filepicker usability tweaks
#if LL_X11
#include "SDL/SDL_syswm.h"
#endif
// This class is used as base class of a singleton and is therefore not
// allowed to have any static members or static local variables!
class LLFilePickerBase
{
public:
enum ELoadFilter
{
FFLOAD_ALL = 1,
FFLOAD_WAV = 2,
FFLOAD_IMAGE = 3,
FFLOAD_ANIM = 4,
#ifdef _CORY_TESTING
FFLOAD_GEOMETRY = 5,
#endif
FFLOAD_XML = 6,
FFLOAD_SLOBJECT = 7,
FFLOAD_RAW = 8,
FFLOAD_TEXT = 9,
};
enum ESaveFilter
{
FFSAVE_ALL = 1,
FFSAVE_WAV = 3,
FFSAVE_TGA = 4,
FFSAVE_BMP = 5,
FFSAVE_AVI = 6,
FFSAVE_ANIM = 7,
#ifdef _CORY_TESTING
FFSAVE_GEOMETRY = 8,
#endif
FFSAVE_XML = 9,
FFSAVE_COLLADA = 10,
FFSAVE_RAW = 11,
FFSAVE_J2C = 12,
FFSAVE_PNG = 13,
FFSAVE_JPEG = 14,
FFSAVE_HPA = 15,
FFSAVE_TEXT = 16,
FFSAVE_LSL = 17
};
// open the dialog. This is a modal operation
bool getSaveFile(ESaveFilter filter, std::string const& filename, std::string const& folder);
bool getLoadFile(ELoadFilter filter, std::string const& folder);
bool getMultipleLoadFiles(ELoadFilter filter, std::string const& folder);
// Get the filename(s) found. getFirstFile() sets the pointer to
// the start of the structure and allows the start of iteration.
const std::string getFirstFile();
// getNextFile() increments the internal representation and
// returns the next file specified by the user. Returns NULL when
// no more files are left. Further calls to getNextFile() are
// undefined.
const std::string getNextFile();
// This utility function extracts the current file name without
// doing any incrementing.
const std::string getCurFile();
// Returns the index of the current file.
S32 getCurFileNum() const { return mCurrentFile; }
S32 getFileCount() const { return (S32)mFiles.size(); }
// See llvfs/lldir.h : getBaseFileName and getDirName to extract base or directory names
// clear any lists of buffers or whatever, and make sure the file
// picker isn't locked.
void reset();
private:
enum
{
SINGLE_FILENAME_BUFFER_SIZE = 1024,
//FILENAME_BUFFER_SIZE = 65536
FILENAME_BUFFER_SIZE = 65000
};
#if LL_WINDOWS
OPENFILENAMEW mOFN; // for open and save dialogs
WCHAR mFilesW[FILENAME_BUFFER_SIZE];
public:
void setWindowID(unsigned long window_id) { mOFN.hwndOwner = (HWND)window_id; }
private:
bool setupFilter(ELoadFilter filter);
#endif // LL_WINDOWS
#if LL_DARWIN
NavDialogCreationOptions mNavOptions;
std::vector<std::string> mFileVector;
UInt32 mFileIndex;
OSStatus doNavChooseDialog(ELoadFilter filter);
OSStatus doNavSaveDialog(ESaveFilter filter, const std::string& filename);
void getFilePath(SInt32 index);
void getFileName(SInt32 index);
static Boolean navOpenFilterProc(AEDesc *theItem, void *info, void *callBackUD, NavFilterModes filterMode);
#endif // LL_DARWIN
#if LL_GTK
static void add_to_selectedfiles(gpointer data, gpointer user_data);
static void chooser_responder(GtkWidget *widget, gint response, gpointer user_data);
// we remember the last path that was accessed for a particular usage
std::map <std::string, std::string> mContextToPathMap;
std::string mCurContextName;
#if LL_X11
Window mX11WindowID;
#endif
public:
std::map <std::string, std::string>& get_ContextToPathMap(void) { return mContextToPathMap; }
#if LL_X11
void setWindowID(unsigned long window_id) { mX11WindowID = (Window)window_id; }
#endif
#endif // LL_GTK
#if !LL_WINDOWS && !(LL_GTK && LL_X11)
void setWindowID(unsigned long window_id) { PLS_WARNS << "Calling unimplemented LLFilePickerBase::setWindowID" << PLS_ENDL; }
#endif
private:
std::vector<std::string> mFiles;
S32 mCurrentFile;
bool mLocked;
bool mMultiFile;
protected:
#if LL_GTK
GtkWindow* buildFilePicker(bool is_save, bool is_folder, std::string const& folder);
#endif
protected:
LLFilePickerBase();
};
// True singleton, private constructors (and no friends).
class LLFilePicker : public LLFilePickerBase
{
public:
// calling this before main() is undefined
static LLFilePicker& instance( void ) { return sInstance; }
private:
static LLFilePicker sInstance;
LLFilePicker() { }
};
namespace translation
{
void add(std::string const& key, std::string const& translation);
}
#endif

View File

@@ -0,0 +1,61 @@
# -*- cmake -*-
project(media_plugin_gstreamer010)
include(00-Common)
include(LLCommon)
include(LLImage)
include(LLPlugin)
include(LLMath)
include(LLRender)
include(LLWindow)
include(Linking)
include(PluginAPI)
include(MediaPluginBase)
include(FindOpenGL)
include(GStreamer010Plugin)
include_directories(
${LLPLUGIN_INCLUDE_DIRS}
${MEDIA_PLUGIN_BASE_INCLUDE_DIRS}
${LLCOMMON_INCLUDE_DIRS}
${LLMATH_INCLUDE_DIRS}
${LLIMAGE_INCLUDE_DIRS}
${LLRENDER_INCLUDE_DIRS}
${LLWINDOW_INCLUDE_DIRS}
${GSTREAMER010_INCLUDE_DIRS}
${GSTREAMER010_PLUGINS_BASE_INCLUDE_DIRS}
)
### media_plugin_gstreamer010
set(media_plugin_gstreamer010_SOURCE_FILES
media_plugin_gstreamer010.cpp
llmediaimplgstreamervidplug.cpp
)
set(media_plugin_gstreamer010_HEADER_FILES
llmediaimplgstreamervidplug.h
llmediaimplgstreamertriviallogging.h
)
add_library(media_plugin_gstreamer010
SHARED
${media_plugin_gstreamer010_SOURCE_FILES}
)
target_link_libraries(media_plugin_gstreamer010
${LLPLUGIN_LIBRARIES}
${MEDIA_PLUGIN_BASE_LIBRARIES}
${LLCOMMON_LIBRARIES}
${PLUGIN_API_WINDOWS_LIBRARIES}
${GSTREAMER010_LIBRARIES}
)
add_dependencies(media_plugin_gstreamer010
${LLPLUGIN_LIBRARIES}
${MEDIA_PLUGIN_BASE_LIBRARIES}
${LLCOMMON_LIBRARIES}
)

View File

@@ -0,0 +1,60 @@
/**
* @file llmediaimplgstreamer.h
* @author Tofu Linden
* @brief implementation that supports media playback via GStreamer.
*
* @cond
* $LicenseInfo:firstyear=2007&license=viewergpl$
*
* 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
* 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
*/
// header guard
#ifndef llmediaimplgstreamer_h
#define llmediaimplgstreamer_h
#if LL_GSTREAMER010_ENABLED
extern "C" {
#include <stdio.h>
#include <gst/gst.h>
#include "apr_pools.h"
#include "apr_dso.h"
}
extern "C" {
gboolean llmediaimplgstreamer_bus_callback (GstBus *bus,
GstMessage *message,
gpointer data);
}
#endif // LL_GSTREAMER010_ENABLED
#endif // llmediaimplgstreamer_h

View File

@@ -0,0 +1,65 @@
/**
* @file llmediaimplgstreamertriviallogging.h
* @brief minimal logging utilities.
*
* @cond
* $LicenseInfo:firstyear=2009&license=viewergpl$
*
* 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
* 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 __LLMEDIAIMPLGSTREAMERTRIVIALLOGGING_H__
#define __LLMEDIAIMPLGSTREAMERTRIVIALLOGGING_H__
#include <cstdio>
/////////////////////////////////////////////////////////////////////////
// Debug/Info/Warning macros.
#if LL_WINDOWS
#include <process.h>
#define LL_GETPID GetCurrentProcessId
#else
#include <sys/types.h>
#include <unistd.h>
#define LL_GETPID getpid
#endif
#define MSGMODULEFOO "(media plugin)"
#define STDERRMSG(...) do{\
fprintf(stderr, " pid:%d: ", (int)LL_GETPID());\
fprintf(stderr, MSGMODULEFOO " %s:%d: ", __FUNCTION__, __LINE__);\
fprintf(stderr, __VA_ARGS__);\
fputc('\n',stderr);\
}while(0)
#define NULLMSG(...) do{}while(0)
#define DEBUGMSG NULLMSG
#define INFOMSG STDERRMSG
#define WARNMSG STDERRMSG
/////////////////////////////////////////////////////////////////////////
#endif /* __LLMEDIAIMPLGSTREAMERTRIVIALLOGGING_H__ */

View File

@@ -0,0 +1,572 @@
/**
* @file llmediaimplgstreamervidplug.cpp
* @brief Video-consuming static GStreamer plugin for gst-to-LLMediaImpl
*
* @cond
* $LicenseInfo:firstyear=2007&license=viewergpl$
*
* 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
* 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
*/
#if LL_GSTREAMER010_ENABLED
#include "linden_common.h"
#include <gst/gst.h>
#include <gst/video/video.h>
#include <gst/video/gstvideosink.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
static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE (
(gchar*)"sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS (SLV_ALLCAPS)
);
GST_BOILERPLATE (GstSLVideo, gst_slvideo, GstVideoSink,
GST_TYPE_VIDEO_SINK);
static void gst_slvideo_set_property (GObject * object, guint prop_id,
const GValue * value,
GParamSpec * pspec);
static void gst_slvideo_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static void
gst_slvideo_base_init (gpointer gclass)
{
static GstElementDetails element_details = {
(gchar*)"PluginTemplate",
(gchar*)"Generic/PluginTemplate",
(gchar*)"Generic Template Element",
(gchar*)"Linden Lab"
};
GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&sink_factory));
gst_element_class_set_details (element_class, &element_details);
}
static void
gst_slvideo_finalize (GObject * object)
{
GstSLVideo *slvideo;
slvideo = GST_SLVIDEO (object);
if (slvideo->caps)
{
gst_caps_unref(slvideo->caps);
}
G_OBJECT_CLASS(parent_class)->finalize (object);
}
static GstFlowReturn
gst_slvideo_show_frame (GstBaseSink * bsink, GstBuffer * buf)
{
GstSLVideo *slvideo;
g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
slvideo = GST_SLVIDEO(bsink);
DEBUGMSG("transferring a frame of %dx%d <- %p (%d)",
slvideo->width, slvideo->height, GST_BUFFER_DATA(buf),
slvideo->format);
if (GST_BUFFER_DATA(buf))
{
// copy frame and frame info into neutral territory
GST_OBJECT_LOCK(slvideo);
slvideo->retained_frame_ready = TRUE;
slvideo->retained_frame_width = slvideo->width;
slvideo->retained_frame_height = slvideo->height;
slvideo->retained_frame_format = slvideo->format;
int rowbytes =
SLVPixelFormatBytes[slvideo->retained_frame_format] *
slvideo->retained_frame_width;
int needbytes = rowbytes * slvideo->retained_frame_width;
// resize retained frame hunk only if necessary
if (needbytes != slvideo->retained_frame_allocbytes)
{
delete[] slvideo->retained_frame_data;
slvideo->retained_frame_data = new unsigned char[needbytes];
slvideo->retained_frame_allocbytes = needbytes;
}
// copy the actual frame data to neutral territory -
// flipped, for GL reasons
for (int ypos=0; ypos<slvideo->height; ++ypos)
{
memcpy(&slvideo->retained_frame_data[(slvideo->height-1-ypos)*rowbytes],
&(((unsigned char*)GST_BUFFER_DATA(buf))[ypos*rowbytes]),
rowbytes);
}
// done with the shared data
GST_OBJECT_UNLOCK(slvideo);
}
return GST_FLOW_OK;
}
static GstStateChangeReturn
gst_slvideo_change_state(GstElement * element, GstStateChange transition)
{
GstSLVideo *slvideo;
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
slvideo = GST_SLVIDEO (element);
switch (transition) {
case GST_STATE_CHANGE_NULL_TO_READY:
break;
case GST_STATE_CHANGE_READY_TO_PAUSED:
break;
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
break;
default:
break;
}
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
if (ret == GST_STATE_CHANGE_FAILURE)
return ret;
switch (transition) {
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
break;
case GST_STATE_CHANGE_PAUSED_TO_READY:
slvideo->fps_n = 0;
slvideo->fps_d = 1;
GST_VIDEO_SINK_WIDTH(slvideo) = 0;
GST_VIDEO_SINK_HEIGHT(slvideo) = 0;
break;
case GST_STATE_CHANGE_READY_TO_NULL:
break;
default:
break;
}
return ret;
}
static GstCaps *
gst_slvideo_get_caps (GstBaseSink * bsink)
{
GstSLVideo *slvideo;
slvideo = GST_SLVIDEO(bsink);
return gst_caps_ref (slvideo->caps);
}
/* this function handles the link with other elements */
static gboolean
gst_slvideo_set_caps (GstBaseSink * bsink, GstCaps * caps)
{
GstSLVideo *filter;
GstStructure *structure;
// GstCaps *intersection;
GST_DEBUG ("set caps with %" GST_PTR_FORMAT, caps);
filter = GST_SLVIDEO(bsink);
/*
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 = 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 = gst_structure_get_value (structure, "pixel-aspect-ratio");
if (!ret)
return FALSE;
INFOMSG("** filter caps set with width=%d, height=%d", width, height);
GST_OBJECT_LOCK(filter);
filter->width = width;
filter->height = height;
filter->fps_n = gst_value_get_fraction_numerator(fps);
filter->fps_d = gst_value_get_fraction_denominator(fps);
if (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(gst_structure_get_name(structure),
"video/x-raw-rgb"))
{
int red_mask;
int green_mask;
int 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)
{
filter->format = SLV_PF_RGBX;
//fprintf(stderr, "\n\nPIXEL FORMAT RGB\n\n");
} else if ((unsigned int)red_mask == 0x0000FF00 &&
(unsigned int)green_mask == 0x00FF0000 &&
(unsigned int)blue_mask == 0xFF000000)
{
filter->format = SLV_PF_BGRX;
//fprintf(stderr, "\n\nPIXEL FORMAT BGR\n\n");
}
}*/
filter->format = SLV_PF_RGBX;
GST_OBJECT_UNLOCK(filter);
return TRUE;
}
static gboolean
gst_slvideo_start (GstBaseSink * bsink)
{
GstSLVideo *slvideo;
gboolean ret = TRUE;
slvideo = GST_SLVIDEO(bsink);
return ret;
}
static gboolean
gst_slvideo_stop (GstBaseSink * bsink)
{
GstSLVideo *slvideo;
slvideo = GST_SLVIDEO(bsink);
// free-up retained frame buffer
GST_OBJECT_LOCK(slvideo);
slvideo->retained_frame_ready = FALSE;
delete[] slvideo->retained_frame_data;
slvideo->retained_frame_data = NULL;
slvideo->retained_frame_allocbytes = 0;
GST_OBJECT_UNLOCK(slvideo);
return TRUE;
}
static GstFlowReturn
gst_slvideo_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size,
GstCaps * caps, GstBuffer ** buf)
{
gint width, height;
GstStructure *structure = NULL;
GstSLVideo *slvideo;
slvideo = GST_SLVIDEO(bsink);
// caps == requested caps
// we can ignore these and reverse-negotiate our preferred dimensions with
// the peer if we like - we need to do this to obey dynamic resize requests
// flowing in from the app.
structure = 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 = gst_buffer_new();
bool made_bufferdata_ptr = false;
#define MAXDEPTHHACK 4
GST_OBJECT_LOCK(slvideo);
if (slvideo->resize_forced_always) // app is giving us a fixed size to work with
{
gint slwantwidth, slwantheight;
slwantwidth = slvideo->resize_try_width;
slwantheight = slvideo->resize_try_height;
if (slwantwidth != width ||
slwantheight != height)
{
// don't like requested caps, we will issue our own suggestion - copy
// the requested caps but substitute our own width and height and see
// if our peer is happy with that.
GstCaps *desired_caps;
GstStructure *desired_struct;
desired_caps = 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);
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);
gst_structure_set_value (desired_struct, "height", &value);
if (gst_pad_peer_accept_caps (GST_VIDEO_SINK_PAD (slvideo),
desired_caps))
{
// todo: re-use buffers from a pool?
// todo: set MALLOCDATA to null, set DATA to point straight to shm?
// peer likes our cap suggestion
DEBUGMSG("peer loves us :)");
GST_BUFFER_SIZE(newbuf) = slwantwidth * slwantheight * MAXDEPTHHACK;
GST_BUFFER_MALLOCDATA(newbuf) = (guint8*)g_malloc(GST_BUFFER_SIZE(newbuf));
GST_BUFFER_DATA(newbuf) = GST_BUFFER_MALLOCDATA(newbuf);
gst_buffer_set_caps (GST_BUFFER_CAST(newbuf), desired_caps);
made_bufferdata_ptr = true;
} else {
// peer hates our cap suggestion
INFOMSG("peer hates us :(");
gst_caps_unref(desired_caps);
}
}
}
GST_OBJECT_UNLOCK(slvideo);
if (!made_bufferdata_ptr) // need to fallback to malloc at original size
{
GST_BUFFER_SIZE(newbuf) = width * height * MAXDEPTHHACK;
GST_BUFFER_MALLOCDATA(newbuf) = (guint8*)g_malloc(GST_BUFFER_SIZE(newbuf));
GST_BUFFER_DATA(newbuf) = GST_BUFFER_MALLOCDATA(newbuf);
gst_buffer_set_caps (GST_BUFFER_CAST(newbuf), caps);
}
*buf = GST_BUFFER_CAST(newbuf);
return GST_FLOW_OK;
}
/* initialize the plugin's class */
static void
gst_slvideo_class_init (GstSLVideoClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
GstBaseSinkClass *gstbasesink_class;
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
gstbasesink_class = (GstBaseSinkClass *) klass;
gobject_class->finalize = gst_slvideo_finalize;
gobject_class->set_property = gst_slvideo_set_property;
gobject_class->get_property = gst_slvideo_get_property;
gstelement_class->change_state = gst_slvideo_change_state;
#define LLGST_DEBUG_FUNCPTR(p) (p)
gstbasesink_class->get_caps = LLGST_DEBUG_FUNCPTR (gst_slvideo_get_caps);
gstbasesink_class->set_caps = LLGST_DEBUG_FUNCPTR( gst_slvideo_set_caps);
gstbasesink_class->buffer_alloc=LLGST_DEBUG_FUNCPTR(gst_slvideo_buffer_alloc);
//gstbasesink_class->get_times = LLGST_DEBUG_FUNCPTR (gst_slvideo_get_times);
gstbasesink_class->preroll = LLGST_DEBUG_FUNCPTR (gst_slvideo_show_frame);
gstbasesink_class->render = LLGST_DEBUG_FUNCPTR (gst_slvideo_show_frame);
gstbasesink_class->start = LLGST_DEBUG_FUNCPTR (gst_slvideo_start);
gstbasesink_class->stop = LLGST_DEBUG_FUNCPTR (gst_slvideo_stop);
// gstbasesink_class->unlock = LLGST_DEBUG_FUNCPTR (gst_slvideo_unlock);
#undef LLGST_DEBUG_FUNCPTR
}
/*
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
* set functions
* initialize structure
*/
static void
gst_slvideo_init (GstSLVideo * filter,
GstSLVideoClass * gclass)
{
filter->caps = NULL;
filter->width = -1;
filter->height = -1;
// this is the info we share with the client app
GST_OBJECT_LOCK(filter);
filter->retained_frame_ready = FALSE;
filter->retained_frame_data = NULL;
filter->retained_frame_allocbytes = 0;
filter->retained_frame_width = filter->width;
filter->retained_frame_height = filter->height;
filter->retained_frame_format = SLV_PF_UNKNOWN;
GstCaps *caps = 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)
{
g_return_if_fail (GST_IS_SLVIDEO (object));
if (prop_id) {
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
gst_slvideo_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
g_return_if_fail (GST_IS_SLVIDEO (object));
if (prop_id) {
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
/* entry point to initialize the plug-in
* initialize the plug-in itself
* register the element factories and pad templates
* register the features
*/
static gboolean
plugin_init (GstPlugin * plugin)
{
DEBUGMSG("PLUGIN INIT");
GST_DEBUG_CATEGORY_INIT (gst_slvideo_debug, (gchar*)"private-slvideo-plugin",
0, (gchar*)"Second Life Video Sink");
return gst_element_register (plugin, "private-slvideo",
GST_RANK_NONE, GST_TYPE_SLVIDEO);
}
/* this is the structure that gstreamer looks for to register plugins
*/
/* NOTE: Can't rely upon GST_PLUGIN_DEFINE_STATIC to self-register, since
some g++ versions buggily avoid __attribute__((constructor)) functions -
so we provide an explicit plugin init function.
*/
void gst_slvideo_init_class (void)
{
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

View File

@@ -0,0 +1,113 @@
/**
* @file llmediaimplgstreamervidplug.h
* @brief Video-consuming static GStreamer plugin for gst-to-LLMediaImpl
*
* @cond
* $LicenseInfo:firstyear=2007&license=viewergpl$
*
* 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
* 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 __GST_SLVIDEO_H__
#define __GST_SLVIDEO_H__
#if LL_GSTREAMER010_ENABLED
extern "C" {
#include <gst/gst.h>
#include <gst/video/video.h>
#include <gst/video/gstvideosink.h>
// #include <glib/gthread.h>
}
G_BEGIN_DECLS
/* #defines don't like whitespacey bits */
#define GST_TYPE_SLVIDEO \
(gst_slvideo_get_type())
#define GST_SLVIDEO(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SLVIDEO,GstSLVideo))
#define GST_SLVIDEO_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SLVIDEO,GstSLVideoClass))
#define GST_IS_SLVIDEO(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SLVIDEO))
#define GST_IS_SLVIDEO_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SLVIDEO))
typedef struct _GstSLVideo GstSLVideo;
typedef struct _GstSLVideoClass GstSLVideoClass;
typedef enum {
SLV_PF_UNKNOWN = 0,
SLV_PF_RGBX = 1,
SLV_PF_BGRX = 2,
SLV__END = 3
} SLVPixelFormat;
const int SLVPixelFormatBytes[SLV__END] = {1, 4, 4};
struct _GstSLVideo
{
GstVideoSink video_sink;
GstCaps *caps;
int fps_n, fps_d;
int par_n, par_d;
int height, width;
SLVPixelFormat format;
// SHARED WITH APPLICATION:
// Access to the following should be protected by GST_OBJECT_LOCK() on
// the GstSLVideo object, and should be totally consistent upon UNLOCK
// (i.e. all written at once to reflect the current retained frame info
// when the retained frame is updated.)
bool retained_frame_ready; // new frame ready since flag last reset. (*TODO: could get the writer to wait on a semaphore instead of having the reader poll, potentially making dropped frames somewhat cheaper.)
unsigned char* retained_frame_data;
int retained_frame_allocbytes;
int retained_frame_width, retained_frame_height;
SLVPixelFormat retained_frame_format;
// sticky resize info
bool resize_forced_always;
int resize_try_width;
int resize_try_height;
};
struct _GstSLVideoClass
{
GstVideoSinkClass parent_class;
};
GType gst_slvideo_get_type (void);
void gst_slvideo_init_class (void);
G_END_DECLS
#endif // LL_GSTREAMER010_ENABLED
#endif /* __GST_SLVIDEO_H__ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,91 @@
# -*- cmake -*-
project(media_plugin_quicktime)
include(00-Common)
include(LLCommon)
include(LLImage)
include(LLPlugin)
include(LLMath)
include(LLRender)
include(LLWindow)
include(Linking)
include(PluginAPI)
include(MediaPluginBase)
include(FindOpenGL)
include(QuickTimePlugin)
include_directories(
${LLPLUGIN_INCLUDE_DIRS}
${MEDIA_PLUGIN_BASE_INCLUDE_DIRS}
${LLCOMMON_INCLUDE_DIRS}
${LLMATH_INCLUDE_DIRS}
${LLIMAGE_INCLUDE_DIRS}
${LLRENDER_INCLUDE_DIRS}
${LLWINDOW_INCLUDE_DIRS}
)
if (DARWIN)
include(CMakeFindFrameworks)
find_library(CARBON_LIBRARY Carbon)
endif (DARWIN)
### media_plugin_quicktime
set(media_plugin_quicktime_SOURCE_FILES
media_plugin_quicktime.cpp
)
add_library(media_plugin_quicktime
SHARED
${media_plugin_quicktime_SOURCE_FILES}
)
target_link_libraries(media_plugin_quicktime
${LLPLUGIN_LIBRARIES}
${MEDIA_PLUGIN_BASE_LIBRARIES}
${LLCOMMON_LIBRARIES}
${QUICKTIME_LIBRARY}
${PLUGIN_API_WINDOWS_LIBRARIES}
)
add_dependencies(media_plugin_quicktime
${LLPLUGIN_LIBRARIES}
${MEDIA_PLUGIN_BASE_LIBRARIES}
${LLCOMMON_LIBRARIES}
)
if (WINDOWS)
set_target_properties(
media_plugin_quicktime
PROPERTIES
LINK_FLAGS "/MANIFEST:NO /NODEFAULTLIB:LIBCMT"
LINK_FLAGS_DEBUG "/MANIFEST:NO /NODEFAULTLIB:\"LIBCMT;LIBCMTD\""
)
endif (WINDOWS)
if (QUICKTIME)
add_definitions(-DLL_QUICKTIME_ENABLED=1)
if (DARWIN)
# Don't prepend 'lib' to the executable name, and don't embed a full path in the library's install name
set_target_properties(
media_plugin_quicktime
PROPERTIES
PREFIX ""
BUILD_WITH_INSTALL_RPATH 1
INSTALL_NAME_DIR "@executable_path"
LINK_FLAGS "-exported_symbols_list ${CMAKE_CURRENT_SOURCE_DIR}/../base_media/media_plugin_base.exp"
)
# We use a bunch of deprecated system APIs.
set_source_files_properties(
media_plugin_quicktime.cpp PROPERTIES
COMPILE_FLAGS -Wno-deprecated-declarations
)
find_library(CARBON_LIBRARY Carbon)
target_link_libraries(media_plugin_quicktime ${CARBON_LIBRARY})
endif (DARWIN)
endif (QUICKTIME)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,121 @@
# -*- cmake -*-
project(media_plugin_webkit)
include(00-Common)
include(LLCommon)
include(LLImage)
include(LLPlugin)
include(LLMath)
include(LLRender)
include(LLWindow)
include(UI)
include(Linking)
include(PluginAPI)
include(MediaPluginBase)
include(FindOpenGL)
include(PulseAudio)
include(WebKitLibPlugin)
include_directories(
${PULSEAUDIO_INCLUDE_DIRS}
${LLPLUGIN_INCLUDE_DIRS}
${MEDIA_PLUGIN_BASE_INCLUDE_DIRS}
${LLCOMMON_INCLUDE_DIRS}
${LLMATH_INCLUDE_DIRS}
${LLIMAGE_INCLUDE_DIRS}
${LLRENDER_INCLUDE_DIRS}
${LLWINDOW_INCLUDE_DIRS}
${LLQTWEBKIT_INCLUDE_DIR}
)
### media_plugin_webkit
set(media_plugin_webkit_SOURCE_FILES
media_plugin_webkit.cpp
)
set(media_plugin_webkit_HEADER_FILES
volume_catcher.h
)
set(media_plugin_webkit_LINK_LIBRARIES
${LLPLUGIN_LIBRARIES}
${MEDIA_PLUGIN_BASE_LIBRARIES}
${LLCOMMON_LIBRARIES}
${WEBKIT_PLUGIN_LIBRARIES}
${PLUGIN_API_WINDOWS_LIBRARIES}
${PULSEAUDIO_LIBRARIES}
)
# Select which VolumeCatcher implementation to use
if (LINUX)
if (PULSEAUDIO)
list(APPEND media_plugin_webkit_SOURCE_FILES linux_volume_catcher.cpp)
endif (PULSEAUDIO)
list(APPEND media_plugin_webkit_LINK_LIBRARIES
${UI_LIBRARIES} # for glib/GTK
)
elseif (DARWIN)
list(APPEND media_plugin_webkit_SOURCE_FILES mac_volume_catcher.cpp)
find_library(CORESERVICES_LIBRARY CoreServices)
find_library(AUDIOUNIT_LIBRARY AudioUnit)
list(APPEND media_plugin_webkit_LINK_LIBRARIES
${CORESERVICES_LIBRARY} # for Component Manager calls
${AUDIOUNIT_LIBRARY} # for AudioUnit calls
)
elseif (WINDOWS)
list(APPEND media_plugin_webkit_SOURCE_FILES windows_volume_catcher.cpp)
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}
PROPERTIES HEADER_FILE_ONLY TRUE)
list(APPEND media_plugin_webkit_SOURCE_FILES ${media_plugin_webkit_HEADER_FILES})
add_library(media_plugin_webkit
SHARED
${media_plugin_webkit_SOURCE_FILES}
)
target_link_libraries(media_plugin_webkit ${media_plugin_webkit_LINK_LIBRARIES})
add_dependencies(media_plugin_webkit
${LLPLUGIN_LIBRARIES}
${MEDIA_PLUGIN_BASE_LIBRARIES}
${LLCOMMON_LIBRARIES}
)
if (WINDOWS)
set_target_properties(
media_plugin_webkit
PROPERTIES
LINK_FLAGS "/MANIFEST:NO"
)
endif (WINDOWS)
if (DARWIN)
# Don't prepend 'lib' to the executable name, and don't embed a full path in the library's install name
set_target_properties(
media_plugin_webkit
PROPERTIES
PREFIX ""
BUILD_WITH_INSTALL_RPATH 1
INSTALL_NAME_DIR "@executable_path"
LINK_FLAGS "-exported_symbols_list ${CMAKE_CURRENT_SOURCE_DIR}/../base_media/media_plugin_base.exp"
)
# copy the webkit dylib to the build directory
add_custom_command(
TARGET media_plugin_webkit POST_BUILD
# OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/libllqtwebkit.dylib
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/../libraries/universal-darwin/lib_release/libllqtwebkit.dylib ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/
DEPENDS media_plugin_webkit ${CMAKE_SOURCE_DIR}/../libraries/universal-darwin/lib_release/libllqtwebkit.dylib
)
endif (DARWIN)

View File

@@ -0,0 +1,65 @@
/**
* @file dummy_volume_catcher.cpp
* @brief A null implementation of the "VolumeCatcher" class for platforms where it's not implemented yet.
*
* @cond
* $LicenseInfo:firstyear=2010&license=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 "volume_catcher.h"
class VolumeCatcherImpl
{
};
/////////////////////////////////////////////////////
VolumeCatcher::VolumeCatcher()
{
pimpl = NULL;
}
VolumeCatcher::~VolumeCatcher()
{
}
void VolumeCatcher::setVolume(F32 volume)
{
}
void VolumeCatcher::setPan(F32 pan)
{
}
void VolumeCatcher::pump()
{
}

View File

@@ -0,0 +1,473 @@
/**
* @file linux_volume_catcher.cpp
* @brief A Linux-specific, PulseAudio-specific hack to detect and volume-adjust new audio sources
*
* @cond
* $LicenseInfo:firstyear=2010&license=viewergpl$
*
* Copyright (c) 2010, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlife.com/developers/opensource/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlife.com/developers/opensource/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*
* @endcond
*/
/*
The high-level design is as follows:
1) Connect to the PulseAudio daemon
2) Watch for the creation of new audio players connecting to the daemon (this includes ALSA clients running on the PulseAudio emulation layer, such as Flash plugins)
3) Examine any new audio player's PID to see if it belongs to our own process
4) If so, tell PA to adjust the volume of that audio player ('sink input' in PA parlance)
5) Keep a list of all living audio players that we care about, adjust the volumes of all of them when we get a new setVolume() call
*/
# include <set> //imprudence
#include "linden_common.h"
#include "volume_catcher.h"
extern "C" {
#include <glib.h>
#include <glib-object.h>
#include <pulse/introspect.h>
#include <pulse/context.h>
#include <pulse/subscribe.h>
#include <pulse/glib-mainloop.h> // There's no special reason why we want the *glib* PA mainloop, but the generic polling implementation seems broken.
#include "aiaprpool.h"
#include "apr_dso.h"
}
////////////////////////////////////////////////////
#define DEBUGMSG(...) do {} while(0)
#define INFOMSG(...) do {} while(0)
#define WARNMSG(...) do {} while(0)
#define LL_PA_SYM(REQUIRED, PASYM, RTN, ...) RTN (*ll##PASYM)(__VA_ARGS__) = NULL
#include "linux_volume_catcher_pa_syms.inc"
#include "linux_volume_catcher_paglib_syms.inc"
#undef LL_PA_SYM
static bool sSymsGrabbed = false;
static AIAPRPool sSymPADSOMemoryPool;
static apr_dso_handle_t *sSymPADSOHandleG = NULL;
bool grab_pa_syms(std::string pulse_dso_name)
{
if (sSymsGrabbed)
{
// already have grabbed good syms
return true;
}
bool sym_error = false;
bool rtn = false;
apr_status_t rv;
apr_dso_handle_t *sSymPADSOHandle = NULL;
#define LL_PA_SYM(REQUIRED, PASYM, RTN, ...) do{rv = apr_dso_sym((apr_dso_handle_sym_t*)&ll##PASYM, sSymPADSOHandle, #PASYM); if (rv != APR_SUCCESS) {INFOMSG("Failed to grab symbol: %s", #PASYM); if (REQUIRED) sym_error = true;} else DEBUGMSG("grabbed symbol: %s from %p", #PASYM, (void*)ll##PASYM);}while(0)
//attempt to load the shared library
sSymPADSOMemoryPool.create();
if ( APR_SUCCESS == (rv = apr_dso_load(&sSymPADSOHandle,
pulse_dso_name.c_str(),
sSymPADSOMemoryPool()) ))
{
INFOMSG("Found DSO: %s", pulse_dso_name.c_str());
#include "linux_volume_catcher_pa_syms.inc"
#include "linux_volume_catcher_paglib_syms.inc"
if ( sSymPADSOHandle )
{
sSymPADSOHandleG = sSymPADSOHandle;
sSymPADSOHandle = NULL;
}
rtn = !sym_error;
}
else
{
INFOMSG("Couldn't load DSO: %s", pulse_dso_name.c_str());
rtn = false; // failure
}
if (sym_error)
{
WARNMSG("Failed to find necessary symbols in PulseAudio libraries.");
}
#undef LL_PA_SYM
sSymsGrabbed = rtn;
return rtn;
}
void ungrab_pa_syms()
{
// should be safe to call regardless of whether we've
// actually grabbed syms.
if ( sSymPADSOHandleG )
{
apr_dso_unload(sSymPADSOHandleG);
sSymPADSOHandleG = NULL;
}
sSymPADSOMemoryPool.destroy();
// NULL-out all of the symbols we'd grabbed
#define LL_PA_SYM(REQUIRED, PASYM, RTN, ...) do{ll##PASYM = NULL;}while(0)
#include "linux_volume_catcher_pa_syms.inc"
#include "linux_volume_catcher_paglib_syms.inc"
#undef LL_PA_SYM
sSymsGrabbed = false;
}
////////////////////////////////////////////////////
// PulseAudio requires a chain of callbacks with C linkage
extern "C" {
void callback_discovered_sinkinput(pa_context *context, const pa_sink_input_info *i, int eol, void *userdata);
void callback_subscription_alert(pa_context *context, pa_subscription_event_type_t t, uint32_t index, void *userdata);
void callback_context_state(pa_context *context, void *userdata);
}
class VolumeCatcherImpl
{
public:
VolumeCatcherImpl();
~VolumeCatcherImpl();
void setVolume(F32 volume);
void pump(void);
// for internal use - can't be private because used from our C callbacks
bool loadsyms(std::string pulse_dso_name);
void init();
void cleanup();
void update_all_volumes(F32 volume);
void update_index_volume(U32 index, F32 volume);
void connected_okay();
std::set<U32> mSinkInputIndices;
std::map<U32,U32> mSinkInputNumChannels;
F32 mDesiredVolume;
pa_glib_mainloop *mMainloop;
pa_context *mPAContext;
bool mConnected;
bool mGotSyms;
};
VolumeCatcherImpl::VolumeCatcherImpl()
: mDesiredVolume(0.0f),
mMainloop(NULL),
mPAContext(NULL),
mConnected(false),
mGotSyms(false)
{
init();
}
VolumeCatcherImpl::~VolumeCatcherImpl()
{
cleanup();
}
bool VolumeCatcherImpl::loadsyms(std::string pulse_dso_name)
{
return grab_pa_syms(pulse_dso_name);
}
void VolumeCatcherImpl::init()
{
// try to be as defensive as possible because PA's interface is a
// bit fragile and (for our purposes) we'd rather simply not function
// than crash
// we cheat and rely upon libpulse-mainloop-glib.so.0 to pull-in
// libpulse.so.0 - this isn't a great assumption, and the two DSOs should
// probably be loaded separately. Our Linux DSO framework needs refactoring,
// we do this sort of thing a lot with practically identical logic...
mGotSyms = loadsyms("libpulse-mainloop-glib.so.0");
if (!mGotSyms) return;
// better make double-sure glib itself is initialized properly.
if (!g_thread_supported ()) g_thread_init (NULL);
g_type_init();
mMainloop = llpa_glib_mainloop_new(g_main_context_default());
if (mMainloop)
{
pa_mainloop_api *api = llpa_glib_mainloop_get_api(mMainloop);
if (api)
{
pa_proplist *proplist = llpa_proplist_new();
if (proplist)
{
llpa_proplist_sets(proplist, PA_PROP_APPLICATION_ICON_NAME, "multimedia-player");
llpa_proplist_sets(proplist, PA_PROP_APPLICATION_ID, "com.secondlife.viewer.mediaplugvoladjust");
llpa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, "SL Plugin Volume Adjuster");
llpa_proplist_sets(proplist, PA_PROP_APPLICATION_VERSION, "1");
// plain old pa_context_new() is broken!
mPAContext = llpa_context_new_with_proplist(api, NULL, proplist);
llpa_proplist_free(proplist);
}
}
}
// Now we've set up a PA context and mainloop, try connecting the
// PA context to a PA daemon.
if (mPAContext)
{
llpa_context_set_state_callback(mPAContext, callback_context_state, this);
pa_context_flags_t cflags = (pa_context_flags)0; // maybe add PA_CONTEXT_NOAUTOSPAWN?
if (llpa_context_connect(mPAContext, NULL, cflags, NULL) >= 0)
{
// Okay! We haven't definitely connected, but we
// haven't definitely failed yet.
}
else
{
// Failed to connect to PA manager... we'll leave
// things like that. Perhaps we should try again later.
}
}
}
void VolumeCatcherImpl::cleanup()
{
mConnected = false;
if (mGotSyms && mPAContext)
{
llpa_context_disconnect(mPAContext);
llpa_context_unref(mPAContext);
}
mPAContext = NULL;
if (mGotSyms && mMainloop)
{
llpa_glib_mainloop_free(mMainloop);
}
mMainloop = NULL;
}
void VolumeCatcherImpl::setVolume(F32 volume)
{
mDesiredVolume = volume;
if (!mGotSyms) return;
if (mConnected && mPAContext)
{
update_all_volumes(mDesiredVolume);
}
pump();
}
void VolumeCatcherImpl::pump()
{
gboolean may_block = FALSE;
g_main_context_iteration(g_main_context_default(), may_block);
}
void VolumeCatcherImpl::connected_okay()
{
pa_operation *op;
// fetch global list of existing sinkinputs
if ((op = llpa_context_get_sink_input_info_list(mPAContext,
callback_discovered_sinkinput,
this)))
{
llpa_operation_unref(op);
}
// subscribe to future global sinkinput changes
llpa_context_set_subscribe_callback(mPAContext,
callback_subscription_alert,
this);
if ((op = llpa_context_subscribe(mPAContext, (pa_subscription_mask_t)
(PA_SUBSCRIPTION_MASK_SINK_INPUT),
NULL, NULL)))
{
llpa_operation_unref(op);
}
}
void VolumeCatcherImpl::update_all_volumes(F32 volume)
{
for (std::set<U32>::iterator it = mSinkInputIndices.begin();
it != mSinkInputIndices.end(); ++it)
{
update_index_volume(*it, volume);
}
}
void VolumeCatcherImpl::update_index_volume(U32 index, F32 volume)
{
static pa_cvolume cvol;
llpa_cvolume_set(&cvol, mSinkInputNumChannels[index],
llpa_sw_volume_from_linear(volume));
pa_context *c = mPAContext;
uint32_t idx = index;
const pa_cvolume *cvolumep = &cvol;
pa_context_success_cb_t cb = NULL; // okay as null
void *userdata = NULL; // okay as null
pa_operation *op;
if ((op = llpa_context_set_sink_input_volume(c, idx, cvolumep, cb, userdata)))
{
llpa_operation_unref(op);
}
}
void callback_discovered_sinkinput(pa_context *context, const pa_sink_input_info *sii, int eol, void *userdata)
{
VolumeCatcherImpl *impl = dynamic_cast<VolumeCatcherImpl*>((VolumeCatcherImpl*)userdata);
llassert(impl);
if (0 == eol)
{
pa_proplist *proplist = sii->proplist;
pid_t sinkpid = atoll(llpa_proplist_gets(proplist, PA_PROP_APPLICATION_PROCESS_ID));
if (sinkpid == getpid()) // does the discovered sinkinput belong to this process?
{
bool is_new = (impl->mSinkInputIndices.find(sii->index) ==
impl->mSinkInputIndices.end());
impl->mSinkInputIndices.insert(sii->index);
impl->mSinkInputNumChannels[sii->index] = sii->channel_map.channels;
if (is_new)
{
// new!
impl->update_index_volume(sii->index, impl->mDesiredVolume);
}
else
{
// seen it already, do nothing.
}
}
}
}
void callback_subscription_alert(pa_context *context, pa_subscription_event_type_t t, uint32_t index, void *userdata)
{
VolumeCatcherImpl *impl = dynamic_cast<VolumeCatcherImpl*>((VolumeCatcherImpl*)userdata);
llassert(impl);
switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) ==
PA_SUBSCRIPTION_EVENT_REMOVE)
{
// forget this sinkinput, if we were caring about it
impl->mSinkInputIndices.erase(index);
impl->mSinkInputNumChannels.erase(index);
}
else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) ==
PA_SUBSCRIPTION_EVENT_NEW)
{
// ask for more info about this new sinkinput
pa_operation *op;
if ((op = llpa_context_get_sink_input_info(impl->mPAContext, index, callback_discovered_sinkinput, impl)))
{
llpa_operation_unref(op);
}
}
else
{
// property change on this sinkinput - we don't care.
}
break;
default:;
}
}
void callback_context_state(pa_context *context, void *userdata)
{
VolumeCatcherImpl *impl = dynamic_cast<VolumeCatcherImpl*>((VolumeCatcherImpl*)userdata);
llassert(impl);
switch (llpa_context_get_state(context))
{
case PA_CONTEXT_READY:
impl->mConnected = true;
impl->connected_okay();
break;
case PA_CONTEXT_TERMINATED:
impl->mConnected = false;
break;
case PA_CONTEXT_FAILED:
impl->mConnected = false;
break;
default:;
}
}
/////////////////////////////////////////////////////
VolumeCatcher::VolumeCatcher()
{
pimpl = new VolumeCatcherImpl();
}
VolumeCatcher::~VolumeCatcher()
{
delete pimpl;
pimpl = NULL;
}
void VolumeCatcher::setVolume(F32 volume)
{
llassert(pimpl);
pimpl->setVolume(volume);
}
void VolumeCatcher::setPan(F32 pan)
{
// TODO: implement this (if possible)
}
void VolumeCatcher::pump()
{
llassert(pimpl);
pimpl->pump();
}

View 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

View File

@@ -0,0 +1,21 @@
// required symbols to grab
LL_PA_SYM(true, pa_context_connect, int, pa_context *c, const char *server, pa_context_flags_t flags, const pa_spawn_api *api);
LL_PA_SYM(true, pa_context_disconnect, void, pa_context *c);
LL_PA_SYM(true, pa_context_get_sink_input_info, pa_operation*, pa_context *c, uint32_t idx, pa_sink_input_info_cb_t cb, void *userdata);
LL_PA_SYM(true, pa_context_get_sink_input_info_list, pa_operation*, pa_context *c, pa_sink_input_info_cb_t cb, void *userdata);
LL_PA_SYM(true, pa_context_get_state, pa_context_state_t, pa_context *c);
LL_PA_SYM(true, pa_context_new_with_proplist, pa_context*, pa_mainloop_api *mainloop, const char *name, pa_proplist *proplist);
LL_PA_SYM(true, pa_context_set_sink_input_volume, pa_operation*, pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata);
LL_PA_SYM(true, pa_context_set_state_callback, void, pa_context *c, pa_context_notify_cb_t cb, void *userdata);
LL_PA_SYM(true, pa_context_set_subscribe_callback, void, pa_context *c, pa_context_subscribe_cb_t cb, void *userdata);
LL_PA_SYM(true, pa_context_subscribe, pa_operation*, pa_context *c, pa_subscription_mask_t m, pa_context_success_cb_t cb, void *userdata);
LL_PA_SYM(true, pa_context_unref, void, pa_context *c);
LL_PA_SYM(true, pa_cvolume_set, pa_cvolume*, pa_cvolume *a, unsigned channels, pa_volume_t v);
LL_PA_SYM(true, pa_operation_unref, void, pa_operation *o);
LL_PA_SYM(true, pa_proplist_free, void, pa_proplist* p);
LL_PA_SYM(true, pa_proplist_gets, const char*, pa_proplist *p, const char *key);
LL_PA_SYM(true, pa_proplist_new, pa_proplist*, void);
LL_PA_SYM(true, pa_proplist_sets, int, pa_proplist *p, const char *key, const char *value);
LL_PA_SYM(true, pa_sw_volume_from_linear, pa_volume_t, double v);
// optional symbols to grab

View File

@@ -0,0 +1,6 @@
// required symbols to grab
LL_PA_SYM(true, pa_glib_mainloop_free, void, pa_glib_mainloop* g);
LL_PA_SYM(true, pa_glib_mainloop_get_api, pa_mainloop_api*, pa_glib_mainloop* g);
LL_PA_SYM(true, pa_glib_mainloop_new, pa_glib_mainloop *, GMainContext *c);
// optional symbols to grab

View File

@@ -0,0 +1,275 @@
/**
* @file mac_volume_catcher.cpp
* @brief A Mac OS X specific hack to control the volume level of all audio channels opened by a process.
*
* @cond
* $LicenseInfo:firstyear=2010&license=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
*/
/**************************************************************************************************************
This code works by using CaptureComponent to capture the "Default Output" audio component
(kAudioUnitType_Output/kAudioUnitSubType_DefaultOutput) and delegating all calls to the original component.
It does this just to keep track of all instances of the default output component, so that it can set the
kHALOutputParam_Volume parameter on all of them to adjust the output volume.
**************************************************************************************************************/
#include "volume_catcher.h"
#include <Carbon/Carbon.h>
#include <QuickTime/QuickTime.h>
#include <AudioUnit/AudioUnit.h>
struct VolumeCatcherStorage;
class VolumeCatcherImpl
{
public:
void setVolume(F32 volume);
void setPan(F32 pan);
void setInstanceVolume(VolumeCatcherStorage *instance);
std::list<VolumeCatcherStorage*> mComponentInstances;
Component mOriginalDefaultOutput;
Component mVolumeAdjuster;
static VolumeCatcherImpl *getInstance();
private:
// This is a singleton class -- both callers and the component implementation should use getInstance() to find the instance.
VolumeCatcherImpl();
static VolumeCatcherImpl *sInstance;
// The singlar instance of this class is expected to last until the process exits.
// To ensure this, we declare the destructor here but never define it, so any code which attempts to destroy the instance will not link.
~VolumeCatcherImpl();
F32 mVolume;
F32 mPan;
};
VolumeCatcherImpl *VolumeCatcherImpl::sInstance = NULL;;
struct VolumeCatcherStorage
{
ComponentInstance self;
ComponentInstance delegate;
};
static ComponentResult volume_catcher_component_entry(ComponentParameters *cp, Handle componentStorage);
static ComponentResult volume_catcher_component_open(VolumeCatcherStorage *storage, ComponentInstance self);
static ComponentResult volume_catcher_component_close(VolumeCatcherStorage *storage, ComponentInstance self);
VolumeCatcherImpl *VolumeCatcherImpl::getInstance()
{
if(!sInstance)
{
sInstance = new VolumeCatcherImpl;
}
return sInstance;
}
VolumeCatcherImpl::VolumeCatcherImpl()
{
mVolume = 1.0; // default to full volume
mPan = 0.0; // and center pan
ComponentDescription desc;
desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_DefaultOutput;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
// Find the original default output component
mOriginalDefaultOutput = FindNextComponent(NULL, &desc);
// Register our own output component with the same parameters
mVolumeAdjuster = RegisterComponent(&desc, NewComponentRoutineUPP(volume_catcher_component_entry), 0, NULL, NULL, NULL);
// Capture the original component, so we always get found instead.
CaptureComponent(mOriginalDefaultOutput, mVolumeAdjuster);
}
static ComponentResult volume_catcher_component_entry(ComponentParameters *cp, Handle componentStorage)
{
ComponentResult result = badComponentSelector;
VolumeCatcherStorage *storage = (VolumeCatcherStorage*)componentStorage;
switch(cp->what)
{
case kComponentOpenSelect:
// std::cerr << "kComponentOpenSelect" << std::endl;
result = CallComponentFunctionWithStorageProcInfo((Handle)storage, cp, (ProcPtr)volume_catcher_component_open, uppCallComponentOpenProcInfo);
break;
case kComponentCloseSelect:
// std::cerr << "kComponentCloseSelect" << std::endl;
result = CallComponentFunctionWithStorageProcInfo((Handle)storage, cp, (ProcPtr)volume_catcher_component_close, uppCallComponentCloseProcInfo);
// CallComponentFunctionWithStorageProcInfo
break;
default:
// std::cerr << "Delegating selector: " << cp->what << " to component instance " << storage->delegate << std::endl;
result = DelegateComponentCall(cp, storage->delegate);
break;
}
return result;
}
static ComponentResult volume_catcher_component_open(VolumeCatcherStorage *storage, ComponentInstance self)
{
ComponentResult result = noErr;
VolumeCatcherImpl *impl = VolumeCatcherImpl::getInstance();
storage = new VolumeCatcherStorage;
storage->self = self;
storage->delegate = NULL;
result = OpenAComponent(impl->mOriginalDefaultOutput, &(storage->delegate));
if(result != noErr)
{
// std::cerr << "OpenAComponent result = " << result << ", component ref = " << storage->delegate << std::endl;
// If we failed to open the delagate component, our open is going to fail. Clean things up.
delete storage;
}
else
{
// Success -- set up this component's storage
SetComponentInstanceStorage(self, (Handle)storage);
// add this instance to the global list
impl->mComponentInstances.push_back(storage);
// and set up the initial volume
impl->setInstanceVolume(storage);
}
return result;
}
static ComponentResult volume_catcher_component_close(VolumeCatcherStorage *storage, ComponentInstance self)
{
ComponentResult result = noErr;
if(storage)
{
if(storage->delegate)
{
CloseComponent(storage->delegate);
storage->delegate = NULL;
}
VolumeCatcherImpl *impl = VolumeCatcherImpl::getInstance();
impl->mComponentInstances.remove(storage);
delete[] storage;
}
return result;
}
void VolumeCatcherImpl::setVolume(F32 volume)
{
VolumeCatcherImpl *impl = VolumeCatcherImpl::getInstance();
impl->mVolume = volume;
// Iterate through all known instances, setting the volume on each.
for(std::list<VolumeCatcherStorage*>::iterator iter = mComponentInstances.begin(); iter != mComponentInstances.end(); ++iter)
{
impl->setInstanceVolume(*iter);
}
}
void VolumeCatcherImpl::setPan(F32 pan)
{
VolumeCatcherImpl *impl = VolumeCatcherImpl::getInstance();
impl->mPan = pan;
// TODO: implement this.
// This will probably require adding a "panner" audio unit to the chain somehow.
// There's also a "3d mixer" component that we might be able to use...
}
void VolumeCatcherImpl::setInstanceVolume(VolumeCatcherStorage *instance)
{
// std::cerr << "Setting volume on component instance: " << (instance->delegate) << " to " << mVolume << std::endl;
OSStatus err = noErr;
if(instance && instance->delegate)
{
err = AudioUnitSetParameter(
instance->delegate,
kHALOutputParam_Volume,
kAudioUnitScope_Global,
0,
mVolume,
0);
}
if(err)
{
// std::cerr << " AudioUnitSetParameter returned " << err << std::endl;
}
}
/////////////////////////////////////////////////////
VolumeCatcher::VolumeCatcher()
{
pimpl = VolumeCatcherImpl::getInstance();
}
VolumeCatcher::~VolumeCatcher()
{
// Let the instance persist until exit.
}
void VolumeCatcher::setVolume(F32 volume)
{
pimpl->setVolume(volume);
}
void VolumeCatcher::setPan(F32 pan)
{
pimpl->setPan(pan);
}
void VolumeCatcher::pump()
{
// No periodic tasks are necessary for this implementation.
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,61 @@
/**
* @file volume_catcher.h
* @brief Interface to a class with platform-specific implementations that allows control of the audio volume of all sources in the current process.
*
* @cond
* $LicenseInfo:firstyear=2010&license=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 VOLUME_CATCHER_H
#define VOLUME_CATCHER_H
#include "linden_common.h"
class VolumeCatcherImpl;
class VolumeCatcher
{
public:
VolumeCatcher();
~VolumeCatcher();
void setVolume(F32 volume); // 0.0 - 1.0
// Set the left-right pan of audio sources
// where -1.0 = left, 0 = center, and 1.0 = right
void setPan(F32 pan);
void pump(); // call this at least a few times a second if you can - it affects how quickly we can 'catch' a new audio source and adjust its volume
private:
VolumeCatcherImpl *pimpl;
};
#endif // VOLUME_CATCHER_H

View File

@@ -0,0 +1,126 @@
/**
* @file windows_volume_catcher.cpp
* @brief A Windows implementation of volume level control of all audio channels opened by a process.
*
* @cond
* $LicenseInfo:firstyear=2010&license=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 "volume_catcher.h"
# define WIN32_LEAN_AND_MEAN
# include <winsock2.h>
#include <windows.h>
#include "llmemory.h"
class VolumeCatcherImpl : public LLSingleton<VolumeCatcherImpl>
{
friend LLSingleton<VolumeCatcherImpl>;
public:
void setVolume(F32 volume);
void setPan(F32 pan);
private:
// This is a singleton class -- both callers and the component implementation should use getInstance() to find the instance.
VolumeCatcherImpl();
~VolumeCatcherImpl();
typedef void (WINAPI *set_volume_func_t)(F32);
typedef void (WINAPI *set_mute_func_t)(bool);
set_volume_func_t mSetVolumeFunc;
set_mute_func_t mSetMuteFunc;
F32 mVolume;
F32 mPan;
};
VolumeCatcherImpl::VolumeCatcherImpl()
: mVolume(1.0f), // default volume is max
mPan(0.f) // default pan is centered
{
HMODULE handle = ::LoadLibrary(L"winmm.dll");
if(handle)
{
mSetVolumeFunc = (set_volume_func_t)::GetProcAddress(handle, "setPluginVolume");
mSetMuteFunc = (set_mute_func_t)::GetProcAddress(handle, "setPluginMute");
}
}
VolumeCatcherImpl::~VolumeCatcherImpl()
{
}
void VolumeCatcherImpl::setVolume(F32 volume)
{
mVolume = volume;
if (mSetMuteFunc)
{
mSetMuteFunc(volume == 0.f);
}
if (mSetVolumeFunc)
{
mSetVolumeFunc(mVolume);
}
}
void VolumeCatcherImpl::setPan(F32 pan)
{ // remember pan for calculating individual channel levels later
mPan = pan;
}
/////////////////////////////////////////////////////
VolumeCatcher::VolumeCatcher()
{
pimpl = VolumeCatcherImpl::getInstance();
}
VolumeCatcher::~VolumeCatcher()
{
// Let the instance persist until exit.
}
void VolumeCatcher::setVolume(F32 volume)
{
pimpl->setVolume(volume);
}
void VolumeCatcher::setPan(F32 pan)
{
pimpl->setPan(pan);
}
void VolumeCatcher::pump()
{
// No periodic tasks are necessary for this implementation.
}