bd7d8572a42c09e332f127ae3c38fb584fe30ede
[swftools.git] / avi2swf / avi2swf.cc
1 /* avi2swf.cc
2    Convert avi movie files into swf.
3
4    Part of the swftools package.
5    
6    Copyright (c) 2001 Matthias Kramm <kramm@quiss.org>
7
8    This file is distributed under the GPL, see file COPYING for details */
9
10 #include <stdio.h>
11 #include <fcntl.h>
12 extern "C" {
13 #include "../lib/rfxswf.h"
14 #include "../lib/args.h"
15 }
16 #undef HAVE_CONFIG_H
17 #include <avifile/version.h>
18 #if (AVIFILE_MAJOR_VERSION == 0) && (AVIFILE_MINOR_VERSION>=6) 
19    #include <avifile.h>
20    #include <aviplay.h>
21    #include <fourcc.h>
22    #include <creators.h>
23    #include <StreamInfo.h>
24    #define VERSION6
25 #else
26    #include <avifile.h>
27    #include <aviplay.h>
28    #include <aviutil.h>
29    #define Width width
30    #define Height height
31    #define Data data
32    #define Bpp bpp
33 #endif
34
35 /*
36 statistics: (for now)
37    37   bytes per shape (rectangle)
38    8-12 bytes per placeobject
39    4    bytes per removeobject2
40    696+ bytes per definejpeg2 (576 jpegtables)
41    576  bytes per jpegtables
42    122+ bytes per definejpeg
43
44    blocks*140 = minimal bytes per frames
45    5000/140   = maximal blocks with framerate 5000
46
47    2    bytes per showframe
48 */
49
50 static int cache_size=38; //in frames
51
52 static char * filename = 0;
53 static char * outputfilename = "output.swf";
54 static unsigned int firstframe = 0;
55 static unsigned int lastframe = 0x7fffffff;
56
57 static int jpeg_quality = 20;
58
59 static char zlib = 0;
60 static double scale = 1.0;
61 static int flip = 0;
62
63 #ifndef ST_DEFINEBITSJPEG
64 #define ST_DEFINEBITSJPEG       6 
65 #endif
66   
67 int filesize = 0;
68
69 struct options_t options[] =
70 {
71  {"v","verbose"},
72  {"o","output"},
73  {"n","num"},
74  {"p","flip"},
75  {"s","start"},
76  {"d","scale"},
77  {"z","zlib"},
78  {"V","version"},
79  {0,0}
80 };
81
82 int args_callback_option(char*name,char*val)
83 {
84     if(!strcmp(name, "V")) {
85         printf("avi2swf - part of %s %s\n", PACKAGE, VERSION);
86         exit(0);
87     } 
88     else if(!strcmp(name, "o")) {
89         outputfilename = val;
90         return 1;
91     }
92     else if(!strcmp(name, "n")) {
93         lastframe = atoi(val);
94         return 1;
95     }
96     else if(!strcmp(name, "s")) {
97         firstframe = atoi(val);
98         return 1;
99     }
100     else if(!strcmp(name, "p")) {
101         flip = 1;
102         return 0;
103     }
104     else if(!strcmp(name, "d")) {
105         scale = atoi(val)/100.0;
106         if(scale>1.0 || scale<=0) {
107             fprintf(stderr, "Scale must be in the range 1-100!\n");
108             exit(1);
109         }
110         return 1;
111     }
112     else if(!strcmp(name, "z")) {
113         zlib = 1;
114         return 0;
115     }
116     fprintf(stderr, "Unknown option: -%s\n", name);
117     exit(1);
118 }
119 int args_callback_longoption(char*name,char*val)
120 {
121     return args_long2shortoption(options, name, val);
122 }
123 void args_callback_usage(char*name)
124 {    
125     printf("\nUsage: %s file.avi\n", name);
126     printf("\t-h , --help\t\t Print help and exit\n");
127     printf("\t-o , --output filename\t Specify output filename\n"); 
128     printf("\t-n , --num frames\t Number of frames to encode\n");
129     printf("\t-s , --start frame\t First frame to encode\n");
130     printf("\t-d , --scale factor\t Scale to factor percent\n");
131     printf("\t-p , --flip\t\t Turn movie upside down\n");
132     printf("\t-V , --version\t\t Print program version and exit\n");
133     exit(0);
134 }
135 int args_callback_command(char*name,char*val)
136 {
137     if(filename) {
138         fprintf(stderr, "Only one file allowed. You supplied at least two. (%s and %s)\n",
139                  filename, name);
140     }
141     filename = name;
142     return 0;
143 }
144
145 /* id allocation/deallocation */
146 char idtab[65536];
147 unsigned int idtab_pos=1;
148 int get_free_id()
149 {
150     while(idtab[idtab_pos] || !idtab_pos)
151         idtab_pos++;
152     idtab[idtab_pos]=1;
153     return idtab_pos;
154 }
155 void free_id(int id)
156 {
157     idtab[id] = 0;
158 }
159
160 void makeshape(int file, int id, int gfxid, int width, int height)
161 {
162     TAG*tag;
163     RGBA rgb;
164     MATRIX m;
165     SHAPE*s;
166     SRECT r;
167     int lines = 0;
168     int ls,fs;
169     tag = swf_InsertTag(NULL, ST_DEFINESHAPE);
170     swf_ShapeNew(&s);
171     rgb.b = rgb.g = rgb.r = 0xff;
172     if(lines)
173         ls = swf_ShapeAddLineStyle(s,20,&rgb);  
174     swf_GetMatrix(NULL,&m);
175     m.sx = 20*65536;
176     m.sy = 20*65536;
177
178     fs = swf_ShapeAddBitmapFillStyle(s,&m,gfxid,0);
179     swf_SetU16(tag,id);   // ID   
180     r.xmin = 0;
181     r.ymin = 0;
182     r.xmax = width*20;
183     r.ymax = height*20;
184     swf_SetRect(tag,&r);
185
186     swf_SetShapeStyles(tag,s);
187     swf_ShapeCountBits(s,NULL,NULL);
188     swf_SetShapeBits(tag,s);
189
190     swf_ShapeSetAll(tag,s,0,0,lines?ls:0,fs,0);
191
192     swf_ShapeSetLine(tag,s,width*20,0);
193     swf_ShapeSetLine(tag,s,0,height*20);
194     swf_ShapeSetLine(tag,s,-width*20,0);
195     swf_ShapeSetLine(tag,s,0,-height*20);
196     swf_ShapeSetEnd(tag);
197     filesize += swf_WriteTag(file, tag);
198     swf_DeleteTag(tag);
199     swf_ShapeFree(s);
200 }
201
202 void setshape(int file,int id,int depth,int x,int y,CXFORM*cx)
203 {
204     TAG*tag;
205     MATRIX m;
206     m.sx = 0x10000; m.sy = 0x10000;
207     m.r0 = 0; m.r1 = 0;
208     m.tx = x*20; 
209     m.ty = y*20;
210     if(cx && !((cx->a0!=256)||(cx->r0!=256)||(cx->g0!=256)||(cx->b0!=256)
211                 ||(cx->a1|cx->r1|cx->g1|cx->b1))) cx = 0;
212     tag = swf_InsertTag(NULL,ST_PLACEOBJECT2);
213       swf_ObjectPlace(tag,id,depth,&m,cx,0);
214     filesize += swf_WriteTag(file, tag);
215     swf_DeleteTag(tag);
216 }
217
218
219 int xblocksize;
220 int yblocksize;
221 struct GfxBlock {
222 //    static int xblocksize;
223 //    static int yblocksize;
224     U8*data;
225     int len;
226 };
227
228 int width=0;
229 int height=0;
230
231 int xblocks;
232 int yblocks;
233
234 U8* blockbuffer = 0;
235    
236 class GfxBlockCache {
237
238     GfxBlock*list;
239     char*expire; //0=block's free
240     int*ids;
241     int size;
242     int pos;
243     int hits;
244     int misses;
245
246     public:
247
248     GfxBlockCache(int file) 
249     {
250         list=0;
251         size = xblocks*yblocks*cache_size;
252         printf("initializing cache (%d entries)\n", size);
253         list = new GfxBlock[size];
254         expire = new char[size];
255         ids = new int[size];
256         memset(expire,0,size);
257         memset(list,0,sizeof(GfxBlock)*size);
258         memset(ids,0,sizeof(int)*size);
259         pos = 0;
260         hits =0;
261         misses =0;
262     }
263     void insert(GfxBlock*block, int gfxid)
264     {
265         int oldpos = pos;
266         while(++pos!=oldpos)
267         {
268             if(pos==size) pos=0;
269             if(!expire[pos])
270                 break;
271         }
272         if(pos==oldpos) {
273             // cache full- don't insert item
274             return;
275         }
276         if(list[pos].data) {
277             free(list[pos].data);
278             list[pos].data = 0;
279             //TODO: free this in the SWF, also
280         }
281         list[pos].data=(U8*)malloc(block->len);
282         memcpy(list[pos].data,block->data,block->len);
283         list[pos].len = block->len;
284         expire[pos] = cache_size;
285         ids[pos] = gfxid;
286     }
287     int find(GfxBlock*block, CXFORM*cxform)
288     {
289         //TODO: do least square regression here to derive cxform
290         int s;
291         int bestsum=-1;
292         int bestid;
293         float best;
294         for(s=0;s<size;s++)
295         if(expire[s])
296         {
297             int t = (block->len);
298             U8*ptr1 = block->data;
299             U8*ptr2 = list[s].data;
300             int sum2 = 0;
301             // notice: we treat r,g,b as equal here.
302             do {
303                 int a = (*ptr1++)-(*ptr2++);
304                 sum2 += a*a;
305             } while(--t);
306             if(bestsum < 0 || bestsum > sum2) {
307                 bestid = s;
308                 bestsum = sum2;
309             }
310         }
311         if(bestsum<0) {
312             misses++;
313             return -1;
314         }
315         best = bestsum/block->len;
316
317         if(best > 64.0) {
318             misses++;
319             return -1;
320         } 
321         expire[bestid]= cache_size;
322         hits++;
323         cxform->a0 = 256;
324         cxform->r0 = 256;
325         cxform->g0 = 256;
326         cxform->b0 = 256;
327         cxform->a1 = 0;
328         cxform->r1 = 0;
329         cxform->g1 = 0;
330         cxform->b1 = 0;
331         return ids[bestid];
332     }
333     void newframe()
334     {
335         int t;
336         for(t=0;t<size;t++)
337             if(expire[t])
338                 expire[t]--;
339
340     }
341     ~GfxBlockCache()
342     {
343         int t;
344         printf("destroying cache...\n");
345         if(hits+misses) {
346             printf("hits:%d (%02d%%)\n", hits, hits*100/(hits+misses));
347             printf("misses:%d (%02d%%)\n", misses, misses*100/(hits+misses));
348         }
349         for(t=0;t<size;t++)
350             if(expire[t] && list[t].data)
351                 free(list[t].data);
352         free(list);
353         free(expire);
354         free(ids);
355     }
356 } * cache = 0;
357
358 class GfxBlockEncoder {
359     int sizex;
360     int sizey;
361     int posx;
362     int posy;
363     int basedepth;
364     int depth[3];
365     public:
366     void init(int depth, int posx,int posy, int sizex, int sizey) 
367     {
368         this->basedepth = depth;
369         this->posx = posx;
370         this->posy = posy;
371         this->sizex = sizex;
372         this->sizey = sizey;
373         this->depth[0] = this->depth[1] = this->depth[2] = -1;
374     }
375     void clear(int file)
376     {
377         /* clear everything in the block */
378         int t;
379         for(t=0;t<3;t++)
380         if(depth[t]>=0)
381         {
382             TAG*tag;
383             tag = swf_InsertTag(NULL, ST_REMOVEOBJECT2);
384             swf_SetU16(tag, basedepth+t); //depth
385             filesize += swf_WriteTag(file, tag);
386             swf_DeleteTag(tag);
387             depth[t] = -1;
388         }
389     }
390     void writeiframe(int file, GfxBlock*block)
391     {
392         clear(file);
393
394         int gfxid = get_free_id();
395         int shapeid = get_free_id();
396
397         //memset(data,0,sizex*sizey*3);
398         TAG*tag = swf_InsertTag(NULL, ST_DEFINEBITS);
399         JPEGBITS * jb = swf_SetJPEGBitsStart(tag,sizex,sizey,jpeg_quality);
400         tag->len = 0; //bad hack
401         swf_SetU16(tag, gfxid);
402         int y;
403         for(y=0;y<sizey;y++)
404             swf_SetJPEGBitsLine(jb,&block->data[y*sizex*3]);
405         swf_SetJPEGBitsFinish(jb);
406         filesize += swf_WriteTag(file, tag);
407         swf_DeleteTag(tag);
408
409         cache->insert(block, shapeid);
410
411         makeshape(file, shapeid, gfxid, sizex, sizey);
412         setshape(file, shapeid, basedepth+1, posx, posy, 0);
413         depth[1] = shapeid;
414     }
415     void writereference(int file, int shapeid, CXFORM*form)
416     {
417         if(depth[1]!=shapeid)
418         {
419             clear(file);
420             setshape(file, shapeid, basedepth+1, posx, posy, form);
421             depth[1] = shapeid;
422         }
423     }
424     void compress(int file, GfxBlock*block)
425     {   
426         CXFORM form;
427         int id = cache->find(block, &form);
428         if(id<0)
429             writeiframe(file, block);
430         else {
431             writereference(file, id, &form);
432         }
433     }
434 } *blocks = 0;
435
436 void initdisplay(int file)
437 {
438     if(blockbuffer)
439         free(blockbuffer);
440     if(blocks) {
441         int t;
442         for(t=0;t<xblocks;t++)
443             blocks[t].clear(file);
444         free(blocks);
445     }
446     if(cache)
447         delete cache;
448     xblocksize = (width/3)&~7;
449     yblocksize = (height/2)&~7;
450     xblocks = width/xblocksize;
451     yblocks = height/yblocksize;
452     printf("%dx%d blocks of size %dx%d\n", xblocks,yblocks, xblocksize, yblocksize);
453     printf("cutting lower %d lines, right %d columns\n", 
454             height-yblocks*yblocksize, width-xblocks*xblocksize);
455     blocks = new GfxBlockEncoder[xblocks*yblocks];
456     blockbuffer = new U8[xblocksize*yblocksize*4]; //should be 3
457     cache = new GfxBlockCache(file);
458     int t;
459     for(t=0;t<xblocks*yblocks;t++) {
460         blocks[t].init(t*64,
461                        (t%xblocks)*xblocksize,
462                        (t/xblocks)*yblocksize,
463                        xblocksize, yblocksize);
464     }
465
466     TAG*tag = swf_InsertTag(NULL, ST_JPEGTABLES);
467     JPEGBITS * jpeg = swf_SetJPEGBitsStart(tag, xblocksize, yblocksize, jpeg_quality);
468     filesize += swf_WriteTag(file, tag);
469     swf_DeleteTag(tag);
470     free(jpeg);
471 }
472
473 void destroydisplay(int file)
474 {
475     delete cache;
476     free(blocks);
477     free(blockbuffer);
478 }
479
480 SWF swf;
481 TAG*tag;
482
483 class SoundReader
484 {
485
486     short int* sound_buffer;
487     int mp3_block_size;
488     int write_pos;
489     int read_pos;
490     IAviReadStream* astream;
491     void readBlock()
492     {
493         unsigned samples_read, bytes_read;
494         int ret;
495         short int tmpbuf[4096];
496         ret = astream->ReadFrames(tmpbuf, 4096*sizeof(short int),
497                 4096, samples_read, bytes_read);
498         if(ret<0) {
499             printf("couldn't read %d samples\n", mp3_block_size);
500             exit(1);
501         }
502         int t;
503         samples_read = bytes_read/sizeof(short int);
504         for(t=0;t<samples_read/2;t++) {
505             sound_buffer[write_pos+t] = tmpbuf[t*2];
506         }
507         write_pos += samples_read/2;
508
509         if(write_pos >= mp3_block_size*8)
510         {
511             if(write_pos > mp3_block_size*8)
512                 memcpy(&sound_buffer[0],&sound_buffer[mp3_block_size*8],write_pos - mp3_block_size*8);
513             write_pos %= (mp3_block_size*8);
514         }
515     }
516     public:
517
518     SoundReader(IAviReadStream*astream)
519     {
520         this->astream = astream;
521         this->write_pos = 0;
522         this->read_pos = 0;
523         this->mp3_block_size = 2304;
524         this->sound_buffer = new short int[mp3_block_size*16];
525     }
526     ~SoundReader()
527     {
528         delete sound_buffer;
529     }
530     int available()
531     {
532         if(read_pos<=write_pos)
533             return write_pos-read_pos;
534         else
535             return (write_pos+mp3_block_size*8)-read_pos;
536     }
537     short int* readFrame()
538     {
539         int tmp;
540         while(available()<mp3_block_size) {
541             readBlock();
542         }
543         tmp = read_pos;
544         read_pos += mp3_block_size;
545         read_pos %= mp3_block_size*8;
546         return &sound_buffer[tmp];
547     }
548 };
549
550
551 static int mp3_block_size = 2304;
552
553 static int do_video = 1;
554 static int do_audio = 1;
555
556 int main (int argc,char ** argv)
557
558   int file;
559   IAviReadFile* player;
560   IAviReadStream* astream;
561   IAviReadStream* vstream;
562   SRECT r;
563   double samplesperframe;
564   int samplerate;
565   int samplefix;
566   double fps;
567   int oldwidth;
568   int oldheight;
569   double reziscale;
570
571   processargs(argc, argv);
572   lastframe += firstframe;
573   if(!filename)
574       exit(0);
575
576   memset(idtab, 0, sizeof(idtab));
577
578   player = CreateIAviReadFile(filename);    
579   astream = player->GetStream(0, AviStream::Audio);
580   vstream = player->GetStream(0, AviStream::Video);
581   if(!vstream)
582       do_video = 0;
583   if(!astream)
584       do_audio = 0;
585 #ifndef VERSION6
586   MainAVIHeader head;
587   int dwMicroSecPerFrame = 0;
588   player->GetFileHeader(&head);
589   printf("fps: %d\n", 1000000/head.dwMicroSecPerFrame);
590   printf("frames: %d\n", head.dwTotalFrames);
591   printf("streams: %d\n", head.dwStreams);
592   printf("width: %d\n", head.dwWidth);
593   printf("height: %d\n", head.dwHeight);
594   printf("sound: %u samples (%f seconds)\n", astream->GetEndPos(),
595           astream->GetEndTime());
596   oldwidth = head.dwWidth;
597   oldheight = head.dwHeight;
598   dwMicroSecPerFrame = head.dwMicroSecPerFrame;
599   samplesperframe = astream->GetEndPos()/astream->GetEndTime()*head.dwMicroSecPerFrame/1000000;
600   samplerate = (int)(astream->GetEndPos()/astream->GetEndTime());
601   fps = 1000000.0/dwMicroSecPerFrame;
602 #else
603   StreamInfo*audioinfo;
604   StreamInfo*videoinfo;
605   if(do_video)
606   {
607     videoinfo = vstream->GetStreamInfo();
608     oldwidth = videoinfo->GetVideoWidth();
609     oldheight = videoinfo->GetVideoHeight();
610     fps = (double)(videoinfo->GetFps());
611     delete(videoinfo);
612   }
613   if(do_audio)
614   {
615     audioinfo = astream->GetStreamInfo();
616     samplerate = audioinfo->GetAudioSamplesPerSec();
617     samplesperframe = audioinfo->GetAudioSamplesPerSec()/videoinfo->GetFps();
618     delete(audioinfo);
619   }
620 #endif
621   width = (int)(oldwidth*scale);
622   height = (int)(oldheight*scale);
623   reziscale = 1/scale;
624
625   vstream -> StartStreaming();
626   if(do_audio)
627   {
628     astream -> StartStreaming();
629     printf("%f framerate\n", fps);
630     printf("%f samples/frame\n", samplesperframe);
631     printf("%d samplerate\n", samplerate);
632   }
633
634   if(zlib)
635     file = open("__tmp__.swf", O_WRONLY|O_CREAT|O_TRUNC, 0644);
636   else
637     file = open(outputfilename, O_WRONLY|O_CREAT|O_TRUNC, 0644);
638   
639   memset(&swf, 0, sizeof(swf));
640   swf.frameRate = (int)(fps*256);
641   swf.fileVersion = 4;
642   swf.fileSize = 0x0fffffff;
643   swf.frameCount = lastframe - firstframe;
644   r.xmin = 0;
645   r.ymin = 0;
646   r.xmax = width*20;
647   r.ymax = height*20;
648   swf.movieSize = r;
649
650   filesize += swf_WriteHeader(file, &swf);
651
652   tag = swf_InsertTag(NULL, ST_SETBACKGROUNDCOLOR);
653   swf_SetU8(tag,0); //black
654   swf_SetU8(tag,0);
655   swf_SetU8(tag,0);
656   filesize += swf_WriteTag(file, tag);
657   swf_DeleteTag(tag);
658
659   tag = swf_InsertTag(NULL, ST_SOUNDSTREAMHEAD2);
660   swf_SetSoundStreamHead(tag, (int)samplesperframe/4);
661   filesize += swf_WriteTag(file, tag);
662   swf_DeleteTag(tag);
663
664   int frame = 0;
665   initdisplay(file);
666
667   double movie_sound_pos = 0;
668   int mp3_sound_pos = 0;
669
670   if(do_audio)
671   {
672       WAVEFORMATEX wave;
673       astream->GetAudioFormatInfo(&wave,0);
674
675       printf("nChannels:%d\n", wave.nChannels);
676       printf("nSamplesPerSec:%d\n", wave.nSamplesPerSec);
677       printf("nAvgBytesPerSec:%d\n", wave.nAvgBytesPerSec);
678       printf("nBlockAlign:%d\n", wave.nBlockAlign);
679       printf("wBitsPerSample:%d\n", wave.wBitsPerSample);
680       printf("cbSize:%d\n", wave.cbSize);
681   }
682
683   SoundReader* sound = new SoundReader(astream);
684
685   while(1) {
686     if(vstream->ReadFrame()<0) {
687         printf("\n");
688         break;
689     }
690
691     if(frame < firstframe)
692     {
693         movie_sound_pos += samplesperframe;
694         if(do_audio)
695         while(mp3_sound_pos<movie_sound_pos) {
696             short int* samples = sound->readFrame();
697             mp3_sound_pos += mp3_block_size;
698         }
699         printf("\rskipping frame %d",frame);
700         fflush(stdout);
701         frame++;
702         if(frame == firstframe)
703             printf("\n");
704         continue;
705     }
706     
707     printf("\rconvert frame %d",frame);
708     fflush(stdout);
709
710     // audio
711     movie_sound_pos += samplesperframe;
712
713     int first=1;
714     if(do_audio)
715     while(mp3_sound_pos<movie_sound_pos) {
716         // rawplay -s 44100 -f s16_le -c 2 samples.test 
717         short int* samples = sound->readFrame();
718         int s;
719         int c=0;
720         if(first) { //first run
721               tag = swf_InsertTag(NULL, ST_SOUNDSTREAMBLOCK);
722               swf_SetSoundStreamBlock(tag, samples, 0, 1);
723         } else {
724               swf_SetSoundStreamBlock(tag, samples, 0, 0);
725         }
726         
727         mp3_sound_pos += mp3_block_size;
728
729         if(mp3_sound_pos>=movie_sound_pos) { // last run
730             filesize += swf_WriteTag(file, tag);
731             swf_DeleteTag(tag);
732         }
733         first = 0;
734     }
735  
736     // video
737
738     CImage*img = vstream->GetFrame();
739     img->ToRGB();
740     U8*data = img->Data();
741     int bpp = img->Bpp();
742     int x,y;
743     int xx,yy;
744     int fs,ls;
745     SHAPE*s;
746     MATRIX m;
747     SRECT r;
748     RGBA rgb;
749
750     /* some movies have changing dimensions */
751     if(img->Width() != oldwidth ||
752        img->Height() != oldheight) {
753         printf("\n");
754         oldwidth = img->Width();
755         oldheight = img->Height();
756         width = (int)(oldwidth*scale);
757         height = (int)(oldheight*scale);
758         initdisplay(file);
759     }
760
761     for(yy=0;yy<yblocks;yy++)
762     for(xx=0;xx<xblocks;xx++) 
763     {
764         int x,y;
765         for(y=0;y<yblocksize;y++) {
766             /* some avifile versions flip the image some don't. Maybe this is
767                even movie dependent. We just let the user decide which side's up. */
768             U8*mydata;
769             if(flip)
770                 mydata = img->At(oldheight-(int)((yy*yblocksize+y)*reziscale));
771             else
772                 mydata = img->At((int)((yy*yblocksize+y)*reziscale));
773
774             for(x=0;x<xblocksize;x++) {
775                 blockbuffer[(y*xblocksize+x)*3+2] = mydata[((int)(((xx*xblocksize+x)*reziscale)))*3+0];
776                 blockbuffer[(y*xblocksize+x)*3+1] = mydata[((int)(((xx*xblocksize+x)*reziscale)))*3+1];
777                 blockbuffer[(y*xblocksize+x)*3+0] = mydata[((int)(((xx*xblocksize+x)*reziscale)))*3+2];
778             }
779         }
780         GfxBlock b;
781         b.data = blockbuffer;
782         b.len = xblocksize*yblocksize*3;
783         blocks[yy*xblocks+xx].compress(file, &b);
784     }
785
786     tag = swf_InsertTag(NULL, ST_SHOWFRAME);
787     filesize += swf_WriteTag(file, tag);
788     swf_DeleteTag(tag);
789
790     cache->newframe();
791
792     frame++;
793     if(frame == lastframe)
794         break;
795   }
796   delete sound;
797   printf("\n");
798   destroydisplay(file);
799
800   tag = swf_InsertTag(NULL, ST_END);
801   filesize += swf_WriteTag(file, tag);
802   swf_DeleteTag(tag);
803
804   close(file);
805
806   FILE*fi;
807   if(zlib)
808     fi=fopen("tmp.swf", "r+");
809   else
810     fi=fopen(outputfilename, "r+");
811
812   if(fi)
813   {
814        fseek(fi,4,SEEK_SET);
815        unsigned char f;
816        f = filesize      ;fwrite(&f,1,1,fi);
817        f = filesize >> 8 ;fwrite(&f,1,1,fi);
818        f = filesize >> 16;fwrite(&f,1,1,fi);
819        f = filesize >> 24;fwrite(&f,1,1,fi);
820        fclose(fi);
821   }
822
823   if(zlib) {
824       char buffer[1024];
825       snprintf(buffer, 1024, "swfcombine -dz __tmp__.swf -o %s", outputfilename);
826       printf("%s\n", buffer);
827       system(buffer);
828       sprintf(buffer, "rm __tmp__.swf");
829       printf("%s\n", buffer);
830       system(buffer);
831   }
832   
833   return 0;
834 }
835