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