mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-07 05:57:47 +00:00
GEEKING
This commit is contained in:
@@ -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>
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -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, ¤tAudioDeviceInfo);
|
||||
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
|
||||
@@ -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>
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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"
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user