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