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