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