small fixes
[swftools.git] / avi2swf / videoreader_vfw.cc
1 /* videoreader_vfw.cc
2    Read avi files using Video For Windows (vfw).
3
4    Part of the swftools package.
5    
6    Copyright (c) 2004 Matthias Kramm <kramm@quiss.org>
7  
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
21
22 #include "videoreader.h"
23 #ifdef WIN32
24 #include <windows.h>
25 #include <vfw.h>
26 #include <stdlib.h>
27 #include <stdio.h>
28
29 typedef struct _videoreader_vfw_internal { 
30     //video:
31     PAVISTREAM vs;
32     //audio:
33     PAVISTREAM as;
34
35     PGETFRAME getframe;
36     IAVIFile* avifile;
37     BITMAPINFOHEADER bitmap;
38     WAVEFORMATEX waveformat;
39
40     int audio_eof;
41     int video_eof;
42
43     int video_pos;
44     int video_end;
45
46     int audio_pos;
47     int audio_end;
48
49     float fps;
50     int width,height;
51
52     int samplerate;
53     int channels;
54
55     int flip;
56 } videoreader_vfw_internal_t;
57
58 static int avifile_initialized = 0;
59 static int verbose;
60
61 #define _TRACE_ {printf("vfw: %s: %d (%s)\n",__FILE__,__LINE__,__func__);fflush(stdout);}
62
63 static int bitmap_to_rgba(BITMAPINFOHEADER*bi, void*buffer, const int dest_width, const int dest_height, int flip)
64 {
65     UCHAR*data = (UCHAR*)(bi+1); // actual bitmap data starts after the header
66
67     if(bi->biPlanes!=1 || bi->biCompression!=0 || bi->biBitCount%4!=0) {
68         /* unsupported format */
69         fprintf(stderr, "bitmap_to_rgba: unsupported format: biPlanes=%d, biCompression=%d biBitCount=%d\n",
70                 bi->biPlanes, bi->biCompression, bi->biBitCount);
71         return 0;
72     }
73     
74     ULONG*dest = (ULONG*)buffer;
75
76     int width = abs(bi->biWidth);
77     int height = abs(bi->biHeight);
78     if(dest_width != width || dest_height != height) {
79         /* TODO: size conversion */
80         fprintf(stderr, "size mismatch: %dx%d != %dx%d\n", width, height, dest_width, dest_height);
81         return 0;
82     }
83
84     /* convert the various image types to RGBA-
85        TODO: is there some way to let the Windows API do this? */
86     int bytesperpixel = ((bi->biWidth*bi->biBitCount)+7)&~7;
87     int linex = ((bytesperpixel/8)+3)&~3;
88     memset(dest, 255, dest_width*dest_height*4);//pre-fill alpha channel
89
90     const int starty = flip? 0 : dest_height-1;
91     const int endy   = flip? dest_height : -1;
92     const int yinc   = flip? 1 : -1;
93
94     if(verbose) {
95         printf("vfw: Convering scanlines %d to %d from bpp %d, %d stepping, flip=%d\n", starty, endy, bi->biBitCount, yinc, flip);
96     }
97
98     if(bi->biBitCount==1) {
99         UCHAR*img = data;
100         int y;
101         for(y=starty;y!=endy;y+=yinc) {
102             UCHAR*line = &img[linex*y];
103             int x;
104             for(x=0;x<dest_width;x++) {
105                 *dest++ = 255*((line[x/8]>>(x&7))&1);
106             }
107         }
108     } else if(bi->biBitCount==4) {
109         UCHAR*img = &data[bi->biClrUsed*4];
110         UCHAR*pal = data;
111         int y;
112         for(y=starty;y!=endy;y+=yinc) {
113             UCHAR*line = &img[linex*y];
114             int x;
115             for(x=0;x<dest_width/2;x++) {
116                 *dest++ = 255|pal[(line[0]>>4)<<2|0]<<8|pal[(line[0]>>4)<<2|1]<<16|pal[(line[0]>>4)<<2|2]<<24;
117                 *dest++ = 255|pal[(line[0]&0x0f)<<2|0]<<8|pal[(line[0]&0x0f)<<2|1]<<16|pal[(line[0]&0x0f)<<2|2]<<24;
118                 line++;
119             }
120         }
121     } else if(bi->biBitCount==8) {
122         UCHAR*img = &data[bi->biClrUsed*4];
123         UCHAR*pal = data;
124         int y;
125         for(y=starty;y!=endy;y+=yinc) {
126             UCHAR*line = &img[linex*y];
127             int x;
128             for(x=0;x<dest_width;x++) {
129                 *dest++ = 255|pal[line[0]*4+2]<<8|pal[line[0]*4+1]<<16|pal[line[0]*4+0]<<24;
130                 line++;
131             }
132         }
133     } else if(bi->biBitCount==16) {
134         UCHAR*img = data;
135         int y;
136         for(y=starty;y!=endy;y+=yinc) {
137             UCHAR*line = &img[linex*y];
138             int x;
139             for(x=0;x<dest_width;x++) {
140                 USHORT c = line[0]|line[1]<<8;
141                 *dest++ = 255|(c&0x1f)<<(24+3)|(c>>5&0x1f)<<(16+3)|(c>>10&0x1f)<<(8+3);
142                 line+=2;
143             }
144         }
145     } else if(bi->biBitCount==24) {
146         UCHAR*img = data;
147         int y;
148         for(y=starty;y!=endy;y+=yinc) {
149             UCHAR*line = &img[linex*y];
150             int x;
151             for(x=0;x<dest_width;x++) {
152                 *dest++ = 255|line[2]<<8|line[1]<<16|line[0]<<24;
153                 line+=3;
154             }
155         }
156     } else if(bi->biBitCount==32) {
157         UCHAR*img = data;
158         int y;
159         for(y=starty;y!=endy;y+=yinc) {
160             UCHAR*line = &img[linex*y];
161             int x;
162             for(x=0;x<dest_width;x++) {
163                 *dest++ = 255|line[0]<<8|line[1]<<16|line[2]<<24;
164                 line+=4;
165             }
166         }
167     } else {
168         fprintf(stderr, "Unsupported format: bitcount=%d\n", bi->biBitCount);
169         return 0;
170     }
171     return 1;
172 }
173
174 static int videoreader_vfw_getimage(videoreader_t* vr, void*buffer)
175 {
176     videoreader_vfw_internal_t* i = (videoreader_vfw_internal_t*)vr->internal;
177
178     if (i->video_pos >= i->video_end)
179         i->video_eof = 1;
180
181     if(i->video_eof)
182         return 0;
183
184     LPBITMAPINFOHEADER bi;
185     bi = (LPBITMAPINFOHEADER)AVIStreamGetFrame(i->getframe, i->video_pos);
186         
187     i->video_pos++;
188     vr->frame++;
189
190     if(!bi) {
191         fprintf(stderr, "AVIStreamGetFrame failed\n");
192         return 0;
193     }
194     
195     if(!bitmap_to_rgba(bi, buffer, i->width, i->height, i->flip)) {
196         fprintf(stderr, "couldn't convert bitmap to RGBA.\n");
197         return 0;
198     }
199     return i->width*i->height*4;
200 }
201
202 static int readAudioBlock(videoreader_vfw_internal_t* i, void*buf, int len)
203 {
204     LONG bytes;
205     LONG samples;
206     AVIStreamRead(i->as, i->audio_pos, len/(2*i->waveformat.nChannels), buf, len, &bytes, &samples);
207     i->audio_pos += samples;
208     return bytes;
209 }
210
211 static int videoreader_vfw_getsamples(videoreader_t* vr, void*buf, int num)
212 {
213     videoreader_vfw_internal_t* i = (videoreader_vfw_internal_t*)vr->internal;
214
215     if(i->audio_eof)
216         return 0;
217    
218     switch(i->waveformat.wBitsPerSample) {
219         case 1: {
220             int len = readAudioBlock(i, buf, num);
221             int t = len-1;
222             do {
223                 ((SHORT*)buf)[t] = ((((BYTE*)buf)[t>>3])>>(t&7))<<15;
224             } while(--t>=0);
225             if(!len) i->audio_eof = 1;
226             return len*8;
227         }
228         case 8: {
229             int len = readAudioBlock(i, buf, num);
230             int t = len-1;
231             do {
232                 ((SHORT*)buf)[t] = (((BYTE*)buf)[t]<<8)^0x8000;
233             } while(--t>=0);
234             if(!len) i->audio_eof = 1;
235             return len*2;
236         }
237         case 16: {
238             int len = readAudioBlock(i, buf, num);
239             if(!len) i->audio_eof = 1;
240             return len;
241         }
242         default: {
243             return 0;
244         }
245     }
246 }
247
248 static void videoreader_vfw_close(videoreader_t* vr)
249 {
250     videoreader_vfw_internal_t* i = (videoreader_vfw_internal_t*)vr->internal;
251
252     AVIStreamGetFrameClose(i->getframe);
253     if(i->vs) {
254         AVIStreamRelease(i->vs); i->vs = 0;
255     }
256     if(i->as) {
257         AVIStreamRelease(i->as); i->vs = 0;
258     }
259     AVIFileRelease(i->avifile); i->avifile = 0;
260     
261     AVIFileExit(); avifile_initialized=0;
262
263     free(vr->internal); vr->internal = 0;
264 }
265
266 static void videoreader_vfw_setparameter(videoreader_t*vr, char*name, char*value)
267 {
268     videoreader_vfw_internal_t* i = (videoreader_vfw_internal_t*)vr->internal;
269     if(!strcmp(name, "flip")) {
270         i->flip = atoi(value);
271     } else if(!strcmp(name, "verbose")) {
272         verbose = atoi(value);
273     }
274 }
275
276 int videoreader_vfw_open(videoreader_t* vr, char* filename)
277 {
278     memset(vr, 0, sizeof(videoreader_t));
279     if(!filename) {
280         /* codec query */
281         return 1;
282     }
283
284     videoreader_vfw_internal_t* i = (videoreader_vfw_internal_t*)malloc(sizeof(videoreader_vfw_internal_t));
285     memset(i, 0, sizeof(videoreader_vfw_internal_t));
286
287     vr->internal = i;
288     vr->getimage = videoreader_vfw_getimage;
289     vr->getsamples = videoreader_vfw_getsamples;
290     vr->close = videoreader_vfw_close;
291     vr->setparameter = videoreader_vfw_setparameter;
292
293     if(!avifile_initialized) {
294         AVIFileInit();
295     }
296     if(AVIFileOpen(&i->avifile, filename, OF_SHARE_DENY_WRITE, 0)) {
297         fprintf(stderr, "Couldn't open %s\n", filename);
298         return -1;
299     }
300     AVIFILEINFO info;
301     AVIFileInfo(i->avifile, &info, sizeof(info));
302    
303     /* calculate framerate */
304     i->fps = (double)info.dwRate/(double)info.dwScale;
305
306     if(verbose) {
307         printf("vfw: file %s has %f fps, and %d streams\n", i->fps, info.dwStreams);
308     }
309
310     unsigned int t=0;
311     while(t<info.dwStreams) {
312         PAVISTREAM stream;
313         if(AVIFileGetStream(i->avifile, &stream, streamtypeANY, t) != AVIERR_OK || !stream)
314             break; //video_end of (working) streams
315
316         AVISTREAMINFO streaminfo;
317         AVIStreamInfo(stream, &streaminfo, sizeof(streaminfo));
318
319         if (streaminfo.fccType == streamtypeVIDEO) {
320             /* video stream */
321
322             BITMAPINFOHEADER bitmap;
323             LONG size = sizeof(bitmap);
324             AVIStreamReadFormat(stream, 0, &bitmap, &size);
325
326             if(1) {
327                 i->bitmap = bitmap;
328                 i->vs = stream;
329                 i->width = abs(bitmap.biWidth);
330                 i->height = abs(bitmap.biHeight);
331             } else {
332                 fprintf(stderr, "Ignoring video stream: %dx%d compression=%d planes=%d\n", 
333                         abs(bitmap.biWidth), abs(bitmap.biHeight),
334                         bitmap.biCompression,bitmap.biPlanes);
335             }
336         }
337         else if (streaminfo.fccType == streamtypeAUDIO) {
338             /* audio stream */
339
340             WAVEFORMATEX waveformat;
341             LONG size = sizeof(waveformat);
342             AVIStreamReadFormat(stream, 0, &waveformat, &size);
343
344             if(waveformat.wBitsPerSample == 16 || 
345                waveformat.wBitsPerSample == 8 ||
346                waveformat.wBitsPerSample == 1
347                ) {
348                 i->waveformat = waveformat;
349                 i->as = stream;
350                 i->channels = waveformat.nChannels;
351                 i->samplerate = waveformat.nSamplesPerSec;
352             } else {
353                 fprintf(stderr, "Ignoring audio stream: bitspersample=%d\n", waveformat.wBitsPerSample);
354             }
355         }
356         t++;
357     }
358
359     if(i->vs) {
360         if(verbose) {
361             printf("vfw: video stream: %dx%d, %.2f\n", i->width, i->height, i->fps);
362         }
363         vr->width = i->width;
364         vr->height = i->height;
365         vr->fps = i->fps;
366     } else {
367         fprintf(stderr, "AVIReader: Warning: No video stream\n");
368     }
369     if(i->as) {
370         if(verbose) {
371             printf("vfw: audio stream: %d channels, %d samples/sec", i->channels, i->samplerate);
372         }
373         vr->channels = i->channels;
374         vr->samplerate = i->samplerate;
375     } else {
376         fprintf(stderr, "AVIReader: Warning: No audio stream\n");
377     }
378     
379     i->getframe = AVIStreamGetFrameOpen(i->vs, 0);
380     if(!i->getframe) {
381         fprintf(stderr, "Couldn't initialize AVIStream for %s- codec missing?\n", filename);
382         return -1;
383     }
384     
385     i->video_pos = AVIStreamStart(i->vs);
386     i->video_end = AVIStreamEnd(i->vs);
387     i->audio_pos = 0;
388     i->audio_end = 0x7fffffff;
389
390     return 0;
391 }
392
393 #else //WIN32
394
395 int videoreader_vfw_open(videoreader_t* vr, char* filename)
396 {
397     return -1;
398 }
399
400 #endif //WIN32
401