fixed framerate/sound synchronization (account for 8.8 rounding error)
[swftools.git] / avi2swf / v2swf.c
index 312fcf7..db32c0c 100644 (file)
@@ -39,10 +39,14 @@ typedef struct _v2swf_internal_t
 
     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;
@@ -165,18 +169,24 @@ 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 = (double) video->rate * speedup / swf_mp3_in_samplerate;
+    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;
+    memset(tmp, 0, sizeof(tmp));
+    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++) {
@@ -192,7 +202,7 @@ static int getSamples(videoreader_t*video, S16*data, int len, double speedup)
        data[t] = tmp[(int)pos];
        pos+=ratio;
     }
-    return 1;
+    return l == r;
 }
 
 static void writeAudioForOneFrame(v2swf_internal_t* i)
@@ -209,8 +219,10 @@ static void writeAudioForOneFrame(v2swf_internal_t* i)
 
     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 = (i->samplerate > 22050) ? 1152 : 576;
     blockspersecond = ((double)i->samplerate)/blocksize;
@@ -229,7 +241,7 @@ static void writeAudioForOneFrame(v2swf_internal_t* i)
        /* 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->rate;
+       swf_mp3_in_samplerate = i->video->samplerate;
 
        /* first run - initialize */
        swf_mp3_channels = 1;//i->video->channels;
@@ -268,8 +280,9 @@ static void writeAudioForOneFrame(v2swf_internal_t* i)
     /* write num frames, max 1 block */
     for(pos=0;pos<num;pos++) {
         if(!getSamples(i->video, block1, blocksize * (double)swf_mp3_in_samplerate/swf_mp3_out_samplerate, speedup)) {
-           i->video->rate = i->video->channels = 0; //end of soundtrack
-           return;
+           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) */
        }
        if(!pos) {
            swf_ResetTag(i->tag, ST_SOUNDSTREAMBLOCK);
@@ -559,6 +572,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];
@@ -570,6 +588,20 @@ 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 encodeoneframe(v2swf_internal_t*i)
 {
     videoreader_t*video = i->video;
@@ -577,27 +609,38 @@ static int encodeoneframe(v2swf_internal_t*i)
 
     checkInit(i);
 
-    if(videoreader_eof(i->video) || !videoreader_getimage(i->video, i->vrbuffer)) 
-    {
-       msg("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(!videoreader_getimage(i->video, i->vrbuffer)) 
+    {
+       i->video_eof = 1;
+       msg("videoreader returned eof\n");
+       if(i->audio_eof) {
+           finish(i);
+           return 0;
+       } else {
+           return writeAudioOnly(i);
+       }
     }
-    
-    msg("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);
-
-    msg("scaling\n");
-
+    }
+    
     scaleimage(i);
 
     msg("version is %d\n", i->version);
@@ -787,6 +830,7 @@ int v2swf_init(v2swf_t*v2swf, videoreader_t * video)
     msg("video: %dx%d, fps %f\n", video->width, video->height, video->fps);
 
     i->video = video;
+    i->video_fps = ((int)(video->fps*256))/256.0;
     i->blockdiff = 64;
     i->keyframe_interval = 8;
     i->quality = 20;
@@ -797,8 +841,8 @@ int v2swf_init(v2swf_t*v2swf, videoreader_t * video)
     i->diffmode = DIFFMODE_QMEAN;
     i->audio_fix = 1.0;
     i->fixheader = 0;
-    i->framerate = video->fps;
-    i->fpsratio = 1.00000000;
+    i->framerate = i->video_fps;
+    i->fpsratio = 1.00000000000;
     i->fpspos = 0.0;
     i->bitrate = 32;
     i->version = 6;
@@ -890,7 +934,7 @@ void v2swf_setparameter(v2swf_t*v2swf, char*name, char*value)
        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;
@@ -972,29 +1016,6 @@ void v2swf_backpatch(v2swf_t*v2swf, char*filename)
     }
 }
 
-float v2swf_getprogress(v2swf_t*v2swf)
-{
-    float* p;
-    v2swf_internal_t* i;
-    msg("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)
 {
     msg("v2swf_setvideoparameter()");