]> git.sesse.net Git - vlc/blob - modules/demux/avi/libavi.c
* all : forgot to add theses new files :p
[vlc] / modules / demux / avi / libavi.c
1 /*****************************************************************************
2  * libavi.c : 
3  *****************************************************************************
4  * Copyright (C) 2001 VideoLAN
5  * $Id: libavi.c,v 1.1 2002/10/15 00:56:43 fenrir Exp $
6  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
7  * 
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  * 
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
21  *****************************************************************************/
22
23 #include <stdlib.h>                                      /* malloc(), free() */
24 #include <string.h>                                              /* strdup() */
25 #include <errno.h>
26 #include <sys/types.h>
27
28 #include <vlc/vlc.h>
29 #include <vlc/input.h>
30
31 #include "libavi.h"
32
33 #define AVI_DEBUG 1
34 #define AVIFOURCC_PRINT( x ) \
35     (x)&0xff,           \
36     ( (x) >>  8 )&0xff, \
37     ( (x) >> 16 )&0xff, \
38     ( (x) >> 24 )&0xff
39     
40 #define FREE( p ) \
41     if( p ) {free( p ); p = NULL; }
42
43 #define __EVEN( x ) ( (x)&0x01 ? (x)+1 : (x) )
44   
45 /* Some functions to manipulate memory */
46 static u16 GetWLE( u8 *p_buff )
47 {
48     return( (p_buff[0]) + ( p_buff[1] <<8 ) );
49 }
50
51 static u32 GetDWLE( u8 *p_buff )
52 {
53     return( p_buff[0] + ( p_buff[1] <<8 ) +
54             ( p_buff[2] <<16 ) + ( p_buff[3] <<24 ) );
55 }
56 static vlc_fourcc_t GetFOURCC( byte_t *p_buff )
57 {
58     return( VLC_FOURCC( p_buff[0], p_buff[1], p_buff[2], p_buff[3] ) );
59 }
60 /*****************************************************************************
61  * Some basic functions to manipulate stream more easily in vlc
62  *
63  * AVI_TellAbsolute get file position
64  * 
65  * AVI_SeekAbsolute seek in the file
66  *
67  * AVI_ReadData read data from the file in a buffer
68  *
69  * AVI_SkipBytes skip bytes
70  *
71  *****************************************************************************/
72 off_t AVI_TellAbsolute( input_thread_t *p_input )
73 {
74     off_t i_pos;
75     
76     vlc_mutex_lock( &p_input->stream.stream_lock );
77     
78     i_pos= p_input->stream.p_selected_area->i_tell -
79             ( p_input->p_last_data - p_input->p_current_data  );
80
81     vlc_mutex_unlock( &p_input->stream.stream_lock );
82
83     return( i_pos );
84 }
85  
86 int AVI_SeekAbsolute( input_thread_t *p_input,
87                       off_t i_pos)
88 {
89     off_t i_filepos;
90
91     if( i_pos >= p_input->stream.p_selected_area->i_size )
92     {
93         return( 0 );
94     }
95     
96     i_filepos = AVI_TellAbsolute( p_input );
97
98     if( i_filepos == i_pos )
99     {
100         return( 1 );
101     }
102
103     if( p_input->stream.b_seekable &&
104         p_input->stream.i_method == INPUT_METHOD_FILE )
105     {
106         p_input->pf_seek( p_input, i_pos );
107         input_AccessReinit( p_input );
108         return( 1 );
109     }
110     else
111     {
112         int i_peek;
113         int i_skip = i_pos - i_filepos;
114         u8  *p_peek;
115         msg_Warn( p_input, "will skip %d bytes, slow", i_skip );
116         if( i_skip < 0 )
117         {
118             return( 0 ); // failed
119         }
120         while( i_skip > 0 )
121         {
122             i_peek = input_Peek( p_input, &p_peek, i_skip+1 );
123             i_peek--;
124             i_skip -= i_peek;
125             vlc_mutex_lock( &p_input->stream.stream_lock );
126             p_input->p_current_data += i_peek;  // skip them
127             vlc_mutex_unlock( &p_input->stream.stream_lock );
128             if( i_peek <= 0 )
129             {
130                 return( 0);
131             }
132         }
133         return( 1 );
134     }
135 }
136
137 /* return 1 if success, 0 if fail */
138 int AVI_ReadData( input_thread_t *p_input, u8 *p_buff, int i_size )
139 {
140     data_packet_t *p_data;
141
142     int i_count;
143     int i_read = 0;
144
145     
146     if( !i_size )
147     {
148         return( 0 );
149     }
150
151     do
152     {
153         i_count = input_SplitBuffer(p_input, &p_data, __MIN( i_size, 1024 ) );
154         if( i_count <= 0 )
155         {
156             return( i_read );
157         }
158         memcpy( p_buff, p_data->p_payload_start, i_count );
159         input_DeletePacket( p_input->p_method_data, p_data );
160         
161         p_buff += i_count;
162         i_size -= i_count;
163         i_read += i_count;
164                 
165     } while( i_size );
166     
167     return( i_read );
168 }
169
170 int  AVI_SkipBytes( input_thread_t *p_input, int i_count )
171 {
172     int i_buff_size;
173     vlc_mutex_lock( &p_input->stream.stream_lock );
174     i_buff_size = p_input->p_last_data - p_input->p_current_data;
175     vlc_mutex_unlock( &p_input->stream.stream_lock );
176             
177     if( i_count > 0 && i_count + 1 < i_buff_size )
178     {
179         u8 *p_peek;
180         
181         input_Peek( p_input, &p_peek, i_count + 1 );
182
183         vlc_mutex_lock( &p_input->stream.stream_lock );
184         p_input->p_current_data += i_count;  // skip them
185         vlc_mutex_unlock( &p_input->stream.stream_lock );
186         return( 1 );
187     }
188     else
189     {
190         return( AVI_SeekAbsolute( p_input, 
191                               AVI_TellAbsolute( p_input ) + i_count ) );
192     }
193 }
194
195 /*****************************************************************************
196  *
197  * AVI_TestFile: look at first bytes to see if it's a valid avi file
198  * 
199  * unseekable: ok
200  *
201  *****************************************************************************/
202 int AVI_TestFile( input_thread_t *p_input )
203 {
204     u8  *p_peek;
205     
206     if( input_Peek( p_input, &p_peek, 8 ) < 8 )
207     {
208         msg_Err( p_input, "cannot peek()" );
209         return( 0 );
210     }
211
212     if( GetDWLE( p_peek ) == AVIFOURCC_RIFF && 
213         GetDWLE( p_peek + 8 ) == AVIFOURCC_AVI )
214     {
215         return( 1 );
216     }
217     else
218     {
219         return( 0 );
220     }
221 }
222 /****************************************************************************
223  *
224  * Basics functions to manipulates chunks
225  *
226  ****************************************************************************/
227 static int AVI_ChunkReadCommon( input_thread_t *p_input,
228                                 avi_chunk_t *p_chk )
229 {
230     u8  *p_peek;
231     int i_peek;
232
233     memset( p_chk, 0, sizeof( avi_chunk_t ) );
234
235     if( ( i_peek = input_Peek( p_input, &p_peek, 8 ) ) < 8 )
236     {
237         return( 0 );
238     }
239     
240     p_chk->common.i_chunk_fourcc = GetDWLE( p_peek );
241     p_chk->common.i_chunk_size   = GetDWLE( p_peek + 4 );
242     p_chk->common.i_chunk_pos    = AVI_TellAbsolute( p_input );
243
244     p_chk->common.p_father = NULL;
245     p_chk->common.p_next = NULL;
246     p_chk->common.p_first = NULL;
247     p_chk->common.p_next = NULL;
248 #ifdef AVI_DEBUG
249     msg_Dbg( p_input, 
250              "Found Chunk fourcc:%c%c%c%c size:%lld pos:%lld",
251              AVIFOURCC_PRINT( p_chk->common.i_chunk_fourcc ),
252              p_chk->common.i_chunk_size,
253              p_chk->common.i_chunk_pos );
254 #endif
255     return( 1 );
256 }
257
258 static int AVI_NextChunk( input_thread_t *p_input,
259                           avi_chunk_t *p_chk )
260 {
261     avi_chunk_t chk;
262     
263     if( !p_chk )
264     {
265         if( !AVI_ChunkReadCommon( p_input, &chk ) )
266         {
267             return( 0 );
268         }
269         p_chk = &chk;
270     }
271
272     if( p_chk->common.p_father )
273     {
274         if( p_chk->common.p_father->common.i_chunk_pos + 
275                 __EVEN( p_chk->common.p_father->common.i_chunk_size ) + 8 <
276             p_chk->common.i_chunk_pos + 
277                 __EVEN( p_chk->common.i_chunk_size ) + 8 )
278         {
279             return( 0 );
280         }
281     }
282     return( AVI_SeekAbsolute( p_input,
283                               p_chk->common.i_chunk_pos + 
284                                   __EVEN( p_chk->common.i_chunk_size ) + 8 ) );
285 }
286
287 int _AVI_ChunkGoto( input_thread_t *p_input,
288                    avi_chunk_t *p_chk )
289 {
290     if( !p_chk )
291     {
292         return( 0 );
293     }
294     return( AVI_SeekAbsolute( p_input, p_chk->common.i_chunk_pos ) );
295 }
296
297 /****************************************************************************
298  *
299  * Functions to read chunks 
300  *
301  ****************************************************************************/
302 static int AVI_ChunkRead_list( input_thread_t *p_input,
303                                avi_chunk_t *p_container,
304                                int b_seekable )
305 {
306     avi_chunk_t *p_chk;
307     u8 *p_peek;
308     
309     if( p_container->common.i_chunk_size < 8 )
310     {
311         /* empty box */
312         msg_Warn( p_input, "empty list chunk" );
313         return( 0 );
314     }
315     if( input_Peek( p_input, &p_peek, 12 ) < 12 )
316     {
317         msg_Warn( p_input, "cannot peek while reading list chunk" );
318         return( 0 );
319     }
320     p_container->list.i_type = GetDWLE( p_peek + 8 );
321
322     if( p_container->common.i_chunk_fourcc == AVIFOURCC_LIST &&
323         p_container->list.i_type == AVIFOURCC_movi )
324     {
325         msg_Dbg( p_input, "Skipping movi chunk" );
326         if( b_seekable )
327         {
328             return( AVI_NextChunk( p_input, p_container ) );
329         }
330         else
331         {
332             return( 1 ); // point at begining of LIST-movi 
333         }
334     }
335
336     AVI_SkipBytes( p_input, 12 );
337 #ifdef AVI_DEBUG
338     msg_Dbg( p_input, 
339              "found LIST chunk: \'%c%c%c%c\'",
340              AVIFOURCC_PRINT( p_container->list.i_type ) );
341 #endif
342     for( ; ; )
343     {
344         p_chk = malloc( sizeof( avi_chunk_t ) );
345         memset( p_chk, 0, sizeof( avi_chunk_t ) );
346         if( !p_container->common.p_first )
347         {
348             p_container->common.p_first = p_chk;
349         }
350         else
351         {
352             p_container->common.p_last->common.p_next = p_chk;
353         }
354         p_container->common.p_last = p_chk;
355
356         if( !AVI_ChunkRead( p_input, p_chk, p_container, b_seekable ) ||
357            ( AVI_TellAbsolute( p_input ) >=
358                 p_chk->common.p_father->common.i_chunk_pos + 
359                     __EVEN( p_chk->common.p_father->common.i_chunk_size ) ) )
360         {
361             break;
362         }
363         /* If we can't seek then stop when we 've found LIST-movi */
364         if( p_chk->common.i_chunk_fourcc == AVIFOURCC_LIST &&
365             p_chk->list.i_type == AVIFOURCC_movi && !b_seekable )
366         {
367             break;
368         }
369
370     } 
371     
372     return( 1 );
373 }
374
375 #define AVI_READCHUNK_ENTER \
376     s64 i_read = __EVEN(p_chk->common.i_chunk_size ) + 8; \
377     u8  *p_read, *p_buff;    \
378     if( !( p_read = p_buff = malloc(i_read ) ) ) \
379     { \
380         return( 0 ); \
381     } \
382     i_read = AVI_ReadData( p_input, p_read, i_read ); \
383     p_read += 8; \
384     i_read -= 8
385     
386 #define AVI_READCHUNK_EXIT( code ) \
387     free( p_buff ); \
388     if( i_read < 0 ) \
389     { \
390         msg_Warn( p_input, "not enougth data" ); \
391     } \
392     return( code )
393 #define AVI_READ2BYTES( i_word ) \
394     i_word = GetWLE( p_read ); \
395     p_read += 2; \
396     i_read -= 2
397
398 #define AVI_READ4BYTES( i_dword ) \
399     i_dword = GetDWLE( p_read ); \
400     p_read += 4; \
401     i_read -= 4
402     
403 #define AVI_READFOURCC( i_dword ) \
404     i_dword = GetFOURCC( p_read ); \
405     p_read += 4; \
406     i_read -= 4
407
408 static int AVI_ChunkRead_avih( input_thread_t *p_input,
409                                avi_chunk_t *p_chk,
410                                int b_seekable )
411 {
412     AVI_READCHUNK_ENTER;
413
414     AVI_READ4BYTES( p_chk->avih.i_microsecperframe);
415     AVI_READ4BYTES( p_chk->avih.i_maxbytespersec );
416     AVI_READ4BYTES( p_chk->avih.i_reserved1 );
417     AVI_READ4BYTES( p_chk->avih.i_flags );
418     AVI_READ4BYTES( p_chk->avih.i_totalframes );
419     AVI_READ4BYTES( p_chk->avih.i_initialframes );
420     AVI_READ4BYTES( p_chk->avih.i_streams );
421     AVI_READ4BYTES( p_chk->avih.i_suggestedbuffersize );
422     AVI_READ4BYTES( p_chk->avih.i_width );
423     AVI_READ4BYTES( p_chk->avih.i_height );
424     AVI_READ4BYTES( p_chk->avih.i_scale );
425     AVI_READ4BYTES( p_chk->avih.i_rate );
426     AVI_READ4BYTES( p_chk->avih.i_start );
427     AVI_READ4BYTES( p_chk->avih.i_length );
428 #ifdef AVI_DEBUG
429     msg_Dbg( p_input, 
430              "avih: streams:%d flags:%s%s%s%s %dx%d", 
431              p_chk->avih.i_streams,
432              p_chk->avih.i_flags&AVIF_HASINDEX?" HAS_INDEX":"",
433              p_chk->avih.i_flags&AVIF_MUSTUSEINDEX?" MUST_USE_INDEX":"",
434              p_chk->avih.i_flags&AVIF_ISINTERLEAVED?" IS_INTERLEAVED":"",
435              p_chk->avih.i_flags&AVIF_TRUSTCKTYPE?" TRUST_CKTYPE":"",
436              p_chk->avih.i_width, p_chk->avih.i_height );
437 #endif 
438     AVI_READCHUNK_EXIT( 1 );
439 }
440
441 static int AVI_ChunkRead_strh( input_thread_t *p_input,
442                                avi_chunk_t *p_chk,
443                                int b_seekable )
444 {
445     AVI_READCHUNK_ENTER;
446
447     AVI_READFOURCC( p_chk->strh.i_type );
448     AVI_READFOURCC( p_chk->strh.i_handler );
449     AVI_READ4BYTES( p_chk->strh.i_flags );
450     AVI_READ4BYTES( p_chk->strh.i_reserved1 );
451     AVI_READ4BYTES( p_chk->strh.i_initialframes );
452     AVI_READ4BYTES( p_chk->strh.i_scale );
453     AVI_READ4BYTES( p_chk->strh.i_rate );
454     AVI_READ4BYTES( p_chk->strh.i_start );
455     AVI_READ4BYTES( p_chk->strh.i_length );
456     AVI_READ4BYTES( p_chk->strh.i_suggestedbuffersize );
457     AVI_READ4BYTES( p_chk->strh.i_quality );
458     AVI_READ4BYTES( p_chk->strh.i_samplesize );
459 #ifdef AVI_DEBUG
460     msg_Dbg( p_input, 
461              "strh: type:%c%c%c%c handler:0x%8.8x samplesize:%d %.2ffps",
462              AVIFOURCC_PRINT( p_chk->strh.i_type ),
463              p_chk->strh.i_handler,
464              p_chk->strh.i_samplesize,
465              ( p_chk->strh.i_scale ? 
466                 (float)p_chk->strh.i_rate / (float)p_chk->strh.i_scale : -1) );
467 #endif
468     
469     AVI_READCHUNK_EXIT( 1 );
470 }
471
472 static int AVI_ChunkRead_strf( input_thread_t *p_input,
473                                avi_chunk_t *p_chk,
474                                int b_seekable )
475 {
476     avi_chunk_t *p_strh;
477
478     AVI_READCHUNK_ENTER;
479     if( p_chk->common.p_father == NULL )
480     {
481         msg_Err( p_input, "malformed avi file" );
482         AVI_READCHUNK_EXIT( 0 );
483     }
484     if( !( p_strh = AVI_ChunkFind( p_chk->common.p_father, AVIFOURCC_strh, 0 ) ) )
485     {
486         msg_Err( p_input, "malformed avi file" );
487         AVI_READCHUNK_EXIT( 0 );
488     }
489     
490     switch( p_strh->strh.i_type )
491     {
492         case( AVIFOURCC_auds ):
493             AVI_READ2BYTES( p_chk->strf.auds.i_formattag );
494             AVI_READ2BYTES( p_chk->strf.auds.i_channels );
495             AVI_READ4BYTES( p_chk->strf.auds.i_samplespersec );
496             AVI_READ4BYTES( p_chk->strf.auds.i_avgbytespersec );
497             AVI_READ2BYTES( p_chk->strf.auds.i_blockalign );
498             AVI_READ2BYTES( p_chk->strf.auds.i_bitspersample );
499             if( p_chk->strf.auds.i_formattag != WAVE_FORMAT_PCM )
500             {
501                 AVI_READ2BYTES( p_chk->strf.auds.i_size );
502             }
503             p_chk->strf.auds.p_wfx = malloc( p_chk->common.i_chunk_size );
504             memcpy( p_chk->strf.auds.p_wfx, 
505                     p_buff + 8, 
506                     p_chk->common.i_chunk_size );
507 #ifdef AVI_DEBUG
508             msg_Dbg( p_input, 
509                      "strf: audio:0x%4.4x channels:%d %dHz %dbits/sample %dkb/s",
510                      p_chk->strf.auds.i_formattag,
511                      p_chk->strf.auds.i_channels,
512                      p_chk->strf.auds.i_samplespersec,
513                      p_chk->strf.auds.i_bitspersample,
514                      p_chk->strf.auds.i_avgbytespersec * 8 / 1024 );
515 #endif
516             break;
517         case( AVIFOURCC_vids ):
518             p_strh->strh.i_samplesize = 0; // XXX for ffmpeg avi file
519             AVI_READ4BYTES( p_chk->strf.vids.i_size );
520             AVI_READ4BYTES( p_chk->strf.vids.i_width );
521             AVI_READ4BYTES( p_chk->strf.vids.i_height );
522             AVI_READ2BYTES( p_chk->strf.vids.i_planes );
523             AVI_READ2BYTES( p_chk->strf.vids.i_bitcount );
524             AVI_READFOURCC( p_chk->strf.vids.i_compression );
525             AVI_READ4BYTES( p_chk->strf.vids.i_sizeimage );
526             AVI_READ4BYTES( p_chk->strf.vids.i_xpelspermeter );
527             AVI_READ4BYTES( p_chk->strf.vids.i_ypelspermeter );
528             AVI_READ4BYTES( p_chk->strf.vids.i_clrused );
529             AVI_READ4BYTES( p_chk->strf.vids.i_clrimportant );
530             p_chk->strf.vids.p_bih = malloc( p_chk->common.i_chunk_size );
531             memcpy( p_chk->strf.vids.p_bih, 
532                     p_buff + 8, 
533                     p_chk->common.i_chunk_size );
534 #ifdef AVI_DEBUG
535             msg_Dbg( p_input,
536                      "strf: video:%c%c%c%c %dx%d planes:%d %dbpp",
537                      AVIFOURCC_PRINT( p_chk->strf.vids.i_compression ),
538                      p_chk->strf.vids.i_width,
539                      p_chk->strf.vids.i_height,
540                      p_chk->strf.vids.i_planes,
541                      p_chk->strf.vids.i_bitcount );
542 #endif
543             break;
544         default:
545             msg_Warn( p_input, "unknown stream type" );
546             break;
547     }
548     AVI_READCHUNK_EXIT( 1 );
549 }
550 static void AVI_ChunkFree_strf( input_thread_t *p_input,
551                                avi_chunk_t *p_chk )
552 {
553
554 }
555
556 static int AVI_ChunkRead_strd( input_thread_t *p_input,
557                                avi_chunk_t *p_chk,
558                                int b_seekable )
559 {
560     AVI_READCHUNK_ENTER;
561     p_chk->strd.p_data = malloc( p_chk->common.i_chunk_size );
562     memcpy( p_chk->strd.p_data,
563             p_buff,
564             p_chk->common.i_chunk_size );
565     AVI_READCHUNK_EXIT( 1 );
566 }
567
568 static int AVI_ChunkRead_idx1( input_thread_t *p_input,
569                                avi_chunk_t *p_chk,
570                                int b_seekable )
571 {
572     int i_count, i_index;
573
574     AVI_READCHUNK_ENTER;
575
576     i_count = __MIN( p_chk->common.i_chunk_size, i_read ) / 16;
577
578     p_chk->idx1.i_entry_count = i_count;
579     p_chk->idx1.i_entry_max   = i_count;
580     if( i_count > 0 )
581     {
582         p_chk->idx1.entry = calloc( i_count, sizeof( idx1_entry_t ) );
583
584         for( i_index = 0; i_index < i_count ; i_index++ )
585         {
586             AVI_READ4BYTES( p_chk->idx1.entry[i_index].i_fourcc );
587             AVI_READ4BYTES( p_chk->idx1.entry[i_index].i_flags );
588             AVI_READ4BYTES( p_chk->idx1.entry[i_index].i_pos );
589             AVI_READ4BYTES( p_chk->idx1.entry[i_index].i_length );
590         }
591     }
592     else
593     {
594         p_chk->idx1.entry = NULL;
595     }
596 #ifdef AVI_DEBUG
597     msg_Dbg( p_input, "idx1: index entry:%d", i_count );
598 #endif
599     AVI_READCHUNK_EXIT( 1 );
600 }
601
602 static void AVI_ChunkFree_idx1( input_thread_t *p_input,
603                                avi_chunk_t *p_chk )
604 {
605     p_chk->idx1.i_entry_count = 0;
606     p_chk->idx1.i_entry_max   = 0;
607     FREE( p_chk->idx1.entry )
608 }
609
610 static struct 
611 {
612     u32 i_fourcc;
613     char *psz_type;
614 } AVI_strz_type[] =
615 {
616     { AVIFOURCC_IARL, "archive location" },
617     { AVIFOURCC_IART, "artist" },
618     { AVIFOURCC_ICMS, "commisioned" },
619     { AVIFOURCC_ICMT, "comments" },
620     { AVIFOURCC_ICOP, "copyright" },
621     { AVIFOURCC_ICRD, "creation date" },
622     { AVIFOURCC_ICRP, "cropped" },
623     { AVIFOURCC_IDIM, "dimensions" },
624     { AVIFOURCC_IDPI, "dots per inch" },
625     { AVIFOURCC_IENG, "enginner" },
626     { AVIFOURCC_IGNR, "genre" },
627     { AVIFOURCC_IKEY, "keywords" },
628     { AVIFOURCC_ILGT, "lightness" },
629     { AVIFOURCC_IMED, "medium" },
630     { AVIFOURCC_INAM, "name" },
631     { AVIFOURCC_IPLT, "palette setting" },
632     { AVIFOURCC_IPRD, "product" },
633     { AVIFOURCC_ISBJ, "subject" },
634     { AVIFOURCC_ISFT, "software" },
635     { AVIFOURCC_ISHP, "sharpness" },
636     { AVIFOURCC_ISRC, "source" },
637     { AVIFOURCC_ISRF, "source form" },
638     { AVIFOURCC_ITCH, "technician" },
639     { AVIFOURCC_ISMP, "time code" },
640     { AVIFOURCC_IDIT, "digitalization time" },
641     { 0,              "???" }
642 };
643 static int AVI_ChunkRead_strz( input_thread_t *p_input,
644                                avi_chunk_t *p_chk,
645                                int b_seekable )
646 {
647     int i_index;
648     avi_chunk_STRING_t *p_strz = (avi_chunk_STRING_t*)p_chk;
649     AVI_READCHUNK_ENTER;
650
651     for( i_index = 0;; i_index++)
652     {
653         if( !AVI_strz_type[i_index].i_fourcc ||
654             AVI_strz_type[i_index].i_fourcc == p_strz->i_chunk_fourcc )
655         {
656             break;
657         }
658     }
659     p_strz->p_type = strdup( AVI_strz_type[i_index].psz_type );
660     p_strz->p_str = malloc( i_read + 1);
661
662     if( p_strz->i_chunk_size )
663     {
664         memcpy( p_strz->p_str, p_read, i_read );
665     }
666     p_strz->p_str[i_read] = 0;
667     
668 #ifdef AVI_DEBUG
669     msg_Dbg( p_input, "%c%c%c%c: %s : %s", 
670              AVIFOURCC_PRINT( p_strz->i_chunk_fourcc), p_strz->p_type, p_strz->p_str);
671 #endif
672     AVI_READCHUNK_EXIT( 1 );
673 }
674 static void AVI_ChunkFree_strz( input_thread_t *p_input,
675                                 avi_chunk_t *p_chk )
676 {
677     avi_chunk_STRING_t *p_strz = (avi_chunk_STRING_t*)p_chk;
678     FREE( p_strz->p_type );
679     FREE( p_strz->p_str );
680 }
681
682 static int AVI_ChunkRead_nothing( input_thread_t *p_input,
683                                avi_chunk_t *p_chk,
684                                int b_seekable )
685 {
686     return( AVI_NextChunk( p_input, p_chk ) );
687 }
688 static void AVI_ChunkFree_nothing( input_thread_t *p_input,
689                                avi_chunk_t *p_chk )
690 {
691
692 }
693
694 static struct
695 {
696     u32   i_fourcc;
697     int   (*AVI_ChunkRead_function)( input_thread_t *p_input, 
698                                      avi_chunk_t *p_chk,
699                                      int b_seekable );
700     void  (*AVI_ChunkFree_function)( input_thread_t *p_input,
701                                      avi_chunk_t *p_chk );
702 } AVI_Chunk_Function [] =
703 {
704     { AVIFOURCC_RIFF, AVI_ChunkRead_list, AVI_ChunkFree_nothing },
705     { AVIFOURCC_LIST, AVI_ChunkRead_list, AVI_ChunkFree_nothing },
706     { AVIFOURCC_avih, AVI_ChunkRead_avih, AVI_ChunkFree_nothing },
707     { AVIFOURCC_strh, AVI_ChunkRead_strh, AVI_ChunkFree_nothing },
708     { AVIFOURCC_strf, AVI_ChunkRead_strf, AVI_ChunkFree_strf },
709     { AVIFOURCC_strd, AVI_ChunkRead_strd, AVI_ChunkFree_nothing },
710     { AVIFOURCC_idx1, AVI_ChunkRead_idx1, AVI_ChunkFree_idx1 },
711     { AVIFOURCC_JUNK, AVI_ChunkRead_nothing, AVI_ChunkFree_nothing },
712
713     { AVIFOURCC_IARL, AVI_ChunkRead_strz, AVI_ChunkFree_strz },
714     { AVIFOURCC_IARL, AVI_ChunkRead_strz, AVI_ChunkFree_strz },
715     { AVIFOURCC_IART, AVI_ChunkRead_strz, AVI_ChunkFree_strz },
716     { AVIFOURCC_ICMS, AVI_ChunkRead_strz, AVI_ChunkFree_strz },
717     { AVIFOURCC_ICMT, AVI_ChunkRead_strz, AVI_ChunkFree_strz },
718     { AVIFOURCC_ICOP, AVI_ChunkRead_strz, AVI_ChunkFree_strz },
719     { AVIFOURCC_ICRD, AVI_ChunkRead_strz, AVI_ChunkFree_strz },
720     { AVIFOURCC_ICRP, AVI_ChunkRead_strz, AVI_ChunkFree_strz },
721     { AVIFOURCC_IDIM, AVI_ChunkRead_strz, AVI_ChunkFree_strz },
722     { AVIFOURCC_IDPI, AVI_ChunkRead_strz, AVI_ChunkFree_strz },
723     { AVIFOURCC_IENG, AVI_ChunkRead_strz, AVI_ChunkFree_strz },
724     { AVIFOURCC_IGNR, AVI_ChunkRead_strz, AVI_ChunkFree_strz },
725     { AVIFOURCC_IKEY, AVI_ChunkRead_strz, AVI_ChunkFree_strz },
726     { AVIFOURCC_ILGT, AVI_ChunkRead_strz, AVI_ChunkFree_strz },
727     { AVIFOURCC_IMED, AVI_ChunkRead_strz, AVI_ChunkFree_strz },
728     { AVIFOURCC_INAM, AVI_ChunkRead_strz, AVI_ChunkFree_strz },
729     { AVIFOURCC_IPLT, AVI_ChunkRead_strz, AVI_ChunkFree_strz },
730     { AVIFOURCC_IPRD, AVI_ChunkRead_strz, AVI_ChunkFree_strz },
731     { AVIFOURCC_ISBJ, AVI_ChunkRead_strz, AVI_ChunkFree_strz },
732     { AVIFOURCC_ISFT, AVI_ChunkRead_strz, AVI_ChunkFree_strz },
733     { AVIFOURCC_ISHP, AVI_ChunkRead_strz, AVI_ChunkFree_strz },
734     { AVIFOURCC_ISRC, AVI_ChunkRead_strz, AVI_ChunkFree_strz },
735     { AVIFOURCC_ISRF, AVI_ChunkRead_strz, AVI_ChunkFree_strz },
736     { AVIFOURCC_ITCH, AVI_ChunkRead_strz, AVI_ChunkFree_strz },
737     { AVIFOURCC_ISMP, AVI_ChunkRead_strz, AVI_ChunkFree_strz },
738     { AVIFOURCC_IDIT, AVI_ChunkRead_strz, AVI_ChunkFree_strz },
739     { 0,           NULL,               NULL }
740 };
741
742 static int AVI_ChunkFunctionFind( int i_fourcc )
743 {
744     int i_index;
745     for( i_index = 0; ; i_index++ )
746     {
747         if( ( AVI_Chunk_Function[i_index].i_fourcc == i_fourcc )||
748             ( AVI_Chunk_Function[i_index].i_fourcc == 0 ) )
749         {
750             return( i_index );
751         }
752     }
753 }
754
755 int  _AVI_ChunkRead( input_thread_t *p_input,
756                      avi_chunk_t *p_chk,
757                      avi_chunk_t *p_father,
758                      int b_seekable )
759 {
760     int i_index;
761     int i_result;
762
763
764     if( !p_chk )
765     {
766         return( 0 );
767     }
768
769     if( !AVI_ChunkReadCommon( p_input, p_chk ) )
770     {
771         msg_Warn( p_input, "cannot read one chunk" );
772         return( 0 );
773     }
774     p_chk->common.p_father = p_father;
775
776     i_index = AVI_ChunkFunctionFind( p_chk->common.i_chunk_fourcc );
777     if( AVI_Chunk_Function[i_index].AVI_ChunkRead_function )
778     {
779         i_result = 
780             AVI_Chunk_Function[i_index].AVI_ChunkRead_function( p_input,
781                                                                 p_chk,
782                                                                 b_seekable );
783     }
784     else
785     {
786         msg_Warn( p_input, "unknown chunk (not loaded)" );
787         i_result = AVI_NextChunk( p_input, p_chk );
788     }
789
790     return( i_result );
791 }
792
793 void _AVI_ChunkFree( input_thread_t *p_input,
794                      avi_chunk_t *p_chk )
795 {
796     int i_index;
797     avi_chunk_t *p_child, *p_next;
798
799     if( !p_chk )
800     {
801         return;
802     }
803     
804     /* Free all child chunk */
805     p_child = p_chk->common.p_first;
806     while( p_child )
807     {
808         p_next = p_child->common.p_next;
809         AVI_ChunkFree( p_input, p_child );
810         free( p_child );
811         p_child = p_next;
812     }
813
814     i_index = AVI_ChunkFunctionFind( p_chk->common.i_chunk_fourcc );
815     if( AVI_Chunk_Function[i_index].AVI_ChunkFree_function )
816     {
817 #ifdef AVI_DEBUG
818         msg_Dbg( p_input, "free chunk %c%c%c%c", 
819                  AVIFOURCC_PRINT( p_chk->common.i_chunk_fourcc ) );
820 #endif
821         AVI_Chunk_Function[i_index].AVI_ChunkFree_function( p_input, p_chk);
822     }
823     else
824     {
825         msg_Warn( p_input, "unknown chunk (not unloaded)" );
826     }
827     p_chk->common.p_first = NULL;
828     p_chk->common.p_last  = NULL;
829     
830     return;
831 }
832
833 int AVI_ChunkReadRoot( input_thread_t *p_input,
834                        avi_chunk_t *p_root,
835                        int b_seekable )
836 {
837     avi_chunk_list_t *p_list = (avi_chunk_list_t*)p_root;
838     avi_chunk_t      *p_chk;
839     
840     p_list->i_chunk_pos  = 0;
841     p_list->i_chunk_size = p_input->stream.p_selected_area->i_size;
842     p_list->i_chunk_fourcc = AVIFOURCC_LIST;
843     p_list->p_father = NULL;
844     p_list->p_next  = NULL;
845     p_list->p_first = NULL;
846     p_list->p_last  = NULL;
847
848     p_list->i_type = MKFOURCC( 'r', 'o', 'o', 't' );
849     
850     for( ; ; )
851     {
852         p_chk = malloc( sizeof( avi_chunk_t ) );
853         memset( p_chk, 0, sizeof( avi_chunk_t ) );
854         if( !p_root->common.p_first )
855         {
856             p_root->common.p_first = p_chk;
857         }
858         else
859         {
860             p_root->common.p_last->common.p_next = p_chk;
861         }
862         p_root->common.p_last = p_chk;
863
864         if( !AVI_ChunkRead( p_input, p_chk, p_root, b_seekable ) ||
865            ( AVI_TellAbsolute( p_input ) >=
866                 p_chk->common.p_father->common.i_chunk_pos + 
867                     __EVEN( p_chk->common.p_father->common.i_chunk_size ) ) )
868         {
869             break;
870         }
871         /* If we can't seek then stop when we 've found first RIFF-AVI */
872         if( p_chk->common.i_chunk_fourcc == AVIFOURCC_RIFF &&
873             p_chk->list.i_type == AVIFOURCC_AVI && !b_seekable )
874         {
875             break;
876         }
877     } 
878     
879     return( 1 );
880 }
881
882 void AVI_ChunkFreeRoot( input_thread_t *p_input,
883                         avi_chunk_t  *p_chk )
884 {
885     AVI_ChunkFree( p_input, p_chk );
886 }
887
888
889 int  _AVI_ChunkCount( avi_chunk_t *p_chk, u32 i_fourcc )
890 {
891     int i_count;
892     avi_chunk_t *p_child;
893
894     if( !p_chk )
895     {
896         return( 0 );
897     }
898
899     i_count = 0;
900     p_child = p_chk->common.p_first;
901     while( p_child )
902     {
903         if( p_child->common.i_chunk_fourcc == i_fourcc ||
904             ( p_child->common.i_chunk_fourcc == AVIFOURCC_LIST && 
905               p_child->list.i_type == i_fourcc ) )
906         {
907             i_count++;
908         }
909         p_child = p_child->common.p_next;
910     }
911     return( i_count );
912 }
913
914 avi_chunk_t *_AVI_ChunkFind( avi_chunk_t *p_chk, u32 i_fourcc, int i_number )
915 {
916     avi_chunk_t *p_child;
917     if( !p_chk )
918     {
919         return( NULL );
920     }
921     p_child = p_chk->common.p_first;
922
923     while( p_child )
924     {
925         if( p_child->common.i_chunk_fourcc == i_fourcc ||
926             ( p_child->common.i_chunk_fourcc == AVIFOURCC_LIST && 
927               p_child->list.i_type == i_fourcc ) )
928         {
929             if( i_number == 0 )
930             {
931                 /* We found it */
932                 return( p_child );
933             }
934
935             i_number--;
936         }
937         p_child = p_child->common.p_next;
938     }
939     return( NULL );
940 }
941
942 static void AVI_ChunkDumpDebug_level( input_thread_t *p_input,
943                                       avi_chunk_t  *p_chk, int i_level )
944 {
945     char str[1024];
946     int i;
947     avi_chunk_t *p_child;
948     
949     memset( str, ' ', sizeof( str ) );
950     for( i = 1; i < i_level; i++ )
951     {
952         str[i * 5] = '|';
953     }
954     if( p_chk->common.i_chunk_fourcc == AVIFOURCC_RIFF||
955         p_chk->common.i_chunk_fourcc == AVIFOURCC_LIST )
956     {
957         sprintf( str + i_level * 5, 
958                  "%c %c%c%c%c-%c%c%c%c size:%lld pos:%lld",
959                  i_level ? '+' : '*',
960                  AVIFOURCC_PRINT( p_chk->common.i_chunk_fourcc ),
961                  AVIFOURCC_PRINT( p_chk->list.i_type ),
962                  p_chk->common.i_chunk_size,
963                  p_chk->common.i_chunk_pos );
964     }
965     else
966     {
967         sprintf( str + i_level * 5, 
968                  "+ %c%c%c%c size:%lld pos:%lld",
969                  AVIFOURCC_PRINT( p_chk->common.i_chunk_fourcc ),
970                  p_chk->common.i_chunk_size,
971                  p_chk->common.i_chunk_pos );
972     }
973     msg_Dbg( p_input, "%s", str );
974
975     p_child = p_chk->common.p_first;
976     while( p_child )
977     {
978         AVI_ChunkDumpDebug_level( p_input, p_child, i_level + 1 );
979         p_child = p_child->common.p_next;
980     }
981 }
982 void _AVI_ChunkDumpDebug( input_thread_t *p_input,
983                          avi_chunk_t  *p_chk )
984 {
985     AVI_ChunkDumpDebug_level( p_input, p_chk, 0 );
986 }
987