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.
47 #define PADDING_SIZE (512)
48 #define PADDING_1GB (0x40000000)
49 #define IX00_INDEX_SIZE (4028)
51 #define AVIF_HASINDEX 0x00000010
52 #define AVIF_MUSTUSEINDEX 0x00000020
53 #define AVIF_TRUSTCKTYPE 0x00000800
54 #define AVIF_ISINTERLEAVED 0x00000100
55 #define AVIF_WASCAPTUREFILE 0x00010000
56 #define AVIF_COPYRIGHTED 0x00020000
59 //static char g_zeroes[ PADDING_SIZE ];
63 \todo mainHdr not initialized
64 \todo add checking for NULL pointers
68 AVIFile::AVIFile() : RIFFFile(),
69 idx1( NULL ), file_list( -1 ), riff_list( -1 ),
70 hdrl_list( -1 ), avih_chunk( -1 ), movi_list( -1 ), junk_chunk( -1 ), idx1_chunk( -1 ),
71 index_type( -1 ), current_ix00( -1 ), odml_list( -1 ), dmlh_chunk( -1 ), isUpdateIdx1( true )
73 // cerr << "0x" << hex << (long)this << dec << " AVIFile::AVIFile() : RIFFFile(), ..." << endl;
75 for ( int i = 0; i < 2; ++i )
77 indx[ i ] = new AVISuperIndex;
78 memset( indx[ i ], 0, sizeof( AVISuperIndex ) );
79 ix[ i ] = new AVIStdIndex;
80 memset( ix[ i ], 0, sizeof( AVIStdIndex ) );
87 idx1 = new AVISimpleIndex;
88 memset( idx1, 0, sizeof( AVISimpleIndex ) );
89 memset( dmlh, 0, sizeof( dmlh ) );
90 memset( &mainHdr, 0, sizeof( mainHdr ) );
91 memset( &streamHdr, 0, sizeof( streamHdr ) );
95 /** The copy constructor
97 \todo add checking for NULL pointers
101 AVIFile::AVIFile( const AVIFile& avi ) : RIFFFile( avi )
103 // cerr << "0x" << hex << (long)this << dec << " 0x" << hex << (long)&avi << dec << " AVIFile::AVIFile(const AVIFile& avi) : RIFFFile(avi)" << endl;
105 mainHdr = avi.mainHdr;
106 idx1 = new AVISimpleIndex;
108 file_list = avi.file_list;
109 riff_list = avi.riff_list;
110 hdrl_list = avi.hdrl_list;
111 avih_chunk = avi.avih_chunk;
112 movi_list = avi.movi_list;
113 junk_chunk = avi.junk_chunk;
114 idx1_chunk = avi.idx1_chunk;
116 for ( int i = 0; i < 2; ++i )
118 indx[ i ] = new AVISuperIndex;
119 *indx[ i ] = *avi.indx[ i ];
120 ix[ i ] = new AVIStdIndex;
121 *ix[ i ] = *avi.ix[ i ];
122 indx_chunk[ i ] = avi.indx_chunk[ i ];
123 ix_chunk[ i ] = avi.ix_chunk[ i ];
124 strl_list[ i ] = avi.strl_list[ i ];
125 strh_chunk[ i ] = avi.strh_chunk[ i ];
126 strf_chunk[ i ] = avi.strf_chunk[ i ];
129 index_type = avi.index_type;
130 current_ix00 = avi.current_ix00;
132 for ( int i = 0; i < 62; ++i )
133 dmlh[ i ] = avi.dmlh[ i ];
135 isUpdateIdx1 = avi.isUpdateIdx1;
139 memset( &streamHdr, 0, sizeof( streamHdr ) );
143 /** The assignment operator
147 AVIFile& AVIFile::operator=( const AVIFile& avi )
149 // cerr << "0x" << hex << (long)this << dec << " 0x" << hex << (long)&avi << dec << " AVIFile& AVIFile::operator=(const AVIFile& avi)" << endl;
153 RIFFFile::operator=( avi );
154 mainHdr = avi.mainHdr;
156 file_list = avi.file_list;
157 riff_list = avi.riff_list;
158 hdrl_list = avi.hdrl_list;
159 avih_chunk = avi.avih_chunk;
160 movi_list = avi.movi_list;
161 junk_chunk = avi.junk_chunk;
162 idx1_chunk = avi.idx1_chunk;
164 for ( int i = 0; i < 2; ++i )
166 *indx[ i ] = *avi.indx[ i ];
167 *ix[ i ] = *avi.ix[ i ];
168 indx_chunk[ i ] = avi.indx_chunk[ i ];
169 ix_chunk[ i ] = avi.ix_chunk[ i ];
170 strl_list[ i ] = avi.strl_list[ i ];
171 strh_chunk[ i ] = avi.strh_chunk[ i ];
172 strf_chunk[ i ] = avi.strf_chunk[ i ];
175 index_type = avi.index_type;
176 current_ix00 = avi.current_ix00;
178 for ( int i = 0; i < 62; ++i )
179 dmlh[ i ] = avi.dmlh[ i ];
181 isUpdateIdx1 = avi.isUpdateIdx1;
193 // cerr << "0x" << hex << (long)this << dec << " AVIFile::~AVIFile()" << endl;
195 for ( int i = 0; i < 2; ++i )
203 /** Initialize the AVI structure to its initial state, either for PAL or NTSC format
205 Initialize the AVIFile attributes: mainHdr, indx, ix00, idx1
207 \todo consolidate AVIFile::Init, AVI1File::Init, AVI2File::Init. They are somewhat redundant.
208 \param format pass AVI_PAL or AVI_NTSC
209 \param sampleFrequency the sample frequency of the audio content
210 \param indexType pass AVI_SMALL_INDEX or AVI_LARGE_INDEX
214 void AVIFile::Init( int format, int sampleFrequency, int indexType )
218 assert( ( format == AVI_PAL ) || ( format == AVI_NTSC ) );
220 index_type = indexType;
225 mainHdr.dwMicroSecPerFrame = 40000;
226 mainHdr.dwSuggestedBufferSize = 144008;
230 mainHdr.dwMicroSecPerFrame = 33366;
231 mainHdr.dwSuggestedBufferSize = 120008;
234 default: /* no default allowed */
239 /* Initialize the 'avih' chunk */
241 mainHdr.dwMaxBytesPerSec = 3600000 + sampleFrequency * 4;
242 mainHdr.dwPaddingGranularity = PADDING_SIZE;
243 mainHdr.dwFlags = AVIF_TRUSTCKTYPE;
244 if ( indexType & AVI_SMALL_INDEX )
245 mainHdr.dwFlags |= AVIF_HASINDEX;
246 mainHdr.dwTotalFrames = 0;
247 mainHdr.dwInitialFrames = 0;
248 mainHdr.dwStreams = 1;
250 mainHdr.dwHeight = 0;
251 mainHdr.dwReserved[ 0 ] = 0;
252 mainHdr.dwReserved[ 1 ] = 0;
253 mainHdr.dwReserved[ 2 ] = 0;
254 mainHdr.dwReserved[ 3 ] = 0;
256 /* Initialize the 'idx1' chunk */
258 for ( int i = 0; i < 8000; ++i )
260 idx1->aIndex[ i ].dwChunkId = 0;
261 idx1->aIndex[ i ].dwFlags = 0;
262 idx1->aIndex[ i ].dwOffset = 0;
263 idx1->aIndex[ i ].dwSize = 0;
265 idx1->nEntriesInUse = 0;
267 /* Initialize the 'indx' chunk */
269 for ( i = 0; i < 2; ++i )
271 indx[ i ] ->wLongsPerEntry = 4;
272 indx[ i ] ->bIndexSubType = 0;
273 indx[ i ] ->bIndexType = KINO_AVI_INDEX_OF_INDEXES;
274 indx[ i ] ->nEntriesInUse = 0;
275 indx[ i ] ->dwReserved[ 0 ] = 0;
276 indx[ i ] ->dwReserved[ 1 ] = 0;
277 indx[ i ] ->dwReserved[ 2 ] = 0;
278 for ( j = 0; j < 2014; ++j )
280 indx[ i ] ->aIndex[ j ].qwOffset = 0;
281 indx[ i ] ->aIndex[ j ].dwSize = 0;
282 indx[ i ] ->aIndex[ j ].dwDuration = 0;
286 /* The ix00 and ix01 chunk will be added dynamically in avi_write_frame
289 /* Initialize the 'dmlh' chunk. I have no clue what this means
292 for ( i = 0; i < 62; ++i )
294 //dmlh[0] = -1; /* frame count + 1? */
299 /** Find position and size of a given frame in the file
301 Depending on which index is available, search one of them to
302 find position and frame size
304 \todo the size parameter is redundant. All frames have the same size, which is also in the mainHdr.
305 \todo all index related operations should be isolated
306 \param offset the file offset to the start of the frame
307 \param size the size of the frame
308 \param frameNum the number of the frame we wish to find
309 \return 0 if the frame could be found, -1 otherwise
312 int AVIFile::GetDVFrameInfo( off_t &offset, int &size, int frameNum )
314 switch ( index_type )
316 case AVI_LARGE_INDEX:
318 /* find relevant index in indx0 */
322 for ( i = 0; frameNum >= indx[ 0 ] ->aIndex[ i ].dwDuration; frameNum -= indx[ 0 ] ->aIndex[ i ].dwDuration, ++i )
325 if ( i != current_ix00 )
327 fail_if( lseek( fd, indx[ 0 ] ->aIndex[ i ].qwOffset + RIFF_HEADERSIZE, SEEK_SET ) == ( off_t ) - 1 );
328 fail_neg( read( fd, ix[ 0 ], indx[ 0 ] ->aIndex[ i ].dwSize - RIFF_HEADERSIZE ) );
332 if ( frameNum < ix[ 0 ] ->nEntriesInUse )
334 offset = ix[ 0 ] ->qwBaseOffset + ix[ 0 ] ->aIndex[ frameNum ].dwOffset;
335 size = ix[ 0 ] ->aIndex[ frameNum ].dwSize;
342 case AVI_SMALL_INDEX:
344 int frameNumIndex = 0;
345 for ( int i = 0; i < idx1->nEntriesInUse; ++i )
347 FOURCC chunkID1 = make_fourcc( "00dc" );
348 FOURCC chunkID2 = make_fourcc( "00db" );
349 if ( idx1->aIndex[ i ].dwChunkId == chunkID1 ||
350 idx1->aIndex[ i ].dwChunkId == chunkID2 )
352 if ( frameNumIndex == frameNum )
362 // compatibility check for broken dvgrab dv2 format
363 if ( idx1->aIndex[ 0 ].dwOffset > GetDirectoryEntry( movi_list ).offset )
365 offset = idx1->aIndex[ index ].dwOffset + RIFF_HEADERSIZE;
369 // new, correct dv2 format
370 offset = idx1->aIndex[ index ].dwOffset + RIFF_HEADERSIZE + GetDirectoryEntry( movi_list ).offset;
372 size = idx1->aIndex[ index ].dwSize;
382 /** Find position and size of a given frame in the file
384 Depending on which index is available, search one of them to
385 find position and frame size
387 \todo the size parameter is redundant. All frames have the same size, which is also in the mainHdr.
388 \todo all index related operations should be isolated
389 \param offset the file offset to the start of the frame
390 \param size the size of the frame
391 \param frameNum the number of the frame we wish to find
392 \param chunkID the ID of the type of chunk we want
393 \return 0 if the frame could be found, -1 otherwise
396 int AVIFile::GetFrameInfo( off_t &offset, int &size, int frameNum, FOURCC chunkID )
398 if ( index_type & AVI_LARGE_INDEX )
402 for ( i = 0; frameNum >= indx[ 0 ] ->aIndex[ i ].dwDuration; frameNum -= indx[ 0 ] ->aIndex[ i ].dwDuration, ++i )
405 if ( i != current_ix00 )
407 fail_if( lseek( fd, indx[ 0 ] ->aIndex[ i ].qwOffset + RIFF_HEADERSIZE, SEEK_SET ) == ( off_t ) - 1 );
408 fail_neg( read( fd, ix[ 0 ], indx[ 0 ] ->aIndex[ i ].dwSize - RIFF_HEADERSIZE ) );
412 if ( frameNum < ix[ 0 ] ->nEntriesInUse )
414 if ( ( FOURCC ) ix[ 0 ] ->dwChunkId == chunkID )
416 offset = ix[ 0 ] ->qwBaseOffset + ix[ 0 ] ->aIndex[ frameNum ].dwOffset;
417 size = ix[ 0 ] ->aIndex[ frameNum ].dwSize;
422 if ( index_type & AVI_SMALL_INDEX )
425 int frameNumIndex = 0;
426 for ( int i = 0; i < idx1->nEntriesInUse; ++i )
428 if ( idx1->aIndex[ i ].dwChunkId == chunkID )
430 if ( frameNumIndex == frameNum )
440 // compatibility check for broken dvgrab dv2 format
441 if ( idx1->aIndex[ 0 ].dwOffset > GetDirectoryEntry( movi_list ).offset )
443 offset = idx1->aIndex[ index ].dwOffset + RIFF_HEADERSIZE;
447 // new, correct dv2 format
448 offset = idx1->aIndex[ index ].dwOffset + RIFF_HEADERSIZE + GetDirectoryEntry( movi_list ).offset;
450 size = idx1->aIndex[ index ].dwSize;
459 \todo we actually don't need the frame here, we could use just a void pointer
460 \param frame a reference to the frame object that will receive the frame data
461 \param frameNum the frame number to read
462 \return 0 if the frame could be read, -1 otherwise
465 int AVIFile::GetDVFrame( uint8_t *data, int frameNum )
470 if ( GetDVFrameInfo( offset, size, frameNum ) != 0 || size < 0 )
472 pthread_mutex_lock( &file_mutex );
473 fail_if( lseek( fd, offset, SEEK_SET ) == ( off_t ) - 1 );
474 fail_neg( read( fd, data, size ) );
475 pthread_mutex_unlock( &file_mutex );
482 \param data a pointer to the audio buffer
483 \param frameNum the frame number to read
484 \param chunkID the ID of the type of chunk we want
485 \return the size the of the frame data, 0 if could not be read
488 int AVIFile::getFrame( void *data, int frameNum, FOURCC chunkID )
493 if ( GetFrameInfo( offset, size, frameNum, chunkID ) != 0 )
495 fail_if( lseek( fd, offset, SEEK_SET ) == ( off_t ) - 1 );
496 fail_neg( read( fd, data, size ) );
501 int AVIFile::GetTotalFrames() const
503 return mainHdr.dwTotalFrames;
507 /** prints out a directory entry in text form
509 Every subclass of RIFFFile is supposed to override this function
510 and to implement it for the entry types it knows about. For all
511 other entry types it should call its parent::PrintDirectoryData.
513 \todo use 64 bit routines
514 \param entry the entry to print
517 void AVIFile::PrintDirectoryEntryData( const RIFFDirEntry &entry ) const
519 static FOURCC lastStreamType = make_fourcc( " " );
521 if ( entry.type == make_fourcc( "avih" ) )
525 MainAVIHeader main_avi_header;
527 fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 );
528 fail_neg( read( fd, &main_avi_header, sizeof( MainAVIHeader ) ) );
530 cout << " dwMicroSecPerFrame: " << ( int ) main_avi_header.dwMicroSecPerFrame << endl
531 << " dwMaxBytesPerSec: " << ( int ) main_avi_header.dwMaxBytesPerSec << endl
532 << " dwPaddingGranularity: " << ( int ) main_avi_header.dwPaddingGranularity << endl
533 << " dwFlags: " << ( int ) main_avi_header.dwFlags << endl
534 << " dwTotalFrames: " << ( int ) main_avi_header.dwTotalFrames << endl
535 << " dwInitialFrames: " << ( int ) main_avi_header.dwInitialFrames << endl
536 << " dwStreams: " << ( int ) main_avi_header.dwStreams << endl
537 << " dwSuggestedBufferSize: " << ( int ) main_avi_header.dwSuggestedBufferSize << endl
538 << " dwWidth: " << ( int ) main_avi_header.dwWidth << endl
539 << " dwHeight: " << ( int ) main_avi_header.dwHeight << endl;
540 for ( i = 0; i < 4; ++i )
541 cout << " dwReserved[" << i << "]: " << ( int ) main_avi_header.dwReserved[ i ] << endl;
544 else if ( entry.type == make_fourcc( "strh" ) )
547 AVIStreamHeader avi_stream_header;
549 fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 );
550 fail_neg( read( fd, &avi_stream_header, sizeof( AVIStreamHeader ) ) );
552 lastStreamType = avi_stream_header.fccType;
554 cout << " fccType: '"
555 << ((char *)&avi_stream_header.fccType)[0]
556 << ((char *)&avi_stream_header.fccType)[1]
557 << ((char *)&avi_stream_header.fccType)[2]
558 << ((char *)&avi_stream_header.fccType)[3]
561 << ((char *)&avi_stream_header.fccHandler)[0]
562 << ((char *)&avi_stream_header.fccHandler)[1]
563 << ((char *)&avi_stream_header.fccHandler)[2]
564 << ((char *)&avi_stream_header.fccHandler)[3]
566 << " dwFlags: " << ( int ) avi_stream_header.dwFlags << endl
567 << " wPriority: " << ( int ) avi_stream_header.wPriority << endl
568 << " wLanguage: " << ( int ) avi_stream_header.wLanguage << endl
569 << " dwInitialFrames: " << ( int ) avi_stream_header.dwInitialFrames << endl
570 << " dwScale: " << ( int ) avi_stream_header.dwScale << endl
571 << " dwRate: " << ( int ) avi_stream_header.dwRate << endl
572 << " dwLength: " << ( int ) avi_stream_header.dwLength << endl
573 << " dwQuality: " << ( int ) avi_stream_header.dwQuality << endl
574 << " dwSampleSize: " << ( int ) avi_stream_header.dwSampleSize << endl;
577 else if ( entry.type == make_fourcc( "indx" ) )
581 AVISuperIndex avi_super_index;
583 fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 );
584 fail_neg( read( fd, &avi_super_index, sizeof( AVISuperIndex ) ) );
586 cout << " wLongsPerEntry: " << ( int ) avi_super_index.wLongsPerEntry
588 << " bIndexSubType: " << ( int ) avi_super_index.bIndexSubType << endl
589 << " bIndexType: " << ( int ) avi_super_index.bIndexType << endl
590 << " nEntriesInUse: " << ( int ) avi_super_index.nEntriesInUse << endl
592 << ((char *)&avi_super_index.dwChunkId)[0]
593 << ((char *)&avi_super_index.dwChunkId)[1]
594 << ((char *)&avi_super_index.dwChunkId)[2]
595 << ((char *)&avi_super_index.dwChunkId)[3]
597 << " dwReserved[0]: " << ( int ) avi_super_index.dwReserved[ 0 ] << endl
598 << " dwReserved[1]: " << ( int ) avi_super_index.dwReserved[ 1 ] << endl
599 << " dwReserved[2]: " << ( int ) avi_super_index.dwReserved[ 2 ] << endl;
600 for ( i = 0; i < avi_super_index.nEntriesInUse; ++i )
602 cout << ' ' << setw( 4 ) << setfill( ' ' ) << i
603 << ": qwOffset : 0x" << setw( 12 ) << setfill( '0' ) << hex << avi_super_index.aIndex[ i ].qwOffset << endl
604 << " dwSize : 0x" << setw( 8 ) << avi_super_index.aIndex[ i ].dwSize << endl
605 << " dwDuration : " << dec << avi_super_index.aIndex[ i ].dwDuration << endl;
608 else if ( entry.type == make_fourcc( "strf" ) )
610 if ( lastStreamType == make_fourcc( "auds" ) )
612 WAVEFORMATEX waveformatex;
613 fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 );
614 fail_neg( read( fd, &waveformatex, sizeof( WAVEFORMATEX ) ) );
615 cout << " waveformatex.wFormatTag : " << waveformatex.wFormatTag << endl;
616 cout << " waveformatex.nChannels : " << waveformatex.nChannels << endl;
617 cout << " waveformatex.nSamplesPerSec : " << waveformatex.nSamplesPerSec << endl;
618 cout << " waveformatex.nAvgBytesPerSec: " << waveformatex.nAvgBytesPerSec << endl;
619 cout << " waveformatex.nBlockAlign : " << waveformatex.nBlockAlign << endl;
620 cout << " waveformatex.wBitsPerSample : " << waveformatex.wBitsPerSample << endl;
621 cout << " waveformatex.cbSize : " << waveformatex.cbSize << endl;
623 else if ( lastStreamType == make_fourcc( "vids" ) )
625 BITMAPINFOHEADER bitmapinfo;
626 fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 );
627 fail_neg( read( fd, &bitmapinfo, sizeof( BITMAPINFOHEADER ) ) );
628 cout << " bitmapinfo.biSize : " << bitmapinfo.biSize << endl;
629 cout << " bitmapinfo.biWidth : " << bitmapinfo.biWidth << endl;
630 cout << " bitmapinfo.biHeight : " << bitmapinfo.biHeight << endl;
631 cout << " bitmapinfo.biPlanes : " << bitmapinfo.biPlanes << endl;
632 cout << " bitmapinfo.biBitCount : " << bitmapinfo.biBitCount << endl;
633 cout << " bitmapinfo.biCompression : " << bitmapinfo.biCompression << endl;
634 cout << " bitmapinfo.biSizeImage : " << bitmapinfo.biSizeImage << endl;
635 cout << " bitmapinfo.biXPelsPerMeter: " << bitmapinfo.biXPelsPerMeter << endl;
636 cout << " bitmapinfo.biYPelsPerMeter: " << bitmapinfo.biYPelsPerMeter << endl;
637 cout << " bitmapinfo.biClrUsed : " << bitmapinfo.biClrUsed << endl;
638 cout << " bitmapinfo.biClrImportant : " << bitmapinfo.biClrImportant << endl;
640 else if ( lastStreamType == make_fourcc( "iavs" ) )
643 fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 );
644 fail_neg( read( fd, &dvinfo, sizeof( DVINFO ) ) );
645 cout << " dvinfo.dwDVAAuxSrc : 0x" << setw( 8 ) << setfill( '0' ) << hex << dvinfo.dwDVAAuxSrc << endl;
646 cout << " dvinfo.dwDVAAuxCtl : 0x" << setw( 8 ) << setfill( '0' ) << hex << dvinfo.dwDVAAuxCtl << endl;
647 cout << " dvinfo.dwDVAAuxSrc1: 0x" << setw( 8 ) << setfill( '0' ) << hex << dvinfo.dwDVAAuxSrc1 << endl;
648 cout << " dvinfo.dwDVAAuxCtl1: 0x" << setw( 8 ) << setfill( '0' ) << hex << dvinfo.dwDVAAuxCtl1 << endl;
649 cout << " dvinfo.dwDVVAuxSrc : 0x" << setw( 8 ) << setfill( '0' ) << hex << dvinfo.dwDVVAuxSrc << endl;
650 cout << " dvinfo.dwDVVAuxCtl : 0x" << setw( 8 ) << setfill( '0' ) << hex << dvinfo.dwDVVAuxCtl << endl;
654 /* This is the Standard Index. It is an array of offsets and
655 sizes relative to some start offset. */
657 else if ( ( entry.type == make_fourcc( "ix00" ) ) || ( entry.type == make_fourcc( "ix01" ) ) )
661 AVIStdIndex avi_std_index;
663 fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 );
664 fail_neg( read( fd, &avi_std_index, sizeof( AVIStdIndex ) ) );
666 cout << " wLongsPerEntry: " << ( int ) avi_std_index.wLongsPerEntry
668 << " bIndexSubType: " << ( int ) avi_std_index.bIndexSubType << endl
669 << " bIndexType: " << ( int ) avi_std_index.bIndexType << endl
670 << " nEntriesInUse: " << ( int ) avi_std_index.nEntriesInUse << endl
672 << ((char *)&avi_std_index.dwChunkId)[0]
673 << ((char *)&avi_std_index.dwChunkId)[1]
674 << ((char *)&avi_std_index.dwChunkId)[2]
675 << ((char *)&avi_std_index.dwChunkId)[3]
677 << " qwBaseOffset: 0x" << setw( 12 ) << hex << avi_std_index.qwBaseOffset << endl
678 << " dwReserved: " << dec << ( int ) avi_std_index.dwReserved << endl;
679 for ( i = 0; i < avi_std_index.nEntriesInUse; ++i )
681 cout << ' ' << setw( 4 ) << setfill( ' ' ) << i
682 << ": dwOffset : 0x" << setw( 8 ) << setfill( '0' ) << hex << avi_std_index.aIndex[ i ].dwOffset
683 << " (0x" << setw( 12 ) << avi_std_index.qwBaseOffset + avi_std_index.aIndex[ i ].dwOffset << ')' << endl
684 << " dwSize : 0x" << setw( 8 ) << avi_std_index.aIndex[ i ].dwSize << dec << endl;
688 else if ( entry.type == make_fourcc( "idx1" ) )
692 int numEntries = entry.length / sizeof( int ) / 4;
693 DWORD *idx1 = new DWORD[ numEntries * 4 ];
694 // FOURCC movi_list = FindDirectoryEntry(make_fourcc("movi"));
696 fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 );
697 fail_neg( read( fd, idx1, entry.length ) );
699 for ( i = 0; i < numEntries; ++i )
702 cout << ' ' << setw( 4 ) << setfill( ' ' ) << i << setfill( '0' ) << ": dwChunkId : '"
703 << ((char *)&idx1[ i * 4 + 0 ])[0]
704 << ((char *)&idx1[ i * 4 + 0 ])[1]
705 << ((char *)&idx1[ i * 4 + 0 ])[2]
706 << ((char *)&idx1[ i * 4 + 0 ])[3]
708 << " dwType : 0x" << setw( 8 ) << hex << idx1[ i * 4 + 1 ] << endl
709 << " dwOffset : 0x" << setw( 8 ) << idx1[ i * 4 + 2 ] << endl
710 // << " (0x" << setw(8) << idx1[i * 4 + 2] + GetDirectoryEntry(movi_list).offset << ')' << endl
711 << " dwSize : 0x" << setw( 8 ) << idx1[ i * 4 + 3 ] << dec << endl;
716 else if ( entry.type == make_fourcc( "dmlh" ) )
719 int numEntries = entry.length / sizeof( int );
720 DWORD *dmlh = new DWORD[ numEntries ];
722 fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 );
723 fail_neg( read( fd, dmlh, entry.length ) );
725 for ( i = 0; i < numEntries; ++i )
727 cout << ' ' << setw( 4 ) << setfill( ' ' ) << i << setfill( '0' ) << ": "
728 << " dwTotalFrames: 0x" << setw( 8 ) << hex << dmlh[ i ]
729 << " (" << dec << dmlh[ i ] << ")" << endl;
736 /** If this is not a movi list, read its contents
740 void AVIFile::ParseList( int parent )
749 /* Read in the chunk header (type and length). */
750 fail_neg( read( fd, &type, sizeof( type ) ) );
751 fail_neg( read( fd, &length, sizeof( length ) ) );
755 /* The contents of the list starts here. Obtain its offset. The list
756 name (4 bytes) is already part of the contents). */
758 pos = lseek( fd, 0, SEEK_CUR );
759 fail_if( pos == ( off_t ) - 1 );
760 fail_neg( read( fd, &name, sizeof( name ) ) );
762 /* if we encounter a movi list, do not read it. It takes too much time
763 and we don't need it anyway. */
765 if ( name != make_fourcc( "movi" ) )
769 /* Add an entry for this list. */
770 list = AddDirectoryEntry( type, name, sizeof( name ), parent );
772 /* Read in any chunks contained in this list. This list is the
773 parent for all chunks it contains. */
775 listEnd = pos + length;
776 while ( pos < listEnd )
779 pos = lseek( fd, 0, SEEK_CUR );
780 fail_if( pos == ( off_t ) - 1 );
785 /* Add an entry for this list. */
787 movi_list = AddDirectoryEntry( type, name, length, parent );
789 pos = lseek( fd, length - 4, SEEK_CUR );
790 fail_if( pos == ( off_t ) - 1 );
795 void AVIFile::ParseRIFF()
797 RIFFFile::ParseRIFF();
799 avih_chunk = FindDirectoryEntry( make_fourcc( "avih" ) );
800 if ( avih_chunk != -1 )
801 ReadChunk( avih_chunk, ( void* ) & mainHdr, sizeof( MainAVIHeader ) );
805 void AVIFile::ReadIndex()
807 indx_chunk[ 0 ] = FindDirectoryEntry( make_fourcc( "indx" ) );
808 if ( indx_chunk[ 0 ] != -1 )
810 ReadChunk( indx_chunk[ 0 ], ( void* ) indx[ 0 ], sizeof( AVISuperIndex ) );
811 index_type = AVI_LARGE_INDEX;
813 /* recalc number of frames from each index */
814 mainHdr.dwTotalFrames = 0;
816 i < indx[ 0 ] ->nEntriesInUse;
817 mainHdr.dwTotalFrames += indx[ 0 ] ->aIndex[ i++ ].dwDuration )
821 idx1_chunk = FindDirectoryEntry( make_fourcc( "idx1" ) );
822 if ( idx1_chunk != -1 )
824 ReadChunk( idx1_chunk, ( void* ) idx1, sizeof( AVISuperIndex ) );
825 idx1->nEntriesInUse = GetDirectoryEntry( idx1_chunk ).length / 16;
826 index_type = AVI_SMALL_INDEX;
828 /* recalc number of frames from the simple index */
829 int frameNumIndex = 0;
830 FOURCC chunkID1 = make_fourcc( "00dc" );
831 FOURCC chunkID2 = make_fourcc( "00db" );
832 for ( int i = 0; i < idx1->nEntriesInUse; ++i )
834 if ( idx1->aIndex[ i ].dwChunkId == chunkID1 ||
835 idx1->aIndex[ i ].dwChunkId == chunkID2 )
840 mainHdr.dwTotalFrames = frameNumIndex;
846 void AVIFile::FlushIndx( int stream )
855 /* Write out the previous index. When this function is
856 entered for the first time, there is no index to
857 write. Note: this may be an expensive operation
858 because of a time consuming seek to the former file
861 if ( ix_chunk[ stream ] != -1 )
862 WriteChunk( ix_chunk[ stream ], ix[ stream ] );
864 /* make a new ix chunk. */
867 type = make_fourcc( "ix00" );
869 type = make_fourcc( "ix01" );
870 ix_chunk[ stream ] = AddDirectoryEntry( type, 0, sizeof( AVIStdIndex ), movi_list );
871 GetDirectoryEntry( ix_chunk[ stream ], type, name, length, offset, parent );
873 /* fill out all required fields. The offsets in the
874 array are relative to qwBaseOffset, so fill in the
875 offset to the next free location in the file
878 ix[ stream ] ->wLongsPerEntry = 2;
879 ix[ stream ] ->bIndexSubType = 0;
880 ix[ stream ] ->bIndexType = KINO_AVI_INDEX_OF_CHUNKS;
881 ix[ stream ] ->nEntriesInUse = 0;
882 ix[ stream ] ->dwChunkId = indx[ stream ] ->dwChunkId;
883 ix[ stream ] ->qwBaseOffset = offset + length;
884 ix[ stream ] ->dwReserved = 0;
886 for ( i = 0; i < IX00_INDEX_SIZE; ++i )
888 ix[ stream ] ->aIndex[ i ].dwOffset = 0;
889 ix[ stream ] ->aIndex[ i ].dwSize = 0;
892 /* add a reference to this new index in our super
895 i = indx[ stream ] ->nEntriesInUse++;
896 indx[ stream ] ->aIndex[ i ].qwOffset = offset - RIFF_HEADERSIZE;
897 indx[ stream ] ->aIndex[ i ].dwSize = length + RIFF_HEADERSIZE;
898 indx[ stream ] ->aIndex[ i ].dwDuration = 0;
902 void AVIFile::UpdateIndx( int stream, int chunk, int duration )
911 /* update the appropiate entry in the super index. It reflects
912 the number of frames in the referenced index. */
914 i = indx[ stream ] ->nEntriesInUse - 1;
915 indx[ stream ] ->aIndex[ i ].dwDuration += duration;
917 /* update the standard index. Calculate the file position of
920 GetDirectoryEntry( chunk, type, name, length, offset, parent );
922 indx[ stream ] ->dwChunkId = type;
923 i = ix[ stream ] ->nEntriesInUse++;
924 ix[ stream ] ->aIndex[ i ].dwOffset = offset - ix[ stream ] ->qwBaseOffset;
925 ix[ stream ] ->aIndex[ i ].dwSize = length;
929 void AVIFile::UpdateIdx1( int chunk, int flags )
931 if ( idx1->nEntriesInUse < 20000 )
939 GetDirectoryEntry( chunk, type, name, length, offset, parent );
941 idx1->aIndex[ idx1->nEntriesInUse ].dwChunkId = type;
942 idx1->aIndex[ idx1->nEntriesInUse ].dwFlags = flags;
943 idx1->aIndex[ idx1->nEntriesInUse ].dwOffset = offset - GetDirectoryEntry( movi_list ).offset - RIFF_HEADERSIZE;
944 idx1->aIndex[ idx1->nEntriesInUse ].dwSize = length;
945 idx1->nEntriesInUse++;
949 bool AVIFile::verifyStreamFormat( FOURCC type )
952 AVIStreamHeader avi_stream_header;
953 BITMAPINFOHEADER bih;
954 FOURCC strh = make_fourcc( "strh" );
955 FOURCC strf = make_fourcc( "strf" );
957 while ( ( i = FindDirectoryEntry( strh, j++ ) ) != -1 )
959 ReadChunk( i, ( void* ) & avi_stream_header, sizeof( AVIStreamHeader ) );
960 if ( avi_stream_header.fccHandler == type )
964 while ( ( i = FindDirectoryEntry( strf, j++ ) ) != -1 )
966 ReadChunk( i, ( void* ) & bih, sizeof( BITMAPINFOHEADER ) );
967 if ( ( FOURCC ) bih.biCompression == type )
974 bool AVIFile::verifyStream( FOURCC type )
977 AVIStreamHeader avi_stream_header;
978 FOURCC strh = make_fourcc( "strh" );
980 while ( ( i = FindDirectoryEntry( strh, j++ ) ) != -1 )
982 ReadChunk( i, ( void* ) & avi_stream_header, sizeof( AVIStreamHeader ) );
983 if ( avi_stream_header.fccType == type )
989 bool AVIFile::isOpenDML( void )
992 FOURCC dmlh = make_fourcc( "dmlh" );
994 while ( ( i = FindDirectoryEntry( dmlh, j++ ) ) != -1 )
1001 AVI1File::AVI1File() : AVIFile()
1003 memset( &dvinfo, 0, sizeof( dvinfo ) );
1007 AVI1File::~AVI1File()
1011 /* Initialize the AVI structure to its initial state, either for PAL
1014 void AVI1File::Init( int format, int sampleFrequency, int indexType )
1023 assert( ( format == AVI_PAL ) || ( format == AVI_NTSC ) );
1025 AVIFile::Init( format, sampleFrequency, indexType );
1030 mainHdr.dwWidth = 720;
1031 mainHdr.dwHeight = 576;
1033 streamHdr[ 0 ].dwScale = 1;
1034 streamHdr[ 0 ].dwRate = 25;
1035 streamHdr[ 0 ].dwSuggestedBufferSize = 144008;
1037 /* initialize the 'strf' chunk */
1039 /* Meaning of the DV stream format chunk per Microsoft
1041 Specifies the Audio Auxiliary Data Source Pack for the first audio block
1042 (first 5 DV DIF sequences for 525-60 systems or 6 DV DIF sequences for 625-50 systems) of
1043 a frame. A DIF sequence is a data block that contains 150 DIF blocks. A DIF block consists
1044 of 80 bytes. The Audio Auxiliary Data Source Pack is defined in section D.7.1 of Part 2,
1045 Annex D, "The Pack Header Table and Contents of Packs" of the Specification of
1046 Consumer-use Digital VCRs.
1048 Specifies the Audio Auxiliary Data Source Control Pack for the first audio block of a
1049 frame. The Audio Auxiliary Data Control Pack is defined in section D.7.2 of Part 2,
1050 Annex D, "The Pack Header Table and Contents of Packs" of the Specification of
1051 Consumer-use Digital VCRs.
1053 Specifies the Audio Auxiliary Data Source Pack for the second audio block
1054 (second 5 DV DIF sequences for 525-60 systems or 6 DV DIF sequences for 625-50 systems) of a frame.
1056 Specifies the Audio Auxiliary Data Source Control Pack for the second audio block of a frame.
1058 Specifies the Video Auxiliary Data Source Pack as defined in section D.8.1 of Part 2,
1059 Annex D, "The Pack Header Table and Contents of Packs" of the Specification of
1060 Consumer-use Digital VCRs.
1062 Specifies the Video Auxiliary Data Source Control Pack as defined in section D.8.2 of Part 2,
1063 Annex D, "The Pack Header Table and Contents of Packs" of the Specification of
1064 Consumer-use Digital VCRs.
1066 Reserved. Set this array to zero.
1069 dvinfo.dwDVAAuxSrc = 0xd1e030d0;
1070 dvinfo.dwDVAAuxCtl = 0xffa0cf3f;
1071 dvinfo.dwDVAAuxSrc1 = 0xd1e03fd0;
1072 dvinfo.dwDVAAuxCtl1 = 0xffa0cf3f;
1073 dvinfo.dwDVVAuxSrc = 0xff20ffff;
1074 dvinfo.dwDVVAuxCtl = 0xfffdc83f;
1075 dvinfo.dwDVReserved[ 0 ] = 0;
1076 dvinfo.dwDVReserved[ 1 ] = 0;
1080 mainHdr.dwWidth = 720;
1081 mainHdr.dwHeight = 480;
1083 streamHdr[ 0 ].dwScale = 1001;
1084 streamHdr[ 0 ].dwRate = 30000;
1085 streamHdr[ 0 ].dwSuggestedBufferSize = 120008;
1087 /* initialize the 'strf' chunk */
1088 dvinfo.dwDVAAuxSrc = 0xc0c000c0;
1089 dvinfo.dwDVAAuxCtl = 0xffa0cf3f;
1090 dvinfo.dwDVAAuxSrc1 = 0xc0c001c0;
1091 dvinfo.dwDVAAuxCtl1 = 0xffa0cf3f;
1092 dvinfo.dwDVVAuxSrc = 0xff80ffff;
1093 dvinfo.dwDVVAuxCtl = 0xfffcc83f;
1094 dvinfo.dwDVReserved[ 0 ] = 0;
1095 dvinfo.dwDVReserved[ 1 ] = 0;
1098 default: /* no default allowed */
1103 indx[ 0 ] ->dwChunkId = make_fourcc( "00__" );
1105 /* Initialize the 'strh' chunk */
1107 streamHdr[ 0 ].fccType = make_fourcc( "iavs" );
1108 streamHdr[ 0 ].fccHandler = make_fourcc( "dvsd" );
1109 streamHdr[ 0 ].dwFlags = 0;
1110 streamHdr[ 0 ].wPriority = 0;
1111 streamHdr[ 0 ].wLanguage = 0;
1112 streamHdr[ 0 ].dwInitialFrames = 0;
1113 streamHdr[ 0 ].dwStart = 0;
1114 streamHdr[ 0 ].dwLength = 0;
1115 streamHdr[ 0 ].dwQuality = 0;
1116 streamHdr[ 0 ].dwSampleSize = 0;
1117 streamHdr[ 0 ].rcFrame.top = 0;
1118 streamHdr[ 0 ].rcFrame.bottom = 0;
1119 streamHdr[ 0 ].rcFrame.left = 0;
1120 streamHdr[ 0 ].rcFrame.right = 0;
1122 /* This is a simple directory structure setup. For details see the
1123 "OpenDML AVI File Format Extensions" document.
1125 An AVI file contains basically two types of objects, a
1126 "chunk" and a "list" object. The list object contains any
1127 number of chunks. Since a list is also a chunk, it is
1128 possible to create a hierarchical "list of lists"
1131 Every AVI file starts with a "RIFF" object, which is a list
1132 of several other required objects. The actual DV data is
1133 contained in a "movi" list, each frame is in its own chunk.
1135 Old AVI files (pre OpenDML V. 1.02) contain only one RIFF
1136 chunk of less than 1 GByte size per file. The current
1137 format which allow for almost arbitrary sizes can contain
1138 several RIFF chunks of less than 1 GByte size. Old software
1139 however would only deal with the first RIFF chunk.
1141 Note that the first entry (FILE) isn�t actually part
1142 of the AVI file. I use this (pseudo-) directory entry to
1143 keep track of the RIFF chunks and their positions in the
1147 /* Create the container directory entry */
1149 file_list = AddDirectoryEntry( make_fourcc( "FILE" ), make_fourcc( "FILE" ), 0, RIFF_NO_PARENT );
1151 /* Create a basic directory structure. Only chunks defined from here on will be written to the AVI file. */
1153 riff_list = AddDirectoryEntry( make_fourcc( "RIFF" ), make_fourcc( "AVI " ), RIFF_LISTSIZE, file_list );
1154 hdrl_list = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "hdrl" ), RIFF_LISTSIZE, riff_list );
1155 avih_chunk = AddDirectoryEntry( make_fourcc( "avih" ), 0, sizeof( MainAVIHeader ), hdrl_list );
1156 strl_list[ 0 ] = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "strl" ), RIFF_LISTSIZE, hdrl_list );
1157 strh_chunk[ 0 ] = AddDirectoryEntry( make_fourcc( "strh" ), 0, sizeof( AVIStreamHeader ), strl_list[ 0 ] );
1158 strf_chunk[ 0 ] = AddDirectoryEntry( make_fourcc( "strf" ), 0, sizeof( dvinfo ), strl_list[ 0 ] );
1159 if ( index_type & AVI_LARGE_INDEX )
1160 indx_chunk[ 0 ] = AddDirectoryEntry( make_fourcc( "indx" ), 0, sizeof( AVISuperIndex ), strl_list[ 0 ] );
1162 odml_list = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "odml" ), RIFF_LISTSIZE, hdrl_list );
1163 dmlh_chunk = AddDirectoryEntry( make_fourcc( "dmlh" ), 0, 0x00f8, odml_list );
1165 /* align movi list to block */
1166 GetDirectoryEntry( hdrl_list, type, name, length, offset, parent );
1167 num_blocks = length / PADDING_SIZE + 1;
1168 length = num_blocks * PADDING_SIZE - length - 5 * RIFF_HEADERSIZE; // why 5?
1169 junk_chunk = AddDirectoryEntry( make_fourcc( "JUNK" ), 0, length, riff_list );
1171 movi_list = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "movi" ), RIFF_LISTSIZE, riff_list );
1173 /* The ix00 chunk will be added dynamically to the movi_list in avi_write_frame
1180 /* Write a DV video frame. This is somewhat complex... */
1183 bool AVI1File::WriteFrame( const Frame &frame )
1194 /* exit if no large index and 1GB reached */
1195 if ( !( index_type & AVI_LARGE_INDEX ) && isUpdateIdx1 == false )
1198 /* Check if we need a new ix00 Standard Index. It has a
1199 capacity of IX00_INDEX_SIZE frames. Whenever we exceed that
1200 number, we need a new index. The new ix00 chunk is also
1201 part of the movi list. */
1203 if ( ( index_type & AVI_LARGE_INDEX ) && ( ( ( streamHdr[ 0 ].dwLength - 0 ) % IX00_INDEX_SIZE ) == 0 ) )
1206 /* Write the DV frame data.
1208 Make a new 00__ chunk for the new frame, write out the
1211 frame_chunk = AddDirectoryEntry( make_fourcc( "00__" ), 0, frame.GetFrameSize(), movi_list );
1212 if ( ( index_type & AVI_LARGE_INDEX ) && ( streamHdr[ 0 ].dwLength % IX00_INDEX_SIZE ) == 0 )
1214 GetDirectoryEntry( frame_chunk, type, name, length, offset, parent );
1215 ix[ 0 ] ->qwBaseOffset = offset - RIFF_HEADERSIZE;
1217 WriteChunk( frame_chunk, frame.data );
1218 // num_blocks = (frame.GetFrameSize() + RIFF_HEADERSIZE) / PADDING_SIZE + 1;
1219 // length = num_blocks * PADDING_SIZE - frame.GetFrameSize() - 2 * RIFF_HEADERSIZE;
1220 // junk_chunk = AddDirectoryEntry(make_fourcc("JUNK"), 0, length, movi_list);
1221 // WriteChunk(junk_chunk, g_zeroes);
1223 if ( index_type & AVI_LARGE_INDEX )
1224 UpdateIndx( 0, frame_chunk, 1 );
1225 if ( ( index_type & AVI_SMALL_INDEX ) && isUpdateIdx1 )
1226 UpdateIdx1( frame_chunk, 0x10 );
1228 /* update some variables with the new frame count. */
1231 ++mainHdr.dwTotalFrames;
1232 ++streamHdr[ 0 ].dwLength;
1235 /* Find out if the current riff list is close to 1 GByte in
1236 size. If so, start a new (extended) RIFF. The only allowed
1237 item in the new RIFF chunk is a movi list (with video
1238 frames and indexes as usual). */
1240 GetDirectoryEntry( riff_list, type, name, length, offset, parent );
1241 if ( length > 0x3f000000 )
1243 /* write idx1 only once and before end of first GB */
1244 if ( ( index_type & AVI_SMALL_INDEX ) && isUpdateIdx1 )
1246 int idx1_chunk = AddDirectoryEntry( make_fourcc( "idx1" ), 0, idx1->nEntriesInUse * 16, riff_list );
1247 WriteChunk( idx1_chunk, ( void* ) idx1 );
1249 isUpdateIdx1 = false;
1251 if ( index_type & AVI_LARGE_INDEX )
1253 /* pad out to 1GB */
1254 //GetDirectoryEntry(riff_list, type, name, length, offset, parent);
1255 //junk_chunk = AddDirectoryEntry(make_fourcc("JUNK"), 0, PADDING_1GB - length - 5 * RIFF_HEADERSIZE, riff_list);
1256 //WriteChunk(junk_chunk, g_zeroes);
1258 /* padding for alignment */
1259 GetDirectoryEntry( riff_list, type, name, length, offset, parent );
1260 num_blocks = ( length + 4 * RIFF_HEADERSIZE ) / PADDING_SIZE + 1;
1261 length = ( num_blocks * PADDING_SIZE ) - length - 4 * RIFF_HEADERSIZE - 2 * RIFF_LISTSIZE;
1264 junk_chunk = AddDirectoryEntry( make_fourcc( "JUNK" ), 0, length, riff_list );
1265 WriteChunk( junk_chunk, g_zeroes );
1268 riff_list = AddDirectoryEntry( make_fourcc( "RIFF" ), make_fourcc( "AVIX" ), RIFF_LISTSIZE, file_list );
1269 movi_list = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "movi" ), RIFF_LISTSIZE, riff_list );
1276 void AVI1File::WriteRIFF()
1279 WriteChunk( avih_chunk, ( void* ) & mainHdr );
1280 WriteChunk( strh_chunk[ 0 ], ( void* ) & streamHdr[ 0 ] );
1281 WriteChunk( strf_chunk[ 0 ], ( void* ) & dvinfo );
1282 WriteChunk( dmlh_chunk, ( void* ) & dmlh );
1284 if ( index_type & AVI_LARGE_INDEX )
1286 WriteChunk( indx_chunk[ 0 ], ( void* ) indx[ 0 ] );
1287 WriteChunk( ix_chunk[ 0 ], ( void* ) ix[ 0 ] );
1290 if ( ( index_type & AVI_SMALL_INDEX ) && isUpdateIdx1 )
1292 int idx1_chunk = AddDirectoryEntry( make_fourcc( "idx1" ), 0, idx1->nEntriesInUse * 16, riff_list );
1293 WriteChunk( idx1_chunk, ( void* ) idx1 );
1296 RIFFFile::WriteRIFF();
1300 AVI2File::AVI2File() : AVIFile()
1304 AVI2File::~AVI2File()
1308 /* Initialize the AVI structure to its initial state, either for PAL
1311 void AVI2File::Init( int format, int sampleFrequency, int indexType )
1320 assert( ( format == AVI_PAL ) || ( format == AVI_NTSC ) );
1322 AVIFile::Init( format, sampleFrequency, indexType );
1328 mainHdr.dwStreams = 2;
1329 mainHdr.dwWidth = 720;
1330 mainHdr.dwHeight = 576;
1332 /* Initialize the 'strh' chunk */
1334 streamHdr[ 0 ].fccType = make_fourcc( "vids" );
1335 streamHdr[ 0 ].fccHandler = make_fourcc( "dvsd" );
1336 streamHdr[ 0 ].dwFlags = 0;
1337 streamHdr[ 0 ].wPriority = 0;
1338 streamHdr[ 0 ].wLanguage = 0;
1339 streamHdr[ 0 ].dwInitialFrames = 0;
1340 streamHdr[ 0 ].dwScale = 1;
1341 streamHdr[ 0 ].dwRate = 25;
1342 streamHdr[ 0 ].dwStart = 0;
1343 streamHdr[ 0 ].dwLength = 0;
1344 streamHdr[ 0 ].dwSuggestedBufferSize = 144008;
1345 streamHdr[ 0 ].dwQuality = -1;
1346 streamHdr[ 0 ].dwSampleSize = 0;
1347 streamHdr[ 0 ].rcFrame.top = 0;
1348 streamHdr[ 0 ].rcFrame.bottom = 0;
1349 streamHdr[ 0 ].rcFrame.left = 0;
1350 streamHdr[ 0 ].rcFrame.right = 0;
1352 bitmapinfo.biSize = sizeof( bitmapinfo );
1353 bitmapinfo.biWidth = 720;
1354 bitmapinfo.biHeight = 576;
1355 bitmapinfo.biPlanes = 1;
1356 bitmapinfo.biBitCount = 24;
1357 bitmapinfo.biCompression = make_fourcc( "dvsd" );
1358 bitmapinfo.biSizeImage = 144000;
1359 bitmapinfo.biXPelsPerMeter = 0;
1360 bitmapinfo.biYPelsPerMeter = 0;
1361 bitmapinfo.biClrUsed = 0;
1362 bitmapinfo.biClrImportant = 0;
1364 streamHdr[ 1 ].fccType = make_fourcc( "auds" );
1365 streamHdr[ 1 ].fccHandler = 0;
1366 streamHdr[ 1 ].dwFlags = 0;
1367 streamHdr[ 1 ].wPriority = 0;
1368 streamHdr[ 1 ].wLanguage = 0;
1369 streamHdr[ 1 ].dwInitialFrames = 0;
1370 streamHdr[ 1 ].dwScale = 2 * 2;
1371 streamHdr[ 1 ].dwRate = sampleFrequency * 2 * 2;
1372 streamHdr[ 1 ].dwStart = 0;
1373 streamHdr[ 1 ].dwLength = 0;
1374 streamHdr[ 1 ].dwSuggestedBufferSize = 8192;
1375 streamHdr[ 1 ].dwQuality = -1;
1376 streamHdr[ 1 ].dwSampleSize = 2 * 2;
1377 streamHdr[ 1 ].rcFrame.top = 0;
1378 streamHdr[ 1 ].rcFrame.bottom = 0;
1379 streamHdr[ 1 ].rcFrame.left = 0;
1380 streamHdr[ 1 ].rcFrame.right = 0;
1385 mainHdr.dwTotalFrames = 0;
1386 mainHdr.dwStreams = 2;
1387 mainHdr.dwWidth = 720;
1388 mainHdr.dwHeight = 480;
1390 /* Initialize the 'strh' chunk */
1392 streamHdr[ 0 ].fccType = make_fourcc( "vids" );
1393 streamHdr[ 0 ].fccHandler = make_fourcc( "dvsd" );
1394 streamHdr[ 0 ].dwFlags = 0;
1395 streamHdr[ 0 ].wPriority = 0;
1396 streamHdr[ 0 ].wLanguage = 0;
1397 streamHdr[ 0 ].dwInitialFrames = 0;
1398 streamHdr[ 0 ].dwScale = 1001;
1399 streamHdr[ 0 ].dwRate = 30000;
1400 streamHdr[ 0 ].dwStart = 0;
1401 streamHdr[ 0 ].dwLength = 0;
1402 streamHdr[ 0 ].dwSuggestedBufferSize = 120008;
1403 streamHdr[ 0 ].dwQuality = -1;
1404 streamHdr[ 0 ].dwSampleSize = 0;
1405 streamHdr[ 0 ].rcFrame.top = 0;
1406 streamHdr[ 0 ].rcFrame.bottom = 0;
1407 streamHdr[ 0 ].rcFrame.left = 0;
1408 streamHdr[ 0 ].rcFrame.right = 0;
1410 bitmapinfo.biSize = sizeof( bitmapinfo );
1411 bitmapinfo.biWidth = 720;
1412 bitmapinfo.biHeight = 480;
1413 bitmapinfo.biPlanes = 1;
1414 bitmapinfo.biBitCount = 24;
1415 bitmapinfo.biCompression = make_fourcc( "dvsd" );
1416 bitmapinfo.biSizeImage = 120000;
1417 bitmapinfo.biXPelsPerMeter = 0;
1418 bitmapinfo.biYPelsPerMeter = 0;
1419 bitmapinfo.biClrUsed = 0;
1420 bitmapinfo.biClrImportant = 0;
1422 streamHdr[ 1 ].fccType = make_fourcc( "auds" );
1423 streamHdr[ 1 ].fccHandler = 0;
1424 streamHdr[ 1 ].dwFlags = 0;
1425 streamHdr[ 1 ].wPriority = 0;
1426 streamHdr[ 1 ].wLanguage = 0;
1427 streamHdr[ 1 ].dwInitialFrames = 1;
1428 streamHdr[ 1 ].dwScale = 2 * 2;
1429 streamHdr[ 1 ].dwRate = sampleFrequency * 2 * 2;
1430 streamHdr[ 1 ].dwStart = 0;
1431 streamHdr[ 1 ].dwLength = 0;
1432 streamHdr[ 1 ].dwSuggestedBufferSize = 8192;
1433 streamHdr[ 1 ].dwQuality = 0;
1434 streamHdr[ 1 ].dwSampleSize = 2 * 2;
1435 streamHdr[ 1 ].rcFrame.top = 0;
1436 streamHdr[ 1 ].rcFrame.bottom = 0;
1437 streamHdr[ 1 ].rcFrame.left = 0;
1438 streamHdr[ 1 ].rcFrame.right = 0;
1442 waveformatex.wFormatTag = 1;
1443 waveformatex.nChannels = 2;
1444 waveformatex.nSamplesPerSec = sampleFrequency;
1445 waveformatex.nAvgBytesPerSec = sampleFrequency * 2 * 2;
1446 waveformatex.nBlockAlign = 4;
1447 waveformatex.wBitsPerSample = 16;
1448 waveformatex.cbSize = 0;
1450 file_list = AddDirectoryEntry( make_fourcc( "FILE" ), make_fourcc( "FILE" ), 0, RIFF_NO_PARENT );
1452 /* Create a basic directory structure. Only chunks defined from here on will be written to the AVI file. */
1454 riff_list = AddDirectoryEntry( make_fourcc( "RIFF" ), make_fourcc( "AVI " ), RIFF_LISTSIZE, file_list );
1455 hdrl_list = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "hdrl" ), RIFF_LISTSIZE, riff_list );
1456 avih_chunk = AddDirectoryEntry( make_fourcc( "avih" ), 0, sizeof( MainAVIHeader ), hdrl_list );
1458 strl_list[ 0 ] = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "strl" ), RIFF_LISTSIZE, hdrl_list );
1459 strh_chunk[ 0 ] = AddDirectoryEntry( make_fourcc( "strh" ), 0, sizeof( AVIStreamHeader ), strl_list[ 0 ] );
1460 strf_chunk[ 0 ] = AddDirectoryEntry( make_fourcc( "strf" ), 0, sizeof( BITMAPINFOHEADER ), strl_list[ 0 ] );
1461 if ( index_type & AVI_LARGE_INDEX )
1463 indx_chunk[ 0 ] = AddDirectoryEntry( make_fourcc( "indx" ), 0, sizeof( AVISuperIndex ), strl_list[ 0 ] );
1465 indx[ 0 ] ->dwChunkId = make_fourcc( "00dc" );
1468 strl_list[ 1 ] = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "strl" ), RIFF_LISTSIZE, hdrl_list );
1469 strh_chunk[ 1 ] = AddDirectoryEntry( make_fourcc( "strh" ), 0, sizeof( AVIStreamHeader ), strl_list[ 1 ] );
1470 strf_chunk[ 1 ] = AddDirectoryEntry( make_fourcc( "strf" ), 0, sizeof( WAVEFORMATEX ) - 2, strl_list[ 1 ] );
1471 junk_chunk = AddDirectoryEntry( make_fourcc( "JUNK" ), 0, 2, strl_list[ 1 ] );
1472 if ( index_type & AVI_LARGE_INDEX )
1474 indx_chunk[ 1 ] = AddDirectoryEntry( make_fourcc( "indx" ), 0, sizeof( AVISuperIndex ), strl_list[ 1 ] );
1476 indx[ 1 ] ->dwChunkId = make_fourcc( "01wb" );
1478 odml_list = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "odml" ), RIFF_LISTSIZE, hdrl_list );
1479 dmlh_chunk = AddDirectoryEntry( make_fourcc( "dmlh" ), 0, 0x00f8, odml_list );
1482 /* align movi list to block */
1483 GetDirectoryEntry( hdrl_list, type, name, length, offset, parent );
1484 num_blocks = length / PADDING_SIZE + 1;
1485 length = num_blocks * PADDING_SIZE - length - 5 * RIFF_HEADERSIZE; // why 5 headers?
1486 junk_chunk = AddDirectoryEntry( make_fourcc( "JUNK" ), 0, length, riff_list );
1488 movi_list = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "movi" ), RIFF_LISTSIZE, riff_list );
1490 idx1->aIndex[ idx1->nEntriesInUse ].dwChunkId = make_fourcc( "7Fxx" );
1491 idx1->aIndex[ idx1->nEntriesInUse ].dwFlags = 0;
1492 idx1->aIndex[ idx1->nEntriesInUse ].dwOffset = 0;
1493 idx1->aIndex[ idx1->nEntriesInUse ].dwSize = 0;
1494 idx1->nEntriesInUse++;
1498 void AVI2File::WriteRIFF()
1500 WriteChunk( avih_chunk, ( void* ) & mainHdr );
1501 WriteChunk( strh_chunk[ 0 ], ( void* ) & streamHdr[ 0 ] );
1502 WriteChunk( strf_chunk[ 0 ], ( void* ) & bitmapinfo );
1503 if ( index_type & AVI_LARGE_INDEX )
1505 WriteChunk( dmlh_chunk, ( void* ) & dmlh );
1506 WriteChunk( indx_chunk[ 0 ], ( void* ) indx[ 0 ] );
1507 WriteChunk( ix_chunk[ 0 ], ( void* ) ix[ 0 ] );
1509 WriteChunk( strh_chunk[ 1 ], ( void* ) & streamHdr[ 1 ] );
1510 WriteChunk( strf_chunk[ 1 ], ( void* ) & waveformatex );
1511 if ( index_type & AVI_LARGE_INDEX )
1513 WriteChunk( indx_chunk[ 1 ], ( void* ) indx[ 1 ] );
1514 WriteChunk( ix_chunk[ 1 ], ( void* ) ix[ 1 ] );
1517 if ( ( index_type & AVI_SMALL_INDEX ) && isUpdateIdx1 )
1519 int idx1_chunk = AddDirectoryEntry( make_fourcc( "idx1" ), 0, idx1->nEntriesInUse * 16, riff_list );
1520 WriteChunk( idx1_chunk, ( void* ) idx1 );
1522 RIFFFile::WriteRIFF();
1526 /** Write a DV video frame
1528 \param frame the frame to write
1532 bool AVI2File::WriteFrame( const Frame &frame )
1537 char soundbuf[ 20000 ];
1546 /* exit if no large index and 1GB reached */
1547 if ( !( index_type & AVI_LARGE_INDEX ) && isUpdateIdx1 == false )
1550 /* Check if we need a new ix00 Standard Index. It has a
1551 capacity of IX00_INDEX_SIZE frames. Whenever we exceed that
1552 number, we need a new index. The new ix00 chunk is also
1553 part of the movi list. */
1555 if ( ( index_type & AVI_LARGE_INDEX ) && ( ( ( streamHdr[ 0 ].dwLength - 0 ) % IX00_INDEX_SIZE ) == 0 ) )
1561 /* Write audio data if we have it */
1563 audio_size = frame.ExtractAudio( soundbuf );
1564 if ( audio_size > 0 )
1566 audio_chunk = AddDirectoryEntry( make_fourcc( "01wb" ), 0, audio_size, movi_list );
1567 if ( ( index_type & AVI_LARGE_INDEX ) && ( streamHdr[ 0 ].dwLength % IX00_INDEX_SIZE ) == 0 )
1569 GetDirectoryEntry( audio_chunk, type, name, length, offset, parent );
1570 ix[ 1 ] ->qwBaseOffset = offset - RIFF_HEADERSIZE;
1572 WriteChunk( audio_chunk, soundbuf );
1573 // num_blocks = (audio_size + RIFF_HEADERSIZE) / PADDING_SIZE + 1;
1574 // length = num_blocks * PADDING_SIZE - audio_size - 2 * RIFF_HEADERSIZE;
1575 // junk_chunk = AddDirectoryEntry(make_fourcc("JUNK"), 0, length, movi_list);
1576 // WriteChunk(junk_chunk, g_zeroes);
1577 if ( index_type & AVI_LARGE_INDEX )
1578 UpdateIndx( 1, audio_chunk, audio_size / waveformatex.nChannels / 2 );
1579 if ( ( index_type & AVI_SMALL_INDEX ) && isUpdateIdx1 )
1580 UpdateIdx1( audio_chunk, 0x00 );
1581 streamHdr[ 1 ].dwLength += audio_size / waveformatex.nChannels / 2;
1585 /* Write video data */
1587 frame_chunk = AddDirectoryEntry( make_fourcc( "00dc" ), 0, frame.GetFrameSize(), movi_list );
1588 if ( ( index_type & AVI_LARGE_INDEX ) && ( streamHdr[ 0 ].dwLength % IX00_INDEX_SIZE ) == 0 )
1590 GetDirectoryEntry( frame_chunk, type, name, length, offset, parent );
1591 ix[ 0 ] ->qwBaseOffset = offset - RIFF_HEADERSIZE;
1593 WriteChunk( frame_chunk, frame.data );
1594 // num_blocks = (frame.GetFrameSize() + RIFF_HEADERSIZE) / PADDING_SIZE + 1;
1595 // length = num_blocks * PADDING_SIZE - frame.GetFrameSize() - 2 * RIFF_HEADERSIZE;
1596 // junk_chunk = AddDirectoryEntry(make_fourcc("JUNK"), 0, length, movi_list);
1597 // WriteChunk(junk_chunk, g_zeroes);
1598 if ( index_type & AVI_LARGE_INDEX )
1599 UpdateIndx( 0, frame_chunk, 1 );
1600 if ( ( index_type & AVI_SMALL_INDEX ) && isUpdateIdx1 )
1601 UpdateIdx1( frame_chunk, 0x10 );
1603 /* update some variables with the new frame count. */
1606 ++mainHdr.dwTotalFrames;
1607 ++streamHdr[ 0 ].dwLength;
1610 /* Find out if the current riff list is close to 1 GByte in
1611 size. If so, start a new (extended) RIFF. The only allowed
1612 item in the new RIFF chunk is a movi list (with video
1613 frames and indexes as usual). */
1615 GetDirectoryEntry( riff_list, type, name, length, offset, parent );
1616 if ( length > 0x3f000000 )
1619 /* write idx1 only once and before end of first GB */
1620 if ( ( index_type & AVI_SMALL_INDEX ) && isUpdateIdx1 )
1622 int idx1_chunk = AddDirectoryEntry( make_fourcc( "idx1" ), 0, idx1->nEntriesInUse * 16, riff_list );
1623 WriteChunk( idx1_chunk, ( void* ) idx1 );
1625 isUpdateIdx1 = false;
1627 if ( index_type & AVI_LARGE_INDEX )
1629 /* padding for alignment */
1630 GetDirectoryEntry( riff_list, type, name, length, offset, parent );
1631 num_blocks = ( length + 4 * RIFF_HEADERSIZE ) / PADDING_SIZE + 1;
1632 length = ( num_blocks * PADDING_SIZE ) - length - 4 * RIFF_HEADERSIZE - 2 * RIFF_LISTSIZE;
1635 junk_chunk = AddDirectoryEntry( make_fourcc( "JUNK" ), 0, length, riff_list );
1636 WriteChunk( junk_chunk, g_zeroes );
1639 riff_list = AddDirectoryEntry( make_fourcc( "RIFF" ), make_fourcc( "AVIX" ), RIFF_LISTSIZE, file_list );
1640 movi_list = AddDirectoryEntry( make_fourcc( "LIST" ), make_fourcc( "movi" ), RIFF_LISTSIZE, riff_list );
1647 void AVI1File::setDVINFO( DVINFO &info )
1649 // do not do this until debugged audio against DirectShow
1652 dvinfo.dwDVAAuxSrc = info.dwDVAAuxSrc;
1653 dvinfo.dwDVAAuxCtl = info.dwDVAAuxCtl;
1654 dvinfo.dwDVAAuxSrc1 = info.dwDVAAuxSrc1;
1655 dvinfo.dwDVAAuxCtl1 = info.dwDVAAuxCtl1;
1656 dvinfo.dwDVVAuxSrc = info.dwDVVAuxSrc;
1657 dvinfo.dwDVVAuxCtl = info.dwDVVAuxCtl;
1661 void AVI2File::setDVINFO( DVINFO &info )
1664 void AVIFile::setFccHandler( FOURCC type, FOURCC handler )
1666 for ( int i = 0; i < mainHdr.dwStreams; i++ )
1668 if ( streamHdr[ i ].fccType == type )
1671 FOURCC strf = make_fourcc( "strf" );
1672 BITMAPINFOHEADER bih;
1674 streamHdr[ i ].fccHandler = handler;
1676 while ( ( k = FindDirectoryEntry( strf, j++ ) ) != -1 )
1678 ReadChunk( k, ( void* ) & bih, sizeof( BITMAPINFOHEADER ) );
1679 bih.biCompression = handler;
1685 bool AVIFile::getStreamFormat( void* data, FOURCC type )
1688 FOURCC strh = make_fourcc( "strh" );
1689 FOURCC strf = make_fourcc( "strf" );
1690 AVIStreamHeader avi_stream_header;
1691 bool result = false;
1693 while ( ( result == false ) && ( i = FindDirectoryEntry( strh, j++ ) ) != -1 )
1695 ReadChunk( i, ( void* ) & avi_stream_header, sizeof( AVIStreamHeader ) );
1696 if ( avi_stream_header.fccType == type )
1701 pthread_mutex_lock( &file_mutex );
1702 fail_neg( read( fd, &chunkID, sizeof( FOURCC ) ) );
1703 if ( chunkID == strf )
1705 fail_neg( read( fd, &size, sizeof( int ) ) );
1706 fail_neg( read( fd, data, size ) );
1709 pthread_mutex_unlock( &file_mutex );