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