diff options
Diffstat (limited to 'indra/newview/llvoiceclient.h')
-rw-r--r-- | indra/newview/llvoiceclient.h | 1124 |
1 files changed, 689 insertions, 435 deletions
diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h index 24d7d7163e..a29c386182 100644 --- a/indra/newview/llvoiceclient.h +++ b/indra/newview/llvoiceclient.h @@ -2,31 +2,38 @@ * @file llvoiceclient.h * @brief Declaration of LLVoiceClient 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-2009, 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$ */ #ifndef LL_VOICE_CLIENT_H #define LL_VOICE_CLIENT_H class LLVOAvatar; +class LLVivoxProtocolParser; #include "lliopipe.h" #include "llpumpio.h" @@ -35,25 +42,16 @@ class LLVOAvatar; #include "v3math.h" #include "llframetimer.h" #include "llviewerregion.h" -#include "llcallingcard.h" // for LLFriendObserver -#include "llsecapi.h" -#include "llcontrol.h" - -// devices - -typedef std::vector<std::string> LLVoiceDeviceList; - +#include "m3math.h" // LLMatrix3 +class LLFriendObserver; class LLVoiceClientParticipantObserver { public: virtual ~LLVoiceClientParticipantObserver() { } - virtual void onParticipantsChanged() = 0; + virtual void onChange() = 0; }; - -/////////////////////////////////// -/// @class LLVoiceClientStatusObserver class LLVoiceClientStatusObserver { public: @@ -67,7 +65,11 @@ public: STATUS_JOINED, STATUS_LEFT_CHANNEL, STATUS_VOICE_DISABLED, + + // Adding STATUS_VOICE_ENABLED as pair status for STATUS_VOICE_DISABLED + // See LLVoiceClient::setVoiceEnabled() STATUS_VOICE_ENABLED, + BEGIN_ERROR_STATUS, ERROR_CHANNEL_FULL, ERROR_CHANNEL_LOCKED, @@ -81,447 +83,699 @@ public: static std::string status2string(EStatusType inStatus); }; -struct LLVoiceVersionInfo +class LLVoiceClient: public LLSingleton<LLVoiceClient> { - std::string serverType; - std::string serverVersion; -}; - -////////////////////////////////// -/// @class LLVoiceModuleInterface -/// @brief Voice module interface -/// -/// Voice modules should provide an implementation for this interface. -///////////////////////////////// + LOG_CLASS(LLVoiceClient); + public: + LLVoiceClient(); + ~LLVoiceClient(); + + public: + static void init(LLPumpIO *pump); // Call this once at application startup (creates connector) + static void terminate(); // Call this to clean up during shutdown + + protected: + bool writeString(const std::string &str); + + public: + + static F32 OVERDRIVEN_POWER_LEVEL; -class LLVoiceModuleInterface -{ -public: - LLVoiceModuleInterface() {} - virtual ~LLVoiceModuleInterface() {} - - virtual void init(LLPumpIO *pump)=0; // Call this once at application startup (creates connector) - virtual void terminate()=0; // Call this to clean up during shutdown - - virtual void updateSettings()=0; // call after loading settings and whenever they change - - virtual bool isVoiceWorking() const = 0; // connected to a voice server and voice channel + static const F32 VOLUME_MIN; + static const F32 VOLUME_DEFAULT; + static const F32 VOLUME_MAX; - virtual const LLVoiceVersionInfo& getVersion()=0; - - ///////////////////// - /// @name Tuning - //@{ - virtual void tuningStart()=0; - virtual void tuningStop()=0; - virtual bool inTuningMode()=0; - - virtual void tuningSetMicVolume(float volume)=0; - virtual void tuningSetSpeakerVolume(float volume)=0; - virtual float tuningGetEnergy(void)=0; - //@} - - ///////////////////// - /// @name Devices - //@{ - // 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; - - // 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 - // (use this if you want to know when it's done). - // If you pass false, you'll have no way to know when the query finishes, but the device lists will not appear empty in the interim. - virtual void refreshDeviceLists(bool clearCurrentList = true)=0; - - virtual void setCaptureDevice(const std::string& name)=0; - virtual void setRenderDevice(const std::string& name)=0; - - virtual LLVoiceDeviceList& getCaptureDevices()=0; - virtual LLVoiceDeviceList& getRenderDevices()=0; - - virtual void getParticipantList(std::set<LLUUID> &participants)=0; - virtual bool isParticipant(const LLUUID& speaker_id)=0; - //@} - - //////////////////////////// - /// @ name Channel stuff - //@{ - // returns true iff the user is currently in a proximal (local spatial) channel. - // Note that gestures should only fire if this returns true. - virtual bool inProximalChannel()=0; - - virtual void setNonSpatialChannel(const std::string &uri, - const std::string &credentials)=0; - - virtual void setSpatialChannel(const std::string &uri, - const std::string &credentials)=0; - - virtual void leaveNonSpatialChannel()=0; - - virtual void leaveChannel(void)=0; - - // Returns the URI of the current channel, or an empty string if not currently in a channel. - // NOTE that it will return an empty string if it's in the process of joining a channel. - virtual std::string getCurrentChannel()=0; - //@} - - - ////////////////////////// - /// @name invitations - //@{ - // start a voice channel with the specified user - virtual void callUser(const LLUUID &uuid)=0; - virtual bool isValidChannel(std::string& channelHandle)=0; - virtual bool answerInvite(std::string &channelHandle)=0; - virtual void declineInvite(std::string &channelHandle)=0; - //@} - - ///////////////////////// - /// @name Volume/gain - //@{ - virtual void setVoiceVolume(F32 volume)=0; - virtual void setMicGain(F32 volume)=0; - //@} - - ///////////////////////// - /// @name enable disable voice and features - //@{ - virtual bool voiceEnabled()=0; - virtual void setVoiceEnabled(bool enabled)=0; - virtual void setLipSyncEnabled(BOOL enabled)=0; - virtual BOOL lipSyncEnabled()=0; - virtual void setMuteMic(bool muted)=0; // Use this to mute the local mic (for when the client is minimized, etc), ignoring user PTT state. - //@} - - //////////////////////// - /// @name PTT - //@{ - virtual void setUserPTTState(bool ptt)=0; - virtual bool getUserPTTState()=0; - virtual void setUsePTT(bool usePTT)=0; - virtual void setPTTIsToggle(bool PTTIsToggle)=0; - virtual bool getPTTIsToggle()=0; - virtual void toggleUserPTTState(void)=0; - virtual void inputUserControlState(bool down)=0; // interpret any sort of up-down mic-open control input according to ptt-toggle prefs + void updateSettings(); // call after loading settings and whenever they change - virtual void keyDown(KEY key, MASK mask)=0; - virtual void keyUp(KEY key, MASK mask)=0; - virtual void middleMouseState(bool down)=0; - //@} - - ////////////////////////// - /// @name nearby speaker accessors - //@{ - virtual BOOL getVoiceEnabled(const LLUUID& id)=0; // true if we've received data for this avatar - virtual std::string getDisplayName(const LLUUID& id)=0; - virtual BOOL isOnlineSIP(const LLUUID &id)=0; - virtual BOOL isParticipantAvatar(const LLUUID &id)=0; - virtual BOOL getIsSpeaking(const LLUUID& id)=0; - virtual BOOL getIsModeratorMuted(const LLUUID& id)=0; - virtual F32 getCurrentPower(const LLUUID& id)=0; // "power" is related to "amplitude" in a defined way. I'm just not sure what the formula is... - virtual BOOL getOnMuteList(const LLUUID& id)=0; - virtual F32 getUserVolume(const LLUUID& id)=0; - virtual void setUserVolume(const LLUUID& id, F32 volume)=0; // set's volume for specified agent, from 0-1 (where .5 is nominal) - //@} - - ////////////////////////// - /// @name text chat - //@{ - 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 void endUserIMSession(const LLUUID &uuid)=0; - //@} - - // authorize the user - virtual void userAuthorized(const std::string& user_id, - const LLUUID &agentID)=0; - - ////////////////////////////// - /// @name Status notification - //@{ - virtual void addObserver(LLVoiceClientStatusObserver* observer)=0; - virtual void removeObserver(LLVoiceClientStatusObserver* observer)=0; - virtual void addObserver(LLFriendObserver* observer)=0; - virtual void removeObserver(LLFriendObserver* observer)=0; - virtual void addObserver(LLVoiceClientParticipantObserver* observer)=0; - virtual void removeObserver(LLVoiceClientParticipantObserver* observer)=0; - //@} - - virtual std::string sipURIFromID(const LLUUID &id)=0; - //@} - -}; + void getCaptureDevicesSendMessage(); + void getRenderDevicesSendMessage(); + + void clearCaptureDevices(); + void addCaptureDevice(const std::string& name); + void setCaptureDevice(const std::string& name); + + void clearRenderDevices(); + void addRenderDevice(const std::string& name); + void setRenderDevice(const std::string& name); + + void tuningStart(); + void tuningStop(); + bool inTuningMode(); + bool inTuningStates(); + + void tuningRenderStartSendMessage(const std::string& name, bool loop); + void tuningRenderStopSendMessage(); + void tuningCaptureStartSendMessage(int duration); + void tuningCaptureStopSendMessage(); + + void tuningSetMicVolume(float volume); + void tuningSetSpeakerVolume(float volume); + float tuningGetEnergy(void); + + // 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(); + + // 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 + // (use this if you want to know when it's done). + // If you pass false, you'll have no way to know when the query finishes, but the device lists will not appear empty in the interim. + void refreshDeviceLists(bool clearCurrentList = true); + + // Call this if the connection to the daemon terminates unexpectedly. It will attempt to reset everything and relaunch. + void daemonDied(); -////////////////////////////////// -/// @class LLVoiceEffectObserver -class LLVoiceEffectObserver -{ -public: - virtual ~LLVoiceEffectObserver() { } - virtual void onVoiceEffectChanged(bool effect_list_updated) = 0; -}; + // Call this if we're just giving up on voice (can't provision an account, etc.). It will clean up and go away. + void giveUp(); + + ///////////////////////////// + // Response/Event handlers + void connectorCreateResponse(int statusCode, std::string &statusString, std::string &connectorHandle, std::string &versionID); + void loginResponse(int statusCode, std::string &statusString, std::string &accountHandle, int numberOfAliases); + void sessionCreateResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle); + void sessionGroupAddSessionResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle); + void sessionConnectResponse(std::string &requestId, int statusCode, std::string &statusString); + void logoutResponse(int statusCode, std::string &statusString); + void connectorShutdownResponse(int statusCode, std::string &statusString); + + void accountLoginStateChangeEvent(std::string &accountHandle, int statusCode, std::string &statusString, int state); + 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); + void participantAddedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, std::string &nameString, std::string &displayNameString, int participantType); + void participantRemovedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, std::string &nameString); + void participantUpdatedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, bool isModeratorMuted, bool isSpeaking, int volume, F32 energy); + void auxAudioPropertiesEvent(F32 energy); + void buddyPresenceEvent(std::string &uriString, std::string &alias, std::string &statusString, std::string &applicationString); + void messageEvent(std::string &sessionHandle, std::string &uriString, std::string &alias, std::string &messageHeader, std::string &messageBody, std::string &applicationString); + void sessionNotificationEvent(std::string &sessionHandle, std::string &uriString, std::string ¬ificationType); + void subscriptionEvent(std::string &buddyURI, std::string &subscriptionHandle, std::string &alias, std::string &displayName, std::string &applicationString, std::string &subscriptionType); + + void buddyListChanged(); + void muteListChanged(); + void updateFriends(U32 mask); + + ///////////////////////////// + // Sending updates of current state +static void updatePosition(void); + void setCameraPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot); + void setAvatarPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot); + bool channelFromRegion(LLViewerRegion *region, std::string &name); + void leaveChannel(void); // call this on logout or teleport begin -typedef std::multimap<const std::string, const LLUUID, LLDictionaryLess> voice_effect_list_t; + + void setMuteMic(bool muted); // Use this to mute the local mic (for when the client is minimized, etc), ignoring user PTT state. + bool getMuteMic() const; + void setUserPTTState(bool ptt); + bool getUserPTTState(); + void toggleUserPTTState(void); + void inputUserControlState(bool down); // interpret any sort of up-down mic-open control input according to ptt-toggle prefs + void setVoiceEnabled(bool enabled); + static bool voiceEnabled(); + // Checks is voice working judging from mState + // Returns true if vivox has successfully logged in and is not in error state + bool voiceWorking(); + void setUsePTT(bool usePTT); + void setPTTIsToggle(bool PTTIsToggle); + bool getPTTIsToggle(); + void setPTTKey(std::string &key); + void setEarLocation(S32 loc); + void setVoiceVolume(F32 volume); + void setMicGain(F32 volume); + void setUserVolume(const LLUUID& id, F32 volume); // sets volume for specified agent, from 0-1 (where .5 is nominal) + void setLipSyncEnabled(BOOL enabled); + BOOL lipSyncEnabled(); + + // PTT key triggering + void keyDown(KEY key, MASK mask); + void keyUp(KEY key, MASK mask); + void middleMouseState(bool down); + + // Return the version of the Vivox library + std::string getAPIVersion() const { return mAPIVersion; } + + ///////////////////////////// + // Accessors for data related to nearby speakers + BOOL getVoiceEnabled(const LLUUID& id); // true if we've received data for this avatar + BOOL getIsSpeaking(const LLUUID& id); + BOOL getIsModeratorMuted(const LLUUID& id); + F32 getCurrentPower(const LLUUID& id); // "power" is related to "amplitude" in a defined way. I'm just not sure what the formula is... + BOOL getOnMuteList(const LLUUID& id); + F32 getUserVolume(const LLUUID& id); + std::string getDisplayName(const LLUUID& id); + + // MBW -- XXX -- Not sure how to get this data out of the TVC + BOOL getUsingPTT(const LLUUID& id); + std::string getGroupID(const LLUUID& id); // group ID if the user is in group chat (empty string if not applicable) -////////////////////////////////// -/// @class LLVoiceEffectInterface -/// @brief Voice effect module interface -/// -/// Voice effect modules should provide an implementation for this interface. -///////////////////////////////// + ///////////////////////////// + BOOL getAreaVoiceDisabled(); // returns true if the area the avatar is in is speech-disabled. + // Use this to determine whether to show a "no speech" icon in the menu bar. + + ///////////////////////////// + // Recording controls + void recordingLoopStart(int seconds = 3600, int deltaFramesPerControlFrame = 200); + void recordingLoopSave(const std::string& filename); + void recordingStop(); + + // Playback controls + void filePlaybackStart(const std::string& filename); + void filePlaybackStop(); + void filePlaybackSetPaused(bool paused); + void filePlaybackSetMode(bool vox = false, float speed = 1.0f); + + + // This is used by the string-keyed maps below, to avoid storing the string twice. + // The 'const std::string *' in the key points to a string actually stored in the object referenced by the map. + // The add and delete operations for each map allocate and delete in the right order to avoid dangling references. + // The default compare operation would just compare pointers, which is incorrect, so they must use this comparitor instead. + struct stringMapComparitor + { + bool operator()(const std::string* a, const std::string * b) const + { + return a->compare(*b) < 0; + } + }; + + struct uuidMapComparitor + { + bool operator()(const LLUUID* a, const LLUUID * b) const + { + return *a < *b; + } + }; + + struct participantState + { + public: + participantState(const std::string &uri); + + bool updateMuteState(); // true if mute state has changed + bool isAvatar(); + + std::string mURI; + LLUUID mAvatarID; + std::string mAccountName; + std::string mDisplayName; + LLFrameTimer mSpeakingTimeout; + F32 mLastSpokeTimestamp; + F32 mPower; + F32 mVolume; + std::string mGroupID; + bool mPTT; + bool mIsSpeaking; + bool mIsModeratorMuted; + bool mOnMuteList; // true if this avatar is on the user's mute list (and should be muted) + bool mVolumeSet; // true if incoming volume messages should not change the volume + bool mVolumeDirty; // true if this participant needs a volume command sent (either mOnMuteList or mUserVolume has changed) + bool mAvatarIDValid; + bool mIsSelf; + }; + typedef std::map<const std::string *, participantState*, stringMapComparitor> participantMap; + + typedef std::map<const LLUUID *, participantState*, uuidMapComparitor> participantUUIDMap; + + enum streamState + { + streamStateUnknown = 0, + streamStateIdle = 1, + streamStateConnected = 2, + streamStateRinging = 3, + }; + + struct sessionState + { + public: + sessionState(); + ~sessionState(); + + participantState *addParticipant(const std::string &uri); + // Note: after removeParticipant returns, the participant* that was passed to it will have been deleted. + // Take care not to use the pointer again after that. + void removeParticipant(participantState *participant); + void removeAllParticipants(); + + participantState *findParticipant(const std::string &uri); + participantState *findParticipantByID(const LLUUID& id); + + bool isCallBackPossible(); + bool isTextIMPossible(); + + std::string mHandle; + std::string mGroupHandle; + std::string mSIPURI; + std::string mAlias; + std::string mName; + std::string mAlternateSIPURI; + std::string mHash; // Channel password + std::string mErrorStatusString; + std::queue<std::string> mTextMsgQueue; + + LLUUID mIMSessionID; + 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) + bool mTextInvitePending; // True if a text invite is pending for this session (usually waiting on a name lookup) + bool mSynthesizedCallerID; // True if the caller ID is a hash of the SIP URI -- this means we shouldn't do a name lookup. + bool mIsChannel; // True for both group and spatial channels (false for p2p, PSTN) + bool mIsSpatial; // True for spatial channels + bool mIsP2P; + bool mIncoming; + bool mVoiceEnabled; + bool mReconnect; // Whether we should try to reconnect to this session if it's dropped + // Set to true when the mute state of someone in the participant list changes. + // The code will have to walk the list to find the changed participant(s). + bool mVolumeDirty; + bool mMuteDirty; + + bool mParticipantsChanged; + participantMap mParticipantsByURI; + participantUUIDMap mParticipantsByUUID; + }; + + participantState *findParticipantByID(const LLUUID& id); + participantMap *getParticipantList(void); + void getParticipantsUUIDSet(std::set<LLUUID>& participant_uuids); + + typedef std::map<const std::string*, sessionState*, stringMapComparitor> sessionMap; + typedef std::set<sessionState*> sessionSet; + + typedef sessionSet::iterator sessionIterator; + sessionIterator sessionsBegin(void); + sessionIterator sessionsEnd(void); + + sessionState *findSession(const std::string &handle); + sessionState *findSessionBeingCreatedByURI(const std::string &uri); + sessionState *findSession(const LLUUID &participant_id); + sessionState *findSessionByCreateID(const std::string &create_id); + + sessionState *addSession(const std::string &uri, const std::string &handle = LLStringUtil::null); + void setSessionHandle(sessionState *session, const std::string &handle = LLStringUtil::null); + void setSessionURI(sessionState *session, const std::string &uri); + void deleteSession(sessionState *session); + void deleteAllSessions(void); -class LLVoiceEffectInterface -{ -public: - LLVoiceEffectInterface() {} - virtual ~LLVoiceEffectInterface() {} - - ////////////////////////// - /// @name Accessors - //@{ - virtual bool setVoiceEffect(const LLUUID& id) = 0; - virtual const LLUUID getVoiceEffect() = 0; - virtual LLSD getVoiceEffectProperties(const LLUUID& id) = 0; - - virtual void refreshVoiceEffectLists(bool clear_lists) = 0; - virtual const voice_effect_list_t &getVoiceEffectList() const = 0; - virtual const voice_effect_list_t &getVoiceEffectTemplateList() const = 0; - //@} - - ////////////////////////////// - /// @name Status notification - //@{ - virtual void addObserver(LLVoiceEffectObserver* observer) = 0; - virtual void removeObserver(LLVoiceEffectObserver* observer) = 0; - //@} - - ////////////////////////////// - /// @name Preview buffer - //@{ - virtual void enablePreviewBuffer(bool enable) = 0; - virtual void recordPreviewBuffer() = 0; - virtual void playPreviewBuffer(const LLUUID& effect_id = LLUUID::null) = 0; - virtual void stopPreviewBuffer() = 0; - - virtual bool isPreviewRecording() = 0; - virtual bool isPreviewPlaying() = 0; - //@} -}; + void verifySessionState(void); + void joinedAudioSession(sessionState *session); + void leftAudioSession(sessionState *session); -class LLVoiceClient: public LLSingleton<LLVoiceClient> -{ - LOG_CLASS(LLVoiceClient); -public: - LLVoiceClient(); - ~LLVoiceClient(); + // This is called in several places where the session _may_ need to be deleted. + // It contains logic for whether to delete the session or keep it around. + void reapSession(sessionState *session); + + // Returns true if the session seems to indicate we've moved to a region on a different voice server + bool sessionNeedsRelog(sessionState *session); + + struct buddyListEntry + { + buddyListEntry(const std::string &uri); + std::string mURI; + std::string mDisplayName; + LLUUID mUUID; + bool mOnlineSL; + bool mOnlineSLim; + bool mCanSeeMeOnline; + bool mHasBlockListEntry; + bool mHasAutoAcceptListEntry; + bool mNameResolved; + bool mInSLFriends; + bool mInVivoxBuddies; + bool mNeedsNameUpdate; + }; + + typedef std::map<const std::string*, buddyListEntry*, stringMapComparitor> buddyListMap; + + // This should be called when parsing a buddy list entry sent by SLVoice. + void processBuddyListEntry(const std::string &uri, const std::string &displayName); + + buddyListEntry *addBuddy(const std::string &uri); + buddyListEntry *addBuddy(const std::string &uri, const std::string &displayName); + buddyListEntry *findBuddy(const std::string &uri); + buddyListEntry *findBuddy(const LLUUID &id); + buddyListEntry *findBuddyByDisplayName(const std::string &name); + void deleteBuddy(const std::string &uri); + void deleteAllBuddies(void); + + void deleteAllBlockRules(void); + void addBlockRule(const std::string &blockMask, const std::string &presenceOnly); + void deleteAllAutoAcceptRules(void); + void addAutoAcceptRule(const std::string &autoAcceptMask, const std::string &autoAddAsBuddy); + void accountListBlockRulesResponse(int statusCode, const std::string &statusString); + void accountListAutoAcceptRulesResponse(int statusCode, const std::string &statusString); + + ///////////////////////////// + // session control messages + void connectorCreate(); + void connectorShutdown(); + + void requestVoiceAccountProvision(S32 retries = 3); + void userAuthorized( + const std::string& firstName, + const std::string& lastName, + const LLUUID &agentID); + void login( + const std::string& account_name, + const std::string& password, + const std::string& voice_sip_uri_hostname, + const std::string& voice_account_server_uri); + void loginSendMessage(); + void logout(); + void logoutSendMessage(); + + void accountListBlockRulesSendMessage(); + void accountListAutoAcceptRulesSendMessage(); + + void sessionGroupCreateSendMessage(); + void sessionCreateSendMessage(sessionState *session, bool startAudio = true, bool startText = false); + void sessionGroupAddSessionSendMessage(sessionState *session, bool startAudio = true, bool startText = false); + void sessionMediaConnectSendMessage(sessionState *session); // just joins the audio session + void sessionTextConnectSendMessage(sessionState *session); // just joins the text session + void sessionTerminateSendMessage(sessionState *session); + void sessionGroupTerminateSendMessage(sessionState *session); + void sessionMediaDisconnectSendMessage(sessionState *session); + void sessionTextDisconnectSendMessage(sessionState *session); + + // Pokes the state machine to leave the audio session next time around. + void sessionTerminate(); + + // Pokes the state machine to shut down the connector and restart it. + void requestRelog(); + + // Does the actual work to get out of the audio session + void leaveAudioSession(); + + void addObserver(LLVoiceClientParticipantObserver* observer); + void removeObserver(LLVoiceClientParticipantObserver* observer); - void init(LLPumpIO *pump); // Call this once at application startup (creates connector) - void terminate(); // Call this to clean up during shutdown - - const LLVoiceVersionInfo getVersion(); - - static const F32 OVERDRIVEN_POWER_LEVEL; + void addObserver(LLVoiceClientStatusObserver* observer); + void removeObserver(LLVoiceClientStatusObserver* observer); - static const F32 VOLUME_MIN; - static const F32 VOLUME_DEFAULT; - static const F32 VOLUME_MAX; + void addObserver(LLFriendObserver* observer); + void removeObserver(LLFriendObserver* observer); + + void lookupName(const LLUUID &id); + static void onAvatarNameLookup(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group); + void avatarNameResolved(const LLUUID &id, const std::string &name); + + typedef std::vector<std::string> deviceList; - void updateSettings(); // call after loading settings and whenever they change + deviceList *getCaptureDevices(); + deviceList *getRenderDevices(); + + void setNonSpatialChannel( + const std::string &uri, + const std::string &credentials); + void setSpatialChannel( + const std::string &uri, + const std::string &credentials); + // start a voice session with the specified user + void callUser(const LLUUID &uuid); + + // Send a text message to the specified user, initiating the session if necessary. + bool sendTextMessage(const LLUUID& participant_id, const std::string& message); + + // close any existing text IM session with the specified user + void endUserIMSession(const LLUUID &uuid); + + bool answerInvite(std::string &sessionHandle); + void declineInvite(std::string &sessionHandle); + void leaveNonSpatialChannel(); - bool isVoiceWorking() const; // connected to a voice server and voice channel + // Returns the URI of the current channel, or an empty string if not currently in a channel. + // NOTE that it will return an empty string if it's in the process of joining a channel. + std::string getCurrentChannel(); + + // returns true iff the user is currently in a proximal (local spatial) channel. + // Note that gestures should only fire if this returns true. + bool inProximalChannel(); - // tuning - void tuningStart(); - void tuningStop(); - bool inTuningMode(); + std::string sipURIFromID(const LLUUID &id); + + // Returns true if the indicated user is online via SIP presence according to SLVoice. + // Note that we only get SIP presence data for other users that are in our vivox buddy list. + bool isOnlineSIP(const LLUUID &id); + + // Returns true if the indicated participant is really an SL avatar. + // This should be used to control the state of the "profile" button. + // Currently this will be false only for PSTN callers into group chats, and PSTN p2p calls. + bool isParticipantAvatar(const LLUUID &id); + + // Returns true if calling back the session URI after the session has closed is possible. + // Currently this will be false only for PSTN P2P calls. + // NOTE: this will return true if the session can't be found. + bool isSessionCallBackPossible(const LLUUID &session_id); + + // Returns true if the session can accepte text IM's. + // Currently this will be false only for PSTN P2P calls. + // NOTE: this will return true if the session can't be found. + bool isSessionTextIMPossible(const LLUUID &session_id); + + private: + + // internal state for a simple state machine. This is used to deal with the asynchronous nature of some of the messages. + // Note: if you change this list, please make corresponding changes to LLVoiceClient::state2string(). + enum state + { + stateDisableCleanup, + stateDisabled, // Voice is turned off. + stateStart, // Class is initialized, socket is created + stateDaemonLaunched, // Daemon has been launched + stateConnecting, // connect() call has been issued + stateConnected, // connection to the daemon has been made, send some initial setup commands. + stateIdle, // socket is connected, ready for messaging + stateMicTuningStart, + stateMicTuningRunning, + stateMicTuningStop, + stateConnectorStart, // connector needs to be started + stateConnectorStarting, // waiting for connector handle + stateConnectorStarted, // connector handle received + stateLoginRetry, // need to retry login (failed due to changing password) + stateLoginRetryWait, // waiting for retry timer + stateNeedsLogin, // send login request + stateLoggingIn, // waiting for account handle + stateLoggedIn, // account handle received + stateCreatingSessionGroup, // Creating the main session group + stateNoChannel, // + stateJoiningSession, // waiting for session handle + stateSessionJoined, // session handle received + stateRunning, // in session, steady state + stateLeavingSession, // waiting for terminate session response + stateSessionTerminated, // waiting for terminate session response + + stateLoggingOut, // waiting for logout response + stateLoggedOut, // logout response received + stateConnectorStopping, // waiting for connector stop + stateConnectorStopped, // connector stop received + + // We go to this state if the login fails because the account needs to be provisioned. + + // error states. No way to recover from these yet. + stateConnectorFailed, + stateConnectorFailedWaiting, + stateLoginFailed, + stateLoginFailedWaiting, + stateJoinSessionFailed, + stateJoinSessionFailedWaiting, + + stateJail // Go here when all else has failed. Nothing will be retried, we're done. + }; + + state mState; + bool mSessionTerminateRequested; + bool mRelogRequested; + // Number of times (in a row) "stateJoiningSession" case for spatial channel is reached in stateMachine(). + // The larger it is the greater is possibility there is a problem with connection to voice server. + // Introduced while fixing EXT-4313. + int mSpatialJoiningNum; + + void setState(state inState); + state getState(void) { return mState; }; + static std::string state2string(state inState); + + void stateMachine(); + static void idle(void *user_data); + + LLHost mDaemonHost; + LLSocket::ptr_t mSocket; + bool mConnected; + + void closeSocket(void); - void tuningSetMicVolume(float volume); - void tuningSetSpeakerVolume(float volume); - float tuningGetEnergy(void); + LLPumpIO *mPump; + friend class LLVivoxProtocolParser; + + std::string mAccountName; + std::string mAccountPassword; + std::string mAccountDisplayName; + std::string mAccountFirstName; + std::string mAccountLastName; - // devices - - // 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 mTuningMode; + float mTuningEnergy; + std::string mTuningAudioFile; + int mTuningMicVolume; + bool mTuningMicVolumeDirty; + int mTuningSpeakerVolume; + bool mTuningSpeakerVolumeDirty; + state mTuningExitState; // state to return to when we leave tuning mode. - // 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 - // (use this if you want to know when it's done). - // If you pass false, you'll have no way to know when the query finishes, but the device lists will not appear empty in the interim. - void refreshDeviceLists(bool clearCurrentList = true); + std::string mSpatialSessionURI; + std::string mSpatialSessionCredentials; - void setCaptureDevice(const std::string& name); - void setRenderDevice(const std::string& name); + std::string mMainSessionGroupHandle; // handle of the "main" session group. + + std::string mChannelName; // Name of the channel to be looked up + bool mAreaVoiceDisabled; + sessionState *mAudioSession; // Session state for the current audio session + bool mAudioSessionChanged; // set to true when the above pointer gets changed, so observers can be notified. - const LLVoiceDeviceList& getCaptureDevices(); - const LLVoiceDeviceList& getRenderDevices(); + sessionState *mNextAudioSession; // Session state for the audio session we're trying to join - //////////////////////////// - // Channel stuff - // - - // returns true iff the user is currently in a proximal (local spatial) channel. - // Note that gestures should only fire if this returns true. - bool inProximalChannel(); - void setNonSpatialChannel( - const std::string &uri, - const std::string &credentials); - void setSpatialChannel( - const std::string &uri, - const std::string &credentials); - void leaveNonSpatialChannel(); - - // Returns the URI of the current channel, or an empty string if not currently in a channel. - // NOTE that it will return an empty string if it's in the process of joining a channel. - std::string getCurrentChannel(); - // start a voice channel with the specified user - void callUser(const LLUUID &uuid); - bool isValidChannel(std::string& channelHandle); - bool answerInvite(std::string &channelHandle); - void declineInvite(std::string &channelHandle); - void leaveChannel(void); // call this on logout or teleport begin - - - ///////////////////////////// - // Sending updates of current state +// std::string mSessionURI; // URI of the session we're in. +// std::string mSessionHandle; // returned by ? + + S32 mCurrentParcelLocalID; // Used to detect parcel boundary crossings + std::string mCurrentRegionName; // Used to detect parcel boundary crossings + + std::string mConnectorHandle; // returned by "Create Connector" message + std::string mAccountHandle; // returned by login message + int mNumberOfAliases; + U32 mCommandCookie; + std::string mVoiceAccountServerURI; + std::string mVoiceSIPURIHostName; + + int mLoginRetryCount; + + sessionMap mSessionsByHandle; // Active sessions, indexed by session handle. Sessions which are being initiated may not be in this map. + sessionSet mSessions; // All sessions, not indexed. This is the canonical session list. + + bool mBuddyListMapPopulated; + bool mBlockRulesListReceived; + bool mAutoAcceptRulesListReceived; + buddyListMap mBuddyListMap; + + deviceList mCaptureDevices; + deviceList mRenderDevices; - void setVoiceVolume(F32 volume); - void setMicGain(F32 volume); - void setUserVolume(const LLUUID& id, F32 volume); // set's volume for specified agent, from 0-1 (where .5 is nominal) - bool voiceEnabled(); - void setLipSyncEnabled(BOOL enabled); - void setMuteMic(bool muted); // Use this to mute the local mic (for when the client is minimized, etc), ignoring user PTT state. - void setUserPTTState(bool ptt); - bool getUserPTTState(); - void toggleUserPTTState(void); - void inputUserControlState(bool down); // interpret any sort of up-down mic-open control input according to ptt-toggle prefs - void setVoiceEnabled(bool enabled); - - void setUsePTT(bool usePTT); - void setPTTIsToggle(bool PTTIsToggle); - bool getPTTIsToggle(); - - BOOL lipSyncEnabled(); - - // PTT key triggering - void keyDown(KEY key, MASK mask); - void keyUp(KEY key, MASK mask); - void middleMouseState(bool down); - - - ///////////////////////////// - // Accessors for data related to nearby speakers - BOOL getVoiceEnabled(const LLUUID& id); // true if we've received data for this avatar - std::string getDisplayName(const LLUUID& id); - BOOL isOnlineSIP(const LLUUID &id); - BOOL isParticipantAvatar(const LLUUID &id); - BOOL getIsSpeaking(const LLUUID& id); - BOOL getIsModeratorMuted(const LLUUID& id); - F32 getCurrentPower(const LLUUID& id); // "power" is related to "amplitude" in a defined way. I'm just not sure what the formula is... - BOOL getOnMuteList(const LLUUID& id); - F32 getUserVolume(const LLUUID& id); - - ///////////////////////////// - BOOL getAreaVoiceDisabled(); // returns true if the area the avatar is in is speech-disabled. - // Use this to determine whether to show a "no speech" icon in the menu bar. - void getParticipantList(std::set<LLUUID> &participants); - bool isParticipant(const LLUUID& speaker_id); - - ////////////////////////// - /// @name text chat - //@{ - BOOL isSessionTextIMPossible(const LLUUID& id); - BOOL isSessionCallBackPossible(const LLUUID& id); - BOOL sendTextMessage(const LLUUID& participant_id, const std::string& message); - void endUserIMSession(const LLUUID &uuid); - //@} - + std::string mCaptureDevice; + std::string mRenderDevice; + bool mCaptureDeviceDirty; + bool mRenderDeviceDirty; + + // This should be called when the code detects we have changed parcels. + // It initiates the call to the server that gets the parcel channel. + void parcelChanged(); + + void switchChannel(std::string uri = std::string(), bool spatial = true, bool no_reconnect = false, bool is_p2p = false, std::string hash = ""); + void joinSession(sessionState *session); + +static std::string nameFromAvatar(LLVOAvatar *avatar); +static std::string nameFromID(const LLUUID &id); +static bool IDFromName(const std::string name, LLUUID &uuid); +static std::string displayNameFromAvatar(LLVOAvatar *avatar); + std::string sipURIFromAvatar(LLVOAvatar *avatar); + std::string sipURIFromName(std::string &name); + + // Returns the name portion of the SIP URI if the string looks vaguely like a SIP URI, or an empty string if not. +static std::string nameFromsipURI(const std::string &uri); - void userAuthorized(const std::string& user_id, - const LLUUID &agentID); - - void addObserver(LLVoiceClientStatusObserver* observer); - void removeObserver(LLVoiceClientStatusObserver* observer); - void addObserver(LLFriendObserver* observer); - void removeObserver(LLFriendObserver* observer); - void addObserver(LLVoiceClientParticipantObserver* observer); - void removeObserver(LLVoiceClientParticipantObserver* observer); - - std::string sipURIFromID(const LLUUID &id); + bool inSpatialChannel(void); + std::string getAudioSessionURI(); + std::string getAudioSessionHandle(); + + void sendPositionalUpdate(void); + + void buildSetCaptureDevice(std::ostringstream &stream); + void buildSetRenderDevice(std::ostringstream &stream); + void buildLocalAudioUpdates(std::ostringstream &stream); + + void clearAllLists(); + void checkFriend(const LLUUID& id); + void sendFriendsListUpdates(); + + // 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); + + bool mSpatialCoordsDirty; + + LLVector3d mCameraPosition; + LLVector3d mCameraRequestedPosition; + LLVector3 mCameraVelocity; + LLMatrix3 mCameraRot; + + LLVector3d mAvatarPosition; + LLVector3 mAvatarVelocity; + LLMatrix3 mAvatarRot; + + bool mPTTDirty; + bool mPTT; + + bool mUsePTT; + bool mPTTIsMiddleMouse; + KEY mPTTKey; + bool mPTTIsToggle; + bool mUserPTTState; + bool mMuteMic; + + // Set to true when the friends list is known to have changed. + bool mFriendsListDirty; + + enum + { + earLocCamera = 0, // ear at camera + earLocAvatar, // ear at avatar + earLocMixed // ear at avatar location/camera direction + }; + + S32 mEarLocation; + + bool mSpeakerVolumeDirty; + bool mSpeakerMuteDirty; + int mSpeakerVolume; - ////////////////////////// - /// @name Voice effects - //@{ - bool getVoiceEffectEnabled() const { return mVoiceEffectEnabled; }; - LLUUID getVoiceEffectDefault() const { return LLUUID(mVoiceEffectDefault); }; + int mMicVolume; + bool mMicVolumeDirty; + + bool mVoiceEnabled; + bool mWriteInProgress; + std::string mWriteString; + + LLTimer mUpdateTimer; + + BOOL mLipSyncEnabled; - // Returns NULL if voice effects are not supported, or not enabled. - LLVoiceEffectInterface* getVoiceEffectInterface() const; - //@} + std::string mAPIVersion; -protected: - LLVoiceModuleInterface* mVoiceModule; - LLPumpIO *m_servicePump; + typedef std::set<LLVoiceClientParticipantObserver*> observer_set_t; + observer_set_t mParticipantObservers; - LLCachedControl<bool> mVoiceEffectEnabled; - LLCachedControl<std::string> mVoiceEffectDefault; -}; + void notifyParticipantObservers(); -/** - * Speaker volume storage helper class - **/ -class LLSpeakerVolumeStorage : public LLSingleton<LLSpeakerVolumeStorage> -{ - LOG_CLASS(LLSpeakerVolumeStorage); -public: + typedef std::set<LLVoiceClientStatusObserver*> status_observer_set_t; + status_observer_set_t mStatusObservers; + + void notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status); - /** - * Stores volume level for specified user. - * - * @param[in] speaker_id - LLUUID of user to store volume level for. - * @param[in] volume - volume level to be stored for user. - */ - void storeSpeakerVolume(const LLUUID& speaker_id, F32 volume); - - /** - * Gets stored volume level for specified speaker - * - * @param[in] speaker_id - LLUUID of user to retrieve volume level for. - * @param[out] volume - set to stored volume if found, otherwise unmodified. - * @return - true if a stored volume is found. - */ - bool getSpeakerVolume(const LLUUID& speaker_id, F32& volume); - - /** - * Removes stored volume level for specified user. - * - * @param[in] speaker_id - LLUUID of user to remove. - */ - void removeSpeakerVolume(const LLUUID& speaker_id); - -private: - friend class LLSingleton<LLSpeakerVolumeStorage>; - LLSpeakerVolumeStorage(); - ~LLSpeakerVolumeStorage(); - - const static std::string SETTINGS_FILE_NAME; - - void load(); - void save(); - - static F32 transformFromLegacyVolume(F32 volume_in); - static F32 transformToLegacyVolume(F32 volume_in); - - typedef std::map<LLUUID, F32> speaker_data_map_t; - speaker_data_map_t mSpeakersData; + typedef std::set<LLFriendObserver*> friend_observer_set_t; + friend_observer_set_t mFriendObservers; + void notifyFriendObservers(); }; +extern LLVoiceClient *gVoiceClient; + #endif //LL_VOICE_CLIENT_H |