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