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