]> git.sesse.net Git - mlt/blob - src/modules/kino/riff.cc
Avoid unnecessary compilation when running "./configure; make; make install" multiple...
[mlt] / src / modules / kino / riff.cc
1 /*
2 * riff.cc library for RIFF file format i/o
3 * Copyright (C) 2000 - 2002 Arne Schirmacher <arne@schirmacher.de>
4 *
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.
9 *
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.
14 *
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.
18 */
19
20 // C++ includes
21
22 #include <string> 
23 //#include <stdio.h>
24 #include <iostream>
25 #include <iomanip>
26
27 using std::cout;
28 using std::hex;
29 using std::dec;
30 using std::setw;
31 using std::setfill;
32 using std::endl;
33
34 // C includes
35
36 #include <fcntl.h>
37 #include <unistd.h>
38 #include <assert.h>
39
40 // local includes
41
42 #include "error.h"
43 #include "riff.h"
44
45
46 /** make a 32 bit "string-id"
47  
48     \param s a pointer to 4 chars
49     \return the 32 bit "string id"
50     \bugs It is not checked whether we really have 4 characters
51  
52     Some compilers understand constants like int id = 'ABCD'; but I
53     could not get it working on the gcc compiler so I had to use this
54     workaround. We can now use id = make_fourcc("ABCD") instead. */
55
56 FOURCC make_fourcc( const char *s )
57 {
58         if ( s[ 0 ] == 0 )
59                 return 0;
60         else
61                 return *( ( FOURCC* ) s );
62 }
63
64
65 RIFFDirEntry::RIFFDirEntry()
66 {
67         type = 0;
68         name = 0;
69         length = 0;
70         offset = 0;
71         parent = 0;
72         written = 0;
73 }
74
75
76 RIFFDirEntry::RIFFDirEntry ( FOURCC t, FOURCC n, int l, int o, int p ) : type( t ), name( n ), length( l ), offset( o ), parent( p ), written( 0 )
77 {}
78
79
80 /** Creates the object without an output file.
81  
82 */
83
84 RIFFFile::RIFFFile() : fd( -1 )
85 {
86         pthread_mutex_init( &file_mutex, NULL );
87 }
88
89
90 /* Copy constructor
91  
92    Duplicate the file descriptor
93 */
94
95 RIFFFile::RIFFFile( const RIFFFile& riff ) : fd( -1 )
96 {
97         if ( riff.fd != -1 )
98         {
99                 fd = dup( riff.fd );
100         }
101         directory = riff.directory;
102 }
103
104
105 /** Destroys the object.
106  
107     If it has an associated opened file, close it. */
108
109 RIFFFile::~RIFFFile()
110 {
111         Close();
112         pthread_mutex_destroy( &file_mutex );
113 }
114
115
116 RIFFFile& RIFFFile::operator=( const RIFFFile& riff )
117 {
118         if ( fd != riff.fd )
119         {
120                 Close();
121                 if ( riff.fd != -1 )
122                 {
123                         fd = dup( riff.fd );
124                 }
125                 directory = riff.directory;
126         }
127         return *this;
128 }
129
130
131 /** Creates or truncates the file.
132  
133     \param s the filename
134 */
135
136 bool RIFFFile::Create( const char *s )
137 {
138         fd = open( s, O_RDWR | O_NONBLOCK | O_CREAT | O_TRUNC, 00644 );
139
140         if ( fd == -1 )
141                 return false;
142         else
143                 return true;
144 }
145
146
147 /** Opens the file read only.
148  
149     \param s the filename
150 */
151
152 bool RIFFFile::Open( const char *s )
153 {
154         fd = open( s, O_RDONLY | O_NONBLOCK );
155
156         if ( fd == -1 )
157                 return false;
158         else
159                 return true;
160 }
161
162
163 /** Destroys the object.
164  
165     If it has an associated opened file, close it. */
166
167 void RIFFFile::Close()
168 {
169         if ( fd != -1 )
170         {
171                 close( fd );
172                 fd = -1;
173         }
174 }
175
176
177 /** Adds an entry to the list of containers.
178  
179     \param type the type of this entry
180     \param name the name
181     \param length the length of the data in the container
182     \param list the container in which this object is contained. 
183     \return the ID of the newly created entry
184  
185     The topmost object is not contained in any other container. Use
186     the special ID RIFF_NO_PARENT to create the topmost object. */
187
188 int RIFFFile::AddDirectoryEntry( FOURCC type, FOURCC name, off_t length, int list )
189 {
190         /* Put all parameters in an RIFFDirEntry object. The offset is
191            currently unknown. */
192
193         RIFFDirEntry entry( type, name, length, 0 /* offset */, list );
194
195         /* If the new chunk is in a list, then get the offset and size
196            of that list. The offset of this chunk is the end of the list
197            (parent_offset + parent_length) plus the size of the chunk
198            header. */
199
200         if ( list != RIFF_NO_PARENT )
201         {
202                 RIFFDirEntry parent = GetDirectoryEntry( list );
203                 entry.offset = parent.offset + parent.length + RIFF_HEADERSIZE;
204         }
205
206         /* The list which this new chunk is a member of has now increased in
207            size. Get that directory entry and bump up its length by the size
208            of the chunk. Since that list may also be contained in another
209            list, walk up to the top of the tree. */
210
211         while ( list != RIFF_NO_PARENT )
212         {
213                 RIFFDirEntry parent = GetDirectoryEntry( list );
214                 parent.length += RIFF_HEADERSIZE + length;
215                 SetDirectoryEntry( list, parent );
216                 list = parent.parent;
217         }
218
219         directory.insert( directory.end(), entry );
220
221         return directory.size() - 1;
222 }
223
224
225 /** Modifies an entry.
226  
227     \param i the ID of the entry which is to modify
228     \param type the type of this entry
229     \param name the name
230     \param length the length of the data in the container
231     \param list the container in which this object is contained.
232     \note Do not change length, offset, or the parent container.
233     \note Do not change an empty name ("") to a name and vice versa */
234
235 void RIFFFile::SetDirectoryEntry( int i, FOURCC type, FOURCC name, off_t length, off_t offset, int list )
236 {
237         RIFFDirEntry entry( type, name, length, offset, list );
238
239         assert( i >= 0 && i < ( int ) directory.size() );
240
241         directory[ i ] = entry;
242 }
243
244
245 /** Modifies an entry.
246  
247     The entry.written flag is set to false because the contents has been modified
248  
249     \param i the ID of the entry which is to modify
250     \param entry the new entry 
251     \note Do not change length, offset, or the parent container.
252     \note Do not change an empty name ("") to a name and vice versa */
253
254 void RIFFFile::SetDirectoryEntry( int i, RIFFDirEntry &entry )
255 {
256         assert( i >= 0 && i < ( int ) directory.size() );
257
258         entry.written = false;
259         directory[ i ] = entry;
260 }
261
262
263 /** Retrieves an entry.
264  
265     Gets the most important member variables.
266  
267     \param i the ID of the entry to retrieve
268     \param type
269     \param name
270     \param length
271     \param offset
272     \param list */
273
274 void RIFFFile::GetDirectoryEntry( int i, FOURCC &type, FOURCC &name, off_t &length, off_t &offset, int &list ) const
275 {
276         RIFFDirEntry entry;
277
278         assert( i >= 0 && i < ( int ) directory.size() );
279
280         entry = directory[ i ];
281         type = entry.type;
282         name = entry.name;
283         length = entry.length;
284         offset = entry.offset;
285         list = entry.parent;
286 }
287
288
289 /** Retrieves an entry.
290  
291     Gets the whole RIFFDirEntry object.
292  
293     \param i the ID of the entry to retrieve
294     \return the entry */
295
296 RIFFDirEntry RIFFFile::GetDirectoryEntry( int i ) const
297 {
298         assert( i >= 0 && i < ( int ) directory.size() );
299
300         return directory[ i ];
301 }
302
303
304 /** Calculates the total size of the file
305  
306     \return the size the file in bytes
307 */
308
309 off_t RIFFFile::GetFileSize( void ) const
310 {
311
312         /* If we have at least one entry, return the length field
313            of the FILE entry, which is the length of its contents,
314            which is the actual size of whatever is currently in the
315            AVI directory structure. 
316
317            Note that the first entry does not belong to the AVI
318            file.
319
320            If we don't have any entry, the file size is zero. */
321
322         if ( directory.size() > 0 )
323                 return directory[ 0 ].length;
324         else
325                 return 0;
326 }
327
328
329 /** prints the attributes of the entry
330  
331     \param i the ID of the entry to print
332 */
333
334 void RIFFFile::PrintDirectoryEntry ( int i ) const
335 {
336         RIFFDirEntry entry;
337         RIFFDirEntry parent;
338         FOURCC entry_name;
339         FOURCC list_name;
340
341         /* Get all attributes of the chunk object. If it is contained
342            in a list, get the name of the list too (otherwise the name of
343            the list is blank). If the chunk object doesnĀ“t have a name (only
344            LISTs and RIFFs have a name), the name is blank. */
345
346         entry = GetDirectoryEntry( i );
347         if ( entry.parent != RIFF_NO_PARENT )
348         {
349                 parent = GetDirectoryEntry( entry.parent );
350                 list_name = parent.name;
351         }
352         else
353         {
354                 list_name = make_fourcc( "    " );
355         }
356         if ( entry.name != 0 )
357         {
358                 entry_name = entry.name;
359         }
360         else
361         {
362                 entry_name = make_fourcc( "    " );
363         }
364
365         /* Print out the ascii representation of type and name, as well as
366            length and file offset. */
367
368         cout << hex << setfill( '0' ) << "type: "
369         << ((char *)&entry.type)[0]
370         << ((char *)&entry.type)[1]
371         << ((char *)&entry.type)[2]
372         << ((char *)&entry.type)[3]
373         << " name: "
374         << ((char *)&entry_name)[0]
375         << ((char *)&entry_name)[1]
376         << ((char *)&entry_name)[2]
377         << ((char *)&entry_name)[3]
378         << " length: 0x" << setw( 12 ) << entry.length
379         << " offset: 0x" << setw( 12 ) << entry.offset
380         << " list: "
381         << ((char *)&list_name)[0]
382         << ((char *)&list_name)[1]
383         << ((char *)&list_name)[2]
384         << ((char *)&list_name)[3] << dec << endl;
385
386         /* print the content itself */
387
388         PrintDirectoryEntryData( entry );
389 }
390
391
392 /** prints the contents of the entry
393  
394     Prints a readable representation of the contents of an index.
395     Override this to print out any objects you store in the RIFF file.
396  
397     \param entry the entry to print */
398
399 void RIFFFile::PrintDirectoryEntryData( const RIFFDirEntry &entry ) const
400         {}
401
402
403 /** prints the contents of the whole directory
404  
405     Prints a readable representation of the contents of an index.
406     Override this to print out any objects you store in the RIFF file.
407  
408     \param entry the entry to print */
409
410 void RIFFFile::PrintDirectory() const
411 {
412         int i;
413         int count = directory.size();
414
415         for ( i = 0; i < count; ++i )
416                 PrintDirectoryEntry( i );
417 }
418
419
420 /** finds the index
421  
422     finds the index of a given directory entry type 
423  
424     \todo inefficient if the directory has lots of items
425     \param type the type of the entry to find
426     \param n    the zero-based instance of type to locate
427     \return the index of the found object in the directory, or -1 if not found */
428
429 int RIFFFile::FindDirectoryEntry ( FOURCC type, int n ) const
430 {
431         int i, j = 0;
432         int count = directory.size();
433
434         for ( i = 0; i < count; ++i )
435                 if ( directory[ i ].type == type )
436                 {
437                         if ( j == n )
438                                 return i;
439                         j++;
440                 }
441
442         return -1;
443 }
444
445
446 /** Reads all items that are contained in one list
447  
448     Read in one chunk and add it to the directory. If the chunk
449     happens to be of type LIST, then call ParseList recursively for
450     it.
451  
452     \param parent The id of the item to process
453 */
454
455 void RIFFFile::ParseChunk( int parent )
456 {
457         FOURCC type;
458         DWORD length;
459         int typesize;
460
461         /* Check whether it is a LIST. If so, let ParseList deal with it */
462
463         fail_if( read( fd, &type, sizeof( type ) ) != sizeof( type ));
464         if ( type == make_fourcc( "LIST" ) )
465         {
466                 typesize = (int) -sizeof( type );
467                 fail_if( lseek( fd, typesize, SEEK_CUR ) == ( off_t ) - 1 );
468                 ParseList( parent );
469         }
470
471         /* it is a normal chunk, create a new directory entry for it */
472
473         else
474         {
475                 fail_neg( read( fd, &length, sizeof( length ) ) );
476                 if ( length & 1 )
477                         length++;
478                 AddDirectoryEntry( type, 0, length, parent );
479                 fail_if( lseek( fd, length, SEEK_CUR ) == ( off_t ) - 1 );
480         }
481 }
482
483
484 /** Reads all items that are contained in one list
485  
486     \param parent The id of the list to process
487  
488 */
489
490 void RIFFFile::ParseList( int parent )
491 {
492         FOURCC type;
493         FOURCC name;
494         int list;
495         DWORD length;
496         off_t pos;
497         off_t   listEnd;
498
499         /* Read in the chunk header (type and length). */
500         fail_neg( read( fd, &type, sizeof( type ) ) );
501         fail_neg( read( fd, &length, sizeof( length ) ) );
502
503         if ( length & 1 )
504                 length++;
505
506         /* The contents of the list starts here. Obtain its offset. The list
507            name (4 bytes) is already part of the contents). */
508
509         pos = lseek( fd, 0, SEEK_CUR );
510         fail_if( pos == ( off_t ) - 1 );
511         fail_neg( read( fd, &name, sizeof( name ) ) );
512
513         /* Add an entry for this list. */
514
515         list = AddDirectoryEntry( type, name, sizeof( name ), parent );
516
517         /* Read in any chunks contained in this list. This list is the
518            parent for all chunks it contains. */
519
520         listEnd = pos + length;
521         while ( pos < listEnd )
522         {
523                 ParseChunk( list );
524                 pos = lseek( fd, 0, SEEK_CUR );
525                 fail_if( pos == ( off_t ) - 1 );
526         }
527 }
528
529
530 /** Reads the directory structure of the whole RIFF file
531  
532 */
533
534 void RIFFFile::ParseRIFF( void )
535 {
536         FOURCC type;
537         DWORD length;
538         off_t filesize = 0;
539         off_t pos;
540         int container = AddDirectoryEntry( make_fourcc( "FILE" ), make_fourcc( "FILE" ), 0, RIFF_NO_PARENT );
541
542         pos = lseek( fd, 0, SEEK_SET );
543         fail_if( pos == -1 );
544
545         /* calculate file size from RIFF header instead from physical file. */
546
547         while ( ( read( fd, &type, sizeof( type ) ) > 0 ) &&
548                 ( read( fd, &length, sizeof( length ) ) > 0 ) &&
549                 ( type == make_fourcc( "RIFF" ) ) )
550         {
551
552                 filesize += length + RIFF_HEADERSIZE;
553
554                 fail_if( lseek( fd, pos, SEEK_SET ) == ( off_t ) - 1 );
555                 ParseList( container );
556                 pos = lseek( fd, 0, SEEK_CUR );
557                 fail_if( pos == ( off_t ) - 1 );
558         }
559 }
560
561
562 /** Reads one item including its contents from the RIFF file
563  
564     \param chunk_index The index of the item to write
565     \param data A pointer to the data
566  
567 */
568
569 void RIFFFile::ReadChunk( int chunk_index, void *data, off_t data_len )
570 {
571         RIFFDirEntry entry;
572
573         entry = GetDirectoryEntry( chunk_index );
574         pthread_mutex_lock( &file_mutex );
575         fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 );
576         fail_neg( read( fd, data, entry.length > data_len ? data_len : entry.length ) );
577         pthread_mutex_unlock( &file_mutex );
578 }
579
580
581 /** Writes one item including its contents to the RIFF file
582  
583     \param chunk_index The index of the item to write
584     \param data A pointer to the data
585  
586 */
587
588 void RIFFFile::WriteChunk( int chunk_index, const void *data )
589 {
590         RIFFDirEntry entry;
591
592         entry = GetDirectoryEntry( chunk_index );
593         pthread_mutex_lock( &file_mutex );
594         fail_if( lseek( fd, entry.offset - RIFF_HEADERSIZE, SEEK_SET ) == ( off_t ) - 1 );
595         fail_neg( write( fd, &entry.type, sizeof( entry.type ) ) );
596         DWORD length = entry.length;
597         fail_neg( write( fd, &length, sizeof( length ) ) );
598         fail_neg( write( fd, data, entry.length ) );
599         pthread_mutex_unlock( &file_mutex );
600
601         /* Remember that this entry already has been written. */
602
603         directory[ chunk_index ].written = true;
604 }
605
606
607 /** Writes out the directory structure
608  
609     For all items in the directory list that have not been written
610     yet, it seeks to the file position where that item should be
611     stored and writes the type and length field. If the item has a
612     name, it will also write the name field.
613  
614     \note It does not write the contents of any item. Use WriteChunk to do that. */
615
616 void RIFFFile::WriteRIFF( void )
617 {
618         int i;
619         RIFFDirEntry entry;
620         int count = directory.size();
621
622         /* Start at the second entry (RIFF), since the first entry (FILE)
623            is needed only for internal purposes and is not written to the
624            file. */
625
626         for ( i = 1; i < count; ++i )
627         {
628
629                 /* Only deal with entries that havenĀ“t been written */
630
631                 entry = GetDirectoryEntry( i );
632                 if ( entry.written == false )
633                 {
634
635                         /* A chunk entry consist of its type and length, a list
636                            entry has an additional name. Look up the entry, seek
637                            to the start of the header, which is at the offset of
638                            the data start minus the header size and write out the
639                            items. */
640
641                         fail_if( lseek( fd, entry.offset - RIFF_HEADERSIZE, SEEK_SET ) == ( off_t ) - 1 ) ;
642                         fail_neg( write( fd, &entry.type, sizeof( entry.type ) ) );
643                         DWORD length = entry.length;
644                         fail_neg( write( fd, &length, sizeof( length ) ) );
645
646                         /* If it has a name, it is a list. Write out the extra name
647                            field. */
648
649                         if ( entry.name != 0 )
650                         {
651                                 fail_neg( write( fd, &entry.name, sizeof( entry.name ) ) );
652                         }
653
654                         /* Remember that this entry already has been written. */
655
656                         directory[ i ].written = true;
657                 }
658         }
659 }