cvar_t scr_screenshot_jpeg = {CVAR_SAVE, "scr_screenshot_jpeg","0"};
cvar_t scr_screenshot_jpeg_quality = {CVAR_SAVE, "scr_screenshot_jpeg_quality","0.9"};
cvar_t scr_screenshot_name = {0, "scr_screenshot_name","dp"};
-cvar_t cl_avidemo = {0, "cl_avidemo", "0"};
+cvar_t cl_capturevideo = {0, "cl_capturevideo", "0"};
+cvar_t cl_capturevideo_fps = {0, "cl_capturevideo_fps", "30"};
+cvar_t cl_capturevideo_raw = {0, "cl_capturevideo_raw", "0"};
cvar_t r_textshadow = {0, "r_textshadow", "0"};
cvar_t r_letterbox = {0, "r_letterbox", "0"};
Cvar_RegisterVariable (&vid_pixelaspect);
Cvar_RegisterVariable (&scr_screenshot_jpeg);
Cvar_RegisterVariable (&scr_screenshot_jpeg_quality);
- Cvar_RegisterVariable (&cl_avidemo);
+ Cvar_RegisterVariable (&cl_capturevideo);
+ Cvar_RegisterVariable (&cl_capturevideo_fps);
+ Cvar_RegisterVariable (&cl_capturevideo_raw);
Cvar_RegisterVariable (&r_textshadow);
Cvar_RegisterVariable (&r_letterbox);
shotnumber++;
}
-static int cl_avidemo_frame = 0;
+typedef enum capturevideoformat_e
+{
+ CAPTUREVIDEOFORMAT_TARGA,
+ CAPTUREVIDEOFORMAT_JPEG,
+ CAPTUREVIDEOFORMAT_RAW
+}
+capturevideoformat_t;
+
+qboolean cl_capturevideo_active = false;
+capturevideoformat_t cl_capturevideo_format;
+static double cl_capturevideo_starttime = 0;
+double cl_capturevideo_framerate = 0;
+static int cl_capturevideo_soundrate = 0;
+static int cl_capturevideo_frame = 0;
+static qbyte *cl_capturevideo_buffer = NULL;
+static qfile_t *cl_capturevideo_videofile = NULL;
+static qfile_t *cl_capturevideo_soundfile = NULL;
+
+void SCR_CaptureVideo_BeginVideo(void)
+{
+ qbyte out[44];
+ if (cl_capturevideo_active)
+ return;
+ // soundrate is figured out on the first SoundFrame
+ cl_capturevideo_active = true;
+ cl_capturevideo_starttime = Sys_DoubleTime();
+ cl_capturevideo_framerate = bound(1, cl_capturevideo_fps.value, 1000);
+ cl_capturevideo_soundrate = 0;
+ cl_capturevideo_frame = 0;
+ cl_capturevideo_buffer = Mem_Alloc(tempmempool, vid.realwidth * vid.realheight * (3+3+3) + 18);
+
+ if (cl_capturevideo_raw.integer)
+ {
+ cl_capturevideo_format = CAPTUREVIDEOFORMAT_RAW;
+ cl_capturevideo_videofile = FS_Open ("video/dpvideo.rgb", "wb", false);
+ }
+ else if (scr_screenshot_jpeg.integer)
+ {
+ cl_capturevideo_format = CAPTUREVIDEOFORMAT_JPEG;
+ cl_capturevideo_videofile = NULL;
+ }
+ else
+ {
+ cl_capturevideo_format = CAPTUREVIDEOFORMAT_TARGA;
+ cl_capturevideo_videofile = NULL;
+ }
-void SCR_CaptureAVIDemo(void)
+ cl_capturevideo_soundfile = FS_Open ("video/dpvideo.wav", "wb", false);
+
+ // wave header will be filled out when video ends
+ memset(out, 0, 44);
+ FS_Write (cl_capturevideo_soundfile, out, 44);
+}
+
+void SCR_CaptureVideo_EndVideo(void)
{
- static qbyte *avi_buffer1 = NULL;
- static qbyte *avi_buffer2 = NULL;
- static qbyte *avi_buffer3 = NULL;
- char filename[32];
- qboolean jpeg = (scr_screenshot_jpeg.integer != 0);
+ int i, n;
+ qbyte out[44];
+ if (!cl_capturevideo_active)
+ return;
+ cl_capturevideo_active = false;
- if (!cl_avidemo.integer)
+ if (cl_capturevideo_videofile)
{
- if (avi_buffer1 != NULL)
- {
- Mem_Free (avi_buffer1);
- Mem_Free (avi_buffer2);
- Mem_Free (avi_buffer3);
- avi_buffer1 = NULL;
- avi_buffer2 = NULL;
- avi_buffer3 = NULL;
- }
- cl_avidemo_frame = 0;
- return;
+ FS_Close(cl_capturevideo_videofile);
+ cl_capturevideo_videofile = NULL;
+ }
+
+ // finish the wave file
+ if (cl_capturevideo_soundfile)
+ {
+ i = FS_Tell (cl_capturevideo_soundfile);
+ //"RIFF", (int) unknown (chunk size), "WAVE",
+ //"fmt ", (int) 16 (chunk size), (short) format 1 (uncompressed PCM), (short) 2 channels, (int) unknown rate, (int) unknown bytes per second, (short) 4 bytes per sample (channels * bytes per channel), (short) 16 bits per channel
+ //"data", (int) unknown (chunk size)
+ memcpy (out, "RIFF****WAVEfmt \x10\x00\x00\x00\x01\x00\x02\x00********\x04\x00\x10\0data****", 44);
+ // the length of the whole RIFF chunk
+ n = i - 8;
+ out[4] = (n) & 0xFF;
+ out[5] = (n >> 8) & 0xFF;
+ out[6] = (n >> 16) & 0xFF;
+ out[7] = (n >> 24) & 0xFF;
+ // rate
+ n = cl_capturevideo_soundrate;
+ out[24] = (n) & 0xFF;
+ out[25] = (n >> 8) & 0xFF;
+ out[26] = (n >> 16) & 0xFF;
+ out[27] = (n >> 24) & 0xFF;
+ // bytes per second (rate * channels * bytes per channel)
+ n = cl_capturevideo_soundrate * 2 * 2;
+ out[28] = (n) & 0xFF;
+ out[29] = (n >> 8) & 0xFF;
+ out[30] = (n >> 16) & 0xFF;
+ out[31] = (n >> 24) & 0xFF;
+ // the length of the data chunk
+ n = i - 44;
+ out[40] = (n) & 0xFF;
+ out[41] = (n >> 8) & 0xFF;
+ out[42] = (n >> 16) & 0xFF;
+ out[43] = (n >> 24) & 0xFF;
+ FS_Seek (cl_capturevideo_soundfile, 0, SEEK_SET);
+ FS_Write (cl_capturevideo_soundfile, out, 44);
+ FS_Close (cl_capturevideo_soundfile);
+ cl_capturevideo_soundfile = NULL;
}
- if (avi_buffer1 == NULL)
+ if (cl_capturevideo_buffer)
{
- avi_buffer1 = Mem_Alloc(tempmempool, vid.realwidth * vid.realheight * 3);
- avi_buffer2 = Mem_Alloc(tempmempool, vid.realwidth * vid.realheight * 3);
- avi_buffer3 = Mem_Alloc(tempmempool, vid.realwidth * vid.realheight * 3 + 18);
+ Mem_Free (cl_capturevideo_buffer);
+ cl_capturevideo_buffer = NULL;
}
- sprintf(filename, "video/dp%06d.%s", cl_avidemo_frame, jpeg ? "jpg" : "tga");
+ cl_capturevideo_starttime = 0;
+ cl_capturevideo_framerate = 0;
+ cl_capturevideo_frame = 0;
+}
- if (SCR_ScreenShot(filename, avi_buffer1, avi_buffer2, avi_buffer3, vid.realx, vid.realy, vid.realwidth, vid.realheight, false, false, false, jpeg))
- cl_avidemo_frame++;
- else
+qboolean SCR_CaptureVideo_VideoFrame(void)
+{
+ int x = vid.realx, y = vid.realy, width = vid.realwidth, height = vid.realheight;
+ char filename[32];
+ //return SCR_ScreenShot(filename, cl_capturevideo_buffer, cl_capturevideo_buffer + vid.realwidth * vid.realheight * 3, cl_capturevideo_buffer + vid.realwidth * vid.realheight * 6, vid.realx, vid.realy, vid.realwidth, vid.realheight, false, false, false, jpeg);
+ // speed is critical here, so do saving as directly as possible
+ if (!r_render.integer)
+ return false;
+ switch (cl_capturevideo_format)
+ {
+ case CAPTUREVIDEOFORMAT_RAW:
+ qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
+ CHECKGLERROR
+ return FS_Write (cl_capturevideo_videofile, cl_capturevideo_buffer, width*height*3);
+ case CAPTUREVIDEOFORMAT_JPEG:
+ sprintf(filename, "video/dp%06d.jpg", cl_capturevideo_frame);
+ qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
+ CHECKGLERROR
+ return JPEG_SaveImage_preflipped (filename, width, height, cl_capturevideo_buffer);
+ case CAPTUREVIDEOFORMAT_TARGA:
+ //return Image_WriteTGARGB_preflipped (filename, width, height, cl_capturevideo_buffer, cl_capturevideo_buffer + vid.realwidth * vid.realheight * 3, );
+ memset (cl_capturevideo_buffer, 0, 18);
+ cl_capturevideo_buffer[2] = 2; // uncompressed type
+ cl_capturevideo_buffer[12] = (width >> 0) & 0xFF;
+ cl_capturevideo_buffer[13] = (width >> 8) & 0xFF;
+ cl_capturevideo_buffer[14] = (height >> 0) & 0xFF;
+ cl_capturevideo_buffer[15] = (height >> 8) & 0xFF;
+ cl_capturevideo_buffer[16] = 24; // pixel size
+ qglReadPixels (x, y, width, height, GL_BGR, GL_UNSIGNED_BYTE, cl_capturevideo_buffer + 18);
+ CHECKGLERROR
+ sprintf(filename, "video/dp%06d.tga", cl_capturevideo_frame);
+ return FS_WriteFile (filename, cl_capturevideo_buffer, width*height*3 + 18);
+ default:
+ return false;
+ }
+}
+
+void SCR_CaptureVideo_SoundFrame(qbyte *bufstereo16le, size_t length, int rate)
+{
+ cl_capturevideo_soundrate = rate;
+ if (FS_Write (cl_capturevideo_soundfile, bufstereo16le, 4 * length) < 4 * length)
+ {
+ Cvar_SetValueQuick(&cl_capturevideo, 0);
+ Con_Printf("video sound saving failed on frame %i, out of disk space? stopping video capture.\n", cl_capturevideo_frame);
+ SCR_CaptureVideo_EndVideo();
+ }
+}
+
+void SCR_CaptureVideo(void)
+{
+ int newframenum;
+ if (cl_capturevideo.integer)
{
- Cvar_SetValueQuick(&cl_avidemo, 0);
- Con_Printf("avi saving failed on frame %i, out of disk space? stopping avi demo capture.\n", cl_avidemo_frame);
- cl_avidemo_frame = 0;
+ if (!cl_capturevideo_active)
+ SCR_CaptureVideo_BeginVideo();
+ if (cl_capturevideo_framerate != cl_capturevideo_fps.value)
+ {
+ Con_Printf("You can not change the video framerate while recording a video.\n");
+ Cvar_SetValueQuick(&cl_capturevideo_fps, cl_capturevideo_framerate);
+ }
+ newframenum = (Sys_DoubleTime() - cl_capturevideo_starttime) * cl_capturevideo_framerate;
+ while (cl_capturevideo_frame < newframenum)
+ {
+ if (SCR_CaptureVideo_VideoFrame())
+ cl_capturevideo_frame++;
+ else
+ {
+ Cvar_SetValueQuick(&cl_capturevideo, 0);
+ Con_Printf("video saving failed on frame %i, out of disk space? stopping avi demo capture.\n", cl_capturevideo_frame);
+ SCR_CaptureVideo_EndVideo();
+ break;
+ }
+ }
}
+ else if (cl_capturevideo_active)
+ SCR_CaptureVideo_EndVideo();
}
/*
if (!scr_initialized || !con_initialized || vid_hidden)
return; // not initialized yet
- SCR_CaptureAVIDemo();
+ SCR_CaptureVideo();
if (cls.signon == SIGNONS)
R_TimeReport("other");
#define GL_SHADING_LANGUAGE_VERSION_ARB 0x8B8C
#endif
+// GL_EXT_bgr
+#define GL_BGR 0x80E0
+
+// GL_EXT_bgra
+#define GL_BGRA 0x80E1
+
#define DEBUGGL
#ifdef DEBUGGL
cvar_t host_speeds = {0, "host_speeds","0"};
// LordHavoc: framerate independent slowmo
cvar_t slowmo = {0, "slowmo", "1.0"};
-// LordHavoc: game logic lower cap on framerate (if framerate is below this is, it pretends it is this, so game logic will run normally)
-cvar_t host_minfps = {CVAR_SAVE, "host_minfps", "10"};
// LordHavoc: framerate upper cap
-cvar_t host_maxfps = {CVAR_SAVE, "host_maxfps", "1000"};
+cvar_t cl_maxfps = {CVAR_SAVE, "cl_maxfps", "1000"};
// print broadcast messages in dedicated mode
cvar_t sv_echobprint = {CVAR_SAVE, "sv_echobprint", "1"};
Cvar_RegisterVariable (&host_framerate);
Cvar_RegisterVariable (&host_speeds);
Cvar_RegisterVariable (&slowmo);
- Cvar_RegisterVariable (&host_minfps);
- Cvar_RegisterVariable (&host_maxfps);
+ Cvar_RegisterVariable (&cl_maxfps);
Cvar_RegisterVariable (&sv_echobprint);
Returns false if the time is too short to run a frame
===================
*/
-extern cvar_t cl_avidemo;
+extern qboolean cl_capturevideo_active;
+extern double cl_capturevideo_framerate;
qboolean Host_FilterTime (double time)
{
double timecap, timeleft;
realtime += time;
- if (slowmo.value < 0.0f)
- Cvar_SetValue("slowmo", 0.0f);
- if (host_minfps.value < 10.0f)
- Cvar_SetValue("host_minfps", 10.0f);
- if (host_maxfps.value < host_minfps.value)
- Cvar_SetValue("host_maxfps", host_minfps.value);
- if (cl_avidemo.value < 0.1f && cl_avidemo.value != 0.0f)
- Cvar_SetValue("cl_avidemo", 0.0f);
+ if (sys_ticrate.value < 0.01 || sys_ticrate.value > 0.1)
+ Cvar_SetValue("sys_ticrate", bound(0.01, sys_ticrate.value, 0.1));
+ if (slowmo.value < 0)
+ Cvar_SetValue("slowmo", 0);
+ if (host_framerate.value < 0.00001 && host_framerate.value != 0)
+ Cvar_SetValue("host_framerate", 0);
+ if (cl_maxfps.value < 1)
+ Cvar_SetValue("cl_maxfps", 1);
- // check if framerate is too high
- if (!cls.timedemo)
+ if (cls.timedemo)
{
- // default to sys_ticrate (server framerate - presumably low) unless we
- // have a good reason to run faster
+ // disable time effects during timedemo
+ cl.frametime = host_realframetime = host_frametime = realtime - oldrealtime;
+ oldrealtime = realtime;
+ return true;
+ }
+
+ // check if framerate is too high
+ // default to sys_ticrate (server framerate - presumably low) unless we
+ // have a good reason to run faster
+ timecap = host_framerate.value;
+ if (!timecap)
timecap = sys_ticrate.value;
- if (cls.state != ca_dedicated)
- {
- if (cl_avidemo.value >= 0.1f)
- timecap = 1.0 / (double)cl_avidemo.value;
- else if (vid_activewindow)
- timecap = 1.0 / host_maxfps.value;
- }
+ if (cls.state != ca_dedicated)
+ {
+ if (cl_capturevideo_active)
+ timecap = 1.0 / cl_capturevideo_framerate;
+ else if (vid_activewindow)
+ timecap = 1.0 / cl_maxfps.value;
+ }
- timeleft = oldrealtime + timecap - realtime;
- if (timeleft > 0)
- {
- // don't totally hog the CPU
- if (timeleft >= 0.02)
- Sys_Sleep((int)(timeleft * 1000) - 5);
- return false;
- }
+ timeleft = (oldrealtime - realtime) + timecap;
+ if (timeleft > 0)
+ {
+ // don't totally hog the CPU
+ if (timeleft >= 0.03)
+ Sys_Sleep((int)(timeleft * 1000) - 10);
+ return false;
}
// LordHavoc: copy into host_realframetime as well
host_realframetime = host_frametime = realtime - oldrealtime;
oldrealtime = realtime;
- if (cls.timedemo)
- {
- // disable time effects
- cl.frametime = host_frametime;
- return true;
- }
+ // apply slowmo scaling
+ host_frametime *= slowmo.value;
- if (host_framerate.value > 0)
+ // host_framerate overrides all else
+ if (host_framerate.value)
host_frametime = host_framerate.value;
- else if (cl_avidemo.value >= 0.1f)
- host_frametime = (1.0 / cl_avidemo.value);
- else
- {
- // don't allow really short frames
- if (host_frametime > (1.0 / host_minfps.value))
- host_frametime = (1.0 / host_minfps.value);
- }
- cl.frametime = host_frametime = bound(0, host_frametime * slowmo.value, 0.1f); // LordHavoc: the QC code relies on no less than 10fps
+ cl.frametime = host_frametime;
return true;
}
*/
void Host_ServerFrame (void)
{
+ double advancetime;
static double frametimetotal = 0, lastservertime = 0;
frametimetotal += host_frametime;
// LordHavoc: cap server at sys_ticrate in networked games
- if (!cl.islocalgame && ((realtime - lastservertime) < sys_ticrate.value))
+ if (frametimetotal < 0.001 || (!cl.islocalgame && cls.state == ca_connected && sv.active && ((realtime - lastservertime) < sys_ticrate.value)))
return;
-
- NetConn_ServerFrame();
-
-// run the world state
- if (!sv.paused && (!cl.islocalgame || (key_dest == key_game && !key_consoleactive)))
- sv.frametime = pr_global_struct->frametime = frametimetotal;
- else
- sv.frametime = 0;
- frametimetotal = 0;
lastservertime = realtime;
-// set the time and clear the general datagram
+ // set the time and clear the general datagram
SV_ClearDatagram();
-// read client messages
- SV_RunClients();
+ // run the world state
+ // don't allow simulation to run too fast or too slow or logic glitches can occur
+ while (frametimetotal > 0)
+ {
+ advancetime = min(frametimetotal, sys_ticrate.value);
+ frametimetotal = frametimetotal - advancetime;
+
+ // only advance time if not paused
+ // the game also pauses in singleplayer when menu or console is used
+ if (!sv.paused && (!cl.islocalgame || (key_dest == key_game && !key_consoleactive)))
+ sv.frametime = advancetime;
+ else
+ sv.frametime = 0;
+
+ pr_global_struct->frametime = sv.frametime;
-// move things around and think
-// always pause in single player if in console or menus
- if (sv.frametime)
- SV_Physics();
+ // check for network packets to the server each world step incase they
+ // come in midframe (particularly if host is running really slow)
+ NetConn_ServerFrame();
-// send all messages to the clients
+ // read client messages
+ SV_RunClients();
+
+ // move things around and think unless paused
+ if (sv.frametime)
+ SV_Physics();
+ }
+
+ // send all messages to the clients
SV_SendClientMessages();
-// send an heartbeat if enough time has passed since the last one
+ // send an heartbeat if enough time has passed since the last one
NetConn_Heartbeat(0);
}
if (setjmp(host_abortserver))
return; // something bad happened, or the server disconnected
- // keep the random time dependent
- rand();
-
// decide the simulation time
if (!Host_FilterTime(time))
return;
+ // keep the random time dependent
+ rand();
+
cl.islocalgame = NetConn_IsLocalGame();
// get new key events
#define PAINTBUFFER_SIZE 2048
portable_samplepair_t paintbuffer[PAINTBUFFER_SIZE];
-// FIXME: it desyncs with the video too easily
-extern cvar_t cl_avidemo;
-static qfile_t *cl_avidemo_soundfile = NULL;
+// FIXME: this desyncs with the video too easily
+extern qboolean cl_capturevideo_active;
+extern void SCR_CaptureVideo_SoundFrame(qbyte *bufstereo16le, size_t length, int rate);
void S_CaptureAVISound(portable_samplepair_t *buf, size_t length)
{
int n;
size_t i;
qbyte out[PAINTBUFFER_SIZE * 4];
-
- if (cl_avidemo.value >= 0.1f)
- {
- if (cl_avidemo_soundfile == NULL)
- {
- cl_avidemo_soundfile = FS_Open ("video/dpavi.wav", "wb", false);
- memset(out, 0, 44);
- FS_Write (cl_avidemo_soundfile, out, 44);
- // header will be filled out when file is closed
- }
- FS_Seek (cl_avidemo_soundfile, 0, SEEK_END);
- // write the sound buffer as little endian 16bit interleaved stereo
- for(i = 0;i < length;i++)
- {
- n = buf[i].left >> 2; // quiet enough to prevent clipping most of the time
- n = bound(-32768, n, 32767);
- out[i*4+0] = n & 0xFF;
- out[i*4+1] = (n >> 8) & 0xFF;
- n = buf[i].right >> 2; // quiet enough to prevent clipping most of the time
- n = bound(-32768, n, 32767);
- out[i*4+2] = n & 0xFF;
- out[i*4+3] = (n >> 8) & 0xFF;
- }
- if (FS_Write (cl_avidemo_soundfile, out, 4 * length) < 4 * length)
- {
- Cvar_SetValueQuick(&cl_avidemo, 0);
- Con_Print("avi saving sound failed, out of disk space? stopping avi demo capture.\n");
- }
- }
- else if (cl_avidemo_soundfile)
+ if (!cl_capturevideo_active)
+ return;
+ // write the sound buffer as little endian 16bit interleaved stereo
+ for(i = 0;i < length;i++)
{
- // file has not been closed yet, close it
- FS_Seek (cl_avidemo_soundfile, 0, SEEK_END);
- i = FS_Tell (cl_avidemo_soundfile);
-
- //"RIFF", (int) unknown (chunk size), "WAVE",
- //"fmt ", (int) 16 (chunk size), (short) format 1 (uncompressed PCM), (short) 2 channels, (int) unknown rate, (int) unknown bytes per second, (short) 4 bytes per sample (channels * bytes per channel), (short) 16 bits per channel
- //"data", (int) unknown (chunk size)
- memcpy (out, "RIFF****WAVEfmt \x10\x00\x00\x00\x01\x00\x02\x00********\x04\x00\x10\0data****", 44);
- // the length of the whole RIFF chunk
- n = i - 8;
- out[4] = (n) & 0xFF;
- out[5] = (n >> 8) & 0xFF;
- out[6] = (n >> 16) & 0xFF;
- out[7] = (n >> 24) & 0xFF;
- // rate
- n = shm->format.speed;
- out[24] = (n) & 0xFF;
- out[25] = (n >> 8) & 0xFF;
- out[26] = (n >> 16) & 0xFF;
- out[27] = (n >> 24) & 0xFF;
- // bytes per second (rate * channels * bytes per channel)
- n = shm->format.speed * 2 * 2;
- out[28] = (n) & 0xFF;
- out[29] = (n >> 8) & 0xFF;
- out[30] = (n >> 16) & 0xFF;
- out[31] = (n >> 24) & 0xFF;
- // the length of the data chunk
- n = i - 44;
- out[40] = (n) & 0xFF;
- out[41] = (n >> 8) & 0xFF;
- out[42] = (n >> 16) & 0xFF;
- out[43] = (n >> 24) & 0xFF;
-
- FS_Seek (cl_avidemo_soundfile, 0, SEEK_SET);
- FS_Write (cl_avidemo_soundfile, out, 44);
- FS_Close (cl_avidemo_soundfile);
- cl_avidemo_soundfile = NULL;
+ n = buf[i].left >> 2; // quiet enough to prevent clipping most of the time
+ n = bound(-32768, n, 32767);
+ out[i*4+0] = n & 0xFF;
+ out[i*4+1] = (n >> 8) & 0xFF;
+ n = buf[i].right >> 2; // quiet enough to prevent clipping most of the time
+ n = bound(-32768, n, 32767);
+ out[i*4+2] = n & 0xFF;
+ out[i*4+3] = (n >> 8) & 0xFF;
}
+ SCR_CaptureVideo_SoundFrame(out, length, shm->format.speed);
}
// TODO: rewrite this function