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