camera ortho_scale (COLLADA xmag ) animation export
[blender-staging.git] / source / blender / avi / intern / avi.c
1 /*
2  * $Id$
3  *
4  * This is external code.
5  *
6  * ***** BEGIN GPL LICENSE BLOCK *****
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21  *
22  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
23  * All rights reserved.
24  *
25  * The Original Code is: all of this file.
26  *
27  * Contributor(s): none yet.
28  *
29  * ***** END GPL LICENSE BLOCK *****
30  *
31  */
32
33 /** \file blender/avi/intern/avi.c
34  *  \ingroup avi
35  */
36
37
38 #include <stdlib.h>
39 #include <string.h>
40 #include <stdarg.h>
41 #include <stdio.h>
42 #include <ctype.h>
43
44 #include "MEM_guardedalloc.h"
45
46 #include "AVI_avi.h"
47 #include "avi_intern.h"
48
49 #include "endian.h"
50
51 static int AVI_DEBUG=0;
52 static char DEBUG_FCC[4];
53
54 #define DEBUG_PRINT(x) if(AVI_DEBUG) printf("AVI DEBUG: " x);
55
56 /* local functions */
57 char *fcc_to_char (unsigned int fcc);
58 char *tcc_to_char (unsigned int tcc);
59
60
61
62 /* implemetation */
63
64 unsigned int GET_FCC (FILE *fp) {
65         unsigned char tmp[4];
66
67         tmp[0] = getc(fp);
68         tmp[1] = getc(fp);
69         tmp[2] = getc(fp);
70         tmp[3] = getc(fp);
71
72         return FCC (tmp);
73 }
74
75 unsigned int GET_TCC (FILE *fp) {
76         char tmp[5];
77
78         tmp[0] = getc(fp);
79         tmp[1] = getc(fp);
80         tmp[2] = 0;
81         tmp[3] = 0;
82
83         return FCC (tmp);
84 }
85
86 char *fcc_to_char (unsigned int fcc) {
87         DEBUG_FCC[0]= (fcc)&0177;
88         DEBUG_FCC[1]= (fcc>>8)&0177;
89         DEBUG_FCC[2]= (fcc>>16)&0177;
90         DEBUG_FCC[3]= (fcc>>24)&0177;
91
92         return DEBUG_FCC;       
93 }
94
95 char *tcc_to_char (unsigned int tcc) {
96         DEBUG_FCC[0]= (tcc)&0177;
97         DEBUG_FCC[1]= (tcc>>8)&0177;
98         DEBUG_FCC[2]= 0;
99         DEBUG_FCC[3]= 0;
100
101         return DEBUG_FCC;       
102 }
103
104 int AVI_get_stream (AviMovie *movie, int avist_type, int stream_num) {
105         int cur_stream;
106
107         if (movie == NULL)
108                 return -AVI_ERROR_OPTION;
109
110         for (cur_stream=0; cur_stream < movie->header->Streams; cur_stream++) {
111                 if (movie->streams[cur_stream].sh.Type == avist_type) {
112                         if (stream_num == 0)
113                                 return cur_stream;
114                         else
115                                 stream_num--;
116                 }
117         }
118
119         return -AVI_ERROR_FOUND;
120 }
121
122 static int fcc_get_stream (int fcc) {
123         char fccs[4];
124
125         fccs[0] = fcc;
126         fccs[1] = fcc>>8;
127         fccs[2] = fcc>>16;
128         fccs[3] = fcc>>24;
129
130         return 10*(fccs[0]-'0') + (fccs[1]-'0');
131 }
132
133 static int fcc_is_data (int fcc) {
134         char fccs[4];
135
136         fccs[0] = fcc;
137         fccs[1] = fcc>>8;
138         fccs[2] = fcc>>16;
139         fccs[3] = fcc>>24;
140
141         if (!isdigit (fccs[0]) || !isdigit (fccs[1]) || (fccs[2] != 'd' && fccs[2] != 'w'))
142                 return 0;
143         if (fccs[3] != 'b' && fccs[3] != 'c')
144                 return 0;
145
146         return 1;
147 }
148
149 AviError AVI_print_error (AviError in_error) {
150         int error;
151
152         if ((int) in_error < 0)
153                 error = -in_error;
154         else
155                 error = in_error;
156
157         switch (error) {
158         case AVI_ERROR_NONE:
159                 break;
160         case AVI_ERROR_COMPRESSION:
161                 printf ("AVI ERROR: compressed in an unsupported format\n");
162                 break;
163         case AVI_ERROR_OPEN:
164                 printf ("AVI ERROR: could not open file\n");
165                 break;
166         case AVI_ERROR_READING:
167                 printf ("AVI ERROR: could not read from file\n");
168                 break;
169         case AVI_ERROR_WRITING:
170                 printf ("AVI ERROR: could not write to file\n");
171                 break;
172         case AVI_ERROR_FORMAT:
173                 printf ("AVI ERROR: file is in an illegal or unrecognized format\n");
174                 break;
175         case AVI_ERROR_ALLOC:
176                 printf ("AVI ERROR: error encountered while allocating memory\n");
177                 break;
178         case AVI_ERROR_OPTION:
179                 printf ("AVI ERROR: program made illegal request\n");
180                 break;
181         case AVI_ERROR_FOUND:
182                 printf ("AVI ERROR: movie did not contain expected item\n");
183                 break;
184         default: 
185                 break;
186         }
187
188         return in_error;
189 }
190 /*
191 void AVI_set_debug (int mode) {
192         AVI_DEBUG= mode;
193 }
194 */
195 /*
196 int AVI_is_avi (char *name) {
197         FILE *fp;
198         int ret;
199         
200         fp = fopen (name, "rb");
201         if (fp == NULL)
202                 return 0;
203
204         if (GET_FCC (fp) != FCC("RIFF") ||
205                 !GET_FCC (fp) ||
206                 GET_FCC (fp) != FCC("AVI ")) {
207                 ret = 0;
208         } else {
209                 ret = 1;
210         }
211
212         fclose(fp);
213         return ret;
214 }
215 */
216
217 int AVI_is_avi (const char *name) {
218         int temp, fcca, j;
219         AviMovie movie= {NULL};
220         AviMainHeader header;
221         AviBitmapInfoHeader bheader;
222         int movie_tracks = 0;
223         
224         DEBUG_PRINT("opening movie\n");
225
226         movie.type = AVI_MOVIE_READ;
227         movie.fp = fopen (name, "rb");
228         movie.offset_table = NULL;
229
230         if (movie.fp == NULL)
231                 return 0;
232
233         if (GET_FCC (movie.fp) != FCC("RIFF") ||
234                 !(movie.size = GET_FCC (movie.fp))) {
235                 fclose(movie.fp);
236                 return 0;
237         }
238
239         movie.header = &header;
240
241         if (GET_FCC (movie.fp) != FCC("AVI ") ||
242                 GET_FCC (movie.fp) != FCC("LIST") ||
243                 !GET_FCC (movie.fp) ||
244                 GET_FCC (movie.fp) != FCC("hdrl") ||
245                 (movie.header->fcc = GET_FCC (movie.fp)) != FCC("avih") ||
246                 !(movie.header->size = GET_FCC (movie.fp))) {
247                 DEBUG_PRINT("bad initial header info\n");
248                 fclose(movie.fp);
249                 return 0;
250         }
251         
252         movie.header->MicroSecPerFrame = GET_FCC(movie.fp);
253         movie.header->MaxBytesPerSec = GET_FCC(movie.fp);
254         movie.header->PaddingGranularity = GET_FCC(movie.fp);
255         movie.header->Flags = GET_FCC(movie.fp);
256         movie.header->TotalFrames = GET_FCC(movie.fp);
257         movie.header->InitialFrames = GET_FCC(movie.fp);
258         movie.header->Streams = GET_FCC(movie.fp);
259         movie.header->SuggestedBufferSize = GET_FCC(movie.fp);
260         movie.header->Width = GET_FCC(movie.fp);
261         movie.header->Height = GET_FCC(movie.fp);
262         movie.header->Reserved[0] = GET_FCC(movie.fp);
263         movie.header->Reserved[1] = GET_FCC(movie.fp);
264         movie.header->Reserved[2] = GET_FCC(movie.fp);
265         movie.header->Reserved[3] = GET_FCC(movie.fp);
266
267         fseek (movie.fp, movie.header->size-14*4, SEEK_CUR);
268
269         if (movie.header->Streams < 1) {
270                 DEBUG_PRINT("streams less than 1\n");
271                 fclose(movie.fp);
272                 return 0;
273         }
274         
275         movie.streams = (AviStreamRec *) MEM_callocN (sizeof(AviStreamRec) * movie.header->Streams, "moviestreams");
276
277         for (temp=0; temp < movie.header->Streams; temp++) {
278
279                 if (GET_FCC(movie.fp) != FCC("LIST") ||
280                         !GET_FCC (movie.fp) ||
281                         GET_FCC (movie.fp) != FCC ("strl") ||
282                         (movie.streams[temp].sh.fcc = GET_FCC (movie.fp)) != FCC ("strh") ||
283                         !(movie.streams[temp].sh.size = GET_FCC (movie.fp))) {
284                         DEBUG_PRINT("bad stream header information\n");
285                         
286                         MEM_freeN(movie.streams);
287                         fclose(movie.fp);
288                         return 0;                               
289                 }
290
291                 movie.streams[temp].sh.Type = GET_FCC (movie.fp);
292                 movie.streams[temp].sh.Handler = GET_FCC (movie.fp);
293
294                 fcca = movie.streams[temp].sh.Handler;
295                 
296                 if (movie.streams[temp].sh.Type == FCC("vids")) {
297                         if (fcca == FCC ("DIB ") ||
298                                 fcca == FCC ("RGB ") ||
299                                 fcca == FCC ("rgb ") ||
300                                 fcca == FCC ("RAW ") ||
301                                 fcca == 0) {
302                                 movie.streams[temp].format = AVI_FORMAT_AVI_RGB;
303                         } else if (fcca == FCC ("mjpg")||fcca == FCC ("MJPG")) {
304                                 movie.streams[temp].format = AVI_FORMAT_MJPEG;
305                         } else {
306                                 MEM_freeN(movie.streams);
307                                 fclose(movie.fp);
308                                 return 0;
309                         }
310                         movie_tracks++;
311                 }
312                 
313                 movie.streams[temp].sh.Flags = GET_FCC (movie.fp);
314                 movie.streams[temp].sh.Priority = GET_TCC (movie.fp);
315                 movie.streams[temp].sh.Language = GET_TCC (movie.fp);
316                 movie.streams[temp].sh.InitialFrames = GET_FCC (movie.fp);
317                 movie.streams[temp].sh.Scale = GET_FCC (movie.fp);
318                 movie.streams[temp].sh.Rate = GET_FCC (movie.fp);
319                 movie.streams[temp].sh.Start = GET_FCC (movie.fp);
320                 movie.streams[temp].sh.Length = GET_FCC (movie.fp);
321                 movie.streams[temp].sh.SuggestedBufferSize = GET_FCC (movie.fp);
322                 movie.streams[temp].sh.Quality = GET_FCC (movie.fp);
323                 movie.streams[temp].sh.SampleSize = GET_FCC (movie.fp);
324                 movie.streams[temp].sh.left = GET_TCC (movie.fp);
325                 movie.streams[temp].sh.top = GET_TCC (movie.fp);
326                 movie.streams[temp].sh.right = GET_TCC (movie.fp);
327                 movie.streams[temp].sh.bottom = GET_TCC (movie.fp);
328
329                 fseek (movie.fp, movie.streams[temp].sh.size-14*4, SEEK_CUR);
330
331                 if (GET_FCC (movie.fp) != FCC("strf")) {
332                         DEBUG_PRINT("no stream format information\n");
333                         MEM_freeN(movie.streams);
334                         fclose(movie.fp);
335                         return 0;
336                 }
337
338                 movie.streams[temp].sf_size= GET_FCC(movie.fp);
339                 if (movie.streams[temp].sh.Type == FCC("vids")) {
340                         j = movie.streams[temp].sf_size - (sizeof(AviBitmapInfoHeader) - 8);
341                         if (j >= 0) {
342                                 AviBitmapInfoHeader *bi;
343                                 
344                                 movie.streams[temp].sf= &bheader;
345                                 bi= (AviBitmapInfoHeader *) movie.streams[temp].sf;
346                                 
347                                 bi->fcc= FCC("strf");
348                                 bi->size= movie.streams[temp].sf_size;
349                                 bi->Size= GET_FCC(movie.fp);
350                                 bi->Width= GET_FCC(movie.fp);
351                                 bi->Height= GET_FCC(movie.fp);
352                                 bi->Planes= GET_TCC(movie.fp);
353                                 bi->BitCount= GET_TCC(movie.fp);
354                                 bi->Compression= GET_FCC(movie.fp);
355                                 bi->SizeImage= GET_FCC(movie.fp);
356                                 bi->XPelsPerMeter= GET_FCC(movie.fp);
357                                 bi->YPelsPerMeter= GET_FCC(movie.fp);
358                                 bi->ClrUsed= GET_FCC(movie.fp);
359                                 bi->ClrImportant= GET_FCC(movie.fp);
360                                 
361                                 fcca = bi->Compression;
362
363                                 if ( movie.streams[temp].format ==
364                                          AVI_FORMAT_AVI_RGB) {
365                                         if (fcca == FCC ("DIB ") ||
366                                                 fcca == FCC ("RGB ") ||
367                                                 fcca == FCC ("rgb ") ||
368                                                 fcca == FCC ("RAW ") ||
369                                                 fcca == 0 ) {
370                                         } else if ( fcca == FCC ("mjpg") || 
371                                                 fcca == FCC ("MJPG")) {
372                                                         movie.streams[temp].format = AVI_FORMAT_MJPEG;
373                                         } else {
374                                                 MEM_freeN(movie.streams);
375                                                 fclose(movie.fp);
376                                                 return 0;
377                                         }
378                                 }
379
380                         } 
381                         if (j > 0) fseek (movie.fp, j, SEEK_CUR);
382                 } else fseek (movie.fp, movie.streams[temp].sf_size, SEEK_CUR);
383
384                 /* Walk to the next LIST */             
385                 while (GET_FCC (movie.fp) != FCC("LIST")) {
386                         temp= GET_FCC (movie.fp);
387                         if (temp<0 || ftell(movie.fp) > movie.size) {
388                                 DEBUG_PRINT("incorrect size in header or error in AVI\n");
389                                 
390                                 MEM_freeN(movie.streams);
391                                 fclose(movie.fp);
392                                 return 0;                               
393                         }
394                         fseek(movie.fp, temp, SEEK_CUR);                        
395                 }
396
397                 fseek(movie.fp, -4L, SEEK_CUR);
398         }
399         
400         MEM_freeN(movie.streams);
401         fclose(movie.fp);
402
403         /* at least one video track is needed */
404         return (movie_tracks != 0); 
405
406 }
407
408 AviError AVI_open_movie (const char *name, AviMovie *movie) {
409         int temp, fcca, size, j;
410         
411         DEBUG_PRINT("opening movie\n");
412
413         memset(movie, 0, sizeof(AviMovie));
414
415         movie->type = AVI_MOVIE_READ;
416         movie->fp = fopen (name, "rb");
417         movie->offset_table = NULL;
418
419         if (movie->fp == NULL)
420                 return AVI_ERROR_OPEN;
421
422         if (GET_FCC (movie->fp) != FCC("RIFF") ||
423                 !(movie->size = GET_FCC (movie->fp)))
424                 return AVI_ERROR_FORMAT;
425
426         movie->header = (AviMainHeader *) MEM_mallocN (sizeof (AviMainHeader), "movieheader");
427
428         if (GET_FCC (movie->fp) != FCC("AVI ") ||
429                 GET_FCC (movie->fp) != FCC("LIST") ||
430                 !GET_FCC (movie->fp) ||
431                 GET_FCC (movie->fp) != FCC("hdrl") ||
432                 (movie->header->fcc = GET_FCC (movie->fp)) != FCC("avih") ||
433                 !(movie->header->size = GET_FCC (movie->fp))) {
434                 DEBUG_PRINT("bad initial header info\n");
435                 return AVI_ERROR_FORMAT;
436         }
437         
438         movie->header->MicroSecPerFrame = GET_FCC(movie->fp);
439         movie->header->MaxBytesPerSec = GET_FCC(movie->fp);
440         movie->header->PaddingGranularity = GET_FCC(movie->fp);
441         movie->header->Flags = GET_FCC(movie->fp);
442         movie->header->TotalFrames = GET_FCC(movie->fp);
443         movie->header->InitialFrames = GET_FCC(movie->fp);
444         movie->header->Streams = GET_FCC(movie->fp);
445         movie->header->SuggestedBufferSize = GET_FCC(movie->fp);
446         movie->header->Width = GET_FCC(movie->fp);
447         movie->header->Height = GET_FCC(movie->fp);
448         movie->header->Reserved[0] = GET_FCC(movie->fp);
449         movie->header->Reserved[1] = GET_FCC(movie->fp);
450         movie->header->Reserved[2] = GET_FCC(movie->fp);
451         movie->header->Reserved[3] = GET_FCC(movie->fp);
452
453         fseek (movie->fp, movie->header->size-14*4, SEEK_CUR);
454
455         if (movie->header->Streams < 1) {
456                 DEBUG_PRINT("streams less than 1\n");
457                 return AVI_ERROR_FORMAT;
458         }
459         
460         movie->streams = (AviStreamRec *) MEM_callocN (sizeof(AviStreamRec) * movie->header->Streams, "moviestreams");
461
462         for (temp=0; temp < movie->header->Streams; temp++) {
463
464                 if (GET_FCC(movie->fp) != FCC("LIST") ||
465                         !GET_FCC (movie->fp) ||
466                         GET_FCC (movie->fp) != FCC ("strl") ||
467                         (movie->streams[temp].sh.fcc = GET_FCC (movie->fp)) != FCC ("strh") ||
468                         !(movie->streams[temp].sh.size = GET_FCC (movie->fp))) {
469                         DEBUG_PRINT("bad stream header information\n");
470                         return AVI_ERROR_FORMAT;                                
471                 }
472
473                 movie->streams[temp].sh.Type = GET_FCC (movie->fp);
474                 movie->streams[temp].sh.Handler = GET_FCC (movie->fp);
475
476                 fcca = movie->streams[temp].sh.Handler;
477                 
478                 if (movie->streams[temp].sh.Type == FCC("vids")) {
479                         if (fcca == FCC ("DIB ") ||
480                                 fcca == FCC ("RGB ") ||
481                                 fcca == FCC ("rgb ") ||
482                                 fcca == FCC ("RAW ") ||
483                                 fcca == 0) {
484                                 movie->streams[temp].format = AVI_FORMAT_AVI_RGB;
485                         } else if (fcca == FCC ("mjpg")||fcca == FCC ("MJPG")) {
486                                 movie->streams[temp].format = AVI_FORMAT_MJPEG;
487                         } else {
488                                 return AVI_ERROR_COMPRESSION;
489                         }
490                 }
491                 
492                 movie->streams[temp].sh.Flags = GET_FCC (movie->fp);
493                 movie->streams[temp].sh.Priority = GET_TCC (movie->fp);
494                 movie->streams[temp].sh.Language = GET_TCC (movie->fp);
495                 movie->streams[temp].sh.InitialFrames = GET_FCC (movie->fp);
496                 movie->streams[temp].sh.Scale = GET_FCC (movie->fp);
497                 movie->streams[temp].sh.Rate = GET_FCC (movie->fp);
498                 movie->streams[temp].sh.Start = GET_FCC (movie->fp);
499                 movie->streams[temp].sh.Length = GET_FCC (movie->fp);
500                 movie->streams[temp].sh.SuggestedBufferSize = GET_FCC (movie->fp);
501                 movie->streams[temp].sh.Quality = GET_FCC (movie->fp);
502                 movie->streams[temp].sh.SampleSize = GET_FCC (movie->fp);
503                 movie->streams[temp].sh.left = GET_TCC (movie->fp);
504                 movie->streams[temp].sh.top = GET_TCC (movie->fp);
505                 movie->streams[temp].sh.right = GET_TCC (movie->fp);
506                 movie->streams[temp].sh.bottom = GET_TCC (movie->fp);
507
508                 fseek (movie->fp, movie->streams[temp].sh.size-14*4, SEEK_CUR);
509
510                 if (GET_FCC (movie->fp) != FCC("strf")) {
511                         DEBUG_PRINT("no stream format information\n");
512                         return AVI_ERROR_FORMAT;
513                 }
514
515                 movie->streams[temp].sf_size= GET_FCC(movie->fp);
516                 if (movie->streams[temp].sh.Type == FCC("vids")) {
517                         j = movie->streams[temp].sf_size - (sizeof(AviBitmapInfoHeader) - 8);
518                         if (j >= 0) {
519                                 AviBitmapInfoHeader *bi;
520                                 
521                                 movie->streams[temp].sf= MEM_mallocN(sizeof(AviBitmapInfoHeader), "streamformat");
522                                 
523                                 bi= (AviBitmapInfoHeader *) movie->streams[temp].sf;
524                                 
525                                 bi->fcc= FCC("strf");
526                                 bi->size= movie->streams[temp].sf_size;
527                                 bi->Size= GET_FCC(movie->fp);
528                                 bi->Width= GET_FCC(movie->fp);
529                                 bi->Height= GET_FCC(movie->fp);
530                                 bi->Planes= GET_TCC(movie->fp);
531                                 bi->BitCount= GET_TCC(movie->fp);
532                                 bi->Compression= GET_FCC(movie->fp);
533                                 bi->SizeImage= GET_FCC(movie->fp);
534                                 bi->XPelsPerMeter= GET_FCC(movie->fp);
535                                 bi->YPelsPerMeter= GET_FCC(movie->fp);
536                                 bi->ClrUsed= GET_FCC(movie->fp);
537                                 bi->ClrImportant= GET_FCC(movie->fp);
538                                 
539                                 fcca = bi->Compression;
540
541                                                                 if ( movie->streams[temp].format ==
542                                          AVI_FORMAT_AVI_RGB) {
543                                         if (fcca == FCC ("DIB ") ||
544                                                 fcca == FCC ("RGB ") ||
545                                                 fcca == FCC ("rgb ") ||
546                                                 fcca == FCC ("RAW ") ||
547                                                 fcca == 0 ) {
548                                         } else if ( fcca == FCC ("mjpg") || 
549                                                 fcca == FCC ("MJPG")) {
550                                                         movie->streams[temp].format = AVI_FORMAT_MJPEG;
551                                         } else {
552                                                 return AVI_ERROR_COMPRESSION;
553                                         }
554                                 }
555
556                         } 
557                         if (j > 0) fseek (movie->fp, j, SEEK_CUR);
558                 } else fseek (movie->fp, movie->streams[temp].sf_size, SEEK_CUR);
559                 
560                 /* Walk to the next LIST */             
561                 while (GET_FCC (movie->fp) != FCC("LIST")) {
562                         temp= GET_FCC (movie->fp);
563                         if (temp<0 || ftell(movie->fp) > movie->size) {
564                                 DEBUG_PRINT("incorrect size in header or error in AVI\n");
565                                 return AVI_ERROR_FORMAT;                                
566                         }
567                         fseek(movie->fp, temp, SEEK_CUR);                       
568                 }
569                 
570                 fseek(movie->fp, -4L, SEEK_CUR);                
571         }
572
573         while (1) {
574                 temp = GET_FCC (movie->fp);
575                 size = GET_FCC (movie->fp);
576
577                 if (size == 0)
578                         break;
579
580                 if (temp == FCC("LIST")) {
581                         if (GET_FCC(movie->fp) == FCC ("movi"))
582                                 break;
583                         else
584                                 fseek (movie->fp, size-4, SEEK_CUR);
585                 } else {
586                         fseek (movie->fp, size, SEEK_CUR);
587                 }
588                 if (ftell(movie->fp) > movie->size) {
589                         DEBUG_PRINT("incorrect size in header or error in AVI\n");
590                         return AVI_ERROR_FORMAT;
591                 }
592         }
593
594         movie->movi_offset = ftell (movie->fp);
595         movie->read_offset = movie->movi_offset;
596         if (AVI_DEBUG) printf ("movi_offset is %d\n", movie->movi_offset);
597         
598         /* Read in the index if the file has one, otherwise create one */
599         if (movie->header->Flags & AVIF_HASINDEX) {
600                 fseek(movie->fp, size-4, SEEK_CUR);
601
602                 if (GET_FCC(movie->fp) != FCC("idx1")) {
603                         DEBUG_PRINT("bad index informatio\n");
604                         return AVI_ERROR_FORMAT;
605                 }
606
607                 movie->index_entries = GET_FCC (movie->fp)/sizeof(AviIndexEntry);
608                 if (movie->index_entries == 0) {
609                         DEBUG_PRINT("no index entries\n");
610                         return AVI_ERROR_FORMAT;
611                 }
612
613                 movie->entries = (AviIndexEntry *) MEM_mallocN (movie->index_entries * sizeof(AviIndexEntry),"movieentries");
614
615                 for (temp=0; temp < movie->index_entries; temp++) {
616                         movie->entries[temp].ChunkId = GET_FCC (movie->fp);
617                         movie->entries[temp].Flags = GET_FCC (movie->fp);
618                         movie->entries[temp].Offset = GET_FCC (movie->fp);
619                         movie->entries[temp].Size = GET_FCC (movie->fp);
620                         
621                         if (AVI_DEBUG) printf ("Index entry %04d: ChunkId:%s Flags:%d Offset:%d Size:%d\n", temp, fcc_to_char(movie->entries[temp].ChunkId), movie->entries[temp].Flags, movie->entries[temp].Offset, movie->entries[temp].Size);
622                 }
623
624 /* Some AVI's have offset entries in absolute coordinates
625  * instead of an offset from the movie beginning... this is...
626  * wacky, but we need to handle it. The wacky offset always
627  * starts at movi_offset it seems... so we'll check that.
628  * Note the the offset needs an extra 4 bytes for some 
629  * undetermined reason */
630  
631                 if (movie->entries[0].Offset == movie->movi_offset)
632                         movie->read_offset= 4;
633         }
634
635         DEBUG_PRINT("movie succesfully opened\n");
636         return AVI_ERROR_NONE;
637 }
638
639 void *AVI_read_frame (AviMovie *movie, AviFormat format, int frame, int stream) {
640         int cur_frame=-1, temp, i=0, rewind=1;
641         void *buffer;
642
643         /* Retrieve the record number of the desired frame in the index 
644                 If a chunk has Size 0 we need to rewind to previous frame */
645         while(rewind && frame > -1) {
646                 i=0;
647                 cur_frame=-1;
648                 rewind = 0;
649
650                 while (cur_frame < frame && i < movie->index_entries) {
651                         if (fcc_is_data (movie->entries[i].ChunkId) &&
652                                 fcc_get_stream (movie->entries[i].ChunkId) == stream) {
653                                 if ((cur_frame == frame -1) && (movie->entries[i].Size == 0)) {
654                                         rewind = 1;
655                                         frame = frame -1;
656                                 } else {
657                                         cur_frame++;
658                                 }
659                         }
660                         i++;
661                 }
662         }
663
664         if (cur_frame != frame) return NULL;
665
666
667         fseek (movie->fp, movie->read_offset + movie->entries[i-1].Offset, SEEK_SET);
668
669         temp = GET_FCC(movie->fp);
670         buffer = MEM_mallocN (temp,"readbuffer");
671
672         if (fread (buffer, 1, temp, movie->fp) != temp) {
673                 MEM_freeN(buffer);
674
675                 return NULL;
676         }
677         
678         buffer = avi_format_convert (movie, stream, buffer, movie->streams[stream].format, format, &temp);
679
680         return buffer;
681 }
682
683 AviError AVI_close (AviMovie *movie) {
684         int i;
685
686         fclose (movie->fp);
687
688         for (i=0; i < movie->header->Streams; i++) {
689                 if (movie->streams[i].sf != NULL)
690                         MEM_freeN (movie->streams[i].sf);
691         }
692
693         if (movie->header != NULL)
694                 MEM_freeN (movie->header);
695         if (movie->streams!= NULL)
696                 MEM_freeN (movie->streams);
697         if (movie->entries != NULL)
698                 MEM_freeN (movie->entries);
699         if (movie->offset_table != NULL)
700                 MEM_freeN (movie->offset_table);
701
702         return AVI_ERROR_NONE;
703 }
704
705 AviError AVI_open_compress (char *name, AviMovie *movie, int streams, ...) {
706         va_list ap;
707         AviList list;
708         AviChunk chunk;
709         int i;
710         int header_pos1, header_pos2;
711         int stream_pos1, stream_pos2;
712
713         movie->type = AVI_MOVIE_WRITE;
714         movie->fp = fopen (name, "wb");
715
716         movie->index_entries = 0;
717
718         if (movie->fp == NULL)
719                 return AVI_ERROR_OPEN;
720
721         movie->offset_table = (long *) MEM_mallocN ((1+streams*2) * sizeof (long),"offsettable");
722         
723         for (i=0; i < 1 + streams*2; i++)
724                 movie->offset_table[i] = -1L;
725
726         movie->entries = NULL;
727
728         movie->header = (AviMainHeader *) MEM_mallocN (sizeof(AviMainHeader),"movieheader");
729
730         movie->header->fcc = FCC("avih");
731         movie->header->size = 56;
732         movie->header->MicroSecPerFrame = 66667;
733          movie->header->MaxBytesPerSec = 0; 
734          movie->header->PaddingGranularity = 0; 
735          movie->header->Flags = AVIF_HASINDEX | AVIF_MUSTUSEINDEX;
736         movie->header->TotalFrames = 0;
737         movie->header->InitialFrames = 0;
738         movie->header->Streams = streams;
739         movie->header->SuggestedBufferSize = 0;
740         movie->header->Width = 0;
741         movie->header->Height = 0;
742         movie->header->Reserved[0] = 0;
743         movie->header->Reserved[1] = 0;
744         movie->header->Reserved[2] = 0;
745         movie->header->Reserved[3] = 0;
746
747         movie->streams = (AviStreamRec *) MEM_mallocN (sizeof(AviStreamRec) * movie->header->Streams,"moviestreams");
748
749         va_start (ap, streams);
750
751         for (i=0; i < movie->header->Streams; i++) {
752                 movie->streams[i].format = va_arg(ap, AviFormat);
753
754                 movie->streams[i].sh.fcc = FCC ("strh");
755                 movie->streams[i].sh.size = 56;
756                 movie->streams[i].sh.Type = avi_get_format_type (movie->streams[i].format);
757                 if (movie->streams[i].sh.Type == 0)
758                         return AVI_ERROR_FORMAT;
759
760                 movie->streams[i].sh.Handler = avi_get_format_fcc (movie->streams[i].format);
761                 if (movie->streams[i].sh.Handler == 0)
762                         return AVI_ERROR_FORMAT;
763
764                 movie->streams[i].sh.Flags = 0;
765                 movie->streams[i].sh.Priority = 0;
766                 movie->streams[i].sh.Language = 0;
767                 movie->streams[i].sh.InitialFrames = 0;
768                 movie->streams[i].sh.Scale = 66667;
769                 movie->streams[i].sh.Rate = 1000000;
770                 movie->streams[i].sh.Start = 0;
771                 movie->streams[i].sh.Length = 0;
772                 movie->streams[i].sh.SuggestedBufferSize = 0;
773                 movie->streams[i].sh.Quality = 10000;
774                 movie->streams[i].sh.SampleSize = 0;
775                 movie->streams[i].sh.left = 0;
776                 movie->streams[i].sh.top = 0;
777                 movie->streams[i].sh.right = 0;
778                 movie->streams[i].sh.bottom = 0;
779
780                 if (movie->streams[i].sh.Type == FCC("vids")) { 
781 /*
782                         if (movie->streams[i].format == AVI_FORMAT_MJPEG) {
783                                 movie->streams[i].sf = MEM_mallocN (sizeof(AviBitmapInfoHeader) 
784                                                                                 + sizeof(AviMJPEGUnknown),"moviestreamformatL");
785                                 movie->streams[i].sf_size = sizeof(AviBitmapInfoHeader) + sizeof(AviMJPEGUnknown);
786                         } else {
787 */
788                         movie->streams[i].sf = MEM_mallocN (sizeof(AviBitmapInfoHeader),  "moviestreamformatS");
789                         movie->streams[i].sf_size = sizeof(AviBitmapInfoHeader);
790
791                         ((AviBitmapInfoHeader *) movie->streams[i].sf)->fcc = FCC ("strf");
792                         ((AviBitmapInfoHeader *) movie->streams[i].sf)->size = movie->streams[i].sf_size - 8;
793                         ((AviBitmapInfoHeader *) movie->streams[i].sf)->Size = movie->streams[i].sf_size - 8;
794                         ((AviBitmapInfoHeader *) movie->streams[i].sf)->Width = 0;
795                         ((AviBitmapInfoHeader *) movie->streams[i].sf)->Height = 0;
796                         ((AviBitmapInfoHeader *) movie->streams[i].sf)->Planes = 1;
797                         ((AviBitmapInfoHeader *) movie->streams[i].sf)->BitCount = 24;
798                         ((AviBitmapInfoHeader *) movie->streams[i].sf)->Compression = avi_get_format_compression (movie->streams[i].format);
799                         ((AviBitmapInfoHeader *) movie->streams[i].sf)->SizeImage = 0;
800                         ((AviBitmapInfoHeader *) movie->streams[i].sf)->XPelsPerMeter = 0;
801                         ((AviBitmapInfoHeader *) movie->streams[i].sf)->YPelsPerMeter = 0;
802                         ((AviBitmapInfoHeader *) movie->streams[i].sf)->ClrUsed = 0;
803                         ((AviBitmapInfoHeader *) movie->streams[i].sf)->ClrImportant = 0;
804
805 /*
806                         if (movie->streams[i].format == AVI_FORMAT_MJPEG) {
807                                 AviMJPEGUnknown *tmp;
808                                 
809                                 tmp = (AviMJPEGUnknown *) ((char*) movie->streams[i].sf +sizeof(AviBitmapInfoHeader));
810                                 
811                                 tmp->a = 44;
812                                 tmp->b = 24;
813                                 tmp->c = 0;
814                                 tmp->d = 2;
815                                 tmp->e = 8;
816                                 tmp->f = 2;
817                                 tmp->g = 1;
818                         }
819                 } else if (movie->streams[i].sh.Type == FCC("auds")) {
820                         ;
821 */
822                 }
823         }
824
825         list.fcc = FCC("RIFF");
826         list.size = 0;
827         list.ids = FCC("AVI ");
828
829         awrite (movie, &list, 1, sizeof(AviList), movie->fp, AVI_LIST);
830
831         list.fcc = FCC("LIST");
832         list.size = 0;
833         list.ids = FCC("hdrl");
834
835         awrite (movie, &list, 1, sizeof(AviList), movie->fp, AVI_LIST);
836
837         header_pos1 = ftell(movie->fp);
838
839         movie->offset_table[0] = ftell(movie->fp);
840
841         awrite (movie, movie->header, 1, sizeof(AviMainHeader), movie->fp, AVI_MAINH);
842
843         for (i=0; i < movie->header->Streams; i++) {
844                 list.fcc = FCC("LIST");
845                 list.size = 0;
846                 list.ids = FCC("strl");
847
848                 awrite (movie, &list, 1, sizeof(AviList), movie->fp, AVI_LIST);
849
850                 stream_pos1 = ftell(movie->fp);
851
852                 movie->offset_table[1+i*2] = ftell(movie->fp);
853                 awrite (movie, &movie->streams[i].sh, 1, sizeof(AviStreamHeader), movie->fp, AVI_STREAMH);
854
855                 movie->offset_table[1+i*2+1] = ftell(movie->fp);
856                 awrite (movie, movie->streams[i].sf, 1, movie->streams[i].sf_size, movie->fp, AVI_BITMAPH);
857
858                 stream_pos2 = ftell(movie->fp);
859
860                 fseek (movie->fp, stream_pos1-8, SEEK_SET);
861
862                 PUT_FCCN((stream_pos2-stream_pos1+4L), movie->fp);
863
864                 fseek (movie->fp, stream_pos2, SEEK_SET);
865         }
866
867         if (ftell(movie->fp) < 2024 - 8) {
868                 chunk.fcc = FCC("JUNK");
869                 chunk.size = 2024-8-ftell(movie->fp);
870
871                 awrite (movie, &chunk, 1, sizeof(AviChunk), movie->fp, AVI_CHUNK);
872
873                 for (i=0; i < chunk.size; i++)
874                         putc(0, movie->fp);
875         }
876
877         header_pos2 = ftell(movie->fp);
878
879         list.fcc = FCC("LIST");
880         list.size = 0;
881         list.ids = FCC("movi");
882
883         awrite (movie, &list, 1, sizeof(AviList), movie->fp, AVI_LIST);
884
885         movie->movi_offset = ftell(movie->fp)-8L;
886
887         fseek (movie->fp, AVI_HDRL_SOFF, SEEK_SET);
888
889         PUT_FCCN((header_pos2-header_pos1+4L), movie->fp);
890
891         return AVI_ERROR_NONE;
892 }
893
894 AviError AVI_write_frame (AviMovie *movie, int frame_num, ...) {
895         AviList list;
896         AviChunk chunk;
897         AviIndexEntry *temp;
898         va_list ap;
899         int stream;
900         long rec_off;
901         AviFormat format;
902         void *buffer;
903         int size;
904
905         if (frame_num < 0)
906                 return AVI_ERROR_OPTION;
907
908         /* Allocate the new memory for the index entry */
909
910         if (frame_num+1 > movie->index_entries) {
911                 temp = (AviIndexEntry *) MEM_mallocN ((frame_num+1) * 
912                         (movie->header->Streams+1) * sizeof(AviIndexEntry),"newidxentry");
913                 if (movie->entries != NULL) {
914                         memcpy (temp, movie->entries, movie->index_entries * (movie->header->Streams+1)
915                                 * sizeof(AviIndexEntry));
916                         MEM_freeN (movie->entries);
917                 }
918
919                 movie->entries = temp;
920                 movie->index_entries = frame_num+1;
921         }
922
923         /* Slap a new record entry onto the end of the file */
924
925         fseek (movie->fp, 0L, SEEK_END);
926
927         list.fcc = FCC("LIST");
928         list.size = 0;
929         list.ids = FCC("rec ");
930
931         awrite (movie, &list, 1, sizeof(AviList), movie->fp, AVI_LIST);
932
933         rec_off = ftell (movie->fp)-8L;
934
935         /* Write a frame for every stream */
936
937         va_start (ap, frame_num);
938
939         for (stream=0; stream < movie->header->Streams; stream++) {
940                 unsigned int tbuf=0;
941                 
942                 format = va_arg (ap, AviFormat);
943                 buffer = va_arg (ap, void*);
944                 size = va_arg (ap, int);
945
946                 /* Convert the buffer into the output format */
947                 buffer = avi_format_convert (movie, stream, buffer, format, movie->streams[stream].format, &size);
948
949                 /* Write the header info for this data chunk */
950
951                 fseek (movie->fp, 0L, SEEK_END);
952
953                 chunk.fcc = avi_get_data_id (format, stream);
954                 chunk.size = size;
955                 
956                 if (size%4) chunk.size += 4 - size%4;
957                 
958                 awrite (movie, &chunk, 1, sizeof(AviChunk), movie->fp, AVI_CHUNK);
959
960                 /* Write the index entry for this data chunk */
961
962                 movie->entries[frame_num * (movie->header->Streams+1) + stream + 1].ChunkId = chunk.fcc;
963                 movie->entries[frame_num * (movie->header->Streams+1) + stream + 1].Flags = AVIIF_KEYFRAME;
964                 movie->entries[frame_num * (movie->header->Streams+1) + stream + 1].Offset = ftell(movie->fp)-12L-movie->movi_offset;
965                 movie->entries[frame_num * (movie->header->Streams+1) + stream + 1].Size = chunk.size;
966
967                 /* Write the chunk */
968                 awrite (movie, buffer, 1, size, movie->fp, AVI_RAW);
969                 MEM_freeN (buffer);
970
971                 if (size%4) awrite (movie, &tbuf, 1, 4-size%4, movie->fp, AVI_RAW);
972
973                 /* Update the stream headers length field */
974                 movie->streams[stream].sh.Length++;
975                 fseek (movie->fp, movie->offset_table[1+stream*2], SEEK_SET);
976                 awrite (movie, &movie->streams[stream].sh, 1, sizeof(AviStreamHeader), movie->fp, AVI_STREAMH);
977         }
978         va_end (ap);
979
980         /* Record the entry for the new record */
981
982         fseek (movie->fp, 0L, SEEK_END);
983
984         movie->entries[frame_num * (movie->header->Streams+1)].ChunkId = FCC("rec ");
985         movie->entries[frame_num * (movie->header->Streams+1)].Flags = AVIIF_LIST;
986         movie->entries[frame_num * (movie->header->Streams+1)].Offset = rec_off-8L-movie->movi_offset;
987         movie->entries[frame_num * (movie->header->Streams+1)].Size = ftell(movie->fp)-(rec_off+4L);
988
989         /* Update the record size */
990         fseek (movie->fp, rec_off, SEEK_SET);
991         PUT_FCCN (movie->entries[frame_num * (movie->header->Streams+1)].Size, movie->fp);
992
993         /* Update the main header information in the file */
994         movie->header->TotalFrames++;
995         fseek (movie->fp, movie->offset_table[0], SEEK_SET);
996         awrite (movie, movie->header, 1, sizeof(AviMainHeader), movie->fp, AVI_MAINH);
997
998         return AVI_ERROR_NONE;
999 }
1000
1001 AviError AVI_close_compress (AviMovie *movie) {
1002         int temp, movi_size, i;
1003
1004         fseek (movie->fp, 0L, SEEK_END);
1005         movi_size = ftell (movie->fp);
1006
1007         PUT_FCC ("idx1", movie->fp);
1008         PUT_FCCN ((movie->index_entries*(movie->header->Streams+1)*16), movie->fp);
1009
1010         for (temp=0; temp < movie->index_entries*(movie->header->Streams+1); temp++)
1011                 awrite (movie, &movie->entries[temp], 1, sizeof(AviIndexEntry), movie->fp, AVI_INDEXE);
1012
1013         temp = ftell (movie->fp);
1014
1015         fseek (movie->fp, AVI_RIFF_SOFF, SEEK_SET);
1016
1017         PUT_FCCN((temp-8L), movie->fp);
1018
1019         fseek (movie->fp, movie->movi_offset, SEEK_SET);
1020
1021         PUT_FCCN((movi_size-(movie->movi_offset+4L)),movie->fp);
1022
1023         fclose (movie->fp);
1024
1025         for (i=0; i < movie->header->Streams; i++) {
1026                 if (movie->streams[i].sf != NULL)
1027                         MEM_freeN (movie->streams[i].sf);
1028         }
1029         if (movie->header != NULL)
1030                 MEM_freeN (movie->header);
1031         if (movie->entries != NULL)
1032                 MEM_freeN (movie->entries);
1033         if (movie->streams != NULL)
1034                 MEM_freeN (movie->streams);
1035         if (movie->offset_table != NULL)
1036                 MEM_freeN (movie->offset_table);
1037         return AVI_ERROR_NONE;
1038 }