diff --git a/extension.cpp b/extension.cpp index f5f82e8..f8515eb 100644 --- a/extension.cpp +++ b/extension.cpp @@ -48,7 +48,7 @@ #include "extension.h" // voice packets are sent over unreliable netchannel -//#define NET_MAX_DATAGRAM_PAYLOAD 4000 // = maximum unreliable payload size +//#define NET_MAX_DATAGRAM_PAYLOAD 4000 // = maximum unreliable payload size // voice packetsize = 64 | netchannel overflows at >4000 bytes // with 22050 samplerate and 512 frames per packet -> 23.22ms per packet // SVC_VoiceData overhead = 5 bytes @@ -138,26 +138,26 @@ int g_aFrameVoiceBytes[SM_MAXPLAYERS + 1]; DETOUR_DECL_STATIC4(SV_BroadcastVoiceData, void, IClient *, pClient, int, nBytes, char *, data, int64, xuid) { - if(g_Interface.OnBroadcastVoiceData(pClient, nBytes, data)) - DETOUR_STATIC_CALL(SV_BroadcastVoiceData)(pClient, nBytes, data, xuid); + if(g_Interface.OnBroadcastVoiceData(pClient, nBytes, data)) + DETOUR_STATIC_CALL(SV_BroadcastVoiceData)(pClient, nBytes, data, xuid); } #ifdef _WIN32 DETOUR_DECL_STATIC2(SV_BroadcastVoiceData_LTCG, void, char *, data, int64, xuid) { - IClient *pClient = NULL; - int nBytes = 0; + IClient *pClient = NULL; + int nBytes = 0; - __asm mov pClient, ecx; - __asm mov nBytes, edx; + __asm mov pClient, ecx; + __asm mov nBytes, edx; - bool ret = g_Interface.OnBroadcastVoiceData(pClient, nBytes, data); + bool ret = g_Interface.OnBroadcastVoiceData(pClient, nBytes, data); - __asm mov ecx, pClient; - __asm mov edx, nBytes; + __asm mov ecx, pClient; + __asm mov edx, nBytes; - if(ret) - DETOUR_STATIC_CALL(SV_BroadcastVoiceData_LTCG)(data, xuid); + if(ret) + DETOUR_STATIC_CALL(SV_BroadcastVoiceData_LTCG)(data, xuid); } #endif @@ -165,155 +165,155 @@ double getTime() { struct timespec tv; if(clock_gettime(CLOCK_REALTIME, &tv) != 0) - return 0; + return 0; return (tv.tv_sec + (tv.tv_nsec / 1000000000.0)); } void OnGameFrame(bool simulating) { - g_Interface.OnGameFrame(simulating); + g_Interface.OnGameFrame(simulating); } CVoice::CVoice() { - m_ListenSocket = -1; + m_ListenSocket = -1; - m_PollFds = 0; - for(int i = 1; i < 1 + MAX_CLIENTS; i++) - m_aPollFds[i].fd = -1; + m_PollFds = 0; + for(int i = 1; i < 1 + MAX_CLIENTS; i++) + m_aPollFds[i].fd = -1; - for(int i = 0; i < MAX_CLIENTS; i++) - m_aClients[i].m_Socket = -1; + for(int i = 0; i < MAX_CLIENTS; i++) + m_aClients[i].m_Socket = -1; m_OpusEncoder = NULL; m_AvailableTime = 0.0; - m_VoiceDetour = NULL; - m_SV_BroadcastVoiceData = NULL; + m_VoiceDetour = NULL; + m_SV_BroadcastVoiceData = NULL; } bool CVoice::SDK_OnLoad(char *error, size_t maxlength, bool late) { - // Setup engine-specific data. - Dl_info info; - void *engineFactory = (void *)g_SMAPI->GetEngineFactory(false); - if(dladdr(engineFactory, &info) == 0) - { - g_SMAPI->Format(error, maxlength, "dladdr(engineFactory) failed."); - return false; - } - - void *pEngineSo = dlopen(info.dli_fname, RTLD_NOW); - if(pEngineSo == NULL) - { - g_SMAPI->Format(error, maxlength, "dlopen(%s) failed.", info.dli_fname); - return false; - } - - int engineVersion = g_SMAPI->GetSourceEngineBuild(); - void *adrVoiceData = NULL; - - switch (engineVersion) - { - case SOURCE_ENGINE_CSGO: -#ifdef _WIN32 - adrVoiceData = memutils->FindPattern(pEngineSo, "\x55\x8B\xEC\x81\xEC\xD0\x00\x00\x00\x53\x56\x57", 12); -#else - adrVoiceData = memutils->ResolveSymbol(pEngineSo, "_Z21SV_BroadcastVoiceDataP7IClientiPcx"); -#endif - break; - - case SOURCE_ENGINE_LEFT4DEAD2: -#ifdef _WIN32 - adrVoiceData = memutils->FindPattern(pEngineSo, "\x55\x8B\xEC\x83\xEC\x70\xA1\x2A\x2A\x2A\x2A\x33\xC5\x89\x45\xFC\xA1\x2A\x2A\x2A\x2A\x53\x56", 23); -#else - adrVoiceData = memutils->ResolveSymbol(pEngineSo, "_Z21SV_BroadcastVoiceDataP7IClientiPcx"); -#endif - break; - - case SOURCE_ENGINE_NUCLEARDAWN: -#ifdef _WIN32 - adrVoiceData = memutils->FindPattern(pEngineSo, "\x55\x8B\xEC\xA1\x2A\x2A\x2A\x2A\x83\xEC\x58\x57\x33\xFF", 14); -#else - adrVoiceData = memutils->ResolveSymbol(pEngineSo, "_Z21SV_BroadcastVoiceDataP7IClientiPcx"); -#endif - break; - - case SOURCE_ENGINE_INSURGENCY: -#ifdef _WIN32 - adrVoiceData = memutils->FindPattern(pEngineSo, "\x55\x8B\xEC\x83\xEC\x74\x68\x2A\x2A\x2A\x2A\x8D\x4D\xE4\xE8", 15); -#else - adrVoiceData = memutils->ResolveSymbol(pEngineSo, "_Z21SV_BroadcastVoiceDataP7IClientiPcx"); -#endif - break; - - case SOURCE_ENGINE_TF2: - case SOURCE_ENGINE_CSS: - case SOURCE_ENGINE_HL2DM: - case SOURCE_ENGINE_DODS: - case SOURCE_ENGINE_SDK2013: -#ifdef _WIN32 - adrVoiceData = memutils->FindPattern(pEngineSo, "\x55\x8B\xEC\xA1\x2A\x2A\x2A\x2A\x83\xEC\x50\x83\x78\x30", 14); -#else - adrVoiceData = memutils->ResolveSymbol(pEngineSo, "_Z21SV_BroadcastVoiceDataP7IClientiPcx"); -#endif - break; - - default: - g_SMAPI->Format(error, maxlength, "Unsupported game."); - dlclose(pEngineSo); - return false; - } - dlclose(pEngineSo); - - m_SV_BroadcastVoiceData = (t_SV_BroadcastVoiceData)adrVoiceData; - if(!m_SV_BroadcastVoiceData) - { - g_SMAPI->Format(error, maxlength, "SV_BroadcastVoiceData sigscan failed."); - return false; - } - - // Setup voice detour. - CDetourManager::Init(g_pSM->GetScriptingEngine(), NULL); - -#ifdef _WIN32 - if (engineVersion == SOURCE_ENGINE_CSGO || engineVersion == SOURCE_ENGINE_INSURGENCY) - { - m_VoiceDetour = DETOUR_CREATE_STATIC(SV_BroadcastVoiceData_LTCG, adrVoiceData); - } - else - { - m_VoiceDetour = DETOUR_CREATE_STATIC(SV_BroadcastVoiceData, adrVoiceData); - } -#else - m_VoiceDetour = DETOUR_CREATE_STATIC(SV_BroadcastVoiceData, adrVoiceData); -#endif - - if (!m_VoiceDetour) - { - g_SMAPI->Format(error, maxlength, "SV_BroadcastVoiceData detour failed."); - return false; - } - - m_VoiceDetour->EnableDetour(); - - //opus edit - int err; - m_OpusEncoder = opus_encoder_create(48000, 1, OPUS_APPLICATION_AUDIO, &err); - if (err<0) + // Setup engine-specific data. + Dl_info info; + void *engineFactory = (void *)g_SMAPI->GetEngineFactory(false); + if(dladdr(engineFactory, &info) == 0) { - smutils->LogError(myself, "failed to create encode: %s", opus_strerror(err)); + g_SMAPI->Format(error, maxlength, "dladdr(engineFactory) failed."); return false; } - opus_encoder_ctl(m_OpusEncoder, OPUS_SET_BITRATE(512000)); - opus_encoder_ctl(m_OpusEncoder, OPUS_SET_BANDWIDTH(OPUS_BANDWIDTH_FULLBAND)); - opus_encoder_ctl(m_OpusEncoder, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_FULLBAND)); - opus_encoder_ctl(m_OpusEncoder, OPUS_SET_COMPLEXITY(10)); + void *pEngineSo = dlopen(info.dli_fname, RTLD_NOW); + if(pEngineSo == NULL) + { + g_SMAPI->Format(error, maxlength, "dlopen(%s) failed.", info.dli_fname); + return false; + } - opus_encoder_ctl(m_OpusEncoder, OPUS_SET_SIGNAL(OPUS_SIGNAL_MUSIC)); + int engineVersion = g_SMAPI->GetSourceEngineBuild(); + void *adrVoiceData = NULL; + + switch (engineVersion) + { + case SOURCE_ENGINE_CSGO: +#ifdef _WIN32 + adrVoiceData = memutils->FindPattern(pEngineSo, "\x55\x8B\xEC\x81\xEC\xD0\x00\x00\x00\x53\x56\x57", 12); +#else + adrVoiceData = memutils->ResolveSymbol(pEngineSo, "_Z21SV_BroadcastVoiceDataP7IClientiPcx"); +#endif + break; + + case SOURCE_ENGINE_LEFT4DEAD2: +#ifdef _WIN32 + adrVoiceData = memutils->FindPattern(pEngineSo, "\x55\x8B\xEC\x83\xEC\x70\xA1\x2A\x2A\x2A\x2A\x33\xC5\x89\x45\xFC\xA1\x2A\x2A\x2A\x2A\x53\x56", 23); +#else + adrVoiceData = memutils->ResolveSymbol(pEngineSo, "_Z21SV_BroadcastVoiceDataP7IClientiPcx"); +#endif + break; + + case SOURCE_ENGINE_NUCLEARDAWN: +#ifdef _WIN32 + adrVoiceData = memutils->FindPattern(pEngineSo, "\x55\x8B\xEC\xA1\x2A\x2A\x2A\x2A\x83\xEC\x58\x57\x33\xFF", 14); +#else + adrVoiceData = memutils->ResolveSymbol(pEngineSo, "_Z21SV_BroadcastVoiceDataP7IClientiPcx"); +#endif + break; + + case SOURCE_ENGINE_INSURGENCY: +#ifdef _WIN32 + adrVoiceData = memutils->FindPattern(pEngineSo, "\x55\x8B\xEC\x83\xEC\x74\x68\x2A\x2A\x2A\x2A\x8D\x4D\xE4\xE8", 15); +#else + adrVoiceData = memutils->ResolveSymbol(pEngineSo, "_Z21SV_BroadcastVoiceDataP7IClientiPcx"); +#endif + break; + + case SOURCE_ENGINE_TF2: + case SOURCE_ENGINE_CSS: + case SOURCE_ENGINE_HL2DM: + case SOURCE_ENGINE_DODS: + case SOURCE_ENGINE_SDK2013: +#ifdef _WIN32 + adrVoiceData = memutils->FindPattern(pEngineSo, "\x55\x8B\xEC\xA1\x2A\x2A\x2A\x2A\x83\xEC\x50\x83\x78\x30", 14); +#else + adrVoiceData = memutils->ResolveSymbol(pEngineSo, "_Z21SV_BroadcastVoiceDataP7IClientiPcx"); +#endif + break; + + default: + g_SMAPI->Format(error, maxlength, "Unsupported game."); + dlclose(pEngineSo); + return false; + } + dlclose(pEngineSo); + + m_SV_BroadcastVoiceData = (t_SV_BroadcastVoiceData)adrVoiceData; + if(!m_SV_BroadcastVoiceData) + { + g_SMAPI->Format(error, maxlength, "SV_BroadcastVoiceData sigscan failed."); + return false; + } + + // Setup voice detour. + CDetourManager::Init(g_pSM->GetScriptingEngine(), NULL); + +#ifdef _WIN32 + if (engineVersion == SOURCE_ENGINE_CSGO || engineVersion == SOURCE_ENGINE_INSURGENCY) + { + m_VoiceDetour = DETOUR_CREATE_STATIC(SV_BroadcastVoiceData_LTCG, adrVoiceData); + } + else + { + m_VoiceDetour = DETOUR_CREATE_STATIC(SV_BroadcastVoiceData, adrVoiceData); + } +#else + m_VoiceDetour = DETOUR_CREATE_STATIC(SV_BroadcastVoiceData, adrVoiceData); +#endif + + if (!m_VoiceDetour) + { + g_SMAPI->Format(error, maxlength, "SV_BroadcastVoiceData detour failed."); + return false; + } + + m_VoiceDetour->EnableDetour(); + + //opus edit + int err; + //m_OpusEncoder = opus_encoder_create(24000, 2, OPUS_APPLICATION_AUDIO, &err); + m_OpusEncoder = opus_encoder_create(48000, 2, OPUS_APPLICATION_AUDIO, &err); + if (err<0) + { + smutils->LogError(myself, "failed to create encode: %s", opus_strerror(err)); + return false; + } + + opus_encoder_ctl(m_OpusEncoder, OPUS_SET_BITRATE(510000)); + opus_encoder_ctl(m_OpusEncoder, OPUS_SET_BANDWIDTH(OPUS_BANDWIDTH_FULLBAND)); + opus_encoder_ctl(m_OpusEncoder, OPUS_SET_SIGNAL(OPUS_SIGNAL_MUSIC)); //MDCT mode opus_encoder_ctl(m_OpusEncoder, OPUS_SET_LSB_DEPTH(16)); + opus_encoder_ctl(m_OpusEncoder, OPUS_SET_PACKET_LOSS_PERC(0)); + opus_encoder_ctl(m_OpusEncoder, OPUS_SET_FORCE_CHANNELS(2)); if (err<0) { @@ -321,402 +321,408 @@ bool CVoice::SDK_OnLoad(char *error, size_t maxlength, bool late) return false; } - return true; + return true; } bool CVoice::SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlen, bool late) { - GET_V_IFACE_CURRENT(GetEngineFactory, g_pCVar, ICvar, CVAR_INTERFACE_VERSION); - gpGlobals = ismm->GetCGlobals(); - ConVar_Register(0, this); + GET_V_IFACE_CURRENT(GetEngineFactory, g_pCVar, ICvar, CVAR_INTERFACE_VERSION); + gpGlobals = ismm->GetCGlobals(); + ConVar_Register(0, this); - return true; + return true; } bool CVoice::RegisterConCommandBase(ConCommandBase *pVar) { - /* Always call META_REGCVAR instead of going through the engine. */ - return META_REGCVAR(pVar); + /* Always call META_REGCVAR instead of going through the engine. */ + return META_REGCVAR(pVar); } cell_t IsClientTalking(IPluginContext *pContext, const cell_t *params) { - int client = params[1]; + int client = params[1]; - if(client < 1 || client > SM_MAXPLAYERS) - { - return pContext->ThrowNativeError("Client index %d is invalid", client); - } + if(client < 1 || client > SM_MAXPLAYERS) + { + return pContext->ThrowNativeError("Client index %d is invalid", client); + } - double d = gpGlobals->curtime - g_fLastVoiceData[client]; + double d = gpGlobals->curtime - g_fLastVoiceData[client]; - if(d < 0) // mapchange - return false; + if(d < 0) // mapchange + return false; - if(d > 0.33) - return false; + if(d > 0.33) + return false; - return true; + return true; } const sp_nativeinfo_t MyNatives[] = { - { "IsClientTalking", IsClientTalking }, - { NULL, NULL } + { "IsClientTalking", IsClientTalking }, + { NULL, NULL } }; static void ListenSocketAction(void *pData) { - CVoice *pThis = (CVoice *)pData; - pThis->ListenSocket(); + CVoice *pThis = (CVoice *)pData; + pThis->ListenSocket(); } void CVoice::SDK_OnAllLoaded() { - sharesys->AddNatives(myself, MyNatives); - sharesys->RegisterLibrary(myself, "Voice"); + sharesys->AddNatives(myself, MyNatives); + sharesys->RegisterLibrary(myself, "Voice"); - SM_GET_LATE_IFACE(SDKTOOLS, g_pSDKTools); - if(g_pSDKTools == NULL) - { - smutils->LogError(myself, "SDKTools interface not found"); - SDK_OnUnload(); - return; - } + SM_GET_LATE_IFACE(SDKTOOLS, g_pSDKTools); + if(g_pSDKTools == NULL) + { + smutils->LogError(myself, "SDKTools interface not found"); + SDK_OnUnload(); + return; + } - iserver = g_pSDKTools->GetIServer(); - if(iserver == NULL) - { - smutils->LogError(myself, "Failed to get IServer interface from SDKTools!"); - SDK_OnUnload(); - return; - } + iserver = g_pSDKTools->GetIServer(); + if(iserver == NULL) + { + smutils->LogError(myself, "Failed to get IServer interface from SDKTools!"); + SDK_OnUnload(); + return; + } - // Init tcp server - m_ListenSocket = socket(AF_INET, SOCK_STREAM, 0); - if(m_ListenSocket < 0) - { - smutils->LogError(myself, "Failed creating socket."); - SDK_OnUnload(); - return; - } + // Init tcp server + m_ListenSocket = socket(AF_INET, SOCK_STREAM, 0); + if(m_ListenSocket < 0) + { + smutils->LogError(myself, "Failed creating socket."); + SDK_OnUnload(); + return; + } - int yes = 1; - if(setsockopt(m_ListenSocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) < 0) - { - smutils->LogError(myself, "Failed setting SO_REUSEADDR on socket."); - SDK_OnUnload(); - return; - } + int yes = 1; + if(setsockopt(m_ListenSocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) < 0) + { + smutils->LogError(myself, "Failed setting SO_REUSEADDR on socket."); + SDK_OnUnload(); + return; + } - // This doesn't seem to work right away ... - engine->ServerCommand("exec sourcemod/extension.Voice.cfg\n"); - engine->ServerExecute(); + // This doesn't seem to work right away ... + engine->ServerCommand("exec sourcemod/extension.Voice.cfg\n"); + engine->ServerExecute(); - // ... delay starting listen server to next frame - smutils->AddFrameAction(ListenSocketAction, this); + // ... delay starting listen server to next frame + smutils->AddFrameAction(ListenSocketAction, this); } void CVoice::ListenSocket() { - if(m_PollFds > 0) - return; + if(m_PollFds > 0) + return; - sockaddr_in bindAddr; - memset(&bindAddr, 0, sizeof(bindAddr)); - bindAddr.sin_family = AF_INET; - inet_aton(g_SmVoiceAddr.GetString(), &bindAddr.sin_addr); - bindAddr.sin_port = htons(g_SmVoicePort.GetInt()); + sockaddr_in bindAddr; + memset(&bindAddr, 0, sizeof(bindAddr)); + bindAddr.sin_family = AF_INET; + inet_aton(g_SmVoiceAddr.GetString(), &bindAddr.sin_addr); + bindAddr.sin_port = htons(g_SmVoicePort.GetInt()); - smutils->LogMessage(myself, "Binding to %s:%d!\n", g_SmVoiceAddr.GetString(), g_SmVoicePort.GetInt()); + smutils->LogMessage(myself, "Binding to %s:%d!\n", g_SmVoiceAddr.GetString(), g_SmVoicePort.GetInt()); - if(bind(m_ListenSocket, (sockaddr *)&bindAddr, sizeof(sockaddr_in)) < 0) - { - smutils->LogError(myself, "Failed binding to socket (%d '%s').", errno, strerror(errno)); - SDK_OnUnload(); - return; - } + if(bind(m_ListenSocket, (sockaddr *)&bindAddr, sizeof(sockaddr_in)) < 0) + { + smutils->LogError(myself, "Failed binding to socket (%d '%s').", errno, strerror(errno)); + SDK_OnUnload(); + return; + } - if(listen(m_ListenSocket, MAX_CLIENTS) < 0) - { - smutils->LogError(myself, "Failed listening on socket."); - SDK_OnUnload(); - return; - } + if(listen(m_ListenSocket, MAX_CLIENTS) < 0) + { + smutils->LogError(myself, "Failed listening on socket."); + SDK_OnUnload(); + return; + } - m_aPollFds[0].fd = m_ListenSocket; - m_aPollFds[0].events = POLLIN; - m_PollFds++; + m_aPollFds[0].fd = m_ListenSocket; + m_aPollFds[0].events = POLLIN; + m_PollFds++; - smutils->AddGameFrameHook(::OnGameFrame); + smutils->AddGameFrameHook(::OnGameFrame); } void CVoice::SDK_OnUnload() { - smutils->RemoveGameFrameHook(::OnGameFrame); + smutils->RemoveGameFrameHook(::OnGameFrame); - if(m_VoiceDetour) - { - m_VoiceDetour->Destroy(); - m_VoiceDetour = NULL; - } + if(m_VoiceDetour) + { + m_VoiceDetour->Destroy(); + m_VoiceDetour = NULL; + } - if(m_ListenSocket != -1) - { - close(m_ListenSocket); - m_ListenSocket = -1; - } + if(m_ListenSocket != -1) + { + close(m_ListenSocket); + m_ListenSocket = -1; + } - for(int Client = 0; Client < MAX_CLIENTS; Client++) - { - if(m_aClients[Client].m_Socket != -1) - { - close(m_aClients[Client].m_Socket); - m_aClients[Client].m_Socket = -1; - } - } + for(int Client = 0; Client < MAX_CLIENTS; Client++) + { + if(m_aClients[Client].m_Socket != -1) + { + close(m_aClients[Client].m_Socket); + m_aClients[Client].m_Socket = -1; + } + } opus_encoder_destroy(m_OpusEncoder); } void CVoice::OnGameFrame(bool simulating) { - HandleNetwork(); - HandleVoiceData(); + HandleNetwork(); + HandleVoiceData(); - // Reset per-client voice byte counter to 0 every frame. - memset(g_aFrameVoiceBytes, 0, sizeof(g_aFrameVoiceBytes)); + // Reset per-client voice byte counter to 0 every frame. + memset(g_aFrameVoiceBytes, 0, sizeof(g_aFrameVoiceBytes)); } bool CVoice::OnBroadcastVoiceData(IClient *pClient, int nBytes, char *data) { - // Reject empty packets - if(nBytes < 1) - return false; + // Reject empty packets + if(nBytes < 1) + return false; - int client = pClient->GetPlayerSlot() + 1; + int client = pClient->GetPlayerSlot() + 1; - // Reject voice packet if we'd send more than NET_MAX_VOICE_BYTES_FRAME voice bytes from this client in the current frame. - // 5 = SVC_VoiceData header/overhead - g_aFrameVoiceBytes[client] += 5 + nBytes; + // Reject voice packet if we'd send more than NET_MAX_VOICE_BYTES_FRAME voice bytes from this client in the current frame. + // 5 = SVC_VoiceData header/overhead + g_aFrameVoiceBytes[client] += 5 + nBytes; - if(g_aFrameVoiceBytes[client] > NET_MAX_VOICE_BYTES_FRAME) - return false; + if(g_aFrameVoiceBytes[client] > NET_MAX_VOICE_BYTES_FRAME) + { + return false; + } - g_fLastVoiceData[client] = gpGlobals->curtime; + g_fLastVoiceData[client] = gpGlobals->curtime; - return true; + return true; } void CVoice::HandleNetwork() { - if(m_ListenSocket == -1) - return; + if(m_ListenSocket == -1) + return; - int PollRes = poll(m_aPollFds, m_PollFds, 0); - if(PollRes <= 0) - return; + int PollRes = poll(m_aPollFds, m_PollFds, 0); + if(PollRes <= 0) + return; - // Accept new clients - if(m_aPollFds[0].revents & POLLIN) - { - // Find slot - int Client; - for(Client = 0; Client < MAX_CLIENTS; Client++) - { - if(m_aClients[Client].m_Socket == -1) - break; - } + // Accept new clients + if(m_aPollFds[0].revents & POLLIN) + { + // Find slot + int Client; + for(Client = 0; Client < MAX_CLIENTS; Client++) + { + if(m_aClients[Client].m_Socket == -1) + break; + } - // no free slot - if(Client != MAX_CLIENTS) - { - sockaddr_in addr; - socklen_t size = sizeof(sockaddr_in); - int Socket = accept(m_ListenSocket, (sockaddr *)&addr, &size); + // no free slot + if(Client != MAX_CLIENTS) + { + sockaddr_in addr; + socklen_t size = sizeof(sockaddr_in); + int Socket = accept(m_ListenSocket, (sockaddr *)&addr, &size); - m_aClients[Client].m_Socket = Socket; - m_aClients[Client].m_BufferWriteIndex = 0; - m_aClients[Client].m_LastLength = 0; - m_aClients[Client].m_LastValidData = 0.0; - m_aClients[Client].m_New = true; - m_aClients[Client].m_UnEven = false; + m_aClients[Client].m_Socket = Socket; + m_aClients[Client].m_BufferWriteIndex = 0; + m_aClients[Client].m_LastLength = 0; + m_aClients[Client].m_LastValidData = 0.0; + m_aClients[Client].m_New = true; + m_aClients[Client].m_UnEven = false; - m_aPollFds[m_PollFds].fd = Socket; - m_aPollFds[m_PollFds].events = POLLIN | POLLHUP; - m_aPollFds[m_PollFds].revents = 0; - m_PollFds++; + m_aPollFds[m_PollFds].fd = Socket; + m_aPollFds[m_PollFds].events = POLLIN | POLLHUP; + m_aPollFds[m_PollFds].revents = 0; + m_PollFds++; - //smutils->LogMessage(myself, "Client %d connected!\n", Client); - } - } + //smutils->LogMessage(myself, "Client %d connected!\n", Client); + } + } - bool CompressPollFds = false; - for(int PollFds = 1; PollFds < m_PollFds; PollFds++) - { - int Client = -1; - for(Client = 0; Client < MAX_CLIENTS; Client++) - { - if(m_aClients[Client].m_Socket == m_aPollFds[PollFds].fd) - break; - } - if(Client == -1) - continue; + bool CompressPollFds = false; + for(int PollFds = 1; PollFds < m_PollFds; PollFds++) + { + int Client = -1; + for(Client = 0; Client < MAX_CLIENTS; Client++) + { + if(m_aClients[Client].m_Socket == m_aPollFds[PollFds].fd) + break; + } + if(Client == -1) + continue; - CClient *pClient = &m_aClients[Client]; + CClient *pClient = &m_aClients[Client]; - // Connection shutdown prematurely ^C - // Make sure to set SO_LINGER l_onoff = 1, l_linger = 0 - if(m_aPollFds[PollFds].revents & POLLHUP) - { - close(pClient->m_Socket); - pClient->m_Socket = -1; - m_aPollFds[PollFds].fd = -1; - CompressPollFds = true; - //smutils->LogMessage(myself, "Client %d disconnected!(2)\n", Client); - continue; - } + // Connection shutdown prematurely ^C + // Make sure to set SO_LINGER l_onoff = 1, l_linger = 0 + if(m_aPollFds[PollFds].revents & POLLHUP) + { + close(pClient->m_Socket); + pClient->m_Socket = -1; + m_aPollFds[PollFds].fd = -1; + CompressPollFds = true; + //smutils->LogMessage(myself, "Client %d disconnected!(2)\n", Client); + continue; + } - // Data available? - if(!(m_aPollFds[PollFds].revents & POLLIN)) - continue; + // Data available? + if(!(m_aPollFds[PollFds].revents & POLLIN)) + continue; - size_t BytesAvailable; - if(ioctl(pClient->m_Socket, FIONREAD, &BytesAvailable) == -1) - continue; + size_t BytesAvailable; + if(ioctl(pClient->m_Socket, FIONREAD, &BytesAvailable) == -1) + continue; - if(pClient->m_New) - { - pClient->m_BufferWriteIndex = m_Buffer.GetReadIndex(); - pClient->m_New = false; - } + if(pClient->m_New) + { + pClient->m_BufferWriteIndex = m_Buffer.GetReadIndex(); + pClient->m_New = false; + } - m_Buffer.SetWriteIndex(pClient->m_BufferWriteIndex); + m_Buffer.SetWriteIndex(pClient->m_BufferWriteIndex); - // Don't recv() when we can't fit data into the ringbuffer - unsigned char aBuf[32768]; - if(min_ext(BytesAvailable, sizeof(aBuf)) > m_Buffer.CurrentFree() * sizeof(int16_t)) - continue; + // Don't recv() when we can't fit data into the ringbuffer + unsigned char aBuf[32768]; + if(min_ext(BytesAvailable, sizeof(aBuf)) > m_Buffer.CurrentFree() * sizeof(int16_t)) + continue; - // Edge case: previously received data is uneven and last recv'd byte has to be prepended - int Shift = 0; - if(pClient->m_UnEven) - { - Shift = 1; - aBuf[0] = pClient->m_Remainder; - pClient->m_UnEven = false; - } + // Edge case: previously received data is uneven and last recv'd byte has to be prepended + int Shift = 0; + if(pClient->m_UnEven) + { + Shift = 1; + aBuf[0] = pClient->m_Remainder; + pClient->m_UnEven = false; + } - ssize_t Bytes = recv(pClient->m_Socket, &aBuf[Shift], sizeof(aBuf) - Shift, 0); + ssize_t Bytes = recv(pClient->m_Socket, &aBuf[Shift], sizeof(aBuf) - Shift, 0); - if(Bytes <= 0) - { - close(pClient->m_Socket); - pClient->m_Socket = -1; - m_aPollFds[PollFds].fd = -1; - CompressPollFds = true; - //smutils->LogMessage(myself, "Client %d disconnected!(1)\n", Client); - continue; - } + if(Bytes <= 0) + { + close(pClient->m_Socket); + pClient->m_Socket = -1; + m_aPollFds[PollFds].fd = -1; + CompressPollFds = true; + //smutils->LogMessage(myself, "Client %d disconnected!(1)\n", Client); + continue; + } - Bytes += Shift; + Bytes += Shift; - // Edge case: data received is uneven (can't be divided by two) - // store last byte, drop it here and prepend it right before the next recv - if(Bytes & 1) - { - pClient->m_UnEven = true; - pClient->m_Remainder = aBuf[Bytes - 1]; - Bytes -= 1; - } + // Edge case: data received is uneven (can't be divided by two) + // store last byte, drop it here and prepend it right before the next recv + if(Bytes & 1) + { + pClient->m_UnEven = true; + pClient->m_Remainder = aBuf[Bytes - 1]; + Bytes -= 1; + } - // Got data! - OnDataReceived(pClient, (int16_t *)aBuf, Bytes / sizeof(int16_t)); + // Got data! + OnDataReceived(pClient, (int16_t *)aBuf, Bytes / sizeof(int16_t)); - pClient->m_LastLength = m_Buffer.CurrentLength(); - pClient->m_BufferWriteIndex = m_Buffer.GetWriteIndex(); - } + pClient->m_LastLength = m_Buffer.CurrentLength(); + pClient->m_BufferWriteIndex = m_Buffer.GetWriteIndex(); + } - if(CompressPollFds) - { - for(int PollFds = 1; PollFds < m_PollFds; PollFds++) - { - if(m_aPollFds[PollFds].fd != -1) - continue; + if(CompressPollFds) + { + for(int PollFds = 1; PollFds < m_PollFds; PollFds++) + { + if(m_aPollFds[PollFds].fd != -1) + continue; - for(int PollFds_ = PollFds; PollFds_ < 1 + MAX_CLIENTS; PollFds_++) - m_aPollFds[PollFds_].fd = m_aPollFds[PollFds_ + 1].fd; + for(int PollFds_ = PollFds; PollFds_ < 1 + MAX_CLIENTS; PollFds_++) + m_aPollFds[PollFds_].fd = m_aPollFds[PollFds_ + 1].fd; - PollFds--; - m_PollFds--; - } - } + PollFds--; + m_PollFds--; + } + } } void CVoice::OnDataReceived(CClient *pClient, int16_t *pData, size_t Samples) { - // Check for empty input - ssize_t DataStartsAt = -1; - for(size_t i = 0; i < Samples; i++) - { - if(pData[i] == 0) - continue; + // Check for empty input + ssize_t DataStartsAt = -1; + for(size_t i = 0; i < Samples; i++) + { + if(pData[i] == 0) + continue; - DataStartsAt = i; - break; - } + DataStartsAt = i; + break; + } - // Discard empty data if last vaild data was more than a second ago. - if(pClient->m_LastValidData + 1.0 < getTime()) - { - // All empty - if(DataStartsAt == -1) - return; + // Discard empty data if last vaild data was more than a second ago. + if(pClient->m_LastValidData + 1.0 < getTime()) + { + // All empty + if(DataStartsAt == -1) + return; - // Data starts here - pData += DataStartsAt; - Samples -= DataStartsAt; - } + // Data starts here + pData += DataStartsAt; + Samples -= DataStartsAt; + } - if(!m_Buffer.Push(pData, Samples)) - { - smutils->LogError(myself, "Buffer push failed!!! Samples: %u, Free: %u\n", Samples, m_Buffer.CurrentFree()); - return; - } + if(!m_Buffer.Push(pData, Samples)) + { + smutils->LogError(myself, "Buffer push failed!!! Samples: %u, Free: %u\n", Samples, m_Buffer.CurrentFree()); + return; + } - pClient->m_LastValidData = getTime(); + pClient->m_LastValidData = getTime(); } -struct SteamVoiceHeader -{ - uint32_t iSteamAccountID : 32; - uint32_t iSteamCommunity : 32; - uint32_t nPayload1 : 8; - uint32_t iSampleRate : 16; - uint32_t nPayload2 : 8; - uint32_t iDataLength : 16; -}; - void CVoice::HandleVoiceData() { - const int SampleRate = 48000; + //uint32_t sampleRate = 24000; + uint32_t sampleRate = 48000; const int SamplesPerChannel = 480; - const int Channels = 1; + const int Channels = 2; int TotalSamplesPerFrame = SamplesPerChannel * Channels; int FramesAvailable = m_Buffer.TotalLength() / TotalSamplesPerFrame; - float TimeAvailable = (float)m_Buffer.TotalLength() / SampleRate; - if(!FramesAvailable) return; - if(m_AvailableTime < getTime() && TimeAvailable < 0.05) - return; - if(m_AvailableTime > getTime() + 0.02) - return; + + int FramesMax = 2; //used to be 5 + bool reset_state = false; + if (FramesAvailable <= FramesMax) + { + reset_state = true; + } + FramesAvailable = min_ext(FramesAvailable, FramesMax); + + // Allow less buffering after audio has started playing if (m_Buffer.TotalLength() < TotalSamplesPerFrame) return; - FramesAvailable = min_ext(FramesAvailable, 5); + float TimeAvailable = (float)m_Buffer.TotalLength() / sampleRate; + if(m_AvailableTime < getTime() && TimeAvailable < 0.2) + //if(m_AvailableTime < getTime() && TimeAvailable < 0.1) + return; + + double maxBuffer = (m_AvailableTime < getTime()) ? 0.2 : 0.04; + //double maxBuffer = (m_AvailableTime < getTime()) ? 0.1 : 0.02; + if(m_AvailableTime > getTime() + maxBuffer) + return; + IClient *pClient = iserver->GetClient(0); if(!pClient) @@ -738,8 +744,8 @@ void CVoice::HandleVoiceData() // 3. Payload Type 11 (1 byte) aFinal[FinalSize++] = 0x0B; + // 4. Sample Rate (2 bytes little-endian) - uint16_t sampleRate = 48000; memcpy(&aFinal[FinalSize], &sampleRate, sizeof(uint16_t)); FinalSize += sizeof(uint16_t); @@ -756,6 +762,10 @@ void CVoice::HandleVoiceData() { int16_t aBuffer[TotalSamplesPerFrame]; + size_t OldReadIdx = m_Buffer.m_ReadIndex; + size_t OldCurLength = m_Buffer.CurrentLength(); + size_t OldTotalLength = m_Buffer.TotalLength(); + if(!m_Buffer.Pop(aBuffer, TotalSamplesPerFrame)) { smutils->LogError(myself, "Buffer pop failed!"); @@ -769,7 +779,6 @@ void CVoice::HandleVoiceData() // Encode with Opus int nbBytes = opus_encode(m_OpusEncoder, (const opus_int16*)aBuffer, SamplesPerChannel, &aFinal[FinalSize], *pFrameSize); - if (nbBytes <= 1) { smutils->LogError(myself, "Opus encode failed: %s", opus_strerror(nbBytes)); @@ -780,6 +789,23 @@ void CVoice::HandleVoiceData() *pFrameSize = (uint16_t)nbBytes; *pTotalDataLength += sizeof(uint16_t) + nbBytes; FinalSize += nbBytes; + + // Check for buffer underruns + for(int Client = 0; Client < MAX_CLIENTS; Client++) + { + CClient *pClient = &m_aClients[Client]; + if(pClient->m_Socket == -1 || pClient->m_New == true) + continue; + + m_Buffer.SetWriteIndex(pClient->m_BufferWriteIndex); + + if(m_Buffer.CurrentLength() > pClient->m_LastLength) + { + pClient->m_BufferWriteIndex = m_Buffer.GetReadIndex(); + m_Buffer.SetWriteIndex(pClient->m_BufferWriteIndex); + pClient->m_LastLength = m_Buffer.CurrentLength(); + } + } } // 8. Add CRC32 @@ -788,20 +814,26 @@ void CVoice::HandleVoiceData() FinalSize += sizeof(uint32_t); BroadcastVoiceData(pClient, FinalSize, aFinal); + //smutils->LogMessage(myself, "FinalSize: %d", FinalSize); if (m_AvailableTime < getTime()) m_AvailableTime = getTime(); m_AvailableTime += (double)FramesAvailable * 0.01; + + if (reset_state) + { + opus_encoder_ctl(m_OpusEncoder, OPUS_RESET_STATE); + } } void CVoice::BroadcastVoiceData(IClient *pClient, int nBytes, unsigned char *pData) { - #ifdef _WIN32 - __asm mov ecx, pClient; - __asm mov edx, nBytes; + #ifdef _WIN32 + __asm mov ecx, pClient; + __asm mov edx, nBytes; - DETOUR_STATIC_CALL(SV_BroadcastVoiceData_LTCG)((char *)pData, 0); - #else - DETOUR_STATIC_CALL(SV_BroadcastVoiceData)(pClient, nBytes, (char *)pData, 0); - #endif + DETOUR_STATIC_CALL(SV_BroadcastVoiceData_LTCG)((char *)pData, 0); + #else + DETOUR_STATIC_CALL(SV_BroadcastVoiceData)(pClient, nBytes, (char *)pData, 0); + #endif }