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