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()
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 );