]> git.sesse.net Git - vlc/blob - plugins/avi/libioRIFF.c
7fe20b90b220b96403cb20e577e14f38c576115b
[vlc] / plugins / avi / libioRIFF.c
1 /*****************************************************************************
2  * libioRIFF.c : AVI file Stream input module for vlc
3  *****************************************************************************
4  * Copyright (C) 2001 VideoLAN
5  * $Id: libioRIFF.c,v 1.6 2002/05/25 16:23:07 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 typedef struct riffchunk_s
24 {
25     u32 i_id;
26     u32 i_size;
27     u32 i_type;
28     u32 i_pos;
29     data_packet_t *p_data;
30     struct riffchunk_s *p_next;
31     struct riffchunk_s *p_subchunk;
32 } riffchunk_t;
33
34 static riffchunk_t  * RIFF_ReadChunk(input_thread_t * p_input);
35 static int            RIFF_NextChunk(input_thread_t * p_input,riffchunk_t *p_rifffather);
36 static int            RIFF_DescendChunk(input_thread_t * p_input);
37 static int            RIFF_AscendChunk(input_thread_t * p_input,riffchunk_t *p_rifffather);
38 static int            RIFF_FindChunk(input_thread_t * p_input,u32 i_id,riffchunk_t *p_rifffather);
39 static int            RIFF_GoToChunkData(input_thread_t * p_input);
40 static int            RIFF_LoadChunkData(input_thread_t * p_input,riffchunk_t *p_riff);
41 static int            RIFF_TestFileHeader(input_thread_t * p_input, riffchunk_t **pp_riff, u32 i_type);
42 static int            RIFF_FindAndLoadChunk( input_thread_t * p_input, riffchunk_t *p_riff, riffchunk_t **pp_fmt, u32 i_type );
43 static int            RIFF_FindAndGotoDataChunk( input_thread_t * p_input, riffchunk_t *p_riff, riffchunk_t **pp_data, u32 i_type );
44 static int            RIFF_FindListChunk( input_thread_t *p_input, riffchunk_t **pp_riff, riffchunk_t *p_rifffather, u32 i_type );
45
46 static void           RIFF_DeleteChunk( input_thread_t * p_input, riffchunk_t *p_chunk );
47
48
49  static int            RIFF_GoToChunk(input_thread_t * p_input,riffchunk_t *p_riff);
50
51 static char         * RIFF_IToStr(u32 i);
52
53 /*************************************************************************/
54
55 /********************************************
56  * Fonction locale maintenant               *
57  ********************************************/
58
59 static int __RIFF_TellPos( input_thread_t *p_input, u32 *pos )
60
61     u32 i;
62     
63     vlc_mutex_lock( &p_input->stream.stream_lock );
64     i = p_input->stream.p_selected_area->i_tell - ( p_input->p_last_data - p_input->p_current_data  );
65     vlc_mutex_unlock( &p_input->stream.stream_lock );
66     *pos = i; 
67     return 0;
68 }
69
70 static int      __RIFF_SkipBytes(input_thread_t * p_input,int nb)
71 {  
72     data_packet_t *p_pack;
73     int i;
74     int i_rest;
75     if( p_input->stream.b_seekable )
76     {
77         u32 i_pos;
78         __RIFF_TellPos( p_input, &i_pos);
79         p_input->pf_seek( p_input, (off_t)(i_pos + nb) );
80         input_AccessReinit( p_input );
81     }
82     else
83     {
84         intf_WarnMsg( 1, "input demux: cannot seek, it will take times" );
85         if( nb < 0 ) { return( -1 ); }
86         i_rest = nb;
87         while (i_rest != 0 )
88         {
89             if ( i_rest >= 4096 )
90             {
91                 i = input_SplitBuffer( p_input, &p_pack, 4096);
92             }
93             else
94             {
95                 i = input_SplitBuffer( p_input, &p_pack, i_rest);
96             }
97                     
98             if ( i < 0 ) { return ( -1 ); }
99             i_rest-=i;
100             input_DeletePacket( p_input->p_method_data, p_pack);
101             if( ( i == 0 )&&( i_rest != 0 )) { return( -1 ); }
102         }
103     }
104         return ( 0 );
105 }
106
107
108 static void             RIFF_DeleteChunk( input_thread_t *p_input, riffchunk_t *p_chunk )
109 {
110     if( p_chunk != NULL)
111     {
112         if( p_chunk->p_data != NULL )
113         {
114             input_DeletePacket( p_input->p_method_data, p_chunk->p_data );
115         }
116         free( p_chunk );
117     }
118 }
119
120 /* ******************************************
121  * lit une structure riffchunk sans avancer *
122  ********************************************/
123 static riffchunk_t     * RIFF_ReadChunk(input_thread_t * p_input)
124 {
125     riffchunk_t * p_riff;
126     int count;
127     byte_t * p_peek;
128  
129         if((p_riff = malloc( sizeof(riffchunk_t))) == NULL)
130         {
131                 return NULL;
132         }
133         
134         p_riff->p_data = NULL;  /* Par defaut */
135         p_riff->p_next = NULL;
136         p_riff->p_subchunk = NULL;
137         /* peek to have the begining, 8+4 where 4 are to get type */
138         count=input_Peek( p_input, &p_peek, 12 );
139         if( count < 8 )
140         {
141                 free(p_riff);
142                 return NULL;
143         }
144         
145         p_riff->i_id = __GetDoubleWordLittleEndianFromBuff( p_peek );
146         p_riff->i_size =__GetDoubleWordLittleEndianFromBuff( p_peek + 4 );
147         if( count == 12 )
148         {
149                 p_riff->i_type = __GetDoubleWordLittleEndianFromBuff( p_peek + 8 );
150         }
151         else
152         {
153                 p_riff->i_type = 0;
154         }
155         __RIFF_TellPos(p_input, &(p_riff->i_pos) );
156         
157         return( p_riff );       
158 }
159
160 /**************************************************
161  * Va au chunk juste d'apres si il en a encore    *
162  * -1 si erreur , 1 si y'en a plus                *
163  **************************************************/
164 static int RIFF_NextChunk( input_thread_t * p_input,riffchunk_t *p_rifffather)
165 {
166     int i_len;
167     int i_lenfather;
168     riffchunk_t *p_riff;
169
170         if( ( p_riff = RIFF_ReadChunk( p_input ) ) == NULL )
171         {
172                 return( -1 );
173         }
174         i_len = p_riff->i_size;
175     if( i_len%2 != 0 ) {i_len++;} /* aligné sur un mot */
176
177         if ( p_rifffather != NULL )
178         {
179                 i_lenfather=p_rifffather->i_size; 
180         if ( i_lenfather%2 !=0 ) {i_lenfather++;}
181                 if ( p_rifffather->i_pos + i_lenfather  <= p_riff->i_pos + i_len + 8 )
182                 {
183                         free( p_riff );
184                         return( 1 ); /* pas dans nos frontiere */
185                 }
186         }
187         if ( __RIFF_SkipBytes( p_input,i_len + 8 ) != 0 )
188         { 
189                 free( p_riff );
190                 return( -1 );
191         }
192         free( p_riff );
193         return( 0 );
194 }
195
196 /****************************************************************
197  * Permet de rentrer dans un ck RIFF ou LIST                    *
198  ****************************************************************/
199 static int      RIFF_DescendChunk(input_thread_t * p_input)
200 {
201         if ( __RIFF_SkipBytes(p_input,12) != 0)
202         {
203                 return ( -1 );
204         }
205         return( 0 );
206 }
207
208 /***************************************************************
209  * Permet de sortir d'un sous chunk et d'aller sur le suivant  *
210  * chunk                                                       *
211  ***************************************************************/
212
213 static int      RIFF_AscendChunk(input_thread_t * p_input ,riffchunk_t *p_rifffather)
214 {
215     int i_skip;
216     u32 i_posactu;
217
218         i_skip  = p_rifffather->i_pos + p_rifffather->i_size + 8;
219     if ( i_skip%2 != 0) {i_skip++;} 
220
221     __RIFF_TellPos(p_input, &i_posactu);
222     i_skip-=i_posactu;
223
224     if (( __RIFF_SkipBytes(p_input,i_skip)) != 0)
225         {
226                 return( -1 );
227         }
228         return( 0 );
229 }
230
231 /***************************************************************
232  * Permet de se deplacer jusqu'au premier chunk avec le bon id *
233  * *************************************************************/
234 static int      RIFF_FindChunk(input_thread_t * p_input ,u32 i_id,riffchunk_t *p_rifffather)
235 {
236  riffchunk_t *p_riff=NULL;
237         do
238         {
239                 if (p_riff!=NULL) 
240                 { 
241                         free(p_riff); 
242                         if ( RIFF_NextChunk(p_input ,p_rifffather) != 0 ) 
243             { 
244                 return( -1 );
245             }
246                 }
247                 p_riff=RIFF_ReadChunk(p_input);
248         } while ( ( p_riff != NULL )&&( p_riff->i_id != i_id ) );
249
250     if ( ( p_riff == NULL )||( p_riff->i_id != i_id ) )
251     { 
252         return( -1 );
253     }
254     free( p_riff );
255         return( 0 );
256 }
257
258 /*****************************************************************
259  * Permet de pointer sur la zone de donné du chunk courant       *
260  *****************************************************************/
261 static int               RIFF_GoToChunkData(input_thread_t * p_input)
262 {
263         if ( __RIFF_SkipBytes(p_input,8) != 0 ) 
264     { 
265         return( -1 );
266     }
267         return( 0 );
268 }
269
270 static int      RIFF_LoadChunkData(input_thread_t * p_input,riffchunk_t *p_riff )
271 {
272     
273         RIFF_GoToChunkData(p_input);
274         if ( input_SplitBuffer( p_input, &p_riff->p_data, p_riff->i_size ) != p_riff->i_size )
275         {
276                 return ( -1 );
277         }
278         if ( p_riff->i_size%2 != 0) 
279     {
280        __RIFF_SkipBytes(p_input,1);
281     } /* aligne sur un mot */
282         return( 0 );
283 }
284
285 static int      RIFF_LoadChunkDataInPES(input_thread_t * p_input,
286                                     pes_packet_t **pp_pes,
287                                     int i_size_index)
288 {
289     u32 i_read;
290     data_packet_t *p_data;
291     riffchunk_t   *p_riff;
292     int i_size;
293     
294     if( (p_riff = RIFF_ReadChunk( p_input )) == NULL )
295     {
296         *pp_pes = NULL;
297         return( -1 );
298     }
299         RIFF_GoToChunkData(p_input);
300     *pp_pes = input_NewPES( p_input->p_method_data );
301
302     if( *pp_pes == NULL )
303     {
304         return( -1 );
305     }
306
307     if( (!p_riff->i_size) || (!i_size_index ) )
308     {
309         i_size = __MAX( i_size_index, p_riff->i_size );
310     }
311     else
312     {
313         i_size = __MIN( p_riff->i_size, i_size_index );
314     }
315     
316     if( p_riff->i_size == 0 )
317     {
318         p_data = input_NewPacket( p_input->p_method_data, 0 );
319         (*pp_pes)->p_first = p_data;
320         (*pp_pes)->p_last  = p_data;
321         (*pp_pes)->i_nb_data = 1;
322         (*pp_pes)->i_pes_size = 0;
323         return( 0 );
324     }
325         
326     do
327     {
328         i_read = input_SplitBuffer(p_input, &p_data, i_size - 
329                                                     (*pp_pes)->i_pes_size );
330         if( i_read < 0 )
331         {
332             /* FIXME free sur tout les packets */
333             return( -1 );
334         }
335         if( (*pp_pes)->p_first == NULL )
336         {
337             (*pp_pes)->p_first = p_data;
338             (*pp_pes)->p_last  = p_data;
339             (*pp_pes)->i_nb_data = 1;
340             (*pp_pes)->i_pes_size = ( p_data->p_payload_end - 
341                                         p_data->p_payload_start );
342         }
343         else
344         {
345             (*pp_pes)->p_last->p_next = p_data;
346             (*pp_pes)->p_last = p_data;
347             (*pp_pes)->i_nb_data++;
348             (*pp_pes)->i_pes_size += ( p_data->p_payload_end -
349                                        p_data->p_payload_start );
350         }
351     } while( ((*pp_pes)->i_pes_size < i_size)&&(i_read != 0) );
352    /* i_read =  0 si fin du stream sinon block */
353         if ( i_size%2 != 0) 
354     {
355        __RIFF_SkipBytes(p_input,1);
356     } /* aligne sur un mot */
357         return( 0 );
358 }
359
360
361
362
363 static int      RIFF_GoToChunk(input_thread_t * p_input, riffchunk_t *p_riff)
364 {
365     /* TODO rajouter les test */
366     if( p_input->stream.b_seekable )
367     {
368         p_input->pf_seek( p_input, (off_t)p_riff->i_pos );
369         input_AccessReinit( p_input );
370             return 0;
371     }
372
373     return( -1 );
374 }
375
376 static char     * RIFF_IToStr(u32 l)
377 {
378  char *str;
379  int i;
380         str=calloc(5,sizeof(char));
381         for( i = 0; i < 4; i++)
382         {
383                 str[i] = ( l >>  ( i * 8) )&0xFF;
384         }
385         str[5] = 0;
386         return( str );
387 }
388
389 static int   RIFF_TestFileHeader( input_thread_t * p_input, riffchunk_t ** pp_riff, u32 i_type )
390 {
391     *pp_riff = RIFF_ReadChunk( p_input );
392     
393     if( *pp_riff == NULL )
394     {
395         return( -1 );
396     }
397     if( (*pp_riff)->i_id != FOURCC_RIFF ) 
398     {
399         free( *pp_riff );
400         return( -1 );
401     }
402     if( (*pp_riff)->i_type != i_type )
403     {
404         free( *pp_riff );
405         return( -1 );
406     } 
407     return( 0 );  
408 }
409
410
411 static int   RIFF_FindAndLoadChunk( input_thread_t * p_input, riffchunk_t *p_riff, riffchunk_t **pp_fmt, u32 i_type )
412 {
413     *pp_fmt = NULL;
414     if ( RIFF_FindChunk( p_input, i_type, p_riff ) != 0)
415     {
416         return( -1 );
417     }
418     if ( ( (*pp_fmt = RIFF_ReadChunk( p_input )) == NULL) || ( RIFF_LoadChunkData( p_input, *pp_fmt ) != 0 ) )
419     {
420         if( *pp_fmt != NULL ) { RIFF_DeleteChunk( p_input, *pp_fmt ); }
421         return( -1 );
422     }
423     return( 0 );
424 }
425
426 static int   RIFF_FindAndGotoDataChunk( input_thread_t * p_input, riffchunk_t *p_riff, riffchunk_t **pp_data, u32 i_type )
427 {
428     *pp_data = NULL;
429     if ( RIFF_FindChunk( p_input, i_type, p_riff ) != 0)
430     {
431         return( -1 );
432     }
433     if ( ( *pp_data = RIFF_ReadChunk( p_input ) ) == NULL )
434     {
435         return( -1 );
436     }
437     if ( RIFF_GoToChunkData( p_input ) != 0 )
438     {
439         RIFF_DeleteChunk( p_input, *pp_data );
440         return( -1 );
441     }
442     return( 0 );
443 }
444
445 static int   RIFF_FindListChunk( input_thread_t *p_input, riffchunk_t **pp_riff, riffchunk_t *p_rifffather, u32 i_type )
446 {
447     int i_ok;
448     
449     *pp_riff = NULL;
450     i_ok = 0;
451     while( i_ok == 0 )
452     {
453         if( *pp_riff != NULL )
454         {
455             free( *pp_riff );
456         }
457         if( RIFF_FindChunk( p_input, FOURCC_LIST, p_rifffather) != 0 )
458         {
459             return( -1 );
460         }
461         *pp_riff = RIFF_ReadChunk( p_input );
462                         
463         if( *pp_riff == NULL )
464         {
465             return( -1 );
466         }
467         if( (*pp_riff)->i_type != i_type )
468         {
469             if( RIFF_NextChunk( p_input, p_rifffather ) != 0 )
470             {
471                 return( -1 );
472             }
473         }
474         else
475         {
476             i_ok = 1;
477         }
478     }
479     return( 0 );  
480 }