added avifile_initialized flag.
[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 #ifdef WIN32
23 #include <windows.h>
24 #include <vfw.h>
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include "videoreader.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 } videoreader_vfw_internal_t;
53
54 bool videoreader_vfw_eof(videoreader_t* vr)
55 {
56     videoreader_vfw_internal_t* i = (videoreader_vfw_internal_t*)vr->internal;
57     return (i->video_pos >= i->video_end);
58 }
59
60 static int bitmap_to_rgba(BITMAPINFOHEADER*bi, void*buffer, const int dest_width, const int dest_height)
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     if(bi->biBitCount==1) {
87         int y;
88         UCHAR*img = data;
89         for(y=0;y<dest_height;y++) {
90             UCHAR*line = &img[linex*y];
91             int x;
92             for(x=0;x<dest_width;x++) {
93                 *dest++ = 255*((line[x/8]>>(x&7))&1);
94             }
95         }
96     } else if(bi->biBitCount==4) {
97         int y;
98         UCHAR*img = &data[bi->biClrUsed*4];
99         UCHAR*pal = data;
100         for(y=0;y<dest_height;y++) {
101             UCHAR*line = &img[linex*y];
102             int x;
103             for(x=0;x<dest_width/2;x++) {
104                 *dest++ = 255|pal[(line[0]>>4)<<2|0]<<8|pal[(line[0]>>4)<<2|1]<<16|pal[(line[0]>>4)<<2|2]<<24;
105                 *dest++ = 255|pal[(line[0]&0x0f)<<2|0]<<8|pal[(line[0]&0x0f)<<2|1]<<16|pal[(line[0]&0x0f)<<2|2]<<24;
106                 line++;
107             }
108         }
109     } else if(bi->biBitCount==8) {
110         int y;
111         UCHAR*img = &data[bi->biClrUsed*4];
112         UCHAR*pal = data;
113         for(y=0;y<dest_height;y++) {
114             UCHAR*line = &img[linex*y];
115             int x;
116             for(x=0;x<dest_width;x++) {
117                 *dest++ = 255|pal[line[0]*4+2]<<8|pal[line[0]*4+1]<<16|pal[line[0]*4+0]<<24;
118                 line++;
119             }
120         }
121     } else if(bi->biBitCount==24) {
122         UCHAR*img = data;
123         int y;
124         for(y=0;y<dest_height;y++) {
125             UCHAR*line = &img[linex*y];
126             int x;
127             for(x=0;x<dest_width;x++) {
128                 *dest++ = 255|line[2]<<8|line[1]<<16|line[0]<<24;
129                 line+=3;
130             }
131         }
132     } else if(bi->biBitCount==32) {
133         UCHAR*img = data;
134         int y;
135         for(y=0;y<dest_height;y++) {
136             UCHAR*line = &img[linex*y];
137             int x;
138             for(x=0;x<dest_width;x++) {
139                 *dest++ = 255|line[0]<<8|line[1]<<16|line[2]<<24;
140                 line+=4;
141             }
142         }
143     } else {
144         fprintf(stderr, "Unsupported format: bitcount=%d\n", bi->biBitCount);
145         return 0;
146     }
147     return 1;
148 }
149
150 int videoreader_vfw_getimage(videoreader_t* vr, void*buffer)
151 {
152     videoreader_vfw_internal_t* i = (videoreader_vfw_internal_t*)vr->internal;
153
154     if(videoreader_vfw_eof(vr))
155         return 0;
156
157     LPBITMAPINFOHEADER bi;
158     bi = (LPBITMAPINFOHEADER)AVIStreamGetFrame(i->getframe, i->video_pos);
159         
160     i->video_pos++;
161
162     if(!bi) {
163         fprintf(stderr, "AVIStreamGetFrame failed\n");
164         return 0;
165     }
166     printf("%dx%d:%d\n", bi->biWidth, bi->biHeight, bi->biBitCount);
167     
168     if(!bitmap_to_rgba(bi, buffer, i->width, i->height)) {
169         fprintf(stderr, "couldn't convert bitmap to RGBA.\n");
170         return 0;
171     }
172     return i->width*i->height*4;
173 }
174
175 static int readAudioBlock(videoreader_vfw_internal_t* i, void*buf, int len)
176 {
177     LONG bytes;
178     LONG samples;
179     AVIStreamRead(i->as, i->audio_pos, len/(2*i->waveformat.nChannels), buf, len, &bytes, &samples);
180     i->audio_pos += samples;
181     return bytes;
182 }
183
184 int videoreader_vfw_getsamples(videoreader_t* vr, void*buf, int num)
185 {
186     videoreader_vfw_internal_t* i = (videoreader_vfw_internal_t*)vr->internal;
187    
188     switch(i->waveformat.wBitsPerSample) {
189         case 1: {
190             int len = readAudioBlock(i, buf, num);
191             int t = len-1;
192             do {
193                 ((SHORT*)buf)[t] = ((((BYTE*)buf)[t>>3])>>(t&7))<<15;
194             } while(--t>=0);
195             return len*8;
196         }
197         case 8: {
198             int len = readAudioBlock(i, buf, num);
199             int t = len-1;
200             do {
201                 ((SHORT*)buf)[t] = (((BYTE*)buf)[t]<<8)^0x8000;
202             } while(--t>=0);
203             return len*2;
204         }
205         case 16: {
206             return readAudioBlock(i, buf, num);
207         }
208         default: {
209             return 0;
210         }
211     }
212 }
213
214 void videoreader_vfw_close(videoreader_t* vr)
215 {
216     videoreader_vfw_internal_t* i = (videoreader_vfw_internal_t*)vr->internal;
217
218     AVIStreamGetFrameClose(i->getframe);
219     if(i->vs) {
220         AVIStreamRelease(i->vs); i->vs = 0;
221     }
222     if(i->as) {
223         AVIStreamRelease(i->as); i->vs = 0;
224     }
225     AVIFileRelease(i->avifile); i->avifile = 0;
226     
227     AVIFileExit(); avifile_initialized=0;
228
229     free(vr->internal); vr->internal = 0;
230 }
231
232 void videoreader_vfw_setparameter(videoreader_t* vr, char*name, char*value) {}
233
234 static int avifile_initialized = 0;
235
236 int videoreader_vfw_open(videoreader_t* vr, char* filename)
237 {
238     memset(vr, 0, sizeof(videoreader_t));
239     if(!filename) {
240         /* codec query */
241         return 1;
242     }
243
244     videoreader_vfw_internal_t* i = (videoreader_vfw_internal_t*)malloc(sizeof(videoreader_vfw_internal_t));
245     memset(i, 0, sizeof(videoreader_vfw_internal_t));
246
247     vr->internal = i;
248     vr->eof = videoreader_vfw_eof;
249     vr->getimage = videoreader_vfw_getimage;
250     vr->getsamples = videoreader_vfw_getsamples;
251     vr->close = videoreader_vfw_close;
252     vr->setparameter = videoreader_vfw_setparameter;
253
254     if(!avifile_initialized) {
255         AVIFileInit();
256     }
257     if(AVIFileOpen(&i->avifile, filename, OF_SHARE_DENY_WRITE, 0)) {
258         fprintf(stderr, "Couldn't open %s\n", filename);
259         return -1;
260     }
261     AVIFILEINFO info;
262     AVIFileInfo(i->avifile, &info, sizeof(info));
263    
264     /* calculate framerate */
265     i->fps = (double)info.dwRate/(double)info.dwScale;
266
267     unsigned int t;
268     while(t<info.dwStreams) {
269         PAVISTREAM stream;
270         if(AVIFileGetStream(i->avifile, &stream, streamtypeANY, t) != AVIERR_OK || !stream)
271             break; //video_end of (working) streams
272
273         AVISTREAMINFO streaminfo;
274         AVIStreamInfo(stream, &streaminfo, sizeof(streaminfo));
275
276         if (streaminfo.fccType == streamtypeVIDEO) {
277             /* video stream */
278
279             BITMAPINFOHEADER bitmap;
280             LONG size = sizeof(i->bitmap);
281             AVIStreamReadFormat(i->vs, 0, &bitmap, &size);
282
283             if(i->bitmap.biCompression == 0/*RGB*/) {
284                 i->bitmap = bitmap;
285                 i->vs = stream;
286                 i->width = bitmap.biWidth;
287                 i->height = bitmap.biHeight;
288             }
289         }
290         else if (streaminfo.fccType == streamtypeAUDIO) {
291             /* audio stream */
292
293             WAVEFORMATEX waveformat;
294             LONG size = sizeof(i->waveformat);
295             AVIStreamReadFormat(i->as, 0, &waveformat, &size);
296
297             if(i->waveformat.wBitsPerSample == 16 || i->waveformat.wBitsPerSample == 8) {
298                 i->waveformat = waveformat;
299                 i->as = stream;
300                 i->channels = i->waveformat.nChannels;
301                 i->samplerate = i->waveformat.nSamplesPerSec;
302             }
303         }
304     }
305
306     if(i->vs) {
307         vr->width = i->width;
308         vr->height = i->height;
309         vr->fps = i->fps;
310     } else {
311         fprintf(stderr, "AVIReader: Warning: No video stream\n");
312     }
313     if(i->as) {
314         vr->channels = i->channels;
315         vr->samplerate = i->samplerate;
316     } else {
317         fprintf(stderr, "AVIReader: Warning: No audio stream\n");
318     }
319     
320     i->getframe = AVIStreamGetFrameOpen(i->vs, 0);
321     if(!i->getframe) {
322         return -1;
323     }
324     
325     i->video_pos = AVIStreamStart(i->vs);
326     i->video_end = AVIStreamEnd(i->vs);
327     i->audio_pos = 0;
328     i->audio_end = 0x7fffffff;
329
330     return 0;
331 }
332
333 #else //WIN32
334
335 int videoreader_vfw_open(videoreader_t* vr, char* filename)
336 {
337     return -1;
338 }
339
340 #endif //WIN32
341