From 48809cb3a9350a0357a0fc710140e2437f3e068e Mon Sep 17 00:00:00 2001 From: Leyla Farazha Date: Mon, 7 Jun 2010 16:01:10 -0700 Subject: Merge --- indra/newview/llvoicevivox.cpp | 1082 +++------------------------------------- 1 file changed, 78 insertions(+), 1004 deletions(-) (limited to 'indra/newview/llvoicevivox.cpp') diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index 2e003dd2b8..d6028b78cb 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -2,25 +2,31 @@ * @file LLVivoxVoiceClient.cpp * @brief Implementation of LLVivoxVoiceClient class which is the interface to the voice client process. * - * $LicenseInfo:firstyear=2001&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * $LicenseInfo:firstyear=2001&license=viewergpl$ + * + * Copyright (c) 2001-2010, Linden Research, Inc. * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 * - * 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. + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception * - * 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 + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ */ @@ -54,7 +60,6 @@ #include "llviewerparcelmgr.h" //#include "llfirstuse.h" #include "llspeakers.h" -#include "lltrans.h" #include "llviewerwindow.h" #include "llviewercamera.h" @@ -62,11 +67,15 @@ #include "llviewernetwork.h" #include "llnotificationsutil.h" -#include "stringize.h" - // for base64 decoding #include "apr_base64.h" +// for SHA1 hash +#include "apr_sha1.h" + +// for MD5 hash +#include "llmd5.h" + #define USE_SESSION_GROUPS 0 const F32 VOLUME_SCALE_VIVOX = 0.01f; @@ -91,15 +100,14 @@ const int MAX_LOGIN_RETRIES = 12; // 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; -// How often to check for expired voice fonts in seconds -const F32 VOICE_FONT_EXPIRY_INTERVAL = 10.f; -// Time of day at which Vivox expires voice font subscriptions. -// Used to replace the time portion of received expiry timestamps. -static const std::string VOICE_FONT_EXPIRY_TIME = "T05:00:00Z"; - -// Maximum length of capture buffer recordings in seconds. -const F32 CAPTURE_BUFFER_MAX_TIME = 10.f; +static void setUUIDFromStringHash(LLUUID &uuid, const std::string &str) +{ + LLMD5 md5_uuid; + md5_uuid.update((const unsigned char*)str.data(), str.size()); + md5_uuid.finalize(); + md5_uuid.raw_digest(uuid.mData); +} static int scale_mic_volume(float volume) { @@ -317,7 +325,6 @@ LLVivoxVoiceClient::LLVivoxVoiceClient() : mBuddyListMapPopulated(false), mBlockRulesListReceived(false), mAutoAcceptRulesListReceived(false), - mCaptureDeviceDirty(false), mRenderDeviceDirty(false), mSpatialCoordsDirty(false), @@ -341,17 +348,10 @@ LLVivoxVoiceClient::LLVivoxVoiceClient() : mVoiceEnabled(false), mWriteInProgress(false), - mLipSyncEnabled(false), + mLipSyncEnabled(false) + - mVoiceFontsReceived(false), - mVoiceFontsNew(false), - mVoiceFontListDirty(false), - mCaptureBufferMode(false), - mCaptureBufferRecording(false), - mCaptureBufferRecorded(false), - mCaptureBufferPlaying(false), - mPlayRequestCount(0) { mSpeakerVolume = scale_speaker_volume(0); @@ -396,16 +396,19 @@ void LLVivoxVoiceClient::init(LLPumpIO *pump) void LLVivoxVoiceClient::terminate() { - if(mConnected) - { - logout(); - connectorShutdown(); - closeSocket(); // Need to do this now -- bad things happen if the destructor does it later. - } - else - { - killGateway(); - } + +// leaveAudioSession(); + logout(); + // As of SDK version 4885, this should no longer be necessary. It will linger after the socket close if it needs to. + // ms_sleep(2000); + connectorShutdown(); + closeSocket(); // Need to do this now -- bad things happen if the destructor does it later. + + // This will do unpleasant things on windows. +// killGateway(); + + + } const LLVoiceVersionInfo& LLVivoxVoiceClient::getVersion() @@ -651,11 +654,6 @@ std::string LLVivoxVoiceClient::state2string(LLVivoxVoiceClient::state inState) CASE(stateMicTuningStart); CASE(stateMicTuningRunning); CASE(stateMicTuningStop); - CASE(stateCaptureBufferPaused); - CASE(stateCaptureBufferRecStart); - CASE(stateCaptureBufferRecording); - CASE(stateCaptureBufferPlayStart); - CASE(stateCaptureBufferPlaying); CASE(stateConnectorStart); CASE(stateConnectorStarting); CASE(stateConnectorStarted); @@ -664,8 +662,6 @@ std::string LLVivoxVoiceClient::state2string(LLVivoxVoiceClient::state inState) CASE(stateNeedsLogin); CASE(stateLoggingIn); CASE(stateLoggedIn); - CASE(stateVoiceFontsWait); - CASE(stateVoiceFontsReceived); CASE(stateCreatingSessionGroup); CASE(stateNoChannel); CASE(stateJoiningSession); @@ -779,10 +775,8 @@ void LLVivoxVoiceClient::stateMachine() // Clean up and reset everything. closeSocket(); deleteAllSessions(); - deleteAllBuddies(); - deleteAllVoiceFonts(); - deleteVoiceFontTemplates(); - + deleteAllBuddies(); + mConnectorHandle.clear(); mAccountHandle.clear(); mAccountPassword.clear(); @@ -1132,97 +1126,8 @@ void LLVivoxVoiceClient::stateMachine() } 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); - } - 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 + + //MARK: stateConnectorStart case stateConnectorStart: if(!mVoiceEnabled) { @@ -1317,18 +1222,6 @@ void LLVivoxVoiceClient::stateMachine() 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); - } - // request the current set of block rules (we'll need them when updating the friends list) accountListBlockRulesSendMessage(); @@ -1360,25 +1253,12 @@ void LLVivoxVoiceClient::stateMachine() 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(); + + setState(stateCreatingSessionGroup); #else // Not using session groups -- skip the stateCreatingSessionGroup state. setState(stateNoChannel); @@ -1426,10 +1306,6 @@ void LLVivoxVoiceClient::stateMachine() mTuningExitState = stateNoChannel; setState(stateMicTuningStart); } - else if(mCaptureBufferMode) - { - setState(stateCaptureBufferPaused); - } else if(sessionNeedsRelog(mNextAudioSession)) { requestRelog(); @@ -1440,7 +1316,6 @@ void LLVivoxVoiceClient::stateMachine() sessionState *oldSession = mAudioSession; mAudioSession = mNextAudioSession; - mAudioSessionChanged = true; if(!mAudioSession->mReconnect) { mNextAudioSession = NULL; @@ -1603,13 +1478,6 @@ void LLVivoxVoiceClient::stateMachine() 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. @@ -1679,8 +1547,6 @@ void LLVivoxVoiceClient::stateMachine() mAccountHandle.clear(); deleteAllSessions(); deleteAllBuddies(); - deleteAllVoiceFonts(); - deleteVoiceFontTemplates(); if(mVoiceEnabled && !mRelogRequested) { @@ -1761,15 +1627,15 @@ void LLVivoxVoiceClient::stateMachine() } - if (mAudioSessionChanged) + if(mAudioSession && mAudioSession->mParticipantsChanged) { - mAudioSessionChanged = false; - notifyParticipantObservers(); - notifyVoiceFontObservers(); + mAudioSession->mParticipantsChanged = false; + mAudioSessionChanged = true; } - else if (mAudioSession && mAudioSession->mParticipantsChanged) + + if(mAudioSessionChanged) { - mAudioSession->mParticipantsChanged = false; + mAudioSessionChanged = false; notifyParticipantObservers(); } } @@ -1885,11 +1751,8 @@ void LLVivoxVoiceClient::sessionGroupCreateSendMessage() void LLVivoxVoiceClient::sessionCreateSendMessage(sessionState *session, bool startAudio, bool startText) { - LL_DEBUGS("Voice") << "Requesting create: " << session->mSIPURI << LL_ENDL; - - S32 font_index = getVoiceFontIndex(session->mVoiceFontID); - LL_DEBUGS("Voice") << "With voice font: " << session->mVoiceFontID << " (" << font_index << ")" << LL_ENDL; - + LL_DEBUGS("Voice") << "requesting create: " << session->mSIPURI << LL_ENDL; + session->mCreateInProgress = true; if(startAudio) { @@ -1913,11 +1776,10 @@ void LLVivoxVoiceClient::sessionCreateSendMessage(sessionState *session, bool st << "" << LLURI::escape(session->mHash, allowed_chars) << "" << "SHA1UserName"; } - + stream << "" << (startAudio?"true":"false") << "" << "" << (startText?"true":"false") << "" - << "" << font_index << "" << "" << mChannelName << "" << "\n\n\n"; writeString(stream.str()); @@ -1925,11 +1787,8 @@ void LLVivoxVoiceClient::sessionCreateSendMessage(sessionState *session, bool st void LLVivoxVoiceClient::sessionGroupAddSessionSendMessage(sessionState *session, bool startAudio, bool startText) { - LL_DEBUGS("Voice") << "Requesting create: " << session->mSIPURI << LL_ENDL; - - S32 font_index = getVoiceFontIndex(session->mVoiceFontID); - LL_DEBUGS("Voice") << "With voice font: " << session->mVoiceFontID << " (" << font_index << ")" << LL_ENDL; - + LL_DEBUGS("Voice") << "requesting create: " << session->mSIPURI << LL_ENDL; + session->mCreateInProgress = true; if(startAudio) { @@ -1955,7 +1814,6 @@ void LLVivoxVoiceClient::sessionGroupAddSessionSendMessage(sessionState *session << "" << mChannelName << "" << "" << (startAudio?"true":"false") << "" << "" << (startText?"true":"false") << "" - << "" << font_index << "" << "" << password << "" << "SHA1UserName" << "\n\n\n" @@ -1966,10 +1824,7 @@ void LLVivoxVoiceClient::sessionGroupAddSessionSendMessage(sessionState *session void LLVivoxVoiceClient::sessionMediaConnectSendMessage(sessionState *session) { - LL_DEBUGS("Voice") << "Connecting audio to session handle: " << session->mHandle << LL_ENDL; - - S32 font_index = getVoiceFontIndex(session->mVoiceFontID); - LL_DEBUGS("Voice") << "With voice font: " << session->mVoiceFontID << " (" << font_index << ")" << LL_ENDL; + LL_DEBUGS("Voice") << "connecting audio to session handle: " << session->mHandle << LL_ENDL; session->mMediaConnectInProgress = true; @@ -1979,7 +1834,6 @@ void LLVivoxVoiceClient::sessionMediaConnectSendMessage(sessionState *session) << "mHandle << "\" action=\"Session.MediaConnect.1\">" << "" << session->mGroupHandle << "" << "" << session->mHandle << "" - << "" << font_index << "" << "Audio" << "\n\n\n"; @@ -3302,7 +3156,7 @@ void LLVivoxVoiceClient::sessionAddedEvent( else { LL_INFOS("Voice") << "Could not generate caller id from uri, using hash of uri " << session->mSIPURI << LL_ENDL; - session->mCallerID.generate(session->mSIPURI); + setUUIDFromStringHash(session->mCallerID, session->mSIPURI); session->mSynthesizedCallerID = true; // Can't look up the name in this case -- we have to extract it from the URI. @@ -3580,26 +3434,6 @@ void LLVivoxVoiceClient::accountLoginStateChangeEvent( } } -void LLVivoxVoiceClient::mediaCompletionEvent(std::string &sessionGroupHandle, std::string &mediaCompletionType) -{ - if (mediaCompletionType == "AuxBufferAudioCapture") - { - mCaptureBufferRecording = false; - } - else if (mediaCompletionType == "AuxBufferAudioRender") - { - // Ignore all but the last stop event - if (--mPlayRequestCount <= 0) - { - mCaptureBufferPlaying = false; - } - } - else - { - LL_DEBUGS("Voice") << "Unknown MediaCompletionType: " << mediaCompletionType << LL_ENDL; - } -} - void LLVivoxVoiceClient::mediaStreamUpdatedEvent( std::string &sessionHandle, std::string &sessionGroupHandle, @@ -4308,8 +4142,8 @@ LLVivoxVoiceClient::participantState *LLVivoxVoiceClient::sessionState::addParti else { // Create a UUID by hashing the URI, but do NOT set mAvatarIDValid. - // This indicates that the ID will not be in the name cache. - result->mAvatarID.generate(uri); + // This tells code in LLVivoxVoiceClient that the ID will not be in the name cache. + setUUIDFromStringHash(result->mAvatarID, uri); } } @@ -4804,7 +4638,7 @@ BOOL LLVivoxVoiceClient::isOnlineSIP(const LLUUID &id) return result; } -bool LLVivoxVoiceClient::isVoiceWorking() const +bool LLVivoxVoiceClient::isVoiceWorking() { //Added stateSessionTerminated state to avoid problems with call in parcels with disabled voice (EXT-4758) // Condition with joining spatial num was added to take into account possible problems with connection to voice @@ -5826,12 +5660,7 @@ LLVivoxVoiceClient::sessionState *LLVivoxVoiceClient::addSession(const std::stri result = new sessionState(); result->mSIPURI = uri; result->mHandle = handle; - - if (LLVoiceClient::instance().getVoiceEffectEnabled()) - { - result->mVoiceFontID = LLVoiceClient::instance().getVoiceEffectDefault(); - } - + mSessions.insert(result); if(!result->mHandle.empty()) @@ -6256,8 +6085,8 @@ void LLVivoxVoiceClient::notifyParticipantObservers() ) { LLVoiceClientParticipantObserver* observer = *it; - observer->onParticipantsChanged(); - // In case onParticipantsChanged() deleted an entry. + observer->onChange(); + // In case onChange() deleted an entry. it = mParticipantObservers.upper_bound(observer); } } @@ -6420,660 +6249,6 @@ void LLVivoxVoiceClient::avatarNameResolved(const LLUUID &id, const std::string } } -bool LLVivoxVoiceClient::setVoiceEffect(const LLUUID& id) -{ - if (!mAudioSession) - { - return false; - } - - if (!id.isNull()) - { - if (mVoiceFontMap.empty()) - { - LL_DEBUGS("Voice") << "Voice fonts not available." << LL_ENDL; - return false; - } - else if (mVoiceFontMap.find(id) == mVoiceFontMap.end()) - { - LL_DEBUGS("Voice") << "Invalid voice font " << id << LL_ENDL; - return false; - } - } - - // *TODO: Check for expired fonts? - mAudioSession->mVoiceFontID = id; - - // *TODO: Separate voice font defaults for spatial chat and IM? - gSavedPerAccountSettings.setString("VoiceEffectDefault", id.asString()); - - sessionSetVoiceFontSendMessage(mAudioSession); - notifyVoiceFontObservers(); - - return true; -} - -const LLUUID LLVivoxVoiceClient::getVoiceEffect() -{ - return mAudioSession ? mAudioSession->mVoiceFontID : LLUUID::null; -} - -LLSD LLVivoxVoiceClient::getVoiceEffectProperties(const LLUUID& id) -{ - LLSD sd; - - voice_font_map_t::iterator iter = mVoiceFontMap.find(id); - if (iter != mVoiceFontMap.end()) - { - sd["template_only"] = false; - } - else - { - // Voice effect is not in the voice font map, see if there is a template - iter = mVoiceFontTemplateMap.find(id); - if (iter == mVoiceFontTemplateMap.end()) - { - LL_WARNS("Voice") << "Voice effect " << id << "not found." << LL_ENDL; - return sd; - } - sd["template_only"] = true; - } - - voiceFontEntry *font = iter->second; - sd["name"] = font->mName; - sd["expiry_date"] = font->mExpirationDate; - sd["is_new"] = font->mIsNew; - - return sd; -} - -LLVivoxVoiceClient::voiceFontEntry::voiceFontEntry(LLUUID& id) : - mID(id), - mFontIndex(0), - mFontType(VOICE_FONT_TYPE_NONE), - mFontStatus(VOICE_FONT_STATUS_NONE), - mIsNew(false) -{ - mExpiryTimer.stop(); - mExpiryWarningTimer.stop(); -} - -LLVivoxVoiceClient::voiceFontEntry::~voiceFontEntry() -{ -} - -void LLVivoxVoiceClient::refreshVoiceEffectLists(bool clear_lists) -{ - if (clear_lists) - { - mVoiceFontsReceived = false; - deleteAllVoiceFonts(); - deleteVoiceFontTemplates(); - } - - accountGetSessionFontsSendMessage(); - accountGetTemplateFontsSendMessage(); -} - -const voice_effect_list_t& LLVivoxVoiceClient::getVoiceEffectList() const -{ - return mVoiceFontList; -} - -const voice_effect_list_t& LLVivoxVoiceClient::getVoiceEffectTemplateList() const -{ - return mVoiceFontTemplateList; -} - -void LLVivoxVoiceClient::addVoiceFont(const S32 font_index, - const std::string &name, - const std::string &description, - const LLDate &expiration_date, - bool has_expired, - const S32 font_type, - const S32 font_status, - const bool template_font) -{ - // Vivox SessionFontIDs are not guaranteed to remain the same between - // sessions or grids so use a UUID for the name. - - // If received name is not a UUID, fudge one by hashing the name and type. - LLUUID font_id; - if (LLUUID::validate(name)) - { - font_id = LLUUID(name); - } - else - { - font_id.generate(STRINGIZE(font_type << ":" << name)); - } - - voiceFontEntry *font = NULL; - - voice_font_map_t& font_map = template_font ? mVoiceFontTemplateMap : mVoiceFontMap; - voice_effect_list_t& font_list = template_font ? mVoiceFontTemplateList : mVoiceFontList; - - // Check whether we've seen this font before. - voice_font_map_t::iterator iter = font_map.find(font_id); - bool new_font = (iter == font_map.end()); - - // Override the has_expired flag if we have passed the expiration_date as a double check. - if (expiration_date.secondsSinceEpoch() < (LLDate::now().secondsSinceEpoch() + VOICE_FONT_EXPIRY_INTERVAL)) - { - has_expired = true; - } - - if (has_expired) - { - LL_DEBUGS("Voice") << "Expired " << (template_font ? "Template " : "") - << expiration_date.asString() << " " << font_id - << " (" << font_index << ") " << name << LL_ENDL; - - // Remove existing session fonts that have expired since we last saw them. - if (!new_font && !template_font) - { - deleteVoiceFont(font_id); - } - return; - } - - if (new_font) - { - // If it is a new font create a new entry. - font = new voiceFontEntry(font_id); - } - else - { - // Not a new font, update the existing entry - font = iter->second; - } - - if (font) - { - font->mFontIndex = font_index; - // Use the description for the human readable name if available, as the - // "name" may be a UUID. - font->mName = description.empty() ? name : description; - font->mFontType = font_type; - font->mFontStatus = font_status; - - // If the font is new or the expiration date has changed the expiry timers need updating. - if (!template_font && (new_font || font->mExpirationDate != expiration_date)) - { - font->mExpirationDate = expiration_date; - - // Set the expiry timer to trigger a notification when the voice font can no longer be used. - font->mExpiryTimer.start(); - font->mExpiryTimer.setExpiryAt(expiration_date.secondsSinceEpoch() - VOICE_FONT_EXPIRY_INTERVAL); - - // Set the warning timer to some interval before actual expiry. - S32 warning_time = gSavedSettings.getS32("VoiceEffectExpiryWarningTime"); - if (warning_time != 0) - { - font->mExpiryWarningTimer.start(); - F64 expiry_time = (expiration_date.secondsSinceEpoch() - (F64)warning_time); - font->mExpiryWarningTimer.setExpiryAt(expiry_time - VOICE_FONT_EXPIRY_INTERVAL); - } - else - { - // Disable the warning timer. - font->mExpiryWarningTimer.stop(); - } - - // Only flag new session fonts after the first time we have fetched the list. - if (mVoiceFontsReceived) - { - font->mIsNew = true; - mVoiceFontsNew = true; - } - } - - LL_DEBUGS("Voice") << (template_font ? "Template " : "") - << font->mExpirationDate.asString() << " " << font->mID - << " (" << font->mFontIndex << ") " << name << LL_ENDL; - - if (new_font) - { - font_map.insert(voice_font_map_t::value_type(font->mID, font)); - font_list.insert(voice_effect_list_t::value_type(font->mName, font->mID)); - } - - mVoiceFontListDirty = true; - - // Debugging stuff - - if (font_type < VOICE_FONT_TYPE_NONE || font_type >= VOICE_FONT_TYPE_UNKNOWN) - { - LL_DEBUGS("Voice") << "Unknown voice font type: " << font_type << LL_ENDL; - } - if (font_status < VOICE_FONT_STATUS_NONE || font_status >= VOICE_FONT_STATUS_UNKNOWN) - { - LL_DEBUGS("Voice") << "Unknown voice font status: " << font_status << LL_ENDL; - } - } -} - -void LLVivoxVoiceClient::expireVoiceFonts() -{ - // *TODO: If we are selling voice fonts in packs, there are probably - // going to be a number of fonts with the same expiration time, so would - // be more efficient to just keep a list of expiration times rather - // than checking each font individually. - - bool have_expired = false; - bool will_expire = false; - bool expired_in_use = false; - - LLUUID current_effect = LLVoiceClient::instance().getVoiceEffectDefault(); - - voice_font_map_t::iterator iter; - for (iter = mVoiceFontMap.begin(); iter != mVoiceFontMap.end(); ++iter) - { - voiceFontEntry* voice_font = iter->second; - LLFrameTimer& expiry_timer = voice_font->mExpiryTimer; - LLFrameTimer& warning_timer = voice_font->mExpiryWarningTimer; - - // Check for expired voice fonts - if (expiry_timer.getStarted() && expiry_timer.hasExpired()) - { - // Check whether it is the active voice font - if (voice_font->mID == current_effect) - { - // Reset to no voice effect. - setVoiceEffect(LLUUID::null); - expired_in_use = true; - } - - LL_DEBUGS("Voice") << "Voice Font " << voice_font->mName << " has expired." << LL_ENDL; - deleteVoiceFont(voice_font->mID); - have_expired = true; - } - - // Check for voice fonts that will expire in less that the warning time - if (warning_timer.getStarted() && warning_timer.hasExpired()) - { - LL_DEBUGS("Voice") << "Voice Font " << voice_font->mName << " will expire soon." << LL_ENDL; - will_expire = true; - warning_timer.stop(); - } - } - - LLSD args; - args["URL"] = LLTrans::getString("voice_morphing_url"); - - // Give a notification if any voice fonts have expired. - if (have_expired) - { - if (expired_in_use) - { - LLNotificationsUtil::add("VoiceEffectsExpiredInUse", args); - } - else - { - LLNotificationsUtil::add("VoiceEffectsExpired", args); - } - - // Refresh voice font lists in the UI. - notifyVoiceFontObservers(); - } - - // Give a warning notification if any voice fonts are due to expire. - if (will_expire) - { - S32 seconds = gSavedSettings.getS32("VoiceEffectExpiryWarningTime"); - args["INTERVAL"] = llformat("%d", seconds / SEC_PER_DAY); - - LLNotificationsUtil::add("VoiceEffectsWillExpire", args); - } -} - -void LLVivoxVoiceClient::deleteVoiceFont(const LLUUID& id) -{ - // Remove the entry from the voice font list. - voice_effect_list_t::iterator list_iter = mVoiceFontList.begin(); - while (list_iter != mVoiceFontList.end()) - { - if (list_iter->second == id) - { - LL_DEBUGS("Voice") << "Removing " << id << " from the voice font list." << LL_ENDL; - mVoiceFontList.erase(list_iter++); - mVoiceFontListDirty = true; - } - else - { - ++list_iter; - } - } - - // Find the entry in the voice font map and erase its data. - voice_font_map_t::iterator map_iter = mVoiceFontMap.find(id); - if (map_iter != mVoiceFontMap.end()) - { - delete map_iter->second; - } - - // Remove the entry from the voice font map. - mVoiceFontMap.erase(map_iter); -} - -void LLVivoxVoiceClient::deleteAllVoiceFonts() -{ - mVoiceFontList.clear(); - - voice_font_map_t::iterator iter; - for (iter = mVoiceFontMap.begin(); iter != mVoiceFontMap.end(); ++iter) - { - delete iter->second; - } - mVoiceFontMap.clear(); -} - -void LLVivoxVoiceClient::deleteVoiceFontTemplates() -{ - mVoiceFontTemplateList.clear(); - - voice_font_map_t::iterator iter; - for (iter = mVoiceFontTemplateMap.begin(); iter != mVoiceFontTemplateMap.end(); ++iter) - { - delete iter->second; - } - mVoiceFontTemplateMap.clear(); -} - -S32 LLVivoxVoiceClient::getVoiceFontIndex(const LLUUID& id) const -{ - S32 result = 0; - if (!id.isNull()) - { - voice_font_map_t::const_iterator it = mVoiceFontMap.find(id); - if (it != mVoiceFontMap.end()) - { - result = it->second->mFontIndex; - } - else - { - LL_DEBUGS("Voice") << "Selected voice font " << id << " is not available." << LL_ENDL; - } - } - return result; -} - -S32 LLVivoxVoiceClient::getVoiceFontTemplateIndex(const LLUUID& id) const -{ - S32 result = 0; - if (!id.isNull()) - { - voice_font_map_t::const_iterator it = mVoiceFontTemplateMap.find(id); - if (it != mVoiceFontTemplateMap.end()) - { - result = it->second->mFontIndex; - } - else - { - LL_DEBUGS("Voice") << "Selected voice font template " << id << " is not available." << LL_ENDL; - } - } - return result; -} - -void LLVivoxVoiceClient::accountGetSessionFontsSendMessage() -{ - if(!mAccountHandle.empty()) - { - std::ostringstream stream; - - LL_DEBUGS("Voice") << "Requesting voice font list." << LL_ENDL; - - stream - << "" - << "" << mAccountHandle << "" - << "" - << "\n\n\n"; - - writeString(stream.str()); - } -} - -void LLVivoxVoiceClient::accountGetTemplateFontsSendMessage() -{ - if(!mAccountHandle.empty()) - { - std::ostringstream stream; - - LL_DEBUGS("Voice") << "Requesting voice font template list." << LL_ENDL; - - stream - << "" - << "" << mAccountHandle << "" - << "" - << "\n\n\n"; - - writeString(stream.str()); - } -} - -void LLVivoxVoiceClient::sessionSetVoiceFontSendMessage(sessionState *session) -{ - S32 font_index = getVoiceFontIndex(session->mVoiceFontID); - LL_DEBUGS("Voice") << "Requesting voice font: " << session->mVoiceFontID << " (" << font_index << "), session handle: " << session->mHandle << LL_ENDL; - - std::ostringstream stream; - - stream - << "" - << "" << session->mHandle << "" - << "" << font_index << "" - << "\n\n\n"; - - writeString(stream.str()); -} - -void LLVivoxVoiceClient::accountGetSessionFontsResponse(int statusCode, const std::string &statusString) -{ - // Voice font list entries were updated via addVoiceFont() during parsing. - if(getState() == stateVoiceFontsWait) - { - setState(stateVoiceFontsReceived); - } - - notifyVoiceFontObservers(); - mVoiceFontsReceived = true; -} - -void LLVivoxVoiceClient::accountGetTemplateFontsResponse(int statusCode, const std::string &statusString) -{ - // Voice font list entries were updated via addVoiceFont() during parsing. - notifyVoiceFontObservers(); -} -void LLVivoxVoiceClient::addObserver(LLVoiceEffectObserver* observer) -{ - mVoiceFontObservers.insert(observer); -} - -void LLVivoxVoiceClient::removeObserver(LLVoiceEffectObserver* observer) -{ - mVoiceFontObservers.erase(observer); -} - -void LLVivoxVoiceClient::notifyVoiceFontObservers() -{ - LL_DEBUGS("Voice") << "Notifying voice effect observers. Lists changed: " << mVoiceFontListDirty << LL_ENDL; - - for (voice_font_observer_set_t::iterator it = mVoiceFontObservers.begin(); - it != mVoiceFontObservers.end(); - ) - { - LLVoiceEffectObserver* observer = *it; - observer->onVoiceEffectChanged(mVoiceFontListDirty); - // In case onVoiceEffectChanged() deleted an entry. - it = mVoiceFontObservers.upper_bound(observer); - } - mVoiceFontListDirty = false; - - // If new Voice Fonts have been added notify the user. - if (mVoiceFontsNew) - { - if(mVoiceFontsReceived) - { - LLNotificationsUtil::add("VoiceEffectsNew"); - } - mVoiceFontsNew = false; - } -} - -void LLVivoxVoiceClient::enablePreviewBuffer(bool enable) -{ - mCaptureBufferMode = enable; - if(mCaptureBufferMode && getState() >= stateNoChannel) - { - LL_DEBUGS("Voice") << "no channel" << LL_ENDL; - sessionTerminate(); - } -} - -void LLVivoxVoiceClient::recordPreviewBuffer() -{ - if (!mCaptureBufferMode) - { - LL_DEBUGS("Voice") << "Not in voice effect preview mode, cannot start recording." << LL_ENDL; - mCaptureBufferRecording = false; - return; - } - - mCaptureBufferRecording = true; -} - -void LLVivoxVoiceClient::playPreviewBuffer(const LLUUID& effect_id) -{ - if (!mCaptureBufferMode) - { - LL_DEBUGS("Voice") << "Not in voice effect preview mode, no buffer to play." << LL_ENDL; - mCaptureBufferRecording = false; - return; - } - - if (!mCaptureBufferRecorded) - { - // Can't play until we have something recorded! - mCaptureBufferPlaying = false; - return; - } - - mPreviewVoiceFont = effect_id; - mCaptureBufferPlaying = true; -} - -void LLVivoxVoiceClient::stopPreviewBuffer() -{ - mCaptureBufferRecording = false; - mCaptureBufferPlaying = false; -} - -bool LLVivoxVoiceClient::isPreviewRecording() -{ - return (mCaptureBufferMode && mCaptureBufferRecording); -} - -bool LLVivoxVoiceClient::isPreviewPlaying() -{ - return (mCaptureBufferMode && mCaptureBufferPlaying); -} - -void LLVivoxVoiceClient::captureBufferRecordStartSendMessage() -{ if(!mAccountHandle.empty()) - { - std::ostringstream stream; - - LL_DEBUGS("Voice") << "Starting audio capture to buffer." << LL_ENDL; - - // Start capture - stream - << "" - << "" - << "\n\n\n"; - - // Unmute the mic - stream << "" - << "" << mConnectorHandle << "" - << "false" - << "\n\n\n"; - - // Dirty the PTT state so that it will get reset when we finishing previewing - mPTTDirty = true; - - writeString(stream.str()); - } -} - -void LLVivoxVoiceClient::captureBufferRecordStopSendMessage() -{ - if(!mAccountHandle.empty()) - { - std::ostringstream stream; - - LL_DEBUGS("Voice") << "Stopping audio capture to buffer." << LL_ENDL; - - // Mute the mic. PTT state was dirtied at recording start, so will be reset when finished previewing. - stream << "" - << "" << mConnectorHandle << "" - << "true" - << "\n\n\n"; - - // Stop capture - stream - << "" - << "" << mAccountHandle << "" - << "" - << "\n\n\n"; - - writeString(stream.str()); - } -} - -void LLVivoxVoiceClient::captureBufferPlayStartSendMessage(const LLUUID& voice_font_id) -{ - if(!mAccountHandle.empty()) - { - // Track how may play requests are sent, so we know how many stop events to - // expect before play actually stops. - ++mPlayRequestCount; - - std::ostringstream stream; - - LL_DEBUGS("Voice") << "Starting audio buffer playback." << LL_ENDL; - - S32 font_index = getVoiceFontTemplateIndex(voice_font_id); - LL_DEBUGS("Voice") << "With voice font: " << voice_font_id << " (" << font_index << ")" << LL_ENDL; - - stream - << "" - << "" << mAccountHandle << "" - << "" << font_index << "" - << "" - << "" - << "\n\n\n"; - - writeString(stream.str()); - } -} - -void LLVivoxVoiceClient::captureBufferPlayStopSendMessage() -{ - if(!mAccountHandle.empty()) - { - std::ostringstream stream; - - LL_DEBUGS("Voice") << "Stopping audio buffer playback." << LL_ENDL; - - stream - << "" - << "" << mAccountHandle << "" - << "" - << "\n\n\n"; - - writeString(stream.str()); - } -} LLVivoxProtocolParser::LLVivoxProtocolParser() { @@ -7111,13 +6286,6 @@ void LLVivoxProtocolParser::reset() alias.clear(); numberOfAliases = 0; applicationString.clear(); - id = 0; - nameString.clear(); - descriptionString.clear(); - expirationDate = LLDate(); - hasExpired = false; - fontType = 0; - fontStatus = 0; } //virtual @@ -7299,30 +6467,7 @@ void LLVivoxProtocolParser::StartTag(const char *tag, const char **attr) { LLVivoxVoiceClient::getInstance()->deleteAllAutoAcceptRules(); } - else if (!stricmp("SessionFont", tag)) - { - id = 0; - nameString.clear(); - descriptionString.clear(); - expirationDate = LLDate(); - hasExpired = false; - fontType = 0; - fontStatus = 0; - } - else if (!stricmp("TemplateFont", tag)) - { - id = 0; - nameString.clear(); - descriptionString.clear(); - expirationDate = LLDate(); - hasExpired = false; - fontType = 0; - fontStatus = 0; - } - else if (!stricmp("MediaCompletionType", tag)) - { - mediaCompletionType.clear(); - } + } } responseDepth++; @@ -7468,43 +6613,8 @@ void LLVivoxProtocolParser::EndTag(const char *tag) subscriptionHandle = string; else if (!stricmp("SubscriptionType", tag)) subscriptionType = string; - else if (!stricmp("SessionFont", tag)) - { - LLVivoxVoiceClient::getInstance()->addVoiceFont(id, nameString, descriptionString, expirationDate, hasExpired, fontType, fontStatus, false); - } - else if (!stricmp("TemplateFont", tag)) - { - LLVivoxVoiceClient::getInstance()->addVoiceFont(id, nameString, descriptionString, expirationDate, hasExpired, fontType, fontStatus, true); - } - else if (!stricmp("ID", tag)) - { - id = strtol(string.c_str(), NULL, 10); - } - else if (!stricmp("Description", tag)) - { - descriptionString = string; - } - else if (!stricmp("ExpirationDate", tag)) - { - expirationDate = expiryTimeStampToLLDate(string); - } - else if (!stricmp("Expired", tag)) - { - hasExpired = !stricmp(string.c_str(), "1"); - } - else if (!stricmp("Type", tag)) - { - fontType = strtol(string.c_str(), NULL, 10); - } - else if (!stricmp("Status", tag)) - { - fontStatus = strtol(string.c_str(), NULL, 10); - } - else if (!stricmp("MediaCompletionType", tag)) - { - mediaCompletionType = string;; - } - + + textBuffer.clear(); accumulateText= false; @@ -7533,21 +6643,6 @@ void LLVivoxProtocolParser::CharData(const char *buffer, int length) // -------------------------------------------------------------------------------- -LLDate LLVivoxProtocolParser::expiryTimeStampToLLDate(const std::string& vivox_ts) -{ - // *HACK: Vivox reports the time incorrectly. LLDate also only parses a - // subset of valid ISO 8601 dates (only handles Z, not offsets). - // So just use the date portion and fix the time here. - std::string time_stamp = vivox_ts.substr(0, 10); - time_stamp += VOICE_FONT_EXPIRY_TIME; - - LL_DEBUGS("VivoxProtocolParser") << "Vivox timestamp " << vivox_ts << " modified to: " << time_stamp << LL_ENDL; - - return LLDate(time_stamp); -} - -// -------------------------------------------------------------------------------- - void LLVivoxProtocolParser::processResponse(std::string tag) { LL_DEBUGS("VivoxProtocolParser") << tag << LL_ENDL; @@ -7601,17 +6696,7 @@ void LLVivoxProtocolParser::processResponse(std::string tag) */ LLVivoxVoiceClient::getInstance()->mediaStreamUpdatedEvent(sessionHandle, sessionGroupHandle, statusCode, statusString, state, incoming); - } - else if (!stricmp(eventTypeCstr, "MediaCompletionEvent")) - { - /* - - - AuxBufferAudioCapture - - */ - LLVivoxVoiceClient::getInstance()->mediaCompletionEvent(sessionGroupHandle, mediaCompletionType); - } + } else if (!stricmp(eventTypeCstr, "TextStreamUpdatedEvent")) { /* @@ -7672,9 +6757,6 @@ void LLVivoxProtocolParser::processResponse(std::string tag) } else if (!stricmp(eventTypeCstr, "AuxAudioPropertiesEvent")) { - // These are really spammy in tuning mode - squelchDebugOutput = true; - LLVivoxVoiceClient::getInstance()->auxAudioPropertiesEvent(energy); } else if (!stricmp(eventTypeCstr, "BuddyPresenceEvent")) @@ -7789,14 +6871,6 @@ void LLVivoxProtocolParser::processResponse(std::string tag) // 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); - } - else if (!stricmp(actionCstr, "Account.GetTemplateFonts.1")) - { - LLVivoxVoiceClient::getInstance()->accountGetTemplateFontsResponse(statusCode, statusString); - } /* else if (!stricmp(actionCstr, "Account.ChannelGetList.1")) { -- cgit v1.2.3 From 90e3d83a5cb35e98a02a3017dd79ebc272bbfe85 Mon Sep 17 00:00:00 2001 From: "Brad Payne (Vir Linden)" Date: Tue, 21 Sep 2010 13:26:52 -0400 Subject: Fix for build failures - disabling tcmalloc for now --- indra/newview/llvoicevivox.cpp | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 indra/newview/llvoicevivox.cpp (limited to 'indra/newview/llvoicevivox.cpp') diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp old mode 100644 new mode 100755 -- cgit v1.2.3 From a5619d16f74863168f45b04b37cc6383e1a92263 Mon Sep 17 00:00:00 2001 From: Oz Linden Date: Wed, 13 Oct 2010 07:24:37 -0400 Subject: correct licenses (fix problem with license change merge) --- indra/newview/llvoicevivox.cpp | 36 +++++++++++++++--------------------- 1 file changed, 15 insertions(+), 21 deletions(-) (limited to 'indra/newview/llvoicevivox.cpp') diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index ada577d1b2..350435078d 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -2,31 +2,25 @@ * @file LLVivoxVoiceClient.cpp * @brief Implementation of LLVivoxVoiceClient class which is the interface to the voice client process. * - * $LicenseInfo:firstyear=2001&license=viewergpl$ - * - * Copyright (c) 2001-2010, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ -- cgit v1.2.3 From d4d292258e31eee6d639106147758f39deae63e3 Mon Sep 17 00:00:00 2001 From: Ricky Curtice Date: Thu, 10 Mar 2011 22:07:06 -0800 Subject: Squared all dist_vec() based comparisons and other dist_vec() operations where sensible. Not all instances of dist_vec() were squared, only those where it wouldn't (hopefully) change the functionality. --- indra/newview/llvoicevivox.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indra/newview/llvoicevivox.cpp') diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index 08e242af8e..08581be38b 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -5088,7 +5088,7 @@ void LLVivoxVoiceClient::enforceTether(void) } } - if(dist_vec(mCameraPosition, tethered) > 0.1) + if(dist_vec_squared(mCameraPosition, tethered) > 0.01) { mCameraPosition = tethered; mSpatialCoordsDirty = true; @@ -5150,7 +5150,7 @@ void LLVivoxVoiceClient::setCameraPosition(const LLVector3d &position, const LLV void LLVivoxVoiceClient::setAvatarPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot) { - if(dist_vec(mAvatarPosition, position) > 0.1) + if(dist_vec_squared(mAvatarPosition, position) > 0.01) { mAvatarPosition = position; mSpatialCoordsDirty = true; -- cgit v1.2.3 From 35ba8f36e432a14c35fb63b1eb49ddbc7ebfbde7 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Thu, 23 Jun 2011 12:00:31 -0700 Subject: Fix for VOICE-3 [crashhunters] LLVoiceVoiceClient::stateMachine Race condition with parcel changing while shutting down. If a parcel changes, we went into code that requests the cap for the new parcel voice channel info. This happened in any state, not the two states where it is really appropriate. stateRunning or stateNoSession. When shutting down, we may be in stateTerminate, stateLeavingSession, or so on, and we don't want to muck with getting caps and such during that time. --- indra/newview/llvoicevivox.cpp | 303 +++++++++++++++++++++++++---------------- 1 file changed, 182 insertions(+), 121 deletions(-) (limited to 'indra/newview/llvoicevivox.cpp') diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index 6ee6822e2f..cd2bbad620 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -195,12 +195,13 @@ static LLVivoxVoiceClientFriendsObserver *friendslist_listener = NULL; class LLVivoxVoiceClientCapResponder : public LLHTTPClient::Responder { public: - LLVivoxVoiceClientCapResponder(void){}; + LLVivoxVoiceClientCapResponder(LLVivoxVoiceClient::state requesting_state) : mRequestingState(requesting_state) {}; virtual void error(U32 status, const std::string& reason); // called with bad status codes virtual void result(const LLSD& content); private: + LLVivoxVoiceClient::state mRequestingState; // state }; void LLVivoxVoiceClientCapResponder::error(U32 status, const std::string& reason) @@ -208,6 +209,7 @@ void LLVivoxVoiceClientCapResponder::error(U32 status, const std::string& reason LL_WARNS("Voice") << "LLVivoxVoiceClientCapResponder::error(" << status << ": " << reason << ")" << LL_ENDL; + LLVivoxVoiceClient::getInstance()->sessionTerminate(); } void LLVivoxVoiceClientCapResponder::result(const LLSD& content) @@ -216,12 +218,12 @@ void LLVivoxVoiceClientCapResponder::result(const LLSD& content) LL_DEBUGS("Voice") << "ParcelVoiceInfoRequest response:" << ll_pretty_print_sd(content) << LL_ENDL; + std::string uri; + std::string credentials; + if ( content.has("voice_credentials") ) { LLSD voice_credentials = content["voice_credentials"]; - std::string uri; - std::string credentials; - if ( voice_credentials.has("channel_uri") ) { uri = voice_credentials["channel_uri"].asString(); @@ -231,7 +233,12 @@ void LLVivoxVoiceClientCapResponder::result(const LLSD& content) credentials = voice_credentials["channel_credentials"].asString(); } - + } + + // set the spatial channel. If no voice credentials or uri are + // available, then we simply drop out of voice spatially. + if(LLVivoxVoiceClient::getInstance()->parcelVoiceInfoReceived(mRequestingState)) + { LLVivoxVoiceClient::getInstance()->setSpatialChannel(uri, credentials); } } @@ -551,18 +558,27 @@ void LLVivoxVoiceClient::userAuthorized(const std::string& user_id, const LLUUID void LLVivoxVoiceClient::requestVoiceAccountProvision(S32 retries) { - if ( gAgent.getRegion() && mVoiceEnabled ) + LLViewerRegion *region = gAgent.getRegion(); + + if ( region && mVoiceEnabled ) { std::string url = - gAgent.getRegion()->getCapability( - "ProvisionVoiceAccountRequest"); - - if ( url == "" ) return; - + region->getCapability("ProvisionVoiceAccountRequest"); + + if ( url.empty() ) + { + // we've not received the capability yet, so return. + // the password will remain empty, so we'll remain in + // stateIdle + return; + } + LLHTTPClient::post( - url, - LLSD(), - new LLVivoxVoiceAccountProvisionResponder(retries)); + url, + LLSD(), + new LLVivoxVoiceAccountProvisionResponder(retries)); + + setState(stateConnectorStart); } } @@ -673,7 +689,8 @@ std::string LLVivoxVoiceClient::state2string(LLVivoxVoiceClient::state inState) CASE(stateVoiceFontsWait); CASE(stateVoiceFontsReceived); CASE(stateCreatingSessionGroup); - CASE(stateNoChannel); + CASE(stateNoChannel); + CASE(stateRetrievingParcelVoiceInfo); CASE(stateJoiningSession); CASE(stateSessionJoined); CASE(stateRunning); @@ -741,42 +758,6 @@ void LLVivoxVoiceClient::stateMachine() } } - // Check for parcel boundary crossing - { - LLViewerRegion *region = gAgent.getRegion(); - LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); - - if(region && parcel) - { - S32 parcelLocalID = parcel->getLocalID(); - std::string regionName = region->getName(); - std::string capURI = region->getCapability("ParcelVoiceInfoRequest"); - -// LL_DEBUGS("Voice") << "Region name = \"" << regionName << "\", parcel local ID = " << parcelLocalID << ", cap URI = \"" << capURI << "\"" << LL_ENDL; - - // The region name starts out empty and gets filled in later. - // Also, the cap gets filled in a short time after the region cross, but a little too late for our purposes. - // If either is empty, wait for the next time around. - if(!regionName.empty()) - { - if(!capURI.empty()) - { - if((parcelLocalID != mCurrentParcelLocalID) || (regionName != mCurrentRegionName)) - { - // We have changed parcels. Initiate a parcel channel lookup. - mCurrentParcelLocalID = parcelLocalID; - mCurrentRegionName = regionName; - - parcelChanged(); - } - } - else - { - LL_WARNS_ONCE("Voice") << "region doesn't have ParcelVoiceInfoRequest capability. This is normal for a short time after teleporting, but bad if it persists for very long." << LL_ENDL; - } - } - } - } switch(getState()) { @@ -1026,22 +1007,9 @@ void LLVivoxVoiceClient::stateMachine() } else if(!mAccountName.empty()) { - LLViewerRegion *region = gAgent.getRegion(); - - if(region) + if ( mAccountPassword.empty() ) { - if ( region->getCapability("ProvisionVoiceAccountRequest") != "" ) - { - if ( mAccountPassword.empty() ) - { - requestVoiceAccountProvision(); - } - setState(stateConnectorStart); - } - else - { - LL_WARNS_ONCE("Voice") << "region doesn't have ProvisionVoiceAccountRequest capability!" << LL_ENDL; - } + requestVoiceAccountProvision(); } } break; @@ -1382,11 +1350,7 @@ void LLVivoxVoiceClient::stateMachine() setState(stateCreatingSessionGroup); sessionGroupCreateSendMessage(); #else - // Not using session groups -- skip the stateCreatingSessionGroup state. - setState(stateNoChannel); - - // Initial kick-off of channel lookup logic - parcelChanged(); + setState(stateNoChannel); #endif break; @@ -1399,19 +1363,29 @@ void LLVivoxVoiceClient::stateMachine() } else if(!mMainSessionGroupHandle.empty()) { - setState(stateNoChannel); - // Start looped recording (needed for "panic button" anti-griefing tool) recordingLoopStart(); - - // Initial kick-off of channel lookup logic - parcelChanged(); + setState(stateNoChannel); } break; + + //MARK: stateRetrievingParcelVoiceInfo + case stateRetrievingParcelVoiceInfo: + // wait until parcel voice info is received. + if(mSessionTerminateRequested || !mVoiceEnabled) + { + // 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; // Do this here as well as inside sendPositionalUpdate(). @@ -1432,6 +1406,16 @@ void LLVivoxVoiceClient::stateMachine() { 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(); @@ -1466,32 +1450,28 @@ void LLVivoxVoiceClient::stateMachine() notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINING); setState(stateJoiningSession); } - else if(!mSpatialSessionURI.empty()) - { - // If we're not headed elsewhere and have a spatial URI, return to spatial. - switchChannel(mSpatialSessionURI, true, false, false, mSpatialSessionCredentials); - } 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) + + // 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); - llwarns << "There seems to be problem with connection to voice server. Disabling voice chat abilities." << llendl; + // Notify observers to let them know there is problem with voice + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED); + llwarns << "There seems to be problem with connection to voice server. Disabling voice chat abilities." << llendl; } - - // 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) + + // 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++; + + mSpatialJoiningNum++; } - + // joinedAudioSession() will transition from here to stateSessionJoined. if(!mVoiceEnabled) { @@ -1511,12 +1491,13 @@ void LLVivoxVoiceClient::stateMachine() } } } - break; - + break; + //MARK: stateSessionJoined case stateSessionJoined: // session handle received - mSpatialJoiningNum = 0; + + 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. @@ -1553,7 +1534,7 @@ void LLVivoxVoiceClient::stateMachine() sessionMediaDisconnectSendMessage(mAudioSession); setState(stateSessionTerminated); } - } + } break; //MARK: stateRunning @@ -1565,6 +1546,7 @@ void LLVivoxVoiceClient::stateMachine() } else { + if(!inSpatialChannel()) { // When in a non-spatial channel, never send positional updates. @@ -1572,8 +1554,22 @@ void LLVivoxVoiceClient::stateMachine() } 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. @@ -3840,7 +3836,7 @@ void LLVivoxVoiceClient::participantUpdatedEvent( // also initialize voice moderate_mode depend on Agent's participant. See EXT-6937. // *TODO: remove once a way to request the current voice channel moderation mode is implemented. - if (gAgentID == participant->mAvatarID) + if (gAgent.getID() == participant->mAvatarID) { speaker_manager->initVoiceModerateMode(); } @@ -4073,7 +4069,7 @@ void LLVivoxVoiceClient::messageEvent( } LL_DEBUGS("Voice") << "adding message, name " << session->mName << " session " << session->mIMSessionID << ", target " << session->mCallerID << LL_ENDL; - gIMMgr->addMessage(session->mIMSessionID, + LLIMMgr::getInstance()->addMessage(session->mIMSessionID, session->mCallerID, session->mName.c_str(), message.c_str(), @@ -4447,24 +4443,91 @@ LLVivoxVoiceClient::participantState* LLVivoxVoiceClient::findParticipantByID(co } -void LLVivoxVoiceClient::parcelChanged() + +// Check for parcel boundary crossing +bool LLVivoxVoiceClient::checkParcelChanged(bool update) { - if(getState() >= stateNoChannel) + LLViewerRegion *region = gAgent.getRegion(); + LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + + if(region && parcel) { - // If the user is logged in, start a channel lookup. - LL_DEBUGS("Voice") << "sending ParcelVoiceInfoRequest (" << mCurrentRegionName << ", " << mCurrentParcelLocalID << ")" << LL_ENDL; + S32 parcelLocalID = parcel->getLocalID(); + std::string regionName = region->getName(); + + // LL_DEBUGS("Voice") << "Region name = \"" << regionName << "\", parcel local ID = " << parcelLocalID << ", cap URI = \"" << capURI << "\"" << LL_ENDL; + + // The region name starts out empty and gets filled in later. + // Also, the cap gets filled in a short time after the region cross, but a little too late for our purposes. + // If either is empty, wait for the next time around. + if(!regionName.empty()) + { + if((parcelLocalID != mCurrentParcelLocalID) || (regionName != mCurrentRegionName)) + { + // We have changed parcels. Initiate a parcel channel lookup. + if (update) + { + mCurrentParcelLocalID = parcelLocalID; + mCurrentRegionName = regionName; + } + return true; + } + } + } + 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; + } +} + - std::string url = gAgent.getRegion()->getCapability("ParcelVoiceInfoRequest"); +bool LLVivoxVoiceClient::requestParcelVoiceInfo() +{ + LL_DEBUGS("Voice") << "sending ParcelVoiceInfoRequest (" << mCurrentRegionName << ", " << mCurrentParcelLocalID << ")" << LL_ENDL; + + // grab the cap for parcel voice info from the region. + LLViewerRegion * region = gAgent.getRegion(); + if (region == NULL) + { + return false; + } + // grab the cap. + std::string url = gAgent.getRegion()->getCapability("ParcelVoiceInfoRequest"); + if (!url.empty()) + { + // 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; + LLHTTPClient::post( - url, - data, - new LLVivoxVoiceClientCapResponder); + url, + data, + new LLVivoxVoiceClientCapResponder(getState())); + return true; } - else + else { - // The transition to stateNoChannel needs to kick this off again. - LL_INFOS("Voice") << "not logged in yet, deferring" << LL_ENDL; + + // we don't have the cap yet, so return false so the caller can try again later. + LL_DEBUGS("Voice") << "ParcelVoiceInfoRequest cap not yet available, deferring" << LL_ENDL; + return false; } } @@ -4488,6 +4551,7 @@ void LLVivoxVoiceClient::switchChannel( case stateJoinSessionFailed: case stateJoinSessionFailedWaiting: case stateNoChannel: + case stateRetrievingParcelVoiceInfo: // Always switch to the new URI from these states. needsSwitch = true; break; @@ -4560,13 +4624,10 @@ void LLVivoxVoiceClient::switchChannel( mNextAudioSession->mIsP2P = is_p2p; } - if(getState() <= stateNoChannel) - { - // We're already set up to join a channel, just needed to fill in the session URI - } - else + if(getState() >= stateRetrievingParcelVoiceInfo) { - // State machine will come around and rejoin if uri/handle is not empty. + // If we're already in a channel, or if we're joining one, terminate + // so we can rejoin with the new session data. sessionTerminate(); } } @@ -6267,13 +6328,13 @@ void LLVivoxVoiceClient::avatarNameResolved(const LLUUID &id, const std::string { session->mTextInvitePending = false; - // We don't need to call gIMMgr->addP2PSession() here. The first incoming message will create the panel. + // We don't need to call LLIMMgr::getInstance()->addP2PSession() here. The first incoming message will create the panel. } if(session->mVoiceInvitePending) { session->mVoiceInvitePending = false; - gIMMgr->inviteToSession( + LLIMMgr::getInstance()->inviteToSession( session->mIMSessionID, session->mName, session->mCallerID, -- cgit v1.2.3