/src/gpac/src/media_tools/avilib.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * avilib.c |
3 | | * |
4 | | * Copyright (C) Thomas ostreich - June 2001 |
5 | | * multiple audio track support Copyright (C) 2002 Thomas ostreich |
6 | | * |
7 | | * Original code: |
8 | | * Copyright (C) 1999 Rainer Johanni <Rainer@Johanni.de> |
9 | | * |
10 | | * This file is part of transcode, a linux video stream processing tool |
11 | | * |
12 | | * transcode is free software; you can redistribute it and/or modify |
13 | | * it under the terms of the GNU Lesser General Public License as published by |
14 | | * the Free Software Foundation; either version 2, or (at your option) |
15 | | * any later version. |
16 | | * |
17 | | * transcode is distributed in the hope that it will be useful, |
18 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
19 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
20 | | * GNU Lesser General Public License for more details. |
21 | | * |
22 | | * You should have received a copy of the GNU Lesser General Public |
23 | | * License aint with this library; see the file COPYING. If not, write to |
24 | | * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. |
25 | | * |
26 | | */ |
27 | | |
28 | | #include <gpac/setup.h> |
29 | | |
30 | | #ifndef GPAC_DISABLE_AVILIB |
31 | | |
32 | | #include <gpac/internal/avilib.h> |
33 | | |
34 | | |
35 | | #define INFO_LIST |
36 | | |
37 | | // add a new riff chunk after XX MB |
38 | | #define NEW_RIFF_THRES (1900*1024*1024) |
39 | | |
40 | | // Maximum number of indices per stream |
41 | 0 | #define NR_IXNN_CHUNKS 96 |
42 | | |
43 | | |
44 | | #define DEBUG_ODML |
45 | | #undef DEBUG_ODML |
46 | | |
47 | | /* The following variable indicates the kind of error */ |
48 | | |
49 | | int AVI_errno = 0; |
50 | | |
51 | 0 | #define MAX_INFO_STRLEN 64 |
52 | | static char id_str[MAX_INFO_STRLEN]; |
53 | | |
54 | 0 | #define FRAME_RATE_SCALE 1000000 |
55 | | |
56 | | /******************************************************************* |
57 | | * * |
58 | | * Utilities for writing an AVI File * |
59 | | * * |
60 | | *******************************************************************/ |
61 | | |
62 | | static u32 avi_read(FILE *fd, char *buf, u32 len) |
63 | 0 | { |
64 | 0 | u32 r = 0; |
65 | |
|
66 | 0 | while (r < len) { |
67 | 0 | s32 n = (s32) gf_fread(buf + r, len - r, fd); |
68 | 0 | if (n == 0) break; |
69 | 0 | if (n < 0) return r; |
70 | 0 | r += n; |
71 | 0 | } |
72 | | |
73 | 0 | return r; |
74 | 0 | } |
75 | | |
76 | | static u32 avi_write (FILE *fd, char *buf, u32 len) |
77 | 0 | { |
78 | 0 | u32 r = 0; |
79 | |
|
80 | 0 | while (r < len) { |
81 | 0 | s32 n = (u32) gf_fwrite (buf + r, len - r, fd); |
82 | 0 | if (n < 0) |
83 | 0 | return n; |
84 | | |
85 | 0 | r += n; |
86 | 0 | } |
87 | 0 | return r; |
88 | 0 | } |
89 | | |
90 | | /* HEADERBYTES: The number of bytes to reserve for the header */ |
91 | | |
92 | 0 | #define HEADERBYTES 2048 |
93 | | |
94 | | /* AVI_MAX_LEN: The maximum length of an AVI file, we stay a bit below |
95 | | the 2GB limit (Remember: 2*10^9 is smaller than 2 GB) */ |
96 | | |
97 | 0 | #define AVI_MAX_LEN (UINT_MAX-(1<<20)*16-HEADERBYTES) |
98 | | |
99 | 0 | #define PAD_EVEN(x) ( ((x)+1) & ~1 ) |
100 | | |
101 | | |
102 | | /* Copy n into dst as a 4 or 2 byte, little endian number. |
103 | | Should also work on big endian machines */ |
104 | | |
105 | | static void long2str(unsigned char *dst, s32 n) |
106 | 0 | { |
107 | 0 | dst[0] = (n )&0xff; |
108 | 0 | dst[1] = (n>> 8)&0xff; |
109 | 0 | dst[2] = (n>>16)&0xff; |
110 | 0 | dst[3] = (n>>24)&0xff; |
111 | 0 | } |
112 | | |
113 | | #ifdef WORDS_BIGENDIAN |
114 | | static void short2str(unsigned char *dst, s32 n) |
115 | | { |
116 | | dst[0] = (n )&0xff; |
117 | | dst[1] = (n>> 8)&0xff; |
118 | | } |
119 | | #endif |
120 | | |
121 | | /* Convert a string of 4 or 2 bytes to a number, |
122 | | also working on big endian machines */ |
123 | | |
124 | | static u64 str2ullong(unsigned char *str) |
125 | 0 | { |
126 | 0 | u64 r = ((u32)str[0] | ((u32)str[1]<<8) | ((u32)str[2]<<16) | ((u32)str[3]<<24)); |
127 | 0 | u64 s = ((u32)str[4] | ((u32)str[5]<<8) | ((u32)str[6]<<16) | ((u32)str[7]<<24)); |
128 | 0 | #ifdef __GNUC__ |
129 | 0 | return ((s<<32)&0xffffffff00000000ULL)|(r&0xffffffff); |
130 | | #else |
131 | | return ((s<<32)&0xffffffff00000000)|(r&0xffffffff); |
132 | | #endif |
133 | 0 | } |
134 | | |
135 | | static u32 str2ulong(unsigned char *str) |
136 | 0 | { |
137 | 0 | return ( (u32)str[0] | ((u32)str[1]<<8) | ((u32)str[2]<<16) | ((u32)str[3]<<24) ); |
138 | 0 | } |
139 | | static u32 str2ushort(unsigned char *str) |
140 | 0 | { |
141 | 0 | return ( (u32)str[0] | ((u32)str[1]<<8) ); |
142 | 0 | } |
143 | | |
144 | | // bit 31 denotes a keyframe |
145 | | static u32 str2ulong_len (unsigned char *str) |
146 | 0 | { |
147 | 0 | return str2ulong(str) & 0x7fffffff; |
148 | 0 | } |
149 | | |
150 | | |
151 | | // if bit 31 is 0, its a keyframe |
152 | | static u32 str2ulong_key (unsigned char *str) |
153 | 0 | { |
154 | 0 | u32 c = str2ulong(str); |
155 | 0 | c &= 0x80000000; |
156 | 0 | if (c == 0) return 0x10; |
157 | 0 | else return 0; |
158 | 0 | } |
159 | | |
160 | | /* Calculate audio sample size from number of bits and number of channels. |
161 | | This may have to be adjusted for eg. 12 bits and stereo */ |
162 | | |
163 | | static int avi_sampsize(avi_t *AVI, int j) |
164 | 0 | { |
165 | 0 | int s; |
166 | 0 | s = ((AVI->track[j].a_bits+7)/8)*AVI->track[j].a_chans; |
167 | | // if(s==0) s=1; /* avoid possible zero divisions */ |
168 | 0 | if(s<4) s=4; /* avoid possible zero divisions */ |
169 | 0 | return s; |
170 | 0 | } |
171 | | |
172 | | /* Add a chunk (=tag and data) to the AVI file, |
173 | | returns -1 on write error, 0 on success */ |
174 | | |
175 | | static int avi_add_chunk(avi_t *AVI, unsigned char *tag, unsigned char *data, u32 length) |
176 | 0 | { |
177 | 0 | unsigned char c[8]; |
178 | 0 | char p=0; |
179 | | |
180 | | /* Copy tag and length int c, so that we need only 1 write system call |
181 | | for these two values */ |
182 | |
|
183 | 0 | memcpy(c,tag,4); |
184 | 0 | long2str(c+4,length); |
185 | | |
186 | | /* Output tag, length and data, restore previous position |
187 | | if the write fails */ |
188 | |
|
189 | 0 | if( avi_write(AVI->fdes,(char *)c,8) != 8 || |
190 | 0 | avi_write(AVI->fdes,(char *)data,length) != length || |
191 | 0 | avi_write(AVI->fdes,&p,length&1) != (length&1)) // if len is uneven, write a pad byte |
192 | 0 | { |
193 | 0 | gf_fseek(AVI->fdes,AVI->pos,SEEK_SET); |
194 | 0 | AVI_errno = AVI_ERR_WRITE; |
195 | 0 | return -1; |
196 | 0 | } |
197 | | |
198 | | /* Update file position */ |
199 | | |
200 | 0 | AVI->pos += 8 + PAD_EVEN(length); |
201 | | |
202 | | //GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] pos=%lu %s\n", AVI->pos, tag)); |
203 | |
|
204 | 0 | return 0; |
205 | 0 | } |
206 | | |
207 | 0 | #define OUTD(n) long2str((unsigned char*) (ix00+bl),(s32)n); bl+=4 |
208 | 0 | #define OUTW(n) ix00[bl] = (n)&0xff; ix00[bl+1] = (n>>8)&0xff; bl+=2 |
209 | 0 | #define OUTC(n) ix00[bl] = (n)&0xff; bl+=1 |
210 | 0 | #define OUTS(s) memcpy(ix00+bl,s,4); bl+=4 |
211 | | |
212 | | // this does the physical writeout of the ix## structure |
213 | | static int avi_ixnn_entry(avi_t *AVI, avistdindex_chunk *ch, avisuperindex_entry *en) |
214 | 0 | { |
215 | 0 | int bl; |
216 | 0 | u32 k; |
217 | 0 | unsigned int max = ch->nEntriesInUse * sizeof (u32) * ch->wLongsPerEntry + 24; // header |
218 | 0 | char *ix00 = (char *)gf_malloc (max); |
219 | 0 | char dfcc[5]; |
220 | 0 | memcpy (dfcc, ch->fcc, 4); |
221 | 0 | dfcc[4] = 0; |
222 | |
|
223 | 0 | bl = 0; |
224 | |
|
225 | 0 | if (en) { |
226 | 0 | en->qwOffset = AVI->pos; |
227 | 0 | en->dwSize = max; |
228 | | //en->dwDuration = ch->nEntriesInUse -1; // NUMBER OF stream ticks == frames for video/samples for audio |
229 | 0 | } |
230 | |
|
231 | | #ifdef DEBUG_ODML |
232 | | //GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] ODML Write %s: Entries %ld size %d \n", dfcc, ch->nEntriesInUse, max)); |
233 | | #endif |
234 | | |
235 | | //OUTS(ch->fcc); |
236 | | //OUTD(max); |
237 | 0 | OUTW(ch->wLongsPerEntry); |
238 | 0 | OUTC(ch->bIndexSubType); |
239 | 0 | OUTC(ch->bIndexType); |
240 | 0 | OUTD(ch->nEntriesInUse); |
241 | 0 | OUTS(ch->dwChunkId); |
242 | 0 | OUTD(ch->qwBaseOffset&0xffffffff); |
243 | 0 | OUTD((ch->qwBaseOffset>>32)&0xffffffff); |
244 | 0 | OUTD(ch->dwReserved3); |
245 | |
|
246 | 0 | for (k = 0; k < ch->nEntriesInUse; k++) { |
247 | 0 | OUTD(ch->aIndex[k].dwOffset); |
248 | 0 | OUTD(ch->aIndex[k].dwSize); |
249 | |
|
250 | 0 | } |
251 | 0 | avi_add_chunk (AVI, (unsigned char*)ch->fcc, (unsigned char*)ix00, max); |
252 | |
|
253 | 0 | gf_free(ix00); |
254 | |
|
255 | 0 | return 0; |
256 | 0 | } |
257 | | #undef OUTS |
258 | | #undef OUTW |
259 | | #undef OUTD |
260 | | #undef OUTC |
261 | | |
262 | | // inits a super index structure including its enclosed stdindex |
263 | | static int avi_init_super_index(avi_t *AVI, unsigned char *idxtag, avisuperindex_chunk **si) |
264 | 0 | { |
265 | 0 | int k; |
266 | |
|
267 | 0 | avisuperindex_chunk *sil = NULL; |
268 | |
|
269 | 0 | if ((sil = (avisuperindex_chunk *) gf_malloc (sizeof (avisuperindex_chunk))) == NULL) { |
270 | 0 | AVI_errno = AVI_ERR_NO_MEM; |
271 | 0 | return -1; |
272 | 0 | } |
273 | 0 | memcpy (sil->fcc, "indx", 4); |
274 | 0 | sil->dwSize = 0; // size of this chunk |
275 | 0 | sil->wLongsPerEntry = 4; |
276 | 0 | sil->bIndexSubType = 0; |
277 | 0 | sil->bIndexType = AVI_INDEX_OF_INDEXES; |
278 | 0 | sil->nEntriesInUse = 0; // none are in use |
279 | 0 | memcpy (sil->dwChunkId, idxtag, 4); |
280 | 0 | memset (sil->dwReserved, 0, sizeof (sil->dwReserved)); |
281 | | |
282 | | // NR_IXNN_CHUNKS == allow 32 indices which means 32 GB files -- arbitrary |
283 | 0 | sil->aIndex = (avisuperindex_entry *) gf_malloc (sil->wLongsPerEntry * NR_IXNN_CHUNKS * sizeof (void*)); |
284 | 0 | if (!sil->aIndex) { |
285 | 0 | AVI_errno = AVI_ERR_NO_MEM; |
286 | 0 | return -1; |
287 | 0 | } |
288 | 0 | memset (sil->aIndex, 0, sil->wLongsPerEntry * NR_IXNN_CHUNKS * sizeof (u32)); |
289 | |
|
290 | 0 | sil->stdindex = (avistdindex_chunk **)gf_malloc (NR_IXNN_CHUNKS * sizeof (avistdindex_chunk *)); |
291 | 0 | if (!sil->stdindex) { |
292 | 0 | AVI_errno = AVI_ERR_NO_MEM; |
293 | 0 | return -1; |
294 | 0 | } |
295 | 0 | for (k = 0; k < NR_IXNN_CHUNKS; k++) { |
296 | 0 | sil->stdindex[k] = (avistdindex_chunk *) gf_malloc (sizeof (avistdindex_chunk)); |
297 | | // gets rewritten later |
298 | 0 | sil->stdindex[k]->qwBaseOffset = (u64)k * AVI->new_riff_threshold; |
299 | 0 | sil->stdindex[k]->aIndex = NULL; |
300 | 0 | } |
301 | |
|
302 | 0 | *si = sil; |
303 | |
|
304 | 0 | return 0; |
305 | 0 | } |
306 | | |
307 | | // fills an alloc'ed stdindex structure and mallocs some entries for the actual chunks |
308 | | static int avi_add_std_index(avi_t *AVI, unsigned char *idxtag, unsigned char *strtag, |
309 | | avistdindex_chunk *stdil) |
310 | 0 | { |
311 | |
|
312 | 0 | memcpy (stdil->fcc, idxtag, 4); |
313 | 0 | stdil->dwSize = 4096; |
314 | 0 | stdil->wLongsPerEntry = 2; //sizeof(avistdindex_entry)/sizeof(u32); |
315 | 0 | stdil->bIndexSubType = 0; |
316 | 0 | stdil->bIndexType = AVI_INDEX_OF_CHUNKS; |
317 | 0 | stdil->nEntriesInUse = 0; |
318 | | |
319 | | // cp 00db ChunkId |
320 | 0 | memcpy(stdil->dwChunkId, strtag, 4); |
321 | | |
322 | | //stdil->qwBaseOffset = AVI->video_superindex->aIndex[ cur_std_idx ]->qwOffset; |
323 | |
|
324 | 0 | stdil->aIndex = (avistdindex_entry *)gf_malloc(stdil->dwSize * sizeof (u32) * stdil->wLongsPerEntry); |
325 | |
|
326 | 0 | if (!stdil->aIndex) { |
327 | 0 | AVI_errno = AVI_ERR_NO_MEM; |
328 | 0 | return -1; |
329 | 0 | } |
330 | | |
331 | | |
332 | 0 | return 0; |
333 | 0 | } |
334 | | |
335 | | static int avi_add_odml_index_entry_core(avi_t *AVI, int flags, u64 pos, unsigned int len, avistdindex_chunk *si) |
336 | 0 | { |
337 | 0 | u32 cur_chunk_idx; |
338 | | // put new chunk into index |
339 | 0 | si->nEntriesInUse++; |
340 | 0 | cur_chunk_idx = si->nEntriesInUse-1; |
341 | | |
342 | | // need to fetch more memory |
343 | 0 | if (cur_chunk_idx >= si->dwSize) { |
344 | 0 | si->dwSize += 4096; |
345 | 0 | si->aIndex = (avistdindex_entry *)gf_realloc ( si->aIndex, si->dwSize * sizeof (u32) * si->wLongsPerEntry); |
346 | 0 | } |
347 | |
|
348 | 0 | if(len>AVI->max_len) AVI->max_len=len; |
349 | | |
350 | | // if bit 31 is set, it is NOT a keyframe |
351 | 0 | if (flags != 0x10) { |
352 | 0 | len |= 0x80000000; |
353 | 0 | } |
354 | |
|
355 | 0 | si->aIndex [ cur_chunk_idx ].dwSize = len; |
356 | 0 | si->aIndex [ cur_chunk_idx ].dwOffset = (u32) (pos - si->qwBaseOffset + 8); |
357 | | |
358 | | //GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] ODML: POS: 0x%lX\n", si->aIndex [ cur_chunk_idx ].dwOffset)); |
359 | |
|
360 | 0 | return 0; |
361 | 0 | } |
362 | | |
363 | | static int avi_add_odml_index_entry(avi_t *AVI, unsigned char *tag, int flags, u64 pos, unsigned int len) |
364 | 0 | { |
365 | 0 | char fcc[5]; |
366 | |
|
367 | 0 | int audio = (strchr ((char*)tag, 'w')?1:0); |
368 | 0 | int video = !audio; |
369 | |
|
370 | 0 | unsigned int cur_std_idx; |
371 | 0 | u32 audtr; |
372 | 0 | s64 towrite = 0; |
373 | |
|
374 | 0 | if (video) { |
375 | |
|
376 | 0 | if (!AVI->video_superindex) { |
377 | 0 | if (avi_init_super_index(AVI, (unsigned char *)"ix00", &AVI->video_superindex) < 0) return -1; |
378 | 0 | AVI->video_superindex->nEntriesInUse++; |
379 | 0 | cur_std_idx = AVI->video_superindex->nEntriesInUse-1; |
380 | |
|
381 | 0 | if (avi_add_std_index (AVI, (unsigned char *)"ix00", (unsigned char *)"00db", AVI->video_superindex->stdindex[ cur_std_idx ]) < 0) |
382 | 0 | return -1; |
383 | 0 | } // init |
384 | |
|
385 | 0 | } // video |
386 | | |
387 | 0 | if (audio) { |
388 | |
|
389 | 0 | fcc[0] = 'i'; |
390 | 0 | fcc[1] = 'x'; |
391 | 0 | fcc[2] = tag[0]; |
392 | 0 | fcc[3] = tag[1]; |
393 | 0 | fcc[4] = '\0'; |
394 | 0 | if (!AVI->track[AVI->aptr].audio_superindex) { |
395 | |
|
396 | | #ifdef DEBUG_ODML |
397 | | GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] ODML: fcc = %s\n", fcc)); |
398 | | #endif |
399 | 0 | if (avi_init_super_index(AVI, (unsigned char *)fcc, &AVI->track[AVI->aptr].audio_superindex) < 0) return -1; |
400 | | |
401 | | |
402 | 0 | AVI->track[AVI->aptr].audio_superindex->nEntriesInUse++; |
403 | |
|
404 | 0 | sprintf(fcc, "ix%02d", AVI->aptr+1); |
405 | 0 | if (avi_add_std_index (AVI, (unsigned char *)fcc, tag, AVI->track[AVI->aptr].audio_superindex->stdindex[ |
406 | 0 | AVI->track[AVI->aptr].audio_superindex->nEntriesInUse - 1 ]) < 0 |
407 | 0 | ) return -1; |
408 | 0 | } // init |
409 | |
|
410 | 0 | } |
411 | | |
412 | 0 | towrite = 0; |
413 | 0 | if (AVI->video_superindex) { |
414 | |
|
415 | 0 | cur_std_idx = AVI->video_superindex->nEntriesInUse-1; |
416 | 0 | towrite += AVI->video_superindex->stdindex[cur_std_idx]->nEntriesInUse*8 |
417 | 0 | + 4+4+2+1+1+4+4+8+4; |
418 | 0 | if (cur_std_idx == 0) { |
419 | 0 | towrite += AVI->n_idx*16 + 8; |
420 | 0 | towrite += HEADERBYTES; |
421 | 0 | } |
422 | 0 | } |
423 | |
|
424 | 0 | for (audtr=0; audtr<AVI->anum; audtr++) { |
425 | 0 | if (AVI->track[audtr].audio_superindex) { |
426 | 0 | cur_std_idx = AVI->track[audtr].audio_superindex->nEntriesInUse-1; |
427 | 0 | towrite += AVI->track[audtr].audio_superindex->stdindex[cur_std_idx]->nEntriesInUse*8 |
428 | 0 | + 4+4+2+1+1+4+4+8+4; |
429 | 0 | } |
430 | 0 | } |
431 | 0 | towrite += len + (len&1) + 8; |
432 | | |
433 | | //GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] ODML: towrite = 0x%llX = %"LLD"\n", towrite, towrite)); |
434 | |
|
435 | 0 | if (AVI->video_superindex && |
436 | 0 | (s64)(AVI->pos+towrite) > (s64)((s64) AVI->new_riff_threshold*AVI->video_superindex->nEntriesInUse)) { |
437 | |
|
438 | 0 | GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] Adding a new RIFF chunk: %d\n", AVI->video_superindex->nEntriesInUse)); |
439 | | |
440 | | // rotate ALL indices |
441 | 0 | AVI->video_superindex->nEntriesInUse++; |
442 | 0 | cur_std_idx = AVI->video_superindex->nEntriesInUse-1; |
443 | |
|
444 | 0 | if (AVI->video_superindex->nEntriesInUse > NR_IXNN_CHUNKS) { |
445 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[avilib] Internal error in avilib - redefine NR_IXNN_CHUNKS\n")); |
446 | 0 | GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] cur_std_idx=%d NR_IXNN_CHUNKS=%d" |
447 | 0 | "POS=%"LLD" towrite=%"LLD"\n", |
448 | 0 | cur_std_idx,NR_IXNN_CHUNKS, AVI->pos, towrite)); |
449 | 0 | return -1; |
450 | 0 | } |
451 | | |
452 | 0 | if (avi_add_std_index (AVI, (unsigned char *)"ix00", (unsigned char *)"00db", AVI->video_superindex->stdindex[ cur_std_idx ]) < 0) |
453 | 0 | return -1; |
454 | | |
455 | 0 | for (audtr = 0; audtr < AVI->anum; audtr++) { |
456 | 0 | char aud[5]; |
457 | 0 | if (!AVI->track[audtr].audio_superindex) { |
458 | | // not initialized -> no index |
459 | 0 | continue; |
460 | 0 | } |
461 | 0 | AVI->track[audtr].audio_superindex->nEntriesInUse++; |
462 | |
|
463 | 0 | sprintf(fcc, "ix%02d", audtr+1); |
464 | 0 | sprintf(aud, "0%01dwb", audtr+1); |
465 | 0 | if (avi_add_std_index (AVI, (unsigned char *)fcc, (unsigned char *)aud, AVI->track[audtr].audio_superindex->stdindex[ |
466 | 0 | AVI->track[audtr].audio_superindex->nEntriesInUse - 1 ]) < 0 |
467 | 0 | ) return -1; |
468 | 0 | } |
469 | | |
470 | | // write the new riff; |
471 | 0 | if (cur_std_idx > 0) { |
472 | | |
473 | | // dump the _previous_ == already finished index |
474 | 0 | avi_ixnn_entry (AVI, AVI->video_superindex->stdindex[cur_std_idx - 1], |
475 | 0 | &AVI->video_superindex->aIndex[cur_std_idx - 1]); |
476 | 0 | AVI->video_superindex->aIndex[cur_std_idx - 1].dwDuration = |
477 | 0 | AVI->video_superindex->stdindex[cur_std_idx - 1]->nEntriesInUse - 1; |
478 | |
|
479 | 0 | for (audtr = 0; audtr < AVI->anum; audtr++) { |
480 | |
|
481 | 0 | if (!AVI->track[audtr].audio_superindex) { |
482 | | // not initialized -> no index |
483 | 0 | continue; |
484 | 0 | } |
485 | 0 | avi_ixnn_entry (AVI, AVI->track[audtr].audio_superindex->stdindex[cur_std_idx - 1], |
486 | 0 | &AVI->track[audtr].audio_superindex->aIndex[cur_std_idx - 1]); |
487 | |
|
488 | 0 | AVI->track[audtr].audio_superindex->aIndex[cur_std_idx - 1].dwDuration = |
489 | 0 | AVI->track[audtr].audio_superindex->stdindex[cur_std_idx - 1]->nEntriesInUse - 1; |
490 | 0 | if (AVI->track[audtr].a_fmt == 0x1) { |
491 | 0 | AVI->track[audtr].audio_superindex->aIndex[cur_std_idx - 1].dwDuration *= |
492 | 0 | AVI->track[audtr].a_bits*AVI->track[audtr].a_rate*AVI->track[audtr].a_chans/800; |
493 | 0 | } |
494 | 0 | } |
495 | | |
496 | | // XXX: dump idx1 structure |
497 | 0 | if (cur_std_idx == 1) { |
498 | 0 | avi_add_chunk(AVI, (unsigned char *)"idx1", (unsigned char *)AVI->idx, AVI->n_idx*16); |
499 | | // qwBaseOffset will contain the start of the second riff chunk |
500 | 0 | } |
501 | | // Fix the Offsets later at closing time |
502 | 0 | avi_add_chunk(AVI, (unsigned char *)"RIFF", (unsigned char *)"AVIXLIST\0\0\0\0movi", 16); |
503 | |
|
504 | 0 | AVI->video_superindex->stdindex[ cur_std_idx ]->qwBaseOffset = AVI->pos -16 -8; |
505 | | #ifdef DEBUG_ODML |
506 | | GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] ODML: RIFF No.%02d at Offset 0x%llX\n", cur_std_idx, AVI->pos -16 -8)); |
507 | | #endif |
508 | |
|
509 | 0 | for (audtr = 0; audtr < AVI->anum; audtr++) { |
510 | 0 | if (AVI->track[audtr].audio_superindex) |
511 | 0 | AVI->track[audtr].audio_superindex->stdindex[ cur_std_idx ]->qwBaseOffset = |
512 | 0 | AVI->pos -16 -8; |
513 | |
|
514 | 0 | } |
515 | | |
516 | | // now we can be sure |
517 | 0 | AVI->is_opendml++; |
518 | 0 | } |
519 | |
|
520 | 0 | } |
521 | | |
522 | | |
523 | 0 | if (video) { |
524 | 0 | avi_add_odml_index_entry_core(AVI, flags, AVI->pos, len, |
525 | 0 | AVI->video_superindex->stdindex[ AVI->video_superindex->nEntriesInUse-1 ]); |
526 | |
|
527 | 0 | AVI->total_frames++; |
528 | 0 | } // video |
529 | |
|
530 | 0 | if (audio) { |
531 | 0 | avi_add_odml_index_entry_core(AVI, flags, AVI->pos, len, |
532 | 0 | AVI->track[AVI->aptr].audio_superindex->stdindex[ |
533 | 0 | AVI->track[AVI->aptr].audio_superindex->nEntriesInUse-1 ]); |
534 | 0 | } |
535 | | |
536 | |
|
537 | 0 | return 0; |
538 | 0 | } |
539 | | |
540 | | // #undef NR_IXNN_CHUNKS |
541 | | |
542 | | static int avi_add_index_entry(avi_t *AVI, unsigned char *tag, int flags, u64 pos, u64 len) |
543 | 0 | { |
544 | 0 | if(AVI->n_idx>=AVI->max_idx) { |
545 | 0 | void *ptr = gf_realloc((void *)AVI->idx,(AVI->max_idx+4096)*16); |
546 | |
|
547 | 0 | if(ptr == 0) { |
548 | 0 | AVI_errno = AVI_ERR_NO_MEM; |
549 | 0 | return -1; |
550 | 0 | } |
551 | 0 | AVI->max_idx += 4096; |
552 | 0 | AVI->idx = (unsigned char((*)[16]) ) ptr; |
553 | 0 | } |
554 | | |
555 | | /* Add index entry */ |
556 | | |
557 | | // GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] INDEX %s %ld %lu %lu\n", tag, flags, pos, len)); |
558 | | |
559 | 0 | memcpy(AVI->idx[AVI->n_idx],tag,4); |
560 | 0 | long2str(AVI->idx[AVI->n_idx]+ 4,flags); |
561 | 0 | long2str(AVI->idx[AVI->n_idx]+ 8, (s32) pos); |
562 | 0 | long2str(AVI->idx[AVI->n_idx]+12, (s32) len); |
563 | | |
564 | | /* Update counter */ |
565 | |
|
566 | 0 | AVI->n_idx++; |
567 | |
|
568 | 0 | if(len>AVI->max_len) AVI->max_len=(u32) len; |
569 | |
|
570 | 0 | return 0; |
571 | 0 | } |
572 | | |
573 | | #if 0 |
574 | | /* Returns 1 if more audio is in that video junk */ |
575 | | int AVI_can_read_audio(avi_t *AVI) |
576 | | { |
577 | | if(AVI->mode==AVI_MODE_WRITE) { |
578 | | return -1; |
579 | | } |
580 | | if(!AVI->video_index) { |
581 | | return -1; |
582 | | } |
583 | | if(!AVI->track[AVI->aptr].audio_index) { |
584 | | return -1; |
585 | | } |
586 | | |
587 | | // is it -1? the last ones got left out --tibit |
588 | | //if (AVI->track[AVI->aptr].audio_posc>=AVI->track[AVI->aptr].audio_chunks-1) { |
589 | | if (AVI->track[AVI->aptr].audio_posc>=AVI->track[AVI->aptr].audio_chunks) { |
590 | | return 0; |
591 | | } |
592 | | |
593 | | if (AVI->video_pos >= AVI->video_frames) return 1; |
594 | | |
595 | | if (AVI->track[AVI->aptr].audio_index[AVI->track[AVI->aptr].audio_posc].pos < AVI->video_index[AVI->video_pos].pos) return 1; |
596 | | else return 0; |
597 | | } |
598 | | #endif |
599 | | |
600 | | /* |
601 | | AVI_open_output_file: Open an AVI File and write a bunch |
602 | | of zero bytes as space for the header. |
603 | | |
604 | | returns a pointer to avi_t on success, a zero pointer on error |
605 | | */ |
606 | | |
607 | | GF_EXPORT |
608 | | avi_t* AVI_open_output_file(char * filename, u64 opendml_threshold) |
609 | 0 | { |
610 | 0 | avi_t *AVI; |
611 | 0 | int i; |
612 | |
|
613 | 0 | unsigned char AVI_header[HEADERBYTES]; |
614 | | |
615 | | /* Allocate the avi_t struct and zero it */ |
616 | |
|
617 | 0 | AVI = (avi_t *) gf_malloc(sizeof(avi_t)); |
618 | 0 | if(AVI==0) |
619 | 0 | { |
620 | 0 | AVI_errno = AVI_ERR_NO_MEM; |
621 | 0 | return 0; |
622 | 0 | } |
623 | 0 | memset((void *)AVI,0,sizeof(avi_t)); |
624 | |
|
625 | 0 | AVI->fdes = gf_fopen(filename, "w+b"); |
626 | 0 | if (!AVI->fdes ) |
627 | 0 | { |
628 | 0 | AVI_errno = AVI_ERR_OPEN; |
629 | 0 | gf_free(AVI); |
630 | 0 | return 0; |
631 | 0 | } |
632 | | |
633 | | /* Write out HEADERBYTES bytes, the header will go here |
634 | | when we are finished with writing */ |
635 | | |
636 | 0 | for (i=0; i<HEADERBYTES; i++) AVI_header[i] = 0; |
637 | 0 | i = avi_write(AVI->fdes,(char *)AVI_header,HEADERBYTES); |
638 | 0 | if (i != HEADERBYTES) |
639 | 0 | { |
640 | 0 | gf_fclose(AVI->fdes); |
641 | 0 | AVI_errno = AVI_ERR_WRITE; |
642 | 0 | gf_free(AVI); |
643 | 0 | return 0; |
644 | 0 | } |
645 | | |
646 | 0 | AVI->pos = HEADERBYTES; |
647 | 0 | AVI->mode = AVI_MODE_WRITE; /* open for writing */ |
648 | 0 | if (opendml_threshold) |
649 | 0 | AVI->new_riff_threshold = opendml_threshold; |
650 | 0 | else |
651 | 0 | AVI->new_riff_threshold = (1900*1024*1024); |
652 | | |
653 | | //init |
654 | 0 | AVI->anum = 0; |
655 | 0 | AVI->aptr = 0; |
656 | |
|
657 | 0 | return AVI; |
658 | 0 | } |
659 | | |
660 | | GF_EXPORT |
661 | | void AVI_set_video(avi_t *AVI, int width, int height, double fps, char *compressor) |
662 | 0 | { |
663 | | /* may only be called if file is open for writing */ |
664 | |
|
665 | 0 | if(AVI->mode==AVI_MODE_READ) return; |
666 | | |
667 | 0 | AVI->width = width; |
668 | 0 | AVI->height = height; |
669 | 0 | AVI->fps = fps; |
670 | |
|
671 | 0 | if(strncmp(compressor, "RGB", 3)==0) { |
672 | 0 | memset(AVI->compressor, 0, 4); |
673 | 0 | } else { |
674 | 0 | memcpy(AVI->compressor,compressor,4); |
675 | 0 | } |
676 | |
|
677 | 0 | AVI->compressor[4] = 0; |
678 | |
|
679 | 0 | avi_update_header(AVI); |
680 | 0 | } |
681 | | |
682 | | GF_EXPORT |
683 | | void AVI_set_audio(avi_t *AVI, int channels, int rate, int bits, int format, int mp3rate) |
684 | 0 | { |
685 | | /* may only be called if file is open for writing */ |
686 | |
|
687 | 0 | if(AVI->mode==AVI_MODE_READ) return; |
688 | | |
689 | | //inc audio tracks |
690 | 0 | AVI->aptr=AVI->anum; |
691 | 0 | ++AVI->anum; |
692 | |
|
693 | 0 | if(AVI->anum > AVI_MAX_TRACKS) { |
694 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[avilib] error - only %d audio tracks supported\n", AVI_MAX_TRACKS)); |
695 | 0 | exit(1); |
696 | 0 | } |
697 | | |
698 | 0 | AVI->track[AVI->aptr].a_chans = channels; |
699 | 0 | AVI->track[AVI->aptr].a_rate = rate; |
700 | 0 | AVI->track[AVI->aptr].a_bits = bits; |
701 | 0 | AVI->track[AVI->aptr].a_fmt = format; |
702 | 0 | AVI->track[AVI->aptr].mp3rate = mp3rate; |
703 | |
|
704 | 0 | avi_update_header(AVI); |
705 | 0 | } |
706 | | |
707 | | #define OUT4CC(s) \ |
708 | 0 | if(nhb<=HEADERBYTES-4) memcpy(AVI_header+nhb,s,4); \ |
709 | 0 | nhb += 4 |
710 | | |
711 | | #define OUTLONG(n) \ |
712 | 0 | if(nhb<=HEADERBYTES-4) long2str(AVI_header+nhb, (s32)(n)); \ |
713 | 0 | nhb += 4 |
714 | | |
715 | | #define OUTSHRT(n) \ |
716 | 0 | if(nhb<=HEADERBYTES-2) { \ |
717 | 0 | AVI_header[nhb ] = (u8) ((n )&0xff); \ |
718 | 0 | AVI_header[nhb+1] = (u8) ((n>>8)&0xff); \ |
719 | 0 | } \ |
720 | 0 | nhb += 2 |
721 | | |
722 | | #define OUTCHR(n) \ |
723 | 0 | if(nhb<=HEADERBYTES-1) { \ |
724 | 0 | AVI_header[nhb ] = (n )&0xff; \ |
725 | 0 | } \ |
726 | 0 | nhb += 1 |
727 | | |
728 | | #define OUTMEM(d, s) \ |
729 | 0 | { \ |
730 | 0 | u32 s_ = (u32) (s); \ |
731 | 0 | if(nhb + s_ <= HEADERBYTES) \ |
732 | 0 | memcpy(AVI_header+nhb, (d), s_); \ |
733 | 0 | nhb += s_; \ |
734 | 0 | } |
735 | | |
736 | | |
737 | | //ThOe write preliminary AVI file header: 0 frames, max vid/aud size |
738 | | int avi_update_header(avi_t *AVI) |
739 | 0 | { |
740 | 0 | int njunk, ms_per_frame, frate, flag; |
741 | 0 | int movi_len, hdrl_start, strl_start; |
742 | 0 | u32 j; |
743 | 0 | unsigned char AVI_header[HEADERBYTES]; |
744 | 0 | u32 nhb; |
745 | 0 | unsigned int xd_size, xd_size_align2; |
746 | | |
747 | | //assume max size |
748 | 0 | movi_len = AVI_MAX_LEN - HEADERBYTES + 4; |
749 | | |
750 | | //assume index will be written |
751 | | // int hasIndex=1; |
752 | |
|
753 | 0 | if(AVI->fps < 0.001) { |
754 | 0 | frate=0; |
755 | 0 | ms_per_frame=0; |
756 | 0 | } else { |
757 | 0 | frate = (int) (FRAME_RATE_SCALE*AVI->fps + 0.5); |
758 | 0 | ms_per_frame=(int) (1000000/AVI->fps + 0.5); |
759 | 0 | } |
760 | | |
761 | | /* Prepare the file header */ |
762 | |
|
763 | 0 | nhb = 0; |
764 | | |
765 | | /* The RIFF header */ |
766 | |
|
767 | 0 | OUT4CC ("RIFF"); |
768 | 0 | OUTLONG(movi_len); // assume max size |
769 | 0 | OUT4CC ("AVI "); |
770 | | |
771 | | /* Start the header list */ |
772 | |
|
773 | 0 | OUT4CC ("LIST"); |
774 | 0 | OUTLONG(0); /* Length of list in bytes, don't know yet */ |
775 | 0 | hdrl_start = nhb; /* Store start position */ |
776 | 0 | OUT4CC ("hdrl"); |
777 | | |
778 | | /* The main AVI header */ |
779 | | |
780 | | /* The Flags in AVI File header */ |
781 | |
|
782 | 0 | #define AVIF_HASINDEX 0x00000010 /* Index at end of file */ |
783 | 0 | #define AVIF_MUSTUSEINDEX 0x00000020 |
784 | 0 | #define AVIF_ISINTERLEAVED 0x00000100 |
785 | 0 | #define AVIF_TRUSTCKTYPE 0x00000800 /* Use CKType to find key frames */ |
786 | 0 | #define AVIF_WASCAPTUREFILE 0x00010000 |
787 | 0 | #define AVIF_COPYRIGHTED 0x00020000 |
788 | |
|
789 | 0 | OUT4CC ("avih"); |
790 | 0 | OUTLONG(56); /* # of bytes to follow */ |
791 | 0 | OUTLONG(ms_per_frame); /* Microseconds per frame */ |
792 | | //ThOe ->0 |
793 | | // OUTLONG(10000000); /* MaxBytesPerSec, I hope this will never be used */ |
794 | 0 | OUTLONG(0); |
795 | 0 | OUTLONG(0); /* PaddingGranularity (whatever that might be) */ |
796 | | /* Other sources call it 'reserved' */ |
797 | 0 | flag = AVIF_ISINTERLEAVED; |
798 | | //if (hasIndex) |
799 | 0 | flag |= AVIF_HASINDEX; |
800 | 0 | if (/*hasIndex && */AVI->must_use_index) |
801 | 0 | flag |= AVIF_MUSTUSEINDEX; |
802 | 0 | OUTLONG(flag); /* Flags */ |
803 | 0 | OUTLONG(0); // no frames yet |
804 | 0 | OUTLONG(0); /* InitialFrames */ |
805 | |
|
806 | 0 | OUTLONG(AVI->anum+1); |
807 | |
|
808 | 0 | OUTLONG(0); /* SuggestedBufferSize */ |
809 | 0 | OUTLONG(AVI->width); /* Width */ |
810 | 0 | OUTLONG(AVI->height); /* Height */ |
811 | | /* MS calls the following 'reserved': */ |
812 | 0 | OUTLONG(0); /* TimeScale: Unit used to measure time */ |
813 | 0 | OUTLONG(0); /* DataRate: Data rate of playback */ |
814 | 0 | OUTLONG(0); /* StartTime: Starting time of AVI data */ |
815 | 0 | OUTLONG(0); /* DataLength: Size of AVI data chunk */ |
816 | | |
817 | | |
818 | | /* Start the video stream list ---------------------------------- */ |
819 | |
|
820 | 0 | OUT4CC ("LIST"); |
821 | 0 | OUTLONG(0); /* Length of list in bytes, don't know yet */ |
822 | 0 | strl_start = nhb; /* Store start position */ |
823 | 0 | OUT4CC ("strl"); |
824 | | |
825 | | /* The video stream header */ |
826 | |
|
827 | 0 | OUT4CC ("strh"); |
828 | 0 | OUTLONG(56); /* # of bytes to follow */ |
829 | 0 | OUT4CC ("vids"); /* Type */ |
830 | 0 | OUT4CC (AVI->compressor); /* Handler */ |
831 | 0 | OUTLONG(0); /* Flags */ |
832 | 0 | OUTLONG(0); /* Reserved, MS says: wPriority, wLanguage */ |
833 | 0 | OUTLONG(0); /* InitialFrames */ |
834 | 0 | OUTLONG(FRAME_RATE_SCALE); /* Scale */ |
835 | 0 | OUTLONG(frate); /* Rate: Rate/Scale == samples/second */ |
836 | 0 | OUTLONG(0); /* Start */ |
837 | 0 | OUTLONG(0); // no frames yet |
838 | 0 | OUTLONG(0); /* SuggestedBufferSize */ |
839 | 0 | OUTLONG(-1); /* Quality */ |
840 | 0 | OUTLONG(0); /* SampleSize */ |
841 | 0 | OUTLONG(0); /* Frame */ |
842 | 0 | OUTLONG(0); /* Frame */ |
843 | | // OUTLONG(0); /* Frame */ |
844 | | //OUTLONG(0); /* Frame */ |
845 | | |
846 | | /* The video stream format */ |
847 | |
|
848 | 0 | xd_size = AVI->extradata_size; |
849 | 0 | xd_size_align2 = (AVI->extradata_size+1) & ~1; |
850 | |
|
851 | 0 | OUT4CC ("strf"); |
852 | 0 | OUTLONG(40 + xd_size_align2);/* # of bytes to follow */ |
853 | 0 | OUTLONG(40 + xd_size); /* Size */ |
854 | 0 | OUTLONG(AVI->width); /* Width */ |
855 | 0 | OUTLONG(AVI->height); /* Height */ |
856 | 0 | OUTSHRT(1); |
857 | 0 | OUTSHRT(24); /* Planes, Count */ |
858 | 0 | OUT4CC (AVI->compressor); /* Compression */ |
859 | | // ThOe (*3) |
860 | 0 | OUTLONG(AVI->width*AVI->height*3); /* SizeImage (in bytes?) */ |
861 | 0 | OUTLONG(0); /* XPelsPerMeter */ |
862 | 0 | OUTLONG(0); /* YPelsPerMeter */ |
863 | 0 | OUTLONG(0); /* ClrUsed: Number of colors used */ |
864 | 0 | OUTLONG(0); /* ClrImportant: Number of colors important */ |
865 | | |
866 | | // write extradata |
867 | 0 | if (xd_size > 0 && AVI->extradata) { |
868 | 0 | OUTMEM(AVI->extradata, xd_size); |
869 | 0 | if (xd_size != xd_size_align2) { |
870 | 0 | OUTCHR(0); |
871 | 0 | } |
872 | 0 | } |
873 | | |
874 | | /* Finish stream list, i.e. put number of bytes in the list to proper pos */ |
875 | |
|
876 | 0 | long2str(AVI_header+strl_start-4,nhb-strl_start); |
877 | | |
878 | | |
879 | | /* Start the audio stream list ---------------------------------- */ |
880 | |
|
881 | 0 | for(j=0; j<AVI->anum; ++j) { |
882 | 0 | int sampsize = avi_sampsize(AVI, j); |
883 | |
|
884 | 0 | OUT4CC ("LIST"); |
885 | 0 | OUTLONG(0); /* Length of list in bytes, don't know yet */ |
886 | 0 | strl_start = nhb; /* Store start position */ |
887 | 0 | OUT4CC ("strl"); |
888 | | |
889 | | /* The audio stream header */ |
890 | |
|
891 | 0 | OUT4CC ("strh"); |
892 | 0 | OUTLONG(56); /* # of bytes to follow */ |
893 | 0 | OUT4CC ("auds"); |
894 | | |
895 | | // ----------- |
896 | | // ThOe |
897 | 0 | OUTLONG(0); /* Format (Optionally) */ |
898 | | // ----------- |
899 | |
|
900 | 0 | OUTLONG(0); /* Flags */ |
901 | 0 | OUTLONG(0); /* Reserved, MS says: wPriority, wLanguage */ |
902 | 0 | OUTLONG(0); /* InitialFrames */ |
903 | | |
904 | | // ThOe /4 |
905 | 0 | OUTLONG(sampsize/4); /* Scale */ |
906 | 0 | OUTLONG(1000*AVI->track[j].mp3rate/8); |
907 | 0 | OUTLONG(0); /* Start */ |
908 | 0 | OUTLONG(4*AVI->track[j].audio_bytes/sampsize); /* Length */ |
909 | 0 | OUTLONG(0); /* SuggestedBufferSize */ |
910 | 0 | OUTLONG(-1); /* Quality */ |
911 | | |
912 | | // ThOe /4 |
913 | 0 | OUTLONG(sampsize/4); /* SampleSize */ |
914 | |
|
915 | 0 | OUTLONG(0); /* Frame */ |
916 | 0 | OUTLONG(0); /* Frame */ |
917 | | // OUTLONG(0); /* Frame */ |
918 | | //OUTLONG(0); /* Frame */ |
919 | | |
920 | | /* The audio stream format */ |
921 | |
|
922 | 0 | OUT4CC ("strf"); |
923 | 0 | OUTLONG(16); /* # of bytes to follow */ |
924 | 0 | OUTSHRT(AVI->track[j].a_fmt); /* Format */ |
925 | 0 | OUTSHRT(AVI->track[j].a_chans); /* Number of channels */ |
926 | 0 | OUTLONG(AVI->track[j].a_rate); /* SamplesPerSec */ |
927 | | // ThOe |
928 | 0 | OUTLONG(1000*AVI->track[j].mp3rate/8); |
929 | | //ThOe (/4) |
930 | |
|
931 | 0 | OUTSHRT(sampsize/4); /* BlockAlign */ |
932 | | |
933 | |
|
934 | 0 | OUTSHRT(AVI->track[j].a_bits); /* BitsPerSample */ |
935 | | |
936 | | /* Finish stream list, i.e. put number of bytes in the list to proper pos */ |
937 | |
|
938 | 0 | long2str(AVI_header+strl_start-4,nhb-strl_start); |
939 | 0 | } |
940 | | |
941 | | /* Finish header list */ |
942 | |
|
943 | 0 | long2str(AVI_header+hdrl_start-4,nhb-hdrl_start); |
944 | | |
945 | | |
946 | | /* Calculate the needed amount of junk bytes, output junk */ |
947 | |
|
948 | 0 | njunk = HEADERBYTES - nhb - 8 - 12; |
949 | | |
950 | | /* Safety first: if njunk <= 0, somebody has played with |
951 | | HEADERBYTES without knowing what (s)he did. |
952 | | This is a fatal error */ |
953 | |
|
954 | 0 | if(njunk<=0) |
955 | 0 | { |
956 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[avilib] AVI_close_output_file: # of header bytes too small\n")); |
957 | 0 | exit(1); |
958 | 0 | } |
959 | | |
960 | 0 | OUT4CC ("JUNK"); |
961 | 0 | OUTLONG(njunk); |
962 | 0 | memset(AVI_header+nhb,0,njunk); |
963 | |
|
964 | 0 | nhb += njunk; |
965 | | |
966 | | /* Start the movi list */ |
967 | |
|
968 | 0 | OUT4CC ("LIST"); |
969 | 0 | OUTLONG(movi_len); /* Length of list in bytes */ |
970 | 0 | OUT4CC ("movi"); |
971 | | |
972 | | /* Output the header, truncate the file to the number of bytes |
973 | | actually written, report an error if someting goes wrong */ |
974 | |
|
975 | 0 | if ( (gf_fseek(AVI->fdes, 0, SEEK_SET) ==(u64)-1) || |
976 | 0 | avi_write(AVI->fdes,(char *)AVI_header,HEADERBYTES)!=HEADERBYTES || |
977 | 0 | (gf_fseek(AVI->fdes,AVI->pos,SEEK_SET)==(u64)-1) |
978 | 0 | ) { |
979 | 0 | AVI_errno = AVI_ERR_CLOSE; |
980 | 0 | return -1; |
981 | 0 | } |
982 | | |
983 | 0 | return 0; |
984 | 0 | } |
985 | | |
986 | | |
987 | | //SLM |
988 | | #ifndef S_IRUSR |
989 | | #define S_IRWXU 00700 /* read, write, execute: owner */ |
990 | | #define S_IRUSR 00400 /* read permission: owner */ |
991 | | #define S_IWUSR 00200 /* write permission: owner */ |
992 | | #define S_IXUSR 00100 /* execute permission: owner */ |
993 | | #define S_IRWXG 00070 /* read, write, execute: group */ |
994 | | #define S_IRGRP 00040 /* read permission: group */ |
995 | | #define S_IWGRP 00020 /* write permission: group */ |
996 | | #define S_IXGRP 00010 /* execute permission: group */ |
997 | | #define S_IRWXO 00007 /* read, write, execute: other */ |
998 | | #define S_IROTH 00004 /* read permission: other */ |
999 | | #define S_IWOTH 00002 /* write permission: other */ |
1000 | | #define S_IXOTH 00001 /* execute permission: other */ |
1001 | | #endif |
1002 | | |
1003 | | /* |
1004 | | Write the header of an AVI file and close it. |
1005 | | returns 0 on success, -1 on write error. |
1006 | | */ |
1007 | | |
1008 | | static int avi_close_output_file(avi_t *AVI) |
1009 | 0 | { |
1010 | 0 | int njunk, hasIndex, ms_per_frame, frate, idxerror, flag; |
1011 | 0 | u64 movi_len; |
1012 | 0 | int hdrl_start, strl_start; |
1013 | 0 | u32 j; |
1014 | 0 | unsigned char AVI_header[HEADERBYTES]; |
1015 | 0 | int nhb; |
1016 | 0 | unsigned int xd_size, xd_size_align2; |
1017 | |
|
1018 | 0 | #ifdef INFO_LIST |
1019 | 0 | int info_len; |
1020 | 0 | int id_len, real_id_len; |
1021 | 0 | int info_start_pos; |
1022 | | // time_t calptr; |
1023 | 0 | #endif |
1024 | | |
1025 | | /* Calculate length of movi list */ |
1026 | | |
1027 | | // dump the rest of the index |
1028 | 0 | if (AVI->is_opendml) { |
1029 | 0 | int cur_std_idx = AVI->video_superindex->nEntriesInUse-1; |
1030 | 0 | u32 audtr; |
1031 | |
|
1032 | | #ifdef DEBUG_ODML |
1033 | | GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] ODML dump the rest indices\n")); |
1034 | | #endif |
1035 | 0 | avi_ixnn_entry (AVI, AVI->video_superindex->stdindex[cur_std_idx], |
1036 | 0 | &AVI->video_superindex->aIndex[cur_std_idx]); |
1037 | |
|
1038 | 0 | AVI->video_superindex->aIndex[cur_std_idx].dwDuration = |
1039 | 0 | AVI->video_superindex->stdindex[cur_std_idx]->nEntriesInUse - 1; |
1040 | |
|
1041 | 0 | for (audtr = 0; audtr < AVI->anum; audtr++) { |
1042 | 0 | if (!AVI->track[audtr].audio_superindex) { |
1043 | | // not initialized -> no index |
1044 | 0 | continue; |
1045 | 0 | } |
1046 | 0 | avi_ixnn_entry (AVI, AVI->track[audtr].audio_superindex->stdindex[cur_std_idx], |
1047 | 0 | &AVI->track[audtr].audio_superindex->aIndex[cur_std_idx]); |
1048 | 0 | AVI->track[audtr].audio_superindex->aIndex[cur_std_idx].dwDuration = |
1049 | 0 | AVI->track[audtr].audio_superindex->stdindex[cur_std_idx]->nEntriesInUse - 1; |
1050 | 0 | if (AVI->track[audtr].a_fmt == 0x1) { |
1051 | 0 | AVI->track[audtr].audio_superindex->aIndex[cur_std_idx].dwDuration *= |
1052 | 0 | AVI->track[audtr].a_bits*AVI->track[audtr].a_rate*AVI->track[audtr].a_chans/800; |
1053 | 0 | } |
1054 | 0 | } |
1055 | | // The AVI->video_superindex->nEntriesInUse contains the offset |
1056 | 0 | AVI->video_superindex->stdindex[ cur_std_idx+1 ]->qwBaseOffset = AVI->pos; |
1057 | | |
1058 | | // Correct! |
1059 | 0 | movi_len = AVI->video_superindex->stdindex[ 1 ]->qwBaseOffset - HEADERBYTES+4 - AVI->n_idx*16 - 8; |
1060 | 0 | } else { |
1061 | 0 | movi_len = AVI->pos - HEADERBYTES + 4; |
1062 | 0 | } |
1063 | | |
1064 | | |
1065 | | /* Try to output the index entries. This may fail e.g. if no space |
1066 | | is left on device. We will report this as an error, but we still |
1067 | | try to write the header correctly (so that the file still may be |
1068 | | readable in the most cases */ |
1069 | |
|
1070 | 0 | idxerror = 0; |
1071 | 0 | hasIndex = 1; |
1072 | 0 | if (!AVI->is_opendml) { |
1073 | | // GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] pos=%lu, index_len=%ld \n", AVI->pos, AVI->n_idx*16)); |
1074 | 0 | int ret = avi_add_chunk(AVI, (unsigned char *)"idx1", (unsigned char *)AVI->idx, AVI->n_idx*16); |
1075 | 0 | hasIndex = (ret==0); |
1076 | | //GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] pos=%lu, index_len=%d\n", AVI->pos, hasIndex)); |
1077 | |
|
1078 | 0 | if(ret) { |
1079 | 0 | idxerror = 1; |
1080 | 0 | AVI_errno = AVI_ERR_WRITE_INDEX; |
1081 | 0 | } |
1082 | 0 | } |
1083 | | |
1084 | | /* Calculate Microseconds per frame */ |
1085 | |
|
1086 | 0 | if(AVI->fps < 0.001) { |
1087 | 0 | frate=0; |
1088 | 0 | ms_per_frame=0; |
1089 | 0 | } else { |
1090 | 0 | frate = (int) (FRAME_RATE_SCALE*AVI->fps + 0.5); |
1091 | 0 | ms_per_frame=(int) (1000000/AVI->fps + 0.5); |
1092 | 0 | } |
1093 | | |
1094 | | /* Prepare the file header */ |
1095 | |
|
1096 | 0 | nhb = 0; |
1097 | | |
1098 | | /* The RIFF header */ |
1099 | |
|
1100 | 0 | OUT4CC ("RIFF"); |
1101 | 0 | if (AVI->is_opendml) { |
1102 | 0 | OUTLONG(AVI->video_superindex->stdindex[ 1 ]->qwBaseOffset - 8); /* # of bytes to follow */ |
1103 | 0 | } else { |
1104 | 0 | OUTLONG(AVI->pos - 8); /* # of bytes to follow */ |
1105 | 0 | } |
1106 | |
|
1107 | 0 | OUT4CC ("AVI "); |
1108 | | |
1109 | | /* Start the header list */ |
1110 | |
|
1111 | 0 | OUT4CC ("LIST"); |
1112 | 0 | OUTLONG(0); /* Length of list in bytes, don't know yet */ |
1113 | 0 | hdrl_start = nhb; /* Store start position */ |
1114 | 0 | OUT4CC ("hdrl"); |
1115 | | |
1116 | | /* The main AVI header */ |
1117 | | |
1118 | | /* The Flags in AVI File header */ |
1119 | |
|
1120 | 0 | #define AVIF_HASINDEX 0x00000010 /* Index at end of file */ |
1121 | 0 | #define AVIF_MUSTUSEINDEX 0x00000020 |
1122 | 0 | #define AVIF_ISINTERLEAVED 0x00000100 |
1123 | 0 | #define AVIF_TRUSTCKTYPE 0x00000800 /* Use CKType to find key frames */ |
1124 | 0 | #define AVIF_WASCAPTUREFILE 0x00010000 |
1125 | 0 | #define AVIF_COPYRIGHTED 0x00020000 |
1126 | |
|
1127 | 0 | OUT4CC ("avih"); |
1128 | 0 | OUTLONG(56); /* # of bytes to follow */ |
1129 | 0 | OUTLONG(ms_per_frame); /* Microseconds per frame */ |
1130 | | //ThOe ->0 |
1131 | | // OUTLONG(10000000); /* MaxBytesPerSec, I hope this will never be used */ |
1132 | 0 | OUTLONG(0); |
1133 | 0 | OUTLONG(0); /* PaddingGranularity (whatever that might be) */ |
1134 | | /* Other sources call it 'reserved' */ |
1135 | 0 | flag = AVIF_ISINTERLEAVED; |
1136 | 0 | if(hasIndex) flag |= AVIF_HASINDEX; |
1137 | 0 | if(hasIndex && AVI->must_use_index) flag |= AVIF_MUSTUSEINDEX; |
1138 | 0 | OUTLONG(flag); /* Flags */ |
1139 | 0 | OUTLONG(AVI->video_frames); /* TotalFrames */ |
1140 | 0 | OUTLONG(0); /* InitialFrames */ |
1141 | |
|
1142 | 0 | OUTLONG(AVI->anum+1); |
1143 | | // if (AVI->track[0].audio_bytes) |
1144 | | // { OUTLONG(2); } /* Streams */ |
1145 | | // else |
1146 | | // { OUTLONG(1); } /* Streams */ |
1147 | |
|
1148 | 0 | OUTLONG(0); /* SuggestedBufferSize */ |
1149 | 0 | OUTLONG(AVI->width); /* Width */ |
1150 | 0 | OUTLONG(AVI->height); /* Height */ |
1151 | | /* MS calls the following 'reserved': */ |
1152 | 0 | OUTLONG(0); /* TimeScale: Unit used to measure time */ |
1153 | 0 | OUTLONG(0); /* DataRate: Data rate of playback */ |
1154 | 0 | OUTLONG(0); /* StartTime: Starting time of AVI data */ |
1155 | 0 | OUTLONG(0); /* DataLength: Size of AVI data chunk */ |
1156 | | |
1157 | | |
1158 | | /* Start the video stream list ---------------------------------- */ |
1159 | |
|
1160 | 0 | OUT4CC ("LIST"); |
1161 | 0 | OUTLONG(0); /* Length of list in bytes, don't know yet */ |
1162 | 0 | strl_start = nhb; /* Store start position */ |
1163 | 0 | OUT4CC ("strl"); |
1164 | | |
1165 | | /* The video stream header */ |
1166 | |
|
1167 | 0 | OUT4CC ("strh"); |
1168 | 0 | OUTLONG(56); /* # of bytes to follow */ |
1169 | 0 | OUT4CC ("vids"); /* Type */ |
1170 | 0 | OUT4CC (AVI->compressor); /* Handler */ |
1171 | 0 | OUTLONG(0); /* Flags */ |
1172 | 0 | OUTLONG(0); /* Reserved, MS says: wPriority, wLanguage */ |
1173 | 0 | OUTLONG(0); /* InitialFrames */ |
1174 | 0 | OUTLONG(FRAME_RATE_SCALE); /* Scale */ |
1175 | 0 | OUTLONG(frate); /* Rate: Rate/Scale == samples/second */ |
1176 | 0 | OUTLONG(0); /* Start */ |
1177 | 0 | OUTLONG(AVI->video_frames); /* Length */ |
1178 | 0 | OUTLONG(AVI->max_len); /* SuggestedBufferSize */ |
1179 | 0 | OUTLONG(0); /* Quality */ |
1180 | 0 | OUTLONG(0); /* SampleSize */ |
1181 | 0 | OUTLONG(0); /* Frame */ |
1182 | 0 | OUTLONG(0); /* Frame */ |
1183 | | //OUTLONG(0); /* Frame */ |
1184 | | //OUTLONG(0); /* Frame */ |
1185 | | |
1186 | | /* The video stream format */ |
1187 | |
|
1188 | 0 | xd_size = AVI->extradata_size; |
1189 | 0 | xd_size_align2 = (AVI->extradata_size+1) & ~1; |
1190 | |
|
1191 | 0 | OUT4CC ("strf"); |
1192 | 0 | OUTLONG(40 + xd_size_align2);/* # of bytes to follow */ |
1193 | 0 | OUTLONG(40 + xd_size); /* Size */ |
1194 | 0 | OUTLONG(AVI->width); /* Width */ |
1195 | 0 | OUTLONG(AVI->height); /* Height */ |
1196 | 0 | OUTSHRT(1); |
1197 | 0 | OUTSHRT(24); /* Planes, Count */ |
1198 | 0 | OUT4CC (AVI->compressor); /* Compression */ |
1199 | | // ThOe (*3) |
1200 | 0 | OUTLONG(AVI->width*AVI->height*3); /* SizeImage (in bytes?) */ |
1201 | 0 | OUTLONG(0); /* XPelsPerMeter */ |
1202 | 0 | OUTLONG(0); /* YPelsPerMeter */ |
1203 | 0 | OUTLONG(0); /* ClrUsed: Number of colors used */ |
1204 | 0 | OUTLONG(0); /* ClrImportant: Number of colors important */ |
1205 | | |
1206 | | // write extradata if present |
1207 | 0 | if (xd_size > 0 && AVI->extradata) { |
1208 | 0 | OUTMEM(AVI->extradata, xd_size); |
1209 | 0 | if (xd_size != xd_size_align2) { |
1210 | 0 | OUTCHR(0); |
1211 | 0 | } |
1212 | 0 | } |
1213 | | |
1214 | | // dump index of indices for audio |
1215 | 0 | if (AVI->is_opendml) { |
1216 | 0 | u32 k; |
1217 | |
|
1218 | 0 | OUT4CC(AVI->video_superindex->fcc); |
1219 | 0 | OUTLONG(2+1+1+4+4+3*4 + AVI->video_superindex->nEntriesInUse * (8+4+4)); |
1220 | 0 | OUTSHRT(AVI->video_superindex->wLongsPerEntry); |
1221 | 0 | OUTCHR(AVI->video_superindex->bIndexSubType); |
1222 | 0 | OUTCHR(AVI->video_superindex->bIndexType); |
1223 | 0 | OUTLONG(AVI->video_superindex->nEntriesInUse); |
1224 | 0 | OUT4CC(AVI->video_superindex->dwChunkId); |
1225 | 0 | OUTLONG(0); |
1226 | 0 | OUTLONG(0); |
1227 | 0 | OUTLONG(0); |
1228 | | |
1229 | |
|
1230 | 0 | for (k = 0; k < AVI->video_superindex->nEntriesInUse; k++) { |
1231 | 0 | u32 r = (u32) ((AVI->video_superindex->aIndex[k].qwOffset >> 32) & 0xffffffff); |
1232 | 0 | u32 s = (u32) ((AVI->video_superindex->aIndex[k].qwOffset) & 0xffffffff); |
1233 | |
|
1234 | 0 | OUTLONG(s); |
1235 | 0 | OUTLONG(r); |
1236 | 0 | OUTLONG(AVI->video_superindex->aIndex[k].dwSize); |
1237 | 0 | OUTLONG(AVI->video_superindex->aIndex[k].dwDuration); |
1238 | 0 | } |
1239 | |
|
1240 | 0 | } |
1241 | | |
1242 | | /* Finish stream list, i.e. put number of bytes in the list to proper pos */ |
1243 | |
|
1244 | 0 | long2str(AVI_header+strl_start-4,nhb-strl_start); |
1245 | | |
1246 | | /* Start the audio stream list ---------------------------------- */ |
1247 | |
|
1248 | 0 | for(j=0; j<AVI->anum; ++j) { |
1249 | | |
1250 | | //if (AVI->track[j].a_chans && AVI->track[j].audio_bytes) |
1251 | 0 | { |
1252 | 0 | unsigned int nBlockAlign = 0; |
1253 | 0 | unsigned int avgbsec = 0; |
1254 | 0 | unsigned int scalerate = 0; |
1255 | |
|
1256 | 0 | int sampsize = avi_sampsize(AVI, j); |
1257 | 0 | sampsize = AVI->track[j].a_fmt==0x1?sampsize*4:sampsize; |
1258 | |
|
1259 | 0 | nBlockAlign = (AVI->track[j].a_rate<32000)?576:1152; |
1260 | | /* |
1261 | | GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] XXX sampsize (%d) block (%ld) rate (%ld) audio_bytes (%ld) mp3rate(%ld,%ld)\n", |
1262 | | sampsize, nBlockAlign, AVI->track[j].a_rate, |
1263 | | (int int)AVI->track[j].audio_bytes, |
1264 | | 1000*AVI->track[j].mp3rate/8, AVI->track[j].mp3rate)); |
1265 | | */ |
1266 | |
|
1267 | 0 | if (AVI->track[j].a_fmt==0x1) { |
1268 | 0 | sampsize = (AVI->track[j].a_chans<2)?sampsize/2:sampsize; |
1269 | 0 | avgbsec = AVI->track[j].a_rate*sampsize/4; |
1270 | 0 | scalerate = AVI->track[j].a_rate*sampsize/4; |
1271 | 0 | } else { |
1272 | 0 | avgbsec = 1000*AVI->track[j].mp3rate/8; |
1273 | 0 | scalerate = 1000*AVI->track[j].mp3rate/8; |
1274 | 0 | } |
1275 | |
|
1276 | 0 | OUT4CC ("LIST"); |
1277 | 0 | OUTLONG(0); /* Length of list in bytes, don't know yet */ |
1278 | 0 | strl_start = nhb; /* Store start position */ |
1279 | 0 | OUT4CC ("strl"); |
1280 | | |
1281 | | /* The audio stream header */ |
1282 | |
|
1283 | 0 | OUT4CC ("strh"); |
1284 | 0 | OUTLONG(56); /* # of bytes to follow */ |
1285 | 0 | OUT4CC ("auds"); |
1286 | | |
1287 | | // ----------- |
1288 | | // ThOe |
1289 | 0 | OUTLONG(0); /* Format (Optionally) */ |
1290 | | // ----------- |
1291 | |
|
1292 | 0 | OUTLONG(0); /* Flags */ |
1293 | 0 | OUTLONG(0); /* Reserved, MS says: wPriority, wLanguage */ |
1294 | 0 | OUTLONG(0); /* InitialFrames */ |
1295 | | |
1296 | | // VBR |
1297 | 0 | if (AVI->track[j].a_fmt == 0x55 && AVI->track[j].a_vbr) { |
1298 | 0 | OUTLONG(nBlockAlign); /* Scale */ |
1299 | 0 | OUTLONG(AVI->track[j].a_rate); /* Rate */ |
1300 | 0 | OUTLONG(0); /* Start */ |
1301 | 0 | OUTLONG(AVI->track[j].audio_chunks); /* Length */ |
1302 | 0 | OUTLONG(0); /* SuggestedBufferSize */ |
1303 | 0 | OUTLONG(0); /* Quality */ |
1304 | 0 | OUTLONG(0); /* SampleSize */ |
1305 | 0 | OUTLONG(0); /* Frame */ |
1306 | 0 | OUTLONG(0); /* Frame */ |
1307 | 0 | } else { |
1308 | 0 | OUTLONG(sampsize/4); /* Scale */ |
1309 | 0 | OUTLONG(scalerate); /* Rate */ |
1310 | 0 | OUTLONG(0); /* Start */ |
1311 | 0 | OUTLONG(4*AVI->track[j].audio_bytes/sampsize); /* Length */ |
1312 | 0 | OUTLONG(0); /* SuggestedBufferSize */ |
1313 | 0 | OUTLONG(0xffffffff); /* Quality */ |
1314 | 0 | OUTLONG(sampsize/4); /* SampleSize */ |
1315 | 0 | OUTLONG(0); /* Frame */ |
1316 | 0 | OUTLONG(0); /* Frame */ |
1317 | 0 | } |
1318 | | |
1319 | | /* The audio stream format */ |
1320 | |
|
1321 | 0 | OUT4CC ("strf"); |
1322 | |
|
1323 | 0 | if (AVI->track[j].a_fmt == 0x55 && AVI->track[j].a_vbr) { |
1324 | |
|
1325 | 0 | OUTLONG(30); /* # of bytes to follow */ // mplayer writes 28 |
1326 | 0 | OUTSHRT(AVI->track[j].a_fmt); /* Format */ // 2 |
1327 | 0 | OUTSHRT(AVI->track[j].a_chans); /* Number of channels */ // 2 |
1328 | 0 | OUTLONG(AVI->track[j].a_rate); /* SamplesPerSec */ // 4 |
1329 | | //ThOe/tibit |
1330 | 0 | OUTLONG(1000*AVI->track[j].mp3rate/8); /* maybe we should write an avg. */ // 4 |
1331 | 0 | OUTSHRT(nBlockAlign); /* BlockAlign */ // 2 |
1332 | 0 | OUTSHRT(AVI->track[j].a_bits); /* BitsPerSample */ // 2 |
1333 | |
|
1334 | 0 | OUTSHRT(12); /* cbSize */ // 2 |
1335 | 0 | OUTSHRT(1); /* wID */ // 2 |
1336 | 0 | OUTLONG(2); /* fdwFlags */ // 4 |
1337 | 0 | OUTSHRT(nBlockAlign); /* nBlockSize */ // 2 |
1338 | 0 | OUTSHRT(1); /* nFramesPerBlock */ // 2 |
1339 | 0 | OUTSHRT(0); /* nCodecDelay */ // 2 |
1340 | |
|
1341 | 0 | } else if (AVI->track[j].a_fmt == 0x55 && !AVI->track[j].a_vbr) { |
1342 | |
|
1343 | 0 | OUTLONG(30); /* # of bytes to follow */ |
1344 | 0 | OUTSHRT(AVI->track[j].a_fmt); /* Format */ |
1345 | 0 | OUTSHRT(AVI->track[j].a_chans); /* Number of channels */ |
1346 | 0 | OUTLONG(AVI->track[j].a_rate); /* SamplesPerSec */ |
1347 | | //ThOe/tibit |
1348 | 0 | OUTLONG(1000*AVI->track[j].mp3rate/8); |
1349 | 0 | OUTSHRT(sampsize/4); /* BlockAlign */ |
1350 | 0 | OUTSHRT(AVI->track[j].a_bits); /* BitsPerSample */ |
1351 | |
|
1352 | 0 | OUTSHRT(12); /* cbSize */ |
1353 | 0 | OUTSHRT(1); /* wID */ |
1354 | 0 | OUTLONG(2); /* fdwFlags */ |
1355 | 0 | OUTSHRT(nBlockAlign); /* nBlockSize */ |
1356 | 0 | OUTSHRT(1); /* nFramesPerBlock */ |
1357 | 0 | OUTSHRT(0); /* nCodecDelay */ |
1358 | |
|
1359 | 0 | } else { |
1360 | |
|
1361 | 0 | OUTLONG(18); /* # of bytes to follow */ |
1362 | 0 | OUTSHRT(AVI->track[j].a_fmt); /* Format */ |
1363 | 0 | OUTSHRT(AVI->track[j].a_chans); /* Number of channels */ |
1364 | 0 | OUTLONG(AVI->track[j].a_rate); /* SamplesPerSec */ |
1365 | | //ThOe/tibit |
1366 | 0 | OUTLONG(avgbsec); /* Avg bytes/sec */ |
1367 | 0 | OUTSHRT(sampsize/4); /* BlockAlign */ |
1368 | 0 | OUTSHRT(AVI->track[j].a_bits); /* BitsPerSample */ |
1369 | 0 | OUTSHRT(0); /* cbSize */ |
1370 | |
|
1371 | 0 | } |
1372 | 0 | } |
1373 | 0 | if (AVI->is_opendml) { |
1374 | 0 | u32 k; |
1375 | |
|
1376 | 0 | if (!AVI->track[j].audio_superindex) { |
1377 | | // not initialized -> no index |
1378 | 0 | continue; |
1379 | 0 | } |
1380 | | |
1381 | 0 | OUT4CC(AVI->track[j].audio_superindex->fcc); /* "indx" */ |
1382 | 0 | OUTLONG(2+1+1+4+4+3*4 + AVI->track[j].audio_superindex->nEntriesInUse * (8+4+4)); |
1383 | 0 | OUTSHRT(AVI->track[j].audio_superindex->wLongsPerEntry); |
1384 | 0 | OUTCHR(AVI->track[j].audio_superindex->bIndexSubType); |
1385 | 0 | OUTCHR(AVI->track[j].audio_superindex->bIndexType); |
1386 | 0 | OUTLONG(AVI->track[j].audio_superindex->nEntriesInUse); |
1387 | 0 | OUT4CC(AVI->track[j].audio_superindex->dwChunkId); |
1388 | 0 | OUTLONG(0); |
1389 | 0 | OUTLONG(0); |
1390 | 0 | OUTLONG(0); |
1391 | |
|
1392 | 0 | for (k = 0; k < AVI->track[j].audio_superindex->nEntriesInUse; k++) { |
1393 | 0 | u32 r = (u32) ((AVI->track[j].audio_superindex->aIndex[k].qwOffset >> 32) & 0xffffffff); |
1394 | 0 | u32 s = (u32) ((AVI->track[j].audio_superindex->aIndex[k].qwOffset) & 0xffffffff); |
1395 | | |
1396 | | /* |
1397 | | GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] AUD[%d] NrEntries %d/%ld (%c%c%c%c) |0x%llX|%ld|%ld| \n", j, k, |
1398 | | AVI->track[j].audio_superindex->nEntriesInUse, |
1399 | | AVI->track[j].audio_superindex->dwChunkId[0], AVI->track[j].audio_superindex->dwChunkId[1], |
1400 | | AVI->track[j].audio_superindex->dwChunkId[2], AVI->track[j].audio_superindex->dwChunkId[3], |
1401 | | AVI->track[j].audio_superindex->aIndex[k].qwOffset, |
1402 | | AVI->track[j].audio_superindex->aIndex[k].dwSize, |
1403 | | AVI->track[j].audio_superindex->aIndex[k].dwDuration |
1404 | | )); |
1405 | | */ |
1406 | |
|
1407 | 0 | OUTLONG(s); |
1408 | 0 | OUTLONG(r); |
1409 | 0 | OUTLONG(AVI->track[j].audio_superindex->aIndex[k].dwSize); |
1410 | 0 | OUTLONG(AVI->track[j].audio_superindex->aIndex[k].dwDuration); |
1411 | 0 | } |
1412 | 0 | } |
1413 | | /* Finish stream list, i.e. put number of bytes in the list to proper pos */ |
1414 | 0 | long2str(AVI_header+strl_start-4,nhb-strl_start); |
1415 | 0 | } |
1416 | |
|
1417 | 0 | if (AVI->is_opendml) { |
1418 | 0 | OUT4CC("LIST"); |
1419 | 0 | OUTLONG(16); |
1420 | 0 | OUT4CC("odml"); |
1421 | 0 | OUT4CC("dmlh"); |
1422 | 0 | OUTLONG(4); |
1423 | 0 | OUTLONG(AVI->total_frames); |
1424 | 0 | } |
1425 | | |
1426 | | /* Finish header list */ |
1427 | |
|
1428 | 0 | long2str(AVI_header+hdrl_start-4,nhb-hdrl_start); |
1429 | | |
1430 | | |
1431 | | // add INFO list --- (0.6.0pre4) |
1432 | |
|
1433 | 0 | #ifdef INFO_LIST |
1434 | 0 | OUT4CC ("LIST"); |
1435 | |
|
1436 | 0 | info_start_pos = nhb; |
1437 | 0 | info_len = MAX_INFO_STRLEN + 12; |
1438 | 0 | OUTLONG(info_len); // rewritten later |
1439 | 0 | OUT4CC ("INFO"); |
1440 | |
|
1441 | 0 | OUT4CC ("ISFT"); |
1442 | | //OUTLONG(MAX_INFO_STRLEN); |
1443 | 0 | memset(id_str, 0, MAX_INFO_STRLEN); |
1444 | 0 | if (gf_sys_is_test_mode()) { |
1445 | 0 | snprintf(id_str, MAX_INFO_STRLEN, "GPAC/avilib"); |
1446 | 0 | } else { |
1447 | 0 | snprintf(id_str, MAX_INFO_STRLEN, "GPAC/avilib-%s", gf_gpac_version()); |
1448 | 0 | } |
1449 | 0 | real_id_len = id_len = (u32) strlen(id_str)+1; |
1450 | 0 | if (id_len&1) id_len++; |
1451 | |
|
1452 | 0 | OUTLONG(real_id_len); |
1453 | |
|
1454 | 0 | memset(AVI_header+nhb, 0, id_len); |
1455 | 0 | memcpy(AVI_header+nhb, id_str, id_len); |
1456 | 0 | nhb += id_len; |
1457 | |
|
1458 | 0 | info_len = 0; |
1459 | | |
1460 | | // write correct len |
1461 | 0 | long2str(AVI_header+info_start_pos, info_len + id_len + 4+4+4); |
1462 | |
|
1463 | 0 | nhb += info_len; |
1464 | | |
1465 | | // OUT4CC ("ICMT"); |
1466 | | // OUTLONG(MAX_INFO_STRLEN); |
1467 | | |
1468 | | // calptr=time(NULL); |
1469 | | // sprintf(id_str, "\t%s %s", ctime(&calptr), ""); |
1470 | | // memset(AVI_header+nhb, 0, MAX_INFO_STRLEN); |
1471 | | // memcpy(AVI_header+nhb, id_str, 25); |
1472 | | // nhb += MAX_INFO_STRLEN; |
1473 | 0 | #endif |
1474 | | |
1475 | | // ---------------------------- |
1476 | | |
1477 | | /* Calculate the needed amount of junk bytes, output junk */ |
1478 | |
|
1479 | 0 | njunk = HEADERBYTES - nhb - 8 - 12; |
1480 | | |
1481 | | /* Safety first: if njunk <= 0, somebody has played with |
1482 | | HEADERBYTES without knowing what (s)he did. |
1483 | | This is a fatal error */ |
1484 | |
|
1485 | 0 | if(njunk<=0) |
1486 | 0 | { |
1487 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[avilib] AVI_close_output_file: # of header bytes too small\n")); |
1488 | 0 | exit(1); |
1489 | 0 | } |
1490 | | |
1491 | 0 | OUT4CC ("JUNK"); |
1492 | 0 | OUTLONG(njunk); |
1493 | 0 | memset(AVI_header+nhb,0,njunk); |
1494 | |
|
1495 | 0 | nhb += njunk; |
1496 | | |
1497 | | /* Start the movi list */ |
1498 | |
|
1499 | 0 | OUT4CC ("LIST"); |
1500 | 0 | OUTLONG(movi_len); /* Length of list in bytes */ |
1501 | 0 | OUT4CC ("movi"); |
1502 | | |
1503 | | /* Output the header, truncate the file to the number of bytes |
1504 | | actually written, report an error if someting goes wrong */ |
1505 | |
|
1506 | 0 | if ( (gf_fseek(AVI->fdes,0,SEEK_SET)==(u64)-1) || |
1507 | 0 | avi_write(AVI->fdes,(char *)AVI_header,HEADERBYTES)!=HEADERBYTES |
1508 | | // || ftruncate(AVI->fdes,AVI->pos)<0 |
1509 | 0 | ) |
1510 | 0 | { |
1511 | 0 | AVI_errno = AVI_ERR_CLOSE; |
1512 | 0 | return -1; |
1513 | 0 | } |
1514 | | |
1515 | | |
1516 | | // Fix up the empty additional RIFF and LIST chunks |
1517 | 0 | if (AVI->is_opendml) { |
1518 | 0 | u32 k; |
1519 | 0 | char f[4]; |
1520 | 0 | u32 len; |
1521 | |
|
1522 | 0 | for (k=1; k<AVI->video_superindex->nEntriesInUse; k++) { |
1523 | | // the len of the RIFF Chunk |
1524 | 0 | gf_fseek(AVI->fdes, AVI->video_superindex->stdindex[k]->qwBaseOffset+4, SEEK_SET); |
1525 | 0 | len = (u32) (AVI->video_superindex->stdindex[k+1]->qwBaseOffset - AVI->video_superindex->stdindex[k]->qwBaseOffset - 8); |
1526 | 0 | long2str((unsigned char *)f, len); |
1527 | 0 | avi_write(AVI->fdes, f, 4); |
1528 | | |
1529 | | // len of the LIST/movi chunk |
1530 | 0 | gf_fseek(AVI->fdes, 8, SEEK_CUR); |
1531 | 0 | len -= 12; |
1532 | 0 | long2str((unsigned char *)f, len); |
1533 | 0 | avi_write(AVI->fdes, f, 4); |
1534 | 0 | } |
1535 | 0 | } |
1536 | | |
1537 | |
|
1538 | 0 | if(idxerror) return -1; |
1539 | | |
1540 | 0 | return 0; |
1541 | 0 | } |
1542 | | |
1543 | | /* |
1544 | | AVI_write_data: |
1545 | | Add video or audio data to the file; |
1546 | | |
1547 | | Return values: |
1548 | | 0 No error; |
1549 | | -1 Error, AVI_errno is set appropriatly; |
1550 | | |
1551 | | */ |
1552 | | |
1553 | | static int avi_write_data(avi_t *AVI, char *data, unsigned int length, int audio, int keyframe) |
1554 | 0 | { |
1555 | 0 | int n = 0; |
1556 | |
|
1557 | 0 | unsigned char astr[5]; |
1558 | | |
1559 | | // transcode core itself checks for the size -- unneeded and |
1560 | | // does harm to xvid 2pass encodes where the first pass can get |
1561 | | // _very_ large -- tibit. |
1562 | |
|
1563 | | #if 0 |
1564 | | /* Check for maximum file length */ |
1565 | | |
1566 | | if ( (AVI->pos + 8 + length + 8 + (AVI->n_idx+1)*16) > AVI_MAX_LEN ) { |
1567 | | AVI_errno = AVI_ERR_SIZELIM; |
1568 | | return -1; |
1569 | | } |
1570 | | #endif |
1571 | | |
1572 | | /* Add index entry */ |
1573 | | |
1574 | | //set tag for current audio track |
1575 | 0 | sprintf((char *)astr, "0%1dwb", (int)(AVI->aptr+1)); |
1576 | |
|
1577 | 0 | if(audio) { |
1578 | 0 | if (!AVI->is_opendml) n = avi_add_index_entry(AVI,astr,0x10,AVI->pos,length); |
1579 | 0 | n += avi_add_odml_index_entry(AVI,astr,0x10,AVI->pos,length); |
1580 | 0 | } else { |
1581 | 0 | if (!AVI->is_opendml) n = avi_add_index_entry(AVI,(unsigned char *)"00db",((keyframe)?0x10:0x0),AVI->pos,length); |
1582 | 0 | n += avi_add_odml_index_entry(AVI,(unsigned char *)"00db",((keyframe)?0x10:0x0),AVI->pos,length); |
1583 | 0 | } |
1584 | |
|
1585 | 0 | if(n) return -1; |
1586 | | |
1587 | | /* Output tag and data */ |
1588 | | |
1589 | 0 | if(audio) |
1590 | 0 | n = avi_add_chunk(AVI,(unsigned char *)astr, (unsigned char *)data, length); |
1591 | 0 | else |
1592 | 0 | n = avi_add_chunk(AVI,(unsigned char *)"00db", (unsigned char *)data, length); |
1593 | |
|
1594 | 0 | if (n) return -1; |
1595 | | |
1596 | 0 | return 0; |
1597 | 0 | } |
1598 | | |
1599 | | GF_EXPORT |
1600 | | int AVI_write_frame(avi_t *AVI, u8 *data, int bytes, int keyframe) |
1601 | 0 | { |
1602 | 0 | s64 pos; |
1603 | |
|
1604 | 0 | if(AVI->mode==AVI_MODE_READ) { |
1605 | 0 | AVI_errno = AVI_ERR_NOT_PERM; |
1606 | 0 | return -1; |
1607 | 0 | } |
1608 | | |
1609 | 0 | pos = AVI->pos; |
1610 | |
|
1611 | 0 | if(avi_write_data(AVI,data,bytes,0,keyframe)) return -1; |
1612 | | |
1613 | 0 | AVI->last_pos = pos; |
1614 | 0 | AVI->last_len = bytes; |
1615 | 0 | AVI->video_frames++; |
1616 | 0 | return 0; |
1617 | 0 | } |
1618 | | |
1619 | | #if 0 //unused |
1620 | | int AVI_dup_frame(avi_t *AVI) |
1621 | | { |
1622 | | if(AVI->mode==AVI_MODE_READ) { |
1623 | | AVI_errno = AVI_ERR_NOT_PERM; |
1624 | | return -1; |
1625 | | } |
1626 | | |
1627 | | if(AVI->last_pos==0) return 0; /* No previous real frame */ |
1628 | | if(avi_add_index_entry(AVI,(unsigned char *)"00db",0x10,AVI->last_pos,AVI->last_len)) return -1; |
1629 | | AVI->video_frames++; |
1630 | | AVI->must_use_index = 1; |
1631 | | return 0; |
1632 | | } |
1633 | | #endif |
1634 | | |
1635 | | GF_EXPORT |
1636 | | int AVI_write_audio(avi_t *AVI, u8 *data, int bytes) |
1637 | 0 | { |
1638 | 0 | if(AVI->mode==AVI_MODE_READ) { |
1639 | 0 | AVI_errno = AVI_ERR_NOT_PERM; |
1640 | 0 | return -1; |
1641 | 0 | } |
1642 | | |
1643 | 0 | if( avi_write_data(AVI,data,bytes,1,0) ) return -1; |
1644 | 0 | AVI->track[AVI->aptr].audio_bytes += bytes; |
1645 | 0 | AVI->track[AVI->aptr].audio_chunks++; |
1646 | 0 | return 0; |
1647 | 0 | } |
1648 | | |
1649 | | #if 0 //unused |
1650 | | |
1651 | | int AVI_append_audio(avi_t *AVI, u8 *data, int bytes) |
1652 | | { |
1653 | | |
1654 | | // won't work for >2gb |
1655 | | int i, length, pos; |
1656 | | unsigned char c[4]; |
1657 | | |
1658 | | if(AVI->mode==AVI_MODE_READ) { |
1659 | | AVI_errno = AVI_ERR_NOT_PERM; |
1660 | | return -1; |
1661 | | } |
1662 | | |
1663 | | // update last index entry: |
1664 | | |
1665 | | --AVI->n_idx; |
1666 | | length = str2ulong(AVI->idx[AVI->n_idx]+12); |
1667 | | pos = str2ulong(AVI->idx[AVI->n_idx]+8); |
1668 | | |
1669 | | //update; |
1670 | | long2str(AVI->idx[AVI->n_idx]+12,length+bytes); |
1671 | | |
1672 | | ++AVI->n_idx; |
1673 | | |
1674 | | AVI->track[AVI->aptr].audio_bytes += bytes; |
1675 | | |
1676 | | //update chunk header |
1677 | | gf_fseek(AVI->fdes, pos+4, SEEK_SET); |
1678 | | long2str(c, length+bytes); |
1679 | | avi_write(AVI->fdes, (char *)c, 4); |
1680 | | |
1681 | | gf_fseek(AVI->fdes, pos+8+length, SEEK_SET); |
1682 | | |
1683 | | i=PAD_EVEN(length + bytes); |
1684 | | |
1685 | | bytes = i - length; |
1686 | | avi_write(AVI->fdes, data, bytes); |
1687 | | AVI->pos = pos + 8 + i; |
1688 | | |
1689 | | return 0; |
1690 | | } |
1691 | | |
1692 | | u64 AVI_bytes_remain(avi_t *AVI) |
1693 | | { |
1694 | | if(AVI->mode==AVI_MODE_READ) return 0; |
1695 | | |
1696 | | return ( AVI_MAX_LEN - (AVI->pos + 8 + 16*AVI->n_idx)); |
1697 | | } |
1698 | | |
1699 | | u64 AVI_bytes_written(avi_t *AVI) |
1700 | | { |
1701 | | if(AVI->mode==AVI_MODE_READ) return 0; |
1702 | | |
1703 | | return (AVI->pos + 8 + 16*AVI->n_idx); |
1704 | | } |
1705 | | #endif |
1706 | | |
1707 | | int AVI_set_audio_track(avi_t *AVI, u32 track) |
1708 | 0 | { |
1709 | |
|
1710 | 0 | if (track + 1 > AVI->anum) return(-1); |
1711 | | |
1712 | | //this info is not written to file anyway |
1713 | 0 | AVI->aptr=track; |
1714 | 0 | return 0; |
1715 | 0 | } |
1716 | | |
1717 | | int AVI_get_audio_track(avi_t *AVI) |
1718 | 0 | { |
1719 | 0 | return(AVI->aptr); |
1720 | 0 | } |
1721 | | |
1722 | | #if 0 //unused |
1723 | | void AVI_set_audio_vbr(avi_t *AVI, int is_vbr) |
1724 | | { |
1725 | | AVI->track[AVI->aptr].a_vbr = is_vbr; |
1726 | | } |
1727 | | |
1728 | | int AVI_get_audio_vbr(avi_t *AVI) |
1729 | | { |
1730 | | return(AVI->track[AVI->aptr].a_vbr); |
1731 | | } |
1732 | | #endif |
1733 | | |
1734 | | |
1735 | | /******************************************************************* |
1736 | | * * |
1737 | | * Utilities for reading video and audio from an AVI File * |
1738 | | * * |
1739 | | *******************************************************************/ |
1740 | | |
1741 | | GF_EXPORT |
1742 | | int AVI_close(avi_t *AVI) |
1743 | 0 | { |
1744 | 0 | int ret; |
1745 | 0 | u32 j; |
1746 | | |
1747 | | /* If the file was open for writing, the header and index still have |
1748 | | to be written */ |
1749 | |
|
1750 | 0 | if(AVI->mode == AVI_MODE_WRITE) |
1751 | 0 | ret = avi_close_output_file(AVI); |
1752 | 0 | else |
1753 | 0 | ret = 0; |
1754 | | |
1755 | | /* Even if there happened an error, we first clean up */ |
1756 | |
|
1757 | 0 | gf_fclose(AVI->fdes); |
1758 | 0 | if(AVI->idx) gf_free(AVI->idx); |
1759 | 0 | if(AVI->video_index) gf_free(AVI->video_index); |
1760 | 0 | if(AVI->video_superindex) { |
1761 | 0 | if(AVI->video_superindex->aIndex) gf_free(AVI->video_superindex->aIndex); |
1762 | 0 | if (AVI->video_superindex->stdindex) { |
1763 | 0 | for (j=0; j < NR_IXNN_CHUNKS; j++) { |
1764 | 0 | if (AVI->video_superindex->stdindex[j]->aIndex) |
1765 | 0 | gf_free(AVI->video_superindex->stdindex[j]->aIndex); |
1766 | 0 | gf_free(AVI->video_superindex->stdindex[j]); |
1767 | 0 | } |
1768 | 0 | gf_free(AVI->video_superindex->stdindex); |
1769 | 0 | } |
1770 | 0 | gf_free(AVI->video_superindex); |
1771 | 0 | } |
1772 | |
|
1773 | 0 | for (j=0; j<AVI->anum; j++) |
1774 | 0 | { |
1775 | 0 | if(AVI->track[j].audio_index) gf_free(AVI->track[j].audio_index); |
1776 | 0 | if(AVI->track[j].audio_superindex) { |
1777 | 0 | avisuperindex_chunk *asi = AVI->track[j].audio_superindex; |
1778 | 0 | if (asi->aIndex) gf_free(asi->aIndex); |
1779 | |
|
1780 | 0 | if (asi->stdindex) { |
1781 | 0 | for (j=0; j < NR_IXNN_CHUNKS; j++) { |
1782 | 0 | if (asi->stdindex[j]->aIndex) |
1783 | 0 | gf_free(asi->stdindex[j]->aIndex); |
1784 | 0 | gf_free(asi->stdindex[j]); |
1785 | 0 | } |
1786 | 0 | gf_free(asi->stdindex); |
1787 | 0 | } |
1788 | 0 | gf_free(asi); |
1789 | 0 | } |
1790 | 0 | } |
1791 | |
|
1792 | 0 | if (AVI->bitmap_info_header) |
1793 | 0 | gf_free(AVI->bitmap_info_header); |
1794 | 0 | for (j = 0; j < AVI->anum; j++) |
1795 | 0 | if (AVI->wave_format_ex[j]) |
1796 | 0 | gf_free(AVI->wave_format_ex[j]); |
1797 | 0 | if (AVI->extradata) |
1798 | 0 | gf_free(AVI->extradata); |
1799 | |
|
1800 | 0 | gf_free(AVI); |
1801 | 0 | return ret; |
1802 | 0 | } |
1803 | | |
1804 | | |
1805 | 0 | #define ERR_EXIT(x) \ |
1806 | 0 | { \ |
1807 | 0 | AVI_close(AVI); \ |
1808 | 0 | AVI_errno = x; \ |
1809 | 0 | return 0; \ |
1810 | 0 | } |
1811 | | |
1812 | | |
1813 | | avi_t *AVI_open_input_file(char *filename, int getIndex) |
1814 | 0 | { |
1815 | 0 | avi_t *AVI; |
1816 | | |
1817 | | /* Create avi_t structure */ |
1818 | |
|
1819 | 0 | AVI = (avi_t *) gf_malloc(sizeof(avi_t)); |
1820 | 0 | if(AVI==NULL) |
1821 | 0 | { |
1822 | 0 | AVI_errno = AVI_ERR_NO_MEM; |
1823 | 0 | return 0; |
1824 | 0 | } |
1825 | 0 | memset((void *)AVI,0,sizeof(avi_t)); |
1826 | |
|
1827 | 0 | AVI->mode = AVI_MODE_READ; /* open for reading */ |
1828 | | |
1829 | | /* Open the file */ |
1830 | |
|
1831 | 0 | AVI->fdes = gf_fopen(filename,"rb"); |
1832 | 0 | if(!AVI->fdes ) |
1833 | 0 | { |
1834 | 0 | AVI_errno = AVI_ERR_OPEN; |
1835 | 0 | gf_free(AVI); |
1836 | 0 | return 0; |
1837 | 0 | } |
1838 | | |
1839 | 0 | AVI_errno = 0; |
1840 | 0 | avi_parse_input_file(AVI, getIndex); |
1841 | |
|
1842 | 0 | if (AVI != NULL && !AVI_errno) { |
1843 | 0 | AVI->aptr=0; //reset |
1844 | 0 | } |
1845 | |
|
1846 | 0 | if (AVI_errno) return NULL; |
1847 | | |
1848 | 0 | return AVI; |
1849 | 0 | } |
1850 | | |
1851 | | #if 0 |
1852 | | avi_t *AVI_open_fd(FILE *fd, int getIndex) |
1853 | | { |
1854 | | avi_t *AVI=NULL; |
1855 | | |
1856 | | /* Create avi_t structure */ |
1857 | | |
1858 | | AVI = (avi_t *) gf_malloc(sizeof(avi_t)); |
1859 | | if(AVI==NULL) |
1860 | | { |
1861 | | AVI_errno = AVI_ERR_NO_MEM; |
1862 | | return 0; |
1863 | | } |
1864 | | memset((void *)AVI,0,sizeof(avi_t)); |
1865 | | |
1866 | | AVI->mode = AVI_MODE_READ; /* open for reading */ |
1867 | | |
1868 | | // file alread open |
1869 | | AVI->fdes = fd; |
1870 | | |
1871 | | AVI_errno = 0; |
1872 | | avi_parse_input_file(AVI, getIndex); |
1873 | | |
1874 | | if (AVI != NULL && !AVI_errno) { |
1875 | | AVI->aptr=0; //reset |
1876 | | } |
1877 | | |
1878 | | if (AVI_errno) |
1879 | | return AVI=NULL; |
1880 | | else |
1881 | | return AVI; |
1882 | | } |
1883 | | #endif |
1884 | | |
1885 | | int avi_parse_input_file(avi_t *AVI, int getIndex) |
1886 | 0 | { |
1887 | 0 | int rate, scale, idx_type; |
1888 | 0 | s64 n, i; |
1889 | 0 | unsigned char *hdrl_data; |
1890 | 0 | u64 header_offset=0; |
1891 | 0 | int hdrl_len=0; |
1892 | 0 | int nvi, nai[AVI_MAX_TRACKS], ioff; |
1893 | 0 | u64 tot[AVI_MAX_TRACKS]; |
1894 | 0 | u32 j; |
1895 | 0 | int lasttag = 0; |
1896 | 0 | int vids_strh_seen = 0; |
1897 | 0 | int vids_strf_seen = 0; |
1898 | 0 | int auds_strh_seen = 0; |
1899 | | // int auds_strf_seen = 0; |
1900 | 0 | int num_stream = 0; |
1901 | 0 | char data[256]; |
1902 | 0 | s64 oldpos=-1, newpos=-1; |
1903 | |
|
1904 | 0 | int aud_chunks = 0; |
1905 | 0 | if (!AVI) { |
1906 | 0 | AVI_errno = AVI_ERR_OPEN; |
1907 | 0 | return 0; |
1908 | 0 | } |
1909 | | |
1910 | | /* Read first 12 bytes and check that this is an AVI file */ |
1911 | 0 | if (avi_read(AVI->fdes,data,12) != 12 ) |
1912 | 0 | ERR_EXIT(AVI_ERR_READ) |
1913 | | |
1914 | 0 | if (strnicmp(data ,"RIFF",4) !=0 || strnicmp(data+8,"AVI ",4) !=0 ) |
1915 | 0 | ERR_EXIT(AVI_ERR_NO_AVI) |
1916 | | |
1917 | | /* Go through the AVI file and extract the header list, |
1918 | | the start position of the 'movi' list and an optionally |
1919 | | present idx1 tag */ |
1920 | | |
1921 | 0 | hdrl_data = 0; |
1922 | |
|
1923 | 0 | while(1) |
1924 | 0 | { |
1925 | 0 | if( avi_read(AVI->fdes,data,8) != 8 ) break; /* We assume it's EOF */ |
1926 | 0 | newpos = gf_ftell(AVI->fdes); |
1927 | 0 | if(oldpos==newpos) { |
1928 | | /* This is a broken AVI stream... */ |
1929 | 0 | return -1; |
1930 | 0 | } |
1931 | 0 | oldpos=newpos; |
1932 | |
|
1933 | 0 | n = str2ulong((unsigned char *)data+4); |
1934 | 0 | n = PAD_EVEN(n); |
1935 | |
|
1936 | 0 | if(strnicmp(data,"LIST",4) == 0) |
1937 | 0 | { |
1938 | 0 | if( avi_read(AVI->fdes,data,4) != 4 ) ERR_EXIT(AVI_ERR_READ) |
1939 | 0 | n -= 4; |
1940 | 0 | if(strnicmp(data,"hdrl",4) == 0) |
1941 | 0 | { |
1942 | 0 | if (n>0xFFFFFFFF) ERR_EXIT(AVI_ERR_READ) |
1943 | 0 | hdrl_len = (u32) n; |
1944 | 0 | hdrl_data = (unsigned char *) gf_malloc((u32)n); |
1945 | 0 | if(hdrl_data==0) ERR_EXIT(AVI_ERR_NO_MEM); |
1946 | | |
1947 | | // offset of header |
1948 | |
|
1949 | 0 | header_offset = gf_ftell(AVI->fdes); |
1950 | |
|
1951 | 0 | if( avi_read(AVI->fdes,(char *)hdrl_data, (u32) n) != n ) { |
1952 | 0 | if (hdrl_data) gf_free(hdrl_data); |
1953 | 0 | ERR_EXIT(AVI_ERR_READ) |
1954 | 0 | } |
1955 | 0 | } |
1956 | 0 | else if(strnicmp(data,"movi",4) == 0) |
1957 | 0 | { |
1958 | 0 | AVI->movi_start = gf_ftell(AVI->fdes); |
1959 | 0 | if (gf_fseek(AVI->fdes,n,SEEK_CUR)==(u64)-1) break; |
1960 | 0 | } |
1961 | 0 | else if (gf_fseek(AVI->fdes,n,SEEK_CUR)==(u64)-1) break; |
1962 | 0 | } |
1963 | 0 | else if(strnicmp(data,"idx1",4) == 0) |
1964 | 0 | { |
1965 | | /* n must be a multiple of 16, but the reading does not |
1966 | | break if this is not the case */ |
1967 | |
|
1968 | 0 | AVI->n_idx = AVI->max_idx = (u32) (n/16); |
1969 | 0 | AVI->idx = (unsigned char((*)[16]) ) gf_malloc((u32)n); |
1970 | 0 | if(AVI->idx==0) { |
1971 | 0 | if (hdrl_data) gf_free(hdrl_data); |
1972 | 0 | ERR_EXIT(AVI_ERR_NO_MEM) |
1973 | 0 | } |
1974 | 0 | if(avi_read(AVI->fdes, (char *) AVI->idx, (u32) n) != n ) { |
1975 | 0 | gf_free( AVI->idx); |
1976 | 0 | AVI->idx=NULL; |
1977 | 0 | AVI->n_idx = 0; |
1978 | 0 | if (hdrl_data) gf_free(hdrl_data); |
1979 | 0 | ERR_EXIT(AVI_ERR_READ) |
1980 | 0 | } |
1981 | 0 | } |
1982 | 0 | else |
1983 | 0 | gf_fseek(AVI->fdes,n,SEEK_CUR); |
1984 | 0 | } |
1985 | | |
1986 | 0 | if(!hdrl_data) ERR_EXIT(AVI_ERR_NO_HDRL) |
1987 | 0 | if(!AVI->movi_start) ERR_EXIT(AVI_ERR_NO_MOVI) |
1988 | | |
1989 | | /* Interpret the header list */ |
1990 | | |
1991 | 0 | for(i=0; i<hdrl_len;) |
1992 | 0 | { |
1993 | | /* List tags are completly ignored */ |
1994 | |
|
1995 | | #ifdef DEBUG_ODML |
1996 | | GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] TAG %c%c%c%c\n", (hdrl_data+i)[0], (hdrl_data+i)[1], (hdrl_data+i)[2], (hdrl_data+i)[3])); |
1997 | | #endif |
1998 | |
|
1999 | 0 | if(strnicmp((char *)hdrl_data+i,"LIST",4)==0) { |
2000 | 0 | i+= 12; |
2001 | 0 | continue; |
2002 | 0 | } |
2003 | | |
2004 | 0 | n = str2ulong(hdrl_data+i+4); |
2005 | 0 | n = PAD_EVEN(n); |
2006 | 0 | if (i+n>hdrl_len) ERR_EXIT(AVI_ERR_READ) |
2007 | | |
2008 | | |
2009 | | /* Interpret the tag and its args */ |
2010 | | |
2011 | 0 | if(strnicmp((char *)hdrl_data+i,"strh",4)==0) |
2012 | 0 | { |
2013 | 0 | i += 8; |
2014 | | #ifdef DEBUG_ODML |
2015 | | GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] TAG %c%c%c%c\n", (hdrl_data+i)[0], (hdrl_data+i)[1], (hdrl_data+i)[2], (hdrl_data+i)[3])); |
2016 | | #endif |
2017 | 0 | if(strnicmp((char *)hdrl_data+i,"vids",4) == 0 && !vids_strh_seen) |
2018 | 0 | { |
2019 | 0 | memcpy(AVI->compressor,hdrl_data+i+4,4); |
2020 | 0 | AVI->compressor[4] = 0; |
2021 | | |
2022 | | // ThOe |
2023 | 0 | AVI->v_codech_off = header_offset + i+4; |
2024 | |
|
2025 | 0 | scale = str2ulong(hdrl_data+i+20); |
2026 | 0 | rate = str2ulong(hdrl_data+i+24); |
2027 | 0 | if(scale!=0) AVI->fps = (double)rate/(double)scale; |
2028 | 0 | AVI->video_frames = str2ulong(hdrl_data+i+32); |
2029 | 0 | AVI->video_strn = num_stream; |
2030 | 0 | AVI->max_len = 0; |
2031 | 0 | vids_strh_seen = 1; |
2032 | 0 | lasttag = 1; /* vids */ |
2033 | 0 | memcpy(&AVI->video_stream_header, hdrl_data + i, |
2034 | 0 | sizeof(alAVISTREAMHEADER)); |
2035 | 0 | } |
2036 | 0 | else if (strnicmp ((char *)hdrl_data+i,"auds",4) ==0 && ! auds_strh_seen) |
2037 | 0 | { |
2038 | | |
2039 | | //inc audio tracks |
2040 | 0 | AVI->aptr=AVI->anum; |
2041 | 0 | ++AVI->anum; |
2042 | |
|
2043 | 0 | if(AVI->anum > AVI_MAX_TRACKS) { |
2044 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[avilib] error - only %d audio tracks supported\n", AVI_MAX_TRACKS)); |
2045 | 0 | return(-1); |
2046 | 0 | } |
2047 | | |
2048 | 0 | AVI->track[AVI->aptr].audio_bytes = str2ulong(hdrl_data+i+32)*avi_sampsize(AVI, 0); |
2049 | 0 | AVI->track[AVI->aptr].audio_strn = num_stream; |
2050 | | |
2051 | | // if samplesize==0 -> vbr |
2052 | 0 | AVI->track[AVI->aptr].a_vbr = !str2ulong(hdrl_data+i+44); |
2053 | |
|
2054 | 0 | AVI->track[AVI->aptr].padrate = str2ulong(hdrl_data+i+24); |
2055 | 0 | memcpy(&AVI->stream_headers[AVI->aptr], hdrl_data + i, |
2056 | 0 | sizeof(alAVISTREAMHEADER)); |
2057 | | |
2058 | | // auds_strh_seen = 1; |
2059 | 0 | lasttag = 2; /* auds */ |
2060 | | |
2061 | | // ThOe |
2062 | 0 | AVI->track[AVI->aptr].a_codech_off = header_offset + i; |
2063 | |
|
2064 | 0 | } |
2065 | 0 | else if (strnicmp ((char*)hdrl_data+i,"iavs",4) ==0 && ! auds_strh_seen) { |
2066 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[avilib] AVILIB: error - DV AVI Type 1 no supported\n")); |
2067 | 0 | return (-1); |
2068 | 0 | } |
2069 | 0 | else |
2070 | 0 | lasttag = 0; |
2071 | 0 | num_stream++; |
2072 | 0 | } |
2073 | 0 | else if(strnicmp((char*)hdrl_data+i,"dmlh",4) == 0) { |
2074 | 0 | AVI->total_frames = str2ulong(hdrl_data+i+8); |
2075 | | #ifdef DEBUG_ODML |
2076 | | GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] real number of frames %d\n", AVI->total_frames)); |
2077 | | #endif |
2078 | 0 | i += 8; |
2079 | 0 | } |
2080 | 0 | else if(strnicmp((char *)hdrl_data+i,"strf",4)==0) |
2081 | 0 | { |
2082 | 0 | i += 8; |
2083 | 0 | if(lasttag == 1) |
2084 | 0 | { |
2085 | 0 | alBITMAPINFOHEADER bih; |
2086 | |
|
2087 | 0 | memcpy(&bih, hdrl_data + i, sizeof(alBITMAPINFOHEADER)); |
2088 | 0 | bih.bi_size = str2ulong((unsigned char *)&bih.bi_size); |
2089 | |
|
2090 | 0 | if (i + bih.bi_size > hdrl_len) ERR_EXIT(AVI_ERR_READ) |
2091 | | |
2092 | 0 | AVI->bitmap_info_header = (alBITMAPINFOHEADER *) gf_malloc(bih.bi_size); |
2093 | 0 | if (AVI->bitmap_info_header != NULL) |
2094 | 0 | memcpy(AVI->bitmap_info_header, hdrl_data + i, bih.bi_size); |
2095 | |
|
2096 | 0 | AVI->width = str2ulong(hdrl_data+i+4); |
2097 | 0 | AVI->height = str2ulong(hdrl_data+i+8); |
2098 | 0 | vids_strf_seen = 1; |
2099 | | //ThOe |
2100 | 0 | AVI->v_codecf_off = header_offset + i+16; |
2101 | |
|
2102 | 0 | memcpy(AVI->compressor2, hdrl_data+i+16, 4); |
2103 | 0 | AVI->compressor2[4] = 0; |
2104 | |
|
2105 | 0 | if (n>40) { |
2106 | 0 | if (n>0xFFFFFFFF) ERR_EXIT(AVI_ERR_READ) |
2107 | 0 | AVI->extradata_size = (u32) (n - 40); |
2108 | 0 | AVI->extradata = gf_malloc(sizeof(u8)* AVI->extradata_size); |
2109 | 0 | if (!AVI->extradata) ERR_EXIT(AVI_ERR_NO_MEM) |
2110 | 0 | memcpy(AVI->extradata, hdrl_data + i + 40, AVI->extradata_size); |
2111 | 0 | } |
2112 | |
|
2113 | 0 | } |
2114 | 0 | else if(lasttag == 2) |
2115 | 0 | { |
2116 | 0 | alWAVEFORMATEX *wfe; |
2117 | 0 | char *nwfe; |
2118 | 0 | int wfes; |
2119 | |
|
2120 | 0 | if ((u32) (hdrl_len - i) < sizeof(alWAVEFORMATEX)) |
2121 | 0 | wfes = (int) (hdrl_len - i); |
2122 | 0 | else |
2123 | 0 | wfes = sizeof(alWAVEFORMATEX); |
2124 | 0 | wfe = (alWAVEFORMATEX *)gf_malloc(sizeof(alWAVEFORMATEX)); |
2125 | 0 | if (wfe != NULL) { |
2126 | 0 | memset(wfe, 0, sizeof(alWAVEFORMATEX)); |
2127 | 0 | memcpy(wfe, hdrl_data + i, wfes); |
2128 | 0 | if (str2ushort((unsigned char *)&wfe->cb_size) != 0) { |
2129 | 0 | nwfe = (char *) |
2130 | 0 | gf_realloc(wfe, sizeof(alWAVEFORMATEX) + |
2131 | 0 | str2ushort((unsigned char *)&wfe->cb_size)); |
2132 | 0 | if (nwfe != 0) { |
2133 | 0 | s64 lpos = gf_ftell(AVI->fdes); |
2134 | 0 | gf_fseek(AVI->fdes, header_offset + i + sizeof(alWAVEFORMATEX), |
2135 | 0 | SEEK_SET); |
2136 | 0 | wfe = (alWAVEFORMATEX *)nwfe; |
2137 | 0 | nwfe = &nwfe[sizeof(alWAVEFORMATEX)]; |
2138 | 0 | avi_read(AVI->fdes, nwfe, |
2139 | 0 | str2ushort((unsigned char *)&wfe->cb_size)); |
2140 | 0 | gf_fseek(AVI->fdes, lpos, SEEK_SET); |
2141 | 0 | } |
2142 | 0 | } |
2143 | 0 | AVI->wave_format_ex[AVI->aptr] = wfe; |
2144 | 0 | } |
2145 | |
|
2146 | 0 | AVI->track[AVI->aptr].a_fmt = str2ushort(hdrl_data+i ); |
2147 | | |
2148 | | //ThOe |
2149 | 0 | AVI->track[AVI->aptr].a_codecf_off = header_offset + i; |
2150 | |
|
2151 | 0 | AVI->track[AVI->aptr].a_chans = str2ushort(hdrl_data+i+2); |
2152 | 0 | AVI->track[AVI->aptr].a_rate = str2ulong (hdrl_data+i+4); |
2153 | | //ThOe: read mp3bitrate |
2154 | 0 | AVI->track[AVI->aptr].mp3rate = 8*str2ulong(hdrl_data+i+8)/1000; |
2155 | | //:ThOe |
2156 | 0 | AVI->track[AVI->aptr].a_bits = str2ushort(hdrl_data+i+14); |
2157 | | // auds_strf_seen = 1; |
2158 | 0 | } |
2159 | 0 | } |
2160 | 0 | else if(strnicmp((char*)hdrl_data+i,"indx",4) == 0) { |
2161 | 0 | char *a; |
2162 | |
|
2163 | 0 | if(lasttag == 1) // V I D E O |
2164 | 0 | { |
2165 | |
|
2166 | 0 | a = (char*)hdrl_data+i; |
2167 | 0 | int avail =(int) (hdrl_len-i); |
2168 | 0 | if (avail<32) ERR_EXIT(AVI_ERR_READ) |
2169 | | |
2170 | 0 | AVI->video_superindex = (avisuperindex_chunk *) gf_malloc (sizeof (avisuperindex_chunk)); |
2171 | 0 | memset(AVI->video_superindex, 0, sizeof (avisuperindex_chunk)); |
2172 | 0 | memcpy (AVI->video_superindex->fcc, a, 4); |
2173 | 0 | a += 4; |
2174 | 0 | AVI->video_superindex->dwSize = str2ulong((unsigned char *)a); |
2175 | 0 | a += 4; |
2176 | 0 | AVI->video_superindex->wLongsPerEntry = str2ushort((unsigned char *)a); |
2177 | 0 | a += 2; |
2178 | 0 | AVI->video_superindex->bIndexSubType = *a; |
2179 | 0 | a += 1; |
2180 | 0 | AVI->video_superindex->bIndexType = *a; |
2181 | 0 | a += 1; |
2182 | 0 | AVI->video_superindex->nEntriesInUse = str2ulong((unsigned char *)a); |
2183 | 0 | a += 4; |
2184 | 0 | memcpy (AVI->video_superindex->dwChunkId, a, 4); |
2185 | 0 | a += 4; |
2186 | | |
2187 | | // 3 * reserved |
2188 | 0 | a += 4; |
2189 | 0 | a += 4; |
2190 | 0 | a += 4; |
2191 | |
|
2192 | 0 | if (AVI->video_superindex->bIndexSubType != 0) { |
2193 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[avilib] Invalid Header, bIndexSubType != 0\n")); |
2194 | 0 | } |
2195 | 0 | avail -= 32; |
2196 | 0 | if (avail < (int) AVI->video_superindex->nEntriesInUse*16) ERR_EXIT(AVI_ERR_READ) |
2197 | | |
2198 | 0 | AVI->video_superindex->aIndex = (avisuperindex_entry*) |
2199 | 0 | gf_malloc (AVI->video_superindex->wLongsPerEntry * AVI->video_superindex->nEntriesInUse * sizeof (u32)); |
2200 | | |
2201 | | // position of ix## chunks |
2202 | 0 | for (j=0; j<AVI->video_superindex->nEntriesInUse; ++j) { |
2203 | 0 | AVI->video_superindex->aIndex[j].qwOffset = str2ullong ((unsigned char*)a); |
2204 | 0 | a += 8; |
2205 | 0 | AVI->video_superindex->aIndex[j].dwSize = str2ulong ((unsigned char*)a); |
2206 | 0 | a += 4; |
2207 | 0 | AVI->video_superindex->aIndex[j].dwDuration = str2ulong ((unsigned char*)a); |
2208 | 0 | a += 4; |
2209 | |
|
2210 | | #ifdef DEBUG_ODML |
2211 | | GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] [%d] 0x%llx 0x%lx %lu\n", j, |
2212 | | (unsigned int long)AVI->video_superindex->aIndex[j].qwOffset, |
2213 | | (unsigned long)AVI->video_superindex->aIndex[j].dwSize, |
2214 | | (unsigned long)AVI->video_superindex->aIndex[j].dwDuration)); |
2215 | | #endif |
2216 | 0 | } |
2217 | | |
2218 | |
|
2219 | | #ifdef DEBUG_ODML |
2220 | | GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] FOURCC \"%c%c%c%c\"\n", AVI->video_superindex->fcc[0], AVI->video_superindex->fcc[1], |
2221 | | AVI->video_superindex->fcc[2], AVI->video_superindex->fcc[3])); |
2222 | | GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] LEN \"%ld\"\n", (long)AVI->video_superindex->dwSize)); |
2223 | | GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] wLongsPerEntry \"%d\"\n", AVI->video_superindex->wLongsPerEntry)); |
2224 | | GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] bIndexSubType \"%d\"\n", AVI->video_superindex->bIndexSubType)); |
2225 | | GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] bIndexType \"%d\"\n", AVI->video_superindex->bIndexType)); |
2226 | | GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] nEntriesInUse \"%ld\"\n", (long)AVI->video_superindex->nEntriesInUse)); |
2227 | | GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] dwChunkId \"%c%c%c%c\"\n", AVI->video_superindex->dwChunkId[0], AVI->video_superindex->dwChunkId[1], |
2228 | | AVI->video_superindex->dwChunkId[2], AVI->video_superindex->dwChunkId[3])); |
2229 | | #endif |
2230 | |
|
2231 | 0 | AVI->is_opendml = 1; |
2232 | |
|
2233 | 0 | } |
2234 | 0 | else if(lasttag == 2) // A U D I O |
2235 | 0 | { |
2236 | |
|
2237 | 0 | a = (char*) hdrl_data+i; |
2238 | 0 | int avail = (int) (hdrl_len-i); |
2239 | 0 | if (avail<32) ERR_EXIT(AVI_ERR_READ) |
2240 | | |
2241 | 0 | AVI->track[AVI->aptr].audio_superindex = (avisuperindex_chunk *) gf_malloc (sizeof (avisuperindex_chunk)); |
2242 | 0 | memcpy (AVI->track[AVI->aptr].audio_superindex->fcc, a, 4); |
2243 | 0 | a += 4; |
2244 | 0 | AVI->track[AVI->aptr].audio_superindex->dwSize = str2ulong((unsigned char*)a); |
2245 | 0 | a += 4; |
2246 | 0 | AVI->track[AVI->aptr].audio_superindex->wLongsPerEntry = str2ushort((unsigned char*)a); |
2247 | 0 | a += 2; |
2248 | 0 | AVI->track[AVI->aptr].audio_superindex->bIndexSubType = *a; |
2249 | 0 | a += 1; |
2250 | 0 | AVI->track[AVI->aptr].audio_superindex->bIndexType = *a; |
2251 | 0 | a += 1; |
2252 | 0 | AVI->track[AVI->aptr].audio_superindex->nEntriesInUse = str2ulong((unsigned char*)a); |
2253 | 0 | a += 4; |
2254 | 0 | memcpy (AVI->track[AVI->aptr].audio_superindex->dwChunkId, a, 4); |
2255 | 0 | a += 4; |
2256 | | |
2257 | | // 3 * reserved |
2258 | 0 | a += 4; |
2259 | 0 | a += 4; |
2260 | 0 | a += 4; |
2261 | |
|
2262 | 0 | if (AVI->track[AVI->aptr].audio_superindex->bIndexSubType != 0) { |
2263 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[avilib] Invalid Header, bIndexSubType != 0\n")); |
2264 | 0 | } |
2265 | |
|
2266 | 0 | avail -= 32; |
2267 | 0 | if (avail < (int) AVI->track[AVI->aptr].audio_superindex->nEntriesInUse*16) ERR_EXIT(AVI_ERR_READ) |
2268 | | |
2269 | 0 | AVI->track[AVI->aptr].audio_superindex->aIndex = (avisuperindex_entry*) |
2270 | 0 | gf_malloc (AVI->track[AVI->aptr].audio_superindex->wLongsPerEntry * |
2271 | 0 | AVI->track[AVI->aptr].audio_superindex->nEntriesInUse * sizeof (u32)); |
2272 | | |
2273 | | // position of ix## chunks |
2274 | 0 | for (j=0; j<AVI->track[AVI->aptr].audio_superindex->nEntriesInUse; ++j) { |
2275 | 0 | AVI->track[AVI->aptr].audio_superindex->aIndex[j].qwOffset = str2ullong ((unsigned char*)a); |
2276 | 0 | a += 8; |
2277 | 0 | AVI->track[AVI->aptr].audio_superindex->aIndex[j].dwSize = str2ulong ((unsigned char*)a); |
2278 | 0 | a += 4; |
2279 | 0 | AVI->track[AVI->aptr].audio_superindex->aIndex[j].dwDuration = str2ulong ((unsigned char*)a); |
2280 | 0 | a += 4; |
2281 | |
|
2282 | | #ifdef DEBUG_ODML |
2283 | | GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] [%d] 0x%llx 0x%lx %lu\n", j, |
2284 | | (unsigned int long)AVI->track[AVI->aptr].audio_superindex->aIndex[j].qwOffset, |
2285 | | (unsigned long)AVI->track[AVI->aptr].audio_superindex->aIndex[j].dwSize, |
2286 | | (unsigned long)AVI->track[AVI->aptr].audio_superindex->aIndex[j].dwDuration)); |
2287 | | #endif |
2288 | 0 | } |
2289 | |
|
2290 | 0 | AVI->track[AVI->aptr].audio_superindex->stdindex = NULL; |
2291 | |
|
2292 | | #ifdef DEBUG_ODML |
2293 | | GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] FOURCC \"%.4s\"\n", AVI->track[AVI->aptr].audio_superindex->fcc)); |
2294 | | GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] LEN \"%ld\"\n", (long)AVI->track[AVI->aptr].audio_superindex->dwSize)); |
2295 | | GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] wLongsPerEntry \"%d\"\n", AVI->track[AVI->aptr].audio_superindex->wLongsPerEntry)); |
2296 | | GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] bIndexSubType \"%d\"\n", AVI->track[AVI->aptr].audio_superindex->bIndexSubType)); |
2297 | | GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] bIndexType \"%d\"\n", AVI->track[AVI->aptr].audio_superindex->bIndexType)); |
2298 | | GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] nEntriesInUse \"%ld\"\n", (long)AVI->track[AVI->aptr].audio_superindex->nEntriesInUse)); |
2299 | | GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] dwChunkId \"%.4s\"\n", AVI->track[AVI->aptr].audio_superindex->dwChunkId[0])); |
2300 | | #endif |
2301 | |
|
2302 | 0 | } |
2303 | 0 | i += 8; |
2304 | 0 | } |
2305 | 0 | else if((strnicmp((char*)hdrl_data+i,"JUNK",4) == 0) || |
2306 | 0 | (strnicmp((char*)hdrl_data+i,"strn",4) == 0) || |
2307 | 0 | (strnicmp((char*)hdrl_data+i,"vprp",4) == 0)) { |
2308 | 0 | i += 8; |
2309 | | // do not reset lasttag |
2310 | 0 | } else |
2311 | 0 | { |
2312 | 0 | i += 8; |
2313 | 0 | lasttag = 0; |
2314 | 0 | } |
2315 | | //GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] adding %ld bytes\n", (int int)n)); |
2316 | | |
2317 | 0 | i += (u32) n; |
2318 | 0 | } |
2319 | | |
2320 | 0 | gf_free(hdrl_data); |
2321 | |
|
2322 | 0 | if(!vids_strh_seen || !vids_strf_seen) ERR_EXIT(AVI_ERR_NO_VIDS) |
2323 | | |
2324 | 0 | AVI->video_tag[0] = AVI->video_strn/10 + '0'; |
2325 | 0 | AVI->video_tag[1] = AVI->video_strn%10 + '0'; |
2326 | 0 | AVI->video_tag[2] = 'd'; |
2327 | 0 | AVI->video_tag[3] = 'b'; |
2328 | | |
2329 | | /* Audio tag is set to "99wb" if no audio present */ |
2330 | 0 | if(!AVI->track[0].a_chans) AVI->track[0].audio_strn = 99; |
2331 | |
|
2332 | 0 | { |
2333 | 0 | int tk=0; |
2334 | 0 | for(j=0; j<AVI->anum+1; ++j) { |
2335 | 0 | if (j == AVI->video_strn) continue; |
2336 | 0 | AVI->track[tk].audio_tag[0] = j/10 + '0'; |
2337 | 0 | AVI->track[tk].audio_tag[1] = j%10 + '0'; |
2338 | 0 | AVI->track[tk].audio_tag[2] = 'w'; |
2339 | 0 | AVI->track[tk].audio_tag[3] = 'b'; |
2340 | 0 | ++tk; |
2341 | 0 | } |
2342 | 0 | } |
2343 | |
|
2344 | 0 | gf_fseek(AVI->fdes,AVI->movi_start,SEEK_SET); |
2345 | |
|
2346 | 0 | if(!getIndex) return(0); |
2347 | | |
2348 | | /* if the file has an idx1, check if this is relative |
2349 | | to the start of the file or to the start of the movi list */ |
2350 | | |
2351 | 0 | idx_type = 0; |
2352 | |
|
2353 | 0 | if(AVI->idx) |
2354 | 0 | { |
2355 | 0 | s64 pos, len; |
2356 | | |
2357 | | /* Search the first videoframe in the idx1 and look where |
2358 | | it is in the file */ |
2359 | |
|
2360 | 0 | for(i=0; i<AVI->n_idx; i++) |
2361 | 0 | if( strnicmp((char *)AVI->idx[i],(char *)AVI->video_tag,3)==0 ) break; |
2362 | 0 | if(i>=AVI->n_idx) ERR_EXIT(AVI_ERR_NO_VIDS) |
2363 | | |
2364 | 0 | pos = str2ulong(AVI->idx[i]+ 8); |
2365 | 0 | len = str2ulong(AVI->idx[i]+12); |
2366 | |
|
2367 | 0 | gf_fseek(AVI->fdes,pos,SEEK_SET); |
2368 | 0 | if(avi_read(AVI->fdes,data,8)!=8) ERR_EXIT(AVI_ERR_READ) |
2369 | 0 | if( strnicmp(data,(char *)AVI->idx[i],4)==0 && str2ulong((unsigned char *)data+4)==len ) |
2370 | 0 | { |
2371 | 0 | idx_type = 1; /* Index from start of file */ |
2372 | 0 | } |
2373 | 0 | else |
2374 | 0 | { |
2375 | 0 | gf_fseek(AVI->fdes,pos+AVI->movi_start-4,SEEK_SET); |
2376 | 0 | if(avi_read(AVI->fdes,data,8)!=8) ERR_EXIT(AVI_ERR_READ) |
2377 | 0 | if( strnicmp(data,(char *)AVI->idx[i],4)==0 && str2ulong((unsigned char *)data+4)==len ) |
2378 | 0 | { |
2379 | 0 | idx_type = 2; /* Index from start of movi list */ |
2380 | 0 | } |
2381 | 0 | } |
2382 | | /* idx_type remains 0 if neither of the two tests above succeeds */ |
2383 | 0 | } |
2384 | | |
2385 | | |
2386 | 0 | if(idx_type == 0 && !AVI->is_opendml && !AVI->total_frames) |
2387 | 0 | { |
2388 | | /* we must search through the file to get the index */ |
2389 | |
|
2390 | 0 | gf_fseek(AVI->fdes, AVI->movi_start, SEEK_SET); |
2391 | |
|
2392 | 0 | AVI->n_idx = 0; |
2393 | |
|
2394 | 0 | while(1) |
2395 | 0 | { |
2396 | 0 | if( avi_read(AVI->fdes,data,8) != 8 ) break; |
2397 | 0 | n = str2ulong((unsigned char *)data+4); |
2398 | | |
2399 | | /* The movi list may contain sub-lists, ignore them */ |
2400 | |
|
2401 | 0 | if(strnicmp(data,"LIST",4)==0) |
2402 | 0 | { |
2403 | 0 | gf_fseek(AVI->fdes,4,SEEK_CUR); |
2404 | 0 | continue; |
2405 | 0 | } |
2406 | | |
2407 | | /* Check if we got a tag ##db, ##dc or ##wb */ |
2408 | | |
2409 | 0 | if( ( (data[2]=='d' || data[2]=='D') && |
2410 | 0 | (data[3]=='b' || data[3]=='B' || data[3]=='c' || data[3]=='C') ) |
2411 | 0 | || ( (data[2]=='w' || data[2]=='W') && |
2412 | 0 | (data[3]=='b' || data[3]=='B') ) ) |
2413 | 0 | { |
2414 | 0 | u64 __pos = gf_ftell(AVI->fdes) - 8; |
2415 | 0 | avi_add_index_entry(AVI,(unsigned char *)data,0,__pos,n); |
2416 | 0 | } |
2417 | |
|
2418 | 0 | gf_fseek(AVI->fdes,PAD_EVEN(n),SEEK_CUR); |
2419 | 0 | } |
2420 | 0 | idx_type = 1; |
2421 | 0 | } |
2422 | | |
2423 | | // ************************ |
2424 | | // OPENDML |
2425 | | // ************************ |
2426 | | |
2427 | | // read extended index chunks |
2428 | 0 | if (AVI->is_opendml) { |
2429 | 0 | u64 offset = 0; |
2430 | 0 | hdrl_len = 4+4+2+1+1+4+4+8+4; |
2431 | 0 | char *en, *chunk_start; |
2432 | 0 | int k = 0; |
2433 | 0 | u32 audtr = 0; |
2434 | 0 | u32 nrEntries = 0; |
2435 | |
|
2436 | 0 | AVI->video_index = NULL; |
2437 | |
|
2438 | 0 | nvi = 0; |
2439 | 0 | for(audtr=0; audtr<AVI->anum; ++audtr) { |
2440 | 0 | nai[audtr] = 0; |
2441 | 0 | tot[audtr] = 0; |
2442 | 0 | } |
2443 | | |
2444 | | // ************************ |
2445 | | // VIDEO |
2446 | | // ************************ |
2447 | |
|
2448 | 0 | for (j=0; j<AVI->video_superindex->nEntriesInUse; j++) { |
2449 | | |
2450 | | // read from file |
2451 | 0 | chunk_start = en = (char*) gf_malloc ((u32) (AVI->video_superindex->aIndex[j].dwSize+hdrl_len) ); |
2452 | |
|
2453 | 0 | if (gf_fseek(AVI->fdes, AVI->video_superindex->aIndex[j].qwOffset, SEEK_SET) == (u64)-1) { |
2454 | 0 | gf_free(chunk_start); |
2455 | 0 | continue; |
2456 | 0 | } |
2457 | | |
2458 | 0 | if (avi_read(AVI->fdes, en, (u32) (AVI->video_superindex->aIndex[j].dwSize+hdrl_len) ) <= 0) { |
2459 | 0 | gf_free(chunk_start); |
2460 | 0 | continue; |
2461 | 0 | } |
2462 | | |
2463 | 0 | nrEntries = str2ulong((unsigned char*)en + 12); |
2464 | | #ifdef DEBUG_ODML |
2465 | | //GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] [%d:0] Video nrEntries %ld\n", j, nrEntries)); |
2466 | | #endif |
2467 | 0 | offset = str2ullong((unsigned char*)en + 20); |
2468 | | |
2469 | | // skip header |
2470 | 0 | en += hdrl_len; |
2471 | 0 | nvi += nrEntries; |
2472 | 0 | AVI->video_index = (video_index_entry *) gf_realloc (AVI->video_index, nvi * sizeof (video_index_entry)); |
2473 | 0 | if (!AVI->video_index) { |
2474 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[avilib] out of mem (size = %ld)\n", nvi * sizeof (video_index_entry))); |
2475 | 0 | exit(1); |
2476 | 0 | } |
2477 | | |
2478 | 0 | while (k < nvi) { |
2479 | |
|
2480 | 0 | AVI->video_index[k].pos = offset + str2ulong((unsigned char*)en); |
2481 | 0 | en += 4; |
2482 | 0 | AVI->video_index[k].len = str2ulong_len((unsigned char*)en); |
2483 | 0 | AVI->video_index[k].key = str2ulong_key((unsigned char*)en); |
2484 | 0 | en += 4; |
2485 | | |
2486 | | // completely empty chunk |
2487 | 0 | if (AVI->video_index[k].pos-offset == 0 && AVI->video_index[k].len == 0) { |
2488 | 0 | k--; |
2489 | 0 | nvi--; |
2490 | 0 | } |
2491 | |
|
2492 | | #ifdef DEBUG_ODML |
2493 | | /* |
2494 | | GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] [%d] POS 0x%llX len=%d key=%s offset (%llx) (%ld)\n", k, |
2495 | | AVI->video_index[k].pos, |
2496 | | (int)AVI->video_index[k].len, |
2497 | | AVI->video_index[k].key?"yes":"no ", offset, |
2498 | | AVI->video_superindex->aIndex[j].dwSize)); |
2499 | | */ |
2500 | | #endif |
2501 | |
|
2502 | 0 | k++; |
2503 | 0 | } |
2504 | |
|
2505 | 0 | gf_free(chunk_start); |
2506 | 0 | } |
2507 | | |
2508 | 0 | AVI->video_frames = nvi; |
2509 | | // this should deal with broken 'rec ' odml files. |
2510 | 0 | if (AVI->video_frames == 0) { |
2511 | 0 | AVI->is_opendml=0; |
2512 | 0 | goto multiple_riff; |
2513 | 0 | } |
2514 | | |
2515 | | // ************************ |
2516 | | // AUDIO |
2517 | | // ************************ |
2518 | | |
2519 | 0 | for(audtr=0; audtr<AVI->anum; ++audtr) { |
2520 | |
|
2521 | 0 | k = 0; |
2522 | 0 | if (!AVI->track[audtr].audio_superindex) { |
2523 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[avilib] (%s) cannot read audio index for track %d\n", __FILE__, audtr)); |
2524 | 0 | continue; |
2525 | 0 | } |
2526 | 0 | for (j=0; j<AVI->track[audtr].audio_superindex->nEntriesInUse; j++) { |
2527 | | |
2528 | | // read from file |
2529 | 0 | chunk_start = en = (char*)gf_malloc ((u32) (AVI->track[audtr].audio_superindex->aIndex[j].dwSize+hdrl_len)); |
2530 | |
|
2531 | 0 | if (gf_fseek(AVI->fdes, AVI->track[audtr].audio_superindex->aIndex[j].qwOffset, SEEK_SET) == (u64)-1) { |
2532 | 0 | gf_free(chunk_start); |
2533 | 0 | continue; |
2534 | 0 | } |
2535 | | |
2536 | 0 | if (avi_read(AVI->fdes, en, (u32) (AVI->track[audtr].audio_superindex->aIndex[j].dwSize+hdrl_len)) <= 0) { |
2537 | 0 | gf_free(chunk_start); |
2538 | 0 | continue; |
2539 | 0 | } |
2540 | | |
2541 | 0 | nrEntries = str2ulong((unsigned char*)en + 12); |
2542 | | //if (nrEntries > 50) nrEntries = 2; // XXX |
2543 | | #ifdef DEBUG_ODML |
2544 | | //GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] [%d:%d] Audio nrEntries %ld\n", j, audtr, nrEntries)); |
2545 | | #endif |
2546 | 0 | offset = str2ullong((unsigned char*)en + 20); |
2547 | | |
2548 | | // skip header |
2549 | 0 | en += hdrl_len; |
2550 | 0 | nai[audtr] += nrEntries; |
2551 | 0 | AVI->track[audtr].audio_index = (audio_index_entry *) gf_realloc (AVI->track[audtr].audio_index, nai[audtr] * sizeof (audio_index_entry)); |
2552 | |
|
2553 | 0 | while (k < nai[audtr]) { |
2554 | |
|
2555 | 0 | AVI->track[audtr].audio_index[k].pos = offset + str2ulong((unsigned char*)en); |
2556 | 0 | en += 4; |
2557 | 0 | AVI->track[audtr].audio_index[k].len = str2ulong_len((unsigned char*)en); |
2558 | 0 | en += 4; |
2559 | 0 | AVI->track[audtr].audio_index[k].tot = tot[audtr]; |
2560 | 0 | tot[audtr] += AVI->track[audtr].audio_index[k].len; |
2561 | |
|
2562 | | #ifdef DEBUG_ODML |
2563 | | /* |
2564 | | GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] [%d:%d] POS 0x%llX len=%d offset (%llx) (%ld)\n", k, audtr, |
2565 | | AVI->track[audtr].audio_index[k].pos, |
2566 | | (int)AVI->track[audtr].audio_index[k].len, |
2567 | | offset, AVI->track[audtr].audio_superindex->aIndex[j].dwSize)); |
2568 | | */ |
2569 | | #endif |
2570 | |
|
2571 | 0 | ++k; |
2572 | 0 | } |
2573 | |
|
2574 | 0 | gf_free(chunk_start); |
2575 | 0 | } |
2576 | |
|
2577 | 0 | AVI->track[audtr].audio_chunks = nai[audtr]; |
2578 | 0 | AVI->track[audtr].audio_bytes = tot[audtr]; |
2579 | 0 | } |
2580 | 0 | } // is opendml |
2581 | | |
2582 | 0 | else if (AVI->total_frames && !AVI->is_opendml && idx_type==0) { |
2583 | | |
2584 | | // ********************* |
2585 | | // MULTIPLE RIFF CHUNKS (and no index) |
2586 | | // ********************* |
2587 | |
|
2588 | 0 | multiple_riff: |
2589 | |
|
2590 | 0 | gf_fseek(AVI->fdes, AVI->movi_start, SEEK_SET); |
2591 | |
|
2592 | 0 | AVI->n_idx = 0; |
2593 | |
|
2594 | 0 | GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] Reconstructing index...")); |
2595 | | |
2596 | | // Number of frames; only one audio track supported |
2597 | 0 | nvi = AVI->video_frames = AVI->total_frames; |
2598 | 0 | nai[0] = AVI->track[0].audio_chunks = AVI->total_frames; |
2599 | 0 | for(j=1; j<AVI->anum; ++j) AVI->track[j].audio_chunks = 0; |
2600 | |
|
2601 | 0 | AVI->video_index = (video_index_entry *) gf_malloc(nvi*sizeof(video_index_entry)); |
2602 | |
|
2603 | 0 | if(AVI->video_index==0) ERR_EXIT(AVI_ERR_NO_MEM); |
2604 | |
|
2605 | 0 | for(j=0; j<AVI->anum; ++j) { |
2606 | 0 | if(AVI->track[j].audio_chunks) { |
2607 | 0 | AVI->track[j].audio_index = (audio_index_entry *) gf_malloc((nai[j]+1)*sizeof(audio_index_entry)); |
2608 | 0 | memset(AVI->track[j].audio_index, 0, (nai[j]+1)*(sizeof(audio_index_entry))); |
2609 | 0 | if(AVI->track[j].audio_index==0) ERR_EXIT(AVI_ERR_NO_MEM); |
2610 | 0 | } |
2611 | 0 | } |
2612 | | |
2613 | 0 | nvi = 0; |
2614 | 0 | for(j=0; j<AVI->anum; ++j) { |
2615 | 0 | nai[j] = 0; |
2616 | 0 | tot[j] = 0; |
2617 | 0 | } |
2618 | |
|
2619 | 0 | aud_chunks = AVI->total_frames; |
2620 | |
|
2621 | 0 | while(1) |
2622 | 0 | { |
2623 | 0 | if (nvi >= AVI->total_frames) break; |
2624 | | |
2625 | 0 | if( avi_read(AVI->fdes,data,8) != 8 ) break; |
2626 | 0 | n = str2ulong((unsigned char *)data+4); |
2627 | | |
2628 | |
|
2629 | 0 | j=0; |
2630 | |
|
2631 | 0 | if (aud_chunks - nai[j] -1 <= 0) { |
2632 | 0 | aud_chunks += AVI->total_frames; |
2633 | 0 | AVI->track[j].audio_index = (audio_index_entry *) |
2634 | 0 | gf_realloc( AVI->track[j].audio_index, (aud_chunks+1)*sizeof(audio_index_entry)); |
2635 | 0 | if (!AVI->track[j].audio_index) { |
2636 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[avilib] Internal error in avilib -- no mem\n")); |
2637 | 0 | AVI_errno = AVI_ERR_NO_MEM; |
2638 | 0 | return -1; |
2639 | 0 | } |
2640 | 0 | } |
2641 | | |
2642 | | /* Check if we got a tag ##db, ##dc or ##wb */ |
2643 | | |
2644 | | // VIDEO |
2645 | 0 | if( |
2646 | 0 | (data[0]=='0' || data[1]=='0') && |
2647 | 0 | (data[2]=='d' || data[2]=='D') && |
2648 | 0 | (data[3]=='b' || data[3]=='B' || data[3]=='c' || data[3]=='C') ) { |
2649 | |
|
2650 | 0 | AVI->video_index[nvi].key = 0x0; |
2651 | 0 | AVI->video_index[nvi].pos = gf_ftell(AVI->fdes); |
2652 | 0 | AVI->video_index[nvi].len = (u32) n; |
2653 | | |
2654 | | /* |
2655 | | GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] Frame %ld pos %"LLD" len %"LLD" key %ld\n", |
2656 | | nvi, AVI->video_index[nvi].pos, AVI->video_index[nvi].len, (long)AVI->video_index[nvi].key)); |
2657 | | */ |
2658 | 0 | nvi++; |
2659 | 0 | gf_fseek(AVI->fdes,PAD_EVEN(n),SEEK_CUR); |
2660 | 0 | } |
2661 | | |
2662 | | //AUDIO |
2663 | 0 | else if( |
2664 | 0 | (data[0]=='0' || data[1]=='1') && |
2665 | 0 | (data[2]=='w' || data[2]=='W') && |
2666 | 0 | (data[3]=='b' || data[3]=='B') ) { |
2667 | | |
2668 | |
|
2669 | 0 | AVI->track[j].audio_index[nai[j]].pos = gf_ftell(AVI->fdes); |
2670 | 0 | AVI->track[j].audio_index[nai[j]].len = (u32) n; |
2671 | 0 | AVI->track[j].audio_index[nai[j]].tot = tot[j]; |
2672 | 0 | tot[j] += AVI->track[j].audio_index[nai[j]].len; |
2673 | 0 | nai[j]++; |
2674 | |
|
2675 | 0 | gf_fseek(AVI->fdes,PAD_EVEN(n),SEEK_CUR); |
2676 | 0 | } |
2677 | 0 | else { |
2678 | 0 | gf_fseek(AVI->fdes,-4,SEEK_CUR); |
2679 | 0 | } |
2680 | |
|
2681 | 0 | } |
2682 | 0 | if (nvi < AVI->total_frames) { |
2683 | 0 | GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[avilib] Uh? Some frames seems missing (%ld/%d)\n", |
2684 | 0 | nvi, AVI->total_frames)); |
2685 | 0 | } |
2686 | | |
2687 | |
|
2688 | 0 | AVI->video_frames = nvi; |
2689 | 0 | AVI->track[0].audio_chunks = nai[0]; |
2690 | |
|
2691 | 0 | for(j=0; j<AVI->anum; ++j) AVI->track[j].audio_bytes = tot[j]; |
2692 | 0 | GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] done. nvi=%ld nai=%ld tot=%ld\n", nvi, nai[0], tot[0])); |
2693 | |
|
2694 | 0 | } // total_frames but no indx chunk (xawtv does this) |
2695 | | |
2696 | 0 | else |
2697 | | |
2698 | 0 | { |
2699 | | // ****************** |
2700 | | // NO OPENDML |
2701 | | // ****************** |
2702 | | |
2703 | | /* Now generate the video index and audio index arrays */ |
2704 | |
|
2705 | 0 | nvi = 0; |
2706 | 0 | for(j=0; j<AVI->anum; ++j) nai[j] = 0; |
2707 | |
|
2708 | 0 | for(i=0; i<AVI->n_idx; i++) { |
2709 | |
|
2710 | 0 | if(strnicmp((char *)AVI->idx[i],AVI->video_tag,3) == 0) nvi++; |
2711 | |
|
2712 | 0 | for(j=0; j<AVI->anum; ++j) if(strnicmp((char *)AVI->idx[i], AVI->track[j].audio_tag,4) == 0) nai[j]++; |
2713 | 0 | } |
2714 | |
|
2715 | 0 | AVI->video_frames = nvi; |
2716 | 0 | for(j=0; j<AVI->anum; ++j) AVI->track[j].audio_chunks = nai[j]; |
2717 | | |
2718 | |
|
2719 | 0 | if(AVI->video_frames==0) ERR_EXIT(AVI_ERR_NO_VIDS); |
2720 | 0 | AVI->video_index = (video_index_entry *) gf_malloc(nvi*sizeof(video_index_entry)); |
2721 | 0 | if(AVI->video_index==0) ERR_EXIT(AVI_ERR_NO_MEM); |
2722 | |
|
2723 | 0 | for(j=0; j<AVI->anum; ++j) { |
2724 | 0 | if(AVI->track[j].audio_chunks) { |
2725 | 0 | AVI->track[j].audio_index = (audio_index_entry *) gf_malloc((nai[j]+1)*sizeof(audio_index_entry)); |
2726 | 0 | memset(AVI->track[j].audio_index, 0, (nai[j]+1)*(sizeof(audio_index_entry))); |
2727 | 0 | if(AVI->track[j].audio_index==0) ERR_EXIT(AVI_ERR_NO_MEM); |
2728 | 0 | } |
2729 | 0 | } |
2730 | | |
2731 | 0 | nvi = 0; |
2732 | 0 | for(j=0; j<AVI->anum; ++j) { |
2733 | 0 | nai[j] = 0; |
2734 | 0 | tot[j] = 0; |
2735 | 0 | } |
2736 | |
|
2737 | 0 | ioff = idx_type == 1 ? 8 : (u32)AVI->movi_start+4; |
2738 | |
|
2739 | 0 | for(i=0; i<AVI->n_idx; i++) { |
2740 | | |
2741 | | //video |
2742 | 0 | if(strnicmp((char *)AVI->idx[i],AVI->video_tag,3) == 0) { |
2743 | 0 | AVI->video_index[nvi].key = str2ulong(AVI->idx[i]+ 4); |
2744 | 0 | AVI->video_index[nvi].pos = str2ulong(AVI->idx[i]+ 8)+ioff; |
2745 | 0 | AVI->video_index[nvi].len = str2ulong(AVI->idx[i]+12); |
2746 | 0 | nvi++; |
2747 | 0 | } |
2748 | | |
2749 | | //audio |
2750 | 0 | for(j=0; j<AVI->anum; ++j) { |
2751 | |
|
2752 | 0 | if(strnicmp((char *)AVI->idx[i],AVI->track[j].audio_tag,4) == 0) { |
2753 | 0 | AVI->track[j].audio_index[nai[j]].pos = str2ulong(AVI->idx[i]+ 8)+ioff; |
2754 | 0 | AVI->track[j].audio_index[nai[j]].len = str2ulong(AVI->idx[i]+12); |
2755 | 0 | AVI->track[j].audio_index[nai[j]].tot = tot[j]; |
2756 | 0 | tot[j] += AVI->track[j].audio_index[nai[j]].len; |
2757 | 0 | nai[j]++; |
2758 | 0 | } |
2759 | 0 | } |
2760 | 0 | } |
2761 | | |
2762 | |
|
2763 | 0 | for(j=0; j<AVI->anum; ++j) AVI->track[j].audio_bytes = tot[j]; |
2764 | |
|
2765 | 0 | } // is no opendml |
2766 | | |
2767 | | /* Reposition the file */ |
2768 | | |
2769 | 0 | gf_fseek(AVI->fdes,AVI->movi_start,SEEK_SET); |
2770 | 0 | AVI->video_pos = 0; |
2771 | |
|
2772 | 0 | return(0); |
2773 | 0 | } |
2774 | | |
2775 | | int AVI_video_frames(avi_t *AVI) |
2776 | 0 | { |
2777 | 0 | return AVI->video_frames; |
2778 | 0 | } |
2779 | | int AVI_video_width(avi_t *AVI) |
2780 | 0 | { |
2781 | 0 | return AVI->width; |
2782 | 0 | } |
2783 | | int AVI_video_height(avi_t *AVI) |
2784 | 0 | { |
2785 | 0 | return AVI->height; |
2786 | 0 | } |
2787 | | double AVI_frame_rate(avi_t *AVI) |
2788 | 0 | { |
2789 | 0 | return AVI->fps; |
2790 | 0 | } |
2791 | | char* AVI_video_compressor(avi_t *AVI) |
2792 | 0 | { |
2793 | 0 | return AVI->compressor2; |
2794 | 0 | } |
2795 | | |
2796 | | #if 0 |
2797 | | int AVI_max_video_chunk(avi_t *AVI) |
2798 | | { |
2799 | | return AVI->max_len; |
2800 | | } |
2801 | | #endif |
2802 | | |
2803 | | int AVI_audio_tracks(avi_t *AVI) |
2804 | 0 | { |
2805 | 0 | return(AVI->anum); |
2806 | 0 | } |
2807 | | |
2808 | | int AVI_audio_channels(avi_t *AVI) |
2809 | 0 | { |
2810 | 0 | return AVI->track[AVI->aptr].a_chans; |
2811 | 0 | } |
2812 | | |
2813 | | int AVI_audio_mp3rate(avi_t *AVI) |
2814 | 0 | { |
2815 | 0 | return AVI->track[AVI->aptr].mp3rate; |
2816 | 0 | } |
2817 | | |
2818 | | #if 0 //unused |
2819 | | int AVI_audio_padrate(avi_t *AVI) |
2820 | | { |
2821 | | return AVI->track[AVI->aptr].padrate; |
2822 | | } |
2823 | | #endif |
2824 | | |
2825 | | int AVI_audio_bits(avi_t *AVI) |
2826 | 0 | { |
2827 | 0 | return AVI->track[AVI->aptr].a_bits; |
2828 | 0 | } |
2829 | | |
2830 | | int AVI_audio_format(avi_t *AVI) |
2831 | 0 | { |
2832 | 0 | return AVI->track[AVI->aptr].a_fmt; |
2833 | 0 | } |
2834 | | |
2835 | | int AVI_audio_rate(avi_t *AVI) |
2836 | 0 | { |
2837 | 0 | return AVI->track[AVI->aptr].a_rate; |
2838 | 0 | } |
2839 | | |
2840 | | |
2841 | | int AVI_frame_size(avi_t *AVI, int frame) |
2842 | 0 | { |
2843 | 0 | if(AVI->mode==AVI_MODE_WRITE) { |
2844 | 0 | AVI_errno = AVI_ERR_NOT_PERM; |
2845 | 0 | return -1; |
2846 | 0 | } |
2847 | 0 | if(!AVI->video_index) { |
2848 | 0 | AVI_errno = AVI_ERR_NO_IDX; |
2849 | 0 | return -1; |
2850 | 0 | } |
2851 | | |
2852 | 0 | if(frame < 0 || frame >= AVI->video_frames) return 0; |
2853 | 0 | return (u32) (AVI->video_index[frame].len); |
2854 | 0 | } |
2855 | | |
2856 | | int AVI_audio_size(avi_t *AVI, int frame) |
2857 | 0 | { |
2858 | 0 | if(AVI->mode==AVI_MODE_WRITE) { |
2859 | 0 | AVI_errno = AVI_ERR_NOT_PERM; |
2860 | 0 | return -1; |
2861 | 0 | } |
2862 | 0 | if(!AVI->track[AVI->aptr].audio_index) { |
2863 | 0 | AVI_errno = AVI_ERR_NO_IDX; |
2864 | 0 | return -1; |
2865 | 0 | } |
2866 | | |
2867 | 0 | if(frame < 0 || frame >= AVI->track[AVI->aptr].audio_chunks) return -1; |
2868 | 0 | return (u32) (AVI->track[AVI->aptr].audio_index[frame].len); |
2869 | 0 | } |
2870 | | |
2871 | | u64 AVI_get_video_position(avi_t *AVI, int frame) |
2872 | 0 | { |
2873 | 0 | if(AVI->mode==AVI_MODE_WRITE) { |
2874 | 0 | AVI_errno = AVI_ERR_NOT_PERM; |
2875 | 0 | return (u64) -1; |
2876 | 0 | } |
2877 | 0 | if(!AVI->video_index) { |
2878 | 0 | AVI_errno = AVI_ERR_NO_IDX; |
2879 | 0 | return (u64) -1; |
2880 | 0 | } |
2881 | | |
2882 | 0 | if(frame < 0 || frame >= AVI->video_frames) return 0; |
2883 | 0 | return(AVI->video_index[frame].pos); |
2884 | 0 | } |
2885 | | |
2886 | | |
2887 | | int AVI_seek_start(avi_t *AVI) |
2888 | 0 | { |
2889 | 0 | if(AVI->mode==AVI_MODE_WRITE) { |
2890 | 0 | AVI_errno = AVI_ERR_NOT_PERM; |
2891 | 0 | return -1; |
2892 | 0 | } |
2893 | | |
2894 | 0 | gf_fseek(AVI->fdes,AVI->movi_start,SEEK_SET); |
2895 | 0 | AVI->video_pos = 0; |
2896 | 0 | return 0; |
2897 | 0 | } |
2898 | | |
2899 | | int AVI_set_video_position(avi_t *AVI, int frame) |
2900 | 0 | { |
2901 | 0 | if(AVI->mode==AVI_MODE_WRITE) { |
2902 | 0 | AVI_errno = AVI_ERR_NOT_PERM; |
2903 | 0 | return -1; |
2904 | 0 | } |
2905 | 0 | if(!AVI->video_index) { |
2906 | 0 | AVI_errno = AVI_ERR_NO_IDX; |
2907 | 0 | return -1; |
2908 | 0 | } |
2909 | | |
2910 | 0 | if (frame < 0 ) frame = 0; |
2911 | 0 | AVI->video_pos = frame; |
2912 | 0 | return 0; |
2913 | 0 | } |
2914 | | |
2915 | | #if 0 //unused |
2916 | | int AVI_set_audio_bitrate(avi_t *AVI, int bitrate) |
2917 | | { |
2918 | | if(AVI->mode==AVI_MODE_READ) { |
2919 | | AVI_errno = AVI_ERR_NOT_PERM; |
2920 | | return -1; |
2921 | | } |
2922 | | |
2923 | | AVI->track[AVI->aptr].mp3rate = bitrate; |
2924 | | return 0; |
2925 | | } |
2926 | | #endif |
2927 | | |
2928 | | int AVI_read_frame(avi_t *AVI, u8 *vidbuf, int *keyframe) |
2929 | 0 | { |
2930 | 0 | int n; |
2931 | |
|
2932 | 0 | if(AVI->mode==AVI_MODE_WRITE) { |
2933 | 0 | AVI_errno = AVI_ERR_NOT_PERM; |
2934 | 0 | return -1; |
2935 | 0 | } |
2936 | 0 | if(!AVI->video_index) { |
2937 | 0 | AVI_errno = AVI_ERR_NO_IDX; |
2938 | 0 | return -1; |
2939 | 0 | } |
2940 | | |
2941 | 0 | if(AVI->video_pos < 0 || AVI->video_pos >= AVI->video_frames) return -1; |
2942 | 0 | n = (u32) AVI->video_index[AVI->video_pos].len; |
2943 | |
|
2944 | 0 | *keyframe = (AVI->video_index[AVI->video_pos].key==0x10) ? 1:0; |
2945 | |
|
2946 | 0 | if (vidbuf == NULL) { |
2947 | 0 | AVI->video_pos++; |
2948 | 0 | return n; |
2949 | 0 | } |
2950 | | |
2951 | 0 | gf_fseek(AVI->fdes, AVI->video_index[AVI->video_pos].pos, SEEK_SET); |
2952 | 0 | AVI->video_pos++; |
2953 | |
|
2954 | 0 | if (avi_read(AVI->fdes,vidbuf,n) != (u32) n) |
2955 | 0 | { |
2956 | 0 | AVI_errno = AVI_ERR_READ; |
2957 | 0 | return -1; |
2958 | 0 | } |
2959 | | |
2960 | 0 | return n; |
2961 | 0 | } |
2962 | | |
2963 | | #if 0 //unused |
2964 | | int AVI_get_audio_position_index(avi_t *AVI) |
2965 | | { |
2966 | | if(AVI->mode==AVI_MODE_WRITE) { |
2967 | | AVI_errno = AVI_ERR_NOT_PERM; |
2968 | | return -1; |
2969 | | } |
2970 | | if(!AVI->track[AVI->aptr].audio_index) { |
2971 | | AVI_errno = AVI_ERR_NO_IDX; |
2972 | | return -1; |
2973 | | } |
2974 | | |
2975 | | return (AVI->track[AVI->aptr].audio_posc); |
2976 | | } |
2977 | | |
2978 | | int AVI_set_audio_position_index(avi_t *AVI, int indexpos) |
2979 | | { |
2980 | | if(AVI->mode==AVI_MODE_WRITE) { |
2981 | | AVI_errno = AVI_ERR_NOT_PERM; |
2982 | | return -1; |
2983 | | } |
2984 | | if(!AVI->track[AVI->aptr].audio_index) { |
2985 | | AVI_errno = AVI_ERR_NO_IDX; |
2986 | | return -1; |
2987 | | } |
2988 | | if(indexpos > AVI->track[AVI->aptr].audio_chunks) { |
2989 | | AVI_errno = AVI_ERR_NO_IDX; |
2990 | | return -1; |
2991 | | } |
2992 | | |
2993 | | AVI->track[AVI->aptr].audio_posc = indexpos; |
2994 | | AVI->track[AVI->aptr].audio_posb = 0; |
2995 | | |
2996 | | return 0; |
2997 | | } |
2998 | | #endif |
2999 | | |
3000 | | |
3001 | | int AVI_set_audio_position(avi_t *AVI, int byte) |
3002 | 0 | { |
3003 | 0 | int n0, n1; |
3004 | |
|
3005 | 0 | if(AVI->mode==AVI_MODE_WRITE) { |
3006 | 0 | AVI_errno = AVI_ERR_NOT_PERM; |
3007 | 0 | return -1; |
3008 | 0 | } |
3009 | 0 | if(!AVI->track[AVI->aptr].audio_index) { |
3010 | 0 | AVI_errno = AVI_ERR_NO_IDX; |
3011 | 0 | return -1; |
3012 | 0 | } |
3013 | | |
3014 | 0 | if(byte < 0) byte = 0; |
3015 | | |
3016 | | /* Binary search in the audio chunks */ |
3017 | |
|
3018 | 0 | n0 = 0; |
3019 | 0 | n1 = AVI->track[AVI->aptr].audio_chunks; |
3020 | |
|
3021 | 0 | while(n0<n1-1) |
3022 | 0 | { |
3023 | 0 | int n = (n0+n1)/2; |
3024 | 0 | if(AVI->track[AVI->aptr].audio_index[n].tot>(u32) byte) |
3025 | 0 | n1 = n; |
3026 | 0 | else |
3027 | 0 | n0 = n; |
3028 | 0 | } |
3029 | |
|
3030 | 0 | AVI->track[AVI->aptr].audio_posc = n0; |
3031 | 0 | AVI->track[AVI->aptr].audio_posb = (u32) (byte - AVI->track[AVI->aptr].audio_index[n0].tot); |
3032 | |
|
3033 | 0 | return 0; |
3034 | 0 | } |
3035 | | |
3036 | | |
3037 | | int AVI_read_audio(avi_t *AVI, u8 *audbuf, int bytes, int *continuous) |
3038 | 0 | { |
3039 | 0 | int nr; |
3040 | 0 | u32 todo; |
3041 | 0 | s64 pos; |
3042 | |
|
3043 | 0 | if(AVI->mode==AVI_MODE_WRITE) { |
3044 | 0 | AVI_errno = AVI_ERR_NOT_PERM; |
3045 | 0 | return -1; |
3046 | 0 | } |
3047 | 0 | if(!AVI->track[AVI->aptr].audio_index) { |
3048 | 0 | AVI_errno = AVI_ERR_NO_IDX; |
3049 | 0 | return -1; |
3050 | 0 | } |
3051 | | |
3052 | 0 | nr = 0; /* total number of bytes read */ |
3053 | |
|
3054 | 0 | if (bytes==0) { |
3055 | 0 | AVI->track[AVI->aptr].audio_posc++; |
3056 | 0 | AVI->track[AVI->aptr].audio_posb = 0; |
3057 | 0 | } |
3058 | |
|
3059 | 0 | *continuous = 1; |
3060 | 0 | while(bytes>0) |
3061 | 0 | { |
3062 | 0 | s64 ret; |
3063 | 0 | u32 left = (u32) (AVI->track[AVI->aptr].audio_index[AVI->track[AVI->aptr].audio_posc].len - AVI->track[AVI->aptr].audio_posb); |
3064 | 0 | if(left==0) |
3065 | 0 | { |
3066 | 0 | if(AVI->track[AVI->aptr].audio_posc>=AVI->track[AVI->aptr].audio_chunks-1) return nr; |
3067 | 0 | AVI->track[AVI->aptr].audio_posc++; |
3068 | 0 | AVI->track[AVI->aptr].audio_posb = 0; |
3069 | 0 | *continuous = 0; |
3070 | 0 | continue; |
3071 | 0 | } |
3072 | 0 | if((u32)bytes<left) |
3073 | 0 | todo = bytes; |
3074 | 0 | else |
3075 | 0 | todo = left; |
3076 | 0 | pos = AVI->track[AVI->aptr].audio_index[AVI->track[AVI->aptr].audio_posc].pos + AVI->track[AVI->aptr].audio_posb; |
3077 | 0 | gf_fseek(AVI->fdes, pos, SEEK_SET); |
3078 | 0 | AVI->track[AVI->aptr].audio_posb += (int)todo; |
3079 | 0 | if ( (ret = avi_read(AVI->fdes,audbuf+nr,todo)) != (s64)todo) |
3080 | 0 | { |
3081 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[avilib] XXX pos = "LLD", ret = "LLD", todo = %ld\n", pos, ret, todo)); |
3082 | 0 | AVI_errno = AVI_ERR_READ; |
3083 | 0 | return -1; |
3084 | 0 | } |
3085 | 0 | bytes -= todo; |
3086 | 0 | nr += todo; |
3087 | 0 | } |
3088 | | |
3089 | 0 | return nr; |
3090 | 0 | } |
3091 | | |
3092 | | |
3093 | | #if 0 //unused |
3094 | | /* AVI_read_data: Special routine for reading the next audio or video chunk |
3095 | | without having an index of the file. */ |
3096 | | |
3097 | | int AVI_read_data(avi_t *AVI, char *vidbuf, int max_vidbuf, |
3098 | | char *audbuf, int max_audbuf, |
3099 | | int *len) |
3100 | | { |
3101 | | |
3102 | | /* |
3103 | | * Return codes: |
3104 | | * |
3105 | | * 1 = video data read |
3106 | | * 2 = audio data read |
3107 | | * 0 = reached EOF |
3108 | | * -1 = video buffer too small |
3109 | | * -2 = audio buffer too small |
3110 | | */ |
3111 | | |
3112 | | s64 n; |
3113 | | char data[8]; |
3114 | | |
3115 | | if(AVI->mode==AVI_MODE_WRITE) return 0; |
3116 | | |
3117 | | while(1) |
3118 | | { |
3119 | | /* Read tag and length */ |
3120 | | |
3121 | | if( avi_read(AVI->fdes,data,8) != 8 ) return 0; |
3122 | | |
3123 | | /* if we got a list tag, ignore it */ |
3124 | | |
3125 | | if(strnicmp(data,"LIST",4) == 0) |
3126 | | { |
3127 | | gf_fseek(AVI->fdes,4,SEEK_CUR); |
3128 | | continue; |
3129 | | } |
3130 | | |
3131 | | n = PAD_EVEN(str2ulong((unsigned char *)data+4)); |
3132 | | |
3133 | | if(strnicmp(data,AVI->video_tag,3) == 0) |
3134 | | { |
3135 | | *len = (u32) n; |
3136 | | AVI->video_pos++; |
3137 | | if(n>max_vidbuf) |
3138 | | { |
3139 | | gf_fseek(AVI->fdes,n,SEEK_CUR); |
3140 | | return -1; |
3141 | | } |
3142 | | if(avi_read(AVI->fdes,vidbuf, (u32) n) != n ) return 0; |
3143 | | return 1; |
3144 | | } |
3145 | | else if(strnicmp(data,AVI->track[AVI->aptr].audio_tag,4) == 0) |
3146 | | { |
3147 | | *len = (u32) n; |
3148 | | if(n>max_audbuf) |
3149 | | { |
3150 | | gf_fseek(AVI->fdes,n,SEEK_CUR); |
3151 | | return -2; |
3152 | | } |
3153 | | if(avi_read(AVI->fdes,audbuf, (u32) n) != n ) return 0; |
3154 | | return 2; |
3155 | | break; |
3156 | | } |
3157 | | else if(gf_fseek(AVI->fdes,n,SEEK_CUR) == (u64) -1) return 0; |
3158 | | } |
3159 | | } |
3160 | | |
3161 | | u64 AVI_max_size(void) |
3162 | | { |
3163 | | return((u64) AVI_MAX_LEN); |
3164 | | } |
3165 | | #endif |
3166 | | |
3167 | | |
3168 | | #endif /*GPAC_DISABLE_AVILIB*/ |