refined character handling
[swftools.git] / avi2swf / v2swf.c
1 /*  v2swf.c 
2     part of swftools
3
4     Copyright (C) 2003 Matthias Kramm <kramm@quiss.org>
5
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
19
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <stdarg.h>
23 #include "v2swf.h"
24 #include "../lib/rfxswf.h"
25 #include "../lib/q.h"
26
27 typedef struct _v2swf_internal_t
28 {
29     TAG*tag;
30
31     int filesize;
32     int headersize;
33     int frames;
34     
35     int myframes;
36
37     writer_t out;
38     writer_t out2;
39
40     ringbuffer_t r;
41     videoreader_t* video;
42     double video_fps;
43
44     int width;
45     int height;
46
47     int video_eof;
48     int audio_eof;
49
50     unsigned char* vrbuffer;
51     unsigned char* buffer;
52     unsigned char* lastbitmap;
53
54     int id;
55     int lastid;
56
57     int quality;
58     int blockdiff;
59     int keyframe_interval;
60     int diffmode;
61
62     float framerate;
63     float fpsratio;
64     float fpspos;
65
66     int bitrate;
67     int samplerate;
68
69     int finished;
70     int keyframe;
71     int showframe;
72
73     int skipframes;
74
75     float samplepos;
76     float framesamplepos;
77     int samplewritepos;
78     double soundframepos;
79     int soundstreamhead;
80     int seek;
81
82     int numframes;
83
84     double audio_fix;
85     int fixheader;
86     int prescale;
87
88     int scale;
89
90     int add_cut;
91     
92     int domotion;
93
94     int head_done;
95
96     int version;
97
98     VIDEOSTREAM stream;
99
100 } v2swf_internal_t;
101
102 static int verbose = 0;
103 static int filelog = 0;
104
105 static void msg(char*format, ...)
106 {
107     char buf[1024];
108     int l;
109     va_list arglist;
110     if(!verbose)
111         return;
112     va_start(arglist, format);
113     vsprintf(buf, format, arglist);
114     va_end(arglist);
115     l = strlen(buf);
116     while(l && buf[l-1]=='\n') {
117         buf[l-1] = 0;
118         l--;
119     }
120     if(filelog)
121     {
122         FILE*fi = fopen("debug.log", "ab+");
123         fprintf(fi, "(v2swf) %s\n", buf);
124         fflush(fi);
125         fclose(fi);
126     }
127
128     printf("(v2swf) %s\n", buf);
129     fflush(stdout);
130 }
131
132 extern int swf_mp3_in_samplerate;
133 extern int swf_mp3_out_samplerate;
134 extern int swf_mp3_channels;
135 extern int swf_mp3_bitrate;
136
137
138 static void writeShape(v2swf_internal_t*i, int id, int gfxid, int width, int height)
139 {
140     RGBA rgb;
141     MATRIX m;
142     SHAPE*shape;
143     SRECT r;
144     int lines = 0;
145     int ls,fs;
146     swf_ResetTag(i->tag, ST_DEFINESHAPE);
147     swf_ShapeNew(&shape);
148     rgb.b = rgb.g = rgb.r = 0xff;
149     if(lines)
150         ls = swf_ShapeAddLineStyle(shape,20,&rgb);  
151     swf_GetMatrix(NULL,&m);
152     m.sx = 20*65536;
153     m.sy = 20*65536;
154
155     fs = swf_ShapeAddBitmapFillStyle(shape,&m,gfxid,0);
156     swf_SetU16(i->tag,id);   // ID   
157     r.xmin = 0;
158     r.ymin = 0;
159     r.xmax = width*20;
160     r.ymax = height*20;
161     swf_SetRect(i->tag,&r);
162
163     swf_SetShapeStyles(i->tag,shape);
164     swf_ShapeCountBits(shape,NULL,NULL);
165     swf_SetShapeBits(i->tag,shape);
166
167     swf_ShapeSetAll(i->tag,shape,0,0,lines?ls:0,fs,0);
168
169     swf_ShapeSetLine(i->tag,shape,width*20,0);
170     swf_ShapeSetLine(i->tag,shape,0,height*20);
171     swf_ShapeSetLine(i->tag,shape,-width*20,0);
172     swf_ShapeSetLine(i->tag,shape,0,-height*20);
173     swf_ShapeSetEnd(i->tag);
174     i->filesize += swf_WriteTag2(&i->out, i->tag);
175     swf_ShapeFree(shape);
176 }
177
178 /* returns 0 on partial read */
179 static int getSamples(videoreader_t*video, S16*data, int len, double speedup)
180 {
181     double pos = 0;
182     double ratio = (double) video->samplerate * speedup / swf_mp3_in_samplerate;
183     int rlen = (int)(len * ratio);
184     int t;
185     S16 tmp[576*32];
186     int r = /*resampled len */ rlen * 
187                   /* s16_le */ 2 * 
188                                video->channels;
189     int l = 0;
190     memset(tmp, 0, sizeof(tmp));
191     if(r>0)
192         l = videoreader_getsamples(video, tmp, r);
193     if(l <= 0) {
194         return 0;
195     }
196     msg("%d samples read", l);
197
198     /* convert to 1 channel */
199     for(t=0;t<rlen;t++) {
200         int s;
201         int a=0;
202         for(s=0;s<video->channels;s++)
203             a += tmp[t*video->channels+s];
204         tmp[t] = a/video->channels;
205     }
206
207     /* down/up-sample to the desired input samplerate (swf_mp3_in_samplerate) */
208     for(t=0;t<len;t++) {
209         data[t] = tmp[(int)pos];
210         pos+=ratio;
211     }
212     return l == r;
213 }
214
215 static void writeAudioForOneFrame(v2swf_internal_t* i)
216 {
217     int blocksize; 
218     double blockspersecond;
219     double framespersecond, framesperblock, samplesperframe, samplesperblock;
220     int seek;
221     int s;
222     double speedup = i->audio_fix;
223     int num = 0;
224     int pos = 0;
225     S16 block1[576*4 * 2];
226
227     msg("writeAudioForOneFrame()");
228
229     if(i->audio_eof || i->video->channels<=0 || i->video->samplerate<=0) {
230         i->audio_eof = 1;
231         return; /* no sound in video */
232     }
233
234     blocksize = (i->samplerate > 22050) ? 1152 : 576;
235     blockspersecond = ((double)i->samplerate)/blocksize;
236
237     /* notice: for framerates greater than about 35, audio starts getting choppy. */
238     framespersecond = i->framerate;
239
240     framesperblock = framespersecond / blockspersecond;
241     samplesperframe = (blocksize * blockspersecond) / framespersecond; /* 11khz-samples per frame */
242     samplesperblock = samplesperframe * framesperblock;
243
244     msg("samplesperblock: %f", samplesperblock);
245
246     if(!i->soundstreamhead) {
247         swf_mp3_out_samplerate = i->samplerate;
248         /* The pre-processing of sound samples in getSamples(..) above
249            re-samples the sound to swf_mp3_in_samplerate. It is best to
250            simply make it the original samplerate:  */
251         swf_mp3_in_samplerate = i->video->samplerate;
252
253         /* first run - initialize */
254         swf_mp3_channels = 1;//i->video->channels;
255         swf_mp3_bitrate = i->bitrate;
256         swf_ResetTag(i->tag, ST_SOUNDSTREAMHEAD);
257         /* samplesperframe overrides the movie framerate: */
258         msg("swf_SetSoundStreamHead(): %08x %d", i->tag, samplesperframe);
259         swf_SetSoundStreamHead(i->tag, samplesperframe);
260         msg("swf_SetSoundStreamHead() done");
261         i->filesize += swf_WriteTag2(&i->out, i->tag);
262         i->soundstreamhead = 1;
263     }
264
265     /* for framerates greater than 19.14, every now and then a frame
266        hasn't a soundstreamblock. Determine whether this is the case.
267     */
268     msg("SOUND: frame:%d soundframepos:%f samplewritepos:%d samplepos:%f\n", i->frames, i->soundframepos, i->samplewritepos, i->samplepos);
269     if(i->frames < i->soundframepos) {
270         msg("SOUND: block skipped\n");
271         i->samplepos += samplesperframe;
272         return;
273     }
274
275     seek = i->seek;
276
277     //while(i->samplewritepos + num * blocksize < i->samplepos + blocksize) {
278     do {
279         i->samplewritepos += blocksize;
280         i->soundframepos += framesperblock;
281         num++;
282     }
283     while(i->samplewritepos < i->samplepos);
284
285     msg("SOUND: number of blocks: %d", num);
286
287     /* write num frames, max 1 block */
288     for(pos=0;pos<num;pos++) {
289         if(!getSamples(i->video, block1, blocksize * (double)swf_mp3_in_samplerate/swf_mp3_out_samplerate, speedup)) {
290             i->audio_eof = 1; i->video->samplerate = i->video->channels = 0; //end of soundtrack
291             /* fall through, this probably was a partial read. (We did, after all,
292                come to this point, so i->audio_eof must have been false so far) */
293         }
294         if(!pos) {
295             swf_ResetTag(i->tag, ST_SOUNDSTREAMBLOCK);
296             swf_SetSoundStreamBlock(i->tag, block1, seek, num);
297         } else {
298             swf_SetSoundStreamBlock(i->tag, block1, seek, 0);
299         }
300     }
301     i->filesize += swf_WriteTag2(&i->out, i->tag);
302
303     i->seek = blocksize - (i->samplewritepos - i->samplepos);
304     i->samplepos += samplesperframe;
305 }
306
307 static void writeShowFrame(v2swf_internal_t* i)
308 {
309     do {
310         writeAudioForOneFrame(i);
311         
312         swf_ResetTag(i->tag, ST_SHOWFRAME);
313         i->filesize += swf_WriteTag2(&i->out, i->tag);
314
315         i->fpspos -= 1.0;
316         i->frames ++;
317     }
318     while(i->fpspos >= 1.0);
319     i->showframe = 0;
320 }
321
322 static void writeShowTags(v2swf_internal_t* i, int shapeid, int bmid, int width, int height)
323 {
324     writeShape(i, shapeid, bmid, width, height);
325
326     swf_ResetTag(i->tag, ST_PLACEOBJECT2);
327     if(!i->prescale) {
328         MATRIX m;
329         swf_GetMatrix(0, &m);
330         m.sx = m.sy = i->scale;
331         swf_ObjectPlace(i->tag,shapeid,shapeid,&m,0,0);
332     } else {
333         swf_ObjectPlace(i->tag,shapeid,shapeid,0,0,0);
334     }
335     i->filesize += swf_WriteTag2(&i->out, i->tag);
336
337     i->showframe = 1;
338 }
339
340 static int wwrite(writer_t*w, void*data, int len)
341 {
342     v2swf_internal_t* i = (v2swf_internal_t*)w->internal;
343     ringbuffer_put(&i->r, data, len);
344     return len;
345 }
346
347 static void wfinish(writer_t*w)
348 {
349     v2swf_internal_t* i = (v2swf_internal_t*)w->internal;
350 }
351
352 static void writehead(v2swf_internal_t*i)
353 {
354     char header[]="FWS\6\0\0\0\4";
355     SWF swf;
356     int ret;
357     int id;
358    
359     header[3] = i->version;
360     if(i->version >= 6) { //MX
361         header[0] = 'C';
362
363         i->out2.write = wwrite;
364         i->out2.finish = wfinish;
365         i->out2.internal = i;
366         i->out2.type = 77;
367         i->out2.bitpos = 0;
368         i->out2.mybyte = 0;
369         i->out2.pos = 0;
370         writer_init_zlibdeflate(&i->out, &i->out2);
371     } else {
372         i->out.write = wwrite;
373         i->out.finish = wfinish;
374         i->out.internal = i;
375         i->out.type = 77;
376         i->out.bitpos = 0;
377         i->out.mybyte = 0;
378         i->out.pos = 0;
379         i->out2 = i->out;
380     }
381
382     if(i->prescale) {
383         i->width = (int)(i->video->width*(i->scale/65536.0));
384         i->height = (int)(i->video->height*(i->scale/65536.0));
385     } else {
386         i->width = i->video->width;
387         i->height = i->video->height;
388     }
389     if(!i->width)
390         i->width = 1;
391     if(!i->height)
392         i->height = 1;
393     i->buffer = (unsigned char*)malloc(i->width*i->height*4);
394     i->vrbuffer = (unsigned char*)malloc(i->video->width*i->video->height*4);
395
396     memset(&swf, 0, sizeof(SWF));
397     swf.fileVersion=i->version;
398     swf.fileSize = 0;
399     swf.frameCount = 65535;
400     swf.movieSize.xmax=i->width*20;
401     swf.movieSize.ymax=i->height*20;
402     swf.compressed = 8; /* 8 = compression done by caller (us) */
403     swf.frameRate = (int)(i->framerate*0x100);//25*0x100;
404
405     /* write the first 8 bytes to out */
406     i->out2.write(&i->out2, header, 8);
407
408     i->filesize += swf_WriteHeader2(&i->out, &swf);
409     i->headersize = i->filesize;
410     
411     i->tag = swf_InsertTag(NULL,  ST_SETBACKGROUNDCOLOR);
412     swf_SetU8(i->tag, 0); //black
413     swf_SetU8(i->tag, 0);
414     swf_SetU8(i->tag, 0);
415     i->filesize += swf_WriteTag2(&i->out, i->tag);
416 }
417
418 static void finish(v2swf_internal_t*i)
419 {
420     msg("finish(): i->finished=%d\n", i->finished);
421     if(!i->finished) {
422         msg("write endtag\n", i->finished);
423
424         if(i->add_cut) {
425             swf_ResetTag(i->tag, ST_SHOWFRAME);
426             i->filesize += swf_WriteTag2(&i->out, i->tag);
427
428             swf_ResetTag(i->tag, ST_REMOVEOBJECT2);
429             swf_SetU16(i->tag, 1); //depth
430             i->filesize += swf_WriteTag2(&i->out, i->tag);
431
432             swf_ResetTag(i->tag, ST_DOACTION);
433             swf_SetU16(i->tag, 0x0007);
434             i->filesize += swf_WriteTag2(&i->out, i->tag);
435         }
436
437         swf_ResetTag(i->tag, ST_END);
438         i->filesize += swf_WriteTag2(&i->out, i->tag);
439
440         i->out.finish(&i->out);
441
442         if(i->version>=6) {
443             swf_VideoStreamClear(&i->stream);
444         }
445         if(i->buffer)  {
446             free(i->buffer);i->buffer = 0;
447         }
448         if(i->vrbuffer)  {
449             free(i->vrbuffer);i->vrbuffer = 0;
450         }
451         if(i->lastbitmap)  {
452             free(i->lastbitmap);i->lastbitmap = 0;
453         }
454
455         /* FIXME: we shouldn't be doing this. the caller should */
456         msg("call videoreader_close(%08x)\n", i->video);
457         videoreader_close(i->video);
458
459         i->finished = 1;
460     }
461     msg("finishing done\n");
462 }
463 static void cleanup(v2swf_internal_t*i)
464 {
465     int t;
466     for(t=i->lastid;t<i->id;t++) {
467         if(!(t&1)) {
468             swf_ResetTag(i->tag, ST_REMOVEOBJECT2);
469             swf_SetU16(i->tag, t);
470             i->filesize += swf_WriteTag2(&i->out, i->tag);
471         }
472         swf_ResetTag(i->tag, ST_FREECHARACTER);
473         swf_SetU16(i->tag, t);
474         i->filesize += swf_WriteTag2(&i->out, i->tag);
475     }
476     i->lastid = i->id;
477 }
478
479 #define DIFFMODE_MAX 1 
480 #define DIFFMODE_MEAN 2
481 #define DIFFMODE_EXACT 3
482 #define DIFFMODE_QMEAN 4
483
484 static int blockdiff_max(U8*d1,U8*d2,int yadd, int maxdiff, int xl, int yl)
485 {
486     int x,y;
487     for(y=0;y<yl;y++) {
488         for(x=0;x<xl;x++) {
489             int rd = d1[1] - d2[1];
490             int gd = d1[2] - d2[2];
491             int bd = d1[3] - d2[3];
492             if(rd < 0) rd = -rd;
493             if(gd < 0) gd = -gd;
494             if(bd < 0) bd = -bd;
495             if(rd+gd+bd>maxdiff)
496                 return 1;
497
498             d1+=4; d2+=4;
499         }
500         d1 += yadd; d2 += yadd;
501     }
502     return 0;
503 }
504
505 static int blockdiff_mean(U8*d1,U8*d2,int yadd, int maxdiff, int xl, int yl)
506 {
507     int mean = 0;
508     int x,y;
509     for(y=0;y<yl;y++) {
510         for(x=0;x<xl;x++) {
511             int rd = d1[1] - d2[1];
512             int gd = d1[2] - d2[2];
513             int bd = d1[3] - d2[3];
514             if(rd < 0) rd = -rd;
515             if(gd < 0) gd = -gd;
516             if(bd < 0) bd = -bd;
517             mean += rd+gd+bd;
518
519             d1+=4; d2+=4;
520         }
521         d1 += yadd; d2 += yadd;
522     }
523     if(mean/(xl*yl) > maxdiff)
524         return 1;
525     return 0;
526 }
527
528 static int blockdiff_qmean(U8*d1,U8*d2,int yadd, int maxdiff, int xl, int yl)
529 {
530     int mean = 0;
531     int x,y;
532     for(y=0;y<yl;y++) {
533         for(x=0;x<xl;x++) {
534             int rd = d1[1] - d2[1];
535             int gd = d1[2] - d2[2];
536             int bd = d1[3] - d2[3];
537             int q;
538             if(rd < 0) rd = -rd;
539             if(gd < 0) gd = -gd;
540             if(bd < 0) bd = -bd;
541             q = rd+gd+bd;
542             mean += q*q;
543
544             d1+=4; d2+=4;
545         }
546         d1 += yadd; d2 += yadd;
547     }
548     if(mean/(xl*yl) > maxdiff*maxdiff)
549         return 1;
550     return 0;
551 }
552
553 static int blockdiff_exact(U8*d1,U8*d2,int yadd, int xl, int yl)
554 {
555     int x,y;
556     for(y=0;y<yl;y++) {
557         for(x=0;x<xl;x++) {
558             if((*(U32*)d1^*(U32*)d2)&0xffffff00) { //bits [RGB_] of [RGBA] differ
559                 return 1;
560             }
561             d1+=4; d2+=4;
562         }
563         d1 += yadd; d2 += yadd;
564     }
565     return 0;
566 }
567
568                 /*U32 r = (*(U32*)d1^-(U32*)d2)&0xffffff00;
569                 U32 g = ((r << 3) ^ r)&0x80808080;
570                 if(g) 
571                     goto differ;*/
572
573 static void checkInit(v2swf_internal_t*i)
574 {
575     if(!i->head_done) {
576         writehead(i);
577         if(i->version>=6) {
578             swf_ResetTag(i->tag,  ST_DEFINEVIDEOSTREAM);
579             swf_SetU16(i->tag, 99);
580             swf_SetVideoStreamDefine(i->tag, &i->stream, 65535, i->width, i->height);
581             i->filesize += swf_WriteTag2(&i->out, i->tag);
582             if(i->domotion) {
583                 i->stream.do_motion = 1;
584             }
585         }
586         i->head_done = 1;
587     }
588 }
589
590 static void scaleimage(v2swf_internal_t*i)
591 {
592     int x,y;
593     int xv,yv;
594     int xm = (i->video->width*65536)/i->width;
595     int ym = (i->video->height*65536)/i->height;
596     msg("scaling from %dx%d to %dx%d\n", 
597             i->video->width, i->video->height,
598             i->width, i->height
599             );
600
601     memset(i->buffer, 255, i->width*i->height*4);
602     for(y=0,yv=0;y<i->height;y++,yv+=ym) {
603         int*src = &((int*)i->vrbuffer)[(yv>>16)*i->video->width];
604         int*dest = &((int*)i->buffer)[y*i->width];
605         for(x=0,xv=0;x<i->width;x++,xv+=xm) {
606             dest[x] = src[xv>>16];
607         }
608     }
609     //memcpy(i->buffer, i->vrbuffer, i->width*i->height*4);
610 }
611
612 static int writeAudioOnly(v2swf_internal_t*i)
613 {
614     if(i->showframe) {
615         i->fpspos += i->fpsratio;
616         /* skip frames */
617         if(i->fpspos<1.0) {
618             return 0;
619         }
620         writeShowFrame(i);
621     }
622     i->showframe = 1;
623     return 1;
624 }
625
626 static int getframe(v2swf_internal_t*i)
627 {
628     if(!i->skipframes)
629         return videoreader_getimage(i->video, i->vrbuffer);
630     else {
631         int t;
632         for(t=0;t<i->skipframes;t++) {
633             int ret = videoreader_getimage(i->video, i->vrbuffer);
634             if(!ret)
635                 return 0;
636         }
637         return 1;
638     }
639 }
640
641 static int encodeoneframe(v2swf_internal_t*i)
642 {
643     videoreader_t*video = i->video;
644     int ret;
645
646     checkInit(i);
647
648     if(i->video_eof && i->audio_eof) {
649         if(!i->finished)
650             finish(i);
651         return 0;
652     }
653
654     if(!i->audio_eof && i->video_eof) {
655         return writeAudioOnly(i);
656     }
657
658     if(!getframe(i) || (i->numframes && i->frames==i->numframes)) 
659     {
660         i->video_eof = 1;
661         msg("videoreader returned eof\n");
662         if(i->audio_eof || (i->numframes && i->frames==i->numframes)) {
663             finish(i);
664             return 0;
665         } else {
666             return writeAudioOnly(i);
667         }
668     }
669
670     msg("encoding image for frame %d\n", i->frames);
671     if(i->showframe) {
672         i->fpspos += i->fpsratio;
673         /* skip frames */
674         if(i->fpspos<1.0) {
675             return 0;
676         }
677         writeShowFrame(i);
678     }
679     
680     scaleimage(i);
681
682     msg("version is %d\n", i->version);
683
684     if(i->version <= 4) {
685
686         int bmid = i->id++;
687         int shapeid = i->id++;
688         int width2 = i->width * 4;
689
690         if(i->id>=4) {
691             swf_ResetTag(i->tag, ST_REMOVEOBJECT2);
692             swf_SetU16(i->tag, i->id-3);
693             i->filesize += swf_WriteTag2(&i->out, i->tag);
694             swf_ResetTag(i->tag, ST_FREECHARACTER);
695             swf_SetU16(i->tag, i->id-4);
696             i->filesize += swf_WriteTag2(&i->out, i->tag);
697         }
698
699         swf_ResetTag(i->tag, ST_DEFINEBITSJPEG2);
700         swf_SetU16(i->tag, bmid);
701         swf_SetJPEGBits2(i->tag, i->width, i->height, (RGBA*)i->buffer, i->quality);
702         i->filesize += swf_WriteTag2(&i->out, i->tag);
703         
704         writeShowTags(i, shapeid, bmid, i->width, i->height);
705
706     } else if(i->version == 5) {
707         int width2 = i->width * 4;
708         int width8 = (i->width+7)/8;
709         int height8 = (i->height+7)/8;
710
711         /* the idea is here to only update those jpeg 8x8 blocks
712            which actually have changed. This means that we have to keep
713            the bitmap from the last frame for the comparison. */
714
715         (i->keyframe)--;
716         if(!i->lastbitmap || !i->keyframe) {
717             int t, bmid,shapeid;
718             cleanup(i);
719
720             if(!i->lastbitmap) {
721                 msg("Creating bitmap buffer for %dx%d (%dx%d), (%dx%d)\n", i->width, i->height, width2, i->height, width8, height8);
722                 i->lastbitmap = (U8*)malloc(width2*i->height);
723             }
724             memcpy(i->lastbitmap, i->buffer, width2*i->height);
725
726             i->keyframe = i->keyframe_interval;
727
728             bmid = i->id++;
729             shapeid = i->id++;
730             width2 = i->width * 4;
731             swf_ResetTag(i->tag, ST_DEFINEBITSJPEG2);
732             swf_SetU16(i->tag, bmid);
733             swf_SetJPEGBits2(i->tag, i->width, i->height, (RGBA*)i->buffer, i->quality);
734             i->filesize += swf_WriteTag2(&i->out, i->tag);
735            
736             writeShowTags(i, shapeid, bmid, i->width, i->height);
737             return 1;
738         } else {
739             /* The following looks so ugly because it's somewhat optimized. 
740                What it does is walk through all the 8x8 blocks, find those
741                which have changed too much and set all others to (R,G,B,A)=(0,0,0,0). 
742                It also set's alpha to 255 in those who haven't changed, and
743                copies them to lastbitmap.
744              */
745
746             int x8, y8;
747             //int maxdiff = ((100 - i->quality)*256)/100;
748             int maxdiff = i->blockdiff*3;
749             for(y8=0;y8<height8;y8++)
750             for(x8=0;x8<width8;x8++) {
751                 int x,y;
752                 int xl=8,yl=8;
753                 int yadd;
754                 U8*d1,*d1b,*d2,*d2b;
755                 if(x8*8+xl > i->width)
756                     xl = i->width - x8*8;
757                 if(y8*8+yl > i->height)
758                     yl = i->height - y8*8;
759                 d1 = &i->buffer[width2*y8*8+x8*8*4];
760                 d1b = d1;
761                 d2 = &i->lastbitmap[width2*y8*8+x8*8*4];
762                 d2b = d2;
763                 yadd = width2 - (xl*4);
764
765                 if(i->diffmode == DIFFMODE_MAX) {
766                     if(blockdiff_max(d1, d2, yadd, maxdiff, xl, yl))
767                         goto differ;
768                 } else if(i->diffmode == DIFFMODE_MEAN) {
769                     if(blockdiff_mean(d1, d2, yadd, maxdiff, xl, yl))
770                         goto differ;
771                 } else if(i->diffmode == DIFFMODE_EXACT) {
772                     if(blockdiff_exact(d1, d2, yadd, xl, yl))
773                         goto differ;
774                 } else if(i->diffmode == DIFFMODE_QMEAN) {
775                     if(blockdiff_qmean(d1, d2, yadd, maxdiff, xl, yl))
776                         goto differ;
777                 }
778
779                 for(y=0;y<yl;y++) {
780                     for(x=0;x<xl;x++) {
781                         *(U32*)d1b = 0;
782                         d1b+=4;
783                     }
784                     d1b += yadd;
785                 }
786                 continue;
787     differ:
788                 for(y=0;y<yl;y++) {
789                     for(x=0;x<xl;x++) {
790                         *(U32*)d2b = *(U32*)d1b;
791                         d1b[0] = 255;
792                         d1b+=4;d2b+=4;
793                     }
794                     d1b += yadd; d2b += yadd;
795                 }
796             }
797
798             /* ok, done. Now a) data is zeroed out in regions which haven't changed
799                              b) lastbitmap equals the bitmap we were called with
800                              c) data's alpha value is set to 255 in regions which did change */
801
802         }
803
804         {
805             int bmid = i->id++;
806             int shapeid = i->id++;
807
808             swf_ResetTag(i->tag, ST_DEFINEBITSJPEG3);
809             swf_SetU16(i->tag, bmid);
810             swf_SetJPEGBits3(i->tag, i->width, i->height, (RGBA*)i->buffer, i->quality);
811             i->filesize += swf_WriteTag2(&i->out, i->tag);
812
813             writeShowTags(i, shapeid, bmid, i->width, i->height);
814         }
815     } else {
816         int quant = 1+(30-(30*i->quality)/100);
817         SWFPLACEOBJECT obj;
818
819         swf_GetPlaceObject(0, &obj);
820         if(!i->prescale) {
821             obj.matrix.sx = obj.matrix.sy = i->scale;
822         }
823
824         if(i->stream.frame==0) {
825             obj.depth = 1;
826             obj.id = 99;
827         } else {
828             obj.move = 1;
829             obj.depth = 1;
830             obj.ratio = i->stream.frame;
831         }
832
833         swf_ResetTag(i->tag, ST_VIDEOFRAME);
834         swf_SetU16(i->tag, 99);
835         if(!(--i->keyframe)) {
836             msg("setting video I-frame, ratio=%d\n", i->stream.frame);
837             swf_SetVideoStreamIFrame(i->tag, &i->stream, (RGBA*)i->buffer, quant);
838             i->keyframe = i->keyframe_interval;
839         } else {
840             msg("setting video P-frame, ratio=%d\n", i->stream.frame);
841             swf_SetVideoStreamPFrame(i->tag, &i->stream, (RGBA*)i->buffer, quant);
842         }
843         i->filesize += swf_WriteTag2(&i->out, i->tag);
844
845         swf_ResetTag(i->tag, ST_PLACEOBJECT2);
846         swf_SetPlaceObject(i->tag,&obj);
847         i->filesize += swf_WriteTag2(&i->out, i->tag);
848         i->showframe = 1;
849     }
850     return 1;
851 }
852
853 static void init_fps(v2swf_internal_t*i)
854 {
855     int oldframerate = i->framerate;
856     i->framerate = i->video->fps / i->skipframes;
857     i->video_fps = ((int)(i->framerate*256))/256.0;
858     if(oldframerate)
859         msg("setting new framerate to %f\n", i->framerate);
860 }
861
862 int v2swf_init(v2swf_t*v2swf, videoreader_t * video)
863 {
864     int ret = 0;
865     int t=0;
866     v2swf_internal_t* i;
867     msg("v2swf_init()\n");
868     memset(v2swf, 0, sizeof(v2swf_t));
869     i = (v2swf_internal_t*)malloc(sizeof(v2swf_internal_t));
870     memset(i, 0, sizeof(v2swf_internal_t));
871     v2swf->internal = i;
872
873     ringbuffer_init(&i->r);
874
875     i->skipframes = 1;
876     i->framerate = 0;
877     i->video = video;
878     init_fps(i);
879
880     msg("video: %dx%d, fps %f\n", video->width, video->height, video->fps);
881
882     i->blockdiff = 64;
883     i->keyframe_interval = 8;
884     i->quality = 20;
885     i->scale = 65536;
886     i->add_cut = 1;
887     i->samplerate = 11025;
888     i->prescale = 0;
889     i->numframes= 0;
890     i->skipframes = 0;
891     i->head_done = 0;
892     i->diffmode = DIFFMODE_QMEAN;
893     i->audio_fix = 1.0;
894     i->fixheader = 0;
895     i->fpsratio = 1.00000000000;
896     i->fpspos = 0.0;
897     i->bitrate = 32;
898     i->version = 6;
899     i->buffer = 0;
900     i->lastbitmap = 0;
901     i->filesize = 8;
902     i->frames = 0;
903     i->id = 1;
904     i->lastid = 1;
905     i->keyframe = 1;
906     i->showframe = 0;
907
908     memset(&i->out, 0, sizeof(writer_t));
909     memset(&i->out2, 0, sizeof(writer_t));
910
911     return 0;
912 }
913 int v2swf_read(v2swf_t*v2swf, void*buffer, int len)
914 {
915     v2swf_internal_t* i;
916     int l;
917     msg("v2swf_read(%d)\n", len);
918     i = (v2swf_internal_t*)v2swf->internal;
919
920     while(!i->finished && i->r.available < len) {
921         if(!encodeoneframe(i)) {
922             break;
923         }
924     }
925     msg("v2swf_read() done: %d bytes available in ringbuffer\n", i->r.available);
926     l = ringbuffer_read(&i->r, buffer, len);
927
928     return l;
929 }
930 void v2swf_close(v2swf_t*v2swf)
931 {
932     v2swf_internal_t* i = (v2swf_internal_t*)v2swf->internal;
933     msg("close(): i->finished=%d\n", i->finished);
934
935     /* needed only if aborting: */
936     finish(i);
937
938     msg("freeing memory\n");
939     free(v2swf->internal);
940     memset(v2swf, 0, sizeof(v2swf_t));
941     msg("close() done\n");
942 }
943
944 static int mp3_bitrates[] =
945 { 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0};
946
947 void v2swf_setparameter(v2swf_t*v2swf, char*name, char*value)
948 {
949     v2swf_internal_t* i;
950
951     msg("set parameters %s to %s\n", name, value);
952
953     if(!strcmp(name, "verbose")) {
954         verbose = 1;
955         msg("set parameters %s to %s\n", name, value);
956         return;
957     }
958
959     if(!v2swf || !v2swf->internal) {
960         printf("error- couldn't set parameter %s: not initialized yet\n", name);fflush(stdout);
961         return;
962     }
963     i = (v2swf_internal_t*)v2swf->internal;
964
965     if(!strcmp(name, "flash_version")) {
966         i->version = atoi(value);
967     } else if(!strcmp(name, "audiosync")) {
968         i->audio_fix = (int)(atof(value));
969     } else if(!strcmp(name, "addcut")) {
970         i->add_cut = atoi(value);
971     } else if(!strcmp(name, "scale")) {
972         i->scale = (int)(atof(value)*65536);
973     } else if(!strcmp(name, "scale65536")) {
974         i->scale = atoi(value);
975     } else if(!strcmp(name, "quality")) {
976         i->quality = atoi(value);
977     } else if(!strcmp(name, "skipframes")) {
978         i->skipframes = atoi(value);
979         init_fps(i);
980     } else if(!strcmp(name, "numframes")) {
981         i->numframes = atoi(value);
982     } else if(!strcmp(name, "motioncompensation")) {
983         i->domotion = atoi(value);
984     } else if(!strcmp(name, "prescale")) {
985         i->prescale = atoi(value);
986     } else if(!strcmp(name, "blockdiff")) {
987         i->blockdiff = atoi(value);
988     } else if(!strcmp(name, "fixheader")) {
989         i->fixheader = atoi(value);
990     } else if(!strcmp(name, "samplerate")) {
991         i->samplerate = atoi(value);
992     } else if(!strcmp(name, "framerate")) {
993         i->framerate = atof(value);
994         i->fpsratio = i->framerate / i->video_fps;
995     }
996     else if(!strcmp(name, "mp3_bitrate")) {
997         int t=0,o;
998         i->bitrate = o = atoi(value);
999         if(i->bitrate>160)
1000             i->bitrate = 160;
1001         while(mp3_bitrates[t]) {
1002             if(i->bitrate <= mp3_bitrates[t]) {
1003                 i->bitrate = mp3_bitrates[t];
1004                 break;
1005             }
1006             t++;
1007         }
1008         msg("bitrate %d requested, setting to %d", o, i->bitrate);
1009     }
1010     else if(!strcmp(name, "blockdiff_mode")) {
1011         if(!strcmp(value, "max")) i->diffmode = DIFFMODE_MAX;
1012         else if(!strcmp(value, "mean")) i->diffmode = DIFFMODE_MEAN;
1013         else if(!strcmp(value, "qmean")) i->diffmode = DIFFMODE_QMEAN;
1014         else if(!strcmp(value, "exact")) i->diffmode = DIFFMODE_EXACT;
1015         else {
1016             printf("diffmode %s not recognized\n", value);
1017             printf("valid diffmodes are: %s\n", "max, mean, qmean, exact");
1018         }
1019     }
1020     else if(!strcmp(name, "keyframe_interval")
1021             || !strcmp(name, "keyframe")) {
1022         int k = atoi(value);if(k<=0) k=1;
1023         i->keyframe_interval = k;
1024     }
1025     else {
1026         printf("Setting encoder.%s not recognized!\n", name);fflush(stdout);
1027         return;
1028     }
1029 }
1030 void v2swf_backpatch(v2swf_t*v2swf, char*filename)
1031 {
1032     FILE* fi;
1033     unsigned char f;
1034     v2swf_internal_t* i = (v2swf_internal_t*)v2swf->internal;
1035     msg("v2swf_backpatch %s\n", filename);
1036     if(!i) {
1037         printf("call backpatch before close\n");fflush(stdout);
1038     }
1039     fi = fopen(filename, "rb+");
1040     if(!fi) {
1041         printf("can't open %s\n", filename);
1042         exit(1);
1043     }
1044     fseek(fi, 4, SEEK_SET);
1045     f = i->filesize      ;fwrite(&f,1,1,fi);
1046     f = i->filesize >> 8 ;fwrite(&f,1,1,fi);
1047     f = i->filesize >> 16;fwrite(&f,1,1,fi);
1048     f = i->filesize >> 24;fwrite(&f,1,1,fi);
1049     if(i->version<6) {
1050         /* no compression- we can backpatch the frames too */
1051         fseek(fi, i->headersize-2, SEEK_SET);
1052         f = i->frames        ;fwrite(&f,1,1,fi);
1053         f = i->frames >> 8   ;fwrite(&f,1,1,fi);
1054     }
1055     fclose(fi);
1056     if(i->fixheader) {
1057         SWF tmp;
1058         int fi;
1059         msg("v2swf_backpatch %s - fix header\n", filename);
1060         memset(&tmp, 0, sizeof(tmp));
1061         fi = open(filename, O_RDONLY|O_BINARY);
1062         if(fi>=0) {
1063             if(swf_ReadSWF(fi, &tmp)>=0) {
1064                 close(fi);
1065                 fi = open(filename, O_WRONLY|O_BINARY|O_TRUNC|O_CREAT, 0666);
1066                 if(fi>=0) {
1067                     swf_WriteSWC(fi, &tmp);
1068                     close(fi);
1069                     msg("v2swf_backpatch %s - fix header: success\n", filename);
1070                 }
1071             }
1072         }
1073     }
1074 }
1075
1076 void v2swf_setvideoparameter(videoreader_t*v, char*name, char*value)
1077 {
1078     msg("v2swf_setvideoparameter()");
1079     videoreader_setparameter(v, name, value);
1080 }