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