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