PageRenderTime 34ms CodeModel.GetById 1ms app.highlight 28ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/newview/llspeakingindicatormanager.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 318 lines | 157 code | 48 blank | 113 comment | 26 complexity | 5467ab98df434227d75af33d71982b8a MD5 | raw file
  1/** 
  2 * @file llspeakingindicatormanager.cpp
  3 * @author Mike Antipov
  4 * @brief Implementation of SpeackerIndicatorManager class to process registered LLSpeackerIndicator
  5 * depend on avatars are in the same voice channel.
  6 *
  7 * $LicenseInfo:firstyear=2010&license=viewerlgpl$
  8 * Second Life Viewer Source Code
  9 * Copyright (C) 2010, Linden Research, Inc.
 10 * 
 11 * This library is free software; you can redistribute it and/or
 12 * modify it under the terms of the GNU Lesser General Public
 13 * License as published by the Free Software Foundation;
 14 * version 2.1 of the License only.
 15 * 
 16 * This library is distributed in the hope that it will be useful,
 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 19 * Lesser General Public License for more details.
 20 * 
 21 * You should have received a copy of the GNU Lesser General Public
 22 * License along with this library; if not, write to the Free Software
 23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 24 * 
 25 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 26 * $/LicenseInfo$
 27 */
 28
 29#include "llviewerprecompiledheaders.h"
 30#include "llspeakingindicatormanager.h"
 31
 32
 33#include "llvoicechannel.h"
 34#include "llvoiceclient.h"
 35
 36/**
 37 * This class intended to control visibility of avatar speaking indicators depend on whether avatars
 38 * are in the same voice channel.
 39 *
 40 * Speaking indicator should be visible for avatars in the same voice channel. See EXT-3976.
 41 *
 42 * It stores passed instances of LLOutputMonitorCtrl in a multimap by avatar LLUUID.
 43 * It observes changing of voice channel and changing of participant list in voice channel.
 44 * When voice channel or voice participant list is changed it updates visibility of an appropriate 
 45 * speaking indicator.
 46 *
 47 * Several indicators can be registered for the same avatar.
 48 */
 49class SpeakingIndicatorManager : public LLSingleton<SpeakingIndicatorManager>, LLVoiceClientParticipantObserver
 50{
 51	LOG_CLASS(SpeakingIndicatorManager);
 52public:
 53
 54	/**
 55	 * Stores passed speaking indicator to control its visibility.
 56	 *
 57	 * Registered indicator is set visible if an appropriate avatar is in the same voice channel with Agent.
 58	 * It ignores instances of Agent's indicator.
 59	 *
 60	 * @param speaker_id LLUUID of an avatar whose speaking indicator is registered.
 61	 * @param speaking_indicator instance of the speaking indicator to be registered.
 62	 * @param session_id session UUID for which indicator should be shown only.
 63	 *		If this parameter is set registered indicator will be shown only in voice channel
 64	 *		which has the same session id (EXT-5562).
 65	 */
 66	void registerSpeakingIndicator(const LLUUID& speaker_id, LLSpeakingIndicator* const speaking_indicator,
 67		const LLUUID& session_id = LLUUID::null);
 68
 69	/**
 70	 * Removes passed speaking indicator from observing.
 71	 *
 72	 * @param speaker_id LLUUID of an avatar whose speaking indicator should be unregistered.
 73	 * @param speaking_indicator instance of the speaking indicator to be unregistered.
 74	 */
 75	void unregisterSpeakingIndicator(const LLUUID& speaker_id, const LLSpeakingIndicator* const speaking_indicator);
 76
 77private:
 78	typedef std::set<LLUUID> speaker_ids_t;
 79	typedef std::multimap<LLUUID, LLSpeakingIndicator*> speaking_indicators_mmap_t;
 80	typedef speaking_indicators_mmap_t::value_type speaking_indicator_value_t;
 81	typedef speaking_indicators_mmap_t::const_iterator indicator_const_iterator;
 82	typedef std::pair<indicator_const_iterator, indicator_const_iterator> indicator_range_t;
 83
 84	friend class LLSingleton<SpeakingIndicatorManager>;
 85	SpeakingIndicatorManager();
 86	~SpeakingIndicatorManager();
 87
 88	/**
 89	 * Callback to determine when voice channel is changed.
 90	 *
 91	 * It switches all registered speaking indicators off.
 92	 * To reduce overheads only switched on indicators are processed.
 93	 */
 94	void sOnCurrentChannelChanged(const LLUUID& session_id);
 95
 96	/**
 97	 * Callback of changing voice participant list (from LLVoiceClientParticipantObserver).
 98	 *
 99	 * Switches off indicators had been switched on and switches on indicators of current participants list.
100	 * There is only a few indicators in lists should be switched off/on.
101	 * So, method does not calculate difference between these list it only switches off already 
102	 * switched on indicators and switches on indicators of voice channel participants
103	 */
104	void onParticipantsChanged();
105
106	/**
107	 * Changes state of indicators specified by LLUUIDs
108	 *
109	 * @param speakers_uuids - avatars' LLUUIDs whose speaking indicators should be switched
110	 * @param switch_on - if TRUE specified indicator will be switched on, off otherwise.
111	 */
112	void switchSpeakerIndicators(const speaker_ids_t& speakers_uuids, BOOL switch_on);
113
114	/**
115	 * Ensures that passed instance of Speaking Indicator does not exist among registered ones.
116	 * If yes, it will be removed.
117	 */
118	void ensureInstanceDoesNotExist(LLSpeakingIndicator* const speaking_indicator);
119
120
121	/**
122	 * Multimap with all registered speaking indicators
123	 */
124	speaking_indicators_mmap_t mSpeakingIndicators;
125
126	/**
127	 * LUUIDs of avatar for which we have speaking indicators switched on.
128	 *
129	 * Is used to switch off all previously ON indicators when voice participant list is changed.
130	 *
131	 * @see onChange()
132	 */
133	speaker_ids_t mSwitchedIndicatorsOn;
134};
135
136//////////////////////////////////////////////////////////////////////////
137// PUBLIC SECTION
138//////////////////////////////////////////////////////////////////////////
139void SpeakingIndicatorManager::registerSpeakingIndicator(const LLUUID& speaker_id, LLSpeakingIndicator* const speaking_indicator,
140														 const LLUUID& session_id)
141{
142	// do not exclude agent's indicators. They should be processed in the same way as others. See EXT-3889.
143
144	LL_DEBUGS("SpeakingIndicator") << "Registering indicator: " << speaker_id << "|"<< speaking_indicator << ", session: " << session_id << LL_ENDL;
145
146
147	ensureInstanceDoesNotExist(speaking_indicator);
148
149	speaking_indicator->setTargetSessionID(session_id);
150
151	speaking_indicator_value_t value_type(speaker_id, speaking_indicator);
152	mSpeakingIndicators.insert(value_type);
153
154	speaker_ids_t speakers_uuids;
155	BOOL is_in_same_voice = LLVoiceClient::getInstance()->isParticipant(speaker_id);
156
157	speakers_uuids.insert(speaker_id);
158	switchSpeakerIndicators(speakers_uuids, is_in_same_voice);
159}
160
161void SpeakingIndicatorManager::unregisterSpeakingIndicator(const LLUUID& speaker_id, const LLSpeakingIndicator* const speaking_indicator)
162{
163	LL_DEBUGS("SpeakingIndicator") << "Unregistering indicator: " << speaker_id << "|"<< speaking_indicator << LL_ENDL;
164	speaking_indicators_mmap_t::iterator it;
165	it = mSpeakingIndicators.find(speaker_id);
166	for (;it != mSpeakingIndicators.end(); ++it)
167	{
168		if (it->second == speaking_indicator)
169		{
170			LL_DEBUGS("SpeakingIndicator") << "Unregistered." << LL_ENDL;
171			mSpeakingIndicators.erase(it);
172			break;
173		}
174	}
175}
176
177//////////////////////////////////////////////////////////////////////////
178// PRIVATE SECTION
179//////////////////////////////////////////////////////////////////////////
180SpeakingIndicatorManager::SpeakingIndicatorManager()
181{
182	LLVoiceChannel::setCurrentVoiceChannelChangedCallback(boost::bind(&SpeakingIndicatorManager::sOnCurrentChannelChanged, this, _1));
183	LLVoiceClient::getInstance()->addObserver(this);
184}
185
186SpeakingIndicatorManager::~SpeakingIndicatorManager()
187{
188	// Don't use LLVoiceClient::getInstance() here without check
189	// singleton MAY have already been destroyed.
190	if(LLVoiceClient::instanceExists())
191	{
192		LLVoiceClient::getInstance()->removeObserver(this);
193	}
194}
195
196void SpeakingIndicatorManager::sOnCurrentChannelChanged(const LLUUID& /*session_id*/)
197{
198	switchSpeakerIndicators(mSwitchedIndicatorsOn, FALSE);
199	mSwitchedIndicatorsOn.clear();
200}
201
202void SpeakingIndicatorManager::onParticipantsChanged()
203{
204	LL_DEBUGS("SpeakingIndicator") << "Voice participant list was changed, updating indicators" << LL_ENDL;
205
206	speaker_ids_t speakers_uuids;
207	LLVoiceClient::getInstance()->getParticipantList(speakers_uuids);
208
209	LL_DEBUGS("SpeakingIndicator") << "Switching all OFF, count: " << mSwitchedIndicatorsOn.size() << LL_ENDL;
210	// switch all indicators off
211	switchSpeakerIndicators(mSwitchedIndicatorsOn, FALSE);
212	mSwitchedIndicatorsOn.clear();
213
214	LL_DEBUGS("SpeakingIndicator") << "Switching all ON, count: " << speakers_uuids.size() << LL_ENDL;
215	// then switch current voice participants indicators on
216	switchSpeakerIndicators(speakers_uuids, TRUE);
217}
218
219void SpeakingIndicatorManager::switchSpeakerIndicators(const speaker_ids_t& speakers_uuids, BOOL switch_on)
220{
221	LLVoiceChannel* voice_channel = LLVoiceChannel::getCurrentVoiceChannel();
222	LLUUID session_id;
223	if (voice_channel)
224	{
225		session_id = voice_channel->getSessionID();
226	}
227
228	speaker_ids_t::const_iterator it_uuid = speakers_uuids.begin(); 
229	for (; it_uuid != speakers_uuids.end(); ++it_uuid)
230	{
231		LL_DEBUGS("SpeakingIndicator") << "Looking for indicator: " << *it_uuid << LL_ENDL;
232		indicator_range_t it_range = mSpeakingIndicators.equal_range(*it_uuid);
233		indicator_const_iterator it_indicator = it_range.first;
234		bool was_found = false;
235		bool was_switched_on = false;
236		for (; it_indicator != it_range.second; ++it_indicator)
237		{
238			was_found = true;
239			LLSpeakingIndicator* indicator = (*it_indicator).second;
240
241			BOOL switch_current_on = switch_on;
242
243			// we should show indicator for specified voice session only if this is current channel. EXT-5562.
244			if (switch_current_on && indicator->getTargetSessionID().notNull())
245			{
246				switch_current_on = indicator->getTargetSessionID() == session_id;
247				LL_DEBUGS("SpeakingIndicator") << "Session: " << session_id << ", target: " << indicator->getTargetSessionID() << ", the same? = " << switch_current_on << LL_ENDL;
248			}
249			was_switched_on = was_switched_on || switch_current_on;
250
251			indicator->switchIndicator(switch_current_on);
252
253		}
254
255		if (was_found)
256		{
257			LL_DEBUGS("SpeakingIndicator") << mSpeakingIndicators.count(*it_uuid) << " indicators where found" << LL_ENDL;
258
259			if (switch_on && !was_switched_on)
260			{
261				LL_DEBUGS("SpeakingIndicator") << "but non of them where switched on" << LL_ENDL;
262			}
263
264			if (was_switched_on)
265			{
266				// store switched on indicator to be able switch it off
267				mSwitchedIndicatorsOn.insert(*it_uuid);
268			}
269		}
270	}
271}
272
273void SpeakingIndicatorManager::ensureInstanceDoesNotExist(LLSpeakingIndicator* const speaking_indicator)
274{
275	LL_DEBUGS("SpeakingIndicator") << "Searching for an registered indicator instance: " << speaking_indicator << LL_ENDL;
276	speaking_indicators_mmap_t::iterator it = mSpeakingIndicators.begin();
277	for (;it != mSpeakingIndicators.end(); ++it)
278	{
279		if (it->second == speaking_indicator)
280		{
281			LL_DEBUGS("SpeakingIndicator") << "Found" << LL_ENDL;
282			break;
283		}
284	}
285
286	// It is possible with LLOutputMonitorCtrl the same instance of indicator is registered several
287	// times with different UUIDs. This leads to crash after instance is destroyed because the
288	// only one (specified by UUID in unregisterSpeakingIndicator()) is removed from the map.
289	// So, using stored deleted pointer leads to crash. See EXT-4782.
290	if (it != mSpeakingIndicators.end())
291	{
292		llwarns << "The same instance of indicator has already been registered, removing it: " << it->first << "|"<< speaking_indicator << llendl;
293		llassert(it == mSpeakingIndicators.end());
294		mSpeakingIndicators.erase(it);
295	}
296}
297
298
299/************************************************************************/
300/*         LLSpeakingIndicatorManager namespace implementation          */
301/************************************************************************/
302
303void LLSpeakingIndicatorManager::registerSpeakingIndicator(const LLUUID& speaker_id, LLSpeakingIndicator* const speaking_indicator,
304														   const LLUUID& session_id)
305{
306	SpeakingIndicatorManager::instance().registerSpeakingIndicator(speaker_id, speaking_indicator, session_id);
307}
308
309void LLSpeakingIndicatorManager::unregisterSpeakingIndicator(const LLUUID& speaker_id, const LLSpeakingIndicator* const speaking_indicator)
310{
311	if(SpeakingIndicatorManager::instanceExists())
312	{
313		SpeakingIndicatorManager::instance().unregisterSpeakingIndicator(speaker_id, speaking_indicator);
314	}
315}
316
317// EOF
318