4 #define AVI_MASTER_INDEX_SIZE 640 // GB ought to be enough for anyone
6 typedef struct capturevideostate_avi_formatspecific_s
9 fs_offset_t videofile_firstchunkframes_offset;
10 fs_offset_t videofile_totalframes_offset1;
11 fs_offset_t videofile_totalframes_offset2;
12 fs_offset_t videofile_totalsampleframes_offset;
13 int videofile_ix_master_audio_inuse;
14 fs_offset_t videofile_ix_master_audio_inuse_offset;
15 fs_offset_t videofile_ix_master_audio_start_offset;
16 int videofile_ix_master_video_inuse;
17 fs_offset_t videofile_ix_master_video_inuse_offset;
18 fs_offset_t videofile_ix_master_video_start_offset;
19 fs_offset_t videofile_ix_movistart;
23 unsigned char riffbufferdata[128];
24 sizebuf_t riffindexbuffer;
26 fs_offset_t riffstackstartoffset[4];
27 fs_offset_t riffstacksizehint[4];
28 const char *riffstackfourcc[4];
30 capturevideostate_avi_formatspecific_t;
31 #define LOAD_FORMATSPECIFIC_AVI() capturevideostate_avi_formatspecific_t *format = (capturevideostate_avi_formatspecific_t *) cls.capturevideo.formatspecific
33 static void SCR_CaptureVideo_RIFF_Start(void)
35 LOAD_FORMATSPECIFIC_AVI();
36 memset(&format->riffbuffer, 0, sizeof(sizebuf_t));
37 format->riffbuffer.maxsize = sizeof(format->riffbufferdata);
38 format->riffbuffer.data = format->riffbufferdata;
42 static void SCR_CaptureVideo_RIFF_Flush(void)
44 LOAD_FORMATSPECIFIC_AVI();
45 if (format->riffbuffer.cursize > 0)
47 if (!FS_Write(cls.capturevideo.videofile, format->riffbuffer.data, format->riffbuffer.cursize))
48 cls.capturevideo.error = true;
49 format->position += format->riffbuffer.cursize;
50 format->riffbuffer.cursize = 0;
51 format->riffbuffer.overflowed = false;
55 static void SCR_CaptureVideo_RIFF_FlushNoIncrease(void)
57 LOAD_FORMATSPECIFIC_AVI();
58 if (format->riffbuffer.cursize > 0)
60 if (!FS_Write(cls.capturevideo.videofile, format->riffbuffer.data, format->riffbuffer.cursize))
61 cls.capturevideo.error = true;
62 format->riffbuffer.cursize = 0;
63 format->riffbuffer.overflowed = false;
67 static void SCR_CaptureVideo_RIFF_WriteBytes(const unsigned char *data, size_t size)
69 LOAD_FORMATSPECIFIC_AVI();
70 SCR_CaptureVideo_RIFF_Flush();
71 if (!FS_Write(cls.capturevideo.videofile, data, size))
72 cls.capturevideo.error = true;
73 format->position += size;
76 static void SCR_CaptureVideo_RIFF_Write32(int n)
78 LOAD_FORMATSPECIFIC_AVI();
79 if (format->riffbuffer.cursize + 4 > format->riffbuffer.maxsize)
80 SCR_CaptureVideo_RIFF_Flush();
81 MSG_WriteLong(&format->riffbuffer, n);
84 static void SCR_CaptureVideo_RIFF_Write16(int n)
86 LOAD_FORMATSPECIFIC_AVI();
87 if (format->riffbuffer.cursize + 2 > format->riffbuffer.maxsize)
88 SCR_CaptureVideo_RIFF_Flush();
89 MSG_WriteShort(&format->riffbuffer, n);
92 static void SCR_CaptureVideo_RIFF_WriteFourCC(const char *chunkfourcc)
94 LOAD_FORMATSPECIFIC_AVI();
95 if (format->riffbuffer.cursize + (int)strlen(chunkfourcc) > format->riffbuffer.maxsize)
96 SCR_CaptureVideo_RIFF_Flush();
97 MSG_WriteUnterminatedString(&format->riffbuffer, chunkfourcc);
100 static void SCR_CaptureVideo_RIFF_WriteTerminatedString(const char *string)
102 LOAD_FORMATSPECIFIC_AVI();
103 if (format->riffbuffer.cursize + (int)strlen(string) > format->riffbuffer.maxsize)
104 SCR_CaptureVideo_RIFF_Flush();
105 MSG_WriteString(&format->riffbuffer, string);
108 static fs_offset_t SCR_CaptureVideo_RIFF_GetPosition(void)
110 LOAD_FORMATSPECIFIC_AVI();
111 SCR_CaptureVideo_RIFF_Flush();
112 //return FS_Tell(cls.capturevideo.videofile);
113 return format->position;
116 static void SCR_CaptureVideo_RIFF_Push(const char *chunkfourcc, const char *listtypefourcc, fs_offset_t sizeHint)
118 LOAD_FORMATSPECIFIC_AVI();
119 if (listtypefourcc && sizeHint >= 0)
120 sizeHint += 4; // size hint is for INNER size
121 SCR_CaptureVideo_RIFF_WriteFourCC(chunkfourcc);
122 SCR_CaptureVideo_RIFF_Write32(sizeHint);
123 SCR_CaptureVideo_RIFF_Flush();
124 format->riffstacksizehint[format->riffstacklevel] = sizeHint;
125 format->riffstackstartoffset[format->riffstacklevel] = SCR_CaptureVideo_RIFF_GetPosition();
126 format->riffstackfourcc[format->riffstacklevel] = chunkfourcc;
127 ++format->riffstacklevel;
129 SCR_CaptureVideo_RIFF_WriteFourCC(listtypefourcc);
132 static void SCR_CaptureVideo_RIFF_Pop(void)
134 LOAD_FORMATSPECIFIC_AVI();
135 fs_offset_t offset, sizehint;
137 unsigned char sizebytes[4];
138 // write out the chunk size and then return to the current file position
139 format->riffstacklevel--;
140 offset = SCR_CaptureVideo_RIFF_GetPosition();
142 sizehint = format->riffstacksizehint[format->riffstacklevel];
143 x = (int)(offset - (format->riffstackstartoffset[format->riffstacklevel]));
150 Con_Printf("WARNING: invalid size hint %d when writing video data (actual size: %d)\n", (int) sizehint, x);
151 for(i = 0; i <= format->riffstacklevel; ++i)
153 Con_Printf(" RIFF level %d = %s\n", i, format->riffstackfourcc[i]);
156 sizebytes[0] = (x) & 0xff;sizebytes[1] = (x >> 8) & 0xff;sizebytes[2] = (x >> 16) & 0xff;sizebytes[3] = (x >> 24) & 0xff;
157 if(FS_Seek(cls.capturevideo.videofile, -(x + 4), SEEK_END) >= 0)
159 FS_Write(cls.capturevideo.videofile, sizebytes, 4);
161 FS_Seek(cls.capturevideo.videofile, 0, SEEK_END);
166 SCR_CaptureVideo_RIFF_WriteBytes((unsigned char *) "\0", 1);
170 static void GrowBuf(sizebuf_t *buf, int extralen)
172 if(buf->cursize + extralen > buf->maxsize)
174 int oldsize = buf->maxsize;
175 unsigned char *olddata;
177 buf->maxsize = max(buf->maxsize * 2, 4096);
178 buf->data = (unsigned char *) Mem_Alloc(tempmempool, buf->maxsize);
181 memcpy(buf->data, olddata, oldsize);
187 static void SCR_CaptureVideo_RIFF_IndexEntry(const char *chunkfourcc, int chunksize, int flags)
189 LOAD_FORMATSPECIFIC_AVI();
191 Host_Error("SCR_CaptureVideo_RIFF_IndexEntry called on non-seekable AVI");
193 if (format->riffstacklevel != 2)
194 Sys_Error("SCR_Capturevideo_RIFF_IndexEntry: RIFF stack level is %i (should be 2)\n", format->riffstacklevel);
195 GrowBuf(&format->riffindexbuffer, 16);
196 SCR_CaptureVideo_RIFF_Flush();
197 MSG_WriteUnterminatedString(&format->riffindexbuffer, chunkfourcc);
198 MSG_WriteLong(&format->riffindexbuffer, flags);
199 MSG_WriteLong(&format->riffindexbuffer, (int)FS_Tell(cls.capturevideo.videofile) - format->riffstackstartoffset[1]);
200 MSG_WriteLong(&format->riffindexbuffer, chunksize);
203 static void SCR_CaptureVideo_RIFF_MakeIxChunk(const char *fcc, const char *dwChunkId, fs_offset_t masteridx_counter, int *masteridx_count, fs_offset_t masteridx_start)
205 LOAD_FORMATSPECIFIC_AVI();
208 fs_offset_t ix = SCR_CaptureVideo_RIFF_GetPosition();
212 Host_Error("SCR_CaptureVideo_RIFF_MakeIxChunk called on non-seekable AVI");
214 if(*masteridx_count >= AVI_MASTER_INDEX_SIZE)
217 nMatching = 0; // go through index and enumerate them
218 for(i = 0; i < format->riffindexbuffer.cursize; i += 16)
219 if(!memcmp(format->riffindexbuffer.data + i, dwChunkId, 4))
223 for(i = 0; i < format->riffindexbuffer.cursize; i += 16)
224 if(!memcmp(format->riffindexbuffer.data + i, dwChunkId, 4))
227 SCR_CaptureVideo_RIFF_Push(fcc, NULL, sz);
228 SCR_CaptureVideo_RIFF_Write16(2); // wLongsPerEntry
229 SCR_CaptureVideo_RIFF_Write16(0x0100); // bIndexType=1, bIndexSubType=0
230 SCR_CaptureVideo_RIFF_Write32(nMatching); // nEntriesInUse
231 SCR_CaptureVideo_RIFF_WriteFourCC(dwChunkId); // dwChunkId
232 SCR_CaptureVideo_RIFF_Write32(format->videofile_ix_movistart & (fs_offset_t) 0xFFFFFFFFu);
233 SCR_CaptureVideo_RIFF_Write32(((fs_offset_t) format->videofile_ix_movistart) >> 32);
234 SCR_CaptureVideo_RIFF_Write32(0); // dwReserved
236 for(i = 0; i < format->riffindexbuffer.cursize; i += 16)
237 if(!memcmp(format->riffindexbuffer.data + i, dwChunkId, 4))
239 unsigned int *p = (unsigned int *) (format->riffindexbuffer.data + i);
240 unsigned int flags = p[1];
241 unsigned int rpos = p[2];
242 unsigned int size = p[3];
244 if(!(flags & 0x10)) // no keyframe?
246 SCR_CaptureVideo_RIFF_Write32(rpos + 8);
247 SCR_CaptureVideo_RIFF_Write32(size);
250 SCR_CaptureVideo_RIFF_Flush();
251 SCR_CaptureVideo_RIFF_Pop();
252 pos = SCR_CaptureVideo_RIFF_GetPosition();
254 if(FS_Seek(cls.capturevideo.videofile, masteridx_start + 16 * *masteridx_count, SEEK_SET) >= 0)
256 SCR_CaptureVideo_RIFF_Write32(ix & (fs_offset_t) 0xFFFFFFFFu);
257 SCR_CaptureVideo_RIFF_Write32(((fs_offset_t) ix) >> 32);
258 SCR_CaptureVideo_RIFF_Write32(pos - ix);
259 SCR_CaptureVideo_RIFF_Write32(nMatching);
260 SCR_CaptureVideo_RIFF_FlushNoIncrease();
263 if(FS_Seek(cls.capturevideo.videofile, masteridx_counter, SEEK_SET) >= 0)
265 SCR_CaptureVideo_RIFF_Write32(++*masteridx_count);
266 SCR_CaptureVideo_RIFF_FlushNoIncrease();
269 FS_Seek(cls.capturevideo.videofile, 0, SEEK_END); // return value doesn't matter here
272 static void SCR_CaptureVideo_RIFF_Finish(qboolean final)
274 LOAD_FORMATSPECIFIC_AVI();
275 // close the "movi" list
276 SCR_CaptureVideo_RIFF_Pop();
277 if(format->videofile_ix_master_video_inuse_offset)
278 SCR_CaptureVideo_RIFF_MakeIxChunk("ix00", "00dc", format->videofile_ix_master_video_inuse_offset, &format->videofile_ix_master_video_inuse, format->videofile_ix_master_video_start_offset);
279 if(format->videofile_ix_master_audio_inuse_offset)
280 SCR_CaptureVideo_RIFF_MakeIxChunk("ix01", "01wb", format->videofile_ix_master_audio_inuse_offset, &format->videofile_ix_master_audio_inuse, format->videofile_ix_master_audio_start_offset);
281 // write the idx1 chunk that we've been building while saving the frames (for old style players)
282 if(final && format->videofile_firstchunkframes_offset)
283 // TODO replace index creating by OpenDML ix##/##ix/indx chunk so it works for more than one AVI part too
285 SCR_CaptureVideo_RIFF_Push("idx1", NULL, format->riffindexbuffer.cursize);
286 SCR_CaptureVideo_RIFF_WriteBytes(format->riffindexbuffer.data, format->riffindexbuffer.cursize);
287 SCR_CaptureVideo_RIFF_Pop();
289 format->riffindexbuffer.cursize = 0;
290 // pop the RIFF chunk itself
291 while (format->riffstacklevel > 0)
292 SCR_CaptureVideo_RIFF_Pop();
293 SCR_CaptureVideo_RIFF_Flush();
294 if(format->videofile_firstchunkframes_offset)
296 Con_DPrintf("Finishing first chunk (%d frames)\n", cls.capturevideo.frame);
297 if(FS_Seek(cls.capturevideo.videofile, format->videofile_firstchunkframes_offset, SEEK_SET) >= 0)
299 SCR_CaptureVideo_RIFF_Write32(cls.capturevideo.frame);
300 SCR_CaptureVideo_RIFF_FlushNoIncrease();
302 FS_Seek(cls.capturevideo.videofile, 0, SEEK_END);
303 format->videofile_firstchunkframes_offset = 0;
306 Con_DPrintf("Finishing another chunk (%d frames)\n", cls.capturevideo.frame);
309 static void SCR_CaptureVideo_RIFF_OverflowCheck(int framesize)
311 LOAD_FORMATSPECIFIC_AVI();
312 fs_offset_t cursize, curfilesize;
313 if (format->riffstacklevel != 2)
314 Sys_Error("SCR_CaptureVideo_RIFF_OverflowCheck: chunk stack leakage!\n");
319 // check where we are in the file
320 SCR_CaptureVideo_RIFF_Flush();
321 cursize = SCR_CaptureVideo_RIFF_GetPosition() - format->riffstackstartoffset[0];
322 curfilesize = SCR_CaptureVideo_RIFF_GetPosition();
324 // if this would overflow the windows limit of 1GB per RIFF chunk, we need
325 // to close the current RIFF chunk and open another for future frames
326 if (8 + cursize + framesize + format->riffindexbuffer.cursize + 8 + format->riffindexbuffer.cursize + 64 > 1<<30) // note that the Ix buffer takes less space... I just don't dare to / 2 here now... sorry, maybe later
328 SCR_CaptureVideo_RIFF_Finish(false);
329 // begin a new 1GB extended section of the AVI
330 SCR_CaptureVideo_RIFF_Push("RIFF", "AVIX", -1);
331 SCR_CaptureVideo_RIFF_Push("LIST", "movi", -1);
332 format->videofile_ix_movistart = format->riffstackstartoffset[1];
336 // converts from BGRA32 to I420 colorspace (identical to YV12 except chroma plane order is reversed), this colorspace is handled by the Intel(r) 4:2:0 codec on Windows
337 static void SCR_CaptureVideo_ConvertFrame_BGRA_to_I420_flip(int width, int height, unsigned char *instart, unsigned char *outstart)
340 int blockr, blockg, blockb;
341 int outoffset = (width/2)*(height/2);
342 unsigned char *b, *out;
343 // process one line at a time, and CbCr every other line at 2 pixel intervals
344 for (y = 0;y < height;y++)
347 for (b = instart + (height-1-y)*width*4, out = outstart + y*width, x = 0;x < width;x++, b += 4, out++)
352 *out = cls.capturevideo.yuvnormalizetable[0][cls.capturevideo.rgbtoyuvscaletable[0][0][blockr] + cls.capturevideo.rgbtoyuvscaletable[0][1][blockg] + cls.capturevideo.rgbtoyuvscaletable[0][2][blockb]];
356 // 2x2 Cr and Cb planes
357 int inpitch = width*4;
358 for (b = instart + (height-2-y)*width*4, out = outstart + width*height + (y/2)*(width/2), x = 0;x < width/2;x++, b += 8, out++)
360 blockr = (b[2] + b[6] + b[inpitch+2] + b[inpitch+6]) >> 2;
361 blockg = (b[1] + b[5] + b[inpitch+1] + b[inpitch+5]) >> 2;
362 blockb = (b[0] + b[4] + b[inpitch+0] + b[inpitch+4]) >> 2;
364 out[0 ] = cls.capturevideo.yuvnormalizetable[1][cls.capturevideo.rgbtoyuvscaletable[1][0][blockr] + cls.capturevideo.rgbtoyuvscaletable[1][1][blockg] + cls.capturevideo.rgbtoyuvscaletable[1][2][blockb] + 128];
366 out[outoffset] = cls.capturevideo.yuvnormalizetable[2][cls.capturevideo.rgbtoyuvscaletable[2][0][blockr] + cls.capturevideo.rgbtoyuvscaletable[2][1][blockg] + cls.capturevideo.rgbtoyuvscaletable[2][2][blockb] + 128];
372 static void SCR_CaptureVideo_Avi_VideoFrames(int num)
374 LOAD_FORMATSPECIFIC_AVI();
375 int x = 0, width = cls.capturevideo.width, height = cls.capturevideo.height;
376 unsigned char *in, *out;
377 // FIXME: width/height must be multiple of 2, enforce this?
378 in = cls.capturevideo.outbuffer;
379 out = cls.capturevideo.outbuffer + width*height*4;
380 SCR_CaptureVideo_ConvertFrame_BGRA_to_I420_flip(width, height, in, out);
381 x = width*height+(width/2)*(height/2)*2;
386 SCR_CaptureVideo_RIFF_OverflowCheck(8 + x);
387 SCR_CaptureVideo_RIFF_IndexEntry("00dc", x, 0x10); // AVIIF_KEYFRAME
392 SCR_CaptureVideo_RIFF_Push("RIFF", "AVIX", 12+8+x);
393 SCR_CaptureVideo_RIFF_Push("LIST", "movi", 8+x);
395 SCR_CaptureVideo_RIFF_Push("00dc", NULL, x);
396 SCR_CaptureVideo_RIFF_WriteBytes(out, x);
397 SCR_CaptureVideo_RIFF_Pop();
400 SCR_CaptureVideo_RIFF_Pop();
401 SCR_CaptureVideo_RIFF_Pop();
406 void SCR_CaptureVideo_Avi_EndVideo()
408 LOAD_FORMATSPECIFIC_AVI();
412 // close any open chunks
413 SCR_CaptureVideo_RIFF_Finish(true);
415 // go back and fix the video frames and audio samples fields
416 if(format->videofile_totalframes_offset1)
417 if(FS_Seek(cls.capturevideo.videofile, format->videofile_totalframes_offset1, SEEK_SET) >= 0)
419 SCR_CaptureVideo_RIFF_Write32(cls.capturevideo.frame);
420 SCR_CaptureVideo_RIFF_FlushNoIncrease();
422 if(format->videofile_totalframes_offset2)
423 if(FS_Seek(cls.capturevideo.videofile, format->videofile_totalframes_offset2, SEEK_SET) >= 0)
425 SCR_CaptureVideo_RIFF_Write32(cls.capturevideo.frame);
426 SCR_CaptureVideo_RIFF_FlushNoIncrease();
428 if (cls.capturevideo.soundrate)
430 if(format->videofile_totalsampleframes_offset)
431 if(FS_Seek(cls.capturevideo.videofile, format->videofile_totalsampleframes_offset, SEEK_SET) >= 0)
433 SCR_CaptureVideo_RIFF_Write32(cls.capturevideo.soundsampleframe);
434 SCR_CaptureVideo_RIFF_FlushNoIncrease();
439 if (format->riffindexbuffer.data)
441 Mem_Free(format->riffindexbuffer.data);
442 format->riffindexbuffer.data = NULL;
445 FS_Close(cls.capturevideo.videofile);
446 cls.capturevideo.videofile = NULL;
451 void SCR_CaptureVideo_Avi_SoundFrame(const portable_sampleframe_t *paintbuffer, size_t length)
453 LOAD_FORMATSPECIFIC_AVI();
455 unsigned char bufstereo16le[PAINTBUFFER_SIZE * 4];
456 unsigned char* out_ptr;
459 // write the sound buffer as little endian 16bit interleaved stereo
460 for(i = 0, out_ptr = bufstereo16le; i < length; i++, out_ptr += 4)
464 n0 = paintbuffer[i].sample[0];
465 n0 = bound(-32768, n0, 32767);
466 out_ptr[0] = (unsigned char)n0;
467 out_ptr[1] = (unsigned char)(n0 >> 8);
469 n1 = paintbuffer[i].sample[1];
470 n1 = bound(-32768, n1, 32767);
471 out_ptr[2] = (unsigned char)n1;
472 out_ptr[3] = (unsigned char)(n1 >> 8);
478 SCR_CaptureVideo_RIFF_OverflowCheck(8 + x);
479 SCR_CaptureVideo_RIFF_IndexEntry("01wb", x, 0x10); // AVIIF_KEYFRAME
484 SCR_CaptureVideo_RIFF_Push("RIFF", "AVIX", 12+8+x);
485 SCR_CaptureVideo_RIFF_Push("LIST", "movi", 8+x);
487 SCR_CaptureVideo_RIFF_Push("01wb", NULL, x);
488 SCR_CaptureVideo_RIFF_WriteBytes(bufstereo16le, x);
489 SCR_CaptureVideo_RIFF_Pop();
492 SCR_CaptureVideo_RIFF_Pop();
493 SCR_CaptureVideo_RIFF_Pop();
497 void SCR_CaptureVideo_Avi_BeginVideo()
499 int width = cls.capturevideo.width;
500 int height = cls.capturevideo.height;
505 aspect = vid.width / (vid.height * vid_pixelheight.value);
507 cls.capturevideo.format = CAPTUREVIDEOFORMAT_AVI_I420;
508 cls.capturevideo.formatextension = "avi";
509 cls.capturevideo.videofile = FS_OpenRealFile(va("%s.%s", cls.capturevideo.basename, cls.capturevideo.formatextension), "wb", false);
510 cls.capturevideo.endvideo = SCR_CaptureVideo_Avi_EndVideo;
511 cls.capturevideo.videoframes = SCR_CaptureVideo_Avi_VideoFrames;
512 cls.capturevideo.soundframe = SCR_CaptureVideo_Avi_SoundFrame;
513 cls.capturevideo.formatspecific = Mem_Alloc(tempmempool, sizeof(capturevideostate_avi_formatspecific_t));
515 LOAD_FORMATSPECIFIC_AVI();
516 format->canseek = (FS_Seek(cls.capturevideo.videofile, 0, SEEK_SET) == 0);
517 SCR_CaptureVideo_RIFF_Start();
518 // enclosing RIFF chunk (there can be multiple of these in >1GB files, the later ones are "AVIX" instead of "AVI " and have no header/stream info)
519 SCR_CaptureVideo_RIFF_Push("RIFF", "AVI ", format->canseek ? -1 : 12+(8+56+12+(12+52+8+40+8+68)+(cls.capturevideo.soundrate?(12+12+52+8+18):0)+12+(8+4))+12+(8+(((int) strlen(engineversion) | 1) + 1))+12);
521 SCR_CaptureVideo_RIFF_Push("LIST", "hdrl", format->canseek ? -1 : 8+56+12+(12+52+8+40+8+68)+(cls.capturevideo.soundrate?(12+12+52+8+18):0)+12+(8+4));
522 SCR_CaptureVideo_RIFF_Push("avih", NULL, 56);
523 SCR_CaptureVideo_RIFF_Write32((int)(1000000.0 / (cls.capturevideo.framerate / cls.capturevideo.framestep))); // microseconds per frame
524 SCR_CaptureVideo_RIFF_Write32(0); // max bytes per second
525 SCR_CaptureVideo_RIFF_Write32(0); // padding granularity
526 SCR_CaptureVideo_RIFF_Write32(0x910); // flags (AVIF_HASINDEX | AVIF_ISINTERLEAVED | AVIF_TRUSTCKTYPE)
527 format->videofile_firstchunkframes_offset = SCR_CaptureVideo_RIFF_GetPosition();
528 SCR_CaptureVideo_RIFF_Write32(0); // total frames
529 SCR_CaptureVideo_RIFF_Write32(0); // initial frames
530 if (cls.capturevideo.soundrate)
531 SCR_CaptureVideo_RIFF_Write32(2); // number of streams
533 SCR_CaptureVideo_RIFF_Write32(1); // number of streams
534 SCR_CaptureVideo_RIFF_Write32(0); // suggested buffer size
535 SCR_CaptureVideo_RIFF_Write32(width); // width
536 SCR_CaptureVideo_RIFF_Write32(height); // height
537 SCR_CaptureVideo_RIFF_Write32(0); // reserved[0]
538 SCR_CaptureVideo_RIFF_Write32(0); // reserved[1]
539 SCR_CaptureVideo_RIFF_Write32(0); // reserved[2]
540 SCR_CaptureVideo_RIFF_Write32(0); // reserved[3]
541 SCR_CaptureVideo_RIFF_Pop();
543 SCR_CaptureVideo_RIFF_Push("LIST", "strl", format->canseek ? -1 : 12+52+8+40+8+68);
544 SCR_CaptureVideo_RIFF_Push("strh", "vids", 52);
545 SCR_CaptureVideo_RIFF_WriteFourCC("I420"); // stream fourcc (I420 colorspace, uncompressed)
546 SCR_CaptureVideo_RIFF_Write32(0); // flags
547 SCR_CaptureVideo_RIFF_Write16(0); // priority
548 SCR_CaptureVideo_RIFF_Write16(0); // language
549 SCR_CaptureVideo_RIFF_Write32(0); // initial frames
550 // find an ideal divisor for the framerate
551 FindFraction(cls.capturevideo.framerate / cls.capturevideo.framestep, &n, &d, 1000);
552 SCR_CaptureVideo_RIFF_Write32(d); // samples/second divisor
553 SCR_CaptureVideo_RIFF_Write32(n); // samples/second multiplied by divisor
554 SCR_CaptureVideo_RIFF_Write32(0); // start
555 format->videofile_totalframes_offset1 = SCR_CaptureVideo_RIFF_GetPosition();
556 SCR_CaptureVideo_RIFF_Write32(0xFFFFFFFF); // length
557 SCR_CaptureVideo_RIFF_Write32(width*height+(width/2)*(height/2)*2); // suggested buffer size
558 SCR_CaptureVideo_RIFF_Write32(0); // quality
559 SCR_CaptureVideo_RIFF_Write32(0); // sample size
560 SCR_CaptureVideo_RIFF_Write16(0); // frame left
561 SCR_CaptureVideo_RIFF_Write16(0); // frame top
562 SCR_CaptureVideo_RIFF_Write16(width); // frame right
563 SCR_CaptureVideo_RIFF_Write16(height); // frame bottom
564 SCR_CaptureVideo_RIFF_Pop();
565 // video stream format
566 SCR_CaptureVideo_RIFF_Push("strf", NULL, 40);
567 SCR_CaptureVideo_RIFF_Write32(40); // BITMAPINFO struct size
568 SCR_CaptureVideo_RIFF_Write32(width); // width
569 SCR_CaptureVideo_RIFF_Write32(height); // height
570 SCR_CaptureVideo_RIFF_Write16(3); // planes
571 SCR_CaptureVideo_RIFF_Write16(12); // bitcount
572 SCR_CaptureVideo_RIFF_WriteFourCC("I420"); // compression
573 SCR_CaptureVideo_RIFF_Write32(width*height+(width/2)*(height/2)*2); // size of image
574 SCR_CaptureVideo_RIFF_Write32(0); // x pixels per meter
575 SCR_CaptureVideo_RIFF_Write32(0); // y pixels per meter
576 SCR_CaptureVideo_RIFF_Write32(0); // color used
577 SCR_CaptureVideo_RIFF_Write32(0); // color important
578 SCR_CaptureVideo_RIFF_Pop();
582 SCR_CaptureVideo_RIFF_Push("indx", NULL, -1);
583 SCR_CaptureVideo_RIFF_Write16(4); // wLongsPerEntry
584 SCR_CaptureVideo_RIFF_Write16(0); // bIndexSubType=0, bIndexType=0
585 format->videofile_ix_master_video_inuse_offset = SCR_CaptureVideo_RIFF_GetPosition();
586 SCR_CaptureVideo_RIFF_Write32(0); // nEntriesInUse
587 SCR_CaptureVideo_RIFF_WriteFourCC("00dc"); // dwChunkId
588 SCR_CaptureVideo_RIFF_Write32(0); // dwReserved1
589 SCR_CaptureVideo_RIFF_Write32(0); // dwReserved2
590 SCR_CaptureVideo_RIFF_Write32(0); // dwReserved3
591 format->videofile_ix_master_video_start_offset = SCR_CaptureVideo_RIFF_GetPosition();
592 for(i = 0; i < AVI_MASTER_INDEX_SIZE * 4; ++i)
593 SCR_CaptureVideo_RIFF_Write32(0); // fill up later
594 SCR_CaptureVideo_RIFF_Pop();
596 // extended format (aspect!)
597 SCR_CaptureVideo_RIFF_Push("vprp", NULL, 68);
598 SCR_CaptureVideo_RIFF_Write32(0); // VideoFormatToken
599 SCR_CaptureVideo_RIFF_Write32(0); // VideoStandard
600 SCR_CaptureVideo_RIFF_Write32((int)(cls.capturevideo.framerate / cls.capturevideo.framestep)); // dwVerticalRefreshRate (bogus)
601 SCR_CaptureVideo_RIFF_Write32(width); // dwHTotalInT
602 SCR_CaptureVideo_RIFF_Write32(height); // dwVTotalInLines
603 FindFraction(aspect, &n, &d, 1000);
604 SCR_CaptureVideo_RIFF_Write32((n << 16) | d); // dwFrameAspectRatio // TODO a word
605 SCR_CaptureVideo_RIFF_Write32(width); // dwFrameWidthInPixels
606 SCR_CaptureVideo_RIFF_Write32(height); // dwFrameHeightInLines
607 SCR_CaptureVideo_RIFF_Write32(1); // nFieldPerFrame
608 SCR_CaptureVideo_RIFF_Write32(width); // CompressedBMWidth
609 SCR_CaptureVideo_RIFF_Write32(height); // CompressedBMHeight
610 SCR_CaptureVideo_RIFF_Write32(width); // ValidBMHeight
611 SCR_CaptureVideo_RIFF_Write32(height); // ValidBMWidth
612 SCR_CaptureVideo_RIFF_Write32(0); // ValidBMXOffset
613 SCR_CaptureVideo_RIFF_Write32(0); // ValidBMYOffset
614 SCR_CaptureVideo_RIFF_Write32(0); // ValidBMXOffsetInT
615 SCR_CaptureVideo_RIFF_Write32(0); // ValidBMYValidStartLine
616 SCR_CaptureVideo_RIFF_Pop();
617 SCR_CaptureVideo_RIFF_Pop();
618 if (cls.capturevideo.soundrate)
621 SCR_CaptureVideo_RIFF_Push("LIST", "strl", format->canseek ? -1 : 12+52+8+18);
622 SCR_CaptureVideo_RIFF_Push("strh", "auds", 52);
623 SCR_CaptureVideo_RIFF_Write32(1); // stream fourcc (PCM audio, uncompressed)
624 SCR_CaptureVideo_RIFF_Write32(0); // flags
625 SCR_CaptureVideo_RIFF_Write16(0); // priority
626 SCR_CaptureVideo_RIFF_Write16(0); // language
627 SCR_CaptureVideo_RIFF_Write32(0); // initial frames
628 SCR_CaptureVideo_RIFF_Write32(1); // samples/second divisor
629 SCR_CaptureVideo_RIFF_Write32((int)(cls.capturevideo.soundrate)); // samples/second multiplied by divisor
630 SCR_CaptureVideo_RIFF_Write32(0); // start
631 format->videofile_totalsampleframes_offset = SCR_CaptureVideo_RIFF_GetPosition();
632 SCR_CaptureVideo_RIFF_Write32(0xFFFFFFFF); // length
633 SCR_CaptureVideo_RIFF_Write32(cls.capturevideo.soundrate * 2); // suggested buffer size (this is a half second)
634 SCR_CaptureVideo_RIFF_Write32(0); // quality
635 SCR_CaptureVideo_RIFF_Write32(4); // sample size
636 SCR_CaptureVideo_RIFF_Write16(0); // frame left
637 SCR_CaptureVideo_RIFF_Write16(0); // frame top
638 SCR_CaptureVideo_RIFF_Write16(0); // frame right
639 SCR_CaptureVideo_RIFF_Write16(0); // frame bottom
640 SCR_CaptureVideo_RIFF_Pop();
641 // audio stream format
642 SCR_CaptureVideo_RIFF_Push("strf", NULL, 18);
643 SCR_CaptureVideo_RIFF_Write16(1); // format (uncompressed PCM?)
644 SCR_CaptureVideo_RIFF_Write16(2); // channels (stereo)
645 SCR_CaptureVideo_RIFF_Write32(cls.capturevideo.soundrate); // sampleframes per second
646 SCR_CaptureVideo_RIFF_Write32(cls.capturevideo.soundrate * 4); // average bytes per second
647 SCR_CaptureVideo_RIFF_Write16(4); // block align
648 SCR_CaptureVideo_RIFF_Write16(16); // bits per sample
649 SCR_CaptureVideo_RIFF_Write16(0); // size
650 SCR_CaptureVideo_RIFF_Pop();
654 SCR_CaptureVideo_RIFF_Push("indx", NULL, -1);
655 SCR_CaptureVideo_RIFF_Write16(4); // wLongsPerEntry
656 SCR_CaptureVideo_RIFF_Write16(0); // bIndexSubType=0, bIndexType=0
657 format->videofile_ix_master_audio_inuse_offset = SCR_CaptureVideo_RIFF_GetPosition();
658 SCR_CaptureVideo_RIFF_Write32(0); // nEntriesInUse
659 SCR_CaptureVideo_RIFF_WriteFourCC("01wb"); // dwChunkId
660 SCR_CaptureVideo_RIFF_Write32(0); // dwReserved1
661 SCR_CaptureVideo_RIFF_Write32(0); // dwReserved2
662 SCR_CaptureVideo_RIFF_Write32(0); // dwReserved3
663 format->videofile_ix_master_audio_start_offset = SCR_CaptureVideo_RIFF_GetPosition();
664 for(i = 0; i < AVI_MASTER_INDEX_SIZE * 4; ++i)
665 SCR_CaptureVideo_RIFF_Write32(0); // fill up later
666 SCR_CaptureVideo_RIFF_Pop();
668 SCR_CaptureVideo_RIFF_Pop();
671 format->videofile_ix_master_audio_inuse = format->videofile_ix_master_video_inuse = 0;
673 // extended header (for total #frames)
674 SCR_CaptureVideo_RIFF_Push("LIST", "odml", 8+4);
675 SCR_CaptureVideo_RIFF_Push("dmlh", NULL, 4);
676 format->videofile_totalframes_offset2 = SCR_CaptureVideo_RIFF_GetPosition();
677 SCR_CaptureVideo_RIFF_Write32(0xFFFFFFFF);
678 SCR_CaptureVideo_RIFF_Pop();
679 SCR_CaptureVideo_RIFF_Pop();
681 // close the AVI header list
682 SCR_CaptureVideo_RIFF_Pop();
683 // software that produced this AVI video file
684 SCR_CaptureVideo_RIFF_Push("LIST", "INFO", 8+((strlen(engineversion) | 1) + 1));
685 SCR_CaptureVideo_RIFF_Push("ISFT", NULL, strlen(engineversion) + 1);
686 SCR_CaptureVideo_RIFF_WriteTerminatedString(engineversion);
687 SCR_CaptureVideo_RIFF_Pop();
688 // enable this junk filler if you like the LIST movi to always begin at 4KB in the file (why?)
690 SCR_CaptureVideo_RIFF_Push("JUNK", NULL);
691 x = 4096 - SCR_CaptureVideo_RIFF_GetPosition();
694 const char *junkfiller = "[ DarkPlaces junk data ]";
695 int i = min(x, (int)strlen(junkfiller));
696 SCR_CaptureVideo_RIFF_WriteBytes((const unsigned char *)junkfiller, i);
699 SCR_CaptureVideo_RIFF_Pop();
701 SCR_CaptureVideo_RIFF_Pop();
702 // begin the actual video section now
703 SCR_CaptureVideo_RIFF_Push("LIST", "movi", format->canseek ? -1 : 0);
704 format->videofile_ix_movistart = format->riffstackstartoffset[1];
705 // we're done with the headers now...
706 SCR_CaptureVideo_RIFF_Flush();
707 if (format->riffstacklevel != 2)
708 Sys_Error("SCR_CaptureVideo_BeginVideo: broken AVI writing code (stack level is %i (should be 2) at end of headers)\n", format->riffstacklevel);
712 // close the movi immediately
713 SCR_CaptureVideo_RIFF_Pop();
714 // close the AVI immediately (we'll put all frames into AVIX)
715 SCR_CaptureVideo_RIFF_Pop();