mirror of
https://github.com/UberGames/lilium-voyager.git
synced 2024-12-13 13:40:56 +00:00
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:
parent
fe619680f8
commit
615b73288f
13 changed files with 167 additions and 240 deletions
80
Makefile
80
Makefile
|
@ -202,10 +202,6 @@ ifndef USE_INTERNAL_LIBS
|
||||||
USE_INTERNAL_LIBS=1
|
USE_INTERNAL_LIBS=1
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifndef USE_INTERNAL_SPEEX
|
|
||||||
USE_INTERNAL_SPEEX=$(USE_INTERNAL_LIBS)
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifndef USE_INTERNAL_OGG
|
ifndef USE_INTERNAL_OGG
|
||||||
USE_INTERNAL_OGG=$(USE_INTERNAL_LIBS)
|
USE_INTERNAL_OGG=$(USE_INTERNAL_LIBS)
|
||||||
endif
|
endif
|
||||||
|
@ -258,7 +254,6 @@ NDIR=$(MOUNT_DIR)/null
|
||||||
UIDIR=$(MOUNT_DIR)/ui
|
UIDIR=$(MOUNT_DIR)/ui
|
||||||
Q3UIDIR=$(MOUNT_DIR)/q3_ui
|
Q3UIDIR=$(MOUNT_DIR)/q3_ui
|
||||||
JPDIR=$(MOUNT_DIR)/jpeg-8c
|
JPDIR=$(MOUNT_DIR)/jpeg-8c
|
||||||
SPEEXDIR=$(MOUNT_DIR)/libspeex
|
|
||||||
OGGDIR=$(MOUNT_DIR)/libogg-1.3.1
|
OGGDIR=$(MOUNT_DIR)/libogg-1.3.1
|
||||||
VORBISDIR=$(MOUNT_DIR)/libvorbis-1.3.4
|
VORBISDIR=$(MOUNT_DIR)/libvorbis-1.3.4
|
||||||
OPUSDIR=$(MOUNT_DIR)/opus-1.1
|
OPUSDIR=$(MOUNT_DIR)/opus-1.1
|
||||||
|
@ -991,8 +986,18 @@ ifeq ($(USE_CURL),1)
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifeq ($(USE_VOIP),1)
|
||||||
|
CLIENT_CFLAGS += -DUSE_VOIP
|
||||||
|
SERVER_CFLAGS += -DUSE_VOIP
|
||||||
|
NEED_OPUS=1
|
||||||
|
endif
|
||||||
|
|
||||||
ifeq ($(USE_CODEC_OPUS),1)
|
ifeq ($(USE_CODEC_OPUS),1)
|
||||||
CLIENT_CFLAGS += -DUSE_CODEC_OPUS
|
CLIENT_CFLAGS += -DUSE_CODEC_OPUS
|
||||||
|
NEED_OPUS=1
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(NEED_OPUS),1)
|
||||||
ifeq ($(USE_INTERNAL_OPUS),1)
|
ifeq ($(USE_INTERNAL_OPUS),1)
|
||||||
OPUS_CFLAGS = -DOPUS_BUILD -DHAVE_LRINTF -DFLOATING_POINT -DUSE_ALLOCA \
|
OPUS_CFLAGS = -DOPUS_BUILD -DHAVE_LRINTF -DFLOATING_POINT -DUSE_ALLOCA \
|
||||||
-I$(OPUSDIR)/include -I$(OPUSDIR)/celt -I$(OPUSDIR)/silk \
|
-I$(OPUSDIR)/include -I$(OPUSDIR)/celt -I$(OPUSDIR)/silk \
|
||||||
|
@ -1038,19 +1043,6 @@ ifeq ($(USE_MUMBLE),1)
|
||||||
CLIENT_CFLAGS += -DUSE_MUMBLE
|
CLIENT_CFLAGS += -DUSE_MUMBLE
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(USE_VOIP),1)
|
|
||||||
CLIENT_CFLAGS += -DUSE_VOIP
|
|
||||||
SERVER_CFLAGS += -DUSE_VOIP
|
|
||||||
ifeq ($(USE_INTERNAL_SPEEX),1)
|
|
||||||
SPEEX_CFLAGS += -DFLOATING_POINT -DUSE_ALLOCA -I$(SPEEXDIR)/include
|
|
||||||
else
|
|
||||||
SPEEX_CFLAGS ?= $(shell pkg-config --silence-errors --cflags speex speexdsp || true)
|
|
||||||
SPEEX_LIBS ?= $(shell pkg-config --silence-errors --libs speex speexdsp || echo -lspeex -lspeexdsp)
|
|
||||||
endif
|
|
||||||
CLIENT_CFLAGS += $(SPEEX_CFLAGS)
|
|
||||||
CLIENT_LIBS += $(SPEEX_LIBS)
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(USE_INTERNAL_ZLIB),1)
|
ifeq ($(USE_INTERNAL_ZLIB),1)
|
||||||
ZLIB_CFLAGS = -DNO_GZIP -I$(ZDIR)
|
ZLIB_CFLAGS = -DNO_GZIP -I$(ZDIR)
|
||||||
else
|
else
|
||||||
|
@ -1828,53 +1820,7 @@ ifeq ($(ARCH),x86_64)
|
||||||
$(B)/client/ftola.o
|
$(B)/client/ftola.o
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(USE_VOIP),1)
|
ifeq ($(NEED_OPUS),1)
|
||||||
ifeq ($(USE_INTERNAL_SPEEX),1)
|
|
||||||
Q3OBJ += \
|
|
||||||
$(B)/client/bits.o \
|
|
||||||
$(B)/client/buffer.o \
|
|
||||||
$(B)/client/cb_search.o \
|
|
||||||
$(B)/client/exc_10_16_table.o \
|
|
||||||
$(B)/client/exc_10_32_table.o \
|
|
||||||
$(B)/client/exc_20_32_table.o \
|
|
||||||
$(B)/client/exc_5_256_table.o \
|
|
||||||
$(B)/client/exc_5_64_table.o \
|
|
||||||
$(B)/client/exc_8_128_table.o \
|
|
||||||
$(B)/client/fftwrap.o \
|
|
||||||
$(B)/client/filterbank.o \
|
|
||||||
$(B)/client/filters.o \
|
|
||||||
$(B)/client/gain_table.o \
|
|
||||||
$(B)/client/gain_table_lbr.o \
|
|
||||||
$(B)/client/hexc_10_32_table.o \
|
|
||||||
$(B)/client/hexc_table.o \
|
|
||||||
$(B)/client/high_lsp_tables.o \
|
|
||||||
$(B)/client/jitter.o \
|
|
||||||
$(B)/client/kiss_fft.o \
|
|
||||||
$(B)/client/kiss_fftr.o \
|
|
||||||
$(B)/client/lpc.o \
|
|
||||||
$(B)/client/lsp.o \
|
|
||||||
$(B)/client/lsp_tables_nb.o \
|
|
||||||
$(B)/client/ltp.o \
|
|
||||||
$(B)/client/mdf.o \
|
|
||||||
$(B)/client/modes.o \
|
|
||||||
$(B)/client/modes_wb.o \
|
|
||||||
$(B)/client/nb_celp.o \
|
|
||||||
$(B)/client/preprocess.o \
|
|
||||||
$(B)/client/quant_lsp.o \
|
|
||||||
$(B)/client/resample.o \
|
|
||||||
$(B)/client/sb_celp.o \
|
|
||||||
$(B)/client/smallft.o \
|
|
||||||
$(B)/client/speex.o \
|
|
||||||
$(B)/client/speex_callbacks.o \
|
|
||||||
$(B)/client/speex_header.o \
|
|
||||||
$(B)/client/stereo.o \
|
|
||||||
$(B)/client/vbr.o \
|
|
||||||
$(B)/client/vq.o \
|
|
||||||
$(B)/client/window.o
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(USE_CODEC_OPUS),1)
|
|
||||||
ifeq ($(USE_INTERNAL_OPUS),1)
|
ifeq ($(USE_INTERNAL_OPUS),1)
|
||||||
Q3OBJ += \
|
Q3OBJ += \
|
||||||
$(B)/client/opus/analysis.o \
|
$(B)/client/opus/analysis.o \
|
||||||
|
@ -2581,9 +2527,6 @@ $(B)/client/%.o: $(CMDIR)/%.c
|
||||||
$(B)/client/%.o: $(BLIBDIR)/%.c
|
$(B)/client/%.o: $(BLIBDIR)/%.c
|
||||||
$(DO_BOT_CC)
|
$(DO_BOT_CC)
|
||||||
|
|
||||||
$(B)/client/%.o: $(SPEEXDIR)/%.c
|
|
||||||
$(DO_CC)
|
|
||||||
|
|
||||||
$(B)/client/%.o: $(OGGDIR)/src/%.c
|
$(B)/client/%.o: $(OGGDIR)/src/%.c
|
||||||
$(DO_CC)
|
$(DO_CC)
|
||||||
|
|
||||||
|
@ -2874,7 +2817,6 @@ ifdef MINGW
|
||||||
USE_OPENAL_DLOPEN=$(USE_OPENAL_DLOPEN) \
|
USE_OPENAL_DLOPEN=$(USE_OPENAL_DLOPEN) \
|
||||||
USE_CURL_DLOPEN=$(USE_CURL_DLOPEN) \
|
USE_CURL_DLOPEN=$(USE_CURL_DLOPEN) \
|
||||||
USE_INTERNAL_OPUS=$(USE_INTERNAL_OPUS) \
|
USE_INTERNAL_OPUS=$(USE_INTERNAL_OPUS) \
|
||||||
USE_INTERNAL_SPEEX=$(USE_INTERNAL_SPEEX) \
|
|
||||||
USE_INTERNAL_ZLIB=$(USE_INTERNAL_ZLIB) \
|
USE_INTERNAL_ZLIB=$(USE_INTERNAL_ZLIB) \
|
||||||
USE_INTERNAL_JPEG=$(USE_INTERNAL_JPEG)
|
USE_INTERNAL_JPEG=$(USE_INTERNAL_JPEG)
|
||||||
else
|
else
|
||||||
|
|
|
@ -905,37 +905,27 @@ void CL_FirstSnapshot( void ) {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_VOIP
|
#ifdef USE_VOIP
|
||||||
if (!clc.speexInitialized) {
|
if (!clc.voipCodecInitialized) {
|
||||||
int i;
|
int i;
|
||||||
speex_bits_init(&clc.speexEncoderBits);
|
int error;
|
||||||
speex_bits_reset(&clc.speexEncoderBits);
|
|
||||||
|
|
||||||
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,
|
if ( error ) {
|
||||||
&clc.speexFrameSize);
|
Com_DPrintf("VoIP: Error opus_encoder_create %d\n", error);
|
||||||
speex_encoder_ctl(clc.speexEncoder, SPEEX_GET_SAMPLING_RATE,
|
return;
|
||||||
&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);
|
|
||||||
|
|
||||||
for (i = 0; i < MAX_CLIENTS; i++) {
|
for (i = 0; i < MAX_CLIENTS; i++) {
|
||||||
speex_bits_init(&clc.speexDecoderBits[i]);
|
clc.opusDecoder[i] = opus_decoder_create(48000, 1, &error);
|
||||||
speex_bits_reset(&clc.speexDecoderBits[i]);
|
if ( error ) {
|
||||||
clc.speexDecoder[i] = speex_decoder_init(&speex_nb_mode);
|
Com_DPrintf("VoIP: Error opus_decoder_create(%d) %d\n", i, error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
clc.voipIgnore[i] = qfalse;
|
clc.voipIgnore[i] = qfalse;
|
||||||
clc.voipGain[i] = 1.0f;
|
clc.voipGain[i] = 1.0f;
|
||||||
}
|
}
|
||||||
clc.speexInitialized = qtrue;
|
clc.voipCodecInitialized = qtrue;
|
||||||
clc.voipMuteAll = qfalse;
|
clc.voipMuteAll = qfalse;
|
||||||
Cmd_AddCommand ("voip", CL_Voip_f);
|
Cmd_AddCommand ("voip", CL_Voip_f);
|
||||||
Cvar_Set("cl_voipSendTarget", "spatial");
|
Cvar_Set("cl_voipSendTarget", "spatial");
|
||||||
|
|
|
@ -788,7 +788,7 @@ void CL_WritePacket( void ) {
|
||||||
{
|
{
|
||||||
if((clc.voipFlags & VOIP_SPATIAL) || Com_IsVoipTarget(clc.voipTargets, sizeof(clc.voipTargets), -1))
|
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_WriteByte (&buf, clc.voipOutgoingGeneration);
|
||||||
MSG_WriteLong (&buf, clc.voipOutgoingSequence);
|
MSG_WriteLong (&buf, clc.voipOutgoingSequence);
|
||||||
MSG_WriteByte (&buf, clc.voipOutgoingDataFrames);
|
MSG_WriteByte (&buf, clc.voipOutgoingDataFrames);
|
||||||
|
@ -809,7 +809,7 @@ void CL_WritePacket( void ) {
|
||||||
MSG_Init (&fakemsg, fakedata, sizeof (fakedata));
|
MSG_Init (&fakemsg, fakedata, sizeof (fakedata));
|
||||||
MSG_Bitstream (&fakemsg);
|
MSG_Bitstream (&fakemsg);
|
||||||
MSG_WriteLong (&fakemsg, clc.reliableAcknowledge);
|
MSG_WriteLong (&fakemsg, clc.reliableAcknowledge);
|
||||||
MSG_WriteByte (&fakemsg, svc_voip);
|
MSG_WriteByte (&fakemsg, svc_voipOpus);
|
||||||
MSG_WriteShort (&fakemsg, clc.clientNum);
|
MSG_WriteShort (&fakemsg, clc.clientNum);
|
||||||
MSG_WriteByte (&fakemsg, clc.voipOutgoingGeneration);
|
MSG_WriteByte (&fakemsg, clc.voipOutgoingGeneration);
|
||||||
MSG_WriteLong (&fakemsg, clc.voipOutgoingSequence);
|
MSG_WriteLong (&fakemsg, clc.voipOutgoingSequence);
|
||||||
|
|
|
@ -44,6 +44,7 @@ cvar_t *cl_voipSendTarget;
|
||||||
cvar_t *cl_voipGainDuringCapture;
|
cvar_t *cl_voipGainDuringCapture;
|
||||||
cvar_t *cl_voipCaptureMult;
|
cvar_t *cl_voipCaptureMult;
|
||||||
cvar_t *cl_voipShowMeter;
|
cvar_t *cl_voipShowMeter;
|
||||||
|
cvar_t *cl_voipProtocol;
|
||||||
cvar_t *cl_voip;
|
cvar_t *cl_voip;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -250,8 +251,8 @@ void CL_Voip_f( void )
|
||||||
|
|
||||||
if (clc.state != CA_ACTIVE)
|
if (clc.state != CA_ACTIVE)
|
||||||
reason = "Not connected to a server";
|
reason = "Not connected to a server";
|
||||||
else if (!clc.speexInitialized)
|
else if (!clc.voipCodecInitialized)
|
||||||
reason = "Speex not initialized";
|
reason = "Voip codec not initialized";
|
||||||
else if (!clc.voipEnabled)
|
else if (!clc.voipEnabled)
|
||||||
reason = "Server doesn't support VoIP";
|
reason = "Server doesn't support VoIP";
|
||||||
else if (!clc.demoplaying && (Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER || Cvar_VariableValue("ui_singlePlayerActive")))
|
else if (!clc.demoplaying && (Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER || Cvar_VariableValue("ui_singlePlayerActive")))
|
||||||
|
@ -306,6 +307,8 @@ void CL_VoipNewGeneration(void)
|
||||||
clc.voipOutgoingGeneration = 1;
|
clc.voipOutgoingGeneration = 1;
|
||||||
clc.voipPower = 0.0f;
|
clc.voipPower = 0.0f;
|
||||||
clc.voipOutgoingSequence = 0;
|
clc.voipOutgoingSequence = 0;
|
||||||
|
|
||||||
|
opus_encoder_ctl(clc.opusEncoder, OPUS_RESET_STATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -394,7 +397,7 @@ void CL_VoipParseTargets(void)
|
||||||
===============
|
===============
|
||||||
CL_CaptureVoip
|
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.
|
data for later transmission.
|
||||||
===============
|
===============
|
||||||
*/
|
*/
|
||||||
|
@ -424,11 +427,12 @@ void CL_CaptureVoip(void)
|
||||||
Com_Printf("Until then, VoIP is disabled.\n");
|
Com_Printf("Until then, VoIP is disabled.\n");
|
||||||
Cvar_Set("cl_voip", "0");
|
Cvar_Set("cl_voip", "0");
|
||||||
}
|
}
|
||||||
|
Cvar_Set("cl_voipProtocol", cl_voip->integer ? "opus" : "");
|
||||||
cl_voip->modified = qfalse;
|
cl_voip->modified = qfalse;
|
||||||
cl_rate->modified = qfalse;
|
cl_rate->modified = qfalse;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!clc.speexInitialized)
|
if (!clc.voipCodecInitialized)
|
||||||
return; // just in case this gets called at a bad time.
|
return; // just in case this gets called at a bad time.
|
||||||
|
|
||||||
if (clc.voipOutgoingDataSize > 0)
|
if (clc.voipOutgoingDataSize > 0)
|
||||||
|
@ -481,80 +485,67 @@ void CL_CaptureVoip(void)
|
||||||
|
|
||||||
if ((cl_voipSend->integer) || (finalFrame)) { // user wants to capture audio?
|
if ((cl_voipSend->integer) || (finalFrame)) { // user wants to capture audio?
|
||||||
int samples = S_AvailableCaptureSamples();
|
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?
|
// enough data buffered in audio hardware to process yet?
|
||||||
if (samples >= (clc.speexFrameSize * mult)) {
|
if (samples >= packetSamples) {
|
||||||
// audio capture is always MONO16 (and that's what speex wants!).
|
// audio capture is always MONO16.
|
||||||
// 2048 will cover 12 uncompressed frames in narrowband mode.
|
static int16_t sampbuffer[VOIP_MAX_PACKET_SAMPLES];
|
||||||
static int16_t sampbuffer[2048];
|
|
||||||
float voipPower = 0.0f;
|
float voipPower = 0.0f;
|
||||||
int speexFrames = 0;
|
int voipFrames;
|
||||||
int wpos = 0;
|
int i, bytes;
|
||||||
int pos = 0;
|
|
||||||
|
|
||||||
if (samples > (clc.speexFrameSize * 4))
|
if (samples > VOIP_MAX_PACKET_SAMPLES)
|
||||||
samples = (clc.speexFrameSize * 4);
|
samples = VOIP_MAX_PACKET_SAMPLES;
|
||||||
|
|
||||||
// !!! FIXME: maybe separate recording from encoding, so voipPower
|
// !!! FIXME: maybe separate recording from encoding, so voipPower
|
||||||
// !!! FIXME: updates faster than 4Hz?
|
// !!! FIXME: updates faster than 4Hz?
|
||||||
|
|
||||||
samples -= samples % clc.speexFrameSize;
|
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;
|
||||||
|
|
||||||
S_Capture(samples, (byte *) sampbuffer); // grab from audio card.
|
S_Capture(samples, (byte *) sampbuffer); // grab from audio card.
|
||||||
|
|
||||||
// this will probably generate multiple speex packets each time.
|
// check the "power" of this packet...
|
||||||
while (samples > 0) {
|
for (i = 0; i < samples; i++) {
|
||||||
int16_t *sampptr = &sampbuffer[pos];
|
const float flsamp = (float) sampbuffer[i];
|
||||||
int i, bytes;
|
const float s = fabs(flsamp);
|
||||||
|
voipPower += s * s;
|
||||||
|
sampbuffer[i] = (int16_t) ((flsamp) * audioMult);
|
||||||
|
}
|
||||||
|
|
||||||
// preprocess samples to remove noise...
|
// encode raw audio samples into Opus data...
|
||||||
speex_preprocess_run(clc.speexPreprocessor, sampptr);
|
bytes = opus_encode(clc.opusEncoder, sampbuffer, samples,
|
||||||
|
(unsigned char *) clc.voipOutgoingData,
|
||||||
// check the "power" of this packet...
|
sizeof (clc.voipOutgoingData));
|
||||||
for (i = 0; i < clc.speexFrameSize; i++) {
|
if ( bytes <= 0 ) {
|
||||||
const float flsamp = (float) sampptr[i];
|
Com_DPrintf("VoIP: Error encoding %d samples\n", samples);
|
||||||
const float s = fabs(flsamp);
|
bytes = 0;
|
||||||
voipPower += s * s;
|
|
||||||
sampptr[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++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
clc.voipPower = (voipPower / (32768.0f * 32768.0f *
|
clc.voipPower = (voipPower / (32768.0f * 32768.0f *
|
||||||
((float) (clc.speexFrameSize * speexFrames)))) *
|
((float) samples))) * 100.0f;
|
||||||
100.0f;
|
|
||||||
|
|
||||||
if ((useVad) && (clc.voipPower < cl_voipVADThreshold->value)) {
|
if ((useVad) && (clc.voipPower < cl_voipVADThreshold->value)) {
|
||||||
CL_VoipNewGeneration(); // no "talk" for at least 1/4 second.
|
CL_VoipNewGeneration(); // no "talk" for at least 1/4 second.
|
||||||
} else {
|
} else {
|
||||||
clc.voipOutgoingDataSize = wpos;
|
clc.voipOutgoingDataSize = bytes;
|
||||||
clc.voipOutgoingDataFrames = speexFrames;
|
clc.voipOutgoingDataFrames = voipFrames;
|
||||||
|
|
||||||
Com_DPrintf("VoIP: Send %d frames, %d bytes, %f power\n",
|
Com_DPrintf("VoIP: Send %d frames, %d bytes, %f power\n",
|
||||||
speexFrames, wpos, clc.voipPower);
|
voipFrames, bytes, clc.voipPower);
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
static FILE *encio = NULL;
|
static FILE *encio = NULL;
|
||||||
if (encio == NULL) encio = fopen("voip-outgoing-encoded.bin", "wb");
|
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;
|
static FILE *decio = NULL;
|
||||||
if (decio == NULL) decio = fopen("voip-outgoing-decoded.bin", "wb");
|
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
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1420,14 +1411,11 @@ void CL_Disconnect( qboolean showMainMenu ) {
|
||||||
cl_voipUseVAD->integer = tmp;
|
cl_voipUseVAD->integer = tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clc.speexInitialized) {
|
if (clc.voipCodecInitialized) {
|
||||||
int i;
|
int i;
|
||||||
speex_bits_destroy(&clc.speexEncoderBits);
|
opus_encoder_destroy(clc.opusEncoder);
|
||||||
speex_encoder_destroy(clc.speexEncoder);
|
|
||||||
speex_preprocess_state_destroy(clc.speexPreprocessor);
|
|
||||||
for (i = 0; i < MAX_CLIENTS; i++) {
|
for (i = 0; i < MAX_CLIENTS; i++) {
|
||||||
speex_bits_destroy(&clc.speexDecoderBits[i]);
|
opus_decoder_destroy(clc.opusDecoder[i]);
|
||||||
speex_decoder_destroy(clc.speexDecoder[i]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Cmd_RemoveCommand ("voip");
|
Cmd_RemoveCommand ("voip");
|
||||||
|
@ -3653,9 +3641,9 @@ void CL_Init( void ) {
|
||||||
cl_voipVADThreshold = Cvar_Get ("cl_voipVADThreshold", "0.25", CVAR_ARCHIVE);
|
cl_voipVADThreshold = Cvar_Get ("cl_voipVADThreshold", "0.25", CVAR_ARCHIVE);
|
||||||
cl_voipShowMeter = Cvar_Get ("cl_voipShowMeter", "1", 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_ARCHIVE);
|
||||||
cl_voip = Cvar_Get ("cl_voip", "1", CVAR_USERINFO | CVAR_ARCHIVE);
|
|
||||||
Cvar_CheckRange( cl_voip, 0, 1, qtrue );
|
Cvar_CheckRange( cl_voip, 0, 1, qtrue );
|
||||||
|
cl_voipProtocol = Cvar_Get ("cl_voipProtocol", cl_voip->integer ? "opus" : "", CVAR_USERINFO | CVAR_ROM);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,8 @@ char *svc_strings[256] = {
|
||||||
"svc_download",
|
"svc_download",
|
||||||
"svc_snapshot",
|
"svc_snapshot",
|
||||||
"svc_EOF",
|
"svc_EOF",
|
||||||
"svc_voip",
|
"svc_voipSpeex",
|
||||||
|
"svc_voipOpus",
|
||||||
};
|
};
|
||||||
|
|
||||||
void SHOWNET( msg_t *msg, char *s) {
|
void SHOWNET( msg_t *msg, char *s) {
|
||||||
|
@ -359,8 +360,8 @@ void CL_SystemInfoChanged( void ) {
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
s = Info_ValueForKey( systemInfo, "sv_voip" );
|
s = Info_ValueForKey( systemInfo, "sv_voipProtocol" );
|
||||||
clc.voipEnabled = atoi(s);
|
clc.voipEnabled = !Q_stricmp(s, "opus");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -680,13 +681,13 @@ static void CL_PlayVoip(int sender, int samplecnt, const byte *data, int flags)
|
||||||
{
|
{
|
||||||
if(flags & VOIP_DIRECT)
|
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);
|
data, clc.voipGain[sender], -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(flags & VOIP_SPATIAL)
|
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);
|
data, 1.0f, sender);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -699,8 +700,8 @@ A VoIP message has been received from the server
|
||||||
=====================
|
=====================
|
||||||
*/
|
*/
|
||||||
static
|
static
|
||||||
void CL_ParseVoip ( msg_t *msg ) {
|
void CL_ParseVoip ( msg_t *msg, qboolean ignoreData ) {
|
||||||
static short decoded[4096]; // !!! FIXME: don't hardcode.
|
static short decoded[VOIP_MAX_PACKET_SAMPLES*4]; // !!! FIXME: don't hard code
|
||||||
|
|
||||||
const int sender = MSG_ReadShort(msg);
|
const int sender = MSG_ReadShort(msg);
|
||||||
const int generation = MSG_ReadByte(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 frames = MSG_ReadByte(msg);
|
||||||
const int packetsize = MSG_ReadShort(msg);
|
const int packetsize = MSG_ReadShort(msg);
|
||||||
const int flags = MSG_ReadBits(msg, VOIP_FLAGCNT);
|
const int flags = MSG_ReadBits(msg, VOIP_FLAGCNT);
|
||||||
char encoded[1024];
|
unsigned char encoded[4000];
|
||||||
|
int numSamples;
|
||||||
int seqdiff;
|
int seqdiff;
|
||||||
int written = 0;
|
int written = 0;
|
||||||
int i;
|
int i;
|
||||||
|
@ -738,14 +740,15 @@ void CL_ParseVoip ( msg_t *msg ) {
|
||||||
return; // overlarge packet, bail.
|
return; // overlarge packet, bail.
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!clc.speexInitialized) {
|
MSG_ReadData(msg, encoded, packetsize);
|
||||||
MSG_ReadData(msg, encoded, packetsize); // skip payload.
|
|
||||||
return; // can't handle VoIP without libspeex!
|
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) {
|
} else if (sender >= MAX_CLIENTS) {
|
||||||
MSG_ReadData(msg, encoded, packetsize); // skip payload.
|
|
||||||
return; // bogus sender.
|
return; // bogus sender.
|
||||||
} else if (CL_ShouldIgnoreVoipSender(sender)) {
|
} else if (CL_ShouldIgnoreVoipSender(sender)) {
|
||||||
MSG_ReadData(msg, encoded, packetsize); // skip payload.
|
|
||||||
return; // Channel is muted, bail.
|
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.
|
// This is a new "generation" ... a new recording started, reset the bits.
|
||||||
if (generation != clc.voipIncomingGeneration[sender]) {
|
if (generation != clc.voipIncomingGeneration[sender]) {
|
||||||
Com_DPrintf("VoIP: new generation %d!\n", generation);
|
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;
|
clc.voipIncomingGeneration[sender] = generation;
|
||||||
seqdiff = 0;
|
seqdiff = 0;
|
||||||
} else if (seqdiff < 0) { // we're ahead of the sequence?!
|
} else if (seqdiff < 0) { // we're ahead of the sequence?!
|
||||||
// This shouldn't happen unless the packet is corrupted or something.
|
// This shouldn't happen unless the packet is corrupted or something.
|
||||||
Com_DPrintf("VoIP: misordered sequence! %d < %d!\n",
|
Com_DPrintf("VoIP: misordered sequence! %d < %d!\n",
|
||||||
sequence, clc.voipIncomingSequence[sender]);
|
sequence, clc.voipIncomingSequence[sender]);
|
||||||
// reset the bits just in case.
|
// reset the decoder just in case.
|
||||||
speex_bits_reset(&clc.speexDecoderBits[sender]);
|
opus_decoder_ctl(clc.opusDecoder[sender], OPUS_RESET_STATE);
|
||||||
seqdiff = 0;
|
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.
|
// just start over.
|
||||||
Com_DPrintf("VoIP: Dropped way too many (%d) frames from client #%d\n",
|
Com_DPrintf("VoIP: Dropped way too many (%d) frames from client #%d\n",
|
||||||
seqdiff, sender);
|
seqdiff, sender);
|
||||||
speex_bits_reset(&clc.speexDecoderBits[sender]);
|
opus_decoder_ctl(clc.opusDecoder[sender], OPUS_RESET_STATE);
|
||||||
seqdiff = 0;
|
seqdiff = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (seqdiff != 0) {
|
if (seqdiff != 0) {
|
||||||
Com_DPrintf("VoIP: Dropped %d frames from client #%d\n",
|
Com_DPrintf("VoIP: Dropped %d frames from client #%d\n",
|
||||||
seqdiff, sender);
|
seqdiff, sender);
|
||||||
// tell speex that we're missing frames...
|
// tell opus that we're missing frames...
|
||||||
for (i = 0; i < seqdiff; i++) {
|
for (i = 0; i < seqdiff; i++) {
|
||||||
assert((written + clc.speexFrameSize) * 2 < sizeof (decoded));
|
assert((written + VOIP_MAX_PACKET_SAMPLES) * 2 < sizeof (decoded));
|
||||||
speex_decode_int(clc.speexDecoder[sender], NULL, decoded + written);
|
numSamples = opus_decode(clc.opusDecoder[sender], NULL, VOIP_MAX_PACKET_SAMPLES * 2, decoded + written, sizeof (decoded) - written, 0);
|
||||||
written += clc.speexFrameSize;
|
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++) {
|
numSamples = opus_decode(clc.opusDecoder[sender], encoded, packetsize, decoded + written, sizeof (decoded) - written, 0);
|
||||||
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 ( numSamples <= 0 ) {
|
||||||
if ((written + clc.speexFrameSize) * 2 > sizeof (decoded)) {
|
Com_DPrintf("VoIP: Error decoding voip data from client #%d\n", sender);
|
||||||
Com_DPrintf("VoIP: playback %d bytes, %d samples, %d frames\n",
|
numSamples = 0;
|
||||||
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 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",
|
Com_DPrintf("VoIP: playback %d bytes, %d samples, %d frames\n",
|
||||||
written * 2, written, i);
|
written * 2, written, frames);
|
||||||
|
|
||||||
if(written > 0)
|
if(written > 0)
|
||||||
CL_PlayVoip(sender, written, (const byte *) decoded, flags);
|
CL_PlayVoip(sender, written, (const byte *) decoded, flags);
|
||||||
|
@ -924,9 +916,14 @@ void CL_ParseServerMessage( msg_t *msg ) {
|
||||||
case svc_download:
|
case svc_download:
|
||||||
CL_ParseDownload( msg );
|
CL_ParseDownload( msg );
|
||||||
break;
|
break;
|
||||||
case svc_voip:
|
case svc_voipSpeex:
|
||||||
#ifdef USE_VOIP
|
#ifdef USE_VOIP
|
||||||
CL_ParseVoip( msg );
|
CL_ParseVoip( msg, qtrue );
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case svc_voipOpus:
|
||||||
|
#ifdef USE_VOIP
|
||||||
|
CL_ParseVoip( msg, !clc.voipEnabled );
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,8 +35,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
#endif /* USE_CURL */
|
#endif /* USE_CURL */
|
||||||
|
|
||||||
#ifdef USE_VOIP
|
#ifdef USE_VOIP
|
||||||
#include "speex/speex.h"
|
#include <opus.h>
|
||||||
#include "speex/speex_preprocess.h"
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// file full of random crap that gets used to create cl_guid
|
// file full of random crap that gets used to create cl_guid
|
||||||
|
@ -238,14 +237,11 @@ typedef struct {
|
||||||
|
|
||||||
#ifdef USE_VOIP
|
#ifdef USE_VOIP
|
||||||
qboolean voipEnabled;
|
qboolean voipEnabled;
|
||||||
qboolean speexInitialized;
|
qboolean voipCodecInitialized;
|
||||||
int speexFrameSize;
|
|
||||||
int speexSampleRate;
|
|
||||||
|
|
||||||
// incoming data...
|
// incoming data...
|
||||||
// !!! FIXME: convert from parallel arrays to array of a struct.
|
// !!! FIXME: convert from parallel arrays to array of a struct.
|
||||||
SpeexBits speexDecoderBits[MAX_CLIENTS];
|
OpusDecoder *opusDecoder[MAX_CLIENTS];
|
||||||
void *speexDecoder[MAX_CLIENTS];
|
|
||||||
byte voipIncomingGeneration[MAX_CLIENTS];
|
byte voipIncomingGeneration[MAX_CLIENTS];
|
||||||
int voipIncomingSequence[MAX_CLIENTS];
|
int voipIncomingSequence[MAX_CLIENTS];
|
||||||
float voipGain[MAX_CLIENTS];
|
float voipGain[MAX_CLIENTS];
|
||||||
|
@ -257,9 +253,7 @@ typedef struct {
|
||||||
// then we are sending to clientnum i.
|
// then we are sending to clientnum i.
|
||||||
uint8_t voipTargets[(MAX_CLIENTS + 7) / 8];
|
uint8_t voipTargets[(MAX_CLIENTS + 7) / 8];
|
||||||
uint8_t voipFlags;
|
uint8_t voipFlags;
|
||||||
SpeexPreprocessState *speexPreprocessor;
|
OpusEncoder *opusEncoder;
|
||||||
SpeexBits speexEncoderBits;
|
|
||||||
void *speexEncoder;
|
|
||||||
int voipOutgoingDataSize;
|
int voipOutgoingDataSize;
|
||||||
int voipOutgoingDataFrames;
|
int voipOutgoingDataFrames;
|
||||||
int voipOutgoingSequence;
|
int voipOutgoingSequence;
|
||||||
|
@ -447,6 +441,13 @@ extern cvar_t *cl_voipGainDuringCapture;
|
||||||
extern cvar_t *cl_voipCaptureMult;
|
extern cvar_t *cl_voipCaptureMult;
|
||||||
extern cvar_t *cl_voipShowMeter;
|
extern cvar_t *cl_voipShowMeter;
|
||||||
extern cvar_t *cl_voip;
|
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
|
#endif
|
||||||
|
|
||||||
//=================================================
|
//=================================================
|
||||||
|
|
|
@ -2681,16 +2681,12 @@ qboolean S_AL_Init( soundInterface_t *si )
|
||||||
|
|
||||||
s_alAvailableInputDevices = Cvar_Get("s_alAvailableInputDevices", inputdevicenames, CVAR_ROM | CVAR_NORESTART);
|
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");
|
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 )
|
if( !alCaptureDevice && inputdevice )
|
||||||
{
|
{
|
||||||
Com_Printf( "Failed to open OpenAL Input device '%s', trying default.\n", 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",
|
Com_Printf( "OpenAL capture device %s.\n",
|
||||||
(alCaptureDevice == NULL) ? "failed to open" : "opened");
|
(alCaptureDevice == NULL) ? "failed to open" : "opened");
|
||||||
|
|
|
@ -300,7 +300,8 @@ enum svc_ops_e {
|
||||||
svc_EOF,
|
svc_EOF,
|
||||||
|
|
||||||
// new commands, supported only by ioquake3 protocol but not legacy
|
// 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, //
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -316,7 +317,8 @@ enum clc_ops_e {
|
||||||
clc_EOF,
|
clc_EOF,
|
||||||
|
|
||||||
// new commands, supported only by ioquake3 protocol but not legacy
|
// 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, //
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -44,7 +44,7 @@ typedef struct voipServerPacket_s
|
||||||
int len;
|
int len;
|
||||||
int sender;
|
int sender;
|
||||||
int flags;
|
int flags;
|
||||||
byte data[1024];
|
byte data[4000];
|
||||||
} voipServerPacket_t;
|
} voipServerPacket_t;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -299,6 +299,7 @@ extern int serverBansCount;
|
||||||
|
|
||||||
#ifdef USE_VOIP
|
#ifdef USE_VOIP
|
||||||
extern cvar_t *sv_voip;
|
extern cvar_t *sv_voip;
|
||||||
|
extern cvar_t *sv_voipProtocol;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1459,8 +1459,8 @@ void SV_UserinfoChanged( client_t *cl ) {
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
val = Info_ValueForKey(cl->userinfo, "cl_voip");
|
val = Info_ValueForKey(cl->userinfo, "cl_voipProtocol");
|
||||||
cl->hasVoip = atoi(val);
|
cl->hasVoip = !Q_stricmp( val, "opus" );
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -1794,7 +1794,7 @@ static qboolean SV_ShouldIgnoreVoipSender(const client_t *cl)
|
||||||
}
|
}
|
||||||
|
|
||||||
static
|
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;
|
int sender, generation, sequence, frames, packetsize;
|
||||||
uint8_t recips[(MAX_CLIENTS + 7) / 8];
|
uint8_t recips[(MAX_CLIENTS + 7) / 8];
|
||||||
|
@ -1829,12 +1829,12 @@ void SV_UserVoip(client_t *cl, msg_t *msg)
|
||||||
|
|
||||||
MSG_ReadData(msg, encoded, packetsize);
|
MSG_ReadData(msg, encoded, packetsize);
|
||||||
|
|
||||||
if (SV_ShouldIgnoreVoipSender(cl))
|
if (ignoreData || SV_ShouldIgnoreVoipSender(cl))
|
||||||
return; // Blacklisted, disabled, etc.
|
return; // Blacklisted, disabled, etc.
|
||||||
|
|
||||||
// !!! FIXME: see if we read past end of msg...
|
// !!! 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?
|
// !!! FIXME: decide if this is bogus data?
|
||||||
|
|
||||||
// decide who needs this VoIP packet sent to them...
|
// decide who needs this VoIP packet sent to them...
|
||||||
|
@ -1983,10 +1983,18 @@ void SV_ExecuteClientMessage( client_t *cl, msg_t *msg ) {
|
||||||
}
|
}
|
||||||
} while ( 1 );
|
} while ( 1 );
|
||||||
|
|
||||||
// read optional voip data
|
// skip legacy speex voip data
|
||||||
if ( c == clc_voip ) {
|
if ( c == clc_voipSpeex ) {
|
||||||
#ifdef USE_VOIP
|
#ifdef USE_VOIP
|
||||||
SV_UserVoip( cl, msg );
|
SV_UserVoip( cl, msg, qtrue );
|
||||||
|
c = MSG_ReadByte( msg );
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// read optional voip data
|
||||||
|
if ( c == clc_voipOpus ) {
|
||||||
|
#ifdef USE_VOIP
|
||||||
|
SV_UserVoip( cl, msg, qfalse );
|
||||||
c = MSG_ReadByte( msg );
|
c = MSG_ReadByte( msg );
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
@ -656,8 +656,9 @@ void SV_Init (void)
|
||||||
sv_serverid = Cvar_Get ("sv_serverid", "0", CVAR_SYSTEMINFO | CVAR_ROM );
|
sv_serverid = Cvar_Get ("sv_serverid", "0", CVAR_SYSTEMINFO | CVAR_ROM );
|
||||||
sv_pure = Cvar_Get ("sv_pure", "1", CVAR_SYSTEMINFO );
|
sv_pure = Cvar_Get ("sv_pure", "1", CVAR_SYSTEMINFO );
|
||||||
#ifdef USE_VOIP
|
#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);
|
Cvar_CheckRange(sv_voip, 0, 1, qtrue);
|
||||||
|
sv_voipProtocol = Cvar_Get("sv_voipProtocol", sv_voip->integer ? "opus" : "", CVAR_SYSTEMINFO | CVAR_ROM );
|
||||||
#endif
|
#endif
|
||||||
Cvar_Get ("sv_paks", "", CVAR_SYSTEMINFO | CVAR_ROM );
|
Cvar_Get ("sv_paks", "", CVAR_SYSTEMINFO | CVAR_ROM );
|
||||||
Cvar_Get ("sv_pakNames", "", CVAR_SYSTEMINFO | CVAR_ROM );
|
Cvar_Get ("sv_pakNames", "", CVAR_SYSTEMINFO | CVAR_ROM );
|
||||||
|
|
|
@ -24,6 +24,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
#ifdef USE_VOIP
|
#ifdef USE_VOIP
|
||||||
cvar_t *sv_voip;
|
cvar_t *sv_voip;
|
||||||
|
cvar_t *sv_voipProtocol;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
serverStatic_t svs; // persistant server info
|
serverStatic_t svs; // persistant server info
|
||||||
|
@ -665,8 +666,8 @@ void SVC_Info( netadr_t from ) {
|
||||||
Info_SetValueForKey(infostring, "g_needpass", va("%d", Cvar_VariableIntegerValue("g_needpass")));
|
Info_SetValueForKey(infostring, "g_needpass", va("%d", Cvar_VariableIntegerValue("g_needpass")));
|
||||||
|
|
||||||
#ifdef USE_VOIP
|
#ifdef USE_VOIP
|
||||||
if (sv_voip->integer) {
|
if (sv_voipProtocol->string && *sv_voipProtocol->string) {
|
||||||
Info_SetValueForKey( infostring, "voip", va("%i", sv_voip->integer ) );
|
Info_SetValueForKey( infostring, "voip", sv_voipProtocol->string );
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -547,7 +547,7 @@ static void SV_WriteVoipToClient(client_t *cl, msg_t *msg)
|
||||||
if (totalbytes > (msg->maxsize - msg->cursize) / 2)
|
if (totalbytes > (msg->maxsize - msg->cursize) / 2)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
MSG_WriteByte(msg, svc_voip);
|
MSG_WriteByte(msg, svc_voipOpus);
|
||||||
MSG_WriteShort(msg, packet->sender);
|
MSG_WriteShort(msg, packet->sender);
|
||||||
MSG_WriteByte(msg, (byte) packet->generation);
|
MSG_WriteByte(msg, (byte) packet->generation);
|
||||||
MSG_WriteLong(msg, packet->sequence);
|
MSG_WriteLong(msg, packet->sequence);
|
||||||
|
|
Loading…
Reference in a new issue