mirror of
https://github.com/rehlds/revoice.git
synced 2025-03-03 09:05:29 +03:00
Implemented Steam P2P stream voice codec
This commit is contained in:
parent
01dab99480
commit
46165f761f
39
revoice/msvc/PostBuild.bat
Normal file
39
revoice/msvc/PostBuild.bat
Normal file
@ -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
|
1
revoice/msvc/PublishPath.txt
Normal file
1
revoice/msvc/PublishPath.txt
Normal file
@ -0,0 +1 @@
|
||||
d:\rehlds_hbtrace\cstrike\addons\revoice\
|
@ -35,6 +35,7 @@
|
||||
<ClInclude Include="..\src\revoice_player.h" />
|
||||
<ClInclude Include="..\src\revoice_rehlds_api.h" />
|
||||
<ClInclude Include="..\src\revoice_shared.h" />
|
||||
<ClInclude Include="..\src\SteamP2PCodec.h" />
|
||||
<ClInclude Include="..\src\VoiceEncoder_Silk.h" />
|
||||
<ClInclude Include="..\src\VoiceEncoder_Speex.h" />
|
||||
<ClInclude Include="..\src\voice_codec_frame.h" />
|
||||
@ -56,6 +57,7 @@
|
||||
<ClCompile Include="..\src\revoice_rehlds_api.cpp" />
|
||||
<ClCompile Include="..\src\revoice_utils.cpp" />
|
||||
<ClCompile Include="..\src\sdk_util.cpp" />
|
||||
<ClCompile Include="..\src\SteamP2PCodec.cpp" />
|
||||
<ClCompile Include="..\src\VoiceEncoder_Silk.cpp" />
|
||||
<ClCompile Include="..\src\VoiceEncoder_Speex.cpp" />
|
||||
<ClCompile Include="..\src\voice_codec_frame.cpp" />
|
||||
@ -120,6 +122,12 @@
|
||||
<AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<ModuleDefinitionFile>revoice.def</ModuleDefinitionFile>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>IF EXIST "$(ProjectDir)PostBuild.bat" (CALL "$(ProjectDir)PostBuild.bat" "$(TargetDir)" "$(TargetName)" "$(TargetExt)" "$(ProjectDir)")</Command>
|
||||
</PostBuildEvent>
|
||||
<PostBuildEvent>
|
||||
<Message>Automatic deployment script</Message>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
|
@ -93,6 +93,9 @@
|
||||
<ClInclude Include="..\src\revoice_main.h">
|
||||
<Filter>src</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\SteamP2PCodec.h">
|
||||
<Filter>src</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\src\dllapi.cpp">
|
||||
@ -143,5 +146,8 @@
|
||||
<ClCompile Include="..\src\revoice_main.cpp">
|
||||
<Filter>src</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\SteamP2PCodec.cpp">
|
||||
<Filter>src</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
120
revoice/src/SteamP2PCodec.cpp
Normal file
120
revoice/src/SteamP2PCodec.cpp
Normal file
@ -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;
|
||||
}
|
20
revoice/src/SteamP2PCodec.h
Normal file
20
revoice/src/SteamP2PCodec.h
Normal file
@ -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;
|
||||
};
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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; }
|
||||
};
|
||||
|
@ -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();
|
||||
|
@ -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, ...)
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user