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