Fixed image size and line size, added 8bit color/palette handling.
[wine] / programs / avitools / aviplay.c
1 /*
2  * Very simple AVIPLAYER
3  * 
4  * Copyright 1999 Marcus Meissner
5  * 
6  * Status:
7  *      - plays .avi streams, video only
8  *      - requires MicroSoft avifil32.dll and builtin msvfw32.dll.
9  *
10  * Todo:
11  *      - audio support (including synchronization etc)
12  *      - replace DirectDraw by a 'normal' window, including dithering, controls
13  *        etc.
14  *      
15  * Bugs:
16  *      - no time scheduling, video plays too fast using DirectDraw/XF86DGA 
17  *      - requires DirectDraw with all disadvantages.
18  */
19
20 #include <stdio.h>
21 #include <time.h>
22 #include <assert.h>
23 #include <strings.h>
24 #include "wintypes.h"
25 #include "windows.h"
26 #include "wingdi.h"
27 #include "mmsystem.h"
28 #include "ddraw.h"
29 #include "vfw.h"
30
31
32 int PASCAL WinMain (HANDLE hInstance, HANDLE prev, LPSTR cmdline, int show)
33 {
34     int                 bytesline,i,n,pos;
35     time_t              tstart,tend;
36     LONG                cnt;
37     BITMAPINFOHEADER    *bmi;
38     HRESULT             hres;
39     HMODULE             avifil32 = LoadLibrary("avifil32.dll");
40     PAVIFILE            avif;
41     PAVISTREAM          vids=NULL,auds=NULL;
42     AVIFILEINFO         afi;
43     AVISTREAMINFO       asi;
44     PGETFRAME           vidgetframe=NULL;
45     LPDIRECTDRAW        ddraw;
46     DDSURFACEDESC       dsdesc;
47     LPDIRECTDRAWSURFACE dsurf;
48     LPDIRECTDRAWPALETTE dpal;
49     PALETTEENTRY        palent[256];
50
51
52 void    (WINAPI *fnAVIFileInit)(void);
53 void    (WINAPI *fnAVIFileExit)(void);
54 ULONG   (WINAPI *fnAVIFileRelease)(PAVIFILE);
55 ULONG   (WINAPI *fnAVIStreamRelease)(PAVISTREAM);
56 HRESULT (WINAPI *fnAVIFileOpen)(PAVIFILE * ppfile,LPCTSTR szFile,UINT uMode,LPCLSID lpHandler);
57 HRESULT (WINAPI *fnAVIFileInfo)(PAVIFILE ppfile,AVIFILEINFO *afi,LONG size);
58 HRESULT (WINAPI *fnAVIFileGetStream)(PAVIFILE ppfile,PAVISTREAM *afi,DWORD fccType,LONG lParam);
59 HRESULT (WINAPI *fnAVIStreamInfo)(PAVISTREAM iface,AVISTREAMINFO *afi,LONG size);
60 HRESULT (WINAPI *fnAVIStreamReadFormat)(PAVISTREAM iface,LONG pos,LPVOID format,LPLONG size);
61 PGETFRAME (WINAPI *fnAVIStreamGetFrameOpen)(PAVISTREAM iface,LPBITMAPINFOHEADER wanted);
62 LPVOID (WINAPI *fnAVIStreamGetFrame)(PGETFRAME pg,LONG pos);
63 HRESULT (WINAPI *fnAVIStreamGetFrameClose)(PGETFRAME pg);
64
65 #define XX(x) fn##x = (void*)GetProcAddress(avifil32,#x);assert(fn##x);
66 #ifdef UNICODE
67 # define XXT(x) fn##x = (void*)GetProcAddress(avifil32,#x##"W");assert(fn##x);
68 #else
69 # define XXT(x) fn##x = (void*)GetProcAddress(avifil32,#x##"A");assert(fn##x);
70 #endif
71         /* non character dependend routines: */
72         XX (AVIFileInit);
73         XX (AVIFileExit);
74         XX (AVIFileRelease);
75         XX (AVIFileGetStream);
76         XX (AVIStreamRelease);
77         XX (AVIStreamReadFormat);
78         XX (AVIStreamGetFrameOpen);
79         XX (AVIStreamGetFrame);
80         XX (AVIStreamGetFrameClose);
81         /* A/W routines: */
82         XXT(AVIFileOpen);
83         XXT(AVIFileInfo);
84         XXT(AVIStreamInfo);
85 #undef XX
86 #undef XXT
87
88     
89     fnAVIFileInit();
90     if (-1==GetFileAttributes(cmdline)) {
91         fprintf(stderr,"Usage: aviplay <avifilename>\n");
92         exit(1);
93     }
94     hres = fnAVIFileOpen(&avif,cmdline,OF_READ,NULL);
95     if (hres) {
96         fprintf(stderr,"AVIFileOpen: 0x%08lx\n",hres);
97         exit(1);
98     }
99     hres = fnAVIFileInfo(avif,&afi,sizeof(afi));
100     if (hres) {
101         fprintf(stderr,"AVIFileInfo: 0x%08lx\n",hres);
102         exit(1);
103     }
104     for (n=0;n<afi.dwStreams;n++) {
105             char buf[5];
106             PAVISTREAM  ast;
107
108             hres = fnAVIFileGetStream(avif,&ast,0,n);
109             if (hres) {
110                 fprintf(stderr,"AVIFileGetStream %d: 0x%08lx\n",n,hres);
111                 exit(1);
112             }
113             hres = fnAVIStreamInfo(ast,&asi,sizeof(asi));
114             if (hres) {
115                 fprintf(stderr,"AVIStreamInfo %d: 0x%08lx\n",n,hres);
116                 exit(1);
117             }
118             fprintf(stderr,"[Stream %d: ",n);
119             buf[4]='\0';memcpy(buf,&(asi.fccType),4);
120             fprintf(stderr,"%s.",buf);
121             buf[4]='\0';memcpy(buf,&(asi.fccHandler),4);
122             fprintf(stderr,"%s, %s]\n",buf,asi.szName);
123             switch (asi.fccType) {
124             case streamtypeVIDEO:
125                 vids = ast;
126                 break;
127             case streamtypeAUDIO: 
128                 auds = ast;
129                 break;
130             default:  {
131                 char type[5];
132                 type[4]='\0';memcpy(type,&(asi.fccType),4);
133
134                 fprintf(stderr,"Unhandled streamtype %s\n",type);
135                 fnAVIStreamRelease(ast);
136                 break;
137             }
138             }
139     }
140 /********************* begin video setup ***********************************/
141     if (!vids) {
142         fprintf(stderr,"No video stream found. Good Bye.\n");
143         exit(0);
144     }
145     cnt = sizeof(BITMAPINFOHEADER)+256*sizeof(RGBQUAD);
146     bmi = HeapAlloc(GetProcessHeap(),0,cnt);
147     hres = fnAVIStreamReadFormat(vids,0,bmi,&cnt);
148     if (hres) {
149         fprintf(stderr,"AVIStreamReadFormat vids: 0x%08lx\n",hres);
150         exit(1);
151     }
152     vidgetframe = NULL;
153     bmi->biCompression = 0; /* we want it in raw form, uncompressed */
154     /* recalculate the image size */
155     bmi->biSizeImage = ((bmi->biWidth*bmi->biBitCount+31)&~0x1f)*bmi->biPlanes*bmi->biHeight/8;
156     bytesline = ((bmi->biWidth*bmi->biBitCount+31)&~0x1f)*bmi->biPlanes/8;
157     vidgetframe = fnAVIStreamGetFrameOpen(vids,bmi);
158     if (!vidgetframe) {
159         fprintf(stderr,"AVIStreamGetFrameOpen: failed\n");
160         exit(1);
161     }
162 /********************* end video setup ***********************************/
163
164 /********************* begin display setup *******************************/
165     hres = DirectDrawCreate(NULL,&ddraw,NULL);
166     if (hres) {
167         fprintf(stderr,"DirectDrawCreate: 0x%08lx\n",hres);
168         exit(1);
169     }
170     hres = ddraw->lpvtbl->fnSetDisplayMode(ddraw,bmi->biWidth,bmi->biHeight,bmi->biBitCount);
171     if (hres) {
172         fprintf(stderr,"ddraw.SetDisplayMode: 0x%08lx (change resolution!)\n",hres);
173         exit(1);
174     }
175     dsdesc.dwSize=sizeof(dsdesc);
176     dsdesc.dwFlags = DDSD_CAPS;
177     dsdesc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
178     hres = ddraw->lpvtbl->fnCreateSurface(ddraw,&dsdesc,&dsurf,NULL);
179     if (hres) {
180         fprintf(stderr,"ddraw.CreateSurface: 0x%08lx\n",hres);
181         exit(1);
182     }
183     if (bmi->biBitCount==8) {
184         RGBQUAD         *rgb = (RGBQUAD*)(bmi+1);
185         int             i;
186
187         hres = ddraw->lpvtbl->fnCreatePalette(ddraw,DDPCAPS_8BIT,NULL,&dpal,NULL);
188         if (hres) {
189             fprintf(stderr,"ddraw.CreateSurface: 0x%08lx\n",hres);
190             exit(1);
191         }
192         dsurf->lpvtbl->fnSetPalette(dsurf,dpal);
193         for (i=0;i<bmi->biClrUsed;i++) {
194             palent[i].peRed = rgb[i].rgbRed;
195             palent[i].peBlue = rgb[i].rgbBlue;
196             palent[i].peGreen = rgb[i].rgbGreen;
197         }
198         dpal->lpvtbl->fnSetEntries(dpal,0,0,bmi->biClrUsed,palent);
199     } else
200         dpal = NULL;
201 /********************* end display setup *******************************/
202
203     tstart = time(NULL);
204     pos = 0;
205     while (1) {
206         LPVOID          decodedframe;
207         LPBITMAPINFOHEADER lpbmi;
208         LPVOID          decodedbits;
209
210 /* video stuff */
211         if (!(decodedframe=fnAVIStreamGetFrame(vidgetframe,pos++)))
212             break;
213         lpbmi = (LPBITMAPINFOHEADER)decodedframe;
214         decodedbits = (LPVOID)(((DWORD)decodedframe)+lpbmi->biSize);
215         if (lpbmi->biBitCount == 8) {
216         /* cant detect palette change that way I think */
217             RGBQUAD     *rgb = (RGBQUAD*)(lpbmi+1);
218             int         i,palchanged;
219
220             /* skip used colorentries. */
221             decodedbits+=bmi->biClrUsed*sizeof(RGBQUAD);
222             palchanged = 0;
223             for (i=0;i<bmi->biClrUsed;i++) {
224                 if (    (palent[i].peRed != rgb[i].rgbRed) ||
225                         (palent[i].peBlue != rgb[i].rgbBlue) ||
226                         (palent[i].peGreen != rgb[i].rgbGreen)
227                 ) {
228                         palchanged = 1;
229                         break;
230                 }
231             }
232             if (palchanged) {
233                 for (i=0;i<bmi->biClrUsed;i++) {
234                     palent[i].peRed = rgb[i].rgbRed;
235                     palent[i].peBlue = rgb[i].rgbBlue;
236                     palent[i].peGreen = rgb[i].rgbGreen;
237                 }
238                 dpal->lpvtbl->fnSetEntries(dpal,0,0,bmi->biClrUsed,palent);
239             }
240         }
241         dsdesc.dwSize = sizeof(dsdesc);
242         hres = dsurf->lpvtbl->fnLock(dsurf,NULL,&dsdesc,DDLOCK_WRITEONLY,0);
243         if (hres) {
244             fprintf(stderr,"dsurf.Lock: 0x%08lx\n",hres);
245             exit(1);
246         }
247         /* Argh. AVIs are upside down. */
248         for (i=0;i<dsdesc.dwHeight;i++) {
249             memcpy( dsdesc.y.lpSurface+(i*dsdesc.lPitch),
250                     decodedbits+bytesline*(dsdesc.dwHeight-i-1),
251                     bytesline
252             );
253         }
254         dsurf->lpvtbl->fnUnlock(dsurf,dsdesc.y.lpSurface);
255     }
256     tend = time(NULL);
257     fnAVIStreamGetFrameClose(vidgetframe);
258
259     ((IUnknown*)dsurf)->lpvtbl->fnRelease((IUnknown*)dsurf);
260     ddraw->lpvtbl->fnRestoreDisplayMode(ddraw);
261     ((IUnknown*)ddraw)->lpvtbl->fnRelease((IUnknown*)ddraw);
262     if (vids) fnAVIStreamRelease(vids);
263     if (auds) fnAVIStreamRelease(auds);
264     fprintf(stderr,"%d frames at %g frames/s\n",pos,pos*1.0/(tend-tstart));
265     fnAVIFileRelease(avif);
266     fnAVIFileExit();
267     return 0;    
268 }