Use Opus for VoIP

Server/client VoIP protocol is handled by adding new cvars
cl_voipProtocol and sv_voipProtocol, sv_voip and cl_voip
are used to auto set/clear them. All users need to touch
are cl/sv_voip as 0 or 1 just like before.

Old Speex VoIP packets in demos are skipped.
New VoIP packets are skipped in demos if sv_voipProtocol
doesn't match cl_voipProtocol.

Notable difference between usage of speex and opus codecs,
when using Speex client would be sent 80ms at a time.
Using Opus, 60ms is sent at a time. This was changed because
the Opus codec supports encoding up to 60ms at a time.
(Simpler to send only one codec frame in a packet.)
This commit is contained in:
Zack Middleton 2013-12-10 21:14:13 -06:00
parent fe619680f8
commit 615b73288f
13 changed files with 167 additions and 240 deletions

View file

@ -34,7 +34,8 @@ char *svc_strings[256] = {
"svc_download",
"svc_snapshot",
"svc_EOF",
"svc_voip",
"svc_voipSpeex",
"svc_voipOpus",
};
void SHOWNET( msg_t *msg, char *s) {
@ -359,8 +360,8 @@ void CL_SystemInfoChanged( void ) {
else
#endif
{
s = Info_ValueForKey( systemInfo, "sv_voip" );
clc.voipEnabled = atoi(s);
s = Info_ValueForKey( systemInfo, "sv_voipProtocol" );
clc.voipEnabled = !Q_stricmp(s, "opus");
}
#endif
@ -680,13 +681,13 @@ static void CL_PlayVoip(int sender, int samplecnt, const byte *data, int flags)
{
if(flags & VOIP_DIRECT)
{
S_RawSamples(sender + 1, samplecnt, clc.speexSampleRate, 2, 1,
S_RawSamples(sender + 1, samplecnt, 48000, 2, 1,
data, clc.voipGain[sender], -1);
}
if(flags & VOIP_SPATIAL)
{
S_RawSamples(sender + MAX_CLIENTS + 1, samplecnt, clc.speexSampleRate, 2, 1,
S_RawSamples(sender + MAX_CLIENTS + 1, samplecnt, 48000, 2, 1,
data, 1.0f, sender);
}
}
@ -699,8 +700,8 @@ A VoIP message has been received from the server
=====================
*/
static
void CL_ParseVoip ( msg_t *msg ) {
static short decoded[4096]; // !!! FIXME: don't hardcode.
void CL_ParseVoip ( msg_t *msg, qboolean ignoreData ) {
static short decoded[VOIP_MAX_PACKET_SAMPLES*4]; // !!! FIXME: don't hard code
const int sender = MSG_ReadShort(msg);
const int generation = MSG_ReadByte(msg);
@ -708,7 +709,8 @@ void CL_ParseVoip ( msg_t *msg ) {
const int frames = MSG_ReadByte(msg);
const int packetsize = MSG_ReadShort(msg);
const int flags = MSG_ReadBits(msg, VOIP_FLAGCNT);
char encoded[1024];
unsigned char encoded[4000];
int numSamples;
int seqdiff;
int written = 0;
int i;
@ -738,14 +740,15 @@ void CL_ParseVoip ( msg_t *msg ) {
return; // overlarge packet, bail.
}
if (!clc.speexInitialized) {
MSG_ReadData(msg, encoded, packetsize); // skip payload.
return; // can't handle VoIP without libspeex!
MSG_ReadData(msg, encoded, packetsize);
if (ignoreData) {
return; // just ignore legacy speex voip data
} else if (!clc.voipCodecInitialized) {
return; // can't handle VoIP without libopus!
} 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.
}
@ -758,70 +761,59 @@ void CL_ParseVoip ( msg_t *msg ) {
// 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]);
opus_decoder_ctl(clc.opusDecoder[sender], OPUS_RESET_STATE);
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]);
// reset the decoder just in case.
opus_decoder_ctl(clc.opusDecoder[sender], OPUS_RESET_STATE);
seqdiff = 0;
} else if (seqdiff * clc.speexFrameSize * 2 >= sizeof (decoded)) { // dropped more than we can handle?
} else if (seqdiff * VOIP_MAX_PACKET_SAMPLES*2 >= sizeof (decoded)) { // dropped more than we can handle?
// just start over.
Com_DPrintf("VoIP: Dropped way too many (%d) frames from client #%d\n",
seqdiff, sender);
speex_bits_reset(&clc.speexDecoderBits[sender]);
opus_decoder_ctl(clc.opusDecoder[sender], OPUS_RESET_STATE);
seqdiff = 0;
}
if (seqdiff != 0) {
Com_DPrintf("VoIP: Dropped %d frames from client #%d\n",
seqdiff, sender);
// tell speex that we're missing frames...
// tell opus 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;
assert((written + VOIP_MAX_PACKET_SAMPLES) * 2 < sizeof (decoded));
numSamples = opus_decode(clc.opusDecoder[sender], NULL, VOIP_MAX_PACKET_SAMPLES * 2, decoded + written, sizeof (decoded) - written, 0);
if ( numSamples <= 0 ) {
Com_DPrintf("VoIP: Error decoding frame %d from client #%d\n", i, sender);
continue;
}
written += numSamples;
}
}
for (i = 0; i < frames; i++) {
const int len = MSG_ReadByte(msg);
if (len < 0) {
Com_DPrintf("VoIP: Short packet!\n");
break;
}
MSG_ReadData(msg, encoded, len);
numSamples = opus_decode(clc.opusDecoder[sender], encoded, packetsize, decoded + written, sizeof (decoded) - written, 0);
// 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);
CL_PlayVoip(sender, written, (const byte *) decoded, flags);
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;
if ( numSamples <= 0 ) {
Com_DPrintf("VoIP: Error decoding voip data from client #%d\n", sender);
numSamples = 0;
}
#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 += numSamples;
Com_DPrintf("VoIP: playback %d bytes, %d samples, %d frames\n",
written * 2, written, i);
written * 2, written, frames);
if(written > 0)
CL_PlayVoip(sender, written, (const byte *) decoded, flags);
@ -924,9 +916,14 @@ void CL_ParseServerMessage( msg_t *msg ) {
case svc_download:
CL_ParseDownload( msg );
break;
case svc_voip:
case svc_voipSpeex:
#ifdef USE_VOIP
CL_ParseVoip( msg );
CL_ParseVoip( msg, qtrue );
#endif
break;
case svc_voipOpus:
#ifdef USE_VOIP
CL_ParseVoip( msg, !clc.voipEnabled );
#endif
break;
}