+ 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 (cl_capturevideo_buffer)
+ {
+ Mem_Free (cl_capturevideo_buffer);
+ cl_capturevideo_buffer = NULL;
+ }
+
+ cl_capturevideo_starttime = 0;
+ cl_capturevideo_framerate = 0;
+ cl_capturevideo_frame = 0;
+}
+
+qboolean SCR_CaptureVideo_VideoFrame(int newframenum)
+{
+ int x = vid.realx, y = vid.realy, width = vid.realwidth, height = vid.realheight;
+ unsigned char *b, *out;
+ char filename[32];
+ int outoffset = (width/2)*(height/2);
+ //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
+ switch (cl_capturevideo_format)
+ {
+ case CAPTUREVIDEOFORMAT_RAWYV12:
+ // FIXME: width/height must be multiple of 2, enforce this?
+ qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
+ CHECKGLERROR
+ // process one line at a time, and CbCr every other line at 2 pixel intervals
+ for (y = 0;y < height;y++)
+ {
+ // 1x1 Y
+ for (b = cl_capturevideo_buffer + (height-1-y)*width*3, out = cl_capturevideo_buffer + width*height*3 + y*width, x = 0;x < width;x++, b += 3, out++)
+ *out = cl_capturevideo_yuvnormalizetable[0][cl_capturevideo_rgbtoyuvscaletable[0][0][b[0]] + cl_capturevideo_rgbtoyuvscaletable[0][1][b[1]] + cl_capturevideo_rgbtoyuvscaletable[0][2][b[2]]];
+ if ((y & 1) == 0)
+ {
+ // 2x2 Cb and Cr planes
+#if 1
+ // low quality, no averaging
+ for (b = cl_capturevideo_buffer + (height-2-y)*width*3, out = cl_capturevideo_buffer + width*height*3 + width*height + (y/2)*(width/2), x = 0;x < width/2;x++, b += 6, out++)
+ {
+ // Cr
+ out[0 ] = cl_capturevideo_yuvnormalizetable[2][cl_capturevideo_rgbtoyuvscaletable[2][0][b[0]] + cl_capturevideo_rgbtoyuvscaletable[2][1][b[1]] + cl_capturevideo_rgbtoyuvscaletable[2][2][b[2]] + 128];
+ // Cb
+ out[outoffset] = cl_capturevideo_yuvnormalizetable[1][cl_capturevideo_rgbtoyuvscaletable[1][0][b[0]] + cl_capturevideo_rgbtoyuvscaletable[1][1][b[1]] + cl_capturevideo_rgbtoyuvscaletable[1][2][b[2]] + 128];
+ }
+#else
+ // high quality, averaging
+ int inpitch = width*3;
+ for (b = cl_capturevideo_buffer + (height-2-y)*width*3, out = cl_capturevideo_buffer + width*height*3 + width*height + (y/2)*(width/2), x = 0;x < width/2;x++, b += 6, out++)
+ {
+ int blockr, blockg, blockb;
+ blockr = (b[0] + b[3] + b[inpitch+0] + b[inpitch+3]) >> 2;
+ blockg = (b[1] + b[4] + b[inpitch+1] + b[inpitch+4]) >> 2;
+ blockb = (b[2] + b[5] + b[inpitch+2] + b[inpitch+5]) >> 2;
+ // Cr
+ out[0 ] = cl_capturevideo_yuvnormalizetable[2][cl_capturevideo_rgbtoyuvscaletable[2][0][blockr] + cl_capturevideo_rgbtoyuvscaletable[2][1][blockg] + cl_capturevideo_rgbtoyuvscaletable[2][2][blockb] + 128];
+ // Cb
+ out[outoffset] = cl_capturevideo_yuvnormalizetable[1][cl_capturevideo_rgbtoyuvscaletable[1][0][blockr] + cl_capturevideo_rgbtoyuvscaletable[1][1][blockg] + cl_capturevideo_rgbtoyuvscaletable[1][2][blockb] + 128];
+ }
+#endif
+ }
+ }
+ for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
+ if (!FS_Write (cl_capturevideo_videofile, cl_capturevideo_buffer + width*height*3, width*height+(width/2)*(height/2)*2))
+ return false;
+ return true;
+ case CAPTUREVIDEOFORMAT_RAWRGB:
+ qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
+ CHECKGLERROR
+ for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)
+ if (!FS_Write (cl_capturevideo_videofile, cl_capturevideo_buffer, width*height*3))
+ return false;
+ return true;
+ case CAPTUREVIDEOFORMAT_JPEG:
+ qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, cl_capturevideo_buffer);
+ CHECKGLERROR
+ for (;cl_capturevideo_frame < newframenum;cl_capturevideo_frame++)