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