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