fix issues, add voice stats

This commit is contained in:
BotoX 2021-05-09 23:22:12 +02:00
parent 39fb1f70f8
commit fa3a80c9dc
8 changed files with 83 additions and 47 deletions

View File

@ -13,28 +13,28 @@
void DemHandlers::CreateDemMsgStructs(DemDataStructArray& demDataStructs)
{
demDataStructs[0] = new DemMsg::Dem_Unknown();
demDataStructs[1] = new DemMsg::Dem_Packet();
demDataStructs[2] = new DemMsg::Dem_Packet();
demDataStructs[3] = new DemMsg::Dem_SyncTick();
demDataStructs[4] = new DemMsg::Dem_ConsoleCmd();
demDataStructs[5] = new DemMsg::Dem_UserCmd();
demDataStructs[6] = new DemMsg::Dem_DataTables();
demDataStructs[7] = new DemMsg::Dem_Stop();
demDataStructs[8] = new DemMsg::Dem_StringTables();
demDataStructs[dem_unknown] = new DemMsg::Dem_Unknown();
demDataStructs[dem_signon] = new DemMsg::Dem_Packet();
demDataStructs[dem_packet] = new DemMsg::Dem_Packet();
demDataStructs[dem_synctick] = new DemMsg::Dem_SyncTick();
demDataStructs[dem_consolecmd] = new DemMsg::Dem_ConsoleCmd();
demDataStructs[dem_usercmd] = new DemMsg::Dem_UserCmd();
demDataStructs[dem_datatables] = new DemMsg::Dem_DataTables();
demDataStructs[dem_stop] = new DemMsg::Dem_Stop();
demDataStructs[dem_stringtables] = new DemMsg::Dem_StringTables();
}
void DemHandlers::DestroyDemMsgStructs(DemDataStructArray& demDataStructs)
{
delete reinterpret_cast<DemMsg::Dem_Unknown*>(demDataStructs[0]);
delete reinterpret_cast<DemMsg::Dem_Packet*>(demDataStructs[1]);
delete reinterpret_cast<DemMsg::Dem_Packet*>(demDataStructs[2]);
delete reinterpret_cast<DemMsg::Dem_SyncTick*>(demDataStructs[3]);
delete reinterpret_cast<DemMsg::Dem_ConsoleCmd*>(demDataStructs[4]);
delete reinterpret_cast<DemMsg::Dem_UserCmd*>(demDataStructs[5]);
delete reinterpret_cast<DemMsg::Dem_DataTables*>(demDataStructs[6]);
delete reinterpret_cast<DemMsg::Dem_Stop*>(demDataStructs[7]);
delete reinterpret_cast<DemMsg::Dem_StringTables*>(demDataStructs[8]);
delete reinterpret_cast<DemMsg::Dem_Unknown*>(demDataStructs[dem_unknown]);
delete reinterpret_cast<DemMsg::Dem_Packet*>(demDataStructs[dem_signon]);
delete reinterpret_cast<DemMsg::Dem_Packet*>(demDataStructs[dem_packet]);
delete reinterpret_cast<DemMsg::Dem_SyncTick*>(demDataStructs[dem_synctick]);
delete reinterpret_cast<DemMsg::Dem_ConsoleCmd*>(demDataStructs[dem_consolecmd]);
delete reinterpret_cast<DemMsg::Dem_UserCmd*>(demDataStructs[dem_usercmd]);
delete reinterpret_cast<DemMsg::Dem_DataTables*>(demDataStructs[dem_datatables]);
delete reinterpret_cast<DemMsg::Dem_Stop*>(demDataStructs[dem_stop]);
delete reinterpret_cast<DemMsg::Dem_StringTables*>(demDataStructs[dem_stringtables]);
}
#define DECLARE_DEM_HANDLER_ARRAY(funcname) \

View File

@ -1,6 +1,7 @@
#include "logic.h"
#include "netmessages/svc_serverinfo.h"
#include "io/voicewriter/voicedatawriter.h"
#include <cstring>
#include <iostream>
@ -13,7 +14,8 @@ Logic::Logic(SourceGameContext* context):
{"header", {}},
{"serverinfo", {}},
{"players", {}},
{"chat", {}}
{"chat", {}},
{"voice", {}}
});
}
@ -60,6 +62,9 @@ void Logic::Finish(bool dirty)
data["demoheader"]["playback_frames"] = context->curFrame;
}
data["voice"]["total_time"] = voiceTotalTime;
data["voice"]["active_time"] = voiceActiveTime;
std::string out = data.dump(2, ' ', false, json::error_handler_t::replace);
out.append("\n");
fwrite(out.c_str(), out.size(), 1, context->outputFp);
@ -101,6 +106,7 @@ void Logic::OnServerInfo(NetMsg::SVC_ServerInfo* serverInfo)
void Logic::OnClientConnected(int client)
{
assert(client >= 0 && client < MAX_PLAYERS);
assert(clients[client].connected == -1);
const auto& info = context->players[client].info;
@ -124,6 +130,7 @@ void Logic::OnClientConnected(int client)
void Logic::OnClientDisconnected(int client, const char* reason)
{
assert(client >= 0 && client < MAX_PLAYERS);
assert(clients[client].connected != -1);
const auto& info = context->players[client].info;
@ -146,6 +153,7 @@ void Logic::OnClientDisconnected(int client, const char* reason)
void Logic::OnClientSettingsChanged(int client)
{
assert(client >= 0 && client < MAX_PLAYERS);
assert(clients[client].connected != -1);
const auto& info = context->players[client].info;
@ -167,6 +175,9 @@ void Logic::OnClientSettingsChanged(int client)
void Logic::OnClientChat(int client, bool bWantsToChat, const char* msgName, const char* msgSender, const char* msgText)
{
assert(client >= 0 && client < MAX_PLAYERS);
assert(clients[client].connected != -1);
const auto& info = context->players[client].info;
json chat = {
{"tick", curTick},
@ -181,16 +192,31 @@ void Logic::OnClientChat(int client, bool bWantsToChat, const char* msgName, con
void Logic::OnClientVoiceChat(int client, float length)
{
assert(client >= 0 && client < MAX_PLAYERS);
assert(clients[client].connected != -1);
clients[client].voiceTime += length;
voiceTotalTime += length;
float now = curTick * context->fTickRate;
float endtime = now + length;
if (now >= voiceEndTime)
{
voiceEndTime = endtime;
voiceActiveTime += length;
}
else if (endtime > voiceEndTime)
{
voiceActiveTime += (endtime - voiceEndTime);
voiceEndTime = endtime;
}
}
void Logic::OnVoiceCodec(const char* codec, int quality, int sampleRate)
{
data["voice_init"] = json({
data["voice"] = json({
{"codec", codec},
{"quality", quality},
{"sampleRate", sampleRate}
});
}
}

View File

@ -34,6 +34,9 @@ struct Logic
void OnVoiceCodec(const char* codec, int quality, int sampleRate);
int32_t curTick = 0;
float voiceTotalTime = 0.0f;
float voiceActiveTime = 0.0f;
float voiceEndTime = 0.0f;
SourceGameContext* context = nullptr;
json data;
};

View File

@ -19,11 +19,12 @@ SourceGameContext::SourceGameContext(std::string outputDir, std::string outputDi
outputDirVoice(outputDirVoice)
{
stringTables = new StringTableContainer(this);
memset(players, 0, sizeof(players));
}
SourceGameContext::~SourceGameContext()
{
delete voiceWriter;
voiceWriter = nullptr;
delete logic;
logic = nullptr;
@ -101,7 +102,7 @@ void SourceGameContext::OnNetPacket(NetPacket& packet)
if(umsg->msgType == UserMsg::SayText2)
{
int client = msg.ReadByte();
int client = msg.ReadByte() - 1;
bool bWantsToChat = msg.ReadByte();
char msgName[2048] = {0};
@ -120,7 +121,6 @@ void SourceGameContext::OnNetPacket(NetPacket& packet)
{
voiceWriter->OnNetPacket(packet);
}
}
void SourceGameContext::OnGameEvent(const char *name, GameEvents::EventDataMap &data)

View File

@ -105,7 +105,7 @@ struct SourceGameContext
struct
{
bool connected;
bool connected = false;
player_info_t info;
} players[MAX_PLAYERS];
};

View File

@ -24,6 +24,7 @@ public:
void Init(const char* file, uint32_t sampleRate)
{
assert(!m_Enc);
m_SampleRate = sampleRate;
m_Samples = 0;
m_Comments = ope_comments_create();
@ -56,8 +57,9 @@ public:
m_Samples += numSamples;
}
void PadSilence(uint64_t numSamples)
void PadSilence(uint64_t milliseconds)
{
uint64_t numSamples = (milliseconds * (uint64_t)m_SampleRate) / 1000UL;
if(!m_Enc || m_Samples >= numSamples)
return;
@ -75,6 +77,7 @@ public:
private:
OggOpusComments *m_Comments = nullptr;
OggOpusEnc *m_Enc = nullptr;
uint32_t m_SampleRate = 0;
uint64_t m_Samples = 0;
static const uint32_t bytesPerSample = 2;

View File

@ -87,11 +87,11 @@ void CeltVoiceDecoder::DecodeFrame(const uint8_t* compressedData, int16_t* uncom
bool SilkVoiceDecoder::DoInit(int32_t sampleRate)
{
m_Silk_DecoderControl.API_sampleRate = sampleRate;
if(m_Silk_DecoderState)
{
return false;
}
m_Silk_DecoderControl.API_sampleRate = sampleRate;
int decoderSize;
SKP_Silk_SDK_Get_Decoder_Size(&decoderSize);
@ -154,7 +154,7 @@ void VoiceDataWriter::Finish()
state.second.celt_decoder.Destroy();
state.second.silk_decoder.Destroy();
state.second.fileWriter.PadSilence(((uint64_t)(m_curTick - m_silenceTicks) * state.second.sampleRate) / context->fTickRate);
state.second.fileWriter.PadSilence((((uint64_t)m_curTick - m_silenceTicks) * 1000000UL) / (uint64_t)(context->fTickRate * 1000UL));
state.second.fileWriter.Close();
state.second.lastVoiceDataTick = -1;
}
@ -174,22 +174,22 @@ void VoiceDataWriter::StartCommandPacket(const CommandPacket& packet)
void VoiceDataWriter::EndCommandPacket(const PacketTrailingBits& trailingBits)
{
const int tickMargin = context->fTickRate / 10.0; // 100ms
const int tickMargin = context->fTickInterval * 150.0; // ms
if (m_curTick <= tickMargin)
return;
// Skip silence if noone talks for at least 5 seconds
if((m_curTick - m_lastVoiceTick) / context->fTickRate > 5.0)
// Skip silence if noone talks for at least 3 seconds
if((m_curTick - m_lastVoiceTick) / context->fTickRate > 3.0)
m_silenceTicks += (m_curTick - m_lastTick);
for(auto& state : m_playerVoiceStates)
{
if((m_curTick - state.second.lastVoiceDataTick) > tickMargin)
state.second.fileWriter.PadSilence(((uint64_t)(m_curTick - m_silenceTicks) * state.second.sampleRate) / context->fTickRate);
state.second.fileWriter.PadSilence((((uint64_t)m_curTick - m_silenceTicks) * 1000000UL) / (uint64_t)(context->fTickRate * 1000UL));
}
}
int VoiceDataWriter::ParseSteamVoicePacket(uint8_t* bytes, int numBytes, PlayerVoiceState& state)
int VoiceDataWriter::ParseSteamVoicePacket(const uint8_t* bytes, int numBytes, PlayerVoiceState& state)
{
int numDecompressedSamples = 0;
int pos = 0;
@ -222,10 +222,10 @@ int VoiceDataWriter::ParseSteamVoicePacket(uint8_t* bytes, int numBytes, PlayerV
{
if(pos + 2 > dataLen)
return numDecompressedSamples;
short rate = *((int16_t *)&bytes[pos]);
uint16_t rate = *((int16_t *)&bytes[pos]);
pos += 2;
state.silk_decoder.DoInit(rate);
state.sampleRate = rate;
if(state.silk_decoder.DoInit(rate))
state.sampleRate = rate;
} break;
case 10: // Unknown / Unused
{
@ -241,14 +241,16 @@ int VoiceDataWriter::ParseSteamVoicePacket(uint8_t* bytes, int numBytes, PlayerV
{
if(pos + 2 > dataLen)
return numDecompressedSamples;
short length = *((int16_t *)&bytes[pos]);
uint16_t length = *((int16_t *)&bytes[pos]);
pos += 2;
if(pos + length > dataLen)
return numDecompressedSamples;
int freeSamples = (sizeof(m_decodeBuffer) / sizeof(int16_t)) - numDecompressedSamples;
if(payloadType == 3)
{
length = MIN(freeSamples * 2, length);
memcpy(&m_decodeBuffer[numDecompressedSamples], &bytes[pos], length);
numDecompressedSamples += length / sizeof(int16_t);
}
@ -258,10 +260,10 @@ int VoiceDataWriter::ParseSteamVoicePacket(uint8_t* bytes, int numBytes, PlayerV
int maxpos = tpos + length;
while(tpos <= (maxpos - 2))
{
short chunkLength = *((int16_t *)&bytes[tpos]);
int16_t chunkLength = *((int16_t *)&bytes[tpos]);
tpos += 2;
if(chunkLength == -1)
if(chunkLength < 0)
{
state.silk_decoder.Reset();
continue;
@ -270,6 +272,7 @@ int VoiceDataWriter::ParseSteamVoicePacket(uint8_t* bytes, int numBytes, PlayerV
{
// DTX (discontinued transmission)
int numEmptySamples = state.sampleRate / 50;
numEmptySamples = MIN(freeSamples, numEmptySamples);
memset(&m_decodeBuffer[numDecompressedSamples], 0, numEmptySamples * sizeof(int16_t));
numDecompressedSamples += numEmptySamples;
continue;
@ -278,8 +281,7 @@ int VoiceDataWriter::ParseSteamVoicePacket(uint8_t* bytes, int numBytes, PlayerV
if(tpos + chunkLength > maxpos)
return numDecompressedSamples;
int ret = state.silk_decoder.Decompress(&bytes[tpos], chunkLength, &m_decodeBuffer[numDecompressedSamples],
(sizeof(m_decodeBuffer) / sizeof(int16_t)) - numDecompressedSamples);
int ret = state.silk_decoder.Decompress(&bytes[tpos], chunkLength, &m_decodeBuffer[numDecompressedSamples], freeSamples);
numDecompressedSamples += ret;
tpos += chunkLength;
}
@ -291,7 +293,9 @@ int VoiceDataWriter::ParseSteamVoicePacket(uint8_t* bytes, int numBytes, PlayerV
{
if(pos + 2 > dataLen)
return numDecompressedSamples;
short numSamples = *((int16_t *)&bytes[pos]);
uint16_t numSamples = *((uint16_t *)&bytes[pos]);
int freeSamples = (sizeof(m_decodeBuffer) / sizeof(int16_t)) - numDecompressedSamples;
numSamples = MIN(freeSamples, numSamples);
memset(&m_decodeBuffer[numDecompressedSamples], 0, numSamples * sizeof(int16_t));
numDecompressedSamples += numSamples;
pos += 2;
@ -329,7 +333,7 @@ void VoiceDataWriter::OnNetPacket(NetPacket& packet)
assert(voiceData->fromClientIndex < MAX_PLAYERS);
const char* guid = context->players[voiceData->fromClientIndex].info.guid;
uint8_t* bytes = voiceData->data.get();
const uint8_t* bytes = voiceData->data.get();
assert((voiceData->dataLengthInBits % 8) == 0);
const int numBytes = voiceData->dataLengthInBits / 8;
@ -343,7 +347,7 @@ void VoiceDataWriter::OnNetPacket(NetPacket& packet)
state.sampleRate = config.sampleRate;
state.celt_decoder.DoInit(m_celtMode, config.frameSizeSamples, config.encodedFrameSizeBytes);
numDecompressedSamples = state.celt_decoder.Decompress(bytes, numBytes, m_decodeBuffer, sizeof(m_decodeBuffer));
numDecompressedSamples = state.celt_decoder.Decompress(bytes, numBytes, m_decodeBuffer, sizeof(m_decodeBuffer) / sizeof(int16_t));
}
else
{
@ -366,7 +370,7 @@ void VoiceDataWriter::OnNetPacket(NetPacket& packet)
{
std::string name = std::string(m_outputPath) + "/" + std::string(guid) + ".opus";
state.fileWriter.Init(name.c_str(), state.sampleRate);
state.fileWriter.PadSilence(((uint64_t)(m_curTick - m_silenceTicks) * state.sampleRate) / context->fTickRate);
state.fileWriter.PadSilence((((uint64_t)m_curTick - m_silenceTicks) * 1000000UL) / (uint64_t)(context->fTickRate * 1000UL));
}
state.fileWriter.WriteSamples(m_decodeBuffer, numDecompressedSamples);

View File

@ -74,7 +74,7 @@ private:
int sampleRate = 0;
};
int ParseSteamVoicePacket(uint8_t* bytes, int numBytes, PlayerVoiceState& state);
int ParseSteamVoicePacket(const uint8_t* bytes, int numBytes, PlayerVoiceState& state);
private:
SourceGameContext *context = nullptr;
@ -88,7 +88,7 @@ private:
int32_t m_silenceTicks = 0;
const char* m_outputPath = nullptr;
int16_t m_decodeBuffer[8192];
int16_t m_decodeBuffer[16384];
static const int sQuality = 3;
eCodec m_Codec = CODEC_NONE;