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