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