+typedef enum capturevideoformat_e
+{
+ CAPTUREVIDEOFORMAT_TARGA,
+ CAPTUREVIDEOFORMAT_JPEG,
+ CAPTUREVIDEOFORMAT_RAWRGB,
+ CAPTUREVIDEOFORMAT_RAWYV12
+}
+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;
+qfile_t *cl_capturevideo_soundfile = NULL;
+static short cl_capturevideo_rgbtoyuvscaletable[3][3][256];
+static unsigned char cl_capturevideo_yuvnormalizetable[3][256];
+//static unsigned char cl_capturevideo_rgbgammatable[3][256];
+
+void SCR_CaptureVideo_BeginVideo(void)
+{
+ double gamma, g;
+ unsigned int i;
+ 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.width * vid.height * (3+3+3) + 18);
+ gamma = 1.0/scr_screenshot_gamma.value;
+
+ /*
+ for (i = 0;i < 256;i++)
+ {
+ unsigned char j = (unsigned char)bound(0, 255*pow(i/255.0, gamma), 255);
+ cl_capturevideo_rgbgammatable[0][i] = j;
+ cl_capturevideo_rgbgammatable[1][i] = j;
+ cl_capturevideo_rgbgammatable[2][i] = j;
+ }
+ */
+/*
+R = Y + 1.4075 * (Cr - 128);
+G = Y + -0.3455 * (Cb - 128) + -0.7169 * (Cr - 128);
+B = Y + 1.7790 * (Cb - 128);
+Y = R * .299 + G * .587 + B * .114;
+Cb = R * -.169 + G * -.332 + B * .500 + 128.;
+Cr = R * .500 + G * -.419 + B * -.0813 + 128.;
+*/
+ for (i = 0;i < 256;i++)
+ {
+ g = 255*pow(i/255.0, gamma);
+ // Y weights from RGB
+ cl_capturevideo_rgbtoyuvscaletable[0][0][i] = (short)(g * 0.299);
+ cl_capturevideo_rgbtoyuvscaletable[0][1][i] = (short)(g * 0.587);
+ cl_capturevideo_rgbtoyuvscaletable[0][2][i] = (short)(g * 0.114);
+ // Cb weights from RGB
+ cl_capturevideo_rgbtoyuvscaletable[1][0][i] = (short)(g * -0.169);
+ cl_capturevideo_rgbtoyuvscaletable[1][1][i] = (short)(g * -0.332);
+ cl_capturevideo_rgbtoyuvscaletable[1][2][i] = (short)(g * 0.500);
+ // Cr weights from RGB
+ cl_capturevideo_rgbtoyuvscaletable[2][0][i] = (short)(g * 0.500);
+ cl_capturevideo_rgbtoyuvscaletable[2][1][i] = (short)(g * -0.419);
+ cl_capturevideo_rgbtoyuvscaletable[2][2][i] = (short)(g * -0.0813);
+ // range reduction of YCbCr to valid signal range
+ cl_capturevideo_yuvnormalizetable[0][i] = 16 + i * (236-16) / 256;
+ cl_capturevideo_yuvnormalizetable[1][i] = 16 + i * (240-16) / 256;
+ cl_capturevideo_yuvnormalizetable[2][i] = 16 + i * (240-16) / 256;
+ }
+
+ if (cl_capturevideo_rawrgb.integer)
+ {
+ cl_capturevideo_format = CAPTUREVIDEOFORMAT_RAWRGB;
+ cl_capturevideo_videofile = FS_Open ("video/dpvideo.rgb", "wb", false, true);
+ }
+ else if (cl_capturevideo_rawyv12.integer)
+ {
+ cl_capturevideo_format = CAPTUREVIDEOFORMAT_RAWYV12;
+ cl_capturevideo_videofile = FS_Open ("video/dpvideo.yv12", "wb", false, true);
+ }
+ 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;
+ }
+
+ if (cl_capturevideo_sound.integer)
+ {
+ cl_capturevideo_soundfile = FS_Open ("video/dpvideo.wav", "wb", false, true);
+ // wave header will be filled out when video ends
+ memset(out, 0, 44);
+ FS_Write (cl_capturevideo_soundfile, out, 44);
+ }
+ else
+ cl_capturevideo_soundfile = NULL;
+}
+
+void SCR_CaptureVideo_EndVideo(void)
+{
+ int i, n;
+ qbyte out[44];
+ if (!cl_capturevideo_active)
+ return;
+ cl_capturevideo_active = false;
+
+ if (cl_capturevideo_videofile)
+ {
+ 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;
+ }