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