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
+29
View File
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ReferencePath>$(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath)</ReferencePath>
<LibraryPath>$(Console_SdkLibPath)</LibraryPath>
<LibraryWPath>$(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath)</LibraryWPath>
<IncludePath>$(Console_SdkIncludeRoot)</IncludePath>
<ExecutablePath>$(Console_SdkRoot)bin;$(VCInstallDir)bin\x86_amd64;$(VCInstallDir)bin;$(WindowsSDK_ExecutablePath_x86);$(VSInstallDir)Common7\Tools\bin;$(VSInstallDir)Common7\tools;$(VSInstallDir)Common7\ide;$(ProgramFiles)\HTML Help Workshop;$(MSBuildToolsPath32);$(FxCopDir);$(PATH);</ExecutablePath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(ApplicationEnvironment)'=='title'">
<Link>
<AdditionalDependencies>ixmlhttprequest2.lib;combase.lib;kernelx.lib;uuid.lib;</AdditionalDependencies>
</Link>
<ClCompile>
<AdditionalUsingDirectories>$(Console_SdkPackagesRoot);$(Console_SdkWindowsMetadataPath);%(AdditionalUsingDirectories)</AdditionalUsingDirectories>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(ApplicationEnvironment)'!='title'">
<Link>
<AdditionalDependencies>msxml6.lib;runtimeobject.lib;mincore.lib;mincore_legacy.lib;mincore_obsolete.lib;user32.lib;uuid.lib;</AdditionalDependencies>
</Link>
<ClCompile>
<AdditionalUsingDirectories>$(Console_SdkPackagesRoot);$(Console_SdkWindowsMetadataPath);%(AdditionalUsingDirectories)</AdditionalUsingDirectories>
</ClCompile>
</ItemDefinitionGroup>
</Project>
+793
View File
@@ -0,0 +1,793 @@
//// 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 "StringUtils.h"
#include "AudioDeviceIDMapper.h"
#include "ChatManagerEvents.h"
#include "ChatAudioThread.h"
#include "ChatClient.h"
#include "ChatNetwork.h"
#include "ChatManager.h"
#if TV_API
using namespace Windows::Foundation;
using namespace concurrency;
namespace Microsoft {
namespace Xbox {
namespace GameChat {
void SetHardwareOffload(bool enabled)
{
PCWSTR chatPath = L"C:\\windows\\system32\\Chat.dll";
HMODULE ChatModule = LoadLibraryEx(chatPath, NULL, 0);
if (ChatModule == NULL)
return;
void (*SetHardwareOffloadEnabled)(bool) =
(void (__cdecl *)(bool))GetProcAddress(ChatModule, "SetHardwareCodecEnabled");
SetHardwareOffloadEnabled(enabled);
return;
}
ChatManager::ChatManager()
{
Initialize( ChatSessionPeriod::ChatPeriodOf40Milliseconds );
}
ChatManager::ChatManager(
_In_ ChatSessionPeriod chatSessionPeriod
)
{
Initialize( chatSessionPeriod );
}
void ChatManager::Initialize(
_In_ ChatSessionPeriod chatSessionPeriod
)
{
// Enable hardware offloaded encoding/decoding by default
SetHardwareOffload(TRUE);
m_factoryCache.reset( new FactoryCache() );
m_chatDiagnostics.reset( new ChatDiagnostics() );
m_chatManagerSettings = ref new ChatManagerSettings(
this
);
uint32 chatSessionPeriodInMilliseconds = ConvertChatSessionPeriodToMilliseconds(chatSessionPeriod);
m_chatClient = ref new ChatClient(
chatSessionPeriodInMilliseconds,
this,
m_chatManagerSettings
);
m_chatAudioThread = ref new ChatAudioThread(
m_chatManagerSettings,
m_chatClient,
this,
m_factoryCache
);
m_chatNetwork = ref new ChatNetwork(
m_chatAudioThread,
m_chatClient,
this,
m_factoryCache,
m_chatManagerSettings
);
m_chatAudioThread->SetChatNetwork( m_chatNetwork );
m_chatClient->SetChatAudioThread( m_chatAudioThread );
}
ChatManagerSettings^ ChatManager::ChatSettings::get()
{
return m_chatManagerSettings;
}
ChatManager::~ChatManager()
{
CHAT_LOG_INFO_MSG(L"ChatManager::~ChatManager");
m_chatAudioThread->Shutdown();
m_chatAudioThread = nullptr;
m_chatClient = nullptr;
m_chatNetwork = nullptr;
}
void ChatManager::LogComment(
_In_ Platform::String^ message
)
{
LogCommentWithError(message, S_OK);
}
void ChatManager::LogCommentWithError(
_In_ Platform::String^ message,
_In_ HRESULT hr
)
{
DebugMessageEventArgs^ args = ref new DebugMessageEventArgs(message, hr);
OnDebugMessage( this, args );
}
void ChatManager::LogCommentFormat(
_In_ LPCWSTR strMsg, ...
)
{
va_list args;
va_start(args, strMsg);
LogComment(StringUtils::GetStringFormat(strMsg, args));
va_end(args);
}
void ChatManager::OnChatPacketReadyHandler(
_In_ Microsoft::Xbox::GameChat::ChatPacketEventArgs^ args
)
{
if( m_chatManagerSettings->PerformanceCountersEnabled )
{
m_chatAudioThread->ChatPerformanceCounters->AddPacketBandwidth( false, args->PacketBuffer->Length );
}
OnOutgoingChatPacketReady( this, args );
}
void ChatManager::OnDebugMessageHandler(
_In_ Microsoft::Xbox::GameChat::DebugMessageEventArgs^ args
)
{
OnDebugMessage( this, args );
}
Windows::Foundation::IAsyncAction^
ChatManager::RemoveLocalUserFromChatChannelAsync(
_In_ uint8 channelIndex,
_In_ Windows::Xbox::System::IUser^ user
)
{
return create_async( [this, channelIndex, user]()
{
CHAT_LOG_INFO_MSG(L"ChatManager::RemoveLocalUserFromChatChannel");
CHAT_THROW_INVALIDARGUMENT_IF_NULL( user );
ChatUser^ chatUser = m_chatClient->GetChatUserForUserId(user->Id);
if( chatUser != nullptr )
{
m_chatClient->RemoveUserFromChatChannel(channelIndex, user, true);
m_chatNetwork->CreateChatUserRemovedPacket(channelIndex, chatUser);
}
#if WINAPI_FAMILY == WINAPI_FAMILY_TV_TITLE
TraceChatRemoveLocalUserFromChatChannel(
user == nullptr ? L"" : user->XboxUserId->Data(),
channelIndex
);
m_chatDiagnostics->TraceChatUserAndAudioDevices( user );
#endif
});
}
Windows::Foundation::IAsyncAction^
ChatManager::RemoveRemoteConsoleAsync(
_In_ Platform::Object^ uniqueRemoteConsoleIdentifier
)
{
return create_async( [this, uniqueRemoteConsoleIdentifier]()
{
LogComment(L"ChatManager::RemoveAllUsersWithConsoleId");
CONSOLE_NAME localNameOfRemoteConsole = m_chatClient->GetLocalNameOfRemoteConsole(uniqueRemoteConsoleIdentifier);
m_chatAudioThread->RemoveRemoteConsole(localNameOfRemoteConsole);
m_chatNetwork->GetAudioDeviceIDMapper()->RemoveRemoteConsole(localNameOfRemoteConsole);
m_chatClient->RemoveRemoteConsole(uniqueRemoteConsoleIdentifier);
#if WINAPI_FAMILY == WINAPI_FAMILY_TV_TITLE
TraceChatRemoveRemoteConsole(
EventEnabledChatRemoveRemoteConsole() ? m_chatDiagnostics->GetDiagnosticNameForConsole( this, uniqueRemoteConsoleIdentifier ) : 0
);
#endif
});
}
Windows::Foundation::IAsyncAction^
ChatManager::AddLocalUserToChatChannelAsync(
_In_ uint8 channelIndex,
_In_ Windows::Xbox::System::IUser^ user
)
{
return create_async( [this, channelIndex, user]()
{
CHAT_THROW_INVALIDARGUMENT_IF_NULL( user );
bool isLocal = true;
Platform::Object^ uniqueConsoleIdentifier = nullptr;
ChatUser^ chatUser = m_chatClient->AddUserToChatChannel(channelIndex, user, uniqueConsoleIdentifier, isLocal, nullptr);
if( chatUser != nullptr )
{
// For each that I know of, send them a UserAddedMessage packet.
std::vector< std::shared_ptr<ConsoleNameIdentifierPair> > myLocalNameOfRemoteConsolesCopy = m_chatClient->GetMyLocalNameOfRemoteConsolesCopy();
for each (std::shared_ptr<ConsoleNameIdentifierPair> consoleNameIdentifierPair in myLocalNameOfRemoteConsolesCopy)
{
m_chatNetwork->CreateChatUserPacket(channelIndex, chatUser, consoleNameIdentifierPair->uniqueConsoleIdentifier);
}
}
#if WINAPI_FAMILY == WINAPI_FAMILY_TV_TITLE
TraceChatAddLocalUserToChatChannel(
user == nullptr ? L"" : user->XboxUserId->Data(),
channelIndex
);
m_chatDiagnostics->TraceChatUserAndAudioDevices( user );
#endif
});
}
void ChatManager::HandleNewRemoteConsole(
_In_ Platform::Object^ uniqueRemoteConsoleIdentifier
)
{
SendLocalUsersToRemoteConsole(uniqueRemoteConsoleIdentifier);
#if WINAPI_FAMILY == WINAPI_FAMILY_TV_TITLE
TraceChatHandleNewRemoteConsole(
EventEnabledChatHandleNewRemoteConsole() ? m_chatDiagnostics->GetDiagnosticNameForConsole( this, uniqueRemoteConsoleIdentifier ) : 0
);
#endif
}
Microsoft::Xbox::GameChat::ChatMessageType ChatManager::ProcessIncomingChatMessage(
_In_ Windows::Storage::Streams::IBuffer^ chatPacket,
_In_ Platform::Object^ uniqueRemoteConsoleIdentifier
)
{
CHAT_THROW_INVALIDARGUMENT_IF_NULL( uniqueRemoteConsoleIdentifier );
Microsoft::Xbox::GameChat::ChatMessageType chatMessageType = m_chatNetwork->ProcessIncomingChatMessage(
chatPacket,
uniqueRemoteConsoleIdentifier,
m_chatClient,
m_chatAudioThread,
this
);
return chatMessageType;
}
void ChatManager::OnChatSessionStateChangedHandler(
_In_ Windows::Xbox::Chat::IChatSessionState^ chatSessionState
)
{
m_chatAudioThread->SetChatSessionState(chatSessionState, m_chatClient);
}
void ChatManager::OnRemoteUserReadyToAddHandler(
_In_ uint8 channelIndex,
_In_ Windows::Xbox::System::IUser^ remoteUser,
_In_ Platform::Object^ remoteUniqueConsoleIdentifier,
_In_ bool hasAddedRemoteUserToLocalChatSession
)
{
Windows::Foundation::Collections::IVectorView<Microsoft::Xbox::GameChat::ChatUser^>^ chatUsers = GetChatUsers();
for each (Microsoft::Xbox::GameChat::ChatUser^ chatUser in chatUsers)
{
// Look at each non-user who doesn't match the XboxUserId and see if they have the same exclusive audio devices.
// If the do, then remove them since non-shared devices aren't allowed to have more than one user
if (chatUser != nullptr && !StringUtils::IsStringEqualCaseInsenstive(chatUser->XboxUserId, remoteUser->XboxUserId) )
{
bool foundExclusiveDeviceMatch = DoesAudioDeviceCollectionsMatchExclusiveDevices( remoteUser->AudioDevices, chatUser->User->AudioDevices );
if( foundExclusiveDeviceMatch )
{
// Remove this user
m_chatClient->RemoveUserFromChatChannel(
channelIndex,
chatUser->User,
false // don't bother updating the session state yet as it will be done during the AddUserToChatChannel call
);
}
}
}
Microsoft::Xbox::GameChat::ChatUser^ chatUser = nullptr;
bool isLocal = false;
// Now that we have a IUser, pass it to the chat client to handle
chatUser = m_chatClient->AddUserToChatChannel(
channelIndex,
remoteUser,
remoteUniqueConsoleIdentifier,
isLocal,
m_chatClient->GetChatParticipantFromChatSession( remoteUser->XboxUserId, (UINT)channelIndex )
);
// Rebuild the list of CHAT_AUDIO_THREAD_STATE objects associated with this remote console since the chatUsers on this console has changed
CONSOLE_NAME localNameOfRemoteConsole = m_chatClient->GetLocalNameOfRemoteConsole(remoteUniqueConsoleIdentifier);
m_chatAudioThread->RemoveRemoteConsole(localNameOfRemoteConsole);
LogCommentFormat( L"OnRemoteUserReadyToAddHandler: AddingUser: XboxUserId %s: localNameOfRemoteConsole: %d", remoteUser->XboxUserId->Data(), localNameOfRemoteConsole );
// If the remote user does not have our user data, then send it to him.
if (hasAddedRemoteUserToLocalChatSession == false)
{
SendLocalUsersToRemoteConsole(remoteUniqueConsoleIdentifier);
}
if( m_chatManagerSettings->AutoMuteBadReputationUsers )
{
// Anonymous (non-friend) users with a bad reputation should be muted by default
MuteUserIfReputationIsBadAsync(chatUser);
}
}
Windows::Foundation::IAsyncAction^
ChatManager::MuteUserIfReputationIsBadAsync(
_In_ Microsoft::Xbox::GameChat::ChatUser^ remoteUser
)
{
return create_async( [this, remoteUser] ()
{
CHAT_THROW_INVALIDARGUMENT_IF_NULL( remoteUser );
CHAT_LOG_INFO_MSG(L"ChatManager::MuteUserIfReputationIsBadAsync");
std::vector<Microsoft::Xbox::GameChat::ChatUser^> localUsers;
// Find the local users connected to chat. This list will be used to verify if a new remote user is a Favorite.
Windows::Foundation::Collections::IVectorView<Microsoft::Xbox::GameChat::ChatUser^>^ chatUsers = GetChatUsers();
for each (Microsoft::Xbox::GameChat::ChatUser^ chatUser in chatUsers)
{
if (chatUser != nullptr && chatUser->IsLocal)
{
if (StringUtils::IsStringEqualCaseInsenstive(chatUser->XboxUserId, remoteUser->XboxUserId))
{
// The new user is also a local user which shouldn't happen.
assert(false);
return;
}
localUsers.push_back(chatUser);
}
}
if (localUsers.size() == 0)
{
// There should always be at least one local user
assert(false);
return;
}
// Check local users' Favorites for the inclusion of the new user
for (std::vector<Microsoft::Xbox::GameChat::ChatUser^>::iterator itr = localUsers.begin(); itr != localUsers.end(); itr++)
{
Microsoft::Xbox::Services::Social::XboxSocialRelationshipResult^ socialResult = nullptr;
std::map<Platform::String^, Microsoft::Xbox::Services::Social::XboxSocialRelationshipResult^>::iterator search;
// Check if we've already performed the social query for this user
search = m_socialRelationships.find((*itr)->XboxUserId);
if (search != m_socialRelationships.end())
{
socialResult = (*search).second;
}
else
{
Microsoft::Xbox::Services::XboxLiveContext^ context = ref new Microsoft::Xbox::Services::XboxLiveContext(static_cast<Windows::Xbox::System::User^>((*itr)->User));
// Query the Social service for the list of social relationships
create_task(
context->SocialService->GetSocialRelationshipsAsync(
Microsoft::Xbox::Services::Social::SocialRelationship::All
)
).then( [&, this] (task<Microsoft::Xbox::Services::Social::XboxSocialRelationshipResult^> taskResult)
{
try
{
socialResult = taskResult.get();
m_socialRelationships[(*itr)->XboxUserId] = socialResult;
}
catch(Platform::Exception^ ex)
{
// The social request failed so skip this user and assume they're not friends
// Fallthrough
}
}).wait();
}
// Check the social graph results
if (socialResult != nullptr && socialResult->TotalCount > 0)
{
Windows::Foundation::Collections::IVectorView<Microsoft::Xbox::Services::Social::XboxSocialRelationship^>^ results = socialResult->Items;
// Check for our local users in the list of Favorites
for each(Microsoft::Xbox::Services::Social::XboxSocialRelationship^ relation in results)
{
if (StringUtils::IsStringEqualCaseInsenstive(relation->XboxUserId, remoteUser->XboxUserId))
{
// The new user is a friend of a local user so we don't need to make the reputation check
if (relation->IsFollowingCaller)
{
return;
}
}
}
}
}
bool shouldMute = false;
Microsoft::Xbox::Services::XboxLiveContext^ context = ref new Microsoft::Xbox::Services::XboxLiveContext(static_cast<Windows::Xbox::System::User^>(localUsers[0]->User));
// Now that we know this is an anonymous user we need to check their reputation.
// We use the context of a local user to make the request so we have proper permissions.
create_task(
context->UserStatisticsService->GetSingleUserStatisticsAsync(
remoteUser->XboxUserId, // User's XUID
STATISTICS_SERVICE_GUID, // System constant GUID for Statistics service
"OverallReputationIsBad" // Stat to query
)
).then( [&, this] (task<Microsoft::Xbox::Services::UserStatistics::UserStatisticsResult^> taskResult)
{
try
{
Microsoft::Xbox::Services::UserStatistics::UserStatisticsResult^ result = taskResult.get();
Windows::Foundation::Collections::IVectorView<Microsoft::Xbox::Services::UserStatistics::ServiceConfigurationStatistic^>^ values = result->ServiceConfigurationStatistics;
// Look through returned statistic values for the overall reputation
for each(Microsoft::Xbox::Services::UserStatistics::ServiceConfigurationStatistic^ serviceStat in values)
{
Windows::Foundation::Collections::IVectorView<Microsoft::Xbox::Services::UserStatistics::Statistic^>^ stats = serviceStat->Statistics;
for each(Microsoft::Xbox::Services::UserStatistics::Statistic^ stat in stats)
{
if (StringUtils::IsStringEqualCaseInsenstive(stat->StatisticName, "OverallReputationIsBad") &&
StringUtils::IsStringEqualCaseInsenstive(stat->Value, "1"))
{
// The user has a bad rep so we'll mute them by default
shouldMute = true;
return;
}
}
}
}
catch(Platform::Exception^ ex)
{
// There was an error querying the Statistics service. Assume the new user does not need to be muted
// Fallthrough
}
}).wait();
// So the final check for a non-favorite with a bad rep
if (shouldMute)
{
// Since this is run async and requires multiple service calls, the user may
// be gone or disconnected so make sure they're still a current user.
Windows::Foundation::Collections::IVectorView<Microsoft::Xbox::GameChat::ChatUser^>^ chatUsers = GetChatUsers();
for each (Microsoft::Xbox::GameChat::ChatUser^ chatUser in chatUsers)
{
if (chatUser && StringUtils::IsStringEqualCaseInsenstive(chatUser->XboxUserId, remoteUser->XboxUserId))
{
MuteUserFromAllChannelsPermanently(chatUser);
break;
}
}
}
});
}
bool ChatManager::DoAudioDeviceCollectionsMatch(
_In_ Windows::Foundation::Collections::IVectorView< Windows::Xbox::System::IAudioDeviceInfo^ >^ audioDevices1,
_In_ Windows::Foundation::Collections::IVectorView< Windows::Xbox::System::IAudioDeviceInfo^ >^ audioDevices2
)
{
if( audioDevices1 == nullptr || audioDevices2 == nullptr )
{
// Shouldn't happen but if either is null then treat as not matching
return false;
}
if( audioDevices1->Size != audioDevices2->Size )
{
// Different number of audio devices means the audio device lists don't match
return false;
}
for each (Windows::Xbox::System::IAudioDeviceInfo^ audioDeviceInfo1 in audioDevices1)
{
// See audioDeviceInfo->Id is in audioDevices2
bool found = false;
for each (Windows::Xbox::System::IAudioDeviceInfo^ audioDeviceInfo2 in audioDevices2)
{
if( StringUtils::IsStringEqualCaseInsenstive(audioDeviceInfo1->Id, audioDeviceInfo2->Id) )
{
found = true;
break;
}
}
if( !found )
{
// Did not find audioDeviceInfo1 in audioDevices2, so these audio device lists don't match
return false;
}
}
// Everything matched
return true;
}
bool ChatManager::DoesAudioDeviceCollectionsMatchExclusiveDevices(
_In_ Windows::Foundation::Collections::IVectorView< Windows::Xbox::System::IAudioDeviceInfo^ >^ audioDevices1,
_In_ Windows::Foundation::Collections::IVectorView< Windows::Xbox::System::IAudioDeviceInfo^ >^ audioDevices2
)
{
if( audioDevices1 == nullptr || audioDevices2 == nullptr )
{
// Shouldn't happen but if either is null then treat as not matching
return false;
}
for each (Windows::Xbox::System::IAudioDeviceInfo^ audioDeviceInfo1 in audioDevices1)
{
bool isSharedDevice = (audioDeviceInfo1->Sharing == Windows::Xbox::System::AudioDeviceSharing::Shared);
if( !isSharedDevice )
{
// See audioDeviceInfo1->Id is in audioDevices2
bool found = false;
for each (Windows::Xbox::System::IAudioDeviceInfo^ audioDeviceInfo2 in audioDevices2)
{
if( StringUtils::IsStringEqualCaseInsenstive(audioDeviceInfo1->Id, audioDeviceInfo2->Id) )
{
found = true;
break;
}
}
if( found )
{
// Found audioDeviceInfo1 in audioDevices2, so return true
return true;
}
}
}
// No match found
return false;
}
void ChatManager::OnRemoteUserReadyToRemoveHandler(
_In_ uint8 channelIndex,
_In_ Platform::String^ remoteUserXuid
)
{
if (remoteUserXuid->IsEmpty() == false)
{
ChatUser^ userToRemove = nullptr;
Windows::Foundation::Collections::IVectorView<Microsoft::Xbox::GameChat::ChatUser^>^ chatUsers = GetChatUsers();
for each (Microsoft::Xbox::GameChat::ChatUser^ chatUser in chatUsers)
{
if (chatUser != nullptr && chatUser->XboxUserId == remoteUserXuid)
{
Windows::Foundation::Collections::IVectorView<uint8>^ allChannels = chatUser->GetAllChannels();
for each (uint8 currentChannelIndex in allChannels)
{
if (currentChannelIndex == channelIndex)
{
userToRemove = chatUser;
break;
}
}
}
if (userToRemove != nullptr)
{
break;
}
}
if (userToRemove != nullptr)
{
m_chatClient->RemoveUserFromChatChannel(
channelIndex,
userToRemove->User,
true
);
}
}
}
bool ChatManager::OnCompareUniqueConsoleIdentifiersHandler(
_In_ Platform::Object^ object1,
_In_ Platform::Object^ object2
)
{
if (object1 == nullptr || object2 == nullptr)
{
return false;
}
return OnCompareUniqueConsoleIdentifiers(object1, object2);
}
Windows::Storage::Streams::IBuffer^
ChatManager::OnPostDecodeAudioBufferHandler(
_In_ Windows::Storage::Streams::IBuffer^ buffer,
_In_ Windows::Xbox::Chat::IFormat^ audioFormat,
_In_ Windows::Foundation::Collections::IVectorView<Microsoft::Xbox::GameChat::ChatUser^>^ chatUsers
)
{
return OnPostDecodeAudioBuffer( buffer, audioFormat, chatUsers );
}
Windows::Storage::Streams::IBuffer^
ChatManager::OnPreEncodeAudioBufferHandler(
_In_ Windows::Storage::Streams::IBuffer^ buffer,
_In_ Windows::Xbox::Chat::IFormat^ audioFormat,
_In_ Windows::Foundation::Collections::IVectorView<Microsoft::Xbox::GameChat::ChatUser^>^ chatUsers
)
{
return OnPreEncodeAudioBuffer( buffer, audioFormat, chatUsers );
}
Windows::Foundation::Collections::IVectorView<Microsoft::Xbox::GameChat::ChatUser^>^ ChatManager::GetChatUsers()
{
return m_chatClient->GetChatUsers();
}
void ChatManager::MuteUserFromAllChannelsPermanently( ChatUser^ user )
{
CHAT_THROW_INVALIDARGUMENT_IF_NULL( user );
user->MutePermanently();
MuteUserFromAllChannels(user);
}
void ChatManager::MuteUserFromAllChannels( ChatUser^ chatUser )
{
CHAT_THROW_INVALIDARGUMENT_IF_NULL( chatUser );
chatUser->Mute();
if (!chatUser->IsLocal)
{
// If muting remote user, also set the volume to 0.
// GameChat will internally set ChatRestriction to Muted when volume equals 0.
float volume = 0.0f;
m_chatClient->ChangeMuteStateForChatParticipantFromAllChannels(
chatUser->User->Id,
volume);
}
#if WINAPI_FAMILY == WINAPI_FAMILY_TV_TITLE
TraceChatMuteUserFromAllChannels(
chatUser == nullptr ? L"" : chatUser->XboxUserId->Data()
);
#endif
}
void ChatManager::UnmuteUserFromAllChannels( ChatUser^ chatUser )
{
CHAT_THROW_INVALIDARGUMENT_IF_NULL( chatUser );
if (!chatUser->IsMutedPermanently)
{
chatUser->Unmute();
if (!chatUser->IsLocal)
{
float volume = 1.0f;
m_chatClient->ChangeMuteStateForChatParticipantFromAllChannels(
chatUser->User->Id,
volume);
}
}
#if WINAPI_FAMILY == WINAPI_FAMILY_TV_TITLE
TraceChatUnmuteUserFromAllChannels(
chatUser == nullptr ? L"" : chatUser->XboxUserId->Data()
);
#endif
}
void ChatManager::MuteAllUsersFromAllChannels()
{
Windows::Foundation::Collections::IVectorView<Microsoft::Xbox::GameChat::ChatUser^>^ chatUsers = GetChatUsers();
for each (Microsoft::Xbox::GameChat::ChatUser^ chatUser in chatUsers)
{
if (chatUser != nullptr)
{
MuteUserFromAllChannels(chatUser);
}
}
#if WINAPI_FAMILY == WINAPI_FAMILY_TV_TITLE
TraceChatMuteAllUsersFromAllChannels();
#endif
}
void ChatManager::UnmuteAllUsersFromAllChannels()
{
Windows::Foundation::Collections::IVectorView<Microsoft::Xbox::GameChat::ChatUser^>^ chatUsers = GetChatUsers();
for each (Microsoft::Xbox::GameChat::ChatUser^ chatUser in chatUsers)
{
if (chatUser != nullptr)
{
UnmuteUserFromAllChannels(chatUser);
}
}
#if WINAPI_FAMILY == WINAPI_FAMILY_TV_TITLE
TraceChatUnmuteAllUsersFromAllChannels();
#endif
}
uint32 ChatManager::ConvertChatSessionPeriodToMilliseconds(
_In_ ChatSessionPeriod chatSessionPeriod
)
{
switch (chatSessionPeriod)
{
case ChatSessionPeriod::ChatPeriodOf20Milliseconds: return 20;
case ChatSessionPeriod::ChatPeriodOf40Milliseconds: return 40;
case ChatSessionPeriod::ChatPeriodOf80Milliseconds: return 80;
}
throw ref new Platform::InvalidArgumentException();
}
void ChatManager::SendLocalUsersToRemoteConsole(
_In_ Platform::Object^ remoteUniqueConsoleIdentifier
)
{
Windows::Foundation::Collections::IVectorView<Microsoft::Xbox::GameChat::ChatUser^>^ chatUsers = GetChatUsers();
for each (Microsoft::Xbox::GameChat::ChatUser^ chatUser in chatUsers)
{
if (chatUser->IsLocal)
{
Windows::Foundation::Collections::IVectorView<uint8>^ allChannels = chatUser->GetAllChannels();
for each (uint8 channelIndex in allChannels)
{
m_chatNetwork->CreateChatUserPacket((uint8)channelIndex, chatUser, remoteUniqueConsoleIdentifier);
}
}
}
}
Windows::Foundation::IAsyncAction^
ChatManager::AddLocalUsersToChatChannelAsync(
_In_ uint8 channelIndex,
_In_ Windows::Foundation::Collections::IVectorView<Windows::Xbox::System::User^>^ users
)
{
return create_async( [this, channelIndex, users]()
{
for each (Windows::Xbox::System::User^ user in users)
{
auto asyncOp = AddLocalUserToChatChannelAsync(channelIndex, user);
create_task(asyncOp).wait();
}
});
}
void ChatManager::OnChatManagerSettingsChangedHandler()
{
m_chatAudioThread->OnChatManagerSettingsChangedHandler();
}
bool ChatManager::HasMicFocus::get()
{
return m_chatAudioThread->HasMicFocus;
}
ChatPerformanceCounters^ ChatManager::ChatPerformanceCounters::get()
{
return m_chatAudioThread->ChatPerformanceCounters;
}
}}}
#endif
+419
View File
@@ -0,0 +1,419 @@
//// 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 "ChatClient.h"
#include "ChatPerformance.h"
#include "ChatDiagnostics.h"
#if TV_API
namespace Microsoft {
namespace Xbox {
namespace GameChat {
#define STATISTICS_SERVICE_GUID "7492baca-c1b4-440d-a391-b7ef364a8d40"
/// <summary>
/// The chat session period in milliseconds.
/// This defines how big the chat capture buffers will be.
/// Larger buffers adds latency
/// </summary>
public enum class ChatSessionPeriod
{
/// <summary>
/// Sets the chat session period to 20 milliseconds
/// </summary>
ChatPeriodOf20Milliseconds,
/// <summary>
/// Sets the chat session period to 40 milliseconds
/// </summary>
ChatPeriodOf40Milliseconds,
/// <summary>
/// Sets the chat session period to 80 milliseconds
/// </summary>
ChatPeriodOf80Milliseconds
};
public ref class ChatManager sealed
{
public:
/// <summary>
/// Creates the chat manager class using a default ChatSession period of 40 milliseconds
/// </summary>
ChatManager();
/// <summary>
/// To shutdown the ChatManager, simple set ChatManager^ to nullptr.
/// This will automatically shutdown the local chat session.
/// It is best to simply tear down and rebuild the whole ChatManger class,
/// otherwise every API would have to do checks against throw if not initialized.
/// </summary>
virtual ~ChatManager();
/// <summary>
/// Creates the chat manager class using the specified ChatSession period
/// </summary>
/// <param name="chatSessionPeriod">
/// The chat session period in milliseconds.
/// This defines how big the chat capture buffers will be.
/// Larger buffers adds latency
/// </param>
ChatManager(
_In_ ChatSessionPeriod chatSessionPeriod
);
/// <summary>
/// Set various chat manager options.
/// If this is not called, defaults values are used.
/// It function can be called at any time to change the previously set options
/// </summary>
property ChatManagerSettings^ ChatSettings { ChatManagerSettings^ get(); }
/// <summary>
/// This event is triggered when the chat manager has a debug message.
/// The game can optionally listen to this event to debug failures and behavior
/// </summary>
event Windows::Foundation::EventHandler<Microsoft::Xbox::GameChat::DebugMessageEventArgs^>^ OnDebugMessage;
/// <summary>
/// This event is triggered when the chat manager has a network packet ready to send out.
/// The ChatPacketEventArgs provide context on the packet buffer, who the packet is for, and options around how to packet is to be sent.
/// The packets are opaque to the game.
/// When the chat message is received by the remote console, it must call ProcessIncomingChatMessage() to so the chat manager can process the message.
/// The game is required to listen to this event.
/// When handling this event, you will need to have a thread safe network layer as this event will be called from
/// an internal worker thread that is real time priority by default.
/// The chat packet must be serviced as quickly as possible since this thread is real time priority by default.
/// The chat packet should also be sent to the remote console as quickly as possible to reduce latency.
/// </summary>
event Windows::Foundation::EventHandler<Microsoft::Xbox::GameChat::ChatPacketEventArgs^>^ OnOutgoingChatPacketReady;
/// <summary>
/// This delegate compares 2 uniqueRemoteConsoleIdentifiers. They are Platform::Object^ and can be cast or unboxed to most types.
/// What exactly you use doesn't matter, but optimally it would be something that uniquely identifies a console on in the session.
/// A Windows::Xbox::Networking::SecureDeviceAssociation^ is perfect to use if you have access to it.
/// This delegate is not optional and must return true if the uniqueRemoteConsoleIdentifier1 matches uuniqueRemoteConsoleIdentifier2
/// </summary>
event CompareUniqueConsoleIdentifiersHandler^ OnCompareUniqueConsoleIdentifiers;
/// <summary>
/// This delegate is called prior to encoding captured audio buffer.
/// This allows titles to apply sound effects to the capture stream
/// To use, register for this delegate and set ChatManagerSettings::PreEncodeCallbackEnabled to true
/// </summary>
event ProcessAudioBufferHandler^ OnPreEncodeAudioBuffer;
/// <summary>
/// This delegate is called after decoding a remote audio buffer.
/// This allows titles to apply sound effects to a remote user's audio mix
/// To use, register for this delegate and set ChatManagerSettings::PostDecodeCallbackEnabled to true
/// </summary>
event ProcessAudioBufferHandler^ OnPostDecodeAudioBuffer;
/// <summary>
/// Processes incoming chat messages from remote consoles.
/// It must call ProcessIncomingChatMessage() to so the chat manager can process the message.
/// </summary>
/// <param name="chatPacket">
/// The incoming chat packet inside an IBuffer.
/// This is how you would convert from byte array to a IBuffer^
///
/// Windows::Storage::Streams::IBuffer^ destBuffer = ref new Windows::Storage::Streams::Buffer( sourceByteBufferSize );
/// byte* destBufferBytes = nullptr;
/// GetBufferBytes( destBuffer, &destBufferBytes );
/// errno_t err = memcpy_s( destBufferBytes, destBuffer->Capacity, sourceByteBuffer, sourceByteBufferSize );
/// THROW_HR_IF(err != 0, E_FAIL);
/// destBuffer->Length = sourceByteBufferSize;
/// </param>
/// <param name="uniqueRemoteConsoleIdentifier">
/// uniqueRemoteConsoleIdentifier is a Platform::Object^ and can be cast or unboxed to most types.
/// What exactly you use doesn't matter, but optimally it would be something that uniquely identifies a console on in the session.
/// A Windows::Xbox::Networking::SecureDeviceAssociation^ is perfect to use if you have access to it.
///
/// This is how you would convert from an int to a Platform::Object^
/// Platform::Object obj = (Object^)5;
/// </param>
/// <returns>
/// The chat message type that was processed. This can be used to track down networking issues, but typically can be ignored
/// </returns>
Microsoft::Xbox::GameChat::ChatMessageType ProcessIncomingChatMessage(
_In_ Windows::Storage::Streams::IBuffer^ chatPacket,
_In_ Platform::Object^ uniqueRemoteConsoleIdentifier
);
/// <summary>
/// This causes existing local users to be resent to a new remote console connection.
/// </summary>
/// <param name="uniqueRemoteConsoleIdentifier">
/// uniqueRemoteConsoleIdentifier is a Platform::Object^ and can be cast or unboxed to most types.
/// What exactly you use doesn't matter, but optimally it would be something that uniquely identifies a console on in the session.
/// A Windows::Xbox::Networking::SecureDeviceAssociation^ is perfect to use if you have access to it.
///
/// This is how you would convert from an int to a Platform::Object^
/// Platform::Object obj = (Object^)5;
/// </param>
void HandleNewRemoteConsole(
_In_ Platform::Object^ uniqueRemoteConsoleIdentifier
);
/// <summary>
/// Adds a local user to the chat channel.
/// Note that this user should have expressed intent to play prior to calling this, because the user may
/// have been signed in due to Kinect automatically but may not want to play or take up a chat slot.
/// This will automatically serialize the local user to a packet that can be sent to all connected consoles.
/// </summary>
/// <param name="channelIndex">The index of the chat channel</param>
/// <param name="user">The user to add to the chat channel</param>
Windows::Foundation::IAsyncAction^
AddLocalUserToChatChannelAsync(
_In_ uint8 channelIndex,
_In_ Windows::Xbox::System::IUser^ user
);
/// <summary>
/// Adds a local user to the chat channel.
/// Note that this user should have expressed intent to play prior to calling this, because the user may
/// have been signed in due to Kinect automatically but may not want to play or take up a chat slot.
/// This will automatically serialize the local user to a packet that can be sent to all connected consoles.
/// </summary>
/// <param name="channelIndex">The index of the chat channel</param>
/// <param name="users">The user to add to the chat channel</param>
Windows::Foundation::IAsyncAction^
AddLocalUsersToChatChannelAsync(
_In_ uint8 channelIndex,
_In_ Windows::Foundation::Collections::IVectorView<Windows::Xbox::System::User^>^ users
);
/// <summary>
/// Adds a remote user to the chat channel.
/// This will automatically create a packet to inform the all connected consoles that this player should be removed.
/// </summary>
/// <param name="channelIndex">The index of the chat channel</param>
/// <param name="user">The user to remove to the chat channel</param>
Windows::Foundation::IAsyncAction^
RemoveLocalUserFromChatChannelAsync(
_In_ uint8 channelIndex,
_In_ Windows::Xbox::System::IUser^ user
);
/// <summary>
/// Remove all remote users that are attached to remote console.
/// This is typically called when a connection to a remote console is destroyed
/// </summary>
/// <param name="consoleId">A consoleId of the remote console</param>
Windows::Foundation::IAsyncAction^
RemoveRemoteConsoleAsync(
_In_ Platform::Object^ uniqueRemoteConsoleIdentifier
);
/// <summary>
/// Returns a list of ChatUser objects.
/// The ChatUser object contains metadata about the user such as if they are talking
/// </summary>
/// <returns>A list of ChatUser objects</returns>
Windows::Foundation::Collections::IVectorView<Microsoft::Xbox::GameChat::ChatUser^>^ GetChatUsers();
/// <summary>
/// Mutes the user from all channels.
/// If you mute a local user, it stops capturing packets from the capture source
/// but does not stop you from receiving packets.
/// </summary>
/// <param name="user">The user to mute</param>
void MuteUserFromAllChannels( ChatUser^ user );
/// <summary>
/// Mutes the user from all channels. And sets permanent mute flag
/// </summary>
/// <param name="user">The user to mute</param>
void MuteUserFromAllChannelsPermanently( ChatUser^ user );
/// <summary>
/// Unmute a specific user from all channels.
/// </summary>
/// <param name="user">The user to unmute</param>
void UnmuteUserFromAllChannels( ChatUser^ user );
/// <summary>
/// Mute all users in the chat session
/// </summary>
void MuteAllUsersFromAllChannels();
/// <summary>
/// Unmute all users in the chat session
/// </summary>
void UnmuteAllUsersFromAllChannels();
/// <summary>
/// Mute non-friend chat user with a poor reputation.
/// </summary>
/// <param name="remoteUser">The user to mute if they fail a reputation check</param>
Windows::Foundation::IAsyncAction^
MuteUserIfReputationIsBadAsync(
_In_ Microsoft::Xbox::GameChat::ChatUser^ user
);
/// <summary>
/// Indicates if the the title has mic focus
/// </summary>
property bool HasMicFocus { bool get(); }
/// <summary>
/// Returns the ChatPerformanceCounters object.
/// The ChatPerformanceCounters object contains performance data for profiling.
/// See ChatManagerSettings::PerformanceCountersEnabled to enable/disable collection of performance data.
/// </summary>
property Microsoft::Xbox::GameChat::ChatPerformanceCounters^ ChatPerformanceCounters { Microsoft::Xbox::GameChat::ChatPerformanceCounters^ get(); }
internal:
std::shared_ptr<ChatDiagnostics> GetChatDiagnostics() { return m_chatDiagnostics; };
bool DoesAudioDeviceCollectionsMatchExclusiveDevices(
_In_ Windows::Foundation::Collections::IVectorView< Windows::Xbox::System::IAudioDeviceInfo^ >^ audioDevices1,
_In_ Windows::Foundation::Collections::IVectorView< Windows::Xbox::System::IAudioDeviceInfo^ >^ audioDevices2
);
/// <summary>
/// Internal event handler
/// </summary>
void OnDebugMessageHandler(
_In_ Microsoft::Xbox::GameChat::DebugMessageEventArgs^ args
);
/// <summary>
/// Internal event handler
/// </summary>
void OnChatPacketReadyHandler(
_In_ Microsoft::Xbox::GameChat::ChatPacketEventArgs^ args
);
/// <summary>
/// Internal event handler
/// </summary>
void OnChatSessionStateChangedHandler(
_In_ Windows::Xbox::Chat::IChatSessionState^ chatSessionState
);
/// <summary>
/// Internal event handler
/// </summary>
void OnRemoteUserReadyToAddHandler(
_In_ uint8 channelIndex,
_In_ Windows::Xbox::System::IUser^ remoteUser,
_In_ Platform::Object^ remoteUniqueConsoleIdentifier,
_In_ bool hasAddedRemoteUserToLocalChatSession
);
/// <summary>
/// Internal event handler
/// </summary>
void OnRemoteUserReadyToRemoveHandler(
_In_ uint8 channelIndex,
_In_ Platform::String^ remoteXboxUserId
);
/// <summary>
/// Internal event handler
/// </summary>
bool OnCompareUniqueConsoleIdentifiersHandler(
_In_ Platform::Object^ uniqueRemoteConsoleIdentifier1,
_In_ Platform::Object^ uniqueRemoteConsoleIdentifier2
);
/// <summary>
/// Internal event handler
/// </summary>
Windows::Storage::Streams::IBuffer^ OnPreEncodeAudioBufferHandler(
_In_ Windows::Storage::Streams::IBuffer^ buffer,
_In_ Windows::Xbox::Chat::IFormat^ audioFormat,
_In_ Windows::Foundation::Collections::IVectorView<Microsoft::Xbox::GameChat::ChatUser^>^ chatUsers
);
/// <summary>
/// Internal event handler
/// </summary>
Windows::Storage::Streams::IBuffer^ OnPostDecodeAudioBufferHandler(
_In_ Windows::Storage::Streams::IBuffer^ buffer,
_In_ Windows::Xbox::Chat::IFormat^ audioFormat,
_In_ Windows::Foundation::Collections::IVectorView<Microsoft::Xbox::GameChat::ChatUser^>^ chatUsers
);
/// <summary>
/// Internal event handler
/// </summary>
void OnChatManagerSettingsChangedHandler();
private:
/// <summary>
/// Internal helper function to initialize the chat manager class using the specified ChatSession period
/// </summary>
/// <param name="chatSessionPeriod">
/// A ChatSessionPeriod enum which represents the chat session period in milliseconds.
/// This defines how big the chat capture buffers will be
/// </param>
void Initialize(
_In_ ChatSessionPeriod chatSessionPeriod
);
/// <summary>
/// Internal helper function to send all local users to a remote console
/// </summary>
void SendLocalUsersToRemoteConsole( _In_ Platform::Object^ remoteUniqueConsoleIdentifier );
/// <summary>
/// Internal helper function to log a comment
/// </summary>
void LogComment(
_In_ Platform::String^ message
);
/// <summary>
/// Internal helper function to log a comment with an error string
/// </summary>
void LogCommentWithError(
_In_ Platform::String^ message,
_In_ HRESULT hr
);
/// <summary>
/// Internal helper function to log a formated comment
/// </summary>
void LogCommentFormat(
_In_ LPCWSTR strMsg, ...
);
/// <summary>
/// Helper function to convert from ChatSessionPeriod to uint32 milliseconds
/// </summary>
/// <param name="args">Returns a uint32 milliseconds</param>
uint32 ConvertChatSessionPeriodToMilliseconds( _In_ ChatSessionPeriod chatSessionPeriod );
/// <summary>
/// Helper function to compare lists of audio devices to mismatches
/// </summary>
bool ChatManager::DoAudioDeviceCollectionsMatch(
_In_ Windows::Foundation::Collections::IVectorView< Windows::Xbox::System::IAudioDeviceInfo^ >^ audioDevices1,
_In_ Windows::Foundation::Collections::IVectorView< Windows::Xbox::System::IAudioDeviceInfo^ >^ audioDevices2
);
private:
std::shared_ptr<FactoryCache> m_factoryCache;
ChatClient^ m_chatClient;
ChatAudioThread^ m_chatAudioThread;
ChatNetwork^ m_chatNetwork;
ChatManagerSettings^ m_chatManagerSettings;
Platform::WeakReference m_chatManagerEventHandler;
std::shared_ptr<ChatDiagnostics> m_chatDiagnostics;
std::map<Platform::String^, Microsoft::Xbox::Services::Social::XboxSocialRelationshipResult^> m_socialRelationships;
};
}}}
#endif
+93
View File
@@ -0,0 +1,93 @@
//// 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 "ChatManagerEvents.h"
#if TV_API
namespace Microsoft {
namespace Xbox {
namespace GameChat {
DebugMessageEventArgs::DebugMessageEventArgs(
_In_ Platform::String^ message,
_In_ int hr ) :
m_message( message ),
m_hresult( hr )
{
}
int DebugMessageEventArgs::ErrorCode::get()
{
return m_hresult;
}
Platform::String^ DebugMessageEventArgs::Message::get()
{
return m_message;
}
ChatPacketEventArgs::ChatPacketEventArgs(
_In_ Windows::Storage::Streams::IBuffer^ packetBuffer,
_In_ Platform::Object^ uniqueTargetConsoleIdentifier,
_In_ bool sendPacketToAllConnectedConsoles,
_In_ bool sendReliable,
_In_ bool sendInOrder,
_In_ Microsoft::Xbox::GameChat::ChatMessageType chatMessageType,
_In_ Microsoft::Xbox::GameChat::ChatUser^ chatUser
) :
m_packetBuffer( packetBuffer ),
m_uniqueTargetConsoleIdentifier( uniqueTargetConsoleIdentifier),
m_sendPacketToAllConnectedConsoles( sendPacketToAllConnectedConsoles ),
m_sendReliable( sendReliable ),
m_sendInOrder( sendInOrder ),
m_chatMessageType( chatMessageType ),
m_chatUser(chatUser)
{
}
Windows::Storage::Streams::IBuffer^ ChatPacketEventArgs::PacketBuffer::get()
{
return m_packetBuffer;
}
Platform::Object^ ChatPacketEventArgs::UniqueTargetConsoleIdentifier::get()
{
return m_uniqueTargetConsoleIdentifier;
}
Microsoft::Xbox::GameChat::ChatUser^ ChatPacketEventArgs::ChatUser::get()
{
return m_chatUser;
}
bool ChatPacketEventArgs::SendPacketToAllConnectedConsoles::get()
{
return m_sendPacketToAllConnectedConsoles;
}
bool ChatPacketEventArgs::SendReliable::get()
{
return m_sendReliable;
}
bool ChatPacketEventArgs::SendInOrder::get()
{
return m_sendInOrder;
}
Microsoft::Xbox::GameChat::ChatMessageType
ChatPacketEventArgs::ChatMessageType::get()
{
return m_chatMessageType;
}
}}}
#endif
+162
View File
@@ -0,0 +1,162 @@
//// 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"
#if TV_API
namespace Microsoft {
namespace Xbox {
namespace GameChat {
/// <summary>
/// Various types of chat messages sent by the GameChat system
/// </summary>
public enum class ChatMessageType
{
/// <summary>
/// Sends voice data along with a unique LOOKUP_ID which is built from the local name of remote console and an audio device index
/// </summary>
ChatVoiceDataMessage = 1,
/// <summary>
/// Sends byte array which is used to create an IUser on the remote console
/// </summary>
UserAddedMessage = 2,
/// <summary>
/// Sends XboxUserId string of the user to remove
/// </summary>
UserRemovedMessage = 3,
/// <summary>
/// ProcessIncomingChatMessage was sent an invalid chat packet
/// </summary>
InvalidMessage = 4
};
/// <summary>
/// Event to report diagnostic and error message information
/// </summary>
public ref class DebugMessageEventArgs sealed
{
public:
/// <summary>
/// The debug message
/// </summary>
property Platform::String^ Message { Platform::String^ get(); }
/// <summary>
/// The HRESULT of the message. S_OK for non-errors
/// </summary>
property int ErrorCode { int get(); }
internal:
DebugMessageEventArgs(
_In_ Platform::String^ message,
_In_ int hr );
private:
Platform::String^ m_message;
int m_hresult;
};
/// <summary>
/// Arguments for the event when a chat packet is ready to be sent
/// </summary>
public ref class ChatPacketEventArgs sealed
{
public:
/// <summary>
/// The buffer of the packet that should be sent to remote console(s)
/// </summary>
property Windows::Storage::Streams::IBuffer^ PacketBuffer { Windows::Storage::Streams::IBuffer^ get(); }
/// <summary>
/// Indicates if the packet should be sent with reliable UDP
/// </summary>
property bool SendReliable { bool get(); }
/// <summary>
/// Indicates if the packet should be sent sequential ordering if available
/// </summary>
property bool SendInOrder { bool get(); }
/// <summary>
/// Indicates if the packet should be sent to all connected consoles or just a single remote console.
/// If this is false, then TargetUniqueConsoleIdentifier indicates who to send the packet to.
/// </summary>
property bool SendPacketToAllConnectedConsoles { bool get(); }
/// <summary>
/// The remote console to send the packet to. This is nullptr if SendPacketToAllConnectedConsoles is true.
/// </summary>
property Platform::Object^ UniqueTargetConsoleIdentifier { Platform::Object^ get(); }
/// <summary>
/// Indicates the ChatMessageType message type of the packet
/// Typically this should be ignored, but some games may need to specific handling around unique types
/// </summary>
property Microsoft::Xbox::GameChat::ChatMessageType ChatMessageType { Microsoft::Xbox::GameChat::ChatMessageType get(); }
/// <summary>
/// If this is a ChatMessageType::ChatVoiceDataMessage message, then this is the first ChatUser whose audio device that created the voice packet.
/// For Kinect, there may be multiple ChatUsers who share the Kinect mic, so this is the first ChatUser.
/// Otherwise, this is nullptr.
/// Typically this can be ignored but some games may want to do specific handling around who created the voice packet.
/// </summary>
property Microsoft::Xbox::GameChat::ChatUser^ ChatUser { Microsoft::Xbox::GameChat::ChatUser^ get(); }
internal:
ChatPacketEventArgs(
_In_ Windows::Storage::Streams::IBuffer^ packetBuffer,
_In_ Platform::Object^ uniqueTargetConsoleIdentifier,
_In_ bool sendPacketToAllConnectedConsoles,
_In_ bool sendReliable,
_In_ bool sendInOrder,
_In_ Microsoft::Xbox::GameChat::ChatMessageType chatMessageType,
_In_ Microsoft::Xbox::GameChat::ChatUser^ chatUser
);
private:
Windows::Storage::Streams::IBuffer^ m_packetBuffer;
Platform::Object^ m_uniqueTargetConsoleIdentifier;
bool m_sendPacketToAllConnectedConsoles;
bool m_sendReliable;
bool m_sendInOrder;
Microsoft::Xbox::GameChat::ChatMessageType m_chatMessageType;
Microsoft::Xbox::GameChat::ChatUser^ m_chatUser;
};
/// <summary>
/// This delegate compares 2 uniqueRemoteConsoleIdentifiers. They are Platform::Object^ and can be cast or unboxed to most types.
/// What exactly you use doesn't matter, but optimally it would be something that uniquely identifies a console on in the session.
/// A Windows::Xbox::Networking::SecureDeviceAssociation^ is perfect to use if you have access to it.
/// This delegate is not optional and must return true if the uniqueRemoteConsoleIdentifier1 matches uuniqueRemoteConsoleIdentifier2
/// </summary>
public delegate bool CompareUniqueConsoleIdentifiersHandler(
_In_ Platform::Object^ uniqueRemoteConsoleIdentifier1,
_In_ Platform::Object^ uniqueRemoteConsoleIdentifier2
);
/// <summary>
/// This delegate enables titles to process the audio stream.
/// To use, set either ChatManagerSettings::PreEncodeCallbackEnabled or ChatManagerSettings::PostDecodeCallbackEnabled to true
/// </summary>
/// <param name="preEncodedRawBuffer">A buffer containing the pre-processed raw audio data</param>
/// <param name="audioFormat">The audio format of the preEncodedRawBuffer</param>
/// <param name="chatUsers">A collection of users associated with this audio buffer</param>
/// <returns>A buffer containing processed audio</returns>
public delegate Windows::Storage::Streams::IBuffer^ ProcessAudioBufferHandler(
_In_ Windows::Storage::Streams::IBuffer^ preEncodedRawBuffer,
_In_ Windows::Xbox::Chat::IFormat^ audioFormat,
_In_ Windows::Foundation::Collections::IVectorView<Microsoft::Xbox::GameChat::ChatUser^>^ chatUsers
);
}}}
#endif
+331
View File
@@ -0,0 +1,331 @@
//// 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 "ChatManagerSettings.h"
#include "ChatAudioThread.h"
#include "ChatNetwork.h"
#include "ChatManagerEvents.h"
#include "ChatManager.h"
#include "ChatDiagnostics.h"
#if TV_API
namespace Microsoft {
namespace Xbox {
namespace GameChat {
ChatManagerSettings::ChatManagerSettings(
_In_ ChatManager^ chatManager
) :
m_audioThreadPeriodInMilliseconds( 40 ),
m_audioThreadAffinityMask( XAUDIO2_DEFAULT_PROCESSOR ), // On XDK, this is set to Processor5 (0x10)
m_audioEncodingQuality( Windows::Xbox::Chat::EncodingQuality::Normal ),
m_jitterBufferMaxPackets( 20 ),
m_jitterBufferLowestNeededPacketCount( 0 ),
m_jitterBufferPacketsBeforeRelaxingNeeded( 5 ),
m_performanceCountersEnabled( false ),
m_chatManager( chatManager ),
m_audioThreadPriority( THREAD_PRIORITY_TIME_CRITICAL ),
m_combineCaptureBuffersIntoSinglePacket( true ),
m_useKinectAsCaptureSource( true ),
m_preEncodeCallbackEnabled( false ),
m_postDecodeCallbackEnabled( false ),
m_gameChatDiagnosticsTraceLevel( GameChatDiagnosticsTraceLevel::Info ),
m_autoMuteBadReputationUsers( true )
{
}
uint32 ChatManagerSettings::AudioThreadPeriodInMilliseconds::get()
{
Concurrency::critical_section::scoped_lock lock(m_chatSettingsStateLock);
return m_audioThreadPeriodInMilliseconds;
}
void ChatManagerSettings::AudioThreadPeriodInMilliseconds::set(
_In_ uint32 value
)
{
{
Concurrency::critical_section::scoped_lock lock(m_chatSettingsStateLock);
m_audioThreadPeriodInMilliseconds = value;
}
ChatManager^ chatManager = m_chatManager.Resolve<ChatManager>();
if( chatManager != nullptr )
{
chatManager->OnChatManagerSettingsChangedHandler();
}
}
uint32 ChatManagerSettings::AudioThreadAffinityMask::get()
{
Concurrency::critical_section::scoped_lock lock(m_chatSettingsStateLock);
return m_audioThreadAffinityMask;
}
void ChatManagerSettings::AudioThreadAffinityMask::set(
_In_ uint32 value
)
{
{
Concurrency::critical_section::scoped_lock lock(m_chatSettingsStateLock);
m_audioThreadAffinityMask = value;
}
ChatManager^ chatManager = m_chatManager.Resolve<ChatManager>();
if( chatManager != nullptr )
{
chatManager->OnChatManagerSettingsChangedHandler();
}
}
int ChatManagerSettings::AudioThreadPriority::get()
{
Concurrency::critical_section::scoped_lock lock(m_chatSettingsStateLock);
return m_audioThreadPriority;
}
void ChatManagerSettings::AudioThreadPriority::set(
_In_ int value
)
{
{
Concurrency::critical_section::scoped_lock lock(m_chatSettingsStateLock);
m_audioThreadPriority = value;
}
ChatManager^ chatManager = m_chatManager.Resolve<ChatManager>();
if( chatManager != nullptr )
{
chatManager->OnChatManagerSettingsChangedHandler();
}
}
Windows::Xbox::Chat::EncodingQuality ChatManagerSettings::AudioEncodingQuality::get()
{
Concurrency::critical_section::scoped_lock lock(m_chatSettingsStateLock);
return m_audioEncodingQuality;
}
void ChatManagerSettings::AudioEncodingQuality::set(
_In_ Windows::Xbox::Chat::EncodingQuality value
)
{
Concurrency::critical_section::scoped_lock lock(m_chatSettingsStateLock);
m_audioEncodingQuality = value;
#if WINAPI_FAMILY == WINAPI_FAMILY_TV_TITLE
TraceChatManagerSettingsAudioEncodingQuality(
static_cast<uint32>(m_audioEncodingQuality)
);
#endif
}
uint32 ChatManagerSettings::JitterBufferMaxPackets::get()
{
Concurrency::critical_section::scoped_lock lock(m_chatSettingsStateLock);
return m_jitterBufferMaxPackets;
}
void ChatManagerSettings::JitterBufferMaxPackets::set(
_In_ uint32 value
)
{
Concurrency::critical_section::scoped_lock lock(m_chatSettingsStateLock);
m_jitterBufferMaxPackets = value;
TraceJitterBufferSettings();
}
uint32 ChatManagerSettings::JitterBufferLowestNeededPacketCount::get()
{
Concurrency::critical_section::scoped_lock lock(m_chatSettingsStateLock);
return m_jitterBufferLowestNeededPacketCount;
}
void ChatManagerSettings::JitterBufferLowestNeededPacketCount::set(
_In_ uint32 value
)
{
Concurrency::critical_section::scoped_lock lock(m_chatSettingsStateLock);
m_jitterBufferLowestNeededPacketCount = value;
TraceJitterBufferSettings();
}
uint32 ChatManagerSettings::JitterBufferPacketsBeforeRelaxingNeeded::get()
{
Concurrency::critical_section::scoped_lock lock(m_chatSettingsStateLock);
return m_jitterBufferPacketsBeforeRelaxingNeeded;
}
void ChatManagerSettings::JitterBufferPacketsBeforeRelaxingNeeded::set(
_In_ uint32 value
)
{
Concurrency::critical_section::scoped_lock lock(m_chatSettingsStateLock);
m_jitterBufferPacketsBeforeRelaxingNeeded = value;
TraceJitterBufferSettings();
}
bool ChatManagerSettings::PerformanceCountersEnabled::get()
{
Concurrency::critical_section::scoped_lock lock(m_chatSettingsStateLock);
return m_performanceCountersEnabled;
}
void ChatManagerSettings::PerformanceCountersEnabled::set(
_In_ bool value
)
{
Concurrency::critical_section::scoped_lock lock(m_chatSettingsStateLock);
m_performanceCountersEnabled = value;
TraceMiscSettings();
}
bool ChatManagerSettings::CombineCaptureBuffersIntoSinglePacket::get()
{
Concurrency::critical_section::scoped_lock lock(m_chatSettingsStateLock);
return m_combineCaptureBuffersIntoSinglePacket;
}
void ChatManagerSettings::CombineCaptureBuffersIntoSinglePacket::set(
_In_ bool value
)
{
Concurrency::critical_section::scoped_lock lock(m_chatSettingsStateLock);
m_combineCaptureBuffersIntoSinglePacket = value;
TraceMiscSettings();
}
bool ChatManagerSettings::UseKinectAsCaptureSource::get()
{
Concurrency::critical_section::scoped_lock lock(m_chatSettingsStateLock);
return m_useKinectAsCaptureSource;
}
void ChatManagerSettings::UseKinectAsCaptureSource::set(
_In_ bool value
)
{
Concurrency::critical_section::scoped_lock lock(m_chatSettingsStateLock);
m_useKinectAsCaptureSource = value;
TraceMiscSettings();
}
bool ChatManagerSettings::PreEncodeCallbackEnabled::get()
{
Concurrency::critical_section::scoped_lock lock(m_chatSettingsStateLock);
return m_preEncodeCallbackEnabled;
}
void ChatManagerSettings::PreEncodeCallbackEnabled::set(
_In_ bool value
)
{
Concurrency::critical_section::scoped_lock lock(m_chatSettingsStateLock);
m_preEncodeCallbackEnabled = value;
TraceEffectSettings();
}
bool ChatManagerSettings::PostDecodeCallbackEnabled::get()
{
Concurrency::critical_section::scoped_lock lock(m_chatSettingsStateLock);
return m_postDecodeCallbackEnabled;
}
void ChatManagerSettings::PostDecodeCallbackEnabled::set(
_In_ bool value
)
{
Concurrency::critical_section::scoped_lock lock(m_chatSettingsStateLock);
m_postDecodeCallbackEnabled = value;
TraceEffectSettings();
}
GameChatDiagnosticsTraceLevel ChatManagerSettings::DiagnosticsTraceLevel::get()
{
Concurrency::critical_section::scoped_lock lock(m_chatSettingsStateLock);
return m_gameChatDiagnosticsTraceLevel;
}
void ChatManagerSettings::DiagnosticsTraceLevel::set(
GameChatDiagnosticsTraceLevel value
)
{
Concurrency::critical_section::scoped_lock lock(m_chatSettingsStateLock);
m_gameChatDiagnosticsTraceLevel = value;
TraceMiscSettings();
}
bool ChatManagerSettings::AutoMuteBadReputationUsers::get()
{
Concurrency::critical_section::scoped_lock lock(m_chatSettingsStateLock);
return m_autoMuteBadReputationUsers;
}
void ChatManagerSettings::AutoMuteBadReputationUsers::set(
_In_ bool value
)
{
Concurrency::critical_section::scoped_lock lock(m_chatSettingsStateLock);
m_autoMuteBadReputationUsers = value;
TraceEffectSettings();
}
bool ChatManagerSettings::IsAtDiagnosticsTraceLevel(
_In_ GameChatDiagnosticsTraceLevel levelOfMessage
)
{
GameChatDiagnosticsTraceLevel diagnosticsTraceLevel = this->DiagnosticsTraceLevel;
return (int)diagnosticsTraceLevel >= (int)levelOfMessage;
}
void ChatManagerSettings::TraceJitterBufferSettings()
{
#if WINAPI_FAMILY == WINAPI_FAMILY_TV_TITLE
TraceChatManagerSettingsJitterBuffer(
m_jitterBufferMaxPackets,
m_jitterBufferLowestNeededPacketCount,
m_jitterBufferPacketsBeforeRelaxingNeeded
);
#endif
}
void ChatManagerSettings::TraceMiscSettings()
{
#if WINAPI_FAMILY == WINAPI_FAMILY_TV_TITLE
TraceChatManagerSettingsMisc(
m_performanceCountersEnabled,
m_combineCaptureBuffersIntoSinglePacket,
m_useKinectAsCaptureSource,
static_cast<int>(m_gameChatDiagnosticsTraceLevel)
);
#endif
}
void ChatManagerSettings::TraceEffectSettings()
{
#if WINAPI_FAMILY == WINAPI_FAMILY_TV_TITLE
TraceChatManagerSettingsEffects(
m_preEncodeCallbackEnabled,
m_postDecodeCallbackEnabled
);
#endif
}
}}}
#endif
+259
View File
@@ -0,0 +1,259 @@
//// 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 "ChatManagerEvents.h"
#if TV_API
// Forward declare
namespace Microsoft { namespace Xbox { namespace GameChat { ref class ChatManager; } } }
namespace Microsoft {
namespace Xbox {
namespace GameChat {
/// <summary>
/// Indicates the level of debug messages send to ChatManager::OnDebugMessage
/// </summary>
public enum class GameChatDiagnosticsTraceLevel
{
/// <summary>
/// Output no tracing and debugging messages.
/// </summary>
Off = 0,
/// <summary>
/// Output error-handling messages.
/// </summary>
Error,
/// <summary>
/// Output warnings and error-handling messages.
/// </summary>
Warning,
/// <summary>
/// Output informational messages, warnings, and error-handling messages.
/// </summary>
Info,
/// <summary>
/// Output all debugging and tracing messages.
/// </summary>
Verbose
};
public ref class ChatManagerSettings sealed
{
public:
/// <summary>
/// Controls how often the audio thread wakes up in milliseconds.
/// A longer time causes the audio thread to process less often which causes larger capture buffers and thus larger voice packets.
// Defaults to 40ms.
/// </summary>
property uint32 AudioThreadPeriodInMilliseconds
{
uint32 get();
void set(_In_ uint32 value);
}
/// <summary>
/// Controls the audio thread's affinity mask.
/// Defaults to XAUDIO2_DEFAULT_PROCESSOR. On XDK, this is set to Processor5 (0x10)
///
/// For background on how thread affinity mask works:
/// A thread affinity mask is a bit vector in which each bit represents a logical processor that a thread is allowed to run on.
/// A thread affinity mask must be a subset of the process affinity mask for the containing process of a thread.
/// A thread can only run on the processors its process can run on.
/// Therefore, the thread affinity mask cannot specify a 1 bit for a processor when the process affinity mask specifies a 0 bit for that processor.
/// </summary>
property uint32 AudioThreadAffinityMask
{
uint32 get();
void set(_In_ uint32 value);
}
/// <summary>
/// Controls the audio thread's priority.
/// Defaults to THREAD_PRIORITY_TIME_CRITICAL so that the audio thread is not interrupted often
/// </summary>
property int AudioThreadPriority
{
int get();
void set(_In_ int value);
}
/// <summary>
/// The compression ratio used by the audio encoder (low, normal, high)
/// Defaults to Normal
/// </summary>
property Windows::Xbox::Chat::EncodingQuality AudioEncodingQuality
{
Windows::Xbox::Chat::EncodingQuality get();
void set(_In_ Windows::Xbox::Chat::EncodingQuality value);
}
/// <summary>
/// Each remote capture source has a jitter buffer that contains a ring buffer.
/// This is the max number of packets that can be stored in that ring buffer
/// If this number is too low, then incoming packets will will be dropped
/// If this number is too high, then memory will be wasted
/// Defaults to 20.
/// </summary>
property uint32 JitterBufferMaxPackets
{
uint32 get();
void set(_In_ uint32 value);
}
/// <summary>
/// Each remote capture source has a jitter buffer.
/// The jitter buffer dynamically adjusts the number of packets it needs before it hands out packets to avoid audio glitches.
/// This value is called DynamicNeededPacketCount which can found on each ChatUser.
/// DynamicNeededPacketCount automatically is adjusted based on internal jitter buffer heuristics.
/// JitterBufferLowestNeededPacketCount is the lowest that DynamicNeededPacketCount can go.
/// The lower this number is the better the latency will be with a potential trade-off of more audio glitches
/// Defaults to 0.
/// </summary>
property uint32 JitterBufferLowestNeededPacketCount
{
uint32 get();
void set(_In_ uint32 value);
}
/// <summary>
/// Each remote capture source has a jitter buffer.
/// The jitter buffer dynamically adjusts the number of packets it needs before it hands out packets to avoid audio glitches.
/// This value is called DynamicNeededPacketCount which can found on each ChatUser.
/// DynamicNeededPacketCount automatically is adjusted based on internal jitter buffer heuristics.
/// JitterBufferPacketsBeforeRelaxingNeeded is the number of packets received while in the sweet spot
/// between DynamicNeededPacketCount and JitterBufferMaxPackets.
/// When it reaches this target, the jitter buffer will lower the DynamicNeededPacketCount.
/// DynamicNeededPacketCount will never go below JitterBufferLowestNeededPacketCount.
/// Defaults to 5.
/// </summary>
property uint32 JitterBufferPacketsBeforeRelaxingNeeded
{
uint32 get();
void set(_In_ uint32 value);
}
/// <summary>
/// This enables or disables the chat performance counters.
/// Defaults to false.
/// </summary>
property bool PerformanceCountersEnabled
{
bool get();
void set(_In_ bool value);
}
/// <summary>
/// This enables or disables combining mic data from multiple local users into a single
/// packet as an optimization before the packet is sent to the OnOutgoingChatPacketReady event.
/// Defaults to true.
/// Some titles may wish to change this to false in order to precisely control
/// which remote consoles receive the mic data of each local user.
/// For example, game logic could determine that local user A's mic data should be sent to
/// remote user C & D while local user B's mic data should be only be sent to remote user E.
/// </summary>
property bool CombineCaptureBuffersIntoSinglePacket
{
bool get();
void set(_In_ bool value);
}
/// <summary>
/// This enables or disables using Kinect as the capture source.
/// Defaults to true.
/// </summary>
property bool UseKinectAsCaptureSource
{
bool get();
void set(_In_ bool value);
}
/// <summary>
/// This enables or disables a callback prior to encoding captured mic data.
/// This allows titles to apply sound effects to the capture stream
/// Defaults to false.
/// </summary>
property bool PreEncodeCallbackEnabled
{
bool get();
void set(_In_ bool value);
}
/// <summary>
/// This enables or disables a callback after to decoding remote chat voice data.
/// This allows titles to apply sound effects to chat voice data
/// Defaults to false.
/// </summary>
property bool PostDecodeCallbackEnabled
{
bool get();
void set(_In_ bool value);
}
/// <summary>
/// Indicates the level of debug messages send to ChatManager::OnDebugMessage
/// Defaults to GameChatDiagnosticsTraceLevel::Info
/// </summary>
property GameChatDiagnosticsTraceLevel DiagnosticsTraceLevel
{
GameChatDiagnosticsTraceLevel get();
void set(GameChatDiagnosticsTraceLevel value);
}
/// <summary>
/// New chat session users will be auto muted if they have a bad reputation
/// and are not a friend of a local user.
/// Defaults to true.
/// </summary>
property bool AutoMuteBadReputationUsers
{
bool get();
void set(_In_ bool value);
}
internal:
bool IsAtDiagnosticsTraceLevel(
_In_ GameChatDiagnosticsTraceLevel levelOfMessage
);
ChatManagerSettings(
_In_ ChatManager^ chatManager
);
private:
Concurrency::critical_section m_chatSettingsStateLock;
void TraceJitterBufferSettings();
void TraceMiscSettings();
void TraceEffectSettings();
uint32 m_audioThreadPeriodInMilliseconds;
uint32 m_audioThreadAffinityMask;
Windows::Xbox::Chat::EncodingQuality m_audioEncodingQuality;
uint32 m_jitterBufferMaxPackets;
uint32 m_jitterBufferLowestNeededPacketCount;
uint32 m_jitterBufferPacketsBeforeRelaxingNeeded;
bool m_performanceCountersEnabled;
Platform::WeakReference m_chatManager;
int m_audioThreadPriority;
bool m_combineCaptureBuffersIntoSinglePacket;
bool m_useKinectAsCaptureSource;
bool m_preEncodeCallbackEnabled;
bool m_postDecodeCallbackEnabled;
GameChatDiagnosticsTraceLevel m_gameChatDiagnosticsTraceLevel;
bool m_autoMuteBadReputationUsers;
};
}}}
#endif
+266
View File
@@ -0,0 +1,266 @@
//// 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 "ChatPerformance.h"
#if TV_API
namespace Microsoft {
namespace Xbox {
namespace GameChat {
ChatPerformanceTime::ChatPerformanceTime() :
m_minTimeInMilliseconds( 100000.0 ),
m_maxTimeInMilliseconds( 0.0 ),
m_averageTimeInMilliseconds( 0.0 ),
m_totalTimeInMilliseconds( 0.0 ),
m_totalExecutionTimeInMilliseconds( 0.0f ),
m_counter( 0 )
{
}
double ChatPerformanceTime::MinTimeInMilliseconds::get()
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
return m_minTimeInMilliseconds;
}
double ChatPerformanceTime::MaxTimeInMilliseconds::get()
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
return m_maxTimeInMilliseconds;
}
double ChatPerformanceTime::AverageTimeInMilliseconds::get()
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
return m_averageTimeInMilliseconds;
}
void
ChatPerformanceTime::Update(
_In_ double executeTimeInMilliseconds,
_In_ double timePassedInMilliseconds
)
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
m_counter++;
if (executeTimeInMilliseconds < m_minTimeInMilliseconds)
{
m_minTimeInMilliseconds = executeTimeInMilliseconds;
}
if (executeTimeInMilliseconds > m_maxTimeInMilliseconds)
{
m_maxTimeInMilliseconds = executeTimeInMilliseconds;
}
m_totalExecutionTimeInMilliseconds += executeTimeInMilliseconds;
m_totalTimeInMilliseconds += timePassedInMilliseconds;
// Update average every so often
if( m_totalTimeInMilliseconds > 500 )
{
m_averageTimeInMilliseconds = m_totalExecutionTimeInMilliseconds / m_counter;
m_totalTimeInMilliseconds = 0.0f;
m_totalExecutionTimeInMilliseconds = 0.0f;
m_counter = 0;
}
}
ChatPerformanceCounters::ChatPerformanceCounters() :
m_incomingPacketBandwidthBitsPerSecond( 0 ),
m_outgoingPacketBandwidthBitsPerSecond( 0 ),
m_incomingPacketBytesIncomingCounter( 0 ),
m_outgoingPacketBytesIncomingCounter( 0 )
{
m_freq.QuadPart = 0;
m_loopStartTime.QuadPart = 0;
m_captureDoneTime.QuadPart = 0;
m_sendDoneTime.QuadPart = 0;
m_loopDoneTime.QuadPart = 0;
m_incomingPacketStartTime.QuadPart = 0;
m_incomingPacketDoneTime.QuadPart = 0;
m_previousLoopStartTime.QuadPart = 0;
m_captureExecutionTime = ref new ChatPerformanceTime();
m_sendExecutionTime = ref new ChatPerformanceTime();
m_renderExecutionTime = ref new ChatPerformanceTime();
m_audioThreadExecutionTime = ref new ChatPerformanceTime();
m_audioThreadPeriodTime = ref new ChatPerformanceTime();
m_incomingPacketTime = ref new ChatPerformanceTime();
QueryPerformanceFrequency(&m_freq);
}
ChatPerformanceTime^ ChatPerformanceCounters::CaptureExecutionTime::get()
{
return m_captureExecutionTime;
}
ChatPerformanceTime^ ChatPerformanceCounters::SendExecutionTime::get()
{
return m_sendExecutionTime;
}
ChatPerformanceTime^ ChatPerformanceCounters::RenderExecutionTime::get()
{
return m_renderExecutionTime;
}
ChatPerformanceTime^ ChatPerformanceCounters::AudioThreadExecutionTime::get()
{
return m_audioThreadExecutionTime;
}
ChatPerformanceTime^ ChatPerformanceCounters::AudioThreadPeriodTime::get()
{
return m_audioThreadPeriodTime;
}
ChatPerformanceTime^ ChatPerformanceCounters::IncomingPacketTime::get()
{
return m_incomingPacketTime;
}
double ChatPerformanceCounters::OutgoingPacketBandwidthBitsPerSecond::get()
{
Concurrency::critical_section::scoped_lock lock(m_packetBytesLock);
return m_outgoingPacketBandwidthBitsPerSecond;
}
double ChatPerformanceCounters::IncomingPacketBandwidthBitsPerSecond::get()
{
Concurrency::critical_section::scoped_lock lock(m_packetBytesLock);
return m_incomingPacketBandwidthBitsPerSecond;
}
double
ChatPerformanceCounters::GetDeltaInMilliseconds(
_In_ const LARGE_INTEGER& startTime,
_In_ const LARGE_INTEGER& endTime,
_In_ const LARGE_INTEGER& freq
)
{
double deltaInSeconds = 0;
if( startTime.QuadPart != 0 &&
endTime.QuadPart != 0 &&
freq.QuadPart != 0)
{
LARGE_INTEGER deltaTicks;
deltaTicks.QuadPart = endTime.QuadPart - startTime.QuadPart;
deltaInSeconds = static_cast< double >( deltaTicks.QuadPart ) / static_cast< double >( freq.QuadPart );
}
return deltaInSeconds * 1000.0f;
}
void ChatPerformanceCounters::QueryLoopStart()
{
QueryPerformanceCounter(&m_loopStartTime);
}
void ChatPerformanceCounters::QueryCaptureDone()
{
QueryPerformanceCounter(&m_captureDoneTime);
}
void ChatPerformanceCounters::QuerySendDone()
{
QueryPerformanceCounter(&m_sendDoneTime);
}
void ChatPerformanceCounters::QueryLoopDone()
{
QueryPerformanceCounter(&m_loopDoneTime);
CalculateTimes();
}
void ChatPerformanceCounters::QueryIncomingPacketStart()
{
QueryPerformanceCounter(&m_incomingPacketStartTime);
}
void ChatPerformanceCounters::QueryIncomingPacketDone()
{
QueryPerformanceCounter(&m_incomingPacketDoneTime);
double incomingPacketTimeInMilliseconds = GetDeltaInMilliseconds(m_incomingPacketStartTime, m_incomingPacketDoneTime, m_freq);
m_incomingPacketTime->Update(incomingPacketTimeInMilliseconds, m_audioThreadPeriodTime->AverageTimeInMilliseconds);
}
void ChatPerformanceCounters::AddPacketBandwidth(
_In_ bool incomingPacket,
_In_ int numberOfBytes
)
{
Concurrency::critical_section::scoped_lock lock(m_packetBytesLock);
if( incomingPacket )
{
m_incomingPacketBytesIncomingCounter += numberOfBytes;
}
else
{
m_outgoingPacketBytesIncomingCounter += numberOfBytes;
}
}
void ChatPerformanceCounters::UpdatePacketBandwidth(
_In_ double totalTimePassedInMilliseconds
)
{
Concurrency::critical_section::scoped_lock lock(m_packetBytesLock);
m_packetsBytesTimeCounterInMilliseconds += totalTimePassedInMilliseconds;
if( m_packetsBytesTimeCounterInMilliseconds > 1000.0f )
{
double timeElapsedInSeconds = m_packetsBytesTimeCounterInMilliseconds / 1000.0;
double incomingBits = static_cast<double>(m_incomingPacketBytesIncomingCounter) * 8;
double outgoingBits = static_cast<double>(m_outgoingPacketBytesIncomingCounter) * 8;
m_incomingPacketBandwidthBitsPerSecond = incomingBits / timeElapsedInSeconds;
m_outgoingPacketBandwidthBitsPerSecond = outgoingBits / timeElapsedInSeconds;
m_packetsBytesTimeCounterInMilliseconds = 0.0;
m_incomingPacketBytesIncomingCounter = 0;
m_outgoingPacketBytesIncomingCounter = 0;
}
}
void ChatPerformanceCounters::CalculateTimes()
{
if ( m_previousLoopStartTime.QuadPart != 0 )
{
double loopTimeInMilliseconds = GetDeltaInMilliseconds(m_loopStartTime, m_loopDoneTime, m_freq);
double captureTimeInMilliseconds = GetDeltaInMilliseconds(m_loopStartTime, m_captureDoneTime, m_freq);
double sendTimeInMilliseconds = GetDeltaInMilliseconds(m_captureDoneTime, m_sendDoneTime, m_freq);
double renderTimeInMilliseconds = GetDeltaInMilliseconds(m_sendDoneTime, m_loopDoneTime, m_freq);
double audioThreadPeriodInMilliseconds = GetDeltaInMilliseconds(m_previousLoopStartTime, m_loopStartTime, m_freq);
double totalTimePassedInMilliseconds = audioThreadPeriodInMilliseconds;
UpdatePacketBandwidth( totalTimePassedInMilliseconds );
m_captureExecutionTime->Update(captureTimeInMilliseconds, totalTimePassedInMilliseconds);
m_sendExecutionTime->Update(sendTimeInMilliseconds, totalTimePassedInMilliseconds);
m_renderExecutionTime->Update(renderTimeInMilliseconds, totalTimePassedInMilliseconds);
m_audioThreadExecutionTime->Update(loopTimeInMilliseconds, totalTimePassedInMilliseconds);
if( audioThreadPeriodInMilliseconds > 10.0 )
{
m_audioThreadPeriodTime->Update(audioThreadPeriodInMilliseconds, totalTimePassedInMilliseconds);
}
else
{
audioThreadPeriodInMilliseconds = 0;
}
}
m_previousLoopStartTime = m_loopStartTime;
}
}}}
#endif
+151
View File
@@ -0,0 +1,151 @@
//// 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 "ChatManagerSettings.h"
#if TV_API
namespace Microsoft {
namespace Xbox {
namespace GameChat {
public ref class ChatPerformanceTime sealed
{
public:
/// <summary>
/// Minimum time value in milliseconds.
// Defaults to 100000.0ms until the first audio thread update
/// </summary>
property double MinTimeInMilliseconds { double get(); }
/// <summary>
/// Maximum time value in milliseconds.
// Defaults to 0.0ms until the first audio thread update
/// </summary>
property double MaxTimeInMilliseconds { double get(); }
/// <summary>
/// Rolling average time value in milliseconds.
/// Value is refreshed every 500ms while the audio thread executes.
// Defaults to 0.0ms until the first audio thread update.
/// </summary>
property double AverageTimeInMilliseconds { double get(); }
internal:
ChatPerformanceTime();
void Update(
_In_ double executeTimeInMilliseconds,
_In_ double timePassedInMilliseconds
);
private:
Concurrency::critical_section m_stateLock;
double m_minTimeInMilliseconds;
double m_maxTimeInMilliseconds;
double m_averageTimeInMilliseconds;
double m_totalTimeInMilliseconds;
double m_totalExecutionTimeInMilliseconds;
long m_counter;
};
public ref class ChatPerformanceCounters sealed
{
public:
/// <summary>
/// ChatPerformanceTime object representing time spent capturing chat data
/// </summary>
property ChatPerformanceTime^ CaptureExecutionTime { ChatPerformanceTime^ get(); }
/// <summary>
/// ChatPerformanceTime object representing time spent sending chat data
/// </summary>
property ChatPerformanceTime^ SendExecutionTime { ChatPerformanceTime^ get(); }
/// <summary>
/// ChatPerformanceTime object representing time spent rendering incoming chat data
/// </summary>
property ChatPerformanceTime^ RenderExecutionTime { ChatPerformanceTime^ get(); }
/// <summary>
/// ChatPerformanceTime object representing time spent executing the audio worker thread
/// </summary>
property ChatPerformanceTime^ AudioThreadExecutionTime { ChatPerformanceTime^ get(); }
/// <summary>
/// ChatPerformanceTime object representing how often the audio thread wakes to do work
/// </summary>
property ChatPerformanceTime^ AudioThreadPeriodTime { ChatPerformanceTime^ get(); }
/// <summary>
/// ChatPerformanceTime object representing the time it takes to process incoming packets
/// </summary>
property ChatPerformanceTime^ IncomingPacketTime { ChatPerformanceTime^ get(); }
/// <summary>
/// Returns the bandwidth in bits per second of incoming packets
/// </summary>
property double IncomingPacketBandwidthBitsPerSecond { double get(); }
/// <summary>
/// Returns the bandwidth in bits per second of outgoing packets
/// </summary>
property double OutgoingPacketBandwidthBitsPerSecond { double get(); }
internal:
ChatPerformanceCounters();
void QueryLoopStart();
void QueryCaptureDone();
void QuerySendDone();
void QueryLoopDone();
void CalculateTimes();
void QueryIncomingPacketStart();
void QueryIncomingPacketDone();
void AddPacketBandwidth( _In_ bool incomingPacket, _In_ int numberOfBytes );
private:
void UpdatePacketBandwidth( _In_ double totalTimePassedInMilliseconds );
static double
GetDeltaInMilliseconds(
_In_ const LARGE_INTEGER& startTime,
_In_ const LARGE_INTEGER& endTime,
_In_ const LARGE_INTEGER& freq
);
LARGE_INTEGER m_freq;
LARGE_INTEGER m_loopStartTime;
LARGE_INTEGER m_captureDoneTime;
LARGE_INTEGER m_sendDoneTime;
LARGE_INTEGER m_loopDoneTime;
LARGE_INTEGER m_previousLoopStartTime;
LARGE_INTEGER m_incomingPacketStartTime;
LARGE_INTEGER m_incomingPacketDoneTime;
ChatPerformanceTime^ m_captureExecutionTime;
ChatPerformanceTime^ m_sendExecutionTime;
ChatPerformanceTime^ m_renderExecutionTime;
ChatPerformanceTime^ m_audioThreadExecutionTime;
ChatPerformanceTime^ m_audioThreadPeriodTime;
ChatPerformanceTime^ m_incomingPacketTime;
Concurrency::critical_section m_packetBytesLock;
int m_incomingPacketBytesIncomingCounter;
int m_outgoingPacketBytesIncomingCounter;
double m_packetsBytesTimeCounterInMilliseconds;
double m_incomingPacketBandwidthBitsPerSecond;
double m_outgoingPacketBandwidthBitsPerSecond;
};
}}}
#endif
@@ -0,0 +1,753 @@
//// 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 "Clock.h"
#include "StringUtils.h"
#include "BufferUtils.h"
#include "ChatClient.h"
#include "ChatUser.h"
#include "ChatNetwork.h"
#include "ChatManagerEvents.h"
#include "ChatManager.h"
#include "ChatManagerSettings.h"
#include "ChatDiagnostics.h"
#if TV_API
using namespace Windows::Xbox::Chat;
using namespace Windows::Storage::Streams;
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 {
ChatAudioThread::ChatAudioThread(
_In_ ChatManagerSettings^ chatManagerSettings,
_In_ ChatClient^ chatClient,
_In_ ChatManager^ chatManager,
_In_ std::shared_ptr<FactoryCache> factoryCache
) :
m_chatManagerSettings( chatManagerSettings ),
m_chatClient( chatClient ),
m_hasMicFocus( true ),
m_chatManager( chatManager ),
m_factoryCache( factoryCache )
{
CHAT_THROW_INVALIDARGUMENT_IF_NULL(chatManagerSettings);
CHAT_THROW_INVALIDARGUMENT_IF_NULL(chatClient);
CHAT_THROW_INVALIDARGUMENT_IF_NULL(chatManager);
CHAT_THROW_INVALIDARGUMENT_IF_NULL(factoryCache);
m_chatPerformanceCounters = ref new Microsoft::Xbox::GameChat::ChatPerformanceCounters();
StartAudioThread();
}
void ChatAudioThread::Shutdown()
{
CHAT_LOG_INFO_MSG(L"ChatAudioThread::Shutdown");
ShutdownAudioThread();
}
void ChatAudioThread::SetChatNetwork(
_In_ ChatNetwork^ chatNetwork
)
{
m_chatNetwork = chatNetwork;
}
void ChatAudioThread::RemoveRemoteConsole(
_In_ CONSOLE_NAME localNameOfRemoteConsoleToRemove
)
{
Concurrency::critical_section::scoped_lock lock(m_remoteCaptureSourcesLock);
for(auto iter = m_remoteCaptureSources.cbegin(); iter != m_remoteCaptureSources.cend(); )
{
LOOKUP_ID lookupId = iter->first;
CONSOLE_NAME localNameOfRemoteConsole = lookupId >> 8;
if( localNameOfRemoteConsoleToRemove == localNameOfRemoteConsole )
{
m_remoteCaptureSources.erase(iter++);
}
else
{
++iter;
}
}
}
void ChatAudioThread::PushRemoteCaptureAudioBuffer(
_In_ LOOKUP_ID lookupId,
_In_ Platform::String^ remoteCaptureSourceId,
_In_reads_(sourceBufferLengthInBytes) BYTE* sourceBuffer,
_In_ UINT sourceBufferLengthInBytes
)
{
std::shared_ptr<CHAT_AUDIO_THREAD_REMOTE_CAPTURE_SOURCE> remoteCaptureSource;
{
std::shared_ptr< CHAT_AUDIO_THREAD_STATE > audioThreadState = GetChatSessionState();
Concurrency::critical_section::scoped_lock lock(m_remoteCaptureSourcesLock);
auto iter = m_remoteCaptureSources.find(lookupId);
if (iter != m_remoteCaptureSources.end())
{
remoteCaptureSource = iter->second;
}
if ( remoteCaptureSource == nullptr && audioThreadState != nullptr )
{
ChatClient^ chatClient = m_chatClient.Resolve<ChatClient>();
if( chatClient == nullptr )
{
// Ignore during shutdown
return;
}
std::vector<ChatUser^> chatUsers = chatClient->GetChatUsersForCaptureSourceId(remoteCaptureSourceId);
if( chatUsers.size() == 0 )
{
// Ignore chat packets from users who aren't yet added.
// Since processing a remote user is async, a few chat voice packets may come in before it is done so skip those
return;
}
// Check if we already have a decoder for this remote capture source.
// If not, create one
ChatDecoder^ chatDecoder = ref new ChatDecoder();
bool isKinect = (wcsstr(remoteCaptureSourceId->Data(), KINECTDESCRIPTOR) != nullptr);
ChatUserTalkingMode talkingMode = isKinect ? ChatUserTalkingMode::TalkingOverKinect : ChatUserTalkingMode::TalkingOverHeadset;
Platform::Collections::Vector<ChatUser^>^ chatUsersVector = ref new Platform::Collections::Vector<ChatUser^>(chatUsers);
Windows::Foundation::Collections::IVectorView<ChatUser^>^ chatUsersView = chatUsersVector->GetView();
remoteCaptureSource.reset( new CHAT_AUDIO_THREAD_REMOTE_CAPTURE_SOURCE() );
remoteCaptureSource->captureSourceId = remoteCaptureSourceId;
remoteCaptureSource->chatDecoder = chatDecoder;
remoteCaptureSource->talkingMode = talkingMode;
remoteCaptureSource->chatUsers = chatUsersView;
UINT maxBytesInPeriod = (UINT)(m_chatManagerSettings->AudioThreadPeriodInMilliseconds / 20.0f) * 256; // max of 256 bytes per 20ms
UINT maxPacketsInRingBuffer = m_chatManagerSettings->JitterBufferMaxPackets;
UINT lowestNeededPacketCount = m_chatManagerSettings->JitterBufferLowestNeededPacketCount;
UINT packetsBeforeRelaxingNeeded = m_chatManagerSettings->JitterBufferPacketsBeforeRelaxingNeeded;
remoteCaptureSource->jitterBuffer.reset( new JitterBuffer(
maxBytesInPeriod,
maxPacketsInRingBuffer,
lowestNeededPacketCount,
packetsBeforeRelaxingNeeded,
m_factoryCache
) );
m_remoteCaptureSources[lookupId] = remoteCaptureSource;
#if WINAPI_FAMILY == WINAPI_FAMILY_TV_TITLE
TraceChatRemoteCaptureSource(
chatUsersView->Size > 0 ? chatUsersView->GetAt(0)->XboxUserId->Data() : L"n/a",
remoteCaptureSourceId->Data(),
lookupId,
chatUsersView->Size
);
#endif
}
}
if( remoteCaptureSource != nullptr )
{
Concurrency::critical_section::scoped_lock lock(remoteCaptureSource->jitterBufferLock);
remoteCaptureSource->jitterBuffer->Push(
sourceBuffer,
sourceBufferLengthInBytes
);
}
}
bool ChatAudioThread::HasMicFocus::get()
{
return m_hasMicFocus;
}
void ChatAudioThread::HasMicFocus::set(
bool val
)
{
m_hasMicFocus = val;
}
void ChatAudioThread::StartAudioThread()
{
if ( m_audioThread == nullptr )
{
m_audioThread = ref new Thread(
m_chatManagerSettings->AudioThreadPeriodInMilliseconds,
m_chatManagerSettings->AudioThreadAffinityMask,
m_chatManagerSettings->AudioThreadPriority
);
Platform::WeakReference wr(this);
m_audioThread->OnDoWork += ref new ThreadDoWorkHandler( [wr]()
{
ChatAudioThread^ audioThread = wr.Resolve<ChatAudioThread>();
if( audioThread != nullptr )
{
audioThread->AudioThreadDoWork();
}
});
}
}
void ChatAudioThread::ShutdownAudioThread()
{
// Need to terminate the processing thread.
if ( m_audioThread != nullptr )
{
m_audioThread->Shutdown();
m_audioThread = nullptr;
}
}
void ChatAudioThread::AudioThreadDoWork()
{
std::shared_ptr< CHAT_AUDIO_THREAD_STATE > chatAudioThreadState = GetChatSessionState();
if (chatAudioThreadState == nullptr)
{
return;
}
bool perfEnabled = chatAudioThreadState->chatPerformanceCountersEnabled;
if( perfEnabled ) { m_chatPerformanceCounters->QueryLoopStart(); }
ChatClient^ chatClient = m_chatClient.Resolve<ChatClient>();
ChatNetwork^ chatNetwork = m_chatNetwork.Resolve<ChatNetwork>();
if (chatClient != nullptr && chatNetwork != nullptr)
{
for each (Microsoft::Xbox::GameChat::ChatUser^ chatUser in chatAudioThreadState->allChatUsers)
{
chatUser->SetTalkingMode(ChatUserTalkingMode::NotTalking);
}
// Capture
CaptureDataFromLocalCaptureSources( chatAudioThreadState );
if( perfEnabled ) { m_chatPerformanceCounters->QueryCaptureDone(); }
// Send
// Now that we have captured the data, send out the network packets
chatNetwork->CreateChatVoicePackets( chatAudioThreadState );
if( perfEnabled ) { m_chatPerformanceCounters->QuerySendDone(); }
// Render
// Play any network packets that have come in
RenderAudioToAllRenderTargets( chatAudioThreadState );
if( perfEnabled ) { m_chatPerformanceCounters->QueryLoopDone(); }
}
}
void ChatAudioThread::LogCommentFormat(
_In_ LPCWSTR strMsg, ...
)
{
va_list args;
va_start(args, strMsg);
LogComment(StringUtils::GetStringFormat(strMsg, args));
va_end(args);
}
void ChatAudioThread::SetChatSessionState(
_In_ IChatSessionState^ chatSessionState,
_In_ ChatClient^ chatClient
)
{
CHAT_THROW_INVALIDARGUMENT_IF_NULL( chatSessionState );
CHAT_THROW_INVALIDARGUMENT_IF_NULL( chatClient );
// Using the new IChatSessionState, create a CHAT_AUDIO_THREAD_STATE
// which precomputes everything needed for the audio thread
std::shared_ptr<CHAT_AUDIO_THREAD_STATE> audioThreadState( new CHAT_AUDIO_THREAD_STATE() );
audioThreadState->chatSessionState = chatSessionState;
Windows::Foundation::Collections::IVectorView<Microsoft::Xbox::GameChat::ChatUser^>^ allChatUsers = chatClient->GetChatUsers();
audioThreadState->allChatUsers = allChatUsers;
for ( UINT i = 0; i < chatSessionState->CaptureSources->Size; ++i )
{
IChatCaptureSource^ chatCaptureSource = chatSessionState->CaptureSources->GetAt( i );
ChatNetwork^ chatNetwork = m_chatNetwork.Resolve<ChatNetwork>();
if( chatNetwork == nullptr)
{
// Ignore during shutdown
return;
}
audioThreadState->preEncodeCallbackEnabled = m_chatManagerSettings->PreEncodeCallbackEnabled;
audioThreadState->postDecodeCallbackEnabled = m_chatManagerSettings->PostDecodeCallbackEnabled;
bool isKinect = (wcsstr(chatCaptureSource->Id->Data(), KINECTDESCRIPTOR) != nullptr);
if ( isKinect &&
m_chatManagerSettings->UseKinectAsCaptureSource == false )
{
continue;
}
ChatEncoder^ chatEncoder = ref new ChatEncoder( chatCaptureSource->Format, m_chatManagerSettings->AudioEncodingQuality );
ChatUserTalkingMode talkingMode = isKinect ? ChatUserTalkingMode::TalkingOverKinect : ChatUserTalkingMode::TalkingOverHeadset;
std::vector<ChatUser^> chatUsers = chatClient->GetChatUsersForCaptureSourceId(chatCaptureSource->Id);
Platform::Collections::Vector<ChatUser^>^ chatUsersVector = ref new Platform::Collections::Vector<ChatUser^>(chatUsers);
if( m_chatManagerSettings->IsAtDiagnosticsTraceLevel(GameChatDiagnosticsTraceLevel::Info) )
{
LogCommentFormat( L"SetChatSessionState: Size: %d chatCaptureSourceId: %s", chatUsersVector->Size, chatCaptureSource->Id->Data() );
for each (ChatUser^ user in chatUsersVector)
{
LogCommentFormat( L"SetChatSessionState: ChatUser: 0x%0.8x XUID: %s UserId: 0x%0.8x", user, user->XboxUserId->Data(), user->User->Id );
}
}
Windows::Foundation::Collections::IVectorView<ChatUser^>^ chatUsersView = chatUsersVector->GetView();
DEVICE_ID captureSourceDeviceId = chatNetwork->GetAudioDeviceIDMapper()->GetLocalDeviceID( chatCaptureSource->Id );
std::shared_ptr<CHAT_AUDIO_THREAD_CAPTURE_SOURCE> chatAudioThreadCaptureSource( new CHAT_AUDIO_THREAD_CAPTURE_SOURCE() );
chatAudioThreadCaptureSource->chatCaptureSource = chatCaptureSource;
chatAudioThreadCaptureSource->audioFormat = chatCaptureSource->Format;
chatAudioThreadCaptureSource->chatEncoder = chatEncoder;
chatAudioThreadCaptureSource->talkingMode = talkingMode;
chatAudioThreadCaptureSource->chatUsers = chatUsersView;
chatAudioThreadCaptureSource->captureSourceDeviceId = captureSourceDeviceId;
audioThreadState->captureSources.push_back( chatAudioThreadCaptureSource );
#if WINAPI_FAMILY == WINAPI_FAMILY_TV_TITLE
ChatUser^ firstChatUser = chatUsersView->Size > 0 ? chatUsersView->GetAt(0) : nullptr;
TraceChatLocalCaptureSource(
firstChatUser != nullptr ? firstChatUser->XboxUserId->Data() : L"n/a",
chatCaptureSource->Id->Data(),
captureSourceDeviceId,
ConvertChatUserTalkingModeToString( chatAudioThreadCaptureSource->talkingMode )->Data()
);
#endif
}
for ( UINT i = 0; i < chatSessionState->RenderTargets->Size; ++i )
{
IChatRenderTarget^ chatRenderTarget = chatSessionState->RenderTargets->GetAt( i );
std::shared_ptr<CHAT_AUDIO_THREAD_RENDER_TARGET> chatAudioThreadRenderTarget( new CHAT_AUDIO_THREAD_RENDER_TARGET() );
chatAudioThreadRenderTarget->chatRenderTarget = chatRenderTarget;
Platform::String^ renderTargetId = chatRenderTarget->Id;
for each (Microsoft::Xbox::GameChat::ChatUser^ chatUser in allChatUsers)
{
if( !chatUser->IsLocal )
{
continue;
}
auto audioDevices = chatUser->User->AudioDevices;
for each (Windows::Xbox::System::IAudioDeviceInfo^ audioDevice in audioDevices)
{
if( StringUtils::IsStringEqualCaseInsenstive(audioDevice->Id, renderTargetId) )
{
chatUser->SetChatRenderTarget( chatRenderTarget );
break; // Stop processing this user but keep looking for other users with this ID
}
}
}
audioThreadState->renderTargets.push_back( chatAudioThreadRenderTarget );
}
audioThreadState->chatPerformanceCountersEnabled = m_chatManagerSettings->PerformanceCountersEnabled;
{
Concurrency::critical_section::scoped_lock lock(m_audioThreadStateLock);
m_audioThreadState = audioThreadState;
}
}
Platform::String^ ChatAudioThread::ConvertChatUserTalkingModeToString(
_In_ ChatUserTalkingMode chatUserTalkingMode
)
{
switch (chatUserTalkingMode)
{
case ChatUserTalkingMode::NotTalking: return L"NotTalking";
case ChatUserTalkingMode::TalkingOverHeadset: return L"TalkingOverHeadset";
case ChatUserTalkingMode::TalkingOverKinect: return L"TalkingOverKinect";
}
return L"Unknown";
}
std::shared_ptr< CHAT_AUDIO_THREAD_STATE > ChatAudioThread::GetChatSessionState()
{
Concurrency::critical_section::scoped_lock lock(m_audioThreadStateLock);
return m_audioThreadState;
}
void ChatAudioThread::CaptureDataFromLocalCaptureSources(
_In_ std::shared_ptr< CHAT_AUDIO_THREAD_STATE > chatAudioThreadState
)
{
for each (std::shared_ptr<CHAT_AUDIO_THREAD_CAPTURE_SOURCE> chatAudioThreadCaptureSource in chatAudioThreadState->captureSources)
{
IBuffer^ captureBuffer = nullptr;
Windows::Xbox::Chat::CaptureBufferStatus status = chatAudioThreadCaptureSource->chatCaptureSource->GetNextBuffer( &captureBuffer );
switch (status)
{
case Windows::Xbox::Chat::CaptureBufferStatus::Filled: __fallthrough;
case Windows::Xbox::Chat::CaptureBufferStatus::Incomplete:
{
m_hasMicFocus = true;
IBuffer^ bufferToEncode;
if( chatAudioThreadState->preEncodeCallbackEnabled )
{
ChatManager^ chatManager = m_chatManager.Resolve<ChatManager>();
if( chatManager != nullptr )
{
bufferToEncode = chatManager->OnPreEncodeAudioBufferHandler(
captureBuffer,
chatAudioThreadCaptureSource->audioFormat,
chatAudioThreadCaptureSource->chatUsers
);
if( bufferToEncode == nullptr )
{
// Skip this capture source
continue;
}
}
else
{
bufferToEncode = captureBuffer;
}
}
else
{
bufferToEncode = captureBuffer;
}
IBuffer^ encodedBuffer = nullptr;
chatAudioThreadCaptureSource->chatEncoder->Encode( bufferToEncode, &encodedBuffer );
// The encoder can return a zero length buffer.
if ( encodedBuffer != nullptr && encodedBuffer->Length > 0 )
{
// Encoder or Capture buffers need to be copied as they will
// not hang around more than until the next pass.
IBuffer^ destBuffer = BufferUtils::BufferCopy( encodedBuffer, m_factoryCache->GetBufferFactory() );
bool isCaptureSourceMuted = false;
for each (ChatUser^ user in chatAudioThreadCaptureSource->chatUsers)
{
if( user->IsLocalUserMuted )
{
isCaptureSourceMuted = true;
}
}
if( !isCaptureSourceMuted )
{
chatAudioThreadCaptureSource->audioBufferQueue.push( destBuffer );
for each (ChatUser^ user in chatAudioThreadCaptureSource->chatUsers)
{
user->SetTalkingMode( chatAudioThreadCaptureSource->talkingMode );
}
}
}
}
break;
case Windows::Xbox::Chat::CaptureBufferStatus::NoMicrophoneFocus:
{
// At least one source does not have microphone focus.
// For simplicity, all rendering will stop until we regain focus.
m_hasMicFocus = false;
ClearChatSessionState();
#if WINAPI_FAMILY == WINAPI_FAMILY_TV_TITLE
TraceChatMicFocus( m_hasMicFocus );
#endif
// Because we have cleared the session, we should return until
// a state change brings us an updated session state.
return;
}
break;
case Windows::Xbox::Chat::CaptureBufferStatus::NotTalking:
{
if (chatAudioThreadCaptureSource->chatEncoder->IsDataInFlight)
{
IBuffer^ encodedBuffer = nullptr;
chatAudioThreadCaptureSource->chatEncoder->Encode( nullptr, &encodedBuffer );
// The encoder can return a zero length buffer.
if ( encodedBuffer != nullptr && encodedBuffer->Length > 0 )
{
// Encoder or Capture buffers need to be copied as they will
// not hang around more than until the next pass.
IBuffer^ destBuffer = BufferUtils::BufferCopy( encodedBuffer, m_factoryCache->GetBufferFactory() );
bool isCaptureSourceMuted = false;
for each (ChatUser^ user in chatAudioThreadCaptureSource->chatUsers)
{
if( user->IsLocalUserMuted )
{
isCaptureSourceMuted = true;
}
}
if( !isCaptureSourceMuted )
{
chatAudioThreadCaptureSource->audioBufferQueue.push( destBuffer );
for each (ChatUser^ user in chatAudioThreadCaptureSource->chatUsers)
{
user->SetTalkingMode( chatAudioThreadCaptureSource->talkingMode );
}
}
}
}
}
default:
// Nothing needs to be done
break;
}
}
}
void ChatAudioThread::RenderAudioToAllRenderTargets(
_In_ std::shared_ptr< CHAT_AUDIO_THREAD_STATE > chatAudioThreadState
)
{
IChatSessionState^ chatSessionState = chatAudioThreadState->chatSessionState;
std::map< LOOKUP_ID, std::shared_ptr<CHAT_AUDIO_THREAD_REMOTE_CAPTURE_SOURCE> > remoteCaptureSourcesCopy;
{
Concurrency::critical_section::scoped_lock lock(m_remoteCaptureSourcesLock);
remoteCaptureSourcesCopy = m_remoteCaptureSources;
}
// Nothing to do if we haven't yet got data from any remote capture sources
if( remoteCaptureSourcesCopy.size() == 0 )
{
return;
}
IBuffer^ decodedBuffer;
for each (std::shared_ptr<CHAT_AUDIO_THREAD_RENDER_TARGET> chatAudioThreadRenderTarget in chatAudioThreadState->renderTargets)
{
IChatRenderTarget^ target = chatAudioThreadRenderTarget->chatRenderTarget;
try
{
target->BeginMix();
for each ( auto it in remoteCaptureSourcesCopy)
{
std::shared_ptr<CHAT_AUDIO_THREAD_REMOTE_CAPTURE_SOURCE> remoteCaptureSource = it.second;
Concurrency::critical_section::scoped_lock lock(remoteCaptureSource->jitterBufferLock);
int currentPacketCount = remoteCaptureSource->jitterBuffer->GetCurrentPacketCount();
if ( !(remoteCaptureSource->chatDecoder->IsDataInFlight) && currentPacketCount == 0 )
{
// Skip this remote capture source if there's no data from this remote capture source
continue;
}
// If data might be in flight (if hardware encoding is being used), pull out the last packet by calling decode with nullptr
IBuffer^ queuedBuffer = nullptr;
HRESULT hr = S_OK;
if (currentPacketCount > 0)
{
hr = remoteCaptureSource->jitterBuffer->GetFront( &queuedBuffer );
}
if( SUCCEEDED(hr) )
{
try
{
if (remoteCaptureSource->cachedDecodedBuffer == nullptr)
{
remoteCaptureSource->chatDecoder->Decode( queuedBuffer, &decodedBuffer ); // hang on to the decoded buffer reference until after SubmitMix() returns
remoteCaptureSource->cachedDecodedBuffer = decodedBuffer;
}
decodedBuffer = remoteCaptureSource->cachedDecodedBuffer;
if (decodedBuffer != nullptr)
{
IBuffer^ mixBuffer = nullptr;
if( chatAudioThreadState->postDecodeCallbackEnabled )
{
ChatManager^ chatManager = m_chatManager.Resolve<ChatManager>();
if( chatManager != nullptr )
{
mixBuffer = chatManager->OnPostDecodeAudioBufferHandler(
decodedBuffer,
remoteCaptureSource->chatDecoder->Format,
remoteCaptureSource->chatUsers
);
if( mixBuffer == nullptr )
{
// Skip this remote capture source
continue;
}
remoteCaptureSource->postDecodeAudioBuffer = mixBuffer; // hang on to the reference to the new mix buffer until after SubmitMix() returns
}
else
{
mixBuffer = decodedBuffer;
}
}
else
{
mixBuffer = decodedBuffer;
}
ChatRestriction restriction = target->AddMixBuffer(
remoteCaptureSource->captureSourceId,
remoteCaptureSource->chatDecoder->Format,
mixBuffer
);
// Take update any users who are associated with this remote capture source
for each (ChatUser^ user in remoteCaptureSource->chatUsers)
{
user->SetTalkingMode( remoteCaptureSource->talkingMode );
user->SetRestrictionMode( restriction );
}
}
}
catch( Platform::Exception^ )
{
// Skip buffer if Decode or AddMixBuffer throws error
}
}
}
target->SubmitMix();
}
catch( Platform::Exception^ )
{
try
{
target->ResetMix();
}
catch (Platform::Exception^ ex)
{
if (ex->HResult == (HRESULT)Windows::Xbox::Chat::ChatErrorStatus::RenderGraphError)
{
ChatClient^ chatClient = m_chatClient.Resolve<ChatClient>();
if( chatClient != nullptr )
{
chatClient->UpdateSessionState();
}
}
}
}
}
for each ( auto it in remoteCaptureSourcesCopy )
{
std::shared_ptr<CHAT_AUDIO_THREAD_REMOTE_CAPTURE_SOURCE> remoteCaptureSource = it.second;
Concurrency::critical_section::scoped_lock lock(remoteCaptureSource->jitterBufferLock);
// Clear out all saved decoded buffers
remoteCaptureSource->cachedDecodedBuffer = nullptr;
if ( remoteCaptureSource->jitterBuffer->GetCurrentPacketCount() != 0 )
{
// Pop the first buffer that we've just rendered to all render targets.
remoteCaptureSource->jitterBuffer->Pop();
}
// For UI & debugging only: This shows the number of pending audio packets that the render queue still has.
for each (ChatUser^ user in remoteCaptureSource->chatUsers)
{
user->SetNumberOfPendingAudioPacketsToPlay( (uint32)remoteCaptureSource->jitterBuffer->GetCurrentPacketCount() );
user->SetDynamicNeededPacketCount( (uint32)remoteCaptureSource->jitterBuffer->GetDynamicNeededPacketCount() );
}
}
}
void ChatAudioThread::ClearChatSessionState()
{
// Need to drain the render and capture queues on session state changes.
{
Concurrency::critical_section::scoped_lock lock(m_audioThreadStateLock);
m_audioThreadState.reset();
}
{
Concurrency::critical_section::scoped_lock lock(m_remoteCaptureSourcesLock);
m_remoteCaptureSources.clear();
}
}
void ChatAudioThread::OnChatManagerSettingsChangedHandler()
{
if( m_audioThread )
{
#if WINAPI_FAMILY == WINAPI_FAMILY_TV_TITLE
TraceChatManagerSettingsAudioThread(
m_chatManagerSettings->AudioThreadPeriodInMilliseconds,
m_chatManagerSettings->AudioThreadAffinityMask,
m_chatManagerSettings->AudioThreadPriority
);
#endif
m_audioThread->SetWorkPeriodInMilliseconds( m_chatManagerSettings->AudioThreadPeriodInMilliseconds );
m_audioThread->SetOptions( m_chatManagerSettings->AudioThreadAffinityMask, m_chatManagerSettings->AudioThreadPriority );
}
}
void ChatAudioThread::LogComment(
_In_ Platform::String^ message
)
{
LogCommentWithError(message, S_OK);
}
void ChatAudioThread::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 );
}
}
ChatPerformanceCounters^ ChatAudioThread::ChatPerformanceCounters::get()
{
return m_chatPerformanceCounters;
}
}}}
#endif
+215
View File
@@ -0,0 +1,215 @@
//// 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"
#include "ChatClient.h"
#include "AudioDeviceIDMapper.h"
#include "JitterBuffer.h"
#include "ChatPerformance.h"
#include "FactoryCache.h"
#if TV_API
// Forward declare
namespace Microsoft { namespace Xbox { namespace GameChat { ref class ChatNetwork; } } }
namespace Microsoft {
namespace Xbox {
namespace GameChat {
#define KINECTDESCRIPTOR L"postmec"
struct CHAT_AUDIO_THREAD_CAPTURE_SOURCE
{
Windows::Xbox::Chat::IChatCaptureSource^ chatCaptureSource;
Windows::Xbox::Chat::ChatEncoder^ chatEncoder;
ChatUserTalkingMode talkingMode;
Windows::Foundation::Collections::IVectorView<ChatUser^>^ chatUsers;
std::queue<Windows::Storage::Streams::IBuffer^> audioBufferQueue;
DEVICE_ID captureSourceDeviceId;
Windows::Xbox::Chat::IFormat^ audioFormat;
};
struct CHAT_AUDIO_THREAD_RENDER_TARGET
{
Windows::Xbox::Chat::IChatRenderTarget^ chatRenderTarget;
};
struct CHAT_AUDIO_THREAD_STATE
{
Windows::Xbox::Chat::IChatSessionState^ chatSessionState;
Windows::Foundation::Collections::IVectorView<Microsoft::Xbox::GameChat::ChatUser^>^ allChatUsers;
std::vector< std::shared_ptr<CHAT_AUDIO_THREAD_CAPTURE_SOURCE> > captureSources;
std::vector< std::shared_ptr<CHAT_AUDIO_THREAD_RENDER_TARGET> > renderTargets;
bool chatPerformanceCountersEnabled;
bool preEncodeCallbackEnabled;
bool postDecodeCallbackEnabled;
};
struct CHAT_AUDIO_THREAD_REMOTE_CAPTURE_SOURCE
{
Platform::String^ captureSourceId;
Windows::Xbox::Chat::ChatDecoder^ chatDecoder;
Windows::Storage::Streams::IBuffer^ cachedDecodedBuffer;
std::shared_ptr<IJitterBuffer> jitterBuffer;
Concurrency::critical_section jitterBufferLock;
ChatUserTalkingMode talkingMode;
Windows::Foundation::Collections::IVectorView<ChatUser^>^ chatUsers;
Windows::Storage::Streams::IBuffer^ postDecodeAudioBuffer;
};
/// <summary>
/// This class handles a real-time thread pumped at a specific frequency to capture chat
/// packets to push to a network queue and poll an incoming queue for packets to render.
/// </summary>
ref class ChatAudioThread sealed
{
internal:
ChatAudioThread(
_In_ ChatManagerSettings^ chatManagerSettings,
_In_ ChatClient^ chatClient,
_In_ ChatManager^ chatManager,
_In_ std::shared_ptr<FactoryCache> factoryCache
);
void Shutdown();
void SetChatNetwork(
_In_ ChatNetwork^ chatNetwork
);
/// <summary>
/// Indicates if the the title has mic focus
/// </summary>
property bool HasMicFocus
{
bool get();
void set(bool val);
}
/// <summary>
/// Push a chat packet that has been received from the network transport layer
/// to the render buffer
/// </summary>
void PushRemoteCaptureAudioBuffer(
_In_ LOOKUP_ID lookupId,
_In_ Platform::String^ captureSourceId,
_In_reads_(sourceBufferLengthInBytes) BYTE* sourceBuffer,
_In_ UINT sourceBufferLengthInBytes
);
/// <summary>
/// Updates the chat session after a ChatParticipant has been added or removed.
/// This clears the capture and render queues so we don't send or render packets
/// without a user attached, as well as potentially shutting down the processing
/// thread if there are no more active ChatParticipants
/// </summary>
void UpdateSessionState();
void SetChatSessionState(
_In_ Windows::Xbox::Chat::IChatSessionState^ chatSessionState,
_In_ ChatClient^ chatClient
);
void ClearChatSessionState();
void OnChatManagerSettingsChangedHandler();
property Microsoft::Xbox::GameChat::ChatPerformanceCounters^ ChatPerformanceCounters
{
Microsoft::Xbox::GameChat::ChatPerformanceCounters^ get();
}
void RemoveRemoteConsole(
_In_ CONSOLE_NAME localNameOfRemoteConsole
);
private:
std::shared_ptr< CHAT_AUDIO_THREAD_STATE > GetChatSessionState();
void LogCommentFormat(
_In_ LPCWSTR strMsg, ...
);
/// <summary>
/// Starts the processing thread
/// </summary>
void StartAudioThread();
/// <summary>
/// Terminates the processing thread
/// </summary>
void ShutdownAudioThread();
/// <summary>
// This is called periodically by the audio thread execution loop
// This is the workhorse component of the audio thread, rendering
// chat packets from the incoming rendering queue and capturing new packets from
// audio hardware and pushing them to the capture queue.
/// </summary>
void AudioThreadDoWork();
/// <summary>
/// Render all audio from each CaptureSource to all RenderTargets. The systems will handle
/// whether a render target should or should not render audio from a particular target. The
/// overhead of sending capture data to a render target that isn't rendered is trivial.
/// Audio sent to a render target may not be rendered if the source/target relationship
/// doesn't allow it for some reason, such as the render target being associated solely with
/// a child account.
/// </summary>
void RenderAudioToAllRenderTargets( _In_ std::shared_ptr< CHAT_AUDIO_THREAD_STATE > chatAudioThreadState );
void CaptureDataFromLocalCaptureSources( _In_ std::shared_ptr< CHAT_AUDIO_THREAD_STATE > chatAudioThreadState );
/// <summary>
/// Pushes an incoming chat buffer to the tail of the capture buffer queue
/// </summary>
void PushCaptureBuffer(
_In_ Platform::String^ captureSourceId,
_In_ Windows::Storage::Streams::IBuffer^ buffer
);
void LogComment(
_In_ Platform::String^ message
);
void LogCommentWithError(
_In_ Platform::String^ message,
_In_ HRESULT hr
);
Platform::String^ ConvertChatUserTalkingModeToString(
_In_ ChatUserTalkingMode chatUserTalkingMode
);
private:
std::map< LOOKUP_ID, std::shared_ptr<CHAT_AUDIO_THREAD_REMOTE_CAPTURE_SOURCE> > m_remoteCaptureSources;
std::shared_ptr<FactoryCache> m_factoryCache;
Thread^ m_audioThread;
Platform::WeakReference m_chatClient;
Platform::WeakReference m_chatNetwork;
ChatManagerSettings^ m_chatManagerSettings;
Platform::WeakReference m_chatManager;
Concurrency::critical_section m_audioThreadStateLock;
Concurrency::critical_section m_remoteCaptureSourcesLock;
std::shared_ptr< CHAT_AUDIO_THREAD_STATE > m_audioThreadState;
bool m_hasMicFocus;
Windows::Foundation::EventRegistrationToken m_tokenOnChatManagerSettingsChanged;
Microsoft::Xbox::GameChat::ChatPerformanceCounters^ m_chatPerformanceCounters;
};
}}}
#endif
+191
View File
@@ -0,0 +1,191 @@
//// 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 "JitterBuffer.h"
#include "StringUtils.h"
#include "BufferUtils.h"
#if TV_API
using namespace Microsoft::WRL;
using namespace Windows::Storage::Streams;
namespace Microsoft {
namespace Xbox {
namespace GameChat {
JitterBuffer::JitterBuffer(
_In_ UINT maxBytesInPeriod,
_In_ UINT maxPacketsInRingBuffer,
_In_ UINT lowestNeededPacketCount,
_In_ UINT packetsBeforeRelaxingNeeded,
_In_ std::shared_ptr<FactoryCache> factoryCache
) :
m_maxPacketsInRingBuffer(maxPacketsInRingBuffer),
m_maxBytesInPeriod(maxBytesInPeriod),
m_readIndex(0),
m_writeIndex(0),
m_currentPacketCount(0),
m_dynamicNeededPacketCount(lowestNeededPacketCount), // Just set to lowest and it'll automatically adjust
m_lowestNeededPacketCount(lowestNeededPacketCount),
m_packetsBeforeRelaxingNeeded(packetsBeforeRelaxingNeeded),
m_numberOfPacketsAboveNeededWhileDraining(0),
m_state(NeedPackets),
m_factoryCache(factoryCache)
{
CHAT_THROW_INVALIDARGUMENT_IF_NULL(factoryCache);
for ( UINT i=0; i < m_maxPacketsInRingBuffer; i++ )
{
m_ringBuffer.push_back( BufferUtils::FastBufferCreate( m_maxBytesInPeriod, m_factoryCache->GetBufferFactory() ) );
}
}
JitterBuffer::~JitterBuffer()
{
}
HRESULT
JitterBuffer::Push(
_In_reads_(sourceBufferLengthInBytes) BYTE* sourceBuffer,
_In_ UINT sourceBufferLengthInBytes
)
{
CHAT_THROW_E_POINTER_IF_NULL( sourceBuffer );
CHAT_THROW_INVALIDARGUMENT_IF( sourceBufferLengthInBytes > m_maxBytesInPeriod );
// Fail if the caller pushed more than m_maxPacketCount
if( m_currentPacketCount >= m_maxPacketsInRingBuffer )
{
return HRESULT_FROM_WIN32( ERROR_BUFFER_OVERFLOW );
}
IBuffer^ buffer = m_ringBuffer[ m_writeIndex ];
BYTE* bufferBytes = NULL;
BufferUtils::GetBufferBytes( buffer, &bufferBytes );
errno_t err = memcpy_s( bufferBytes, buffer->Capacity, sourceBuffer, sourceBufferLengthInBytes );
CHAT_THROW_HR_IF(err != 0, E_FAIL); // This should not happen
buffer->Length = sourceBufferLengthInBytes;
// Advance to the next empty buffer in the ring buffer and update the size.
m_currentPacketCount++;
m_writeIndex++;
m_writeIndex %= m_maxPacketsInRingBuffer;
// Heuristics for adjusting the dynamic needed packet count
if ( m_state == NeedPackets )
{
if ( m_currentPacketCount >= m_dynamicNeededPacketCount )
{
m_state = Draining;
}
else
{
// Stay in NeedPackets
}
}
else // if ( m_state == Draining )
{
m_numberOfPacketsAboveNeededWhileDraining++;
// After we get above m_packetsBeforeRelaxingNeeded, we can relax the needed packet
if ( m_numberOfPacketsAboveNeededWhileDraining > m_packetsBeforeRelaxingNeeded )
{
m_numberOfPacketsAboveNeededWhileDraining = 0;
if( m_dynamicNeededPacketCount > 0 )
{
m_dynamicNeededPacketCount--;
m_dynamicNeededPacketCount = max(m_dynamicNeededPacketCount, m_lowestNeededPacketCount);
}
}
}
return S_OK;
}
HRESULT
JitterBuffer::GetFront(
_Out_ Windows::Storage::Streams::IBuffer^* pBuffer
)
{
if( m_currentPacketCount == 0 ||
m_state == NeedPackets )
{
// Return failure if we're empty or we still haven't gotten above the needed packet count
*pBuffer = nullptr;
return HRESULT_FROM_WIN32( ERROR_NO_MORE_ITEMS );
}
else // if ( m_state == Draining )
{
*pBuffer = m_ringBuffer[ m_readIndex ];
return S_OK;
}
}
HRESULT
JitterBuffer::Pop()
{
if( m_currentPacketCount == 0 ||
m_state == NeedPackets )
{
// Return failure if we're empty or we still haven't gotten above the needed packet count
return HRESULT_FROM_WIN32( ERROR_NO_MORE_ITEMS );
}
else // if ( m_state == Draining )
{
m_readIndex++;
m_readIndex %= m_maxPacketsInRingBuffer;
m_currentPacketCount--;
if ( m_currentPacketCount < m_lowestNeededPacketCount )
{
// When going below the needed packet count, reset the number of packets we have gotten
m_numberOfPacketsAboveNeededWhileDraining = 0;
}
if ( m_currentPacketCount == 0 )
{
// When we hit 0 packets, then we have fully drained and switch the state to NeedPackets
m_state = NeedPackets;
// We hit 0 packet, so increase the needed packet count for more safety but higher latency
m_dynamicNeededPacketCount++;
m_dynamicNeededPacketCount = min(m_dynamicNeededPacketCount, m_maxPacketsInRingBuffer);
}
return S_OK;
}
}
UINT
JitterBuffer::GetCurrentPacketCount()
{
return m_currentPacketCount;
}
UINT
JitterBuffer::GetLowestNeededPacketCount()
{
return m_lowestNeededPacketCount;
}
UINT
JitterBuffer::GetMaxPacketsInRingBuffer()
{
return m_maxPacketsInRingBuffer;
}
UINT JitterBuffer::GetDynamicNeededPacketCount()
{
return m_dynamicNeededPacketCount;
}
}}}
#endif
+114
View File
@@ -0,0 +1,114 @@
//// 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 <vector>
#include "FactoryCache.h"
#if TV_API
namespace Microsoft {
namespace Xbox {
namespace GameChat {
enum JitterBufferState
{
NeedPackets = 0,
Draining
};
class IJitterBuffer
{
public:
virtual HRESULT Push( _In_reads_(sourceBufferLengthInBytes) BYTE* sourceBuffer, _In_ UINT sourceBufferLengthInBytes ) = 0;
virtual HRESULT GetFront( _Out_ Windows::Storage::Streams::IBuffer^* buffer ) = 0;
virtual HRESULT Pop() = 0;
virtual UINT GetCurrentPacketCount() = 0;
virtual UINT GetLowestNeededPacketCount() = 0;
virtual UINT GetMaxPacketsInRingBuffer() = 0;
virtual UINT GetDynamicNeededPacketCount() = 0;
};
class JitterBuffer : public IJitterBuffer
{
public:
JitterBuffer(
_In_ UINT maxBytesInPeriod,
_In_ UINT maxPacketsInRingBuffer,
_In_ UINT lowestNeededPacketCount,
_In_ UINT packetsBeforeRelaxingNeeded,
_In_ std::shared_ptr<FactoryCache> factoryCache
);
virtual ~JitterBuffer();
// IJitterBuffer
/// <summary>
/// Title is pushing the next buffer of data.
/// </summary>
virtual HRESULT
Push(
_In_reads_(sourceBufferLengthInBytes) BYTE* sourceBuffer,
_In_ UINT sourceBufferLengthInBytes
);
/// <summary>
/// Title is asking for a reference to the next IBuffer that needs to be rendered.
/// </summary>
virtual HRESULT
GetFront(
_Out_ Windows::Storage::Streams::IBuffer^* buffer
);
/// <summary>
/// Title is telling the JitterBuffer that it is ok to reuse the last IBuffer that was
/// handed down by the Jitter Buffer, essentially advancing the JB ring buffer.
/// </summary>
virtual HRESULT Pop();
/// <summary>
/// Title can use this property to query the current size/latency, expressed in periods/packets.
/// </summary>
virtual UINT GetCurrentPacketCount();
/// <summary>
/// Title can use this property to query the min needed packet count, expressed in periods.
/// </summary>
virtual UINT GetLowestNeededPacketCount();
/// <summary>
/// Title can use this property to query the max packets in the ring buffer, expressed in periods.
/// </summary>
virtual UINT GetMaxPacketsInRingBuffer();
/// <summary>
/// Title can use this property to query the current dynamic needed packets, expressed in periods.
/// </summary>
virtual UINT GetDynamicNeededPacketCount();
private:
// Ring buffer
UINT m_maxPacketsInRingBuffer;
UINT m_maxBytesInPeriod;
std::vector<Windows::Storage::Streams::IBuffer^> m_ringBuffer;
UINT m_readIndex;
UINT m_writeIndex;
UINT m_currentPacketCount;
std::shared_ptr<FactoryCache> m_factoryCache;
// Heuristics
UINT m_dynamicNeededPacketCount;
UINT m_lowestNeededPacketCount;
UINT m_packetsBeforeRelaxingNeeded;
UINT m_numberOfPacketsAboveNeededWhileDraining;
JitterBufferState m_state;
};
}}}
#endif
+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
+155
View File
@@ -0,0 +1,155 @@
//// 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 "StringUtils.h"
#include "ChatDiagnostics.h"
#include "BufferUtils.h"
#include "ChatAudioThread.h"
#include "ChatNetwork.h"
#include "ChatManager.h"
#if TV_API
using namespace Windows::Foundation;
using namespace concurrency;
namespace Microsoft {
namespace Xbox {
namespace GameChat {
class ChatDiagnosticsRegistration
{
public:
ChatDiagnosticsRegistration()
{
#if WINAPI_FAMILY == WINAPI_FAMILY_TV_TITLE
EventRegisterXbox_GameChat_API();
#endif
}
~ChatDiagnosticsRegistration()
{
#if WINAPI_FAMILY == WINAPI_FAMILY_TV_TITLE
EventUnregisterXbox_GameChat_API();
#endif
}
};
static ChatDiagnosticsRegistration s_chatDiagnosticsRegistration;
ChatDiagnostics::ChatDiagnostics() :
m_diagnosticConsoleNameTracker(1)
{
}
uint32 ChatDiagnostics::GetDiagnosticNameForConsole(
_In_ ChatManager^ chatManager,
_In_ Platform::Object^ uniqueConsoleIdentifier
)
{
if( uniqueConsoleIdentifier == nullptr )
{
return 0;
}
Concurrency::critical_section::scoped_lock lock(m_diagnosticNameOfConsolesLock);
for each (std::shared_ptr<ChatEventConsoleNameIdentifierPair> pair in m_diagnosticNameOfConsoles)
{
if( chatManager->OnCompareUniqueConsoleIdentifiersHandler(pair->uniqueConsoleIdentifier, uniqueConsoleIdentifier) )
{
return pair->consoleName;
}
}
// Record the name for next time
std::shared_ptr<ChatEventConsoleNameIdentifierPair> pair( new ChatEventConsoleNameIdentifierPair() );
pair->consoleName = InterlockedIncrement(&m_diagnosticConsoleNameTracker);
pair->uniqueConsoleIdentifier = uniqueConsoleIdentifier;
m_diagnosticNameOfConsoles.push_back( pair );
return pair->consoleName;
}
void ChatDiagnostics::TraceChatUserAndAudioDevices(
_In_ Windows::Xbox::System::IUser^ user
)
{
if( user == nullptr )
{
return;
}
#if WINAPI_FAMILY == WINAPI_FAMILY_TV_TITLE
TraceChatUserInfo(
user->XboxUserId->Data(),
user->Id,
user->IsGuest,
user->IsSignedIn,
user->Sponsor != nullptr, // HasSponsor
user->Controllers != nullptr ? user->Controllers->Size : 0, // NumControllers
user->AudioDevices->Size // NumAudioDevices
);
for each (Windows::Xbox::System::IAudioDeviceInfo^ audioDeviceInfo in user->AudioDevices)
{
TraceChatUserAudioDevice(
user->XboxUserId->Data(),
audioDeviceInfo->Id->Data(),
ConvertAudioDeviceCategoryToString( audioDeviceInfo->DeviceCategory )->Data(),
ConvertAudioDeviceTypeToString( audioDeviceInfo->DeviceType )->Data(),
audioDeviceInfo->IsMicrophoneMuted,
ConvertAudioDeviceSharingToString( audioDeviceInfo->Sharing )->Data()
);
}
#endif
}
Platform::String^ ChatDiagnostics::ConvertAudioDeviceTypeToString(
_In_ Windows::Xbox::System::AudioDeviceType audioDeviceType
)
{
switch (audioDeviceType)
{
case Windows::Xbox::System::AudioDeviceType::Capture: return L"Capture";
case Windows::Xbox::System::AudioDeviceType::Render: return L"Render";
}
return L"Unknown";
}
Platform::String^ ChatDiagnostics::ConvertAudioDeviceCategoryToString(
_In_ Windows::Xbox::System::AudioDeviceCategory audioDeviceCategory
)
{
switch (audioDeviceCategory)
{
case Windows::Xbox::System::AudioDeviceCategory::Communications: return L"Communications";
case Windows::Xbox::System::AudioDeviceCategory::Multimedia: return L"Multimedia";
case Windows::Xbox::System::AudioDeviceCategory::Voice: return L"Voice";
}
return L"Unknown";
}
Platform::String^ ChatDiagnostics::ConvertAudioDeviceSharingToString(
_In_ Windows::Xbox::System::AudioDeviceSharing audioDeviceSharing
)
{
switch (audioDeviceSharing)
{
case Windows::Xbox::System::AudioDeviceSharing::Exclusive: return L"Exclusive";
case Windows::Xbox::System::AudioDeviceSharing::Private: return L"Private";
case Windows::Xbox::System::AudioDeviceSharing::Shared: return L"Shared";
}
return L"Unknown";
}
}}}
#endif
+62
View File
@@ -0,0 +1,62 @@
#pragma once
#if WINAPI_FAMILY == WINAPI_FAMILY_TV_TITLE
typedef ULONG64 TRACEHANDLE, *PTRACEHANDLE;
#define EVENT_CONTROL_CODE_DISABLE_PROVIDER 0
#define EVENT_CONTROL_CODE_ENABLE_PROVIDER 1
#define EVENT_CONTROL_CODE_CAPTURE_STATE 2
#include "ChatEvents.h"
#endif
// Forward declare
namespace Microsoft { namespace Xbox { namespace GameChat { ref class ChatManager; } } }
#if TV_API
namespace Microsoft {
namespace Xbox {
namespace GameChat {
struct ChatEventConsoleNameIdentifierPair
{
Platform::Object^ uniqueConsoleIdentifier;
uint32 consoleName;
};
class ChatDiagnostics
{
public:
ChatDiagnostics();
uint32 GetDiagnosticNameForConsole(
_In_ ChatManager^ chatManager,
_In_ Platform::Object^ uniqueConsoleIdentifier
);
void TraceChatUserAndAudioDevices(
_In_ Windows::Xbox::System::IUser^ user
);
private:
Platform::String^ ConvertAudioDeviceSharingToString(
_In_ Windows::Xbox::System::AudioDeviceSharing audioDeviceSharing
);
Platform::String^ ConvertAudioDeviceCategoryToString(
_In_ Windows::Xbox::System::AudioDeviceCategory audioDeviceCategory
);
Platform::String^ ConvertAudioDeviceTypeToString(
_In_ Windows::Xbox::System::AudioDeviceType audioDeviceType
);
std::vector< std::shared_ptr<ChatEventConsoleNameIdentifierPair> > m_diagnosticNameOfConsoles;
Concurrency::critical_section m_diagnosticNameOfConsolesLock;
LONG m_diagnosticConsoleNameTracker;
};
}}}
#endif
File diff suppressed because it is too large Load Diff
+3
View File
@@ -0,0 +1,3 @@
LANGUAGE 0x9,0x1
1 11 "ChatEvents_MSG00001.bin"
1 WEVT_TEMPLATE "ChatEventsTEMP.BIN"
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,771 @@
<?xml version='1.0' encoding='utf-8' standalone='yes'?>
<assembly
xmlns="urn:schemas-microsoft-com:asm.v3"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
manifestVersion="1.0"
>
<assemblyIdentity
buildType="$(build.buildType)"
language="neutral"
name="Xbox-GameChat-Events"
processorArchitecture="$(build.arch)"
publicKeyToken="$(Build.WindowsPublicKeyToken)"
version="$(build.version)"
versionScope="nonSxS"
/>
<instrumentation xmlns:win="http://manifests.microsoft.com/win/2004/08/windows/events">
<events xmlns="http://schemas.microsoft.com/win/2004/08/events">
<provider
guid="{2AB79849-74D6-497E-A129-738FEA405207}"
messageFileName="Microsoft.Xbox.GameChat.dll"
name="Xbox GameChat API"
resourceFileName="Microsoft.Xbox.GameChat.dll"
symbol="XboxGameChatEventsProvider"
>
<!-- Channels -->
<channels>
<channel
chid="Debug"
name="Chat/Debug"
type="Debug"
/>
</channels>
<!-- Tasks -->
<tasks>
<task
name="ChatManagerSettingsAudioThread"
value="1101"
/>
<task
name="ChatManagerSettingsAudioEncodingQuality"
value="1102"
/>
<task
name="ChatManagerSettingsJitterBuffer"
value="1103"
/>
<task
name="ChatManagerSettingsMisc"
value="1104"
/>
<task
name="ChatManagerSettingsEffects"
value="1105"
/>
<task
name="ChatHandleNewRemoteConsole"
value="1201"
/>
<task
name="ChatRemoveRemoteConsole"
value="1202"
/>
<task
name="ChatAddLocalUserToChatChannel"
value="1203"
/>
<task
name="ChatRemoveLocalUserFromChatChannel"
value="1204"
/>
<task
name="ChatMuteUserFromAllChannels"
value="1213"
/>
<task
name="ChatUnmuteUserFromAllChannels"
value="1214"
/>
<task
name="ChatMuteAllUsersFromAllChannels"
value="1215"
/>
<task
name="ChatUnmuteAllUsersFromAllChannels"
value="1216"
/>
<task
name="ChatUserInfo"
value="1217"
/>
<task
name="ChatUserAudioDevice"
value="1218"
/>
<task
name="ChatMicFocus"
value="1219"
/>
<task
name="ChatLocalCaptureSource"
value="1220"
/>
<task
name="ChatRemoteCaptureSource"
value="1221"
/>
<task
name="ChatCorrelateAudioDeviceToUser"
value="1222"
/>
<task
name="ChatIncomingChatVoiceDataPacket"
value="1301"
/>
<task
name="ChatIncomingChatUserAddedPacket"
value="1302"
/>
<task
name="ChatIncomingChatUserRemovedPacket"
value="1303"
/>
<task
name="ChatIncomingChatInvalidPacket"
value="1304"
/>
<task
name="ChatOutgoingChatVoiceDataPacket"
value="1401"
/>
<task
name="ChatOutgoingChatUserAddedPacket"
value="1402"
/>
<task
name="ChatOutgoingChatUserRemovedPacket"
value="1403"
/>
</tasks>
<!-- Events -->
<events>
<!-- Level Descriptions -->
<!--
LogAlways(0) - Public API or events that should always fire
Critical(1) - Errors that block chat entirely
Error(2) - Errors that block at least one user from chatting
Warning(3) - Errors and warnings that still allow chat
Informational(4) - Normal flow events that do not fire very often
Verbose(5) - Verbose events, including verbose Public API
-->
<event
channel="Debug"
level="win:Informational"
opcode="win:Info"
symbol="ChatManagerSettingsAudioThread"
task="ChatManagerSettingsAudioThread"
template="ChatManagerSettingsAudioThread"
value="1101"
/>
<event
channel="Debug"
level="win:Informational"
opcode="win:Info"
symbol="ChatManagerSettingsAudioEncodingQuality"
task="ChatManagerSettingsAudioEncodingQuality"
template="ChatManagerSettingsAudioEncodingQuality"
value="1102"
/>
<event
channel="Debug"
level="win:Informational"
opcode="win:Info"
symbol="ChatManagerSettingsJitterBuffer"
task="ChatManagerSettingsJitterBuffer"
template="ChatManagerSettingsJitterBuffer"
value="1103"
/>
<event
channel="Debug"
level="win:Informational"
opcode="win:Info"
symbol="ChatManagerSettingsMisc"
task="ChatManagerSettingsMisc"
template="ChatManagerSettingsMisc"
value="1104"
/>
<event
channel="Debug"
level="win:Informational"
opcode="win:Info"
symbol="ChatManagerSettingsEffects"
task="ChatManagerSettingsEffects"
template="ChatManagerSettingsEffects"
value="1105"
/>
<event
channel="Debug"
level="win:Informational"
opcode="win:Info"
symbol="ChatHandleNewRemoteConsole"
task="ChatHandleNewRemoteConsole"
template="ChatHandleNewRemoteConsole"
value="1201"
/>
<event
channel="Debug"
level="win:Informational"
opcode="win:Info"
symbol="ChatRemoveRemoteConsole"
task="ChatRemoveRemoteConsole"
template="ChatRemoveRemoteConsole"
value="1202"
/>
<event
channel="Debug"
level="win:Informational"
opcode="win:Info"
symbol="ChatAddLocalUserToChatChannel"
task="ChatAddLocalUserToChatChannel"
template="ChatAddLocalUserToChatChannel"
value="1203"
/>
<event
channel="Debug"
level="win:Informational"
opcode="win:Info"
symbol="ChatRemoveLocalUserFromChatChannel"
task="ChatRemoveLocalUserFromChatChannel"
template="ChatRemoveLocalUserFromChatChannel"
value="1204"
/>
<event
channel="Debug"
level="win:Informational"
opcode="win:Info"
symbol="ChatMuteUserFromAllChannels"
task="ChatMuteUserFromAllChannels"
template="ChatMuteUserFromAllChannels"
value="1207"
/>
<event
channel="Debug"
level="win:Informational"
opcode="win:Info"
symbol="ChatUnmuteUserFromAllChannels"
task="ChatUnmuteUserFromAllChannels"
template="ChatUnmuteUserFromAllChannels"
value="1208"
/>
<event
channel="Debug"
level="win:Informational"
opcode="win:Info"
symbol="ChatMuteAllUsersFromAllChannels"
task="ChatMuteAllUsersFromAllChannels"
template="ChatMuteAllUsersFromAllChannels"
value="1209"
/>
<event
channel="Debug"
level="win:Informational"
opcode="win:Info"
symbol="ChatUnmuteAllUsersFromAllChannels"
task="ChatUnmuteAllUsersFromAllChannels"
template="ChatUnmuteAllUsersFromAllChannels"
value="1210"
/>
<event
channel="Debug"
level="win:Informational"
opcode="win:Info"
symbol="ChatUserInfo"
task="ChatUserInfo"
template="ChatUserInfo"
value="1211"
/>
<event
channel="Debug"
level="win:Informational"
opcode="win:Info"
symbol="ChatUserAudioDevice"
task="ChatUserAudioDevice"
template="ChatUserAudioDevice"
value="1212"
/>
<event
channel="Debug"
level="win:Informational"
opcode="win:Info"
symbol="ChatMicFocus"
task="ChatMicFocus"
template="ChatMicFocus"
value="1213"
/>
<event
channel="Debug"
level="win:Informational"
opcode="win:Info"
symbol="ChatLocalCaptureSource"
task="ChatLocalCaptureSource"
template="ChatLocalCaptureSource"
value="1214"
/>
<event
channel="Debug"
level="win:Informational"
opcode="win:Info"
symbol="ChatRemoteCaptureSource"
task="ChatRemoteCaptureSource"
template="ChatRemoteCaptureSource"
value="1215"
/>
<event
channel="Debug"
level="win:Informational"
opcode="win:Info"
symbol="ChatCorrelateAudioDeviceToUser"
task="ChatCorrelateAudioDeviceToUser"
template="ChatCorrelateAudioDeviceToUser"
value="1216"
/>
<event
channel="Debug"
level="win:Verbose"
opcode="win:Info"
symbol="ChatIncomingChatVoiceDataPacket"
task="ChatIncomingChatVoiceDataPacket"
template="ChatIncomingChatVoiceDataPacket"
value="1301"
/>
<event
channel="Debug"
level="win:Informational"
opcode="win:Info"
symbol="ChatIncomingChatUserAddedPacket"
task="ChatIncomingChatUserAddedPacket"
template="ChatIncomingChatUserAddedPacket"
value="1302"
/>
<event
channel="Debug"
level="win:Informational"
opcode="win:Info"
symbol="ChatIncomingChatUserRemovedPacket"
task="ChatIncomingChatUserRemovedPacket"
template="ChatIncomingChatUserRemovedPacket"
value="1303"
/>
<event
channel="Debug"
level="win:Error"
opcode="win:Info"
symbol="ChatIncomingChatInvalidPacket"
task="ChatIncomingChatInvalidPacket"
template="ChatIncomingChatInvalidPacket"
value="1304"
/>
<event
channel="Debug"
level="win:Verbose"
opcode="win:Info"
symbol="ChatOutgoingChatVoiceDataPacket"
task="ChatOutgoingChatVoiceDataPacket"
template="ChatOutgoingChatVoiceDataPacket"
value="1401"
/>
<event
channel="Debug"
level="win:Informational"
opcode="win:Info"
symbol="ChatOutgoingChatUserAddedPacket"
task="ChatOutgoingChatUserAddedPacket"
template="ChatOutgoingChatUserAddedPacket"
value="1402"
/>
<event
channel="Debug"
level="win:Informational"
opcode="win:Info"
symbol="ChatOutgoingChatUserRemovedPacket"
task="ChatOutgoingChatUserRemovedPacket"
template="ChatOutgoingChatUserRemovedPacket"
value="1403"
/>
</events>
<!-- Templates -->
<templates>
<template tid="ChatManagerSettingsAudioThread">
<data
inType="win:UInt32"
name="AudioThreadPeriodInMilliseconds"
/>
<data
inType="win:UInt32"
name="AudioThreadAffinityMask"
/>
<data
inType="win:Int32"
name="AudioThreadPriority"
/>
</template>
<template tid="ChatManagerSettingsAudioEncodingQuality">
<data
inType="win:UInt32"
name="AudioEncodingQuality"
/>
</template>
<template tid="ChatManagerSettingsJitterBuffer">
<data
inType="win:UInt32"
name="JitterBufferMaxPackets"
/>
<data
inType="win:UInt32"
name="JitterBufferLowestNeededPacketCount"
/>
<data
inType="win:UInt32"
name="JitterBufferPacketsBeforeRelaxingNeeded"
/>
</template>
<template tid="ChatManagerSettingsMisc">
<data
inType="win:Boolean"
name="PerformanceCountersEnabled"
/>
<data
inType="win:Boolean"
name="CombineCaptureBuffersIntoSinglePacket"
/>
<data
inType="win:Boolean"
name="UseKinectAsCaptureSource"
/>
<data
inType="win:UInt32"
name="DiagnosticsTraceLevel "
/>
</template>
<template tid="ChatManagerSettingsEffects">
<data
inType="win:Boolean"
name="PreEncodeCallbackEnabled"
/>
<data
inType="win:Boolean"
name="PostDecodeCallbackEnabled"
/>
</template>
<template tid="ChatHandleNewRemoteConsole">
<data
inType="win:UInt32"
name="DiagnosticConsoleId"
/>
</template>
<template tid="ChatRemoveRemoteConsole">
<data
inType="win:UInt32"
name="DiagnosticConsoleId"
/>
</template>
<template tid="ChatAddLocalUserToChatChannel">
<data
inType="win:UnicodeString"
name="XboxUserId"
/>
<data
inType="win:UInt32"
name="ChannelIndex"
/>
</template>
<template tid="ChatUserInfo">
<data
inType="win:UnicodeString"
name="XboxUserId"
/>
<data
inType="win:UInt32"
name="UserId"
/>
<data
inType="win:Boolean"
name="IsGuest"
/>
<data
inType="win:Boolean"
name="IsSignedIn"
/>
<data
inType="win:Boolean"
name="HasSponsor"
/>
<data
inType="win:UInt32"
name="NumControllers"
/>
<data
inType="win:UInt32"
name="NumAudioDevices"
/>
</template>
<template tid="ChatUserAudioDevice">
<data
inType="win:UnicodeString"
name="XboxUserId"
/>
<data
inType="win:UnicodeString"
name="Id"
/>
<data
inType="win:UnicodeString"
name="DeviceCategory"
/>
<data
inType="win:UnicodeString"
name="DeviceType"
/>
<data
inType="win:Boolean"
name="IsMicrophoneMuted"
/>
<data
inType="win:UnicodeString"
name="Sharing"
/>
</template>
<template tid="ChatMicFocus">
<data
inType="win:Boolean"
name="HasMicFocus"
/>
</template>
<template tid="ChatLocalCaptureSource">
<data
inType="win:UnicodeString"
name="XboxUserId"
/>
<data
inType="win:UnicodeString"
name="AudioCaptureSourceId"
/>
<data
inType="win:UInt32"
name="CaptureSourceDeviceId"
/>
<data
inType="win:UnicodeString"
name="TalkingMode"
/>
</template>
<template tid="ChatRemoteCaptureSource">
<data
inType="win:UnicodeString"
name="XboxUserId"
/>
<data
inType="win:UnicodeString"
name="AudioCaptureSourceId"
/>
<data
inType="win:UInt32"
name="LookupId"
/>
<data
inType="win:UInt32"
name="NumberOfChatUsers"
/>
</template>
<template tid="ChatCorrelateAudioDeviceToUser">
<data
inType="win:UnicodeString"
name="AudioCaptureSourceId"
/>
<data
inType="win:UInt32"
name="UserId"
/>
<data
inType="win:Boolean"
name="IsSharedDevice"
/>
<data
inType="win:Boolean"
name="IsRemove"
/>
</template>
<template tid="ChatRemoveLocalUserFromChatChannel">
<data
inType="win:UnicodeString"
name="XboxUserId"
/>
<data
inType="win:UInt32"
name="ChannelIndex"
/>
</template>
<template tid="ChatIncomingChatVoiceDataPacket">
<data
inType="win:UInt32"
name="DiagnosticConsoleId"
/>
<data
inType="win:UnicodeString"
name="ChatPacketBase64"
/>
<data
inType="win:UInt32"
name="ChatPacketLength"
/>
<data
inType="win:UInt32"
name="LocalNameOfRemoteConsole"
/>
<data
inType="win:UInt32"
name="AudioDeviceID"
/>
<data
inType="win:UnicodeString"
name="AudioCaptureSourceId"
/>
</template>
<template tid="ChatIncomingChatUserAddedPacket">
<data
inType="win:UInt32"
name="DiagnosticConsoleId"
/>
<data
inType="win:UnicodeString"
name="ChatPacketBase64"
/>
<data
inType="win:UInt32"
name="ChatPacketLength"
/>
</template>
<template tid="ChatIncomingChatUserRemovedPacket">
<data
inType="win:UInt32"
name="DiagnosticConsoleId"
/>
<data
inType="win:UnicodeString"
name="ChatPacketBase64"
/>
<data
inType="win:UInt32"
name="ChatPacketLength"
/>
</template>
<template tid="ChatIncomingChatInvalidPacket">
<data
inType="win:UInt32"
name="DiagnosticConsoleId"
/>
<data
inType="win:UnicodeString"
name="ChatPacketBase64"
/>
<data
inType="win:UInt32"
name="ChatPacketLength"
/>
</template>
<template tid="ChatOutgoingChatVoiceDataPacket">
<data
inType="win:UnicodeString"
name="ChatUserXboxUserId"
/>
<data
inType="win:UnicodeString"
name="ChatPacketBase64"
/>
<data
inType="win:UInt32"
name="ChatPacketLength"
/>
<data
inType="win:Boolean"
name="SendReliable"
/>
<data
inType="win:Boolean"
name="SendPacketToAllConnectedConsoles"
/>
<data
inType="win:Boolean"
name="SendInOrder"
/>
<data
inType="win:UInt32"
name="AudioDeviceID"
/>
</template>
<template tid="ChatOutgoingChatUserAddedPacket">
<data
inType="win:UnicodeString"
name="ChatUserXboxUserId"
/>
<data
inType="win:UnicodeString"
name="ChatPacketBase64"
/>
<data
inType="win:UInt32"
name="ChatPacketLength"
/>
<data
inType="win:UInt32"
name="DiagnosticConsoleId"
/>
<data
inType="win:Boolean"
name="SendReliable"
/>
<data
inType="win:Boolean"
name="SendPacketToAllConnectedConsoles"
/>
<data
inType="win:Boolean"
name="SendInOrder"
/>
</template>
<template tid="ChatOutgoingChatUserRemovedPacket">
<data
inType="win:UnicodeString"
name="ChatPacketBase64"
/>
<data
inType="win:UInt32"
name="ChatPacketLength"
/>
<data
inType="win:Boolean"
name="SendReliable"
/>
<data
inType="win:Boolean"
name="SendPacketToAllConnectedConsoles"
/>
<data
inType="win:Boolean"
name="SendInOrder"
/>
<data
inType="win:UnicodeString"
name="ChatUserXboxUserId"
/>
</template>
<template tid="ChatMuteUserFromAllChannels">
<data
inType="win:UnicodeString"
name="xboxUserId"
/>
</template>
<template tid="ChatUnmuteUserFromAllChannels">
<data
inType="win:UnicodeString"
name="xboxUserId"
/>
</template>
<template tid="ChatMuteAllUsersFromAllChannels"/>
<template tid="ChatUnmuteAllUsersFromAllChannels"/>
</templates>
</provider>
</events>
</instrumentation>
</assembly>
@@ -0,0 +1,18 @@
@echo off
if "%1"=="" goto help
echo.
@echo on
%SystemRoot%\system32\wevtutil.exe um Microsoft-Xbox-GameChat-Events.man
mkdir c:\temp
copy "%1\Microsoft.Xbox.GameChat.dll" c:\temp
%SystemRoot%\system32\wevtutil.exe im Microsoft-Xbox-GameChat-Events.man /rf:c:\temp\Microsoft.Xbox.GameChat.dll /mf:c:\temp\Microsoft.Xbox.GameChat.dll /pf:c:\temp\Microsoft.Xbox.GameChat.dll
@echo off
goto done
:help
echo.
echo Usage:
echo gamechat-trace-setup.cmd [path to Microsoft.Xbox.GameChat.dll]
echo.
:done
@@ -0,0 +1,4 @@
echo.
echo gamechat-trace-start.cmd [optional address of console]
echo.
xbrun /x %1/title /o tracelog.exe -start gamechat -guid #2AB79849-74D6-497E-A129-738FEA405207 -level 255 -f d:\gamechat.etl
@@ -0,0 +1,29 @@
@echo off
echo.
echo gamechat-trace-stop.cmd [optional address of console]
echo.
echo Using tracelog.exe to stop trace
xbrun /x %1/title /o tracelog.exe -stop gamechat
mkdir c:\temp
xbcp /x %1/title xd:\gamechat.etl c:\temp\gamechat%1.etl
set xperfpath="%ProgramFiles(x86)%\Windows Kits\8.1\Windows Performance Toolkit\xperf.exe"
if NOT EXIST %xperfpath% set xperfpath="%ProgramFiles(x86)%\Windows Kits\8.0\Windows Performance Toolkit\xperf.exe"
if NOT EXIST %xperfpath% goto help
%xperfpath% -symbols verbose -i c:\temp\gamechat%1.etl -o c:\temp\gamechat%1.csv
echo Open in c:\temp\gamechat%1.csv in Excel
start c:\temp\gamechat%1.csv
goto done
:help
echo.
echo Can not find xperf.exe. Install xperf by installing the Windows Performance Toolkit that is found
echo inside the Windows SDK: http://msdn.microsoft.com/en-US/windows/desktop/aa904949.aspx
echo.
:done
+990
View File
@@ -0,0 +1,990 @@
//// 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 "StringUtils.h"
#include "ChatManagerEvents.h"
#include "ChatPacker.h"
#include "ChatUnPacker.h"
#include "AudioDeviceInfo.h"
#include "RemoteChatUser.h"
#include "RemoteAudioDevice.h"
#include "AudioDeviceIDMapper.h"
#include "ChatAudioThread.h"
#include "ChatClient.h"
#include "ChatNetwork.h"
#include "BufferUtils.h"
#include "ChatPacker.h"
#include "ChatManagerEvents.h"
#include "ChatManager.h"
#include "BufferUtils.h"
#if TV_API
using namespace Windows::Xbox::System;
using namespace Windows::Storage::Streams;
namespace Microsoft {
namespace Xbox {
namespace GameChat {
typedef struct MIX_DATA
{
DEVICE_ID captureSourceDeviceId;
Windows::Storage::Streams::IBuffer^ captureBuffer;
} MIX_DATA;
ChatNetwork::ChatNetwork(
_In_ ChatAudioThread^ chatAudioThread,
_In_ ChatClient^ chatClient,
_In_ ChatManager^ chatManager,
_In_ std::shared_ptr<FactoryCache> factoryCache,
_In_ ChatManagerSettings^ chatManagerSettings
) :
m_chatManager( chatManager ),
m_factoryCache( factoryCache ),
m_chatManagerSettings( chatManagerSettings )
{
CHAT_THROW_INVALIDARGUMENT_IF_NULL(chatAudioThread);
CHAT_THROW_INVALIDARGUMENT_IF_NULL(chatClient);
CHAT_THROW_INVALIDARGUMENT_IF_NULL(chatManager);
CHAT_THROW_INVALIDARGUMENT_IF_NULL(factoryCache);
CHAT_THROW_INVALIDARGUMENT_IF_NULL(chatManagerSettings);
m_chatAudioThread = Platform::WeakReference(chatAudioThread);
m_chatClient = Platform::WeakReference(chatClient);
m_audioDeviceIDMapper = ref new AudioDeviceIDMapper();
}
ChatNetwork::~ChatNetwork()
{
CHAT_LOG_INFO_MSG(L"ChatNetwork::~ChatNetwork");
}
void ChatNetwork::LogComment(
_In_ Platform::String^ message
)
{
LogCommentWithError(message, S_OK);
}
void ChatNetwork::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 );
}
}
Microsoft::Xbox::GameChat::ChatMessageType ChatNetwork::ProcessIncomingChatMessage(
_In_ Windows::Storage::Streams::IBuffer^ chatPacket,
_In_ Platform::Object^ uniqueRemoteConsoleIdentifier,
_In_ ChatClient^ chatClient,
_In_ ChatAudioThread^ chatAudioThread,
_In_ ChatManager^ chatManager
)
{
CHAT_THROW_INVALIDARGUMENT_IF_NULL(chatPacket);
CHAT_THROW_INVALIDARGUMENT_IF_NULL(uniqueRemoteConsoleIdentifier);
if ( chatPacket->Length == 0 )
{
return ChatMessageType::InvalidMessage;
}
byte* chatPacketBytes = nullptr;
BufferUtils::GetBufferBytes( chatPacket, &chatPacketBytes );
if ( chatPacket->Length < sizeof(ChatPacketHeader) )
{
// Ignore invalid packets
CHAT_LOG_ERROR_MSG( L"Ignoring invalid packet" );
return ChatMessageType::InvalidMessage;
}
bool performanceCountersEnabled = chatManager->ChatSettings->PerformanceCountersEnabled;
if( performanceCountersEnabled ) { chatManager->ChatPerformanceCounters->QueryIncomingPacketStart(); }
ChatPacketHeader& chatPacketHeader = (ChatPacketHeader&)*chatPacketBytes;
ChatMessageType messageType = static_cast<ChatMessageType>(chatPacketHeader.messageType);
switch (messageType)
{
case ChatMessageType::ChatVoiceDataMessage:
{
byte* dataPacketBufferBytes = chatPacketBytes + sizeof(ChatPacketHeader);
uint32 dataPacketLength = chatPacket->Length - sizeof(ChatPacketHeader);
ProcessIncomingChatVoicePacket(
dataPacketBufferBytes,
dataPacketLength,
uniqueRemoteConsoleIdentifier,
chatClient,
chatAudioThread,
chatManager,
chatPacket
);
break;
}
case ChatMessageType::UserAddedMessage:
{
// Must pass the IBuffer here to keep the reference on the packet buffer
Concurrency::create_async( [this, chatPacket, uniqueRemoteConsoleIdentifier]()
{
byte* chatPacketBytes = nullptr;
BufferUtils::GetBufferBytes( chatPacket, &chatPacketBytes );
byte* dataPacketBufferBytes = chatPacketBytes + sizeof(ChatPacketHeader);
uint32 dataPacketLength = chatPacket->Length - sizeof(ChatPacketHeader);
ProcessIncomingUserAddedPacket(
dataPacketBufferBytes,
dataPacketLength,
uniqueRemoteConsoleIdentifier,
chatPacket
);
});
break;
}
case ChatMessageType::UserRemovedMessage:
{
// Must pass the IBuffer here to keep the reference on the packet buffer
Concurrency::create_async( [this, chatPacket, uniqueRemoteConsoleIdentifier]()
{
byte* chatPacketBytes = nullptr;
BufferUtils::GetBufferBytes( chatPacket, &chatPacketBytes );
byte* dataPacketBufferBytes = chatPacketBytes + sizeof(ChatPacketHeader);
uint32 dataPacketLength = chatPacket->Length - sizeof(ChatPacketHeader);
ProcessIncomingUserRemovedPacket(
dataPacketBufferBytes,
dataPacketLength,
uniqueRemoteConsoleIdentifier,
chatPacket
);
});
break;
}
default:
{
// Ignore invalid packets
CHAT_LOG_ERROR_MSG( L"Ignoring invalid packet type" );
messageType = ChatMessageType::InvalidMessage;
#if WINAPI_FAMILY == WINAPI_FAMILY_TV_TITLE
uint32 diagnosticConsoleId = chatManager->GetChatDiagnostics()->GetDiagnosticNameForConsole( chatManager, uniqueRemoteConsoleIdentifier );
TraceChatIncomingChatInvalidPacket(
diagnosticConsoleId,
BufferUtils::GetBase64String( chatPacket )->Data(),
BufferUtils::GetLength( chatPacket )
);
#endif
break;
}
}
if( performanceCountersEnabled )
{
chatManager->ChatPerformanceCounters->AddPacketBandwidth( true, chatPacket->Length );
chatManager->ChatPerformanceCounters->QueryIncomingPacketDone();
}
return messageType;
}
IBuffer^ ChatNetwork::GetPacketWithHeader(
_In_ uint32 packetSize,
_In_ uint8 messageType
)
{
CHAT_THROW_INVALIDARGUMENT_IF(packetSize < sizeof(ChatPacketHeader));
IBuffer^ networkPacketBuffer = BufferUtils::FastBufferCreate( packetSize, m_factoryCache->GetBufferFactory() );
networkPacketBuffer->Length = packetSize;
byte* messageBufferPtr = nullptr;
BufferUtils::GetBufferBytes( networkPacketBuffer, &messageBufferPtr );
// Fill out ChatPacketHeader
ChatPacketHeader& packet = (ChatPacketHeader&)*messageBufferPtr;
packet.messageType = messageType;
packet.messageSize = (uint16)(packetSize);
return networkPacketBuffer;
}
void ChatNetwork::CreateChatVoicePackets(
_In_ std::shared_ptr< CHAT_AUDIO_THREAD_STATE > chatAudioThreadState
)
{
ChatAudioThread^ chatAudioThread = m_chatAudioThread.Resolve<ChatAudioThread>();
ChatManager^ chatManager = m_chatManager.Resolve<ChatManager>();
if( chatAudioThread == nullptr ||
chatManager == nullptr )
{
return;
}
if( chatManager->ChatSettings->CombineCaptureBuffersIntoSinglePacket )
{
CreateCombinedChatVoicePacket(chatAudioThreadState);
}
else
{
CreateChatVoicePacketsForEachLocalCaptureSource(chatAudioThreadState);
}
}
void ChatNetwork::CreateCombinedChatVoicePacket(
_In_ std::shared_ptr< CHAT_AUDIO_THREAD_STATE > chatAudioThreadState
)
{
ChatManager^ chatManager = m_chatManager.Resolve<ChatManager>();
if( chatManager == nullptr )
{
return;
}
std::vector< MIX_DATA > captureBuffers;
DWORD chatBufferSize = 0;
errno_t err;
// Collect the capture source buffers and count how
// large the network packet will need to be
for each (std::shared_ptr<CHAT_AUDIO_THREAD_CAPTURE_SOURCE> chatAudioThreadCaptureSource in chatAudioThreadState->captureSources)
{
for (;;)
{
IBuffer^ captureBuffer = nullptr;
if ( chatAudioThreadCaptureSource->audioBufferQueue.size() > 0 )
{
captureBuffer = chatAudioThreadCaptureSource->audioBufferQueue.front();
chatAudioThreadCaptureSource->audioBufferQueue.pop();
}
if ( captureBuffer == nullptr )
{
break;
}
MIX_DATA md;
md.captureSourceDeviceId = chatAudioThreadCaptureSource->captureSourceDeviceId;
md.captureBuffer = captureBuffer;
captureBuffers.push_back(md);
// each chat message will contain a DEVICE ID that's local to this console. When combined with the console name will create the LOOKUP_ID
chatBufferSize += (DWORD) sizeof(DEVICE_ID);
chatBufferSize += (DWORD) sizeof(USHORT);
chatBufferSize += captureBuffer->Length;
}
}
if (chatBufferSize == 0)
{
return;
}
if( chatBufferSize > USHRT_MAX )
{
// The packet uses an unsigned short to sort the length of this buffer, so USHRT_MAX is max size.
// Normally its less than 100 bytes so this should be fine.
CHAT_LOG_ERROR_MSG( L"Chat buffer too big" );
return;
}
// Allocate a network packet buffer and pack all the chat packets into it.
UINT packetSize = sizeof(ChatPacketHeader) + chatBufferSize;
Windows::Storage::Streams::IBuffer^ packetBuffer = GetPacketWithHeader(packetSize, (uint8)ChatMessageType::ChatVoiceDataMessage );
byte* packetBufferBytes = nullptr;
BufferUtils::GetBufferBytes( packetBuffer, &packetBufferBytes );
byte* networkPacketBufferBytes = packetBufferBytes + sizeof(ChatPacketHeader);
DWORD len = 0;
for( auto it = captureBuffers.begin(); it != captureBuffers.end(); it++ )
{
// The network packet contains an lookup id, which we use to find the string that we should use
networkPacketBufferBytes[len] = it->captureSourceDeviceId;
len += sizeof(DEVICE_ID);
// serialize the buffer
byte* captureBufferBytes = nullptr;
BufferUtils::GetBufferBytes( it->captureBuffer, &captureBufferBytes );
unsigned short captureBufferSize = static_cast<unsigned short>(it->captureBuffer->Length);
err = memcpy_s( &networkPacketBufferBytes[len], packetBuffer->Capacity-len, &captureBufferSize, sizeof(captureBufferSize));
CHAT_THROW_HR_IF(err != 0, E_FAIL);
len += sizeof(captureBufferSize);
err = memcpy_s( &networkPacketBufferBytes[len], packetBuffer->Capacity-len, captureBufferBytes, captureBufferSize);
CHAT_THROW_HR_IF(err != 0, E_FAIL);
len += captureBufferSize;
}
CHAT_THROW_HR_IF(len != chatBufferSize, E_FAIL);
bool sendPacketToAllConnectedConsoles = true;
bool sendReliable = false;
bool sendInOrder = true;
ChatPacketEventArgs^ args = ref new ChatPacketEventArgs(
packetBuffer,
nullptr,
sendPacketToAllConnectedConsoles,
sendReliable,
sendInOrder,
ChatMessageType::ChatVoiceDataMessage,
nullptr
);
chatManager->OnChatPacketReadyHandler( args );
#if WINAPI_FAMILY == WINAPI_FAMILY_TV_TITLE
TraceChatOutgoingChatVoiceDataPacket(
L"n/a", // Local user
BufferUtils::GetBase64String( packetBuffer )->Data(),
BufferUtils::GetLength( packetBuffer ),
sendReliable,
sendPacketToAllConnectedConsoles,
sendInOrder,
captureBuffers.size() > 0 ? captureBuffers[0].captureSourceDeviceId : 0
);
#endif
}
void ChatNetwork::CreateChatVoicePacketsForEachLocalCaptureSource(
_In_ std::shared_ptr< CHAT_AUDIO_THREAD_STATE > chatAudioThreadState
)
{
ChatManager^ chatManager = m_chatManager.Resolve<ChatManager>();
if( chatManager == nullptr )
{
return;
}
errno_t err;
// Collect the capture source buffers and count how
// large the network packet will need to be
for each (std::shared_ptr<CHAT_AUDIO_THREAD_CAPTURE_SOURCE> chatAudioThreadCaptureSource in chatAudioThreadState->captureSources)
{
for (;;)
{
IBuffer^ captureBuffer = nullptr;
if ( chatAudioThreadCaptureSource->audioBufferQueue.size() > 0 )
{
captureBuffer = chatAudioThreadCaptureSource->audioBufferQueue.front();
chatAudioThreadCaptureSource->audioBufferQueue.pop();
}
if ( captureBuffer == nullptr )
{
break;
}
MIX_DATA md;
md.captureSourceDeviceId = chatAudioThreadCaptureSource->captureSourceDeviceId;
md.captureBuffer = captureBuffer;
// each chat message will contain a DEVICE ID that's local to this console. When combined with the console name will create the LOOKUP_ID
DWORD chatBufferSize = (DWORD) sizeof(DEVICE_ID);
chatBufferSize += (DWORD) sizeof(USHORT);
chatBufferSize += captureBuffer->Length;
if (chatBufferSize == 0)
{
continue;
}
if( chatBufferSize > USHRT_MAX )
{
// The packet uses an unsigned short to sort the length of this buffer, so USHRT_MAX is max size.
// Normally its less than 100 bytes so this should be fine.
CHAT_LOG_ERROR_MSG( L"Chat buffer too big" );
continue;
}
// Allocate a network packet buffer and pack all the chat packets into it.
UINT packetSize = sizeof(ChatPacketHeader) + chatBufferSize;
Windows::Storage::Streams::IBuffer^ packetBuffer = GetPacketWithHeader(packetSize, (uint8)ChatMessageType::ChatVoiceDataMessage );
byte* packetBufferBytes = nullptr;
BufferUtils::GetBufferBytes( packetBuffer, &packetBufferBytes );
byte* networkPacketBufferBytes = packetBufferBytes + sizeof(ChatPacketHeader);
DWORD len = 0;
// The network packet contains an lookup id, which we use to find the string that we should use
networkPacketBufferBytes[len] = md.captureSourceDeviceId;
len += sizeof(DEVICE_ID);
// serialize the buffer
byte* captureBufferBytes = nullptr;
BufferUtils::GetBufferBytes( md.captureBuffer, &captureBufferBytes );
unsigned short captureBufferSize = static_cast<unsigned short>(md.captureBuffer->Length);
err = memcpy_s( &networkPacketBufferBytes[len], packetBuffer->Capacity-len, &captureBufferSize, sizeof(captureBufferSize));
CHAT_THROW_HR_IF(err != 0, E_FAIL);
len += sizeof(captureBufferSize);
err = memcpy_s( &networkPacketBufferBytes[len], packetBuffer->Capacity-len, captureBufferBytes, captureBufferSize);
CHAT_THROW_HR_IF(err != 0, E_FAIL);
len += captureBufferSize;
CHAT_THROW_HR_IF(len != chatBufferSize, E_FAIL);
ChatUser^ localChatUser = nullptr;
ChatUserTalkingMode talkingMode = chatAudioThreadCaptureSource->talkingMode;
if (talkingMode == ChatUserTalkingMode::TalkingOverHeadset &&
chatAudioThreadCaptureSource->chatUsers->Size == 1)
{
localChatUser = chatAudioThreadCaptureSource->chatUsers->GetAt(0);
}
bool sendPacketToAllConnectedConsoles = true;
bool sendReliable = false;
bool sendInOrder = true;
ChatPacketEventArgs^ args = ref new ChatPacketEventArgs(
packetBuffer,
nullptr,
sendPacketToAllConnectedConsoles,
sendReliable,
sendInOrder,
ChatMessageType::ChatVoiceDataMessage,
localChatUser
);
chatManager->OnChatPacketReadyHandler( args );
#if WINAPI_FAMILY == WINAPI_FAMILY_TV_TITLE
TraceChatOutgoingChatVoiceDataPacket(
localChatUser != nullptr ? localChatUser->XboxUserId->Data() : L"n/a",
BufferUtils::GetBase64String( packetBuffer )->Data(),
BufferUtils::GetLength( packetBuffer ),
sendReliable,
sendPacketToAllConnectedConsoles,
sendInOrder,
md.captureSourceDeviceId
);
#endif
}
}
}
void ChatNetwork::ProcessIncomingChatVoicePacket(
_In_reads_bytes_(chatVoicePacketLength) byte* chatVoicePacketBytes,
_In_ uint32 chatVoicePacketLength,
_In_ Platform::Object^ uniqueRemoteConsoleIdentifier,
_In_ ChatClient^ chatClient,
_In_ ChatAudioThread^ chatAudioThread,
_In_ ChatManager^ chatManager,
_In_ Windows::Storage::Streams::IBuffer^ chatPacket
)
{
CHAT_THROW_INVALIDARGUMENT_IF_NULL(chatVoicePacketBytes);
CHAT_THROW_INVALIDARGUMENT_IF_NULL(uniqueRemoteConsoleIdentifier);
DWORD offset = 0;
CONSOLE_NAME localNameOfRemoteConsole;
localNameOfRemoteConsole = chatClient->GetLocalNameOfRemoteConsole(uniqueRemoteConsoleIdentifier);
while( offset < chatVoicePacketLength )
{
// Deserialize the lookup id
DEVICE_ID deviceID;
if( chatVoicePacketLength < offset + sizeof(deviceID) )
{
CHAT_LOG_ERROR_MSG( L"Ignoring invalid voice packet" );
return;
}
deviceID = chatVoicePacketBytes[offset];
offset += sizeof(deviceID);
LOOKUP_ID lookupId = m_audioDeviceIDMapper->ConstructLookupID(localNameOfRemoteConsole, deviceID);
// Deserialize the audio data
// part 1: size of buffer in unsigned short
if( chatVoicePacketLength < offset + sizeof(unsigned short) )
{
CHAT_LOG_ERROR_MSG( L"Ignoring invalid voice packet" );
return;
}
unsigned short audioBufferSize = *reinterpret_cast<unsigned short *>(&chatVoicePacketBytes[offset]);
offset += sizeof(unsigned short);
// Deserialize the audio data
// part 2: audio data byte array
if( chatVoicePacketLength < offset + audioBufferSize )
{
CHAT_LOG_ERROR_MSG( L"Ignoring invalid voice packet" );
return;
}
Platform::String^ captureSourceId = m_audioDeviceIDMapper->GetRemoteAudioDevice(lookupId);
if( !captureSourceId->IsEmpty() ) // Ignore audio devices that have not been remembered during a previous USER_ADDED packet
{
// Hand off the audio buffer to the audio thread's render queue
chatAudioThread->PushRemoteCaptureAudioBuffer(
lookupId,
captureSourceId,
&chatVoicePacketBytes[offset],
audioBufferSize
);
}
#if WINAPI_FAMILY == WINAPI_FAMILY_TV_TITLE
uint32 diagnosticConsoleId = chatManager->GetChatDiagnostics()->GetDiagnosticNameForConsole( chatManager, uniqueRemoteConsoleIdentifier );
TraceChatIncomingChatVoiceDataPacket(
diagnosticConsoleId,
BufferUtils::GetBase64String( chatPacket )->Data(),
BufferUtils::GetLength( chatPacket ),
localNameOfRemoteConsole,
deviceID,
captureSourceId->Data()
);
#endif
offset += audioBufferSize;
}
}
void ChatNetwork::CreateChatUserPacket(
_In_ uint8 channelIndex,
_In_ ChatUser^ localChatUser,
_In_ Platform::Object^ uniqueTargetConsoleIdentifier
)
{
CHAT_THROW_INVALIDARGUMENT_IF_NULL(localChatUser);
ChatManager^ chatManager = m_chatManager.Resolve<ChatManager>();
ChatClient^ chatClient = m_chatClient.Resolve<ChatClient>();
if( chatManager == nullptr || chatClient == nullptr )
{
return;
}
Mwrl::ComPtr<Awxs::IUser> currentUser = reinterpret_cast<ABI::Windows::Xbox::System::IUser*>(localChatUser->User);
BYTE* userBufferBytes = nullptr;
UINT userBufferSize = 0;
// Packing the user to send across the network into a set of bytes
Packer packer;
HRESULT hr = packer.PackBegin();
if( FAILED(hr) )
{
// Ignoring invalid chat user packet
return;
}
hr = packer.PackUser( currentUser, m_audioDeviceIDMapper );
if( FAILED(hr) )
{
// Ignoring invalid chat user packet
return;
}
hr = packer.PackEnd( &userBufferBytes, &userBufferSize );
if( FAILED(hr) )
{
// Ignoring invalid chat user packet
return;
}
bool hasAddedRemoteUserToLocalChatSession = false;
// Check if the uniqueTargetConsoleIdentifier already exists in our chat session.
if(chatClient->DoesRemoteUniqueConsoleIdentifierExist(uniqueTargetConsoleIdentifier))
{
hasAddedRemoteUserToLocalChatSession = true;
}
UINT packetSize = sizeof(ChatPacketHeader) + userBufferSize + sizeof(channelIndex) + sizeof(hasAddedRemoteUserToLocalChatSession);
Windows::Storage::Streams::IBuffer^ packetBuffer = GetPacketWithHeader(packetSize, (uint8)ChatMessageType::UserAddedMessage );
byte* packetBufferBytes = nullptr;
BufferUtils::GetBufferBytes( packetBuffer, &packetBufferBytes );
// Fill out a userBufferBytes, which appears after the ChatPacketHeader
BYTE* userBufferBytesPtr = packetBufferBytes + sizeof(ChatPacketHeader);
errno_t err = memcpy_s(userBufferBytesPtr, packetSize - sizeof(ChatPacketHeader), userBufferBytes, userBufferSize);
CHAT_THROW_HR_IF(err != 0, E_FAIL);
// Serialize a channelIndex, which appears after the ChatPacketHeader and the userBuffer
BYTE* channelIndexPtr = packetBufferBytes + sizeof(ChatPacketHeader) + userBufferSize;
err = memcpy_s(channelIndexPtr, packetSize - sizeof(ChatPacketHeader) - userBufferSize , &channelIndex, sizeof(channelIndex));
CHAT_THROW_HR_IF(err != 0, E_FAIL);
// Serialize the hasAddedRemoteUserToLocalChatSession flag, which appears after the ChatPacketHeader, the userBuffer and channelIndex
BYTE* hasAddedRemoteUserToLocalChatSessionPtr = packetBufferBytes + sizeof(ChatPacketHeader) + userBufferSize + sizeof(channelIndex);
err = memcpy_s(hasAddedRemoteUserToLocalChatSessionPtr,
packetSize - sizeof(ChatPacketHeader) - userBufferSize - sizeof(channelIndex),
&hasAddedRemoteUserToLocalChatSession,
sizeof(hasAddedRemoteUserToLocalChatSession)
);
CHAT_THROW_HR_IF(err != 0, E_FAIL);
bool sendPacketToAllConnectedConsoles = false;
bool sendReliable = true;
bool sendInOrder = false;
#if WINAPI_FAMILY == WINAPI_FAMILY_TV_TITLE
uint32 diagnosticConsoleId = chatManager->GetChatDiagnostics()->GetDiagnosticNameForConsole( chatManager, uniqueTargetConsoleIdentifier );
TraceChatOutgoingChatUserAddedPacket(
localChatUser != nullptr ? localChatUser->XboxUserId->Data() : L"n/a",
BufferUtils::GetBase64String( packetBuffer )->Data(),
BufferUtils::GetLength( packetBuffer ),
diagnosticConsoleId,
sendReliable,
sendPacketToAllConnectedConsoles,
sendInOrder
);
#endif
ChatPacketEventArgs^ args = ref new ChatPacketEventArgs(
packetBuffer,
uniqueTargetConsoleIdentifier,
sendPacketToAllConnectedConsoles,
sendReliable,
sendInOrder,
ChatMessageType::UserAddedMessage,
localChatUser
);
chatManager->OnChatPacketReadyHandler( args );
}
void ChatNetwork::CreateChatUserRemovedPacket(
_In_ uint8 channelIndex,
_In_ ChatUser^ localChatUser
)
{
CHAT_THROW_INVALIDARGUMENT_IF_NULL(localChatUser);
ChatManager^ chatManager = m_chatManager.Resolve<ChatManager>();
if( chatManager == nullptr )
{
return;
}
Platform::String^ xuid = localChatUser->XboxUserId;
UINT xuidSizeInChars = xuid->Length();
UINT xuidSizeInBytes = xuidSizeInChars * 2;
UINT packetSize = sizeof(ChatPacketHeader) + xuidSizeInBytes + sizeof(channelIndex);
Windows::Storage::Streams::IBuffer^ packetBuffer = GetPacketWithHeader(packetSize, (uint8)ChatMessageType::UserRemovedMessage );
byte* packetBufferBytes = nullptr;
BufferUtils::GetBufferBytes( packetBuffer, &packetBufferBytes );
// Serialize a channelIndex, which appears after the ChatPacketHeader
BYTE* channelIndexPtr = packetBufferBytes + sizeof(ChatPacketHeader);
errno_t err = memcpy_s(channelIndexPtr, packetSize - sizeof(ChatPacketHeader), &channelIndex, sizeof(channelIndex));
CHAT_THROW_HR_IF(err != 0, E_FAIL);
// Serialize the xuid, which appears after the ChatPacketHeader and the channelIndex
BYTE* xuidPtr = packetBufferBytes + sizeof(ChatPacketHeader) + sizeof(channelIndex);
err = memcpy_s(xuidPtr, packetSize - sizeof(ChatPacketHeader) - sizeof(channelIndex), xuid->Data(), xuidSizeInBytes);
CHAT_THROW_HR_IF(err != 0, E_FAIL);
bool sendPacketToAllConnectedConsoles = true;
bool sendReliable = true;
bool sendInOrder = false;
#if WINAPI_FAMILY == WINAPI_FAMILY_TV_TITLE
TraceChatOutgoingChatUserRemovedPacket(
BufferUtils::GetBase64String( packetBuffer )->Data(),
BufferUtils::GetLength( packetBuffer ),
sendReliable,
sendPacketToAllConnectedConsoles,
sendInOrder,
xuid->Data()
);
#endif
ChatPacketEventArgs^ args = ref new ChatPacketEventArgs(
packetBuffer,
nullptr,
sendPacketToAllConnectedConsoles,
sendReliable,
sendInOrder,
ChatMessageType::UserRemovedMessage,
localChatUser
);
chatManager->OnChatPacketReadyHandler( args );
}
void ChatNetwork::ProcessIncomingUserAddedPacket(
_In_reads_bytes_(chatUserPacketLength) byte* chatUserPacketBytes,
_In_ uint32 chatUserPacketLength,
_In_ Platform::Object^ uniqueRemoteConsoleIdentifier,
_In_ Windows::Storage::Streams::IBuffer^ chatPacket
)
{
CHAT_THROW_INVALIDARGUMENT_IF_NULL(chatUserPacketBytes);
CHAT_THROW_INVALIDARGUMENT_IF_NULL(uniqueRemoteConsoleIdentifier);
Concurrency::critical_section::scoped_lock lock(m_userAddRemoveLock);
ChatClient^ chatClient = m_chatClient.Resolve<ChatClient>();
ChatManager^ chatManager = m_chatManager.Resolve<ChatManager>();
if ( chatClient == nullptr || chatManager == nullptr )
{
return;
}
uint8 localNameOfRemoteConsole = chatClient->GetLocalNameOfRemoteConsole(uniqueRemoteConsoleIdentifier);
Mwrl::ComPtr<Awxs::IUser> user = nullptr;
Unpacker unpacker;
HRESULT hr;
// This packet contains a set of bytes that represent a remote user.
// Convert those bytes into an IUser
// uint8 is for the channelIndex; bool is for the hasAddedRemoteUserToLocalChatSession flag
hr = unpacker.UnpackBegin( chatUserPacketBytes, chatUserPacketLength - sizeof(uint8) - sizeof(bool));
if( FAILED(hr) )
{
// Ignoring invalid chat user packet
return;
}
hr = unpacker.UnpackUser( user, m_audioDeviceIDMapper, localNameOfRemoteConsole );
if( FAILED(hr) )
{
// Ignoring invalid chat user packet
return;
}
hr = unpacker.UnpackEnd();
if( FAILED(hr) )
{
// Ignoring invalid chat user packet
return;
}
bool hasAddedRemoteUserToLocalChatSession = *reinterpret_cast<const bool *>(&chatUserPacketBytes[chatUserPacketLength-sizeof(bool)]);
uint8 channelIndex = *reinterpret_cast<const uint8 *>(&chatUserPacketBytes[chatUserPacketLength-sizeof(uint8)-sizeof(bool)]);
Platform::String^ chatUserXuid;
HSTRING userXuid;
unsigned int userId;
boolean isGuestBoolean;
hr = user->get_XboxUserId( &userXuid );
if( FAILED(hr) )
{
// Ignoring invalid chat user packet
return;
}
chatUserXuid = ref new Platform::String( userXuid );
WindowsDeleteString( userXuid );
userXuid = nullptr;
if( chatUserXuid->IsEmpty() )
{
// Ignoring invalid chat user packet
return;
}
hr = user->get_Id( &userId );
if( FAILED(hr) )
{
// Ignoring invalid chat user packet
return;
}
hr = user->get_IsGuest( &isGuestBoolean );
if( FAILED(hr) )
{
// Ignoring invalid chat user packet
return;
}
bool isGuest = (isGuestBoolean != 0);
// Create a set of audio devices that this remote user will contain
Windows::Foundation::Collections::IVector< Wxs::IAudioDeviceInfo^ >^ audioDevices = ref new Platform::Collections::Vector< Wxs::IAudioDeviceInfo^ >();
Microsoft::WRL::ComPtr< ABI::Windows::Foundation::Collections::IVectorView<ABI::Windows::Xbox::System::IAudioDeviceInfo*> > devices;
hr = user->get_AudioDevices(&devices);
if( FAILED(hr) )
{
// Ignoring invalid chat user packet
return;
}
UINT size = 0;
hr = devices->get_Size(&size);
if( FAILED(hr) || size == 0 )
{
// Ignoring invalid chat user packet
return;
}
for (UINT i = 0; i < size; i++)
{
ABI::Windows::Xbox::System::IAudioDeviceInfo* currentAudioDeviceInfo = nullptr;
ABI::Windows::Xbox::System::AudioDeviceCategory audioDeviceCategory = ABI::Windows::Xbox::System::AudioDeviceCategory::AudioDeviceCategory_Communications;
ABI::Windows::Xbox::System::AudioDeviceType audioDeviceType = ABI::Windows::Xbox::System::AudioDeviceType::AudioDeviceType_Capture;
ABI::Windows::Xbox::System::AudioDeviceSharing audioDeviceSharing = ABI::Windows::Xbox::System::AudioDeviceSharing::AudioDeviceSharing_Exclusive;
HSTRING audioDeviceInfoID = nullptr;
boolean audioDeviceInfoIsMicMuted = false;
hr = devices->GetAt(i, &currentAudioDeviceInfo);
if( FAILED(hr) )
{
// Ignoring invalid chat user packet
return;
}
hr = currentAudioDeviceInfo->get_DeviceCategory(&audioDeviceCategory);
if( FAILED(hr) )
{
// Ignoring invalid chat user packet
return;
}
hr = currentAudioDeviceInfo->get_DeviceType(&audioDeviceType);
if( FAILED(hr) )
{
// Ignoring invalid chat user packet
return;
}
hr = currentAudioDeviceInfo->get_Id(&audioDeviceInfoID);
if( FAILED(hr) )
{
// Ignoring invalid chat user packet
return;
}
// Cleanup the HSTRING
Platform::String^ strAudioDeviceInfoID = ref new Platform::String( audioDeviceInfoID );
WindowsDeleteString( audioDeviceInfoID );
audioDeviceInfoID = nullptr;
hr = currentAudioDeviceInfo->get_IsMicrophoneMuted(&audioDeviceInfoIsMicMuted);
if ( hr == E_NOTIMPL )
{
audioDeviceInfoIsMicMuted = false;
}
else if ( FAILED(hr) )
{
// Ignoring invalid chat user packet
return;
}
hr = currentAudioDeviceInfo->get_Sharing(&audioDeviceSharing);
if( FAILED(hr) )
{
// Ignoring invalid chat user packet
return;
}
auto dInfo = ref new RemoteAudioDeviceInfo(
static_cast<Windows::Xbox::System::AudioDeviceCategory>(audioDeviceCategory),
static_cast<Windows::Xbox::System::AudioDeviceType>(audioDeviceType),
strAudioDeviceInfoID,
audioDeviceInfoIsMicMuted ? true : false,
(Windows::Xbox::System::AudioDeviceSharing)audioDeviceSharing);
audioDevices->Append( dInfo);
}
IUser^ remoteUser = nullptr;
// Users can be added to multiple channels so check if we already know about this one
Windows::Foundation::Collections::IVectorView<Microsoft::Xbox::GameChat::ChatUser^>^ chatUsers = chatManager->GetChatUsers();
for each (Microsoft::Xbox::GameChat::ChatUser^ chatUser in chatUsers)
{
if (chatUser != nullptr && StringUtils::IsStringEqualCaseInsenstive(chatUser->XboxUserId, chatUserXuid) )
{
remoteUser = chatUser->User;
}
}
if (remoteUser == nullptr)
{
remoteUser = ref new RemoteChatUser( chatUserXuid, isGuest, audioDevices->GetView() );
}
else
{
auto existingUser = dynamic_cast<RemoteChatUser^>(remoteUser);
if (existingUser != nullptr)
{
existingUser->ResetAudioDevices( audioDevices->GetView() );
remoteUser = existingUser;
}
}
#if WINAPI_FAMILY == WINAPI_FAMILY_TV_TITLE
uint32 diagnosticConsoleId = chatManager->GetChatDiagnostics()->GetDiagnosticNameForConsole( chatManager, uniqueRemoteConsoleIdentifier );
TraceChatIncomingChatUserAddedPacket(
diagnosticConsoleId,
BufferUtils::GetBase64String( chatPacket )->Data(),
chatUserPacketLength
);
chatManager->GetChatDiagnostics()->TraceChatUserAndAudioDevices( remoteUser );
#endif
chatManager->OnRemoteUserReadyToAddHandler( channelIndex, remoteUser, uniqueRemoteConsoleIdentifier, hasAddedRemoteUserToLocalChatSession );
}
void ChatNetwork::ProcessIncomingUserRemovedPacket(
_In_reads_bytes_(chatUserRemovedPacketLength) byte* chatUserRemovedPacketBytes,
_In_ uint32 chatUserRemovedPacketLength,
_In_ Platform::Object^ uniqueRemoteConsoleIdentifier,
_In_ Windows::Storage::Streams::IBuffer^ chatPacket
)
{
Concurrency::critical_section::scoped_lock lock(m_userAddRemoveLock);
ChatManager^ chatManager = m_chatManager.Resolve<ChatManager>();
if( chatManager == nullptr )
{
return;
}
if( chatUserRemovedPacketLength < sizeof(uint8) )
{
CHAT_LOG_ERROR_MSG( L"Ignoring invalid user removed packet" );
return;
}
uint32 offset = 0;
uint8 channelIndex = *reinterpret_cast<const uint8 *>(&chatUserRemovedPacketBytes[offset]);
offset += sizeof(uint8);
// Deserialize the user xuid.
uint32 bytesRemaining = chatUserRemovedPacketLength - offset;
if( bytesRemaining == 0 || bytesRemaining > 25*sizeof(WCHAR) ) // 20 chars is max length for max XUID in decimal string form
{
CHAT_LOG_ERROR_MSG( L"Ignoring invalid user removed packet" );
return;
}
BYTE* userXuidPtr = chatUserRemovedPacketBytes + offset;
uint32 sizeOfXboxUserIdInChars = bytesRemaining / sizeof(WCHAR);
Platform::String^ userXuid = ref new Platform::String((WCHAR*)userXuidPtr, sizeOfXboxUserIdInChars);
#if WINAPI_FAMILY == WINAPI_FAMILY_TV_TITLE
uint32 diagnosticConsoleId = chatManager->GetChatDiagnostics()->GetDiagnosticNameForConsole( chatManager, uniqueRemoteConsoleIdentifier );
TraceChatIncomingChatUserRemovedPacket(
diagnosticConsoleId,
BufferUtils::GetBase64String( chatPacket )->Data(),
chatUserRemovedPacketLength
);
#endif
chatManager->OnRemoteUserReadyToRemoveHandler( channelIndex, userXuid );
}
AudioDeviceIDMapper^ ChatNetwork::GetAudioDeviceIDMapper()
{
return m_audioDeviceIDMapper;
}
}}}
#endif
+184
View File
@@ -0,0 +1,184 @@
//// 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 "FactoryCache.h"
#if TV_API
namespace Microsoft {
namespace Xbox {
namespace GameChat {
// Set data alignment to be 1 byte
#pragma pack(push)
#pragma pack(1)
struct ChatPacketHeader
{
/// <summary>
/// Type of message (MessageTypeEnum)
/// </summary>
uint8 messageType;
/// <summary>
/// Total number of bytes in the packet
/// </summary>
uint16 messageSize;
};
// Store data alignment
#pragma pack(pop)
ref class ChatNetwork sealed
{
internal:
/// <summary>
/// Creates internal ChatNetwork class. This class handles the chat network packets and talks to both the ChatClient and ChatAudioThread components.
/// </summary>
ChatNetwork(
_In_ ChatAudioThread^ chatAudioThread,
_In_ ChatClient^ chatClient,
_In_ ChatManager^ chatManager,
_In_ std::shared_ptr<FactoryCache> factoryCache,
_In_ ChatManagerSettings^ chatManagerSettings
);
public:
/// <summary>
/// Shuts down the ChatNetwork
/// </summary>
virtual ~ChatNetwork();
internal:
/// <summary>
/// Handles incoming chat messages
/// </summary>
Microsoft::Xbox::GameChat::ChatMessageType ProcessIncomingChatMessage(
_In_ Windows::Storage::Streams::IBuffer^ chatPacket,
_In_ Platform::Object^ uniqueRemoteConsoleIdentifier,
_In_ ChatClient^ chatClient,
_In_ ChatAudioThread^ chatAudioThread,
_In_ ChatManager^ chatManager
);
/// <summary>
/// Creates and raises events to send out chat voice packets.
/// </summary>
void CreateChatVoicePackets(
_In_ std::shared_ptr< CHAT_AUDIO_THREAD_STATE > chatAudioThreadState
);
/// <summary>
/// Creates and raises events to send out chat voice packets that are specific to a remote console
/// This is called from CreateChatVoicePackets when chatManager->ChatSettings->CombineCaptureBuffersIntoSinglePacket is true.
/// </summary>
void CreateCombinedChatVoicePacket(
_In_ std::shared_ptr< CHAT_AUDIO_THREAD_STATE > chatAudioThreadState
);
/// <summary>
/// Creates and raises events to send out chat voice packets that are generic for all remote consoles
/// This is called from CreateChatVoicePackets when chatManager->ChatSettings->CombineCaptureBuffersIntoSinglePacket is false.
/// </summary>
void CreateChatVoicePacketsForEachLocalCaptureSource(
_In_ std::shared_ptr< CHAT_AUDIO_THREAD_STATE > chatAudioThreadState
);
/// <summary>
/// This is called when a remote user packet needs to be sent out.
/// It creates a chat user packet and triggers the OnChatPacketReady event with it
/// </summary>
void CreateChatUserPacket(
_In_ uint8 channelIndex,
_In_ ChatUser^ localChatUser,
_In_ Platform::Object^ uniqueTargetConsoleIdentifier
);
/// <summary>
/// This is called when a remote user is removed.
/// It creates a chat user packet and triggers the OnChatPacketReady event with it
/// </summary>
void ChatNetwork::CreateChatUserRemovedPacket(
_In_ uint8 channelIndex,
_In_ ChatUser^ localChatUser
);
AudioDeviceIDMapper^ GetAudioDeviceIDMapper();
private:
void LogComment(
_In_ Platform::String^ message
);
void LogCommentWithError(
_In_ Platform::String^ message,
_In_ HRESULT hr
);
/// <summary>
/// This is an internal help function to create a Buffer with a specific size
/// and ChatMessageTypeEnum message type.
/// </summary>
Windows::Storage::Streams::IBuffer^ GetPacketWithHeader(
_In_ uint32 packetSize,
_In_ uint8 messageType
);
/// <summary>
/// This is called when a remote console sends a chat voice packet.
/// It is deserialized here before and is pushed to the ChatAudioThread render buffer queue
/// later playback in ChatAudioThread::RenderAudioToAllRenderTargets()
/// </summary>
void ProcessIncomingChatVoicePacket(
_In_reads_bytes_(chatVoicePacketLength) byte* chatVoicePacketBytes,
_In_ uint32 chatVoicePacketLength,
_In_ Platform::Object^ uniqueRemoteConsoleIdentifier,
_In_ ChatClient^ chatClient,
_In_ ChatAudioThread^ chatAudioThread,
_In_ ChatManager^ chatManager,
_In_ Windows::Storage::Streams::IBuffer^ chatPacket
);
/// <summary>
/// This is called when a remote console sends a chat user packet.
/// This function unpacks remote user data and creates a RemoteChatUser and associated
/// audio devices
/// </summary>
void ProcessIncomingUserAddedPacket(
_In_reads_bytes_(chatUserPacketLength) byte* chatUserPacketBytes,
_In_ uint32 chatUserPacketLength,
_In_ Platform::Object^ uniqueRemoteConsoleIdentifier,
_In_ Windows::Storage::Streams::IBuffer^ chatPacket
);
/// <summary>
/// This is called when a remote console sends a chat user remove packet.
/// </summary>
void ProcessIncomingUserRemovedPacket(
_In_reads_bytes_(chatUserRemovedPacketLength) byte* chatUserRemovedPacketBytes,
_In_ uint32 chatUserRemovedPacketLength,
_In_ Platform::Object^ uniqueRemoteConsoleIdentifier,
_In_ Windows::Storage::Streams::IBuffer^ chatPacket
);
private:
Concurrency::critical_section m_stateLock;
Concurrency::critical_section m_userAddRemoveLock;
Platform::WeakReference m_chatAudioThread;
Platform::WeakReference m_chatClient;
Platform::WeakReference m_chatManager;
ChatManagerSettings^ m_chatManagerSettings;
AudioDeviceIDMapper^ m_audioDeviceIDMapper;
std::shared_ptr<FactoryCache> m_factoryCache;
};
}}}
#endif
@@ -0,0 +1,103 @@
//// 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 "AudioDeviceInfo.h"
#if TV_API
namespace Microsoft {
namespace Xbox {
namespace GameChat {
ComStyleRemoteAudioDeviceInfo::ComStyleRemoteAudioDeviceInfo() :
m_type( Awxs::AudioDeviceType_Capture ),
m_sharing( Awxs::AudioDeviceSharing_Exclusive )
{
}
ComStyleRemoteAudioDeviceInfo::~ComStyleRemoteAudioDeviceInfo()
{
}
HRESULT ComStyleRemoteAudioDeviceInfo::RuntimeClassInitialize(
const HSTRING id,
const Awxs::AudioDeviceType type,
const Awxs::AudioDeviceSharing sharing,
const Awxs::AudioDeviceCategory category
)
{
HRESULT hr = E_UNEXPECTED;
CHECKHR_EXIT( m_id.Set( id ) );
m_type = type;
m_sharing = sharing;
m_category = category;
exit:
return hr;
}
HRESULT ComStyleRemoteAudioDeviceInfo::get_Id( _Out_ HSTRING* pOut )
{
if ( pOut == nullptr )
{
return E_POINTER;
}
return WindowsDuplicateString( m_id.Get(), pOut );
}
HRESULT ComStyleRemoteAudioDeviceInfo::get_DeviceType( _Out_ Awxs::AudioDeviceType* pOut )
{
if ( pOut == nullptr )
{
return E_POINTER;
}
*pOut = m_type;
return S_OK;
}
HRESULT ComStyleRemoteAudioDeviceInfo::get_Sharing( _Out_ Awxs::AudioDeviceSharing* pOut )
{
if ( pOut == nullptr )
{
return E_POINTER;
}
*pOut = m_sharing;
return S_OK;
}
HRESULT ComStyleRemoteAudioDeviceInfo::get_DeviceCategory( _Out_ Awxs::AudioDeviceCategory* pOut )
{
if ( pOut == nullptr )
{
return E_POINTER;
}
*pOut = m_category;
return S_OK;
}
HRESULT ComStyleRemoteAudioDeviceInfo::get_Volume( _Out_ float* /* pOut */ )
{
return E_UNEXPECTED;
}
HRESULT ComStyleRemoteAudioDeviceInfo::get_Muted( _Out_ boolean* /* pOut */ )
{
return E_UNEXPECTED;
}
HRESULT ComStyleRemoteAudioDeviceInfo::get_IsMicrophoneMuted( _Out_ boolean* /* pOut */ )
{
return E_NOTIMPL;
}
}}}
#endif
@@ -0,0 +1,55 @@
//// 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 "ChatUserSerializationCommon.h"
#if TV_API
namespace Microsoft {
namespace Xbox {
namespace GameChat {
/*
===============================================================================
This class is used to represent a remote audio device
IMPORTANT
1. Implements IAudioDevice
2. We create remote audio device for remote user, based on network data
===============================================================================
*/
class ComStyleRemoteAudioDeviceInfo : public Mwrl::RuntimeClass<Awxs::IAudioDeviceInfo, Mwrl::FtmBase>
{
public:
ComStyleRemoteAudioDeviceInfo();
virtual ~ComStyleRemoteAudioDeviceInfo();
HRESULT RuntimeClassInitialize( const HSTRING id,
const Awxs::AudioDeviceType type,
const Awxs::AudioDeviceSharing sharing,
const Awxs::AudioDeviceCategory category );
// IAudioDevice
HRESULT get_Id( _Out_ HSTRING* pOut );
HRESULT get_DeviceType( _Out_ Awxs::AudioDeviceType* pOut );
HRESULT get_Sharing( _Out_ Awxs::AudioDeviceSharing* pOut );
HRESULT get_DeviceCategory( _Out_ Awxs::AudioDeviceCategory* pOut );
HRESULT get_Volume( _Out_ float* /* pOut */ );
HRESULT get_Muted( _Out_ boolean* /* pOut */ );
HRESULT get_IsMicrophoneMuted( _Out_ boolean* /* pOut */ );
private:
Mwrlw::HString m_id;
Awxs::AudioDeviceType m_type;
Awxs::AudioDeviceSharing m_sharing;
Awxs::AudioDeviceCategory m_category;
};
}}}
#endif
@@ -0,0 +1,59 @@
//// 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 "AudioDevices.h"
#if TV_API
namespace Microsoft {
namespace Xbox {
namespace GameChat {
ComStyleRemoteAudioDeviceInfos::ComStyleRemoteAudioDeviceInfos()
{
}
ComStyleRemoteAudioDeviceInfos::~ComStyleRemoteAudioDeviceInfos()
{
}
void ComStyleRemoteAudioDeviceInfos::PushBack(
const Mwrl::ComPtr<Awxs::IAudioDeviceInfo>& spIAudioDevice
)
{
m_vector.push_back( spIAudioDevice );
}
HRESULT ComStyleRemoteAudioDeviceInfos::GetAt( UINT index, Awxs::IAudioDeviceInfo** ppOut )
{
if ( ppOut == nullptr )
{
return E_POINTER;
}
return m_vector.at( index ).CopyTo( ppOut );
}
HRESULT ComStyleRemoteAudioDeviceInfos::get_Size( UINT* pOut )
{
if ( pOut == nullptr )
{
return E_POINTER;
}
*pOut = ( UINT )m_vector.size();
return S_OK;
}
HRESULT ComStyleRemoteAudioDeviceInfos::IndexOf( Awxs::IAudioDeviceInfo* /* pIAudioDevice */, UINT* /* pOutIndex */, boolean* /* pOutFound */ )
{
return E_UNEXPECTED;
}
}}}
#endif
@@ -0,0 +1,43 @@
//// 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 "ChatUserSerializationCommon.h"
#if TV_API
namespace Microsoft {
namespace Xbox {
namespace GameChat {
/*
====================================================================
This class is used to represent a vector of Mwrl::ComPtr<Awxs::IAudioDeviceInfo>
IMPORTANT
Implements IAudioDevices
====================================================================
*/
class ComStyleRemoteAudioDeviceInfos : public Mwrl::RuntimeClass<Awfc::IVectorView<Awxs::IAudioDeviceInfo*>,Mwrl::FtmBase>
{
public:
ComStyleRemoteAudioDeviceInfos();
virtual ~ComStyleRemoteAudioDeviceInfos();
void PushBack( const Mwrl::ComPtr<Awxs::IAudioDeviceInfo>& spIAudioDevice );
// IAudioDevices
HRESULT GetAt( UINT index, Awxs::IAudioDeviceInfo** ppOut );
HRESULT get_Size( UINT* pOut );
HRESULT IndexOf( Awxs::IAudioDeviceInfo* /* pIAudioDevice */, UINT* /* pOutIndex */, boolean* /* pOutFound */ );
private:
std::vector<Mwrl::ComPtr<Awxs::IAudioDeviceInfo>> m_vector;
};
}}}
#endif
@@ -0,0 +1,332 @@
//// 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 "ChatPacker.h"
#if TV_API
namespace Microsoft {
namespace Xbox {
namespace GameChat {
Packer::Packer()
{
}
Packer::~Packer()
{
}
HRESULT Packer::PackBegin()
{
HRESULT hr = E_UNEXPECTED;
CHECKHR_EXIT( m_buffer.Cleanup() );
exit:
return hr;
}
// Returns the packed data, if there is no packed data, the behavior is E_UNEXPECTED
HRESULT Packer::PackEnd(
_Outptr_ BYTE** ppOut,
_Out_ UINT* pOut
)
{
HRESULT hr = E_UNEXPECTED;
CHECKHR_EXIT( m_buffer.Length( pOut ) );
CHECKHR_EXIT( m_buffer.ReadFast( ppOut, *pOut ) );
exit:
return hr;
}
//----------------------------------------------------------------------------------------
// Pack functions
// 1. Pack primitive types
// a. No input data validation
// b. Input data validation
// 2. Pack complex types
// a. Input data validation( Not handled in sub-pack functions )
// b. Group of sub-pack functions
//----------------------------------------------------------------------------------------
HRESULT Packer::PackBYTE(
_In_ const BYTE data
)
{
HRESULT hr = E_UNEXPECTED;
// No input data validation
CHECKHR_EXIT( m_buffer.Write( &data, sizeof( BYTE ) ) );
exit:
return hr;
}
HRESULT Packer::PackUSHORT(
_In_ const USHORT data
)
{
HRESULT hr = E_UNEXPECTED;
// No input data validation
CHECKHR_EXIT( m_buffer.Write( (BYTE*)&data, sizeof( USHORT ) ) );
exit:
return hr;
}
HRESULT Packer::PackUINT(
_In_ const UINT data
)
{
HRESULT hr = E_UNEXPECTED;
// No input data validation
CHECKHR_EXIT( m_buffer.Write( ( BYTE* )&data, sizeof( UINT ) ) );
exit:
return hr;
}
HRESULT Packer::PackBytes(
_In_reads_bytes_(size) const BYTE* const pData,
_In_ const UINT size
)
{
HRESULT hr = S_FALSE; // if size if 0 or no data
// Input data validation
if ( pData && size ) {
CHECKHR_EXIT( m_buffer.Write( pData, size ) );
}
exit:
return hr;
}
HRESULT Packer::PackString(
_In_ const Mwrlw::HString& data
)
{
HRESULT hr = E_UNEXPECTED;
UINT strLen = 0;
UINT size = 0;
// Input data validation
if ( data != nullptr )
{
const wchar_t* pStr = data.GetRawBuffer( &strLen );
size = strLen * sizeof( wchar_t );
std::string pUtf8Str;
ConvertWideToUtf8(pStr, &pUtf8Str);
CHECKHR_EXIT( PackUINT( (UINT)pUtf8Str.size() ) );
CHECKHR_EXIT( PackBytes( ( BYTE* )pUtf8Str.data(), (UINT)pUtf8Str.size() ) );
}
exit:
return hr;
}
HRESULT Packer::PackBuffer(
_In_ const Mwrl::ComPtr<Awss::IBuffer>& spData )
{
HRESULT hr = E_UNEXPECTED;
UINT size = 0;
Mwrl::ComPtr<Wss::IBufferByteAccess> spIBufferByteAccess;
BYTE* pBytes = nullptr;
// Input data validation
if ( spData ) {
CHECKHR_EXIT( spData->get_Length( &size ) );
CHECKHR_EXIT( spData.As( &spIBufferByteAccess ) );
CHECKHR_EXIT( spIBufferByteAccess->Buffer( &pBytes ) );
CHECKHR_EXIT( PackUINT( size ) );
CHECKHR_EXIT( PackBytes( pBytes, size ) );
}
exit:
return hr;
}
//----------------------------------------------------------------------------------------
// IMPORTANT
// 1. When we pack an audio device, it is a "local" audio device from a "local" user
// 2. Id, type and sharing, use BYTE to represent enum
//----------------------------------------------------------------------------------------
HRESULT Packer::PackAudioDevice(
_In_ const Mwrl::ComPtr<Awxs::IAudioDeviceInfo>& spData,
_In_ AudioDeviceIDMapper^ audioDeviceIDMapper
)
{
HRESULT hr = E_UNEXPECTED;
Mwrlw::HString id;
Awxs::AudioDeviceType type;
Awxs::AudioDeviceSharing sharing;
Awxs::AudioDeviceCategory category;
// Input data validation
if ( spData )
{
CHECKHR_EXIT( spData->get_Id( id.GetAddressOf() ) );
CHECKHR_EXIT( spData->get_DeviceType( &type ) );
CHECKHR_EXIT( spData->get_Sharing( &sharing ) );
CHECKHR_EXIT( spData->get_DeviceCategory( &category ) );
// Save and send a deviceId
UINT length = 0;
Platform::String^ strId = ref new Platform::String( id.GetRawBuffer(&length) );
DEVICE_ID deviceId = audioDeviceIDMapper->GetLocalDeviceID(strId);
CHECKHR_EXIT( PackBYTE( deviceId ) );
uint8 isKinect = (wcsstr(strId->Data(), L"postmec") != nullptr);
CHECKHR_EXIT( PackBYTE( ( BYTE )isKinect ) );
CHECKHR_EXIT( PackBYTE( ( BYTE )type ) );
CHECKHR_EXIT( PackBYTE( ( BYTE )sharing ) );
CHECKHR_EXIT( PackBYTE( ( BYTE )category ) );
#ifdef DEBUG_REMOTE_AUDIO_DEVICES
WCHAR text[1024] = {0};
swprintf_s(
text, ARRAYSIZE(text),
L"Packer::PackAudioDevice: strId = %s, deviceId = %d, isKinect = %d\r\n",
strId->Data(),
deviceId,
isKinect
);
OutputDebugString( text );
#endif
}
exit:
return hr;
}
//----------------------------------------------------------------------------------------
// IMPORTANT
// 1. When we pack audio devices, it is "local" audio devices from a "local" user
//----------------------------------------------------------------------------------------
HRESULT Packer::PackAudioDevices(
_In_ const Mwrl::ComPtr<Awfc::IVectorView<Awxs::IAudioDeviceInfo*>>& spData,
_In_ AudioDeviceIDMapper^ audioDeviceIDMapper
)
{
HRESULT hr = E_UNEXPECTED;
UINT size = 0;
// Input data validation
if ( spData ) {
// Pack size
CHECKHR_EXIT( spData->get_Size( &size ) );
CHECKHR_EXIT( PackUINT( size ) );
// Pack each Mwrl::ComPtr<Awxs::IAudioDeviceInfo>
for ( UINT i = 0; i < size; i++ ) {
Mwrl::ComPtr<Awxs::IAudioDeviceInfo> spIAudioDevice;
CHECKHR_EXIT( spData->GetAt( i, &spIAudioDevice ) );
CHECKHR_EXIT( PackAudioDevice( spIAudioDevice, audioDeviceIDMapper ) );
}
}
exit:
return hr;
}
//----------------------------------------------------------------------------------------
// IMPORTANT
// 1. When we pack an user, it is "local" user
//----------------------------------------------------------------------------------------
HRESULT Packer::PackUser(
_In_ const Mwrl::ComPtr<Awxs::IUser>& spData,
_In_ AudioDeviceIDMapper^ audioDeviceIDMapper
)
{
HRESULT hr = E_UNEXPECTED;
boolean isGuest = false;
Mwrlw::HString xuid;
Mwrl::ComPtr<Awfc::IVectorView<Awxs::IAudioDeviceInfo*>> spIAudioDevices;
// Input data validation
if ( spData ) {
CHECKHR_EXIT( spData->get_IsGuest( &isGuest ) );
CHECKHR_EXIT( spData->get_XboxUserId( xuid.GetAddressOf() ) );
CHECKHR_EXIT( spData->get_AudioDevices( &spIAudioDevices ) );
CHECKHR_EXIT( PackBYTE( isGuest ) );
CHECKHR_EXIT( PackString( xuid ) );
CHECKHR_EXIT( PackAudioDevices( spIAudioDevices, audioDeviceIDMapper ) );
}
exit:
return hr;
}
HRESULT Packer::ConvertWideToUtf8(
_In_ PCWSTR wideStr,
_Inout_ std::string* pUtf8Str
)
{
if ( !pUtf8Str )
{
return E_POINTER;
}
HRESULT hr = S_OK;
try
{
pUtf8Str->clear();
if (wideStr == nullptr || wideStr[0] == L'\0')
return S_OK;
int wideStrLen = (int)wcslen(wideStr) + 1; // WideCharToMultiByte expects the NULL terminator to be counted
// calculate the required size
int nBytes = WideCharToMultiByte(
CP_UTF8,
0,
wideStr,
wideStrLen,
nullptr,
0,
nullptr,
nullptr);
if(nBytes == 0)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Exit;
}
std::unique_ptr<char[]> utf8Buffer(new char[nBytes]);
if(WideCharToMultiByte(
CP_UTF8,
0,
wideStr,
wideStrLen,
utf8Buffer.get(),
nBytes,
nullptr,
nullptr) == 0)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Exit;
}
pUtf8Str->assign(utf8Buffer.get());
hr = S_OK;
goto Exit;
}
catch (std::bad_alloc)
{
hr = E_OUTOFMEMORY;
goto Exit;
}
Exit:
return hr;
}
}}}
#endif
@@ -0,0 +1,81 @@
//// 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 "ChatUserSerializationCommon.h"
#include "ChatReadWriteBuffer.h"
#include "AudioDeviceIDMapper.h"
#if TV_API
namespace Microsoft {
namespace Xbox {
namespace GameChat {
// Class that used to perform data serialization
class Packer
{
public:
Packer();
~Packer();
HRESULT PackBegin();
HRESULT PackEnd(
_Outptr_ BYTE** ppOut,
_Out_ UINT* pOut
);
HRESULT PackBYTE(
_In_ const BYTE data
);
HRESULT PackUSHORT(
_In_ const USHORT data
);
HRESULT PackUINT(
_In_ const UINT data
);
HRESULT PackBytes(
_In_reads_bytes_(size) const BYTE* const pData,
_In_ const UINT size
);
HRESULT PackString(
_In_ const Mwrlw::HString& data
);
HRESULT PackBuffer(
_In_ const Mwrl::ComPtr<Awss::IBuffer>& spData
);
HRESULT PackAudioDevice(
_In_ const Mwrl::ComPtr<Awxs::IAudioDeviceInfo>& spData,
_In_ AudioDeviceIDMapper^ audioDeviceIDMapper
);
HRESULT PackAudioDevices(
_In_ const Mwrl::ComPtr<Awfc::IVectorView<Awxs::IAudioDeviceInfo*>>& spData,
_In_ AudioDeviceIDMapper^ audioDeviceIDMapper
);
HRESULT PackUser(
_In_ const Mwrl::ComPtr<Awxs::IUser>& spData,
_In_ AudioDeviceIDMapper^ audioDeviceIDMapper
);
private:
ReadWriteBuffer m_buffer;
static HRESULT ConvertWideToUtf8( _In_ PCWSTR wideStr, _Inout_ std::string* pUtf8Str);
};
}}}
#endif
@@ -0,0 +1,107 @@
//// 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 "ChatReadWriteBuffer.h"
#if TV_API
namespace Microsoft {
namespace Xbox {
namespace GameChat {
ReadWriteBuffer::ReadWriteBuffer( const unsigned int capa ) :
m_pData( nullptr ),
m_capacity( 0 ),
m_isCreated( false ),
m_pR( nullptr ),
m_pW( nullptr )
{
if ( capa )
{
m_pData.reset(new BYTE[capa]);
if ( m_pData )
{
m_capacity = capa;
m_isCreated = true;
Cleanup();
}
}
}
ReadWriteBuffer::~ReadWriteBuffer()
{
}
HRESULT ReadWriteBuffer::Cleanup()
{
if ( m_isCreated )
{
m_pR = m_pW = m_pData.get();
return S_OK;
}
return E_UNEXPECTED;
}
HRESULT ReadWriteBuffer::Length( unsigned int* pOut ) const
{
if ( m_isCreated )
{
*pOut = ( unsigned int )( m_pW - m_pR );
return S_OK;
}
return E_UNEXPECTED;
}
// IMPORTANT
// 1. When m_pR == m_pW, m_pR is pointing to garbage data, or it is out of boundary
// 2. When m_pW == ( m_pData + m_capacity ), m_pW is out of boundary
// We treat size zero as unexpected behaviors when performing read and write
// ReadFast does not copy memory out, just return the start address
HRESULT ReadWriteBuffer::ReadFast( BYTE** ppOut, const unsigned int size )
{
if ( size != 0 && IsAbleToRead( size ) )
{
*ppOut = m_pR;
m_pR += size;
return S_OK;
}
return E_UNEXPECTED;
}
HRESULT ReadWriteBuffer::Write( const BYTE* const pData, const unsigned int size )
{
if ( size != 0 && IsAbleToWrite( size ) )
{
if ( !memcpy_s( m_pW, size, pData, size ) )
{
m_pW += size;
return S_OK;
}
}
return E_UNEXPECTED;
}
bool ReadWriteBuffer::IsAbleToRead( const unsigned int size ) const {
if ( m_isCreated ) {
return ( m_pR + size ) <= ( m_pW );
}
return false;
}
bool ReadWriteBuffer::IsAbleToWrite( const unsigned int size ) const
{
if ( m_isCreated )
{
return ( m_pW + size ) <= ( m_pData.get() + m_capacity );
}
return false;
}
}}}
#endif
@@ -0,0 +1,43 @@
//// 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 "ChatUserSerializationCommon.h"
#if TV_API
namespace Microsoft {
namespace Xbox {
namespace GameChat {
#define DEFAULT_CAPACITY 8192
// Fixed capacity buffer with a read and a write pointer
class ReadWriteBuffer
{
public:
ReadWriteBuffer( const unsigned int capa = DEFAULT_CAPACITY );
~ReadWriteBuffer();
HRESULT Cleanup();
HRESULT Length( unsigned int* pOut ) const;
HRESULT ReadFast( BYTE** ppOut, const unsigned int size );
HRESULT Write( const BYTE* const pData, const unsigned int size );
private:
std::unique_ptr<BYTE[]> m_pData;
unsigned int m_capacity;
bool m_isCreated;
BYTE* m_pR;
BYTE* m_pW;
bool IsAbleToRead( const unsigned int size ) const;
bool IsAbleToWrite( const unsigned int size ) const;
};
}}}
#endif
@@ -0,0 +1,279 @@
//// 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 "AudioDeviceInfo.h"
#include "AudioDevices.h"
#include "RemoteUser.h"
#include "ChatUnpacker.h"
#if TV_API
namespace Microsoft {
namespace Xbox {
namespace GameChat {
Unpacker::Unpacker()
{
}
Unpacker::~Unpacker()
{
}
// Cleanup buffer and feed it with data
HRESULT Unpacker::UnpackBegin(
_In_reads_bytes_(size) const BYTE* const pData,
_In_ const UINT size )
{
HRESULT hr = E_UNEXPECTED;
CHECKHR_EXIT( m_buffer.Cleanup() );
CHECKHR_EXIT( m_buffer.Write( pData, size ) );
exit:
return hr;
}
HRESULT Unpacker::UnpackEnd() const
{
return S_OK;
}
//----------------------------------------------------------------------------------------
// Unpack functions
// 1. Unpack primitive types
// a. No input data validation
// b. Input data validation
// 2. Unpack complex types
// a. Group of sub-unpack functions
//----------------------------------------------------------------------------------------
HRESULT Unpacker::UnpackBYTE(
_Out_ BYTE* pOut
)
{
HRESULT hr = E_UNEXPECTED;
BYTE* pR = nullptr;
// No input data validation
CHECKHR_EXIT( m_buffer.ReadFast( &pR, sizeof( BYTE ) ) );
*pOut = *pR;
exit:
return hr;
}
HRESULT Unpacker::UnpackUSHORT(
_Out_ USHORT* pOut
)
{
HRESULT hr = E_UNEXPECTED;
BYTE* pR = nullptr;
// No input data validation
CHECKHR_EXIT( m_buffer.ReadFast( &pR, sizeof( USHORT ) ) );
*pOut = *( USHORT* )pR;
exit:
return hr;
}
HRESULT Unpacker::UnpackUINT(
_Out_ UINT* pOut
)
{
HRESULT hr = E_UNEXPECTED;
BYTE* pR = nullptr;
// No input data validation
CHECKHR_EXIT( m_buffer.ReadFast( &pR, sizeof( UINT ) ) );
*pOut = *( UINT* )pR;
exit:
return hr;
}
// Returns pointer, no memory copy
HRESULT Unpacker::UnpackBytesFast(
_Outptr_result_maybenull_ BYTE** ppOut,
_In_ const UINT size
)
{
if ( !ppOut )
{
return E_POINTER;
}
*ppOut = nullptr;
HRESULT hr = S_FALSE;
BYTE* pR = nullptr;
// Input data validation
if ( size )
{
CHECKHR_EXIT( m_buffer.ReadFast( &pR, size ) );
*ppOut = pR;
return hr;
}
exit:
return hr;
}
HRESULT Unpacker::UnpackString(
_Out_ Mwrlw::HString& out
)
{
HRESULT hr = E_UNEXPECTED;
UINT size = 0;
BYTE* pBytes = nullptr;
CHECKHR_EXIT( UnpackUINT( &size ) );
CHECKHR_EXIT( UnpackBytesFast( &pBytes, size ) );
{
std::string byteString ((char*)pBytes, size);
std::unique_ptr<WCHAR[]> pConverted( new WCHAR[size] );
::MultiByteToWideChar( CP_UTF8,
0,
&byteString[0],
-1,
pConverted.get(),
size );
CHECKHR_EXIT( WindowsCreateString( pConverted.get(), size , out.GetAddressOf() ) );
}
exit:
return hr;
}
// Returns pointer and size, no memory copy
HRESULT Unpacker::UnpackBufferFast(
_Outptr_result_maybenull_ BYTE** ppOut,
_Out_ UINT* pOut
)
{
HRESULT hr = E_UNEXPECTED;
UINT size = 0;
BYTE* pBytes = nullptr;
CHECKHR_EXIT( UnpackUINT( &size ) );
CHECKHR_EXIT( UnpackBytesFast( &pBytes, size ) );
*ppOut = pBytes;
*pOut = size;
exit:
return hr;
}
//----------------------------------------------------------------------------------------
// IMPORTANT
// 1. When we unpack an audio device, it is a "remote" audio device for a "remote" user
// 2. Id, type, sharing, use BYTE to represent enum
//----------------------------------------------------------------------------------------
HRESULT Unpacker::UnpackAudioDevice(
_Out_ Mwrl::ComPtr<Awxs::IAudioDeviceInfo>& spOut,
_In_ AudioDeviceIDMapper^ audioDeviceIDMapper,
_In_ uint8 localNameOfRemoteConsole
)
{
HRESULT hr = E_UNEXPECTED;
Mwrlw::HString id;
BYTE type = 0;
BYTE sharing = 0;
BYTE category = 0;
BYTE isKinect;
DEVICE_ID deviceId;
Platform::String^ strId;
CHECKHR_EXIT( UnpackBYTE( &deviceId ) );
CHECKHR_EXIT( UnpackBYTE( &isKinect ) );
CHECKHR_EXIT( UnpackBYTE( &type ) );
CHECKHR_EXIT( UnpackBYTE( &sharing ) );
CHECKHR_EXIT( UnpackBYTE( &category ) );
// Store the lookupID and strId correlation for later use
LOOKUP_ID lookUpId = audioDeviceIDMapper->ConstructLookupID( localNameOfRemoteConsole, deviceId );
strId = lookUpId.ToString();
if (isKinect)
{
strId += L"postmec";
}
audioDeviceIDMapper->AddRemoteAudioDevice( lookUpId, strId );
id.Set(strId->Data());
// RemoteAudioDevice
CHECKHR_EXIT( Mwrl::MakeAndInitialize<ComStyleRemoteAudioDeviceInfo>( &spOut,
id.Get(),
( Awxs::AudioDeviceType )type,
( Awxs::AudioDeviceSharing )sharing,
( Awxs::AudioDeviceCategory )category ) );
exit:
return hr;
}
//----------------------------------------------------------------------------------------
// IMPORTANT
// 1. When we unpack audio devices, it is "remote" audio devices for a "remote user"
//----------------------------------------------------------------------------------------
HRESULT Unpacker::UnpackAudioDevices(
_Out_ Mwrl::ComPtr<Awfc::IVectorView<Awxs::IAudioDeviceInfo*>>& spOut,
_In_ AudioDeviceIDMapper^ audioDeviceIDMapper,
_In_ uint8 localNameOfRemoteConsole
)
{
HRESULT hr = E_UNEXPECTED;
Mwrl::ComPtr<ComStyleRemoteAudioDeviceInfos> spTemp;
UINT size = 0;
// RemoteAudioDevices
CHECKHR_EXIT( Mwrl::MakeAndInitialize<ComStyleRemoteAudioDeviceInfos>( &spTemp ) );
CHECKHR_EXIT( UnpackUINT( &size ) );
for ( UINT i = 0; i < size; i++ )
{
Mwrl::ComPtr<Awxs::IAudioDeviceInfo> spIAudioDevice;
CHECKHR_EXIT( UnpackAudioDevice( spIAudioDevice, audioDeviceIDMapper, localNameOfRemoteConsole ) );
spTemp->PushBack( spIAudioDevice );
}
CHECKHR_EXIT( spTemp.As( &spOut ) );
exit:
return hr;
}
//----------------------------------------------------------------------------------------
// IMPORTANT
// 1. When we unpack an user, it is a "remote" user
//----------------------------------------------------------------------------------------
HRESULT Unpacker::UnpackUser(
_Out_ Mwrl::ComPtr<Awxs::IUser>& spOut,
_In_ AudioDeviceIDMapper^ audioDeviceIDMapper,
_In_ uint8 localNameOfRemoteConsole
)
{
HRESULT hr = E_UNEXPECTED;
BYTE isGuest = 0;
Mwrlw::HString xuid;
Mwrl::ComPtr<Awfc::IVectorView<Awxs::IAudioDeviceInfo*>> spIAudioDevices;
CHECKHR_EXIT( UnpackBYTE( &isGuest ) );
CHECKHR_EXIT( UnpackString( xuid ) );
CHECKHR_EXIT( UnpackAudioDevices( spIAudioDevices, audioDeviceIDMapper, localNameOfRemoteConsole ) );
// RemoteUser
CHECKHR_EXIT( Mwrl::MakeAndInitialize<ComStyleRemoteUser>( &spOut,
isGuest,
xuid.Get(),
spIAudioDevices.Get() ) );
exit:
return hr;
}
}}}
#endif
@@ -0,0 +1,82 @@
//// 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 "ChatUserSerializationCommon.h"
#include "ChatReadWriteBuffer.h"
#if TV_API
namespace Microsoft {
namespace Xbox {
namespace GameChat {
// Class that used to perform data deserialization
class Unpacker
{
public:
Unpacker();
~Unpacker();
HRESULT UnpackBegin(
_In_reads_bytes_(size) const BYTE* const pData,
_In_ const UINT size
);
HRESULT UnpackEnd() const;
HRESULT UnpackBYTE(
_Out_ BYTE* pOut
);
HRESULT UnpackUSHORT(
_Out_ USHORT* pOut
);
HRESULT UnpackUINT(
_Out_ UINT* pOut
);
HRESULT UnpackBytesFast(
_Outptr_result_maybenull_ BYTE** ppOut,
_In_ const UINT size
);
HRESULT UnpackString(
_Out_ Mwrlw::HString& out
);
HRESULT UnpackBufferFast(
_Outptr_result_maybenull_ BYTE** ppOut,
_Out_ UINT* pOut
);
HRESULT UnpackAudioDevice(
_Out_ Mwrl::ComPtr<Awxs::IAudioDeviceInfo>& spOut,
_In_ AudioDeviceIDMapper^ audioDeviceIDMapper,
_In_ uint8 localNameOfRemoteConsole
);
HRESULT UnpackAudioDevices(
_Out_ Mwrl::ComPtr<Awfc::IVectorView<Awxs::IAudioDeviceInfo*>>& spOut,
_In_ AudioDeviceIDMapper^ audioDeviceIDMapper,
_In_ uint8 localNameOfRemoteConsole
);
HRESULT UnpackUser(
_Out_ Mwrl::ComPtr<Awxs::IUser>& spOut,
_In_ AudioDeviceIDMapper^ audioDeviceIDMapper,
_In_ uint8 localNameOfRemoteConsole
);
private:
ReadWriteBuffer m_buffer;
};
}}}
#endif
@@ -0,0 +1,30 @@
//// 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 {
//----------------------------------------------------------------------------------------------
// Namespaces
//----------------------------------------------------------------------------------------------
namespace Mwrl = Microsoft::WRL;
namespace Mwrlw = Microsoft::WRL::Wrappers;
namespace Wf = Windows::Foundation;
namespace Awf = ABI::Windows::Foundation;
namespace Awfc = ABI::Windows::Foundation::Collections;
namespace Wss = Windows::Storage::Streams;
namespace Awss = ABI::Windows::Storage::Streams;
namespace Wxs = Windows::Xbox::System;
namespace Awxs = ABI::Windows::Xbox::System;
namespace Awxi = ABI::Windows::Xbox::Input;
}}}
#endif
@@ -0,0 +1,201 @@
//// 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 "RemoteUser.h"
#if TV_API
namespace Microsoft {
namespace Xbox {
namespace GameChat {
#define REMOTEUSERSTART 0x01000000
#define REMOTEUSEREND 0x01100000
unsigned ComStyleRemoteUser::s_id = REMOTEUSERSTART;
//----------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------
ComStyleRemoteUser::ComStyleRemoteUser() :
m_id( s_id < REMOTEUSEREND ? s_id++ : (s_id = REMOTEUSERSTART, s_id++) ), // using the , operator, returns last expression
m_isGuest( false )
{
}
//----------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------
ComStyleRemoteUser::~ComStyleRemoteUser()
{
}
//----------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------
HRESULT ComStyleRemoteUser::RuntimeClassInitialize(
const boolean isGuest,
const HSTRING xuid,
Awfc::IVectorView<Awxs::IAudioDeviceInfo*>* const pIAudioDevices )
{
HRESULT hr = E_UNEXPECTED;
m_isGuest = isGuest;
CHECKHR_EXIT( m_xuid.Set( xuid ) );
m_spIAudioDevices = pIAudioDevices;
exit:
return hr;
}
//----------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------
HRESULT ComStyleRemoteUser::get_Id( UINT32* id )
{
if ( id == nullptr )
{
return E_POINTER;
}
*id = m_id;
return S_OK;
}
//----------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------
HRESULT ComStyleRemoteUser::get_AudioDevices( Awfc::IVectorView<Awxs::IAudioDeviceInfo*>** audioDevices )
{
if ( audioDevices == nullptr )
{
return E_POINTER;
}
return m_spIAudioDevices.CopyTo( audioDevices );
}
//----------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------
HRESULT ComStyleRemoteUser::get_Controllers( Awfc::IVectorView<Awxi::IController*>** /* controllers */ )
{
return E_NOTIMPL;
}
//----------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------
HRESULT ComStyleRemoteUser::get_DisplayInfo( Awxs::IUserDisplayInfo** /* displayInfo */ )
{
return E_NOTIMPL;
}
//----------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------
HRESULT ComStyleRemoteUser::get_IsGuest( boolean* isGuest )
{
if ( isGuest == nullptr )
{
return E_POINTER;
}
*isGuest = m_isGuest;
return S_OK;
}
//----------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------
HRESULT ComStyleRemoteUser::get_IsSignedIn( boolean* isSignedIn )
{
if ( isSignedIn == nullptr )
{
return E_POINTER;
}
*isSignedIn = true;
return S_OK;
}
//----------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------
HRESULT ComStyleRemoteUser::get_Location( Awxs::UserLocation* location )
{
if ( location == nullptr )
{
return E_POINTER;
}
*location = Awxs::UserLocation_Remote;
return S_OK;
}
//----------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------
HRESULT ComStyleRemoteUser::get_Sponsor( IUser** /* sponsor */ )
{
return E_NOTIMPL;
}
//----------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------
HRESULT ComStyleRemoteUser::get_XboxUserHash( HSTRING* /* userhash */ )
{
return E_NOTIMPL;
}
//----------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------
HRESULT ComStyleRemoteUser::get_XboxUserId( _Out_ HSTRING* xuid )
{
if ( !xuid )
{
return E_POINTER;
}
HRESULT hr = WindowsDuplicateString( m_xuid.Get(), xuid );
#ifdef __PREFAST__
if ( FAILED(hr) )
{
*xuid = nullptr;
}
#endif
return hr;
}
//----------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------
HRESULT ComStyleRemoteUser::GetTokenAndSignatureAsync(
HSTRING /* httpMethod */,
HSTRING /* url */,
HSTRING /* headers */,
Awf::IAsyncOperation<Awxs::GetTokenAndSignatureResult*>** /* operation */ )
{
return E_NOTIMPL;
}
//----------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------
HRESULT ComStyleRemoteUser::GetTokenAndSignatureWithBodyAsync(
HSTRING /* httpMethod */,
HSTRING /* url */,
HSTRING /* headers */,
UINT32 /* __bodySize */,
BYTE* /* body */,
Awf::IAsyncOperation<Awxs::GetTokenAndSignatureResult*>** /* operation */ )
{
return E_NOTIMPL;
}
//----------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------
HRESULT ComStyleRemoteUser::GetTokenAndSignatureWithStringBodyAsync(
HSTRING /* httpMethod */,
HSTRING /* url */,
HSTRING /* headers */,
HSTRING /* body */,
Awf::IAsyncOperation<Awxs::GetTokenAndSignatureResult*>** /* operation */ )
{
return E_NOTIMPL;
}
}}}
#endif
@@ -0,0 +1,73 @@
//// 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 "ChatUserSerializationCommon.h"
#if TV_API
namespace Microsoft {
namespace Xbox {
namespace GameChat {
/*
=====================================================================
This class is used to represent a remote user, remote user is
not created by user manager
IMPORTANT
1. Implements IUser
=====================================================================
*/
class ComStyleRemoteUser : public Mwrl::RuntimeClass<ABI::Windows::Xbox::System::IUser, Mwrl::FtmBase>
{
public:
ComStyleRemoteUser();
virtual ~ComStyleRemoteUser();
HRESULT RuntimeClassInitialize( const boolean isGuest,
const HSTRING xuid,
Awfc::IVectorView<Awxs::IAudioDeviceInfo*>* const pIAudioDevices );
HRESULT get_Id( UINT32* id );
HRESULT get_AudioDevices( Awfc::IVectorView<Awxs::IAudioDeviceInfo*>** audioDevices );
HRESULT get_Controllers( Awfc::IVectorView<Awxi::IController*>** controllers );
HRESULT get_DisplayInfo( Awxs::IUserDisplayInfo** displayInfo );
HRESULT get_IsGuest( boolean* isGuest );
HRESULT get_IsSignedIn( boolean* isSignedIn );
HRESULT get_Location( Awxs::UserLocation* location );
HRESULT get_Sponsor( ABI::Windows::Xbox::System::IUser** sponsor );
HRESULT get_XboxUserHash( HSTRING* userhash );
HRESULT get_XboxUserId( _Out_ HSTRING* xuid );
HRESULT GetTokenAndSignatureAsync( HSTRING httpMethod,
HSTRING url,
HSTRING headers,
Awf::IAsyncOperation<Awxs::GetTokenAndSignatureResult*>** operation );
HRESULT GetTokenAndSignatureWithBodyAsync( HSTRING httpMethod,
HSTRING url,
HSTRING headers,
UINT32 __bodySize,
BYTE* body,
Awf::IAsyncOperation<Awxs::GetTokenAndSignatureResult*>** operation );
HRESULT GetTokenAndSignatureWithStringBodyAsync( HSTRING httpMethod,
HSTRING url,
HSTRING headers,
HSTRING body,
Awf::IAsyncOperation<Awxs::GetTokenAndSignatureResult*>** operation );
private:
UINT m_id;
boolean m_isGuest;
Mwrlw::HString m_xuid;
Mwrl::ComPtr<Awfc::IVectorView<Awxs::IAudioDeviceInfo*>> m_spIAudioDevices;
static UINT s_id;
};
}}}
#endif
@@ -0,0 +1,34 @@
//// 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 "RemoteAudioDevice.h"
#if TV_API
namespace Microsoft {
namespace Xbox {
namespace GameChat {
RemoteAudioDeviceInfo::RemoteAudioDeviceInfo(
_In_ Windows::Xbox::System::AudioDeviceCategory deviceCategory,
_In_ Windows::Xbox::System::AudioDeviceType deviceType,
_In_ Platform::String^ id,
_In_ bool isMicrophoneMuted,
_In_ Windows::Xbox::System::AudioDeviceSharing sharing
) :
m_deviceCategory( deviceCategory ),
m_deviceType( deviceType ),
m_id( id ),
m_isMicrophoneMuted( isMicrophoneMuted ),
m_sharing( sharing )
{
}
}}}
#endif
@@ -0,0 +1,79 @@
//// 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 {
namespace Wxs = Windows::Xbox::System;
ref class RemoteAudioDeviceInfo sealed : public Wxs::IAudioDeviceInfo
{
public:
RemoteAudioDeviceInfo(
_In_ Windows::Xbox::System::AudioDeviceCategory deviceCategory,
_In_ Windows::Xbox::System::AudioDeviceType deviceType,
_In_ Platform::String^ id,
_In_ bool isMicrophoneMuted,
_In_ Windows::Xbox::System::AudioDeviceSharing sharing
);
virtual property Windows::Xbox::System::AudioDeviceCategory DeviceCategory
{
Windows::Xbox::System::AudioDeviceCategory get()
{
return m_deviceCategory;
}
}
virtual property Windows::Xbox::System::AudioDeviceType DeviceType
{
Windows::Xbox::System::AudioDeviceType get()
{
return m_deviceType;
}
}
virtual property Platform::String^ Id
{
Platform::String^ get()
{
return m_id;
}
}
virtual property bool IsMicrophoneMuted
{
bool get()
{
return m_isMicrophoneMuted;
}
}
virtual property Windows::Xbox::System::AudioDeviceSharing Sharing
{
Windows::Xbox::System::AudioDeviceSharing get()
{
return m_sharing;
}
}
private:
Windows::Xbox::System::AudioDeviceCategory m_deviceCategory;
Windows::Xbox::System::AudioDeviceType m_deviceType;
Platform::String^ m_id;
bool m_isMicrophoneMuted;
Windows::Xbox::System::AudioDeviceSharing m_sharing;
};
}}}
#endif
@@ -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
#include "pch.h"
#include "RemoteChatUser.h"
#if TV_API
using namespace Concurrency;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
namespace Microsoft {
namespace Xbox {
namespace GameChat {
const UINT REMOTE_USER_ID_SEED = 0x01001000;
RemoteChatUser::RemoteChatUser(
_In_ Platform::String^ xuid,
_In_ bool isGuest,
_In_ Wfc::IVectorView< Wxs::IAudioDeviceInfo^ >^ audioDevices
) :
m_isGuest(isGuest)
{
Initialize( xuid, audioDevices );
}
RemoteChatUser::RemoteChatUser() :
m_xuid( nullptr ),
m_isGuest(false)
{
}
void RemoteChatUser::Initialize(
_In_ Platform::String^ xuid,
_In_ Wfc::IVectorView< Wxs::IAudioDeviceInfo^ >^ audioDevices)
{
m_xuid = xuid;
for( unsigned int i = 0; i < audioDevices->Size; ++i )
{
AddAudioDevice( audioDevices->GetAt( i ) );
}
static UINT remoteIdTracker = REMOTE_USER_ID_SEED;
m_id = InterlockedIncrement(&remoteIdTracker);
}
void RemoteChatUser::AddAudioDevice( _In_ Wxs::IAudioDeviceInfo^ audioDevice )
{
m_audioDevices.Append( audioDevice );
}
void RemoteChatUser::ClearAudioDevices()
{
m_audioDevices.Clear();
}
void RemoteChatUser::ResetAudioDevices(
_In_ Wfc::IVectorView< Wxs::IAudioDeviceInfo^ >^ audioDevices
)
{
m_audioDevices.Clear();
for (auto device : audioDevices)
{
m_audioDevices.Append( device );
}
}
}}}
#endif
@@ -0,0 +1,195 @@
//// 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 Wfc = Windows::Foundation::Collections;
namespace Wxs = Windows::Xbox::System;
namespace Wxi = Windows::Xbox::Input;
namespace Microsoft {
namespace Xbox {
namespace GameChat {
ref class RemoteChatUser sealed : public Wxs::IUser
{
public:
RemoteChatUser(
_In_ Platform::String^ xuid,
_In_ bool isGuest,
_In_ Wfc::IVectorView< Wxs::IAudioDeviceInfo^ >^ audioDevices
);
// IUser interface implementation, these are not implemented
virtual Windows::Foundation::IAsyncOperation< Wxs::GetTokenAndSignatureResult^ >^ GetTokenAndSignatureAsync(
_In_ Platform::String^ xuid,
_In_ Platform::String^ url,
_In_ Platform::String^ headers
)
{
throw E_NOTIMPL;
}
virtual Windows::Foundation::IAsyncOperation< Wxs::GetTokenAndSignatureResult^ >^ GetTokenAndSignatureAsync(
_In_ Platform::String^ xuid,
_In_ Platform::String^ url,
_In_ Platform::String^ headers,
_In_ const Platform::Array<unsigned char>^ body
)
{
throw E_NOTIMPL;
}
virtual Windows::Foundation::IAsyncOperation< Wxs::GetTokenAndSignatureResult^ >^ GetTokenAndSignatureAsync(
_In_ Platform::String^ m_xuid,
_In_ Platform::String^ url,
_In_ Platform::String^ headers,
_In_ Platform::String^ body
)
{
throw E_NOTIMPL;
}
// These properties are not used by the Chat libraries and are left unimplemented
virtual property Wfc::IVectorView< Wxs::IAudioDeviceInfo^ >^ AudioDevices
{
Wfc::IVectorView< Wxs::IAudioDeviceInfo^ >^ get()
{
return m_audioDevices.GetView();
}
}
virtual property Wfc::IVectorView< Wxi::IController^ >^ Controllers
{
Wfc::IVectorView< Wxi::IController^ >^ get()
{
return nullptr;
}
}
virtual property Wxs::UserDisplayInfo^ DisplayInfo
{
Wxs::UserDisplayInfo^ get()
{
throw E_NOTIMPL;
}
}
virtual property Wfc::IVectorView< UINT>^ Privileges
{
Wfc::IVectorView< UINT >^ get()
{
throw E_NOTIMPL;
}
}
virtual property Platform::String^ XboxUserHash
{
Platform::String^ get()
{
throw E_NOTIMPL;
}
}
virtual property bool IsAdult
{
bool get()
{
throw E_NOTIMPL;
}
}
virtual property Wxs::User^ Sponsor
{
Wxs::User^ get()
{
return nullptr;
}
}
virtual property UINT Id
{
UINT get()
{
return m_id;
}
}
// This isn't currently used, but may be used in the future
virtual property bool IsGuest
{
bool get()
{
return m_isGuest;
}
}
// Remote users are never signed in on our console
virtual property bool IsSignedIn
{
bool get()
{
return false;
}
}
// By definition, these users are always remote
virtual property Wxs::UserLocation Location
{
Wxs::UserLocation get()
{
return Wxs::UserLocation::Remote;
}
}
// XUID's are always important
virtual property Platform::String^ XboxUserId
{
Platform::String^ get()
{
return m_xuid;
}
}
virtual property Platform::String^ Gamertag
{
Platform::String^ get()
{
return m_gamertag;
}
}
void AddAudioDevice(
_In_ Wxs::IAudioDeviceInfo^ audioDevice
);
void ClearAudioDevices();
void ResetAudioDevices(
_In_ Wfc::IVectorView< Wxs::IAudioDeviceInfo^ >^ audioDevices
);
private:
RemoteChatUser();
void Initialize(
_In_ Platform::String^ xuid,
_In_ Wfc::IVectorView< Wxs::IAudioDeviceInfo^ >^ audioDevices
);
private:
Platform::Collections::Vector< Wxs::IAudioDeviceInfo^ > m_audioDevices;
Platform::String^ m_xuid;
UINT m_id;
bool m_isGuest;
Platform::String^ m_gamertag;
};
}}}
#endif
@@ -0,0 +1,207 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|ARM">
<Configuration>Debug</Configuration>
<Platform>ARM</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|Durango">
<Configuration>Debug</Configuration>
<Platform>Durango</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Profile|ARM">
<Configuration>Profile</Configuration>
<Platform>ARM</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Profile|Durango">
<Configuration>Profile</Configuration>
<Platform>Durango</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|ARM">
<Configuration>Release</Configuration>
<Platform>ARM</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Durango">
<Configuration>Release</Configuration>
<Platform>Durango</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ApplicationEnvironment>title</ApplicationEnvironment>
<ProjectGuid>{9B399639-7A3F-44CF-82EF-D4C50718130E}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<ProjectName>Microsoft.Xbox.GameChat</ProjectName>
<RootNamespace>Microsoft.Xbox.GameChat</RootNamespace>
<DefaultLanguage>en-US</DefaultLanguage>
<MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion>
<ConsumeWinRT>true</ConsumeWinRT>
<ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v140</PlatformToolset>
<UseXboxServices>false</UseXboxServices>
<UsePublicSdk>true</UsePublicSdk>
<SccProjectName>SAK</SccProjectName>
<SccAuxPath>SAK</SccAuxPath>
<SccLocalPath>SAK</SccLocalPath>
<SccProvider>SAK</SccProvider>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<CharacterSet>Unicode</CharacterSet>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Profile'" Label="Configuration">
<CharacterSet>Unicode</CharacterSet>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup>
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Platform)'=='arm'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Durango'">
<OutDir>$(ProjectDir)$(Platform)\$(Configuration)\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
<OutDir>$(ProjectDir)$(Platform)\$(Configuration)\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Profile|Durango'">
<OutDir>$(ProjectDir)$(Platform)\$(Configuration)\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Profile|ARM'">
<OutDir>$(ProjectDir)$(Platform)\$(Configuration)\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Durango'">
<OutDir>$(ProjectDir)$(Platform)\$(Configuration)\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
<OutDir>$(ProjectDir)$(Platform)\$(Configuration)\</OutDir>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PreprocessorDefinitions>_WINRT_DLL;WIN32_LEAN_AND_MEAN=1;ENABLE_INTSAFE_SIGNED_FUNCTIONS;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
<WarningLevel>Level4</WarningLevel>
<TreatWarningAsError>true</TreatWarningAsError>
<AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)Utils;$(ProjectDir)Chat;$(ProjectDir)ChatUserSerialization\CxStyle;$(ProjectDir)ChatUserSerialization\ComStyle;$(ProjectDir)ChatClient;$(ProjectDir)ChatAudioThread;$(ProjectDir)ChatNetwork;$(ProjectDir)ChatEvents;$(GeneratedFilesDir);$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<AdditionalDependencies>uuid.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies Condition="'$(Platform)'!='Durango' and '$(BuildingInsideVisualStudio)'=='true'">msxml6.lib;%(AdditionalDependencies)</AdditionalDependencies>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
<ClCompile>
<PreprocessorDefinitions>NDEBUG;ENABLE_INTSAFE_SIGNED_FUNCTIONS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<AdditionalOptions>/DEBUGTYPE:CV,FIXUP %(AdditionalOptions)</AdditionalOptions>
<ProgramDatabaseFile>$(OutDir)$(TargetName).pdb</ProgramDatabaseFile>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="ChatAudioThread\ChatAudioThread.h" />
<ClInclude Include="ChatAudioThread\JitterBuffer.h" />
<ClInclude Include="ChatClient\AudioDeviceIDMapper.h" />
<ClInclude Include="ChatClient\ChatClient.h" />
<ClInclude Include="ChatClient\ChatUser.h" />
<ClInclude Include="ChatEvents\ChatEvents.h" />
<ClInclude Include="ChatEvents\ChatDiagnostics.h" />
<ClInclude Include="ChatNetwork\ChatNetwork.h" />
<ClInclude Include="ChatUserSerialization\ComStyle\AudioDeviceInfo.h" />
<ClInclude Include="ChatUserSerialization\ComStyle\AudioDevices.h" />
<ClInclude Include="ChatUserSerialization\ComStyle\ChatPacker.h" />
<ClInclude Include="ChatUserSerialization\ComStyle\ChatReadWriteBuffer.h" />
<ClInclude Include="ChatUserSerialization\ComStyle\ChatUnpacker.h" />
<ClInclude Include="ChatUserSerialization\ComStyle\ChatUserSerializationCommon.h" />
<ClInclude Include="ChatUserSerialization\ComStyle\RemoteUser.h" />
<ClInclude Include="ChatUserSerialization\CxStyle\RemoteAudioDevice.h" />
<ClInclude Include="ChatUserSerialization\CxStyle\RemoteChatUser.h" />
<ClInclude Include="Chat\ChatManagerEvents.h" />
<ClInclude Include="Chat\ChatManagerSettings.h" />
<ClInclude Include="Chat\ChatPerformance.h" />
<ClInclude Include="Utils\BufferUtils.h" />
<ClInclude Include="Utils\ChatMacros.h" />
<ClInclude Include="Utils\ErrorUtils.h" />
<ClInclude Include="Utils\FactoryCache.h" />
<ClInclude Include="Utils\Thread.h" />
<ClInclude Include="Chat\ChatManager.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="Utils\Clock.h" />
<ClInclude Include="Utils\StringUtils.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="ChatAudioThread\ChatAudioThread.cpp" />
<ClCompile Include="ChatAudioThread\JitterBuffer.cpp" />
<ClCompile Include="ChatClient\AudioDeviceIDMapper.cpp" />
<ClCompile Include="ChatClient\ChatClient.cpp" />
<ClCompile Include="ChatClient\ChatUser.cpp" />
<ClCompile Include="ChatEvents\ChatDiagnostics.cpp" />
<ClCompile Include="ChatNetwork\ChatNetwork.cpp" />
<ClCompile Include="ChatUserSerialization\ComStyle\AudioDeviceInfo.cpp" />
<ClCompile Include="ChatUserSerialization\ComStyle\AudioDevices.cpp" />
<ClCompile Include="ChatUserSerialization\ComStyle\ChatPacker.cpp" />
<ClCompile Include="ChatUserSerialization\ComStyle\ChatReadWriteBuffer.cpp" />
<ClCompile Include="ChatUserSerialization\ComStyle\ChatUnpacker.cpp" />
<ClCompile Include="ChatUserSerialization\ComStyle\RemoteUser.cpp" />
<ClCompile Include="ChatUserSerialization\CxStyle\RemoteAudioDevice.cpp" />
<ClCompile Include="ChatUserSerialization\CxStyle\RemoteChatUser.cpp" />
<ClCompile Include="Chat\ChatManagerEvents.cpp" />
<ClCompile Include="Chat\ChatManagerSettings.cpp" />
<ClCompile Include="Chat\ChatPerformance.cpp" />
<ClCompile Include="Utils\BufferUtils.cpp" />
<ClCompile Include="Utils\Clock.cpp" />
<ClCompile Include="Utils\ErrorUtils.cpp" />
<ClCompile Include="Utils\FactoryCache.cpp" />
<ClCompile Include="Utils\Thread.cpp" />
<ClCompile Include="Chat\ChatManager.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="Utils\StringUtils.cpp" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="ChatEvents\ChatEvents.rc" />
</ItemGroup>
<ItemGroup>
<None Include="ChatEvents\Microsoft-Xbox-GameChat-Events.man" />
</ItemGroup>
<ItemGroup Condition="'$(BuildingInsideVisualStudio)'=='true'">
<SDKReference Include="Xbox Services API, Version=8.0" />
</ItemGroup>
<ItemGroup Condition="'$(BuildingInsideVisualStudio)'!='true' And '$(TargetPlatform)' == 'sra'">
<Reference Include="Microsoft.Xbox.Services">
<HintPath>$(ObjectPath)\..\..\..\..\services\runtime\public\adk\$(ObjectDirectory)\Microsoft.Xbox.Services.winmd</HintPath>
<IsWinMDFile>true</IsWinMDFile>
<SpecificVersion>False</SpecificVersion>
</Reference>
</ItemGroup>
<ItemGroup Condition="'$(BuildingInsideVisualStudio)'!='true' And '$(TargetPlatform)' == 'lnm'">
<Reference Include="Microsoft.Xbox.Services">
<HintPath>$(ObjectPath)\..\..\..\..\services\runtime\public\xdk\$(ObjectDirectory)\Microsoft.Xbox.Services.winmd</HintPath>
<IsWinMDFile>true</IsWinMDFile>
<SpecificVersion>False</SpecificVersion>
</Reference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Import Project="Microsoft.Xbox.GameChat.internal.props" Condition="exists('Microsoft.Xbox.GameChat.internal.props') And '$(BuildingInsideVisualStudio)'!='true'" />
<Import Project="Build.$(Platform).IDE.Cpp.props" Condition="exists('Build.$(Platform).IDE.Cpp.props') And '$(BuildingInsideVisualStudio)'=='true'" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
@@ -0,0 +1,211 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="pch.cpp">
<Filter>Utils</Filter>
</ClCompile>
<ClCompile Include="Utils\Clock.cpp">
<Filter>Utils</Filter>
</ClCompile>
<ClCompile Include="Utils\ErrorUtils.cpp">
<Filter>Utils</Filter>
</ClCompile>
<ClCompile Include="Utils\BufferUtils.cpp">
<Filter>Utils</Filter>
</ClCompile>
<ClCompile Include="Utils\StringUtils.cpp">
<Filter>Utils</Filter>
</ClCompile>
<ClCompile Include="ChatUserSerialization\ComStyle\AudioDeviceInfo.cpp">
<Filter>ChatUserSerialization\ComStyle</Filter>
</ClCompile>
<ClCompile Include="ChatUserSerialization\ComStyle\AudioDevices.cpp">
<Filter>ChatUserSerialization\ComStyle</Filter>
</ClCompile>
<ClCompile Include="ChatUserSerialization\ComStyle\ChatPacker.cpp">
<Filter>ChatUserSerialization\ComStyle</Filter>
</ClCompile>
<ClCompile Include="ChatUserSerialization\ComStyle\ChatReadWriteBuffer.cpp">
<Filter>ChatUserSerialization\ComStyle</Filter>
</ClCompile>
<ClCompile Include="ChatUserSerialization\ComStyle\ChatUnpacker.cpp">
<Filter>ChatUserSerialization\ComStyle</Filter>
</ClCompile>
<ClCompile Include="ChatUserSerialization\ComStyle\RemoteUser.cpp">
<Filter>ChatUserSerialization\ComStyle</Filter>
</ClCompile>
<ClCompile Include="ChatUserSerialization\CxStyle\RemoteAudioDevice.cpp">
<Filter>ChatUserSerialization\CxStyle</Filter>
</ClCompile>
<ClCompile Include="ChatUserSerialization\CxStyle\RemoteChatUser.cpp">
<Filter>ChatUserSerialization\CxStyle</Filter>
</ClCompile>
<ClCompile Include="Chat\ChatManager.cpp">
<Filter>Chat</Filter>
</ClCompile>
<ClCompile Include="ChatClient\AudioDeviceIDMapper.cpp">
<Filter>ChatClient</Filter>
</ClCompile>
<ClCompile Include="ChatClient\ChatClient.cpp">
<Filter>ChatClient</Filter>
</ClCompile>
<ClCompile Include="Utils\Thread.cpp">
<Filter>Utils</Filter>
</ClCompile>
<ClCompile Include="ChatAudioThread\ChatAudioThread.cpp">
<Filter>ChatAudioThread</Filter>
</ClCompile>
<ClCompile Include="ChatNetwork\ChatNetwork.cpp">
<Filter>ChatNetwork</Filter>
</ClCompile>
<ClCompile Include="Chat\ChatManagerEvents.cpp">
<Filter>Chat</Filter>
</ClCompile>
<ClCompile Include="ChatClient\ChatUser.cpp">
<Filter>Chat</Filter>
</ClCompile>
<ClCompile Include="Chat\ChatManagerSettings.cpp">
<Filter>Chat</Filter>
</ClCompile>
<ClCompile Include="ChatAudioThread\JitterBuffer.cpp">
<Filter>ChatAudioThread</Filter>
</ClCompile>
<ClCompile Include="Chat\ChatPerformance.cpp">
<Filter>Chat</Filter>
</ClCompile>
<ClCompile Include="Utils\FactoryCache.cpp">
<Filter>Utils</Filter>
</ClCompile>
<ClCompile Include="ChatEvents\ChatDiagnostics.cpp">
<Filter>ChatEvents</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Utils\Clock.h">
<Filter>Utils</Filter>
</ClInclude>
<ClInclude Include="pch.h">
<Filter>Utils</Filter>
</ClInclude>
<ClInclude Include="Utils\ErrorUtils.h">
<Filter>Utils</Filter>
</ClInclude>
<ClInclude Include="Utils\BufferUtils.h">
<Filter>Utils</Filter>
</ClInclude>
<ClInclude Include="Utils\StringUtils.h">
<Filter>Utils</Filter>
</ClInclude>
<ClInclude Include="ChatUserSerialization\ComStyle\AudioDeviceInfo.h">
<Filter>ChatUserSerialization\ComStyle</Filter>
</ClInclude>
<ClInclude Include="ChatUserSerialization\ComStyle\AudioDevices.h">
<Filter>ChatUserSerialization\ComStyle</Filter>
</ClInclude>
<ClInclude Include="ChatUserSerialization\ComStyle\ChatPacker.h">
<Filter>ChatUserSerialization\ComStyle</Filter>
</ClInclude>
<ClInclude Include="ChatUserSerialization\ComStyle\ChatReadWriteBuffer.h">
<Filter>ChatUserSerialization\ComStyle</Filter>
</ClInclude>
<ClInclude Include="ChatUserSerialization\ComStyle\ChatUnpacker.h">
<Filter>ChatUserSerialization\ComStyle</Filter>
</ClInclude>
<ClInclude Include="ChatUserSerialization\ComStyle\RemoteUser.h">
<Filter>ChatUserSerialization\ComStyle</Filter>
</ClInclude>
<ClInclude Include="ChatUserSerialization\CxStyle\RemoteAudioDevice.h">
<Filter>ChatUserSerialization\CxStyle</Filter>
</ClInclude>
<ClInclude Include="ChatUserSerialization\CxStyle\RemoteChatUser.h">
<Filter>ChatUserSerialization\CxStyle</Filter>
</ClInclude>
<ClInclude Include="ChatUserSerialization\ComStyle\ChatUserSerializationCommon.h">
<Filter>ChatUserSerialization\ComStyle</Filter>
</ClInclude>
<ClInclude Include="Chat\ChatManagerEvents.h">
<Filter>Chat</Filter>
</ClInclude>
<ClInclude Include="Chat\ChatManager.h">
<Filter>Chat</Filter>
</ClInclude>
<ClInclude Include="ChatClient\AudioDeviceIDMapper.h">
<Filter>ChatClient</Filter>
</ClInclude>
<ClInclude Include="ChatClient\ChatClient.h">
<Filter>ChatClient</Filter>
</ClInclude>
<ClInclude Include="Utils\Thread.h">
<Filter>Utils</Filter>
</ClInclude>
<ClInclude Include="ChatAudioThread\ChatAudioThread.h">
<Filter>ChatAudioThread</Filter>
</ClInclude>
<ClInclude Include="ChatNetwork\ChatNetwork.h">
<Filter>ChatNetwork</Filter>
</ClInclude>
<ClInclude Include="ChatClient\ChatUser.h">
<Filter>Chat</Filter>
</ClInclude>
<ClInclude Include="Chat\ChatManagerSettings.h">
<Filter>Chat</Filter>
</ClInclude>
<ClInclude Include="ChatAudioThread\JitterBuffer.h">
<Filter>ChatAudioThread</Filter>
</ClInclude>
<ClInclude Include="Chat\ChatPerformance.h">
<Filter>Chat</Filter>
</ClInclude>
<ClInclude Include="Utils\FactoryCache.h">
<Filter>Utils</Filter>
</ClInclude>
<ClInclude Include="Utils\ChatMacros.h">
<Filter>Utils</Filter>
</ClInclude>
<ClInclude Include="ChatEvents\ChatEvents.h">
<Filter>ChatEvents</Filter>
</ClInclude>
<ClInclude Include="ChatEvents\ChatDiagnostics.h">
<Filter>ChatEvents</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="Utils">
<UniqueIdentifier>{50ed652a-0ed3-40e9-b25a-3fbb8b1cd5e5}</UniqueIdentifier>
</Filter>
<Filter Include="Chat">
<UniqueIdentifier>{ba39e991-f6c7-42b1-88f3-9a89a5af049b}</UniqueIdentifier>
</Filter>
<Filter Include="ChatUserSerialization">
<UniqueIdentifier>{5a44211e-7dde-467c-b0e1-54bbcdf76326}</UniqueIdentifier>
</Filter>
<Filter Include="ChatUserSerialization\ComStyle">
<UniqueIdentifier>{09e4adad-7018-489f-86e0-2b342dd2aae8}</UniqueIdentifier>
</Filter>
<Filter Include="ChatUserSerialization\CxStyle">
<UniqueIdentifier>{6eaff0f9-6e68-4ebd-92d3-63af3a085427}</UniqueIdentifier>
</Filter>
<Filter Include="ChatClient">
<UniqueIdentifier>{e424e73a-510f-43a8-b3af-8065414414b6}</UniqueIdentifier>
</Filter>
<Filter Include="ChatAudioThread">
<UniqueIdentifier>{63d6146f-cb8f-43b7-a3e6-0a0394227f4a}</UniqueIdentifier>
</Filter>
<Filter Include="ChatNetwork">
<UniqueIdentifier>{179e6613-d7bb-47c9-910a-01d33228e22f}</UniqueIdentifier>
</Filter>
<Filter Include="ChatEvents">
<UniqueIdentifier>{308a602a-776a-4e80-b0db-543e1895e763}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="ChatEvents\ChatEvents.rc">
<Filter>ChatEvents</Filter>
</ResourceCompile>
</ItemGroup>
<ItemGroup>
<None Include="ChatEvents\Microsoft-Xbox-GameChat-Events.man">
<Filter>ChatEvents</Filter>
</None>
</ItemGroup>
</Project>
+206
View File
@@ -0,0 +1,206 @@
//// 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 "BufferUtils.h"
#include "StringUtils.h"
#include "ErrorUtils.h"
#if TV_API
using namespace Platform;
using namespace Windows::Foundation;
using namespace Windows::Storage::Streams;
namespace Microsoft {
namespace Xbox {
namespace GameChat {
Windows::Storage::Streams::IBuffer^
BufferUtils::FastBufferCreate(
_In_ uint32 capacity,
_In_ Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IBufferFactory> bufferFactory
)
{
// All of this can be done with this C++/CX line:
// Windows::Storage::Streams::IBuffer^ cxBuffer = ref new Buffer(capacity);
//
// However there is a performance hit since inside this call is slow to lookup the activation factory by string.
// The caller should cache the result of GetActivationFactory and reuse it upon sequential calls.
//
// Create the factory like so:
// hr = GetActivationFactory(Microsoft::WRL::Wrappers::HStringReference( RuntimeClass_Windows_Storage_Streams_Buffer ).Get(), &bufferFactory);
CHAT_THROW_INVALIDARGUMENT_IF_NULL( bufferFactory );
HRESULT hr;
Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IBuffer> abiBuffer;
hr = bufferFactory->Create( capacity, &abiBuffer );
CHAT_THROW_IF_HR_FAILED( hr );
CHAT_THROW_E_POINTER_IF_NULL( abiBuffer );
Windows::Storage::Streams::IBuffer^ cxBuffer = reinterpret_cast<Windows::Storage::Streams::IBuffer^>(abiBuffer.Get());
return cxBuffer;
}
Windows::Storage::Streams::IBuffer^
BufferUtils::BufferCopy(
_In_ Windows::Storage::Streams::IBuffer^ source,
_In_ Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IBufferFactory> bufferFactory
)
{
CHAT_THROW_INVALIDARGUMENT_IF_NULL(source);
byte* srcBufferBytes = nullptr;
GetBufferBytes( source, &srcBufferBytes );
Windows::Storage::Streams::IBuffer^ destination = BufferUtils::FastBufferCreate(source->Length, bufferFactory);
byte* destBufferBytes = nullptr;
GetBufferBytes( destination, &destBufferBytes );
errno_t err = memcpy_s(
destBufferBytes,
destination->Capacity,
srcBufferBytes,
source->Length
);
// Params were validated prior to the memcpy_s call, so this should never happen
CHAT_THROW_HR_IF(err != 0, E_FAIL);
destination->Length = source->Length;
return destination;
}
void BufferUtils::GetBufferBytes(
_In_ Windows::Storage::Streams::IBuffer^ buffer,
_Outptr_ byte** ppOut
)
{
if ( ppOut == nullptr || buffer == nullptr )
{
throw ref new Platform::InvalidArgumentException();
}
*ppOut = nullptr;
Microsoft::WRL::ComPtr<IInspectable> srcBufferInspectable(reinterpret_cast<IInspectable*>( buffer ));
Microsoft::WRL::ComPtr<Windows::Storage::Streams::IBufferByteAccess> srcBufferByteAccess;
HRESULT hr = srcBufferInspectable.As(&srcBufferByteAccess);
CHAT_THROW_IF_HR_FAILED(hr);
hr = srcBufferByteAccess->Buffer(ppOut);
CHAT_THROW_IF_HR_FAILED(hr);
}
Windows::Storage::Streams::IBuffer^ BufferUtils::CreateBufferFromBytes(
_In_reads_bytes_(sourceByteBufferSize) byte* sourceByteBuffer,
_In_ UINT sourceByteBufferSize,
_In_ Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IBufferFactory> bufferFactory
)
{
Windows::Storage::Streams::IBuffer^ destBuffer = BufferUtils::FastBufferCreate(sourceByteBufferSize, bufferFactory);
byte* destBufferBytes = nullptr;
GetBufferBytes( destBuffer, &destBufferBytes );
errno_t err = memcpy_s( destBufferBytes, destBuffer->Capacity, sourceByteBuffer, sourceByteBufferSize );
CHAT_THROW_HR_IF(err != 0, E_FAIL);
destBuffer->Length = sourceByteBufferSize;
return destBuffer;
}
Platform::String^
BufferUtils::GetBase64String(
_In_ Windows::Storage::Streams::IBuffer^ buffer
)
{
if( buffer == nullptr )
{
return L"";
}
UINT32 length = buffer->Length;
if ( !length )
{
return L"";
}
// Read off the buffer, encode in base64, and push onto the string.
auto dataReader = Windows::Storage::Streams::DataReader::FromBuffer( buffer );
Platform::String^ base64EncodedConnectivityInfo = ref new Platform::String(L"");
static const char s_chBase64EncodingTable[64] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q',
'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y',
'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' };
while (length >= 3)
{
UINT8 byte0 = dataReader->ReadByte();
UINT8 byte1 = dataReader->ReadByte();
UINT8 byte2 = dataReader->ReadByte();
wchar_t Digit[5];
UCHAR index0 = (byte0 >> 2);
UCHAR index1 = ((byte0 & 3/*00000011*/) << 4) + ((byte1 & 240/*11110000*/) >> 4);
UCHAR index2 = ((byte2 & 192/*11000000*/) >> 6) + ((byte1 & 15/*00001111*/) << 2);
UCHAR index3 = (byte2 & 63/*00111111*/ );
Digit[0] = s_chBase64EncodingTable[index0];
Digit[1] = s_chBase64EncodingTable[index1];
Digit[2] = s_chBase64EncodingTable[index2];
Digit[3] = s_chBase64EncodingTable[index3];
Digit[4] = 0;
base64EncodedConnectivityInfo += ref new Platform::String(Digit);
length -= 3;
}
if(length == 1) // 2 equals
{
UINT8 byte0 = dataReader->ReadByte();
UINT8 byte1 = 0;
wchar_t Digit[5];
UCHAR index0 = (byte0 >> 2);
UCHAR index1 = ((byte0 & 3/*00000011*/) << 4) + ((byte1 & 240/*11110000*/) >> 4);
Digit[0] = s_chBase64EncodingTable[index0];
Digit[1] = s_chBase64EncodingTable[index1];
Digit[2] = L'=';
Digit[3] = L'=';
Digit[4] = 0;
base64EncodedConnectivityInfo += ref new Platform::String(Digit);
}
else if(length == 2) // 1 equals
{
UINT8 byte0 = dataReader->ReadByte();
UINT8 byte1 = dataReader->ReadByte();
UINT8 byte2 = 0;
wchar_t Digit[5];
UCHAR index0 = (byte0 >> 2);
UCHAR index1 = ((byte0 & 3/*00000011*/) << 4) + ((byte1 & 240/*11110000*/) >> 4);
UCHAR index2 = ((byte2 & 192/*11000000*/) >> 6) + ((byte1 & 15/*00001111*/) << 2);
Digit[0] = s_chBase64EncodingTable[index0];
Digit[1] = s_chBase64EncodingTable[index1];
Digit[2] = s_chBase64EncodingTable[index2];
Digit[3] = L'=';
Digit[4] = 0;
base64EncodedConnectivityInfo += ref new Platform::String(Digit);
}
return base64EncodedConnectivityInfo;
}
int
BufferUtils::GetLength(
_In_ Windows::Storage::Streams::IBuffer^ buffer
)
{
return ( buffer != nullptr ) ? buffer->Length : 0;
}
}}}
#endif
+66
View File
@@ -0,0 +1,66 @@
//// 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 {
private class BufferUtils
{
public:
static Windows::Storage::Streams::IBuffer^ FastBufferCreate(
_In_ uint32 capacity,
_In_ Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IBufferFactory> bufferFactory
);
/// <summary>
/// Creates a copy of a buffer from an existing buffer
/// </summary>
/// <param name="source">The buffer to copy</param>
/// <returns>A new buffer that contains the contents of the input buffer</returns>
static Windows::Storage::Streams::IBuffer^ BufferCopy(
_In_ Windows::Storage::Streams::IBuffer^ source,
_In_ Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IBufferFactory> bufferFactory
);
/// <summary>
/// Gets a byte* from a buffer
/// </summary>
/// <param name="buffer">The buffer to get the byte* from</param>
/// <param name="ppOut">A pointer to a byte* that will be set</param>
static void GetBufferBytes(
_In_ Windows::Storage::Streams::IBuffer^ buffer,
_Outptr_ byte** ppOut
);
/// <summary>
/// Creates a new buffer from a byte array
/// </summary>
/// <param name="sourceByteBuffer">The input byte array</param>
/// <param name="sourceByteBufferSize">The length of the sourceByteBuffer in number of bytes</param>
/// <returns>A new buffer that contains the contents of byte array</returns>
static Windows::Storage::Streams::IBuffer^ CreateBufferFromBytes(
_In_reads_bytes_(sourceByteBufferSize) byte* sourceByteBuffer,
_In_ UINT sourceByteBufferSize,
_In_ Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IBufferFactory> bufferFactory
);
static Platform::String^ GetBase64String(
_In_ Windows::Storage::Streams::IBuffer^ buffer
);
static int GetLength(
_In_ Windows::Storage::Streams::IBuffer^ buffer
);
};
}}}
#endif
+53
View File
@@ -0,0 +1,53 @@
//// 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
#define TEXTW(quote) _TEXTW(quote)
#define _TEXTW(quote) L##quote
#define CHAT_LOG_EXCEPTION(hr) Microsoft::Xbox::GameChat::StringUtils::GetLogExceptionDebugInfo(hr, TEXTW(__FUNCTION__), TEXTW(__FILE__), __LINE__ );
#define CHAT_LOG_INFO_MSG(msg) { if (m_chatManagerSettings->IsAtDiagnosticsTraceLevel(GameChatDiagnosticsTraceLevel::Info)) { LogComment( msg ); } }
#define CHAT_LOG_ERROR_MSG(msg) { if (m_chatManagerSettings->IsAtDiagnosticsTraceLevel(GameChatDiagnosticsTraceLevel::Error)) { LogComment( msg ); } }
#define CHAT_LOG_VERBOSE_MSG(msg) { if (m_chatManagerSettings->IsAtDiagnosticsTraceLevel(GameChatDiagnosticsTraceLevel::Verbose)) { LogComment( msg ); } }
#define CHAT_THROW_INVALIDARGUMENT_IF(x) if ( x ) { CHAT_LOG_EXCEPTION(E_INVALIDARG); throw ref new Platform::InvalidArgumentException(); }
#define CHAT_THROW_INVALIDARGUMENT_IF_NULL(x) if ( ( x ) == nullptr ) { CHAT_LOG_EXCEPTION(E_INVALIDARG); throw ref new Platform::InvalidArgumentException(); }
#define CHAT_THROW_E_POINTER_IF_NULL(x) if ( ( x ) == nullptr ) { CHAT_LOG_EXCEPTION(E_POINTER); throw ref new Platform::COMException(E_POINTER); }
#define CHAT_THROW_E_POINTER_IF_NULL_WITH_LOG(x,msg) if ( ( x ) == nullptr ) { CHAT_LOG_ERROR_MSG( msg ); CHAT_LOG_EXCEPTION(E_POINTER); throw ref new Platform::COMException(E_POINTER); }
#define CHAT_THROW_INVALIDARGUMENT_IF_STRING_EMPTY(x) { auto y = x; if ( y->IsEmpty() ) { CHAT_LOG_EXCEPTION(E_INVALIDARG); throw ref new Platform::InvalidArgumentException(); } }
#define CHAT_THROW_IF_HR_FAILED(hr) { HRESULT hr2 = hr; if ( FAILED( hr2 ) ) { CHAT_LOG_EXCEPTION(hr2); throw ref new Platform::COMException(hr2); } }
#define CHAT_THROW_HR(hr) { CHAT_LOG_EXCEPTION(hr); throw ref new Platform::COMException(hr); }
#define CHAT_THROW_HR_IF(x,hr) if ( x ) { CHAT_LOG_EXCEPTION(hr); throw ref new Platform::COMException(hr); }
#define CHAT_THROW_WIN32_IF(x,e) if ( x ) { HRESULT hr = __HRESULT_FROM_WIN32(e); CHAT_LOG_EXCEPTION(hr); throw ref new Platform::COMException(hr); }
#ifndef E_INVALIDARG_IF
#define E_INVALIDARG_IF(x) if ( x ) { return E_INVALIDARG; }
#endif
#ifndef E_POINTER_IF_NULL
#define E_POINTER_IF_NULL(x) if ( ( x ) == nullptr ) { return E_POINTER; }
#endif
#ifndef E_POINTER_OR_INVALIDARG_IF_STRING_EMPTY
#define E_POINTER_OR_INVALIDARG_IF_STRING_EMPTY(x) { auto y = x; E_POINTER_IF_NULL( y ); E_INVALIDARG_IF( wcslen( y ) == 0 ); }
#endif
#ifndef CHECKHR_EXIT
#define CHECKHR_EXIT(hrResult) { hr = hrResult; if ( FAILED( hr ) ) { goto exit; } }
#endif
#define TV_API (WINAPI_FAMILY == WINAPI_FAMILY_TV_APP | WINAPI_FAMILY == WINAPI_FAMILY_TV_TITLE)
#ifdef PRERELEASE
#define PUBLIC_ONLY_IN_PRERELEASE public
#define PUBLIC_ONLY_IN_PRERELEASE_INTERNAL_OTHERWISE public
#else
#define PUBLIC_ONLY_IN_PRERELEASE private
#define PUBLIC_ONLY_IN_PRERELEASE_INTERNAL_OTHERWISE internal
#endif
#ifdef INTERNAL_BUILD
#define PUBLIC_ONLY_IN_DEVKIT private
#else
#define PUBLIC_ONLY_IN_DEVKIT public
#endif
+112
View File
@@ -0,0 +1,112 @@
//// 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 "Clock.h"
#if TV_API
namespace Microsoft {
namespace Xbox {
namespace GameChat {
Clock::Clock() :
m_heartbeats(0)
{
m_timerFrequency.QuadPart = 0;
m_timerStart.QuadPart = 0;
m_countPerPeriod.QuadPart = 0;
}
void Clock::Initialize( UINT uPeriodMS )
{
if (!QueryPerformanceFrequency(&m_timerFrequency))
{
assert(false);
}
SetInterval(uPeriodMS);
}
void Clock::SetInterval( UINT uPeriodMS )
{
m_countPerPeriod.QuadPart = ( m_timerFrequency.QuadPart * uPeriodMS ) / c_uOneSecondInMS;
}
void Clock::SleepUntilNextHeartbeat()
{
LARGE_INTEGER timeOfNextHeartbeat = GetNextHeartbeat();
LARGE_INTEGER timeNow;
m_heartbeats++; // so that the next one will be 20ms (or whatever) later...
if (!QueryPerformanceCounter(&timeNow))
{
assert(false);
}
LONGLONG timeDelta = timeOfNextHeartbeat.QuadPart - timeNow.QuadPart;
if ( timeDelta > 0 )
{
LONGLONG numberOfMilliSecondsSinceLast = c_uOneSecondInMS * timeDelta / m_timerFrequency.QuadPart;
WCHAR text[200] = {0};
swprintf_s( text, L"I Slept: %I64d", numberOfMilliSecondsSinceLast );
OutputDebugString( text );
DWORD dwSleepTime = static_cast<DWORD>(numberOfMilliSecondsSinceLast);
Sleep( dwSleepTime );
}
}
DWORD Clock::WaitForEventsOrHeartbeat( HANDLE hObject, HANDLE hObject2 )
{
LARGE_INTEGER liExpected = GetNextHeartbeat();
LARGE_INTEGER liNow;
m_heartbeats++; // so that the next one will be 20ms (or whatever) later...
if (!QueryPerformanceCounter(&liNow))
{
assert(false);
}
LONGLONG llDiff = liExpected.QuadPart - liNow.QuadPart;
HANDLE aObjects[2];
aObjects[0] = hObject;
aObjects[1] = hObject2;
if ( llDiff > 0 )
{
DWORD dwSleepTime = static_cast<DWORD>(c_uOneSecondInMS * llDiff / m_timerFrequency.QuadPart);
return ::WaitForMultipleObjectsEx( 2, aObjects, false, dwSleepTime, false );
}
// we're already late, no need to wait, but we still need to test the object
return ::WaitForMultipleObjectsEx( 2, aObjects, false, 0, false );
}
LARGE_INTEGER Clock::GetNextHeartbeat()
{
LARGE_INTEGER timeExpected;
// Update Start
if ( m_timerStart.QuadPart == 0 )
{
if (!QueryPerformanceCounter( &m_timerStart ))
{
assert(false);
}
m_heartbeats = 1; // we are on the very first heartbeat
}
timeExpected.QuadPart = m_timerStart.QuadPart + (m_countPerPeriod.QuadPart * m_heartbeats);
return timeExpected;
}
}}}
#endif
+40
View File
@@ -0,0 +1,40 @@
//// 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 {
class Clock
{
public:
Clock();
void Initialize( UINT uPeriodMS );
void SetInterval( UINT uPeriodMS );
void SleepUntilNextHeartbeat();
DWORD WaitForEventsOrHeartbeat(HANDLE hObject, HANDLE hObject2);
private:
static const UINT c_uOneSecondInMS = 1000;
UINT m_heartbeats;
LARGE_INTEGER m_timerFrequency;
LARGE_INTEGER m_timerStart;
LARGE_INTEGER m_countPerPeriod;
LARGE_INTEGER GetNextHeartbeat();
};
}}}
#endif
+546
View File
@@ -0,0 +1,546 @@
//// 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 "ErrorUtils.h"
#if TV_API
using namespace Platform;
using namespace Windows::Foundation;
using namespace Windows::Storage::Streams;
namespace Microsoft {
namespace Xbox {
namespace GameChat {
enum WebErrorStatus
{
WebErrorStatus_Unknown = 0,
WebErrorStatus_CertificateCommonNameIsIncorrect = 1,
WebErrorStatus_CertificateExpired = 2,
WebErrorStatus_CertificateContainsErrors = 3,
WebErrorStatus_CertificateRevoked = 4,
WebErrorStatus_CertificateIsInvalid = 5,
WebErrorStatus_ServerUnreachable = 6,
WebErrorStatus_Timeout = 7,
WebErrorStatus_ErrorHttpInvalidServerResponse = 8,
WebErrorStatus_ConnectionAborted = 9,
WebErrorStatus_ConnectionReset = 10,
WebErrorStatus_Disconnected = 11,
WebErrorStatus_HttpToHttpsOnRedirection = 12,
WebErrorStatus_HttpsToHttpOnRedirection = 13,
WebErrorStatus_CannotConnect = 14,
WebErrorStatus_HostNameNotResolved = 15,
WebErrorStatus_OperationCanceled = 16,
WebErrorStatus_RedirectFailed = 17,
WebErrorStatus_UnexpectedStatusCode = 18,
WebErrorStatus_UnexpectedRedirection = 19,
WebErrorStatus_UnexpectedClientError = 20,
WebErrorStatus_UnexpectedServerError = 21,
WebErrorStatus_MultipleChoices = 300,
WebErrorStatus_MovedPermanently = 301,
WebErrorStatus_Found = 302,
WebErrorStatus_SeeOther = 303,
WebErrorStatus_NotModified = 304,
WebErrorStatus_UseProxy = 305,
WebErrorStatus_TemporaryRedirect = 307,
WebErrorStatus_BadRequest = 400,
WebErrorStatus_Unauthorized = 401,
WebErrorStatus_PaymentRequired = 402,
WebErrorStatus_Forbidden = 403,
WebErrorStatus_NotFound = 404,
WebErrorStatus_MethodNotAllowed = 405,
WebErrorStatus_NotAcceptable = 406,
WebErrorStatus_ProxyAuthenticationRequired = 407,
WebErrorStatus_RequestTimeout = 408,
WebErrorStatus_Conflict = 409,
WebErrorStatus_Gone = 410,
WebErrorStatus_LengthRequired = 411,
WebErrorStatus_PreconditionFailed = 412,
WebErrorStatus_RequestEntityTooLarge = 413,
WebErrorStatus_RequestUriTooLong = 414,
WebErrorStatus_UnsupportedMediaType = 415,
WebErrorStatus_RequestedRangeNotSatisfiable = 416,
WebErrorStatus_ExpectationFailed = 417,
WebErrorStatus_InternalServerError = 500,
WebErrorStatus_NotImplemented = 501,
WebErrorStatus_BadGateway = 502,
WebErrorStatus_ServiceUnavailable = 503,
WebErrorStatus_GatewayTimeout = 504,
WebErrorStatus_HttpVersionNotSupported = 505
};
HRESULT
ErrorUtils::ConvertHttpStatusCodeToHR(
_In_ uint32 httpStatusCode
)
{
HRESULT hr = HTTP_E_STATUS_UNEXPECTED;
// 2xx are http success codes
if ((httpStatusCode >= 200) && (httpStatusCode < 300))
{
hr = S_OK;
}
// MSXML XHR bug: get_status() returns HTTP/1223 for HTTP/204:
// http://blogs.msdn.com/b/ieinternals/archive/2009/07/23/the-ie8-native-xmlhttprequest-object.aspx
// treat it as success code as well
else if ( httpStatusCode == 1223 )
{
hr = S_OK;
}
else
{
switch(httpStatusCode)
{
//
// 101 server has switched protocols in upgrade header. Not suppose to happen in product.
//
case 101:
hr = INET_E_UNKNOWN_PROTOCOL;
break;
//
// 300 Multiple Choices
//
case WebErrorStatus::WebErrorStatus_MultipleChoices:
hr = HTTP_E_STATUS_AMBIGUOUS;
break;
//
// 301 Moved Permanently
//
case WebErrorStatus::WebErrorStatus_MovedPermanently:
hr = HTTP_E_STATUS_MOVED;
break;
//
// 302 Found
//
case WebErrorStatus::WebErrorStatus_Found:
hr = HTTP_E_STATUS_REDIRECT;
break;
//
// 303 See Other
//
case WebErrorStatus::WebErrorStatus_SeeOther:
hr = HTTP_E_STATUS_REDIRECT_METHOD;
break;
//
// 304 Not Modified
//
case WebErrorStatus::WebErrorStatus_NotModified:
hr = HTTP_E_STATUS_NOT_MODIFIED;
break;
//
// 305 Use Proxy
//
case WebErrorStatus::WebErrorStatus_UseProxy:
hr = HTTP_E_STATUS_USE_PROXY;
break;
//
// 307 Temporary Redirect
//
case WebErrorStatus::WebErrorStatus_TemporaryRedirect:
hr = HTTP_E_STATUS_REDIRECT_KEEP_VERB;
break;
//
// 400 Bad Request
//
case WebErrorStatus::WebErrorStatus_BadRequest:
hr = HTTP_E_STATUS_BAD_REQUEST;
break;
//
// 401 Unauthorized
//
case WebErrorStatus::WebErrorStatus_Unauthorized:
hr = HTTP_E_STATUS_DENIED;
break;
//
// 402 Payment Required
//
case WebErrorStatus::WebErrorStatus_PaymentRequired:
hr = HTTP_E_STATUS_PAYMENT_REQ;
break;
//
// 403 Forbidden.
//
case WebErrorStatus::WebErrorStatus_Forbidden:
hr = HTTP_E_STATUS_FORBIDDEN;
break;
//
// 404 Not Found.
//
case WebErrorStatus::WebErrorStatus_NotFound:
hr = HTTP_E_STATUS_NOT_FOUND;
break;
//
// 405 Method Not Allowed
//
case WebErrorStatus::WebErrorStatus_MethodNotAllowed:
hr = HTTP_E_STATUS_BAD_METHOD;
break;
//
// 406 Not Acceptable
//
case WebErrorStatus::WebErrorStatus_NotAcceptable:
hr = HTTP_E_STATUS_NONE_ACCEPTABLE;
break;
//
// 407 Proxy Authentication Required
//
case WebErrorStatus::WebErrorStatus_ProxyAuthenticationRequired:
hr = HTTP_E_STATUS_PROXY_AUTH_REQ;
break;
//
// 408 Request Timeout
//
case WebErrorStatus::WebErrorStatus_RequestTimeout:
hr = HTTP_E_STATUS_REQUEST_TIMEOUT;
break;
//
// 409 Conflict
//
case WebErrorStatus::WebErrorStatus_Conflict:
hr = HTTP_E_STATUS_CONFLICT;
break;
//
// 410 Gone
//
case WebErrorStatus::WebErrorStatus_Gone:
hr = HTTP_E_STATUS_GONE;
break;
//
// 411 Length Required
//
case WebErrorStatus::WebErrorStatus_LengthRequired:
hr = HTTP_E_STATUS_LENGTH_REQUIRED;
break;
//
// 412 Precondition Failed
//
case WebErrorStatus::WebErrorStatus_PreconditionFailed:
hr = HTTP_E_STATUS_PRECOND_FAILED;
break;
//
// 413 Request Entity Too Large
//
case WebErrorStatus::WebErrorStatus_RequestEntityTooLarge:
hr = HTTP_E_STATUS_REQUEST_TOO_LARGE;
break;
//
// 414 Request URI Too Long
//
case WebErrorStatus::WebErrorStatus_RequestUriTooLong:
hr = HTTP_E_STATUS_URI_TOO_LONG;
break;
//
// 415 Unsupported Media Type
//
case WebErrorStatus::WebErrorStatus_UnsupportedMediaType:
hr = HTTP_E_STATUS_UNSUPPORTED_MEDIA;
break;
//
// 416 Requested Range Not Satisfiable
//
case WebErrorStatus::WebErrorStatus_RequestedRangeNotSatisfiable:
hr = HTTP_E_STATUS_RANGE_NOT_SATISFIABLE;
break;
//
// 417 Expectation Failed
//
case WebErrorStatus::WebErrorStatus_ExpectationFailed:
hr = HTTP_E_STATUS_EXPECTATION_FAILED;
break;
//
// 449 Retry after doing the appropriate action.
//
case 449:
hr = HTTP_E_STATUS_BAD_REQUEST;
break;
//
// 500 Internal Server Error
//
case WebErrorStatus::WebErrorStatus_InternalServerError:
hr = HTTP_E_STATUS_SERVER_ERROR;
break;
//
// 501 Not Implemented
//
case WebErrorStatus::WebErrorStatus_NotImplemented:
hr = HTTP_E_STATUS_NOT_SUPPORTED;
break;
//
// 502 Bad Gateway
//
case WebErrorStatus::WebErrorStatus_BadGateway:
hr = HTTP_E_STATUS_BAD_GATEWAY;
break;
//
// 503 Service Unavailable
//
case WebErrorStatus::WebErrorStatus_ServiceUnavailable:
hr = HTTP_E_STATUS_SERVICE_UNAVAIL;
break;
//
// 504 Gateway Timeout
//
case WebErrorStatus::WebErrorStatus_GatewayTimeout:
hr = HTTP_E_STATUS_GATEWAY_TIMEOUT;
break;
//
// 505 HTTP Version Not Supported.
//
case WebErrorStatus::WebErrorStatus_HttpVersionNotSupported:
hr = HTTP_E_STATUS_VERSION_NOT_SUP;
break;
default:
hr = HTTP_E_STATUS_UNEXPECTED;
break;
}
}
return hr;
}
HRESULT
ErrorUtils::ConvertExceptionToHRESULT()
{
// Default value, if there is no exception appears, return S_OK
HRESULT hr = S_OK;
try
{
throw;
}
// std exceptions
catch( const std::bad_alloc& ) // is an exception
{
hr = E_OUTOFMEMORY;
}
catch( const std::invalid_argument& ) // is a logic_error
{
hr = E_INVALIDARG;
}
catch( const std::domain_error& ) // is a logic_error
{
hr = E_INVALIDARG;
}
catch( const std::out_of_range& ) // is a logic_error
{
hr = E_INVALIDARG;
}
catch( const std::length_error& ) // is a logic_error
{
hr = __HRESULT_FROM_WIN32( ERROR_ARITHMETIC_OVERFLOW );
}
catch( const std::overflow_error& ) // is a runtime_error
{
hr = __HRESULT_FROM_WIN32( ERROR_ARITHMETIC_OVERFLOW );
}
catch( const std::underflow_error& ) // is a runtime_error
{
hr = RPC_S_FP_UNDERFLOW;
}
catch( const std::range_error& ) // is a runtime_error
{
hr = E_INVALIDARG;
}
catch ( const std::logic_error& ) // is an exception
{
hr = E_UNEXPECTED;
}
catch ( const std::runtime_error& ) // is an exception
{
hr = E_FAIL;
}
catch ( const std::exception& ) // base class for standard C++ exceptions
{
hr = E_FAIL;
}
catch ( Platform::Exception^ exception )
{
hr = (HRESULT)exception->HResult;
}
catch ( HRESULT exceptionHR )
{
hr = exceptionHR;
}
catch (...) // everything else
{
hr = E_FAIL;
}
return hr;
}
Platform::String^ ErrorUtils::ConvertHResultToString( HRESULT hr )
{
WCHAR tmp[ 1024 ];
_snwprintf_s( tmp, _countof( tmp ), _TRUNCATE, L"0x%0.8x", hr);
return ref new Platform::String(tmp);
}
Platform::String^ ErrorUtils::ConvertHResultToErrorName( HRESULT hr )
{
switch( hr )
{
// Generic errors
case S_OK: return L"S_OK";
case S_FALSE: return L"S_FALSE";
case E_OUTOFMEMORY: return L"E_OUTOFMEMORY";
case E_ACCESSDENIED: return L"E_ACCESSDENIED";
case E_INVALIDARG: return L"E_INVALIDARG";
case E_UNEXPECTED: return L"E_UNEXPECTED";
case E_ABORT: return L"E_ABORT";
case E_FAIL: return L"E_FAIL";
case E_NOTIMPL: return L"E_NOTIMPL";
case E_ILLEGAL_METHOD_CALL: return L"E_ILLEGAL_METHOD_CALL";
// Authentication specific errors
case 0x87DD0003: return L"AM_E_XASD_UNEXPECTED";
case 0x87DD0004: return L"AM_E_XASU_UNEXPECTED";
case 0x87DD0005: return L"AM_E_XAST_UNEXPECTED";
case 0x87DD0006: return L"AM_E_XSTS_UNEXPECTED";
case 0x87DD0007: return L"AM_E_XDEVICE_UNEXPECTED";
case 0x87DD0008: return L"AM_E_DEVMODE_NOT_AUTHORIZED";
case 0x87DD0009: return L"AM_E_NOT_AUTHORIZED";
case 0x87DD000A: return L"AM_E_FORBIDDEN";
case 0x87DD000B: return L"AM_E_UNKNOWN_TARGET";
case 0x87DD000C: return L"AM_E_NSAL_READ_FAILED";
case 0x87DD000D: return L"AM_E_TITLE_NOT_AUTHENTICATED";
case 0x87DD000E: return L"AM_E_TITLE_NOT_AUTHORIZED";
case 0x87DD000F: return L"AM_E_DEVICE_NOT_AUTHENTICATED";
case 0x87DD0010: return L"AM_E_INVALID_USER_INDEX";
case 0x8015DC00: return L"XO_E_DEVMODE_NOT_AUTHORIZED";
case 0x8015DC01: return L"XO_E_SYSTEM_UPDATE_REQUIRED";
case 0x8015DC02: return L"XO_E_CONTENT_UPDATE_REQUIRED";
case 0x8015DC03: return L"XO_E_ENFORCEMENT_BAN";
case 0x8015DC04: return L"XO_E_THIRD_PARTY_BAN";
case 0x8015DC05: return L"XO_E_ACCOUNT_PARENTALLY_RESTRICTED";
case 0x8015DC06: return L"XO_E_DEVICE_SUBSCRIPTION_NOT_ACTIVATED";
case 0x8015DC08: return L"XO_E_ACCOUNT_BILLING_MAINTENANCE_REQUIRED";
case 0x8015DC09: return L"XO_E_ACCOUNT_CREATION_REQUIRED";
case 0x8015DC0A: return L"XO_E_ACCOUNT_TERMS_OF_USE_NOT_ACCEPTED";
case 0x8015DC0B: return L"XO_E_ACCOUNT_COUNTRY_NOT_AUTHORIZED";
case 0x8015DC0C: return L"XO_E_ACCOUNT_AGE_VERIFICATION_REQUIRED";
case 0x8015DC0D: return L"XO_E_ACCOUNT_CURFEW";
case 0x8015DC0E: return L"XO_E_ACCOUNT_ZEST_MAINTENANCE_REQUIRED";
case 0x8015DC0F: return L"XO_E_ACCOUNT_CSV_TRANSITION_REQUIRED";
case 0x8015DC10: return L"XO_E_ACCOUNT_MAINTENANCE_REQUIRED";
case 0x8015DC11: return L"XO_E_ACCOUNT_TYPE_NOT_ALLOWED";
case 0x8015DC12: return L"XO_E_CONTENT_ISOLATION (Verify SCID / Sandbox)";
case 0x8015DC13: return L"XO_E_ACCOUNT_NAME_CHANGE_REQUIRED";
case 0x8015DC14: return L"XO_E_DEVICE_CHALLENGE_REQUIRED";
case 0x8015DC20: return L"XO_E_EXPIRED_DEVICE_TOKEN";
case 0x8015DC21: return L"XO_E_EXPIRED_TITLE_TOKEN";
case 0x8015DC22: return L"XO_E_EXPIRED_USER_TOKEN";
case 0x8015DC23: return L"XO_E_INVALID_DEVICE_TOKEN";
case 0x8015DC24: return L"XO_E_INVALID_TITLE_TOKEN";
case 0x8015DC25: return L"XO_E_INVALID_USER_TOKEN";
// HTTP specific errors
case WEB_E_UNSUPPORTED_FORMAT: return L"WEB_E_UNSUPPORTED_FORMAT";
case WEB_E_INVALID_XML: return L"WEB_E_INVALID_XML";
case WEB_E_MISSING_REQUIRED_ELEMENT: return L"WEB_E_MISSING_REQUIRED_ELEMENT";
case WEB_E_MISSING_REQUIRED_ATTRIBUTE: return L"WEB_E_MISSING_REQUIRED_ATTRIBUTE";
case WEB_E_UNEXPECTED_CONTENT: return L"WEB_E_UNEXPECTED_CONTENT";
case WEB_E_RESOURCE_TOO_LARGE: return L"WEB_E_RESOURCE_TOO_LARGE";
case WEB_E_INVALID_JSON_STRING: return L"WEB_E_INVALID_JSON_STRING";
case WEB_E_INVALID_JSON_NUMBER: return L"WEB_E_INVALID_JSON_NUMBER";
case WEB_E_JSON_VALUE_NOT_FOUND: return L"WEB_E_JSON_VALUE_NOT_FOUND";
case HTTP_E_STATUS_UNEXPECTED: return L"HTTP_E_STATUS_UNEXPECTED";
case HTTP_E_STATUS_UNEXPECTED_REDIRECTION: return L"HTTP_E_STATUS_UNEXPECTED_REDIRECTION";
case HTTP_E_STATUS_UNEXPECTED_CLIENT_ERROR: return L"HTTP_E_STATUS_UNEXPECTED_CLIENT_ERROR";
case HTTP_E_STATUS_UNEXPECTED_SERVER_ERROR: return L"HTTP_E_STATUS_UNEXPECTED_SERVER_ERROR";
case HTTP_E_STATUS_AMBIGUOUS: return L"HTTP_E_STATUS_AMBIGUOUS";
case HTTP_E_STATUS_MOVED: return L"HTTP_E_STATUS_MOVED";
case HTTP_E_STATUS_REDIRECT: return L"HTTP_E_STATUS_REDIRECT";
case HTTP_E_STATUS_REDIRECT_METHOD: return L"HTTP_E_STATUS_REDIRECT_METHOD";
case HTTP_E_STATUS_NOT_MODIFIED: return L"HTTP_E_STATUS_NOT_MODIFIED";
case HTTP_E_STATUS_USE_PROXY: return L"HTTP_E_STATUS_USE_PROXY";
case HTTP_E_STATUS_REDIRECT_KEEP_VERB: return L"HTTP_E_STATUS_REDIRECT_KEEP_VERB";
case HTTP_E_STATUS_BAD_REQUEST: return L"HTTP_E_STATUS_BAD_REQUEST";
case HTTP_E_STATUS_DENIED: return L"HTTP_E_STATUS_DENIED";
case HTTP_E_STATUS_PAYMENT_REQ: return L"HTTP_E_STATUS_PAYMENT_REQ";
case HTTP_E_STATUS_FORBIDDEN: return L"HTTP_E_STATUS_FORBIDDEN";
case HTTP_E_STATUS_NOT_FOUND: return L"HTTP_E_STATUS_NOT_FOUND";
case HTTP_E_STATUS_BAD_METHOD: return L"HTTP_E_STATUS_BAD_METHOD";
case HTTP_E_STATUS_NONE_ACCEPTABLE: return L"HTTP_E_STATUS_NONE_ACCEPTABLE";
case HTTP_E_STATUS_PROXY_AUTH_REQ: return L"HTTP_E_STATUS_PROXY_AUTH_REQ";
case HTTP_E_STATUS_REQUEST_TIMEOUT: return L"HTTP_E_STATUS_REQUEST_TIMEOUT";
case HTTP_E_STATUS_CONFLICT: return L"HTTP_E_STATUS_CONFLICT";
case HTTP_E_STATUS_GONE: return L"HTTP_E_STATUS_GONE";
case HTTP_E_STATUS_LENGTH_REQUIRED: return L"HTTP_E_STATUS_LENGTH_REQUIRED";
case HTTP_E_STATUS_PRECOND_FAILED: return L"HTTP_E_STATUS_PRECOND_FAILED";
case HTTP_E_STATUS_REQUEST_TOO_LARGE: return L"HTTP_E_STATUS_REQUEST_TOO_LARGE";
case HTTP_E_STATUS_URI_TOO_LONG: return L"HTTP_E_STATUS_URI_TOO_LONG";
case HTTP_E_STATUS_UNSUPPORTED_MEDIA: return L"HTTP_E_STATUS_UNSUPPORTED_MEDIA";
case HTTP_E_STATUS_RANGE_NOT_SATISFIABLE: return L"HTTP_E_STATUS_RANGE_NOT_SATISFIABLE";
case HTTP_E_STATUS_EXPECTATION_FAILED: return L"HTTP_E_STATUS_EXPECTATION_FAILED";
case HTTP_E_STATUS_SERVER_ERROR: return L"HTTP_E_STATUS_SERVER_ERROR";
case HTTP_E_STATUS_NOT_SUPPORTED: return L"HTTP_E_STATUS_NOT_SUPPORTED";
case HTTP_E_STATUS_BAD_GATEWAY: return L"HTTP_E_STATUS_BAD_GATEWAY";
case HTTP_E_STATUS_SERVICE_UNAVAIL: return L"HTTP_E_STATUS_SERVICE_UNAVAIL";
case HTTP_E_STATUS_GATEWAY_TIMEOUT: return L"HTTP_E_STATUS_GATEWAY_TIMEOUT";
case HTTP_E_STATUS_VERSION_NOT_SUP: return L"HTTP_E_STATUS_VERSION_NOT_SUP";
// WinINet specific errors
case INET_E_INVALID_URL: return L"INET_E_INVALID_URL";
case INET_E_NO_SESSION: return L"INET_E_NO_SESSION";
case INET_E_CANNOT_CONNECT: return L"INET_E_CANNOT_CONNECT";
case INET_E_RESOURCE_NOT_FOUND: return L"INET_E_RESOURCE_NOT_FOUND";
case INET_E_OBJECT_NOT_FOUND: return L"INET_E_OBJECT_NOT_FOUND";
case INET_E_DATA_NOT_AVAILABLE: return L"INET_E_DATA_NOT_AVAILABLE";
case INET_E_DOWNLOAD_FAILURE: return L"INET_E_DOWNLOAD_FAILURE";
case INET_E_AUTHENTICATION_REQUIRED: return L"INET_E_AUTHENTICATION_REQUIRED";
case INET_E_NO_VALID_MEDIA: return L"INET_E_NO_VALID_MEDIA";
case INET_E_CONNECTION_TIMEOUT: return L"INET_E_CONNECTION_TIMEOUT";
case INET_E_INVALID_REQUEST: return L"INET_E_INVALID_REQUEST";
case INET_E_UNKNOWN_PROTOCOL: return L"INET_E_UNKNOWN_PROTOCOL";
case INET_E_SECURITY_PROBLEM: return L"INET_E_SECURITY_PROBLEM";
case INET_E_CANNOT_LOAD_DATA: return L"INET_E_CANNOT_LOAD_DATA";
case INET_E_CANNOT_INSTANTIATE_OBJECT: return L"INET_E_CANNOT_INSTANTIATE_OBJECT";
case INET_E_INVALID_CERTIFICATE: return L"INET_E_INVALID_CERTIFICATE";
case INET_E_REDIRECT_FAILED: return L"INET_E_REDIRECT_FAILED";
case INET_E_REDIRECT_TO_DIR: return L"INET_E_REDIRECT_TO_DIR";
}
return L"Unknown error";
}
}}}
#endif
+38
View File
@@ -0,0 +1,38 @@
//// 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 {
private class ErrorUtils
{
public:
/// <summary>
/// Converts HTTP status Code (e.g., 200, 401, 304) to HRESULT
/// </summary>
static HRESULT
ConvertHttpStatusCodeToHR(
_In_ uint32 HttpStatusCode
);
static HRESULT
ConvertExceptionToHRESULT();
static Platform::String^
ConvertHResultToString( HRESULT hr );
static Platform::String^
ConvertHResultToErrorName( HRESULT hr );
};
}}}
#endif
+31
View File
@@ -0,0 +1,31 @@
//// 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 "FactoryCache.h"
#include "StringUtils.h"
#if TV_API
using namespace Platform;
using namespace Windows::Foundation;
using namespace Windows::Storage::Streams;
namespace Microsoft {
namespace Xbox {
namespace GameChat {
FactoryCache::FactoryCache()
{
HRESULT hr;
hr = Windows::Foundation::GetActivationFactory( Microsoft::WRL::Wrappers::HStringReference( RuntimeClass_Windows_Storage_Streams_Buffer ).Get(), &m_bufferFactory );
CHAT_THROW_IF_HR_FAILED( hr );
CHAT_THROW_E_POINTER_IF_NULL( m_bufferFactory );
}
}}}
#endif
+31
View File
@@ -0,0 +1,31 @@
//// 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 {
class FactoryCache
{
public:
FactoryCache();
inline Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IBufferFactory> GetBufferFactory()
{
return m_bufferFactory;
}
private:
Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IBufferFactory> m_bufferFactory;
};
}}}
#endif
+89
View File
@@ -0,0 +1,89 @@
//// 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 "StringUtils.h"
#include "ErrorUtils.h"
#if TV_API
using namespace Platform;
using namespace Windows::Foundation;
using namespace Windows::Storage::Streams;
namespace Microsoft {
namespace Xbox {
namespace GameChat {
Platform::String^
StringUtils::GetLogExceptionDebugInfo(
_In_ HRESULT hr,
_In_opt_ PCWSTR pwszFunction,
_In_opt_ PCWSTR pwszFile,
_In_ uint32 uLine
)
{
std::wstring info = L"[Exception]: HRESULT: ";
info += ErrorUtils::ConvertHResultToString(hr)->Data();
info += L"\n";
if( pwszFunction != nullptr )
{
info += L"\t\tFunction: ";
info += pwszFunction;
info += L"\n";
}
if( pwszFile != nullptr )
{
info += L"\t\tFile:";
info += pwszFile;
info += L"(";
info += uLine.ToString()->Data();
info += L")\n";
}
return ref new Platform::String( info.c_str() );
}
bool StringUtils::IsStringEqualCaseInsenstive(
_In_ Platform::String^ val1,
_In_ Platform::String^ val2
)
{
return ( _wcsicmp(val1->Data(), val2->Data()) == 0 );
}
Platform::String^
StringUtils::GetStringFormat(
_In_ LPCWSTR strMsg,
_In_ va_list args
)
{
WCHAR strBuffer[2048];
_vsnwprintf_s( strBuffer, 2048, _TRUNCATE, strMsg, args );
strBuffer[2047] = L'\0';
return ref new Platform::String(strBuffer);
}
Platform::String^
StringUtils::FormatString( LPCWSTR strMsg, ... )
{
WCHAR strBuffer[2048];
va_list args;
va_start(args, strMsg);
_vsnwprintf_s( strBuffer, 2048, _TRUNCATE, strMsg, args );
strBuffer[2047] = L'\0';
va_end(args);
Platform::String^ str = ref new Platform::String(strBuffer);
return str;
}
}}}
#endif
+43
View File
@@ -0,0 +1,43 @@
//// 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 {
private class StringUtils
{
public:
static Platform::String^
GetLogExceptionDebugInfo(
_In_ HRESULT hr,
_In_opt_ PCWSTR pwszFunction,
_In_opt_ PCWSTR pwszFile,
_In_ uint32 uLine
);
static bool IsStringEqualCaseInsenstive(
_In_ Platform::String^ val1,
_In_ Platform::String^ val2
);
static Platform::String^
GetStringFormat(
_In_ LPCWSTR strMsg,
_In_ va_list args
);
static Platform::String^
FormatString( LPCWSTR strMsg, ... );
};
}}}
#endif
+132
View File
@@ -0,0 +1,132 @@
//// 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"
#if TV_API
namespace Microsoft {
namespace Xbox {
namespace GameChat {
Thread::Thread(
_In_ UINT workPeriodInMilliseconds,
_In_ int32 threadAffinityMask,
_In_ int threadPriority
) :
m_threadPriority(threadPriority),
m_workPeriodInMilliseconds(workPeriodInMilliseconds),
m_terminateThreadEvent(nullptr),
m_threadAffinityMask(threadAffinityMask),
m_threadHandle(nullptr),
m_wakeupEventHandle(nullptr)
{
m_wakeupEventHandle = CreateEvent( nullptr, false, false, nullptr );
InitializeCriticalSection(&m_threadManagementLock);
m_terminateThreadEvent = CreateEvent( nullptr, false, false, nullptr );
if ( !m_terminateThreadEvent )
{
throw E_UNEXPECTED;
}
m_threadHandle = CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)Thread::StaticThreadProc, (LPVOID)this, CREATE_SUSPENDED, nullptr);
if ( !m_threadHandle )
{
throw HRESULT_FROM_WIN32( GetLastError() );
}
SetThreadPriority(m_threadHandle, m_threadPriority);
#if WINAPI_FAMILY == WINAPI_FAMILY_TV_TITLE
SetThreadAffinityMask(m_threadHandle, m_threadAffinityMask);
#endif
ResumeThread(m_threadHandle);
}
Thread::~Thread()
{
Shutdown();
DeleteCriticalSection(&m_threadManagementLock);
}
UINT Thread::GetWorkPeriodInMilliseconds()
{
return m_workPeriodInMilliseconds;
}
void Thread::SetWorkPeriodInMilliseconds(
_In_ UINT sendPeriodInMilliseconds
)
{
m_workPeriodInMilliseconds = sendPeriodInMilliseconds;
m_clock.SetInterval(m_workPeriodInMilliseconds);
}
void Thread::SetOptions(
_In_ int32 threadAffinityMask,
_In_ int threadPriority
)
{
m_threadPriority = threadPriority;
m_threadAffinityMask = threadAffinityMask;
if( m_threadHandle != nullptr )
{
SetThreadPriority(m_threadHandle, m_threadPriority);
#if WINAPI_FAMILY == WINAPI_FAMILY_TV_TITLE
SetThreadAffinityMask(m_threadHandle, m_threadAffinityMask);
#endif
}
}
void Thread::Shutdown()
{
EnterCriticalSection(&m_threadManagementLock);
if( m_threadHandle != nullptr )
{
SetEvent(m_terminateThreadEvent);
WaitForSingleObject(m_threadHandle, INFINITE);
CloseHandle(m_threadHandle);
m_threadHandle = nullptr;
}
LeaveCriticalSection(&m_threadManagementLock);
}
DWORD WINAPI Thread::StaticThreadProc(
_In_ Thread^ networkSendThread
)
{
return networkSendThread->ThreadProc();
}
DWORD WINAPI Thread::ThreadProc()
{
m_clock.Initialize( m_workPeriodInMilliseconds );
static const UINT c_uOneSecondInMS = 1000;
LARGE_INTEGER m_timerFrequency;
QueryPerformanceFrequency(&m_timerFrequency);
while( m_clock.WaitForEventsOrHeartbeat( m_terminateThreadEvent, m_wakeupEventHandle ) != WAIT_OBJECT_0 )
{
OnDoWork();
}
return 0;
}
void Thread::WakeupThread()
{
SetEvent( m_wakeupEventHandle );
}
}}}
#endif
+66
View File
@@ -0,0 +1,66 @@
//// 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 "Clock.h"
#if TV_API
namespace Microsoft {
namespace Xbox {
namespace GameChat {
delegate void ThreadDoWorkHandler();
ref class Thread sealed
{
public:
Thread(
_In_ UINT sendPeriodInMilliseconds,
_In_ int32 threadAffinityMask,
_In_ int threadPriority
);
virtual ~Thread();
void Shutdown();
UINT GetWorkPeriodInMilliseconds();
void SetWorkPeriodInMilliseconds(
_In_ UINT sendPeriodInMilliseconds
);
void SetOptions(
_In_ int32 threadAffinityMask,
_In_ int threadPriority
);
void WakeupThread();
event ThreadDoWorkHandler^ OnDoWork;
private:
static DWORD WINAPI StaticThreadProc(
_In_ Thread^ networkSendThread
);
DWORD ThreadProc();
private:
CRITICAL_SECTION m_threadManagementLock;
Clock m_clock;
HANDLE m_terminateThreadEvent;
HANDLE m_threadHandle;
HANDLE m_wakeupEventHandle;
UINT m_workPeriodInMilliseconds;
int m_threadPriority;
uint32 m_threadAffinityMask;
};
}}}
#endif
+7
View File
@@ -0,0 +1,7 @@
//// 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"
+31
View File
@@ -0,0 +1,31 @@
//// 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 <Windows.h>
#include <tchar.h>
#include <assert.h>
#include <queue>
#include <map>
#include <vector>
#include <hash_set>
#include <ppltasks.h>
#include <memory>
#include <collection.h>
#include <wrl.h>
#include <tchar.h>
#include <ppltasks.h>
#include <Robuffer.h>
#include <mmdeviceapi.h>
#include <xaudio2.h>
#include <Windows.Foundation.h>
#include <Windows.Xbox.Chat.h>
#include <locale>
#include "ChatMacros.h"
//#define DEBUG_REMOTE_AUDIO_DEVICES 1