X-Git-Url: http://research.m1stereo.tv/gitweb?a=blobdiff_plain;f=src%2Fmodules%2Fkino%2Friff.cc;fp=src%2Fmodules%2Fkino%2Friff.cc;h=bc616dda02cd972d2f0185b8188581b6eb516696;hb=815c458e95c73c39433fa7913afb4a830d7fd376;hp=0000000000000000000000000000000000000000;hpb=cdc142529076b33e4856a423547b9e93fb56330a;p=melted diff --git a/src/modules/kino/riff.cc b/src/modules/kino/riff.cc new file mode 100644 index 0000000..bc616dd --- /dev/null +++ b/src/modules/kino/riff.cc @@ -0,0 +1,705 @@ +/* +* riff.cc library for RIFF file format i/o +* Copyright (C) 2000 - 2002 Arne Schirmacher +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +* Tag: $Name$ +* +* Change log: +* +* $Log$ +* Revision 1.1 2005/04/15 14:28:26 lilo_booter +* Initial version +* +* Revision 1.18 2005/04/01 23:43:10 ddennedy +* apply endian fixes from Daniel Kobras +* +* Revision 1.17 2004/10/11 01:37:11 ddennedy +* mutex safety locks in RIFF and AVI classes, type 2 AVI optimization, mencoder export script +* +* Revision 1.16 2003/11/25 23:01:24 ddennedy +* cleanup and a few bugfixes +* +* Revision 1.15 2003/10/21 16:34:34 ddennedy +* GNOME2 port phase 1: initial checkin +* +* Revision 1.13.2.3 2003/08/26 20:39:00 ddennedy +* relocate mutex unlock and add assert includes +* +* Revision 1.13.2.2 2003/01/28 12:54:13 lilo_booter +* New 'no change' image transition +* +* Revision 1.13.2.1 2002/11/25 04:48:31 ddennedy +* bugfix to report errors when loading files +* +* Revision 1.13 2002/09/13 06:49:49 ddennedy +* build update, cleanup, bugfixes +* +* Revision 1.12 2002/04/21 06:36:40 ddennedy +* kindler avc and 1394 bus reset support in catpure page, honor max file size +* +* Revision 1.11 2002/04/09 06:53:42 ddennedy +* cleanup, new libdv 0.9.5, large AVI, dnd storyboard +* +* Revision 1.4 2002/03/25 21:34:25 arne +* Support for large (64 bit) files mostly completed +* +* Revision 1.3 2002/03/10 21:28:29 arne +* release 1.1b1, 64 bit support for type 1 avis +* +* Revision 1.2 2002/03/04 19:22:43 arne +* updated to latest Kino avi code +* +* Revision 1.1.1.1 2002/03/03 19:08:08 arne +* import of version 1.01 +* +*/ + +#include "config.h" + +// C++ includes + +#include +//#include +#include +#include +#include + +using std::cout; +using std::hex; +using std::dec; +using std::setw; +using std::setfill; +using std::endl; + +// C includes + +#include +#include +#include + +// local includes + +#include "error.h" +#include "riff.h" + + +/** make a 32 bit "string-id" + + \param s a pointer to 4 chars + \return the 32 bit "string id" + \bugs It is not checked whether we really have 4 characters + + Some compilers understand constants like int id = 'ABCD'; but I + could not get it working on the gcc compiler so I had to use this + workaround. We can now use id = make_fourcc("ABCD") instead. */ + +FOURCC make_fourcc( char *s ) +{ + if ( s[ 0 ] == 0 ) + return 0; + else + return *( ( FOURCC* ) s ); +} + + +RIFFDirEntry::RIFFDirEntry() +{} + + +RIFFDirEntry::RIFFDirEntry ( FOURCC t, FOURCC n, int l, int o, int p ) : type( t ), name( n ), length( l ), offset( o ), parent( p ), written( 0 ) +{} + + +/** Creates the object without an output file. + +*/ + +RIFFFile::RIFFFile() : fd( -1 ) +{ + pthread_mutex_init( &file_mutex, NULL ); +} + + +/* Copy constructor + + Duplicate the file descriptor +*/ + +RIFFFile::RIFFFile( const RIFFFile& riff ) : fd( -1 ) +{ + if ( riff.fd != -1 ) + { + fd = dup( riff.fd ); + } + directory = riff.directory; +} + + +/** Destroys the object. + + If it has an associated opened file, close it. */ + +RIFFFile::~RIFFFile() +{ + Close(); + pthread_mutex_destroy( &file_mutex ); +} + + +RIFFFile& RIFFFile::operator=( const RIFFFile& riff ) +{ + if ( fd != riff.fd ) + { + Close(); + if ( riff.fd != -1 ) + { + fd = dup( riff.fd ); + } + directory = riff.directory; + } + return *this; +} + + +/** Creates or truncates the file. + + \param s the filename +*/ + +bool RIFFFile::Create( const char *s ) +{ + fd = open( s, O_RDWR | O_NONBLOCK | O_CREAT | O_TRUNC, 00644 ); + + if ( fd == -1 ) + return false; + else + return true; +} + + +/** Opens the file read only. + + \param s the filename +*/ + +bool RIFFFile::Open( const char *s ) +{ + fd = open( s, O_RDONLY | O_NONBLOCK ); + + if ( fd == -1 ) + return false; + else + return true; +} + + +/** Destroys the object. + + If it has an associated opened file, close it. */ + +void RIFFFile::Close() +{ + if ( fd != -1 ) + { + close( fd ); + fd = -1; + } +} + + +/** Adds an entry to the list of containers. + + \param type the type of this entry + \param name the name + \param length the length of the data in the container + \param list the container in which this object is contained. + \return the ID of the newly created entry + + The topmost object is not contained in any other container. Use + the special ID RIFF_NO_PARENT to create the topmost object. */ + +int RIFFFile::AddDirectoryEntry( FOURCC type, FOURCC name, off_t length, int list ) +{ + /* Put all parameters in an RIFFDirEntry object. The offset is + currently unknown. */ + + RIFFDirEntry entry( type, name, length, 0 /* offset */, list ); + + /* If the new chunk is in a list, then get the offset and size + of that list. The offset of this chunk is the end of the list + (parent_offset + parent_length) plus the size of the chunk + header. */ + + if ( list != RIFF_NO_PARENT ) + { + RIFFDirEntry parent = GetDirectoryEntry( list ); + entry.offset = parent.offset + parent.length + RIFF_HEADERSIZE; + } + + /* The list which this new chunk is a member of has now increased in + size. Get that directory entry and bump up its length by the size + of the chunk. Since that list may also be contained in another + list, walk up to the top of the tree. */ + + while ( list != RIFF_NO_PARENT ) + { + RIFFDirEntry parent = GetDirectoryEntry( list ); + parent.length += RIFF_HEADERSIZE + length; + SetDirectoryEntry( list, parent ); + list = parent.parent; + } + + directory.insert( directory.end(), entry ); + + return directory.size() - 1; +} + + +/** Modifies an entry. + + \param i the ID of the entry which is to modify + \param type the type of this entry + \param name the name + \param length the length of the data in the container + \param list the container in which this object is contained. + \note Do not change length, offset, or the parent container. + \note Do not change an empty name ("") to a name and vice versa */ + +void RIFFFile::SetDirectoryEntry( int i, FOURCC type, FOURCC name, off_t length, off_t offset, int list ) +{ + RIFFDirEntry entry( type, name, length, offset, list ); + + assert( i >= 0 && i < ( int ) directory.size() ); + + directory[ i ] = entry; +} + + +/** Modifies an entry. + + The entry.written flag is set to false because the contents has been modified + + \param i the ID of the entry which is to modify + \param entry the new entry + \note Do not change length, offset, or the parent container. + \note Do not change an empty name ("") to a name and vice versa */ + +void RIFFFile::SetDirectoryEntry( int i, RIFFDirEntry &entry ) +{ + assert( i >= 0 && i < ( int ) directory.size() ); + + entry.written = false; + directory[ i ] = entry; +} + + +/** Retrieves an entry. + + Gets the most important member variables. + + \param i the ID of the entry to retrieve + \param type + \param name + \param length + \param offset + \param list */ + +void RIFFFile::GetDirectoryEntry( int i, FOURCC &type, FOURCC &name, off_t &length, off_t &offset, int &list ) const +{ + RIFFDirEntry entry; + + assert( i >= 0 && i < ( int ) directory.size() ); + + entry = directory[ i ]; + type = entry.type; + name = entry.name; + length = entry.length; + offset = entry.offset; + list = entry.parent; +} + + +/** Retrieves an entry. + + Gets the whole RIFFDirEntry object. + + \param i the ID of the entry to retrieve + \return the entry */ + +RIFFDirEntry RIFFFile::GetDirectoryEntry( int i ) const +{ + assert( i >= 0 && i < ( int ) directory.size() ); + + return directory[ i ]; +} + + +/** Calculates the total size of the file + + \return the size the file in bytes +*/ + +off_t RIFFFile::GetFileSize( void ) const +{ + + /* If we have at least one entry, return the length field + of the FILE entry, which is the length of its contents, + which is the actual size of whatever is currently in the + AVI directory structure. + + Note that the first entry does not belong to the AVI + file. + + If we don't have any entry, the file size is zero. */ + + if ( directory.size() > 0 ) + return directory[ 0 ].length; + else + return 0; +} + + +/** prints the attributes of the entry + + \param i the ID of the entry to print +*/ + +void RIFFFile::PrintDirectoryEntry ( int i ) const +{ + RIFFDirEntry entry; + RIFFDirEntry parent; + FOURCC entry_name; + FOURCC list_name; + + /* Get all attributes of the chunk object. If it is contained + in a list, get the name of the list too (otherwise the name of + the list is blank). If the chunk object doesn´t have a name (only + LISTs and RIFFs have a name), the name is blank. */ + + entry = GetDirectoryEntry( i ); + if ( entry.parent != RIFF_NO_PARENT ) + { + parent = GetDirectoryEntry( entry.parent ); + list_name = parent.name; + } + else + { + list_name = make_fourcc( " " ); + } + if ( entry.name != 0 ) + { + entry_name = entry.name; + } + else + { + entry_name = make_fourcc( " " ); + } + + /* Print out the ascii representation of type and name, as well as + length and file offset. */ + + cout << hex << setfill( '0' ) << "type: " + << ((char *)&entry.type)[0] + << ((char *)&entry.type)[1] + << ((char *)&entry.type)[2] + << ((char *)&entry.type)[3] + << " name: " + << ((char *)&entry_name)[0] + << ((char *)&entry_name)[1] + << ((char *)&entry_name)[2] + << ((char *)&entry_name)[3] + << " length: 0x" << setw( 12 ) << entry.length + << " offset: 0x" << setw( 12 ) << entry.offset + << " list: " + << ((char *)&list_name)[0] + << ((char *)&list_name)[1] + << ((char *)&list_name)[2] + << ((char *)&list_name)[3] << dec << endl; + + /* print the content itself */ + + PrintDirectoryEntryData( entry ); +} + + +/** prints the contents of the entry + + Prints a readable representation of the contents of an index. + Override this to print out any objects you store in the RIFF file. + + \param entry the entry to print */ + +void RIFFFile::PrintDirectoryEntryData( const RIFFDirEntry &entry ) const + {} + + +/** prints the contents of the whole directory + + Prints a readable representation of the contents of an index. + Override this to print out any objects you store in the RIFF file. + + \param entry the entry to print */ + +void RIFFFile::PrintDirectory() const +{ + int i; + int count = directory.size(); + + for ( i = 0; i < count; ++i ) + PrintDirectoryEntry( i ); +} + + +/** finds the index + + finds the index of a given directory entry type + + \todo inefficient if the directory has lots of items + \param type the type of the entry to find + \param n the zero-based instance of type to locate + \return the index of the found object in the directory, or -1 if not found */ + +int RIFFFile::FindDirectoryEntry ( FOURCC type, int n ) const +{ + int i, j = 0; + int count = directory.size(); + + for ( i = 0; i < count; ++i ) + if ( directory[ i ].type == type ) + { + if ( j == n ) + return i; + j++; + } + + return -1; +} + + +/** Reads all items that are contained in one list + + Read in one chunk and add it to the directory. If the chunk + happens to be of type LIST, then call ParseList recursively for + it. + + \param parent The id of the item to process +*/ + +void RIFFFile::ParseChunk( int parent ) +{ + FOURCC type; + DWORD length; + int typesize; + + /* Check whether it is a LIST. If so, let ParseList deal with it */ + + read( fd, &type, sizeof( type ) ); + if ( type == make_fourcc( "LIST" ) ) + { + typesize = -sizeof( type ); + fail_if( lseek( fd, typesize, SEEK_CUR ) == ( off_t ) - 1 ); + ParseList( parent ); + } + + /* it is a normal chunk, create a new directory entry for it */ + + else + { + fail_neg( read( fd, &length, sizeof( length ) ) ); + if ( length & 1 ) + length++; + AddDirectoryEntry( type, 0, length, parent ); + fail_if( lseek( fd, length, SEEK_CUR ) == ( off_t ) - 1 ); + } +} + + +/** Reads all items that are contained in one list + + \param parent The id of the list to process + +*/ + +void RIFFFile::ParseList( int parent ) +{ + FOURCC type; + FOURCC name; + int list; + DWORD length; + off_t pos; + off_t listEnd; + + /* Read in the chunk header (type and length). */ + fail_neg( read( fd, &type, sizeof( type ) ) ); + fail_neg( read( fd, &length, sizeof( length ) ) ); + + if ( length & 1 ) + length++; + + /* The contents of the list starts here. Obtain its offset. The list + name (4 bytes) is already part of the contents). */ + + pos = lseek( fd, 0, SEEK_CUR ); + fail_if( pos == ( off_t ) - 1 ); + fail_neg( read( fd, &name, sizeof( name ) ) ); + + /* Add an entry for this list. */ + + list = AddDirectoryEntry( type, name, sizeof( name ), parent ); + + /* Read in any chunks contained in this list. This list is the + parent for all chunks it contains. */ + + listEnd = pos + length; + while ( pos < listEnd ) + { + ParseChunk( list ); + pos = lseek( fd, 0, SEEK_CUR ); + fail_if( pos == ( off_t ) - 1 ); + } +} + + +/** Reads the directory structure of the whole RIFF file + +*/ + +void RIFFFile::ParseRIFF( void ) +{ + FOURCC type; + DWORD length; + off_t filesize; + off_t pos; + int container = AddDirectoryEntry( make_fourcc( "FILE" ), make_fourcc( "FILE" ), 0, RIFF_NO_PARENT ); + + pos = lseek( fd, 0, SEEK_SET ); + + /* calculate file size from RIFF header instead from physical file. */ + + while ( ( read( fd, &type, sizeof( type ) ) > 0 ) && + ( read( fd, &length, sizeof( length ) ) > 0 ) && + ( type == make_fourcc( "RIFF" ) ) ) + { + + filesize += length + RIFF_HEADERSIZE; + + fail_if( lseek( fd, pos, SEEK_SET ) == ( off_t ) - 1 ); + ParseList( container ); + pos = lseek( fd, 0, SEEK_CUR ); + fail_if( pos == ( off_t ) - 1 ); + } +} + + +/** Reads one item including its contents from the RIFF file + + \param chunk_index The index of the item to write + \param data A pointer to the data + +*/ + +void RIFFFile::ReadChunk( int chunk_index, void *data ) +{ + RIFFDirEntry entry; + + entry = GetDirectoryEntry( chunk_index ); + pthread_mutex_lock( &file_mutex ); + fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 ); + fail_neg( read( fd, data, entry.length ) ); + pthread_mutex_unlock( &file_mutex ); +} + + +/** Writes one item including its contents to the RIFF file + + \param chunk_index The index of the item to write + \param data A pointer to the data + +*/ + +void RIFFFile::WriteChunk( int chunk_index, const void *data ) +{ + RIFFDirEntry entry; + + entry = GetDirectoryEntry( chunk_index ); + pthread_mutex_lock( &file_mutex ); + fail_if( lseek( fd, entry.offset - RIFF_HEADERSIZE, SEEK_SET ) == ( off_t ) - 1 ); + fail_neg( write( fd, &entry.type, sizeof( entry.type ) ) ); + DWORD length = entry.length; + fail_neg( write( fd, &length, sizeof( length ) ) ); + fail_neg( write( fd, data, entry.length ) ); + pthread_mutex_unlock( &file_mutex ); + + /* Remember that this entry already has been written. */ + + directory[ chunk_index ].written = true; +} + + +/** Writes out the directory structure + + For all items in the directory list that have not been written + yet, it seeks to the file position where that item should be + stored and writes the type and length field. If the item has a + name, it will also write the name field. + + \note It does not write the contents of any item. Use WriteChunk to do that. */ + +void RIFFFile::WriteRIFF( void ) +{ + int i; + RIFFDirEntry entry; + int count = directory.size(); + + /* Start at the second entry (RIFF), since the first entry (FILE) + is needed only for internal purposes and is not written to the + file. */ + + for ( i = 1; i < count; ++i ) + { + + /* Only deal with entries that haven´t been written */ + + entry = GetDirectoryEntry( i ); + if ( entry.written == false ) + { + + /* A chunk entry consist of its type and length, a list + entry has an additional name. Look up the entry, seek + to the start of the header, which is at the offset of + the data start minus the header size and write out the + items. */ + + fail_if( lseek( fd, entry.offset - RIFF_HEADERSIZE, SEEK_SET ) == ( off_t ) - 1 ) ; + fail_neg( write( fd, &entry.type, sizeof( entry.type ) ) ); + DWORD length = entry.length; + fail_neg( write( fd, &length, sizeof( length ) ) ); + + /* If it has a name, it is a list. Write out the extra name + field. */ + + if ( entry.name != 0 ) + { + fail_neg( write( fd, &entry.name, sizeof( entry.name ) ) ); + } + + /* Remember that this entry already has been written. */ + + directory[ i ].written = true; + } + } +}