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