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