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