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