added path handling for language packs.
[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->samplerate * 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->samplerate<=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->samplerate;
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->samplerate = 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     msg("encoding image for frame %d\n", i->frames);
588     if(i->showframe) {
589         i->fpspos += i->fpsratio;
590         /* skip frames */
591         if(i->fpspos<1.0) {
592             return 0;
593         }
594         writeShowFrame(i);
595     }
596     
597     msg("scaling\n");
598
599     scaleimage(i);
600
601     msg("version is %d\n", i->version);
602
603     if(i->version <= 4) {
604
605         int bmid = i->id++;
606         int shapeid = i->id++;
607         int width2 = i->width * 4;
608
609         if(i->id>=4) {
610             swf_ResetTag(i->tag, ST_REMOVEOBJECT2);
611             swf_SetU16(i->tag, i->id-3);
612             i->filesize += swf_WriteTag2(&i->out, i->tag);
613             swf_ResetTag(i->tag, ST_FREECHARACTER);
614             swf_SetU16(i->tag, i->id-4);
615             i->filesize += swf_WriteTag2(&i->out, i->tag);
616         }
617
618         swf_ResetTag(i->tag, ST_DEFINEBITSJPEG2);
619         swf_SetU16(i->tag, bmid);
620         swf_SetJPEGBits2(i->tag, i->width, i->height, (RGBA*)i->buffer, i->quality);
621         i->filesize += swf_WriteTag2(&i->out, i->tag);
622         
623         writeShowTags(i, shapeid, bmid, i->width, i->height);
624
625     } else if(i->version == 5) {
626         int width2 = i->width * 4;
627         int width8 = (i->width+7)/8;
628         int height8 = (i->height+7)/8;
629
630         /* the idea is here to only update those jpeg 8x8 blocks
631            which actually have changed. This means that we have to keep
632            the bitmap from the last frame for the comparison. */
633
634         (i->keyframe)--;
635         if(!i->lastbitmap || !i->keyframe) {
636             int t, bmid,shapeid;
637             cleanup(i);
638
639             if(!i->lastbitmap) {
640                 msg("Creating bitmap buffer for %dx%d (%dx%d), (%dx%d)\n", i->width, i->height, width2, i->height, width8, height8);
641                 i->lastbitmap = (U8*)malloc(width2*i->height);
642             }
643             memcpy(i->lastbitmap, i->buffer, width2*i->height);
644
645             i->keyframe = i->keyframe_interval;
646
647             bmid = i->id++;
648             shapeid = i->id++;
649             width2 = i->width * 4;
650             swf_ResetTag(i->tag, ST_DEFINEBITSJPEG2);
651             swf_SetU16(i->tag, bmid);
652             swf_SetJPEGBits2(i->tag, i->width, i->height, (RGBA*)i->buffer, i->quality);
653             i->filesize += swf_WriteTag2(&i->out, i->tag);
654            
655             writeShowTags(i, shapeid, bmid, i->width, i->height);
656             return 1;
657         } else {
658             /* The following looks so ugly because it's somewhat optimized. 
659                What it does is walk through all the 8x8 blocks, find those
660                which have changed too much and set all others to (R,G,B,A)=(0,0,0,0). 
661                It also set's alpha to 255 in those who haven't changed, and
662                copies them to lastbitmap.
663              */
664
665             int x8, y8;
666             //int maxdiff = ((100 - i->quality)*256)/100;
667             int maxdiff = i->blockdiff*3;
668             for(y8=0;y8<height8;y8++)
669             for(x8=0;x8<width8;x8++) {
670                 int x,y;
671                 int xl=8,yl=8;
672                 int yadd;
673                 U8*d1,*d1b,*d2,*d2b;
674                 if(x8*8+xl > i->width)
675                     xl = i->width - x8*8;
676                 if(y8*8+yl > i->height)
677                     yl = i->height - y8*8;
678                 d1 = &i->buffer[width2*y8*8+x8*8*4];
679                 d1b = d1;
680                 d2 = &i->lastbitmap[width2*y8*8+x8*8*4];
681                 d2b = d2;
682                 yadd = width2 - (xl*4);
683
684                 if(i->diffmode == DIFFMODE_MAX) {
685                     if(blockdiff_max(d1, d2, yadd, maxdiff, xl, yl))
686                         goto differ;
687                 } else if(i->diffmode == DIFFMODE_MEAN) {
688                     if(blockdiff_mean(d1, d2, yadd, maxdiff, xl, yl))
689                         goto differ;
690                 } else if(i->diffmode == DIFFMODE_EXACT) {
691                     if(blockdiff_exact(d1, d2, yadd, xl, yl))
692                         goto differ;
693                 } else if(i->diffmode == DIFFMODE_QMEAN) {
694                     if(blockdiff_qmean(d1, d2, yadd, maxdiff, xl, yl))
695                         goto differ;
696                 }
697
698                 for(y=0;y<yl;y++) {
699                     for(x=0;x<xl;x++) {
700                         *(U32*)d1b = 0;
701                         d1b+=4;
702                     }
703                     d1b += yadd;
704                 }
705                 continue;
706     differ:
707                 for(y=0;y<yl;y++) {
708                     for(x=0;x<xl;x++) {
709                         *(U32*)d2b = *(U32*)d1b;
710                         d1b[0] = 255;
711                         d1b+=4;d2b+=4;
712                     }
713                     d1b += yadd; d2b += yadd;
714                 }
715             }
716
717             /* ok, done. Now a) data is zeroed out in regions which haven't changed
718                              b) lastbitmap equals the bitmap we were called with
719                              c) data's alpha value is set to 255 in regions which did change */
720
721         }
722
723         {
724             int bmid = i->id++;
725             int shapeid = i->id++;
726
727             swf_ResetTag(i->tag, ST_DEFINEBITSJPEG3);
728             swf_SetU16(i->tag, bmid);
729             swf_SetJPEGBits3(i->tag, i->width, i->height, (RGBA*)i->buffer, i->quality);
730             i->filesize += swf_WriteTag2(&i->out, i->tag);
731
732             writeShowTags(i, shapeid, bmid, i->width, i->height);
733         }
734     } else {
735         int quant = 1+(30-(30*i->quality)/100);
736         SWFPLACEOBJECT obj;
737
738         swf_GetPlaceObject(0, &obj);
739         if(!i->prescale) {
740             obj.matrix.sx = obj.matrix.sy = i->scale;
741         }
742
743         if(i->stream.frame==0) {
744             obj.depth = 1;
745             obj.id = 99;
746         } else {
747             obj.move = 1;
748             obj.depth = 1;
749             obj.ratio = i->stream.frame;
750         }
751
752         swf_ResetTag(i->tag, ST_VIDEOFRAME);
753         swf_SetU16(i->tag, 99);
754         if(!(--i->keyframe)) {
755             msg("setting video I-frame, ratio=%d\n", i->stream.frame);
756             swf_SetVideoStreamIFrame(i->tag, &i->stream, (RGBA*)i->buffer, quant);
757             i->keyframe = i->keyframe_interval;
758         } else {
759             msg("setting video P-frame, ratio=%d\n", i->stream.frame);
760             swf_SetVideoStreamPFrame(i->tag, &i->stream, (RGBA*)i->buffer, quant);
761         }
762         i->filesize += swf_WriteTag2(&i->out, i->tag);
763
764         swf_ResetTag(i->tag, ST_PLACEOBJECT2);
765         swf_SetPlaceObject(i->tag,&obj);
766         i->filesize += swf_WriteTag2(&i->out, i->tag);
767         i->showframe = 1;
768     }
769     return 1;
770 }
771
772 int v2swf_init(v2swf_t*v2swf, videoreader_t * video)
773 {
774     int ret = 0;
775     int t=0;
776     v2swf_internal_t* i;
777     msg("v2swf_init()\n");
778     memset(v2swf, 0, sizeof(v2swf_t));
779     i = (v2swf_internal_t*)malloc(sizeof(v2swf_internal_t));
780     memset(i, 0, sizeof(v2swf_internal_t));
781     v2swf->internal = i;
782
783     ringbuffer_init(&i->r);
784
785     msg("video: %dx%d, fps %f\n", video->width, video->height, video->fps);
786
787     i->video = video;
788     i->blockdiff = 64;
789     i->keyframe_interval = 8;
790     i->quality = 20;
791     i->scale = 65536;
792     i->samplerate = 11025;
793     i->prescale = 0;
794     i->head_done = 0;
795     i->diffmode = DIFFMODE_QMEAN;
796     i->audio_fix = 1.0;
797     i->fixheader = 0;
798     i->framerate = video->fps;
799     i->fpsratio = 1.00000000000;
800     i->fpspos = 0.0;
801     i->bitrate = 32;
802     i->version = 6;
803     i->buffer = 0;
804     i->lastbitmap = 0;
805     i->filesize = 8;
806     i->frames = 0;
807     i->id = 1;
808     i->lastid = 1;
809     i->keyframe = 1;
810     i->showframe = 0;
811
812     memset(&i->out, 0, sizeof(struct writer_t));
813     memset(&i->out2, 0, sizeof(struct writer_t));
814
815     return 0;
816 }
817 int v2swf_read(v2swf_t*v2swf, void*buffer, int len)
818 {
819     v2swf_internal_t* i;
820     int l;
821     msg("v2swf_read(%d)\n", len);
822     i = (v2swf_internal_t*)v2swf->internal;
823
824     while(!i->finished && i->r.available < len) {
825         if(!encodeoneframe(i)) {
826             break;
827         }
828     }
829     msg("v2swf_read() done: %d bytes available in ringbuffer\n", i->r.available);
830     l = ringbuffer_read(&i->r, buffer, len);
831
832     return l;
833 }
834 void v2swf_close(v2swf_t*v2swf)
835 {
836     v2swf_internal_t* i = (v2swf_internal_t*)v2swf->internal;
837     msg("close(): i->finished=%d\n", i->finished);
838
839     /* needed only if aborting: */
840     finish(i);
841
842     msg("freeing memory\n");
843     free(v2swf->internal);
844     memset(v2swf, 0, sizeof(v2swf_t));
845     msg("close() done\n");
846 }
847
848 static int mp3_bitrates[] =
849 { 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0};
850
851 void v2swf_setparameter(v2swf_t*v2swf, char*name, char*value)
852 {
853     v2swf_internal_t* i;
854
855     msg("set parameters %s to %s\n", name, value);
856
857     if(!strcmp(name, "verbose")) {
858         verbose = 1;
859         msg("set parameters %s to %s\n", name, value);
860         return;
861     }
862
863     if(!v2swf || !v2swf->internal) {
864         printf("error- couldn't set parameter %s: not initialized yet\n", name);fflush(stdout);
865         return;
866     }
867     i = (v2swf_internal_t*)v2swf->internal;
868
869     if(!strcmp(name, "flash_version")) {
870         i->version = atoi(value);
871     } else if(!strcmp(name, "audiosync")) {
872         i->audio_fix = (int)(atof(value));
873     } else if(!strcmp(name, "scale")) {
874         i->scale = (int)(atof(value)*65536);
875     } else if(!strcmp(name, "scale65536")) {
876         i->scale = atoi(value);
877     } else if(!strcmp(name, "quality")) {
878         i->quality = atoi(value);
879     } else if(!strcmp(name, "motioncompensation")) {
880         i->domotion = atoi(value);
881     } else if(!strcmp(name, "prescale")) {
882         i->prescale = atoi(value);
883     } else if(!strcmp(name, "blockdiff")) {
884         i->blockdiff = atoi(value);
885     } else if(!strcmp(name, "fixheader")) {
886         i->fixheader = atoi(value);
887     } else if(!strcmp(name, "samplerate")) {
888         i->samplerate = atoi(value);
889     } else if(!strcmp(name, "framerate")) {
890         i->framerate = atof(value);
891         i->fpsratio = i->framerate / i->video->fps;
892     }
893     else if(!strcmp(name, "mp3_bitrate")) {
894         int t=0,o;
895         i->bitrate = o = atoi(value);
896         if(i->bitrate>160)
897             i->bitrate = 160;
898         while(mp3_bitrates[t]) {
899             if(i->bitrate <= mp3_bitrates[t]) {
900                 i->bitrate = mp3_bitrates[t];
901                 break;
902             }
903             t++;
904         }
905         msg("bitrate %d requested, setting to %d", o, i->bitrate);
906     }
907     else if(!strcmp(name, "blockdiff_mode")) {
908         if(!strcmp(value, "max")) i->diffmode = DIFFMODE_MAX;
909         else if(!strcmp(value, "mean")) i->diffmode = DIFFMODE_MEAN;
910         else if(!strcmp(value, "qmean")) i->diffmode = DIFFMODE_QMEAN;
911         else if(!strcmp(value, "exact")) i->diffmode = DIFFMODE_EXACT;
912         else {
913             printf("diffmode %s not recognized\n", value);
914             printf("valid diffmodes are: %s\n", "max, mean, qmean, exact");
915         }
916     }
917     else if(!strcmp(name, "keyframe_interval")
918             || !strcmp(name, "keyframe")) {
919         int k = atoi(value);if(k<=0) k=1;
920         i->keyframe_interval = k;
921     }
922     else {
923         printf("Setting encoder.%s not recognized!\n", name);fflush(stdout);
924         return;
925     }
926 }
927 void v2swf_backpatch(v2swf_t*v2swf, char*filename)
928 {
929     FILE* fi;
930     unsigned char f;
931     v2swf_internal_t* i = (v2swf_internal_t*)v2swf->internal;
932     msg("v2swf_backpatch %s\n", filename);
933     if(!i) {
934         printf("call backpatch before close\n");fflush(stdout);
935     }
936     fi = fopen(filename, "rb+");
937     if(!fi) {
938         printf("can't open %s\n", filename);
939         exit(1);
940     }
941     fseek(fi, 4, SEEK_SET);
942     f = i->filesize      ;fwrite(&f,1,1,fi);
943     f = i->filesize >> 8 ;fwrite(&f,1,1,fi);
944     f = i->filesize >> 16;fwrite(&f,1,1,fi);
945     f = i->filesize >> 24;fwrite(&f,1,1,fi);
946     if(i->version<6) {
947         /* no compression- we can backpatch the frames too */
948         fseek(fi, i->headersize-2, SEEK_SET);
949         f = i->frames        ;fwrite(&f,1,1,fi);
950         f = i->frames >> 8   ;fwrite(&f,1,1,fi);
951     }
952     fclose(fi);
953     if(i->fixheader) {
954         SWF tmp;
955         int fi;
956         msg("v2swf_backpatch %s - fix header\n", filename);
957         memset(&tmp, 0, sizeof(tmp));
958         fi = open(filename, O_RDONLY|O_BINARY);
959         if(fi>=0) {
960             if(swf_ReadSWF(fi, &tmp)>=0) {
961                 close(fi);
962                 fi = open(filename, O_WRONLY|O_BINARY|O_TRUNC|O_CREAT, 0666);
963                 if(fi>=0) {
964                     swf_WriteSWC(fi, &tmp);
965                     close(fi);
966                     msg("v2swf_backpatch %s - fix header: success\n", filename);
967                 }
968             }
969         }
970     }
971 }
972
973 void v2swf_setvideoparameter(videoreader_t*v, char*name, char*value)
974 {
975     msg("v2swf_setvideoparameter()");
976     videoreader_setparameter(v, name, value);
977 }