~ubuntu-branches/ubuntu/precise/openarena/precise

« back to all changes in this revision

Viewing changes to code/client/cl_avi.c

  • Committer: Bazaar Package Importer
  • Author(s): Bruno "Fuddl" Kleinert
  • Date: 2007-01-20 12:28:09 UTC
  • Revision ID: james.westby@ubuntu.com-20070120122809-2yza5ojt7nqiyiam
Tags: upstream-0.6.0
ImportĀ upstreamĀ versionĀ 0.6.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
===========================================================================
 
3
Copyright (C) 2005-2006 Tim Angus
 
4
 
 
5
This file is part of Quake III Arena source code.
 
6
 
 
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.
 
11
 
 
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.
 
16
 
 
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
===========================================================================
 
21
*/
 
22
 
 
23
#include "client.h"
 
24
#include "snd_local.h"
 
25
 
 
26
#define INDEX_FILE_EXTENSION ".index.dat"
 
27
 
 
28
#define MAX_RIFF_CHUNKS 16
 
29
 
 
30
typedef struct audioFormat_s
 
31
{
 
32
  int rate;
 
33
  int format;
 
34
  int channels;
 
35
  int bits;
 
36
 
 
37
  int sampleSize;
 
38
  int totalBytes;
 
39
} audioFormat_t;
 
40
 
 
41
typedef struct aviFileData_s
 
42
{
 
43
  qboolean      fileOpen;
 
44
  fileHandle_t  f;
 
45
  char          fileName[ MAX_QPATH ];
 
46
  int           fileSize;
 
47
  int           moviOffset;
 
48
  int           moviSize;
 
49
 
 
50
  fileHandle_t  idxF;
 
51
  int           numIndices;
 
52
 
 
53
  int           frameRate;
 
54
  int           framePeriod;
 
55
  int           width, height;
 
56
  int           numVideoFrames;
 
57
  int           maxRecordSize;
 
58
  qboolean      motionJpeg;
 
59
 
 
60
  qboolean      audio;
 
61
  audioFormat_t a;
 
62
  int           numAudioFrames;
 
63
 
 
64
  int           chunkStack[ MAX_RIFF_CHUNKS ];
 
65
  int           chunkStackTop;
 
66
 
 
67
  byte          *cBuffer, *eBuffer;
 
68
} aviFileData_t;
 
69
 
 
70
static aviFileData_t afd;
 
71
 
 
72
#define MAX_AVI_BUFFER 2048
 
73
 
 
74
static byte buffer[ MAX_AVI_BUFFER ];
 
75
static int  bufIndex;
 
76
 
 
77
/*
 
78
===============
 
79
SafeFS_Write
 
80
===============
 
81
*/
 
82
static ID_INLINE void SafeFS_Write( const void *buffer, int len, fileHandle_t f )
 
83
{
 
84
  if( FS_Write( buffer, len, f ) < len )
 
85
    Com_Error( ERR_DROP, "Failed to write avi file\n" );
 
86
}
 
87
 
 
88
/*
 
89
===============
 
90
WRITE_STRING
 
91
===============
 
92
*/
 
93
static ID_INLINE void WRITE_STRING( const char *s )
 
94
{
 
95
  Com_Memcpy( &buffer[ bufIndex ], s, strlen( s ) );
 
96
  bufIndex += strlen( s );
 
97
}
 
98
 
 
99
/*
 
100
===============
 
101
WRITE_4BYTES
 
102
===============
 
103
*/
 
104
static ID_INLINE void WRITE_4BYTES( int x )
 
105
{
 
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 );
 
110
  bufIndex += 4;
 
111
}
 
112
 
 
113
/*
 
114
===============
 
115
WRITE_2BYTES
 
116
===============
 
117
*/
 
118
static ID_INLINE void WRITE_2BYTES( int x )
 
119
{
 
120
  buffer[ bufIndex + 0 ] = (byte)( ( x >>  0 ) & 0xFF );
 
121
  buffer[ bufIndex + 1 ] = (byte)( ( x >>  8 ) & 0xFF );
 
122
  bufIndex += 2;
 
123
}
 
124
 
 
125
/*
 
126
===============
 
127
WRITE_1BYTES
 
128
===============
 
129
*/
 
130
static ID_INLINE void WRITE_1BYTES( int x )
 
131
{
 
132
  buffer[ bufIndex ] = x;
 
133
  bufIndex += 1;
 
134
}
 
135
 
 
136
/*
 
137
===============
 
138
START_CHUNK
 
139
===============
 
140
*/
 
141
static ID_INLINE void START_CHUNK( const char *s )
 
142
{
 
143
  if( afd.chunkStackTop == MAX_RIFF_CHUNKS )
 
144
  {
 
145
    Com_Error( ERR_DROP, "ERROR: Top of chunkstack breached\n" );
 
146
  }
 
147
 
 
148
  afd.chunkStack[ afd.chunkStackTop ] = bufIndex;
 
149
  afd.chunkStackTop++;
 
150
  WRITE_STRING( s );
 
151
  WRITE_4BYTES( 0 );
 
152
}
 
153
 
 
154
/*
 
155
===============
 
156
END_CHUNK
 
157
===============
 
158
*/
 
159
static ID_INLINE void END_CHUNK( void )
 
160
{
 
161
  int endIndex = bufIndex;
 
162
 
 
163
  if( afd.chunkStackTop <= 0 )
 
164
  {
 
165
    Com_Error( ERR_DROP, "ERROR: Bottom of chunkstack breached\n" );
 
166
  }
 
167
 
 
168
  afd.chunkStackTop--;
 
169
  bufIndex = afd.chunkStack[ afd.chunkStackTop ];
 
170
  bufIndex += 4;
 
171
  WRITE_4BYTES( endIndex - bufIndex - 4 );
 
172
  bufIndex = endIndex;
 
173
  bufIndex = PAD( bufIndex, 2 );
 
174
}
 
175
 
 
176
/*
 
177
===============
 
178
CL_WriteAVIHeader
 
179
===============
 
180
*/
 
181
void CL_WriteAVIHeader( void )
 
182
{
 
183
  bufIndex = 0;
 
184
  afd.chunkStackTop = 0;
 
185
 
 
186
  START_CHUNK( "RIFF" );
 
187
  {
 
188
    WRITE_STRING( "AVI " );
 
189
    {
 
190
      START_CHUNK( "LIST" );
 
191
      {
 
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
 
202
 
 
203
        if( afd.audio )                         //dwStreams
 
204
          WRITE_4BYTES( 2 );
 
205
        else
 
206
          WRITE_4BYTES( 1 );
 
207
 
 
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 ]
 
215
 
 
216
        START_CHUNK( "LIST" );
 
217
        {
 
218
          WRITE_STRING( "strl" );
 
219
          WRITE_STRING( "strh" );
 
220
          WRITE_4BYTES( 56 );                   //"strh" "chunk" size
 
221
          WRITE_STRING( "vids" );
 
222
 
 
223
          if( afd.motionJpeg )
 
224
            WRITE_STRING( "MJPG" );
 
225
          else
 
226
            WRITE_4BYTES( 0 );                  // BI_RGB
 
227
 
 
228
          WRITE_4BYTES( 0 );                    //dwFlags
 
229
          WRITE_4BYTES( 0 );                    //dwPriority
 
230
          WRITE_4BYTES( 0 );                    //dwInitialFrame
 
231
 
 
232
          WRITE_4BYTES( 1 );                    //dwTimescale
 
233
          WRITE_4BYTES( afd.frameRate );        //dwDataRate
 
234
          WRITE_4BYTES( 0 );                    //dwStartTime
 
235
          WRITE_4BYTES( afd.numVideoFrames );   //dwDataLength
 
236
 
 
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
 
244
 
 
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
 
252
 
 
253
          if( afd.motionJpeg )   {              //biCompression
 
254
            WRITE_STRING( "MJPG" );
 
255
            WRITE_4BYTES( afd.width *
 
256
              afd.height );                     //biSizeImage
 
257
          } else {
 
258
            WRITE_4BYTES( 0 );                  // BI_RGB
 
259
            WRITE_4BYTES( afd.width *
 
260
            afd.height*3 );                     //biSizeImage
 
261
          }
 
262
 
 
263
          WRITE_4BYTES( 0 );                    //biXPelsPetMeter
 
264
          WRITE_4BYTES( 0 );                    //biYPelsPetMeter
 
265
          WRITE_4BYTES( 0 );                    //biClrUsed
 
266
          WRITE_4BYTES( 0 );                    //biClrImportant
 
267
        }
 
268
        END_CHUNK( );
 
269
 
 
270
        if( afd.audio )
 
271
        {
 
272
          START_CHUNK( "LIST" );
 
273
          {
 
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
 
282
 
 
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
 
289
 
 
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
 
297
 
 
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
 
308
          }
 
309
          END_CHUNK( );
 
310
        }
 
311
      }
 
312
      END_CHUNK( );
 
313
 
 
314
      afd.moviOffset = bufIndex;
 
315
 
 
316
      START_CHUNK( "LIST" );
 
317
      {
 
318
        WRITE_STRING( "movi" );
 
319
      }
 
320
    }
 
321
  }
 
322
}
 
323
 
 
324
/*
 
325
===============
 
326
CL_OpenAVIForWriting
 
327
 
 
328
Creates an AVI file and gets it into a state where
 
329
writing the actual data can begin
 
330
===============
 
331
*/
 
332
qboolean CL_OpenAVIForWriting( const char *fileName )
 
333
{
 
334
  if( afd.fileOpen )
 
335
    return qfalse;
 
336
 
 
337
  Com_Memset( &afd, 0, sizeof( aviFileData_t ) );
 
338
 
 
339
  // Don't start if a framerate has not been chosen
 
340
  if( cl_aviFrameRate->integer <= 0 )
 
341
  {
 
342
    Com_Printf( S_COLOR_RED "cl_aviFrameRate must be >= 1\n" );
 
343
    return qfalse;
 
344
  }
 
345
 
 
346
  if( ( afd.f = FS_FOpenFileWrite( fileName ) ) <= 0 )
 
347
    return qfalse;
 
348
 
 
349
  if( ( afd.idxF = FS_FOpenFileWrite(
 
350
          va( "%s" INDEX_FILE_EXTENSION, fileName ) ) ) <= 0 )
 
351
  {
 
352
    FS_FCloseFile( afd.f );
 
353
    return qfalse;
 
354
  }
 
355
 
 
356
  Q_strncpyz( afd.fileName, fileName, MAX_QPATH );
 
357
 
 
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;
 
362
 
 
363
  if( cl_aviMotionJpeg->integer )
 
364
    afd.motionJpeg = qtrue;
 
365
  else
 
366
    afd.motionJpeg = qfalse;
 
367
 
 
368
  afd.cBuffer = Z_Malloc( afd.width * afd.height * 4 );
 
369
  afd.eBuffer = Z_Malloc( afd.width * afd.height * 4 );
 
370
 
 
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;
 
376
 
 
377
  if( afd.a.rate % afd.frameRate )
 
378
  {
 
379
    int suggestRate = afd.frameRate;
 
380
 
 
381
    while( ( afd.a.rate % suggestRate ) && suggestRate >= 1 )
 
382
      suggestRate--;
 
383
 
 
384
    Com_Printf( S_COLOR_YELLOW "WARNING: cl_aviFrameRate is not a divisor "
 
385
        "of the audio rate, suggest %d\n", suggestRate );
 
386
  }
 
387
 
 
388
  if( !Cvar_VariableIntegerValue( "s_initsound" ) )
 
389
  {
 
390
    afd.audio = qfalse;
 
391
  }
 
392
  else if( Q_stricmp( Cvar_VariableString( "s_backend" ), "OpenAL" ) )
 
393
  {
 
394
    if( afd.a.bits == 16 && afd.a.channels == 2 )
 
395
      afd.audio = qtrue;
 
396
    else
 
397
      afd.audio = qfalse; //FIXME: audio not implemented for this case
 
398
  }
 
399
  else
 
400
  {
 
401
    afd.audio = qfalse;
 
402
    Com_Printf( S_COLOR_YELLOW "WARNING: Audio capture is not supported "
 
403
        "with OpenAL. Set s_useOpenAL to 0 for audio capture\n" );
 
404
  }
 
405
 
 
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( );
 
409
 
 
410
  SafeFS_Write( buffer, bufIndex, afd.f );
 
411
  afd.fileSize = bufIndex;
 
412
 
 
413
  bufIndex = 0;
 
414
  START_CHUNK( "idx1" );
 
415
  SafeFS_Write( buffer, bufIndex, afd.idxF );
 
416
 
 
417
  afd.moviSize = 4; // For the "movi"
 
418
  afd.fileOpen = qtrue;
 
419
 
 
420
  return qtrue;
 
421
}
 
422
 
 
423
/*
 
424
===============
 
425
CL_CheckFileSize
 
426
===============
 
427
*/
 
428
static qboolean CL_CheckFileSize( int bytesToAdd )
 
429
{
 
430
  unsigned int newFileSize;
 
431
 
 
432
  newFileSize =
 
433
    afd.fileSize +                // Current file size
 
434
    bytesToAdd +                  // What we want to add
 
435
    ( afd.numIndices * 16 ) +     // The index
 
436
    4;                            // The index size
 
437
 
 
438
  // I assume all the operating systems
 
439
  // we target can handle a 2Gb file
 
440
  if( newFileSize > INT_MAX )
 
441
  {
 
442
    // Close the current file...
 
443
    CL_CloseAVI( );
 
444
 
 
445
    // ...And open a new one
 
446
    CL_OpenAVIForWriting( va( "%s_", afd.fileName ) );
 
447
 
 
448
    return qtrue;
 
449
  }
 
450
 
 
451
  return qfalse;
 
452
}
 
453
 
 
454
/*
 
455
===============
 
456
CL_WriteAVIVideoFrame
 
457
===============
 
458
*/
 
459
void CL_WriteAVIVideoFrame( const byte *imageBuffer, int size )
 
460
{
 
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 };
 
465
 
 
466
  if( !afd.fileOpen )
 
467
    return;
 
468
 
 
469
  // Chunk header + contents + padding
 
470
  if( CL_CheckFileSize( 8 + size + 2 ) )
 
471
    return;
 
472
 
 
473
  bufIndex = 0;
 
474
  WRITE_STRING( "00dc" );
 
475
  WRITE_4BYTES( size );
 
476
 
 
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 );
 
481
 
 
482
  afd.numVideoFrames++;
 
483
  afd.moviSize += ( chunkSize + paddingSize );
 
484
 
 
485
  if( size > afd.maxRecordSize )
 
486
    afd.maxRecordSize = size;
 
487
 
 
488
  // Index
 
489
  bufIndex = 0;
 
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 );
 
495
 
 
496
  afd.numIndices++;
 
497
}
 
498
 
 
499
#define PCM_BUFFER_SIZE 44100
 
500
 
 
501
/*
 
502
===============
 
503
CL_WriteAVIAudioFrame
 
504
===============
 
505
*/
 
506
void CL_WriteAVIAudioFrame( const byte *pcmBuffer, int size )
 
507
{
 
508
  static byte pcmCaptureBuffer[ PCM_BUFFER_SIZE ] = { 0 };
 
509
  static int  bytesInBuffer = 0;
 
510
 
 
511
  if( !afd.audio )
 
512
    return;
 
513
 
 
514
  if( !afd.fileOpen )
 
515
    return;
 
516
 
 
517
  // Chunk header + contents + padding
 
518
  if( CL_CheckFileSize( 8 + bytesInBuffer + size + 2 ) )
 
519
    return;
 
520
 
 
521
  if( bytesInBuffer + size > PCM_BUFFER_SIZE )
 
522
  {
 
523
    Com_Printf( S_COLOR_YELLOW
 
524
        "WARNING: Audio capture buffer overflow -- truncating\n" );
 
525
    size = PCM_BUFFER_SIZE - bytesInBuffer;
 
526
  }
 
527
 
 
528
  Com_Memcpy( &pcmCaptureBuffer[ bytesInBuffer ], pcmBuffer, size );
 
529
  bytesInBuffer += size;
 
530
 
 
531
  // Only write if we have a frame's worth of audio
 
532
  if( bytesInBuffer >= (int)ceil( (float)afd.a.rate / (float)afd.frameRate ) *
 
533
        afd.a.sampleSize )
 
534
  {
 
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 };
 
539
 
 
540
    bufIndex = 0;
 
541
    WRITE_STRING( "01wb" );
 
542
    WRITE_4BYTES( bytesInBuffer );
 
543
 
 
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 );
 
548
 
 
549
    afd.numAudioFrames++;
 
550
    afd.moviSize += ( chunkSize + paddingSize );
 
551
    afd.a.totalBytes =+ bytesInBuffer;
 
552
 
 
553
    // Index
 
554
    bufIndex = 0;
 
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 );
 
560
 
 
561
    afd.numIndices++;
 
562
 
 
563
    bytesInBuffer = 0;
 
564
  }
 
565
}
 
566
 
 
567
/*
 
568
===============
 
569
CL_TakeVideoFrame
 
570
===============
 
571
*/
 
572
void CL_TakeVideoFrame( void )
 
573
{
 
574
  // AVI file isn't open
 
575
  if( !afd.fileOpen )
 
576
    return;
 
577
 
 
578
  re.TakeVideoFrame( afd.width, afd.height,
 
579
      afd.cBuffer, afd.eBuffer, afd.motionJpeg );
 
580
}
 
581
 
 
582
/*
 
583
===============
 
584
CL_CloseAVI
 
585
 
 
586
Closes the AVI file and writes an index chunk
 
587
===============
 
588
*/
 
589
qboolean CL_CloseAVI( void )
 
590
{
 
591
  int indexRemainder;
 
592
  int indexSize = afd.numIndices * 16;
 
593
  const char *idxFileName = va( "%s" INDEX_FILE_EXTENSION, afd.fileName );
 
594
 
 
595
  // AVI file isn't open
 
596
  if( !afd.fileOpen )
 
597
    return qfalse;
 
598
 
 
599
  afd.fileOpen = qfalse;
 
600
 
 
601
  FS_Seek( afd.idxF, 4, FS_SEEK_SET );
 
602
  bufIndex = 0;
 
603
  WRITE_4BYTES( indexSize );
 
604
  SafeFS_Write( buffer, bufIndex, afd.idxF );
 
605
  FS_FCloseFile( afd.idxF );
 
606
 
 
607
  // Write index
 
608
 
 
609
  // Open the temp index file
 
610
  if( ( indexSize = FS_FOpenFileRead( idxFileName,
 
611
          &afd.idxF, qtrue ) ) <= 0 )
 
612
  {
 
613
    FS_FCloseFile( afd.f );
 
614
    return qfalse;
 
615
  }
 
616
 
 
617
  indexRemainder = indexSize;
 
618
 
 
619
  // Append index to end of avi file
 
620
  while( indexRemainder > MAX_AVI_BUFFER )
 
621
  {
 
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;
 
626
  }
 
627
  FS_Read( buffer, indexRemainder, afd.idxF );
 
628
  SafeFS_Write( buffer, indexRemainder, afd.f );
 
629
  afd.fileSize += indexRemainder;
 
630
  FS_FCloseFile( afd.idxF );
 
631
 
 
632
  // Remove temp index file
 
633
  FS_HomeRemove( idxFileName );
 
634
 
 
635
  // Write the real header
 
636
  FS_Seek( afd.f, 0, FS_SEEK_SET );
 
637
  CL_WriteAVIHeader( );
 
638
 
 
639
  bufIndex = 4;
 
640
  WRITE_4BYTES( afd.fileSize - 8 ); // "RIFF" size
 
641
 
 
642
  bufIndex = afd.moviOffset + 4;    // Skip "LIST"
 
643
  WRITE_4BYTES( afd.moviSize );
 
644
 
 
645
  SafeFS_Write( buffer, bufIndex, afd.f );
 
646
 
 
647
  Z_Free( afd.cBuffer );
 
648
  Z_Free( afd.eBuffer );
 
649
  FS_FCloseFile( afd.f );
 
650
 
 
651
  Com_Printf( "Wrote %d:%d frames to %s\n", afd.numVideoFrames, afd.numAudioFrames, afd.fileName );
 
652
 
 
653
  return qtrue;
 
654
}
 
655
 
 
656
/*
 
657
===============
 
658
CL_VideoRecording
 
659
===============
 
660
*/
 
661
qboolean CL_VideoRecording( void )
 
662
{
 
663
  return afd.fileOpen;
 
664
}