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