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