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