Initial version
[melted] / 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 }