diff options
Diffstat (limited to 'src/client/cl_parse.c')
-rw-r--r-- | src/client/cl_parse.c | 196 |
1 files changed, 193 insertions, 3 deletions
diff --git a/src/client/cl_parse.c b/src/client/cl_parse.c index ef57ee3f..96d4f799 100644 --- a/src/client/cl_parse.c +++ b/src/client/cl_parse.c @@ -33,7 +33,10 @@ char *svc_strings[256] = { "svc_baseline", "svc_serverCommand", "svc_download", - "svc_snapshot" + "svc_snapshot", + "svc_EOF", + "svc_extension", + "svc_voip", }; void SHOWNET( msg_t *msg, char *s) { @@ -328,6 +331,10 @@ void CL_ParseSnapshot( msg_t *msg ) { int cl_connectedToPureServer; int cl_connectedToCheatServer; +#ifdef USE_VOIP +int cl_connectedToVoipServer; +#endif + /* ================== CL_SystemInfoChanged @@ -356,6 +363,13 @@ void CL_SystemInfoChanged( void ) { return; } +#ifdef USE_VOIP + // in the future, (val) will be a protocol version string, so only + // accept explicitly 1, not generally non-zero. + s = Info_ValueForKey( systemInfo, "sv_voip" ); + cl_connectedToVoipServer = (atoi( s ) == 1); +#endif + s = Info_ValueForKey( systemInfo, "sv_cheats" ); cl_connectedToCheatServer = atoi( s ); if ( !cl_connectedToCheatServer ) { @@ -622,6 +636,164 @@ void CL_ParseDownload ( msg_t *msg ) { } } +#ifdef USE_VOIP +static +qboolean CL_ShouldIgnoreVoipSender(int sender) +{ + if (!cl_voip->integer) + return qtrue; // VoIP is disabled. + else if ((sender == clc.clientNum) && (!clc.demoplaying)) + return qtrue; // ignore own voice (unless playing back a demo). + else if (clc.voipMuteAll) + return qtrue; // all channels are muted with extreme prejudice. + else if (clc.voipIgnore[sender]) + return qtrue; // just ignoring this guy. + else if (clc.voipGain[sender] == 0.0f) + return qtrue; // too quiet to play. + + return qfalse; +} + +/* +===================== +CL_ParseVoip + +A VoIP message has been received from the server +===================== +*/ +static +void CL_ParseVoip ( msg_t *msg ) { + static short decoded[4096]; // !!! FIXME: don't hardcode. + + const int sender = MSG_ReadShort(msg); + const int generation = MSG_ReadByte(msg); + const int sequence = MSG_ReadLong(msg); + const int frames = MSG_ReadByte(msg); + const int packetsize = MSG_ReadShort(msg); + char encoded[1024]; + int seqdiff = sequence - clc.voipIncomingSequence[sender]; + int written = 0; + int i; + + Com_DPrintf("VoIP: %d-byte packet from client %d\n", packetsize, sender); + + if (sender < 0) + return; // short/invalid packet, bail. + else if (generation < 0) + return; // short/invalid packet, bail. + else if (sequence < 0) + return; // short/invalid packet, bail. + else if (frames < 0) + return; // short/invalid packet, bail. + else if (packetsize < 0) + return; // short/invalid packet, bail. + + if (packetsize > sizeof (encoded)) { // overlarge packet? + int bytesleft = packetsize; + while (bytesleft) { + int br = bytesleft; + if (br > sizeof (encoded)) + br = sizeof (encoded); + MSG_ReadData(msg, encoded, br); + bytesleft -= br; + } + return; // overlarge packet, bail. + } + + if (!clc.speexInitialized) { + MSG_ReadData(msg, encoded, packetsize); // skip payload. + return; // can't handle VoIP without libspeex! + } else if (sender >= MAX_CLIENTS) { + MSG_ReadData(msg, encoded, packetsize); // skip payload. + return; // bogus sender. + } else if (CL_ShouldIgnoreVoipSender(sender)) { + MSG_ReadData(msg, encoded, packetsize); // skip payload. + return; // Channel is muted, bail. + } + + // !!! FIXME: make sure data is narrowband? Does decoder handle this? + + Com_DPrintf("VoIP: packet accepted!\n"); + + // This is a new "generation" ... a new recording started, reset the bits. + if (generation != clc.voipIncomingGeneration[sender]) { + Com_DPrintf("VoIP: new generation %d!\n", generation); + speex_bits_reset(&clc.speexDecoderBits[sender]); + clc.voipIncomingGeneration[sender] = generation; + seqdiff = 0; + } else if (seqdiff < 0) { // we're ahead of the sequence?! + // This shouldn't happen unless the packet is corrupted or something. + Com_DPrintf("VoIP: misordered sequence! %d < %d!\n", + sequence, clc.voipIncomingSequence[sender]); + // reset the bits just in case. + speex_bits_reset(&clc.speexDecoderBits[sender]); + seqdiff = 0; + } else if (seqdiff > 100) { // more than 2 seconds of audio dropped? + // just start over. + Com_DPrintf("VoIP: Dropped way too many (%d) frames from client #%d\n", + seqdiff, sender); + speex_bits_reset(&clc.speexDecoderBits[sender]); + seqdiff = 0; + } + + if (seqdiff != 0) { + Com_DPrintf("VoIP: Dropped %d frames from client #%d\n", + seqdiff, sender); + // tell speex that we're missing frames... + for (i = 0; i < seqdiff; i++) { + assert((written + clc.speexFrameSize) * 2 < sizeof (decoded)); + speex_decode_int(clc.speexDecoder[sender], NULL, decoded + written); + written += clc.speexFrameSize; + } + } + + for (i = 0; i < frames; i++) { + char encoded[256]; + const int len = MSG_ReadByte(msg); + if (len < 0) { + Com_DPrintf("VoIP: Short packet!\n"); + break; + } + MSG_ReadData(msg, encoded, len); + + // shouldn't happen, but just in case... + if ((written + clc.speexFrameSize) * 2 > sizeof (decoded)) { + Com_DPrintf("VoIP: playback %d bytes, %d samples, %d frames\n", + written * 2, written, i); + S_RawSamples(sender + 1, written, clc.speexSampleRate, 2, 1, + (const byte *) decoded, clc.voipGain[sender]); + written = 0; + } + + speex_bits_read_from(&clc.speexDecoderBits[sender], encoded, len); + speex_decode_int(clc.speexDecoder[sender], + &clc.speexDecoderBits[sender], decoded + written); + + #if 0 + static FILE *encio = NULL; + if (encio == NULL) encio = fopen("voip-incoming-encoded.bin", "wb"); + if (encio != NULL) { fwrite(encoded, len, 1, encio); fflush(encio); } + static FILE *decio = NULL; + if (decio == NULL) decio = fopen("voip-incoming-decoded.bin", "wb"); + if (decio != NULL) { fwrite(decoded+written, clc.speexFrameSize*2, 1, decio); fflush(decio); } + #endif + + written += clc.speexFrameSize; + } + + Com_DPrintf("VoIP: playback %d bytes, %d samples, %d frames\n", + written * 2, written, i); + + if (written > 0) { + S_RawSamples(sender + 1, written, clc.speexSampleRate, 2, 1, + (const byte *) decoded, clc.voipGain[sender]); + } + + clc.voipIncomingSequence[sender] = sequence + frames; +} +#endif + + /* ===================== CL_ParseCommandString @@ -683,13 +855,26 @@ void CL_ParseServerMessage( msg_t *msg ) { cmd = MSG_ReadByte( msg ); - if ( cmd == svc_EOF) { + // See if this is an extension command after the EOF, which means we + // got data that a legacy client should ignore. + if ((cmd == svc_EOF) && (MSG_LookaheadByte( msg ) == svc_extension)) { + SHOWNET( msg, "EXTENSION" ); + MSG_ReadByte( msg ); // throw the svc_extension byte away. + cmd = MSG_ReadByte( msg ); // something legacy clients can't do! + // sometimes you get a svc_extension at end of stream...dangling + // bits in the huffman decoder giving a bogus value? + if (cmd == -1) { + cmd = svc_EOF; + } + } + + if (cmd == svc_EOF) { SHOWNET( msg, "END OF MESSAGE" ); break; } if ( cl_shownet->integer >= 2 ) { - if ( !svc_strings[cmd] ) { + if ( (cmd < 0) || (!svc_strings[cmd]) ) { Com_Printf( "%3i:BAD CMD %i\n", msg->readcount-1, cmd ); } else { SHOWNET( msg, svc_strings[cmd] ); @@ -715,6 +900,11 @@ void CL_ParseServerMessage( msg_t *msg ) { case svc_download: CL_ParseDownload( msg ); break; + case svc_voip: +#ifdef USE_VOIP + CL_ParseVoip( msg ); +#endif + break; } } } |