2 * avi.cc library for AVI file format i/o
3 * Copyright (C) 2000 - 2002 Arne Schirmacher <arne@schirmacher.de>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software Foundation,
17 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
49 #define PADDING_SIZE (512)
50 #define PADDING_1GB (0x40000000)
51 #define IX00_INDEX_SIZE (4028)
53 #define AVIF_HASINDEX 0x00000010
54 #define AVIF_MUSTUSEINDEX 0x00000020
55 #define AVIF_TRUSTCKTYPE 0x00000800
56 #define AVIF_ISINTERLEAVED 0x00000100
57 #define AVIF_WASCAPTUREFILE 0x00010000
58 #define AVIF_COPYRIGHTED 0x00020000
61 //static char g_zeroes[ PADDING_SIZE ];
65 \todo mainHdr not initialized
66 \todo add checking for NULL pointers
70 AVIFile::AVIFile() : RIFFFile(),
71 idx1( NULL ), file_list( -1 ), riff_list( -1 ),
72 hdrl_list( -1 ), avih_chunk( -1 ), movi_list( -1 ), junk_chunk( -1 ), idx1_chunk( -1 ),
73 index_type( -1 ), current_ix00( -1 ), odml_list( -1 ), dmlh_chunk( -1 ), isUpdateIdx1( true )
75 // cerr << "0x" << hex << (long)this << dec << " AVIFile::AVIFile() : RIFFFile(), ..." << endl;
77 for ( int i = 0; i < 2; ++i )
79 indx[ i ] = new AVISuperIndex;
80 memset( indx[ i ], 0, sizeof( AVISuperIndex ) );
81 ix[ i ] = new AVIStdIndex;
82 memset( ix[ i ], 0, sizeof( AVIStdIndex ) );
89 idx1 = new AVISimpleIndex;
90 memset( idx1, 0, sizeof( AVISimpleIndex ) );
91 memset( dmlh, 0, sizeof( dmlh ) );
92 memset( &mainHdr, 0, sizeof( mainHdr ) );
93 memset( &streamHdr, 0, sizeof( streamHdr ) );
97 /** The copy constructor
99 \todo add checking for NULL pointers
103 AVIFile::AVIFile( const AVIFile& avi ) : RIFFFile( avi )
105 // cerr << "0x" << hex << (long)this << dec << " 0x" << hex << (long)&avi << dec << " AVIFile::AVIFile(const AVIFile& avi) : RIFFFile(avi)" << endl;
107 mainHdr = avi.mainHdr;
108 idx1 = new AVISimpleIndex;
110 file_list = avi.file_list;
111 riff_list = avi.riff_list;
112 hdrl_list = avi.hdrl_list;
113 avih_chunk = avi.avih_chunk;
114 movi_list = avi.movi_list;
115 junk_chunk = avi.junk_chunk;
116 idx1_chunk = avi.idx1_chunk;
118 for ( int i = 0; i < 2; ++i )
120 indx[ i ] = new AVISuperIndex;
121 *indx[ i ] = *avi.indx[ i ];
122 ix[ i ] = new AVIStdIndex;
123 *ix[ i ] = *avi.ix[ i ];
124 indx_chunk[ i ] = avi.indx_chunk[ i ];
125 ix_chunk[ i ] = avi.ix_chunk[ i ];
126 strl_list[ i ] = avi.strl_list[ i ];
127 strh_chunk[ i ] = avi.strh_chunk[ i ];
128 strf_chunk[ i ] = avi.strf_chunk[ i ];
131 index_type = avi.index_type;
132 current_ix00 = avi.current_ix00;
134 for ( int i = 0; i < 62; ++i )
135 dmlh[ i ] = avi.dmlh[ i ];
137 isUpdateIdx1 = avi.isUpdateIdx1;
141 memset( &streamHdr, 0, sizeof( streamHdr ) );
145 /** The assignment operator
149 AVIFile& AVIFile::operator=( const AVIFile& avi )
151 // cerr << "0x" << hex << (long)this << dec << " 0x" << hex << (long)&avi << dec << " AVIFile& AVIFile::operator=(const AVIFile& avi)" << endl;
155 RIFFFile::operator=( avi );
156 mainHdr = avi.mainHdr;
158 file_list = avi.file_list;
159 riff_list = avi.riff_list;
160 hdrl_list = avi.hdrl_list;
161 avih_chunk = avi.avih_chunk;
162 movi_list = avi.movi_list;
163 junk_chunk = avi.junk_chunk;
164 idx1_chunk = avi.idx1_chunk;
166 for ( int i = 0; i < 2; ++i )
168 *indx[ i ] = *avi.indx[ i ];
169 *ix[ i ] = *avi.ix[ i ];
170 indx_chunk[ i ] = avi.indx_chunk[ i ];
171 ix_chunk[ i ] = avi.ix_chunk[ i ];
172 strl_list[ i ] = avi.strl_list[ i ];
173 strh_chunk[ i ] = avi.strh_chunk[ i ];
174 strf_chunk[ i ] = avi.strf_chunk[ i ];
177 index_type = avi.index_type;
178 current_ix00 = avi.current_ix00;
180 for ( int i = 0; i < 62; ++i )
181 dmlh[ i ] = avi.dmlh[ i ];
183 isUpdateIdx1 = avi.isUpdateIdx1;
195 // cerr << "0x" << hex << (long)this << dec << " AVIFile::~AVIFile()" << endl;
197 for ( int i = 0; i < 2; ++i )
205 /** Initialize the AVI structure to its initial state, either for PAL or NTSC format
207 Initialize the AVIFile attributes: mainHdr, indx, ix00, idx1
209 \todo consolidate AVIFile::Init, AVI1File::Init, AVI2File::Init. They are somewhat redundant.
210 \param format pass AVI_PAL or AVI_NTSC
211 \param sampleFrequency the sample frequency of the audio content
212 \param indexType pass AVI_SMALL_INDEX or AVI_LARGE_INDEX
216 void AVIFile::Init( int format, int sampleFrequency, int indexType )
220 assert( ( format == AVI_PAL ) || ( format == AVI_NTSC ) );
222 index_type = indexType;
227 mainHdr.dwMicroSecPerFrame = 40000;
228 mainHdr.dwSuggestedBufferSize = 144008;
232 mainHdr.dwMicroSecPerFrame = 33366;
233 mainHdr.dwSuggestedBufferSize = 120008;
236 default: /* no default allowed */
241 /* Initialize the 'avih' chunk */
243 mainHdr.dwMaxBytesPerSec = 3600000 + sampleFrequency * 4;
244 mainHdr.dwPaddingGranularity = PADDING_SIZE;
245 mainHdr.dwFlags = AVIF_TRUSTCKTYPE;
246 if ( indexType & AVI_SMALL_INDEX )
247 mainHdr.dwFlags |= AVIF_HASINDEX;
248 mainHdr.dwTotalFrames = 0;
249 mainHdr.dwInitialFrames = 0;
250 mainHdr.dwStreams = 1;
252 mainHdr.dwHeight = 0;
253 mainHdr.dwReserved[ 0 ] = 0;
254 mainHdr.dwReserved[ 1 ] = 0;
255 mainHdr.dwReserved[ 2 ] = 0;
256 mainHdr.dwReserved[ 3 ] = 0;
258 /* Initialize the 'idx1' chunk */
260 for ( int i = 0; i < 8000; ++i )
262 idx1->aIndex[ i ].dwChunkId = 0;
263 idx1->aIndex[ i ].dwFlags = 0;
264 idx1->aIndex[ i ].dwOffset = 0;
265 idx1->aIndex[ i ].dwSize = 0;
267 idx1->nEntriesInUse = 0;
269 /* Initialize the 'indx' chunk */
271 for ( i = 0; i < 2; ++i )
273 indx[ i ] ->wLongsPerEntry = 4;
274 indx[ i ] ->bIndexSubType = 0;
275 indx[ i ] ->bIndexType = KINO_AVI_INDEX_OF_INDEXES;
276 indx[ i ] ->nEntriesInUse = 0;
277 indx[ i ] ->dwReserved[ 0 ] = 0;
278 indx[ i ] ->dwReserved[ 1 ] = 0;
279 indx[ i ] ->dwReserved[ 2 ] = 0;
280 for ( j = 0; j < 2014; ++j )
282 indx[ i ] ->aIndex[ j ].qwOffset = 0;
283 indx[ i ] ->aIndex[ j ].dwSize = 0;
284 indx[ i ] ->aIndex[ j ].dwDuration = 0;
288 /* The ix00 and ix01 chunk will be added dynamically in avi_write_frame
291 /* Initialize the 'dmlh' chunk. I have no clue what this means
294 for ( i = 0; i < 62; ++i )
296 //dmlh[0] = -1; /* frame count + 1? */
301 /** Find position and size of a given frame in the file
303 Depending on which index is available, search one of them to
304 find position and frame size
306 \todo the size parameter is redundant. All frames have the same size, which is also in the mainHdr.
307 \todo all index related operations should be isolated
308 \param offset the file offset to the start of the frame
309 \param size the size of the frame
310 \param frameNum the number of the frame we wish to find
311 \return 0 if the frame could be found, -1 otherwise
314 int AVIFile::GetDVFrameInfo( off_t &offset, int &size, int frameNum )
316 switch ( index_type )
318 case AVI_LARGE_INDEX:
320 /* find relevant index in indx0 */
324 for ( i = 0; frameNum >= indx[ 0 ] ->aIndex[ i ].dwDuration; frameNum -= indx[ 0 ] ->aIndex[ i ].dwDuration, ++i )
327 if ( i != current_ix00 )
329 fail_if( lseek( fd, indx[ 0 ] ->aIndex[ i ].qwOffset + RIFF_HEADERSIZE, SEEK_SET ) == ( off_t ) - 1 );
330 fail_neg( read( fd, ix[ 0 ], indx[ 0 ] ->aIndex[ i ].dwSize - RIFF_HEADERSIZE ) );
334 if ( frameNum < ix[ 0 ] ->nEntriesInUse )
336 offset = ix[ 0 ] ->qwBaseOffset + ix[ 0 ] ->aIndex[ frameNum ].dwOffset;
337 size = ix[ 0 ] ->aIndex[ frameNum ].dwSize;
344 case AVI_SMALL_INDEX:
346 int frameNumIndex = 0;
347 for ( int i = 0; i < idx1->nEntriesInUse; ++i )
349 FOURCC chunkID1 = make_fourcc( "00dc" );
350 FOURCC chunkID2 = make_fourcc( "00db" );
351 if ( idx1->aIndex[ i ].dwChunkId == chunkID1 ||
352 idx1->aIndex[ i ].dwChunkId == chunkID2 )
354 if ( frameNumIndex == frameNum )
364 // compatibility check for broken dvgrab dv2 format
365 if ( idx1->aIndex[ 0 ].dwOffset > GetDirectoryEntry( movi_list ).offset )
367 offset = idx1->aIndex[ index ].dwOffset + RIFF_HEADERSIZE;
371 // new, correct dv2 format
372 offset = idx1->aIndex[ index ].dwOffset + RIFF_HEADERSIZE + GetDirectoryEntry( movi_list ).offset;
374 size = idx1->aIndex[ index ].dwSize;
384 /** Find position and size of a given frame in the file
386 Depending on which index is available, search one of them to
387 find position and frame size
389 \todo the size parameter is redundant. All frames have the same size, which is also in the mainHdr.
390 \todo all index related operations should be isolated
391 \param offset the file offset to the start of the frame
392 \param size the size of the frame
393 \param frameNum the number of the frame we wish to find
394 \param chunkID the ID of the type of chunk we want
395 \return 0 if the frame could be found, -1 otherwise
398 int AVIFile::GetFrameInfo( off_t &offset, int &size, int frameNum, FOURCC chunkID )
400 if ( index_type & AVI_LARGE_INDEX )
404 for ( i = 0; frameNum >= indx[ 0 ] ->aIndex[ i ].dwDuration; frameNum -= indx[ 0 ] ->aIndex[ i ].dwDuration, ++i )
407 if ( i != current_ix00 )
409 fail_if( lseek( fd, indx[ 0 ] ->aIndex[ i ].qwOffset + RIFF_HEADERSIZE, SEEK_SET ) == ( off_t ) - 1 );
410 fail_neg( read( fd, ix[ 0 ], indx[ 0 ] ->aIndex[ i ].dwSize - RIFF_HEADERSIZE ) );
414 if ( frameNum < ix[ 0 ] ->nEntriesInUse )
416 if ( ( FOURCC ) ix[ 0 ] ->dwChunkId == chunkID )
418 offset = ix[ 0 ] ->qwBaseOffset + ix[ 0 ] ->aIndex[ frameNum ].dwOffset;
419 size = ix[ 0 ] ->aIndex[ frameNum ].dwSize;
424 if ( index_type & AVI_SMALL_INDEX )
427 int frameNumIndex = 0;
428 for ( int i = 0; i < idx1->nEntriesInUse; ++i )
430 if ( idx1->aIndex[ i ].dwChunkId == chunkID )
432 if ( frameNumIndex == frameNum )
442 // compatibility check for broken dvgrab dv2 format
443 if ( idx1->aIndex[ 0 ].dwOffset > GetDirectoryEntry( movi_list ).offset )
445 offset = idx1->aIndex[ index ].dwOffset + RIFF_HEADERSIZE;
449 // new, correct dv2 format
450 offset = idx1->aIndex[ index ].dwOffset + RIFF_HEADERSIZE + GetDirectoryEntry( movi_list ).offset;
452 size = idx1->aIndex[ index ].dwSize;
461 \todo we actually don't need the frame here, we could use just a void pointer
462 \param frame a reference to the frame object that will receive the frame data
463 \param frameNum the frame number to read
464 \return 0 if the frame could be read, -1 otherwise
467 int AVIFile::GetDVFrame( uint8_t *data, int frameNum )
472 if ( GetDVFrameInfo( offset, size, frameNum ) != 0 || size < 0 )
474 pthread_mutex_lock( &file_mutex );
475 fail_if( lseek( fd, offset, SEEK_SET ) == ( off_t ) - 1 );
476 fail_neg( read( fd, data, size ) );
477 pthread_mutex_unlock( &file_mutex );
484 \param data a pointer to the audio buffer
485 \param frameNum the frame number to read
486 \param chunkID the ID of the type of chunk we want
487 \return the size the of the frame data, 0 if could not be read
490 int AVIFile::getFrame( void *data, int frameNum, FOURCC chunkID )
495 if ( GetFrameInfo( offset, size, frameNum, chunkID ) != 0 )
497 fail_if( lseek( fd, offset, SEEK_SET ) == ( off_t ) - 1 );
498 fail_neg( read( fd, data, size ) );
503 int AVIFile::GetTotalFrames() const
505 return mainHdr.dwTotalFrames;
509 /** prints out a directory entry in text form
511 Every subclass of RIFFFile is supposed to override this function
512 and to implement it for the entry types it knows about. For all
513 other entry types it should call its parent::PrintDirectoryData.
515 \todo use 64 bit routines
516 \param entry the entry to print
519 void AVIFile::PrintDirectoryEntryData( const RIFFDirEntry &entry ) const
521 static FOURCC lastStreamType = make_fourcc( " " );
523 if ( entry.type == make_fourcc( "avih" ) )
527 MainAVIHeader main_avi_header;
529 fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 );
530 fail_neg( read( fd, &main_avi_header, sizeof( MainAVIHeader ) ) );
532 cout << " dwMicroSecPerFrame: " << ( int ) main_avi_header.dwMicroSecPerFrame << endl
533 << " dwMaxBytesPerSec: " << ( int ) main_avi_header.dwMaxBytesPerSec << endl
534 << " dwPaddingGranularity: " << ( int ) main_avi_header.dwPaddingGranularity << endl
535 << " dwFlags: " << ( int ) main_avi_header.dwFlags << endl
536 << " dwTotalFrames: " << ( int ) main_avi_header.dwTotalFrames << endl
537 << " dwInitialFrames: " << ( int ) main_avi_header.dwInitialFrames << endl
538 << " dwStreams: " << ( int ) main_avi_header.dwStreams << endl
539 << " dwSuggestedBufferSize: " << ( int ) main_avi_header.dwSuggestedBufferSize << endl
540 << " dwWidth: " << ( int ) main_avi_header.dwWidth << endl
541 << " dwHeight: " << ( int ) main_avi_header.dwHeight << endl;
542 for ( i = 0; i < 4; ++i )
543 cout << " dwReserved[" << i << "]: " << ( int ) main_avi_header.dwReserved[ i ] << endl;
546 else if ( entry.type == make_fourcc( "strh" ) )
549 AVIStreamHeader avi_stream_header;
551 fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 );
552 fail_neg( read( fd, &avi_stream_header, sizeof( AVIStreamHeader ) ) );
554 lastStreamType = avi_stream_header.fccType;
556 cout << " fccType: '"
557 << ((char *)&avi_stream_header.fccType)[0]
558 << ((char *)&avi_stream_header.fccType)[1]
559 << ((char *)&avi_stream_header.fccType)[2]
560 << ((char *)&avi_stream_header.fccType)[3]
563 << ((char *)&avi_stream_header.fccHandler)[0]
564 << ((char *)&avi_stream_header.fccHandler)[1]
565 << ((char *)&avi_stream_header.fccHandler)[2]
566 << ((char *)&avi_stream_header.fccHandler)[3]
568 << " dwFlags: " << ( int ) avi_stream_header.dwFlags << endl
569 << " wPriority: " << ( int ) avi_stream_header.wPriority << endl
570 << " wLanguage: " << ( int ) avi_stream_header.wLanguage << endl
571 << " dwInitialFrames: " << ( int ) avi_stream_header.dwInitialFrames << endl
572 << " dwScale: " << ( int ) avi_stream_header.dwScale << endl
573 << " dwRate: " << ( int ) avi_stream_header.dwRate << endl
574 << " dwLength: " << ( int ) avi_stream_header.dwLength << endl
575 << " dwQuality: " << ( int ) avi_stream_header.dwQuality << endl
576 << " dwSampleSize: " << ( int ) avi_stream_header.dwSampleSize << endl;
579 else if ( entry.type == make_fourcc( "indx" ) )
583 AVISuperIndex avi_super_index;
585 fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 );
586 fail_neg( read( fd, &avi_super_index, sizeof( AVISuperIndex ) ) );
588 cout << " wLongsPerEntry: " << ( int ) avi_super_index.wLongsPerEntry
590 << " bIndexSubType: " << ( int ) avi_super_index.bIndexSubType << endl
591 << " bIndexType: " << ( int ) avi_super_index.bIndexType << endl
592 << " nEntriesInUse: " << ( int ) avi_super_index.nEntriesInUse << endl
594 << ((char *)&avi_super_index.dwChunkId)[0]
595 << ((char *)&avi_super_index.dwChunkId)[1]
596 << ((char *)&avi_super_index.dwChunkId)[2]
597 << ((char *)&avi_super_index.dwChunkId)[3]
599 << " dwReserved[0]: " << ( int ) avi_super_index.dwReserved[ 0 ] << endl
600 << " dwReserved[1]: " << ( int ) avi_super_index.dwReserved[ 1 ] << endl
601 << " dwReserved[2]: " << ( int ) avi_super_index.dwReserved[ 2 ] << endl;
602 for ( i = 0; i < avi_super_index.nEntriesInUse; ++i )
604 cout << ' ' << setw( 4 ) << setfill( ' ' ) << i
605 << ": qwOffset : 0x" << setw( 12 ) << setfill( '0' ) << hex << avi_super_index.aIndex[ i ].qwOffset << endl
606 << " dwSize : 0x" << setw( 8 ) << avi_super_index.aIndex[ i ].dwSize << endl
607 << " dwDuration : " << dec << avi_super_index.aIndex[ i ].dwDuration << endl;
610 else if ( entry.type == make_fourcc( "strf" ) )
612 if ( lastStreamType == make_fourcc( "auds" ) )
614 WAVEFORMATEX waveformatex;
615 fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 );
616 fail_neg( read( fd, &waveformatex, sizeof( WAVEFORMATEX ) ) );
617 cout << " waveformatex.wFormatTag : " << waveformatex.wFormatTag << endl;
618 cout << " waveformatex.nChannels : " << waveformatex.nChannels << endl;
619 cout << " waveformatex.nSamplesPerSec : " << waveformatex.nSamplesPerSec << endl;
620 cout << " waveformatex.nAvgBytesPerSec: " << waveformatex.nAvgBytesPerSec << endl;
621 cout << " waveformatex.nBlockAlign : " << waveformatex.nBlockAlign << endl;
622 cout << " waveformatex.wBitsPerSample : " << waveformatex.wBitsPerSample << endl;
623 cout << " waveformatex.cbSize : " << waveformatex.cbSize << endl;
625 else if ( lastStreamType == make_fourcc( "vids" ) )
627 BITMAPINFOHEADER bitmapinfo;
628 fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 );
629 fail_neg( read( fd, &bitmapinfo, sizeof( BITMAPINFOHEADER ) ) );
630 cout << " bitmapinfo.biSize : " << bitmapinfo.biSize << endl;
631 cout << " bitmapinfo.biWidth : " << bitmapinfo.biWidth << endl;
632 cout << " bitmapinfo.biHeight : " << bitmapinfo.biHeight << endl;
633 cout << " bitmapinfo.biPlanes : " << bitmapinfo.biPlanes << endl;
634 cout << " bitmapinfo.biBitCount : " << bitmapinfo.biBitCount << endl;
635 cout << " bitmapinfo.biCompression : " << bitmapinfo.biCompression << endl;
636 cout << " bitmapinfo.biSizeImage : " << bitmapinfo.biSizeImage << endl;
637 cout << " bitmapinfo.biXPelsPerMeter: " << bitmapinfo.biXPelsPerMeter << endl;
638 cout << " bitmapinfo.biYPelsPerMeter: " << bitmapinfo.biYPelsPerMeter << endl;
639 cout << " bitmapinfo.biClrUsed : " << bitmapinfo.biClrUsed << endl;
640 cout << " bitmapinfo.biClrImportant : " << bitmapinfo.biClrImportant << endl;
642 else if ( lastStreamType == make_fourcc( "iavs" ) )
645 fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 );
646 fail_neg( read( fd, &dvinfo, sizeof( DVINFO ) ) );
647 cout << " dvinfo.dwDVAAuxSrc : 0x" << setw( 8 ) << setfill( '0' ) << hex << dvinfo.dwDVAAuxSrc << endl;
648 cout << " dvinfo.dwDVAAuxCtl : 0x" << setw( 8 ) << setfill( '0' ) << hex << dvinfo.dwDVAAuxCtl << endl;
649 cout << " dvinfo.dwDVAAuxSrc1: 0x" << setw( 8 ) << setfill( '0' ) << hex << dvinfo.dwDVAAuxSrc1 << endl;
650 cout << " dvinfo.dwDVAAuxCtl1: 0x" << setw( 8 ) << setfill( '0' ) << hex << dvinfo.dwDVAAuxCtl1 << endl;
651 cout << " dvinfo.dwDVVAuxSrc : 0x" << setw( 8 ) << setfill( '0' ) << hex << dvinfo.dwDVVAuxSrc << endl;
652 cout << " dvinfo.dwDVVAuxCtl : 0x" << setw( 8 ) << setfill( '0' ) << hex << dvinfo.dwDVVAuxCtl << endl;
656 /* This is the Standard Index. It is an array of offsets and
657 sizes relative to some start offset. */
659 else if ( ( entry.type == make_fourcc( "ix00" ) ) || ( entry.type == make_fourcc( "ix01" ) ) )
663 AVIStdIndex avi_std_index;
665 fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 );
666 fail_neg( read( fd, &avi_std_index, sizeof( AVIStdIndex ) ) );
668 cout << " wLongsPerEntry: " << ( int ) avi_std_index.wLongsPerEntry
670 << " bIndexSubType: " << ( int ) avi_std_index.bIndexSubType << endl
671 << " bIndexType: " << ( int ) avi_std_index.bIndexType << endl
672 << " nEntriesInUse: " << ( int ) avi_std_index.nEntriesInUse << endl
674 << ((char *)&avi_std_index.dwChunkId)[0]
675 << ((char *)&avi_std_index.dwChunkId)[1]
676 << ((char *)&avi_std_index.dwChunkId)[2]
677 << ((char *)&avi_std_index.dwChunkId)[3]
679 << " qwBaseOffset: 0x" << setw( 12 ) << hex << avi_std_index.qwBaseOffset << endl
680 << " dwReserved: " << dec << ( int ) avi_std_index.dwReserved << endl;
681 for ( i = 0; i < avi_std_index.nEntriesInUse; ++i )
683 cout << ' ' << setw( 4 ) << setfill( ' ' ) << i
684 << ": dwOffset : 0x" << setw( 8 ) << setfill( '0' ) << hex << avi_std_index.aIndex[ i ].dwOffset
685 << " (0x" << setw( 12 ) << avi_std_index.qwBaseOffset + avi_std_index.aIndex[ i ].dwOffset << ')' << endl
686 << " dwSize : 0x" << setw( 8 ) << avi_std_index.aIndex[ i ].dwSize << dec << endl;
690 else if ( entry.type == make_fourcc( "idx1" ) )
694 int numEntries = entry.length / sizeof( int ) / 4;
695 DWORD *idx1 = new DWORD[ numEntries * 4 ];
696 // FOURCC movi_list = FindDirectoryEntry(make_fourcc("movi"));
698 fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 );
699 fail_neg( read( fd, idx1, entry.length ) );
701 for ( i = 0; i < numEntries; ++i )
704 cout << ' ' << setw( 4 ) << setfill( ' ' ) << i << setfill( '0' ) << ": dwChunkId : '"
705 << ((char *)&idx1[ i * 4 + 0 ])[0]
706 << ((char *)&idx1[ i * 4 + 0 ])[1]
707 << ((char *)&idx1[ i * 4 + 0 ])[2]
708 << ((char *)&idx1[ i * 4 + 0 ])[3]
710 << " dwType : 0x" << setw( 8 ) << hex << idx1[ i * 4 + 1 ] << endl
711 << " dwOffset : 0x" << setw( 8 ) << idx1[ i * 4 + 2 ] << endl
712 // << " (0x" << setw(8) << idx1[i * 4 + 2] + GetDirectoryEntry(movi_list).offset << ')' << endl
713 << " dwSize : 0x" << setw( 8 ) << idx1[ i * 4 + 3 ] << dec << endl;
718 else if ( entry.type == make_fourcc( "dmlh" ) )
721 int numEntries = entry.length / sizeof( int );
722 DWORD *dmlh = new DWORD[ numEntries ];
724 fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 );
725 fail_neg( read( fd, dmlh, entry.length ) );
727 for ( i = 0; i < numEntries; ++i )
729 cout << ' ' << setw( 4 ) << setfill( ' ' ) << i << setfill( '0' ) << ": "
730 << " dwTotalFrames: 0x" << setw( 8 ) << hex << dmlh[ i ]
731 << " (" << dec << dmlh[ i ] << ")" << endl;
738 /** If this is not a movi list, read its contents
742 void AVIFile::ParseList( int parent )
751 /* Read in the chunk header (type and length). */
752 fail_neg( read( fd, &type, sizeof( type ) ) );
753 fail_neg( read( fd, &length, sizeof( length ) ) );
757 /* The contents of the list starts here. Obtain its offset. The list
758 name (4 bytes) is already part of the contents). */
760 pos = lseek( fd, 0, SEEK_CUR );
761 fail_if( pos == ( off_t ) - 1 );
762 fail_neg( read( fd, &name, sizeof( name ) ) );
764 /* if we encounter a movi list, do not read it. It takes too much time
765 and we don't need it anyway. */
767 if ( name != make_fourcc( "movi" ) )
771 /* Add an entry for this list. */
772 list = AddDirectoryEntry( type, name, sizeof( name ), parent );
774 /* Read in any chunks contained in this list. This list is the
775 parent for all chunks it contains. */
777 listEnd = pos + length;
778 while ( pos < listEnd )
781 pos = lseek( fd, 0, SEEK_CUR );
782 fail_if( pos == ( off_t ) - 1 );
787 /* Add an entry for this list. */
789 movi_list = AddDirectoryEntry( type, name, length, parent );
791 pos = lseek( fd, length - 4, SEEK_CUR );
792 fail_if( pos == ( off_t ) - 1 );
797 void AVIFile::ParseRIFF()
799 RIFFFile::ParseRIFF();
801 avih_chunk = FindDirectoryEntry( make_fourcc( "avih" ) );
802 if ( avih_chunk != -1 )
803 ReadChunk( avih_chunk, ( void* ) & mainHdr, sizeof( MainAVIHeader ) );
807 void AVIFile::ReadIndex()
809 indx_chunk[ 0 ] = FindDirectoryEntry( make_fourcc( "indx" ) );
810 if ( indx_chunk[ 0 ] != -1 )
812 ReadChunk( indx_chunk[ 0 ], ( void* ) indx[ 0 ], sizeof( AVISuperIndex ) );
813 index_type = AVI_LARGE_INDEX;
815 /* recalc number of frames from each index */
816 mainHdr.dwTotalFrames = 0;
818 i < indx[ 0 ] ->nEntriesInUse;
819 mainHdr.dwTotalFrames += indx[ 0 ] ->aIndex[ i++ ].dwDuration )
823 idx1_chunk = FindDirectoryEntry( make_fourcc( "idx1" ) );
824 if ( idx1_chunk != -1 )
826 ReadChunk( idx1_chunk, ( void* ) idx1, sizeof( AVISuperIndex ) );
827 idx1->nEntriesInUse = GetDirectoryEntry( idx1_chunk ).length / 16;
828 index_type = AVI_SMALL_INDEX;
830 /* recalc number of frames from the simple index */
831 int frameNumIndex = 0;
832 FOURCC chunkID1 = make_fourcc( "00dc" );
833 FOURCC chunkID2 = make_fourcc( "00db" );
834 for ( int i = 0; i < idx1->nEntriesInUse; ++i )
836 if ( idx1->aIndex[ i ].dwChunkId == chunkID1 ||
837 idx1->aIndex[ i ].dwChunkId == chunkID2 )
842 mainHdr.dwTotalFrames = frameNumIndex;
848 void AVIFile::FlushIndx( int stream )
857 /* Write out the previous index. When this function is
858 entered for the first time, there is no index to
859 write. Note: this may be an expensive operation
860 because of a time consuming seek to the former file
863 if ( ix_chunk[ stream ] != -1 )
864 WriteChunk( ix_chunk[ stream ], ix[ stream ] );
866 /* make a new ix chunk. */
869 type = make_fourcc( "ix00" );
871 type = make_fourcc( "ix01" );
872 ix_chunk[ stream ] = AddDirectoryEntry( type, 0, sizeof( AVIStdIndex ), movi_list );
873 GetDirectoryEntry( ix_chunk[ stream ], type, name, length, offset, parent );
875 /* fill out all required fields. The offsets in the
876 array are relative to qwBaseOffset, so fill in the
877 offset to the next free location in the file
880 ix[ stream ] ->wLongsPerEntry = 2;
881 ix[ stream ] ->bIndexSubType = 0;
882 ix[ stream ] ->bIndexType = KINO_AVI_INDEX_OF_CHUNKS;
883 ix[ stream ] ->nEntriesInUse = 0;
884 ix[ stream ] ->dwChunkId = indx[ stream ] ->dwChunkId;
885 ix[ stream ] ->qwBaseOffset = offset + length;
886 ix[ stream ] ->dwReserved = 0;
888 for ( i = 0; i < IX00_INDEX_SIZE; ++i )
890 ix[ stream ] ->aIndex[ i ].dwOffset = 0;
891 ix[ stream ] ->aIndex[ i ].dwSize = 0;
894 /* add a reference to this new index in our super
897 i = indx[ stream ] ->nEntriesInUse++;
898 indx[ stream ] ->aIndex[ i ].qwOffset = offset - RIFF_HEADERSIZE;
899 indx[ stream ] ->aIndex[ i ].dwSize = length + RIFF_HEADERSIZE;
900 indx[ stream ] ->aIndex[ i ].dwDuration = 0;
904 void AVIFile::UpdateIndx( int stream, int chunk, int duration )
913 /* update the appropiate entry in the super index. It reflects
914 the number of frames in the referenced index. */
916 i = indx[ stream ] ->nEntriesInUse - 1;
917 indx[ stream ] ->aIndex[ i ].dwDuration += duration;
919 /* update the standard index. Calculate the file position of
922 GetDirectoryEntry( chunk, type, name, length, offset, parent );
924 indx[ stream ] ->dwChunkId = type;
925 i = ix[ stream ] ->nEntriesInUse++;
926 ix[ stream ] ->aIndex[ i ].dwOffset = offset - ix[ stream ] ->qwBaseOffset;
927 ix[ stream ] ->aIndex[ i ].dwSize = length;
931 void AVIFile::UpdateIdx1( int chunk, int flags )
933 if ( idx1->nEntriesInUse < 20000 )
941 GetDirectoryEntry( chunk, type, name, length, offset, parent );
943 idx1->aIndex[ idx1->nEntriesInUse ].dwChunkId = type;
944 idx1->aIndex[ idx1->nEntriesInUse ].dwFlags = flags;
945 idx1->aIndex[ idx1->nEntriesInUse ].dwOffset = offset - GetDirectoryEntry( movi_list ).offset - RIFF_HEADERSIZE;
946 idx1->aIndex[ idx1->nEntriesInUse ].dwSize = length;
947 idx1->nEntriesInUse++;
951 bool AVIFile::verifyStreamFormat( FOURCC type )
954 AVIStreamHeader avi_stream_header;
955 BITMAPINFOHEADER bih;
956 FOURCC strh = make_fourcc( "strh" );
957 FOURCC strf = make_fourcc( "strf" );
959 while ( ( i = FindDirectoryEntry( strh, j++ ) ) != -1 )
961 ReadChunk( i, ( void* ) & avi_stream_header, sizeof( AVIStreamHeader ) );
962 if ( avi_stream_header.fccHandler == type )
966 while ( ( i = FindDirectoryEntry( strf, j++ ) ) != -1 )
968 ReadChunk( i, ( void* ) & bih, sizeof( BITMAPINFOHEADER ) );
969 if ( ( FOURCC ) bih.biCompression == type )
976 bool AVIFile::verifyStream( FOURCC type )
979 AVIStreamHeader avi_stream_header;
980 FOURCC strh = make_fourcc( "strh" );
982 while ( ( i = FindDirectoryEntry( strh, j++ ) ) != -1 )
984 ReadChunk( i, ( void* ) & avi_stream_header, sizeof( AVIStreamHeader ) );
985 if ( avi_stream_header.fccType == type )
991 bool AVIFile::isOpenDML( void )
994 FOURCC dmlh = make_fourcc( "dmlh" );
996 while ( ( i = FindDirectoryEntry( dmlh, j++ ) ) != -1 )
1003 AVI1File::AVI1File() : AVIFile()
1005 memset( &dvinfo, 0, sizeof( dvinfo ) );
1009 AVI1File::~AVI1File()
1013 /* Initialize the AVI structure to its initial state, either for PAL
1016 void AVI1File::Init( int format, int sampleFrequency, int indexType )
1025 assert( ( format == AVI_PAL ) || ( format == AVI_NTSC ) );
1027 AVIFile::Init( format, sampleFrequency, indexType );
1032 mainHdr.dwWidth = 720;
1033 mainHdr.dwHeight = 576;
1035 streamHdr[ 0 ].dwScale = 1;
1036 streamHdr[ 0 ].dwRate = 25;
1037 streamHdr[ 0 ].dwSuggestedBufferSize = 144008;
1039 /* initialize the 'strf' chunk */
1041 /* Meaning of the DV stream format chunk per Microsoft
1043 Specifies the Audio Auxiliary Data Source Pack for the first audio block
1044 (first 5 DV DIF sequences for 525-60 systems or 6 DV DIF sequences for 625-50 systems) of
1045 a frame. A DIF sequence is a data block that contains 150 DIF blocks. A DIF block consists
1046 of 80 bytes. The Audio Auxiliary Data Source Pack is defined in section D.7.1 of Part 2,
1047 Annex D, "The Pack Header Table and Contents of Packs" of the Specification of
1048 Consumer-use Digital VCRs.
1050 Specifies the Audio Auxiliary Data Source Control Pack for the first audio block of a
1051 frame. The Audio Auxiliary Data Control Pack is defined in section D.7.2 of Part 2,
1052 Annex D, "The Pack Header Table and Contents of Packs" of the Specification of
1053 Consumer-use Digital VCRs.
1055 Specifies the Audio Auxiliary Data Source Pack for the second audio block
1056 (second 5 DV DIF sequences for 525-60 systems or 6 DV DIF sequences for 625-50 systems) of a frame.
1058 Specifies the Audio Auxiliary Data Source Control Pack for the second audio block of a frame.
1060 Specifies the Video Auxiliary Data Source Pack as defined in section D.8.1 of Part 2,
1061 Annex D, "The Pack Header Table and Contents of Packs" of the Specification of
1062 Consumer-use Digital VCRs.
1064 Specifies the Video Auxiliary Data Source Control Pack as defined in section D.8.2 of Part 2,
1065 Annex D, "The Pack Header Table and Contents of Packs" of the Specification of
1066 Consumer-use Digital VCRs.
1068 Reserved. Set this array to zero.
1071 dvinfo.dwDVAAuxSrc = 0xd1e030d0;
1072 dvinfo.dwDVAAuxCtl = 0xffa0cf3f;
1073 dvinfo.dwDVAAuxSrc1 = 0xd1e03fd0;
1074 dvinfo.dwDVAAuxCtl1 = 0xffa0cf3f;
1075 dvinfo.dwDVVAuxSrc = 0xff20ffff;
1076 dvinfo.dwDVVAuxCtl = 0xfffdc83f;
1077 dvinfo.dwDVReserved[ 0 ] = 0;
1078 dvinfo.dwDVReserved[ 1 ] = 0;
1082 mainHdr.dwWidth = 720;
1083 mainHdr.dwHeight = 480;
1085 streamHdr[ 0 ].dwScale = 1001;
1086 streamHdr[ 0 ].dwRate = 30000;
1087 streamHdr[ 0 ].dwSuggestedBufferSize = 120008;
1089 /* initialize the 'strf' chunk */
1090 dvinfo.dwDVAAuxSrc = 0xc0c000c0;
1091 dvinfo.dwDVAAuxCtl = 0xffa0cf3f;
1092 dvinfo.dwDVAAuxSrc1 = 0xc0c001c0;
1093 dvinfo.dwDVAAuxCtl1 = 0xffa0cf3f;
1094 dvinfo.dwDVVAuxSrc = 0xff80ffff;
1095 dvinfo.dwDVVAuxCtl = 0xfffcc83f;
1096 dvinfo.dwDVReserved[ 0 ] = 0;
1097 dvinfo.dwDVReserved[ 1 ] = 0;
1100 default: /* no default allowed */
1105 indx[ 0 ] ->dwChunkId = make_fourcc( "00__" );
1107 /* Initialize the 'strh' chunk */
1109 streamHdr[ 0 ].fccType = make_fourcc( "iavs" );
1110 streamHdr[ 0 ].fccHandler = make_fourcc( "dvsd" );
1111 streamHdr[ 0 ].dwFlags = 0;
1112 streamHdr[ 0 ].wPriority = 0;
1113 streamHdr[ 0 ].wLanguage = 0;
1114 streamHdr[ 0 ].dwInitialFrames = 0;
1115 streamHdr[ 0 ].dwStart = 0;
1116 streamHdr[ 0 ].dwLength = 0;
1117 streamHdr[ 0 ].dwQuality = 0;
1118 streamHdr[ 0 ].dwSampleSize = 0;
1119 streamHdr[ 0 ].rcFrame.top = 0;
1120 streamHdr[ 0 ].rcFrame.bottom = 0;
1121 streamHdr[ 0 ].rcFrame.left = 0;
1122 streamHdr[ 0 ].rcFrame.right = 0;
1124 /* This is a simple directory structure setup. For details see the
1125 "OpenDML AVI File Format Extensions" document.
1127 An AVI file contains basically two types of objects, a
1128 "chunk" and a "list" object. The list object contains any
1129 number of chunks. Since a list is also a chunk, it is
1130 possible to create a hierarchical "list of lists"
1133 Every AVI file starts with a "RIFF" object, which is a list
1134 of several other required objects. The actual DV data is
1135 contained in a "movi" list, each frame is in its own chunk.
1137 Old AVI files (pre OpenDML V. 1.02) contain only one RIFF
1138 chunk of less than 1 GByte size per file. The current
1139 format which allow for almost arbitrary sizes can contain
1140 several RIFF chunks of less than 1 GByte size. Old software
1141 however would only deal with the first RIFF chunk.
1143 Note that the first entry (FILE) isn�t actually part
1144 of the AVI file. I use this (pseudo-) directory entry to
1145 keep track of the RIFF chunks and their positions in the
1149 /* Create the container directory entry */
1151 file_list = AddDirectoryEntry( make_fourcc( "FILE" ), make_fourcc( "FILE" ), 0, RIFF_NO_PARENT );
1153 /* Create a basic directory structure. Only chunks defined from here on will be written to the AVI file. */
1155 riff_list = AddDirectoryEntry( make_fourcc( "RIFF" ), make_fourcc( "AVI " ), RIFF_LISTSIZE, file_list );
1156 hdrl_list = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "hdrl" ), RIFF_LISTSIZE, riff_list );
1157 avih_chunk = AddDirectoryEntry( make_fourcc( "avih" ), 0, sizeof( MainAVIHeader ), hdrl_list );
1158 strl_list[ 0 ] = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "strl" ), RIFF_LISTSIZE, hdrl_list );
1159 strh_chunk[ 0 ] = AddDirectoryEntry( make_fourcc( "strh" ), 0, sizeof( AVIStreamHeader ), strl_list[ 0 ] );
1160 strf_chunk[ 0 ] = AddDirectoryEntry( make_fourcc( "strf" ), 0, sizeof( dvinfo ), strl_list[ 0 ] );
1161 if ( index_type & AVI_LARGE_INDEX )
1162 indx_chunk[ 0 ] = AddDirectoryEntry( make_fourcc( "indx" ), 0, sizeof( AVISuperIndex ), strl_list[ 0 ] );
1164 odml_list = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "odml" ), RIFF_LISTSIZE, hdrl_list );
1165 dmlh_chunk = AddDirectoryEntry( make_fourcc( "dmlh" ), 0, 0x00f8, odml_list );
1167 /* align movi list to block */
1168 GetDirectoryEntry( hdrl_list, type, name, length, offset, parent );
1169 num_blocks = length / PADDING_SIZE + 1;
1170 length = num_blocks * PADDING_SIZE - length - 5 * RIFF_HEADERSIZE; // why 5?
1171 junk_chunk = AddDirectoryEntry( make_fourcc( "JUNK" ), 0, length, riff_list );
1173 movi_list = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "movi" ), RIFF_LISTSIZE, riff_list );
1175 /* The ix00 chunk will be added dynamically to the movi_list in avi_write_frame
1182 /* Write a DV video frame. This is somewhat complex... */
1185 bool AVI1File::WriteFrame( const Frame &frame )
1196 /* exit if no large index and 1GB reached */
1197 if ( !( index_type & AVI_LARGE_INDEX ) && isUpdateIdx1 == false )
1200 /* Check if we need a new ix00 Standard Index. It has a
1201 capacity of IX00_INDEX_SIZE frames. Whenever we exceed that
1202 number, we need a new index. The new ix00 chunk is also
1203 part of the movi list. */
1205 if ( ( index_type & AVI_LARGE_INDEX ) && ( ( ( streamHdr[ 0 ].dwLength - 0 ) % IX00_INDEX_SIZE ) == 0 ) )
1208 /* Write the DV frame data.
1210 Make a new 00__ chunk for the new frame, write out the
1213 frame_chunk = AddDirectoryEntry( make_fourcc( "00__" ), 0, frame.GetFrameSize(), movi_list );
1214 if ( ( index_type & AVI_LARGE_INDEX ) && ( streamHdr[ 0 ].dwLength % IX00_INDEX_SIZE ) == 0 )
1216 GetDirectoryEntry( frame_chunk, type, name, length, offset, parent );
1217 ix[ 0 ] ->qwBaseOffset = offset - RIFF_HEADERSIZE;
1219 WriteChunk( frame_chunk, frame.data );
1220 // num_blocks = (frame.GetFrameSize() + RIFF_HEADERSIZE) / PADDING_SIZE + 1;
1221 // length = num_blocks * PADDING_SIZE - frame.GetFrameSize() - 2 * RIFF_HEADERSIZE;
1222 // junk_chunk = AddDirectoryEntry(make_fourcc("JUNK"), 0, length, movi_list);
1223 // WriteChunk(junk_chunk, g_zeroes);
1225 if ( index_type & AVI_LARGE_INDEX )
1226 UpdateIndx( 0, frame_chunk, 1 );
1227 if ( ( index_type & AVI_SMALL_INDEX ) && isUpdateIdx1 )
1228 UpdateIdx1( frame_chunk, 0x10 );
1230 /* update some variables with the new frame count. */
1233 ++mainHdr.dwTotalFrames;
1234 ++streamHdr[ 0 ].dwLength;
1237 /* Find out if the current riff list is close to 1 GByte in
1238 size. If so, start a new (extended) RIFF. The only allowed
1239 item in the new RIFF chunk is a movi list (with video
1240 frames and indexes as usual). */
1242 GetDirectoryEntry( riff_list, type, name, length, offset, parent );
1243 if ( length > 0x3f000000 )
1245 /* write idx1 only once and before end of first GB */
1246 if ( ( index_type & AVI_SMALL_INDEX ) && isUpdateIdx1 )
1248 int idx1_chunk = AddDirectoryEntry( make_fourcc( "idx1" ), 0, idx1->nEntriesInUse * 16, riff_list );
1249 WriteChunk( idx1_chunk, ( void* ) idx1 );
1251 isUpdateIdx1 = false;
1253 if ( index_type & AVI_LARGE_INDEX )
1255 /* pad out to 1GB */
1256 //GetDirectoryEntry(riff_list, type, name, length, offset, parent);
1257 //junk_chunk = AddDirectoryEntry(make_fourcc("JUNK"), 0, PADDING_1GB - length - 5 * RIFF_HEADERSIZE, riff_list);
1258 //WriteChunk(junk_chunk, g_zeroes);
1260 /* padding for alignment */
1261 GetDirectoryEntry( riff_list, type, name, length, offset, parent );
1262 num_blocks = ( length + 4 * RIFF_HEADERSIZE ) / PADDING_SIZE + 1;
1263 length = ( num_blocks * PADDING_SIZE ) - length - 4 * RIFF_HEADERSIZE - 2 * RIFF_LISTSIZE;
1266 junk_chunk = AddDirectoryEntry( make_fourcc( "JUNK" ), 0, length, riff_list );
1267 WriteChunk( junk_chunk, g_zeroes );
1270 riff_list = AddDirectoryEntry( make_fourcc( "RIFF" ), make_fourcc( "AVIX" ), RIFF_LISTSIZE, file_list );
1271 movi_list = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "movi" ), RIFF_LISTSIZE, riff_list );
1278 void AVI1File::WriteRIFF()
1281 WriteChunk( avih_chunk, ( void* ) & mainHdr );
1282 WriteChunk( strh_chunk[ 0 ], ( void* ) & streamHdr[ 0 ] );
1283 WriteChunk( strf_chunk[ 0 ], ( void* ) & dvinfo );
1284 WriteChunk( dmlh_chunk, ( void* ) & dmlh );
1286 if ( index_type & AVI_LARGE_INDEX )
1288 WriteChunk( indx_chunk[ 0 ], ( void* ) indx[ 0 ] );
1289 WriteChunk( ix_chunk[ 0 ], ( void* ) ix[ 0 ] );
1292 if ( ( index_type & AVI_SMALL_INDEX ) && isUpdateIdx1 )
1294 int idx1_chunk = AddDirectoryEntry( make_fourcc( "idx1" ), 0, idx1->nEntriesInUse * 16, riff_list );
1295 WriteChunk( idx1_chunk, ( void* ) idx1 );
1298 RIFFFile::WriteRIFF();
1302 AVI2File::AVI2File() : AVIFile()
1306 AVI2File::~AVI2File()
1310 /* Initialize the AVI structure to its initial state, either for PAL
1313 void AVI2File::Init( int format, int sampleFrequency, int indexType )
1322 assert( ( format == AVI_PAL ) || ( format == AVI_NTSC ) );
1324 AVIFile::Init( format, sampleFrequency, indexType );
1330 mainHdr.dwStreams = 2;
1331 mainHdr.dwWidth = 720;
1332 mainHdr.dwHeight = 576;
1334 /* Initialize the 'strh' chunk */
1336 streamHdr[ 0 ].fccType = make_fourcc( "vids" );
1337 streamHdr[ 0 ].fccHandler = make_fourcc( "dvsd" );
1338 streamHdr[ 0 ].dwFlags = 0;
1339 streamHdr[ 0 ].wPriority = 0;
1340 streamHdr[ 0 ].wLanguage = 0;
1341 streamHdr[ 0 ].dwInitialFrames = 0;
1342 streamHdr[ 0 ].dwScale = 1;
1343 streamHdr[ 0 ].dwRate = 25;
1344 streamHdr[ 0 ].dwStart = 0;
1345 streamHdr[ 0 ].dwLength = 0;
1346 streamHdr[ 0 ].dwSuggestedBufferSize = 144008;
1347 streamHdr[ 0 ].dwQuality = -1;
1348 streamHdr[ 0 ].dwSampleSize = 0;
1349 streamHdr[ 0 ].rcFrame.top = 0;
1350 streamHdr[ 0 ].rcFrame.bottom = 0;
1351 streamHdr[ 0 ].rcFrame.left = 0;
1352 streamHdr[ 0 ].rcFrame.right = 0;
1354 bitmapinfo.biSize = sizeof( bitmapinfo );
1355 bitmapinfo.biWidth = 720;
1356 bitmapinfo.biHeight = 576;
1357 bitmapinfo.biPlanes = 1;
1358 bitmapinfo.biBitCount = 24;
1359 bitmapinfo.biCompression = make_fourcc( "dvsd" );
1360 bitmapinfo.biSizeImage = 144000;
1361 bitmapinfo.biXPelsPerMeter = 0;
1362 bitmapinfo.biYPelsPerMeter = 0;
1363 bitmapinfo.biClrUsed = 0;
1364 bitmapinfo.biClrImportant = 0;
1366 streamHdr[ 1 ].fccType = make_fourcc( "auds" );
1367 streamHdr[ 1 ].fccHandler = 0;
1368 streamHdr[ 1 ].dwFlags = 0;
1369 streamHdr[ 1 ].wPriority = 0;
1370 streamHdr[ 1 ].wLanguage = 0;
1371 streamHdr[ 1 ].dwInitialFrames = 0;
1372 streamHdr[ 1 ].dwScale = 2 * 2;
1373 streamHdr[ 1 ].dwRate = sampleFrequency * 2 * 2;
1374 streamHdr[ 1 ].dwStart = 0;
1375 streamHdr[ 1 ].dwLength = 0;
1376 streamHdr[ 1 ].dwSuggestedBufferSize = 8192;
1377 streamHdr[ 1 ].dwQuality = -1;
1378 streamHdr[ 1 ].dwSampleSize = 2 * 2;
1379 streamHdr[ 1 ].rcFrame.top = 0;
1380 streamHdr[ 1 ].rcFrame.bottom = 0;
1381 streamHdr[ 1 ].rcFrame.left = 0;
1382 streamHdr[ 1 ].rcFrame.right = 0;
1387 mainHdr.dwTotalFrames = 0;
1388 mainHdr.dwStreams = 2;
1389 mainHdr.dwWidth = 720;
1390 mainHdr.dwHeight = 480;
1392 /* Initialize the 'strh' chunk */
1394 streamHdr[ 0 ].fccType = make_fourcc( "vids" );
1395 streamHdr[ 0 ].fccHandler = make_fourcc( "dvsd" );
1396 streamHdr[ 0 ].dwFlags = 0;
1397 streamHdr[ 0 ].wPriority = 0;
1398 streamHdr[ 0 ].wLanguage = 0;
1399 streamHdr[ 0 ].dwInitialFrames = 0;
1400 streamHdr[ 0 ].dwScale = 1001;
1401 streamHdr[ 0 ].dwRate = 30000;
1402 streamHdr[ 0 ].dwStart = 0;
1403 streamHdr[ 0 ].dwLength = 0;
1404 streamHdr[ 0 ].dwSuggestedBufferSize = 120008;
1405 streamHdr[ 0 ].dwQuality = -1;
1406 streamHdr[ 0 ].dwSampleSize = 0;
1407 streamHdr[ 0 ].rcFrame.top = 0;
1408 streamHdr[ 0 ].rcFrame.bottom = 0;
1409 streamHdr[ 0 ].rcFrame.left = 0;
1410 streamHdr[ 0 ].rcFrame.right = 0;
1412 bitmapinfo.biSize = sizeof( bitmapinfo );
1413 bitmapinfo.biWidth = 720;
1414 bitmapinfo.biHeight = 480;
1415 bitmapinfo.biPlanes = 1;
1416 bitmapinfo.biBitCount = 24;
1417 bitmapinfo.biCompression = make_fourcc( "dvsd" );
1418 bitmapinfo.biSizeImage = 120000;
1419 bitmapinfo.biXPelsPerMeter = 0;
1420 bitmapinfo.biYPelsPerMeter = 0;
1421 bitmapinfo.biClrUsed = 0;
1422 bitmapinfo.biClrImportant = 0;
1424 streamHdr[ 1 ].fccType = make_fourcc( "auds" );
1425 streamHdr[ 1 ].fccHandler = 0;
1426 streamHdr[ 1 ].dwFlags = 0;
1427 streamHdr[ 1 ].wPriority = 0;
1428 streamHdr[ 1 ].wLanguage = 0;
1429 streamHdr[ 1 ].dwInitialFrames = 1;
1430 streamHdr[ 1 ].dwScale = 2 * 2;
1431 streamHdr[ 1 ].dwRate = sampleFrequency * 2 * 2;
1432 streamHdr[ 1 ].dwStart = 0;
1433 streamHdr[ 1 ].dwLength = 0;
1434 streamHdr[ 1 ].dwSuggestedBufferSize = 8192;
1435 streamHdr[ 1 ].dwQuality = 0;
1436 streamHdr[ 1 ].dwSampleSize = 2 * 2;
1437 streamHdr[ 1 ].rcFrame.top = 0;
1438 streamHdr[ 1 ].rcFrame.bottom = 0;
1439 streamHdr[ 1 ].rcFrame.left = 0;
1440 streamHdr[ 1 ].rcFrame.right = 0;
1444 waveformatex.wFormatTag = 1;
1445 waveformatex.nChannels = 2;
1446 waveformatex.nSamplesPerSec = sampleFrequency;
1447 waveformatex.nAvgBytesPerSec = sampleFrequency * 2 * 2;
1448 waveformatex.nBlockAlign = 4;
1449 waveformatex.wBitsPerSample = 16;
1450 waveformatex.cbSize = 0;
1452 file_list = AddDirectoryEntry( make_fourcc( "FILE" ), make_fourcc( "FILE" ), 0, RIFF_NO_PARENT );
1454 /* Create a basic directory structure. Only chunks defined from here on will be written to the AVI file. */
1456 riff_list = AddDirectoryEntry( make_fourcc( "RIFF" ), make_fourcc( "AVI " ), RIFF_LISTSIZE, file_list );
1457 hdrl_list = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "hdrl" ), RIFF_LISTSIZE, riff_list );
1458 avih_chunk = AddDirectoryEntry( make_fourcc( "avih" ), 0, sizeof( MainAVIHeader ), hdrl_list );
1460 strl_list[ 0 ] = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "strl" ), RIFF_LISTSIZE, hdrl_list );
1461 strh_chunk[ 0 ] = AddDirectoryEntry( make_fourcc( "strh" ), 0, sizeof( AVIStreamHeader ), strl_list[ 0 ] );
1462 strf_chunk[ 0 ] = AddDirectoryEntry( make_fourcc( "strf" ), 0, sizeof( BITMAPINFOHEADER ), strl_list[ 0 ] );
1463 if ( index_type & AVI_LARGE_INDEX )
1465 indx_chunk[ 0 ] = AddDirectoryEntry( make_fourcc( "indx" ), 0, sizeof( AVISuperIndex ), strl_list[ 0 ] );
1467 indx[ 0 ] ->dwChunkId = make_fourcc( "00dc" );
1470 strl_list[ 1 ] = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "strl" ), RIFF_LISTSIZE, hdrl_list );
1471 strh_chunk[ 1 ] = AddDirectoryEntry( make_fourcc( "strh" ), 0, sizeof( AVIStreamHeader ), strl_list[ 1 ] );
1472 strf_chunk[ 1 ] = AddDirectoryEntry( make_fourcc( "strf" ), 0, sizeof( WAVEFORMATEX ) - 2, strl_list[ 1 ] );
1473 junk_chunk = AddDirectoryEntry( make_fourcc( "JUNK" ), 0, 2, strl_list[ 1 ] );
1474 if ( index_type & AVI_LARGE_INDEX )
1476 indx_chunk[ 1 ] = AddDirectoryEntry( make_fourcc( "indx" ), 0, sizeof( AVISuperIndex ), strl_list[ 1 ] );
1478 indx[ 1 ] ->dwChunkId = make_fourcc( "01wb" );
1480 odml_list = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "odml" ), RIFF_LISTSIZE, hdrl_list );
1481 dmlh_chunk = AddDirectoryEntry( make_fourcc( "dmlh" ), 0, 0x00f8, odml_list );
1484 /* align movi list to block */
1485 GetDirectoryEntry( hdrl_list, type, name, length, offset, parent );
1486 num_blocks = length / PADDING_SIZE + 1;
1487 length = num_blocks * PADDING_SIZE - length - 5 * RIFF_HEADERSIZE; // why 5 headers?
1488 junk_chunk = AddDirectoryEntry( make_fourcc( "JUNK" ), 0, length, riff_list );
1490 movi_list = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "movi" ), RIFF_LISTSIZE, riff_list );
1492 idx1->aIndex[ idx1->nEntriesInUse ].dwChunkId = make_fourcc( "7Fxx" );
1493 idx1->aIndex[ idx1->nEntriesInUse ].dwFlags = 0;
1494 idx1->aIndex[ idx1->nEntriesInUse ].dwOffset = 0;
1495 idx1->aIndex[ idx1->nEntriesInUse ].dwSize = 0;
1496 idx1->nEntriesInUse++;
1500 void AVI2File::WriteRIFF()
1502 WriteChunk( avih_chunk, ( void* ) & mainHdr );
1503 WriteChunk( strh_chunk[ 0 ], ( void* ) & streamHdr[ 0 ] );
1504 WriteChunk( strf_chunk[ 0 ], ( void* ) & bitmapinfo );
1505 if ( index_type & AVI_LARGE_INDEX )
1507 WriteChunk( dmlh_chunk, ( void* ) & dmlh );
1508 WriteChunk( indx_chunk[ 0 ], ( void* ) indx[ 0 ] );
1509 WriteChunk( ix_chunk[ 0 ], ( void* ) ix[ 0 ] );
1511 WriteChunk( strh_chunk[ 1 ], ( void* ) & streamHdr[ 1 ] );
1512 WriteChunk( strf_chunk[ 1 ], ( void* ) & waveformatex );
1513 if ( index_type & AVI_LARGE_INDEX )
1515 WriteChunk( indx_chunk[ 1 ], ( void* ) indx[ 1 ] );
1516 WriteChunk( ix_chunk[ 1 ], ( void* ) ix[ 1 ] );
1519 if ( ( index_type & AVI_SMALL_INDEX ) && isUpdateIdx1 )
1521 int idx1_chunk = AddDirectoryEntry( make_fourcc( "idx1" ), 0, idx1->nEntriesInUse * 16, riff_list );
1522 WriteChunk( idx1_chunk, ( void* ) idx1 );
1524 RIFFFile::WriteRIFF();
1528 /** Write a DV video frame
1530 \param frame the frame to write
1534 bool AVI2File::WriteFrame( const Frame &frame )
1539 char soundbuf[ 20000 ];
1548 /* exit if no large index and 1GB reached */
1549 if ( !( index_type & AVI_LARGE_INDEX ) && isUpdateIdx1 == false )
1552 /* Check if we need a new ix00 Standard Index. It has a
1553 capacity of IX00_INDEX_SIZE frames. Whenever we exceed that
1554 number, we need a new index. The new ix00 chunk is also
1555 part of the movi list. */
1557 if ( ( index_type & AVI_LARGE_INDEX ) && ( ( ( streamHdr[ 0 ].dwLength - 0 ) % IX00_INDEX_SIZE ) == 0 ) )
1563 /* Write audio data if we have it */
1565 audio_size = frame.ExtractAudio( soundbuf );
1566 if ( audio_size > 0 )
1568 audio_chunk = AddDirectoryEntry( make_fourcc( "01wb" ), 0, audio_size, movi_list );
1569 if ( ( index_type & AVI_LARGE_INDEX ) && ( streamHdr[ 0 ].dwLength % IX00_INDEX_SIZE ) == 0 )
1571 GetDirectoryEntry( audio_chunk, type, name, length, offset, parent );
1572 ix[ 1 ] ->qwBaseOffset = offset - RIFF_HEADERSIZE;
1574 WriteChunk( audio_chunk, soundbuf );
1575 // num_blocks = (audio_size + RIFF_HEADERSIZE) / PADDING_SIZE + 1;
1576 // length = num_blocks * PADDING_SIZE - audio_size - 2 * RIFF_HEADERSIZE;
1577 // junk_chunk = AddDirectoryEntry(make_fourcc("JUNK"), 0, length, movi_list);
1578 // WriteChunk(junk_chunk, g_zeroes);
1579 if ( index_type & AVI_LARGE_INDEX )
1580 UpdateIndx( 1, audio_chunk, audio_size / waveformatex.nChannels / 2 );
1581 if ( ( index_type & AVI_SMALL_INDEX ) && isUpdateIdx1 )
1582 UpdateIdx1( audio_chunk, 0x00 );
1583 streamHdr[ 1 ].dwLength += audio_size / waveformatex.nChannels / 2;
1587 /* Write video data */
1589 frame_chunk = AddDirectoryEntry( make_fourcc( "00dc" ), 0, frame.GetFrameSize(), movi_list );
1590 if ( ( index_type & AVI_LARGE_INDEX ) && ( streamHdr[ 0 ].dwLength % IX00_INDEX_SIZE ) == 0 )
1592 GetDirectoryEntry( frame_chunk, type, name, length, offset, parent );
1593 ix[ 0 ] ->qwBaseOffset = offset - RIFF_HEADERSIZE;
1595 WriteChunk( frame_chunk, frame.data );
1596 // num_blocks = (frame.GetFrameSize() + RIFF_HEADERSIZE) / PADDING_SIZE + 1;
1597 // length = num_blocks * PADDING_SIZE - frame.GetFrameSize() - 2 * RIFF_HEADERSIZE;
1598 // junk_chunk = AddDirectoryEntry(make_fourcc("JUNK"), 0, length, movi_list);
1599 // WriteChunk(junk_chunk, g_zeroes);
1600 if ( index_type & AVI_LARGE_INDEX )
1601 UpdateIndx( 0, frame_chunk, 1 );
1602 if ( ( index_type & AVI_SMALL_INDEX ) && isUpdateIdx1 )
1603 UpdateIdx1( frame_chunk, 0x10 );
1605 /* update some variables with the new frame count. */
1608 ++mainHdr.dwTotalFrames;
1609 ++streamHdr[ 0 ].dwLength;
1612 /* Find out if the current riff list is close to 1 GByte in
1613 size. If so, start a new (extended) RIFF. The only allowed
1614 item in the new RIFF chunk is a movi list (with video
1615 frames and indexes as usual). */
1617 GetDirectoryEntry( riff_list, type, name, length, offset, parent );
1618 if ( length > 0x3f000000 )
1621 /* write idx1 only once and before end of first GB */
1622 if ( ( index_type & AVI_SMALL_INDEX ) && isUpdateIdx1 )
1624 int idx1_chunk = AddDirectoryEntry( make_fourcc( "idx1" ), 0, idx1->nEntriesInUse * 16, riff_list );
1625 WriteChunk( idx1_chunk, ( void* ) idx1 );
1627 isUpdateIdx1 = false;
1629 if ( index_type & AVI_LARGE_INDEX )
1631 /* padding for alignment */
1632 GetDirectoryEntry( riff_list, type, name, length, offset, parent );
1633 num_blocks = ( length + 4 * RIFF_HEADERSIZE ) / PADDING_SIZE + 1;
1634 length = ( num_blocks * PADDING_SIZE ) - length - 4 * RIFF_HEADERSIZE - 2 * RIFF_LISTSIZE;
1637 junk_chunk = AddDirectoryEntry( make_fourcc( "JUNK" ), 0, length, riff_list );
1638 WriteChunk( junk_chunk, g_zeroes );
1641 riff_list = AddDirectoryEntry( make_fourcc( "RIFF" ), make_fourcc( "AVIX" ), RIFF_LISTSIZE, file_list );
1642 movi_list = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "movi" ), RIFF_LISTSIZE, riff_list );
1649 void AVI1File::setDVINFO( DVINFO &info )
1651 // do not do this until debugged audio against DirectShow
1654 dvinfo.dwDVAAuxSrc = info.dwDVAAuxSrc;
1655 dvinfo.dwDVAAuxCtl = info.dwDVAAuxCtl;
1656 dvinfo.dwDVAAuxSrc1 = info.dwDVAAuxSrc1;
1657 dvinfo.dwDVAAuxCtl1 = info.dwDVAAuxCtl1;
1658 dvinfo.dwDVVAuxSrc = info.dwDVVAuxSrc;
1659 dvinfo.dwDVVAuxCtl = info.dwDVVAuxCtl;
1663 void AVI2File::setDVINFO( DVINFO &info )
1666 void AVIFile::setFccHandler( FOURCC type, FOURCC handler )
1668 for ( int i = 0; i < mainHdr.dwStreams; i++ )
1670 if ( streamHdr[ i ].fccType == type )
1673 FOURCC strf = make_fourcc( "strf" );
1674 BITMAPINFOHEADER bih;
1676 streamHdr[ i ].fccHandler = handler;
1678 while ( ( k = FindDirectoryEntry( strf, j++ ) ) != -1 )
1680 ReadChunk( k, ( void* ) & bih, sizeof( BITMAPINFOHEADER ) );
1681 bih.biCompression = handler;
1687 bool AVIFile::getStreamFormat( void* data, FOURCC type )
1690 FOURCC strh = make_fourcc( "strh" );
1691 FOURCC strf = make_fourcc( "strf" );
1692 AVIStreamHeader avi_stream_header;
1693 bool result = false;
1695 while ( ( result == false ) && ( i = FindDirectoryEntry( strh, j++ ) ) != -1 )
1697 ReadChunk( i, ( void* ) & avi_stream_header, sizeof( AVIStreamHeader ) );
1698 if ( avi_stream_header.fccType == type )
1703 pthread_mutex_lock( &file_mutex );
1704 fail_neg( read( fd, &chunkID, sizeof( FOURCC ) ) );
1705 if ( chunkID == strf )
1707 fail_neg( read( fd, &size, sizeof( int ) ) );
1708 fail_neg( read( fd, data, size ) );
1711 pthread_mutex_unlock( &file_mutex );