Initial revision
[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/BL DUAL 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. The Blender
14  * Foundation also sells licenses for use in proprietary software under
15  * the Blender License.  See http://www.blender.org/BL/ for information
16  * about this.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
26  *
27  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
28  * All rights reserved.
29  *
30  * The Original Code is: all of this file.
31  *
32  * Contributor(s): none yet.
33  *
34  * ***** END GPL/BL DUAL LICENSE BLOCK *****
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 "AVI_avi.h"
45 #include "avi_intern.h"
46
47 #include "endian.h"
48
49 static int AVI_DEBUG=0;
50 static char DEBUG_FCC[4];
51
52 #define DEBUG(x) if(AVI_DEBUG) printf("AVI DEBUG: " x);
53
54 /* local functions */
55 char *fcc_to_char (unsigned int fcc);
56 char *tcc_to_char (unsigned int tcc);
57
58
59
60 /* implemetation */
61
62 unsigned int GET_FCC (FILE *fp) {
63         unsigned char tmp[4];
64
65         tmp[0] = getc(fp);
66         tmp[1] = getc(fp);
67         tmp[2] = getc(fp);
68         tmp[3] = getc(fp);
69
70         return FCC (tmp);
71 }
72
73 unsigned int GET_TCC (FILE *fp) {
74         char tmp[5];
75
76         tmp[0] = getc(fp);
77         tmp[1] = getc(fp);
78         tmp[2] = 0;
79         tmp[3] = 0;
80
81         return FCC (tmp);
82 }
83
84 char *fcc_to_char (unsigned int fcc) {
85         DEBUG_FCC[0]= (fcc)&0177;
86         DEBUG_FCC[1]= (fcc>>8)&0177;
87         DEBUG_FCC[2]= (fcc>>16)&0177;
88         DEBUG_FCC[3]= (fcc>>24)&0177;
89
90         return DEBUG_FCC;       
91 }
92
93 char *tcc_to_char (unsigned int tcc) {
94         DEBUG_FCC[0]= (tcc)&0177;
95         DEBUG_FCC[1]= (tcc>>8)&0177;
96         DEBUG_FCC[2]= 0;
97         DEBUG_FCC[3]= 0;
98
99         return DEBUG_FCC;       
100 }
101
102 int AVI_get_stream (AviMovie *movie, int avist_type, int stream_num) {
103         int cur_stream;
104
105         if (movie == NULL)
106                 return -AVI_ERROR_OPTION;
107
108         for (cur_stream=0; cur_stream < movie->header->Streams; cur_stream++) {
109                 if (movie->streams[cur_stream].sh.Type == avist_type) {
110                         if (stream_num == 0)
111                                 return cur_stream;
112                         else
113                                 stream_num--;
114                 }
115         }
116
117         return -AVI_ERROR_FOUND;
118 }
119
120 static int fcc_get_stream (int fcc) {
121         char fccs[4];
122
123         fccs[0] = fcc;
124         fccs[1] = fcc>>8;
125         fccs[2] = fcc>>16;
126         fccs[3] = fcc>>24;
127
128         return 10*(fccs[0]-'0') + (fccs[1]-'0');
129 }
130
131 static int fcc_is_data (int fcc) {
132         char fccs[4];
133
134         fccs[0] = fcc;
135         fccs[1] = fcc>>8;
136         fccs[2] = fcc>>16;
137         fccs[3] = fcc>>24;
138
139         if (!isdigit (fccs[0]) || !isdigit (fccs[1]) || (fccs[2] != 'd' && fccs[2] != 'w'))
140                 return 0;
141         if (fccs[3] != 'b' && fccs[3] != 'c')
142                 return 0;
143
144         return 1;
145 }
146
147 AviError AVI_print_error (AviError in_error) {
148         int error;
149
150         if ((int) in_error < 0)
151                 error = -in_error;
152         else
153                 error = in_error;
154
155         switch (error) {
156         case AVI_ERROR_NONE:
157                 break;
158         case AVI_ERROR_COMPRESSION:
159                 printf ("AVI ERROR: compressed in an unsupported format\n");
160                 break;
161         case AVI_ERROR_OPEN:
162                 printf ("AVI ERROR: could not open file\n");
163                 break;
164         case AVI_ERROR_READING:
165                 printf ("AVI ERROR: could not read from file\n");
166                 break;
167         case AVI_ERROR_WRITING:
168                 printf ("AVI ERROR: could not write to file\n");
169                 break;
170         case AVI_ERROR_FORMAT:
171                 printf ("AVI ERROR: file is in an illegal or unrecognized format\n");
172                 break;
173         case AVI_ERROR_ALLOC:
174                 printf ("AVI ERROR: error encountered while allocating memory\n");
175                 break;
176         case AVI_ERROR_OPTION:
177                 printf ("AVI ERROR: program made illegal request\n");
178                 break;
179         case AVI_ERROR_FOUND:
180                 printf ("AVI ERROR: movie did not contain expected item\n");
181                 break;
182         default: 
183                 break;
184         }
185
186         return in_error;
187 }
188
189 void AVI_set_debug (int mode) {
190         AVI_DEBUG= mode;
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 AviError AVI_open_movie (char *name, AviMovie *movie) {
214         int temp, fcca, size;
215         
216         DEBUG("opening movie\n");
217
218         memset(movie, 0, sizeof(AviMovie));
219
220         movie->type = AVI_MOVIE_READ;
221         movie->fp = fopen (name, "rb");
222         movie->offset_table = NULL;
223
224         if (movie->fp == NULL)
225                 return AVI_ERROR_OPEN;
226
227         if (GET_FCC (movie->fp) != FCC("RIFF") ||
228             !(movie->size = GET_FCC (movie->fp)))
229                 return AVI_ERROR_FORMAT;
230
231         movie->header = (AviMainHeader *) MEM_mallocN (sizeof (AviMainHeader), "movieheader");
232 /*      movie->header = (AviMainHeader *) malloc (sizeof (AviMainHeader)); */
233
234         if (GET_FCC (movie->fp) != FCC("AVI ") ||
235             GET_FCC (movie->fp) != FCC("LIST") ||
236             !GET_FCC (movie->fp) ||
237             GET_FCC (movie->fp) != FCC("hdrl") ||
238             (movie->header->fcc = GET_FCC (movie->fp)) != FCC("avih") ||
239             !(movie->header->size = GET_FCC (movie->fp))) {
240                 DEBUG("bad initial header info\n");
241                 return AVI_ERROR_FORMAT;
242         }
243         
244         movie->header->MicroSecPerFrame = GET_FCC(movie->fp);
245         movie->header->MaxBytesPerSec = GET_FCC(movie->fp);
246         movie->header->PaddingGranularity = GET_FCC(movie->fp);
247         movie->header->Flags = GET_FCC(movie->fp);
248         movie->header->TotalFrames = GET_FCC(movie->fp);
249         movie->header->InitialFrames = GET_FCC(movie->fp);
250         movie->header->Streams = GET_FCC(movie->fp);
251         movie->header->SuggestedBufferSize = GET_FCC(movie->fp);
252         movie->header->Width = GET_FCC(movie->fp);
253         movie->header->Height = GET_FCC(movie->fp);
254         movie->header->Reserved[0] = GET_FCC(movie->fp);
255         movie->header->Reserved[1] = GET_FCC(movie->fp);
256         movie->header->Reserved[2] = GET_FCC(movie->fp);
257         movie->header->Reserved[3] = GET_FCC(movie->fp);
258
259         fseek (movie->fp, movie->header->size-14*4, SEEK_CUR);
260
261         if (movie->header->Streams < 1) {
262                 DEBUG("streams less than 1\n");
263                 return AVI_ERROR_FORMAT;
264         }
265         
266         movie->streams = (AviStreamRec *) MEM_callocN (sizeof(AviStreamRec) * movie->header->Streams, "moviestreams");
267 /*      movie->streams = (AviStreamRec *) */
268 /*              malloc (sizeof(AviStreamRec) * movie->header->Streams); */
269
270         for (temp=0; temp < movie->header->Streams; temp++) {
271
272                 if (GET_FCC(movie->fp) != FCC("LIST") ||
273                     !GET_FCC (movie->fp) ||
274                     GET_FCC (movie->fp) != FCC ("strl") ||
275                     (movie->streams[temp].sh.fcc = GET_FCC (movie->fp)) != FCC ("strh") ||
276                     !(movie->streams[temp].sh.size = GET_FCC (movie->fp))) {
277                         DEBUG("bad stream header information\n");
278                         return AVI_ERROR_FORMAT;                                
279                 }
280
281                 movie->streams[temp].sh.Type = GET_FCC (movie->fp);
282                 movie->streams[temp].sh.Handler = GET_FCC (movie->fp);
283
284                 fcca = movie->streams[temp].sh.Handler;
285                 
286                 if (movie->streams[temp].sh.Type == FCC("vids")) {
287                         if (fcca == FCC ("DIB ") ||
288                                 fcca == FCC ("RGB ") ||
289                                 fcca == FCC ("rgb ") ||
290                                 fcca == FCC ("RAW ") ||
291                                 fcca == 0) {
292                                 movie->streams[temp].format = AVI_FORMAT_AVI_RGB;
293                         } else if (fcca == FCC ("mjpg")||fcca == FCC ("MJPG")) {
294                                 movie->streams[temp].format = AVI_FORMAT_MJPEG;
295                         } else {
296                                 return AVI_ERROR_COMPRESSION;
297                         }
298                 }
299                 
300                 movie->streams[temp].sh.Flags = GET_FCC (movie->fp);
301                 movie->streams[temp].sh.Priority = GET_TCC (movie->fp);
302                 movie->streams[temp].sh.Language = GET_TCC (movie->fp);
303                 movie->streams[temp].sh.InitialFrames = GET_FCC (movie->fp);
304                 movie->streams[temp].sh.Scale = GET_FCC (movie->fp);
305                 movie->streams[temp].sh.Rate = GET_FCC (movie->fp);
306                 movie->streams[temp].sh.Start = GET_FCC (movie->fp);
307                 movie->streams[temp].sh.Length = GET_FCC (movie->fp);
308                 movie->streams[temp].sh.SuggestedBufferSize = GET_FCC (movie->fp);
309                 movie->streams[temp].sh.Quality = GET_FCC (movie->fp);
310                 movie->streams[temp].sh.SampleSize = GET_FCC (movie->fp);
311                 movie->streams[temp].sh.left = GET_TCC (movie->fp);
312                 movie->streams[temp].sh.top = GET_TCC (movie->fp);
313                 movie->streams[temp].sh.right = GET_TCC (movie->fp);
314                 movie->streams[temp].sh.bottom = GET_TCC (movie->fp);
315
316                 fseek (movie->fp, movie->streams[temp].sh.size-14*4, SEEK_CUR);
317
318                 if (GET_FCC (movie->fp) != FCC("strf")) {
319                         DEBUG("no stream format information\n");
320                         return AVI_ERROR_FORMAT;
321                 }
322
323                 movie->streams[temp].sf_size= GET_FCC(movie->fp);
324                 if (movie->streams[temp].sh.Type == FCC("vids")) {
325                         if (movie->streams[temp].sf_size == sizeof(AviBitmapInfoHeader)-8) {
326                                 AviBitmapInfoHeader *bi;
327                                 
328                                 movie->streams[temp].sf= MEM_mallocN(sizeof(AviBitmapInfoHeader), "streamformat");
329 /*                              movie->streams[temp].sf= malloc(sizeof(AviBitmapInfoHeader)); */
330                                 
331                                 bi= (AviBitmapInfoHeader *) movie->streams[temp].sf;
332                                 
333                                 bi->fcc= FCC("strf");
334                                 bi->size= movie->streams[temp].sf_size;
335                                 bi->Size= GET_FCC(movie->fp);
336                                 bi->Width= GET_FCC(movie->fp);
337                                 bi->Height= GET_FCC(movie->fp);
338                                 bi->Planes= GET_TCC(movie->fp);
339                                 bi->BitCount= GET_TCC(movie->fp);
340                                 bi->Compression= GET_FCC(movie->fp);
341                                 bi->SizeImage= GET_FCC(movie->fp);
342                                 bi->XPelsPerMeter= GET_FCC(movie->fp);
343                                 bi->YPelsPerMeter= GET_FCC(movie->fp);
344                                 bi->ClrUsed= GET_FCC(movie->fp);
345                                 bi->ClrImportant= GET_FCC(movie->fp);
346                                 
347                                 fcca = bi->Compression;
348
349                                 if (fcca == FCC ("DIB ") ||
350                                         fcca == FCC ("RGB ") ||
351                                         fcca == FCC ("rgb ") ||
352                                         fcca == FCC ("RAW ") ||
353                                         fcca == FCC ("mjpg") ||
354                                         fcca == FCC ("MJPG") ||
355                                         fcca == 0) {
356                                 } else {
357                                         return AVI_ERROR_COMPRESSION;
358                                 }
359
360                         } else fseek (movie->fp, movie->streams[temp].sf_size, SEEK_CUR);
361                 } else fseek (movie->fp, movie->streams[temp].sf_size, SEEK_CUR);
362                 
363                 /* Walk to the next LIST */             
364                 while (GET_FCC (movie->fp) != FCC("LIST")) {
365                         temp= GET_FCC (movie->fp);
366                         if (temp<0 || ftell(movie->fp) > movie->size) {
367                                 DEBUG("incorrect size in header or error in AVI\n");
368                             return AVI_ERROR_FORMAT;                            
369                         }
370                         fseek(movie->fp, temp, SEEK_CUR);                       
371                 }
372                 
373                 fseek(movie->fp, -4L, SEEK_CUR);                
374         }
375
376         while (1) {
377                 temp = GET_FCC (movie->fp);
378                 size = GET_FCC (movie->fp);
379
380                 if (size == 0)
381                         break;
382
383                 if (temp == FCC("LIST")) {
384                         if (GET_FCC(movie->fp) == FCC ("movi"))
385                                 break;
386                         else
387                                 fseek (movie->fp, size-4, SEEK_CUR);
388                 } else {
389                         fseek (movie->fp, size, SEEK_CUR);
390                 }
391                 if (ftell(movie->fp) > movie->size) {
392                         DEBUG("incorrect size in header or error in AVI\n");
393                     return AVI_ERROR_FORMAT;
394                 }
395         }
396
397         movie->movi_offset = ftell (movie->fp);
398         movie->read_offset = movie->movi_offset;
399         if (AVI_DEBUG) printf ("movi_offset is %d\n", movie->movi_offset);
400         
401         /* Read in the index if the file has one, otherwise create one */
402         if (movie->header->Flags & AVIF_HASINDEX) {
403                 fseek(movie->fp, size-4, SEEK_CUR);
404
405                 if (GET_FCC(movie->fp) != FCC("idx1")) {
406                         DEBUG("bad index informatio\n");
407                         return AVI_ERROR_FORMAT;
408                 }
409
410                 movie->index_entries = GET_FCC (movie->fp)/sizeof(AviIndexEntry);
411                 if (movie->index_entries == 0) {
412                         DEBUG("no index entries\n");
413                         return AVI_ERROR_FORMAT;
414                 }
415
416                 movie->entries = (AviIndexEntry *) MEM_mallocN (movie->index_entries * sizeof(AviIndexEntry),"movieentries");
417 /*              movie->entries = (AviIndexEntry *) */
418 /*                      malloc (movie->index_entries * sizeof(AviIndexEntry)); */
419
420                 for (temp=0; temp < movie->index_entries; temp++) {
421                         movie->entries[temp].ChunkId = GET_FCC (movie->fp);
422                         movie->entries[temp].Flags = GET_FCC (movie->fp);
423                         movie->entries[temp].Offset = GET_FCC (movie->fp);
424                         movie->entries[temp].Size = GET_FCC (movie->fp);
425                         
426                         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);
427                 }
428
429 /* Some AVI's have offset entries in absolute coordinates
430  * instead of an offset from the movie beginning... this is...
431  * wacky, but we need to handle it. The wacky offset always
432  * starts at movi_offset it seems... so we'll check that.
433  * Note the the offset needs an extra 4 bytes for some 
434  * undetermined reason */
435  
436                 if (movie->entries[0].Offset == movie->movi_offset)
437                         movie->read_offset= 4;
438         }
439
440         DEBUG("movie succesfully opened\n");
441         return AVI_ERROR_NONE;
442 }
443
444 void *AVI_read_frame (AviMovie *movie, AviFormat format, int frame, int stream) {
445         int cur_frame=-1, i=0, temp;
446         void *buffer;
447
448         /* Retrieve the record number of the desired frame in the index */
449
450         while (cur_frame < frame && i < movie->index_entries) {
451                 if (fcc_is_data (movie->entries[i].ChunkId) &&
452                     fcc_get_stream (movie->entries[i].ChunkId) == stream)
453                         cur_frame++;
454
455                 i++;
456         }
457
458         if (cur_frame != frame) return NULL;
459         
460         fseek (movie->fp, movie->read_offset + movie->entries[i-1].Offset, SEEK_SET);
461
462         temp = GET_FCC(movie->fp);
463         buffer = MEM_mallocN (temp,"readbuffer");
464 /*      buffer = malloc(temp); */
465
466         if (fread (buffer, 1, temp, movie->fp) != temp) {
467                 MEM_freeN(buffer);
468 /*              free(buffer); */
469
470                 return NULL;
471         }
472         
473         buffer = avi_format_convert (movie, stream, buffer, movie->streams[stream].format, format, &temp);
474
475         return buffer;
476 }
477
478 AviError AVI_close (AviMovie *movie) {
479         int i;
480
481         fclose (movie->fp);
482
483         for (i=0; i < movie->header->Streams; i++) {
484                 if (movie->streams[i].sf != NULL)
485                         MEM_freeN (movie->streams[i].sf);
486         }
487
488         if (movie->header != NULL)
489                 MEM_freeN (movie->header);
490         if (movie->streams!= NULL)
491                 MEM_freeN (movie->streams);
492         if (movie->entries != NULL)
493                 MEM_freeN (movie->entries);
494         if (movie->offset_table != NULL)
495                 MEM_freeN (movie->offset_table);
496
497         return AVI_ERROR_NONE;
498 }
499
500 AviError AVI_open_compress (char *name, AviMovie *movie, int streams, ...) {
501         va_list ap;
502         AviList list;
503         AviChunk chunk;
504         int i;
505         int header_pos1, header_pos2;
506         int stream_pos1, stream_pos2;
507
508         movie->type = AVI_MOVIE_WRITE;
509         movie->fp = fopen (name, "wb");
510
511         movie->index_entries = 0;
512
513         if (movie->fp == NULL)
514                 return AVI_ERROR_OPEN;
515
516         movie->offset_table = (long *) MEM_mallocN ((1+streams*2) * sizeof (long),"offsettable");
517 /*      movie->offset_table = (long *) malloc ((1+streams*2) * sizeof (long)); */
518         
519         for (i=0; i < 1 + streams*2; i++)
520                 movie->offset_table[i] = -1L;
521
522         movie->entries = NULL;
523
524         movie->header = (AviMainHeader *) MEM_mallocN (sizeof(AviMainHeader),"movieheader");
525 /*      movie->header = (AviMainHeader *) malloc (sizeof(AviMainHeader)); */
526
527         movie->header->fcc = FCC("avih");
528         movie->header->size = 56;
529         movie->header->MicroSecPerFrame = 66667;
530         movie->header->MaxBytesPerSec = 0; 
531         movie->header->PaddingGranularity = 0; 
532         movie->header->Flags = AVIF_HASINDEX | AVIF_MUSTUSEINDEX;
533         movie->header->TotalFrames = 0;
534         movie->header->InitialFrames = 0;
535         movie->header->Streams = streams;
536         movie->header->SuggestedBufferSize = 0;
537         movie->header->Width = 0;
538         movie->header->Height = 0;
539         movie->header->Reserved[0] = 0;
540         movie->header->Reserved[1] = 0;
541         movie->header->Reserved[2] = 0;
542         movie->header->Reserved[3] = 0;
543
544         movie->streams = (AviStreamRec *) MEM_mallocN (sizeof(AviStreamRec) * movie->header->Streams,"moviestreams");
545 /*      movie->streams = (AviStreamRec *) malloc (sizeof(AviStreamRec) * movie->header->Streams); */
546
547         va_start (ap, streams);
548
549         for (i=0; i < movie->header->Streams; i++) {
550                 movie->streams[i].format = va_arg(ap, AviFormat);
551
552                 movie->streams[i].sh.fcc = FCC ("strh");
553                 movie->streams[i].sh.size = 56;
554                 movie->streams[i].sh.Type = avi_get_format_type (movie->streams[i].format);
555                 if (movie->streams[i].sh.Type == 0)
556                         return AVI_ERROR_FORMAT;
557
558                 movie->streams[i].sh.Handler = avi_get_format_fcc (movie->streams[i].format);
559                 if (movie->streams[i].sh.Handler == 0)
560                         return AVI_ERROR_FORMAT;
561
562                 movie->streams[i].sh.Flags = 0;
563                 movie->streams[i].sh.Priority = 0;
564                 movie->streams[i].sh.Language = 0;
565                 movie->streams[i].sh.InitialFrames = 0;
566                 movie->streams[i].sh.Scale = 66667;
567                 movie->streams[i].sh.Rate = 1000000;
568                 movie->streams[i].sh.Start = 0;
569                 movie->streams[i].sh.Length = 0;
570                 movie->streams[i].sh.SuggestedBufferSize = 0;
571                 movie->streams[i].sh.Quality = 10000;
572                 movie->streams[i].sh.SampleSize = 0;
573                 movie->streams[i].sh.left = 0;
574                 movie->streams[i].sh.top = 0;
575                 movie->streams[i].sh.right = 0;
576                 movie->streams[i].sh.bottom = 0;
577
578                 if (movie->streams[i].sh.Type == FCC("vids")) { 
579                         if (movie->streams[i].format == AVI_FORMAT_MJPEG) {
580                                 movie->streams[i].sf = MEM_mallocN (sizeof(AviBitmapInfoHeader) 
581                                                                                 + sizeof(AviMJPEGUnknown),"moviestreamformatL");
582 /*                              movie->streams[i].sf = malloc (sizeof(AviBitmapInfoHeader)  */
583 /*                                                                                         + sizeof(AviMJPEGUnknown)); */
584                                 movie->streams[i].sf_size = sizeof(AviBitmapInfoHeader) + sizeof(AviMJPEGUnknown);
585                         } else {
586                                 movie->streams[i].sf = MEM_mallocN (sizeof(AviBitmapInfoHeader),  "moviestreamformatS");
587 /*                              movie->streams[i].sf = malloc (sizeof(AviBitmapInfoHeader)); */
588                                 movie->streams[i].sf_size = sizeof(AviBitmapInfoHeader);
589                         }
590                         ((AviBitmapInfoHeader *) movie->streams[i].sf)->fcc = FCC ("strf");
591                         ((AviBitmapInfoHeader *) movie->streams[i].sf)->size = movie->streams[i].sf_size - 8;
592                         ((AviBitmapInfoHeader *) movie->streams[i].sf)->Size = movie->streams[i].sf_size - 8;
593                         ((AviBitmapInfoHeader *) movie->streams[i].sf)->Width = 0;
594                         ((AviBitmapInfoHeader *) movie->streams[i].sf)->Height = 0;
595                         ((AviBitmapInfoHeader *) movie->streams[i].sf)->Planes = 1;
596                         ((AviBitmapInfoHeader *) movie->streams[i].sf)->BitCount = 24;
597                         ((AviBitmapInfoHeader *) movie->streams[i].sf)->Compression = avi_get_format_compression (movie->streams[i].format);
598                         ((AviBitmapInfoHeader *) movie->streams[i].sf)->SizeImage = 0;
599                         ((AviBitmapInfoHeader *) movie->streams[i].sf)->XPelsPerMeter = 0;
600                         ((AviBitmapInfoHeader *) movie->streams[i].sf)->YPelsPerMeter = 0;
601                         ((AviBitmapInfoHeader *) movie->streams[i].sf)->ClrUsed = 0;
602                         ((AviBitmapInfoHeader *) movie->streams[i].sf)->ClrImportant = 0;
603
604                         if (movie->streams[i].format == AVI_FORMAT_MJPEG) {
605                                 AviMJPEGUnknown *tmp;
606                                 
607                                 tmp = (AviMJPEGUnknown *) ((char*) movie->streams[i].sf +sizeof(AviBitmapInfoHeader));
608                                 
609                                 tmp->a = 44;
610                                 tmp->b = 24;
611                                 tmp->c = 0;
612                                 tmp->d = 2;
613                                 tmp->e = 8;
614                                 tmp->f = 2;
615                                 tmp->g = 1;
616                         }
617                 } else if (movie->streams[i].sh.Type == FCC("auds")) {
618                         ;
619                 }
620         }
621
622         list.fcc = FCC("RIFF");
623         list.size = 0;
624         list.ids = FCC("AVI ");
625
626         awrite (movie, &list, 1, sizeof(AviList), movie->fp, AVI_LIST);
627
628         list.fcc = FCC("LIST");
629         list.size = 0;
630         list.ids = FCC("hdrl");
631
632         awrite (movie, &list, 1, sizeof(AviList), movie->fp, AVI_LIST);
633
634         header_pos1 = ftell(movie->fp);
635
636         movie->offset_table[0] = ftell(movie->fp);
637
638         awrite (movie, movie->header, 1, sizeof(AviMainHeader), movie->fp, AVI_MAINH);
639
640         for (i=0; i < movie->header->Streams; i++) {
641                 list.fcc = FCC("LIST");
642                 list.size = 0;
643                 list.ids = FCC("strl");
644
645                 awrite (movie, &list, 1, sizeof(AviList), movie->fp, AVI_LIST);
646
647                 stream_pos1 = ftell(movie->fp);
648
649                 movie->offset_table[1+i*2] = ftell(movie->fp);
650                 awrite (movie, &movie->streams[i].sh, 1, sizeof(AviStreamHeader), movie->fp, AVI_STREAMH);
651
652                 movie->offset_table[1+i*2+1] = ftell(movie->fp);
653                 awrite (movie, movie->streams[i].sf, 1, movie->streams[i].sf_size, movie->fp, AVI_BITMAPH);
654
655                 stream_pos2 = ftell(movie->fp);
656
657                 fseek (movie->fp, stream_pos1-8, SEEK_SET);
658
659                 PUT_FCCN((stream_pos2-stream_pos1+4L), movie->fp);
660
661                 fseek (movie->fp, stream_pos2, SEEK_SET);
662         }
663
664         if (ftell(movie->fp) < 2024 - 8) {
665                 chunk.fcc = FCC("JUNK");
666                 chunk.size = 2024-8-ftell(movie->fp);
667
668                 awrite (movie, &chunk, 1, sizeof(AviChunk), movie->fp, AVI_CHUNK);
669
670                 for (i=0; i < chunk.size; i++)
671                         putc(0, movie->fp);
672         }
673
674         header_pos2 = ftell(movie->fp);
675
676         list.fcc = FCC("LIST");
677         list.size = 0;
678         list.ids = FCC("movi");
679
680         awrite (movie, &list, 1, sizeof(AviList), movie->fp, AVI_LIST);
681
682         movie->movi_offset = ftell(movie->fp)-8L;
683
684         fseek (movie->fp, AVI_HDRL_SOFF, SEEK_SET);
685
686         PUT_FCCN((header_pos2-header_pos1+4L), movie->fp);
687
688         return AVI_ERROR_NONE;
689 }
690
691 AviError AVI_write_frame (AviMovie *movie, int frame_num, ...) {
692         AviList list;
693         AviChunk chunk;
694         AviIndexEntry *temp;
695         va_list ap;
696         int stream;
697         long rec_off;
698         AviFormat format;
699         void *buffer;
700         int size;
701
702         if (frame_num < 0)
703                 return AVI_ERROR_OPTION;
704
705         /* Allocate the new memory for the index entry */
706
707         if (frame_num+1 > movie->index_entries) {
708                 temp = (AviIndexEntry *) MEM_mallocN ((frame_num+1) * 
709                     (movie->header->Streams+1) * sizeof(AviIndexEntry),"newidxentry");
710                 if (movie->entries != NULL) {
711                         memcpy (temp, movie->entries, movie->index_entries * (movie->header->Streams+1)
712                             * sizeof(AviIndexEntry));
713                         MEM_freeN (movie->entries);
714                 }
715
716                 movie->entries = temp;
717                 movie->index_entries = frame_num+1;
718         }
719
720         /* Slap a new record entry onto the end of the file */
721
722         fseek (movie->fp, 0L, SEEK_END);
723
724         list.fcc = FCC("LIST");
725         list.size = 0;
726         list.ids = FCC("rec ");
727
728         awrite (movie, &list, 1, sizeof(AviList), movie->fp, AVI_LIST);
729
730         rec_off = ftell (movie->fp)-8L;
731
732         /* Write a frame for every stream */
733
734         va_start (ap, frame_num);
735
736         for (stream=0; stream < movie->header->Streams; stream++) {
737                 unsigned int tbuf=0;
738                 
739                 format = va_arg (ap, AviFormat);
740                 buffer = va_arg (ap, void*);
741                 size = va_arg (ap, int);
742
743                 /* Convert the buffer into the output format */
744                 buffer = avi_format_convert (movie, stream, buffer, format, movie->streams[stream].format, &size);
745
746                 /* Write the header info for this data chunk */
747
748                 fseek (movie->fp, 0L, SEEK_END);
749
750                 chunk.fcc = avi_get_data_id (format, stream);
751                 chunk.size = size;
752                 
753                 if (size%4) chunk.size += 4 - size%4;
754                 
755                 awrite (movie, &chunk, 1, sizeof(AviChunk), movie->fp, AVI_CHUNK);
756
757                 /* Write the index entry for this data chunk */
758
759                 movie->entries[frame_num * (movie->header->Streams+1) + stream + 1].ChunkId = chunk.fcc;
760                 movie->entries[frame_num * (movie->header->Streams+1) + stream + 1].Flags = AVIIF_KEYFRAME;
761                 movie->entries[frame_num * (movie->header->Streams+1) + stream + 1].Offset = ftell(movie->fp)-12L-movie->movi_offset;
762                 movie->entries[frame_num * (movie->header->Streams+1) + stream + 1].Size = chunk.size;
763
764                 /* Write the chunk */
765                 awrite (movie, buffer, 1, size, movie->fp, AVI_RAW);
766                 MEM_freeN (buffer);
767
768                 if (size%4) awrite (movie, &tbuf, 1, 4-size%4, movie->fp, AVI_RAW);
769
770                 /* Update the stream headers length field */
771                 movie->streams[stream].sh.Length++;
772                 fseek (movie->fp, movie->offset_table[1+stream*2], SEEK_SET);
773                 awrite (movie, &movie->streams[stream].sh, 1, sizeof(AviStreamHeader), movie->fp, AVI_STREAMH);
774         }
775         va_end (ap);
776
777         /* Record the entry for the new record */
778
779         fseek (movie->fp, 0L, SEEK_END);
780
781         movie->entries[frame_num * (movie->header->Streams+1)].ChunkId = FCC("rec ");
782         movie->entries[frame_num * (movie->header->Streams+1)].Flags = AVIIF_LIST;
783         movie->entries[frame_num * (movie->header->Streams+1)].Offset = rec_off-8L-movie->movi_offset;
784         movie->entries[frame_num * (movie->header->Streams+1)].Size = ftell(movie->fp)-(rec_off+4L);
785
786         /* Update the record size */
787         fseek (movie->fp, rec_off, SEEK_SET);
788         PUT_FCCN (movie->entries[frame_num * (movie->header->Streams+1)].Size, movie->fp);
789
790         /* Update the main header information in the file */
791         movie->header->TotalFrames++;
792         fseek (movie->fp, movie->offset_table[0], SEEK_SET);
793         awrite (movie, movie->header, 1, sizeof(AviMainHeader), movie->fp, AVI_MAINH);
794
795         return AVI_ERROR_NONE;
796 }
797
798 AviError AVI_close_compress (AviMovie *movie) {
799         int temp, movi_size, i;
800
801         fseek (movie->fp, 0L, SEEK_END);
802         movi_size = ftell (movie->fp);
803
804         PUT_FCC ("idx1", movie->fp);
805         PUT_FCCN ((movie->index_entries*(movie->header->Streams+1)*16), movie->fp);
806
807         for (temp=0; temp < movie->index_entries*(movie->header->Streams+1); temp++)
808                 awrite (movie, &movie->entries[temp], 1, sizeof(AviIndexEntry), movie->fp, AVI_INDEXE);
809
810         temp = ftell (movie->fp);
811
812         fseek (movie->fp, AVI_RIFF_SOFF, SEEK_SET);
813
814         PUT_FCCN((temp-8L), movie->fp);
815
816         fseek (movie->fp, movie->movi_offset, SEEK_SET);
817
818         PUT_FCCN((movi_size-(movie->movi_offset+4L)),movie->fp);
819
820         fclose (movie->fp);
821
822         for (i=0; i < movie->header->Streams; i++) {
823                 if (movie->streams[i].sf != NULL)
824                         MEM_freeN (movie->streams[i].sf);
825         }
826         if (movie->header != NULL)
827                 MEM_freeN (movie->header);
828         if (movie->entries != NULL)
829                 MEM_freeN (movie->entries);
830         if (movie->streams != NULL)
831                 MEM_freeN (movie->streams);
832         if (movie->offset_table != NULL)
833                 MEM_freeN (movie->offset_table);
834         return AVI_ERROR_NONE;
835 }