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