945 lines
26 KiB
C++
945 lines
26 KiB
C++
/**
|
|
* @file llvoicechannel.cpp
|
|
* @brief Voice Channel related classes
|
|
*
|
|
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
|
|
* Second Life Viewer Source Code
|
|
* 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.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
|
* $/LicenseInfo$
|
|
*/
|
|
|
|
#include "llviewerprecompiledheaders.h"
|
|
|
|
#include "llagent.h"
|
|
#include "llfloaterreg.h"
|
|
#include "llimview.h"
|
|
#include "llnotifications.h"
|
|
#include "llnotificationsutil.h"
|
|
#include "llpanel.h"
|
|
#include "llrecentpeople.h"
|
|
#include "llviewercontrol.h"
|
|
#include "llviewerregion.h"
|
|
#include "llvoicechannel.h"
|
|
#include "llcorehttputil.h"
|
|
|
|
LLVoiceChannel::voice_channel_map_t LLVoiceChannel::sVoiceChannelMap;
|
|
LLVoiceChannel* LLVoiceChannel::sCurrentVoiceChannel = NULL;
|
|
LLVoiceChannel* LLVoiceChannel::sSuspendedVoiceChannel = NULL;
|
|
LLVoiceChannel::channel_changed_signal_t LLVoiceChannel::sCurrentVoiceChannelChangedSignal;
|
|
|
|
bool LLVoiceChannel::sSuspended = false;
|
|
|
|
//
|
|
// Constants
|
|
//
|
|
const U32 DEFAULT_RETRIES_COUNT = 3;
|
|
|
|
//
|
|
// LLVoiceChannel
|
|
//
|
|
LLVoiceChannel::LLVoiceChannel(const LLUUID& session_id, const std::string& session_name) :
|
|
mSessionID(session_id),
|
|
mState(STATE_NO_CHANNEL_INFO),
|
|
mSessionName(session_name),
|
|
mCallDirection(OUTGOING_CALL),
|
|
mIgnoreNextSessionLeave(false),
|
|
mCallEndedByAgent(false)
|
|
{
|
|
mNotifyArgs["VOICE_CHANNEL_NAME"] = mSessionName;
|
|
|
|
if (!sVoiceChannelMap.insert(std::make_pair(session_id, this)).second)
|
|
{
|
|
// a voice channel already exists for this session id, so this instance will be orphaned
|
|
// the end result should simply be the failure to make voice calls
|
|
LL_WARNS("Voice") << "Duplicate voice channels registered for session_id " << session_id << LL_ENDL;
|
|
}
|
|
}
|
|
|
|
LLVoiceChannel::~LLVoiceChannel()
|
|
{
|
|
if (sSuspendedVoiceChannel == this)
|
|
{
|
|
sSuspendedVoiceChannel = NULL;
|
|
}
|
|
if (sCurrentVoiceChannel == this)
|
|
{
|
|
sCurrentVoiceChannel = NULL;
|
|
// Must check instance exists here, the singleton MAY have already been destroyed.
|
|
if(LLVoiceClient::instanceExists())
|
|
{
|
|
LLVoiceClient::getInstance()->removeObserver(this);
|
|
}
|
|
}
|
|
|
|
sVoiceChannelMap.erase(mSessionID);
|
|
}
|
|
|
|
void LLVoiceChannel::setChannelInfo(const LLSD &channelInfo)
|
|
{
|
|
mChannelInfo = channelInfo;
|
|
|
|
if (mState == STATE_NO_CHANNEL_INFO)
|
|
{
|
|
if (mChannelInfo.isUndefined() || !mChannelInfo.isMap() || mChannelInfo.size() == 0)
|
|
{
|
|
LLNotificationsUtil::add("VoiceChannelJoinFailed", mNotifyArgs);
|
|
LL_WARNS("Voice") << "Received empty channel info for channel " << mSessionName << LL_ENDL;
|
|
deactivate();
|
|
}
|
|
else
|
|
{
|
|
setState(STATE_READY);
|
|
|
|
// if we are supposed to be active, reconnect
|
|
// this will happen on initial connect, as we request credentials on first use
|
|
if (sCurrentVoiceChannel == this)
|
|
{
|
|
// just in case we got new channel info while active
|
|
// should move over to new channel
|
|
activate();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void LLVoiceChannel::onChange(EStatusType type, const LLSD& channelInfo, bool proximal)
|
|
{
|
|
LL_DEBUGS("Voice") << "Incoming channel info: " << channelInfo << LL_ENDL;
|
|
LL_DEBUGS("Voice") << "Current channel info: " << mChannelInfo << LL_ENDL;
|
|
if (mChannelInfo.isUndefined() || (mChannelInfo.isMap() && mChannelInfo.size() == 0))
|
|
{
|
|
mChannelInfo = channelInfo;
|
|
}
|
|
if (!LLVoiceClient::getInstance()->compareChannels(mChannelInfo, channelInfo))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (type < BEGIN_ERROR_STATUS)
|
|
{
|
|
handleStatusChange(type);
|
|
}
|
|
else
|
|
{
|
|
handleError(type);
|
|
}
|
|
}
|
|
|
|
void LLVoiceChannel::handleStatusChange(EStatusType type)
|
|
{
|
|
// status updates
|
|
switch(type)
|
|
{
|
|
case STATUS_LOGIN_RETRY:
|
|
// no user notice
|
|
break;
|
|
case STATUS_LOGGED_IN:
|
|
break;
|
|
case STATUS_LEFT_CHANNEL:
|
|
if (callStarted() && !sSuspended)
|
|
{
|
|
// if forceably removed from channel
|
|
// update the UI and revert to default channel
|
|
// deactivate will set the State to STATE_HUNG_UP
|
|
// so when handleStatusChange is called again during
|
|
// shutdown callStarted will return false and deactivate
|
|
// won't be called again.
|
|
deactivate();
|
|
}
|
|
break;
|
|
case STATUS_JOINING:
|
|
if (callStarted())
|
|
{
|
|
setState(STATE_RINGING);
|
|
}
|
|
break;
|
|
case STATUS_JOINED:
|
|
if (callStarted())
|
|
{
|
|
setState(STATE_CONNECTED);
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// default behavior is to just deactivate channel
|
|
// derived classes provide specific error messages
|
|
void LLVoiceChannel::handleError(EStatusType type)
|
|
{
|
|
deactivate();
|
|
setState(STATE_ERROR);
|
|
}
|
|
|
|
bool LLVoiceChannel::isActive() const
|
|
{
|
|
// only considered active when currently bound channel matches what our channel
|
|
return callStarted() && LLVoiceClient::getInstance()->isCurrentChannel(mChannelInfo);
|
|
}
|
|
|
|
bool LLVoiceChannel::callStarted() const
|
|
{
|
|
return mState >= STATE_CALL_STARTED;
|
|
}
|
|
|
|
void LLVoiceChannel::deactivate()
|
|
{
|
|
if (mState >= STATE_RINGING)
|
|
{
|
|
// ignore session leave event
|
|
mIgnoreNextSessionLeave = true;
|
|
}
|
|
|
|
if (callStarted())
|
|
{
|
|
setState(STATE_HUNG_UP);
|
|
|
|
//Default mic is OFF when leaving voice calls
|
|
if (gSavedSettings.getBOOL("AutoDisengageMic") &&
|
|
sCurrentVoiceChannel == this &&
|
|
LLVoiceClient::getInstance()->getUserPTTState())
|
|
{
|
|
gSavedSettings.setBOOL("PTTCurrentlyEnabled", true);
|
|
LLVoiceClient::getInstance()->setUserPTTState(false);
|
|
}
|
|
}
|
|
LLVoiceClient::getInstance()->removeObserver(this);
|
|
|
|
if (sCurrentVoiceChannel == this)
|
|
{
|
|
// default channel is proximal channel
|
|
sCurrentVoiceChannel = LLVoiceChannelProximal::getInstance();
|
|
sCurrentVoiceChannel->activate();
|
|
}
|
|
}
|
|
|
|
void LLVoiceChannel::activate()
|
|
{
|
|
if (callStarted())
|
|
{
|
|
return;
|
|
}
|
|
|
|
// deactivate old channel and mark ourselves as the active one
|
|
if (sCurrentVoiceChannel != this)
|
|
{
|
|
// mark as current before deactivating the old channel to prevent
|
|
// activating the proximal channel between IM calls
|
|
LLVoiceChannel* old_channel = sCurrentVoiceChannel;
|
|
sCurrentVoiceChannel = this;
|
|
if (old_channel)
|
|
{
|
|
old_channel->deactivate();
|
|
}
|
|
}
|
|
|
|
if (mState == STATE_NO_CHANNEL_INFO)
|
|
{
|
|
// responsible for setting status to active
|
|
requestChannelInfo();
|
|
}
|
|
else
|
|
{
|
|
setState(STATE_CALL_STARTED);
|
|
}
|
|
|
|
LLVoiceClient::getInstance()->addObserver(this);
|
|
|
|
//do not send earlier, channel should be initialized, should not be in STATE_NO_CHANNEL_INFO state
|
|
sCurrentVoiceChannelChangedSignal(this->mSessionID);
|
|
}
|
|
|
|
void LLVoiceChannel::requestChannelInfo()
|
|
{
|
|
// pretend we have everything we need
|
|
if (sCurrentVoiceChannel == this)
|
|
{
|
|
setState(STATE_CALL_STARTED);
|
|
}
|
|
}
|
|
|
|
//static
|
|
LLVoiceChannel* LLVoiceChannel::getChannelByID(const LLUUID& session_id)
|
|
{
|
|
voice_channel_map_t::iterator found_it = sVoiceChannelMap.find(session_id);
|
|
if (found_it == sVoiceChannelMap.end())
|
|
{
|
|
return NULL;
|
|
}
|
|
else
|
|
{
|
|
return found_it->second;
|
|
}
|
|
}
|
|
|
|
LLVoiceChannel* LLVoiceChannel::getCurrentVoiceChannel()
|
|
{
|
|
return sCurrentVoiceChannel;
|
|
}
|
|
|
|
void LLVoiceChannel::updateSessionID(const LLUUID& new_session_id)
|
|
{
|
|
sVoiceChannelMap.erase(sVoiceChannelMap.find(mSessionID));
|
|
mSessionID = new_session_id;
|
|
sVoiceChannelMap.insert(std::make_pair(mSessionID, this));
|
|
}
|
|
|
|
void LLVoiceChannel::setState(EState state)
|
|
{
|
|
switch(state)
|
|
{
|
|
case STATE_RINGING:
|
|
//TODO: remove or redirect this call status notification
|
|
// LLCallInfoDialog::show("ringing", mNotifyArgs);
|
|
break;
|
|
case STATE_CONNECTED:
|
|
//TODO: remove or redirect this call status notification
|
|
// LLCallInfoDialog::show("connected", mNotifyArgs);
|
|
break;
|
|
case STATE_HUNG_UP:
|
|
//TODO: remove or redirect this call status notification
|
|
// LLCallInfoDialog::show("hang_up", mNotifyArgs);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
doSetState(state);
|
|
}
|
|
|
|
void LLVoiceChannel::doSetState(const EState& new_state)
|
|
{
|
|
EState old_state = mState;
|
|
mState = new_state;
|
|
|
|
if (!mStateChangedCallback.empty())
|
|
mStateChangedCallback(old_state, mState, mCallDirection, mCallEndedByAgent, mSessionID);
|
|
}
|
|
|
|
//static
|
|
void LLVoiceChannel::initClass()
|
|
{
|
|
sCurrentVoiceChannel = LLVoiceChannelProximal::getInstance();
|
|
}
|
|
|
|
//static
|
|
void LLVoiceChannel::suspend()
|
|
{
|
|
if (!sSuspended)
|
|
{
|
|
sSuspendedVoiceChannel = sCurrentVoiceChannel;
|
|
sSuspended = true;
|
|
}
|
|
}
|
|
|
|
//static
|
|
void LLVoiceChannel::resume()
|
|
{
|
|
if (sSuspended)
|
|
{
|
|
if (LLVoiceClient::getInstance()->voiceEnabled())
|
|
{
|
|
if (sSuspendedVoiceChannel)
|
|
{
|
|
sSuspendedVoiceChannel->activate();
|
|
}
|
|
else
|
|
{
|
|
LLVoiceChannelProximal::getInstance()->activate();
|
|
}
|
|
}
|
|
sSuspended = false;
|
|
}
|
|
}
|
|
|
|
boost::signals2::connection LLVoiceChannel::setCurrentVoiceChannelChangedCallback(channel_changed_callback_t cb, bool at_front)
|
|
{
|
|
if (at_front)
|
|
{
|
|
return sCurrentVoiceChannelChangedSignal.connect(cb, boost::signals2::at_front);
|
|
}
|
|
else
|
|
{
|
|
return sCurrentVoiceChannelChangedSignal.connect(cb);
|
|
}
|
|
}
|
|
|
|
//
|
|
// LLVoiceChannelGroup
|
|
//
|
|
|
|
LLVoiceChannelGroup::LLVoiceChannelGroup(const LLUUID &session_id,
|
|
const std::string &session_name,
|
|
bool is_p2p) :
|
|
LLVoiceChannel(session_id, session_name),
|
|
mIsP2P(is_p2p)
|
|
{
|
|
mRetries = DEFAULT_RETRIES_COUNT;
|
|
mIsRetrying = false;
|
|
}
|
|
|
|
void LLVoiceChannelGroup::deactivate()
|
|
{
|
|
if (callStarted())
|
|
{
|
|
LLVoiceClient::getInstance()->leaveNonSpatialChannel();
|
|
}
|
|
LLVoiceChannel::deactivate();
|
|
|
|
if (mIsP2P)
|
|
{
|
|
// void the channel info for p2p adhoc channels
|
|
// so we request it again, hence throwing up the
|
|
// connect dialogue on the other side.
|
|
setState(STATE_NO_CHANNEL_INFO);
|
|
}
|
|
}
|
|
|
|
void LLVoiceChannelGroup::activate()
|
|
{
|
|
if (callStarted()) return;
|
|
|
|
LLVoiceChannel::activate();
|
|
|
|
if (callStarted())
|
|
{
|
|
// we have the channel info, just need to use it now
|
|
LLVoiceClient::getInstance()->setNonSpatialChannel(mChannelInfo,
|
|
mIsP2P && (mCallDirection == OUTGOING_CALL),
|
|
mIsP2P);
|
|
|
|
if (mIsP2P)
|
|
{
|
|
LLIMModel::addSpeakersToRecent(mSessionID);
|
|
}
|
|
else
|
|
{
|
|
if (!gAgent.isInGroup(mSessionID)) // ad-hoc channel
|
|
{
|
|
LLIMModel::LLIMSession *session = LLIMModel::getInstance()->findIMSession(mSessionID);
|
|
// Adding ad-hoc call participants to Recent People List.
|
|
// If it's an outgoing ad-hoc, we can use mInitialTargetIDs that holds IDs of people we
|
|
// called(both online and offline) as source to get people for recent (STORM-210).
|
|
if (session && session->isOutgoingAdHoc())
|
|
{
|
|
for (uuid_vec_t::iterator it = session->mInitialTargetIDs.begin(); it != session->mInitialTargetIDs.end(); ++it)
|
|
{
|
|
const LLUUID id = *it;
|
|
LLRecentPeople::instance().add(id);
|
|
}
|
|
}
|
|
// If this ad-hoc is incoming then trying to get ids of people from mInitialTargetIDs
|
|
// would lead to EXT-8246. So in this case we get them from speakers list.
|
|
else
|
|
{
|
|
LLIMModel::addSpeakersToRecent(mSessionID);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Mic default state is OFF on initiating/joining Ad-Hoc/Group calls. It's on for P2P using the AdHoc infra.
|
|
|
|
LLVoiceClient::getInstance()->setUserPTTState(mIsP2P);
|
|
}
|
|
}
|
|
|
|
void LLVoiceChannelGroup::requestChannelInfo()
|
|
{
|
|
LLViewerRegion* region = gAgent.getRegion();
|
|
if (region)
|
|
{
|
|
std::string url = region->getCapability("ChatSessionRequest");
|
|
|
|
LLCoros::instance().launch("LLVoiceChannelGroup::voiceCallCapCoro",
|
|
boost::bind(&LLVoiceChannelGroup::voiceCallCapCoro, this, url));
|
|
}
|
|
}
|
|
|
|
void LLVoiceChannelGroup::setChannelInfo(const LLSD& channelInfo)
|
|
{
|
|
mChannelInfo = channelInfo;
|
|
|
|
if (mState == STATE_NO_CHANNEL_INFO)
|
|
{
|
|
if(mChannelInfo.isDefined() && mChannelInfo.isMap())
|
|
{
|
|
setState(STATE_READY);
|
|
|
|
// if we are supposed to be active, reconnect
|
|
// this will happen on initial connect, as we request credentials on first use
|
|
if (sCurrentVoiceChannel == this)
|
|
{
|
|
// just in case we got new channel info while active
|
|
// should move over to new channel
|
|
activate();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//*TODO: notify user
|
|
LL_WARNS("Voice") << "Received invalid credentials for channel " << mSessionName << LL_ENDL;
|
|
deactivate();
|
|
}
|
|
}
|
|
else if ( mIsRetrying )
|
|
{
|
|
// we have the channel info, just need to use it now
|
|
LLVoiceClient::getInstance()->setNonSpatialChannel(channelInfo,
|
|
mCallDirection == OUTGOING_CALL,
|
|
mIsP2P);
|
|
}
|
|
}
|
|
|
|
void LLVoiceChannelGroup::handleStatusChange(EStatusType type)
|
|
{
|
|
// status updates
|
|
switch(type)
|
|
{
|
|
case STATUS_JOINED:
|
|
mRetries = 3;
|
|
mIsRetrying = false;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
LLVoiceChannel::handleStatusChange(type);
|
|
}
|
|
|
|
void LLVoiceChannelGroup::handleError(EStatusType status)
|
|
{
|
|
std::string notify;
|
|
switch(status)
|
|
{
|
|
case ERROR_CHANNEL_LOCKED:
|
|
case ERROR_CHANNEL_FULL:
|
|
notify = "VoiceChannelFull";
|
|
break;
|
|
case ERROR_NOT_AVAILABLE:
|
|
//clear URI and credentials
|
|
//set the state to be no info
|
|
//and activate
|
|
if ( mRetries > 0 )
|
|
{
|
|
mRetries--;
|
|
mIsRetrying = true;
|
|
mIgnoreNextSessionLeave = true;
|
|
|
|
requestChannelInfo();
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
notify = "VoiceChannelJoinFailed";
|
|
mRetries = DEFAULT_RETRIES_COUNT;
|
|
mIsRetrying = false;
|
|
}
|
|
|
|
break;
|
|
|
|
case ERROR_UNKNOWN:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// notification
|
|
if (!notify.empty())
|
|
{
|
|
LLNotificationPtr notification = LLNotificationsUtil::add(notify, mNotifyArgs);
|
|
// echo to im window
|
|
gIMMgr->addMessage(mSessionID, LLUUID::null, SYSTEM_FROM, notification->getMessage());
|
|
}
|
|
|
|
LLVoiceChannel::handleError(status);
|
|
}
|
|
|
|
void LLVoiceChannelGroup::setState(EState state)
|
|
{
|
|
switch(state)
|
|
{
|
|
case STATE_RINGING:
|
|
if ( !mIsRetrying )
|
|
{
|
|
//TODO: remove or redirect this call status notification
|
|
// LLCallInfoDialog::show("ringing", mNotifyArgs);
|
|
}
|
|
|
|
doSetState(state);
|
|
break;
|
|
default:
|
|
LLVoiceChannel::setState(state);
|
|
}
|
|
}
|
|
|
|
void LLVoiceChannelGroup::voiceCallCapCoro(std::string url)
|
|
{
|
|
LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
|
|
LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
|
|
httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("voiceCallCapCoro", httpPolicy));
|
|
LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
|
|
|
|
LLSD postData;
|
|
postData["method"] = "call";
|
|
postData["session-id"] = mSessionID;
|
|
LLSD altParams;
|
|
std::string preferred_voice_server_type = gSavedSettings.getString("VoiceServerType");
|
|
if (preferred_voice_server_type.empty())
|
|
{
|
|
// default to the server type associated with the region we're on.
|
|
LLVoiceVersionInfo versionInfo = LLVoiceClient::getInstance()->getVersion();
|
|
preferred_voice_server_type = versionInfo.internalVoiceServerType;
|
|
}
|
|
altParams["preferred_voice_server_type"] = preferred_voice_server_type;
|
|
postData["alt_params"] = altParams;
|
|
|
|
LL_INFOS("Voice", "voiceCallCapCoro") << "Generic POST for " << url << LL_ENDL;
|
|
|
|
LLSD result = httpAdapter->postAndSuspend(httpRequest, url, postData);
|
|
|
|
LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
|
|
LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
|
|
|
|
LLVoiceChannel* channelp = LLVoiceChannel::getChannelByID(mSessionID);
|
|
if (!channelp)
|
|
{
|
|
LL_WARNS("Voice") << "Unable to retrieve channel with Id = " << mSessionID << LL_ENDL;
|
|
return;
|
|
}
|
|
|
|
if (!status)
|
|
{
|
|
if (status == LLCore::HttpStatus(HTTP_FORBIDDEN))
|
|
{
|
|
//403 == no ability
|
|
LLNotificationsUtil::add(
|
|
"VoiceNotAllowed",
|
|
channelp->getNotifyArgs());
|
|
}
|
|
else
|
|
{
|
|
LLNotificationsUtil::add(
|
|
"VoiceCallGenericError",
|
|
channelp->getNotifyArgs());
|
|
}
|
|
channelp->deactivate();
|
|
return;
|
|
}
|
|
|
|
result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS);
|
|
|
|
LLSD::map_const_iterator iter;
|
|
for (iter = result.beginMap(); iter != result.endMap(); ++iter)
|
|
{
|
|
LL_DEBUGS("Voice") << "LLVoiceChannelGroup::voiceCallCapCoro got "
|
|
<< iter->first << LL_ENDL;
|
|
}
|
|
LL_INFOS("Voice") << "LLVoiceChannelGroup::voiceCallCapCoro got " << result << LL_ENDL;
|
|
|
|
channelp->setChannelInfo(result["voice_credentials"]);
|
|
}
|
|
|
|
|
|
//
|
|
// LLVoiceChannelProximal
|
|
//
|
|
LLVoiceChannelProximal::LLVoiceChannelProximal() :
|
|
LLVoiceChannel(LLUUID::null, LLStringUtil::null)
|
|
{
|
|
}
|
|
|
|
bool LLVoiceChannelProximal::isActive() const
|
|
{
|
|
return callStarted() && LLVoiceClient::getInstance()->inProximalChannel();
|
|
}
|
|
|
|
void LLVoiceChannelProximal::activate()
|
|
{
|
|
if (callStarted()) return;
|
|
|
|
if((LLVoiceChannel::sCurrentVoiceChannel != this) && (LLVoiceChannel::getState() == STATE_CONNECTED))
|
|
{
|
|
// we're connected to a non-spatial channel, so disconnect.
|
|
LLVoiceClient::getInstance()->leaveNonSpatialChannel();
|
|
}
|
|
|
|
LLVoiceClient::getInstance()->activateSpatialChannel(true);
|
|
LLVoiceChannel::activate();
|
|
}
|
|
|
|
void LLVoiceChannelProximal::onChange(EStatusType type, const LLSD& channelInfo, bool proximal)
|
|
{
|
|
if (!proximal)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (type < BEGIN_ERROR_STATUS)
|
|
{
|
|
handleStatusChange(type);
|
|
}
|
|
else
|
|
{
|
|
handleError(type);
|
|
}
|
|
}
|
|
|
|
void LLVoiceChannelProximal::handleStatusChange(EStatusType status)
|
|
{
|
|
// status updates
|
|
switch(status)
|
|
{
|
|
case STATUS_LEFT_CHANNEL:
|
|
// do not notify user when leaving proximal channel
|
|
return;
|
|
case STATUS_VOICE_DISABLED:
|
|
LLVoiceClient::getInstance()->setUserPTTState(false);
|
|
gAgent.setVoiceConnected(false);
|
|
//skip showing "Voice not available at your current location" when agent voice is disabled (EXT-4749)
|
|
if(LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking())
|
|
{
|
|
//TODO: remove or redirect this call status notification
|
|
// LLCallInfoDialog::show("unavailable", mNotifyArgs);
|
|
}
|
|
return;
|
|
default:
|
|
break;
|
|
}
|
|
LLVoiceChannel::handleStatusChange(status);
|
|
}
|
|
|
|
|
|
void LLVoiceChannelProximal::handleError(EStatusType status)
|
|
{
|
|
std::string notify;
|
|
switch(status)
|
|
{
|
|
case ERROR_CHANNEL_LOCKED:
|
|
case ERROR_CHANNEL_FULL:
|
|
notify = "ProximalVoiceChannelFull";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// notification
|
|
if (!notify.empty())
|
|
{
|
|
LLNotificationsUtil::add(notify, mNotifyArgs);
|
|
}
|
|
|
|
// proximal voice remains up and the provider will try to reconnect.
|
|
}
|
|
|
|
void LLVoiceChannelProximal::deactivate()
|
|
{
|
|
if (callStarted())
|
|
{
|
|
setState(STATE_HUNG_UP);
|
|
}
|
|
LLVoiceClient::getInstance()->removeObserver(this);
|
|
LLVoiceClient::getInstance()->activateSpatialChannel(false);
|
|
}
|
|
|
|
|
|
//
|
|
// LLVoiceChannelP2P
|
|
//
|
|
LLVoiceChannelP2P::LLVoiceChannelP2P(const LLUUID &session_id,
|
|
const std::string &session_name,
|
|
const LLUUID &other_user_id,
|
|
LLVoiceP2POutgoingCallInterface* outgoing_call_interface) :
|
|
LLVoiceChannelGroup(session_id, session_name, true),
|
|
mOtherUserID(other_user_id),
|
|
mReceivedCall(false),
|
|
mOutgoingCallInterface(outgoing_call_interface)
|
|
{
|
|
mChannelInfo = LLVoiceClient::getInstance()->getP2PChannelInfoTemplate(other_user_id);
|
|
}
|
|
|
|
void LLVoiceChannelP2P::handleStatusChange(EStatusType type)
|
|
{
|
|
LL_INFOS("Voice") << "P2P CALL CHANNEL STATUS CHANGE: incoming=" << int(mReceivedCall) << " newstatus=" << LLVoiceClientStatusObserver::status2string(type) << " (mState=" << mState << ")" << LL_ENDL;
|
|
|
|
// status updates
|
|
switch(type)
|
|
{
|
|
case STATUS_LEFT_CHANNEL:
|
|
if (callStarted() && !mIgnoreNextSessionLeave && !sSuspended)
|
|
{
|
|
// *TODO: use it to show DECLINE voice notification
|
|
if (mState == STATE_RINGING)
|
|
{
|
|
// other user declined call
|
|
LLNotificationsUtil::add("P2PCallDeclined", mNotifyArgs);
|
|
}
|
|
else
|
|
{
|
|
// other user hung up, so we didn't end the call
|
|
mCallEndedByAgent = false;
|
|
}
|
|
deactivate();
|
|
}
|
|
mIgnoreNextSessionLeave = false;
|
|
return;
|
|
case STATUS_JOINING:
|
|
// because we join session we expect to process session leave event in the future. EXT-7371
|
|
// may be this should be done in the LLVoiceChannel::handleStatusChange.
|
|
mIgnoreNextSessionLeave = false;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
LLVoiceChannel::handleStatusChange(type);
|
|
}
|
|
|
|
void LLVoiceChannelP2P::handleError(EStatusType type)
|
|
{
|
|
switch(type)
|
|
{
|
|
case ERROR_NOT_AVAILABLE:
|
|
LLNotificationsUtil::add("P2PCallNoAnswer", mNotifyArgs);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
LLVoiceChannel::handleError(type);
|
|
}
|
|
|
|
void LLVoiceChannelP2P::activate()
|
|
{
|
|
if (callStarted()) return;
|
|
|
|
//call will be counted as ended by user unless this variable is changed in handleStatusChange()
|
|
mCallEndedByAgent = true;
|
|
|
|
LLVoiceChannel::activate();
|
|
|
|
if (callStarted())
|
|
{
|
|
// no session handle yet, we're starting the call
|
|
if (mIncomingCallInterface == nullptr)
|
|
{
|
|
mReceivedCall = false;
|
|
mOutgoingCallInterface->callUser(mOtherUserID);
|
|
}
|
|
// otherwise answering the call
|
|
else
|
|
{
|
|
if (!mIncomingCallInterface->answerInvite())
|
|
{
|
|
mCallEndedByAgent = false;
|
|
mIncomingCallInterface.reset();
|
|
handleError(ERROR_UNKNOWN);
|
|
return;
|
|
}
|
|
// using the incoming call interface invalidates it. Clear it out here so we can't reuse it by accident.
|
|
mIncomingCallInterface.reset();
|
|
}
|
|
|
|
// Add the party to the list of people with which we've recently interacted.
|
|
addToTheRecentPeopleList();
|
|
|
|
//Default mic is ON on initiating/joining P2P calls
|
|
if (!LLVoiceClient::getInstance()->getUserPTTState() && LLVoiceClient::getInstance()->getPTTIsToggle())
|
|
{
|
|
LLVoiceClient::getInstance()->inputUserControlState(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
void LLVoiceChannelP2P::deactivate()
|
|
{
|
|
if (callStarted())
|
|
{
|
|
mOutgoingCallInterface->hangup();
|
|
}
|
|
LLVoiceChannel::deactivate();
|
|
}
|
|
|
|
|
|
void LLVoiceChannelP2P::requestChannelInfo()
|
|
{
|
|
// pretend we have everything we need, since P2P doesn't use channel info
|
|
if (sCurrentVoiceChannel == this)
|
|
{
|
|
setState(STATE_CALL_STARTED);
|
|
}
|
|
}
|
|
|
|
// receiving session from other user who initiated call
|
|
void LLVoiceChannelP2P::setChannelInfo(const LLSD& channel_info)
|
|
{
|
|
mChannelInfo = channel_info;
|
|
bool needs_activate = false;
|
|
if (callStarted())
|
|
{
|
|
// defer to lower agent id when already active
|
|
if (mOtherUserID < gAgent.getID())
|
|
{
|
|
// pretend we haven't started the call yet, so we can connect to this session instead
|
|
deactivate();
|
|
needs_activate = true;
|
|
}
|
|
else
|
|
{
|
|
// we are active and have priority, invite the other user again
|
|
// under the assumption they will join this new session
|
|
mOutgoingCallInterface->callUser(mOtherUserID);
|
|
return;
|
|
}
|
|
}
|
|
|
|
mReceivedCall = true;
|
|
if (channel_info.isDefined() && channel_info.isMap())
|
|
{
|
|
mIncomingCallInterface = LLVoiceClient::getInstance()->getIncomingCallInterface(channel_info);
|
|
}
|
|
if (needs_activate)
|
|
{
|
|
activate();
|
|
}
|
|
}
|
|
|
|
void LLVoiceChannelP2P::setState(EState state)
|
|
{
|
|
LL_INFOS("Voice") << "P2P CALL STATE CHANGE: incoming=" << int(mReceivedCall) << " oldstate=" << mState << " newstate=" << state << LL_ENDL;
|
|
|
|
if (mReceivedCall) // incoming call
|
|
{
|
|
// you only "answer" voice invites in p2p mode
|
|
// so provide a special purpose message here
|
|
if (mReceivedCall && state == STATE_RINGING)
|
|
{
|
|
//TODO: remove or redirect this call status notification
|
|
// LLCallInfoDialog::show("answering", mNotifyArgs);
|
|
doSetState(state);
|
|
return;
|
|
}
|
|
}
|
|
|
|
LLVoiceChannel::setState(state);
|
|
}
|
|
|
|
void LLVoiceChannelP2P::addToTheRecentPeopleList()
|
|
{
|
|
LLRecentPeople::instance().add(mOtherUserID);
|
|
}
|