484e8bbfc2
reworked network addresses to separate address family and connection type. this should make banning people more reliable, as well as simplifying a whole load of logic (no need to check for ipv4 AND ipv6). tcpconnect will keep trying to connect even if the connection wasn't instant, instead of giving up instantly. rewrote tcp connections quite a bit. sv_port_tcp now handles qtv+qizmo+http+ws+rtcbroker+tls equivalents. qtv_streamport is now a legacy cvar and now acts equivalently to sv_port_tcp (but still separate). rewrote screenshot and video capture code to use strides. this solves image-is-upside down issues with vulkan. ignore alt key in browser port. oh no! no more red text! oh no! no more alt-being-wrongly-down-and-being-unable-to-type-anything-without-forcing-alt-released! reworked audio decoder interface. now has clearly defined success/unavailable/end-of-file results. this should solve a whole load of issues with audio streaming. fixed various openal audio streaming issues too. openal also got some workarounds for emscripten's poor emulation. fixed ogg decoder to retain sync properly if seeked. updated menu_media a bit. now reads vorbis comments/id3v1 tags to get proper track names. also saves the playlist so you don't have to manually repopulate the list so it might actually be usable now (after how many years?) r_stains now defaults to 0, and is no longer enabled by presets. use decals if you want that sort of thing. added fs_noreexec cvar, so configs will not be reexeced on gamedir change. this also means defaults won't be reapplied, etc. added 'nvvk' renderer on windows, using nvidia's vulkan-inside-opengl gl extension. mostly just to see how much slower it is. fixed up the ftp server quite a lot. more complete, more compliant, and should do ipv6 properly to-boot. file transfers also threaded. fixed potential crash inside runclientphys. experimental sv_antilag=3 setting. totally untested. the aim is to avoid missing due to lagged knockbacks. may be expensive for the server. browser port's websockets support fixed. experimental support for webrtc ('works for me', requires a broker server). updated avplug(renamed to ffmpeg so people know what it is) to use ffmpeg 3.2.4 properly, with its new encoder api. should be much more robust... also added experimental audio decoder for game music etc (currently doesn't resample, so playback rates are screwed, disabled by cvar). git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5097 fc73d0e0-1445-4013-8a0c-d673dee63da5
505 lines
13 KiB
C
505 lines
13 KiB
C
#include "quakedef.h"
|
|
|
|
#ifdef AVAIL_OGGVORBIS
|
|
#define OV_EXCLUDE_STATIC_CALLBACKS
|
|
|
|
#ifdef __MORPHOS__
|
|
#include <exec/exec.h>
|
|
#include <libraries/vorbisfile.h>
|
|
|
|
#include <proto/exec.h>
|
|
#include <proto/vorbisfile.h>
|
|
#else
|
|
#include <vorbis/vorbisfile.h>
|
|
#endif
|
|
|
|
|
|
#ifdef LIBVORBISFILE_STATIC
|
|
#define p_ov_open_callbacks ov_open_callbacks
|
|
#define p_ov_clear ov_clear
|
|
#define p_ov_info ov_info
|
|
#define p_ov_comment ov_comment
|
|
#define p_ov_pcm_total ov_pcm_total
|
|
#define p_ov_time_total ov_time_total
|
|
#define p_ov_read ov_read
|
|
#define p_ov_pcm_seek ov_pcm_seek
|
|
#else
|
|
#if defined(__MORPHOS__)
|
|
|
|
#define oggvorbislibrary VorbisFileBase
|
|
struct Library *VorbisFileBase;
|
|
|
|
#else
|
|
dllhandle_t *oggvorbislibrary;
|
|
#endif
|
|
|
|
#ifdef __MORPHOS__
|
|
#define p_ov_open_callbacks(a, b, c, d, e) ov_open_callbacks(a, b, c, d, &e)
|
|
#define p_ov_clear ov_clear
|
|
#define p_ov_info ov_info
|
|
#define p_ov_comment ov_comment
|
|
#define p_ov_pcm_total ov_pcm_total
|
|
#define p_ov_time_total ov_time_total
|
|
#define p_ov_read ov_read
|
|
#define p_ov_pcm_seek ov_pcm_seek
|
|
#else
|
|
int (VARGS *p_ov_open_callbacks) (void *datasource, OggVorbis_File *vf, char *initial, long ibytes, ov_callbacks callbacks);
|
|
int (VARGS *p_ov_clear)(OggVorbis_File *vf);
|
|
vorbis_info *(VARGS *p_ov_info)(OggVorbis_File *vf,int link);
|
|
vorbis_comment *(VARGS *p_ov_comment) (OggVorbis_File *vf,int link);
|
|
ogg_int64_t (VARGS *p_ov_pcm_total)(OggVorbis_File *vf,int i);
|
|
double (VARGS *p_ov_time_total)(OggVorbis_File *vf,int i);
|
|
long (VARGS *p_ov_read)(OggVorbis_File *vf,char *buffer,int length,int bigendianp,int word,int sgned,int *bitstream);
|
|
int (VARGS *p_ov_pcm_seek)(OggVorbis_File *vf,ogg_int64_t pos);
|
|
#endif
|
|
#endif
|
|
|
|
|
|
typedef struct {
|
|
unsigned char *start; //file positions
|
|
unsigned long length;
|
|
unsigned long pos;
|
|
int srcspeed;
|
|
int srcchannels;
|
|
|
|
qboolean failed;
|
|
|
|
char *tempbuffer;
|
|
int tempbufferbytes;
|
|
|
|
char *decodedbuffer;
|
|
int decodedbufferbytes;
|
|
int decodedbytestart;
|
|
int decodedbytecount;
|
|
|
|
OggVorbis_File vf;
|
|
|
|
sfx_t *s;
|
|
} ovdecoderbuffer_t;
|
|
|
|
float QDECL OV_Query(struct sfx_s *sfx, struct sfxcache_s *buf, char *name, size_t namesize);
|
|
static sfxcache_t *QDECL OV_DecodeSome(struct sfx_s *sfx, struct sfxcache_s *buf, ssamplepos_t start, int length);
|
|
static void QDECL OV_CancelDecoder(sfx_t *s);
|
|
static qboolean OV_StartDecode(unsigned char *start, unsigned long length, ovdecoderbuffer_t *buffer);
|
|
|
|
qboolean QDECL S_LoadOVSound (sfx_t *s, qbyte *data, size_t datalen, int sndspeed)
|
|
{
|
|
ovdecoderbuffer_t *buffer;
|
|
|
|
if (datalen < 4 || strncmp(data, "OggS", 4))
|
|
return false;
|
|
|
|
buffer = Z_Malloc(sizeof(ovdecoderbuffer_t));
|
|
|
|
buffer->decodedbytestart = 0;
|
|
buffer->decodedbytecount = 0;
|
|
buffer->s = s;
|
|
s->decoder.buf = buffer;
|
|
s->loopstart = -1;
|
|
|
|
if (!OV_StartDecode(data, datalen, buffer))
|
|
{
|
|
if (buffer->decodedbuffer)
|
|
{
|
|
BZ_Free(buffer->decodedbuffer);
|
|
buffer->decodedbuffer = NULL;
|
|
}
|
|
buffer->decodedbufferbytes = 0;
|
|
buffer->decodedbytestart = 0;
|
|
buffer->decodedbytecount = 0;
|
|
Z_Free(s->decoder.buf);
|
|
s->decoder.buf = NULL;
|
|
s->loadstate = SLS_FAILED; //failed!
|
|
return false;
|
|
}
|
|
s->decoder.decodedata = OV_DecodeSome;
|
|
s->decoder.querydata = OV_Query;
|
|
s->decoder.purge = OV_CancelDecoder;
|
|
s->decoder.ended = OV_CancelDecoder;
|
|
|
|
s->decoder.decodedata(s, NULL, 0, 100);
|
|
|
|
return true;
|
|
}
|
|
|
|
float QDECL OV_Query(struct sfx_s *sfx, struct sfxcache_s *buf, char *name, size_t namesize)
|
|
{
|
|
ovdecoderbuffer_t *dec = sfx->decoder.buf;
|
|
if (!dec)
|
|
return -1;
|
|
if (buf)
|
|
{
|
|
buf->data = NULL; //you're not meant to actually be using the data here
|
|
buf->soundoffset = 0;
|
|
buf->length = p_ov_pcm_total(&dec->vf, -1);
|
|
buf->numchannels = dec->srcchannels;
|
|
buf->speed = dec->srcspeed;
|
|
buf->width = 2;
|
|
}
|
|
if (name)
|
|
{
|
|
vorbis_comment *c = p_ov_comment(&dec->vf, -1);
|
|
int i;
|
|
const char *artist = NULL;
|
|
const char *title = NULL;
|
|
for (i = 0; i < c->comments; i++)
|
|
{
|
|
if (!strncmp(c->user_comments[i], "ARTIST=", 7))
|
|
artist = c->user_comments[i]+7;
|
|
else if (!strncmp(c->user_comments[i], "TITLE=", 6))
|
|
title = c->user_comments[i]+6;
|
|
}
|
|
|
|
if (artist && title)
|
|
Q_snprintfz(name, namesize, "%s - %s", artist, title);
|
|
else if (title)
|
|
Q_snprintfz(name, namesize, "%s", title);
|
|
}
|
|
return p_ov_time_total(&dec->vf, -1);
|
|
}
|
|
|
|
static sfxcache_t *QDECL OV_DecodeSome(struct sfx_s *sfx, struct sfxcache_s *buf, ssamplepos_t start, int length)
|
|
{
|
|
extern int snd_speed;
|
|
extern cvar_t snd_linearresample_stream;
|
|
int bigendianp = bigendian;
|
|
int current_section = 0;
|
|
|
|
ovdecoderbuffer_t *dec = sfx->decoder.buf;
|
|
int bytesread;
|
|
|
|
int outspeed = snd_speed;
|
|
|
|
// Con_Printf("Minlength = %03i ", minlength);
|
|
|
|
start *= 2*dec->srcchannels;
|
|
length *= 2*dec->srcchannels;
|
|
|
|
if (start < dec->decodedbytestart)
|
|
{
|
|
// Con_Printf("Rewound to %i\n", start);
|
|
dec->failed = false;
|
|
|
|
//check pos
|
|
if (p_ov_pcm_seek(&dec->vf, start * (dec->srcspeed/(2.0*dec->srcchannels*outspeed))) == 0)
|
|
{
|
|
/*something rewound, purge clear the buffer*/
|
|
dec->decodedbytecount = 0;
|
|
dec->decodedbytestart = start;
|
|
}
|
|
}
|
|
|
|
/* if (start > dec->decodedbytestart + dec->decodedbytecount)
|
|
{
|
|
dec->decodedbytestart = start;
|
|
p_ov_pcm_seek(&dec->vf, (dec->decodedbytestart * dec->srcspeed) / outspeed);
|
|
}
|
|
*/
|
|
if (dec->decodedbytecount > outspeed*8)
|
|
{
|
|
/*everything is okay, but our buffer is getting needlessly large.
|
|
keep anything after the 'new' position, but discard all before that
|
|
trim shouldn't be able to go negative
|
|
*/
|
|
int trim = start - dec->decodedbytestart;
|
|
if (trim < 0)
|
|
{
|
|
dec->decodedbytecount = 0;
|
|
dec->decodedbytestart = start;
|
|
// Con_Printf("trim < 0\n");
|
|
}
|
|
else if (trim > dec->decodedbytecount)
|
|
{
|
|
if (0==p_ov_pcm_seek(&dec->vf, start * (dec->srcspeed/(2.0*dec->srcchannels*outspeed))))
|
|
{
|
|
dec->decodedbytecount = 0;
|
|
dec->decodedbytestart = start;
|
|
}
|
|
// Con_Printf("trim > count\n");
|
|
}
|
|
else
|
|
{
|
|
// Con_Printf("trim retain\n");
|
|
//FIXME: retain an extra half-second for dual+ sound devices running slightly out of sync
|
|
memmove(dec->decodedbuffer, dec->decodedbuffer + trim, dec->decodedbytecount - trim);
|
|
dec->decodedbytecount -= trim;
|
|
dec->decodedbytestart += trim;
|
|
}
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
if (dec->failed || start+length <= dec->decodedbytestart + dec->decodedbytecount)
|
|
break;
|
|
|
|
if (dec->decodedbufferbytes < start+length - dec->decodedbytestart + 128) //expand if needed.
|
|
{
|
|
// Con_Printf("Expand buffer\n");
|
|
dec->decodedbufferbytes = (start+length - dec->decodedbytestart) + outspeed;
|
|
dec->decodedbuffer = BZ_Realloc(dec->decodedbuffer, dec->decodedbufferbytes);
|
|
}
|
|
|
|
if (outspeed == dec->srcspeed)
|
|
{
|
|
bytesread = p_ov_read(&dec->vf, dec->decodedbuffer+dec->decodedbytecount, (start+length) - (dec->decodedbytestart+dec->decodedbytecount), bigendianp, 2, 1, ¤t_section);
|
|
if (bytesread <= 0)
|
|
{
|
|
if (bytesread != 0) //0==eof
|
|
{
|
|
dec->failed = true;
|
|
Con_Printf("ogg decoding failed\n");
|
|
break;
|
|
}
|
|
if (start >= dec->decodedbytestart+dec->decodedbytecount)
|
|
return NULL; //let the mixer know that we hit the end
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
double scale = dec->srcspeed / (double)outspeed;
|
|
int decodesize = ceil((dec->decodedbufferbytes-dec->decodedbytecount) * scale);
|
|
/*round down...*/
|
|
decodesize &= ~(2 * dec->srcchannels - 1);
|
|
if (decodesize > dec->tempbufferbytes)
|
|
{
|
|
dec->tempbuffer = BZ_Realloc(dec->tempbuffer, decodesize);
|
|
dec->tempbufferbytes = decodesize;
|
|
}
|
|
|
|
bytesread = p_ov_read(&dec->vf, dec->tempbuffer, decodesize, bigendianp, 2, 1, ¤t_section);
|
|
|
|
if (bytesread <= 0)
|
|
{
|
|
if (bytesread != 0) //0==eof
|
|
{
|
|
dec->failed = true;
|
|
Con_Printf("ogg decoding failed\n");
|
|
return NULL;
|
|
}
|
|
if (start >= dec->decodedbytestart+dec->decodedbytecount)
|
|
return NULL; //let the mixer know that we hit the end
|
|
break;
|
|
}
|
|
|
|
SND_ResampleStream(dec->tempbuffer,
|
|
dec->srcspeed,
|
|
2,
|
|
dec->srcchannels,
|
|
bytesread / (2 * dec->srcchannels),
|
|
dec->decodedbuffer+dec->decodedbytecount,
|
|
outspeed,
|
|
2,
|
|
dec->srcchannels,
|
|
snd_linearresample_stream.ival);
|
|
|
|
bytesread = (int)floor(bytesread / scale) & ~(2 * dec->srcchannels - 1);
|
|
}
|
|
|
|
dec->decodedbytecount += bytesread;
|
|
}
|
|
|
|
if (buf)
|
|
{
|
|
buf->data = dec->decodedbuffer;
|
|
buf->soundoffset = dec->decodedbytestart / (2 * dec->srcchannels);
|
|
buf->length = dec->decodedbytecount / (2 * dec->srcchannels);
|
|
buf->numchannels = dec->srcchannels;
|
|
buf->speed = snd_speed;
|
|
buf->width = 2;
|
|
}
|
|
return buf;
|
|
}
|
|
/*static void OV_CanceledDecoder(void *ctx, void *data, size_t a, size_t b)
|
|
{
|
|
sfx_t *s = ctx;
|
|
if (s->loadstate != SLS_LOADING)
|
|
s->loadstate = SLS_NOTLOADED;
|
|
}*/
|
|
static void QDECL OV_CancelDecoder(sfx_t *s)
|
|
{
|
|
ovdecoderbuffer_t *dec;
|
|
s->loadstate = SLS_FAILED;
|
|
|
|
dec = s->decoder.buf;
|
|
s->decoder.buf = NULL;
|
|
s->decoder.purge = NULL;
|
|
s->decoder.ended = NULL;
|
|
s->decoder.querydata = NULL;
|
|
s->decoder.decodedata = NULL;
|
|
p_ov_clear (&dec->vf); //close the decoder
|
|
|
|
if (dec->tempbuffer)
|
|
{
|
|
BZ_Free(dec->tempbuffer);
|
|
dec->tempbufferbytes = 0;
|
|
}
|
|
|
|
BZ_Free(dec->decodedbuffer);
|
|
dec->decodedbuffer = NULL;
|
|
|
|
BZ_Free(dec);
|
|
|
|
//due to the nature of message passing, we can get into a state where the main thread is going to flag us as loaded when we have already failed.
|
|
//that is bad.
|
|
//so post a message to the main thread to override it, just in case.
|
|
// COM_AddWork(WG_MAIN, OV_CanceledDecoder, s, NULL, SLS_NOTLOADED, 0);
|
|
s->loadstate = SLS_NOTLOADED;
|
|
}
|
|
|
|
static size_t VARGS read_func (void *ptr, size_t size, size_t nmemb, void *datasource)
|
|
{
|
|
ovdecoderbuffer_t *buffer = datasource;
|
|
int spare = buffer->length - buffer->pos;
|
|
|
|
if (size*nmemb > spare)
|
|
nmemb = spare / size;
|
|
memcpy(ptr, &buffer->start[buffer->pos], size*nmemb);
|
|
buffer->pos += size*nmemb;
|
|
return nmemb;
|
|
}
|
|
|
|
static int VARGS seek_func (void *datasource, ogg_int64_t offset, int whence)
|
|
{
|
|
ovdecoderbuffer_t *buffer = datasource;
|
|
switch(whence)
|
|
{
|
|
case SEEK_SET:
|
|
buffer->pos = offset;
|
|
break;
|
|
case SEEK_END:
|
|
buffer->pos = buffer->length+offset;
|
|
break;
|
|
case SEEK_CUR:
|
|
buffer->pos+=offset;
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int VARGS close_func (void *datasource)
|
|
{
|
|
ovdecoderbuffer_t *buffer = datasource;
|
|
BZ_Free(buffer->start);
|
|
buffer->start=0;
|
|
return 0;
|
|
}
|
|
|
|
static long VARGS tell_func (void *datasource)
|
|
{
|
|
ovdecoderbuffer_t *buffer = datasource;
|
|
return buffer->pos;
|
|
}
|
|
static ov_callbacks callbacks = {
|
|
read_func,
|
|
seek_func,
|
|
close_func,
|
|
tell_func,
|
|
};
|
|
static qboolean OV_StartDecode(unsigned char *start, unsigned long length, ovdecoderbuffer_t *buffer)
|
|
{
|
|
#ifndef LIBVORBISFILE_STATIC
|
|
static qboolean tried;
|
|
#ifndef __MORPHOS__
|
|
static dllfunction_t funcs[] =
|
|
{
|
|
{(void*)&p_ov_open_callbacks, "ov_open_callbacks"},
|
|
{(void*)&p_ov_comment, "ov_comment"},
|
|
{(void*)&p_ov_pcm_total, "ov_pcm_total"},
|
|
{(void*)&p_ov_time_total, "ov_time_total"},
|
|
{(void*)&p_ov_clear, "ov_clear"},
|
|
{(void*)&p_ov_info, "ov_info"},
|
|
{(void*)&p_ov_read, "ov_read"},
|
|
{(void*)&p_ov_pcm_seek, "ov_pcm_seek"},
|
|
{NULL}
|
|
};
|
|
#endif
|
|
|
|
if (!oggvorbislibrary && !tried)
|
|
#if defined(__MORPHOS__)
|
|
{
|
|
VorbisFileBase = OpenLibrary("vorbisfile.library", 2);
|
|
if (!VorbisFileBase)
|
|
{
|
|
Con_Printf("Unable to open vorbisfile.library version 2\n");
|
|
}
|
|
}
|
|
#elif defined(_WIN32)
|
|
{
|
|
oggvorbislibrary = Sys_LoadLibrary("vorbisfile", funcs);
|
|
if (!oggvorbislibrary)
|
|
oggvorbislibrary = Sys_LoadLibrary("libvorbisfile", funcs);
|
|
|
|
if (!oggvorbislibrary)
|
|
{
|
|
oggvorbislibrary = Sys_LoadLibrary("libvorbisfile-3", funcs);
|
|
if (!oggvorbislibrary)
|
|
oggvorbislibrary = Sys_LoadLibrary("libvorbisfile", funcs);
|
|
}
|
|
|
|
if (!oggvorbislibrary)
|
|
Con_Printf("Couldn't load DLL: \"vorbisfile.dll\" or \"libvorbisfile-3\".\n");
|
|
}
|
|
#else
|
|
{
|
|
oggvorbislibrary = Sys_LoadLibrary("libvorbisfile.so.3", funcs);
|
|
if (!oggvorbislibrary)
|
|
oggvorbislibrary = Sys_LoadLibrary("libvorbisfile", funcs);
|
|
if (!oggvorbislibrary)
|
|
Con_Printf("Couldn't load library: \"libvorbisfile\".\n");
|
|
}
|
|
#endif
|
|
|
|
tried = true;
|
|
|
|
if (!oggvorbislibrary)
|
|
{
|
|
Con_Printf("ogg vorbis library is not loaded.\n");
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
buffer->start = start;
|
|
buffer->length = length;
|
|
buffer->pos = 0;
|
|
if (p_ov_open_callbacks(buffer, &buffer->vf, NULL, 0, callbacks))
|
|
{
|
|
Con_Printf("Input %s does not appear to be an Ogg Vorbis bitstream.\n", buffer->s->name);
|
|
return false;
|
|
}
|
|
|
|
/* Print the comments plus a few lines about the bitstream we're
|
|
decoding */
|
|
{
|
|
// char **ptr=p_ov_comment(&buffer->vf,-1)->user_comments;
|
|
vorbis_info *vi=p_ov_info(&buffer->vf,-1);
|
|
|
|
if (vi->channels < 1 || vi->channels > 2)
|
|
{
|
|
p_ov_clear (&buffer->vf);
|
|
Con_Printf("Input %s has %i channels.\n", buffer->s->name, vi->channels);
|
|
return false;
|
|
}
|
|
|
|
buffer->srcchannels = vi->channels;
|
|
buffer->srcspeed = vi->rate;
|
|
/*
|
|
while(*ptr){
|
|
Con_Printf("%s\n",*ptr);
|
|
ptr++;
|
|
}
|
|
Con_Printf("\nBitstream is %d channel, %ldHz\n",vi->channels,vi->rate);
|
|
Con_Printf("\nDecoded length: %ld samples\n",
|
|
(long)p_ov_pcm_total(&buffer->vf,-1));
|
|
Con_Printf("Encoded by: %s\n\n",p_ov_comment(&buffer->vf,-1)->vendor);
|
|
*/ }
|
|
buffer->tempbuffer = NULL;
|
|
buffer->tempbufferbytes = 0;
|
|
|
|
buffer->start = BZ_Malloc(length);
|
|
memcpy(buffer->start, start, length);
|
|
|
|
return true;
|
|
}
|
|
#endif
|
|
|