From 46165f761ff0dd46b99594ea4f2afb1f60d1ff0f Mon Sep 17 00:00:00 2001 From: thecrock Date: Mon, 14 Dec 2015 17:13:59 +0400 Subject: [PATCH] Implemented Steam P2P stream voice codec --- revoice/msvc/PostBuild.bat | 39 +++++++++ revoice/msvc/PublishPath.txt | 1 + revoice/msvc/ReVoice.vcxproj | 8 ++ revoice/msvc/ReVoice.vcxproj.filters | 6 ++ revoice/src/SteamP2PCodec.cpp | 120 +++++++++++++++++++++++++++ revoice/src/SteamP2PCodec.h | 20 +++++ revoice/src/VoiceEncoder_Silk.cpp | 2 +- revoice/src/VoiceEncoder_Speex.cpp | 1 - revoice/src/precompiled.h | 1 + revoice/src/revoice_main.cpp | 12 ++- revoice/src/revoice_player.cpp | 2 +- revoice/src/revoice_player.h | 6 +- revoice/src/revoice_shared.h | 1 + revoice/src/revoice_utils.cpp | 8 ++ 14 files changed, 219 insertions(+), 8 deletions(-) create mode 100644 revoice/msvc/PostBuild.bat create mode 100644 revoice/msvc/PublishPath.txt create mode 100644 revoice/src/SteamP2PCodec.cpp create mode 100644 revoice/src/SteamP2PCodec.h diff --git a/revoice/msvc/PostBuild.bat b/revoice/msvc/PostBuild.bat new file mode 100644 index 0000000..aa8a294 --- /dev/null +++ b/revoice/msvc/PostBuild.bat @@ -0,0 +1,39 @@ +@echo OFF +:: +:: Post-build auto-deploy script +:: Create and fill PublishPath.txt file with path to deployment folder +:: I.e. PublishPath.txt should contain one line with a folder path +:: Call it so: +:: IF EXIST "$(ProjectDir)PostBuild.bat" (CALL "$(ProjectDir)PostBuild.bat" "$(TargetDir)" "$(TargetName)" "$(TargetExt)" "$(ProjectDir)") +:: + +SET targetDir=%~1 +SET targetName=%~2 +SET targetExt=%~3 +SET projectDir=%~4 +SET destination= + +IF NOT EXIST "%projectDir%\PublishPath.txt" ( + ECHO No deployment path specified. Create PublishPath.txt near PostBuild.bat with paths on separate lines for auto deployment. + exit /B 0 +) + +FOR /f "tokens=* delims= usebackq" %%a IN ("%projectDir%\PublishPath.txt") DO ( + ECHO Deploying to: %%a + IF NOT "%%a" == "" ( + copy /Y "%targetDir%%targetName%%targetExt%" "%%a" + IF NOT ERRORLEVEL 1 ( + IF EXIST "%targetDir%%targetName%.pdb" ( + copy /Y "%targetDir%%targetName%.pdb" "%%a" + ) + ) ELSE ( + ECHO PostBuild.bat ^(27^) : warning : Can't copy '%targetName%%targetExt%' to deploy path '%%a' + ) + ) +) + +IF "%%a" == "" ( + ECHO No deployment path specified. +) + +exit /B 0 \ No newline at end of file diff --git a/revoice/msvc/PublishPath.txt b/revoice/msvc/PublishPath.txt new file mode 100644 index 0000000..ae7cc7c --- /dev/null +++ b/revoice/msvc/PublishPath.txt @@ -0,0 +1 @@ +d:\rehlds_hbtrace\cstrike\addons\revoice\ diff --git a/revoice/msvc/ReVoice.vcxproj b/revoice/msvc/ReVoice.vcxproj index 36e956c..ccc4e9b 100644 --- a/revoice/msvc/ReVoice.vcxproj +++ b/revoice/msvc/ReVoice.vcxproj @@ -35,6 +35,7 @@ + @@ -56,6 +57,7 @@ + @@ -120,6 +122,12 @@ ws2_32.lib;%(AdditionalDependencies) revoice.def + + IF EXIST "$(ProjectDir)PostBuild.bat" (CALL "$(ProjectDir)PostBuild.bat" "$(TargetDir)" "$(TargetName)" "$(TargetExt)" "$(ProjectDir)") + + + Automatic deployment script + diff --git a/revoice/msvc/ReVoice.vcxproj.filters b/revoice/msvc/ReVoice.vcxproj.filters index f9c453d..6d51482 100644 --- a/revoice/msvc/ReVoice.vcxproj.filters +++ b/revoice/msvc/ReVoice.vcxproj.filters @@ -93,6 +93,9 @@ src + + src + @@ -143,5 +146,8 @@ src + + src + \ No newline at end of file diff --git a/revoice/src/SteamP2PCodec.cpp b/revoice/src/SteamP2PCodec.cpp new file mode 100644 index 0000000..7b90feb --- /dev/null +++ b/revoice/src/SteamP2PCodec.cpp @@ -0,0 +1,120 @@ +#include "precompiled.h" + +CSteamP2PCodec::CSteamP2PCodec(IVoiceCodec* backend) { + m_BackendCodec = backend; +} + +bool CSteamP2PCodec::Init(int quality) { + return m_BackendCodec->Init(quality); +} + +void CSteamP2PCodec::Release() { + m_BackendCodec->Release(); + delete this; +} + +bool CSteamP2PCodec::ResetState() { + return m_BackendCodec->ResetState(); +} + +int CSteamP2PCodec::StreamDecode(const char *pCompressed, int compressedBytes, char *pUncompressed, int maxUncompressedBytes) const { + const char* maxReadPos = pCompressed + compressedBytes; + const char* readPos = pCompressed; + while (readPos < maxReadPos) { + uint8 opcode = *(uint8*)readPos; + readPos++; + + switch (opcode) { + case 0xB: //Set sampling rate + if (readPos + 2 > maxReadPos) { + return 0; + } + readPos += 2; + break; + + case 0x04: //Voice payoad + { + if (readPos + 2 > maxReadPos) { + return 0; + } + uint16 len = *(uint16*)readPos; + readPos += 2; + + if (readPos + len > maxReadPos) { + return 0; + } + + int decompressedLen = m_BackendCodec->Decompress(readPos, len, pUncompressed, maxUncompressedBytes); + return decompressedLen; + } + + default: //Invalid or unknown opcode + return 0; + } + } + + return 0; // no voice payload in the stream +} + +int CSteamP2PCodec::StreamEncode(const char *pUncompressedBytes, int nSamples, char *pCompressed, int maxCompressedBytes, bool bFinal) const { + char* writePos = pCompressed; + if (maxCompressedBytes < 10) { // no room + return 0; + } + + *(writePos++) = 0x0B; //set sampling rate + *(uint16*)writePos = 16000; + writePos += 2; + + *(writePos++) = 0x04; //voice payload + + int compressRes = m_BackendCodec->Compress(pUncompressedBytes, nSamples, writePos + 2, maxCompressedBytes - (1 + 2 + 1 + 2), bFinal); + if (compressRes == 0) { + return 0; + } + + *(uint16*)writePos = compressRes; + writePos += 2; + writePos += compressRes; + + return writePos - pCompressed; +} + +int CSteamP2PCodec::Decompress(const char *pCompressed, int compressedBytes, char *pUncompressed, int maxUncompressedBytes) { + if (compressedBytes < 12) { + return 0; + } + + uint32 computedChecksum = crc32(pCompressed, compressedBytes - 4); + uint32 wireChecksum = *(uint32*)(pCompressed + compressedBytes - 4); + if (computedChecksum != wireChecksum) { + return 0; + } + + return StreamDecode(pCompressed + 8, compressedBytes - 12, pUncompressed, maxUncompressedBytes); +} + +int CSteamP2PCodec::Compress(const char *pUncompressedBytes, int nSamples, char *pCompressed, int maxCompressedBytes, bool bFinal) { + if (maxCompressedBytes < 12) { //no room + return 0; + } + + char* writePos = pCompressed; + *(uint32*)writePos = 0x00000011; //steamid (low part) + writePos += 4; + + *(uint32*)writePos = 0x01100001; //steamid (high part) + writePos += 4; + + int encodeRes = StreamEncode(pUncompressedBytes, nSamples, writePos, maxCompressedBytes - 12, bFinal); + if (encodeRes <= 0) { + return 0; + } + writePos += encodeRes; + + uint32 cksum = crc32(pUncompressedBytes, writePos - pCompressed); + *(uint32*)writePos = cksum; + writePos += 4; + + return writePos - pCompressed; +} diff --git a/revoice/src/SteamP2PCodec.h b/revoice/src/SteamP2PCodec.h new file mode 100644 index 0000000..c7cf8b5 --- /dev/null +++ b/revoice/src/SteamP2PCodec.h @@ -0,0 +1,20 @@ +#pragma once + +#include "revoice_shared.h" +#include "VoiceEncoder_Silk.h" + +class CSteamP2PCodec : public IVoiceCodec { +public: + CSteamP2PCodec(IVoiceCodec* backend); + virtual bool Init(int quality); + virtual void Release(); + virtual int Compress(const char *pUncompressedBytes, int nSamples, char *pCompressed, int maxCompressedBytes, bool bFinal); + virtual int Decompress(const char *pCompressed, int compressedBytes, char *pUncompressed, int maxUncompressedBytes); + virtual bool ResetState(); + +private: + int StreamDecode(const char *pCompressed, int compressedBytes, char *pUncompressed, int maxUncompressedBytes) const; + int StreamEncode(const char *pUncompressedBytes, int nSamples, char *pCompressed, int maxCompressedBytes, bool bFinal) const; +private: + IVoiceCodec* m_BackendCodec; +}; \ No newline at end of file diff --git a/revoice/src/VoiceEncoder_Silk.cpp b/revoice/src/VoiceEncoder_Silk.cpp index 92179d4..2473186 100644 --- a/revoice/src/VoiceEncoder_Silk.cpp +++ b/revoice/src/VoiceEncoder_Silk.cpp @@ -95,7 +95,7 @@ int VoiceEncoder_Silk::Compress(const char *pUncompressedIn, int nSamplesIn, cha int originalNBytes = (pWritePosMax - pWritePos > 0xFFFF) ? -1 : (pWritePosMax - pWritePos); nSamplesToEncode = (nSamples < nSamplesPerFrame) ? nSamples : nSamplesPerFrame; - this->m_encControl.useDTX = 1; + this->m_encControl.useDTX = 0; this->m_encControl.maxInternalSampleRate = 16000; this->m_encControl.useInBandFEC = 0; this->m_encControl.API_sampleRate = inSampleRate; diff --git a/revoice/src/VoiceEncoder_Speex.cpp b/revoice/src/VoiceEncoder_Speex.cpp index 3f70a0c..88c22bc 100644 --- a/revoice/src/VoiceEncoder_Speex.cpp +++ b/revoice/src/VoiceEncoder_Speex.cpp @@ -55,7 +55,6 @@ bool VoiceEncoder_Speex::Init(int quality, int &rawFrameSize, int &encodedFrameS encodedFrameSize = ENCODED_FRAME_SIZE[m_Quality]; speex_encoder_ctl(m_EncoderState, SPEEX_SET_QUALITY, &m_Quality); - speex_decoder_ctl(m_DecoderState, SPEEX_SET_QUALITY, &m_Quality); postfilter = 1; speex_decoder_ctl(m_DecoderState, SPEEX_SET_ENH, &postfilter); diff --git a/revoice/src/precompiled.h b/revoice/src/precompiled.h index 3e3974d..c7a842b 100644 --- a/revoice/src/precompiled.h +++ b/revoice/src/precompiled.h @@ -21,6 +21,7 @@ #include "VoiceEncoder_Silk.h" #include "VoiceEncoder_Speex.h" #include "voice_codec_frame.h" +#include "SteamP2PCodec.h" #include "revoice_player.h" #include "revoice_main.h" diff --git a/revoice/src/revoice_main.cpp b/revoice/src/revoice_main.cpp index 321978f..2d861b4 100644 --- a/revoice/src/revoice_main.cpp +++ b/revoice/src/revoice_main.cpp @@ -35,7 +35,7 @@ void mm_CvarValue2(const edict_t *pEnt, int requestID, const char *cvarName, con // ] sv_version // "sv_version" is "1.1.2.1/2.0.0.0,48,4554" - const char* lastSeparator = strrchr(cvarName, ','); + const char* lastSeparator = strrchr(value, ','); int buildNumber = 0; if (lastSeparator) { buildNumber = atoi(lastSeparator + 1); @@ -89,10 +89,16 @@ void SV_ParseVoiceData_emu(IGameClient* cl) { switch (srcPlayer->GetCodecType()) { case vct_silk: + { + CRC32_t hCrc; + g_engfuncs.pfnCRC32_Init(&hCrc); + g_engfuncs.pfnCRC32_ProcessBuffer(&hCrc, chReceived, nDataLength - 4); + hCrc = g_engfuncs.pfnCRC32_Final(hCrc); silkData = chReceived; silkDataLen = nDataLength; speexData = transcodedBuf; speexDataLen = TranscodeVoice(silkData, silkDataLen, srcPlayer->GetSilkCodec(), srcPlayer->GetSpeexCodec(), transcodedBuf, sizeof(transcodedBuf)); break; + } case vct_speex: speexData = chReceived; speexDataLen = nDataLength; @@ -130,7 +136,7 @@ void SV_ParseVoiceData_emu(IGameClient* cl) { break; } - if (sendBuf == NULL) { + if (sendBuf == NULL || nSendLen == 0) { continue; } @@ -178,7 +184,7 @@ void SV_WriteVoiceCodec_hooked(IRehldsHook_SV_WriteVoiceCodec* chain, sizebuf_t* case vct_speex: g_RehldsFuncs->MSG_WriteByte(sb, 52); //svc_voiceinit - g_RehldsFuncs->MSG_WriteString(sb, "speex"); //codec id + g_RehldsFuncs->MSG_WriteString(sb, "voice_speex"); //codec id g_RehldsFuncs->MSG_WriteByte(sb, 5); //quality break; diff --git a/revoice/src/revoice_player.cpp b/revoice/src/revoice_player.cpp index 12cf584..8596907 100644 --- a/revoice/src/revoice_player.cpp +++ b/revoice/src/revoice_player.cpp @@ -6,7 +6,7 @@ CRevoicePlayer g_Players[MAX_PLAYERS]; CRevoicePlayer::CRevoicePlayer() { m_CodecType = vct_none; m_SpeexCodec = new VoiceCodec_Frame(new VoiceEncoder_Speex()); - m_SilkCodec = new VoiceEncoder_Silk(); + m_SilkCodec = new CSteamP2PCodec(new VoiceEncoder_Silk()); m_SpeexCodec->Init(5); m_SilkCodec->Init(5); diff --git a/revoice/src/revoice_player.h b/revoice/src/revoice_player.h index af4bdce..eeb9c39 100644 --- a/revoice/src/revoice_player.h +++ b/revoice/src/revoice_player.h @@ -2,14 +2,16 @@ #include "revoice_shared.h" #include "VoiceEncoder_Silk.h" +#include "SteamP2PCodec.h" #include "VoiceEncoder_Speex.h" #include "voice_codec_frame.h" + class CRevoicePlayer { private: IGameClient* m_RehldsClient; revoice_codec_type m_CodecType; - VoiceEncoder_Silk* m_SilkCodec; + CSteamP2PCodec* m_SilkCodec; VoiceCodec_Frame* m_SpeexCodec; int m_Protocol; @@ -22,7 +24,7 @@ public: int GetProtocol() const { return m_Protocol; } revoice_codec_type GetCodecType() const { return m_CodecType; } - VoiceEncoder_Silk* GetSilkCodec() const { return m_SilkCodec; } + CSteamP2PCodec* GetSilkCodec() const { return m_SilkCodec; } VoiceCodec_Frame* GetSpeexCodec() const { return m_SpeexCodec; } IGameClient* GetClient() const { return m_RehldsClient; } }; diff --git a/revoice/src/revoice_shared.h b/revoice/src/revoice_shared.h index 165ee5d..551bc2a 100644 --- a/revoice/src/revoice_shared.h +++ b/revoice/src/revoice_shared.h @@ -40,6 +40,7 @@ T clamp(T a, T min, T max) { extern char* trimbuf(char *str); extern void LCPrintf(bool critical, const char *fmt, ...); +extern uint32 crc32(const void* buf, unsigned int bufLen); extern bool Revoice_Load(); extern bool Revoice_Utils_Init(); diff --git a/revoice/src/revoice_utils.cpp b/revoice/src/revoice_utils.cpp index 3bc4070..8e958a1 100644 --- a/revoice/src/revoice_utils.cpp +++ b/revoice/src/revoice_utils.cpp @@ -61,6 +61,14 @@ char* trimbuf(char *str) { return str; } +uint32 crc32(const void* buf, unsigned int bufLen) { + CRC32_t hCrc; + g_engfuncs.pfnCRC32_Init(&hCrc); + g_engfuncs.pfnCRC32_ProcessBuffer(&hCrc, (void*)buf, bufLen); + hCrc = g_engfuncs.pfnCRC32_Final(hCrc); + return hCrc; +} + void util_syserror(const char* fmt, ...) {