/src/vlc/modules/demux/avi/libavi.c
Line | Count | Source (jump to first uncovered line) |
1 | | /***************************************************************************** |
2 | | * libavi.c : LibAVI |
3 | | ***************************************************************************** |
4 | | * Copyright (C) 2001 VLC authors and VideoLAN |
5 | | * Authors: Laurent Aimar <fenrir@via.ecp.fr> |
6 | | * |
7 | | * This program is free software; you can redistribute it and/or modify it |
8 | | * under the terms of the GNU Lesser General Public License as published by |
9 | | * the Free Software Foundation; either version 2.1 of the License, or |
10 | | * (at your option) any later version. |
11 | | * |
12 | | * This program is distributed in the hope that it will be useful, |
13 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | | * GNU Lesser General Public License for more details. |
16 | | * |
17 | | * You should have received a copy of the GNU Lesser General Public License |
18 | | * along with this program; if not, write to the Free Software Foundation, |
19 | | * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. |
20 | | *****************************************************************************/ |
21 | | |
22 | | |
23 | | #ifdef HAVE_CONFIG_H |
24 | | # include "config.h" |
25 | | #endif |
26 | | |
27 | | #include <vlc_common.h> |
28 | | #include <vlc_demux.h> /* stream_*, *_ES */ |
29 | | #include <vlc_codecs.h> /* VLC_BITMAPINFOHEADER */ |
30 | | |
31 | | #include "libavi.h" |
32 | | |
33 | | #include <limits.h> |
34 | | |
35 | | #ifndef NDEBUG |
36 | | # define AVI_DEBUG 1 |
37 | | #endif |
38 | | |
39 | 0 | #define __EVEN( x ) (((x) + 1) & ~1) |
40 | | |
41 | | static vlc_fourcc_t GetFOURCC( const uint8_t *p_buff ) |
42 | 0 | { |
43 | 0 | return VLC_FOURCC( p_buff[0], p_buff[1], p_buff[2], p_buff[3] ); |
44 | 0 | } |
45 | | |
46 | | static uint64_t AVI_ChunkSize( const avi_chunk_t *p_ck ) |
47 | 0 | { |
48 | 0 | return __EVEN(p_ck->common.i_chunk_size) + 8; |
49 | 0 | } |
50 | | |
51 | | static uint64_t AVI_ChunkEnd( const avi_chunk_t *p_ck ) |
52 | 0 | { |
53 | 0 | return p_ck->common.i_chunk_pos + AVI_ChunkSize( p_ck ); |
54 | 0 | } |
55 | | |
56 | | /**************************************************************************** |
57 | | * |
58 | | * Basics functions to manipulates chunks |
59 | | * |
60 | | ****************************************************************************/ |
61 | | static int AVI_ChunkReadCommon( stream_t *s, avi_chunk_t *p_chk, |
62 | | const avi_chunk_t *p_father ) |
63 | 0 | { |
64 | 0 | const uint8_t *p_peek; |
65 | |
|
66 | 0 | memset( p_chk, 0, sizeof( avi_chunk_t ) ); |
67 | |
|
68 | 0 | const uint64_t i_pos = vlc_stream_Tell( s ); |
69 | 0 | if( vlc_stream_Peek( s, &p_peek, 8 ) < 8 ) |
70 | 0 | { |
71 | 0 | if( stream_Size( s ) > 0 && (uint64_t) stream_Size( s ) > i_pos ) |
72 | 0 | msg_Warn( s, "can't peek at %"PRIu64, i_pos ); |
73 | 0 | else |
74 | 0 | msg_Dbg( s, "no more data at %"PRIu64, i_pos ); |
75 | 0 | return VLC_EGENERIC; |
76 | 0 | } |
77 | | |
78 | 0 | p_chk->common.i_chunk_fourcc = GetFOURCC( p_peek ); |
79 | 0 | p_chk->common.i_chunk_size = GetDWLE( p_peek + 4 ); |
80 | 0 | p_chk->common.i_chunk_pos = i_pos; |
81 | |
|
82 | 0 | if( p_chk->common.i_chunk_size >= UINT64_MAX - 8 || |
83 | 0 | p_chk->common.i_chunk_pos > UINT64_MAX - 8 || |
84 | 0 | UINT64_MAX - p_chk->common.i_chunk_pos - 8 < __EVEN(p_chk->common.i_chunk_size) ) |
85 | 0 | return VLC_EGENERIC; |
86 | | |
87 | 0 | if( p_father && AVI_ChunkEnd( p_chk ) > AVI_ChunkEnd( p_father ) ) |
88 | 0 | { |
89 | 0 | msg_Warn( s, "chunk %4.4s does not fit into parent %"PRIu64, |
90 | 0 | (char*)&p_chk->common.i_chunk_fourcc, AVI_ChunkEnd( p_father ) ); |
91 | | |
92 | | /* How hard is to produce files with the correct declared size ? */ |
93 | 0 | if( p_father->common.i_chunk_fourcc != AVIFOURCC_RIFF || |
94 | 0 | p_father->common.p_father == NULL || |
95 | 0 | p_father->common.p_father->common.p_father != NULL ) /* Root > RIFF only */ |
96 | 0 | return VLC_EGENERIC; |
97 | 0 | } |
98 | | |
99 | 0 | #ifdef AVI_DEBUG |
100 | 0 | msg_Dbg( s, |
101 | 0 | "found chunk, fourcc: %4.4s size:%"PRIu64" pos:%"PRIu64, |
102 | 0 | (char*)&p_chk->common.i_chunk_fourcc, |
103 | 0 | p_chk->common.i_chunk_size, |
104 | 0 | p_chk->common.i_chunk_pos ); |
105 | 0 | #endif |
106 | 0 | return VLC_SUCCESS; |
107 | 0 | } |
108 | | |
109 | | static int AVI_GotoNextChunk( stream_t *s, const avi_chunk_t *p_chk ) |
110 | 0 | { |
111 | 0 | bool b_seekable = false; |
112 | 0 | const uint64_t i_offset = AVI_ChunkEnd( p_chk ); |
113 | 0 | if ( !vlc_stream_Control(s, STREAM_CAN_SEEK, &b_seekable) && b_seekable ) |
114 | 0 | { |
115 | 0 | return vlc_stream_Seek( s, i_offset ); |
116 | 0 | } |
117 | 0 | ssize_t i_read = i_offset - vlc_stream_Tell( s ); |
118 | 0 | if (i_read < 0) |
119 | 0 | return VLC_EGENERIC; |
120 | 0 | return vlc_stream_Read( s, NULL, i_read ) != i_read ? VLC_EGENERIC : VLC_SUCCESS; |
121 | 0 | } |
122 | | |
123 | | static int AVI_NextChunk( stream_t *s, avi_chunk_t *p_chk ) |
124 | 0 | { |
125 | 0 | avi_chunk_t chk; |
126 | |
|
127 | 0 | if( !p_chk ) |
128 | 0 | { |
129 | 0 | if( AVI_ChunkReadCommon( s, &chk, NULL ) ) |
130 | 0 | { |
131 | 0 | return VLC_EGENERIC; |
132 | 0 | } |
133 | 0 | p_chk = &chk; |
134 | 0 | } |
135 | | |
136 | 0 | return AVI_GotoNextChunk( s, p_chk ); |
137 | 0 | } |
138 | | |
139 | | /**************************************************************************** |
140 | | * |
141 | | * Functions to read chunks |
142 | | * |
143 | | ****************************************************************************/ |
144 | | static int AVI_ChunkRead_list( stream_t *s, avi_chunk_t *p_container ) |
145 | 0 | { |
146 | 0 | avi_chunk_t *p_chk; |
147 | 0 | const uint8_t *p_peek; |
148 | 0 | bool b_seekable; |
149 | 0 | int i_ret = VLC_SUCCESS; |
150 | |
|
151 | 0 | if( p_container->common.i_chunk_size > 0 && p_container->common.i_chunk_size < 4 ) |
152 | 0 | { |
153 | | /* empty box */ |
154 | 0 | msg_Warn( s, "empty list chunk" ); |
155 | 0 | return VLC_EGENERIC; |
156 | 0 | } |
157 | 0 | if( vlc_stream_Peek( s, &p_peek, 12 ) < 12 ) |
158 | 0 | { |
159 | 0 | msg_Warn( s, "unexpected end of file while reading list chunk" ); |
160 | 0 | return VLC_EGENERIC; |
161 | 0 | } |
162 | | |
163 | 0 | vlc_stream_Control( s, STREAM_CAN_SEEK, &b_seekable ); |
164 | |
|
165 | 0 | p_container->list.i_type = GetFOURCC( p_peek + 8 ); |
166 | | |
167 | | /* XXX fixed for on2 hack */ |
168 | 0 | if( p_container->common.i_chunk_fourcc == AVIFOURCC_ON2 && p_container->list.i_type == AVIFOURCC_ON2f ) |
169 | 0 | { |
170 | 0 | p_container->common.i_chunk_fourcc = AVIFOURCC_RIFF; |
171 | 0 | p_container->list.i_type = AVIFOURCC_AVI; |
172 | 0 | } |
173 | |
|
174 | 0 | if( p_container->common.i_chunk_fourcc == AVIFOURCC_LIST && |
175 | 0 | p_container->list.i_type == AVIFOURCC_movi ) |
176 | 0 | { |
177 | 0 | if( !b_seekable ) |
178 | 0 | return VLC_SUCCESS; |
179 | 0 | msg_Dbg( s, "skipping movi chunk" ); |
180 | 0 | return AVI_NextChunk( s, p_container ); /* points at beginning of LIST-movi if not seekable */ |
181 | 0 | } |
182 | | |
183 | 0 | if( vlc_stream_Read( s, NULL, 12 ) != 12 ) |
184 | 0 | { |
185 | 0 | msg_Warn( s, "cannot enter chunk" ); |
186 | 0 | return VLC_EGENERIC; |
187 | 0 | } |
188 | | |
189 | 0 | #ifdef AVI_DEBUG |
190 | 0 | msg_Dbg( s, |
191 | 0 | "found LIST chunk: \'%4.4s\'", |
192 | 0 | (char*)&p_container->list.i_type ); |
193 | 0 | #endif |
194 | 0 | msg_Dbg( s, "<list \'%4.4s\'>", (char*)&p_container->list.i_type ); |
195 | |
|
196 | 0 | avi_chunk_t **pp_append = &p_container->common.p_first; |
197 | 0 | for( ; ; ) |
198 | 0 | { |
199 | 0 | p_chk = calloc( 1, sizeof( avi_chunk_t ) ); |
200 | 0 | if( !p_chk ) |
201 | 0 | return VLC_EGENERIC; |
202 | | |
203 | 0 | i_ret = AVI_ChunkRead( s, p_chk, p_container ); |
204 | 0 | if( i_ret ) |
205 | 0 | { |
206 | 0 | AVI_ChunkClean( s, p_chk ); |
207 | 0 | free( p_chk ); |
208 | 0 | p_chk = NULL; |
209 | 0 | if( i_ret != AVI_ZEROSIZED_CHUNK ) |
210 | 0 | break; |
211 | 0 | } |
212 | | |
213 | 0 | if( p_chk ) |
214 | 0 | { |
215 | 0 | *pp_append = p_chk; |
216 | 0 | while( *pp_append ) |
217 | 0 | pp_append = &((*pp_append)->common.p_next); |
218 | 0 | } |
219 | |
|
220 | 0 | if( p_container->common.i_chunk_size > 0 && |
221 | 0 | vlc_stream_Tell( s ) >= AVI_ChunkEnd( p_container ) ) |
222 | 0 | { |
223 | 0 | break; |
224 | 0 | } |
225 | | |
226 | | /* If we can't seek then stop when we 've found LIST-movi */ |
227 | 0 | if( p_chk && |
228 | 0 | p_chk->common.i_chunk_fourcc == AVIFOURCC_LIST && |
229 | 0 | p_chk->list.i_type == AVIFOURCC_movi && |
230 | 0 | ( !b_seekable || p_chk->common.i_chunk_size == 0 ) ) |
231 | 0 | { |
232 | 0 | break; |
233 | 0 | } |
234 | |
|
235 | 0 | } |
236 | 0 | msg_Dbg( s, "</list \'%4.4s\'>%x", (char*)&p_container->list.i_type, i_ret ); |
237 | |
|
238 | 0 | if( i_ret == AVI_ZERO_FOURCC || i_ret == AVI_ZEROSIZED_CHUNK ) |
239 | 0 | return AVI_GotoNextChunk( s, p_container ); |
240 | | |
241 | 0 | return VLC_SUCCESS; |
242 | 0 | } |
243 | | |
244 | | /* Allow to append indexes after starting playback */ |
245 | | int AVI_ChunkFetchIndexes( stream_t *s, avi_chunk_t *p_riff ) |
246 | 0 | { |
247 | 0 | avi_chunk_t *p_movi = AVI_ChunkFind( p_riff, AVIFOURCC_movi, 0, true ); |
248 | 0 | if ( !p_movi ) |
249 | 0 | return VLC_EGENERIC; |
250 | | |
251 | 0 | avi_chunk_t *p_chk; |
252 | 0 | const uint64_t i_indexpos = AVI_ChunkEnd( p_movi ); |
253 | 0 | bool b_seekable = false; |
254 | 0 | int i_ret = VLC_SUCCESS; |
255 | |
|
256 | 0 | vlc_stream_Control( s, STREAM_CAN_SEEK, &b_seekable ); |
257 | 0 | if ( !b_seekable || vlc_stream_Seek( s, i_indexpos ) ) |
258 | 0 | return VLC_EGENERIC; |
259 | | |
260 | 0 | avi_chunk_t **pp_append = &p_riff->common.p_first; |
261 | 0 | for( ; ; ) |
262 | 0 | { |
263 | 0 | p_chk = calloc( 1, sizeof( avi_chunk_t ) ); |
264 | 0 | if( !p_chk ) |
265 | 0 | { |
266 | 0 | i_ret = VLC_EGENERIC; |
267 | 0 | break; |
268 | 0 | } |
269 | | |
270 | 0 | i_ret = AVI_ChunkRead( s, p_chk, p_riff ); |
271 | 0 | if( i_ret ) |
272 | 0 | { |
273 | 0 | AVI_ChunkClean( s, p_chk ); |
274 | 0 | free( p_chk ); |
275 | 0 | break; |
276 | 0 | } |
277 | | |
278 | 0 | *pp_append = p_chk; |
279 | 0 | while( *pp_append ) |
280 | 0 | pp_append = &((*pp_append)->common.p_next); |
281 | |
|
282 | 0 | if( p_chk->common.p_father->common.i_chunk_size > 0 && |
283 | 0 | ( vlc_stream_Tell( s ) > |
284 | 0 | p_chk->common.p_father->common.i_chunk_pos + |
285 | 0 | __EVEN( p_chk->common.p_father->common.i_chunk_size ) ) ) |
286 | 0 | { |
287 | 0 | break; |
288 | 0 | } |
289 | | |
290 | | /* If we can't seek then stop when we 've found any index */ |
291 | 0 | if( p_chk->common.i_chunk_fourcc == AVIFOURCC_indx || |
292 | 0 | p_chk->common.i_chunk_fourcc == AVIFOURCC_idx1 ) |
293 | 0 | { |
294 | 0 | break; |
295 | 0 | } |
296 | |
|
297 | 0 | } |
298 | |
|
299 | 0 | return i_ret; |
300 | 0 | } |
301 | | |
302 | | #define AVI_READCHUNK_ENTER \ |
303 | 0 | int64_t i_read = __EVEN(p_chk->common.i_chunk_size ) + 8; \ |
304 | 0 | if( i_read > 100000000 ) \ |
305 | 0 | { \ |
306 | 0 | msg_Err( s, "Big chunk ignored" ); \ |
307 | 0 | return VLC_EGENERIC; \ |
308 | 0 | } \ |
309 | 0 | uint8_t *p_read, *p_buff; \ |
310 | 0 | if( !( p_read = p_buff = malloc(i_read ) ) ) \ |
311 | 0 | { \ |
312 | 0 | return VLC_EGENERIC; \ |
313 | 0 | } \ |
314 | 0 | i_read = vlc_stream_Read( s, p_read, i_read ); \ |
315 | 0 | if( i_read < (int64_t)__EVEN(p_chk->common.i_chunk_size ) + 8 ) \ |
316 | 0 | { \ |
317 | 0 | free( p_buff ); \ |
318 | 0 | return VLC_EGENERIC; \ |
319 | 0 | }\ |
320 | 0 | p_read += 8; \ |
321 | 0 | i_read -= 8 |
322 | | |
323 | | #define AVI_READ( res, func, size ) \ |
324 | 0 | if( i_read < size ) { \ |
325 | 0 | free( p_buff); \ |
326 | 0 | return VLC_EGENERIC; \ |
327 | 0 | } \ |
328 | 0 | i_read -= size; \ |
329 | 0 | res = func( p_read ); \ |
330 | 0 | p_read += size \ |
331 | | |
332 | | #define AVI_READCHUNK_EXIT( code ) \ |
333 | 0 | do { \ |
334 | 0 | free( p_buff ); \ |
335 | 0 | return code; \ |
336 | 0 | } while(0) |
337 | | |
338 | | static inline uint8_t GetB( uint8_t *ptr ) |
339 | 0 | { |
340 | 0 | return *ptr; |
341 | 0 | } |
342 | | |
343 | | #define AVI_READ1BYTE( i_byte ) \ |
344 | 0 | AVI_READ( i_byte, GetB, 1 ) |
345 | | |
346 | | #define AVI_READ2BYTES( i_word ) \ |
347 | 0 | AVI_READ( i_word, GetWLE, 2 ) |
348 | | |
349 | | #define AVI_READ4BYTES( i_dword ) \ |
350 | 0 | AVI_READ( i_dword, GetDWLE, 4 ) |
351 | | |
352 | | #define AVI_READ8BYTES( i_qword ) \ |
353 | 0 | AVI_READ( i_qword, GetQWLE, 8 ) |
354 | | |
355 | | #define AVI_READFOURCC( i_dword ) \ |
356 | 0 | AVI_READ( i_dword, GetFOURCC, 4 ) |
357 | | |
358 | | static int AVI_ChunkRead_avih( stream_t *s, avi_chunk_t *p_chk ) |
359 | 0 | { |
360 | 0 | AVI_READCHUNK_ENTER; |
361 | |
|
362 | 0 | p_chk->common.i_chunk_fourcc = AVIFOURCC_avih; |
363 | 0 | AVI_READ4BYTES( p_chk->avih.i_microsecperframe); |
364 | 0 | AVI_READ4BYTES( p_chk->avih.i_maxbytespersec ); |
365 | 0 | AVI_READ4BYTES( p_chk->avih.i_reserved1 ); |
366 | 0 | AVI_READ4BYTES( p_chk->avih.i_flags ); |
367 | 0 | AVI_READ4BYTES( p_chk->avih.i_totalframes ); |
368 | 0 | AVI_READ4BYTES( p_chk->avih.i_initialframes ); |
369 | 0 | AVI_READ4BYTES( p_chk->avih.i_streams ); |
370 | 0 | AVI_READ4BYTES( p_chk->avih.i_suggestedbuffersize ); |
371 | 0 | AVI_READ4BYTES( p_chk->avih.i_width ); |
372 | 0 | AVI_READ4BYTES( p_chk->avih.i_height ); |
373 | 0 | AVI_READ4BYTES( p_chk->avih.i_scale ); |
374 | 0 | AVI_READ4BYTES( p_chk->avih.i_rate ); |
375 | 0 | AVI_READ4BYTES( p_chk->avih.i_start ); |
376 | 0 | AVI_READ4BYTES( p_chk->avih.i_length ); |
377 | 0 | #ifdef AVI_DEBUG |
378 | 0 | msg_Dbg( s, |
379 | 0 | "avih: streams:%d flags:%s%s%s%s %dx%d", |
380 | 0 | p_chk->avih.i_streams, |
381 | 0 | p_chk->avih.i_flags&AVIF_HASINDEX?" HAS_INDEX":"", |
382 | 0 | p_chk->avih.i_flags&AVIF_MUSTUSEINDEX?" MUST_USE_INDEX":"", |
383 | 0 | p_chk->avih.i_flags&AVIF_ISINTERLEAVED?" IS_INTERLEAVED":"", |
384 | 0 | p_chk->avih.i_flags&AVIF_TRUSTCKTYPE?" TRUST_CKTYPE":"", |
385 | 0 | p_chk->avih.i_width, p_chk->avih.i_height ); |
386 | 0 | #endif |
387 | 0 | AVI_READCHUNK_EXIT( VLC_SUCCESS ); |
388 | 0 | } |
389 | | |
390 | | static int AVI_ChunkRead_strh( stream_t *s, avi_chunk_t *p_chk ) |
391 | 0 | { |
392 | 0 | AVI_READCHUNK_ENTER; |
393 | |
|
394 | 0 | AVI_READFOURCC( p_chk->strh.i_type ); |
395 | 0 | AVI_READFOURCC( p_chk->strh.i_handler ); |
396 | 0 | AVI_READ4BYTES( p_chk->strh.i_flags ); |
397 | 0 | AVI_READ4BYTES( p_chk->strh.i_reserved1 ); |
398 | 0 | AVI_READ4BYTES( p_chk->strh.i_initialframes ); |
399 | 0 | AVI_READ4BYTES( p_chk->strh.i_scale ); |
400 | 0 | AVI_READ4BYTES( p_chk->strh.i_rate ); |
401 | 0 | AVI_READ4BYTES( p_chk->strh.i_start ); |
402 | 0 | AVI_READ4BYTES( p_chk->strh.i_length ); |
403 | 0 | AVI_READ4BYTES( p_chk->strh.i_suggestedbuffersize ); |
404 | 0 | AVI_READ4BYTES( p_chk->strh.i_quality ); |
405 | 0 | AVI_READ4BYTES( p_chk->strh.i_samplesize ); |
406 | 0 | #ifdef AVI_DEBUG |
407 | 0 | msg_Dbg( s, |
408 | 0 | "strh: type:%4.4s handler:0x%8.8x samplesize:%d %.2ffps", |
409 | 0 | (char*)&p_chk->strh.i_type, |
410 | 0 | p_chk->strh.i_handler, |
411 | 0 | p_chk->strh.i_samplesize, |
412 | 0 | ( p_chk->strh.i_scale ? |
413 | 0 | (float)p_chk->strh.i_rate / (float)p_chk->strh.i_scale : -1) ); |
414 | 0 | #endif |
415 | |
|
416 | 0 | AVI_READCHUNK_EXIT( VLC_SUCCESS ); |
417 | 0 | } |
418 | | |
419 | | static int AVI_ChunkRead_strf( stream_t *s, avi_chunk_t *p_chk ) |
420 | 0 | { |
421 | 0 | avi_chunk_t *p_strh; |
422 | |
|
423 | 0 | AVI_READCHUNK_ENTER; |
424 | 0 | if( p_chk->common.p_father == NULL ) |
425 | 0 | { |
426 | 0 | msg_Err( s, "malformed avi file" ); |
427 | 0 | AVI_READCHUNK_EXIT( VLC_EGENERIC ); |
428 | 0 | } |
429 | 0 | if( !( p_strh = AVI_ChunkFind( p_chk->common.p_father, AVIFOURCC_strh, 0, false ) ) ) |
430 | 0 | { |
431 | 0 | msg_Err( s, "malformed avi file" ); |
432 | 0 | AVI_READCHUNK_EXIT( p_chk->common.i_chunk_size > 0 ? VLC_EGENERIC : AVI_ZEROSIZED_CHUNK ); |
433 | 0 | } |
434 | | |
435 | 0 | switch( p_strh->strh.i_type ) |
436 | 0 | { |
437 | 0 | case( AVIFOURCC_auds ): |
438 | 0 | p_chk->strf.auds.i_cat = AUDIO_ES; |
439 | 0 | p_chk->strf.auds.p_wf = malloc( __MAX( p_chk->common.i_chunk_size, sizeof( WAVEFORMATEX ) ) ); |
440 | 0 | if ( !p_chk->strf.auds.p_wf ) |
441 | 0 | { |
442 | 0 | AVI_READCHUNK_EXIT( VLC_ENOMEM ); |
443 | 0 | } |
444 | 0 | AVI_READ2BYTES( p_chk->strf.auds.p_wf->wFormatTag ); |
445 | 0 | AVI_READ2BYTES( p_chk->strf.auds.p_wf->nChannels ); |
446 | 0 | AVI_READ4BYTES( p_chk->strf.auds.p_wf->nSamplesPerSec ); |
447 | 0 | AVI_READ4BYTES( p_chk->strf.auds.p_wf->nAvgBytesPerSec ); |
448 | 0 | AVI_READ2BYTES( p_chk->strf.auds.p_wf->nBlockAlign ); |
449 | 0 | AVI_READ2BYTES( p_chk->strf.auds.p_wf->wBitsPerSample ); |
450 | |
|
451 | 0 | if( p_chk->strf.auds.p_wf->wFormatTag != WAVE_FORMAT_PCM |
452 | 0 | && p_chk->common.i_chunk_size > sizeof( WAVEFORMATEX ) ) |
453 | 0 | { |
454 | 0 | AVI_READ2BYTES( p_chk->strf.auds.p_wf->cbSize ); |
455 | | |
456 | | /* prevent segfault */ |
457 | 0 | if( p_chk->strf.auds.p_wf->cbSize > |
458 | 0 | p_chk->common.i_chunk_size - sizeof( WAVEFORMATEX ) ) |
459 | 0 | { |
460 | 0 | p_chk->strf.auds.p_wf->cbSize = |
461 | 0 | p_chk->common.i_chunk_size - sizeof( WAVEFORMATEX ); |
462 | 0 | } |
463 | |
|
464 | 0 | if( p_chk->strf.auds.p_wf->wFormatTag == WAVE_FORMAT_EXTENSIBLE ) |
465 | 0 | { |
466 | 0 | msg_Dbg( s, "Extended header found" ); |
467 | 0 | } |
468 | 0 | } |
469 | 0 | else |
470 | 0 | { |
471 | 0 | p_chk->strf.auds.p_wf->cbSize = 0; |
472 | 0 | } |
473 | 0 | if( p_chk->strf.auds.p_wf->cbSize > 0 ) |
474 | 0 | { |
475 | 0 | memcpy( &p_chk->strf.auds.p_wf[1] , |
476 | 0 | p_buff + 8 + sizeof( WAVEFORMATEX ), /* 8=fourcc+size */ |
477 | 0 | p_chk->strf.auds.p_wf->cbSize ); |
478 | 0 | } |
479 | 0 | #ifdef AVI_DEBUG |
480 | 0 | msg_Dbg( s, |
481 | 0 | "strf: audio:0x%4.4x channels:%d %dHz %dbits/sample %dkbps", |
482 | 0 | p_chk->strf.auds.p_wf->wFormatTag, |
483 | 0 | p_chk->strf.auds.p_wf->nChannels, |
484 | 0 | p_chk->strf.auds.p_wf->nSamplesPerSec, |
485 | 0 | p_chk->strf.auds.p_wf->wBitsPerSample, |
486 | 0 | p_chk->strf.auds.p_wf->nAvgBytesPerSec * 8 / 1000 ); |
487 | 0 | #endif |
488 | 0 | break; |
489 | 0 | case( AVIFOURCC_vids ): |
490 | 0 | p_strh->strh.i_samplesize = 0; /* XXX for ffmpeg avi file */ |
491 | 0 | p_chk->strf.vids.i_cat = VIDEO_ES; |
492 | 0 | p_chk->strf.vids.p_bih = malloc( __MAX( p_chk->common.i_chunk_size, |
493 | 0 | sizeof( *p_chk->strf.vids.p_bih ) ) ); |
494 | 0 | if ( !p_chk->strf.vids.p_bih ) |
495 | 0 | { |
496 | 0 | AVI_READCHUNK_EXIT( VLC_ENOMEM ); |
497 | 0 | } |
498 | 0 | AVI_READ4BYTES( p_chk->strf.vids.p_bih->biSize ); |
499 | 0 | AVI_READ4BYTES( p_chk->strf.vids.p_bih->biWidth ); |
500 | 0 | AVI_READ4BYTES( p_chk->strf.vids.p_bih->biHeight ); |
501 | 0 | AVI_READ2BYTES( p_chk->strf.vids.p_bih->biPlanes ); |
502 | 0 | AVI_READ2BYTES( p_chk->strf.vids.p_bih->biBitCount ); |
503 | 0 | AVI_READFOURCC( p_chk->strf.vids.p_bih->biCompression ); |
504 | 0 | AVI_READ4BYTES( p_chk->strf.vids.p_bih->biSizeImage ); |
505 | 0 | AVI_READ4BYTES( p_chk->strf.vids.p_bih->biXPelsPerMeter ); |
506 | 0 | AVI_READ4BYTES( p_chk->strf.vids.p_bih->biYPelsPerMeter ); |
507 | 0 | AVI_READ4BYTES( p_chk->strf.vids.p_bih->biClrUsed ); |
508 | 0 | AVI_READ4BYTES( p_chk->strf.vids.p_bih->biClrImportant ); |
509 | 0 | if( p_chk->strf.vids.p_bih->biSize > p_chk->common.i_chunk_size ) |
510 | 0 | { |
511 | 0 | p_chk->strf.vids.p_bih->biSize = p_chk->common.i_chunk_size; |
512 | 0 | } |
513 | 0 | if ( p_chk->common.i_chunk_size > sizeof(VLC_BITMAPINFOHEADER) ) |
514 | 0 | { |
515 | 0 | uint64_t i_extrasize = p_chk->common.i_chunk_size - sizeof(VLC_BITMAPINFOHEADER); |
516 | | |
517 | | /* There's a color palette appended, set up VLC_BITMAPINFO */ |
518 | 0 | memcpy( &p_chk->strf.vids.p_bih[1], |
519 | 0 | p_buff + 8 + sizeof(VLC_BITMAPINFOHEADER), /* 8=fourrc+size */ |
520 | 0 | i_extrasize ); |
521 | |
|
522 | 0 | if ( !p_chk->strf.vids.p_bih->biClrUsed ) |
523 | 0 | { |
524 | 0 | if( p_chk->strf.vids.p_bih->biBitCount < 32 ) |
525 | 0 | p_chk->strf.vids.p_bih->biClrUsed = (1 << p_chk->strf.vids.p_bih->biBitCount); |
526 | 0 | else |
527 | 0 | p_chk->strf.vids.p_bih->biBitCount = UINT16_MAX; |
528 | 0 | } |
529 | |
|
530 | 0 | if( i_extrasize / sizeof(uint32_t) > UINT32_MAX ) |
531 | 0 | p_chk->strf.vids.p_bih->biClrUsed = UINT32_MAX; |
532 | 0 | else |
533 | 0 | { |
534 | 0 | p_chk->strf.vids.p_bih->biClrUsed = |
535 | 0 | __MIN( i_extrasize / sizeof(uint32_t), |
536 | 0 | p_chk->strf.vids.p_bih->biClrUsed ); |
537 | 0 | } |
538 | | |
539 | | /* stay within VLC's limits */ |
540 | 0 | p_chk->strf.vids.p_bih->biClrUsed = |
541 | 0 | __MIN( VIDEO_PALETTE_COLORS_MAX, p_chk->strf.vids.p_bih->biClrUsed ); |
542 | 0 | } |
543 | 0 | else p_chk->strf.vids.p_bih->biClrUsed = 0; |
544 | 0 | #ifdef AVI_DEBUG |
545 | 0 | msg_Dbg( s, |
546 | 0 | "strf: video:%4.4s %"PRIu32"x%"PRIu32" planes:%d %dbpp", |
547 | 0 | (char*)&p_chk->strf.vids.p_bih->biCompression, |
548 | 0 | (uint32_t)p_chk->strf.vids.p_bih->biWidth, |
549 | 0 | p_chk->strf.vids.p_bih->biHeight <= INT32_MAX ? p_chk->strf.vids.p_bih->biHeight |
550 | 0 | : -1 * p_chk->strf.vids.p_bih->biHeight, |
551 | 0 | p_chk->strf.vids.p_bih->biPlanes, |
552 | 0 | p_chk->strf.vids.p_bih->biBitCount ); |
553 | 0 | #endif |
554 | 0 | break; |
555 | 0 | case AVIFOURCC_iavs: |
556 | 0 | case AVIFOURCC_ivas: |
557 | 0 | p_chk->strf.common.i_cat = UNKNOWN_ES; |
558 | 0 | break; |
559 | 0 | case( AVIFOURCC_txts ): |
560 | 0 | p_chk->strf.common.i_cat = SPU_ES; |
561 | 0 | break; |
562 | 0 | default: |
563 | 0 | msg_Warn( s, "unknown stream type: %4.4s", |
564 | 0 | (char*)&p_strh->strh.i_type ); |
565 | 0 | p_chk->strf.common.i_cat = UNKNOWN_ES; |
566 | 0 | break; |
567 | 0 | } |
568 | 0 | AVI_READCHUNK_EXIT( VLC_SUCCESS ); |
569 | 0 | } |
570 | | static void AVI_ChunkFree_strf( avi_chunk_t *p_chk ) |
571 | 0 | { |
572 | 0 | avi_chunk_strf_t *p_strf = (avi_chunk_strf_t*)p_chk; |
573 | 0 | if( p_strf->common.i_cat == AUDIO_ES ) |
574 | 0 | { |
575 | 0 | FREENULL( p_strf->auds.p_wf ); |
576 | 0 | } |
577 | 0 | else if( p_strf->common.i_cat == VIDEO_ES ) |
578 | 0 | { |
579 | 0 | FREENULL( p_strf->vids.p_bih ); |
580 | 0 | } |
581 | 0 | } |
582 | | |
583 | | static int AVI_ChunkRead_strd( stream_t *s, avi_chunk_t *p_chk ) |
584 | 0 | { |
585 | 0 | if ( p_chk->common.i_chunk_size == 0 ) |
586 | 0 | { |
587 | 0 | msg_Dbg( s, "Zero sized pre-JUNK section met" ); |
588 | 0 | return AVI_ZEROSIZED_CHUNK; |
589 | 0 | } |
590 | | |
591 | 0 | AVI_READCHUNK_ENTER; |
592 | 0 | p_chk->strd.p_data = malloc( p_chk->common.i_chunk_size ); |
593 | 0 | if( p_chk->strd.p_data ) |
594 | 0 | memcpy( p_chk->strd.p_data, p_buff + 8, p_chk->common.i_chunk_size ); |
595 | 0 | AVI_READCHUNK_EXIT( p_chk->strd.p_data ? VLC_SUCCESS : VLC_EGENERIC ); |
596 | 0 | } |
597 | | |
598 | | static void AVI_ChunkFree_strd( avi_chunk_t *p_chk ) |
599 | 0 | { |
600 | 0 | free( p_chk->strd.p_data ); |
601 | 0 | } |
602 | | |
603 | | static int AVI_ChunkRead_idx1( stream_t *s, avi_chunk_t *p_chk ) |
604 | 0 | { |
605 | 0 | unsigned int i_count, i_index; |
606 | |
|
607 | 0 | AVI_READCHUNK_ENTER; |
608 | |
|
609 | 0 | i_count = __MIN( (int64_t)p_chk->common.i_chunk_size, i_read ) / 16; |
610 | |
|
611 | 0 | p_chk->idx1.i_entry_count = i_count; |
612 | 0 | p_chk->idx1.i_entry_max = i_count; |
613 | 0 | if( i_count > 0 ) |
614 | 0 | { |
615 | 0 | p_chk->idx1.entry = calloc( i_count, sizeof( idx1_entry_t ) ); |
616 | 0 | if( !p_chk->idx1.entry ) |
617 | 0 | AVI_READCHUNK_EXIT( VLC_EGENERIC ); |
618 | | |
619 | 0 | for( i_index = 0; i_index < i_count ; i_index++ ) |
620 | 0 | { |
621 | 0 | AVI_READFOURCC( p_chk->idx1.entry[i_index].i_fourcc ); |
622 | 0 | AVI_READ4BYTES( p_chk->idx1.entry[i_index].i_flags ); |
623 | 0 | AVI_READ4BYTES( p_chk->idx1.entry[i_index].i_pos ); |
624 | 0 | AVI_READ4BYTES( p_chk->idx1.entry[i_index].i_length ); |
625 | 0 | } |
626 | 0 | } |
627 | 0 | else |
628 | 0 | { |
629 | 0 | p_chk->idx1.entry = NULL; |
630 | 0 | } |
631 | 0 | #ifdef AVI_DEBUG |
632 | 0 | msg_Dbg( s, "idx1: index entry:%d", i_count ); |
633 | 0 | #endif |
634 | 0 | AVI_READCHUNK_EXIT( VLC_SUCCESS ); |
635 | 0 | } |
636 | | |
637 | | static void AVI_ChunkFree_idx1( avi_chunk_t *p_chk ) |
638 | 0 | { |
639 | 0 | p_chk->idx1.i_entry_count = 0; |
640 | 0 | p_chk->idx1.i_entry_max = 0; |
641 | 0 | FREENULL( p_chk->idx1.entry ); |
642 | 0 | } |
643 | | |
644 | | |
645 | | |
646 | | static int AVI_ChunkRead_indx( stream_t *s, avi_chunk_t *p_chk ) |
647 | 0 | { |
648 | 0 | unsigned int i_count, i; |
649 | 0 | int i_ret = VLC_SUCCESS; |
650 | 0 | int32_t i_dummy; |
651 | 0 | VLC_UNUSED(i_dummy); |
652 | 0 | avi_chunk_indx_t *p_indx = (avi_chunk_indx_t*)p_chk; |
653 | |
|
654 | 0 | AVI_READCHUNK_ENTER; |
655 | |
|
656 | 0 | AVI_READ2BYTES( p_indx->i_longsperentry ); |
657 | 0 | AVI_READ1BYTE ( p_indx->i_indexsubtype ); |
658 | 0 | AVI_READ1BYTE ( p_indx->i_indextype ); |
659 | 0 | AVI_READ4BYTES( p_indx->i_entriesinuse ); |
660 | |
|
661 | 0 | AVI_READ4BYTES( p_indx->i_id ); |
662 | 0 | p_indx->idx.std = NULL; |
663 | 0 | p_indx->idx.field = NULL; |
664 | 0 | p_indx->idx.super = NULL; |
665 | |
|
666 | 0 | if( p_indx->i_indextype == AVI_INDEX_OF_CHUNKS && p_indx->i_indexsubtype == 0 ) |
667 | 0 | { |
668 | 0 | AVI_READ8BYTES( p_indx->i_baseoffset ); |
669 | 0 | AVI_READ4BYTES( i_dummy ); |
670 | |
|
671 | 0 | i_count = __MIN( p_indx->i_entriesinuse, i_read / 8 ); |
672 | 0 | p_indx->i_entriesinuse = i_count; |
673 | 0 | p_indx->idx.std = calloc( i_count, sizeof( indx_std_entry_t ) ); |
674 | 0 | if( i_count == 0 || p_indx->idx.std ) |
675 | 0 | { |
676 | 0 | for( i = 0; i < i_count; i++ ) |
677 | 0 | { |
678 | 0 | AVI_READ4BYTES( p_indx->idx.std[i].i_offset ); |
679 | 0 | AVI_READ4BYTES( p_indx->idx.std[i].i_size ); |
680 | 0 | } |
681 | 0 | } |
682 | 0 | else i_ret = VLC_EGENERIC; |
683 | 0 | } |
684 | 0 | else if( p_indx->i_indextype == AVI_INDEX_OF_CHUNKS && p_indx->i_indexsubtype == AVI_INDEX_2FIELD ) |
685 | 0 | { |
686 | 0 | AVI_READ8BYTES( p_indx->i_baseoffset ); |
687 | 0 | AVI_READ4BYTES( i_dummy ); |
688 | |
|
689 | 0 | i_count = __MIN( p_indx->i_entriesinuse, i_read / 12 ); |
690 | 0 | p_indx->i_entriesinuse = i_count; |
691 | 0 | p_indx->idx.field = calloc( i_count, sizeof( indx_field_entry_t ) ); |
692 | 0 | if( i_count == 0 || p_indx->idx.field ) |
693 | 0 | { |
694 | 0 | for( i = 0; i < i_count; i++ ) |
695 | 0 | { |
696 | 0 | AVI_READ4BYTES( p_indx->idx.field[i].i_offset ); |
697 | 0 | AVI_READ4BYTES( p_indx->idx.field[i].i_size ); |
698 | 0 | AVI_READ4BYTES( p_indx->idx.field[i].i_offsetfield2 ); |
699 | 0 | } |
700 | 0 | } |
701 | 0 | else i_ret = VLC_EGENERIC; |
702 | 0 | } |
703 | 0 | else if( p_indx->i_indextype == AVI_INDEX_OF_INDEXES ) |
704 | 0 | { |
705 | 0 | p_indx->i_baseoffset = 0; |
706 | 0 | AVI_READ4BYTES( i_dummy ); |
707 | 0 | AVI_READ4BYTES( i_dummy ); |
708 | 0 | AVI_READ4BYTES( i_dummy ); |
709 | |
|
710 | 0 | i_count = __MIN( p_indx->i_entriesinuse, i_read / 16 ); |
711 | 0 | p_indx->i_entriesinuse = i_count; |
712 | 0 | p_indx->idx.super = calloc( i_count, sizeof( indx_super_entry_t ) ); |
713 | 0 | if( i_count == 0 || p_indx->idx.super ) |
714 | 0 | { |
715 | 0 | for( i = 0; i < i_count; i++ ) |
716 | 0 | { |
717 | 0 | AVI_READ8BYTES( p_indx->idx.super[i].i_offset ); |
718 | 0 | AVI_READ4BYTES( p_indx->idx.super[i].i_size ); |
719 | 0 | AVI_READ4BYTES( p_indx->idx.super[i].i_duration ); |
720 | 0 | } |
721 | 0 | } |
722 | 0 | else i_ret = VLC_EGENERIC; |
723 | 0 | } |
724 | 0 | else |
725 | 0 | { |
726 | 0 | msg_Warn( s, "unknown type/subtype index" ); |
727 | 0 | } |
728 | | |
729 | 0 | #ifdef AVI_DEBUG |
730 | 0 | msg_Dbg( s, "indx: type=%d subtype=%d entry=%d", |
731 | 0 | p_indx->i_indextype, p_indx->i_indexsubtype, p_indx->i_entriesinuse ); |
732 | 0 | #endif |
733 | 0 | AVI_READCHUNK_EXIT( i_ret ); |
734 | 0 | } |
735 | | static void AVI_ChunkFree_indx( avi_chunk_t *p_chk ) |
736 | 0 | { |
737 | 0 | avi_chunk_indx_t *p_indx = (avi_chunk_indx_t*)p_chk; |
738 | |
|
739 | 0 | FREENULL( p_indx->idx.std ); |
740 | 0 | FREENULL( p_indx->idx.field ); |
741 | 0 | FREENULL( p_indx->idx.super ); |
742 | 0 | } |
743 | | |
744 | | static int AVI_ChunkRead_vprp( stream_t *s, avi_chunk_t *p_chk ) |
745 | 0 | { |
746 | 0 | avi_chunk_vprp_t *p_vprp = (avi_chunk_vprp_t*)p_chk; |
747 | |
|
748 | 0 | AVI_READCHUNK_ENTER; |
749 | |
|
750 | 0 | AVI_READ4BYTES( p_vprp->i_video_format_token ); |
751 | 0 | AVI_READ4BYTES( p_vprp->i_video_standard ); |
752 | 0 | AVI_READ4BYTES( p_vprp->i_vertical_refresh ); |
753 | 0 | AVI_READ4BYTES( p_vprp->i_h_total_in_t ); |
754 | 0 | AVI_READ4BYTES( p_vprp->i_v_total_in_lines ); |
755 | 0 | AVI_READ4BYTES( p_vprp->i_frame_aspect_ratio ); |
756 | 0 | AVI_READ4BYTES( p_vprp->i_frame_width_in_pixels ); |
757 | 0 | AVI_READ4BYTES( p_vprp->i_frame_height_in_pixels ); |
758 | 0 | AVI_READ4BYTES( p_vprp->i_nb_fields_per_frame ); |
759 | 0 | for( unsigned i = 0; i < __MIN( p_vprp->i_nb_fields_per_frame, 2 ); i++ ) |
760 | 0 | { |
761 | 0 | AVI_READ4BYTES( p_vprp->field_info[i].i_compressed_bm_height ); |
762 | 0 | AVI_READ4BYTES( p_vprp->field_info[i].i_compressed_bm_width ); |
763 | 0 | AVI_READ4BYTES( p_vprp->field_info[i].i_valid_bm_height ); |
764 | 0 | AVI_READ4BYTES( p_vprp->field_info[i].i_valid_bm_width ); |
765 | 0 | AVI_READ4BYTES( p_vprp->field_info[i].i_valid_bm_x_offset ); |
766 | 0 | AVI_READ4BYTES( p_vprp->field_info[i].i_valid_bm_y_offset ); |
767 | 0 | AVI_READ4BYTES( p_vprp->field_info[i].i_video_x_offset_in_t ); |
768 | 0 | AVI_READ4BYTES( p_vprp->field_info[i].i_video_y_valid_start_line ); |
769 | 0 | } |
770 | | |
771 | 0 | #ifdef AVI_DEBUG |
772 | 0 | msg_Dbg( s, "vprp: format:%d standard:%d", |
773 | 0 | p_vprp->i_video_format_token, p_vprp->i_video_standard ); |
774 | 0 | #endif |
775 | 0 | AVI_READCHUNK_EXIT( VLC_SUCCESS ); |
776 | 0 | } |
777 | | |
778 | | static int AVI_ChunkRead_dmlh( stream_t *s, avi_chunk_t *p_chk ) |
779 | 0 | { |
780 | 0 | avi_chunk_dmlh_t *p_dmlh = (avi_chunk_dmlh_t*)p_chk; |
781 | |
|
782 | 0 | AVI_READCHUNK_ENTER; |
783 | |
|
784 | 0 | AVI_READ4BYTES( p_dmlh->dwTotalFrames ); |
785 | |
|
786 | 0 | #ifdef AVI_DEBUG |
787 | 0 | msg_Dbg( s, "dmlh: dwTotalFrames %d", |
788 | 0 | p_dmlh->dwTotalFrames ); |
789 | 0 | #endif |
790 | 0 | AVI_READCHUNK_EXIT( VLC_SUCCESS ); |
791 | 0 | } |
792 | | |
793 | | static const struct |
794 | | { |
795 | | vlc_fourcc_t i_fourcc; |
796 | | const char *psz_type; |
797 | | } AVI_strz_type[] = |
798 | | { |
799 | | { AVIFOURCC_IARL, "Archive location" }, |
800 | | { AVIFOURCC_IART, "Artist" }, |
801 | | { AVIFOURCC_ICMS, "Commisioned" }, |
802 | | { AVIFOURCC_ICMT, "Comments" }, |
803 | | { AVIFOURCC_ICOP, "Copyright" }, |
804 | | { AVIFOURCC_ICRD, "Creation date" }, |
805 | | { AVIFOURCC_ICRP, "Cropped" }, |
806 | | { AVIFOURCC_IDIM, "Dimensions" }, |
807 | | { AVIFOURCC_IDPI, "Dots per inch" }, |
808 | | { AVIFOURCC_IENG, "Engineer" }, |
809 | | { AVIFOURCC_IGNR, "Genre" }, |
810 | | { AVIFOURCC_ISGN, "Secondary Genre" }, |
811 | | { AVIFOURCC_IKEY, "Keywords" }, |
812 | | { AVIFOURCC_ILGT, "Lightness" }, |
813 | | { AVIFOURCC_IMED, "Medium" }, |
814 | | { AVIFOURCC_INAM, "Name" }, |
815 | | { AVIFOURCC_IPLT, "Palette setting" }, |
816 | | { AVIFOURCC_IPRD, "Product" }, |
817 | | { AVIFOURCC_ISBJ, "Subject" }, |
818 | | { AVIFOURCC_ISFT, "Software" }, |
819 | | { AVIFOURCC_ISHP, "Sharpness" }, |
820 | | { AVIFOURCC_ISRC, "Source" }, |
821 | | { AVIFOURCC_ISRF, "Source form" }, |
822 | | { AVIFOURCC_ITCH, "Technician" }, |
823 | | { AVIFOURCC_ISMP, "Time code" }, |
824 | | { AVIFOURCC_IDIT, "Digitalization time" }, |
825 | | { AVIFOURCC_IWRI, "Writer" }, |
826 | | { AVIFOURCC_IPRO, "Producer" }, |
827 | | { AVIFOURCC_ICNM, "Cinematographer" }, |
828 | | { AVIFOURCC_IPDS, "Production designer" }, |
829 | | { AVIFOURCC_IEDT, "Editor" }, |
830 | | { AVIFOURCC_ICDS, "Costume designer" }, |
831 | | { AVIFOURCC_IMUS, "Music" }, |
832 | | { AVIFOURCC_ISTD, "Production studio" }, |
833 | | { AVIFOURCC_IDST, "Distributor" }, |
834 | | { AVIFOURCC_ICNT, "Country" }, |
835 | | { AVIFOURCC_ISTR, "Starring" }, |
836 | | { AVIFOURCC_IFRM, "Total number of parts" }, |
837 | | { AVIFOURCC_strn, "Stream name" }, |
838 | | { AVIFOURCC_IAS1, "First Language" }, |
839 | | { AVIFOURCC_IAS2, "Second Language" }, |
840 | | { AVIFOURCC_IAS3, "Third Language" }, |
841 | | { AVIFOURCC_IAS4, "Fourth Language" }, |
842 | | { AVIFOURCC_IAS5, "Fifth Language" }, |
843 | | { AVIFOURCC_IAS6, "Sixth Language" }, |
844 | | { AVIFOURCC_IAS7, "Seventh Language" }, |
845 | | { AVIFOURCC_IAS8, "Eighth Language" }, |
846 | | { AVIFOURCC_IAS9, "Ninth Language" }, |
847 | | |
848 | | { 0, "???" } |
849 | | }; |
850 | | |
851 | | static int AVI_ChunkRead_strz( stream_t *s, avi_chunk_t *p_chk ) |
852 | 0 | { |
853 | 0 | int i_index; |
854 | 0 | avi_chunk_STRING_t *p_strz = (avi_chunk_STRING_t*)p_chk; |
855 | 0 | AVI_READCHUNK_ENTER; |
856 | |
|
857 | 0 | for( i_index = 0;; i_index++) |
858 | 0 | { |
859 | 0 | if( !AVI_strz_type[i_index].i_fourcc || |
860 | 0 | AVI_strz_type[i_index].i_fourcc == p_strz->i_chunk_fourcc ) |
861 | 0 | { |
862 | 0 | break; |
863 | 0 | } |
864 | 0 | } |
865 | 0 | p_strz->p_type = strdup( AVI_strz_type[i_index].psz_type ); |
866 | 0 | p_strz->p_str = malloc( p_strz->i_chunk_size + 1 ); |
867 | 0 | if( !p_strz->p_type || !p_strz->p_str ) |
868 | 0 | { |
869 | 0 | free( p_strz->p_type ); |
870 | 0 | free( p_strz->p_str ); |
871 | 0 | AVI_READCHUNK_EXIT( VLC_EGENERIC ); |
872 | 0 | } |
873 | 0 | memcpy( p_strz->p_str, p_read, p_strz->i_chunk_size ); |
874 | 0 | p_strz->p_str[p_strz->i_chunk_size] = 0; |
875 | |
|
876 | 0 | #ifdef AVI_DEBUG |
877 | 0 | msg_Dbg( s, "%4.4s: %s : %s", |
878 | 0 | (char*)&p_strz->i_chunk_fourcc, p_strz->p_type, p_strz->p_str); |
879 | 0 | #endif |
880 | 0 | AVI_READCHUNK_EXIT( VLC_SUCCESS ); |
881 | 0 | } |
882 | | static void AVI_ChunkFree_strz( avi_chunk_t *p_chk ) |
883 | 0 | { |
884 | 0 | avi_chunk_STRING_t *p_strz = (avi_chunk_STRING_t*)p_chk; |
885 | 0 | FREENULL( p_strz->p_type ); |
886 | 0 | FREENULL( p_strz->p_str ); |
887 | 0 | } |
888 | | |
889 | | static int AVI_ChunkRead_nothing( stream_t *s, avi_chunk_t *p_chk ) |
890 | 0 | { |
891 | 0 | return AVI_NextChunk( s, p_chk ); |
892 | 0 | } |
893 | | static void AVI_ChunkFree_nothing( avi_chunk_t *p_chk ) |
894 | 0 | { |
895 | 0 | VLC_UNUSED( p_chk ); |
896 | 0 | } |
897 | | |
898 | | static const struct |
899 | | { |
900 | | vlc_fourcc_t i_fourcc; |
901 | | int (*AVI_ChunkRead_function)( stream_t *s, avi_chunk_t *p_chk ); |
902 | | void (*AVI_ChunkFree_function)( avi_chunk_t *p_chk ); |
903 | | } AVI_Chunk_Function [] = |
904 | | { |
905 | | { AVIFOURCC_RIFF, AVI_ChunkRead_list, AVI_ChunkFree_nothing }, |
906 | | { AVIFOURCC_ON2, AVI_ChunkRead_list, AVI_ChunkFree_nothing }, |
907 | | { AVIFOURCC_LIST, AVI_ChunkRead_list, AVI_ChunkFree_nothing }, |
908 | | { AVIFOURCC_avih, AVI_ChunkRead_avih, AVI_ChunkFree_nothing }, |
909 | | { AVIFOURCC_ON2h, AVI_ChunkRead_avih, AVI_ChunkFree_nothing }, |
910 | | { AVIFOURCC_strh, AVI_ChunkRead_strh, AVI_ChunkFree_nothing }, |
911 | | { AVIFOURCC_strf, AVI_ChunkRead_strf, AVI_ChunkFree_strf }, |
912 | | { AVIFOURCC_strd, AVI_ChunkRead_strd, AVI_ChunkFree_strd }, |
913 | | { AVIFOURCC_idx1, AVI_ChunkRead_idx1, AVI_ChunkFree_idx1 }, |
914 | | { AVIFOURCC_indx, AVI_ChunkRead_indx, AVI_ChunkFree_indx }, |
915 | | { AVIFOURCC_vprp, AVI_ChunkRead_vprp, AVI_ChunkFree_nothing }, |
916 | | { AVIFOURCC_JUNK, AVI_ChunkRead_nothing, AVI_ChunkFree_nothing }, |
917 | | { AVIFOURCC_dmlh, AVI_ChunkRead_dmlh, AVI_ChunkFree_nothing }, |
918 | | |
919 | | { AVIFOURCC_IARL, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
920 | | { AVIFOURCC_IART, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
921 | | { AVIFOURCC_ICMS, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
922 | | { AVIFOURCC_ICMT, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
923 | | { AVIFOURCC_ICOP, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
924 | | { AVIFOURCC_ICRD, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
925 | | { AVIFOURCC_ICRP, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
926 | | { AVIFOURCC_IDIM, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
927 | | { AVIFOURCC_IDPI, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
928 | | { AVIFOURCC_IENG, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
929 | | { AVIFOURCC_IGNR, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
930 | | { AVIFOURCC_ISGN, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
931 | | { AVIFOURCC_IKEY, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
932 | | { AVIFOURCC_ILGT, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
933 | | { AVIFOURCC_IMED, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
934 | | { AVIFOURCC_INAM, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
935 | | { AVIFOURCC_IPLT, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
936 | | { AVIFOURCC_IPRD, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
937 | | { AVIFOURCC_ISBJ, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
938 | | { AVIFOURCC_ISFT, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
939 | | { AVIFOURCC_ISHP, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
940 | | { AVIFOURCC_ISRC, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
941 | | { AVIFOURCC_ISRF, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
942 | | { AVIFOURCC_ITCH, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
943 | | { AVIFOURCC_ISMP, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
944 | | { AVIFOURCC_IDIT, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
945 | | { AVIFOURCC_ILNG, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
946 | | { AVIFOURCC_IRTD, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
947 | | { AVIFOURCC_IWEB, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
948 | | { AVIFOURCC_IPRT, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
949 | | { AVIFOURCC_IWRI, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
950 | | { AVIFOURCC_IPRO, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
951 | | { AVIFOURCC_ICNM, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
952 | | { AVIFOURCC_IPDS, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
953 | | { AVIFOURCC_IEDT, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
954 | | { AVIFOURCC_ICDS, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
955 | | { AVIFOURCC_IMUS, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
956 | | { AVIFOURCC_ISTD, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
957 | | { AVIFOURCC_IDST, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
958 | | { AVIFOURCC_ICNT, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
959 | | { AVIFOURCC_ISTR, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
960 | | { AVIFOURCC_IFRM, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
961 | | { AVIFOURCC_IAS1, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
962 | | { AVIFOURCC_IAS2, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
963 | | { AVIFOURCC_IAS3, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
964 | | { AVIFOURCC_IAS4, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
965 | | { AVIFOURCC_IAS5, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
966 | | { AVIFOURCC_IAS6, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
967 | | { AVIFOURCC_IAS7, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
968 | | { AVIFOURCC_IAS8, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
969 | | { AVIFOURCC_IAS9, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
970 | | |
971 | | |
972 | | { AVIFOURCC_strn, AVI_ChunkRead_strz, AVI_ChunkFree_strz }, |
973 | | { 0, NULL, NULL } |
974 | | }; |
975 | | |
976 | | static int AVI_ChunkFunctionFind( vlc_fourcc_t i_fourcc ) |
977 | 0 | { |
978 | 0 | unsigned int i_index; |
979 | 0 | for( i_index = 0; ; i_index++ ) |
980 | 0 | { |
981 | 0 | if( ( AVI_Chunk_Function[i_index].i_fourcc == i_fourcc )|| |
982 | 0 | ( AVI_Chunk_Function[i_index].i_fourcc == 0 ) ) |
983 | 0 | { |
984 | 0 | return i_index; |
985 | 0 | } |
986 | 0 | } |
987 | 0 | } |
988 | | |
989 | | int AVI_ChunkRead( stream_t *s, avi_chunk_t *p_chk, avi_chunk_t *p_father ) |
990 | 0 | { |
991 | 0 | int i_index; |
992 | |
|
993 | 0 | if( !p_chk ) |
994 | 0 | { |
995 | 0 | msg_Warn( s, "cannot read null chunk" ); |
996 | 0 | return VLC_EGENERIC; |
997 | 0 | } |
998 | | |
999 | 0 | if( AVI_ChunkReadCommon( s, p_chk, p_father ) ) |
1000 | 0 | return VLC_EGENERIC; |
1001 | | |
1002 | 0 | if( p_chk->common.i_chunk_fourcc == VLC_FOURCC( 0, 0, 0, 0 ) ) |
1003 | 0 | { |
1004 | 0 | msg_Warn( s, "found null fourcc chunk (corrupted file?)" ); |
1005 | 0 | return AVI_ZERO_FOURCC; |
1006 | 0 | } |
1007 | 0 | p_chk->common.p_father = p_father; |
1008 | |
|
1009 | 0 | i_index = AVI_ChunkFunctionFind( p_chk->common.i_chunk_fourcc ); |
1010 | 0 | if( AVI_Chunk_Function[i_index].AVI_ChunkRead_function ) |
1011 | 0 | { |
1012 | 0 | return AVI_Chunk_Function[i_index].AVI_ChunkRead_function( s, p_chk ); |
1013 | 0 | } |
1014 | 0 | else if( ( ((char*)&p_chk->common.i_chunk_fourcc)[0] == 'i' && |
1015 | 0 | ((char*)&p_chk->common.i_chunk_fourcc)[1] == 'x' ) || |
1016 | 0 | ( ((char*)&p_chk->common.i_chunk_fourcc)[2] == 'i' && |
1017 | 0 | ((char*)&p_chk->common.i_chunk_fourcc)[3] == 'x' ) ) |
1018 | 0 | { |
1019 | 0 | p_chk->common.i_chunk_fourcc = AVIFOURCC_indx; |
1020 | 0 | return AVI_ChunkRead_indx( s, p_chk ); |
1021 | 0 | } |
1022 | | |
1023 | 0 | msg_Warn( s, "unknown chunk: %4.4s (not loaded)", |
1024 | 0 | (char*)&p_chk->common.i_chunk_fourcc ); |
1025 | 0 | return AVI_NextChunk( s, p_chk ); |
1026 | 0 | } |
1027 | | |
1028 | | void AVI_ChunkClean( stream_t *s, |
1029 | | avi_chunk_t *p_chk ) |
1030 | 0 | { |
1031 | 0 | int i_index; |
1032 | 0 | avi_chunk_t *p_child, *p_next; |
1033 | |
|
1034 | 0 | if( !p_chk ) |
1035 | 0 | { |
1036 | 0 | return; |
1037 | 0 | } |
1038 | | |
1039 | | /* Free all child chunk */ |
1040 | 0 | p_child = p_chk->common.p_first; |
1041 | 0 | while( p_child ) |
1042 | 0 | { |
1043 | 0 | p_next = p_child->common.p_next; |
1044 | 0 | AVI_ChunkClean( s, p_child ); |
1045 | 0 | free( p_child ); |
1046 | 0 | p_child = p_next; |
1047 | 0 | } |
1048 | |
|
1049 | 0 | i_index = AVI_ChunkFunctionFind( p_chk->common.i_chunk_fourcc ); |
1050 | 0 | if( AVI_Chunk_Function[i_index].AVI_ChunkFree_function ) |
1051 | 0 | { |
1052 | 0 | #ifdef AVI_DEBUG |
1053 | 0 | msg_Dbg( s, "free chunk %4.4s", |
1054 | 0 | (char*)&p_chk->common.i_chunk_fourcc ); |
1055 | 0 | #endif |
1056 | 0 | AVI_Chunk_Function[i_index].AVI_ChunkFree_function( p_chk); |
1057 | 0 | } |
1058 | 0 | else if( p_chk->common.i_chunk_fourcc != 0 ) |
1059 | 0 | { |
1060 | 0 | msg_Warn( s, "unknown chunk: %4.4s (not unloaded)", |
1061 | 0 | (char*)&p_chk->common.i_chunk_fourcc ); |
1062 | 0 | } |
1063 | 0 | p_chk->common.p_first = NULL; |
1064 | |
|
1065 | 0 | return; |
1066 | 0 | } |
1067 | | |
1068 | | static void AVI_ChunkDumpDebug_level( vlc_object_t *p_obj, |
1069 | | avi_chunk_t *p_chk, unsigned i_level ) |
1070 | 0 | { |
1071 | 0 | avi_chunk_t *p_child; |
1072 | |
|
1073 | 0 | char str[512]; |
1074 | 0 | if( i_level >= (sizeof(str) - 1)/4 ) |
1075 | 0 | return; |
1076 | | |
1077 | 0 | memset( str, ' ', sizeof( str ) ); |
1078 | 0 | for( unsigned i = 1; i < i_level; i++ ) |
1079 | 0 | { |
1080 | 0 | str[i * 4] = '|'; |
1081 | 0 | } |
1082 | 0 | if( p_chk->common.i_chunk_fourcc == AVIFOURCC_RIFF || |
1083 | 0 | p_chk->common.i_chunk_fourcc == AVIFOURCC_ON2 || |
1084 | 0 | p_chk->common.i_chunk_fourcc == AVIFOURCC_LIST ) |
1085 | 0 | { |
1086 | 0 | snprintf( &str[i_level * 4], sizeof(str) - 4*i_level, |
1087 | 0 | "%c %4.4s-%4.4s size:%"PRIu64" pos:%"PRIu64, |
1088 | 0 | i_level ? '+' : '*', |
1089 | 0 | (char*)&p_chk->common.i_chunk_fourcc, |
1090 | 0 | (char*)&p_chk->list.i_type, |
1091 | 0 | p_chk->common.i_chunk_size, |
1092 | 0 | p_chk->common.i_chunk_pos ); |
1093 | 0 | } |
1094 | 0 | else |
1095 | 0 | { |
1096 | 0 | snprintf( &str[i_level * 4], sizeof(str) - 4*i_level, |
1097 | 0 | "+ %4.4s size:%"PRIu64" pos:%"PRIu64, |
1098 | 0 | (char*)&p_chk->common.i_chunk_fourcc, |
1099 | 0 | p_chk->common.i_chunk_size, |
1100 | 0 | p_chk->common.i_chunk_pos ); |
1101 | 0 | } |
1102 | 0 | msg_Dbg( p_obj, "%s", str ); |
1103 | |
|
1104 | 0 | p_child = p_chk->common.p_first; |
1105 | 0 | while( p_child ) |
1106 | 0 | { |
1107 | 0 | AVI_ChunkDumpDebug_level( p_obj, p_child, i_level + 1 ); |
1108 | 0 | p_child = p_child->common.p_next; |
1109 | 0 | } |
1110 | 0 | } |
1111 | | |
1112 | | int AVI_ChunkReadRoot( stream_t *s, avi_chunk_t *p_root ) |
1113 | 0 | { |
1114 | 0 | avi_chunk_list_t *p_list = (avi_chunk_list_t*)p_root; |
1115 | 0 | avi_chunk_t *p_chk; |
1116 | 0 | bool b_seekable; |
1117 | |
|
1118 | 0 | vlc_stream_Control( s, STREAM_CAN_SEEK, &b_seekable ); |
1119 | |
|
1120 | 0 | p_list->i_chunk_pos = 0; |
1121 | 0 | p_list->i_chunk_size = ((UINT64_MAX - 12) >> 1) << 1; |
1122 | 0 | p_list->i_chunk_fourcc = AVIFOURCC_LIST; |
1123 | 0 | p_list->p_father = NULL; |
1124 | 0 | p_list->p_next = NULL; |
1125 | 0 | p_list->p_first = NULL; |
1126 | |
|
1127 | 0 | p_list->i_type = VLC_FOURCC( 'r', 'o', 'o', 't' ); |
1128 | |
|
1129 | 0 | avi_chunk_t **pp_append = &p_root->common.p_first; |
1130 | 0 | for( ; ; ) |
1131 | 0 | { |
1132 | 0 | p_chk = calloc( 1, sizeof( avi_chunk_t ) ); |
1133 | 0 | if( !p_chk ) |
1134 | 0 | return VLC_EGENERIC; |
1135 | | |
1136 | 0 | if( AVI_ChunkRead( s, p_chk, p_root ) != VLC_SUCCESS ) |
1137 | 0 | { |
1138 | 0 | AVI_ChunkClean( s, p_chk ); |
1139 | 0 | free( p_chk ); |
1140 | 0 | break; |
1141 | 0 | } |
1142 | | |
1143 | 0 | *pp_append = p_chk; |
1144 | 0 | while( *pp_append ) |
1145 | 0 | pp_append = &((*pp_append)->common.p_next); |
1146 | |
|
1147 | 0 | if( vlc_stream_Tell( s ) >= |
1148 | 0 | p_chk->common.p_father->common.i_chunk_pos + |
1149 | 0 | __EVEN( p_chk->common.p_father->common.i_chunk_size ) ) |
1150 | 0 | { |
1151 | 0 | break; |
1152 | 0 | } |
1153 | | |
1154 | | /* If we can't seek then stop when we 've found first RIFF-AVI */ |
1155 | 0 | if( p_chk->common.i_chunk_fourcc == AVIFOURCC_RIFF && |
1156 | 0 | p_chk->list.i_type == AVIFOURCC_AVI && !b_seekable ) |
1157 | 0 | { |
1158 | 0 | break; |
1159 | 0 | } |
1160 | 0 | } |
1161 | | |
1162 | 0 | p_list->i_chunk_size = stream_Size( s ); |
1163 | |
|
1164 | 0 | AVI_ChunkDumpDebug_level( VLC_OBJECT(s), p_root, 0 ); |
1165 | 0 | return VLC_SUCCESS; |
1166 | 0 | } |
1167 | | |
1168 | | void AVI_ChunkFreeRoot( stream_t *s, |
1169 | | avi_chunk_t *p_chk ) |
1170 | 0 | { |
1171 | 0 | AVI_ChunkClean( s, p_chk ); |
1172 | 0 | } |
1173 | | |
1174 | | |
1175 | | int AVI_ChunkCount_( avi_chunk_t *p_chk, vlc_fourcc_t i_fourcc, bool b_list ) |
1176 | 0 | { |
1177 | 0 | if( !p_chk ) |
1178 | 0 | return 0; |
1179 | | |
1180 | 0 | int i_count = 0; |
1181 | 0 | for( avi_chunk_t *p_child = p_chk->common.p_first; |
1182 | 0 | p_child; p_child = p_child->common.p_next ) |
1183 | 0 | { |
1184 | 0 | if( b_list && p_child->list.i_type == 0 ) |
1185 | 0 | continue; |
1186 | | |
1187 | 0 | if( p_child->common.i_chunk_fourcc != i_fourcc && |
1188 | 0 | (!b_list || p_child->list.i_type != i_fourcc) ) |
1189 | 0 | continue; |
1190 | | |
1191 | 0 | i_count++; |
1192 | 0 | } |
1193 | |
|
1194 | 0 | return i_count; |
1195 | 0 | } |
1196 | | |
1197 | | void *AVI_ChunkFind_( avi_chunk_t *p_chk, |
1198 | | vlc_fourcc_t i_fourcc, int i_number, bool b_list ) |
1199 | 0 | { |
1200 | 0 | if( !p_chk ) |
1201 | 0 | return NULL; |
1202 | | |
1203 | 0 | for( avi_chunk_t *p_child = p_chk->common.p_first; |
1204 | 0 | p_child; p_child = p_child->common.p_next ) |
1205 | 0 | { |
1206 | 0 | if( b_list && p_child->list.i_type == 0 ) |
1207 | 0 | continue; |
1208 | | |
1209 | 0 | if( p_child->common.i_chunk_fourcc != i_fourcc && |
1210 | 0 | (!b_list || p_child->list.i_type != i_fourcc) ) |
1211 | 0 | continue; |
1212 | | |
1213 | 0 | if( i_number-- == 0 ) |
1214 | 0 | return p_child; /* We found it */ |
1215 | 0 | } |
1216 | | |
1217 | 0 | return NULL; |
1218 | 0 | } |
1219 | | |