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