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