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