2
===========================================================================
3
Copyright (C) 2005-2006 Tim Angus
5
This file is part of Quake III Arena source code.
7
Quake III Arena source code is free software; you can redistribute it
8
and/or modify it under the terms of the GNU General Public License as
9
published by the Free Software Foundation; either version 2 of the License,
10
or (at your option) any later version.
12
Quake III Arena source code is distributed in the hope that it will be
13
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
GNU General Public License for more details.
17
You should have received a copy of the GNU General Public License
18
along with Quake III Arena source code; if not, write to the Free Software
19
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20
===========================================================================
24
#include "snd_local.h"
26
#define INDEX_FILE_EXTENSION ".index.dat"
28
#define MAX_RIFF_CHUNKS 16
30
typedef struct audioFormat_s
41
typedef struct aviFileData_s
45
char fileName[ MAX_QPATH ];
64
int chunkStack[ MAX_RIFF_CHUNKS ];
67
byte *cBuffer, *eBuffer;
70
static aviFileData_t afd;
72
#define MAX_AVI_BUFFER 2048
74
static byte buffer[ MAX_AVI_BUFFER ];
82
static ID_INLINE void SafeFS_Write( const void *buffer, int len, fileHandle_t f )
84
if( FS_Write( buffer, len, f ) < len )
85
Com_Error( ERR_DROP, "Failed to write avi file\n" );
93
static ID_INLINE void WRITE_STRING( const char *s )
95
Com_Memcpy( &buffer[ bufIndex ], s, strlen( s ) );
96
bufIndex += strlen( s );
104
static ID_INLINE void WRITE_4BYTES( int x )
106
buffer[ bufIndex + 0 ] = (byte)( ( x >> 0 ) & 0xFF );
107
buffer[ bufIndex + 1 ] = (byte)( ( x >> 8 ) & 0xFF );
108
buffer[ bufIndex + 2 ] = (byte)( ( x >> 16 ) & 0xFF );
109
buffer[ bufIndex + 3 ] = (byte)( ( x >> 24 ) & 0xFF );
118
static ID_INLINE void WRITE_2BYTES( int x )
120
buffer[ bufIndex + 0 ] = (byte)( ( x >> 0 ) & 0xFF );
121
buffer[ bufIndex + 1 ] = (byte)( ( x >> 8 ) & 0xFF );
130
static ID_INLINE void WRITE_1BYTES( int x )
132
buffer[ bufIndex ] = x;
141
static ID_INLINE void START_CHUNK( const char *s )
143
if( afd.chunkStackTop == MAX_RIFF_CHUNKS )
145
Com_Error( ERR_DROP, "ERROR: Top of chunkstack breached\n" );
148
afd.chunkStack[ afd.chunkStackTop ] = bufIndex;
159
static ID_INLINE void END_CHUNK( void )
161
int endIndex = bufIndex;
163
if( afd.chunkStackTop <= 0 )
165
Com_Error( ERR_DROP, "ERROR: Bottom of chunkstack breached\n" );
169
bufIndex = afd.chunkStack[ afd.chunkStackTop ];
171
WRITE_4BYTES( endIndex - bufIndex - 4 );
173
bufIndex = PAD( bufIndex, 2 );
181
void CL_WriteAVIHeader( void )
184
afd.chunkStackTop = 0;
186
START_CHUNK( "RIFF" );
188
WRITE_STRING( "AVI " );
190
START_CHUNK( "LIST" );
192
WRITE_STRING( "hdrl" );
193
WRITE_STRING( "avih" );
194
WRITE_4BYTES( 56 ); //"avih" "chunk" size
195
WRITE_4BYTES( afd.framePeriod ); //dwMicroSecPerFrame
196
WRITE_4BYTES( afd.maxRecordSize *
197
afd.frameRate ); //dwMaxBytesPerSec
198
WRITE_4BYTES( 0 ); //dwReserved1
199
WRITE_4BYTES( 0x110 ); //dwFlags bits HAS_INDEX and IS_INTERLEAVED
200
WRITE_4BYTES( afd.numVideoFrames ); //dwTotalFrames
201
WRITE_4BYTES( 0 ); //dwInitialFrame
203
if( afd.audio ) //dwStreams
208
WRITE_4BYTES( afd.maxRecordSize ); //dwSuggestedBufferSize
209
WRITE_4BYTES( afd.width ); //dwWidth
210
WRITE_4BYTES( afd.height ); //dwHeight
211
WRITE_4BYTES( 0 ); //dwReserved[ 0 ]
212
WRITE_4BYTES( 0 ); //dwReserved[ 1 ]
213
WRITE_4BYTES( 0 ); //dwReserved[ 2 ]
214
WRITE_4BYTES( 0 ); //dwReserved[ 3 ]
216
START_CHUNK( "LIST" );
218
WRITE_STRING( "strl" );
219
WRITE_STRING( "strh" );
220
WRITE_4BYTES( 56 ); //"strh" "chunk" size
221
WRITE_STRING( "vids" );
224
WRITE_STRING( "MJPG" );
226
WRITE_4BYTES( 0 ); // BI_RGB
228
WRITE_4BYTES( 0 ); //dwFlags
229
WRITE_4BYTES( 0 ); //dwPriority
230
WRITE_4BYTES( 0 ); //dwInitialFrame
232
WRITE_4BYTES( 1 ); //dwTimescale
233
WRITE_4BYTES( afd.frameRate ); //dwDataRate
234
WRITE_4BYTES( 0 ); //dwStartTime
235
WRITE_4BYTES( afd.numVideoFrames ); //dwDataLength
237
WRITE_4BYTES( afd.maxRecordSize ); //dwSuggestedBufferSize
238
WRITE_4BYTES( -1 ); //dwQuality
239
WRITE_4BYTES( 0 ); //dwSampleSize
240
WRITE_2BYTES( 0 ); //rcFrame
241
WRITE_2BYTES( 0 ); //rcFrame
242
WRITE_2BYTES( afd.width ); //rcFrame
243
WRITE_2BYTES( afd.height ); //rcFrame
245
WRITE_STRING( "strf" );
246
WRITE_4BYTES( 40 ); //"strf" "chunk" size
247
WRITE_4BYTES( 40 ); //biSize
248
WRITE_4BYTES( afd.width ); //biWidth
249
WRITE_4BYTES( afd.height ); //biHeight
250
WRITE_2BYTES( 1 ); //biPlanes
251
WRITE_2BYTES( 24 ); //biBitCount
253
if( afd.motionJpeg ) { //biCompression
254
WRITE_STRING( "MJPG" );
255
WRITE_4BYTES( afd.width *
256
afd.height ); //biSizeImage
258
WRITE_4BYTES( 0 ); // BI_RGB
259
WRITE_4BYTES( afd.width *
260
afd.height*3 ); //biSizeImage
263
WRITE_4BYTES( 0 ); //biXPelsPetMeter
264
WRITE_4BYTES( 0 ); //biYPelsPetMeter
265
WRITE_4BYTES( 0 ); //biClrUsed
266
WRITE_4BYTES( 0 ); //biClrImportant
272
START_CHUNK( "LIST" );
274
WRITE_STRING( "strl" );
275
WRITE_STRING( "strh" );
276
WRITE_4BYTES( 56 ); //"strh" "chunk" size
277
WRITE_STRING( "auds" );
278
WRITE_4BYTES( 0 ); //FCC
279
WRITE_4BYTES( 0 ); //dwFlags
280
WRITE_4BYTES( 0 ); //dwPriority
281
WRITE_4BYTES( 0 ); //dwInitialFrame
283
WRITE_4BYTES( afd.a.sampleSize ); //dwTimescale
284
WRITE_4BYTES( afd.a.sampleSize *
285
afd.a.rate ); //dwDataRate
286
WRITE_4BYTES( 0 ); //dwStartTime
287
WRITE_4BYTES( afd.a.totalBytes /
288
afd.a.sampleSize ); //dwDataLength
290
WRITE_4BYTES( 0 ); //dwSuggestedBufferSize
291
WRITE_4BYTES( -1 ); //dwQuality
292
WRITE_4BYTES( afd.a.sampleSize ); //dwSampleSize
293
WRITE_2BYTES( 0 ); //rcFrame
294
WRITE_2BYTES( 0 ); //rcFrame
295
WRITE_2BYTES( 0 ); //rcFrame
296
WRITE_2BYTES( 0 ); //rcFrame
298
WRITE_STRING( "strf" );
299
WRITE_4BYTES( 18 ); //"strf" "chunk" size
300
WRITE_2BYTES( afd.a.format ); //wFormatTag
301
WRITE_2BYTES( afd.a.channels ); //nChannels
302
WRITE_4BYTES( afd.a.rate ); //nSamplesPerSec
303
WRITE_4BYTES( afd.a.sampleSize *
304
afd.a.rate ); //nAvgBytesPerSec
305
WRITE_2BYTES( afd.a.sampleSize ); //nBlockAlign
306
WRITE_2BYTES( afd.a.bits ); //wBitsPerSample
307
WRITE_2BYTES( 0 ); //cbSize
314
afd.moviOffset = bufIndex;
316
START_CHUNK( "LIST" );
318
WRITE_STRING( "movi" );
328
Creates an AVI file and gets it into a state where
329
writing the actual data can begin
332
qboolean CL_OpenAVIForWriting( const char *fileName )
337
Com_Memset( &afd, 0, sizeof( aviFileData_t ) );
339
// Don't start if a framerate has not been chosen
340
if( cl_aviFrameRate->integer <= 0 )
342
Com_Printf( S_COLOR_RED "cl_aviFrameRate must be >= 1\n" );
346
if( ( afd.f = FS_FOpenFileWrite( fileName ) ) <= 0 )
349
if( ( afd.idxF = FS_FOpenFileWrite(
350
va( "%s" INDEX_FILE_EXTENSION, fileName ) ) ) <= 0 )
352
FS_FCloseFile( afd.f );
356
Q_strncpyz( afd.fileName, fileName, MAX_QPATH );
358
afd.frameRate = cl_aviFrameRate->integer;
359
afd.framePeriod = (int)( 1000000.0f / afd.frameRate );
360
afd.width = cls.glconfig.vidWidth;
361
afd.height = cls.glconfig.vidHeight;
363
if( cl_aviMotionJpeg->integer )
364
afd.motionJpeg = qtrue;
366
afd.motionJpeg = qfalse;
368
afd.cBuffer = Z_Malloc( afd.width * afd.height * 4 );
369
afd.eBuffer = Z_Malloc( afd.width * afd.height * 4 );
371
afd.a.rate = dma.speed;
372
afd.a.format = WAV_FORMAT_PCM;
373
afd.a.channels = dma.channels;
374
afd.a.bits = dma.samplebits;
375
afd.a.sampleSize = ( afd.a.bits / 8 ) * afd.a.channels;
377
if( afd.a.rate % afd.frameRate )
379
int suggestRate = afd.frameRate;
381
while( ( afd.a.rate % suggestRate ) && suggestRate >= 1 )
384
Com_Printf( S_COLOR_YELLOW "WARNING: cl_aviFrameRate is not a divisor "
385
"of the audio rate, suggest %d\n", suggestRate );
388
if( !Cvar_VariableIntegerValue( "s_initsound" ) )
392
else if( Q_stricmp( Cvar_VariableString( "s_backend" ), "OpenAL" ) )
394
if( afd.a.bits == 16 && afd.a.channels == 2 )
397
afd.audio = qfalse; //FIXME: audio not implemented for this case
402
Com_Printf( S_COLOR_YELLOW "WARNING: Audio capture is not supported "
403
"with OpenAL. Set s_useOpenAL to 0 for audio capture\n" );
406
// This doesn't write a real header, but allocates the
407
// correct amount of space at the beginning of the file
408
CL_WriteAVIHeader( );
410
SafeFS_Write( buffer, bufIndex, afd.f );
411
afd.fileSize = bufIndex;
414
START_CHUNK( "idx1" );
415
SafeFS_Write( buffer, bufIndex, afd.idxF );
417
afd.moviSize = 4; // For the "movi"
418
afd.fileOpen = qtrue;
428
static qboolean CL_CheckFileSize( int bytesToAdd )
430
unsigned int newFileSize;
433
afd.fileSize + // Current file size
434
bytesToAdd + // What we want to add
435
( afd.numIndices * 16 ) + // The index
438
// I assume all the operating systems
439
// we target can handle a 2Gb file
440
if( newFileSize > INT_MAX )
442
// Close the current file...
445
// ...And open a new one
446
CL_OpenAVIForWriting( va( "%s_", afd.fileName ) );
456
CL_WriteAVIVideoFrame
459
void CL_WriteAVIVideoFrame( const byte *imageBuffer, int size )
461
int chunkOffset = afd.fileSize - afd.moviOffset - 8;
462
int chunkSize = 8 + size;
463
int paddingSize = PAD( size, 2 ) - size;
464
byte padding[ 4 ] = { 0 };
469
// Chunk header + contents + padding
470
if( CL_CheckFileSize( 8 + size + 2 ) )
474
WRITE_STRING( "00dc" );
475
WRITE_4BYTES( size );
477
SafeFS_Write( buffer, 8, afd.f );
478
SafeFS_Write( imageBuffer, size, afd.f );
479
SafeFS_Write( padding, paddingSize, afd.f );
480
afd.fileSize += ( chunkSize + paddingSize );
482
afd.numVideoFrames++;
483
afd.moviSize += ( chunkSize + paddingSize );
485
if( size > afd.maxRecordSize )
486
afd.maxRecordSize = size;
490
WRITE_STRING( "00dc" ); //dwIdentifier
491
WRITE_4BYTES( 0x00000010 ); //dwFlags (all frames are KeyFrames)
492
WRITE_4BYTES( chunkOffset ); //dwOffset
493
WRITE_4BYTES( size ); //dwLength
494
SafeFS_Write( buffer, 16, afd.idxF );
499
#define PCM_BUFFER_SIZE 44100
503
CL_WriteAVIAudioFrame
506
void CL_WriteAVIAudioFrame( const byte *pcmBuffer, int size )
508
static byte pcmCaptureBuffer[ PCM_BUFFER_SIZE ] = { 0 };
509
static int bytesInBuffer = 0;
517
// Chunk header + contents + padding
518
if( CL_CheckFileSize( 8 + bytesInBuffer + size + 2 ) )
521
if( bytesInBuffer + size > PCM_BUFFER_SIZE )
523
Com_Printf( S_COLOR_YELLOW
524
"WARNING: Audio capture buffer overflow -- truncating\n" );
525
size = PCM_BUFFER_SIZE - bytesInBuffer;
528
Com_Memcpy( &pcmCaptureBuffer[ bytesInBuffer ], pcmBuffer, size );
529
bytesInBuffer += size;
531
// Only write if we have a frame's worth of audio
532
if( bytesInBuffer >= (int)ceil( (float)afd.a.rate / (float)afd.frameRate ) *
535
int chunkOffset = afd.fileSize - afd.moviOffset - 8;
536
int chunkSize = 8 + bytesInBuffer;
537
int paddingSize = PAD( bytesInBuffer, 2 ) - bytesInBuffer;
538
byte padding[ 4 ] = { 0 };
541
WRITE_STRING( "01wb" );
542
WRITE_4BYTES( bytesInBuffer );
544
SafeFS_Write( buffer, 8, afd.f );
545
SafeFS_Write( pcmBuffer, bytesInBuffer, afd.f );
546
SafeFS_Write( padding, paddingSize, afd.f );
547
afd.fileSize += ( chunkSize + paddingSize );
549
afd.numAudioFrames++;
550
afd.moviSize += ( chunkSize + paddingSize );
551
afd.a.totalBytes =+ bytesInBuffer;
555
WRITE_STRING( "01wb" ); //dwIdentifier
556
WRITE_4BYTES( 0 ); //dwFlags
557
WRITE_4BYTES( chunkOffset ); //dwOffset
558
WRITE_4BYTES( bytesInBuffer ); //dwLength
559
SafeFS_Write( buffer, 16, afd.idxF );
572
void CL_TakeVideoFrame( void )
574
// AVI file isn't open
578
re.TakeVideoFrame( afd.width, afd.height,
579
afd.cBuffer, afd.eBuffer, afd.motionJpeg );
586
Closes the AVI file and writes an index chunk
589
qboolean CL_CloseAVI( void )
592
int indexSize = afd.numIndices * 16;
593
const char *idxFileName = va( "%s" INDEX_FILE_EXTENSION, afd.fileName );
595
// AVI file isn't open
599
afd.fileOpen = qfalse;
601
FS_Seek( afd.idxF, 4, FS_SEEK_SET );
603
WRITE_4BYTES( indexSize );
604
SafeFS_Write( buffer, bufIndex, afd.idxF );
605
FS_FCloseFile( afd.idxF );
609
// Open the temp index file
610
if( ( indexSize = FS_FOpenFileRead( idxFileName,
611
&afd.idxF, qtrue ) ) <= 0 )
613
FS_FCloseFile( afd.f );
617
indexRemainder = indexSize;
619
// Append index to end of avi file
620
while( indexRemainder > MAX_AVI_BUFFER )
622
FS_Read( buffer, MAX_AVI_BUFFER, afd.idxF );
623
SafeFS_Write( buffer, MAX_AVI_BUFFER, afd.f );
624
afd.fileSize += MAX_AVI_BUFFER;
625
indexRemainder -= MAX_AVI_BUFFER;
627
FS_Read( buffer, indexRemainder, afd.idxF );
628
SafeFS_Write( buffer, indexRemainder, afd.f );
629
afd.fileSize += indexRemainder;
630
FS_FCloseFile( afd.idxF );
632
// Remove temp index file
633
FS_HomeRemove( idxFileName );
635
// Write the real header
636
FS_Seek( afd.f, 0, FS_SEEK_SET );
637
CL_WriteAVIHeader( );
640
WRITE_4BYTES( afd.fileSize - 8 ); // "RIFF" size
642
bufIndex = afd.moviOffset + 4; // Skip "LIST"
643
WRITE_4BYTES( afd.moviSize );
645
SafeFS_Write( buffer, bufIndex, afd.f );
647
Z_Free( afd.cBuffer );
648
Z_Free( afd.eBuffer );
649
FS_FCloseFile( afd.f );
651
Com_Printf( "Wrote %d:%d frames to %s\n", afd.numVideoFrames, afd.numAudioFrames, afd.fileName );
661
qboolean CL_VideoRecording( void )