new parameter addspacechars
[swftools.git] / avi2swf / v2swf.c
index 8e48deb..c974bfb 100644 (file)
@@ -34,15 +34,19 @@ typedef struct _v2swf_internal_t
     
     int myframes;
 
-    struct writer_t out;
-    struct writer_t out2;
+    writer_t out;
+    writer_t out2;
 
     ringbuffer_t r;
     videoreader_t* video;
+    double video_fps;
 
     int width;
     int height;
 
+    int video_eof;
+    int audio_eof;
+
     unsigned char* vrbuffer;
     unsigned char* buffer;
     unsigned char* lastbitmap;
@@ -60,11 +64,14 @@ typedef struct _v2swf_internal_t
     float fpspos;
 
     int bitrate;
+    int samplerate;
 
     int finished;
     int keyframe;
     int showframe;
 
+    int skipframes;
+
     float samplepos;
     float framesamplepos;
     int samplewritepos;
@@ -72,11 +79,15 @@ typedef struct _v2swf_internal_t
     int soundstreamhead;
     int seek;
 
+    int numframes;
+
     double audio_fix;
     int fixheader;
     int prescale;
 
     int scale;
+
+    int add_cut;
     
     int domotion;
 
@@ -91,7 +102,7 @@ typedef struct _v2swf_internal_t
 static int verbose = 0;
 static int filelog = 0;
 
-static void logf(char*format, ...)
+static void msg(char*format, ...)
 {
     char buf[1024];
     int l;
@@ -99,7 +110,7 @@ static void logf(char*format, ...)
     if(!verbose)
        return;
     va_start(arglist, format);
-    vsprintf(buf, format, arglist);
+    vsnprintf(buf, sizeof(buf)-1, format, arglist);
     va_end(arglist);
     l = strlen(buf);
     while(l && buf[l-1]=='\n') {
@@ -118,6 +129,12 @@ static void logf(char*format, ...)
     fflush(stdout);
 }
 
+extern int swf_mp3_in_samplerate;
+extern int swf_mp3_out_samplerate;
+extern int swf_mp3_channels;
+extern int swf_mp3_bitrate;
+
+
 static void writeShape(v2swf_internal_t*i, int id, int gfxid, int width, int height)
 {
     RGBA rgb;
@@ -158,18 +175,25 @@ static void writeShape(v2swf_internal_t*i, int id, int gfxid, int width, int hei
     swf_ShapeFree(shape);
 }
 
+/* returns 0 on partial read */
 static int getSamples(videoreader_t*video, S16*data, int len, double speedup)
 {
     double pos = 0;
-    double ratio = video->rate * speedup / 44100.0;
+    double ratio = (double) video->samplerate * speedup / swf_mp3_in_samplerate;
     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)
+    int l = 0;
+    memset(tmp, 0, sizeof(tmp));
+    if(r>0)
+       l = videoreader_getsamples(video, tmp, r);
+    if(l <= 0) {
        return 0;
+    }
+    msg("%d samples read", l);
 
     /* convert to 1 channel */
     for(t=0;t<rlen;t++) {
@@ -180,17 +204,14 @@ static int getSamples(videoreader_t*video, S16*data, int len, double speedup)
        tmp[t] = a/video->channels;
     }
 
-    /* down/up-sample to 44khz */
+    /* down/up-sample to the desired input samplerate (swf_mp3_in_samplerate) */
     for(t=0;t<len;t++) {
        data[t] = tmp[(int)pos];
        pos+=ratio;
     }
-    return 1;
+    return l == r;
 }
 
-extern int swf_mp3_channels;
-extern int swf_mp3_bitrate;
-extern int swf_mp3_samplerate;
 static void writeAudioForOneFrame(v2swf_internal_t* i)
 {
     int blocksize; 
@@ -203,13 +224,15 @@ static void writeAudioForOneFrame(v2swf_internal_t* i)
     int pos = 0;
     S16 block1[576*4 * 2];
 
-    logf("writeAudioForOneFrame()");
+    msg("writeAudioForOneFrame()");
 
-    if(i->video->channels<=0 || i->video->rate<=0)
+    if(i->audio_eof || i->video->channels<=0 || i->video->samplerate<=0) {
+       i->audio_eof = 1;
        return; /* no sound in video */
+    }
 
-    blocksize = 576; /* 11khz samples per mp3 block */
-    blockspersecond = 11025.0/blocksize;
+    blocksize = (i->samplerate > 22050) ? 1152 : 576;
+    blockspersecond = ((double)i->samplerate)/blocksize;
 
     /* notice: for framerates greater than about 35, audio starts getting choppy. */
     framespersecond = i->framerate;
@@ -218,17 +241,23 @@ static void writeAudioForOneFrame(v2swf_internal_t* i)
     samplesperframe = (blocksize * blockspersecond) / framespersecond; /* 11khz-samples per frame */
     samplesperblock = samplesperframe * framesperblock;
 
-    logf("samplesperblock: %f", samplesperblock);
+    msg("samplesperblock: %f", samplesperblock);
 
     if(!i->soundstreamhead) {
+       swf_mp3_out_samplerate = i->samplerate;
+       /* The pre-processing of sound samples in getSamples(..) above
+          re-samples the sound to swf_mp3_in_samplerate. It is best to
+          simply make it the original samplerate:  */
+       swf_mp3_in_samplerate = i->video->samplerate;
+
        /* 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);
+       msg("swf_SetSoundStreamHead(): %08x %d", i->tag, samplesperframe);
        swf_SetSoundStreamHead(i->tag, samplesperframe);
-       logf("swf_SetSoundStreamHead() done");
+       msg("swf_SetSoundStreamHead() done");
        i->filesize += swf_WriteTag2(&i->out, i->tag);
        i->soundstreamhead = 1;
     }
@@ -236,7 +265,9 @@ static void writeAudioForOneFrame(v2swf_internal_t* i)
     /* for framerates greater than 19.14, every now and then a frame
        hasn't a soundstreamblock. Determine whether this is the case.
     */
+    msg("SOUND: frame:%d soundframepos:%f samplewritepos:%d samplepos:%f\n", i->frames, i->soundframepos, i->samplewritepos, i->samplepos);
     if(i->frames < i->soundframepos) {
+       msg("SOUND: block skipped\n");
        i->samplepos += samplesperframe;
        return;
     }
@@ -244,26 +275,22 @@ static void writeAudioForOneFrame(v2swf_internal_t* i)
     seek = i->seek;
 
     //while(i->samplewritepos + num * blocksize < i->samplepos + blocksize) {
-    while(i->samplewritepos < i->samplepos + blocksize) {
+    do {
        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);
+    while(i->samplewritepos < i->samplepos);
+
+    msg("SOUND: number of blocks: %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;
+        if(!getSamples(i->video, block1, blocksize * (double)swf_mp3_in_samplerate/swf_mp3_out_samplerate, speedup)) {
+           i->audio_eof = 1; i->video->samplerate = i->video->channels = 0; //end of soundtrack
+           /* fall through, this probably was a partial read. (We did, after all,
+              come to this point, so i->audio_eof must have been false so far) */
        }
-       logf("pos: %d- encode mp3", pos);
        if(!pos) {
            swf_ResetTag(i->tag, ST_SOUNDSTREAMBLOCK);
            swf_SetSoundStreamBlock(i->tag, block1, seek, num);
@@ -273,9 +300,8 @@ static void writeAudioForOneFrame(v2swf_internal_t* i)
     }
     i->filesize += swf_WriteTag2(&i->out, i->tag);
 
-    i->seek = i->samplewritepos - (i->samplepos + blocksize);
+    i->seek = blocksize - (i->samplewritepos - i->samplepos);
     i->samplepos += samplesperframe;
-    logf("writeSamplesForOneFrame(): done");
 }
 
 static void writeShowFrame(v2swf_internal_t* i)
@@ -311,14 +337,14 @@ static void writeShowTags(v2swf_internal_t* i, int shapeid, int bmid, int width,
     i->showframe = 1;
 }
 
-static int wwrite(struct writer_t*w, void*data, int len)
+static int wwrite(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)
+static void wfinish(writer_t*w)
 {
     v2swf_internal_t* i = (v2swf_internal_t*)w->internal;
 }
@@ -391,12 +417,26 @@ static void writehead(v2swf_internal_t*i)
 
 static void finish(v2swf_internal_t*i)
 {
-    logf("finish(): i->finished=%d\n", i->finished);
+    msg("finish(): i->finished=%d\n", i->finished);
     if(!i->finished) {
-       logf("write endtag\n", i->finished);
+       msg("write endtag\n", i->finished);
+
+       if(i->add_cut) {
+           swf_ResetTag(i->tag, ST_SHOWFRAME);
+           i->filesize += swf_WriteTag2(&i->out, i->tag);
+
+           swf_ResetTag(i->tag, ST_REMOVEOBJECT2);
+           swf_SetU16(i->tag, 1); //depth
+           i->filesize += swf_WriteTag2(&i->out, i->tag);
+
+           swf_ResetTag(i->tag, ST_DOACTION);
+           swf_SetU16(i->tag, 0x0007);
+           i->filesize += swf_WriteTag2(&i->out, i->tag);
+       }
 
        swf_ResetTag(i->tag, ST_END);
        i->filesize += swf_WriteTag2(&i->out, i->tag);
+
        i->out.finish(&i->out);
 
        if(i->version>=6) {
@@ -413,12 +453,12 @@ static void finish(v2swf_internal_t*i)
        }
 
        /* FIXME: we shouldn't be doing this. the caller should */
-       logf("call videoreader_close(%08x)\n", i->video);
+       msg("call videoreader_close(%08x)\n", i->video);
        videoreader_close(i->video);
 
        i->finished = 1;
     }
-    logf("finishing done\n");
+    msg("finishing done\n");
 }
 static void cleanup(v2swf_internal_t*i)
 {
@@ -553,6 +593,11 @@ static void scaleimage(v2swf_internal_t*i)
     int xv,yv;
     int xm = (i->video->width*65536)/i->width;
     int ym = (i->video->height*65536)/i->height;
+    msg("scaling from %dx%d to %dx%d\n", 
+           i->video->width, i->video->height,
+           i->width, 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];
@@ -564,6 +609,35 @@ static void scaleimage(v2swf_internal_t*i)
     //memcpy(i->buffer, i->vrbuffer, i->width*i->height*4);
 }
 
+static int writeAudioOnly(v2swf_internal_t*i)
+{
+    if(i->showframe) {
+       i->fpspos += i->fpsratio;
+       /* skip frames */
+       if(i->fpspos<1.0) {
+           return 0;
+       }
+       writeShowFrame(i);
+    }
+    i->showframe = 1;
+    return 1;
+}
+
+static int getframe(v2swf_internal_t*i)
+{
+    if(!i->skipframes)
+        return videoreader_getimage(i->video, i->vrbuffer);
+    else {
+        int t;
+        for(t=0;t<i->skipframes;t++) {
+            int ret = videoreader_getimage(i->video, i->vrbuffer);
+            if(!ret)
+                return 0;
+        }
+        return 1;
+    }
+}
+
 static int encodeoneframe(v2swf_internal_t*i)
 {
     videoreader_t*video = i->video;
@@ -571,30 +645,41 @@ static int encodeoneframe(v2swf_internal_t*i)
 
     checkInit(i);
 
-    if(videoreader_eof(i->video) || !videoreader_getimage(i->video, i->vrbuffer)) 
-    {
-       logf("videoreader returned eof\n");
-       finish(i);
+    if(i->video_eof && i->audio_eof) {
+       if(!i->finished)
+           finish(i);
        return 0;
     }
 
-    i->fpspos += i->fpsratio;
+    if(!i->audio_eof && i->video_eof) {
+       return writeAudioOnly(i);
+    }
 
-    /* skip frames */
-    if(i->fpspos<1.0) {
-       return 0;
+    if(!getframe(i) || (i->numframes && i->frames==i->numframes)) 
+    {
+       i->video_eof = 1;
+       msg("videoreader returned eof\n");
+       if(i->audio_eof || (i->numframes && i->frames==i->numframes)) {
+           finish(i);
+           return 0;
+       } else {
+           return writeAudioOnly(i);
+       }
     }
-    
-    logf("encoding image for frame %d\n", i->frames);
 
-    if(i->showframe)
+    msg("encoding image for frame %d\n", i->frames);
+    if(i->showframe) {
+       i->fpspos += i->fpsratio;
+       /* skip frames */
+       if(i->fpspos<1.0) {
+           return 0;
+       }
        writeShowFrame(i);
-
-    logf("scaling\n");
-
+    }
+    
     scaleimage(i);
 
-    logf("version is %d\n", i->version);
+    msg("version is %d\n", i->version);
 
     if(i->version <= 4) {
 
@@ -633,7 +718,7 @@ static int encodeoneframe(v2swf_internal_t*i)
            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);
+               msg("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);
@@ -748,11 +833,11 @@ static int encodeoneframe(v2swf_internal_t*i)
        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);
+           msg("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);
+           msg("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);
@@ -765,12 +850,21 @@ static int encodeoneframe(v2swf_internal_t*i)
     return 1;
 }
 
+static void init_fps(v2swf_internal_t*i)
+{
+    int oldframerate = i->framerate;
+    i->framerate = i->video->fps / i->skipframes;
+    i->video_fps = ((int)(i->framerate*256))/256.0;
+    if(oldframerate)
+        msg("setting new framerate to %f\n", i->framerate);
+}
+
 int v2swf_init(v2swf_t*v2swf, videoreader_t * video)
 {
     int ret = 0;
     int t=0;
     v2swf_internal_t* i;
-    logf("v2swf_init()\n");
+    msg("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));
@@ -778,20 +872,27 @@ int v2swf_init(v2swf_t*v2swf, videoreader_t * video)
 
     ringbuffer_init(&i->r);
 
-    logf("video: %dx%d, fps %f\n", video->width, video->height, video->fps);
-
+    i->skipframes = 1;
+    i->framerate = 0;
     i->video = video;
+    init_fps(i);
+
+    msg("video: %dx%d, fps %f\n", video->width, video->height, video->fps);
+
     i->blockdiff = 64;
     i->keyframe_interval = 8;
     i->quality = 20;
     i->scale = 65536;
+    i->add_cut = 1;
+    i->samplerate = 11025;
     i->prescale = 0;
+    i->numframes= 0;
+    i->skipframes = 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->fpsratio = 1.00000000000;
     i->fpspos = 0.0;
     i->bitrate = 32;
     i->version = 6;
@@ -804,8 +905,8 @@ int v2swf_init(v2swf_t*v2swf, videoreader_t * video)
     i->keyframe = 1;
     i->showframe = 0;
 
-    memset(&i->out, 0, sizeof(struct writer_t));
-    memset(&i->out2, 0, sizeof(struct writer_t));
+    memset(&i->out, 0, sizeof(writer_t));
+    memset(&i->out2, 0, sizeof(writer_t));
 
     return 0;
 }
@@ -813,7 +914,7 @@ int v2swf_read(v2swf_t*v2swf, void*buffer, int len)
 {
     v2swf_internal_t* i;
     int l;
-    logf("v2swf_read(%d)\n", len);
+    msg("v2swf_read(%d)\n", len);
     i = (v2swf_internal_t*)v2swf->internal;
 
     while(!i->finished && i->r.available < len) {
@@ -821,7 +922,7 @@ int v2swf_read(v2swf_t*v2swf, void*buffer, int len)
            break;
        }
     }
-    logf("v2swf_read() done: %d bytes available in ringbuffer\n", i->r.available);
+    msg("v2swf_read() done: %d bytes available in ringbuffer\n", i->r.available);
     l = ringbuffer_read(&i->r, buffer, len);
 
     return l;
@@ -829,15 +930,15 @@ int v2swf_read(v2swf_t*v2swf, void*buffer, int len)
 void v2swf_close(v2swf_t*v2swf)
 {
     v2swf_internal_t* i = (v2swf_internal_t*)v2swf->internal;
-    logf("close(): i->finished=%d\n", i->finished);
+    msg("close(): i->finished=%d\n", i->finished);
 
     /* needed only if aborting: */
     finish(i);
 
-    logf("freeing memory\n");
+    msg("freeing memory\n");
     free(v2swf->internal);
     memset(v2swf, 0, sizeof(v2swf_t));
-    logf("close() done\n");
+    msg("close() done\n");
 }
 
 static int mp3_bitrates[] =
@@ -847,11 +948,11 @@ void v2swf_setparameter(v2swf_t*v2swf, char*name, char*value)
 {
     v2swf_internal_t* i;
 
-    logf("set parameters %s to %s\n", name, value);
+    msg("set parameters %s to %s\n", name, value);
 
     if(!strcmp(name, "verbose")) {
        verbose = 1;
-       logf("set parameters %s to %s\n", name, value);
+       msg("set parameters %s to %s\n", name, value);
        return;
     }
 
@@ -865,12 +966,19 @@ void v2swf_setparameter(v2swf_t*v2swf, char*name, char*value)
        i->version = atoi(value);
     } else if(!strcmp(name, "audiosync")) {
        i->audio_fix = (int)(atof(value));
+    } else if(!strcmp(name, "addcut")) {
+       i->add_cut = atoi(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, "skipframes")) {
+       i->skipframes = atoi(value);
+        init_fps(i);
+    } else if(!strcmp(name, "numframes")) {
+       i->numframes = atoi(value);
     } else if(!strcmp(name, "motioncompensation")) {
        i->domotion = atoi(value);
     } else if(!strcmp(name, "prescale")) {
@@ -879,9 +987,11 @@ void v2swf_setparameter(v2swf_t*v2swf, char*name, char*value)
        i->blockdiff = atoi(value);
     } else if(!strcmp(name, "fixheader")) {
        i->fixheader = atoi(value);
+    } else if(!strcmp(name, "samplerate")) {
+       i->samplerate = atoi(value);
     } else if(!strcmp(name, "framerate")) {
        i->framerate = atof(value);
-       i->fpsratio = i->framerate / i->video->fps;
+       i->fpsratio = i->framerate / i->video_fps;
     }
     else if(!strcmp(name, "mp3_bitrate")) {
        int t=0,o;
@@ -895,7 +1005,7 @@ void v2swf_setparameter(v2swf_t*v2swf, char*name, char*value)
            }
            t++;
        }
-       logf("bitrate %d requested, setting to %d", o, i->bitrate);
+       msg("bitrate %d requested, setting to %d", o, i->bitrate);
     }
     else if(!strcmp(name, "blockdiff_mode")) {
        if(!strcmp(value, "max")) i->diffmode = DIFFMODE_MAX;
@@ -922,7 +1032,7 @@ 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);
+    msg("v2swf_backpatch %s\n", filename);
     if(!i) {
        printf("call backpatch before close\n");fflush(stdout);
     }
@@ -946,7 +1056,7 @@ void v2swf_backpatch(v2swf_t*v2swf, char*filename)
     if(i->fixheader) {
        SWF tmp;
        int fi;
-       logf("v2swf_backpatch %s - fix header\n", filename);
+       msg("v2swf_backpatch %s - fix header\n", filename);
        memset(&tmp, 0, sizeof(tmp));
        fi = open(filename, O_RDONLY|O_BINARY);
        if(fi>=0) {
@@ -954,40 +1064,17 @@ void v2swf_backpatch(v2swf_t*v2swf, char*filename)
                close(fi);
                fi = open(filename, O_WRONLY|O_BINARY|O_TRUNC|O_CREAT, 0666);
                if(fi>=0) {
-                   swf_WriteSWC(fi, &tmp);
+                   swf_WriteSWF(fi, &tmp);
                    close(fi);
-                   logf("v2swf_backpatch %s - fix header: success\n", filename);
+                   msg("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()");
+    msg("v2swf_setvideoparameter()");
     videoreader_setparameter(v, name, value);
 }