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