fixed drawlink() ruby callback
[swftools.git] / avi2swf / videoreader_vfw.cc
index 2c2f809..ca8a6b7 100644 (file)
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
 
+#include "videoreader.h"
 #ifdef WIN32
 #include <windows.h>
 #include <vfw.h>
 #include <stdlib.h>
 #include <stdio.h>
-#include "videoreader.h"
 
 typedef struct _videoreader_vfw_internal { 
     //video:
@@ -37,6 +37,9 @@ typedef struct _videoreader_vfw_internal {
     BITMAPINFOHEADER bitmap;
     WAVEFORMATEX waveformat;
 
+    int audio_eof;
+    int video_eof;
+
     int video_pos;
     int video_end;
 
@@ -49,15 +52,15 @@ typedef struct _videoreader_vfw_internal {
     int samplerate;
     int channels;
 
+    int flip;
 } videoreader_vfw_internal_t;
 
-bool videoreader_vfw_eof(videoreader_t* vr)
-{
-    videoreader_vfw_internal_t* i = (videoreader_vfw_internal_t*)vr->internal;
-    return (i->video_pos >= i->video_end);
-}
+static int avifile_initialized = 0;
+static int verbose;
+
+#define _TRACE_ {printf("vfw: %s: %d (%s)\n",__FILE__,__LINE__,__func__);fflush(stdout);}
 
-static int bitmap_to_rgba(BITMAPINFOHEADER*bi, void*buffer, const int dest_width, const int dest_height)
+static int bitmap_to_rgba(BITMAPINFOHEADER*bi, void*buffer, const int dest_width, const int dest_height, int flip)
 {
     UCHAR*data = (UCHAR*)(bi+1); // actual bitmap data starts after the header
 
@@ -70,8 +73,8 @@ static int bitmap_to_rgba(BITMAPINFOHEADER*bi, void*buffer, const int dest_width
     
     ULONG*dest = (ULONG*)buffer;
 
-    int width = bi->biWidth;
-    int height = bi->biHeight;
+    int width = abs(bi->biWidth);
+    int height = abs(bi->biHeight);
     if(dest_width != width || dest_height != height) {
        /* TODO: size conversion */
        fprintf(stderr, "size mismatch: %dx%d != %dx%d\n", width, height, dest_width, dest_height);
@@ -83,10 +86,19 @@ static int bitmap_to_rgba(BITMAPINFOHEADER*bi, void*buffer, const int dest_width
     int bytesperpixel = ((bi->biWidth*bi->biBitCount)+7)&~7;
     int linex = ((bytesperpixel/8)+3)&~3;
     memset(dest, 255, dest_width*dest_height*4);//pre-fill alpha channel
+
+    const int starty = flip? 0 : dest_height-1;
+    const int endy   = flip? dest_height : -1;
+    const int yinc   = flip? 1 : -1;
+
+    if(verbose) {
+       printf("vfw: Convering scanlines %d to %d from bpp %d, %d stepping, flip=%d\n", starty, endy, bi->biBitCount, yinc, flip);
+    }
+
     if(bi->biBitCount==1) {
-       int y;
        UCHAR*img = data;
-       for(y=0;y<dest_height;y++) {
+       int y;
+       for(y=starty;y!=endy;y+=yinc) {
            UCHAR*line = &img[linex*y];
            int x;
            for(x=0;x<dest_width;x++) {
@@ -94,10 +106,10 @@ static int bitmap_to_rgba(BITMAPINFOHEADER*bi, void*buffer, const int dest_width
            }
        }
     } else if(bi->biBitCount==4) {
-       int y;
        UCHAR*img = &data[bi->biClrUsed*4];
        UCHAR*pal = data;
-       for(y=0;y<dest_height;y++) {
+       int y;
+       for(y=starty;y!=endy;y+=yinc) {
            UCHAR*line = &img[linex*y];
            int x;
            for(x=0;x<dest_width/2;x++) {
@@ -107,10 +119,10 @@ static int bitmap_to_rgba(BITMAPINFOHEADER*bi, void*buffer, const int dest_width
            }
        }
     } else if(bi->biBitCount==8) {
-       int y;
        UCHAR*img = &data[bi->biClrUsed*4];
        UCHAR*pal = data;
-       for(y=0;y<dest_height;y++) {
+       int y;
+       for(y=starty;y!=endy;y+=yinc) {
            UCHAR*line = &img[linex*y];
            int x;
            for(x=0;x<dest_width;x++) {
@@ -118,10 +130,22 @@ static int bitmap_to_rgba(BITMAPINFOHEADER*bi, void*buffer, const int dest_width
                line++;
            }
        }
+    } else if(bi->biBitCount==16) {
+       UCHAR*img = data;
+       int y;
+       for(y=starty;y!=endy;y+=yinc) {
+           UCHAR*line = &img[linex*y];
+           int x;
+           for(x=0;x<dest_width;x++) {
+               USHORT c = line[0]|line[1]<<8;
+               *dest++ = 255|(c&0x1f)<<(24+3)|(c>>5&0x1f)<<(16+3)|(c>>10&0x1f)<<(8+3);
+               line+=2;
+           }
+       }
     } else if(bi->biBitCount==24) {
        UCHAR*img = data;
        int y;
-       for(y=0;y<dest_height;y++) {
+       for(y=starty;y!=endy;y+=yinc) {
            UCHAR*line = &img[linex*y];
            int x;
            for(x=0;x<dest_width;x++) {
@@ -132,7 +156,7 @@ static int bitmap_to_rgba(BITMAPINFOHEADER*bi, void*buffer, const int dest_width
     } else if(bi->biBitCount==32) {
        UCHAR*img = data;
        int y;
-       for(y=0;y<dest_height;y++) {
+       for(y=starty;y!=endy;y+=yinc) {
            UCHAR*line = &img[linex*y];
            int x;
            for(x=0;x<dest_width;x++) {
@@ -147,25 +171,28 @@ static int bitmap_to_rgba(BITMAPINFOHEADER*bi, void*buffer, const int dest_width
     return 1;
 }
 
-int videoreader_vfw_getimage(videoreader_t* vr, void*buffer)
+static int videoreader_vfw_getimage(videoreader_t* vr, void*buffer)
 {
     videoreader_vfw_internal_t* i = (videoreader_vfw_internal_t*)vr->internal;
 
-    if(videoreader_vfw_eof(vr))
+    if (i->video_pos >= i->video_end)
+       i->video_eof = 1;
+
+    if(i->video_eof)
        return 0;
 
     LPBITMAPINFOHEADER bi;
     bi = (LPBITMAPINFOHEADER)AVIStreamGetFrame(i->getframe, i->video_pos);
        
     i->video_pos++;
+    vr->frame++;
 
     if(!bi) {
        fprintf(stderr, "AVIStreamGetFrame failed\n");
        return 0;
     }
-    printf("%dx%d:%d\n", bi->biWidth, bi->biHeight, bi->biBitCount);
     
-    if(!bitmap_to_rgba(bi, buffer, i->width, i->height)) {
+    if(!bitmap_to_rgba(bi, buffer, i->width, i->height, i->flip)) {
        fprintf(stderr, "couldn't convert bitmap to RGBA.\n");
        return 0;
     }
@@ -181,9 +208,12 @@ static int readAudioBlock(videoreader_vfw_internal_t* i, void*buf, int len)
     return bytes;
 }
 
-int videoreader_vfw_getsamples(videoreader_t* vr, void*buf, int num)
+static int videoreader_vfw_getsamples(videoreader_t* vr, void*buf, int num)
 {
     videoreader_vfw_internal_t* i = (videoreader_vfw_internal_t*)vr->internal;
+
+    if(i->audio_eof)
+       return 0;
    
     switch(i->waveformat.wBitsPerSample) {
        case 1: {
@@ -192,6 +222,7 @@ int videoreader_vfw_getsamples(videoreader_t* vr, void*buf, int num)
            do {
                ((SHORT*)buf)[t] = ((((BYTE*)buf)[t>>3])>>(t&7))<<15;
            } while(--t>=0);
+           if(!len) i->audio_eof = 1;
            return len*8;
        }
        case 8: {
@@ -200,10 +231,13 @@ int videoreader_vfw_getsamples(videoreader_t* vr, void*buf, int num)
            do {
                ((SHORT*)buf)[t] = (((BYTE*)buf)[t]<<8)^0x8000;
            } while(--t>=0);
+           if(!len) i->audio_eof = 1;
            return len*2;
        }
        case 16: {
-           return readAudioBlock(i, buf, num);
+           int len = readAudioBlock(i, buf, num);
+           if(!len) i->audio_eof = 1;
+           return len;
        }
        default: {
            return 0;
@@ -211,7 +245,7 @@ int videoreader_vfw_getsamples(videoreader_t* vr, void*buf, int num)
     }
 }
 
-void videoreader_vfw_close(videoreader_t* vr)
+static void videoreader_vfw_close(videoreader_t* vr)
 {
     videoreader_vfw_internal_t* i = (videoreader_vfw_internal_t*)vr->internal;
 
@@ -223,12 +257,21 @@ void videoreader_vfw_close(videoreader_t* vr)
        AVIStreamRelease(i->as); i->vs = 0;
     }
     AVIFileRelease(i->avifile); i->avifile = 0;
-    AVIFileExit();
+    
+    AVIFileExit(); avifile_initialized=0;
 
     free(vr->internal); vr->internal = 0;
 }
 
-void videoreader_vfw_setparameter(videoreader_t* vr, char*name, char*value) {}
+static void videoreader_vfw_setparameter(videoreader_t*vr, char*name, char*value)
+{
+    videoreader_vfw_internal_t* i = (videoreader_vfw_internal_t*)vr->internal;
+    if(!strcmp(name, "flip")) {
+       i->flip = atoi(value);
+    } else if(!strcmp(name, "verbose")) {
+       verbose = atoi(value);
+    }
+}
 
 int videoreader_vfw_open(videoreader_t* vr, char* filename)
 {
@@ -242,13 +285,14 @@ int videoreader_vfw_open(videoreader_t* vr, char* filename)
     memset(i, 0, sizeof(videoreader_vfw_internal_t));
 
     vr->internal = i;
-    vr->eof = videoreader_vfw_eof;
     vr->getimage = videoreader_vfw_getimage;
     vr->getsamples = videoreader_vfw_getsamples;
     vr->close = videoreader_vfw_close;
     vr->setparameter = videoreader_vfw_setparameter;
 
-    AVIFileInit();
+    if(!avifile_initialized) {
+       AVIFileInit();
+    }
     if(AVIFileOpen(&i->avifile, filename, OF_SHARE_DENY_WRITE, 0)) {
         fprintf(stderr, "Couldn't open %s\n", filename);
         return -1;
@@ -259,7 +303,11 @@ int videoreader_vfw_open(videoreader_t* vr, char* filename)
     /* calculate framerate */
     i->fps = (double)info.dwRate/(double)info.dwScale;
 
-    unsigned int t;
+    if(verbose) {
+       printf("vfw: file %s has %f fps, and %d streams\n", i->fps, info.dwStreams);
+    }
+
+    unsigned int t=0;
     while(t<info.dwStreams) {
        PAVISTREAM stream;
         if(AVIFileGetStream(i->avifile, &stream, streamtypeANY, t) != AVIERR_OK || !stream)
@@ -272,33 +320,46 @@ int videoreader_vfw_open(videoreader_t* vr, char* filename)
            /* video stream */
 
            BITMAPINFOHEADER bitmap;
-           LONG size = sizeof(i->bitmap);
-           AVIStreamReadFormat(i->vs, 0, &bitmap, &size);
+           LONG size = sizeof(bitmap);
+           AVIStreamReadFormat(stream, 0, &bitmap, &size);
 
-           if(i->bitmap.biCompression == 0/*RGB*/) {
+           if(1) {
                i->bitmap = bitmap;
                i->vs = stream;
-               i->width = bitmap.biWidth;
-               i->height = bitmap.biHeight;
+               i->width = abs(bitmap.biWidth);
+               i->height = abs(bitmap.biHeight);
+           } else {
+               fprintf(stderr, "Ignoring video stream: %dx%d compression=%d planes=%d\n", 
+                       abs(bitmap.biWidth), abs(bitmap.biHeight),
+                       bitmap.biCompression,bitmap.biPlanes);
            }
         }
         else if (streaminfo.fccType == streamtypeAUDIO) {
            /* audio stream */
 
            WAVEFORMATEX waveformat;
-           LONG size = sizeof(i->waveformat);
-           AVIStreamReadFormat(i->as, 0, &waveformat, &size);
+           LONG size = sizeof(waveformat);
+           AVIStreamReadFormat(stream, 0, &waveformat, &size);
 
-           if(i->waveformat.wBitsPerSample == 16 || i->waveformat.wBitsPerSample == 8) {
+           if(waveformat.wBitsPerSample == 16 || 
+              waveformat.wBitsPerSample == 8 ||
+              waveformat.wBitsPerSample == 1
+              ) {
                i->waveformat = waveformat;
                i->as = stream;
-               i->channels = i->waveformat.nChannels;
-               i->samplerate = i->waveformat.nSamplesPerSec;
+               i->channels = waveformat.nChannels;
+               i->samplerate = waveformat.nSamplesPerSec;
+           } else {
+               fprintf(stderr, "Ignoring audio stream: bitspersample=%d\n", waveformat.wBitsPerSample);
            }
         }
+       t++;
     }
 
     if(i->vs) {
+       if(verbose) {
+           printf("vfw: video stream: %dx%d, %.2f\n", i->width, i->height, i->fps);
+       }
        vr->width = i->width;
        vr->height = i->height;
        vr->fps = i->fps;
@@ -306,6 +367,9 @@ int videoreader_vfw_open(videoreader_t* vr, char* filename)
        fprintf(stderr, "AVIReader: Warning: No video stream\n");
     }
     if(i->as) {
+       if(verbose) {
+           printf("vfw: audio stream: %d channels, %d samples/sec", i->channels, i->samplerate);
+       }
        vr->channels = i->channels;
        vr->samplerate = i->samplerate;
     } else {
@@ -314,6 +378,7 @@ int videoreader_vfw_open(videoreader_t* vr, char* filename)
     
     i->getframe = AVIStreamGetFrameOpen(i->vs, 0);
     if(!i->getframe) {
+       fprintf(stderr, "Couldn't initialize AVIStream for %s- codec missing?\n", filename);
        return -1;
     }