44a082ca1e655af9bc21000554834f5106b2109e
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
87 #endif /* __FreeBSD__ */
108 /** make a 32 bit "string-id"
110 \param s a pointer to 4 chars
111 \return the 32 bit "string id"
112 \bugs It is not checked whether we really have 4 characters
114 Some compilers understand constants like int id = 'ABCD'; but I
115 could not get it working on the gcc compiler so I had to use this
116 workaround. We can now use id = make_fourcc("ABCD") instead. */
118 FOURCC
make_fourcc( const char *s
)
123 return *( ( FOURCC
* ) s
);
127 RIFFDirEntry
::RIFFDirEntry()
131 RIFFDirEntry
::RIFFDirEntry ( FOURCC t
, FOURCC n
, int l
, int o
, int p
) : type( t
), name( n
), length( l
), offset( o
), parent( p
), written( 0 )
135 /** Creates the object without an output file.
139 RIFFFile
::RIFFFile() : fd( -1 )
141 pthread_mutex_init( &file_mutex
, NULL
);
147 Duplicate the file descriptor
150 RIFFFile
::RIFFFile( const RIFFFile
& riff
) : fd( -1 )
156 directory
= riff
.directory
;
160 /** Destroys the object.
162 If it has an associated opened file, close it. */
164 RIFFFile
::~RIFFFile()
167 pthread_mutex_destroy( &file_mutex
);
171 RIFFFile
& RIFFFile
::operator=( const RIFFFile
& riff
)
180 directory
= riff
.directory
;
186 /** Creates or truncates the file.
188 \param s the filename
191 bool RIFFFile
::Create( const char *s
)
193 fd
= open( s
, O_RDWR
| O_NONBLOCK
| O_CREAT
| O_TRUNC
, 00644 );
202 /** Opens the file read only.
204 \param s the filename
207 bool RIFFFile
::Open( const char *s
)
209 fd
= open( s
, O_RDONLY
| O_NONBLOCK
);
218 /** Destroys the object.
220 If it has an associated opened file, close it. */
222 void RIFFFile
::Close()
232 /** Adds an entry to the list of containers.
234 \param type the type of this entry
236 \param length the length of the data in the container
237 \param list the container in which this object is contained.
238 \return the ID of the newly created entry
240 The topmost object is not contained in any other container. Use
241 the special ID RIFF_NO_PARENT to create the topmost object. */
243 int RIFFFile
::AddDirectoryEntry( FOURCC type
, FOURCC name
, off_t length
, int list
)
245 /* Put all parameters in an RIFFDirEntry object. The offset is
246 currently unknown. */
248 RIFFDirEntry
entry( type
, name
, length
, 0 /* offset */, list
);
250 /* If the new chunk is in a list, then get the offset and size
251 of that list. The offset of this chunk is the end of the list
252 (parent_offset + parent_length) plus the size of the chunk
255 if ( list
!= RIFF_NO_PARENT
)
257 RIFFDirEntry parent
= GetDirectoryEntry( list
);
258 entry
.offset
= parent
.offset
+ parent
.length
+ RIFF_HEADERSIZE
;
261 /* The list which this new chunk is a member of has now increased in
262 size. Get that directory entry and bump up its length by the size
263 of the chunk. Since that list may also be contained in another
264 list, walk up to the top of the tree. */
266 while ( list
!= RIFF_NO_PARENT
)
268 RIFFDirEntry parent
= GetDirectoryEntry( list
);
269 parent
.length
+= RIFF_HEADERSIZE
+ length
;
270 SetDirectoryEntry( list
, parent
);
271 list
= parent
.parent
;
274 directory
.insert( directory
.end(), entry
);
276 return directory
.size() - 1;
280 /** Modifies an entry.
282 \param i the ID of the entry which is to modify
283 \param type the type of this entry
285 \param length the length of the data in the container
286 \param list the container in which this object is contained.
287 \note Do not change length, offset, or the parent container.
288 \note Do not change an empty name ("") to a name and vice versa */
290 void RIFFFile
::SetDirectoryEntry( int i
, FOURCC type
, FOURCC name
, off_t length
, off_t offset
, int list
)
292 RIFFDirEntry
entry( type
, name
, length
, offset
, list
);
294 assert( i
>= 0 && i
< ( int ) directory
.size() );
296 directory
[ i
] = entry
;
300 /** Modifies an entry.
302 The entry.written flag is set to false because the contents has been modified
304 \param i the ID of the entry which is to modify
305 \param entry the new entry
306 \note Do not change length, offset, or the parent container.
307 \note Do not change an empty name ("") to a name and vice versa */
309 void RIFFFile
::SetDirectoryEntry( int i
, RIFFDirEntry
&entry
)
311 assert( i
>= 0 && i
< ( int ) directory
.size() );
313 entry
.written
= false;
314 directory
[ i
] = entry
;
318 /** Retrieves an entry.
320 Gets the most important member variables.
322 \param i the ID of the entry to retrieve
329 void RIFFFile
::GetDirectoryEntry( int i
, FOURCC
&type
, FOURCC
&name
, off_t
&length
, off_t
&offset
, int &list
) const
333 assert( i
>= 0 && i
< ( int ) directory
.size() );
335 entry
= directory
[ i
];
338 length
= entry
.length
;
339 offset
= entry
.offset
;
344 /** Retrieves an entry.
346 Gets the whole RIFFDirEntry object.
348 \param i the ID of the entry to retrieve
351 RIFFDirEntry RIFFFile
::GetDirectoryEntry( int i
) const
353 assert( i
>= 0 && i
< ( int ) directory
.size() );
355 return directory
[ i
];
359 /** Calculates the total size of the file
361 \return the size the file in bytes
364 off_t RIFFFile
::GetFileSize( void ) const
367 /* If we have at least one entry, return the length field
368 of the FILE entry, which is the length of its contents,
369 which is the actual size of whatever is currently in the
370 AVI directory structure.
372 Note that the first entry does not belong to the AVI
375 If we don't have any entry, the file size is zero. */
377 if ( directory
.size() > 0 )
378 return directory
[ 0 ].length
;
384 /** prints the attributes of the entry
386 \param i the ID of the entry to print
389 void RIFFFile
::PrintDirectoryEntry ( int i
) const
396 /* Get all attributes of the chunk object. If it is contained
397 in a list, get the name of the list too (otherwise the name of
398 the list is blank). If the chunk object doesn´t have a name (only
399 LISTs and RIFFs have a name), the name is blank. */
401 entry
= GetDirectoryEntry( i
);
402 if ( entry
.parent
!= RIFF_NO_PARENT
)
404 parent
= GetDirectoryEntry( entry
.parent
);
405 list_name
= parent
.name
;
409 list_name
= make_fourcc( " " );
411 if ( entry
.name
!= 0 )
413 entry_name
= entry
.name
;
417 entry_name
= make_fourcc( " " );
420 /* Print out the ascii representation of type and name, as well as
421 length and file offset. */
423 cout
<< hex
<< setfill( '0' ) << "type: "
424 << ((char *)&entry
.type
)[0]
425 << ((char *)&entry
.type
)[1]
426 << ((char *)&entry
.type
)[2]
427 << ((char *)&entry
.type
)[3]
429 << ((char *)&entry_name
)[0]
430 << ((char *)&entry_name
)[1]
431 << ((char *)&entry_name
)[2]
432 << ((char *)&entry_name
)[3]
433 << " length: 0x" << setw( 12 ) << entry
.length
434 << " offset: 0x" << setw( 12 ) << entry
.offset
436 << ((char *)&list_name
)[0]
437 << ((char *)&list_name
)[1]
438 << ((char *)&list_name
)[2]
439 << ((char *)&list_name
)[3] << dec
<< endl
;
441 /* print the content itself */
443 PrintDirectoryEntryData( entry
);
447 /** prints the contents of the entry
449 Prints a readable representation of the contents of an index.
450 Override this to print out any objects you store in the RIFF file.
452 \param entry the entry to print */
454 void RIFFFile
::PrintDirectoryEntryData( const RIFFDirEntry
&entry
) const
458 /** prints the contents of the whole directory
460 Prints a readable representation of the contents of an index.
461 Override this to print out any objects you store in the RIFF file.
463 \param entry the entry to print */
465 void RIFFFile
::PrintDirectory() const
468 int count
= directory
.size();
470 for ( i
= 0; i
< count
; ++i
)
471 PrintDirectoryEntry( i
);
477 finds the index of a given directory entry type
479 \todo inefficient if the directory has lots of items
480 \param type the type of the entry to find
481 \param n the zero-based instance of type to locate
482 \return the index of the found object in the directory, or -1 if not found */
484 int RIFFFile
::FindDirectoryEntry ( FOURCC type
, int n
) const
487 int count
= directory
.size();
489 for ( i
= 0; i
< count
; ++i
)
490 if ( directory
[ i
].type
== type
)
501 /** Reads all items that are contained in one list
503 Read in one chunk and add it to the directory. If the chunk
504 happens to be of type LIST, then call ParseList recursively for
507 \param parent The id of the item to process
510 void RIFFFile
::ParseChunk( int parent
)
516 /* Check whether it is a LIST. If so, let ParseList deal with it */
518 fail_if( read( fd
, &type
, sizeof( type
) ) != sizeof( type
));
519 if ( type
== make_fourcc( "LIST" ) )
521 typesize
= (int) -sizeof( type
);
522 fail_if( lseek( fd
, typesize
, SEEK_CUR
) == ( off_t
) - 1 );
526 /* it is a normal chunk, create a new directory entry for it */
530 fail_neg( read( fd
, &length
, sizeof( length
) ) );
533 AddDirectoryEntry( type
, 0, length
, parent
);
534 fail_if( lseek( fd
, length
, SEEK_CUR
) == ( off_t
) - 1 );
539 /** Reads all items that are contained in one list
541 \param parent The id of the list to process
545 void RIFFFile
::ParseList( int parent
)
554 /* Read in the chunk header (type and length). */
555 fail_neg( read( fd
, &type
, sizeof( type
) ) );
556 fail_neg( read( fd
, &length
, sizeof( length
) ) );
561 /* The contents of the list starts here. Obtain its offset. The list
562 name (4 bytes) is already part of the contents). */
564 pos
= lseek( fd
, 0, SEEK_CUR
);
565 fail_if( pos
== ( off_t
) - 1 );
566 fail_neg( read( fd
, &name
, sizeof( name
) ) );
568 /* Add an entry for this list. */
570 list
= AddDirectoryEntry( type
, name
, sizeof( name
), parent
);
572 /* Read in any chunks contained in this list. This list is the
573 parent for all chunks it contains. */
575 listEnd
= pos
+ length
;
576 while ( pos
< listEnd
)
579 pos
= lseek( fd
, 0, SEEK_CUR
);
580 fail_if( pos
== ( off_t
) - 1 );
585 /** Reads the directory structure of the whole RIFF file
589 void RIFFFile
::ParseRIFF( void )
595 int container
= AddDirectoryEntry( make_fourcc( "FILE" ), make_fourcc( "FILE" ), 0, RIFF_NO_PARENT
);
597 pos
= lseek( fd
, 0, SEEK_SET
);
599 /* calculate file size from RIFF header instead from physical file. */
601 while ( ( read( fd
, &type
, sizeof( type
) ) > 0 ) &&
602 ( read( fd
, &length
, sizeof( length
) ) > 0 ) &&
603 ( type
== make_fourcc( "RIFF" ) ) )
606 filesize
+= length
+ RIFF_HEADERSIZE
;
608 fail_if( lseek( fd
, pos
, SEEK_SET
) == ( off_t
) - 1 );
609 ParseList( container
);
610 pos
= lseek( fd
, 0, SEEK_CUR
);
611 fail_if( pos
== ( off_t
) - 1 );
616 /** Reads one item including its contents from the RIFF file
618 \param chunk_index The index of the item to write
619 \param data A pointer to the data
623 void RIFFFile
::ReadChunk( int chunk_index
, void *data
, off_t data_len
)
627 entry
= GetDirectoryEntry( chunk_index
);
628 pthread_mutex_lock( &file_mutex
);
629 fail_if( lseek( fd
, entry
.offset
, SEEK_SET
) == ( off_t
) - 1 );
630 fail_neg( read( fd
, data
, entry
.length
> data_len ? data_len
: entry
.length
) );
631 pthread_mutex_unlock( &file_mutex
);
635 /** Writes one item including its contents to the RIFF file
637 \param chunk_index The index of the item to write
638 \param data A pointer to the data
642 void RIFFFile
::WriteChunk( int chunk_index
, const void *data
)
646 entry
= GetDirectoryEntry( chunk_index
);
647 pthread_mutex_lock( &file_mutex
);
648 fail_if( lseek( fd
, entry
.offset
- RIFF_HEADERSIZE
, SEEK_SET
) == ( off_t
) - 1 );
649 fail_neg( write( fd
, &entry
.type
, sizeof( entry
.type
) ) );
650 DWORD length
= entry
.length
;
651 fail_neg( write( fd
, &length
, sizeof( length
) ) );
652 fail_neg( write( fd
, data
, entry
.length
) );
653 pthread_mutex_unlock( &file_mutex
);
655 /* Remember that this entry already has been written. */
657 directory
[ chunk_index
].written
= true;
661 /** Writes out the directory structure
663 For all items in the directory list that have not been written
664 yet, it seeks to the file position where that item should be
665 stored and writes the type and length field. If the item has a
666 name, it will also write the name field.
668 \note It does not write the contents of any item. Use WriteChunk to do that. */
670 void RIFFFile
::WriteRIFF( void )
674 int count
= directory
.size();
676 /* Start at the second entry (RIFF), since the first entry (FILE)
677 is needed only for internal purposes and is not written to the
680 for ( i
= 1; i
< count
; ++i
)
683 /* Only deal with entries that haven´t been written */
685 entry
= GetDirectoryEntry( i
);
686 if ( entry
.written
== false )
689 /* A chunk entry consist of its type and length, a list
690 entry has an additional name. Look up the entry, seek
691 to the start of the header, which is at the offset of
692 the data start minus the header size and write out the
695 fail_if( lseek( fd
, entry
.offset
- RIFF_HEADERSIZE
, SEEK_SET
) == ( off_t
) - 1 ) ;
696 fail_neg( write( fd
, &entry
.type
, sizeof( entry
.type
) ) );
697 DWORD length
= entry
.length
;
698 fail_neg( write( fd
, &length
, sizeof( length
) ) );
700 /* If it has a name, it is a list. Write out the extra name
703 if ( entry
.name
!= 0 )
705 fail_neg( write( fd
, &entry
.name
, sizeof( entry
.name
) ) );
708 /* Remember that this entry already has been written. */
710 directory
[ i
].written
= true;