Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Namespace Members | Class Members | File Members | Related Pages

riff.cc

Go to the documentation of this file.
00001 /*
00002 * riff.cc library for RIFF file format i/o
00003 * Copyright (C) 2000 - 2002 Arne Schirmacher <arne@schirmacher.de>
00004 * Copyright (C) 2001-2007 Dan Dennedy <dan@dennedy.org>
00005 *
00006 * This program is free software; you can redistribute it and/or modify
00007 * it under the terms of the GNU General Public License as published by
00008 * the Free Software Foundation; either version 2 of the License, or
00009 * (at your option) any later version.
00010 *
00011 * This program is distributed in the hope that it will be useful,
00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014 * GNU General Public License for more details.
00015 *
00016 * You should have received a copy of the GNU General Public License
00017 * along with this program; if not, write to the Free Software Foundation,
00018 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00019 */
00020 
00021 #ifdef HAVE_CONFIG_H
00022 #include <config.h>
00023 #endif
00024 
00025 // C++ includes
00026 
00027 #include <string> 
00028 //#include <stdio.h>
00029 #include <iostream>
00030 #include <iomanip>
00031 #include <byteswap.h>
00032 
00033 using std::cout;
00034 using std::cerr;
00035 using std::hex;
00036 using std::dec;
00037 using std::setw;
00038 using std::setfill;
00039 using std::endl;
00040 
00041 // C includes
00042 
00043 #include <fcntl.h>
00044 #include <unistd.h>
00045 #include <assert.h>
00046 
00047 // local includes
00048 
00049 #include "error.h"
00050 #include "riff.h"
00051 
00052 
00063 FOURCC make_fourcc( char *s )
00064 {
00065     if ( s[ 0 ] == 0 )
00066         return 0;
00067     else
00068         return *( ( FOURCC* ) s );
00069 }
00070 
00071 
00072 RIFFDirEntry::RIFFDirEntry()
00073 {}
00074 
00075 
00076 RIFFDirEntry::RIFFDirEntry ( FOURCC t, FOURCC n, int l, int o, int p ) : type( t ), name( n ), length( l ), offset( o ), parent( p ), written( 0 )
00077 {}
00078 
00079 
00084 RIFFFile::RIFFFile() : fd( -1 )
00085 {
00086     pthread_mutex_init( &file_mutex, NULL );
00087 }
00088 
00089 
00090 /* Copy constructor
00091  
00092    Duplicate the file descriptor
00093 */
00094 
00095 RIFFFile::RIFFFile( const RIFFFile& riff ) : fd( -1 )
00096 {
00097     if ( riff.fd != -1 )
00098     {
00099         fd = dup( riff.fd );
00100     }
00101     directory = riff.directory;
00102 }
00103 
00104 
00109 RIFFFile::~RIFFFile()
00110 {
00111     Close();
00112     pthread_mutex_destroy( &file_mutex );
00113 }
00114 
00115 
00116 RIFFFile& RIFFFile::operator=( const RIFFFile& riff )
00117 {
00118     if ( fd != riff.fd )
00119     {
00120         Close();
00121         if ( riff.fd != -1 )
00122         {
00123             fd = dup( riff.fd );
00124         }
00125         directory = riff.directory;
00126     }
00127     return *this;
00128 }
00129 
00130 
00136 bool RIFFFile::Create( const char *s )
00137 {
00138     fd = open( s, O_RDWR | O_NONBLOCK | O_CREAT | O_TRUNC, 00644 );
00139 
00140     if ( fd == -1 )
00141         return false;
00142     else
00143         return true;
00144 }
00145 
00146 
00152 bool RIFFFile::Open( const char *s )
00153 {
00154     fd = open( s, O_RDONLY | O_NONBLOCK );
00155 
00156     if ( fd == -1 )
00157         return false;
00158     else
00159         return true;
00160 }
00161 
00162 
00167 void RIFFFile::Close()
00168 {
00169     if ( fd != -1 )
00170     {
00171         close( fd );
00172         fd = -1;
00173     }
00174 }
00175 
00176 
00188 int RIFFFile::AddDirectoryEntry( FOURCC type, FOURCC name, off_t length, int list )
00189 {
00190     /* Put all parameters in an RIFFDirEntry object. The offset is
00191        currently unknown. */
00192 
00193     RIFFDirEntry entry( type, name, length, 0 /* offset */, list );
00194 
00195     /* If the new chunk is in a list, then get the offset and size
00196        of that list. The offset of this chunk is the end of the list
00197        (parent_offset + parent_length) plus the size of the chunk
00198        header. */
00199 
00200     if ( list != RIFF_NO_PARENT )
00201     {
00202         RIFFDirEntry parent = GetDirectoryEntry( list );
00203         entry.offset = parent.offset + parent.length + RIFF_HEADERSIZE;
00204     }
00205 
00206     /* The list which this new chunk is a member of has now increased in
00207        size. Get that directory entry and bump up its length by the size
00208        of the chunk. Since that list may also be contained in another
00209        list, walk up to the top of the tree. */
00210 
00211     while ( list != RIFF_NO_PARENT )
00212     {
00213         RIFFDirEntry parent = GetDirectoryEntry( list );
00214         parent.length += RIFF_HEADERSIZE + length;
00215         SetDirectoryEntry( list, parent );
00216         list = parent.parent;
00217     }
00218 
00219     directory.insert( directory.end(), entry );
00220 
00221     return directory.size() - 1;
00222 }
00223 
00224 
00235 void RIFFFile::SetDirectoryEntry( int i, FOURCC type, FOURCC name, off_t length, off_t offset, int list )
00236 {
00237     RIFFDirEntry entry( type, name, length, offset, list );
00238 
00239     assert( i >= 0 && i < ( int ) directory.size() );
00240 
00241     directory[ i ] = entry;
00242 }
00243 
00244 
00254 void RIFFFile::SetDirectoryEntry( int i, RIFFDirEntry &entry )
00255 {
00256     assert( i >= 0 && i < ( int ) directory.size() );
00257 
00258     entry.written = false;
00259     directory[ i ] = entry;
00260 }
00261 
00262 
00274 void RIFFFile::GetDirectoryEntry( int i, FOURCC &type, FOURCC &name, off_t &length, off_t &offset, int &list ) const
00275 {
00276     RIFFDirEntry entry;
00277 
00278     assert( i >= 0 && i < ( int ) directory.size() );
00279 
00280     entry = directory[ i ];
00281     type = entry.type;
00282     name = entry.name;
00283     length = entry.length;
00284     offset = entry.offset;
00285     list = entry.parent;
00286 }
00287 
00288 
00296 RIFFDirEntry RIFFFile::GetDirectoryEntry( int i ) const
00297 {
00298     assert( i >= 0 && i < ( int ) directory.size() );
00299 
00300     return directory[ i ];
00301 }
00302 
00303 
00309 off_t RIFFFile::GetFileSize( void ) const
00310 {
00311 
00312     /* If we have at least one entry, return the length field
00313        of the FILE entry, which is the length of its contents,
00314        which is the actual size of whatever is currently in the
00315        AVI directory structure. 
00316 
00317        Note that the first entry does not belong to the AVI
00318        file.
00319 
00320        If we don't have any entry, the file size is zero. */
00321 
00322     if ( directory.size() > 0 )
00323         return directory[ 0 ].length;
00324     else
00325         return 0;
00326 }
00327 
00328 
00334 void RIFFFile::PrintDirectoryEntry ( int i ) const
00335 {
00336     RIFFDirEntry entry;
00337     RIFFDirEntry parent;
00338     FOURCC entry_name;
00339     FOURCC list_name;
00340 
00341     /* Get all attributes of the chunk object. If it is contained
00342        in a list, get the name of the list too (otherwise the name of
00343        the list is blank). If the chunk object doesn´t have a name (only
00344        LISTs and RIFFs have a name), the name is blank. */
00345 
00346     entry = GetDirectoryEntry( i );
00347     if ( entry.parent != RIFF_NO_PARENT )
00348     {
00349         parent = GetDirectoryEntry( entry.parent );
00350         list_name = parent.name;
00351     }
00352     else
00353     {
00354         list_name = make_fourcc( "    " );
00355     }
00356     if ( entry.name != 0 )
00357     {
00358         entry_name = entry.name;
00359     }
00360     else
00361     {
00362         entry_name = make_fourcc( "    " );
00363     }
00364 
00365     /* Print out the ascii representation of type and name, as well as
00366        length and file offset. */
00367 
00368     cout << hex << setfill( '0' ) << "type: "
00369     << ((char *)&entry.type)[0]
00370     << ((char *)&entry.type)[1]
00371     << ((char *)&entry.type)[2]
00372     << ((char *)&entry.type)[3]
00373     << " name: "
00374     << ((char *)&entry_name)[0]
00375     << ((char *)&entry_name)[1]
00376     << ((char *)&entry_name)[2]
00377     << ((char *)&entry_name)[3]
00378     << " length: 0x" << setw( 12 ) << entry.length
00379     << " offset: 0x" << setw( 12 ) << entry.offset
00380     << " list: "
00381     << ((char *)&list_name)[0]
00382     << ((char *)&list_name)[1]
00383     << ((char *)&list_name)[2]
00384     << ((char *)&list_name)[3] << dec << endl;
00385 
00386     /* print the content itself */
00387 
00388     PrintDirectoryEntryData( entry );
00389 }
00390 
00391 
00399 void RIFFFile::PrintDirectoryEntryData( const RIFFDirEntry &entry ) const
00400     {}
00401 
00402 
00410 void RIFFFile::PrintDirectory() const
00411 {
00412     int i;
00413     int count = directory.size();
00414 
00415     for ( i = 0; i < count; ++i )
00416         PrintDirectoryEntry( i );
00417 }
00418 
00419 
00429 int RIFFFile::FindDirectoryEntry ( FOURCC type, int n ) const
00430 {
00431     int i, j = 0;
00432     int count = directory.size();
00433 
00434     for ( i = 0; i < count; ++i )
00435         if ( directory[ i ].type == type )
00436         {
00437             if ( j == n )
00438                 return i;
00439             j++;
00440         }
00441 
00442     return -1;
00443 }
00444 
00445 
00455 void RIFFFile::ParseChunk( int parent )
00456 {
00457     FOURCC type;
00458     DWORD length;
00459     int typesize;
00460 
00461     /* Check whether it is a LIST. If so, let ParseList deal with it */
00462 
00463     read( fd, &type, sizeof( type ) );
00464     if ( type == make_fourcc( "LIST" ) )
00465     {
00466         typesize = -sizeof( type );
00467         fail_if( lseek( fd, typesize, SEEK_CUR ) == ( off_t ) - 1 );
00468         ParseList( parent );
00469     }
00470 
00471     /* it is a normal chunk, create a new directory entry for it */
00472 
00473     else
00474     {
00475         fail_neg( read( fd, &length, sizeof( length ) ) );
00476         fail_if( length == 0 );
00477         if ( length & 1 )
00478             length++;
00479         AddDirectoryEntry( type, 0, length, parent );
00480         fail_if( lseek( fd, length, SEEK_CUR ) == ( off_t ) - 1 );
00481     }
00482 }
00483 
00484 
00491 void RIFFFile::ParseList( int parent )
00492 {
00493     FOURCC type;
00494     FOURCC name;
00495     int list;
00496     DWORD length;
00497     off_t pos;
00498     off_t   listEnd;
00499 
00500     /* Read in the chunk header (type and length). */
00501     fail_neg( read( fd, &type, sizeof( type ) ) );
00502     fail_neg( read( fd, &length, sizeof( length ) ) );
00503 
00504     if ( length & 1 )
00505         length++;
00506 
00507     /* The contents of the list starts here. Obtain its offset. The list
00508        name (4 bytes) is already part of the contents). */
00509 
00510     pos = lseek( fd, 0, SEEK_CUR );
00511     fail_if( pos == ( off_t ) - 1 );
00512     fail_neg( read( fd, &name, sizeof( name ) ) );
00513 
00514     /* Add an entry for this list. */
00515 
00516     list = AddDirectoryEntry( type, name, sizeof( name ), parent );
00517 
00518     /* Read in any chunks contained in this list. This list is the
00519        parent for all chunks it contains. */
00520 
00521     listEnd = pos + length;
00522     while ( pos < listEnd )
00523     {
00524         ParseChunk( list );
00525         pos = lseek( fd, 0, SEEK_CUR );
00526         fail_if( pos == ( off_t ) - 1 );
00527     }
00528 }
00529 
00530 
00535 void RIFFFile::ParseRIFF( void )
00536 {
00537     FOURCC type;
00538     DWORD length;
00539     off_t filesize;
00540     off_t pos;
00541     int container = AddDirectoryEntry( make_fourcc( "FILE" ), make_fourcc( "FILE" ), 0, RIFF_NO_PARENT );
00542 
00543     pos = lseek( fd, 0, SEEK_SET );
00544 
00545     /* calculate file size from RIFF header instead from physical file. */
00546 
00547     while ( ( read( fd, &type, sizeof( type ) ) > 0 ) &&
00548             ( read( fd, &length, sizeof( length ) ) > 0 ) &&
00549             ( type == make_fourcc( "RIFF" ) ) )
00550     {
00551 
00552         filesize += length + RIFF_HEADERSIZE;
00553 
00554         fail_if( lseek( fd, pos, SEEK_SET ) == ( off_t ) - 1 );
00555         ParseList( container );
00556         pos = lseek( fd, 0, SEEK_CUR );
00557         fail_if( pos == ( off_t ) - 1 );
00558     }
00559 }
00560 
00561 
00569 bool RIFFFile::ReadChunk( int chunk_index, void *data, off_t data_len )
00570 {
00571     RIFFDirEntry entry;
00572 
00573     entry = GetDirectoryEntry( chunk_index );
00574 #ifdef DEBUG
00575     if ( entry.length > data_len ) {
00576         FOURCC entry_name = entry.name ? entry.name : make_fourcc( "    " );
00577 
00578         cerr << dec << "Warning: insufficient storage in RIFFFile::ReadChunk(): data_len " << data_len << " entry.length " << entry.length << endl;
00579         cerr << hex << setfill( '0' ) << "         type: "
00580         << ((char *)&entry.type)[0]
00581         << ((char *)&entry.type)[1]
00582         << ((char *)&entry.type)[2]
00583         << ((char *)&entry.type)[3]
00584         << " name: "
00585         << ((char *)&entry_name)[0]
00586         << ((char *)&entry_name)[1]
00587         << ((char *)&entry_name)[2]
00588         << ((char *)&entry_name)[3]
00589         << " length: 0x" << setw( 12 ) << entry.length
00590         << " offset: 0x" << setw( 12 ) << entry.offset
00591         << endl;
00592     }
00593 #endif
00594     pthread_mutex_lock( &file_mutex );
00595     fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 );
00596     fail_neg( read( fd, data, entry.length > data_len ? data_len : entry.length ) );
00597     pthread_mutex_unlock( &file_mutex );
00598 
00599     return ( entry.length <= data_len );
00600 }
00601 
00602 
00610 void RIFFFile::WriteChunk( int chunk_index, const void *data )
00611 {
00612     RIFFDirEntry entry;
00613 
00614     entry = GetDirectoryEntry( chunk_index );
00615     pthread_mutex_lock( &file_mutex );
00616     fail_if( lseek( fd, entry.offset - RIFF_HEADERSIZE, SEEK_SET ) == ( off_t ) - 1 );
00617     fail_neg( write( fd, &entry.type, sizeof( entry.type ) ) );
00618     DWORD length = entry.length;
00619     fail_neg( write( fd, &length, sizeof( length ) ) );
00620     fail_neg( write( fd, data, entry.length ) );
00621     pthread_mutex_unlock( &file_mutex );
00622 
00623     /* Remember that this entry already has been written. */
00624 
00625     directory[ chunk_index ].written = true;
00626 }
00627 
00628 
00638 void RIFFFile::WriteRIFF( void )
00639 {
00640     int i;
00641     RIFFDirEntry entry;
00642     int count = directory.size();
00643 
00644     /* Start at the second entry (RIFF), since the first entry (FILE)
00645        is needed only for internal purposes and is not written to the
00646        file. */
00647 
00648     for ( i = 1; i < count; ++i )
00649     {
00650 
00651         /* Only deal with entries that haven´t been written */
00652 
00653         entry = GetDirectoryEntry( i );
00654         if ( entry.written == false )
00655         {
00656 
00657             /* A chunk entry consist of its type and length, a list
00658                entry has an additional name. Look up the entry, seek
00659                to the start of the header, which is at the offset of
00660                the data start minus the header size and write out the
00661                items. */
00662 
00663             fail_if( lseek( fd, entry.offset - RIFF_HEADERSIZE, SEEK_SET ) == ( off_t ) - 1 ) ;
00664             fail_neg( write( fd, &entry.type, sizeof( entry.type ) ) );
00665             DWORD length = entry.length;
00666             fail_neg( write( fd, &length, sizeof( length ) ) );
00667 
00668             /* If it has a name, it is a list. Write out the extra name
00669                field. */
00670 
00671             if ( entry.name != 0 )
00672             {
00673                 fail_neg( write( fd, &entry.name, sizeof( entry.name ) ) );
00674             }
00675 
00676             /* Remember that this entry already has been written. */
00677 
00678             directory[ i ].written = true;
00679         }
00680     }
00681 }

Generated on Sun Mar 11 22:11:47 2007 for Kino by  doxygen 1.4.2