2 * riff.cc library for RIFF 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.
24 * Revision 1.1 2005/04/15 14:28:26 lilo_booter
27 * Revision 1.18 2005/04/01 23:43:10 ddennedy
28 * apply endian fixes from Daniel Kobras
30 * Revision 1.17 2004/10/11 01:37:11 ddennedy
31 * mutex safety locks in RIFF and AVI classes, type 2 AVI optimization, mencoder export script
33 * Revision 1.16 2003/11/25 23:01:24 ddennedy
34 * cleanup and a few bugfixes
36 * Revision 1.15 2003/10/21 16:34:34 ddennedy
37 * GNOME2 port phase 1: initial checkin
39 * Revision 1.13.2.3 2003/08/26 20:39:00 ddennedy
40 * relocate mutex unlock and add assert includes
42 * Revision 1.13.2.2 2003/01/28 12:54:13 lilo_booter
43 * New 'no change' image transition
45 * Revision 1.13.2.1 2002/11/25 04:48:31 ddennedy
46 * bugfix to report errors when loading files
48 * Revision 1.13 2002/09/13 06:49:49 ddennedy
49 * build update, cleanup, bugfixes
51 * Revision 1.12 2002/04/21 06:36:40 ddennedy
52 * kindler avc and 1394 bus reset support in catpure page, honor max file size
54 * Revision 1.11 2002/04/09 06:53:42 ddennedy
55 * cleanup, new libdv 0.9.5, large AVI, dnd storyboard
57 * Revision 1.4 2002/03/25 21:34:25 arne
58 * Support for large (64 bit) files mostly completed
60 * Revision 1.3 2002/03/10 21:28:29 arne
61 * release 1.1b1, 64 bit support for type 1 avis
63 * Revision 1.2 2002/03/04 19:22:43 arne
64 * updated to latest Kino avi code
66 * Revision 1.1.1.1 2002/03/03 19:08:08 arne
67 * import of version 1.01
100 /** make a 32 bit "string-id"
102 \param s a pointer to 4 chars
103 \return the 32 bit "string id"
104 \bugs It is not checked whether we really have 4 characters
106 Some compilers understand constants like int id = 'ABCD'; but I
107 could not get it working on the gcc compiler so I had to use this
108 workaround. We can now use id = make_fourcc("ABCD") instead. */
110 FOURCC
make_fourcc( char *s
)
115 return *( ( FOURCC
* ) s
);
119 RIFFDirEntry
::RIFFDirEntry()
123 RIFFDirEntry
::RIFFDirEntry ( FOURCC t
, FOURCC n
, int l
, int o
, int p
) : type( t
), name( n
), length( l
), offset( o
), parent( p
), written( 0 )
127 /** Creates the object without an output file.
131 RIFFFile
::RIFFFile() : fd( -1 )
133 pthread_mutex_init( &file_mutex
, NULL
);
139 Duplicate the file descriptor
142 RIFFFile
::RIFFFile( const RIFFFile
& riff
) : fd( -1 )
148 directory
= riff
.directory
;
152 /** Destroys the object.
154 If it has an associated opened file, close it. */
156 RIFFFile
::~RIFFFile()
159 pthread_mutex_destroy( &file_mutex
);
163 RIFFFile
& RIFFFile
::operator=( const RIFFFile
& riff
)
172 directory
= riff
.directory
;
178 /** Creates or truncates the file.
180 \param s the filename
183 bool RIFFFile
::Create( const char *s
)
185 fd
= open( s
, O_RDWR
| O_NONBLOCK
| O_CREAT
| O_TRUNC
, 00644 );
194 /** Opens the file read only.
196 \param s the filename
199 bool RIFFFile
::Open( const char *s
)
201 fd
= open( s
, O_RDONLY
| O_NONBLOCK
);
210 /** Destroys the object.
212 If it has an associated opened file, close it. */
214 void RIFFFile
::Close()
224 /** Adds an entry to the list of containers.
226 \param type the type of this entry
228 \param length the length of the data in the container
229 \param list the container in which this object is contained.
230 \return the ID of the newly created entry
232 The topmost object is not contained in any other container. Use
233 the special ID RIFF_NO_PARENT to create the topmost object. */
235 int RIFFFile
::AddDirectoryEntry( FOURCC type
, FOURCC name
, off_t length
, int list
)
237 /* Put all parameters in an RIFFDirEntry object. The offset is
238 currently unknown. */
240 RIFFDirEntry
entry( type
, name
, length
, 0 /* offset */, list
);
242 /* If the new chunk is in a list, then get the offset and size
243 of that list. The offset of this chunk is the end of the list
244 (parent_offset + parent_length) plus the size of the chunk
247 if ( list
!= RIFF_NO_PARENT
)
249 RIFFDirEntry parent
= GetDirectoryEntry( list
);
250 entry
.offset
= parent
.offset
+ parent
.length
+ RIFF_HEADERSIZE
;
253 /* The list which this new chunk is a member of has now increased in
254 size. Get that directory entry and bump up its length by the size
255 of the chunk. Since that list may also be contained in another
256 list, walk up to the top of the tree. */
258 while ( list
!= RIFF_NO_PARENT
)
260 RIFFDirEntry parent
= GetDirectoryEntry( list
);
261 parent
.length
+= RIFF_HEADERSIZE
+ length
;
262 SetDirectoryEntry( list
, parent
);
263 list
= parent
.parent
;
266 directory
.insert( directory
.end(), entry
);
268 return directory
.size() - 1;
272 /** Modifies an entry.
274 \param i the ID of the entry which is to modify
275 \param type the type of this entry
277 \param length the length of the data in the container
278 \param list the container in which this object is contained.
279 \note Do not change length, offset, or the parent container.
280 \note Do not change an empty name ("") to a name and vice versa */
282 void RIFFFile
::SetDirectoryEntry( int i
, FOURCC type
, FOURCC name
, off_t length
, off_t offset
, int list
)
284 RIFFDirEntry
entry( type
, name
, length
, offset
, list
);
286 assert( i
>= 0 && i
< ( int ) directory
.size() );
288 directory
[ i
] = entry
;
292 /** Modifies an entry.
294 The entry.written flag is set to false because the contents has been modified
296 \param i the ID of the entry which is to modify
297 \param entry the new entry
298 \note Do not change length, offset, or the parent container.
299 \note Do not change an empty name ("") to a name and vice versa */
301 void RIFFFile
::SetDirectoryEntry( int i
, RIFFDirEntry
&entry
)
303 assert( i
>= 0 && i
< ( int ) directory
.size() );
305 entry
.written
= false;
306 directory
[ i
] = entry
;
310 /** Retrieves an entry.
312 Gets the most important member variables.
314 \param i the ID of the entry to retrieve
321 void RIFFFile
::GetDirectoryEntry( int i
, FOURCC
&type
, FOURCC
&name
, off_t
&length
, off_t
&offset
, int &list
) const
325 assert( i
>= 0 && i
< ( int ) directory
.size() );
327 entry
= directory
[ i
];
330 length
= entry
.length
;
331 offset
= entry
.offset
;
336 /** Retrieves an entry.
338 Gets the whole RIFFDirEntry object.
340 \param i the ID of the entry to retrieve
343 RIFFDirEntry RIFFFile
::GetDirectoryEntry( int i
) const
345 assert( i
>= 0 && i
< ( int ) directory
.size() );
347 return directory
[ i
];
351 /** Calculates the total size of the file
353 \return the size the file in bytes
356 off_t RIFFFile
::GetFileSize( void ) const
359 /* If we have at least one entry, return the length field
360 of the FILE entry, which is the length of its contents,
361 which is the actual size of whatever is currently in the
362 AVI directory structure.
364 Note that the first entry does not belong to the AVI
367 If we don't have any entry, the file size is zero. */
369 if ( directory
.size() > 0 )
370 return directory
[ 0 ].length
;
376 /** prints the attributes of the entry
378 \param i the ID of the entry to print
381 void RIFFFile
::PrintDirectoryEntry ( int i
) const
388 /* Get all attributes of the chunk object. If it is contained
389 in a list, get the name of the list too (otherwise the name of
390 the list is blank). If the chunk object doesn´t have a name (only
391 LISTs and RIFFs have a name), the name is blank. */
393 entry
= GetDirectoryEntry( i
);
394 if ( entry
.parent
!= RIFF_NO_PARENT
)
396 parent
= GetDirectoryEntry( entry
.parent
);
397 list_name
= parent
.name
;
401 list_name
= make_fourcc( " " );
403 if ( entry
.name
!= 0 )
405 entry_name
= entry
.name
;
409 entry_name
= make_fourcc( " " );
412 /* Print out the ascii representation of type and name, as well as
413 length and file offset. */
415 cout
<< hex
<< setfill( '0' ) << "type: "
416 << ((char *)&entry
.type
)[0]
417 << ((char *)&entry
.type
)[1]
418 << ((char *)&entry
.type
)[2]
419 << ((char *)&entry
.type
)[3]
421 << ((char *)&entry_name
)[0]
422 << ((char *)&entry_name
)[1]
423 << ((char *)&entry_name
)[2]
424 << ((char *)&entry_name
)[3]
425 << " length: 0x" << setw( 12 ) << entry
.length
426 << " offset: 0x" << setw( 12 ) << entry
.offset
428 << ((char *)&list_name
)[0]
429 << ((char *)&list_name
)[1]
430 << ((char *)&list_name
)[2]
431 << ((char *)&list_name
)[3] << dec
<< endl
;
433 /* print the content itself */
435 PrintDirectoryEntryData( entry
);
439 /** prints the contents of the entry
441 Prints a readable representation of the contents of an index.
442 Override this to print out any objects you store in the RIFF file.
444 \param entry the entry to print */
446 void RIFFFile
::PrintDirectoryEntryData( const RIFFDirEntry
&entry
) const
450 /** prints the contents of the whole directory
452 Prints a readable representation of the contents of an index.
453 Override this to print out any objects you store in the RIFF file.
455 \param entry the entry to print */
457 void RIFFFile
::PrintDirectory() const
460 int count
= directory
.size();
462 for ( i
= 0; i
< count
; ++i
)
463 PrintDirectoryEntry( i
);
469 finds the index of a given directory entry type
471 \todo inefficient if the directory has lots of items
472 \param type the type of the entry to find
473 \param n the zero-based instance of type to locate
474 \return the index of the found object in the directory, or -1 if not found */
476 int RIFFFile
::FindDirectoryEntry ( FOURCC type
, int n
) const
479 int count
= directory
.size();
481 for ( i
= 0; i
< count
; ++i
)
482 if ( directory
[ i
].type
== type
)
493 /** Reads all items that are contained in one list
495 Read in one chunk and add it to the directory. If the chunk
496 happens to be of type LIST, then call ParseList recursively for
499 \param parent The id of the item to process
502 void RIFFFile
::ParseChunk( int parent
)
508 /* Check whether it is a LIST. If so, let ParseList deal with it */
510 read( fd
, &type
, sizeof( type
) );
511 if ( type
== make_fourcc( "LIST" ) )
513 typesize
= -sizeof( type
);
514 fail_if( lseek( fd
, typesize
, SEEK_CUR
) == ( off_t
) - 1 );
518 /* it is a normal chunk, create a new directory entry for it */
522 fail_neg( read( fd
, &length
, sizeof( length
) ) );
525 AddDirectoryEntry( type
, 0, length
, parent
);
526 fail_if( lseek( fd
, length
, SEEK_CUR
) == ( off_t
) - 1 );
531 /** Reads all items that are contained in one list
533 \param parent The id of the list to process
537 void RIFFFile
::ParseList( int parent
)
546 /* Read in the chunk header (type and length). */
547 fail_neg( read( fd
, &type
, sizeof( type
) ) );
548 fail_neg( read( fd
, &length
, sizeof( length
) ) );
553 /* The contents of the list starts here. Obtain its offset. The list
554 name (4 bytes) is already part of the contents). */
556 pos
= lseek( fd
, 0, SEEK_CUR
);
557 fail_if( pos
== ( off_t
) - 1 );
558 fail_neg( read( fd
, &name
, sizeof( name
) ) );
560 /* Add an entry for this list. */
562 list
= AddDirectoryEntry( type
, name
, sizeof( name
), parent
);
564 /* Read in any chunks contained in this list. This list is the
565 parent for all chunks it contains. */
567 listEnd
= pos
+ length
;
568 while ( pos
< listEnd
)
571 pos
= lseek( fd
, 0, SEEK_CUR
);
572 fail_if( pos
== ( off_t
) - 1 );
577 /** Reads the directory structure of the whole RIFF file
581 void RIFFFile
::ParseRIFF( void )
587 int container
= AddDirectoryEntry( make_fourcc( "FILE" ), make_fourcc( "FILE" ), 0, RIFF_NO_PARENT
);
589 pos
= lseek( fd
, 0, SEEK_SET
);
591 /* calculate file size from RIFF header instead from physical file. */
593 while ( ( read( fd
, &type
, sizeof( type
) ) > 0 ) &&
594 ( read( fd
, &length
, sizeof( length
) ) > 0 ) &&
595 ( type
== make_fourcc( "RIFF" ) ) )
598 filesize
+= length
+ RIFF_HEADERSIZE
;
600 fail_if( lseek( fd
, pos
, SEEK_SET
) == ( off_t
) - 1 );
601 ParseList( container
);
602 pos
= lseek( fd
, 0, SEEK_CUR
);
603 fail_if( pos
== ( off_t
) - 1 );
608 /** Reads one item including its contents from the RIFF file
610 \param chunk_index The index of the item to write
611 \param data A pointer to the data
615 void RIFFFile
::ReadChunk( int chunk_index
, void *data
)
619 entry
= GetDirectoryEntry( chunk_index
);
620 pthread_mutex_lock( &file_mutex
);
621 fail_if( lseek( fd
, entry
.offset
, SEEK_SET
) == ( off_t
) - 1 );
622 fail_neg( read( fd
, data
, entry
.length
) );
623 pthread_mutex_unlock( &file_mutex
);
627 /** Writes one item including its contents to the RIFF file
629 \param chunk_index The index of the item to write
630 \param data A pointer to the data
634 void RIFFFile
::WriteChunk( int chunk_index
, const void *data
)
638 entry
= GetDirectoryEntry( chunk_index
);
639 pthread_mutex_lock( &file_mutex
);
640 fail_if( lseek( fd
, entry
.offset
- RIFF_HEADERSIZE
, SEEK_SET
) == ( off_t
) - 1 );
641 fail_neg( write( fd
, &entry
.type
, sizeof( entry
.type
) ) );
642 DWORD length
= entry
.length
;
643 fail_neg( write( fd
, &length
, sizeof( length
) ) );
644 fail_neg( write( fd
, data
, entry
.length
) );
645 pthread_mutex_unlock( &file_mutex
);
647 /* Remember that this entry already has been written. */
649 directory
[ chunk_index
].written
= true;
653 /** Writes out the directory structure
655 For all items in the directory list that have not been written
656 yet, it seeks to the file position where that item should be
657 stored and writes the type and length field. If the item has a
658 name, it will also write the name field.
660 \note It does not write the contents of any item. Use WriteChunk to do that. */
662 void RIFFFile
::WriteRIFF( void )
666 int count
= directory
.size();
668 /* Start at the second entry (RIFF), since the first entry (FILE)
669 is needed only for internal purposes and is not written to the
672 for ( i
= 1; i
< count
; ++i
)
675 /* Only deal with entries that haven´t been written */
677 entry
= GetDirectoryEntry( i
);
678 if ( entry
.written
== false )
681 /* A chunk entry consist of its type and length, a list
682 entry has an additional name. Look up the entry, seek
683 to the start of the header, which is at the offset of
684 the data start minus the header size and write out the
687 fail_if( lseek( fd
, entry
.offset
- RIFF_HEADERSIZE
, SEEK_SET
) == ( off_t
) - 1 ) ;
688 fail_neg( write( fd
, &entry
.type
, sizeof( entry
.type
) ) );
689 DWORD length
= entry
.length
;
690 fail_neg( write( fd
, &length
, sizeof( length
) ) );
692 /* If it has a name, it is a list. Write out the extra name
695 if ( entry
.name
!= 0 )
697 fail_neg( write( fd
, &entry
.name
, sizeof( entry
.name
) ) );
700 /* Remember that this entry already has been written. */
702 directory
[ i
].written
= true;