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