summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorZack Middleton <zturtleman@gmail.com>2013-12-10 21:14:13 -0600
committerTim Angus <tim@ngus.net>2016-04-07 11:50:43 +0100
commit975d4d97e4b9459c3d21b4dc3ecd807e9c330d9a (patch)
treeff58ec02fe6d22a097f71d1163c3527bc9abcea1 /src
parentfae2cb94e089fabe9a69d0909a5c25e1014cb25f (diff)
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.)
Diffstat (limited to 'src')
-rw-r--r--src/client/cl_cgame.c36
-rw-r--r--src/client/cl_input.c4
-rw-r--r--src/client/cl_main.c108
-rw-r--r--src/client/cl_parse.c105
-rw-r--r--src/client/client.h21
-rw-r--r--src/client/snd_openal.c8
-rw-r--r--src/qcommon/qcommon.h6
-rw-r--r--src/server/server.h3
-rw-r--r--src/server/sv_client.c22
-rw-r--r--src/server/sv_init.c3
-rw-r--r--src/server/sv_main.c5
-rw-r--r--src/server/sv_snapshot.c2
12 files changed, 154 insertions, 169 deletions
diff --git a/src/client/cl_cgame.c b/src/client/cl_cgame.c
index 1c7d91cc..23f3010c 100644
--- a/src/client/cl_cgame.c
+++ b/src/client/cl_cgame.c
@@ -956,37 +956,27 @@ void CL_FirstSnapshot( void ) {
#endif
#ifdef USE_VOIP
- if (!clc.speexInitialized) {
+ if (!clc.voipCodecInitialized) {
int i;
- speex_bits_init(&clc.speexEncoderBits);
- speex_bits_reset(&clc.speexEncoderBits);
+ int error;
- clc.speexEncoder = speex_encoder_init(&speex_nb_mode);
+ clc.opusEncoder = opus_encoder_create(48000, 1, OPUS_APPLICATION_VOIP, &error);
- speex_encoder_ctl(clc.speexEncoder, SPEEX_GET_FRAME_SIZE,
- &clc.speexFrameSize);
- speex_encoder_ctl(clc.speexEncoder, SPEEX_GET_SAMPLING_RATE,
- &clc.speexSampleRate);
-
- clc.speexPreprocessor = speex_preprocess_state_init(clc.speexFrameSize,
- clc.speexSampleRate);
-
- i = 1;
- speex_preprocess_ctl(clc.speexPreprocessor,
- SPEEX_PREPROCESS_SET_DENOISE, &i);
-
- i = 1;
- speex_preprocess_ctl(clc.speexPreprocessor,
- SPEEX_PREPROCESS_SET_AGC, &i);
+ if ( error ) {
+ Com_DPrintf("VoIP: Error opus_encoder_create %d\n", error);
+ return;
+ }
for (i = 0; i < MAX_CLIENTS; i++) {
- speex_bits_init(&clc.speexDecoderBits[i]);
- speex_bits_reset(&clc.speexDecoderBits[i]);
- clc.speexDecoder[i] = speex_decoder_init(&speex_nb_mode);
+ clc.opusDecoder[i] = opus_decoder_create(48000, 1, &error);
+ if ( error ) {
+ Com_DPrintf("VoIP: Error opus_decoder_create(%d) %d\n", i, error);
+ return;
+ }
clc.voipIgnore[i] = qfalse;
clc.voipGain[i] = 1.0f;
}
- clc.speexInitialized = qtrue;
+ clc.voipCodecInitialized = qtrue;
clc.voipMuteAll = qfalse;
Cmd_AddCommand ("voip", CL_Voip_f);
Cvar_Set("cl_voipSendTarget", "spatial");
diff --git a/src/client/cl_input.c b/src/client/cl_input.c
index fdd0c474..48bf51df 100644
--- a/src/client/cl_input.c
+++ b/src/client/cl_input.c
@@ -789,7 +789,7 @@ void CL_WritePacket( void ) {
{
if((clc.voipFlags & VOIP_SPATIAL) || Com_IsVoipTarget(clc.voipTargets, sizeof(clc.voipTargets), -1))
{
- MSG_WriteByte (&buf, clc_voip);
+ MSG_WriteByte (&buf, clc_voipOpus);
MSG_WriteByte (&buf, clc.voipOutgoingGeneration);
MSG_WriteLong (&buf, clc.voipOutgoingSequence);
MSG_WriteByte (&buf, clc.voipOutgoingDataFrames);
@@ -810,7 +810,7 @@ void CL_WritePacket( void ) {
MSG_Init (&fakemsg, fakedata, sizeof (fakedata));
MSG_Bitstream (&fakemsg);
MSG_WriteLong (&fakemsg, clc.reliableAcknowledge);
- MSG_WriteByte (&fakemsg, svc_voip);
+ MSG_WriteByte (&fakemsg, svc_voipOpus);
MSG_WriteShort (&fakemsg, clc.clientNum);
MSG_WriteByte (&fakemsg, clc.voipOutgoingGeneration);
MSG_WriteLong (&fakemsg, clc.voipOutgoingSequence);
diff --git a/src/client/cl_main.c b/src/client/cl_main.c
index fa47988e..6df57b4d 100644
--- a/src/client/cl_main.c
+++ b/src/client/cl_main.c
@@ -45,6 +45,7 @@ cvar_t *cl_voipSendTarget;
cvar_t *cl_voipGainDuringCapture;
cvar_t *cl_voipCaptureMult;
cvar_t *cl_voipShowMeter;
+cvar_t *cl_voipProtocol;
cvar_t *cl_voip;
#endif
@@ -250,8 +251,8 @@ void CL_Voip_f( void )
if (clc.state != CA_ACTIVE)
reason = "Not connected to a server";
- else if (!clc.speexInitialized)
- reason = "Speex not initialized";
+ else if (!clc.voipCodecInitialized)
+ reason = "Voip codec not initialized";
else if (!clc.voipEnabled)
reason = "Server doesn't support VoIP";
@@ -304,6 +305,8 @@ void CL_VoipNewGeneration(void)
clc.voipOutgoingGeneration = 1;
clc.voipPower = 0.0f;
clc.voipOutgoingSequence = 0;
+
+ opus_encoder_ctl(clc.opusEncoder, OPUS_RESET_STATE);
}
/*
@@ -392,7 +395,7 @@ void CL_VoipParseTargets(void)
===============
CL_CaptureVoip
-Record more audio from the hardware if required and encode it into Speex
+Record more audio from the hardware if required and encode it into Opus
data for later transmission.
===============
*/
@@ -422,11 +425,12 @@ void CL_CaptureVoip(void)
Com_Printf("Until then, VoIP is disabled.\n");
Cvar_Set("cl_voip", "0");
}
+ Cvar_Set("cl_voipProtocol", cl_voip->integer ? "opus" : "");
cl_voip->modified = qfalse;
cl_rate->modified = qfalse;
}
- if (!clc.speexInitialized)
+ if (!clc.voipCodecInitialized)
return; // just in case this gets called at a bad time.
if (clc.voipOutgoingDataSize > 0)
@@ -479,80 +483,67 @@ void CL_CaptureVoip(void)
if ((cl_voipSend->integer) || (finalFrame)) { // user wants to capture audio?
int samples = S_AvailableCaptureSamples();
- const int mult = (finalFrame) ? 1 : 4; // 4 == 80ms of audio.
+ const int packetSamples = (finalFrame) ? VOIP_MAX_FRAME_SAMPLES : VOIP_MAX_PACKET_SAMPLES;
// enough data buffered in audio hardware to process yet?
- if (samples >= (clc.speexFrameSize * mult)) {
- // audio capture is always MONO16 (and that's what speex wants!).
- // 2048 will cover 12 uncompressed frames in narrowband mode.
- static int16_t sampbuffer[2048];
+ if (samples >= packetSamples) {
+ // audio capture is always MONO16.
+ static int16_t sampbuffer[VOIP_MAX_PACKET_SAMPLES];
float voipPower = 0.0f;
- int speexFrames = 0;
- int wpos = 0;
- int pos = 0;
+ int voipFrames;
+ int i, bytes;
- if (samples > (clc.speexFrameSize * 4))
- samples = (clc.speexFrameSize * 4);
+ if (samples > VOIP_MAX_PACKET_SAMPLES)
+ samples = VOIP_MAX_PACKET_SAMPLES;
// !!! FIXME: maybe separate recording from encoding, so voipPower
// !!! FIXME: updates faster than 4Hz?
- samples -= samples % clc.speexFrameSize;
- S_Capture(samples, (byte *) sampbuffer); // grab from audio card.
-
- // this will probably generate multiple speex packets each time.
- while (samples > 0) {
- int16_t *sampptr = &sampbuffer[pos];
- int i, bytes;
+ samples -= samples % VOIP_MAX_FRAME_SAMPLES;
+ if (samples != 120 && samples != 240 && samples != 480 && samples != 960 && samples != 1920 && samples != 2880 ) {
+ Com_Printf("Voip: bad number of samples %d\n", samples);
+ return;
+ }
+ voipFrames = samples / VOIP_MAX_FRAME_SAMPLES;
- // preprocess samples to remove noise...
- speex_preprocess_run(clc.speexPreprocessor, sampptr);
+ S_Capture(samples, (byte *) sampbuffer); // grab from audio card.
- // check the "power" of this packet...
- for (i = 0; i < clc.speexFrameSize; i++) {
- const float flsamp = (float) sampptr[i];
- const float s = fabs(flsamp);
- voipPower += s * s;
- sampptr[i] = (int16_t) ((flsamp) * audioMult);
- }
+ // check the "power" of this packet...
+ for (i = 0; i < samples; i++) {
+ const float flsamp = (float) sampbuffer[i];
+ const float s = fabs(flsamp);
+ voipPower += s * s;
+ sampbuffer[i] = (int16_t) ((flsamp) * audioMult);
+ }
- // encode raw audio samples into Speex data...
- speex_bits_reset(&clc.speexEncoderBits);
- speex_encode_int(clc.speexEncoder, sampptr,
- &clc.speexEncoderBits);
- bytes = speex_bits_write(&clc.speexEncoderBits,
- (char *) &clc.voipOutgoingData[wpos+1],
- sizeof (clc.voipOutgoingData) - (wpos+1));
- assert((bytes > 0) && (bytes < 256));
- clc.voipOutgoingData[wpos] = (byte) bytes;
- wpos += bytes + 1;
-
- // look at the data for the next packet...
- pos += clc.speexFrameSize;
- samples -= clc.speexFrameSize;
- speexFrames++;
+ // encode raw audio samples into Opus data...
+ bytes = opus_encode(clc.opusEncoder, sampbuffer, samples,
+ (unsigned char *) clc.voipOutgoingData,
+ sizeof (clc.voipOutgoingData));
+ if ( bytes <= 0 ) {
+ Com_DPrintf("VoIP: Error encoding %d samples\n", samples);
+ bytes = 0;
}
clc.voipPower = (voipPower / (32768.0f * 32768.0f *
- ((float) (clc.speexFrameSize * speexFrames)))) *
- 100.0f;
+ ((float) samples))) * 100.0f;
if ((useVad) && (clc.voipPower < cl_voipVADThreshold->value)) {
CL_VoipNewGeneration(); // no "talk" for at least 1/4 second.
} else {
- clc.voipOutgoingDataSize = wpos;
- clc.voipOutgoingDataFrames = speexFrames;
+ clc.voipOutgoingDataSize = bytes;
+ clc.voipOutgoingDataFrames = voipFrames;
Com_DPrintf("VoIP: Send %d frames, %d bytes, %f power\n",
- speexFrames, wpos, clc.voipPower);
+ voipFrames, bytes, clc.voipPower);
#if 0
static FILE *encio = NULL;
if (encio == NULL) encio = fopen("voip-outgoing-encoded.bin", "wb");
- if (encio != NULL) { fwrite(clc.voipOutgoingData, wpos, 1, encio); fflush(encio); }
+ if (encio != NULL) { fwrite(clc.voipOutgoingData, bytes, 1, encio); fflush(encio); }
static FILE *decio = NULL;
if (decio == NULL) decio = fopen("voip-outgoing-decoded.bin", "wb");
- if (decio != NULL) { fwrite(sampbuffer, speexFrames * clc.speexFrameSize * 2, 1, decio); fflush(decio); }
+ if (decio != NULL) { fwrite(sampbuffer, voipFrames * VOIP_MAX_FRAME_SAMPLES * 2, 1, decio); fflush(decio); }
#endif
}
}
@@ -1419,14 +1410,11 @@ void CL_Disconnect( qboolean showMainMenu ) {
cl_voipUseVAD->integer = tmp;
}
- if (clc.speexInitialized) {
+ if (clc.voipCodecInitialized) {
int i;
- speex_bits_destroy(&clc.speexEncoderBits);
- speex_encoder_destroy(clc.speexEncoder);
- speex_preprocess_state_destroy(clc.speexPreprocessor);
+ opus_encoder_destroy(clc.opusEncoder);
for (i = 0; i < MAX_CLIENTS; i++) {
- speex_bits_destroy(&clc.speexDecoderBits[i]);
- speex_decoder_destroy(clc.speexDecoder[i]);
+ opus_decoder_destroy(clc.opusDecoder[i]);
}
}
Cmd_RemoveCommand ("voip");
@@ -3706,9 +3694,9 @@ void CL_Init( void ) {
cl_voipVADThreshold = Cvar_Get ("cl_voipVADThreshold", "0.25", CVAR_ARCHIVE);
cl_voipShowMeter = Cvar_Get ("cl_voipShowMeter", "1", CVAR_ARCHIVE);
- // This is a protocol version number.
- cl_voip = Cvar_Get ("cl_voip", "1", CVAR_USERINFO | CVAR_ARCHIVE);
+ cl_voip = Cvar_Get ("cl_voip", "1", CVAR_ARCHIVE);
Cvar_CheckRange( cl_voip, 0, 1, qtrue );
+ cl_voipProtocol = Cvar_Get ("cl_voipProtocol", cl_voip->integer ? "opus" : "", CVAR_USERINFO | CVAR_ROM);
#endif
diff --git a/src/client/cl_parse.c b/src/client/cl_parse.c
index a8b68ca5..4230c04f 100644
--- a/src/client/cl_parse.c
+++ b/src/client/cl_parse.c
@@ -35,7 +35,8 @@ char *svc_strings[256] = {
"svc_download",
"svc_snapshot",
"svc_EOF",
- "svc_voip",
+ "svc_voipSpeex",
+ "svc_voipOpus",
};
void SHOWNET( msg_t *msg, char *s) {
@@ -354,8 +355,8 @@ void CL_SystemInfoChanged( void ) {
cl.serverId = atoi( Info_ValueForKey( systemInfo, "sv_serverid" ) );
#ifdef USE_VOIP
- s = Info_ValueForKey( systemInfo, "sv_voip" );
- clc.voipEnabled = atoi(s);
+ s = Info_ValueForKey( systemInfo, "sv_voipProtocol" );
+ clc.voipEnabled = !Q_stricmp(s, "opus");
#endif
// don't set any vars when playing a demo
@@ -674,13 +675,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);
}
}
@@ -693,8 +694,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);
@@ -702,7 +703,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;
@@ -732,14 +734,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.
}
@@ -752,70 +755,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 ( 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
+ #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;
- }
+ 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);
@@ -918,9 +910,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, qtrue );
+#endif
+ break;
+ case svc_voipOpus:
#ifdef USE_VOIP
- CL_ParseVoip( msg );
+ CL_ParseVoip( msg, !clc.voipEnabled );
#endif
break;
}
diff --git a/src/client/client.h b/src/client/client.h
index 541dca40..b4016467 100644
--- a/src/client/client.h
+++ b/src/client/client.h
@@ -36,8 +36,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#endif /* USE_CURL */
#ifdef USE_VOIP
-#include "speex/speex.h"
-#include "speex/speex_preprocess.h"
+#include <opus.h>
#endif
// file full of random crap that gets used to create cl_guid
@@ -241,14 +240,11 @@ typedef struct {
#ifdef USE_VOIP
qboolean voipEnabled;
- qboolean speexInitialized;
- int speexFrameSize;
- int speexSampleRate;
+ qboolean voipCodecInitialized;
// incoming data...
// !!! FIXME: convert from parallel arrays to array of a struct.
- SpeexBits speexDecoderBits[MAX_CLIENTS];
- void *speexDecoder[MAX_CLIENTS];
+ OpusDecoder *opusDecoder[MAX_CLIENTS];
byte voipIncomingGeneration[MAX_CLIENTS];
int voipIncomingSequence[MAX_CLIENTS];
float voipGain[MAX_CLIENTS];
@@ -260,9 +256,7 @@ typedef struct {
// then we are sending to clientnum i.
uint8_t voipTargets[(MAX_CLIENTS + 7) / 8];
uint8_t voipFlags;
- SpeexPreprocessState *speexPreprocessor;
- SpeexBits speexEncoderBits;
- void *speexEncoder;
+ OpusEncoder *opusEncoder;
int voipOutgoingDataSize;
int voipOutgoingDataFrames;
int voipOutgoingSequence;
@@ -449,6 +443,13 @@ extern cvar_t *cl_voipGainDuringCapture;
extern cvar_t *cl_voipCaptureMult;
extern cvar_t *cl_voipShowMeter;
extern cvar_t *cl_voip;
+
+// 20ms at 48k
+#define VOIP_MAX_FRAME_SAMPLES ( 20 * 48 )
+
+// 3 frame is 60ms of audio, the max opus will encode at once
+#define VOIP_MAX_PACKET_FRAMES 3
+#define VOIP_MAX_PACKET_SAMPLES ( VOIP_MAX_FRAME_SAMPLES * VOIP_MAX_PACKET_FRAMES )
#endif
//=================================================
diff --git a/src/client/snd_openal.c b/src/client/snd_openal.c
index d90e6d23..b5c89918 100644
--- a/src/client/snd_openal.c
+++ b/src/client/snd_openal.c
@@ -2700,16 +2700,12 @@ qboolean S_AL_Init( soundInterface_t *si )
s_alAvailableInputDevices = Cvar_Get("s_alAvailableInputDevices", inputdevicenames, CVAR_ROM | CVAR_NORESTART);
- // !!! FIXME: 8000Hz is what Speex narrowband mode needs, but we
- // !!! FIXME: should probably open the capture device after
- // !!! FIXME: initializing Speex so we can change to wideband
- // !!! FIXME: if we like.
Com_Printf("OpenAL default capture device is '%s'\n", defaultinputdevice ? defaultinputdevice : "none");
- alCaptureDevice = qalcCaptureOpenDevice(inputdevice, 8000, AL_FORMAT_MONO16, 4096);
+ alCaptureDevice = qalcCaptureOpenDevice(inputdevice, 48000, AL_FORMAT_MONO16, VOIP_MAX_PACKET_SAMPLES*4);
if( !alCaptureDevice && inputdevice )
{
Com_Printf( "Failed to open OpenAL Input device '%s', trying default.\n", inputdevice );
- alCaptureDevice = qalcCaptureOpenDevice(NULL, 8000, AL_FORMAT_MONO16, 4096);
+ alCaptureDevice = qalcCaptureOpenDevice(NULL, 48000, AL_FORMAT_MONO16, VOIP_MAX_PACKET_SAMPLES*4);
}
Com_Printf( "OpenAL capture device %s.\n",
(alCaptureDevice == NULL) ? "failed to open" : "opened");
diff --git a/src/qcommon/qcommon.h b/src/qcommon/qcommon.h
index d71531b6..1b4a5a16 100644
--- a/src/qcommon/qcommon.h
+++ b/src/qcommon/qcommon.h
@@ -279,7 +279,8 @@ enum svc_ops_e {
svc_EOF,
// new commands, supported only by ioquake3 protocol but not legacy
- svc_voip, // not wrapped in USE_VOIP, so this value is reserved.
+ svc_voipSpeex, // not wrapped in USE_VOIP, so this value is reserved.
+ svc_voipOpus, //
};
@@ -295,7 +296,8 @@ enum clc_ops_e {
clc_EOF,
// new commands, supported only by ioquake3 protocol but not legacy
- clc_voip, // not wrapped in USE_VOIP, so this value is reserved.
+ clc_voipSpeex, // not wrapped in USE_VOIP, so this value is reserved.
+ clc_voipOpus, //
};
/*
diff --git a/src/server/server.h b/src/server/server.h
index 59b3ff8b..57d34b5b 100644
--- a/src/server/server.h
+++ b/src/server/server.h
@@ -45,7 +45,7 @@ typedef struct voipServerPacket_s
int len;
int sender;
int flags;
- byte data[1024];
+ byte data[4000];
} voipServerPacket_t;
#endif
@@ -281,6 +281,7 @@ extern cvar_t *sv_banFile;
#ifdef USE_VOIP
extern cvar_t *sv_voip;
+extern cvar_t *sv_voipProtocol;
#endif
diff --git a/src/server/sv_client.c b/src/server/sv_client.c
index ba4392f7..dcf6311e 100644
--- a/src/server/sv_client.c
+++ b/src/server/sv_client.c
@@ -1200,8 +1200,8 @@ void SV_UserinfoChanged( client_t *cl ) {
}
#ifdef USE_VOIP
- val = Info_ValueForKey(cl->userinfo, "cl_voip");
- cl->hasVoip = atoi(val);
+ val = Info_ValueForKey(cl->userinfo, "cl_voipProtocol");
+ cl->hasVoip = !Q_stricmp( val, "opus" );
#endif
// TTimo
@@ -1536,7 +1536,7 @@ static qboolean SV_ShouldIgnoreVoipSender(const client_t *cl)
}
static
-void SV_UserVoip(client_t *cl, msg_t *msg)
+void SV_UserVoip(client_t *cl, msg_t *msg, qboolean ignoreData)
{
int sender, generation, sequence, frames, packetsize;
uint8_t recips[(MAX_CLIENTS + 7) / 8];
@@ -1571,12 +1571,12 @@ void SV_UserVoip(client_t *cl, msg_t *msg)
MSG_ReadData(msg, encoded, packetsize);
- if (SV_ShouldIgnoreVoipSender(cl))
+ if (ignoreData || SV_ShouldIgnoreVoipSender(cl))
return; // Blacklisted, disabled, etc.
// !!! FIXME: see if we read past end of msg...
- // !!! FIXME: reject if not speex narrowband codec.
+ // !!! FIXME: reject if not opus data.
// !!! FIXME: decide if this is bogus data?
// decide who needs this VoIP packet sent to them...
@@ -1725,10 +1725,18 @@ void SV_ExecuteClientMessage( client_t *cl, msg_t *msg ) {
}
} while ( 1 );
+ // skip legacy speex voip data
+ if ( c == clc_voipSpeex ) {
+#ifdef USE_VOIP
+ SV_UserVoip( cl, msg, qtrue );
+ c = MSG_ReadByte( msg );
+#endif
+ }
+
// read optional voip data
- if ( c == clc_voip ) {
+ if ( c == clc_voipOpus ) {
#ifdef USE_VOIP
- SV_UserVoip( cl, msg );
+ SV_UserVoip( cl, msg, qfalse );
c = MSG_ReadByte( msg );
#endif
}
diff --git a/src/server/sv_init.c b/src/server/sv_init.c
index 30fd3936..997d58f6 100644
--- a/src/server/sv_init.c
+++ b/src/server/sv_init.c
@@ -646,8 +646,9 @@ void SV_Init (void)
sv_serverid = Cvar_Get ("sv_serverid", "0", CVAR_SYSTEMINFO | CVAR_ROM );
sv_pure = Cvar_Get ("sv_pure", "1", CVAR_SYSTEMINFO );
#ifdef USE_VOIP
- sv_voip = Cvar_Get("sv_voip", "1", CVAR_SYSTEMINFO | CVAR_LATCH);
+ sv_voip = Cvar_Get("sv_voip", "1", CVAR_LATCH);
Cvar_CheckRange(sv_voip, 0, 1, qtrue);
+ sv_voipProtocol = Cvar_Get("sv_voipProtocol", sv_voip->integer ? "opus" : "", CVAR_SYSTEMINFO | CVAR_ROM );
#endif
Cvar_Get ("sv_paks", "", CVAR_SYSTEMINFO | CVAR_ROM );
Cvar_Get ("sv_pakNames", "", CVAR_SYSTEMINFO | CVAR_ROM );
diff --git a/src/server/sv_main.c b/src/server/sv_main.c
index 77d1a35d..0c4c7f46 100644
--- a/src/server/sv_main.c
+++ b/src/server/sv_main.c
@@ -25,6 +25,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#ifdef USE_VOIP
cvar_t *sv_voip;
+cvar_t *sv_voipProtocol;
#endif
serverStatic_t svs; // persistant server info
@@ -656,8 +657,8 @@ void SVC_Info( netadr_t from ) {
Info_SetValueForKey( infostring, "pure", va("%i", sv_pure->integer ) );
#ifdef USE_VOIP
- if (sv_voip->integer) {
- Info_SetValueForKey( infostring, "voip", va("%i", sv_voip->integer ) );
+ if (sv_voipProtocol->string && *sv_voipProtocol->string) {
+ Info_SetValueForKey( infostring, "voip", sv_voipProtocol->string );
}
#endif
diff --git a/src/server/sv_snapshot.c b/src/server/sv_snapshot.c
index 71c4b86c..58136388 100644
--- a/src/server/sv_snapshot.c
+++ b/src/server/sv_snapshot.c
@@ -548,7 +548,7 @@ static void SV_WriteVoipToClient(client_t *cl, msg_t *msg)
if (totalbytes > (msg->maxsize - msg->cursize) / 2)
break;
- MSG_WriteByte(msg, svc_voip);
+ MSG_WriteByte(msg, svc_voipOpus);
MSG_WriteShort(msg, packet->sender);
MSG_WriteByte(msg, (byte) packet->generation);
MSG_WriteLong(msg, packet->sequence);