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