This commit is contained in:
watrabi
2025-09-18 17:55:52 -04:00
commit 977f1ff4b8
15030 changed files with 17324420 additions and 0 deletions
+167
View File
@@ -0,0 +1,167 @@
//// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
//// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
//// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//// PARTICULAR PURPOSE.
////
//// Copyright (c) Microsoft Corporation. All rights reserved
#include "pch.h"
#include "AudioDeviceIDMapper.h"
#include "StringUtils.h"
#if TV_API
namespace Microsoft {
namespace Xbox {
namespace GameChat {
AudioDeviceIDMapper::AudioDeviceIDMapper()
{
}
void AudioDeviceIDMapper::Reset()
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
m_stringToLocalDeviceID.clear();
}
DEVICE_ID AudioDeviceIDMapper::GetLocalDeviceID(
_In_ Platform::String^ stringID
)
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
// Only add if not already there
if (!StringDeviceIDExists(stringID))
{
m_nextLocalDeviceID = (DEVICE_ID)m_nextLocalDeviceIDLong;
m_stringToLocalDeviceID[stringID] = m_nextLocalDeviceID;
InterlockedIncrement(&m_nextLocalDeviceIDLong);
return m_nextLocalDeviceID;
}
else
{
return m_stringToLocalDeviceID[stringID];
}
}
bool AudioDeviceIDMapper::StringDeviceIDExists(
_In_ Platform::String^ stringID
)
{
return m_stringToLocalDeviceID.find(stringID) != m_stringToLocalDeviceID.end();
}
bool AudioDeviceIDMapper::StringRemoteIDExists(
_In_ Platform::String^ stringID
)
{
return m_stringToRemoteDeviceID.find(stringID) != m_stringToRemoteDeviceID.end();
}
bool AudioDeviceIDMapper::DoesLookupIDExistsForRemote(
_In_ LOOKUP_ID lookupId
)
{
return m_remoteIdToString.find(lookupId) != m_remoteIdToString.end();
}
LOOKUP_ID AudioDeviceIDMapper::ConstructLookupID(
_In_ CONSOLE_NAME localNameOfRemoteConsole,
_In_ DEVICE_ID deviceID
)
{
return (localNameOfRemoteConsole << 8) + deviceID;
}
void AudioDeviceIDMapper::RemoveRemoteConsole(
_In_ CONSOLE_NAME localNameOfRemoteConsoleToRemove
)
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
#ifdef DEBUG_REMOTE_AUDIO_DEVICES
OutputDebugString( L"AudioDeviceIDMapper::RemoveRemoteConsole before\r\n" );
DebugDumpRemoteIdToStringMap();
#endif
for(auto iter = m_remoteIdToString.cbegin(); iter != m_remoteIdToString.cend(); )
{
LOOKUP_ID lookupId = iter->first;
CONSOLE_NAME localNameOfRemoteConsole = lookupId >> 8;
//DEVICE_ID deviceID = lookupId & 0xFF;
if( localNameOfRemoteConsoleToRemove == localNameOfRemoteConsole )
{
m_remoteIdToString.erase(iter++);
}
else
{
++iter;
}
}
#ifdef DEBUG_REMOTE_AUDIO_DEVICES
OutputDebugString( L"AudioDeviceIDMapper::RemoveRemoteConsole after\r\n" );
DebugDumpRemoteIdToStringMap();
#endif
}
Platform::String^ AudioDeviceIDMapper::GetRemoteAudioDevice(
_In_ LOOKUP_ID lookUpId
)
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
// Only add if not already there
if (DoesLookupIDExistsForRemote(lookUpId))
{
return m_remoteIdToString[lookUpId];
}
return nullptr;
}
void AudioDeviceIDMapper::AddRemoteAudioDevice(
_In_ LOOKUP_ID lookUpId,
_In_ Platform::String^ stringID
)
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
m_stringToRemoteDeviceID[stringID] = lookUpId;
m_remoteIdToString[lookUpId] = stringID;
#ifdef DEBUG_REMOTE_AUDIO_DEVICES
OutputDebugString( L"AudioDeviceIDMapper::AddRemoteAudioDevice\r\n" );
DebugDumpRemoteIdToStringMap();
#endif
}
void AudioDeviceIDMapper::DebugDumpRemoteIdToStringMap()
{
#ifdef DEBUG_REMOTE_AUDIO_DEVICES
int iterIndex = 0;
for(std::map<LOOKUP_ID, Platform::String^>::iterator iter = m_remoteIdToString.begin(); iter != m_remoteIdToString.end(); ++iter)
{
LOOKUP_ID iterLookupId = iter->first;
Platform::String^ audioStringId = iter->second;
WCHAR text[1024];
swprintf_s(text, ARRAYSIZE(text), L"m_remoteIdToString[%d] = %d -> %s\r\n",
iterIndex,
iterLookupId,
audioStringId->Data()
);
OutputDebugString( text );
iterIndex++;
}
#endif
}
}}}
#endif
+77
View File
@@ -0,0 +1,77 @@
//// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
//// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
//// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//// PARTICULAR PURPOSE.
////
//// Copyright (c) Microsoft Corporation. All rights reserved
#pragma once
#if TV_API
// Make this so we can make the underlying data type larger or smaller if we want
#define LOOKUP_ID UINT16 // The type of the full lookup id (CONSOLE_ID << 8 + LOOKUP_ID)
#define DEVICE_ID UINT8 // The type of the second half of the lookup id (unique device id)
#define CONSOLE_NAME UINT8 // The type of the first half of the lookup id (unique console id)
namespace Microsoft {
namespace Xbox {
namespace GameChat {
ref class AudioDeviceIDMapper sealed
{
public:
AudioDeviceIDMapper();
void Reset();
DEVICE_ID GetLocalDeviceID(
_In_ Platform::String^ stringID
);
void AddRemoteAudioDevice(
_In_ LOOKUP_ID deviceId,
_In_ Platform::String^ strId
);
Platform::String^ GetRemoteAudioDevice(
_In_ LOOKUP_ID lookupId
);
LOOKUP_ID ConstructLookupID(
_In_ CONSOLE_NAME localNameOfRemoteConsole,
_In_ DEVICE_ID deviceID
);
void RemoveRemoteConsole(
_In_ CONSOLE_NAME localNameOfRemoteConsole
);
void DebugDumpRemoteIdToStringMap();
private:
bool StringDeviceIDExists(
_In_ Platform::String^ stringID
);
bool StringRemoteIDExists(
_In_ Platform::String^ stringID
);
bool DoesLookupIDExistsForRemote(
_In_ LOOKUP_ID lookupId
);
private:
Concurrency::critical_section m_stateLock;
std::map<Platform::String^, DEVICE_ID> m_stringToLocalDeviceID;
std::map<Platform::String^, LOOKUP_ID> m_stringToRemoteDeviceID;
std::map<LOOKUP_ID, Platform::String^> m_remoteIdToString;
DEVICE_ID m_nextLocalDeviceID;
LONG m_nextLocalDeviceIDLong;
};
}}}
#endif
+920
View File
@@ -0,0 +1,920 @@
//// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
//// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
//// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//// PARTICULAR PURPOSE.
////
//// Copyright (c) Microsoft Corporation. All rights reserved
#include "pch.h"
#include "Thread.h"
#include "ChatManagerEvents.h"
#include "ChatAudioThread.h"
#include "ChatClient.h"
#include "Clock.h"
#include "StringUtils.h"
#include "BufferUtils.h"
#include "ChatManagerEvents.h"
#include "ChatManager.h"
#include <Audioclient.h>
#if TV_API
using namespace Windows::Xbox::Chat;
using namespace Windows::Xbox::System;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Platform;
using namespace Concurrency;
using namespace Microsoft::WRL::Details;
namespace Microsoft {
namespace Xbox {
namespace GameChat {
ChatClient::ChatClient(
_In_ uint32 chatPeriodInMilliseconds,
_In_ ChatManager^ chatManager,
_In_ ChatManagerSettings^ chatManagerSettings
) :
m_chatManager(chatManager),
m_chatManagerSettings( chatManagerSettings )
{
m_userIdsToUserData = std::map<unsigned int, ChatUser^>();
m_audioCaptureSourceIdToUserId = std::map<Platform::String^, std::hash_set<unsigned int>>();
TimeSpan chatPeriod;
chatPeriod.Duration = chatPeriodInMilliseconds * 10000; // milliseconds to hundred nanoseconds
m_chatSession = ref new ChatSession(chatPeriod, chatManagerSettings->AudioThreadAffinityMask, ChatFeatures::Default);
// Hook up the state changed handler
Platform::WeakReference wr(this);
m_tokenStateChangedEvent = m_chatSession->StateChangedEvent += ref new ChatSessionStateChangedHandler(
[ wr ]( IChatSession^ chatSession, ChatSessionStateChangeReason reason )
{
ChatClient^ chatClient = wr.Resolve<ChatClient>();
if( chatClient != nullptr )
{
chatClient->ChatSessionStateChanged(chatSession, reason);
}
});
}
ChatClient::~ChatClient()
{
CHAT_LOG_INFO_MSG(L"ChatClient::~ChatClient");
if( m_chatSession != nullptr )
{
m_chatSession->StateChangedEvent -= m_tokenStateChangedEvent;
}
}
bool ChatClient::DoesChannelContainUser(
_In_ Windows::Foundation::Collections::IVector<Windows::Xbox::Chat::IChatParticipant^>^ participants,
_In_ IUser^ user
)
{
for ( UINT i = 0; i < participants->Size; ++i )
{
if( StringUtils::IsStringEqualCaseInsenstive(participants->GetAt( i )->User->XboxUserId, user->XboxUserId) )
{
return true;
}
}
return false;
}
void ChatClient::LogCommentFormat(
_In_ LPCWSTR strMsg, ...
)
{
va_list args;
va_start(args, strMsg);
LogComment(StringUtils::GetStringFormat(strMsg, args));
va_end(args);
}
Microsoft::Xbox::GameChat::ChatUser^ ChatClient::AddUserToChatChannel(
_In_ uint8 channelIndex,
_In_ IUser^ user,
_In_opt_ Platform::Object^ uniqueConsoleIdentifier,
_In_ bool isLocal,
_In_opt_ Windows::Xbox::Chat::IChatParticipant^ chatParticipantToUpdate
)
{
CHAT_THROW_INVALIDARGUMENT_IF_NULL( user );
CHAT_THROW_INVALIDARGUMENT_IF( channelIndex >= NUMBER_OF_CHAT_CHANNELS );
Microsoft::Xbox::GameChat::ChatUser^ chatUserAddedOrUpdated = nullptr;
{
Concurrency::critical_section::scoped_lock lock(m_chatSessionLock);
// If the channel index doesn't exist, add 0-channelIndex channels first.
int channelsNeeded = channelIndex + 1;
int channelsExisting = m_chatSession->Channels->Size;
int channelsToAdd = 0;
if (channelsExisting < channelsNeeded)
{
channelsToAdd = abs(channelsExisting - channelsNeeded);
}
for (int i = 0; i < channelsToAdd; i++)
{
m_chatSession->Channels->Append( ref new ChatChannel() );
}
IChatChannel^ channel = m_chatSession->Channels->GetAt(channelIndex);
CHAT_THROW_HR_IF( channel == nullptr, E_UNEXPECTED ); // We must have a channel for channelIndex since we just created it above
if( chatParticipantToUpdate != nullptr )
{
// If the audio devices don't match, that means the remote user packet contained an updated user
// and we should remove the user from the chat channel and add the user back
bool removeFromAllChannels = false;
RemoveUserFromChatChannelHelper(channel, chatParticipantToUpdate->User, channelIndex, removeFromAllChannels);
}
// Check for an existing ChatUser to use
Windows::Foundation::Collections::IVectorView<Microsoft::Xbox::GameChat::ChatUser^>^ chatUsers = GetChatUsers();
for each (Microsoft::Xbox::GameChat::ChatUser^ chatUser in chatUsers)
{
if (StringUtils::IsStringEqualCaseInsenstive(chatUser->User->XboxUserId, user->XboxUserId) )
{
chatUserAddedOrUpdated = chatUser;
break;
}
}
// Verify the user isn't already in the channel to avoid adding the user twice
if( false == DoesChannelContainUser( channel->Participants, user ) )
{
// Add a ChatParticipant to the channel
Windows::Xbox::Chat::IChatParticipant^ chatParticipant = ref new ChatParticipant( user );
channel->Participants->Append( chatParticipant );
if( chatUserAddedOrUpdated != nullptr )
{
// If the ChatUser already exists, then append the channel to his channel list.
chatUserAddedOrUpdated->AddChannelIndexToChannelList(channelIndex);
if( m_chatManagerSettings->IsAtDiagnosticsTraceLevel(GameChatDiagnosticsTraceLevel::Info) )
{
LogCommentFormat( L"AddUserToChatChannel: AddChannelIndexToChannelList: XUID: %s chatUser: 0x%0.8x", user->XboxUserId->Data(), chatUserAddedOrUpdated );
}
}
else
{
// No pre-existing ChatUser found, so create one
chatUserAddedOrUpdated = AddUserToUserDataMap(user, channelIndex, isLocal, uniqueConsoleIdentifier, chatParticipant);
if( m_chatManagerSettings->IsAtDiagnosticsTraceLevel(GameChatDiagnosticsTraceLevel::Info) )
{
LogCommentFormat( L"AddUserToChatChannel: AddUserToAudioDevicesMap: XUID: %s UserId: 0x%0.8x chatUser: 0x%0.8x", user->XboxUserId->Data(), user->Id, chatUserAddedOrUpdated );
}
}
}
}
// The user may now have a new audio device, so link all of this user's audio devices to the userId
AddUserToAudioDevicesMap(user);
UpdateSessionState();
return chatUserAddedOrUpdated;
}
uint8 ChatClient::GetLocalNameOfRemoteConsole(
_In_ Platform::Object^ consoleIdentifier
)
{
// Note that the local console will not be added to this list
bool matchFound = false;
uint8 localName = 0;
Concurrency::critical_section::scoped_lock lock(m_localNameOfRemoteConsolesLock);
ChatManager^ chatManager = m_chatManager.Resolve<ChatManager>();
if( chatManager != nullptr )
{
for each (std::shared_ptr<ConsoleNameIdentifierPair> uniqueConsole in m_localNameOfRemoteConsoles)
{
if( chatManager->OnCompareUniqueConsoleIdentifiersHandler(uniqueConsole->uniqueConsoleIdentifier, consoleIdentifier) )
{
matchFound = true;
localName = uniqueConsole->consoleName;
break;
}
}
}
if( !matchFound )
{
std::shared_ptr<ConsoleNameIdentifierPair> consoleNameIdentifierPair(new ConsoleNameIdentifierPair());
consoleNameIdentifierPair->uniqueConsoleIdentifier = consoleIdentifier;
bool success = CallerLock_GetUnusedLocalNameOfRemoteConsole(localName);
if( success )
{
consoleNameIdentifierPair->consoleName = localName;
m_localNameOfRemoteConsoles.push_back( consoleNameIdentifierPair );
}
}
return localName;
}
std::vector< std::shared_ptr<ConsoleNameIdentifierPair> > ChatClient::GetMyLocalNameOfRemoteConsolesCopy()
{
Concurrency::critical_section::scoped_lock lock(m_localNameOfRemoteConsolesLock);
std::vector< std::shared_ptr<ConsoleNameIdentifierPair> > myLocalNameOfRemoteConsolesCopy( m_localNameOfRemoteConsoles );
return myLocalNameOfRemoteConsolesCopy;
}
void ChatClient::RemoveUserFromAllChatChannels(
_In_ IUser^ user
)
{
CHAT_THROW_INVALIDARGUMENT_IF_NULL( user );
{
Concurrency::critical_section::scoped_lock lock(m_chatSessionLock);
UINT numChannels = m_chatSession->Channels->Size;
for (uint8 channelIndex = 0; channelIndex < numChannels; channelIndex++)
{
if (DoesChannelExist(channelIndex))
{
IChatChannel^ channel = m_chatSession->Channels->GetAt(channelIndex);
bool removeFromAllChannels = true;
RemoveUserFromChatChannelHelper(channel, user, 0, removeFromAllChannels);
}
}
}
UpdateSessionState();
}
void ChatClient::RemoveUserFromChatChannel(
_In_ uint8 channelIndex,
_In_ IUser^ user,
_In_ bool updateSessionState
)
{
CHAT_THROW_INVALIDARGUMENT_IF_NULL( user );
CHAT_THROW_INVALIDARGUMENT_IF( channelIndex >= NUMBER_OF_CHAT_CHANNELS );
{
Concurrency::critical_section::scoped_lock lock(m_chatSessionLock);
if (DoesChannelExist(channelIndex))
{
IChatChannel^ channel = m_chatSession->Channels->GetAt(channelIndex);
bool removeFromAllChannels = false;
RemoveUserFromChatChannelHelper(channel, user, channelIndex, removeFromAllChannels);
}
}
if( updateSessionState )
{
UpdateSessionState();
}
}
void ChatClient::RemoveUserFromChatChannelHelper(
_In_ IChatChannel^ channel,
_In_ IUser^ user,
_In_ uint8 channelIndex,
_In_ bool removeFromAllChannels
)
{
CHAT_THROW_INVALIDARGUMENT_IF_NULL( user );
CHAT_THROW_INVALIDARGUMENT_IF_NULL( channel );
// Note: This function does not require a lock as the functions
// that call his helper already have a m_chatSessionLock.
UINT numParticipants = channel->Participants->Size;
for (UINT j=0; j < numParticipants; )
{
IChatParticipant^ participant = channel->Participants->GetAt(j);
if( participant != nullptr &&
participant->User != nullptr &&
StringUtils::IsStringEqualCaseInsenstive(participant->User->XboxUserId, user->XboxUserId) )
{
channel->Participants->RemoveAt(j);
// Update the UserDataMap.
RemoveUserFromUserDataMap(user, channelIndex, removeFromAllChannels);
break;
}
else
{
j++;
}
}
}
void ChatClient::RemoveLocalNameOfRemoteConsole(
_In_ Platform::Object^ uniqueRemoteConsoleIdentifier
)
{
Concurrency::critical_section::scoped_lock lock(m_localNameOfRemoteConsolesLock);
ChatManager^ chatManager = m_chatManager.Resolve<ChatManager>();
if( chatManager != nullptr )
{
bool found = false;
auto iter = m_localNameOfRemoteConsoles.begin();
for( ; iter != m_localNameOfRemoteConsoles.end(); iter++ )
{
std::shared_ptr<ConsoleNameIdentifierPair> iterConsoleNameIdentifier = *iter;
if( chatManager->OnCompareUniqueConsoleIdentifiersHandler(iterConsoleNameIdentifier->uniqueConsoleIdentifier, uniqueRemoteConsoleIdentifier) )
{
found = true;
break;
}
}
if (found)
{
m_localNameOfRemoteConsoles.erase(iter);
}
}
}
bool ChatClient::DoesChannelExist(
_In_ uint8 channelIndex
)
{
CHAT_THROW_INVALIDARGUMENT_IF( channelIndex >= NUMBER_OF_CHAT_CHANNELS );
// Note: This function does not require a lock as the functions
// that call his helper already have a m_chatSessionLock.
if (channelIndex >= m_chatSession->Channels->Size)
{
return false;
}
IChatChannel^ channel = m_chatSession->Channels->GetAt(channelIndex);
return channel != nullptr;
}
IChatParticipant^ ChatClient::GetChatParticipantFromUserId(
_In_ unsigned int userId
)
{
Concurrency::critical_section::scoped_lock lock(m_chatSessionLock);
// Note that this could be turned into a lookup map for more efficient lookup
// The map can be updated updated every time the session state changes
for (UINT i=0; i < m_chatSession->Channels->Size; i++)
{
IChatChannel^ channel = m_chatSession->Channels->GetAt(i);
for (UINT j=0; j < channel->Participants->Size; j++)
{
IChatParticipant^ participant = channel->Participants->GetAt(j);
if( participant != nullptr &&
participant->User != nullptr &&
participant->User->Id == userId )
{
return participant;
}
}
}
return nullptr;
}
UINT ChatClient::GetChatChannelIndexFromUserId(
_In_ unsigned int userId
)
{
Concurrency::critical_section::scoped_lock lock(m_chatSessionLock);
// Note that this could be turned into a lookup map for more efficient lookup
// The map can be updated updated every time the session state changes
for (UINT i=0; i < m_chatSession->Channels->Size; i++)
{
IChatChannel^ channel = m_chatSession->Channels->GetAt(i);
for (UINT j=0; j < channel->Participants->Size; j++)
{
IChatParticipant^ participant = channel->Participants->GetAt(j);
if( participant != nullptr &&
participant->User != nullptr &&
participant->User->Id == userId )
{
return i;
}
}
}
return NUMBER_OF_CHAT_CHANNELS;
}
void ChatClient::ChangeMuteStateForChatParticipantFromAllChannels(
_In_ unsigned int userId,
_In_ float volume
)
{
Concurrency::critical_section::scoped_lock lock(m_chatSessionLock);
for (UINT i=0; i < m_chatSession->Channels->Size; i++)
{
IChatChannel^ channel = m_chatSession->Channels->GetAt(i);
for (UINT j=0; j < channel->Participants->Size; j++)
{
IChatParticipant^ participant = channel->Participants->GetAt(j);
if( participant != nullptr &&
participant->User != nullptr &&
participant->User->Id == userId )
{
participant->Volume = volume;
break;
}
}
}
}
void ChatClient::ChatSessionStateChanged(
_In_ IChatSession^ chatSession,
_In_ ChatSessionStateChangeReason reason
)
{
UNREFERENCED_PARAMETER( chatSession );
if (reason == ChatSessionStateChangeReason::MicrophoneFocusGained)
{
ChatAudioThread^ chatAudioThread = m_chatAudioThread.Resolve<ChatAudioThread>();
if (chatAudioThread != nullptr)
{
chatAudioThread->HasMicFocus = true;
#if WINAPI_FAMILY == WINAPI_FAMILY_TV_TITLE
TraceChatMicFocus( chatAudioThread->HasMicFocus );
#endif
}
}
UpdateSessionState();
}
void ChatClient::RemoveAllUsers()
{
Concurrency::critical_section::scoped_lock lock(m_chatSessionLock);
m_chatSession->Channels->Clear();
}
void ChatClient::RemoveRemoteConsole(
_In_ Platform::Object^ uniqueRemoteConsoleIdentifier
)
{
CHAT_THROW_INVALIDARGUMENT_IF_NULL(uniqueRemoteConsoleIdentifier);
RemoveLocalNameOfRemoteConsole(uniqueRemoteConsoleIdentifier);
Windows::Foundation::Collections::IVectorView<unsigned int>^ userIds = GetAllUserIdsForConsoleId( uniqueRemoteConsoleIdentifier );
for each (unsigned int userId in userIds)
{
IChatParticipant^ chatUserToRemove = GetChatParticipantFromUserId(userId);
if( chatUserToRemove != nullptr )
{
IUser^ userToRemove = chatUserToRemove->User;
if( userToRemove != nullptr )
{
RemoveUserFromAllChatChannels(userToRemove);
}
}
}
}
IChatParticipant^ ChatClient::GetChatParticipantFromChatSession(
_In_ Platform::String^ xboxUserId,
_In_ UINT channelIndex
)
{
Concurrency::critical_section::scoped_lock lock(m_chatSessionLock);
if (DoesChannelExist((uint8)channelIndex))
{
IChatChannel^ channel = m_chatSession->Channels->GetAt(channelIndex);
for (UINT j=0; j < channel->Participants->Size; ++j)
{
IChatParticipant^ participant = channel->Participants->GetAt(j);
if( participant != nullptr &&
participant->User != nullptr &&
StringUtils::IsStringEqualCaseInsenstive(participant->User->XboxUserId, xboxUserId) )
{
return participant;
}
}
}
return nullptr;
}
Windows::Foundation::Collections::IVectorView<unsigned int>^ ChatClient::GetAllUserIdsForConsoleId(
_In_ Platform::Object^ uniqueConsoleIdentifier
)
{
Platform::Collections::Vector<unsigned int>^ userIds = ref new Platform::Collections::Vector<unsigned int>();
ChatManager^ chatManager = m_chatManager.Resolve<ChatManager>();
if( chatManager != nullptr )
{
Windows::Foundation::Collections::IVectorView<Microsoft::Xbox::GameChat::ChatUser^>^ chatUsers = GetChatUsers();
for each (Microsoft::Xbox::GameChat::ChatUser^ chatUser in chatUsers)
{
if( chatManager->OnCompareUniqueConsoleIdentifiersHandler(chatUser->UniqueConsoleIdentifier, uniqueConsoleIdentifier) )
{
unsigned int userId = chatUser->User->Id;
userIds->Append(userId);
}
}
}
return userIds->GetView();
}
Windows::Foundation::Collections::IVectorView<ChatUser^>^ ChatClient::GetChatUsers()
{
Concurrency::critical_section::scoped_lock lock(m_userIdsToUserDataLock);
Platform::Collections::Vector<ChatUser^>^ chatUsers = ref new Platform::Collections::Vector<ChatUser^>();
for(std::map<unsigned int, ChatUser^>::iterator iter = m_userIdsToUserData.begin(); iter != m_userIdsToUserData.end(); ++iter)
{
ChatUser^ user = iter->second;
if( user != nullptr )
{
chatUsers->Append(user);
}
}
return chatUsers->GetView();
}
ChatUser^ ChatClient::GetChatUserForUserId(
_In_ unsigned int userId
)
{
Concurrency::critical_section::scoped_lock lock(m_userIdsToUserDataLock);
if (m_userIdsToUserData.find(userId) != m_userIdsToUserData.end())
{
return m_userIdsToUserData[userId];
}
return nullptr;
};
std::vector<unsigned int> ChatClient::GetUserIdsForDevice(
_In_ Platform::String^ captureSourceId
)
{
std::vector<unsigned int> ids;
Concurrency::critical_section::scoped_lock lock(m_audioCaptureSourceIdToUserIdLock);
if( m_chatManagerSettings->IsAtDiagnosticsTraceLevel(GameChatDiagnosticsTraceLevel::Verbose) )
{
LogCommentFormat( L"GetUserIdsForDevice: Size:%d", m_audioCaptureSourceIdToUserId.size() );
for(std::map<Platform::String^, std::hash_set<unsigned int>>::iterator iter = m_audioCaptureSourceIdToUserId.begin(); iter != m_audioCaptureSourceIdToUserId.end(); ++iter)
{
LogCommentFormat( L"GetUserIdsForDevice: captureSourceId: %s", iter->first->Data() );
std::hash_set<unsigned int> hashSet = iter->second;
for each (unsigned int userId in hashSet)
{
LogCommentFormat( L"GetUserIdsForDevice: userId: 0x%0.8x", userId );
}
}
}
if (m_audioCaptureSourceIdToUserId.find(captureSourceId) != m_audioCaptureSourceIdToUserId.end())
{
ids.insert( std::begin( ids ), std::begin( m_audioCaptureSourceIdToUserId[captureSourceId] ), std::end( m_audioCaptureSourceIdToUserId[captureSourceId] ) );
}
return std::move( ids );
};
void ChatClient::CorrelateDeviceToUser(
_In_ Platform::String^ deviceId,
_In_ unsigned int userId,
_In_ bool isSharedDevice
)
{
CHAT_THROW_INVALIDARGUMENT_IF_STRING_EMPTY( deviceId );
Concurrency::critical_section::scoped_lock lock(m_audioCaptureSourceIdToUserIdLock);
if( !isSharedDevice )
{
// Exclusive devices like headset can only have one user tied to them so clear out old users.
// This happens when 2 users have sign-in with Kinect enabled and swap controllers
m_audioCaptureSourceIdToUserId[deviceId].clear();
}
if( m_audioCaptureSourceIdToUserId[deviceId].find(userId) == m_audioCaptureSourceIdToUserId[deviceId].end() )
{
m_audioCaptureSourceIdToUserId[deviceId].insert( userId );
}
#if WINAPI_FAMILY == WINAPI_FAMILY_TV_TITLE
TraceChatCorrelateAudioDeviceToUser(
deviceId->Data(),
userId,
isSharedDevice,
false
);
#endif
}
IChatSession^ ChatClient::GetChatSession()
{
return m_chatSession;
}
void ChatClient::UpdateSessionState()
{
Concurrency::critical_section::scoped_lock lock(m_chatSessionLock);
CHAT_LOG_INFO_MSG( L"Windows::Xbox::Chat::IChatSession::GetStateAsync starting" );
auto asyncOp = m_chatSession->GetStateAsync();
create_task(asyncOp)
.then([this] (task<Windows::Xbox::Chat::IChatSessionState^> t)
{
try
{
CHAT_LOG_INFO_MSG( L"Windows::Xbox::Chat::IChatSession::GetStateAsync complete" );
IChatSessionState^ chatSessionState = t.get();
ChatManager^ chatManager = m_chatManager.Resolve<ChatManager>();
if( chatManager != nullptr )
{
chatManager->OnChatSessionStateChangedHandler( chatSessionState );
}
}
catch ( Platform::COMException^ ex )
{
if (ex->HResult == (HRESULT)Windows::Xbox::Chat::ChatErrorStatus::NoMicrophoneFocus)
{
ChatAudioThread^ chatAudioThread = m_chatAudioThread.Resolve<ChatAudioThread>();
if (chatAudioThread != nullptr)
{
chatAudioThread->HasMicFocus = false;
#if WINAPI_FAMILY == WINAPI_FAMILY_TV_TITLE
TraceChatMicFocus( chatAudioThread->HasMicFocus );
#endif
}
}
// ERROR NOT FOUND is expected
if (ex->HResult != HRESULT_FROM_WIN32(ERROR_NOT_FOUND))
{
if (ex->HResult == AUDCLNT_E_DEVICE_IN_USE)
{
// Package.appxmanifest should contain capabilities like this:
//
// <Capabilities>
// <Capability Name="internetClientServer" />
// <mx:Capability Name="kinectAudio"/>
// <mx:Capability Name="kinectGamechat"/>
// </Capabilities>
LogCommentWithError( L"GetStateAsync failed. This error often indicates missing kinectGamechat in package.appxmanifest", ex->HResult );
}
else
{
LogCommentWithError( L"GetStateAsync", ex->HResult );
}
}
}
}).wait();
}
void ChatClient::RemoveUserFromUserDataMap(
_In_ IUser^ user,
_In_ uint8 channelIndex,
_In_ bool removeFromAllChannels
)
{
CHAT_THROW_INVALIDARGUMENT_IF_NULL( user );
Concurrency::critical_section::scoped_lock lock(m_userIdsToUserDataLock);
if( !user->XboxUserId->IsEmpty() )
{
if (m_userIdsToUserData.find(user->Id) != m_userIdsToUserData.end())
{
ChatUser^ chatUser = m_userIdsToUserData[user->Id];
if (removeFromAllChannels || chatUser->GetAllChannels()->Size == 1)
{
RemoveUserIdFromAudioCaptureSourceIdMap(user);
// If the user is only in 1 channel, then remove him completely.
m_userIdsToUserData.erase(user->Id);
}
else
{
// else, just remove the channel index from the channel list.
if (chatUser)
{
chatUser->RemoveChannelIndexFromChannelList(channelIndex);
}
}
}
}
}
void ChatClient::RemoveUserIdFromAudioCaptureSourceIdMap(
_In_ IUser^ user
)
{
CHAT_THROW_INVALIDARGUMENT_IF_NULL( user );
Concurrency::critical_section::scoped_lock lock(m_audioCaptureSourceIdToUserIdLock);
for(std::map<Platform::String^, std::hash_set<unsigned int>>::iterator iter = m_audioCaptureSourceIdToUserId.begin(); iter != m_audioCaptureSourceIdToUserId.end(); ++iter)
{
m_audioCaptureSourceIdToUserId[iter->first].erase( user->Id );
#if WINAPI_FAMILY == WINAPI_FAMILY_TV_TITLE
TraceChatCorrelateAudioDeviceToUser(
iter->first->Data(),
user->Id,
false,
true
);
#endif
}
}
Microsoft::Xbox::GameChat::ChatUser^
ChatClient::AddUserToUserDataMap(
_In_ IUser^ user,
_In_ uint8 channelIndex,
_In_ bool isLocal,
_In_opt_ Platform::Object^ uniqueConsoleIdentifier,
_In_ Windows::Xbox::Chat::IChatParticipant^ chatParticipant
)
{
CHAT_THROW_E_POINTER_IF_NULL( user );
CHAT_THROW_E_POINTER_IF_NULL( chatParticipant );
// Add user meta data for lookup
Concurrency::critical_section::scoped_lock lock(m_userIdsToUserDataLock);
if ( m_userIdsToUserData.find( user->Id ) == m_userIdsToUserData.end() )
{
m_userIdsToUserData[user->Id] = ref new ChatUser(user->XboxUserId, isLocal, uniqueConsoleIdentifier, chatParticipant);
}
ChatUser^ chatUser = m_userIdsToUserData[user->Id];
m_userIdsToUserData[user->Id]->AddChannelIndexToChannelList(channelIndex);
return chatUser;
}
void ChatClient::AddUserToAudioDevicesMap(
_In_ IUser^ user
)
{
CHAT_THROW_E_POINTER_IF_NULL( user );
auto audioDevices = user->AudioDevices;
for (UINT i = 0; i < audioDevices->Size; i++)
{
// Store relation of device ids to user id
auto device = audioDevices->GetAt(i);
auto deviceId = device->Id;
auto userId = user->Id;
bool isSharedDevice = (device->Sharing == Windows::Xbox::System::AudioDeviceSharing::Shared);
CorrelateDeviceToUser(deviceId, userId, isSharedDevice);
}
}
std::vector<ChatUser^> ChatClient::GetChatUsersForCaptureSourceId(
_In_ Platform::String^ captureSourceId
)
{
std::vector<ChatUser^> chatUsers;
auto userIdsForDevice = GetUserIdsForDevice( captureSourceId );
for ( auto uid : userIdsForDevice )
{
auto user = GetChatUserForUserId( uid );
if (user != nullptr)
{
chatUsers.push_back(user);
}
}
return chatUsers;
}
void ChatClient::LogComment(
_In_ Platform::String^ message
)
{
LogCommentWithError(message, S_OK);
}
void ChatClient::LogCommentWithError(
_In_ Platform::String^ message,
_In_ HRESULT hr
)
{
DebugMessageEventArgs^ args = ref new DebugMessageEventArgs(message, hr);
ChatManager^ chatManager = m_chatManager.Resolve<ChatManager>();
if( chatManager != nullptr )
{
chatManager->OnDebugMessageHandler( args );
}
}
void ChatClient::ClearTalkingModeForAllChatUsers()
{
Windows::Foundation::Collections::IVectorView<Microsoft::Xbox::GameChat::ChatUser^>^ chatUsers = GetChatUsers();
for each (Microsoft::Xbox::GameChat::ChatUser^ chatUser in chatUsers)
{
chatUser->SetTalkingMode(ChatUserTalkingMode::NotTalking);
}
}
bool ChatClient::CallerLock_GetUnusedLocalNameOfRemoteConsole(
_Out_ uint8& localNameOfRemoteConsole
)
{
// Note that the local console will not be added to this list
unsigned int localName = 0;
// Not taking lock here since caller will hold the lock
// Concurrency::critical_section::scoped_lock lock(m_localNameOfRemoteConsolesLock);
// Look for the first unused name in our list of remote console names. 0 is reserved for local console.
bool foundUnusedLocalName = true;
for( localName=1; localName<=0xFF; localName++ )
{
foundUnusedLocalName = !IsLocalNameTaken( static_cast<uint8>(localName) );
// If we found a free slot then stop. We will hand that unused name. Otherwise keep looking
if( foundUnusedLocalName )
{
break;
}
}
if( !foundUnusedLocalName )
{
LogCommentWithError( L"Couldn't find unused local name for remote console. Should not happen due limited of number of network connections allowed", E_UNEXPECTED );
return false;
}
localNameOfRemoteConsole = static_cast<uint8>(localName);
return true;
}
void ChatClient::SetChatAudioThread(
_In_ ChatAudioThread^ chatAudioThread
)
{
m_chatAudioThread = chatAudioThread;
}
bool ChatClient::IsLocalNameTaken( uint8 localName )
{
bool isTaken = false;
for each (std::shared_ptr<ConsoleNameIdentifierPair> uniqueConsole in m_localNameOfRemoteConsoles)
{
if( uniqueConsole->consoleName == localName )
{
isTaken = true;
break;
}
}
return isTaken;
}
bool ChatClient::DoesRemoteUniqueConsoleIdentifierExist(
_In_ Platform::Object^ remoteUniqueConsoleIdentifier
)
{
CHAT_THROW_INVALIDARGUMENT_IF_NULL( remoteUniqueConsoleIdentifier );
ChatManager^ chatManager = m_chatManager.Resolve<ChatManager>();
if( remoteUniqueConsoleIdentifier != nullptr &&
chatManager != nullptr )
{
Windows::Foundation::Collections::IVectorView<Microsoft::Xbox::GameChat::ChatUser^>^ chatUsers = GetChatUsers();
for each (Microsoft::Xbox::GameChat::ChatUser^ chatUser in chatUsers)
{
if( chatManager->OnCompareUniqueConsoleIdentifiersHandler(chatUser->UniqueConsoleIdentifier, remoteUniqueConsoleIdentifier) )
{
return true;
}
}
}
return false;
}
}}}
#endif
+254
View File
@@ -0,0 +1,254 @@
//// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
//// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
//// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//// PARTICULAR PURPOSE.
////
//// Copyright (c) Microsoft Corporation. All rights reserved
#pragma once
#include "ChatUser.h"
#include "Thread.h"
#include "ChatManagerSettings.h"
#define NUMBER_OF_CHAT_BUFFER_LEVELS 16
#define NUMBER_OF_CHAT_CHANNELS 256
#if TV_API
// Forward declare
namespace Microsoft { namespace Xbox { namespace GameChat { ref class ChatAudioThread; } } }
namespace Microsoft { namespace Xbox { namespace GameChat { ref class ChatManager; } } }
namespace Microsoft {
namespace Xbox {
namespace GameChat {
struct ConsoleNameIdentifierPair
{
Platform::Object^ uniqueConsoleIdentifier;
uint8 consoleName;
};
/// <summary>
/// ChatClient class implements the guts of the chat system capture / render interface.
/// This class handles management of the chat session, adding and removing
/// participants from the chat channel as appropriate.
/// </summary>
ref class ChatClient sealed
{
public:
ChatClient(
_In_ uint32 chatPeriodInMilliseconds,
_In_ ChatManager^ chatManager,
_In_ ChatManagerSettings^ chatManagerSettings
);
Windows::Xbox::Chat::IChatSession^ GetChatSession();
void SetChatAudioThread(
_In_ ChatAudioThread^ chatAudioThread
);
void AddNewChannel();
Microsoft::Xbox::GameChat::ChatUser^ AddUserToChatChannel(
_In_ uint8 channelIndex,
_In_ Windows::Xbox::System::IUser^ user,
_In_opt_ Platform::Object^ uniqueConsoleIdentifier,
_In_ bool isLocal,
_In_opt_ Windows::Xbox::Chat::IChatParticipant^ chatParticipantToUpdate
);
Windows::Xbox::Chat::IChatParticipant^ GetChatParticipantFromChatSession(
_In_ Platform::String^ xboxUserId,
_In_ UINT channelIndex
);
void RemoveAllUsers();
/// <summary>
/// Remove all users from the channel that are on a given console. Used when
/// console connectivity is lost
/// </summary>
void RemoveRemoteConsole(
_In_ Platform::Object^ uniqueRemoteConsoleIdentifier
);
void RemoveUserFromChatChannel(
_In_ uint8 channelIndex,
_In_ Windows::Xbox::System::IUser^ user,
_In_ bool updateSessionState
);
void RemoveUserFromAllChatChannels(
_In_ Windows::Xbox::System::IUser^ user
);
/// <summary>
/// Takes an IUser as input, generates a ChatParitcipant and adds to the
/// chat channel. Note that device::user correlation is done here as well
/// </summary>
Windows::Foundation::Collections::IVectorView<unsigned int>^ GetAllUserIdsForConsoleId(
_In_ Platform::Object^ uniqueConsoleIdentifier
);
Windows::Xbox::Chat::IChatParticipant^ GetChatParticipantFromUserId(
_In_ unsigned int userId
);
/// <summary>
/// Returns NUMBER_OF_CHAT_CHANNELS if the user doesn't exists
/// </summary>
UINT GetChatChannelIndexFromUserId(
_In_ unsigned int userId
);
Windows::Foundation::Collections::IVectorView<ChatUser^>^ GetChatUsers();
internal:
void LogCommentFormat(
_In_ LPCWSTR strMsg, ...
);
std::vector<ChatUser^> GetChatUsersForCaptureSourceId(
_In_ Platform::String^ captureSourceId
);
std::vector<unsigned int> GetUserIdsForDevice(
_In_ Platform::String^ captureSourceId
);
void ChangeMuteStateForChatParticipantFromAllChannels(
_In_ unsigned int userId,
_In_ float volume
);
ChatUser^ GetChatUserForUserId(
_In_ unsigned int userId
);
void RemoveUserFromChatChannelHelper(
_In_ Windows::Xbox::Chat::IChatChannel^ channel,
_In_ Windows::Xbox::System::IUser^ user,
_In_ uint8 channelIndex,
_In_ bool removeFromAllChannels
);
void UpdateSessionState();
/// <summary>
/// We set all chat users to NotTalking, and let the capture and render source
/// set the talking mode in the AudioThreadDoWork update.
/// </summary>
void ClearTalkingModeForAllChatUsers();
/// <summary>
/// Returns the local name of a remote console. The name is uint8
/// </summary>
/// <param name="consoleIdentifier">The remote console to return the local name of</param>
uint8 GetLocalNameOfRemoteConsole(
_In_ Platform::Object^ consoleIdentifier
);
/// <summary>
/// Returns the list of names this local console has assigned to the remote consoles
/// </summary>
std::vector< std::shared_ptr<ConsoleNameIdentifierPair> > GetMyLocalNameOfRemoteConsolesCopy();
bool DoesRemoteUniqueConsoleIdentifierExist(
_In_ Platform::Object^ remoteUniqueConsoleIdentifier
);
private:
void LogComment(
_In_ Platform::String^ message
);
void LogCommentWithError(
_In_ Platform::String^ message,
_In_ HRESULT hr
);
bool DoesChannelContainUser(
_In_ Windows::Foundation::Collections::IVector<Windows::Xbox::Chat::IChatParticipant^>^ participants,
_In_ Windows::Xbox::System::IUser^ user
);
void CorrelateDeviceToUser(
_In_ Platform::String^ deviceId,
_In_ unsigned int userId,
_In_ bool isSharedDevice
);
/// <summary>
/// Called by most of the event handlers when a player or device is added or
/// removed, updates the session state (above)
/// </summary>
void ChatSessionStateChanged(
_In_ Windows::Xbox::Chat::IChatSession^ chatSession,
_In_ Windows::Xbox::Chat::ChatSessionStateChangeReason reason
);
Windows::Xbox::Chat::IChatParticipant^ AddUserToChatSessionChannel(
_In_ uint8 channelIndex,
_In_ Windows::Xbox::System::IUser^ user
);
void AddUserToAudioDevicesMap(
_In_ Windows::Xbox::System::IUser^ user
);
Microsoft::Xbox::GameChat::ChatUser^
AddUserToUserDataMap(
_In_ Windows::Xbox::System::IUser^ user,
_In_ uint8 channelIndex,
_In_ bool isLocal,
_In_opt_ Platform::Object^ uniqueConsoleIdentifier,
_In_ Windows::Xbox::Chat::IChatParticipant^ chatParticipant
);
void ChatClient::RemoveUserFromUserDataMap(
_In_ Windows::Xbox::System::IUser^ user,
_In_ uint8 channelIndex,
_In_ bool removeFromAllChannels
);
void RemoveUserIdFromAudioCaptureSourceIdMap(
_In_ Windows::Xbox::System::IUser^ user
);
void RemoveLocalNameOfRemoteConsole(
_In_ Platform::Object^ uniqueRemoteConsoleIdentifier
);
bool DoesChannelExist(
_In_ uint8 channelIndex
);
bool CallerLock_GetUnusedLocalNameOfRemoteConsole( _Out_ uint8& localNameOfRemoteConsole );
bool IsLocalNameTaken( uint8 localName );
private:
~ChatClient();
Windows::Foundation::EventRegistrationToken m_tokenStateChangedEvent;
Concurrency::critical_section m_chatSessionLock; // Locks down m_chatSession
Concurrency::critical_section m_audioCaptureSourceIdToUserIdLock;
Concurrency::critical_section m_userIdsToUserDataLock;
Concurrency::critical_section m_localNameOfRemoteConsolesLock;
Windows::Xbox::Chat::IChatSession^ m_chatSession;
std::map<UINT, UINT> m_userIdsToBufferSizes;
Platform::WeakReference m_chatManager;
ChatManagerSettings^ m_chatManagerSettings;
std::vector< std::shared_ptr<ConsoleNameIdentifierPair> > m_localNameOfRemoteConsoles;
std::map<Platform::String^, std::hash_set<unsigned int>> m_audioCaptureSourceIdToUserId;
std::map<unsigned int, ChatUser^> m_userIdsToUserData;
Platform::WeakReference m_chatAudioThread;
};
}}}
#endif
+300
View File
@@ -0,0 +1,300 @@
//// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
//// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
//// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//// PARTICULAR PURPOSE.
////
//// Copyright (c) Microsoft Corporation. All rights reserved
#include "pch.h"
#include "ChatUser.h"
#include "StringUtils.h"
#if TV_API
namespace Microsoft {
namespace Xbox {
namespace GameChat {
ChatUser::ChatUser(
_In_ Platform::String^ xuid,
_In_ bool isLocal,
_In_opt_ Platform::Object^ uniqueConsoleIdentifier,
_In_ Windows::Xbox::Chat::IChatParticipant^ chatParticipant
) :
m_xuid(xuid),
m_isLocal(isLocal),
m_uniqueConsoleIdentifier(uniqueConsoleIdentifier),
m_chatParticipant(chatParticipant),
m_talkingMode(ChatUserTalkingMode::NotTalking),
m_restrictionMode(Windows::Xbox::Chat::ChatRestriction::None),
m_chatParticipantVolume( 0.0f ),
m_dynamicNeededPacketCount( 0 ),
m_numberOfPendingAudioPacketsToPlay( 0 ),
m_isMuted( false ),
m_shouldBepermanentlyMuted( false )
{
CHAT_THROW_E_POINTER_IF_NULL( chatParticipant );
CHAT_THROW_INVALIDARGUMENT_IF_STRING_EMPTY( xuid );
}
Platform::String^ ChatUser::XboxUserId::get()
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
return m_xuid;
};
Platform::Object^ ChatUser::UniqueConsoleIdentifier::get()
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
return m_uniqueConsoleIdentifier;
}
Windows::Xbox::Chat::IChatParticipant^ ChatUser::ChatParticipant::get()
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
return m_chatParticipant;
}
Windows::Xbox::System::IUser^ ChatUser::User::get()
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
return m_chatParticipant->User;
}
ChatUserTalkingMode ChatUser::TalkingMode::get()
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
ChatUserTalkingMode result = (ChatUserTalkingMode) m_talkingMode;
return result;
}
void ChatUser::SetTalkingMode(
_In_ ChatUserTalkingMode mode
)
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
m_talkingMode = mode;
};
uint32 ChatUser::NumberOfPendingAudioPacketsToPlay::get()
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
return m_numberOfPendingAudioPacketsToPlay;
}
void ChatUser::SetNumberOfPendingAudioPacketsToPlay(
_In_ uint32 numberOfPendingAudioPacketsToPlay
)
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
m_numberOfPendingAudioPacketsToPlay = numberOfPendingAudioPacketsToPlay;
}
Windows::Xbox::Chat::ChatRestriction ChatUser::RestrictionMode::get()
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
return m_restrictionMode;
}
void ChatUser::SetRestrictionMode(
_In_ Windows::Xbox::Chat::ChatRestriction restriction
)
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
m_restrictionMode = restriction;
};
bool ChatUser::IsLocal::get()
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
return m_isLocal;
}
bool ChatUser::IsMutedPermanently::get()
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
return m_shouldBepermanentlyMuted;
}
bool ChatUser::IsLocalUserMuted::get()
{
// If muting myself, then don't send capture packets from that user
Concurrency::critical_section::scoped_lock lock(m_stateLock);
return m_isMuted && m_isLocal;
}
void ChatUser::Mute()
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
m_isMuted = true;
}
void ChatUser::Unmute()
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
if (!m_shouldBepermanentlyMuted)
{
m_isMuted = false;
// The restriction will be re-instantiated for the remote user on the next update.
m_restrictionMode = Windows::Xbox::Chat::ChatRestriction::None;
}
}
void ChatUser::MutePermanently()
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
m_shouldBepermanentlyMuted = true;
m_isMuted = true;
}
void ChatUser::UnmutePermanently()
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
m_shouldBepermanentlyMuted = false;
// The restriction will be re-instantiated for the remote user on the next update.
m_restrictionMode = Windows::Xbox::Chat::ChatRestriction::None;
}
bool ChatUser::IsMuted::get()
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
return m_isMuted;
}
Windows::Foundation::Collections::IVectorView<uint8>^ ChatUser::GetAllChannels()
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
Platform::Collections::Vector<uint8>^ channels = ref new Platform::Collections::Vector<uint8>();
for each (uint8 channelIndex in m_channels)
{
channels->Append(channelIndex);
}
return channels->GetView();
}
void ChatUser::AddChannelIndexToChannelList(
_In_ uint8 channelIndex
)
{
bool channelIndexFound = false;
Concurrency::critical_section::scoped_lock lock(m_stateLock);
for each (uint8 existingChannelIndex in m_channels)
{
if( existingChannelIndex == channelIndex )
{
channelIndexFound = true;
break;
}
}
if( !channelIndexFound )
{
m_channels.push_back( channelIndex );
}
}
void ChatUser::RemoveChannelIndexFromChannelList(
_In_ uint8 channelIndex
)
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
bool found = false;
auto iter = m_channels.begin();
for( ; iter != m_channels.end(); iter++ )
{
uint8 existingChannelIndex = *iter;
if ( existingChannelIndex == channelIndex )
{
found = true;
break;
}
}
if (found)
{
m_channels.erase(iter);
}
}
uint32 ChatUser::DynamicNeededPacketCount::get()
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
return m_dynamicNeededPacketCount;
}
void ChatUser::SetDynamicNeededPacketCount(
_In_ uint32 dynamicNeededPacketCount
)
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
m_dynamicNeededPacketCount = dynamicNeededPacketCount;
}
Windows::Xbox::Chat::ChatParticipantTypes ChatUser::ParticipantType::get()
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
return m_chatParticipant->ParticipantType;
}
void ChatUser::ParticipantType::set( Windows::Xbox::Chat::ChatParticipantTypes val )
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
m_chatParticipant->ParticipantType = val;
}
float ChatUser::Volume::get()
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
return m_chatParticipant->Volume;
}
void ChatUser::Volume::set( float val )
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
m_chatParticipant->Volume = val;
}
void ChatUser::SetChatRenderTarget(
_In_ Windows::Xbox::Chat::IChatRenderTarget^ chatRenderTarget
)
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
m_chatRenderTarget = chatRenderTarget;
}
float ChatUser::LocalRenderTargetVolume::get()
{
if( !IsLocal || m_chatRenderTarget == nullptr )
{
return 0.0f;
}
Concurrency::critical_section::scoped_lock lock(m_stateLock);
Windows::Xbox::Chat::IChatRenderTargetVolume^ targetVolume = (Windows::Xbox::Chat::IChatRenderTargetVolume^)m_chatRenderTarget;
return targetVolume->Volume;
}
void ChatUser::LocalRenderTargetVolume::set( float val )
{
if( !IsLocal || m_chatRenderTarget == nullptr )
{
return;
}
Concurrency::critical_section::scoped_lock lock(m_stateLock);
Windows::Xbox::Chat::IChatRenderTargetVolume^ targetVolume = (Windows::Xbox::Chat::IChatRenderTargetVolume^)m_chatRenderTarget;
targetVolume->Volume = val;
}
}}}
#endif
+300
View File
@@ -0,0 +1,300 @@
//// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
//// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
//// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//// PARTICULAR PURPOSE.
////
//// Copyright (c) Microsoft Corporation. All rights reserved
#pragma once
#if TV_API
namespace Microsoft {
namespace Xbox {
namespace GameChat {
/// <summary>
/// Indicates the talking mode of the user
/// </summary>
public enum class ChatUserTalkingMode
{
/// <summary>
/// Indicates the chat user is not talking
/// </summary>
NotTalking = 0x0,
/// <summary>
/// Indicates the chat user is talking over a headset
/// </summary>
TalkingOverHeadset = 0x1,
/// <summary>
/// Indicates the chat user is talking over the Kinect
/// </summary>
TalkingOverKinect = 0x2
};
public ref class ChatUser sealed
{
public:
/// <summary>
/// The XboxUserID of this ChatUser
/// </summary>
property Platform::String^ XboxUserId
{
Platform::String^ get();
}
/// <summary>
/// The UniqueConsoleIdentifier for this ChatUser
/// </summary>
property Platform::Object^ UniqueConsoleIdentifier
{
Platform::Object^ get();
}
/// <summary>
/// Indicates if the this ChatUser is talking or not
/// </summary>
property ChatUserTalkingMode TalkingMode
{
ChatUserTalkingMode get();
}
/// <summary>
/// Indicates number of pending audio packets to play.
/// This is controlled by the jitter buffer.
/// The jitter buffer can be controlled by some settings found on ChatManagerSettings
/// </summary>
property uint32 NumberOfPendingAudioPacketsToPlay
{
uint32 get();
}
/// <summary>
/// Indicates the current value of the dynamic needed packet count.
/// This is the number of packets required before audio will start playing.
/// This number is dynamically adjusted based on heuristics monitoring how often packets arrive from the network
/// </summary>
property uint32 DynamicNeededPacketCount
{
uint32 get();
}
/// <summary>
/// Indicates if the chat user is restricted
/// </summary>
property Windows::Xbox::Chat::ChatRestriction RestrictionMode
{
Windows::Xbox::Chat::ChatRestriction get();
}
/// <summary>
/// Returns the IUser for this ChatUser
/// </summary>
property Windows::Xbox::System::IUser^ User
{
Windows::Xbox::System::IUser^ get();
}
/// <summary>
/// Returns if the user is local for the console
/// </summary>
property bool IsLocal
{
bool get();
}
/// <summary>
/// Returns if the local user is muted.
/// If local user is muted, then doesn't send capture packets to others.
/// </summary>
property bool IsLocalUserMuted
{
bool get();
}
/// <summary>
/// Returns if the user is muted.
/// </summary>
property bool IsMuted
{
bool get();
}
/// <summary>
/// Returns if the user is muted permanently.
/// </summary>
property bool IsMutedPermanently
{
bool get();
}
/// <summary>
/// Returns a list of all channels the user is part of.
/// </summary>
Windows::Foundation::Collections::IVectorView<uint8>^ GetAllChannels();
property Windows::Xbox::Chat::ChatParticipantTypes ParticipantType
{
/// <summary>
/// Gets the ParticipantType for this ChatUser
/// </summary>
Windows::Xbox::Chat::ChatParticipantTypes get();
/// <summary>
/// Sets the ParticipantType for this ChatUser
/// </summary>
void set(Windows::Xbox::Chat::ChatParticipantTypes val);
}
property float Volume
{
/// <summary>
/// Gets the volume for this ChatUser
/// Volume is a percentage 0.0f to 1.0f
/// </summary>
float get();
/// <summary>
/// Sets the volume for this ChatUser
/// Volume is a percentage 0.0f to 1.0f
/// </summary>
void set(float val);
}
property float LocalRenderTargetVolume
{
/// <summary>
/// Gets the volume for the render target if this is a local user
/// Volume is a percentage 0.0f to 1.0f.
/// Returns 0 if this is a remote user
/// </summary>
float get();
/// <summary>
/// Sets the volume for the chat render target if this is a local user.
/// Volume is a percentage 0.0f to 1.0f
/// Value is ignored if this is a remote user
/// </summary>
void set(float val);
}
internal:
/// <summary>
/// Returns the IChatParticipant if this ChatUser
/// </summary>
property Windows::Xbox::Chat::IChatParticipant^ ChatParticipant
{
Windows::Xbox::Chat::IChatParticipant^ get();
}
/// <summary>
/// Mute user permanently
/// </summary>
void MutePermanently();
/// <summary>
/// UnMute user permanently
/// </summary>
void UnmutePermanently();
/// <summary>
/// Internal helper function. If muting local user, then don't send capture packets to others.
/// Else, set ChatParticipant volume to 0.
/// </summary>
void Mute();
/// <summary>
/// Internal helper function to unmute user
/// </summary>
void Unmute();
/// <summary>
/// Internal function to construct a ChatUser
/// </summary>
ChatUser(
_In_ Platform::String^ xuid,
_In_ bool isLocal,
_In_opt_ Platform::Object^ uniqueConsoleIdentifier,
_In_ Windows::Xbox::Chat::IChatParticipant^ chatParticipant
);
/// <summary>
/// Internal function to change the talking mode of the ChatUser
/// </summary>
void SetTalkingMode(
_In_ ChatUserTalkingMode mode
);
/// <summary>
/// Internal function to set number of pending audio packets for this ChatUser
/// </summary>
void SetNumberOfPendingAudioPacketsToPlay(
_In_ uint32 numberOfPendingAudioPacketsToPlay
);
/// <summary>
/// Internal function to set the restriction mode for this chat user
/// </summary>
void SetRestrictionMode(
_In_ Windows::Xbox::Chat::ChatRestriction restriction
);
/// <summary>
/// Internal function to add this chat user to a specific channel
/// </summary>
void AddChannelIndexToChannelList(
_In_ uint8 channelIndex
);
/// <summary>
/// Internal function to remove this chat user from a specific channel
/// </summary>
void RemoveChannelIndexFromChannelList(
_In_ uint8 channelIndex
);
/// <summary>
/// Set the dynamic needed packet count for this chat user
/// </summary>
void SetDynamicNeededPacketCount(
_In_ uint32 dynamicNeededPacketCount
);
/// <summary>
/// Set the Windows::Xbox::Chat::IChatRenderTarget for this chat user
/// </summary>
void SetChatRenderTarget(
_In_ Windows::Xbox::Chat::IChatRenderTarget^ chatRenderTarget
);
private:
Concurrency::critical_section m_stateLock;
Platform::String^ m_xuid;
Platform::Object^ m_uniqueConsoleIdentifier;
Windows::Xbox::Chat::IChatParticipant^ m_chatParticipant;
Platform::String^ m_gamertag;
ChatUserTalkingMode m_talkingMode;
uint32 m_numberOfPendingAudioPacketsToPlay;
Windows::Xbox::Chat::ChatRestriction m_restrictionMode;
bool m_isLocal;
bool m_isMuted;
bool m_shouldBepermanentlyMuted;
uint32 m_dynamicNeededPacketCount;
Windows::Xbox::Chat::IChatRenderTarget^ m_chatRenderTarget;
/// <summary>
/// List of channels the user is currently part of.
/// </summary>
std::vector<uint8> m_channels;
/// <summary>
/// ChatParticipant volume prior to mute.
/// </summary>
float m_chatParticipantVolume;
};
}}}
#endif