9c59fafc3e
git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@228 fc73d0e0-1445-4013-8a0c-d673dee63da5
436 lines
10 KiB
C
436 lines
10 KiB
C
//voice chat routines.
|
|
|
|
//needs quite a bit of work.
|
|
|
|
//it needs a proper protocol.
|
|
//the server needs to be able to shutdown again.
|
|
//options about who gets the sound data is also needed.
|
|
|
|
#include "bothdefs.h"
|
|
|
|
#ifdef VOICECHAT
|
|
|
|
#include "quakedef.h"
|
|
#ifdef _WIN32
|
|
#include "winquake.h"
|
|
|
|
#define EWOULDBLOCK WSAEWOULDBLOCK
|
|
#define EMSGSIZE WSAEMSGSIZE
|
|
#define ECONNRESET WSAECONNRESET
|
|
#define ECONNABORTED WSAECONNABORTED
|
|
#define ECONNREFUSED WSAECONNREFUSED
|
|
#define EADDRNOTAVAIL WSAEADDRNOTAVAIL
|
|
|
|
#define qerrno WSAGetLastError()
|
|
#else
|
|
#define qerrno errno
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <netdb.h>
|
|
#include <sys/param.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/uio.h>
|
|
#include <arpa/inet.h>
|
|
#include <errno.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#ifdef sun
|
|
#include <sys/filio.h>
|
|
#endif
|
|
|
|
#ifdef NeXT
|
|
#include <libc.h>
|
|
#endif
|
|
|
|
#define closesocket close
|
|
#define ioctlsocket ioctl
|
|
#endif
|
|
|
|
#include "voicechat.h"
|
|
|
|
static int CLVS_socket;
|
|
static int SVVS_socket;
|
|
static qboolean SVVS_inited;
|
|
|
|
static qbyte inputbuffer[44100];
|
|
static int readpoint;
|
|
static qbyte outputbuffer[44100];
|
|
static int sendpoint;
|
|
|
|
/*
|
|
Protocol:
|
|
Sound chunk:
|
|
(char) data begins with codec id.
|
|
(short) followed by number of samples
|
|
(short) then size in bytes of chunk.
|
|
*/
|
|
|
|
#ifndef CLIENTONLY
|
|
void SVVC_ServerInit(void)
|
|
{
|
|
netadr_t adr;
|
|
struct sockaddr_in address;
|
|
unsigned long _true = true;
|
|
int i;
|
|
int port = PORT_SERVER;
|
|
|
|
if ((SVVS_socket = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
|
|
{
|
|
Sys_Error ("FTP_TCP_OpenSocket: socket:", strerror(qerrno));
|
|
}
|
|
|
|
if (ioctlsocket (SVVS_socket, FIONBIO, &_true) == -1)
|
|
{
|
|
Sys_Error ("FTP_TCP_OpenSocket: ioctl FIONBIO:", strerror(qerrno));
|
|
}
|
|
|
|
address.sin_family = AF_INET;
|
|
//ZOID -- check for interface binding option
|
|
if ((i = COM_CheckParm("-ip")) != 0 && i < com_argc) {
|
|
address.sin_addr.s_addr = inet_addr(com_argv[i+1]);
|
|
Con_TPrintf(TL_NETBINDINTERFACE,
|
|
inet_ntoa(address.sin_addr));
|
|
} else
|
|
address.sin_addr.s_addr = INADDR_ANY;
|
|
|
|
if (port == PORT_ANY)
|
|
address.sin_port = 0;
|
|
else
|
|
address.sin_port = htons((short)port);
|
|
|
|
if( bind (SVVS_socket, (void *)&address, sizeof(address)) == -1)
|
|
{
|
|
closesocket(SVVS_socket);
|
|
return;
|
|
}
|
|
|
|
listen(SVVS_socket, 3);
|
|
|
|
SVVS_inited = true;
|
|
|
|
|
|
Con_Printf("VC server is running\n");
|
|
SockadrToNetadr((struct sockaddr_qstorage*)&address, &adr);
|
|
Info_SetValueForKey(svs.info, "voiceaddress", NET_AdrToString(adr), MAX_SERVERINFO_STRING);
|
|
return;
|
|
}
|
|
|
|
//currently a dum forwarding/broadcasting mechanism
|
|
void SVVC_Frame (qboolean running)
|
|
{
|
|
struct sockaddr_in frm;
|
|
int size = sizeof(frm);
|
|
int newcl;
|
|
int i, j;
|
|
char buffer[1400];
|
|
|
|
if (!running)
|
|
return;
|
|
|
|
if (!SVVS_socket)
|
|
{
|
|
SVVC_ServerInit();
|
|
return;
|
|
}
|
|
newcl = accept(SVVS_socket, (struct sockaddr *)&frm, &size);
|
|
if (newcl != INVALID_SOCKET)
|
|
{
|
|
for (i = 0; i < MAX_CLIENTS; i++)
|
|
{
|
|
if (!svs.clients[i].voicechat.socket) //this really isn't the right way...
|
|
{
|
|
svs.clients[i].voicechat.socket = newcl;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < MAX_CLIENTS; i++)
|
|
{
|
|
host_client = &svs.clients[i];
|
|
|
|
if (host_client->voicechat.socket)
|
|
{
|
|
size = recv(host_client->voicechat.socket, buffer, sizeof(buffer), 0);
|
|
if (size > 0)
|
|
{
|
|
for (j = 0; j < MAX_CLIENTS; j++)
|
|
{
|
|
if (j != i && svs.clients[j].voicechat.socket) //gotta be capable of receiving, and not the sender (that would be dumb).
|
|
send(svs.clients[j].voicechat.socket, buffer, size, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifndef SERVERONLY
|
|
|
|
sfxcache_t *voicesoundbuffer[2];
|
|
sfx_t recordaudio[2] = {
|
|
{"recordaudio1",
|
|
{NULL, false},
|
|
NULL},
|
|
{"recordaudio2",
|
|
{NULL, false},
|
|
NULL}
|
|
};
|
|
int audiobuffer;
|
|
void SNDVC_Submit(int codec, qbyte *buffer, int samples, int freq, int width)
|
|
{
|
|
S_RawAudio(0, buffer, freq, samples, 1, width);
|
|
/*
|
|
soundcardinfo_t *cursndcard;
|
|
audiobuffer=0;
|
|
if (!recordaudio[audiobuffer].cache.data)
|
|
{
|
|
voicesoundbuffer[audiobuffer] = BZ_Malloc(44100*2+sizeof(sfxcache_t));
|
|
recordaudio[audiobuffer].cache.data = voicesoundbuffer[audiobuffer];
|
|
recordaudio[audiobuffer].cache.fake = true;
|
|
}
|
|
|
|
cursndcard = sndcardinfo;
|
|
if (!cursndcard)
|
|
{
|
|
Con_Printf("Accepting voice chat with no sound card\n");
|
|
return;
|
|
}
|
|
voicesoundbuffer[audiobuffer]->length = samples;
|
|
voicesoundbuffer[audiobuffer]->stereo = false;
|
|
voicesoundbuffer[audiobuffer]->speed = sndcardinfo->sn.speed;
|
|
voicesoundbuffer[audiobuffer]->width = width;
|
|
voicesoundbuffer[audiobuffer]->loopstart=-1;
|
|
|
|
// Con_DPrintf("Submit %i\n", (int)samples);
|
|
|
|
if (codec == 0) //codec 0 is special. (A straight copy)
|
|
ResampleSfx(&recordaudio[audiobuffer], freq, width, buffer);
|
|
else
|
|
{
|
|
qbyte *temp = BZ_Malloc(samples*width);
|
|
audiocodecs[codec].decode(buffer, (short*)temp, samples);
|
|
ResampleSfx(&recordaudio[audiobuffer], freq, width, temp);
|
|
BZ_Free(temp);
|
|
}
|
|
|
|
for (cursndcard = sndcardinfo; cursndcard; cursndcard=cursndcard->next)
|
|
{
|
|
if (0&&cursndcard->channel[MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS+1-audiobuffer].sfx == &recordaudio[1-audiobuffer]) //other buffer is playing.
|
|
{
|
|
cursndcard->channel[MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS+audiobuffer].sfx = &recordaudio[audiobuffer];
|
|
cursndcard->channel[MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS+audiobuffer].pos = cursndcard->channel[MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS+1-audiobuffer].end - voicesoundbuffer[1-audiobuffer]->length- cursndcard->channel[MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS+1-audiobuffer].pos;
|
|
cursndcard->channel[MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS+audiobuffer].end = cursndcard->channel[MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS+audiobuffer].end + samples;
|
|
|
|
if (cursndcard->channel[MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS+audiobuffer].pos >= voicesoundbuffer[audiobuffer]->length)
|
|
{
|
|
cursndcard->channel[MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS+1-audiobuffer].pos = voicesoundbuffer[1-audiobuffer]->length;
|
|
cursndcard->channel[MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS+audiobuffer].pos = 0;
|
|
Con_Printf("Sound out of sync\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cursndcard->channel[MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS+audiobuffer].sfx = &recordaudio[audiobuffer];
|
|
cursndcard->channel[MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS+audiobuffer].pos = 0;
|
|
cursndcard->channel[MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS+audiobuffer].end = cursndcard->paintedtime + samples;
|
|
}
|
|
}
|
|
audiobuffer = 1-audiobuffer;*/
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void CLVC_SetServer (char *addy)
|
|
{
|
|
unsigned long _true = true;
|
|
struct sockaddr_qstorage from;
|
|
|
|
|
|
if ((CLVS_socket = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
|
|
{
|
|
Sys_Error ("FTP_UDP_OpenSocket: socket: %s\n", strerror(qerrno));
|
|
}
|
|
|
|
|
|
{//quake routines using dns and stuff (Really, I wanna keep quake and ftp fairly seperate)
|
|
netadr_t qaddy;
|
|
if (!NET_StringToAdr (addy, &qaddy)) //server doesn't exist.
|
|
return;
|
|
if (qaddy.type != NA_IP) //Only TCP is supported.
|
|
return;
|
|
if (!qaddy.port)
|
|
qaddy.port = htons(PORT_SERVER);
|
|
NetadrToSockadr(&qaddy, &from);
|
|
|
|
}
|
|
|
|
//not yet non blocking.
|
|
if (connect(CLVS_socket, (struct sockaddr*)&from, sizeof(from)) == -1)
|
|
{
|
|
Con_Printf ("FTP_TCP_OpenSocket: connect: %i %s\n", qerrno, strerror(qerrno));
|
|
closesocket(CLVS_socket);
|
|
CLVS_socket = 0;
|
|
return;
|
|
}
|
|
|
|
if (ioctlsocket (CLVS_socket, FIONBIO, &_true) == -1) //now make it non blocking.
|
|
{
|
|
Sys_Error ("FTP_TCP_OpenSocket: ioctl FIONBIO: %s\n", strerror(qerrno));
|
|
}
|
|
}
|
|
|
|
void CLVC_Disconnect (void)
|
|
{
|
|
closesocket(CLVS_socket);
|
|
CLVS_socket = 0;
|
|
}
|
|
|
|
void CLVC_Poll (void)
|
|
{
|
|
int codec;
|
|
int size;
|
|
|
|
if (!CLVS_socket)
|
|
return;
|
|
|
|
while (1)
|
|
{
|
|
size = recv(CLVS_socket, &inputbuffer[readpoint], sizeof(inputbuffer) - readpoint, 0);
|
|
if (size>0)
|
|
{
|
|
int samps;
|
|
int bytes;
|
|
readpoint+=size;
|
|
if (readpoint >= 1)
|
|
{
|
|
codec = *inputbuffer;
|
|
|
|
if (codec >= 0 && codec <= 127 && readpoint>=5) //just in case.
|
|
{
|
|
samps = LittleLong(*(signed short *)(inputbuffer+1));
|
|
bytes = LittleLong(*(unsigned short *)(inputbuffer+3));
|
|
// Con_DPrintf("read %i\n", size);
|
|
if (samps < 0) //something special
|
|
{
|
|
readpoint=0;
|
|
}
|
|
else
|
|
{
|
|
if (readpoint >= bytes+5)
|
|
{
|
|
if (codec == 1)
|
|
Con_Printf("Reading\n");
|
|
if (codec < audionumcodecs && audiocodecs[codec].decode)
|
|
{
|
|
SNDVC_Submit(codec, ((qbyte *)inputbuffer)+5, samps, 11025, 2);
|
|
readpoint -= bytes+5;
|
|
memmove(inputbuffer, &inputbuffer[readpoint+bytes+5], readpoint);
|
|
}
|
|
else
|
|
{
|
|
Con_Printf("Bad codec %i\n", (int)codec);
|
|
readpoint=0;
|
|
closesocket(CLVS_socket);
|
|
CLVS_socket = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (readpoint >= sizeof(inputbuffer) || readpoint < 0)
|
|
{
|
|
Con_Printf("Too small buffer or extended client %i\n", (int)readpoint);
|
|
readpoint=0;
|
|
closesocket(CLVS_socket);
|
|
CLVS_socket = 0;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void SNDVC_MicInput(qbyte *buffer, int samples, int freq, int width) //this correctly buffers data ready to be sent.
|
|
{
|
|
int sent;
|
|
qbyte codec;
|
|
unsigned short *sampleswritten;
|
|
samples/=width;
|
|
if (!CLVS_socket)
|
|
{
|
|
if (cls.state)
|
|
{
|
|
char *server;
|
|
server = Info_ValueForKey(cl.serverinfo, "voiceaddress");
|
|
if (*server)
|
|
CLVC_SetServer(server);
|
|
}
|
|
|
|
SNDVC_Submit(0, buffer, samples, freq, width);
|
|
return;
|
|
}
|
|
else if (!cls.state)
|
|
{
|
|
readpoint = 0;
|
|
sendpoint= 0;
|
|
SNDVC_Submit(0, buffer, samples, freq, width);
|
|
return;
|
|
}
|
|
|
|
SNDVC_Submit(0, buffer, samples, freq, width); //remembering at all times that we will not be allowed to hear it ourselves.
|
|
|
|
//add to send buffer.
|
|
if (samples > 0x7ffe)
|
|
samples = 0x7ffe;
|
|
|
|
if (sendpoint + samples*width+sizeof(unsigned char)+sizeof(short)+sizeof(*sampleswritten) < sizeof(outputbuffer))
|
|
{
|
|
// Con_DPrintf("sending %i\n", (int)samples);
|
|
codec = 1;
|
|
outputbuffer[sendpoint] = codec; sendpoint += sizeof(unsigned char);
|
|
*(unsigned short*)(&outputbuffer[sendpoint]) = samples; sendpoint += sizeof(unsigned short);
|
|
sampleswritten = (short *)&outputbuffer[sendpoint]; sendpoint += sizeof(*sampleswritten);
|
|
*sampleswritten = audiocodecs[codec].encode((short *)buffer, &outputbuffer[sendpoint], samples);
|
|
sendpoint += *sampleswritten;
|
|
}
|
|
else
|
|
{
|
|
Con_Printf("Connection overflowing\n");
|
|
}
|
|
|
|
//try and send it
|
|
sent = send(CLVS_socket, outputbuffer, sendpoint, 0);
|
|
if (sent > 0)
|
|
{
|
|
// Con_DPrintf("sent %i\n", (int)sent);
|
|
sendpoint -= sent;
|
|
}
|
|
else
|
|
{
|
|
CLVS_socket=0;
|
|
}
|
|
|
|
|
|
// SNDVC_Submit(buffer, samples, freq, width);
|
|
}
|
|
|
|
#endif
|
|
#endif
|