51ed2101dd8796a051d38d130460939032df6ff1
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.3 2005/07/25 14:41:29 lilo_booter
25 * + Minor correction for entry length being less than the data length
27 * Revision 1.2 2005/07/25 07:21:39 lilo_booter
28 * + fixes for opendml dv avi
30 * Revision 1.1 2005/04/15 14:28:26 lilo_booter
33 * Revision 1.18 2005/04/01 23:43:10 ddennedy
34 * apply endian fixes from Daniel Kobras
36 * Revision 1.17 2004/10/11 01:37:11 ddennedy
37 * mutex safety locks in RIFF and AVI classes, type 2 AVI optimization, mencoder export script
39 * Revision 1.16 2003/11/25 23:01:24 ddennedy
40 * cleanup and a few bugfixes
42 * Revision 1.15 2003/10/21 16:34:34 ddennedy
43 * GNOME2 port phase 1: initial checkin
45 * Revision 1.13.2.3 2003/08/26 20:39:00 ddennedy
46 * relocate mutex unlock and add assert includes
48 * Revision 1.13.2.2 2003/01/28 12:54:13 lilo_booter
49 * New 'no change' image transition
51 * Revision 1.13.2.1 2002/11/25 04:48:31 ddennedy
52 * bugfix to report errors when loading files
54 * Revision 1.13 2002/09/13 06:49:49 ddennedy
55 * build update, cleanup, bugfixes
57 * Revision 1.12 2002/04/21 06:36:40 ddennedy
58 * kindler avc and 1394 bus reset support in catpure page, honor max file size
60 * Revision 1.11 2002/04/09 06:53:42 ddennedy
61 * cleanup, new libdv 0.9.5, large AVI, dnd storyboard
63 * Revision 1.4 2002/03/25 21:34:25 arne
64 * Support for large (64 bit) files mostly completed
66 * Revision 1.3 2002/03/10 21:28:29 arne
67 * release 1.1b1, 64 bit support for type 1 avis
69 * Revision 1.2 2002/03/04 19:22:43 arne
70 * updated to latest Kino avi code
72 * Revision 1.1.1.1 2002/03/03 19:08:08 arne
73 * import of version 1.01
106 /** make a 32 bit "string-id"
108 \param s a pointer to 4 chars
109 \return the 32 bit "string id"
110 \bugs It is not checked whether we really have 4 characters
112 Some compilers understand constants like int id = 'ABCD'; but I
113 could not get it working on the gcc compiler so I had to use this
114 workaround. We can now use id = make_fourcc("ABCD") instead. */
116 FOURCC
make_fourcc( char *s
)
121 return *( ( FOURCC
* ) s
);
125 RIFFDirEntry
::RIFFDirEntry()
129 RIFFDirEntry
::RIFFDirEntry ( FOURCC t
, FOURCC n
, int l
, int o
, int p
) : type( t
), name( n
), length( l
), offset( o
), parent( p
), written( 0 )
133 /** Creates the object without an output file.
137 RIFFFile
::RIFFFile() : fd( -1 )
139 pthread_mutex_init( &file_mutex
, NULL
);
145 Duplicate the file descriptor
148 RIFFFile
::RIFFFile( const RIFFFile
& riff
) : fd( -1 )
154 directory
= riff
.directory
;
158 /** Destroys the object.
160 If it has an associated opened file, close it. */
162 RIFFFile
::~RIFFFile()
165 pthread_mutex_destroy( &file_mutex
);
169 RIFFFile
& RIFFFile
::operator=( const RIFFFile
& riff
)
178 directory
= riff
.directory
;
184 /** Creates or truncates the file.
186 \param s the filename
189 bool RIFFFile
::Create( const char *s
)
191 fd
= open( s
, O_RDWR
| O_NONBLOCK
| O_CREAT
| O_TRUNC
, 00644 );
200 /** Opens the file read only.
202 \param s the filename
205 bool RIFFFile
::Open( const char *s
)
207 fd
= open( s
, O_RDONLY
| O_NONBLOCK
);
216 /** Destroys the object.
218 If it has an associated opened file, close it. */
220 void RIFFFile
::Close()
230 /** Adds an entry to the list of containers.
232 \param type the type of this entry
234 \param length the length of the data in the container
235 \param list the container in which this object is contained.
236 \return the ID of the newly created entry
238 The topmost object is not contained in any other container. Use
239 the special ID RIFF_NO_PARENT to create the topmost object. */
241 int RIFFFile
::AddDirectoryEntry( FOURCC type
, FOURCC name
, off_t length
, int list
)
243 /* Put all parameters in an RIFFDirEntry object. The offset is
244 currently unknown. */
246 RIFFDirEntry
entry( type
, name
, length
, 0 /* offset */, list
);
248 /* If the new chunk is in a list, then get the offset and size
249 of that list. The offset of this chunk is the end of the list
250 (parent_offset + parent_length) plus the size of the chunk
253 if ( list
!= RIFF_NO_PARENT
)
255 RIFFDirEntry parent
= GetDirectoryEntry( list
);
256 entry
.offset
= parent
.offset
+ parent
.length
+ RIFF_HEADERSIZE
;
259 /* The list which this new chunk is a member of has now increased in
260 size. Get that directory entry and bump up its length by the size
261 of the chunk. Since that list may also be contained in another
262 list, walk up to the top of the tree. */
264 while ( list
!= RIFF_NO_PARENT
)
266 RIFFDirEntry parent
= GetDirectoryEntry( list
);
267 parent
.length
+= RIFF_HEADERSIZE
+ length
;
268 SetDirectoryEntry( list
, parent
);
269 list
= parent
.parent
;
272 directory
.insert( directory
.end(), entry
);
274 return directory
.size() - 1;
278 /** Modifies an entry.
280 \param i the ID of the entry which is to modify
281 \param type the type of this entry
283 \param length the length of the data in the container
284 \param list the container in which this object is contained.
285 \note Do not change length, offset, or the parent container.
286 \note Do not change an empty name ("") to a name and vice versa */
288 void RIFFFile
::SetDirectoryEntry( int i
, FOURCC type
, FOURCC name
, off_t length
, off_t offset
, int list
)
290 RIFFDirEntry
entry( type
, name
, length
, offset
, list
);
292 assert( i
>= 0 && i
< ( int ) directory
.size() );
294 directory
[ i
] = entry
;
298 /** Modifies an entry.
300 The entry.written flag is set to false because the contents has been modified
302 \param i the ID of the entry which is to modify
303 \param entry the new entry
304 \note Do not change length, offset, or the parent container.
305 \note Do not change an empty name ("") to a name and vice versa */
307 void RIFFFile
::SetDirectoryEntry( int i
, RIFFDirEntry
&entry
)
309 assert( i
>= 0 && i
< ( int ) directory
.size() );
311 entry
.written
= false;
312 directory
[ i
] = entry
;
316 /** Retrieves an entry.
318 Gets the most important member variables.
320 \param i the ID of the entry to retrieve
327 void RIFFFile
::GetDirectoryEntry( int i
, FOURCC
&type
, FOURCC
&name
, off_t
&length
, off_t
&offset
, int &list
) const
331 assert( i
>= 0 && i
< ( int ) directory
.size() );
333 entry
= directory
[ i
];
336 length
= entry
.length
;
337 offset
= entry
.offset
;
342 /** Retrieves an entry.
344 Gets the whole RIFFDirEntry object.
346 \param i the ID of the entry to retrieve
349 RIFFDirEntry RIFFFile
::GetDirectoryEntry( int i
) const
351 assert( i
>= 0 && i
< ( int ) directory
.size() );
353 return directory
[ i
];
357 /** Calculates the total size of the file
359 \return the size the file in bytes
362 off_t RIFFFile
::GetFileSize( void ) const
365 /* If we have at least one entry, return the length field
366 of the FILE entry, which is the length of its contents,
367 which is the actual size of whatever is currently in the
368 AVI directory structure.
370 Note that the first entry does not belong to the AVI
373 If we don't have any entry, the file size is zero. */
375 if ( directory
.size() > 0 )
376 return directory
[ 0 ].length
;
382 /** prints the attributes of the entry
384 \param i the ID of the entry to print
387 void RIFFFile
::PrintDirectoryEntry ( int i
) const
394 /* Get all attributes of the chunk object. If it is contained
395 in a list, get the name of the list too (otherwise the name of
396 the list is blank). If the chunk object doesn´t have a name (only
397 LISTs and RIFFs have a name), the name is blank. */
399 entry
= GetDirectoryEntry( i
);
400 if ( entry
.parent
!= RIFF_NO_PARENT
)
402 parent
= GetDirectoryEntry( entry
.parent
);
403 list_name
= parent
.name
;
407 list_name
= make_fourcc( " " );
409 if ( entry
.name
!= 0 )
411 entry_name
= entry
.name
;
415 entry_name
= make_fourcc( " " );
418 /* Print out the ascii representation of type and name, as well as
419 length and file offset. */
421 cout
<< hex
<< setfill( '0' ) << "type: "
422 << ((char *)&entry
.type
)[0]
423 << ((char *)&entry
.type
)[1]
424 << ((char *)&entry
.type
)[2]
425 << ((char *)&entry
.type
)[3]
427 << ((char *)&entry_name
)[0]
428 << ((char *)&entry_name
)[1]
429 << ((char *)&entry_name
)[2]
430 << ((char *)&entry_name
)[3]
431 << " length: 0x" << setw( 12 ) << entry
.length
432 << " offset: 0x" << setw( 12 ) << entry
.offset
434 << ((char *)&list_name
)[0]
435 << ((char *)&list_name
)[1]
436 << ((char *)&list_name
)[2]
437 << ((char *)&list_name
)[3] << dec
<< endl
;
439 /* print the content itself */
441 PrintDirectoryEntryData( entry
);
445 /** prints the contents of the entry
447 Prints a readable representation of the contents of an index.
448 Override this to print out any objects you store in the RIFF file.
450 \param entry the entry to print */
452 void RIFFFile
::PrintDirectoryEntryData( const RIFFDirEntry
&entry
) const
456 /** prints the contents of the whole directory
458 Prints a readable representation of the contents of an index.
459 Override this to print out any objects you store in the RIFF file.
461 \param entry the entry to print */
463 void RIFFFile
::PrintDirectory() const
466 int count
= directory
.size();
468 for ( i
= 0; i
< count
; ++i
)
469 PrintDirectoryEntry( i
);
475 finds the index of a given directory entry type
477 \todo inefficient if the directory has lots of items
478 \param type the type of the entry to find
479 \param n the zero-based instance of type to locate
480 \return the index of the found object in the directory, or -1 if not found */
482 int RIFFFile
::FindDirectoryEntry ( FOURCC type
, int n
) const
485 int count
= directory
.size();
487 for ( i
= 0; i
< count
; ++i
)
488 if ( directory
[ i
].type
== type
)
499 /** Reads all items that are contained in one list
501 Read in one chunk and add it to the directory. If the chunk
502 happens to be of type LIST, then call ParseList recursively for
505 \param parent The id of the item to process
508 void RIFFFile
::ParseChunk( int parent
)
514 /* Check whether it is a LIST. If so, let ParseList deal with it */
516 read( fd
, &type
, sizeof( type
) );
517 if ( type
== make_fourcc( "LIST" ) )
519 typesize
= -sizeof( type
);
520 fail_if( lseek( fd
, typesize
, SEEK_CUR
) == ( off_t
) - 1 );
524 /* it is a normal chunk, create a new directory entry for it */
528 fail_neg( read( fd
, &length
, sizeof( length
) ) );
531 AddDirectoryEntry( type
, 0, length
, parent
);
532 fail_if( lseek( fd
, length
, SEEK_CUR
) == ( off_t
) - 1 );
537 /** Reads all items that are contained in one list
539 \param parent The id of the list to process
543 void RIFFFile
::ParseList( int parent
)
552 /* Read in the chunk header (type and length). */
553 fail_neg( read( fd
, &type
, sizeof( type
) ) );
554 fail_neg( read( fd
, &length
, sizeof( length
) ) );
559 /* The contents of the list starts here. Obtain its offset. The list
560 name (4 bytes) is already part of the contents). */
562 pos
= lseek( fd
, 0, SEEK_CUR
);
563 fail_if( pos
== ( off_t
) - 1 );
564 fail_neg( read( fd
, &name
, sizeof( name
) ) );
566 /* Add an entry for this list. */
568 list
= AddDirectoryEntry( type
, name
, sizeof( name
), parent
);
570 /* Read in any chunks contained in this list. This list is the
571 parent for all chunks it contains. */
573 listEnd
= pos
+ length
;
574 while ( pos
< listEnd
)
577 pos
= lseek( fd
, 0, SEEK_CUR
);
578 fail_if( pos
== ( off_t
) - 1 );
583 /** Reads the directory structure of the whole RIFF file
587 void RIFFFile
::ParseRIFF( void )
593 int container
= AddDirectoryEntry( make_fourcc( "FILE" ), make_fourcc( "FILE" ), 0, RIFF_NO_PARENT
);
595 pos
= lseek( fd
, 0, SEEK_SET
);
597 /* calculate file size from RIFF header instead from physical file. */
599 while ( ( read( fd
, &type
, sizeof( type
) ) > 0 ) &&
600 ( read( fd
, &length
, sizeof( length
) ) > 0 ) &&
601 ( type
== make_fourcc( "RIFF" ) ) )
604 filesize
+= length
+ RIFF_HEADERSIZE
;
606 fail_if( lseek( fd
, pos
, SEEK_SET
) == ( off_t
) - 1 );
607 ParseList( container
);
608 pos
= lseek( fd
, 0, SEEK_CUR
);
609 fail_if( pos
== ( off_t
) - 1 );
614 /** Reads one item including its contents from the RIFF file
616 \param chunk_index The index of the item to write
617 \param data A pointer to the data
621 void RIFFFile
::ReadChunk( int chunk_index
, void *data
, off_t data_len
)
625 entry
= GetDirectoryEntry( chunk_index
);
626 pthread_mutex_lock( &file_mutex
);
627 fail_if( lseek( fd
, entry
.offset
, SEEK_SET
) == ( off_t
) - 1 );
628 fail_neg( read( fd
, data
, entry
.length
> data_len ? data_len
: entry
.length
) );
629 pthread_mutex_unlock( &file_mutex
);
633 /** Writes one item including its contents to the RIFF file
635 \param chunk_index The index of the item to write
636 \param data A pointer to the data
640 void RIFFFile
::WriteChunk( int chunk_index
, const void *data
)
644 entry
= GetDirectoryEntry( chunk_index
);
645 pthread_mutex_lock( &file_mutex
);
646 fail_if( lseek( fd
, entry
.offset
- RIFF_HEADERSIZE
, SEEK_SET
) == ( off_t
) - 1 );
647 fail_neg( write( fd
, &entry
.type
, sizeof( entry
.type
) ) );
648 DWORD length
= entry
.length
;
649 fail_neg( write( fd
, &length
, sizeof( length
) ) );
650 fail_neg( write( fd
, data
, entry
.length
) );
651 pthread_mutex_unlock( &file_mutex
);
653 /* Remember that this entry already has been written. */
655 directory
[ chunk_index
].written
= true;
659 /** Writes out the directory structure
661 For all items in the directory list that have not been written
662 yet, it seeks to the file position where that item should be
663 stored and writes the type and length field. If the item has a
664 name, it will also write the name field.
666 \note It does not write the contents of any item. Use WriteChunk to do that. */
668 void RIFFFile
::WriteRIFF( void )
672 int count
= directory
.size();
674 /* Start at the second entry (RIFF), since the first entry (FILE)
675 is needed only for internal purposes and is not written to the
678 for ( i
= 1; i
< count
; ++i
)
681 /* Only deal with entries that haven´t been written */
683 entry
= GetDirectoryEntry( i
);
684 if ( entry
.written
== false )
687 /* A chunk entry consist of its type and length, a list
688 entry has an additional name. Look up the entry, seek
689 to the start of the header, which is at the offset of
690 the data start minus the header size and write out the
693 fail_if( lseek( fd
, entry
.offset
- RIFF_HEADERSIZE
, SEEK_SET
) == ( off_t
) - 1 ) ;
694 fail_neg( write( fd
, &entry
.type
, sizeof( entry
.type
) ) );
695 DWORD length
= entry
.length
;
696 fail_neg( write( fd
, &length
, sizeof( length
) ) );
698 /* If it has a name, it is a list. Write out the extra name
701 if ( entry
.name
!= 0 )
703 fail_neg( write( fd
, &entry
.name
, sizeof( entry
.name
) ) );
706 /* Remember that this entry already has been written. */
708 directory
[ i
].written
= true;