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