initial revision.
authorkramm <kramm>
Tue, 6 May 2003 11:39:20 +0000 (11:39 +0000)
committerkramm <kramm>
Tue, 6 May 2003 11:39:20 +0000 (11:39 +0000)
avi2swf/v2swf.c [new file with mode: 0644]
avi2swf/v2swf.h [new file with mode: 0644]

diff --git a/avi2swf/v2swf.c b/avi2swf/v2swf.c
new file mode 100644 (file)
index 0000000..5720199
--- /dev/null
@@ -0,0 +1,993 @@
+/*  v2swf.c 
+    part of swftools
+
+    Copyright (C) 2003 Matthias Kramm <kramm@quiss.org>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include "v2swf.h"
+#include "../lib/rfxswf.h"
+#include "ringbuffer.c"
+
+typedef struct _v2swf_internal_t
+{
+    TAG*tag;
+
+    int filesize;
+    int headersize;
+    int frames;
+    
+    int myframes;
+
+    struct writer_t out;
+    struct writer_t out2;
+
+    ringbuffer_t r;
+    videoreader_t* video;
+
+    int width;
+    int height;
+
+    unsigned char* vrbuffer;
+    unsigned char* buffer;
+    unsigned char* lastbitmap;
+
+    int id;
+    int lastid;
+
+    int quality;
+    int blockdiff;
+    int keyframe_interval;
+    int diffmode;
+
+    float framerate;
+    float fpsratio;
+    float fpspos;
+
+    int bitrate;
+
+    int finished;
+    int keyframe;
+    int showframe;
+
+    float samplepos;
+    float framesamplepos;
+    int samplewritepos;
+    double soundframepos;
+    int soundstreamhead;
+    int seek;
+
+    double audio_fix;
+    int fixheader;
+    int prescale;
+
+    int scale;
+    
+    int domotion;
+
+    int head_done;
+
+    int version;
+
+    VIDEOSTREAM stream;
+
+} v2swf_internal_t;
+
+static int verbose = 0;
+static int filelog = 0;
+
+static void logf(char*format, ...)
+{
+    char buf[1024];
+    int l;
+    va_list arglist;
+    if(!verbose)
+       return;
+    va_start(arglist, format);
+    vsprintf(buf, format, arglist);
+    va_end(arglist);
+    l = strlen(buf);
+    while(l && buf[l-1]=='\n') {
+       buf[l-1] = 0;
+       l--;
+    }
+    if(filelog)
+    {
+       FILE*fi = fopen("debug.log", "ab+");
+       fprintf(fi, "(v2swf) %s\n", buf);
+       fflush(fi);
+       fclose(fi);
+    }
+
+    printf("(v2swf) %s\n", buf);
+    fflush(stdout);
+}
+
+static void writeShape(v2swf_internal_t*i, int id, int gfxid, int width, int height)
+{
+    RGBA rgb;
+    MATRIX m;
+    SHAPE*shape;
+    SRECT r;
+    int lines = 0;
+    int ls,fs;
+    swf_ResetTag(i->tag, ST_DEFINESHAPE);
+    swf_ShapeNew(&shape);
+    rgb.b = rgb.g = rgb.r = 0xff;
+    if(lines)
+       ls = swf_ShapeAddLineStyle(shape,20,&rgb);  
+    swf_GetMatrix(NULL,&m);
+    m.sx = 20*65536;
+    m.sy = 20*65536;
+
+    fs = swf_ShapeAddBitmapFillStyle(shape,&m,gfxid,0);
+    swf_SetU16(i->tag,id);   // ID   
+    r.xmin = 0;
+    r.ymin = 0;
+    r.xmax = width*20;
+    r.ymax = height*20;
+    swf_SetRect(i->tag,&r);
+
+    swf_SetShapeStyles(i->tag,shape);
+    swf_ShapeCountBits(shape,NULL,NULL);
+    swf_SetShapeBits(i->tag,shape);
+
+    swf_ShapeSetAll(i->tag,shape,0,0,lines?ls:0,fs,0);
+
+    swf_ShapeSetLine(i->tag,shape,width*20,0);
+    swf_ShapeSetLine(i->tag,shape,0,height*20);
+    swf_ShapeSetLine(i->tag,shape,-width*20,0);
+    swf_ShapeSetLine(i->tag,shape,0,-height*20);
+    swf_ShapeSetEnd(i->tag);
+    i->filesize += swf_WriteTag2(&i->out, i->tag);
+    swf_ShapeFree(shape);
+}
+
+static int getSamples(videoreader_t*video, S16*data, int len, double speedup)
+{
+    double pos = 0;
+    double ratio = video->rate * speedup / 44100.0;
+    int rlen = (int)(len * ratio);
+    int t;
+    S16 tmp[576*32];
+    int r = /*resampled len */ rlen * 
+                 /* s16_le */ 2 * 
+                              video->channels;
+    if(videoreader_getsamples(video, tmp, r) < r)
+       return 0;
+
+    /* convert to 1 channel */
+    for(t=0;t<rlen;t++) {
+       int s;
+       int a=0;
+       for(s=0;s<video->channels;s++)
+           a += tmp[t*video->channels+s];
+       tmp[t] = a/video->channels;
+    }
+
+    /* down/up-sample to 44khz */
+    for(t=0;t<len;t++) {
+       data[t] = tmp[(int)pos];
+       pos+=ratio;
+    }
+    return 1;
+}
+
+extern int swf_mp3_channels;
+extern int swf_mp3_bitrate;
+extern int swf_mp3_samplerate;
+static void writeAudioForOneFrame(v2swf_internal_t* i)
+{
+    int blocksize; 
+    double blockspersecond;
+    double framespersecond, framesperblock, samplesperframe, samplesperblock;
+    int seek;
+    int s;
+    double speedup = i->audio_fix;
+    int num = 0;
+    int pos = 0;
+    S16 block1[576*4 * 2];
+
+    logf("writeAudioForOneFrame()");
+
+    if(i->video->channels<=0 || i->video->rate<=0)
+       return; /* no sound in video */
+
+    blocksize = 576; /* 11khz samples per mp3 block */
+    blockspersecond = 11025.0/blocksize;
+
+    /* notice: for framerates greater than about 35, audio starts getting choppy. */
+    framespersecond = i->framerate;
+
+    framesperblock = framespersecond / blockspersecond;
+    samplesperframe = (blocksize * blockspersecond) / framespersecond; /* 11khz-samples per frame */
+    samplesperblock = samplesperframe * framesperblock;
+
+    logf("samplesperblock: %f", samplesperblock);
+
+    if(!i->soundstreamhead) {
+       /* first run - initialize */
+       swf_mp3_channels = 1;//i->video->channels;
+       swf_mp3_bitrate = i->bitrate;
+       swf_ResetTag(i->tag, ST_SOUNDSTREAMHEAD);
+       /* samplesperframe overrides the movie framerate: */
+       logf("swf_SetSoundStreamHead(): %08x %d", i->tag, samplesperframe);
+       swf_SetSoundStreamHead(i->tag, samplesperframe);
+       logf("swf_SetSoundStreamHead() done");
+       i->filesize += swf_WriteTag2(&i->out, i->tag);
+       i->soundstreamhead = 1;
+    }
+
+    /* for framerates greater than 19.14, every now and then a frame
+       hasn't a soundstreamblock. Determine whether this is the case.
+    */
+    if(i->frames < i->soundframepos) {
+       i->samplepos += samplesperframe;
+       return;
+    }
+
+    seek = i->seek;
+
+    //while(i->samplewritepos + num * blocksize < i->samplepos + blocksize) {
+    while(i->samplewritepos < i->samplepos + blocksize) {
+       i->samplewritepos += blocksize;
+       i->soundframepos += framesperblock;
+       num++;
+    }
+    if(!num) {
+       logf("num is zero");
+       printf(" num is zero\n");
+       fprintf(stderr, "  num is zero\n");
+    }
+    logf("num: %d", num);
+
+    /* write num frames, max 1 block */
+    for(pos=0;pos<num;pos++) {
+       logf("pos: %d- getsamples", pos);
+       if(!getSamples(i->video, block1, 576*4, speedup)) { /* 4 = 44100/11025 */
+           i->video->rate = i->video->channels = 0; //end of soundtrack
+           return;
+       }
+       logf("pos: %d- encode mp3", pos);
+       if(!pos) {
+           swf_ResetTag(i->tag, ST_SOUNDSTREAMBLOCK);
+           swf_SetSoundStreamBlock(i->tag, block1, seek, num);
+       } else {
+           swf_SetSoundStreamBlock(i->tag, block1, seek, 0);
+       }
+    }
+    i->filesize += swf_WriteTag2(&i->out, i->tag);
+
+    i->seek = i->samplewritepos - (i->samplepos + blocksize);
+    i->samplepos += samplesperframe;
+    logf("writeSamplesForOneFrame(): done");
+}
+
+static void writeShowFrame(v2swf_internal_t* i)
+{
+    do {
+       writeAudioForOneFrame(i);
+       
+       swf_ResetTag(i->tag, ST_SHOWFRAME);
+       i->filesize += swf_WriteTag2(&i->out, i->tag);
+
+       i->fpspos -= 1.0;
+       i->frames ++;
+    }
+    while(i->fpspos >= 1.0);
+    i->showframe = 0;
+}
+
+static void writeShowTags(v2swf_internal_t* i, int shapeid, int bmid, int width, int height)
+{
+    writeShape(i, shapeid, bmid, width, height);
+
+    swf_ResetTag(i->tag, ST_PLACEOBJECT2);
+    if(!i->prescale) {
+       MATRIX m;
+       swf_GetMatrix(0, &m);
+       m.sx = m.sy = i->scale;
+       swf_ObjectPlace(i->tag,shapeid,shapeid,&m,0,0);
+    } else {
+       swf_ObjectPlace(i->tag,shapeid,shapeid,0,0,0);
+    }
+    i->filesize += swf_WriteTag2(&i->out, i->tag);
+
+    i->showframe = 1;
+}
+
+static int wwrite(struct writer_t*w, void*data, int len)
+{
+    v2swf_internal_t* i = (v2swf_internal_t*)w->internal;
+    ringbuffer_put(&i->r, data, len);
+    return len;
+}
+
+static void wfinish(struct writer_t*w)
+{
+    v2swf_internal_t* i = (v2swf_internal_t*)w->internal;
+}
+
+static void writehead(v2swf_internal_t*i)
+{
+    char header[]="FWS\6\0\0\0\4";
+    SWF swf;
+    int ret;
+    int id;
+   
+    header[3] = i->version;
+    if(i->version >= 6) { //MX
+       header[0] = 'C';
+
+       i->out2.write = wwrite;
+       i->out2.finish = wfinish;
+       i->out2.internal = i;
+       i->out2.type = 77;
+       i->out2.bitpos = 0;
+       i->out2.mybyte = 0;
+       i->out2.pos = 0;
+       writer_init_zlibdeflate(&i->out, &i->out2);
+    } else {
+       i->out.write = wwrite;
+       i->out.finish = wfinish;
+       i->out.internal = i;
+       i->out.type = 77;
+       i->out.bitpos = 0;
+       i->out.mybyte = 0;
+       i->out.pos = 0;
+       i->out2 = i->out;
+    }
+
+    if(i->prescale) {
+       i->width = (int)(i->video->width*(i->scale/65536.0));
+       i->height = (int)(i->video->height*(i->scale/65536.0));
+    } else {
+       i->width = i->video->width;
+       i->height = i->video->height;
+    }
+    if(!i->width)
+       i->width = 1;
+    if(!i->height)
+       i->height = 1;
+    i->buffer = (unsigned char*)malloc(i->width*i->height*4);
+    i->vrbuffer = (unsigned char*)malloc(i->video->width*i->video->height*4);
+
+    memset(&swf, 0, sizeof(SWF));
+    swf.fileVersion=i->version;
+    swf.fileSize = 0;
+    swf.frameCount = 65535;
+    swf.movieSize.xmax=i->width*20;
+    swf.movieSize.ymax=i->height*20;
+    swf.compressed = 8; /* 8 = compression done by caller (us) */
+    swf.frameRate = (int)(i->framerate*0x100);//25*0x100;
+
+    /* write the first 8 bytes to out */
+    i->out2.write(&i->out2, header, 8);
+
+    i->filesize += swf_WriteHeader2(&i->out, &swf);
+    i->headersize = i->filesize;
+    
+    i->tag = swf_InsertTag(NULL,  ST_SETBACKGROUNDCOLOR);
+    swf_SetU8(i->tag, 0); //black
+    swf_SetU8(i->tag, 0);
+    swf_SetU8(i->tag, 0);
+    i->filesize += swf_WriteTag2(&i->out, i->tag);
+}
+
+static void finish(v2swf_internal_t*i)
+{
+    logf("finish(): i->finished=%d\n", i->finished);
+    if(!i->finished) {
+       logf("write endtag\n", i->finished);
+
+       swf_ResetTag(i->tag, ST_END);
+       i->filesize += swf_WriteTag2(&i->out, i->tag);
+       i->out.finish(&i->out);
+
+       if(i->version>=6) {
+           swf_VideoStreamClear(&i->stream);
+       }
+       if(i->buffer)  {
+           free(i->buffer);i->buffer = 0;
+       }
+       if(i->vrbuffer)  {
+           free(i->vrbuffer);i->vrbuffer = 0;
+       }
+       if(i->lastbitmap)  {
+           free(i->lastbitmap);i->lastbitmap = 0;
+       }
+
+       /* FIXME: we shouldn't be doing this. the caller should */
+       logf("call videoreader_close(%08x)\n", i->video);
+       videoreader_close(i->video);
+
+       i->finished = 1;
+    }
+    logf("finishing done\n");
+}
+static void cleanup(v2swf_internal_t*i)
+{
+    int t;
+    for(t=i->lastid;t<i->id;t++) {
+       if(!(t&1)) {
+           swf_ResetTag(i->tag, ST_REMOVEOBJECT2);
+           swf_SetU16(i->tag, t);
+           i->filesize += swf_WriteTag2(&i->out, i->tag);
+       }
+       swf_ResetTag(i->tag, ST_FREECHARACTER);
+       swf_SetU16(i->tag, t);
+       i->filesize += swf_WriteTag2(&i->out, i->tag);
+    }
+    i->lastid = i->id;
+}
+
+#define DIFFMODE_MAX 1 
+#define DIFFMODE_MEAN 2
+#define DIFFMODE_EXACT 3
+#define DIFFMODE_QMEAN 4
+
+static int blockdiff_max(U8*d1,U8*d2,int yadd, int maxdiff, int xl, int yl)
+{
+    int x,y;
+    for(y=0;y<yl;y++) {
+       for(x=0;x<xl;x++) {
+           int rd = d1[1] - d2[1];
+           int gd = d1[2] - d2[2];
+           int bd = d1[3] - d2[3];
+           if(rd < 0) rd = -rd;
+           if(gd < 0) gd = -gd;
+           if(bd < 0) bd = -bd;
+           if(rd+gd+bd>maxdiff)
+               return 1;
+
+           d1+=4; d2+=4;
+       }
+       d1 += yadd; d2 += yadd;
+    }
+    return 0;
+}
+
+static int blockdiff_mean(U8*d1,U8*d2,int yadd, int maxdiff, int xl, int yl)
+{
+    int mean = 0;
+    int x,y;
+    for(y=0;y<yl;y++) {
+       for(x=0;x<xl;x++) {
+           int rd = d1[1] - d2[1];
+           int gd = d1[2] - d2[2];
+           int bd = d1[3] - d2[3];
+           if(rd < 0) rd = -rd;
+           if(gd < 0) gd = -gd;
+           if(bd < 0) bd = -bd;
+           mean += rd+gd+bd;
+
+           d1+=4; d2+=4;
+       }
+       d1 += yadd; d2 += yadd;
+    }
+    if(mean/(xl*yl) > maxdiff)
+       return 1;
+    return 0;
+}
+
+static int blockdiff_qmean(U8*d1,U8*d2,int yadd, int maxdiff, int xl, int yl)
+{
+    int mean = 0;
+    int x,y;
+    for(y=0;y<yl;y++) {
+       for(x=0;x<xl;x++) {
+           int rd = d1[1] - d2[1];
+           int gd = d1[2] - d2[2];
+           int bd = d1[3] - d2[3];
+           int q;
+           if(rd < 0) rd = -rd;
+           if(gd < 0) gd = -gd;
+           if(bd < 0) bd = -bd;
+           q = rd+gd+bd;
+           mean += q*q;
+
+           d1+=4; d2+=4;
+       }
+       d1 += yadd; d2 += yadd;
+    }
+    if(mean/(xl*yl) > maxdiff*maxdiff)
+       return 1;
+    return 0;
+}
+
+static int blockdiff_exact(U8*d1,U8*d2,int yadd, int xl, int yl)
+{
+    int x,y;
+    for(y=0;y<yl;y++) {
+       for(x=0;x<xl;x++) {
+           if((*(U32*)d1^*(U32*)d2)&0xffffff00) { //bits [RGB_] of [RGBA] differ
+               return 1;
+           }
+           d1+=4; d2+=4;
+       }
+       d1 += yadd; d2 += yadd;
+    }
+    return 0;
+}
+
+               /*U32 r = (*(U32*)d1^-(U32*)d2)&0xffffff00;
+               U32 g = ((r << 3) ^ r)&0x80808080;
+               if(g) 
+                   goto differ;*/
+
+static void checkInit(v2swf_internal_t*i)
+{
+    if(!i->head_done) {
+       writehead(i);
+       if(i->version>=6) {
+           swf_ResetTag(i->tag,  ST_DEFINEVIDEOSTREAM);
+           swf_SetU16(i->tag, 99);
+           swf_SetVideoStreamDefine(i->tag, &i->stream, 65535, i->width, i->height);
+           i->filesize += swf_WriteTag2(&i->out, i->tag);
+           if(i->domotion) {
+               i->stream.do_motion = 1;
+           }
+       }
+       i->head_done = 1;
+    }
+}
+
+static void scaleimage(v2swf_internal_t*i)
+{
+    int x,y;
+    int xv,yv;
+    int xm = (i->video->width*65536)/i->width;
+    int ym = (i->video->height*65536)/i->height;
+    memset(i->buffer, 255, i->width*i->height*4);
+    for(y=0,yv=0;y<i->height;y++,yv+=ym) {
+       int*src = &((int*)i->vrbuffer)[(yv>>16)*i->video->width];
+       int*dest = &((int*)i->buffer)[y*i->width];
+       for(x=0,xv=0;x<i->width;x++,xv+=xm) {
+           dest[x] = src[xv>>16];
+       }
+    }
+    //memcpy(i->buffer, i->vrbuffer, i->width*i->height*4);
+}
+
+static int encodeoneframe(v2swf_internal_t*i)
+{
+    videoreader_t*video = i->video;
+    int ret;
+
+    checkInit(i);
+
+    if(videoreader_eof(i->video) || !videoreader_getimage(i->video, i->vrbuffer)) 
+    {
+       logf("videoreader returned eof\n");
+       finish(i);
+       return 0;
+    }
+
+    i->fpspos += i->fpsratio;
+
+    /* skip frames */
+    if(i->fpspos<1.0) {
+       return 0;
+    }
+    
+    logf("encoding image for frame %d\n", i->frames);
+
+    if(i->showframe)
+       writeShowFrame(i);
+
+    logf("scaling\n");
+
+    scaleimage(i);
+
+    logf("version is %d\n", i->version);
+
+    if(i->version <= 4) {
+
+       int bmid = i->id++;
+       int shapeid = i->id++;
+       int width2 = i->width * 4;
+
+       if(i->id>=4) {
+           swf_ResetTag(i->tag, ST_REMOVEOBJECT2);
+           swf_SetU16(i->tag, i->id-3);
+           i->filesize += swf_WriteTag2(&i->out, i->tag);
+           swf_ResetTag(i->tag, ST_FREECHARACTER);
+           swf_SetU16(i->tag, i->id-4);
+           i->filesize += swf_WriteTag2(&i->out, i->tag);
+       }
+
+       swf_ResetTag(i->tag, ST_DEFINEBITSJPEG2);
+       swf_SetU16(i->tag, bmid);
+       swf_SetJPEGBits2(i->tag, i->width, i->height, (RGBA*)i->buffer, i->quality);
+       i->filesize += swf_WriteTag2(&i->out, i->tag);
+       
+       writeShowTags(i, shapeid, bmid, i->width, i->height);
+
+    } else if(i->version == 5) {
+       int width2 = i->width * 4;
+       int width8 = (i->width+7)/8;
+       int height8 = (i->height+7)/8;
+
+       /* the idea is here to only update those jpeg 8x8 blocks
+          which actually have changed. This means that we have to keep
+          the bitmap from the last frame for the comparison. */
+
+       (i->keyframe)--;
+       if(!i->lastbitmap || !i->keyframe) {
+           int t, bmid,shapeid;
+           cleanup(i);
+
+           if(!i->lastbitmap) {
+               logf("Creating bitmap buffer for %dx%d (%dx%d), (%dx%d)\n", i->width, i->height, width2, i->height, width8, height8);
+               i->lastbitmap = (U8*)malloc(width2*i->height);
+           }
+           memcpy(i->lastbitmap, i->buffer, width2*i->height);
+
+           i->keyframe = i->keyframe_interval;
+
+           bmid = i->id++;
+           shapeid = i->id++;
+           width2 = i->width * 4;
+           swf_ResetTag(i->tag, ST_DEFINEBITSJPEG2);
+           swf_SetU16(i->tag, bmid);
+           swf_SetJPEGBits2(i->tag, i->width, i->height, (RGBA*)i->buffer, i->quality);
+           i->filesize += swf_WriteTag2(&i->out, i->tag);
+          
+           writeShowTags(i, shapeid, bmid, i->width, i->height);
+           return 1;
+       } else {
+           /* The following looks so ugly because it's somewhat optimized. 
+              What it does is walk through all the 8x8 blocks, find those
+              which have changed too much and set all others to (R,G,B,A)=(0,0,0,0). 
+              It also set's alpha to 255 in those who haven't changed, and
+              copies them to lastbitmap.
+            */
+
+           int x8, y8;
+           //int maxdiff = ((100 - i->quality)*256)/100;
+           int maxdiff = i->blockdiff*3;
+           for(y8=0;y8<height8;y8++)
+           for(x8=0;x8<width8;x8++) {
+               int x,y;
+               int xl=8,yl=8;
+               int yadd;
+               U8*d1,*d1b,*d2,*d2b;
+               if(x8*8+xl > i->width)
+                   xl = i->width - x8*8;
+               if(y8*8+yl > i->height)
+                   yl = i->height - y8*8;
+               d1 = &i->buffer[width2*y8*8+x8*8*4];
+               d1b = d1;
+               d2 = &i->lastbitmap[width2*y8*8+x8*8*4];
+               d2b = d2;
+               yadd = width2 - (xl*4);
+
+               if(i->diffmode == DIFFMODE_MAX) {
+                   if(blockdiff_max(d1, d2, yadd, maxdiff, xl, yl))
+                       goto differ;
+               } else if(i->diffmode == DIFFMODE_MEAN) {
+                   if(blockdiff_mean(d1, d2, yadd, maxdiff, xl, yl))
+                       goto differ;
+               } else if(i->diffmode == DIFFMODE_EXACT) {
+                   if(blockdiff_exact(d1, d2, yadd, xl, yl))
+                       goto differ;
+               } else if(i->diffmode == DIFFMODE_QMEAN) {
+                   if(blockdiff_qmean(d1, d2, yadd, maxdiff, xl, yl))
+                       goto differ;
+               }
+
+               for(y=0;y<yl;y++) {
+                   for(x=0;x<xl;x++) {
+                       *(U32*)d1b = 0;
+                       d1b+=4;
+                   }
+                   d1b += yadd;
+               }
+               continue;
+    differ:
+               for(y=0;y<yl;y++) {
+                   for(x=0;x<xl;x++) {
+                       *(U32*)d2b = *(U32*)d1b;
+                       d1b[0] = 255;
+                       d1b+=4;d2b+=4;
+                   }
+                   d1b += yadd; d2b += yadd;
+               }
+           }
+
+           /* ok, done. Now a) data is zeroed out in regions which haven't changed
+                            b) lastbitmap equals the bitmap we were called with
+                            c) data's alpha value is set to 255 in regions which did change */
+
+       }
+
+       {
+           int bmid = i->id++;
+           int shapeid = i->id++;
+
+           swf_ResetTag(i->tag, ST_DEFINEBITSJPEG3);
+           swf_SetU16(i->tag, bmid);
+           swf_SetJPEGBits3(i->tag, i->width, i->height, (RGBA*)i->buffer, i->quality);
+           i->filesize += swf_WriteTag2(&i->out, i->tag);
+
+           writeShowTags(i, shapeid, bmid, i->width, i->height);
+       }
+    } else {
+       int quant = 1+(30-(30*i->quality)/100);
+       SWFPLACEOBJECT obj;
+
+       swf_GetPlaceObject(0, &obj);
+       if(!i->prescale) {
+           obj.matrix.sx = obj.matrix.sy = i->scale;
+       }
+
+       if(i->stream.frame==0) {
+           obj.depth = 1;
+           obj.id = 99;
+       } else {
+           obj.move = 1;
+           obj.depth = 1;
+           obj.ratio = i->stream.frame;
+       }
+
+       swf_ResetTag(i->tag, ST_VIDEOFRAME);
+       swf_SetU16(i->tag, 99);
+       if(!(--i->keyframe)) {
+           logf("setting video I-frame, ratio=%d\n", i->stream.frame);
+           swf_SetVideoStreamIFrame(i->tag, &i->stream, (RGBA*)i->buffer, quant);
+           i->keyframe = i->keyframe_interval;
+       } else {
+           logf("setting video P-frame, ratio=%d\n", i->stream.frame);
+           swf_SetVideoStreamPFrame(i->tag, &i->stream, (RGBA*)i->buffer, quant);
+       }
+       i->filesize += swf_WriteTag2(&i->out, i->tag);
+
+       swf_ResetTag(i->tag, ST_PLACEOBJECT2);
+       swf_SetPlaceObject(i->tag,&obj);
+       i->filesize += swf_WriteTag2(&i->out, i->tag);
+       i->showframe = 1;
+    }
+    return 1;
+}
+
+int v2swf_init(v2swf_t*v2swf, videoreader_t * video)
+{
+    int ret = 0;
+    int t=0;
+    v2swf_internal_t* i;
+    logf("v2swf_init()\n");
+    memset(v2swf, 0, sizeof(v2swf_t));
+    i = (v2swf_internal_t*)malloc(sizeof(v2swf_internal_t));
+    memset(i, 0, sizeof(v2swf_internal_t));
+    v2swf->internal = i;
+
+    ringbuffer_init(&i->r);
+
+    logf("video: %dx%d, fps %f\n", video->width, video->height, video->fps);
+
+    i->video = video;
+    i->blockdiff = 64;
+    i->keyframe_interval = 8;
+    i->quality = 20;
+    i->scale = 65536;
+    i->prescale = 0;
+    i->head_done = 0;
+    i->diffmode = DIFFMODE_QMEAN;
+    i->audio_fix = 1.0;
+    i->fixheader = 0;
+    i->framerate = video->fps;
+    i->fpsratio = 1.00000000;
+    i->fpspos = 0.0;
+    i->bitrate = 32;
+    i->version = 6;
+    i->buffer = 0;
+    i->lastbitmap = 0;
+    i->filesize = 8;
+    i->frames = 0;
+    i->id = 1;
+    i->lastid = 1;
+    i->keyframe = 1;
+    i->showframe = 0;
+
+    memset(&i->out, 0, sizeof(struct writer_t));
+    memset(&i->out2, 0, sizeof(struct writer_t));
+
+    return 0;
+}
+int v2swf_read(v2swf_t*v2swf, void*buffer, int len)
+{
+    v2swf_internal_t* i;
+    int l;
+    logf("v2swf_read(%d)\n", len);
+    i = (v2swf_internal_t*)v2swf->internal;
+
+    while(!i->finished && i->r.available < len) {
+       if(!encodeoneframe(i)) {
+           break;
+       }
+    }
+    logf("v2swf_read() done: %d bytes available in ringbuffer\n", i->r.available);
+    l = ringbuffer_read(&i->r, buffer, len);
+
+    return l;
+}
+void v2swf_close(v2swf_t*v2swf)
+{
+    v2swf_internal_t* i = (v2swf_internal_t*)v2swf->internal;
+    logf("close(): i->finished=%d\n", i->finished);
+
+    /* needed only if aborting: */
+    finish(i);
+
+    logf("freeing memory\n");
+    free(v2swf->internal);
+    memset(v2swf, 0, sizeof(v2swf_t));
+    logf("close() done\n");
+}
+
+static int mp3_bitrates[] =
+{ 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0};
+
+void v2swf_setparameter(v2swf_t*v2swf, char*name, char*value)
+{
+    v2swf_internal_t* i;
+
+    logf("set parameters %s to %s\n", name, value);
+
+    if(!strcmp(name, "verbose")) {
+       verbose = 1;
+       logf("set parameters %s to %s\n", name, value);
+       return;
+    }
+
+    if(!v2swf || !v2swf->internal) {
+       printf("error- couldn't set parameter %s: not initialized yet\n", name);fflush(stdout);
+       return;
+    }
+    i = (v2swf_internal_t*)v2swf->internal;
+
+    if(!strcmp(name, "flash_version")) {
+       i->version = atoi(value);
+    } else if(!strcmp(name, "audiosync")) {
+       i->audio_fix = (int)(atof(value));
+    } else if(!strcmp(name, "scale")) {
+       i->scale = (int)(atof(value)*65536);
+    } else if(!strcmp(name, "scale65536")) {
+       i->scale = atoi(value);
+    } else if(!strcmp(name, "quality")) {
+       i->quality = atoi(value);
+    } else if(!strcmp(name, "motioncompensation")) {
+       i->domotion = atoi(value);
+    } else if(!strcmp(name, "prescale")) {
+       i->prescale = atoi(value);
+    } else if(!strcmp(name, "blockdiff")) {
+       i->blockdiff = atoi(value);
+    } else if(!strcmp(name, "fixheader")) {
+       i->fixheader = atoi(value);
+    } else if(!strcmp(name, "framerate")) {
+       i->framerate = atof(value);
+       i->fpsratio = i->framerate / i->video->fps;
+    }
+    else if(!strcmp(name, "mp3_bitrate")) {
+       int t=0,o;
+       i->bitrate = o = atoi(value);
+       if(i->bitrate>160)
+           i->bitrate = 160;
+       while(mp3_bitrates[t]) {
+           if(i->bitrate <= mp3_bitrates[t]) {
+               i->bitrate = mp3_bitrates[t];
+               break;
+           }
+           t++;
+       }
+       logf("bitrate %d requested, setting to %d", o, i->bitrate);
+    }
+    else if(!strcmp(name, "blockdiff_mode")) {
+       if(!strcmp(value, "max")) i->diffmode = DIFFMODE_MAX;
+       else if(!strcmp(value, "mean")) i->diffmode = DIFFMODE_MEAN;
+       else if(!strcmp(value, "qmean")) i->diffmode = DIFFMODE_QMEAN;
+       else if(!strcmp(value, "exact")) i->diffmode = DIFFMODE_EXACT;
+       else {
+           printf("diffmode %s not recognized\n", value);
+           printf("valid diffmodes are: %s\n", "max, mean, qmean, exact");
+       }
+    }
+    else if(!strcmp(name, "keyframe_interval")
+            || !strcmp(name, "keyframe")) {
+       int k = atoi(value);if(k<=0) k=1;
+       i->keyframe_interval = k;
+    }
+    else {
+       printf("Setting encoder.%s not recognized!\n", name);fflush(stdout);
+       return;
+    }
+}
+void v2swf_backpatch(v2swf_t*v2swf, char*filename)
+{
+    FILE* fi;
+    unsigned char f;
+    v2swf_internal_t* i = (v2swf_internal_t*)v2swf->internal;
+    logf("v2swf_backpatch %s\n", filename);
+    if(!i) {
+       printf("call backpatch before close\n");fflush(stdout);
+    }
+    fi = fopen(filename, "rb+");
+    if(!fi) {
+       printf("can't open %s\n", filename);
+       exit(1);
+    }
+    fseek(fi, 4, SEEK_SET);
+    f = i->filesize      ;fwrite(&f,1,1,fi);
+    f = i->filesize >> 8 ;fwrite(&f,1,1,fi);
+    f = i->filesize >> 16;fwrite(&f,1,1,fi);
+    f = i->filesize >> 24;fwrite(&f,1,1,fi);
+    if(i->version<6) {
+       /* no compression- we can backpatch the frames too */
+       fseek(fi, i->headersize-2, SEEK_SET);
+       f = i->frames        ;fwrite(&f,1,1,fi);
+       f = i->frames >> 8   ;fwrite(&f,1,1,fi);
+    }
+    fclose(fi);
+    if(i->fixheader) {
+       SWF tmp;
+       int fi;
+       logf("v2swf_backpatch %s - fix header\n", filename);
+       memset(&tmp, 0, sizeof(tmp));
+       fi = open(filename, O_RDONLY|O_BINARY);
+       if(fi>=0) {
+           if(swf_ReadSWF(fi, &tmp)>=0) {
+               close(fi);
+               fi = open(filename, O_WRONLY|O_BINARY|O_TRUNC|O_CREAT, 0666);
+               if(fi>=0) {
+                   swf_WriteSWC(fi, &tmp);
+                   close(fi);
+                   logf("v2swf_backpatch %s - fix header: success\n", filename);
+               }
+           }
+       }
+    }
+}
+
+float v2swf_getprogress(v2swf_t*v2swf)
+{
+    float* p;
+    v2swf_internal_t* i;
+    logf("v2swf_getprogress()");
+    if(!v2swf || !v2swf->internal) {
+       return 0.0;
+    }
+    i = (v2swf_internal_t*)v2swf->internal;
+
+    p = (float*)videoreader_getinfo(i->video, "position");
+
+    if(p) {
+       return *p;
+    } else {
+       float f = i->frames/1500.0; /*fake*/
+       if(f>1.0)
+           return 1.0;
+       else
+           return f;
+    }
+}
+
+void v2swf_setvideoparameter(videoreader_t*v, char*name, char*value)
+{
+    logf("v2swf_setvideoparameter()");
+    videoreader_setparameter(v, name, value);
+}
diff --git a/avi2swf/v2swf.h b/avi2swf/v2swf.h
new file mode 100644 (file)
index 0000000..94a9526
--- /dev/null
@@ -0,0 +1,73 @@
+/*  v2swf.c 
+    header file for v2swf.h - part of SWFTools
+
+    Copyright (C) 2003 Matthias Kramm <kramm@quiss.org>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
+#ifndef __video_h__
+#define __video_h__
+
+#ifdef __cplusplus
+extern "C" {
+#else
+typedef unsigned char bool;
+#endif
+
+typedef struct _videoreader_t
+{
+    void*internal;
+
+    /* video */
+    int width;
+    int height;
+    double fps;
+
+    /* audio */
+    int channels;
+    int rate;
+
+    int (*getsamples) (struct _videoreader_t*, void*buffer, int num);
+    /* buffer must be big enough to hold width*height*4 bytes: */
+    int (*getimage) (struct _videoreader_t*, void*buffer);
+    bool (*eof) (struct _videoreader_t*);
+    /* multi purpose functions */
+    void (*setparameter) (struct _videoreader_t*, char*name, char*value);
+    void* (*getinfo) (struct _videoreader_t*, char*name);
+    void (*close) (struct _videoreader_t*);
+} videoreader_t;
+
+#define videoreader_getsamples(v, buffer, num) ((v)->getsamples((v),(buffer),(num)))
+#define videoreader_getimage(v, buffer) ((v)->getimage((v),(buffer)))
+#define videoreader_eof(v) ((v)->eof(v))
+#define videoreader_setparameter(v,name,value) ((v)->setparameter((v),(name),(value)))
+#define videoreader_getinfo(v,name) ((v)->getinfo((v),(name)))
+#define videoreader_close(v) ((v)->close(v))
+
+typedef struct _v2swf_t
+{
+    void*internal;
+} v2swf_t;
+
+int v2swf_init(v2swf_t*v2swf, videoreader_t * video);
+int v2swf_read(v2swf_t*v2swf, void*buffer, int len);
+void v2swf_setparameter(v2swf_t*v2swf, char*name, char*value);
+void v2swf_close(v2swf_t*v2swf);
+void v2swf_backpatch(v2swf_t*v2swf, char*filename);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif