]> git.xonotic.org Git - xonotic/darkplaces.git/commitdiff
new option -capturedemo to capture a demo to an AVI file;
authordivverent <divverent@d7cf8633-e32d-0410-b094-e92efae38249>
Sun, 20 May 2007 14:59:03 +0000 (14:59 +0000)
committerdivverent <divverent@d7cf8633-e32d-0410-b094-e92efae38249>
Sun, 20 May 2007 14:59:03 +0000 (14:59 +0000)
new cvars cl_capturevideo_width, cl_capturevideo_height to scale down video capture
when capturing >1GB, don't write an index chunk (in that case, an ix00/ix01 chunk has to be written, but that's not yet implemented)
when capturing, write a "dmlh" header to store the total number of frames, and store the partial number of frames in the "avih" header, as specified by OpenDML
when capturing, store the aspect ratio in a "vprp" header chunk (OpenDML)
make Host_Quit_f do nothing when already quitting (fixes "quit" issue while playing back a demo); note that Host_Quit_f is only used by the console and by -benchmark/-demo/-capturedemo

git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@7321 d7cf8633-e32d-0410-b094-e92efae38249

cl_demo.c
cl_main.c
cl_screen.c
cl_screen.h
client.h
host.c
host_cmd.c

index 27761778f689a7ca4d0cd0a6c2053ea99d5b4898..2485afb908de566dd40700ba0af69b276bff5918 100644 (file)
--- a/cl_demo.c
+++ b/cl_demo.c
@@ -20,6 +20,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
 #include "quakedef.h"
 
+extern cvar_t cl_capturevideo;
 int old_vsync = 0;
 
 void CL_FinishTimeDemo (void);
@@ -87,7 +88,7 @@ void CL_StopPlayback (void)
        if (cls.timedemo)
                CL_FinishTimeDemo ();
 
-       if (COM_CheckParm("-demo"))
+       if (COM_CheckParm("-demo") || COM_CheckParm("-capturedemo"))
                Host_Quit_f();
 
 }
index d07b186a66563abaec53368c1e03fe2bc8267b6e..8cba25a7d602e8be5cad11550323dba3af94d38a 100644 (file)
--- a/cl_main.c
+++ b/cl_main.c
@@ -2146,6 +2146,7 @@ CL_Shutdown
 */
 void CL_Shutdown (void)
 {
+       CL_Screen_Shutdown();
        CL_Particles_Shutdown();
        CL_Parse_Shutdown();
 
index 4768f4b7d6b46c55c4b9491b14e1395ef50c33d9..b338efe564fe9f641114aff7e040b39fd244aed7 100644 (file)
@@ -30,6 +30,8 @@ cvar_t scr_screenshot_jpeg_quality = {CVAR_SAVE, "scr_screenshot_jpeg_quality","
 cvar_t scr_screenshot_gammaboost = {CVAR_SAVE, "scr_screenshot_gammaboost","1", "gamma correction on saved screenshots and videos, 1.0 saves unmodified images"};
 // scr_screenshot_name is defined in fs.c
 cvar_t cl_capturevideo = {0, "cl_capturevideo", "0", "enables saving of video to a .avi file using uncompressed I420 colorspace and PCM audio, note that scr_screenshot_gammaboost affects the brightness of the output)"};
+cvar_t cl_capturevideo_width = {0, "cl_capturevideo_width", "0", "scales all frames to this resolution before saving the video"};
+cvar_t cl_capturevideo_height = {0, "cl_capturevideo_height", "0", "scales all frames to this resolution before saving the video"};
 cvar_t cl_capturevideo_realtime = {0, "cl_capturevideo_realtime", "0", "causes video saving to operate in realtime (mostly useful while playing, not while capturing demos), this can produce a much lower quality video due to poor sound/video sync and will abort saving if your machine stalls for over 1 second"};
 cvar_t cl_capturevideo_fps = {0, "cl_capturevideo_fps", "30", "how many frames per second to save (29.97 for NTSC, 30 for typical PC video, 15 can be useful)"};
 cvar_t cl_capturevideo_number = {CVAR_SAVE, "cl_capturevideo_number", "1", "number to append to video filename, incremented each time a capture begins"};
@@ -719,6 +721,12 @@ void SCR_SizeDown_f (void)
        Cvar_SetValue ("viewsize",scr_viewsize.value-10);
 }
 
+void SCR_CaptureVideo_EndVideo(void);
+void CL_Screen_Shutdown(void)
+{
+       SCR_CaptureVideo_EndVideo();
+}
+
 void CL_Screen_Init(void)
 {
        Cvar_RegisterVariable (&scr_fov);
@@ -740,6 +748,8 @@ void CL_Screen_Init(void)
        Cvar_RegisterVariable (&scr_screenshot_jpeg_quality);
        Cvar_RegisterVariable (&scr_screenshot_gammaboost);
        Cvar_RegisterVariable (&cl_capturevideo);
+       Cvar_RegisterVariable (&cl_capturevideo_width);
+       Cvar_RegisterVariable (&cl_capturevideo_height);
        Cvar_RegisterVariable (&cl_capturevideo_realtime);
        Cvar_RegisterVariable (&cl_capturevideo_fps);
        Cvar_RegisterVariable (&cl_capturevideo_number);
@@ -930,49 +940,110 @@ static void SCR_CaptureVideo_RIFF_IndexEntry(const char *chunkfourcc, int chunks
        MSG_WriteLong(&cls.capturevideo.riffindexbuffer, chunksize);
 }
 
-static void SCR_CaptureVideo_RIFF_Finish(void)
+static void SCR_CaptureVideo_RIFF_Finish(qboolean final)
 {
        // close the "movi" list
        SCR_CaptureVideo_RIFF_Pop();
        // write the idx1 chunk that we've been building while saving the frames
-       SCR_CaptureVideo_RIFF_Push("idx1", NULL);
-       SCR_CaptureVideo_RIFF_WriteBytes(cls.capturevideo.riffindexbuffer.data, cls.capturevideo.riffindexbuffer.cursize);
-       SCR_CaptureVideo_RIFF_Pop();
+       if(final && cls.capturevideo.videofile_firstchunkframes_offset)
+       // TODO replace index creating by OpenDML ix##/##ix/indx chunk so it works for more than one AVI part too
+       {
+               SCR_CaptureVideo_RIFF_Push("idx1", NULL);
+               SCR_CaptureVideo_RIFF_WriteBytes(cls.capturevideo.riffindexbuffer.data, cls.capturevideo.riffindexbuffer.cursize);
+               SCR_CaptureVideo_RIFF_Pop();
+       }
        cls.capturevideo.riffindexbuffer.cursize = 0;
        // pop the RIFF chunk itself
        while (cls.capturevideo.riffstacklevel > 0)
                SCR_CaptureVideo_RIFF_Pop();
        SCR_CaptureVideo_RIFF_Flush();
+       if(cls.capturevideo.videofile_firstchunkframes_offset)
+       {
+               Con_DPrintf("Finishing first chunk (%d frames)\n", cls.capturevideo.frame);
+               FS_Seek(cls.capturevideo.videofile, cls.capturevideo.videofile_firstchunkframes_offset, SEEK_SET);
+               SCR_CaptureVideo_RIFF_Write32(cls.capturevideo.frame);
+               SCR_CaptureVideo_RIFF_Flush();
+               FS_Seek(cls.capturevideo.videofile, 0, SEEK_END);
+               cls.capturevideo.videofile_firstchunkframes_offset = 0;
+       }
+       else
+               Con_DPrintf("Finishing another chunk (%d frames)\n", cls.capturevideo.frame);
 }
 
 static void SCR_CaptureVideo_RIFF_OverflowCheck(int framesize)
 {
-       fs_offset_t cursize;
+       fs_offset_t cursize, curfilesize;
        if (cls.capturevideo.riffstacklevel != 2)
                Sys_Error("SCR_CaptureVideo_RIFF_OverflowCheck: chunk stack leakage!\n");
        // check where we are in the file
        SCR_CaptureVideo_RIFF_Flush();
        cursize = SCR_CaptureVideo_RIFF_GetPosition() - cls.capturevideo.riffstackstartoffset[0];
+       curfilesize = SCR_CaptureVideo_RIFF_GetPosition();
+
        // if this would overflow the windows limit of 1GB per RIFF chunk, we need
        // to close the current RIFF chunk and open another for future frames
        if (8 + cursize + framesize + cls.capturevideo.riffindexbuffer.cursize + 8 > 1<<30)
        {
-               SCR_CaptureVideo_RIFF_Finish();
+               SCR_CaptureVideo_RIFF_Finish(false);
                // begin a new 1GB extended section of the AVI
                SCR_CaptureVideo_RIFF_Push("RIFF", "AVIX");
                SCR_CaptureVideo_RIFF_Push("LIST", "movi");
        }
 }
 
+static void FindFraction(double val, int *num, int *denom, int denomMax)
+{
+       int i;
+       double bestdiff;
+       // initialize
+       bestdiff = fabs(val);
+       *num = 0;
+       *denom = 1;
+
+       for(i = 1; i <= denomMax; ++i)
+       {
+               int inum = floor(0.5 + val * i);
+               double diff = fabs(val - inum / (double)i);
+               if(diff < bestdiff)
+               {
+                       bestdiff = diff;
+                       *num = inum;
+                       *denom = i;
+               }
+       }
+}
+
 void SCR_CaptureVideo_BeginVideo(void)
 {
-       double gamma, g;
-       int width = vid.width, height = vid.height, x;
+       double gamma, g, aspect;
+       int width = cl_capturevideo_width.integer, height = cl_capturevideo_height.integer;
+       int n, d;
        unsigned int i;
        if (cls.capturevideo.active)
                return;
        memset(&cls.capturevideo, 0, sizeof(cls.capturevideo));
        // soundrate is figured out on the first SoundFrame
+
+       if(width == 0 && height != 0)
+               width = (int) (height * (double)vid.width / ((double)vid.height * vid_pixelheight.value)); // keep aspect
+       if(width != 0 && height == 0)
+               height = (int) (width * ((double)vid.height * vid_pixelheight.value) / (double)vid.width); // keep aspect
+
+       if(width < 2 || width > vid.width) // can't scale up
+               width = vid.width;
+       if(height < 2 || height > vid.height) // can't scale up
+               height = vid.height;
+
+       aspect = vid.width / (vid.height * vid_pixelheight.value);
+
+       // ensure it's all even; if not, scale down a little
+       if(width % 1)
+               --width;
+       if(height % 1)
+               --height;
+
+       cls.capturevideo.width = width;
+       cls.capturevideo.height = height;
        cls.capturevideo.active = true;
        cls.capturevideo.starttime = realtime;
        cls.capturevideo.framerate = bound(1, cl_capturevideo_fps.value, 1000);
@@ -980,7 +1051,8 @@ void SCR_CaptureVideo_BeginVideo(void)
        cls.capturevideo.frame = 0;
        cls.capturevideo.soundsampleframe = 0;
        cls.capturevideo.realtime = cl_capturevideo_realtime.integer != 0;
-       cls.capturevideo.buffer = (unsigned char *)Mem_Alloc(tempmempool, vid.width * vid.height * (3+3+3) + 18);
+       cls.capturevideo.screenbuffer = (unsigned char *)Mem_Alloc(tempmempool, vid.width * vid.height * 3);
+       cls.capturevideo.outbuffer = (unsigned char *)Mem_Alloc(tempmempool, width * height * (3+3+3) + 18);
        gamma = 1.0/scr_screenshot_gammaboost.value;
        dpsnprintf(cls.capturevideo.basename, sizeof(cls.capturevideo.basename), "video/dpvideo%03i", cl_capturevideo_number.integer);
        Cvar_SetValueQuick(&cl_capturevideo_number, cl_capturevideo_number.integer + 1);
@@ -1040,7 +1112,7 @@ Cr = R *  .500 + G * -.419 + B * -.0813 + 128.;
                SCR_CaptureVideo_RIFF_Write32(0); // max bytes per second
                SCR_CaptureVideo_RIFF_Write32(0); // padding granularity
                SCR_CaptureVideo_RIFF_Write32(0x910); // flags (AVIF_HASINDEX | AVIF_ISINTERLEAVED | AVIF_TRUSTCKTYPE)
-               cls.capturevideo.videofile_totalframes_offset1 = SCR_CaptureVideo_RIFF_GetPosition();
+               cls.capturevideo.videofile_firstchunkframes_offset = SCR_CaptureVideo_RIFF_GetPosition();
                SCR_CaptureVideo_RIFF_Write32(0); // total frames
                SCR_CaptureVideo_RIFF_Write32(0); // initial frames
                if (cls.capturevideo.soundrate)
@@ -1064,13 +1136,11 @@ Cr = R *  .500 + G * -.419 + B * -.0813 + 128.;
                SCR_CaptureVideo_RIFF_Write16(0); // language
                SCR_CaptureVideo_RIFF_Write32(0); // initial frames
                // find an ideal divisor for the framerate
-               for (x = 1;x < 1000;x++)
-                       if (cls.capturevideo.framerate * x == floor(cls.capturevideo.framerate * x))
-                               break;
-               SCR_CaptureVideo_RIFF_Write32(x); // samples/second divisor
-               SCR_CaptureVideo_RIFF_Write32((int)(cls.capturevideo.framerate * x)); // samples/second multiplied by divisor
+               FindFraction(cls.capturevideo.framerate, &n, &d, 1000);
+               SCR_CaptureVideo_RIFF_Write32(d); // samples/second divisor
+               SCR_CaptureVideo_RIFF_Write32(n); // samples/second multiplied by divisor
                SCR_CaptureVideo_RIFF_Write32(0); // start
-               cls.capturevideo.videofile_totalframes_offset2 = SCR_CaptureVideo_RIFF_GetPosition();
+               cls.capturevideo.videofile_totalframes_offset1 = SCR_CaptureVideo_RIFF_GetPosition();
                SCR_CaptureVideo_RIFF_Write32(0); // length
                SCR_CaptureVideo_RIFF_Write32(width*height+(width/2)*(height/2)*2); // suggested buffer size
                SCR_CaptureVideo_RIFF_Write32(0); // quality
@@ -1094,6 +1164,27 @@ Cr = R *  .500 + G * -.419 + B * -.0813 + 128.;
                SCR_CaptureVideo_RIFF_Write32(0); // color used
                SCR_CaptureVideo_RIFF_Write32(0); // color important
                SCR_CaptureVideo_RIFF_Pop();
+               // extended format (aspect!)
+               SCR_CaptureVideo_RIFF_Push("vprp", NULL);
+               SCR_CaptureVideo_RIFF_Write32(0); // VideoFormatToken
+               SCR_CaptureVideo_RIFF_Write32(0); // VideoStandard
+               SCR_CaptureVideo_RIFF_Write32(cls.capturevideo.framerate); // dwVerticalRefreshRate (bogus)
+               SCR_CaptureVideo_RIFF_Write32(width); // dwHTotalInT
+               SCR_CaptureVideo_RIFF_Write32(height); // dwVTotalInLines
+               FindFraction(aspect, &n, &d, 1000);
+               SCR_CaptureVideo_RIFF_Write32((n << 16) | d); // dwFrameAspectRatio // TODO a word
+               SCR_CaptureVideo_RIFF_Write32(width); // dwFrameWidthInPixels
+               SCR_CaptureVideo_RIFF_Write32(height); // dwFrameHeightInLines
+               SCR_CaptureVideo_RIFF_Write32(1); // nFieldPerFrame
+               SCR_CaptureVideo_RIFF_Write32(width); // CompressedBMWidth
+               SCR_CaptureVideo_RIFF_Write32(height); // CompressedBMHeight
+               SCR_CaptureVideo_RIFF_Write32(width); // ValidBMHeight
+               SCR_CaptureVideo_RIFF_Write32(height); // ValidBMWidth
+               SCR_CaptureVideo_RIFF_Write32(0); // ValidBMXOffset
+               SCR_CaptureVideo_RIFF_Write32(0); // ValidBMYOffset
+               SCR_CaptureVideo_RIFF_Write32(0); // ValidBMXOffsetInT
+               SCR_CaptureVideo_RIFF_Write32(0); // ValidBMYValidStartLine
+               SCR_CaptureVideo_RIFF_Pop();
                SCR_CaptureVideo_RIFF_Pop();
                if (cls.capturevideo.soundrate)
                {
@@ -1130,6 +1221,15 @@ Cr = R *  .500 + G * -.419 + B * -.0813 + 128.;
                        SCR_CaptureVideo_RIFF_Pop();
                        SCR_CaptureVideo_RIFF_Pop();
                }
+
+               // extended header (for total #frames)
+               SCR_CaptureVideo_RIFF_Push("LIST", "odml");
+               SCR_CaptureVideo_RIFF_Push("dmlh", NULL);
+               cls.capturevideo.videofile_totalframes_offset2 = SCR_CaptureVideo_RIFF_GetPosition();
+               SCR_CaptureVideo_RIFF_Write32(0);
+               SCR_CaptureVideo_RIFF_Pop();
+               SCR_CaptureVideo_RIFF_Pop();
+
                // close the AVI header list
                SCR_CaptureVideo_RIFF_Pop();
                // software that produced this AVI video file
@@ -1179,8 +1279,9 @@ void SCR_CaptureVideo_EndVideo(void)
                {
                case CAPTUREVIDEOFORMAT_AVI_I420:
                        // close any open chunks
-                       SCR_CaptureVideo_RIFF_Finish();
+                       SCR_CaptureVideo_RIFF_Finish(true);
                        // go back and fix the video frames and audio samples fields
+                       Con_DPrintf("Finishing capture (%d frames, %d audio frames)\n", cls.capturevideo.frame, cls.capturevideo.soundsampleframe);
                        FS_Seek(cls.capturevideo.videofile, cls.capturevideo.videofile_totalframes_offset1, SEEK_SET);
                        SCR_CaptureVideo_RIFF_Write32(cls.capturevideo.frame);
                        SCR_CaptureVideo_RIFF_Flush();
@@ -1201,10 +1302,16 @@ void SCR_CaptureVideo_EndVideo(void)
                cls.capturevideo.videofile = NULL;
        }
 
-       if (cls.capturevideo.buffer)
+       if (cls.capturevideo.screenbuffer)
        {
-               Mem_Free (cls.capturevideo.buffer);
-               cls.capturevideo.buffer = NULL;
+               Mem_Free (cls.capturevideo.screenbuffer);
+               cls.capturevideo.screenbuffer = NULL;
+       }
+
+       if (cls.capturevideo.outbuffer)
+       {
+               Mem_Free (cls.capturevideo.outbuffer);
+               cls.capturevideo.outbuffer = NULL;
        }
 
        if (cls.capturevideo.riffindexbuffer.data)
@@ -1259,9 +1366,55 @@ void SCR_CaptureVideo_ConvertFrame_RGB_to_I420_flip(int width, int height, unsig
        }
 }
 
+static void SCR_ScaleDown(unsigned char *in, int inw, int inh, unsigned char *out, int outw, int outh)
+{
+       // TODO optimize this function
+
+       int x, y;
+       float area;
+
+       // memcpy is faster than me
+       if(inw == outw && inh == outh)
+       {
+               memcpy(out, in, 3 * inw * inh);
+               return;
+       }
+
+       // otherwise: a box filter
+       area = (float)outw * (float)outh / (float)inw / (float)inh;
+       for(y = 0; y < outh; ++y)
+       {
+               float iny0 =  y    / (float)outh * inh; int iny0_i = floor(iny0);
+               float iny1 = (y+1) / (float)outh * inh; int iny1_i = ceil(iny1);
+               for(x = 0; x < outw; ++x)
+               {
+                       float inx0 =  x    / (float)outw * inw; int inx0_i = floor(inx0);
+                       float inx1 = (x+1) / (float)outw * inw; int inx1_i = ceil(inx1);
+                       float r = 0, g = 0, b = 0;
+                       int xx, yy;
+
+                       for(yy = iny0_i; yy < iny1_i; ++yy)
+                       {
+                               float ya = min(yy+1, iny1) - max(iny0, yy);
+                               for(xx = inx0_i; xx < inx1_i; ++xx)
+                               {
+                                       float a = ya * (min(xx+1, inx1) - max(inx0, xx));
+                                       r += a * in[3*(xx + inw * yy)+0];
+                                       g += a * in[3*(xx + inw * yy)+1];
+                                       b += a * in[3*(xx + inw * yy)+2];
+                               }
+                       }
+
+                       out[3*(x + outw * y)+0] = r * area;
+                       out[3*(x + outw * y)+1] = g * area;
+                       out[3*(x + outw * y)+2] = b * area;
+               }
+       }
+}
+
 qboolean SCR_CaptureVideo_VideoFrame(int newframenum)
 {
-       int x = 0, y = 0, width = vid.width, height = vid.height;
+       int x = 0, y = 0, width = cls.capturevideo.width, height = cls.capturevideo.height;
        unsigned char *in, *out;
        CHECKGLERROR
        //return SCR_ScreenShot(filename, cls.capturevideo.buffer, cls.capturevideo.buffer + vid.width * vid.height * 3, cls.capturevideo.buffer + vid.width * vid.height * 6, 0, 0, vid.width, vid.height, false, false, false, jpeg, true);
@@ -1273,9 +1426,10 @@ qboolean SCR_CaptureVideo_VideoFrame(int newframenum)
                if (!cls.capturevideo.videofile)
                        return false;
                // FIXME: width/height must be multiple of 2, enforce this?
-               qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cls.capturevideo.buffer);CHECKGLERROR
-               in = cls.capturevideo.buffer;
-               out = cls.capturevideo.buffer + width*height*3;
+               qglReadPixels (x, y, vid.width, vid.height, GL_RGB, GL_UNSIGNED_BYTE, cls.capturevideo.screenbuffer);CHECKGLERROR
+               SCR_ScaleDown (cls.capturevideo.screenbuffer, vid.width, vid.height, cls.capturevideo.outbuffer, width, height);
+               in = cls.capturevideo.outbuffer;
+               out = cls.capturevideo.outbuffer + width*height*3;
                SCR_CaptureVideo_ConvertFrame_RGB_to_I420_flip(width, height, in, out);
                x = width*height+(width/2)*(height/2)*2;
                SCR_CaptureVideo_RIFF_OverflowCheck(8 + x);
index 471a36456065d05a7af8f39aa3a9fdba4a1f8c4e..9083ee313968237096b7ec68cb0dacc2f423b914 100644 (file)
@@ -17,6 +17,7 @@ extern cvar_t scr_screenshot_name;
 
 void CL_Screen_NewMap(void);
 void CL_Screen_Init(void);
+void CL_Screen_Shutdown(void);
 void CL_UpdateScreen(void);
 
 #endif
index 76fce5bf96e08db2c5ff03e95156cc84754208dc..0b16a62d0a8089b5b99bf1b778e4c0231b0e46ee 100644 (file)
--- a/client.h
+++ b/client.h
@@ -439,6 +439,7 @@ typedef struct capturevideostate_s
        double starttime;
        double framerate;
        // for AVI saving some values have to be written after capture ends
+       fs_offset_t videofile_firstchunkframes_offset;
        fs_offset_t videofile_totalframes_offset1;
        fs_offset_t videofile_totalframes_offset2;
        fs_offset_t videofile_totalsampleframes_offset;
@@ -450,7 +451,8 @@ typedef struct capturevideostate_s
        int soundrate;
        int frame;
        int soundsampleframe; // for AVI saving
-       unsigned char *buffer;
+       unsigned char *screenbuffer;
+       unsigned char *outbuffer;
        sizebuf_t riffbuffer;
        unsigned char riffbufferdata[128];
        // note: riffindex buffer has an allocated ->data member, not static like most!
@@ -460,6 +462,7 @@ typedef struct capturevideostate_s
        short rgbtoyuvscaletable[3][3][256];
        unsigned char yuvnormalizetable[3][256];
        char basename[64];
+       int width, height;
 }
 capturevideostate_t;
 
diff --git a/host.c b/host.c
index 66f4fa76a5478d8a74dfe40cff4221a17959ab73..812a2cb63ac022ec015b0620f71fef30ed205fd1 100644 (file)
--- a/host.c
+++ b/host.c
@@ -222,7 +222,7 @@ void Host_SaveConfig_f(void)
 // dedicated servers initialize the host but don't parse and set the
 // config.cfg cvars
        // LordHavoc: don't save a config if it crashed in startup
-       if (host_framecount >= 3 && cls.state != ca_dedicated && !COM_CheckParm("-benchmark"))
+       if (host_framecount >= 3 && cls.state != ca_dedicated && !COM_CheckParm("-benchmark") && !COM_CheckParm("-capturedemo"))
        {
                f = FS_Open ("config.cfg", "wb", false, false);
                if (!f)
@@ -1005,6 +1005,15 @@ static void Host_Init (void)
                Cbuf_Execute();
        }
 
+// COMMANDLINEOPTION: Client: -capturedemo <demoname> captures a playdemo and quits
+       i = COM_CheckParm("-capturedemo");
+       if (i && i + 1 < com_argc)
+       if (!sv.active && !cls.demoplayback && !cls.connect_trying)
+       {
+               Cbuf_AddText(va("playdemo %s\ncl_capturevideo 1\n", com_argv[i + 1]));
+               Cbuf_Execute();
+       }
+
        if (cls.state == ca_dedicated || COM_CheckParm("-listen"))
        if (!sv.active && !cls.demoplayback && !cls.connect_trying)
        {
@@ -1044,6 +1053,11 @@ void Host_Shutdown(void)
                Con_Print("recursive shutdown\n");
                return;
        }
+       if (setjmp(host_abortframe))
+       {
+               Con_Print("aborted the quitting frame?!?\n");
+               return;
+       }
        isdown = true;
 
        // be quiet while shutting down
index 7875bffa6c84551de823e8c446c2d75a92588bbb..eb23e394364c4734c3f77feda227bfa52724c133 100644 (file)
@@ -30,6 +30,8 @@ cvar_t skin = {CVAR_USERINFO | CVAR_SAVE, "skin", "", "QW player skin name (exam
 cvar_t noaim = {CVAR_USERINFO | CVAR_SAVE, "noaim", "1", "QW option to disable vertical autoaim"};
 qboolean allowcheats = false;
 
+extern qboolean host_shuttingdown;
+
 /*
 ==================
 Host_Quit_f
@@ -38,7 +40,10 @@ Host_Quit_f
 
 void Host_Quit_f (void)
 {
-       Sys_Quit (0);
+       if(host_shuttingdown)
+               Con_Printf("shutting down already!\n");
+       else
+               Sys_Quit (0);
 }
 
 
@@ -1910,7 +1915,7 @@ void Host_Startdemos_f (void)
 {
        int             i, c;
 
-       if (cls.state == ca_dedicated || COM_CheckParm("-listen") || COM_CheckParm("-benchmark") || COM_CheckParm("-demo"))
+       if (cls.state == ca_dedicated || COM_CheckParm("-listen") || COM_CheckParm("-benchmark") || COM_CheckParm("-demo") || COM_CheckParm("-capturedemo"))
                return;
 
        c = Cmd_Argc() - 1;