summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xautobuild.xml14
-rwxr-xr-xindra/cmake/Boost.cmake5
-rwxr-xr-xindra/cmake/Copy3rdPartyLibs.cmake2
-rwxr-xr-xindra/cmake/LLCommon.cmake14
-rwxr-xr-xindra/llcommon/llcoros.cpp26
-rwxr-xr-xindra/llcommon/llcoros.h15
-rwxr-xr-xindra/llcommon/lleventcoro.cpp44
-rwxr-xr-xindra/llcommon/lleventcoro.h16
-rwxr-xr-xindra/llcommon/llevents.cpp49
-rwxr-xr-xindra/llcommon/llevents.h61
-rwxr-xr-xindra/llcommon/tests/lleventcoro_test.cpp4
-rwxr-xr-xindra/llmessage/CMakeLists.txt37
-rw-r--r--indra/llmessage/llexperiencecache.cpp5
-rwxr-xr-xindra/newview/CMakeLists.txt3
-rwxr-xr-xindra/newview/llappviewer.cpp2
-rwxr-xr-xindra/newview/lleventpoll.cpp4
-rw-r--r--indra/newview/llexperienceassociationresponder.cpp104
-rw-r--r--indra/newview/llexperienceassociationresponder.h58
-rwxr-xr-xindra/newview/llfloaterperms.cpp5
-rwxr-xr-xindra/newview/llimview.cpp12
-rwxr-xr-xindra/newview/llpanelvoicedevicesettings.cpp30
-rwxr-xr-xindra/newview/llpanelvoicedevicesettings.h2
-rwxr-xr-xindra/newview/llviewerregion.cpp2
-rwxr-xr-xindra/newview/llvoiceclient.cpp26
-rwxr-xr-xindra/newview/llvoiceclient.h8
-rwxr-xr-xindra/newview/llvoicevivox.cpp2838
-rwxr-xr-xindra/newview/llvoicevivox.h80
-rwxr-xr-xindra/newview/skins/default/xui/en/panel_sound_devices.xml4
-rwxr-xr-xindra/newview/viewer_manifest.py2
-rwxr-xr-xindra/viewer_components/updater/CMakeLists.txt9
30 files changed, 2092 insertions, 1389 deletions
diff --git a/autobuild.xml b/autobuild.xml
index 8cbe12dbf5..85d0ccc5c9 100755
--- a/autobuild.xml
+++ b/autobuild.xml
@@ -2014,9 +2014,9 @@
<key>archive</key>
<map>
<key>hash</key>
- <string>68a8fab5ad3a180487598d3a3e0d57b1</string>
+ <string>3dd9bf4185bf2df413d890ca9eeab76c</string>
<key>url</key>
- <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/slvoice_3p-update-slvoice/rev/298329/arch/Darwin/installer/slvoice-4.6.0017.21209.298329-darwin-298329.tar.bz2</string>
+ <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/vivox_3p-slvoice/rev/302004/arch/Darwin/installer/slvoice-4.6.0017.22050.302004-darwin-302004.tar.bz2</string>
</map>
<key>name</key>
<string>darwin</string>
@@ -2026,9 +2026,9 @@
<key>archive</key>
<map>
<key>hash</key>
- <string>48ed7ddcf93fa3c751d677f5eb5f9367</string>
+ <string>06c3a9b1005249f0c94a8b9f3775f3d3</string>
<key>url</key>
- <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/slvoice_3p-update-slvoice/rev/298329/arch/Linux/installer/slvoice-3.2.0002.10426.298329-linux-298329.tar.bz2</string>
+ <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/vivox_3p-slvoice/rev/302004/arch/Linux/installer/slvoice-3.2.0002.10426.302004-linux-302004.tar.bz2</string>
</map>
<key>name</key>
<string>linux</string>
@@ -2038,16 +2038,16 @@
<key>archive</key>
<map>
<key>hash</key>
- <string>399afab7047e6fa62e7b2fb1768059ea</string>
+ <string>47a3316dae47cc4e7c1ea7b74ba8dd1e</string>
<key>url</key>
- <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/slvoice_3p-update-slvoice/rev/298329/arch/CYGWIN/installer/slvoice-4.6.0017.21209.298329-windows-298329.tar.bz2</string>
+ <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/vivox_3p-slvoice/rev/302004/arch/CYGWIN/installer/slvoice-4.6.0017.22050.302004-windows-302004.tar.bz2</string>
</map>
<key>name</key>
<string>windows</string>
</map>
</map>
<key>version</key>
- <string>4.6.0017.21209.2988329</string>
+ <string>3.2.0002.10426.302004</string>
</map>
<key>tut</key>
<map>
diff --git a/indra/cmake/Boost.cmake b/indra/cmake/Boost.cmake
index 25e54b7cbd..d239e1f3b0 100755
--- a/indra/cmake/Boost.cmake
+++ b/indra/cmake/Boost.cmake
@@ -123,3 +123,8 @@ else (USESYSTEMLIBS)
debug boost_thread-mt-d)
endif (WINDOWS)
endif (USESYSTEMLIBS)
+
+if (LINUX)
+ set(BOOST_SYSTEM_LIBRARY ${BOOST_SYSTEM_LIBRARY} rt)
+endif (LINUX)
+
diff --git a/indra/cmake/Copy3rdPartyLibs.cmake b/indra/cmake/Copy3rdPartyLibs.cmake
index a6fd756c88..70d85b864c 100755
--- a/indra/cmake/Copy3rdPartyLibs.cmake
+++ b/indra/cmake/Copy3rdPartyLibs.cmake
@@ -22,10 +22,8 @@ if(WINDOWS)
SLVoice.exe
ca-bundle.crt
libsndfile-1.dll
- vivoxplatform.dll
vivoxsdk.dll
ortp.dll
- zlib1.dll
vivoxoal.dll
)
diff --git a/indra/cmake/LLCommon.cmake b/indra/cmake/LLCommon.cmake
index b52556a73e..b50b4bcdb2 100755
--- a/indra/cmake/LLCommon.cmake
+++ b/indra/cmake/LLCommon.cmake
@@ -19,9 +19,19 @@ if (LINUX)
# In order to support using ld.gold on linux, we need to explicitely
# specify all libraries that llcommon uses.
# llcommon uses `clock_gettime' which is provided by librt on linux.
- set(LLCOMMON_LIBRARIES llcommon rt)
+ set(LLCOMMON_LIBRARIES llcommon
+ ${BOOST_COROUTINE_LIBRARY}
+ ${BOOST_CONTEXT_LIBRARY}
+ ${BOOST_THREAD_LIBRARY}
+ ${BOOST_SYSTEM_LIBRARY}
+ rt
+ )
else (LINUX)
- set(LLCOMMON_LIBRARIES llcommon)
+ set(LLCOMMON_LIBRARIES llcommon
+ ${BOOST_COROUTINE_LIBRARY}
+ ${BOOST_CONTEXT_LIBRARY}
+ ${BOOST_THREAD_LIBRARY}
+ ${BOOST_SYSTEM_LIBRARY} )
endif (LINUX)
# add_definitions(${TCMALLOC_FLAG})
diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp
index 548a6d22be..d16bf0160b 100755
--- a/indra/llcommon/llcoros.cpp
+++ b/indra/llcommon/llcoros.cpp
@@ -51,14 +51,32 @@ boost::thread_specific_ptr<LLCoros::CoroData>
LLCoros::sCurrentCoro(LLCoros::no_cleanup);
//static
-LLCoros::coro::self& LLCoros::get_self()
+LLCoros::CoroData& LLCoros::get_CoroData(const std::string& caller)
{
CoroData* current = sCurrentCoro.get();
if (! current)
{
- LL_ERRS("LLCoros") << "Calling get_self() from non-coroutine context!" << LL_ENDL;
+ LL_ERRS("LLCoros") << "Calling " << caller << " from non-coroutine context!" << LL_ENDL;
}
- return *current->mSelf;
+ return *current;
+}
+
+//static
+LLCoros::coro::self& LLCoros::get_self()
+{
+ return *get_CoroData("get_self()").mSelf;
+}
+
+//static
+void LLCoros::set_consuming(bool consuming)
+{
+ get_CoroData("set_consuming()").mConsuming = consuming;
+}
+
+//static
+bool LLCoros::get_consuming()
+{
+ return get_CoroData("get_consuming()").mConsuming;
}
llcoro::Suspending::Suspending():
@@ -245,6 +263,8 @@ LLCoros::CoroData::CoroData(CoroData* prev, const std::string& name,
// Wrap the caller's callable in our toplevel() function so we can manage
// sCurrentCoro appropriately at startup and shutdown of each coroutine.
mCoro(boost::bind(toplevel, _1, this, callable), stacksize),
+ // don't consume events unless specifically directed
+ mConsuming(false),
mSelf(0)
{
}
diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h
index 56eed8cafe..0b1f58f48e 100755
--- a/indra/llcommon/llcoros.h
+++ b/indra/llcommon/llcoros.h
@@ -150,6 +150,18 @@ public:
/// get the current coro::self& for those who really really care
static coro::self& get_self();
+ /**
+ * Most coroutines, most of the time, don't "consume" the events for which
+ * they're suspending. This way, an arbitrary number of listeners (whether
+ * coroutines or simple callbacks) can be registered on a particular
+ * LLEventPump, every listener responding to each of the events on that
+ * LLEventPump. But a particular coroutine can assert that it will consume
+ * each event for which it suspends. (See also llcoro::postAndSuspend(),
+ * llcoro::VoidListener)
+ */
+ static void set_consuming(bool consuming);
+ static bool get_consuming();
+
private:
LLCoros();
friend class LLSingleton<LLCoros>;
@@ -159,6 +171,7 @@ private:
struct CoroData;
static void no_cleanup(CoroData*);
static void toplevel(coro::self& self, CoroData* data, const callable_t& callable);
+ static CoroData& get_CoroData(const std::string& caller);
S32 mStackSize;
@@ -178,6 +191,8 @@ private:
const std::string mName;
// the actual coroutine instance
LLCoros::coro mCoro;
+ // set_consuming() state
+ bool mConsuming;
// When the dcoroutine library calls a top-level callable, it implicitly
// passes coro::self& as the first parameter. All our consumer code used
// to explicitly pass coro::self& down through all levels of call stack,
diff --git a/indra/llcommon/lleventcoro.cpp b/indra/llcommon/lleventcoro.cpp
index c9bfcacedc..9b7e8fb65f 100755
--- a/indra/llcommon/lleventcoro.cpp
+++ b/indra/llcommon/lleventcoro.cpp
@@ -41,6 +41,8 @@
#include "llerror.h"
#include "llcoros.h"
+#include "lleventfilter.h"
+
namespace
{
@@ -153,11 +155,19 @@ void llcoro::suspend()
suspendUntilEventOn("mainloop");
}
+void llcoro::suspendUntilTimeout(float seconds)
+{
+ LLEventTimeout timeout;
+
+ timeout.eventAfter(seconds, LLSD());
+ llcoro::suspendUntilEventOn(timeout);
+}
+
LLSD llcoro::postAndSuspend(const LLSD& event, const LLEventPumpOrPumpName& requestPump,
const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath)
{
// declare the future
- boost::dcoroutines::future<LLSD> future(LLCoros::get_self());
+ boost::dcoroutines::future<LLSD_consumed> future(LLCoros::get_self());
// make a callback that will assign a value to the future, and listen on
// the specified LLEventPump with that callback
std::string listenerName(listenerNameForCoro());
@@ -183,21 +193,25 @@ LLSD llcoro::postAndSuspend(const LLSD& event, const LLEventPumpOrPumpName& requ
<< " about to wait on LLEventPump " << replyPump.getPump().getName()
<< LL_ENDL;
// trying to dereference ("resolve") the future makes us wait for it
- LLSD value;
+ LLSD_consumed value;
{
// instantiate Suspending to manage the "current" coroutine
llcoro::Suspending suspended;
value = *future;
} // destroy Suspending as soon as we're back
LL_DEBUGS("lleventcoro") << "postAndSuspend(): coroutine " << listenerName
- << " resuming with " << value << LL_ENDL;
+ << " resuming with " << value.first << LL_ENDL;
+ // immediately set consumed according to consuming
+ *value.second = LLCoros::get_consuming();
// returning should disconnect the connection
- return value;
+ return value.first;
}
namespace
{
+typedef std::pair<LLEventWithID, bool*> LLEventWithID_consumed;
+
/**
* This helper is specifically for the two-pump version of suspendUntilEventOn().
* We use a single future object, but we want to listen on two pumps with it.
@@ -217,13 +231,15 @@ public:
mListener(listener),
mDiscrim(discriminator)
{}
+
// this signature is required for an LLEventPump listener
bool operator()(const LLSD& event)
{
- // our future object is defined to accept LLEventWithID
- mListener(LLEventWithID(event, mDiscrim));
- // don't swallow the event, let other listeners see it
- return false;
+ bool consumed = false;
+ // our future object is defined to accept LLEventWithID_consumed
+ mListener(LLEventWithID_consumed(LLEventWithID(event, mDiscrim), &consumed));
+ // tell LLEventPump whether or not event was consumed
+ return consumed;
}
private:
LISTENER mListener;
@@ -250,7 +266,7 @@ LLEventWithID postAndSuspend2(const LLSD& event,
const LLSD& replyPump1NamePath)
{
// declare the future
- boost::dcoroutines::future<LLEventWithID> future(LLCoros::get_self());
+ boost::dcoroutines::future<LLEventWithID_consumed> future(LLCoros::get_self());
// either callback will assign a value to this future; listen on
// each specified LLEventPump with a callback
std::string name(listenerNameForCoro());
@@ -279,17 +295,19 @@ LLEventWithID postAndSuspend2(const LLSD& event,
<< " about to wait on LLEventPumps " << replyPump0.getPump().getName()
<< ", " << replyPump1.getPump().getName() << LL_ENDL;
// trying to dereference ("resolve") the future makes us wait for it
- LLEventWithID value;
+ LLEventWithID_consumed value;
{
// instantiate Suspending to manage "current" coroutine
llcoro::Suspending suspended;
value = *future;
} // destroy Suspending as soon as we're back
LL_DEBUGS("lleventcoro") << "postAndSuspend(): coroutine " << name
- << " resuming with (" << value.first << ", " << value.second << ")"
- << LL_ENDL;
+ << " resuming with (" << value.first.first
+ << ", " << value.first.second << ")" << LL_ENDL;
+ // tell LLEventPump whether we're consuming
+ *value.second = LLCoros::get_consuming();
// returning should disconnect both connections
- return value;
+ return value.first;
}
LLSD errorException(const LLEventWithID& result, const std::string& desc)
diff --git a/indra/llcommon/lleventcoro.h b/indra/llcommon/lleventcoro.h
index 6fda3d2572..acf2ad24a4 100755
--- a/indra/llcommon/lleventcoro.h
+++ b/indra/llcommon/lleventcoro.h
@@ -32,6 +32,7 @@
#include <boost/optional.hpp>
#include <string>
#include <stdexcept>
+#include <utility> // std::pair
#include "llevents.h"
#include "llerror.h"
@@ -75,6 +76,8 @@ private:
namespace llcoro
{
+typedef std::pair<LLSD, bool*> LLSD_consumed;
+
/// This is an adapter for a signature like void LISTENER(const LLSD&), which
/// isn't a valid LLEventPump listener: such listeners should return bool.
template <typename LISTENER>
@@ -84,11 +87,13 @@ public:
VoidListener(const LISTENER& listener):
mListener(listener)
{}
+
bool operator()(const LLSD& event)
{
- mListener(event);
- // don't swallow the event, let other listeners see it
- return false;
+ bool consumed = false;
+ mListener(LLSD_consumed(event, &consumed));
+ // tell upstream LLEventPump whether listener consumed
+ return consumed;
}
private:
LISTENER mListener;
@@ -109,6 +114,11 @@ VoidListener<LISTENER> voidlistener(const LISTENER& listener)
void suspend();
/**
+ * Yield control from a coroutine for at least the specified number of seconds
+ */
+void suspendUntilTimeout(float seconds);
+
+/**
* Post specified LLSD event on the specified LLEventPump, then suspend for a
* response on specified other LLEventPump. This is more than mere
* convenience: the difference between this function and the sequence
diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp
index 1c928b3db8..0c5e55dc76 100755
--- a/indra/llcommon/llevents.cpp
+++ b/indra/llcommon/llevents.cpp
@@ -57,6 +57,7 @@
#include "stringize.h"
#include "llerror.h"
#include "llsdutil.h"
+#include "llcoros.h"
#if LL_MSVC
#pragma warning (disable : 4702)
#endif
@@ -132,6 +133,17 @@ LLEventPump& LLEventPumps::obtain(const std::string& name)
return *newInstance;
}
+bool LLEventPumps::post(const std::string&name, const LLSD&message)
+{
+ PumpMap::iterator found = mPumpMap.find(name);
+
+ if (found == mPumpMap.end())
+ return false;
+
+ return (*found).second->post(message);
+}
+
+
void LLEventPumps::flush()
{
// Flush every known LLEventPump instance. Leave it up to each instance to
@@ -497,6 +509,43 @@ bool LLEventStream::post(const LLSD& event)
}
/*****************************************************************************
+ * LLEventMailDrop
+ *****************************************************************************/
+bool LLEventMailDrop::post(const LLSD& event)
+{
+ bool posted = false;
+
+ if (!mSignal->empty())
+ posted = LLEventStream::post(event);
+
+ if (!posted)
+ { // if the event was not handled we will save it for later so that it can
+ // be posted to any future listeners when they attach.
+ mEventHistory.push_back(event);
+ }
+
+ return posted;
+}
+
+LLBoundListener LLEventMailDrop::listen_impl(const std::string& name,
+ const LLEventListener& listener,
+ const NameList& after,
+ const NameList& before)
+{
+ if (!mEventHistory.empty())
+ {
+ if (listener(mEventHistory.front()) || LLCoros::get_consuming())
+ {
+ mEventHistory.pop_front();
+ }
+ }
+
+ return LLEventStream::listen_impl(name, listener, after, before);
+
+}
+
+
+/*****************************************************************************
* LLEventQueue
*****************************************************************************/
bool LLEventQueue::post(const LLSD& event)
diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h
index 0cbd1da32d..6175329a9d 100755
--- a/indra/llcommon/llevents.h
+++ b/indra/llcommon/llevents.h
@@ -217,6 +217,18 @@ public:
* an instance without conferring @em ownership.
*/
LLEventPump& obtain(const std::string& name);
+
+ /**
+ * Find the named LLEventPump instance. If it exists post the message to it.
+ * If the pump does not exist, do nothing.
+ *
+ * returns the result of the LLEventPump::post. If no pump exists returns false.
+ *
+ * This is syntactically similar to LLEventPumps::instance().post(name, message),
+ * however if the pump does not already exist it will not be created.
+ */
+ bool post(const std::string&, const LLSD&);
+
/**
* Flush all known LLEventPump instances
*/
@@ -496,7 +508,7 @@ public:
// at the boost::bind object itself before that happens.
return LLEventDetail::visit_and_connect(name,
listener,
- boost::bind(&LLEventPump::listen_impl,
+ boost::bind(&LLEventPump::listen_invoke,
this,
name,
_1,
@@ -541,13 +553,23 @@ private:
virtual void reset();
+
+
private:
- virtual LLBoundListener listen_impl(const std::string& name, const LLEventListener&,
- const NameList& after,
- const NameList& before);
+ LLBoundListener listen_invoke(const std::string& name, const LLEventListener& listener,
+ const NameList& after,
+ const NameList& before)
+ {
+ return this->listen_impl(name, listener, after, before);
+ }
+
std::string mName;
protected:
+ virtual LLBoundListener listen_impl(const std::string& name, const LLEventListener&,
+ const NameList& after,
+ const NameList& before);
+
/// implement the dispatching
boost::shared_ptr<LLStandardSignal> mSignal;
@@ -586,10 +608,39 @@ public:
};
/*****************************************************************************
+ * LLEventMailDrop
+ *****************************************************************************/
+/**
+ * LLEventMailDrop is a specialization of LLEventStream. Events are posted normally,
+ * however if no listeners return that they have handled the event it is placed in
+ * a queue. Subsequent attaching listeners will receive stored events from the queue
+ * until a listener indicates that the event has been handled. In order to receive
+ * multiple events from a mail drop the listener must disconnect and reconnect.
+ */
+class LL_COMMON_API LLEventMailDrop : public LLEventStream
+{
+public:
+ LLEventMailDrop(const std::string& name, bool tweak = false) : LLEventStream(name, tweak) {}
+ virtual ~LLEventMailDrop() {}
+
+ /// Post an event to all listeners
+ virtual bool post(const LLSD& event);
+
+protected:
+ virtual LLBoundListener listen_impl(const std::string& name, const LLEventListener&,
+ const NameList& after,
+ const NameList& before);
+
+private:
+ typedef std::list<LLSD> EventList;
+ EventList mEventHistory;
+};
+
+/*****************************************************************************
* LLEventQueue
*****************************************************************************/
/**
- * LLEventQueue isa LLEventPump whose post() method defers calling registered
+ * LLEventQueue is a LLEventPump whose post() method defers calling registered
* listeners until flush() is called.
*/
class LL_COMMON_API LLEventQueue: public LLEventPump
diff --git a/indra/llcommon/tests/lleventcoro_test.cpp b/indra/llcommon/tests/lleventcoro_test.cpp
index 1ee79e9eb6..da1439418f 100755
--- a/indra/llcommon/tests/lleventcoro_test.cpp
+++ b/indra/llcommon/tests/lleventcoro_test.cpp
@@ -240,7 +240,7 @@ namespace tut
// ... do whatever preliminary stuff must happen ...
// declare the future
- boost::dcoroutines::future<LLSD> future(self);
+ boost::dcoroutines::future<llcoro::LLSD_consumed> future(self);
// tell the future what to suspend for
LLTempBoundListener connection(
LLEventPumps::instance().obtain("source").listen("coro", voidlistener(boost::dcoroutines::make_callback(future))));
@@ -248,7 +248,7 @@ namespace tut
// attempting to dereference ("resolve") the future causes the calling
// coroutine to suspend for it
debug("about to suspend");
- result = *future;
+ result = (*future).first;
ensure("Got it", future);
}
END
diff --git a/indra/llmessage/CMakeLists.txt b/indra/llmessage/CMakeLists.txt
index 78c84f366a..87bec60d95 100755
--- a/indra/llmessage/CMakeLists.txt
+++ b/indra/llmessage/CMakeLists.txt
@@ -203,6 +203,8 @@ set_source_files_properties(${llmessage_HEADER_FILES}
list(APPEND llmessage_SOURCE_FILES ${llmessage_HEADER_FILES})
add_library (llmessage ${llmessage_SOURCE_FILES})
+
+if (LINUX)
target_link_libraries(
llmessage
${CURL_LIBRARIES}
@@ -217,7 +219,25 @@ target_link_libraries(
${BOOST_COROUTINE_LIBRARY}
${BOOST_CONTEXT_LIBRARY}
${BOOST_SYSTEM_LIBRARY}
+ rt
)
+else (LINUX)
+target_link_libraries(
+ llmessage
+ ${CURL_LIBRARIES}
+ ${LLCOMMON_LIBRARIES}
+ ${LLVFS_LIBRARIES}
+ ${LLMATH_LIBRARIES}
+ ${JSONCPP_LIBRARIES}
+ ${OPENSSL_LIBRARIES}
+ ${CRYPTO_LIBRARIES}
+ ${XMLRPCEPI_LIBRARIES}
+ ${LLCOREHTTP_LIBRARIES}
+ ${BOOST_COROUTINE_LIBRARY}
+ ${BOOST_CONTEXT_LIBRARY}
+ ${BOOST_SYSTEM_LIBRARY}
+ )
+endif(LINUX)
# tests
if (LL_TESTS)
@@ -231,6 +251,22 @@ if (LL_TESTS)
# set(TEST_DEBUG on)
+if (LINUX)
+ set(test_libs
+ ${WINDOWS_LIBRARIES}
+ ${LLVFS_LIBRARIES}
+ ${LLMATH_LIBRARIES}
+ ${CURL_LIBRARIES}
+ ${LLCOMMON_LIBRARIES}
+ ${LLMESSAGE_LIBRARIES}
+ ${LLCOREHTTP_LIBRARIES}
+ ${JSONCPP_LIBRARIES}
+ ${BOOST_COROUTINE_LIBRARY}
+ ${BOOST_CONTEXT_LIBRARY}
+ rt
+ ${GOOGLEMOCK_LIBRARIES}
+ )
+else (LINUX)
set(test_libs
${WINDOWS_LIBRARIES}
${LLVFS_LIBRARIES}
@@ -244,6 +280,7 @@ if (LL_TESTS)
${BOOST_CONTEXT_LIBRARY}
${GOOGLEMOCK_LIBRARIES}
)
+endif(LINUX)
#LL_ADD_INTEGRATION_TEST(llavatarnamecache "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llhost "" "${test_libs}")
diff --git a/indra/llmessage/llexperiencecache.cpp b/indra/llmessage/llexperiencecache.cpp
index 92fcd38d3b..779d1d9d99 100644
--- a/indra/llmessage/llexperiencecache.cpp
+++ b/indra/llmessage/llexperiencecache.cpp
@@ -396,12 +396,9 @@ void LLExperienceCache::idleCoro()
const F32 ERASE_EXPIRED_TIMEOUT = 60.f; // seconds
LL_INFOS("ExperienceCache") << "Launching Experience cache idle coro." << LL_ENDL;
- LLEventTimeout timeout;
-
do
{
- timeout.eventAfter(SECS_BETWEEN_REQUESTS, LLSD());
- llcoro::suspendUntilEventOn(timeout);
+ llcoro::suspendUntilTimeout(SECS_BETWEEN_REQUESTS);
if (mEraseExpiredTimer.checkExpirationAndReset(ERASE_EXPIRED_TIMEOUT))
{
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 51efd76484..2661dd759a 100755
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -1736,8 +1736,6 @@ if (WINDOWS)
${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/vivoxsdk.dll
${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/ortp.dll
${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/libsndfile-1.dll
- ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/zlib1.dll
- ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/vivoxplatform.dll
${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/vivoxoal.dll
${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/ca-bundle.crt
${GOOGLE_PERF_TOOLS_SOURCE}
@@ -2204,6 +2202,7 @@ if (LL_TESTS)
)
set(test_libs
+ ${LLCOMMON_LIBRARIES}
${JSONCPP_LIBRARIES}
${CURL_LIBRARIES}
)
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index be497fffc0..413cc413e6 100755
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -2753,7 +2753,7 @@ bool LLAppViewer::initConfiguration()
//
gWindowTitle = LLTrans::getString("APP_NAME");
#if LL_DEBUG
- gWindowTitle += std::string(" [DEBUG]");
+ gWindowTitle += std::string(" [DEBUG]");
#endif
if (!gArgs.empty())
{
diff --git a/indra/newview/lleventpoll.cpp b/indra/newview/lleventpoll.cpp
index 72e159bcec..7178042b32 100755
--- a/indra/newview/lleventpoll.cpp
+++ b/indra/newview/lleventpoll.cpp
@@ -197,7 +197,6 @@ namespace Details
// request. Calculate a timeout and wait for it to expire(sleep)
// before trying again. The sleep time is increased by 5 seconds
// for each consecutive error.
- LLEventTimeout timeout;
++errorCount;
F32 waitToRetry = EVENT_POLL_ERROR_RETRY_SECONDS
@@ -206,8 +205,7 @@ namespace Details
LL_WARNS("LLEventPollImpl") << "<" << counter << "> Retrying in " << waitToRetry <<
" seconds, error count is now " << errorCount << LL_ENDL;
- timeout.eventAfter(waitToRetry, LLSD());
- llcoro::suspendUntilEventOn(timeout);
+ llcoro::suspendUntilTimeout(waitToRetry);
if (mDone)
break;
diff --git a/indra/newview/llexperienceassociationresponder.cpp b/indra/newview/llexperienceassociationresponder.cpp
new file mode 100644
index 0000000000..7f2363aadc
--- /dev/null
+++ b/indra/newview/llexperienceassociationresponder.cpp
@@ -0,0 +1,104 @@
+/**
+ * @file llexperienceassociationresponder.cpp
+ * @brief llexperienceassociationresponder implementation. This class combines
+ * a lookup for a script association and an experience details request. The first
+ * is always async, but the second may be cached locally.
+ *
+ * $LicenseInfo:firstyear=2013&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2013, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llexperienceassociationresponder.h"
+#include "llexperiencecache.h"
+#include "llviewerobjectlist.h"
+#include "llviewerregion.h"
+#include "llagent.h"
+
+ExperienceAssociationResponder::ExperienceAssociationResponder(ExperienceAssociationResponder::callback_t callback):mCallback(callback)
+{
+ ref();
+}
+
+void ExperienceAssociationResponder::fetchAssociatedExperience( const LLUUID& object_id, const LLUUID& item_id, callback_t callback )
+{
+ LLSD request;
+ request["object-id"]=object_id;
+ request["item-id"]=item_id;
+ fetchAssociatedExperience(request, callback);
+}
+
+void ExperienceAssociationResponder::fetchAssociatedExperience(LLSD& request, callback_t callback)
+{
+ LLViewerObject* object = gObjectList.findObject(request["object-id"]);
+ if (!object)
+ {
+ LL_WARNS() << "Failed to find object with ID " << request["object-id"] << " in fetchAssociatedExperience" << LL_ENDL;
+ return;
+ }
+ LLViewerRegion* region = object->getRegion();
+ if (region)
+ {
+ std::string lookup_url=region->getCapability("GetMetadata");
+ if(!lookup_url.empty())
+ {
+ LLSD fields;
+ fields.append("experience");
+ request["fields"] = fields;
+ LLHTTPClient::post(lookup_url, request, new ExperienceAssociationResponder(callback));
+ }
+ }
+}
+
+void ExperienceAssociationResponder::httpFailure()
+{
+ LLSD msg;
+ msg["error"]=(LLSD::Integer)getStatus();
+ msg["message"]=getReason();
+ LL_INFOS("ExperienceAssociation") << "Failed to look up associated experience: " << getStatus() << ": " << getReason() << LL_ENDL;
+
+ sendResult(msg);
+
+}
+void ExperienceAssociationResponder::httpSuccess()
+{
+ if(!getContent().has("experience"))
+ {
+
+ LLSD msg;
+ msg["message"]="no experience";
+ msg["error"]=-1;
+ sendResult(msg);
+ return;
+ }
+
+ LLExperienceCache::get(getContent()["experience"].asUUID(), boost::bind(&ExperienceAssociationResponder::sendResult, this, _1));
+
+}
+
+void ExperienceAssociationResponder::sendResult( const LLSD& experience )
+{
+ mCallback(experience);
+ unref();
+}
+
+
+
diff --git a/indra/newview/llexperienceassociationresponder.h b/indra/newview/llexperienceassociationresponder.h
new file mode 100644
index 0000000000..2bdc3d251b
--- /dev/null
+++ b/indra/newview/llexperienceassociationresponder.h
@@ -0,0 +1,58 @@
+#include "llhttpclient.h"
+#include "llsd.h"
+/**
+ * @file llexperienceassociationresponder.h
+ * @brief llexperienceassociationresponder and related class definitions
+ *
+ * $LicenseInfo:firstyear=2013&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2013, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+
+
+#ifndef LL_LLEXPERIENCEASSOCIATIONRESPONDER_H
+#define LL_LLEXPERIENCEASSOCIATIONRESPONDER_H
+
+#include "llhttpclient.h"
+#include "llsd.h"
+
+class ExperienceAssociationResponder : public LLHTTPClient::Responder
+{
+public:
+ typedef boost::function<void(const LLSD& experience)> callback_t;
+
+ ExperienceAssociationResponder(callback_t callback);
+
+ /*virtual*/ void httpSuccess();
+ /*virtual*/ void httpFailure();
+
+ static void fetchAssociatedExperience(const LLUUID& object_it, const LLUUID& item_id, callback_t callback);
+
+private:
+ static void fetchAssociatedExperience(LLSD& request, callback_t callback);
+
+ void sendResult(const LLSD& experience);
+
+ callback_t mCallback;
+
+};
+
+#endif // LL_LLEXPERIENCEASSOCIATIONRESPONDER_H
diff --git a/indra/newview/llfloaterperms.cpp b/indra/newview/llfloaterperms.cpp
index cb67787af3..b0b2770c6e 100755
--- a/indra/newview/llfloaterperms.cpp
+++ b/indra/newview/llfloaterperms.cpp
@@ -232,8 +232,6 @@ void LLFloaterPermsDefault::updateCapCoro(std::string url)
if (!status)
{
- LLEventTimeout timeout;
-
const std::string& reason = status.toString();
// Do not display the same error more than once in a row
if (reason != previousReason)
@@ -244,8 +242,7 @@ void LLFloaterPermsDefault::updateCapCoro(std::string url)
LLNotificationsUtil::add("DefaultObjectPermissions", args);
}
- timeout.eventAfter(RETRY_TIMEOUT, LLSD());
- llcoro::suspendUntilEventOn(timeout);
+ llcoro::suspendUntilTimeout(RETRY_TIMEOUT);
if (retryCount < MAX_HTTP_RETRIES)
continue;
diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp
index 53b97a58c5..951389b856 100755
--- a/indra/newview/llimview.cpp
+++ b/indra/newview/llimview.cpp
@@ -1388,12 +1388,12 @@ void LLIMModel::sendMessage(const std::string& utf8_text,
info = LLAvatarTracker::instance().getBuddyInfo(other_participant_id);
U8 offline = (!info || info->isOnline()) ? IM_ONLINE : IM_OFFLINE;
-
- if((offline == IM_OFFLINE) && (LLVoiceClient::getInstance()->isOnlineSIP(other_participant_id)))
- {
- // User is online through the OOW connector, but not with a regular viewer. Try to send the message via SLVoice.
- sent = LLVoiceClient::getInstance()->sendTextMessage(other_participant_id, utf8_text);
- }
+ // Old call to send messages to SLim client, no longer supported.
+ //if((offline == IM_OFFLINE) && (LLVoiceClient::getInstance()->isOnlineSIP(other_participant_id)))
+ //{
+ // // User is online through the OOW connector, but not with a regular viewer. Try to send the message via SLVoice.
+ // sent = LLVoiceClient::getInstance()->sendTextMessage(other_participant_id, utf8_text);
+ //}
if(!sent)
{
diff --git a/indra/newview/llpanelvoicedevicesettings.cpp b/indra/newview/llpanelvoicedevicesettings.cpp
index 3946d6a63b..07f8045546 100755
--- a/indra/newview/llpanelvoicedevicesettings.cpp
+++ b/indra/newview/llpanelvoicedevicesettings.cpp
@@ -51,7 +51,7 @@ LLPanelVoiceDeviceSettings::LLPanelVoiceDeviceSettings()
mCtrlOutputDevices = NULL;
mInputDevice = gSavedSettings.getString("VoiceInputAudioDevice");
mOutputDevice = gSavedSettings.getString("VoiceOutputAudioDevice");
- mDevicesUpdated = FALSE;
+ mDevicesUpdated = FALSE; //obsolete
mUseTuningMode = true;
// grab "live" mic volume level
@@ -80,6 +80,10 @@ BOOL LLPanelVoiceDeviceSettings::postBuild()
mLocalizedDeviceNames[DEFAULT_DEVICE] = getString("default_text");
mLocalizedDeviceNames["No Device"] = getString("name_no_device");
mLocalizedDeviceNames["Default System Device"] = getString("name_default_system_device");
+
+ mCtrlOutputDevices->setMouseDownCallback(boost::bind(&LLPanelVoiceDeviceSettings::onOutputDevicesClicked, this));
+ mCtrlInputDevices->setMouseDownCallback(boost::bind(&LLPanelVoiceDeviceSettings::onInputDevicesClicked, this));
+
return TRUE;
}
@@ -226,9 +230,8 @@ void LLPanelVoiceDeviceSettings::refresh()
mCtrlOutputDevices->add(getLocalizedDeviceName(mOutputDevice), mOutputDevice, ADD_BOTTOM);
mCtrlOutputDevices->setValue(mOutputDevice);
}
- mDevicesUpdated = FALSE;
}
- else if (!mDevicesUpdated)
+ else if (LLVoiceClient::getInstance()->deviceSettingsUpdated())
{
LLVoiceDeviceList::const_iterator iter;
@@ -272,7 +275,6 @@ void LLPanelVoiceDeviceSettings::refresh()
mOutputDevice = DEFAULT_DEVICE;
}
}
- mDevicesUpdated = TRUE;
}
}
@@ -281,7 +283,6 @@ void LLPanelVoiceDeviceSettings::initialize()
mInputDevice = gSavedSettings.getString("VoiceInputAudioDevice");
mOutputDevice = gSavedSettings.getString("VoiceOutputAudioDevice");
mMicVolume = gSavedSettings.getF32("AudioLevelMic");
- mDevicesUpdated = FALSE;
// ask for new device enumeration
LLVoiceClient::getInstance()->refreshDeviceLists();
@@ -314,8 +315,8 @@ void LLPanelVoiceDeviceSettings::onCommitInputDevice()
{
if(LLVoiceClient::getInstance())
{
- LLVoiceClient::getInstance()->setCaptureDevice(
- mCtrlInputDevices->getValue().asString());
+ mInputDevice = mCtrlInputDevices->getValue().asString();
+ LLVoiceClient::getInstance()->setRenderDevice(mInputDevice);
}
}
@@ -323,7 +324,18 @@ void LLPanelVoiceDeviceSettings::onCommitOutputDevice()
{
if(LLVoiceClient::getInstance())
{
- LLVoiceClient::getInstance()->setRenderDevice(
- mCtrlInputDevices->getValue().asString());
+
+ mOutputDevice = mCtrlOutputDevices->getValue().asString();
+ LLVoiceClient::getInstance()->setRenderDevice(mOutputDevice);
}
}
+
+void LLPanelVoiceDeviceSettings::onOutputDevicesClicked()
+{
+ LLVoiceClient::getInstance()->refreshDeviceLists(false); // fill in the pop up menus again if needed.
+}
+
+void LLPanelVoiceDeviceSettings::onInputDevicesClicked()
+{
+ LLVoiceClient::getInstance()->refreshDeviceLists(false); // fill in the pop up menus again if needed.
+}
diff --git a/indra/newview/llpanelvoicedevicesettings.h b/indra/newview/llpanelvoicedevicesettings.h
index 83464f476a..355bc02b05 100755
--- a/indra/newview/llpanelvoicedevicesettings.h
+++ b/indra/newview/llpanelvoicedevicesettings.h
@@ -53,6 +53,8 @@ protected:
void onCommitInputDevice();
void onCommitOutputDevice();
+ void onOutputDevicesClicked();
+ void onInputDevicesClicked();
F32 mMicVolume;
std::string mInputDevice;
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index b0280ef3e0..a4109d5885 100755
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -310,7 +310,7 @@ void LLViewerRegionImpl::requestBaseCapabilitiesCoro(U64 regionHandle)
<< "Capability '" << iter->first << "' is '" << iter->second << "'" << LL_ENDL;
}
-#if 0
+#if 1
log_capabilities(mCapabilities);
#endif
diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp
index e24884fe81..56c0910983 100755
--- a/indra/newview/llvoiceclient.cpp
+++ b/indra/newview/llvoiceclient.cpp
@@ -266,6 +266,18 @@ bool LLVoiceClient::deviceSettingsAvailable()
}
}
+bool LLVoiceClient::deviceSettingsUpdated()
+{
+ if (mVoiceModule)
+ {
+ return mVoiceModule->deviceSettingsUpdated();
+ }
+ else
+ {
+ return false;
+ }
+}
+
void LLVoiceClient::refreshDeviceLists(bool clearCurrentList)
{
if (mVoiceModule) mVoiceModule->refreshDeviceLists(clearCurrentList);
@@ -363,6 +375,7 @@ BOOL LLVoiceClient::isSessionCallBackPossible(const LLUUID& id)
}
}
+/* obsolete
BOOL LLVoiceClient::sendTextMessage(const LLUUID& participant_id, const std::string& message)
{
if (mVoiceModule)
@@ -374,12 +387,13 @@ BOOL LLVoiceClient::sendTextMessage(const LLUUID& participant_id, const std::str
return FALSE;
}
}
+*/
void LLVoiceClient::endUserIMSession(const LLUUID& participant_id)
{
if (mVoiceModule)
{
- mVoiceModule->endUserIMSession(participant_id);
+ // mVoiceModule->endUserIMSession(participant_id); // A SLim leftover
}
}
@@ -645,9 +659,8 @@ void LLVoiceClient::keyDown(KEY key, MASK mask)
if(!mPTTIsMiddleMouse && LLAgent::isActionAllowed("speak"))
{
- bool down = (mPTTKey != KEY_NONE)
- && gKeyboard->getKeyDown(mPTTKey);
- inputUserControlState(down);
+ bool down = (mPTTKey != KEY_NONE) && gKeyboard->getKeyDown(mPTTKey);
+ if (down) { inputUserControlState(down); }
}
}
@@ -655,9 +668,8 @@ void LLVoiceClient::keyUp(KEY key, MASK mask)
{
if(!mPTTIsMiddleMouse)
{
- bool down = (mPTTKey != KEY_NONE)
- && gKeyboard->getKeyDown(mPTTKey);
- inputUserControlState(down);
+ bool down = (mPTTKey != KEY_NONE) && gKeyboard->getKeyDown(mPTTKey);
+ if (down) { inputUserControlState(down); }
}
}
void LLVoiceClient::middleMouseState(bool down)
diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h
index fb387301be..b05bcb23b7 100755
--- a/indra/newview/llvoiceclient.h
+++ b/indra/newview/llvoiceclient.h
@@ -128,6 +128,7 @@ public:
// This returns true when it's safe to bring up the "device settings" dialog in the prefs.
// i.e. when the daemon is running and connected, and the device lists are populated.
virtual bool deviceSettingsAvailable()=0;
+ virtual bool deviceSettingsUpdated() = 0;
// Requery the vivox daemon for the current list of input/output devices.
// If you pass true for clearCurrentList, deviceSettingsAvailable() will be false until the query has completed
@@ -155,7 +156,7 @@ public:
virtual void setNonSpatialChannel(const std::string &uri,
const std::string &credentials)=0;
- virtual void setSpatialChannel(const std::string &uri,
+ virtual bool setSpatialChannel(const std::string &uri,
const std::string &credentials)=0;
virtual void leaveNonSpatialChannel()=0;
@@ -214,7 +215,7 @@ public:
//@{
virtual BOOL isSessionTextIMPossible(const LLUUID& id)=0;
virtual BOOL isSessionCallBackPossible(const LLUUID& id)=0;
- virtual BOOL sendTextMessage(const LLUUID& participant_id, const std::string& message)=0;
+ //virtual BOOL sendTextMessage(const LLUUID& participant_id, const std::string& message)=0;
virtual void endUserIMSession(const LLUUID &uuid)=0;
//@}
@@ -335,6 +336,7 @@ public:
// This returns true when it's safe to bring up the "device settings" dialog in the prefs.
// i.e. when the daemon is running and connected, and the device lists are populated.
bool deviceSettingsAvailable();
+ bool deviceSettingsUpdated(); // returns true when the device list has been updated recently.
// Requery the vivox daemon for the current list of input/output devices.
// If you pass true for clearCurrentList, deviceSettingsAvailable() will be false until the query has completed
@@ -431,7 +433,7 @@ public:
//@{
BOOL isSessionTextIMPossible(const LLUUID& id);
BOOL isSessionCallBackPossible(const LLUUID& id);
- BOOL sendTextMessage(const LLUUID& participant_id, const std::string& message);
+ //BOOL sendTextMessage(const LLUUID& participant_id, const std::string& message) const {return true;} ;
void endUserIMSession(const LLUUID &uuid);
//@}
diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp
index 1425499b12..a27afef052 100755
--- a/indra/newview/llvoicevivox.cpp
+++ b/indra/newview/llvoicevivox.cpp
@@ -60,11 +60,13 @@
#include "lltrans.h"
#include "llviewerwindow.h"
#include "llviewercamera.h"
+#include "llversioninfo.h"
#include "llviewernetwork.h"
#include "llnotificationsutil.h"
#include "llcorehttputil.h"
+#include "lleventfilter.h"
#include "stringize.h"
@@ -93,11 +95,10 @@ const F32 LOGIN_RETRY_SECONDS = 10.0f;
const int MAX_LOGIN_RETRIES = 12;
// Defines the maximum number of times(in a row) "stateJoiningSession" case for spatial channel is reached in stateMachine()
-// which is treated as normal. If this number is exceeded we suspect there is a problem with connection
-// to voice server (EXT-4313). When voice works correctly, there is from 1 to 15 times. 50 was chosen
-// to make sure we don't make mistake when slight connection problems happen- situation when connection to server is
-// blocked is VERY rare and it's better to sacrifice response time in this situation for the sake of stability.
-const int MAX_NORMAL_JOINING_SPATIAL_NUM = 50;
+// which is treated as normal. The is the number of frames to wait for a channel join before giving up. This was changed
+// from the original count of 50 for two reason. Modern PCs have higher frame rates and sometimes the SLVoice process
+// backs up processing join requests. There is a log statement that records when channel joins take longer than 100 frames.
+const int MAX_NORMAL_JOINING_SPATIAL_NUM = 1500;
// How often to check for expired voice fonts in seconds
const F32 VOICE_FONT_EXPIRY_INTERVAL = 10.f;
@@ -166,9 +167,10 @@ LLVivoxVoiceClient::LLVivoxVoiceClient() :
mTuningEnergy(0.0f),
mTuningMicVolume(0),
mTuningMicVolumeDirty(true),
- mTuningSpeakerVolume(0),
+ mTuningSpeakerVolume(50), // Set to 50 so the user can hear himself when he sets his mic volume
mTuningSpeakerVolumeDirty(true),
mTuningExitState(stateDisabled),
+ mDevicesListUpdated(false),
mAreaVoiceDisabled(false),
mAudioSession(NULL),
@@ -215,7 +217,12 @@ LLVivoxVoiceClient::LLVivoxVoiceClient() :
mShutdownComplete(true),
mPlayRequestCount(0),
- mAvatarNameCacheConnection()
+ mAvatarNameCacheConnection(),
+ mIsInTuningMode(false),
+ mIsInChannel(false),
+ mIsJoiningSession(false),
+
+ mVivoxPump("vivoxClientPump")
{
mSpeakerVolume = scale_speaker_volume(0);
@@ -238,6 +245,7 @@ LLVivoxVoiceClient::LLVivoxVoiceClient() :
signal(SIGCHLD, SIG_IGN);
#endif
+
// set up state machine
setState(stateDisabled);
@@ -279,6 +287,7 @@ void LLVivoxVoiceClient::terminate()
#endif
closeSocket(); // Need to do this now -- bad things happen if the destructor does it later.
cleanUp();
+ mConnected = false;
}
else
{
@@ -339,17 +348,20 @@ bool LLVivoxVoiceClient::writeString(const std::string &str)
(const char*)str.data(),
&written);
- if(err == 0)
+ if(err == 0 && written == size)
{
// Success.
result = true;
}
- // TODO: handle partial writes (written is number of bytes written)
- // Need to set socket to non-blocking before this will work.
-// else if(APR_STATUS_IS_EAGAIN(err))
-// {
-// //
-// }
+ else if (err == 0 && written != size) {
+ // Did a short write, log it for now
+ LL_WARNS("Voice") << ") short write on socket sending data to vivox daemon." << "Sent " << written << "bytes instead of " << size <<LL_ENDL;
+ }
+ else if(APR_STATUS_IS_EAGAIN(err))
+ {
+ char buf[MAX_STRING];
+ LL_WARNS("Voice") << "EAGAIN error " << err << " (" << apr_strerror(err, buf, MAX_STRING) << ") sending data to vivox daemon." << LL_ENDL;
+ }
else
{
// Assume any socket error means something bad. For now, just close the socket.
@@ -372,8 +384,6 @@ void LLVivoxVoiceClient::connectorCreate()
std::string loglevel = "0";
// Transition to stateConnectorStarted when the connector handle comes back.
- setState(stateConnectorStarting);
-
std::string savedLogLevel = gSavedSettings.getString("VivoxDebugLevel");
if(savedLogLevel != "0")
@@ -387,14 +397,15 @@ void LLVivoxVoiceClient::connectorCreate()
<< "<AccountManagementServer>" << mVoiceAccountServerURI << "</AccountManagementServer>"
<< "<Mode>Normal</Mode>"
<< "<Logging>"
- << "<Folder>" << logpath << "</Folder>"
- << "<FileNamePrefix>Connector</FileNamePrefix>"
- << "<FileNameSuffix>.log</FileNameSuffix>"
- << "<LogLevel>" << loglevel << "</LogLevel>"
+ << "<Folder>" << logpath << "</Folder>"
+ << "<FileNamePrefix>Connector</FileNamePrefix>"
+ << "<FileNameSuffix>.log</FileNameSuffix>"
+ << "<LogLevel>" << loglevel << "</LogLevel>"
<< "</Logging>"
- << "<Application></Application>" //Name can cause problems per vivox.
- << "<MaxCalls>12</MaxCalls>"
- << "</Request>\n\n\n";
+ << "<Application>" << LLVersionInfo::getChannel().c_str() << " " << LLVersionInfo::getVersion().c_str() << "</Application>"
+ //<< "<Application></Application>" //Name can cause problems per vivox.
+ << "<MaxCalls>12</MaxCalls>"
+ << "</Request>\n\n\n";
writeString(stream.str());
}
@@ -429,68 +440,7 @@ void LLVivoxVoiceClient::userAuthorized(const std::string& user_id, const LLUUID
mAccountName = nameFromID(agentID);
}
-void LLVivoxVoiceClient::requestVoiceAccountProvision(S32 retries)
-{
- LLViewerRegion *region = gAgent.getRegion();
-
- // If we've not received the capability yet, return.
- // the password will remain empty, so we'll remain in
- // stateIdle
- if ( region &&
- region->capabilitiesReceived() &&
- (mVoiceEnabled || !mIsInitialized))
- {
- std::string url =
- region->getCapability("ProvisionVoiceAccountRequest");
-
- if ( !url.empty() )
- {
- LLCoros::instance().launch("LLVivoxVoiceClient::voiceAccountProvisionCoro",
- boost::bind(&LLVivoxVoiceClient::voiceAccountProvisionCoro, this, url, retries));
- setState(stateConnectorStart);
- }
- }
-}
-
-void LLVivoxVoiceClient::voiceAccountProvisionCoro(std::string url, S32 retries)
-{
- LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
- LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
- httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("voiceAccountProvision", httpPolicy));
- LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
- LLCore::HttpOptions::ptr_t httpOpts = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions);
-
- httpOpts->setRetries(retries);
-
- LLSD result = httpAdapter->postAndSuspend(httpRequest, url, LLSD(), httpOpts);
-
- LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
- LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
-
- if (!status)
- {
- LL_WARNS("Voice") << "Unable to provision voice account." << LL_ENDL;
- giveUp();
- return;
- }
-
- std::string voice_sip_uri_hostname;
- std::string voice_account_server_uri;
-
- //LL_DEBUGS("Voice") << "ProvisionVoiceAccountRequest response:" << dumpResponse() << LL_ENDL;
-
- if (result.has("voice_sip_uri_hostname"))
- voice_sip_uri_hostname = result["voice_sip_uri_hostname"].asString();
-
- // this key is actually misnamed -- it will be an entire URI, not just a hostname.
- if (result.has("voice_account_server_name"))
- voice_account_server_uri = result["voice_account_server_name"].asString();
-
- login(result["username"].asString(), result["password"].asString(),
- voice_sip_uri_hostname, voice_account_server_uri);
-}
-
-void LLVivoxVoiceClient::login(
+void LLVivoxVoiceClient::setLoginInfo(
const std::string& account_name,
const std::string& password,
const std::string& voice_sip_uri_hostname,
@@ -639,9 +589,12 @@ void LLVivoxVoiceClient::stateMachine()
setVoiceEnabled(false);
}
- if(mVoiceEnabled || (!mIsInitialized &&!mTerminateDaemon) )
+ if ((getState() == stateRunning) && inSpatialChannel() && /*mUpdateTimer.hasExpired() &&*/ !mTerminateDaemon)
{
+#if 0
+ // poll the avatar position so its available in various states when a 3d position is sent.
updatePosition();
+#endif
}
else if(mTuningMode)
{
@@ -649,24 +602,31 @@ void LLVivoxVoiceClient::stateMachine()
}
else
{
- if((getState() != stateDisabled) && (getState() != stateDisableCleanup))
+ if (!gSavedSettings.getBOOL("EnableVoiceChat"))
+ //if((getState() != stateDisabled) && (getState() != stateDisableCleanup))
{
// User turned off voice support. Send the cleanup messages, close the socket, and reset.
- if(!mConnected || mTerminateDaemon)
+ if(!mConnected && mTerminateDaemon)
{
// if voice was turned off after the daemon was launched but before we could connect to it, we may need to issue a kill.
LL_INFOS("Voice") << "Disabling voice before connection to daemon, terminating." << LL_ENDL;
killGateway();
mTerminateDaemon = false;
+ mConnected = false;
+ }
+ else {
+ logout();
+ connectorShutdown();
}
-
- logout();
- connectorShutdown();
setState(stateDisableCleanup);
}
}
+
+ // send any requests to adjust mic and speaker settings if they have changed
+ sendLocalAudioUpdates();
+
switch(getState())
{
@@ -687,516 +647,62 @@ void LLVivoxVoiceClient::stateMachine()
case stateDisabled:
if(mTuningMode || ((mVoiceEnabled || !mIsInitialized) && !mAccountName.empty()))
{
- setState(stateStart);
- }
- break;
-
- //MARK: stateStart
- case stateStart:
- if(gSavedSettings.getBOOL("CmdLineDisableVoice"))
- {
- // Voice is locked out, we must not launch the vivox daemon.
- setState(stateJail);
- }
- else if(!isGatewayRunning() && gSavedSettings.getBOOL("EnableVoiceChat"))
- {
- if (true) // production build, not test
- {
- // Launch the voice daemon
-
- // *FIX:Mani - Using the executable dir instead
- // of mAppRODataDir, the working directory from which the app
- // is launched.
- //std::string exe_path = gDirUtilp->getAppRODataDir();
- std::string exe_path = gDirUtilp->getExecutableDir();
- exe_path += gDirUtilp->getDirDelimiter();
-#if LL_WINDOWS
- exe_path += "SLVoice.exe";
-#elif LL_DARWIN
- exe_path += "../Resources/SLVoice";
-#else
- exe_path += "SLVoice";
-#endif
- // See if the vivox executable exists
- llstat s;
- if (!LLFile::stat(exe_path, &s))
- {
- // vivox executable exists. Build the command line and launch the daemon.
- LLProcess::Params params;
- params.executable = exe_path;
-
- std::string loglevel = gSavedSettings.getString("VivoxDebugLevel");
- std::string shutdown_timeout = gSavedSettings.getString("VivoxShutdownTimeout");
- if(loglevel.empty())
- {
- loglevel = "0"; // turn logging off completely
- }
-
- params.args.add("-ll");
- params.args.add(loglevel);
-
- std::string log_folder = gSavedSettings.getString("VivoxLogDirectory");
-
- if (log_folder.empty())
- {
- log_folder = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "");
- }
-
- params.args.add("-lf");
- params.args.add(log_folder);
-
- if(!shutdown_timeout.empty())
- {
- params.args.add("-st");
- params.args.add(shutdown_timeout);
- }
- params.cwd = gDirUtilp->getAppRODataDir();
- sGatewayPtr = LLProcess::create(params);
-
- mDaemonHost = LLHost(gSavedSettings.getString("VivoxVoiceHost").c_str(), gSavedSettings.getU32("VivoxVoicePort"));
- }
- else
- {
- LL_INFOS("Voice") << exe_path << " not found." << LL_ENDL;
- }
- }
- else
- {
- // SLIM SDK: port changed from 44124 to 44125.
- // We can connect to a client gateway running on another host. This is useful for testing.
- // To do this, launch the gateway on a nearby host like this:
- // vivox-gw.exe -p tcp -i 0.0.0.0:44125
- // and put that host's IP address here.
- mDaemonHost = LLHost(gSavedSettings.getString("VivoxVoiceHost"), gSavedSettings.getU32("VivoxVoicePort"));
- }
-
- mUpdateTimer.start();
- mUpdateTimer.setTimerExpirySec(CONNECT_THROTTLE_SECONDS);
-
- setState(stateDaemonLaunched);
-
- // Dirty the states we'll need to sync with the daemon when it comes up.
- mMuteMicDirty = true;
- mMicVolumeDirty = true;
- mSpeakerVolumeDirty = true;
- mSpeakerMuteDirty = true;
- // These only need to be set if they're not default (i.e. empty string).
- mCaptureDeviceDirty = !mCaptureDevice.empty();
- mRenderDeviceDirty = !mRenderDevice.empty();
-
- mMainSessionGroupHandle.clear();
- }
- break;
-
- //MARK: stateDaemonLaunched
- case stateDaemonLaunched:
- if(mUpdateTimer.hasExpired())
- {
- LL_DEBUGS("Voice") << "Connecting to vivox daemon:" << mDaemonHost << LL_ENDL;
-
- mUpdateTimer.setTimerExpirySec(CONNECT_THROTTLE_SECONDS);
-
- if(!mSocket)
- {
- mSocket = LLSocket::create(gAPRPoolp, LLSocket::STREAM_TCP);
- }
-
- mConnected = mSocket->blockingConnect(mDaemonHost);
- if(mConnected)
- {
- setState(stateConnecting);
- }
- else
- {
- // If the connect failed, the socket may have been put into a bad state. Delete it.
- closeSocket();
- }
- }
- break;
-
- //MARK: stateConnecting
- case stateConnecting:
- // Can't do this until we have the pump available.
- if(mPump)
- {
- // MBW -- Note to self: pumps and pipes examples in
- // indra/test/io.cpp
- // indra/test/llpipeutil.{cpp|h}
-
- // Attach the pumps and pipes
-
- LLPumpIO::chain_t readChain;
-
- readChain.push_back(LLIOPipe::ptr_t(new LLIOSocketReader(mSocket)));
- readChain.push_back(LLIOPipe::ptr_t(new LLVivoxProtocolParser()));
-
- mPump->addChain(readChain, NEVER_CHAIN_EXPIRY_SECS);
-
- setState(stateConnected);
- }
-
- break;
-
- //MARK: stateConnected
- case stateConnected:
- // Initial devices query
- getCaptureDevicesSendMessage();
- getRenderDevicesSendMessage();
-
- mLoginRetryCount = 0;
-
- setState(stateIdle);
- break;
-
- //MARK: stateIdle
- case stateIdle:
- // This is the idle state where we're connected to the daemon but haven't set up a connector yet.
- if(mTuningMode)
- {
- mTuningExitState = stateIdle;
- setState(stateMicTuningStart);
- }
- else if(!mVoiceEnabled && mIsInitialized)
- {
- // We never started up the connector. This will shut down the daemon.
- setState(stateConnectorStopped);
- }
- else if(!mAccountName.empty())
- {
- if ( mAccountPassword.empty() )
- {
- requestVoiceAccountProvision();
- }
- }
- break;
-
- //MARK: stateMicTuningStart
- case stateMicTuningStart:
- if(mUpdateTimer.hasExpired())
- {
- if(mCaptureDeviceDirty || mRenderDeviceDirty)
- {
- // These can't be changed while in tuning mode. Set them before starting.
- std::ostringstream stream;
-
- buildSetCaptureDevice(stream);
- buildSetRenderDevice(stream);
-
- if(!stream.str().empty())
- {
- writeString(stream.str());
- }
-
- // This will come around again in the same state and start the capture, after the timer expires.
- mUpdateTimer.start();
- mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
- }
- else
- {
- // duration parameter is currently unused, per Mike S.
- tuningCaptureStartSendMessage(10000);
-
- setState(stateMicTuningRunning);
- }
- }
-
- break;
-
- //MARK: stateMicTuningRunning
- case stateMicTuningRunning:
- if(!mTuningMode || mCaptureDeviceDirty || mRenderDeviceDirty)
- {
- // All of these conditions make us leave tuning mode.
- setState(stateMicTuningStop);
- }
- else
- {
- // process mic/speaker volume changes
- if(mTuningMicVolumeDirty || mTuningSpeakerVolumeDirty)
- {
- std::ostringstream stream;
-
- if(mTuningMicVolumeDirty)
- {
- LL_INFOS("Voice") << "setting tuning mic level to " << mTuningMicVolume << LL_ENDL;
- stream
- << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetMicLevel.1\">"
- << "<Level>" << mTuningMicVolume << "</Level>"
- << "</Request>\n\n\n";
- }
-
- if(mTuningSpeakerVolumeDirty)
- {
- stream
- << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetSpeakerLevel.1\">"
- << "<Level>" << mTuningSpeakerVolume << "</Level>"
- << "</Request>\n\n\n";
- }
-
- mTuningMicVolumeDirty = false;
- mTuningSpeakerVolumeDirty = false;
-
- if(!stream.str().empty())
- {
- writeString(stream.str());
- }
- }
- }
- break;
-
- //MARK: stateMicTuningStop
- case stateMicTuningStop:
- {
- // transition out of mic tuning
- tuningCaptureStopSendMessage();
-
- setState(mTuningExitState);
-
- // if we exited just to change devices, this will keep us from re-entering too fast.
- mUpdateTimer.start();
- mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
-
- }
- break;
-
- //MARK: stateCaptureBufferPaused
- case stateCaptureBufferPaused:
- if (!mCaptureBufferMode)
- {
- // Leaving capture mode.
-
- mCaptureBufferRecording = false;
- mCaptureBufferRecorded = false;
- mCaptureBufferPlaying = false;
-
- // Return to stateNoChannel to trigger reconnection to a channel.
- setState(stateNoChannel);
- }
- else if (mCaptureBufferRecording)
- {
- setState(stateCaptureBufferRecStart);
- }
- else if (mCaptureBufferPlaying)
- {
- setState(stateCaptureBufferPlayStart);
- }
- break;
-
- //MARK: stateCaptureBufferRecStart
- case stateCaptureBufferRecStart:
- captureBufferRecordStartSendMessage();
-
- // Flag that something is recorded to allow playback.
- mCaptureBufferRecorded = true;
-
- // Start the timer, recording will be stopped when it expires.
- mCaptureTimer.start();
- mCaptureTimer.setTimerExpirySec(CAPTURE_BUFFER_MAX_TIME);
-
- // Update UI, should really use a separate callback.
- notifyVoiceFontObservers();
-
- setState(stateCaptureBufferRecording);
- break;
-
- //MARK: stateCaptureBufferRecording
- case stateCaptureBufferRecording:
- if (!mCaptureBufferMode || !mCaptureBufferRecording ||
- mCaptureBufferPlaying || mCaptureTimer.hasExpired())
- {
- // Stop recording
- captureBufferRecordStopSendMessage();
- mCaptureBufferRecording = false;
-
- // Update UI, should really use a separate callback.
- notifyVoiceFontObservers();
-
- setState(stateCaptureBufferPaused);
+ LLCoros::instance().launch("LLVivoxVoiceClient::voiceControlCoro();",
+ boost::bind(&LLVivoxVoiceClient::voiceControlCoro, this));
}
break;
- //MARK: stateCaptureBufferPlayStart
- case stateCaptureBufferPlayStart:
- captureBufferPlayStartSendMessage(mPreviewVoiceFont);
-
- // Store the voice font being previewed, so that we know to restart if it changes.
- mPreviewVoiceFontLast = mPreviewVoiceFont;
-
- // Update UI, should really use a separate callback.
- notifyVoiceFontObservers();
-
- setState(stateCaptureBufferPlaying);
- break;
-
- //MARK: stateCaptureBufferPlaying
- case stateCaptureBufferPlaying:
- if (mCaptureBufferPlaying && mPreviewVoiceFont != mPreviewVoiceFontLast)
- {
- // If the preview voice font changes, restart playing with the new font.
- setState(stateCaptureBufferPlayStart);
- }
- else if (!mCaptureBufferMode || !mCaptureBufferPlaying || mCaptureBufferRecording)
- {
- // Stop playing.
- captureBufferPlayStopSendMessage();
- mCaptureBufferPlaying = false;
-
- // Update UI, should really use a separate callback.
- notifyVoiceFontObservers();
-
- setState(stateCaptureBufferPaused);
- }
- break;
-
- //MARK: stateConnectorStart
- case stateConnectorStart:
- if(!mVoiceEnabled && mIsInitialized)
- {
- // We were never logged in. This will shut down the connector.
- setState(stateLoggedOut);
- }
- else if(!mVoiceAccountServerURI.empty())
- {
- connectorCreate();
- }
- break;
-
- //MARK: stateConnectorStarting
- case stateConnectorStarting: // waiting for connector handle
- // connectorCreateResponse() will transition from here to stateConnectorStarted.
- break;
-
- //MARK: stateConnectorStarted
- case stateConnectorStarted: // connector handle received
- if(!mVoiceEnabled && mIsInitialized)
- {
- // We were never logged in. This will shut down the connector.
- setState(stateLoggedOut);
- }
- else
- {
- // The connector is started. Send a login message.
- setState(stateNeedsLogin);
- }
- break;
-
- //MARK: stateLoginRetry
- case stateLoginRetry:
- if(mLoginRetryCount == 0)
- {
- // First retry -- display a message to the user
- notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGIN_RETRY);
- }
-
- mLoginRetryCount++;
-
- if(mLoginRetryCount > MAX_LOGIN_RETRIES)
- {
- LL_WARNS("Voice") << "too many login retries, giving up." << LL_ENDL;
- setState(stateLoginFailed);
- LLSD args;
- std::stringstream errs;
- errs << mVoiceAccountServerURI << "\n:UDP: 3478, 3479, 5060, 5062, 12000-17000";
- args["HOSTID"] = errs.str();
- mTerminateDaemon = true;
- if (LLGridManager::getInstance()->isSystemGrid())
- {
- LLNotificationsUtil::add("NoVoiceConnect", args);
- }
- else
- {
- LLNotificationsUtil::add("NoVoiceConnect-GIAB", args);
- }
- }
- else
- {
- LL_INFOS("Voice") << "will retry login in " << LOGIN_RETRY_SECONDS << " seconds." << LL_ENDL;
- mUpdateTimer.start();
- mUpdateTimer.setTimerExpirySec(LOGIN_RETRY_SECONDS);
- setState(stateLoginRetryWait);
- }
- break;
-
- //MARK: stateLoginRetryWait
- case stateLoginRetryWait:
- if(mUpdateTimer.hasExpired())
- {
- setState(stateNeedsLogin);
- }
- break;
-
- //MARK: stateNeedsLogin
- case stateNeedsLogin:
- if(!mAccountPassword.empty())
- {
- setState(stateLoggingIn);
- loginSendMessage();
- }
- break;
-
- //MARK: stateLoggingIn
- case stateLoggingIn: // waiting for account handle
- // loginResponse() will transition from here to stateLoggedIn.
- break;
-
- //MARK: stateLoggedIn
- case stateLoggedIn: // account handle received
-
- notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN);
-
- if (LLVoiceClient::instance().getVoiceEffectEnabled())
- {
- // Request the set of available voice fonts.
- setState(stateVoiceFontsWait);
- refreshVoiceEffectLists(true);
- }
- else
- {
- // If voice effects are disabled, pretend we've received them and carry on.
- setState(stateVoiceFontsReceived);
- }
-
- // Set up the mute list observer if it hasn't been set up already.
- if((!sMuteListListener_listening))
- {
- LLMuteList::getInstance()->addObserver(&mutelist_listener);
- sMuteListListener_listening = true;
- }
-
- // Set the initial state of mic mute, local speaker volume, etc.
- {
- std::ostringstream stream;
-
- buildLocalAudioUpdates(stream);
-
- if(!stream.str().empty())
- {
- writeString(stream.str());
- }
- }
- break;
-
- //MARK: stateVoiceFontsWait
- case stateVoiceFontsWait: // Await voice font list
- // accountGetSessionFontsResponse() will transition from here to
- // stateVoiceFontsReceived, to ensure we have the voice font list
- // before attempting to create a session.
- break;
-
- //MARK: stateVoiceFontsReceived
- case stateVoiceFontsReceived: // Voice font list received
- // Set up the timer to check for expiring voice fonts
- mVoiceFontExpiryTimer.start();
- mVoiceFontExpiryTimer.setTimerExpirySec(VOICE_FONT_EXPIRY_INTERVAL);
-
-#if USE_SESSION_GROUPS
- // create the main session group
- setState(stateCreatingSessionGroup);
- sessionGroupCreateSendMessage();
-#else
- setState(stateNoChannel);
-#endif
- break;
+//--------------------------------------------------------------------------
+ case stateStart:
+ case stateDaemonLaunched:
+ case stateConnecting:
+ case stateConnected:
+ // moved to coroutine LLVivoxVoiceClient::startAndLaunchDaemon
+ break;
+//--------------------------------------------------------------------------
+
+ case stateIdle:
+ break;
+//--------------------------------------------------------------------------
+ case stateMicTuningStart:
+ case stateMicTuningRunning:
+ case stateMicTuningStop:
+ // moved to coroutine LLVivoxVoiceClient::performMicTuning
+ break;
+//--------------------------------------------------------------------------
+
+ // *TODO: Not working yet....
+
+ //MARK: stateCaptureBufferPaused
+ case stateCaptureBufferPaused:
+ // moved to recordingAndPlaybackMode()
+ case stateCaptureBufferRecStart:
+ case stateCaptureBufferRecording:
+ // moved to voiceRecordBuffer()
+ case stateCaptureBufferPlayStart:
+ case stateCaptureBufferPlaying:
+ // moved to voicePlaybackBuffer()
+ break;
+//-------------------------------------------------------------------------
+ case stateConnectorStart:
+ case stateConnectorStarting:
+ case stateConnectorStarted:
+ // moved to establishVoiceConnection
+ break;
+
+//-------------------------------------------------------------------------
+ case stateLoginRetry:
+ case stateLoginRetryWait:
+ case stateNeedsLogin:
+ case stateLoggingIn:
+ case stateLoggedIn:
+ // moved to loginToVivox
+ break;
+
+ case stateVoiceFontsWait: // Await voice font list
+ case stateVoiceFontsReceived: // Voice font list received
+ // moved to retrieveVoiceFonts
+ break;
//MARK: stateCreatingSessionGroup
case stateCreatingSessionGroup:
@@ -1213,272 +719,25 @@ void LLVivoxVoiceClient::stateMachine()
}
break;
- //MARK: stateRetrievingParcelVoiceInfo
- case stateRetrievingParcelVoiceInfo:
- // wait until parcel voice info is received.
- if(mSessionTerminateRequested || (!mVoiceEnabled && mIsInitialized))
- {
- // if a terminate request has been received,
- // bail and go to the stateSessionTerminated
- // state. If the cap request is still pending,
- // the responder will check to see if we've moved
- // to a new session and won't change any state.
- setState(stateSessionTerminated);
- }
- break;
-
-
- //MARK: stateNoChannel
- case stateNoChannel:
- LL_DEBUGS("Voice") << "State No Channel" << LL_ENDL;
- mSpatialJoiningNum = 0;
-
-
- if(mSessionTerminateRequested || (!mVoiceEnabled && mIsInitialized))
- {
- // TODO: Question: Is this the right way out of this state?
- setState(stateSessionTerminated);
- }
- else if(mTuningMode)
- {
- mTuningExitState = stateNoChannel;
- setState(stateMicTuningStart);
- }
- else if(mCaptureBufferMode)
- {
- setState(stateCaptureBufferPaused);
- }
- else if(checkParcelChanged() || (mNextAudioSession == NULL))
- {
- // the parcel is changed, or we have no pending audio sessions,
- // so try to request the parcel voice info
- // if we have the cap, we move to the appropriate state
- if(requestParcelVoiceInfo())
- {
- setState(stateRetrievingParcelVoiceInfo);
- }
- }
- else if(sessionNeedsRelog(mNextAudioSession))
- {
- requestRelog();
- setState(stateSessionTerminated);
- }
- else if(mNextAudioSession)
- {
- sessionState *oldSession = mAudioSession;
-
- mAudioSession = mNextAudioSession;
- mAudioSessionChanged = true;
- if(!mAudioSession->mReconnect)
- {
- mNextAudioSession = NULL;
- }
-
- // The old session may now need to be deleted.
- reapSession(oldSession);
-
- if(!mAudioSession->mHandle.empty())
- {
- // Connect to a session by session handle
-
- sessionMediaConnectSendMessage(mAudioSession);
- }
- else
- {
- // Connect to a session by URI
- sessionCreateSendMessage(mAudioSession, true, false);
- }
-
- notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINING);
- setState(stateJoiningSession);
- }
- break;
-
- //MARK: stateJoiningSession
- case stateJoiningSession: // waiting for session handle
-
- // If this is true we have problem with connection to voice server (EXT-4313).
- // See descriptions of mSpatialJoiningNum and MAX_NORMAL_JOINING_SPATIAL_NUM.
- if(mSpatialJoiningNum == MAX_NORMAL_JOINING_SPATIAL_NUM)
- {
- // Notify observers to let them know there is problem with voice
- notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED);
- LL_WARNS() << "There seems to be problem with connection to voice server. Disabling voice chat abilities." << LL_ENDL;
- }
-
- // Increase mSpatialJoiningNum only for spatial sessions- it's normal to reach this case for
- // example for p2p many times while waiting for response, so it can't be used to detect errors
- if(mAudioSession && mAudioSession->mIsSpatial)
- {
-
- mSpatialJoiningNum++;
- }
-
- // joinedAudioSession() will transition from here to stateSessionJoined.
- if(!mVoiceEnabled && mIsInitialized)
- {
- // User bailed out during connect -- jump straight to teardown.
- setState(stateSessionTerminated);
- }
- else if(mSessionTerminateRequested)
- {
- if(mAudioSession && !mAudioSession->mHandle.empty())
- {
- // Only allow direct exits from this state in p2p calls (for cancelling an invite).
- // Terminating a half-connected session on other types of calls seems to break something in the vivox gateway.
- if(mAudioSession->mIsP2P)
- {
- sessionMediaDisconnectSendMessage(mAudioSession);
- setState(stateSessionTerminated);
- }
- }
- }
- break;
-
- //MARK: stateSessionJoined
- case stateSessionJoined: // session handle received
-
-
- mSpatialJoiningNum = 0;
- // It appears that I need to wait for BOTH the SessionGroup.AddSession response and the SessionStateChangeEvent with state 4
- // before continuing from this state. They can happen in either order, and if I don't wait for both, things can get stuck.
- // For now, the SessionGroup.AddSession response handler sets mSessionHandle and the SessionStateChangeEvent handler transitions to stateSessionJoined.
- // This is a cheap way to make sure both have happened before proceeding.
- if(mAudioSession && mAudioSession->mVoiceEnabled)
- {
- // Dirty state that may need to be sync'ed with the daemon.
- mMuteMicDirty = true;
- mSpeakerVolumeDirty = true;
- mSpatialCoordsDirty = true;
-
- setState(stateRunning);
-
- // Start the throttle timer
- mUpdateTimer.start();
- mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
-
- // Events that need to happen when a session is joined could go here.
- // Maybe send initial spatial data?
- notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED);
-
- }
- else if(!mVoiceEnabled && mIsInitialized)
- {
- // User bailed out during connect -- jump straight to teardown.
- setState(stateSessionTerminated);
- }
- else if(mSessionTerminateRequested)
- {
- // Only allow direct exits from this state in p2p calls (for cancelling an invite).
- // Terminating a half-connected session on other types of calls seems to break something in the vivox gateway.
- if(mAudioSession && mAudioSession->mIsP2P)
- {
- sessionMediaDisconnectSendMessage(mAudioSession);
- setState(stateSessionTerminated);
- }
- }
- break;
-
- //MARK: stateRunning
- case stateRunning: // steady state
- // Disabling voice or disconnect requested.
- if((!mVoiceEnabled && mIsInitialized) || mSessionTerminateRequested)
- {
- leaveAudioSession();
- }
- else
- {
-
- if(!inSpatialChannel())
- {
- // When in a non-spatial channel, never send positional updates.
- mSpatialCoordsDirty = false;
- }
- else
- {
- if(checkParcelChanged())
- {
- // if the parcel has changed, attempted to request the
- // cap for the parcel voice info. If we can't request it
- // then we don't have the cap URL so we do nothing and will
- // recheck next time around
- if(requestParcelVoiceInfo())
- {
- // we did get the cap, and we made the request,
- // so go wait for the response.
- setState(stateRetrievingParcelVoiceInfo);
- }
- }
- // Do the calculation that enforces the listener<->speaker tether (and also updates the real camera position)
- enforceTether();
-
- }
-
- // Do notifications for expiring Voice Fonts.
- if (mVoiceFontExpiryTimer.hasExpired())
- {
- expireVoiceFonts();
- mVoiceFontExpiryTimer.setTimerExpirySec(VOICE_FONT_EXPIRY_INTERVAL);
- }
-
- // Send an update only if the ptt or mute state has changed (which shouldn't be able to happen that often
- // -- the user can only click so fast) or every 10hz, whichever is sooner.
- // Sending for every volume update causes an excessive flood of messages whenever a volume slider is dragged.
- if((mAudioSession && mAudioSession->mMuteDirty) || mMuteMicDirty || mUpdateTimer.hasExpired())
- {
- mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
- sendPositionalUpdate();
- }
- mIsInitialized = true;
- }
- break;
-
- //MARK: stateLeavingSession
+ case stateRetrievingParcelVoiceInfo:
+ break;
+
+ case stateNoChannel:
+ // moved to waitForChannel
+ break;
+ case stateJoiningSession: // waiting for session handle
+ case stateSessionJoined: // session handle received
+ // moved to addAndJoinSession()
+ break;
+ //MARK: stateRunning
+ case stateRunning: // steady state
+ //MARK: stateRunning
+ // moved to runSession
+ break;
case stateLeavingSession: // waiting for terminate session response
- // The handler for the Session.Terminate response will transition from here to stateSessionTerminated.
- break;
-
- //MARK: stateSessionTerminated
- case stateSessionTerminated:
-
- // Must do this first, since it uses mAudioSession.
- notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL);
-
- if(mAudioSession)
- {
- sessionState *oldSession = mAudioSession;
-
- mAudioSession = NULL;
- // We just notified status observers about this change. Don't do it again.
- mAudioSessionChanged = false;
-
- // The old session may now need to be deleted.
- reapSession(oldSession);
- }
- else
- {
- LL_WARNS("Voice") << "stateSessionTerminated with NULL mAudioSession" << LL_ENDL;
- }
-
- // Always reset the terminate request flag when we get here.
- mSessionTerminateRequested = false;
-
- if((mVoiceEnabled || !mIsInitialized) && !mRelogRequested && !LLApp::isExiting())
- {
- // Just leaving a channel, go back to stateNoChannel (the "logged in but have no channel" state).
- setState(stateNoChannel);
- }
- else
- {
- // Shutting down voice, continue with disconnecting.
- logout();
-
- // The state machine will take it from here
- mRelogRequested = false;
- }
-
- break;
-
+ case stateSessionTerminated:
+ // moved to terminateAudioSession
+ break;
//MARK: stateLoggingOut
case stateLoggingOut: // waiting for logout response
// The handler for the AccountLoginStateChangeEvent will transition from here to stateLoggedOut.
@@ -1503,7 +762,8 @@ void LLVivoxVoiceClient::stateMachine()
}
break;
- //MARK: stateConnectorStopping
+//-------------------------------------------------------------------------
+ //MARK: stateConnectorStopping
case stateConnectorStopping: // waiting for connector stop
// The handler for the Connector.InitiateShutdown response will transition from here to stateConnectorStopped.
mShutdownComplete = true;
@@ -1514,7 +774,8 @@ void LLVivoxVoiceClient::stateMachine()
setState(stateDisableCleanup);
break;
- //MARK: stateConnectorFailed
+//-------------------------------------------------------------------------
+ //MARK: stateConnectorFailed
case stateConnectorFailed:
setState(stateConnectorFailedWaiting);
break;
@@ -1525,6 +786,7 @@ void LLVivoxVoiceClient::stateMachine()
setState(stateDisableCleanup);
}
break;
+//-------------------------------------------------------------------------
//MARK: stateLoginFailed
case stateLoginFailed:
@@ -1537,33 +799,9 @@ void LLVivoxVoiceClient::stateMachine()
setState(stateDisableCleanup);
}
break;
-
- //MARK: stateJoinSessionFailed
- case stateJoinSessionFailed:
- // Transition to error state. Send out any notifications here.
- if(mAudioSession)
- {
- LL_WARNS("Voice") << "stateJoinSessionFailed: (" << mAudioSession->mErrorStatusCode << "): " << mAudioSession->mErrorStatusString << LL_ENDL;
- }
- else
- {
- LL_WARNS("Voice") << "stateJoinSessionFailed with no current session" << LL_ENDL;
- }
-
- notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN);
- setState(stateJoinSessionFailedWaiting);
- break;
-
- //MARK: stateJoinSessionFailedWaiting
- case stateJoinSessionFailedWaiting:
- // Joining a channel failed, either due to a failed channel name -> sip url lookup or an error from the join message.
- // Region crossings may leave this state and try the join again.
- if(mSessionTerminateRequested)
- {
- setState(stateSessionTerminated);
- }
- break;
-
+ case stateJoinSessionFailed:
+ case stateJoinSessionFailedWaiting:
+ break;
//MARK: stateJail
case stateJail:
// We have given up. Do nothing.
@@ -1571,19 +809,1159 @@ void LLVivoxVoiceClient::stateMachine()
}
- if (mAudioSessionChanged)
- {
- mAudioSessionChanged = false;
- notifyParticipantObservers();
- notifyVoiceFontObservers();
- }
- else if (mAudioSession && mAudioSession->mParticipantsChanged)
- {
- mAudioSession->mParticipantsChanged = false;
- notifyParticipantObservers();
- }
+// if (mAudioSessionChanged)
+// {
+// mAudioSessionChanged = false;
+// notifyParticipantObservers();
+// notifyVoiceFontObservers();
+// }
+// else if (mAudioSession && mAudioSession->mParticipantsChanged)
+// {
+// mAudioSession->mParticipantsChanged = false;
+// notifyParticipantObservers();
+// }
+}
+
+//=========================================================================
+// the following are methods to support the coroutine implementation of the
+// voice connection and processing. They should only be called in the context
+// of a coroutine.
+//
+// calls to setState() in these are historical and used because some of the other
+// query routines will ask what state the state machine is in.
+//
+void LLVivoxVoiceClient::voiceControlCoro()
+{
+ LLCoros::set_consuming(true);
+ startAndConnectSession();
+
+ if (mTuningMode)
+ {
+ performMicTuning(stateIdle);
+ }
+ else if (mVoiceEnabled)
+ {
+ waitForChannel();
+ }
+
+}
+
+
+bool LLVivoxVoiceClient::startAndConnectSession()
+{
+ if (!startAndLaunchDaemon())
+ {
+ setState(stateJail);
+ return false;
+ }
+
+ if (!provisionVoiceAccount())
+ {
+ giveUp();
+ return false;
+ }
+
+ if (!establishVoiceConnection())
+ {
+ if (getState() != stateConnectorFailed)
+ {
+ setState(stateLoggedOut);
+ }
+ giveUp();
+ return false;
+ }
+
+ setState(stateIdle);
+
+ return true;
+}
+
+bool LLVivoxVoiceClient::startAndLaunchDaemon()
+{
+ //---------------------------------------------------------------------
+ setState(stateStart);
+
+ if (gSavedSettings.getBOOL("CmdLineDisableVoice"))
+ {
+ // Voice is locked out, we must not launch the vivox daemon.
+ setState(stateJail);
+ return false;
+ }
+
+ if (!isGatewayRunning() && gSavedSettings.getBOOL("EnableVoiceChat"))
+ {
+#ifndef VIVOXDAEMON_REMOTEHOST
+ // Launch the voice daemon
+
+ // *FIX:Mani - Using the executable dir instead
+ // of mAppRODataDir, the working directory from which the app
+ // is launched.
+ //std::string exe_path = gDirUtilp->getAppRODataDir();
+ std::string exe_path = gDirUtilp->getExecutableDir();
+ exe_path += gDirUtilp->getDirDelimiter();
+#if LL_WINDOWS
+ exe_path += "SLVoice.exe";
+#elif LL_DARWIN
+ exe_path += "../Resources/SLVoice";
+#else
+ exe_path += "SLVoice";
+#endif
+ // See if the vivox executable exists
+ llstat s;
+ if (!LLFile::stat(exe_path, &s))
+ {
+ // vivox executable exists. Build the command line and launch the daemon.
+ LLProcess::Params params;
+ params.executable = exe_path;
+
+ std::string loglevel = gSavedSettings.getString("VivoxDebugLevel");
+ std::string shutdown_timeout = gSavedSettings.getString("VivoxShutdownTimeout");
+ if (loglevel.empty())
+ {
+ loglevel = "-1"; // turn logging off completely, was 0 for error level logging.
+ }
+
+ params.args.add("-ll");
+ params.args.add(loglevel);
+
+ std::string log_folder = gSavedSettings.getString("VivoxLogDirectory");
+
+ if (log_folder.empty())
+ {
+ log_folder = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "");
+ }
+
+ params.args.add("-lf");
+ params.args.add(log_folder);
+
+ if (!shutdown_timeout.empty())
+ {
+ params.args.add("-st");
+ params.args.add(shutdown_timeout);
+ }
+ params.cwd = gDirUtilp->getAppRODataDir();
+ sGatewayPtr = LLProcess::create(params);
+
+ mDaemonHost = LLHost(gSavedSettings.getString("VivoxVoiceHost").c_str(), gSavedSettings.getU32("VivoxVoicePort"));
+ }
+ else
+ {
+ LL_INFOS("Voice") << exe_path << " not found." << LL_ENDL;
+ return false;
+ }
+#else
+ // SLIM SDK: port changed from 44124 to 44125.
+ // We can connect to a client gateway running on another host. This is useful for testing.
+ // To do this, launch the gateway on a nearby host like this:
+ // vivox-gw.exe -p tcp -i 0.0.0.0:44125
+ // and put that host's IP address here.
+ mDaemonHost = LLHost(gSavedSettings.getString("VivoxVoiceHost"), gSavedSettings.getU32("VivoxVoicePort"));
+#endif
+
+ // Dirty the states we'll need to sync with the daemon when it comes up.
+ mMuteMicDirty = true;
+ mMicVolumeDirty = true;
+ mSpeakerVolumeDirty = true;
+ mSpeakerMuteDirty = true;
+ // These only need to be set if they're not default (i.e. empty string).
+ mCaptureDeviceDirty = !mCaptureDevice.empty();
+ mRenderDeviceDirty = !mRenderDevice.empty();
+
+ mMainSessionGroupHandle.clear();
+ }
+
+ //---------------------------------------------------------------------
+ llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS);
+ setState(stateDaemonLaunched);
+
+ LL_DEBUGS("Voice") << "Connecting to vivox daemon:" << mDaemonHost << LL_ENDL;
+
+ int connectAttempt = 0;
+
+ while (!mConnected)
+ {
+ ++connectAttempt;
+ LL_DEBUGS("Voice") << "Connecting to vivox daemon:" << mDaemonHost << " (#" << connectAttempt << ")" << LL_ENDL;
+ closeSocket();
+ if (!mSocket)
+ {
+ mSocket = LLSocket::create(gAPRPoolp, LLSocket::STREAM_TCP);
+ }
+
+ mConnected = mSocket->blockingConnect(mDaemonHost);
+ if (!mConnected)
+ llcoro::suspendUntilTimeout(CONNECT_THROTTLE_SECONDS);
+ }
+
+ //---------------------------------------------------------------------
+ llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS);
+ setState(stateConnecting);
+
+
+ while (!mPump)
+ { // Can't do this until we have the pump available.
+ llcoro::suspend();
+ }
+
+
+ // MBW -- Note to self: pumps and pipes examples in
+ // indra/test/io.cpp
+ // indra/test/llpipeutil.{cpp|h}
+
+ // Attach the pumps and pipes
+
+ LLPumpIO::chain_t readChain;
+
+ readChain.push_back(LLIOPipe::ptr_t(new LLIOSocketReader(mSocket)));
+ readChain.push_back(LLIOPipe::ptr_t(new LLVivoxProtocolParser()));
+
+ mPump->addChain(readChain, NEVER_CHAIN_EXPIRY_SECS);
+
+ //---------------------------------------------------------------------
+ llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS);
+ setState(stateConnected);
+
+ // Initial devices query
+ getCaptureDevicesSendMessage();
+ getRenderDevicesSendMessage();
+
+ mLoginRetryCount = 0;
+
+ return true;
+}
+
+bool LLVivoxVoiceClient::provisionVoiceAccount()
+{
+
+ while (!gAgent.getRegion())
+ {
+ // *TODO* Set up a call back on agent that sends a message to a pump we can use to wake up.
+ llcoro::suspend();
+ }
+
+ LLViewerRegion *region = gAgent.getRegion();
+
+ while (!region->capabilitiesReceived())
+ {
+ // *TODO* Pump a message for wake up.
+ llcoro::suspend();
+ }
+
+ std::string url = region->getCapability("ProvisionVoiceAccountRequest");
+
+ LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
+ LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
+ httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("voiceAccountProvision", httpPolicy));
+ LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
+ LLCore::HttpOptions::ptr_t httpOpts = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions);
+ int retryCount(0);
+
+ LLSD result;
+
+ do
+ {
+ result = httpAdapter->postAndSuspend(httpRequest, url, LLSD(), httpOpts);
+
+ LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
+ LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
+
+ if (status == LLCore::HttpStatus(404))
+ {
+ if (++retryCount > 5)
+ {
+ LL_WARNS("Voice") << "Could not access voice provision cap after 5 attempts." << LL_ENDL;
+ return false;
+ }
+ LL_WARNS("Voice") << "Provision CAP 404. Retrying in 1.0" << LL_ENDL;
+ llcoro::suspendUntilTimeout(1.0);
+
+ continue;
+ }
+ else if (!status)
+ {
+ LL_WARNS("Voice") << "Unable to provision voice account." << LL_ENDL;
+ return false;
+ }
+ break;
+ } while (true);
+
+ std::string voiceSipUriHostname;
+ std::string voiceAccountServerUri;
+ std::string voiceUserName = result["username"].asString();
+ std::string voicePassword = result["password"].asString();
+
+ //LL_DEBUGS("Voice") << "ProvisionVoiceAccountRequest response:" << dumpResponse() << LL_ENDL;
+
+ if (result.has("voice_sip_uri_hostname"))
+ voiceSipUriHostname = result["voice_sip_uri_hostname"].asString();
+
+ // this key is actually misnamed -- it will be an entire URI, not just a hostname.
+ if (result.has("voice_account_server_name"))
+ voiceAccountServerUri = result["voice_account_server_name"].asString();
+
+ setLoginInfo(voiceUserName, voicePassword, voiceSipUriHostname, voiceAccountServerUri);
+
+ return true;
+}
+
+bool LLVivoxVoiceClient::establishVoiceConnection()
+{
+ LLEventPump &voiceConnectPump = LLEventPumps::instance().obtain("vivoxClientPump");
+
+ if (!mVoiceEnabled && mIsInitialized)
+ return false;
+
+ setState(stateConnectorStart);
+
+ connectorCreate();
+
+ setState(stateConnectorStarting);
+
+ LLSD result;
+ do
+ {
+ result = llcoro::suspendUntilEventOn(voiceConnectPump);
+ LL_INFOS("Voice") << "event=" << ll_pretty_print_sd(result) << LL_ENDL;
+ }
+ while (!result.has("connector"));
+
+ if (!result["connector"])
+ {
+
+ setState(stateConnectorFailed);
+ return false;
+ }
+
+ setState(stateConnectorStarted);
+ if (!mVoiceEnabled && mIsInitialized)
+ return false;
+
+ return true;
+}
+
+bool LLVivoxVoiceClient::loginToVivox()
+{
+ int loginRetryCount(0);
+ LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump");
+
+ bool response_ok(false);
+ bool account_login(false);
+ bool send_login(true);
+
+ do
+ {
+ setState(stateLoggingIn);
+ if (send_login)
+ loginSendMessage();
+
+ send_login = false;
+
+ LLSD result;
+
+ result = llcoro::suspendUntilEventOn(voicePump);
+ LL_INFOS("Voice") << "event=" << ll_pretty_print_sd(result) << LL_ENDL;
+
+ if (result.has("login"))
+ {
+ std::string loginresp = result["login"];
+
+ if (loginresp == "retry")
+ {
+ if (!loginRetryCount)
+ { // on first retry notify user
+ notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGIN_RETRY);
+ }
+
+ if ((++loginRetryCount > MAX_LOGIN_RETRIES) || (!result["login_retry"]))
+ {
+ LL_WARNS("Voice") << "too many login retries, giving up." << LL_ENDL;
+ LLSD args;
+ std::stringstream errs;
+ errs << mVoiceAccountServerURI << "\n:UDP: 3478, 3479, 5060, 5062, 12000-17000";
+ args["HOSTID"] = errs.str();
+ mTerminateDaemon = true;
+ if (LLGridManager::getInstance()->isSystemGrid())
+ {
+ LLNotificationsUtil::add("NoVoiceConnect", args);
+ }
+ else
+ {
+ LLNotificationsUtil::add("NoVoiceConnect-GIAB", args);
+ }
+
+ setState(stateLoginFailed);
+ return false;
+ }
+
+ response_ok = false;
+ account_login = false;
+ send_login = true;
+
+ LL_INFOS("Voice") << "will retry login in " << LOGIN_RETRY_SECONDS << " seconds." << LL_ENDL;
+ setState(stateLoginRetryWait);
+ llcoro::suspendUntilTimeout(LOGIN_RETRY_SECONDS);
+ }
+ else if (loginresp == "failed")
+ {
+ setState(stateLoginFailed);
+ return false;
+ }
+ else if (loginresp == "response_ok")
+ {
+ response_ok = true;
+ }
+ else if (loginresp == "account_login")
+ {
+ account_login = true;
+ }
+ }
+
+ } while (!response_ok || !account_login);
+
+ setState(stateLoggedIn);
+ notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN);
+
+ // Set up the mute list observer if it hasn't been set up already.
+ if ((!sMuteListListener_listening))
+ {
+ LLMuteList::getInstance()->addObserver(&mutelist_listener);
+ sMuteListListener_listening = true;
+ }
+
+ // Set the initial state of mic mute, local speaker volume, etc.
+ sendLocalAudioUpdates();
+
+ return true;
+}
+
+bool LLVivoxVoiceClient::retrieveVoiceFonts()
+{
+ LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump");
+
+ // Request the set of available voice fonts.
+ setState(stateVoiceFontsWait);
+ refreshVoiceEffectLists(true);
+
+ LLSD result;
+ do
+ {
+ result = llcoro::suspendUntilEventOn(voicePump);
+
+ LL_INFOS("Voice") << "event=" << ll_pretty_print_sd(result) << LL_ENDL;
+ if (result.has("voice_fonts"))
+ break;
+ } while (true);
+
+
+ mVoiceFontExpiryTimer.start();
+ mVoiceFontExpiryTimer.setTimerExpirySec(VOICE_FONT_EXPIRY_INTERVAL);
+
+ return result["voice_fonts"].asBoolean();
+}
+
+bool LLVivoxVoiceClient::requestParcelVoiceInfo()
+{
+ setState(stateRetrievingParcelVoiceInfo);
+
+ LL_INFOS("Voice") << "Requesting voice info for Parcel" << LL_ENDL;
+
+ LLViewerRegion * region = gAgent.getRegion();
+ if (region == NULL || !region->capabilitiesReceived())
+ {
+ LL_DEBUGS("Voice") << "ParcelVoiceInfoRequest capability not yet available, deferring" << LL_ENDL;
+ return false;
+ }
+
+ // grab the cap.
+ std::string url = gAgent.getRegion()->getCapability("ParcelVoiceInfoRequest");
+ if (url.empty())
+ {
+ // Region dosn't have the cap. Stop probing.
+ LL_DEBUGS("Voice") << "ParcelVoiceInfoRequest capability not available in this region" << LL_ENDL;
+ setState(stateDisableCleanup);
+ return false;
+ }
+
+ // update the parcel
+ checkParcelChanged(true);
+
+ LL_DEBUGS("Voice") << "sending ParcelVoiceInfoRequest (" << mCurrentRegionName << ", " << mCurrentParcelLocalID << ")" << LL_ENDL;
+
+ LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
+ LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
+ httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("parcelVoiceInfoRequest", httpPolicy));
+ LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
+
+ LLSD result = httpAdapter->postAndSuspend(httpRequest, url, LLSD());
+
+ LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
+ LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
+
+ if (mSessionTerminateRequested || (!mVoiceEnabled && mIsInitialized))
+ {
+ // if a terminate request has been received,
+ // bail and go to the stateSessionTerminated
+ // state. If the cap request is still pending,
+ // the responder will check to see if we've moved
+ // to a new session and won't change any state.
+ terminateAudioSession(true);
+ return false;
+ }
+
+ if ((!status) || (mSessionTerminateRequested || (!mVoiceEnabled && mIsInitialized)))
+ {
+ if (mSessionTerminateRequested || (!mVoiceEnabled && mIsInitialized))
+ {
+ LL_WARNS("Voice") << "Session terminated." << LL_ENDL;
+ }
+
+ LL_WARNS("Voice") << "No voice on parcel" << LL_ENDL;
+ sessionTerminate();
+ return false;
+ }
+
+ std::string uri;
+ std::string credentials;
+
+ if (result.has("voice_credentials"))
+ {
+ LLSD voice_credentials = result["voice_credentials"];
+ if (voice_credentials.has("channel_uri"))
+ {
+ uri = voice_credentials["channel_uri"].asString();
+ }
+ if (voice_credentials.has("channel_credentials"))
+ {
+ credentials =
+ voice_credentials["channel_credentials"].asString();
+ }
+ }
+
+ if (!uri.empty())
+ LL_INFOS("Voice") << "Voice URI is " << uri << LL_ENDL;
+
+ // set the spatial channel. If no voice credentials or uri are
+ // available, then we simply drop out of voice spatially.
+ return !setSpatialChannel(uri, credentials);
+}
+
+bool LLVivoxVoiceClient::addAndJoinSession(sessionState *nextSession)
+{
+ LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump");
+ mIsJoiningSession = true;
+
+ sessionState *oldSession = mAudioSession;
+
+ LL_INFOS("Voice") << "Adding or joining voice session " << nextSession->mHandle << LL_ENDL;
+
+ mAudioSession = nextSession;
+ mAudioSessionChanged = true;
+ if (!mAudioSession->mReconnect)
+ {
+ mNextAudioSession = NULL;
+ }
+
+ // The old session may now need to be deleted.
+ reapSession(oldSession);
+
+ if (!mAudioSession->mHandle.empty())
+ {
+ // Connect to a session by session handle
+
+ sessionMediaConnectSendMessage(mAudioSession);
+ }
+ else
+ {
+ // Connect to a session by URI
+ sessionCreateSendMessage(mAudioSession, true, false);
+ }
+
+ notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINING);
+
+ llcoro::suspend();
+
+ LLSD result;
+
+ if (mSpatialJoiningNum == MAX_NORMAL_JOINING_SPATIAL_NUM)
+ {
+ // Notify observers to let them know there is problem with voice
+ notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED);
+ LL_WARNS() << "There seems to be problem with connection to voice server. Disabling voice chat abilities." << LL_ENDL;
+ }
+
+ // Increase mSpatialJoiningNum only for spatial sessions- it's normal to reach this case for
+ // example for p2p many times while waiting for response, so it can't be used to detect errors
+ if (mAudioSession && mAudioSession->mIsSpatial)
+ {
+ mSpatialJoiningNum++;
+ }
+
+ // joinedAudioSession() will transition from here to stateSessionJoined.
+ if (!mVoiceEnabled && mIsInitialized)
+ {
+ mIsJoiningSession = false;
+ // User bailed out during connect -- jump straight to teardown.
+ terminateAudioSession(true);
+ return false;
+ }
+ else if (mSessionTerminateRequested)
+ {
+ if (mAudioSession && !mAudioSession->mHandle.empty())
+ {
+ // Only allow direct exits from this state in p2p calls (for cancelling an invite).
+ // Terminating a half-connected session on other types of calls seems to break something in the vivox gateway.
+ if (mAudioSession->mIsP2P)
+ {
+ terminateAudioSession(true);
+ mIsJoiningSession = false;
+ return false;
+ }
+ }
+ }
+
+ bool added(true);
+ bool joined(false);
+
+ // It appears that I need to wait for BOTH the SessionGroup.AddSession response and the SessionStateChangeEvent with state 4
+ // before continuing from this state. They can happen in either order, and if I don't wait for both, things can get stuck.
+ // For now, the SessionGroup.AddSession response handler sets mSessionHandle and the SessionStateChangeEvent handler transitions to stateSessionJoined.
+ // This is a cheap way to make sure both have happened before proceeding.
+ do
+ {
+ result = llcoro::suspendUntilEventOn(voicePump);
+
+ LL_INFOS("Voice") << "event=" << ll_pretty_print_sd(result) << LL_ENDL;
+ if (result.has("session"))
+ {
+ if (result.has("handle"))
+ {
+ if (result["handle"] != mAudioSession->mHandle)
+ {
+ LL_WARNS("Voice") << "Message for session handle \"" << result["handle"] << "\" while waiting for \"" << mAudioSession->mHandle << "\"." << LL_ENDL;
+ continue;
+ }
+ }
+
+ std::string message = result["session"].asString();
+ if ((message == "added") || (message == "created"))
+ added = true;
+ else if (message == "joined")
+ joined = true;
+ else if (message == "failed")
+ {
+ mIsJoiningSession = false;
+ setState(stateJoinSessionFailed);
+ return false;
+ }
+ }
+ } while (!added || !joined);
+
+ mIsJoiningSession = false;
+
+ if (mSpatialJoiningNum > 100)
+ {
+ LL_WARNS("Voice") << "There seems to be problem with connecting to a voice channel. Frames to join were " << mSpatialJoiningNum << LL_ENDL;
+ }
+
+ mSpatialJoiningNum = 0;
+ // Dirty state that may need to be sync'ed with the daemon.
+ mMuteMicDirty = true;
+ mSpeakerVolumeDirty = true;
+ mSpatialCoordsDirty = true;
+
+ // Events that need to happen when a session is joined could go here.
+ // Maybe send initial spatial data?
+ notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED);
+
+ return true;
+}
+
+bool LLVivoxVoiceClient::terminateAudioSession(bool wait)
+{
+
+ if (mAudioSession)
+ {
+ LL_INFOS("Voice") << "Terminating current voice session " << mAudioSession->mHandle << LL_ENDL;
+
+ if (!mAudioSession->mHandle.empty())
+ {
+
+#if RECORD_EVERYTHING
+ // HACK: for testing only
+ // Save looped recording
+ std::string savepath("/tmp/vivoxrecording");
+ {
+ time_t now = time(NULL);
+ const size_t BUF_SIZE = 64;
+ char time_str[BUF_SIZE]; /* Flawfinder: ignore */
+
+ strftime(time_str, BUF_SIZE, "%Y-%m-%dT%H:%M:%SZ", gmtime(&now));
+ savepath += time_str;
+ }
+ recordingLoopSave(savepath);
+#endif
+
+ sessionMediaDisconnectSendMessage(mAudioSession);
+
+ if (wait)
+ {
+ LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump");
+ LLSD result;
+ do
+ {
+ result = llcoro::suspendUntilEventOn(voicePump);
+
+ LL_INFOS("Voice") << "event=" << ll_pretty_print_sd(result) << LL_ENDL;
+ if (result.has("session"))
+ {
+ if (result.has("handle"))
+ {
+ if (result["handle"] != mAudioSession->mHandle)
+ {
+ LL_WARNS("Voice") << "Message for session handle \"" << result["handle"] << "\" while waiting for \"" << mAudioSession->mHandle << "\"." << LL_ENDL;
+ continue;
+ }
+ }
+
+ std::string message = result["session"].asString();
+ if (message == "removed")
+ break;
+ }
+ } while (true);
+
+ }
+ }
+ else
+ {
+ LL_WARNS("Voice") << "called with no session handle" << LL_ENDL;
+ }
+
+ sessionState *oldSession = mAudioSession;
+
+ mAudioSession = NULL;
+ // We just notified status observers about this change. Don't do it again.
+ mAudioSessionChanged = false;
+
+ // The old session may now need to be deleted.
+ reapSession(oldSession);
+
+ }
+ else
+ {
+ LL_WARNS("Voice") << "stateSessionTerminated with NULL mAudioSession" << LL_ENDL;
+ }
+
+ notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL);
+ setState(stateSessionTerminated);
+
+ // Always reset the terminate request flag when we get here.
+ // Some slower PCs have a race condition where they can switch to an incoming P2P call faster than the state machine leaves
+ // the region chat.
+ mSessionTerminateRequested = false;
+
+ if ((mVoiceEnabled || !mIsInitialized) && !mRelogRequested && !LLApp::isExiting())
+ {
+ // Just leaving a channel, go back to stateNoChannel (the "logged in but have no channel" state).
+ return true;
+ }
+
+ return false;
+}
+
+bool LLVivoxVoiceClient::waitForChannel()
+{
+ LL_INFOS("Voice") << "Waiting for channel" << LL_ENDL;
+
+ do
+ {
+ if (!loginToVivox())
+ {
+ setState(stateLoginFailed);
+ return false;
+ }
+
+ if (LLVoiceClient::instance().getVoiceEffectEnabled())
+ {
+ retrieveVoiceFonts();
+
+ // Request the set of available voice fonts.
+ refreshVoiceEffectLists(true);
+ }
+
+#if USE_SESSION_GROUPS
+ // create the main session group
+ setState(stateCreatingSessionGroup);
+ sessionGroupCreateSendMessage();
+#endif
+
+ do
+ {
+ LL_INFOS("Voice") << "Waiting for channel" << LL_ENDL;
+ setState(stateNoChannel);
+ llcoro::suspend();
+
+ if (mTuningMode)
+ {
+ performMicTuning(stateNoChannel);
+ }
+ else if (mCaptureBufferMode)
+ {
+ recordingAndPlaybackMode();
+ }
+ else if (checkParcelChanged() || (mNextAudioSession == NULL))
+ {
+ // the parcel is changed, or we have no pending audio sessions,
+ // so try to request the parcel voice info
+ // if we have the cap, we move to the appropriate state
+ requestParcelVoiceInfo();
+ }
+ else if (sessionNeedsRelog(mNextAudioSession))
+ {
+ requestRelog();
+ terminateAudioSession(true);
+ break;
+ }
+ else if (mNextAudioSession)
+ {
+ runSession(mNextAudioSession);
+ }
+
+ if (!mNextAudioSession)
+ llcoro::suspendUntilTimeout(1.0);
+ } while (mVoiceEnabled && !mRelogRequested);
+
+ } while (mVoiceEnabled && mRelogRequested);
+
+ return true;
+}
+
+bool LLVivoxVoiceClient::runSession(sessionState *session)
+{
+ LL_INFOS("Voice") << "running new voice session " << session->mHandle << LL_ENDL;
+ bool doTerminate(true);
+
+ if (!addAndJoinSession(session))
+ {
+ notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN);
+
+ if (mSessionTerminateRequested)
+ terminateAudioSession(true);
+
+ return false;
+ }
+
+ notifyParticipantObservers();
+ notifyVoiceFontObservers();
+
+ LLSD timeoutEvent = LLSD::emptyMap();
+ timeoutEvent["timeout"] = LLSD::Boolean(true);
+
+ LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump");
+ LLEventTimeout timeout(voicePump);
+ mIsInChannel = true;
+
+ while (mVoiceEnabled && !mSessionTerminateRequested && !mTuningMode)
+ {
+ setState(stateRunning);
+
+ if (!inSpatialChannel())
+ {
+ // When in a non-spatial channel, never send positional updates.
+ mSpatialCoordsDirty = false;
+ }
+ else
+ {
+ updatePosition();
+
+ if (checkParcelChanged())
+ {
+ // if the parcel has changed, attempted to request the
+ // cap for the parcel voice info. If we can't request it
+ // then we don't have the cap URL so we do nothing and will
+ // recheck next time around
+ if (requestParcelVoiceInfo())
+ { // The parcel voice URI has changed.. break out and reconnect.
+ break;
+ }
+ }
+ // Do the calculation that enforces the listener<->speaker tether (and also updates the real camera position)
+ enforceTether();
+ }
+
+ // Do notifications for expiring Voice Fonts.
+ if (mVoiceFontExpiryTimer.hasExpired())
+ {
+ expireVoiceFonts();
+ mVoiceFontExpiryTimer.setTimerExpirySec(VOICE_FONT_EXPIRY_INTERVAL);
+ }
+
+ // Send an update only if the ptt or mute state has changed (which shouldn't be able to happen that often
+ // -- the user can only click so fast) or every 10hz, whichever is sooner.
+ // Sending for every volume update causes an excessive flood of messages whenever a volume slider is dragged.
+ if ((mAudioSession && mAudioSession->mMuteDirty) || mMuteMicDirty)
+ {
+ sendPositionalUpdate();
+ }
+ mIsInitialized = true;
+ timeout.eventAfter(UPDATE_THROTTLE_SECONDS, timeoutEvent);
+ LLSD result = llcoro::suspendUntilEventOn(timeout);
+ if (!result.has("timeout")) // logging the timeout event spamms the log
+ LL_INFOS("Voice") << "event=" << ll_pretty_print_sd(result) << LL_ENDL;
+ if (result.has("session"))
+ {
+ if (result.has("handle"))
+ {
+ if (result["handle"] != mAudioSession->mHandle)
+ {
+ LL_WARNS("Voice") << "Message for session handle \"" << result["handle"] << "\" while waiting for \"" << mAudioSession->mHandle << "\"." << LL_ENDL;
+ continue;
+ }
+ }
+
+ std::string message = result["session"];
+
+ if (message == "removed")
+ {
+ doTerminate = false;
+ break;
+ }
+ }
+
+ if (mAudioSession && mAudioSession->mParticipantsChanged)
+ {
+ mAudioSession->mParticipantsChanged = false;
+ notifyParticipantObservers();
+ }
+ }
+
+ mIsInChannel = false;
+ if (doTerminate)
+ terminateAudioSession(true);
+ return true;
+}
+
+void LLVivoxVoiceClient::recordingAndPlaybackMode()
+{
+ LL_INFOS("Voice") << "In voice capture/playback mode." << LL_ENDL;
+ LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump");
+
+ while (true)
+ {
+ setState(stateCaptureBufferPaused);
+
+ LLSD command;
+ do
+ {
+ command = llcoro::suspendUntilEventOn(voicePump);
+ LL_INFOS("Voice") << "event=" << ll_pretty_print_sd(command) << LL_ENDL;
+ } while (!command.has("recplay"));
+
+ if (command["recplay"].asString() == "quit")
+ {
+ mCaptureBufferMode = false;
+ break;
+ }
+ else if (command["recplay"].asString() == "record")
+ {
+ voiceRecordBuffer();
+ }
+ else if (command["recplay"].asString() == "playback")
+ {
+ voicePlaybackBuffer();
+ }
+ }
+
+ LL_INFOS("Voice") << "Leaving capture/playback mode." << LL_ENDL;
+ mCaptureBufferRecording = false;
+ mCaptureBufferRecorded = false;
+ mCaptureBufferPlaying = false;
+
+ return;
}
+int LLVivoxVoiceClient::voiceRecordBuffer()
+{
+ setState(stateCaptureBufferRecStart);
+
+ LLSD timeoutResult;
+ timeoutResult["recplay"] = LLSD::String("stop");
+
+ LL_INFOS("Voice") << "Recording voice buffer" << LL_ENDL;
+
+ LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump");
+ LLEventTimeout timeout(voicePump);
+ timeout.eventAfter(CAPTURE_BUFFER_MAX_TIME, timeoutResult);
+ LLSD result;
+
+ setState(stateCaptureBufferRecording);
+ captureBufferRecordStartSendMessage();
+
+ notifyVoiceFontObservers();
+ do
+ {
+ result = llcoro::suspendUntilEventOn(voicePump);
+ LL_INFOS("Voice") << "event=" << ll_pretty_print_sd(result) << LL_ENDL;
+ } while (!result.has("recplay"));
+
+ mCaptureBufferRecorded = true;
+
+ captureBufferRecordStopSendMessage();
+ mCaptureBufferRecording = false;
+
+ // Update UI, should really use a separate callback.
+ notifyVoiceFontObservers();
+
+ return true;
+ /*TODO expand return to move directly into play*/
+}
+
+int LLVivoxVoiceClient::voicePlaybackBuffer()
+{
+ setState(stateCaptureBufferPlayStart);
+
+ LLSD timeoutResult;
+ timeoutResult["recplay"] = LLSD::String("stop");
+
+ LL_INFOS("Voice") << "Playing voice buffer" << LL_ENDL;
+
+ LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump");
+ LLEventTimeout timeout(voicePump);
+ timeout.eventAfter(CAPTURE_BUFFER_MAX_TIME, timeoutResult);
+ LLSD result;
+
+ setState(stateCaptureBufferPlaying);
+ do
+ {
+ captureBufferPlayStartSendMessage(mPreviewVoiceFont);
+
+ // Store the voice font being previewed, so that we know to restart if it changes.
+ mPreviewVoiceFontLast = mPreviewVoiceFont;
+
+ do
+ {
+ // Update UI, should really use a separate callback.
+ notifyVoiceFontObservers();
+
+ result = llcoro::suspendUntilEventOn(voicePump);
+ LL_INFOS("Voice") << "event=" << ll_pretty_print_sd(result) << LL_ENDL;
+ } while (!result.has("recplay"));
+
+ if (result["recplay"] == "playback")
+ continue; // restart playback... May be a font change.
+
+ break;
+ } while (true);
+
+ // Stop playing.
+ captureBufferPlayStopSendMessage();
+ mCaptureBufferPlaying = false;
+
+ // Update UI, should really use a separate callback.
+ notifyVoiceFontObservers();
+
+ return true;
+}
+
+
+bool LLVivoxVoiceClient::performMicTuning(LLVivoxVoiceClient::state exitState)
+{
+ LL_INFOS("Voice") << "Entering voice tuning mode." << LL_ENDL;
+
+ mIsInTuningMode = true;
+ llcoro::suspend();
+
+ while (mTuningMode)
+ {
+
+ if (mCaptureDeviceDirty || mRenderDeviceDirty)
+ {
+ // These can't be changed while in tuning mode. Set them before starting.
+ std::ostringstream stream;
+
+ buildSetCaptureDevice(stream);
+ buildSetRenderDevice(stream);
+
+ if (!stream.str().empty())
+ {
+ writeString(stream.str());
+ }
+
+ llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS);
+ }
+
+ // loop mic back to render device.
+ //setMuteMic(0); // make sure the mic is not muted
+ std::ostringstream stream;
+
+ stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.MuteLocalMic.1\">"
+ << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
+ << "<Value>false</Value>"
+ << "</Request>\n\n\n";
+
+ // Dirty the mute mic state so that it will get reset when we finishing previewing
+ mMuteMicDirty = true;
+ mTuningSpeakerVolumeDirty = true;
+
+ writeString(stream.str());
+ tuningCaptureStartSendMessage(1); // 1-loop, zero, don't loop
+
+ //---------------------------------------------------------------------
+ llcoro::suspend();
+
+ while (mTuningMode && !mCaptureDeviceDirty && !mRenderDeviceDirty)
+ {
+ // process mic/speaker volume changes
+ if (mTuningMicVolumeDirty || mTuningSpeakerVolumeDirty)
+ {
+ std::ostringstream stream;
+
+ if (mTuningMicVolumeDirty)
+ {
+ LL_INFOS("Voice") << "setting tuning mic level to " << mTuningMicVolume << LL_ENDL;
+ stream
+ << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetMicLevel.1\">"
+ << "<Level>" << mTuningMicVolume << "</Level>"
+ << "</Request>\n\n\n";
+ }
+
+ if (mTuningSpeakerVolumeDirty)
+ {
+ stream
+ << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetSpeakerLevel.1\">"
+ << "<Level>" << mTuningSpeakerVolume << "</Level>"
+ << "</Request>\n\n\n";
+ }
+
+ mTuningMicVolumeDirty = false;
+ mTuningSpeakerVolumeDirty = false;
+
+ if (!stream.str().empty())
+ {
+ writeString(stream.str());
+ }
+ }
+ llcoro::suspend();
+ }
+
+ //---------------------------------------------------------------------
+
+ // transition out of mic tuning
+ tuningCaptureStopSendMessage();
+ if (mCaptureDeviceDirty || mRenderDeviceDirty)
+ {
+ llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS);
+ }
+ }
+
+ mIsInTuningMode = false;
+
+ //---------------------------------------------------------------------
+ setState(exitState);
+ return true;
+}
+
+//=========================================================================
+
void LLVivoxVoiceClient::closeSocket(void)
{
mSocket.reset();
@@ -1604,7 +1982,8 @@ void LLVivoxVoiceClient::loginSendMessage()
<< "<AccountName>" << mAccountName << "</AccountName>"
<< "<AccountPassword>" << mAccountPassword << "</AccountPassword>"
<< "<AudioSessionAnswerMode>VerifyAnswer</AudioSessionAnswerMode>"
- << "<EnableBuddiesAndPresence>false</EnableBuddiesAndPresence>"
+ << "<EnableBuddiesAndPresence>false</EnableBuddiesAndPresence>"
+ << "<EnablePresencePersistence>0</EnablePresencePersistence>"
<< "<BuddyManagementMode>Application</BuddyManagementMode>"
<< "<ParticipantPropertyFrequency>5</ParticipantPropertyFrequency>"
<< (autoPostCrashDumps?"<AutopostCrashDumps>true</AutopostCrashDumps>":"")
@@ -1802,9 +2181,11 @@ void LLVivoxVoiceClient::leaveAudioSession()
// Skip the join failed transition state so we don't send out error notifications.
setState(stateJoinSessionFailedWaiting);
break;
+#if 0
case stateJoiningSession:
case stateSessionJoined:
case stateRunning:
+ case stateSessionTerminated:
if(!mAudioSession->mHandle.empty())
{
@@ -1835,10 +2216,13 @@ void LLVivoxVoiceClient::leaveAudioSession()
case stateJoinSessionFailed:
case stateJoinSessionFailedWaiting:
setState(stateSessionTerminated);
+ break;
+ case stateLeavingSession: // managed to get back to this case statement before the media gets disconnected.
break;
+#endif
default:
- LL_WARNS("Voice") << "called from unknown state" << LL_ENDL;
+ LL_WARNS("Voice") << "called from unknown state " << getState() << LL_ENDL;
break;
}
}
@@ -1852,7 +2236,10 @@ void LLVivoxVoiceClient::leaveAudioSession()
void LLVivoxVoiceClient::sessionTerminateSendMessage(sessionState *session)
{
std::ostringstream stream;
-
+
+ sessionGroupTerminateSendMessage(session);
+ return;
+ /*
LL_DEBUGS("Voice") << "Sending Session.Terminate with handle " << session->mHandle << LL_ENDL;
stream
<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.Terminate.1\">"
@@ -1860,6 +2247,7 @@ void LLVivoxVoiceClient::sessionTerminateSendMessage(sessionState *session)
<< "</Request>\n\n\n";
writeString(stream.str());
+ */
}
void LLVivoxVoiceClient::sessionGroupTerminateSendMessage(sessionState *session)
@@ -1878,7 +2266,9 @@ void LLVivoxVoiceClient::sessionGroupTerminateSendMessage(sessionState *session)
void LLVivoxVoiceClient::sessionMediaDisconnectSendMessage(sessionState *session)
{
std::ostringstream stream;
-
+ sessionGroupTerminateSendMessage(session);
+ return;
+ /*
LL_DEBUGS("Voice") << "Sending Session.MediaDisconnect with handle " << session->mHandle << LL_ENDL;
stream
<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.MediaDisconnect.1\">"
@@ -1888,22 +2278,10 @@ void LLVivoxVoiceClient::sessionMediaDisconnectSendMessage(sessionState *session
<< "</Request>\n\n\n";
writeString(stream.str());
+ */
}
-void LLVivoxVoiceClient::sessionTextDisconnectSendMessage(sessionState *session)
-{
- std::ostringstream stream;
-
- LL_DEBUGS("Voice") << "Sending Session.TextDisconnect with handle " << session->mHandle << LL_ENDL;
- stream
- << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.TextDisconnect.1\">"
- << "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>"
- << "<SessionHandle>" << session->mHandle << "</SessionHandle>"
- << "</Request>\n\n\n";
-
- writeString(stream.str());
-}
void LLVivoxVoiceClient::getCaptureDevicesSendMessage()
{
@@ -1962,6 +2340,10 @@ void LLVivoxVoiceClient::setCaptureDevice(const std::string& name)
}
}
}
+void LLVivoxVoiceClient::setDevicesListUpdated(bool state)
+{
+ mDevicesListUpdated = state;
+}
void LLVivoxVoiceClient::clearRenderDevices()
{
@@ -2003,9 +2385,9 @@ void LLVivoxVoiceClient::setRenderDevice(const std::string& name)
void LLVivoxVoiceClient::tuningStart()
{
- mTuningMode = true;
- LL_DEBUGS("Voice") << "Starting tuning" << LL_ENDL;
- if(getState() >= stateNoChannel)
+ LL_DEBUGS("Voice") << "Starting tuning" << LL_ENDL;
+ mTuningMode = true;
+ if (mIsInChannel)
{
LL_DEBUGS("Voice") << "no channel" << LL_ENDL;
sessionTerminate();
@@ -2019,16 +2401,7 @@ void LLVivoxVoiceClient::tuningStop()
bool LLVivoxVoiceClient::inTuningMode()
{
- bool result = false;
- switch(getState())
- {
- case stateMicTuningRunning:
- result = true;
- break;
- default:
- break;
- }
- return result;
+ return mIsInTuningMode;
}
void LLVivoxVoiceClient::tuningRenderStartSendMessage(const std::string& name, bool loop)
@@ -2055,14 +2428,15 @@ void LLVivoxVoiceClient::tuningRenderStopSendMessage()
writeString(stream.str());
}
-void LLVivoxVoiceClient::tuningCaptureStartSendMessage(int duration)
+void LLVivoxVoiceClient::tuningCaptureStartSendMessage(int loop)
{
LL_DEBUGS("Voice") << "sending CaptureAudioStart" << LL_ENDL;
std::ostringstream stream;
stream
<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.CaptureAudioStart.1\">"
- << "<Duration>" << duration << "</Duration>"
+ << "<Duration>-1</Duration>"
+ << "<LoopToRenderDevice>" << loop << "</LoopToRenderDevice>"
<< "</Request>\n\n\n";
writeString(stream.str());
@@ -2121,6 +2495,16 @@ bool LLVivoxVoiceClient::deviceSettingsAvailable()
return result;
}
+bool LLVivoxVoiceClient::deviceSettingsUpdated()
+{
+ if (mDevicesListUpdated)
+ {
+ // a hot swap event or a polling of the audio devices has been parsed since the last redraw of the input and output device panel.
+ mDevicesListUpdated = !mDevicesListUpdated; // toggle the setting
+ return true;
+ }
+ return false;
+}
void LLVivoxVoiceClient::refreshDeviceLists(bool clearCurrentList)
{
@@ -2284,6 +2668,7 @@ void LLVivoxVoiceClient::sendPositionalUpdate(void)
{
std::ostringstream stream;
+
if(mSpatialCoordsDirty)
{
LLVector3 l, u, a, vel;
@@ -2423,6 +2808,7 @@ void LLVivoxVoiceClient::sendPositionalUpdate(void)
stream << "</ListenerPosition>";
+ stream << "<ReqDispositionType>1</ReqDispositionType>"; //do not generate responses for update requests
stream << "</Request>\n\n\n";
}
@@ -2491,7 +2877,7 @@ void LLVivoxVoiceClient::sendPositionalUpdate(void)
}
}
- buildLocalAudioUpdates(stream);
+ //sendLocalAudioUpdates(); obsolete, used to send volume setting on position updates
if(!stream.str().empty())
{
@@ -2531,68 +2917,73 @@ void LLVivoxVoiceClient::buildSetRenderDevice(std::ostringstream &stream)
}
}
-void LLVivoxVoiceClient::buildLocalAudioUpdates(std::ostringstream &stream)
+void LLVivoxVoiceClient::sendLocalAudioUpdates()
{
- buildSetCaptureDevice(stream);
+ // Check all of the dirty states and then send messages to those needing to be changed.
+ // Tuningmode hands its own mute settings.
- buildSetRenderDevice(stream);
+ std::ostringstream stream;
- if(mMuteMicDirty)
+ if (mMuteMicDirty && !mTuningMode)
{
mMuteMicDirty = false;
// Send a local mute command.
-
- LL_DEBUGS("Voice") << "Sending MuteLocalMic command with parameter " << (mMuteMic?"true":"false") << LL_ENDL;
+
+ LL_DEBUGS("Voice") << "Sending MuteLocalMic command with parameter " << (mMuteMic ? "true" : "false") << LL_ENDL;
stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.MuteLocalMic.1\">"
<< "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
- << "<Value>" << (mMuteMic?"true":"false") << "</Value>"
+ << "<Value>" << (mMuteMic ? "true" : "false") << "</Value>"
<< "</Request>\n\n\n";
-
+
}
- if(mSpeakerMuteDirty)
+ if (mSpeakerMuteDirty && !mTuningMode)
{
- const char *muteval = ((mSpeakerVolume <= scale_speaker_volume(0))?"true":"false");
+ const char *muteval = ((mSpeakerVolume <= scale_speaker_volume(0)) ? "true" : "false");
mSpeakerMuteDirty = false;
- LL_INFOS("Voice") << "Setting speaker mute to " << muteval << LL_ENDL;
-
+ LL_INFOS("Voice") << "Setting speaker mute to " << muteval << LL_ENDL;
+
stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.MuteLocalSpeaker.1\">"
<< "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
<< "<Value>" << muteval << "</Value>"
- << "</Request>\n\n\n";
-
+ << "</Request>\n\n\n";
+
}
-
- if(mSpeakerVolumeDirty)
+
+ if (mSpeakerVolumeDirty)
{
mSpeakerVolumeDirty = false;
- LL_INFOS("Voice") << "Setting speaker volume to " << mSpeakerVolume << LL_ENDL;
+ LL_INFOS("Voice") << "Setting speaker volume to " << mSpeakerVolume << LL_ENDL;
stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.SetLocalSpeakerVolume.1\">"
<< "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
<< "<Value>" << mSpeakerVolume << "</Value>"
<< "</Request>\n\n\n";
-
+
}
-
- if(mMicVolumeDirty)
+
+ if (mMicVolumeDirty)
{
mMicVolumeDirty = false;
- LL_INFOS("Voice") << "Setting mic volume to " << mMicVolume << LL_ENDL;
+ LL_INFOS("Voice") << "Setting mic volume to " << mMicVolume << LL_ENDL;
stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.SetLocalMicVolume.1\">"
<< "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
<< "<Value>" << mMicVolume << "</Value>"
- << "</Request>\n\n\n";
+ << "</Request>\n\n\n";
}
-
+
+ if (!stream.str().empty())
+ {
+ writeString(stream.str());
+ }
}
/////////////////////////////
@@ -2600,10 +2991,11 @@ void LLVivoxVoiceClient::buildLocalAudioUpdates(std::ostringstream &stream)
void LLVivoxVoiceClient::connectorCreateResponse(int statusCode, std::string &statusString, std::string &connectorHandle, std::string &versionID)
{
+ LLSD result = LLSD::emptyMap();
+
if(statusCode != 0)
{
LL_WARNS("Voice") << "Connector.Create response failure: " << statusString << LL_ENDL;
- setState(stateConnectorFailed);
LLSD args;
std::stringstream errs;
errs << mVoiceAccountServerURI << "\n:UDP: 3478, 3479, 5060, 5062, 12000-17000";
@@ -2617,6 +3009,8 @@ void LLVivoxVoiceClient::connectorCreateResponse(int statusCode, std::string &st
{
LLNotificationsUtil::add("NoVoiceConnect-GIAB", args);
}
+
+ result["connector"] = LLSD::Boolean(false);
}
else
{
@@ -2625,16 +3019,18 @@ void LLVivoxVoiceClient::connectorCreateResponse(int statusCode, std::string &st
mVoiceVersion.serverVersion = versionID;
mConnectorHandle = connectorHandle;
mTerminateDaemon = false;
- if(getState() == stateConnectorStarting)
- {
- setState(stateConnectorStarted);
- }
+
+ result["connector"] = LLSD::Boolean(true);
}
+
+ LLEventPumps::instance().post("vivoxClientPump", result);
}
void LLVivoxVoiceClient::loginResponse(int statusCode, std::string &statusString, std::string &accountHandle, int numberOfAliases)
{
- LL_DEBUGS("Voice") << "Account.Login response (" << statusCode << "): " << statusString << LL_ENDL;
+ LLSD result = LLSD::emptyMap();
+
+ LL_DEBUGS("Voice") << "Account.Login response (" << statusCode << "): " << statusString << LL_ENDL;
// Status code of 20200 means "bad password". We may want to special-case that at some point.
@@ -2642,24 +3038,28 @@ void LLVivoxVoiceClient::loginResponse(int statusCode, std::string &statusString
{
// Login failure which is probably caused by the delay after a user's password being updated.
LL_INFOS("Voice") << "Account.Login response failure (" << statusCode << "): " << statusString << LL_ENDL;
- setState(stateLoginRetry);
+ result["login"] = LLSD::String("retry");
}
else if(statusCode != 0)
{
LL_WARNS("Voice") << "Account.Login response failure (" << statusCode << "): " << statusString << LL_ENDL;
- setState(stateLoginFailed);
+ result["login"] = LLSD::String("failed");
}
else
{
// Login succeeded, move forward.
mAccountHandle = accountHandle;
mNumberOfAliases = numberOfAliases;
+ result["login"] = LLSD::String("response_ok");
// This needs to wait until the AccountLoginStateChangeEvent is received.
// if(getState() == stateLoggingIn)
// {
// setState(stateLoggedIn);
// }
}
+
+ LLEventPumps::instance().post("vivoxClientPump", result);
+
}
void LLVivoxVoiceClient::sessionCreateResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle)
@@ -2680,8 +3080,13 @@ void LLVivoxVoiceClient::sessionCreateResponse(std::string &requestId, int statu
session->mErrorStatusString = statusString;
if(session == mAudioSession)
{
- setState(stateJoinSessionFailed);
- }
+ LLSD vivoxevent = LLSD::emptyMap();
+
+ vivoxevent["handle"] = LLSD::String(sessionHandle);
+ vivoxevent["session"] = LLSD::String("failed");
+
+ LLEventPumps::instance().post("vivoxClientPump", vivoxevent);
+ }
else
{
reapSession(session);
@@ -2695,6 +3100,12 @@ void LLVivoxVoiceClient::sessionCreateResponse(std::string &requestId, int statu
{
setSessionHandle(session, sessionHandle);
}
+ LLSD vivoxevent = LLSD::emptyMap();
+
+ vivoxevent["handle"] = LLSD::String(sessionHandle);
+ vivoxevent["session"] = LLSD::String("created");
+
+ LLEventPumps::instance().post("vivoxClientPump", vivoxevent);
}
}
@@ -2716,7 +3127,12 @@ void LLVivoxVoiceClient::sessionGroupAddSessionResponse(std::string &requestId,
session->mErrorStatusString = statusString;
if(session == mAudioSession)
{
- setState(stateJoinSessionFailed);
+ LLSD vivoxevent = LLSD::emptyMap();
+
+ vivoxevent["handle"] = LLSD::String(sessionHandle);
+ vivoxevent["session"] = LLSD::String("failed");
+
+ LLEventPumps::instance().post("vivoxClientPump", vivoxevent);
}
else
{
@@ -2731,22 +3147,42 @@ void LLVivoxVoiceClient::sessionGroupAddSessionResponse(std::string &requestId,
{
setSessionHandle(session, sessionHandle);
}
+
+ LLSD vivoxevent = LLSD::emptyMap();
+
+ vivoxevent["handle"] = LLSD::String(sessionHandle);
+ vivoxevent["session"] = LLSD::String("added");
+
+ LLEventPumps::instance().post("vivoxClientPump", vivoxevent);
+
}
}
void LLVivoxVoiceClient::sessionConnectResponse(std::string &requestId, int statusCode, std::string &statusString)
{
sessionState *session = findSession(requestId);
- if(statusCode != 0)
+ // 1026 is session already has media, somehow mediaconnect was called twice on the same session.
+ // set the session info to reflect that the user is already connected.
+ if (statusCode == 1026)
+ {
+ session->mVoiceEnabled = true;
+ session->mMediaConnectInProgress = false;
+ session->mMediaStreamState = streamStateConnected;
+ //session->mTextStreamState = streamStateConnected;
+ session->mErrorStatusCode = 0;
+ }
+ else if (statusCode != 0)
{
LL_WARNS("Voice") << "Session.Connect response failure (" << statusCode << "): " << statusString << LL_ENDL;
- if(session)
+ if (session)
{
session->mMediaConnectInProgress = false;
- session->mErrorStatusCode = statusCode;
+ session->mErrorStatusCode = statusCode;
session->mErrorStatusString = statusString;
- if(session == mAudioSession)
+ if (session == mAudioSession)
+ {
setState(stateJoinSessionFailed);
+ }
}
}
else
@@ -2853,7 +3289,7 @@ void LLVivoxVoiceClient::sessionGroupAddedEvent(std::string &sessionGroupHandle)
{
LL_DEBUGS("Voice") << "handle " << sessionGroupHandle << LL_ENDL;
-#if USE_SESSION_GROUPS
+#if USE_SESSION_GROUPS
if(mMainSessionGroupHandle.empty())
{
// This is the first (i.e. "main") session group. Save its handle.
@@ -2881,11 +3317,15 @@ void LLVivoxVoiceClient::joinedAudioSession(sessionState *session)
}
// This is the session we're joining.
- if(getState() == stateJoiningSession)
+ if(mIsJoiningSession)
{
- setState(stateSessionJoined);
-
- // SLIM SDK: we don't always receive a participant state change for ourselves when joining a channel now.
+ LLSD vivoxevent = LLSD::emptyMap();
+
+ vivoxevent["handle"] = LLSD::String(session->mHandle);
+ vivoxevent["session"] = LLSD::String("joined");
+
+ LLEventPumps::instance().post("vivoxClientPump", vivoxevent);
+
// Add the current user as a participant here.
participantState *participant = session->addParticipant(sipURIFromName(mAccountName));
if(participant)
@@ -2941,26 +3381,25 @@ void LLVivoxVoiceClient::sessionRemovedEvent(
// Reset the media state (we now have no info)
session->mMediaStreamState = streamStateUnknown;
- session->mTextStreamState = streamStateUnknown;
+ //session->mTextStreamState = streamStateUnknown;
// Conditionally delete the session
reapSession(session);
}
else
{
- LL_WARNS("Voice") << "unknown session " << sessionHandle << " removed" << LL_ENDL;
+ // Already reaped this session.
+ LL_DEBUGS("Voice") << "unknown session " << sessionHandle << " removed" << LL_ENDL;
}
+
}
void LLVivoxVoiceClient::reapSession(sessionState *session)
{
if(session)
{
- if(!session->mHandle.empty())
- {
- LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (non-null session handle)" << LL_ENDL;
- }
- else if(session->mCreateInProgress)
+
+ if(session->mCreateInProgress)
{
LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (create in progress)" << LL_ENDL;
}
@@ -2978,7 +3417,6 @@ void LLVivoxVoiceClient::reapSession(sessionState *session)
}
else
{
- // TODO: Question: Should we check for queued text messages here?
// We don't have a reason to keep tracking this session, so just delete it.
LL_DEBUGS("Voice") << "deleting session " << session->mSIPURI << LL_ENDL;
deleteSession(session);
@@ -3027,6 +3465,17 @@ bool LLVivoxVoiceClient::sessionNeedsRelog(sessionState *session)
void LLVivoxVoiceClient::leftAudioSession(
sessionState *session)
{
+#if 1
+ if (mAudioSession == session)
+ {
+ LLSD vivoxevent = LLSD::emptyMap();
+
+ vivoxevent["handle"] = LLSD::String(session->mHandle);
+ vivoxevent["session"] = LLSD::String("removed");
+
+ LLEventPumps::instance().post("vivoxClientPump", vivoxevent);
+ }
+#else
if(mAudioSession == session)
{
switch(getState())
@@ -3053,6 +3502,10 @@ void LLVivoxVoiceClient::leftAudioSession(
break;
}
}
+ else if ( mAudioSession == NULL && (getState() == stateSessionTerminated) ){
+ setState(stateNoChannel);
+ }
+#endif
}
void LLVivoxVoiceClient::accountLoginStateChangeEvent(
@@ -3061,6 +3514,10 @@ void LLVivoxVoiceClient::accountLoginStateChangeEvent(
std::string &statusString,
int state)
{
+#if 1
+ LLSD levent = LLSD::emptyMap();
+
+#endif
/*
According to Mike S., status codes for this event are:
login_state_logged_out=0,
@@ -3075,34 +3532,37 @@ void LLVivoxVoiceClient::accountLoginStateChangeEvent(
switch(state)
{
case 1:
- if(getState() == stateLoggingIn)
- {
- setState(stateLoggedIn);
- }
- break;
+ levent["login"] = LLSD::String("account_login");
+ LLEventPumps::instance().post("vivoxClientPump", levent);
+ break;
case 3:
- // The user is in the process of logging out.
- setState(stateLoggingOut);
- break;
+ levent["login"] = LLSD::String("account_loggingOut");
+
+ LLEventPumps::instance().post("vivoxClientPump", levent);
+ break;
case 0:
- // The user has been logged out.
- setState(stateLoggedOut);
- break;
+ levent["login"] = LLSD::String("account_logout");
+
+ LLEventPumps::instance().post("vivoxClientPump", levent);
+ break;
default:
//Used to be a commented out warning
LL_DEBUGS("Voice") << "unknown state: " << state << LL_ENDL;
- break;
+ break;
}
}
void LLVivoxVoiceClient::mediaCompletionEvent(std::string &sessionGroupHandle, std::string &mediaCompletionType)
{
+ LLSD result;
+
if (mediaCompletionType == "AuxBufferAudioCapture")
{
mCaptureBufferRecording = false;
+ result["recplay"] = "end";
}
else if (mediaCompletionType == "AuxBufferAudioRender")
{
@@ -3110,12 +3570,17 @@ void LLVivoxVoiceClient::mediaCompletionEvent(std::string &sessionGroupHandle, s
if (--mPlayRequestCount <= 0)
{
mCaptureBufferPlaying = false;
- }
+ result["recplay"] = "end";
+// result["recplay"] = "done";
+ }
}
else
{
LL_DEBUGS("Voice") << "Unknown MediaCompletionType: " << mediaCompletionType << LL_ENDL;
}
+
+ if (!result.isUndefined())
+ LLEventPumps::instance().post("vivoxClientPump", result);
}
void LLVivoxVoiceClient::mediaStreamUpdatedEvent(
@@ -3152,8 +3617,9 @@ void LLVivoxVoiceClient::mediaStreamUpdatedEvent(
switch(state)
{
- case streamStateIdle:
- // Standard "left audio session"
+ case streamStateDisconnecting:
+ case streamStateIdle:
+ // Standard "left audio session", Vivox state 'disconnected'
session->mVoiceEnabled = false;
session->mMediaConnectInProgress = false;
leftAudioSession(session);
@@ -3163,6 +3629,7 @@ void LLVivoxVoiceClient::mediaStreamUpdatedEvent(
session->mVoiceEnabled = true;
session->mMediaConnectInProgress = false;
joinedAudioSession(session);
+ case streamStateConnecting: // do nothing, but prevents a warning getting into the logs.
break;
case streamStateRinging:
@@ -3193,54 +3660,8 @@ void LLVivoxVoiceClient::mediaStreamUpdatedEvent(
}
else
{
- LL_WARNS("Voice") << "session " << sessionHandle << "not found"<< LL_ENDL;
- }
-}
-
-void LLVivoxVoiceClient::textStreamUpdatedEvent(
- std::string &sessionHandle,
- std::string &sessionGroupHandle,
- bool enabled,
- int state,
- bool incoming)
-{
- sessionState *session = findSession(sessionHandle);
-
- if(session)
- {
- // Save the state for later use
- session->mTextStreamState = state;
-
- // We know about this session
- switch(state)
- {
- case 0: // We see this when the text stream closes
- LL_DEBUGS("Voice") << "stream closed" << LL_ENDL;
- break;
-
- case 1: // We see this on an incoming call from the Connector
- // Try to send any text messages queued for this session.
- sendQueuedTextMessages(session);
-
- // Send the text chat invite to the GUI layer
- // TODO: Question: Should we correlate with the mute list here?
- session->mTextInvitePending = true;
- if(session->mName.empty())
- {
- lookupName(session->mCallerID);
- }
- else
- {
- // Act like we just finished resolving the name
- avatarNameResolved(session->mCallerID, session->mName);
- }
- break;
-
- default:
- LL_WARNS("Voice") << "unknown state " << state << LL_ENDL;
- break;
-
- }
+ // session disconnectintg and disconnected events arriving after we have already left the session.
+ LL_DEBUGS("Voice") << "session " << sessionHandle << " not found"<< LL_ENDL;
}
}
@@ -3314,6 +3735,7 @@ void LLVivoxVoiceClient::participantRemovedEvent(
}
else
{
+ // a late arriving event on a session we have already left.
LL_DEBUGS("Voice") << "unknown session " << sessionHandle << LL_ENDL;
}
}
@@ -3398,7 +3820,7 @@ void LLVivoxVoiceClient::participantUpdatedEvent(
}
else
{
- LL_INFOS("Voice") << "unknown session " << sessionHandle << LL_ENDL;
+ LL_DEBUGS("Voice") << "unknown session " << sessionHandle << LL_ENDL;
}
}
@@ -3880,109 +4302,7 @@ bool LLVivoxVoiceClient::checkParcelChanged(bool update)
return false;
}
-bool LLVivoxVoiceClient::parcelVoiceInfoReceived(state requesting_state)
-{
- // pop back to the state we were in when the parcel changed and we managed to
- // do the request.
- if(getState() == stateRetrievingParcelVoiceInfo)
- {
- setState(requesting_state);
- return true;
- }
- else
- {
- // we've dropped out of stateRetrievingParcelVoiceInfo
- // before we received the cap result, due to a terminate
- // or transition to a non-voice channel. Don't switch channels.
- return false;
- }
-}
-
-
-bool LLVivoxVoiceClient::requestParcelVoiceInfo()
-{
- LLViewerRegion * region = gAgent.getRegion();
- if (region == NULL || !region->capabilitiesReceived())
- {
- // we don't have the cap yet, so return false so the caller can try again later.
-
- LL_DEBUGS("Voice") << "ParcelVoiceInfoRequest capability not yet available, deferring" << LL_ENDL;
- return false;
- }
-
- // grab the cap.
- std::string url = gAgent.getRegion()->getCapability("ParcelVoiceInfoRequest");
- if (url.empty())
- {
- // Region dosn't have the cap. Stop probing.
- LL_DEBUGS("Voice") << "ParcelVoiceInfoRequest capability not available in this region" << LL_ENDL;
- setState(stateDisableCleanup);
- return false;
- }
- else
- {
- // if we've already retrieved the cap from the region, go ahead and make the request,
- // and return true so we can go into the state that waits for the response.
- checkParcelChanged(true);
- LLSD data;
- LL_DEBUGS("Voice") << "sending ParcelVoiceInfoRequest (" << mCurrentRegionName << ", " << mCurrentParcelLocalID << ")" << LL_ENDL;
-
- LLCoros::instance().launch("LLVivoxVoiceClient::parcelVoiceInfoRequestCoro",
- boost::bind(&LLVivoxVoiceClient::parcelVoiceInfoRequestCoro, this, url));
- return true;
- }
-}
-
-void LLVivoxVoiceClient::parcelVoiceInfoRequestCoro(std::string url)
-{
- LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
- LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
- httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("parcelVoiceInfoRequest", httpPolicy));
- LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
- state requestingState = getState();
-
- LLSD result = httpAdapter->postAndSuspend(httpRequest, url, LLSD());
-
- LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
- LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
-
- if (!status)
- {
- LL_WARNS("Voice") << "No voice on parcel" << LL_ENDL;
- sessionTerminate();
- return;
- }
-
- std::string uri;
- std::string credentials;
-
- if (result.has("voice_credentials"))
- {
- LLSD voice_credentials = result["voice_credentials"];
- if (voice_credentials.has("channel_uri"))
- {
- uri = voice_credentials["channel_uri"].asString();
- }
- if (voice_credentials.has("channel_credentials"))
- {
- credentials =
- voice_credentials["channel_credentials"].asString();
- }
- }
-
- if (!uri.empty())
- LL_INFOS("Voice") << "Voice URI is " << uri << LL_ENDL;
-
- // set the spatial channel. If no voice credentials or uri are
- // available, then we simply drop out of voice spatially.
- if (parcelVoiceInfoReceived(requestingState))
- {
- setSpatialChannel(uri, credentials);
- }
-
-}
-
-void LLVivoxVoiceClient::switchChannel(
+bool LLVivoxVoiceClient::switchChannel(
std::string uri,
bool spatial,
bool no_reconnect,
@@ -4082,6 +4402,8 @@ void LLVivoxVoiceClient::switchChannel(
sessionTerminate();
}
}
+
+ return needsSwitch;
}
void LLVivoxVoiceClient::joinSession(sessionState *session)
@@ -4106,7 +4428,7 @@ void LLVivoxVoiceClient::setNonSpatialChannel(
switchChannel(uri, false, false, false, credentials);
}
-void LLVivoxVoiceClient::setSpatialChannel(
+bool LLVivoxVoiceClient::setSpatialChannel(
const std::string &uri,
const std::string &credentials)
{
@@ -4116,14 +4438,15 @@ void LLVivoxVoiceClient::setSpatialChannel(
LL_DEBUGS("Voice") << "got spatial channel uri: \"" << uri << "\"" << LL_ENDL;
- if((mAudioSession && !(mAudioSession->mIsSpatial)) || (mNextAudioSession && !(mNextAudioSession->mIsSpatial)))
+ if((mIsInChannel && mAudioSession && !(mAudioSession->mIsSpatial)) || (mNextAudioSession && !(mNextAudioSession->mIsSpatial)))
{
// User is in a non-spatial chat or joining a non-spatial chat. Don't switch channels.
LL_INFOS("Voice") << "in non-spatial chat, not switching channels" << LL_ENDL;
+ return false;
}
else
{
- switchChannel(mSpatialSessionURI, true, false, false, mSpatialSessionCredentials);
+ return switchChannel(mSpatialSessionURI, true, false, false, mSpatialSessionCredentials);
}
}
@@ -4156,7 +4479,7 @@ LLVivoxVoiceClient::sessionState* LLVivoxVoiceClient::startUserIMSession(const L
if(session->mHandle.empty())
{
// Session isn't active -- start it up.
- sessionCreateSendMessage(session, false, true);
+ sessionCreateSendMessage(session, false, false);
}
else
{
@@ -4167,59 +4490,6 @@ LLVivoxVoiceClient::sessionState* LLVivoxVoiceClient::startUserIMSession(const L
return session;
}
-BOOL LLVivoxVoiceClient::sendTextMessage(const LLUUID& participant_id, const std::string& message)
-{
- bool result = false;
-
- // Attempt to locate the indicated session
- sessionState *session = startUserIMSession(participant_id);
- if(session)
- {
- // found the session, attempt to send the message
- session->mTextMsgQueue.push(message);
-
- // Try to send queued messages (will do nothing if the session is not open yet)
- sendQueuedTextMessages(session);
-
- // The message is queued, so we succeed.
- result = true;
- }
- else
- {
- LL_DEBUGS("Voice") << "Session not found for participant ID " << participant_id << LL_ENDL;
- }
-
- return result;
-}
-
-void LLVivoxVoiceClient::sendQueuedTextMessages(sessionState *session)
-{
- if(session->mTextStreamState == 1)
- {
- if(!session->mTextMsgQueue.empty())
- {
- std::ostringstream stream;
-
- while(!session->mTextMsgQueue.empty())
- {
- std::string message = session->mTextMsgQueue.front();
- session->mTextMsgQueue.pop();
- stream
- << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.SendMessage.1\">"
- << "<SessionHandle>" << session->mHandle << "</SessionHandle>"
- << "<MessageHeader>text/HTML</MessageHeader>"
- << "<MessageBody>" << message << "</MessageBody>"
- << "</Request>"
- << "\n\n\n";
- }
- writeString(stream.str());
- }
- }
- else
- {
- // Session isn't connected yet, defer until later.
- }
-}
void LLVivoxVoiceClient::endUserIMSession(const LLUUID &uuid)
{
@@ -4230,7 +4500,7 @@ void LLVivoxVoiceClient::endUserIMSession(const LLUUID &uuid)
// found the session
if(!session->mHandle.empty())
{
- sessionTextDisconnectSendMessage(session);
+ // sessionTextDisconnectSendMessage(session); // a SLim leftover, not used any more.
}
}
else
@@ -4581,7 +4851,7 @@ void LLVivoxVoiceClient::enforceTether(void)
void LLVivoxVoiceClient::updatePosition(void)
{
-
+
LLViewerRegion *region = gAgent.getRegion();
if(region && isAgentAvatarValid())
{
@@ -5044,7 +5314,7 @@ void LLVivoxVoiceClient::filePlaybackSetMode(bool vox, float speed)
LLVivoxVoiceClient::sessionState::sessionState() :
mErrorStatusCode(0),
mMediaStreamState(streamStateUnknown),
- mTextStreamState(streamStateUnknown),
+ //mTextStreamState(streamStateUnknown),
mCreateInProgress(false),
mMediaConnectInProgress(false),
mVoiceInvitePending(false),
@@ -6010,12 +6280,16 @@ void LLVivoxVoiceClient::sessionSetVoiceFontSendMessage(sessionState *session)
void LLVivoxVoiceClient::accountGetSessionFontsResponse(int statusCode, const std::string &statusString)
{
- // Voice font list entries were updated via addVoiceFont() during parsing.
- if(getState() == stateVoiceFontsWait)
- {
- setState(stateVoiceFontsReceived);
- }
+ if (getState() == stateVoiceFontsWait)
+ {
+ // *TODO: We seem to get multiple events of this type. Should figure a way to advance only after
+ // receiving the last one.
+ LLSD result = LLSD::emptyMap();
+
+ result["voice_fonts"] = LLSD::Boolean(true);
+ LLEventPumps::instance().post("vivoxClientPump", result);
+ }
notifyVoiceFontObservers();
mVoiceFontsReceived = true;
}
@@ -6158,7 +6432,16 @@ void LLVivoxVoiceClient::notifyVoiceFontObservers()
void LLVivoxVoiceClient::enablePreviewBuffer(bool enable)
{
- mCaptureBufferMode = enable;
+ LLSD result;
+ mCaptureBufferMode = enable;
+
+ if (enable)
+ result["recplay"] = "start";
+ else
+ result["recplay"] = "quit";
+
+ LLEventPumps::instance().post("vivoxClientPump", result);
+
if(mCaptureBufferMode && getState() >= stateNoChannel)
{
LL_DEBUGS("Voice") << "no channel" << LL_ENDL;
@@ -6176,6 +6459,10 @@ void LLVivoxVoiceClient::recordPreviewBuffer()
}
mCaptureBufferRecording = true;
+
+ LLSD result;
+ result["recplay"] = "record";
+ LLEventPumps::instance().post("vivoxClientPump", result);
}
void LLVivoxVoiceClient::playPreviewBuffer(const LLUUID& effect_id)
@@ -6196,12 +6483,20 @@ void LLVivoxVoiceClient::playPreviewBuffer(const LLUUID& effect_id)
mPreviewVoiceFont = effect_id;
mCaptureBufferPlaying = true;
+
+ LLSD result;
+ result["recplay"] = "playback";
+ LLEventPumps::instance().post("vivoxClientPump", result);
}
void LLVivoxVoiceClient::stopPreviewBuffer()
{
mCaptureBufferRecording = false;
mCaptureBufferPlaying = false;
+
+ LLSD result;
+ result["recplay"] = "quit";
+ LLEventPumps::instance().post("vivoxClientPump", result);
}
bool LLVivoxVoiceClient::isPreviewRecording()
@@ -6640,6 +6935,10 @@ void LLVivoxProtocolParser::EndTag(const char *tag)
uriString = string;
else if (!stricmp("Presence", tag))
statusString = string;
+ else if (!stricmp("CaptureDevices", tag))
+ LLVivoxVoiceClient::getInstance()->setDevicesListUpdated(true);
+ else if (!stricmp("RenderDevices", tag))
+ LLVivoxVoiceClient::getInstance()->setDevicesListUpdated(true);
else if (!stricmp("CaptureDevice", tag))
{
LLVivoxVoiceClient::getInstance()->addCaptureDevice(deviceString);
@@ -6768,7 +7067,13 @@ void LLVivoxProtocolParser::processResponse(std::string tag)
if (isEvent)
{
const char *eventTypeCstr = eventTypeString.c_str();
- if (!stricmp(eventTypeCstr, "AccountLoginStateChangeEvent"))
+ if (!stricmp(eventTypeCstr, "ParticipantUpdatedEvent"))
+ {
+ // These happen so often that logging them is pretty useless.
+ squelchDebugOutput = true;
+ LLVivoxVoiceClient::getInstance()->participantUpdatedEvent(sessionHandle, sessionGroupHandle, uriString, alias, isModeratorMuted, isSpeaking, volume, energy);
+ }
+ else if (!stricmp(eventTypeCstr, "AccountLoginStateChangeEvent"))
{
LLVivoxVoiceClient::getInstance()->accountLoginStateChangeEvent(accountHandle, statusCode, statusString, state);
}
@@ -6790,6 +7095,10 @@ void LLVivoxProtocolParser::processResponse(std::string tag)
{
LLVivoxVoiceClient::getInstance()->sessionRemovedEvent(sessionHandle, sessionGroupHandle);
}
+ else if (!stricmp(eventTypeCstr, "SessionGroupUpdatedEvent"))
+ {
+ //nothng useful to process for this event, but we should not WARN that we have received it.
+ }
else if (!stricmp(eventTypeCstr, "SessionGroupAddedEvent"))
{
LLVivoxVoiceClient::getInstance()->sessionGroupAddedEvent(sessionGroupHandle);
@@ -6818,19 +7127,6 @@ void LLVivoxProtocolParser::processResponse(std::string tag)
*/
LLVivoxVoiceClient::getInstance()->mediaCompletionEvent(sessionGroupHandle, mediaCompletionType);
}
- else if (!stricmp(eventTypeCstr, "TextStreamUpdatedEvent"))
- {
- /*
- <Event type="TextStreamUpdatedEvent">
- <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg1</SessionGroupHandle>
- <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==1</SessionHandle>
- <Enabled>true</Enabled>
- <State>1</State>
- <Incoming>true</Incoming>
- </Event>
- */
- LLVivoxVoiceClient::getInstance()->textStreamUpdatedEvent(sessionHandle, sessionGroupHandle, enabled, state, incoming);
- }
else if (!stricmp(eventTypeCstr, "ParticipantAddedEvent"))
{
/*
@@ -6857,52 +7153,20 @@ void LLVivoxProtocolParser::processResponse(std::string tag)
*/
LLVivoxVoiceClient::getInstance()->participantRemovedEvent(sessionHandle, sessionGroupHandle, uriString, alias, nameString);
}
- else if (!stricmp(eventTypeCstr, "ParticipantUpdatedEvent"))
- {
- /*
- <Event type="ParticipantUpdatedEvent">
- <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle>
- <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==0</SessionHandle>
- <ParticipantUri>sip:xFnPP04IpREWNkuw1cOXlhw==@bhr.vivox.com</ParticipantUri>
- <IsModeratorMuted>false</IsModeratorMuted>
- <IsSpeaking>true</IsSpeaking>
- <Volume>44</Volume>
- <Energy>0.0879437</Energy>
- </Event>
- */
-
- // These happen so often that logging them is pretty useless.
- squelchDebugOutput = true;
-
- LLVivoxVoiceClient::getInstance()->participantUpdatedEvent(sessionHandle, sessionGroupHandle, uriString, alias, isModeratorMuted, isSpeaking, volume, energy);
- }
else if (!stricmp(eventTypeCstr, "AuxAudioPropertiesEvent"))
{
// These are really spammy in tuning mode
squelchDebugOutput = true;
-
LLVivoxVoiceClient::getInstance()->auxAudioPropertiesEvent(energy);
}
- else if (!stricmp(eventTypeCstr, "BuddyChangedEvent"))
- {
- /*
- <Event type="BuddyChangedEvent">
- <AccountHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==</AccountHandle>
- <BuddyURI>sip:x9fFHFZjOTN6OESF1DUPrZQ==@bhr.vivox.com</BuddyURI>
- <DisplayName>Monroe Tester</DisplayName>
- <BuddyData />
- <GroupID>0</GroupID>
- <ChangeType>Set</ChangeType>
- </Event>
- */
- // TODO: Question: Do we need to process this at all?
- }
else if (!stricmp(eventTypeCstr, "MessageEvent"))
{
+ //TODO: This probably is not received any more, it was used to support SLim clients
LLVivoxVoiceClient::getInstance()->messageEvent(sessionHandle, uriString, alias, messageHeader, messageBody, applicationString);
}
else if (!stricmp(eventTypeCstr, "SessionNotificationEvent"))
{
+ //TODO: This probably is not received any more, it was used to support SLim clients
LLVivoxVoiceClient::getInstance()->sessionNotificationEvent(sessionHandle, uriString, notificationType);
}
else if (!stricmp(eventTypeCstr, "SessionUpdatedEvent"))
@@ -6922,19 +7186,29 @@ void LLVivoxProtocolParser::processResponse(std::string tag)
*/
// We don't need to process this, but we also shouldn't warn on it, since that confuses people.
}
-
- else if (!stricmp(eventTypeCstr, "SessionGroupRemovedEvent"))
+ else if (!stricmp(eventTypeCstr, "SessionGroupRemovedEvent"))
{
- /*
- <Event type="SessionGroupRemovedEvent">
- <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle>
- </Event>
- */
// We don't need to process this, but we also shouldn't warn on it, since that confuses people.
}
- else if (!stricmp(eventTypeCstr, "VoiceServiceConnectionStateChangedEvent"))
+ else if (!stricmp(eventTypeCstr, "VoiceServiceConnectionStateChangedEvent"))
{ // Yet another ignored event
}
+ else if (!stricmp(eventTypeCstr, "AudioDeviceHotSwapEvent"))
+ {
+ /*
+ <Event type = "AudioDeviceHotSwapEvent">
+ <EventType>RenderDeviceChanged< / EventType>
+ <RelevantDevice>
+ <Device>Speakers(Turtle Beach P11 Headset)< / Device>
+ <DisplayName>Speakers(Turtle Beach P11 Headset)< / DisplayName>
+ <Type>SpecificDevice< / Type>
+ < / RelevantDevice>
+ < / Event>
+ */
+ // an audio device was removed or added, fetch and update the local list of audio devices.
+ LLVivoxVoiceClient::getInstance()->getCaptureDevicesSendMessage();
+ LLVivoxVoiceClient::getInstance()->getRenderDevicesSendMessage();
+ }
else
{
LL_WARNS("VivoxProtocolParser") << "Unknown event type " << eventTypeString << LL_ENDL;
@@ -6943,7 +7217,12 @@ void LLVivoxProtocolParser::processResponse(std::string tag)
else
{
const char *actionCstr = actionString.c_str();
- if (!stricmp(actionCstr, "Connector.Create.1"))
+ if (!stricmp(actionCstr, "Session.Set3DPosition.1"))
+ {
+ // We don't need to process these, but they're so spammy we don't want to log them.
+ squelchDebugOutput = true;
+ }
+ else if (!stricmp(actionCstr, "Connector.Create.1"))
{
LLVivoxVoiceClient::getInstance()->connectorCreateResponse(statusCode, statusString, connectorHandle, versionID);
}
@@ -6971,11 +7250,6 @@ void LLVivoxProtocolParser::processResponse(std::string tag)
{
LLVivoxVoiceClient::getInstance()->connectorShutdownResponse(statusCode, statusString);
}
- else if (!stricmp(actionCstr, "Session.Set3DPosition.1"))
- {
- // We don't need to process these, but they're so spammy we don't want to log them.
- squelchDebugOutput = true;
- }
else if (!stricmp(actionCstr, "Account.GetSessionFonts.1"))
{
LLVivoxVoiceClient::getInstance()->accountGetSessionFontsResponse(statusCode, statusString);
diff --git a/indra/newview/llvoicevivox.h b/indra/newview/llvoicevivox.h
index b12ed80e41..6cbd5efdcd 100755
--- a/indra/newview/llvoicevivox.h
+++ b/indra/newview/llvoicevivox.h
@@ -93,6 +93,7 @@ public:
// This returns true when it's safe to bring up the "device settings" dialog in the prefs.
// i.e. when the daemon is running and connected, and the device lists are populated.
virtual bool deviceSettingsAvailable();
+ virtual bool deviceSettingsUpdated(); //return if the list has been updated and never fetched, only to be called from the voicepanel.
// Requery the vivox daemon for the current list of input/output devices.
// If you pass true for clearCurrentList, deviceSettingsAvailable() will be false until the query has completed
@@ -111,7 +112,7 @@ public:
virtual bool isParticipant(const LLUUID& speaker_id);
// Send a text message to the specified user, initiating the session if necessary.
- virtual BOOL sendTextMessage(const LLUUID& participant_id, const std::string& message);
+ // virtual BOOL sendTextMessage(const LLUUID& participant_id, const std::string& message) const {return false;};
// close any existing text IM session with the specified user
virtual void endUserIMSession(const LLUUID &uuid);
@@ -137,7 +138,7 @@ public:
virtual void setNonSpatialChannel(const std::string &uri,
const std::string &credentials);
- virtual void setSpatialChannel(const std::string &uri,
+ virtual bool setSpatialChannel(const std::string &uri,
const std::string &credentials);
virtual void leaveNonSpatialChannel();
@@ -262,6 +263,8 @@ protected:
streamStateIdle = 1,
streamStateConnected = 2,
streamStateRinging = 3,
+ streamStateConnecting = 6, // same as Vivox session_media_connecting enum
+ streamStateDisconnecting = 7, //Same as Vivox session_media_disconnecting enum
};
struct participantState
{
@@ -326,7 +329,6 @@ protected:
LLUUID mCallerID;
int mErrorStatusCode;
int mMediaStreamState;
- int mTextStreamState;
bool mCreateInProgress; // True if a Session.Create has been sent for this session and no response has been received yet.
bool mMediaConnectInProgress; // True if a Session.MediaConnect has been sent for this session and no response has been received yet.
bool mVoiceInvitePending; // True if a voice invite is pending for this session (usually waiting on a name lookup)
@@ -431,8 +433,8 @@ protected:
void connectorShutdown();
void closeSocket(void);
- void requestVoiceAccountProvision(S32 retries = 3);
- void login(
+// void requestVoiceAccountProvision(S32 retries = 3);
+ void setLoginInfo(
const std::string& account_name,
const std::string& password,
const std::string& voice_sip_uri_hostname,
@@ -458,14 +460,15 @@ protected:
void clearCaptureDevices();
void addCaptureDevice(const std::string& name);
void clearRenderDevices();
+ void setDevicesListUpdated(bool state);
void addRenderDevice(const std::string& name);
void buildSetAudioDevices(std::ostringstream &stream);
void getCaptureDevicesSendMessage();
void getRenderDevicesSendMessage();
- // local audio updates
- void buildLocalAudioUpdates(std::ostringstream &stream);
+ // local audio updates, mic mute, speaker mute, mic volume and speaker volumes
+ void sendLocalAudioUpdates();
/////////////////////////////
@@ -481,7 +484,6 @@ protected:
void accountLoginStateChangeEvent(std::string &accountHandle, int statusCode, std::string &statusString, int state);
void mediaCompletionEvent(std::string &sessionGroupHandle, std::string &mediaCompletionType);
void mediaStreamUpdatedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, int statusCode, std::string &statusString, int state, bool incoming);
- void textStreamUpdatedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, bool enabled, int state, bool incoming);
void sessionAddedEvent(std::string &uriString, std::string &alias, std::string &sessionHandle, std::string &sessionGroupHandle, bool isChannel, bool incoming, std::string &nameString, std::string &applicationString);
void sessionGroupAddedEvent(std::string &sessionGroupHandle);
void sessionRemovedEvent(std::string &sessionHandle, std::string &sessionGroupHandle);
@@ -597,7 +599,7 @@ protected:
void sessionTerminateSendMessage(sessionState *session);
void sessionGroupTerminateSendMessage(sessionState *session);
void sessionMediaDisconnectSendMessage(sessionState *session);
- void sessionTextDisconnectSendMessage(sessionState *session);
+ // void sessionTextDisconnectSendMessage(sessionState *session);
@@ -610,9 +612,6 @@ protected:
// Does the actual work to get out of the audio session
void leaveAudioSession();
- // notifies the voice client that we've received parcel voice info
- bool parcelVoiceInfoReceived(state requesting_state);
-
friend class LLVivoxVoiceClientCapResponder;
@@ -637,11 +636,37 @@ protected:
private:
- void voiceAccountProvisionCoro(std::string url, S32 retries);
- void parcelVoiceInfoRequestCoro(std::string url);
+// void voiceAccountProvisionCoro(std::string url, S32 retries);
+// void parcelVoiceInfoRequestCoro(std::string url);
LLVoiceVersionInfo mVoiceVersion;
+ // Coroutine support methods
+ void voiceControlCoro();
+
+ bool startAndConnectSession();
+
+ bool startAndLaunchDaemon();
+ bool provisionVoiceAccount();
+ bool establishVoiceConnection();
+ bool loginToVivox();
+ bool retrieveVoiceFonts();
+
+ bool requestParcelVoiceInfo();
+
+ bool addAndJoinSession(sessionState *nextSession);
+ bool terminateAudioSession(bool wait);
+
+
+ bool waitForChannel();
+ bool runSession(sessionState *session);
+
+ void recordingAndPlaybackMode();
+ int voiceRecordBuffer();
+ int voicePlaybackBuffer();
+
+ bool performMicTuning(state exitState);
+
/// Clean up objects created during a voice session.
void cleanUp();
@@ -681,7 +706,9 @@ private:
bool mTuningMicVolumeDirty;
int mTuningSpeakerVolume;
bool mTuningSpeakerVolumeDirty;
- state mTuningExitState; // state to return to when we leave tuning mode.
+ state mTuningExitState; // state to return to when we leave tuning mode.
+ bool mDevicesListUpdated; // set to true when the device list has been updated
+ // and false when the panelvoicedevicesettings has queried for an update status.
std::string mSpatialSessionURI;
std::string mSpatialSessionCredentials;
@@ -734,9 +761,11 @@ private:
bool checkParcelChanged(bool update = false);
// This should be called when the code detects we have changed parcels.
// It initiates the call to the server that gets the parcel channel.
+#if 0
bool requestParcelVoiceInfo();
-
- void switchChannel(std::string uri = std::string(), bool spatial = true, bool no_reconnect = false, bool is_p2p = false, std::string hash = "");
+#endif
+
+ bool switchChannel(std::string uri = std::string(), bool spatial = true, bool no_reconnect = false, bool is_p2p = false, std::string hash = "");
void joinSession(sessionState *session);
std::string nameFromAvatar(LLVOAvatar *avatar);
@@ -765,7 +794,6 @@ private:
// start a text IM session with the specified user
// This will be asynchronous, the session may be established at a future time.
sessionState* startUserIMSession(const LLUUID& uuid);
- void sendQueuedTextMessages(sessionState *session);
void enforceTether(void);
@@ -808,8 +836,6 @@ private:
std::string mWriteString;
size_t mWriteOffset;
- LLTimer mUpdateTimer;
-
BOOL mLipSyncEnabled;
typedef std::set<LLVoiceClientParticipantObserver*> observer_set_t;
@@ -904,10 +930,15 @@ private:
bool mCaptureBufferRecorded; // A voice sample is captured in the buffer ready to play.
bool mCaptureBufferPlaying; // A voice sample is being played.
- LLTimer mCaptureTimer;
- LLUUID mPreviewVoiceFont;
- LLUUID mPreviewVoiceFontLast;
- S32 mPlayRequestCount;
+ LLTimer mCaptureTimer;
+ LLUUID mPreviewVoiceFont;
+ LLUUID mPreviewVoiceFontLast;
+ S32 mPlayRequestCount;
+ bool mIsInTuningMode;
+ bool mIsInChannel;
+ bool mIsJoiningSession;
+
+ LLEventMailDrop mVivoxPump;
};
/**
@@ -1018,6 +1049,7 @@ protected:
void EndTag(const char *tag);
void CharData(const char *buffer, int length);
LLDate expiryTimeStampToLLDate(const std::string& vivox_ts);
+
};
diff --git a/indra/newview/skins/default/xui/en/panel_sound_devices.xml b/indra/newview/skins/default/xui/en/panel_sound_devices.xml
index 46cbc1e87f..3dbb7fb7fc 100755
--- a/indra/newview/skins/default/xui/en/panel_sound_devices.xml
+++ b/indra/newview/skins/default/xui/en/panel_sound_devices.xml
@@ -98,7 +98,7 @@
name="My volume label"
top_pad="14"
width="200">
- My volume:
+ Mic volume:
</text>
<slider_bar
control_name="AudioLevelMic"
@@ -110,7 +110,7 @@
left_delta="95"
max_val="2"
name="mic_volume_slider"
- tool_tip="Change the volume using this slider"
+ tool_tip="Change the mic level using this slider"
top_pad="-18"
width="110" />
<text
diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py
index f7992dba90..9bcb6fce9f 100755
--- a/indra/newview/viewer_manifest.py
+++ b/indra/newview/viewer_manifest.py
@@ -400,8 +400,6 @@ class Windows_i686_Manifest(ViewerManifest):
self.path("vivoxsdk.dll")
self.path("ortp.dll")
self.path("libsndfile-1.dll")
- self.path("zlib1.dll")
- self.path("vivoxplatform.dll")
self.path("vivoxoal.dll")
self.path("ca-bundle.crt")
diff --git a/indra/viewer_components/updater/CMakeLists.txt b/indra/viewer_components/updater/CMakeLists.txt
index 48c065c2ed..7f866336d9 100755
--- a/indra/viewer_components/updater/CMakeLists.txt
+++ b/indra/viewer_components/updater/CMakeLists.txt
@@ -73,18 +73,23 @@ if(LL_TESTS)
llupdaterservice.cpp
)
+set(test_libs
+ ${LLCOMMON_LIBRARIES})
+
set_source_files_properties(
llupdaterservice.cpp
PROPERTIES
- LL_TEST_ADDITIONAL_LIBRARIES "${BOOST_SYSTEM_LIBRARY}"
+ LL_TEST_ADDITIONAL_LIBRARIES ${test_libs}
# *NOTE:Mani - I was trying to use the preprocessor seam to mock out
# llifstream (and other) llcommon classes. It didn't work
# because of the windows declspec(dllimport)attribute.
# LL_TEST_ADDITIONAL_CFLAGS "-Dllifstream=llus_mock_llifstream"
)
- LL_ADD_PROJECT_UNIT_TESTS(llupdaterservice "${llupdater_service_TEST_SOURCE_FILES}")
+if (NOT LINUX)
+ LL_ADD_PROJECT_UNIT_TESTS(llupdaterservice "${llupdater_service_TEST_SOURCE_FILES}" ${test_libs})
+endif (NOT LINUX)
endif(LL_TESTS)
set(UPDATER_INCLUDE_DIRS