Only trace FOURCC handler when one is specified
[wine] / dlls / winmm / mciavi / mmoutput.c
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2
3 /*
4  * Digital video MCI Wine Driver
5  *
6  * Copyright 1999, 2000 Eric POUECH
7  * Copyright 2003 Dmitry Timoshkov
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23
24 #include "private_mciavi.h"
25 #include "wine/debug.h"
26
27 WINE_DEFAULT_DEBUG_CHANNEL(mciavi);
28
29 static BOOL MCIAVI_GetInfoAudio(WINE_MCIAVI* wma, const MMCKINFO* mmckList, MMCKINFO *mmckStream)
30 {
31     MMCKINFO    mmckInfo;
32
33     mmioRead(wma->hFile, (LPSTR)&wma->ash_audio, sizeof(wma->ash_audio));
34
35     TRACE("ash.fccType='%c%c%c%c'\n",           LOBYTE(LOWORD(wma->ash_audio.fccType)),
36                                                 HIBYTE(LOWORD(wma->ash_audio.fccType)),
37                                                 LOBYTE(HIWORD(wma->ash_audio.fccType)),
38                                                 HIBYTE(HIWORD(wma->ash_audio.fccType)));
39     if (wma->ash_audio.fccHandler) /* not all streams specify a handler */
40         TRACE("ash.fccHandler='%c%c%c%c'\n",    LOBYTE(LOWORD(wma->ash_audio.fccHandler)),
41                                                 HIBYTE(LOWORD(wma->ash_audio.fccHandler)),
42                                                 LOBYTE(HIWORD(wma->ash_audio.fccHandler)),
43                                                 HIBYTE(HIWORD(wma->ash_audio.fccHandler)));
44     else
45         TRACE("ash.fccHandler=0, no handler specified\n");
46     TRACE("ash.dwFlags=%ld\n",                  wma->ash_audio.dwFlags);
47     TRACE("ash.wPriority=%d\n",                 wma->ash_audio.wPriority);
48     TRACE("ash.wLanguage=%d\n",                 wma->ash_audio.wLanguage);
49     TRACE("ash.dwInitialFrames=%ld\n",          wma->ash_audio.dwInitialFrames);
50     TRACE("ash.dwScale=%ld\n",                  wma->ash_audio.dwScale);
51     TRACE("ash.dwRate=%ld\n",                   wma->ash_audio.dwRate);
52     TRACE("ash.dwStart=%ld\n",                  wma->ash_audio.dwStart);
53     TRACE("ash.dwLength=%ld\n",                 wma->ash_audio.dwLength);
54     TRACE("ash.dwSuggestedBufferSize=%ld\n",    wma->ash_audio.dwSuggestedBufferSize);
55     TRACE("ash.dwQuality=%ld\n",                wma->ash_audio.dwQuality);
56     TRACE("ash.dwSampleSize=%ld\n",             wma->ash_audio.dwSampleSize);
57     TRACE("ash.rcFrame=(%d,%d,%d,%d)\n",        wma->ash_audio.rcFrame.top, wma->ash_audio.rcFrame.left,
58           wma->ash_audio.rcFrame.bottom, wma->ash_audio.rcFrame.right);
59
60     /* rewind to the start of the stream */
61     mmioAscend(wma->hFile, mmckStream, 0);
62
63     mmckInfo.ckid = ckidSTREAMFORMAT;
64     if (mmioDescend(wma->hFile, &mmckInfo, mmckList, MMIO_FINDCHUNK) != 0) {
65        WARN("Can't find 'strf' chunk\n");
66         return FALSE;
67     }
68     if (mmckInfo.cksize < sizeof(WAVEFORMAT)) {
69         WARN("Size of strf chunk (%ld) < audio format struct\n", mmckInfo.cksize);
70         return FALSE;
71     }
72     wma->lpWaveFormat = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
73     if (!wma->lpWaveFormat) {
74         WARN("Can't alloc WaveFormat\n");
75         return FALSE;
76     }
77
78     mmioRead(wma->hFile, (LPSTR)wma->lpWaveFormat, mmckInfo.cksize);
79
80     TRACE("waveFormat.wFormatTag=%d\n",         wma->lpWaveFormat->wFormatTag);
81     TRACE("waveFormat.nChannels=%d\n",          wma->lpWaveFormat->nChannels);
82     TRACE("waveFormat.nSamplesPerSec=%ld\n",    wma->lpWaveFormat->nSamplesPerSec);
83     TRACE("waveFormat.nAvgBytesPerSec=%ld\n",   wma->lpWaveFormat->nAvgBytesPerSec);
84     TRACE("waveFormat.nBlockAlign=%d\n",        wma->lpWaveFormat->nBlockAlign);
85     TRACE("waveFormat.wBitsPerSample=%d\n",     wma->lpWaveFormat->wBitsPerSample);
86     if (mmckInfo.cksize >= sizeof(WAVEFORMATEX))
87         TRACE("waveFormat.cbSize=%d\n",                 wma->lpWaveFormat->cbSize);
88
89     return TRUE;
90 }
91
92 static BOOL MCIAVI_GetInfoVideo(WINE_MCIAVI* wma, const MMCKINFO* mmckList, MMCKINFO* mmckStream)
93 {
94     MMCKINFO    mmckInfo;
95
96     mmioRead(wma->hFile, (LPSTR)&wma->ash_video, sizeof(wma->ash_video));
97
98     TRACE("ash.fccType='%c%c%c%c'\n",           LOBYTE(LOWORD(wma->ash_video.fccType)),
99                                                 HIBYTE(LOWORD(wma->ash_video.fccType)),
100                                                 LOBYTE(HIWORD(wma->ash_video.fccType)),
101                                                 HIBYTE(HIWORD(wma->ash_video.fccType)));
102     TRACE("ash.fccHandler='%c%c%c%c'\n",        LOBYTE(LOWORD(wma->ash_video.fccHandler)),
103                                                 HIBYTE(LOWORD(wma->ash_video.fccHandler)),
104                                                 LOBYTE(HIWORD(wma->ash_video.fccHandler)),
105                                                 HIBYTE(HIWORD(wma->ash_video.fccHandler)));
106     TRACE("ash.dwFlags=%ld\n",                  wma->ash_video.dwFlags);
107     TRACE("ash.wPriority=%d\n",                 wma->ash_video.wPriority);
108     TRACE("ash.wLanguage=%d\n",                 wma->ash_video.wLanguage);
109     TRACE("ash.dwInitialFrames=%ld\n",          wma->ash_video.dwInitialFrames);
110     TRACE("ash.dwScale=%ld\n",                  wma->ash_video.dwScale);
111     TRACE("ash.dwRate=%ld\n",                   wma->ash_video.dwRate);
112     TRACE("ash.dwStart=%ld\n",                  wma->ash_video.dwStart);
113     TRACE("ash.dwLength=%ld\n",                 wma->ash_video.dwLength);
114     TRACE("ash.dwSuggestedBufferSize=%ld\n",    wma->ash_video.dwSuggestedBufferSize);
115     TRACE("ash.dwQuality=%ld\n",                wma->ash_video.dwQuality);
116     TRACE("ash.dwSampleSize=%ld\n",             wma->ash_video.dwSampleSize);
117     TRACE("ash.rcFrame=(%d,%d,%d,%d)\n",        wma->ash_video.rcFrame.top, wma->ash_video.rcFrame.left,
118           wma->ash_video.rcFrame.bottom, wma->ash_video.rcFrame.right);
119
120     /* rewind to the start of the stream */
121     mmioAscend(wma->hFile, mmckStream, 0);
122
123     mmckInfo.ckid = ckidSTREAMFORMAT;
124     if (mmioDescend(wma->hFile, &mmckInfo, mmckList, MMIO_FINDCHUNK) != 0) {
125        WARN("Can't find 'strf' chunk\n");
126         return FALSE;
127     }
128
129     wma->inbih = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
130     if (!wma->inbih) {
131         WARN("Can't alloc input BIH\n");
132         return FALSE;
133     }
134
135     mmioRead(wma->hFile, (LPSTR)wma->inbih, mmckInfo.cksize);
136
137     TRACE("bih.biSize=%ld\n",           wma->inbih->biSize);
138     TRACE("bih.biWidth=%ld\n",          wma->inbih->biWidth);
139     TRACE("bih.biHeight=%ld\n",         wma->inbih->biHeight);
140     TRACE("bih.biPlanes=%d\n",          wma->inbih->biPlanes);
141     TRACE("bih.biBitCount=%d\n",        wma->inbih->biBitCount);
142     TRACE("bih.biCompression=%lx\n",    wma->inbih->biCompression);
143     TRACE("bih.biSizeImage=%ld\n",      wma->inbih->biSizeImage);
144     TRACE("bih.biXPelsPerMeter=%ld\n",  wma->inbih->biXPelsPerMeter);
145     TRACE("bih.biYPelsPerMeter=%ld\n",  wma->inbih->biYPelsPerMeter);
146     TRACE("bih.biClrUsed=%ld\n",        wma->inbih->biClrUsed);
147     TRACE("bih.biClrImportant=%ld\n",   wma->inbih->biClrImportant);
148
149     wma->source.left = 0;
150     wma->source.top = 0;
151     wma->source.right = wma->inbih->biWidth;
152     wma->source.bottom = wma->inbih->biHeight;
153
154     wma->dest = wma->source;
155
156     return TRUE;
157 }
158
159 struct AviListBuild {
160     DWORD       numVideoFrames;
161     DWORD       numAudioAllocated;
162     DWORD       numAudioBlocks;
163     DWORD       inVideoSize;
164     DWORD       inAudioSize;
165 };
166
167 static BOOL     MCIAVI_AddFrame(WINE_MCIAVI* wma, LPMMCKINFO mmck,
168                                 struct AviListBuild* alb)
169 {
170     const BYTE *p;
171     DWORD stream_n;
172     DWORD twocc;
173
174     if (mmck->ckid == ckidAVIPADDING) return TRUE;
175
176     p = (const BYTE *)&mmck->ckid;
177
178     if (!isxdigit(p[0]) || !isxdigit(p[1]))
179     {
180         WARN("wrongly encoded stream #\n");
181         return FALSE;
182     }
183
184     stream_n = (p[0] <= '9') ? (p[0] - '0') : (tolower(p[0]) - 'a' + 10);
185     stream_n <<= 4;
186     stream_n |= (p[1] <= '9') ? (p[1] - '0') : (tolower(p[1]) - 'a' + 10);
187
188     TRACE("ckid %4.4s (stream #%ld)\n", (LPSTR)&mmck->ckid, stream_n);
189
190     /* Some (rare?) AVI files have video streams name XXYY where XX = stream number and YY = TWOCC
191      * of the last 2 characters of the biCompression member of the BITMAPINFOHEADER structure.
192      * Ex: fccHandler = IV32 & biCompression = IV32 => stream name = XX32
193      *     fccHandler = MSVC & biCompression = CRAM => stream name = XXAM
194      * Another possibility is that these TWOCC are simply ignored.
195      * Default to cktypeDIBcompressed when this case happens.
196      */
197     twocc = TWOCCFromFOURCC(mmck->ckid);
198     if (twocc == TWOCCFromFOURCC(wma->inbih->biCompression))
199         twocc = cktypeDIBcompressed;
200     
201     switch (twocc) {
202     case cktypeDIBbits:
203     case cktypeDIBcompressed:
204     case cktypePALchange:
205         if (stream_n != wma->video_stream_n)
206         {
207             TRACE("data belongs to another video stream #%ld\n", stream_n);
208             return FALSE;
209         }
210
211         TRACE("Adding video frame[%ld]: %ld bytes\n",
212               alb->numVideoFrames, mmck->cksize);
213
214         if (alb->numVideoFrames < wma->dwPlayableVideoFrames) {
215             wma->lpVideoIndex[alb->numVideoFrames].dwOffset = mmck->dwDataOffset;
216             wma->lpVideoIndex[alb->numVideoFrames].dwSize = mmck->cksize;
217             if (alb->inVideoSize < mmck->cksize)
218                 alb->inVideoSize = mmck->cksize;
219             alb->numVideoFrames++;
220         } else {
221             WARN("Too many video frames\n");
222         }
223         break;
224     case cktypeWAVEbytes:
225         if (stream_n != wma->audio_stream_n)
226         {
227             TRACE("data belongs to another audio stream #%ld\n", stream_n);
228             return FALSE;
229         }
230
231         TRACE("Adding audio frame[%ld]: %ld bytes\n",
232               alb->numAudioBlocks, mmck->cksize);
233         if (wma->lpWaveFormat) {
234             if (alb->numAudioBlocks >= alb->numAudioAllocated) {
235                 alb->numAudioAllocated += 32;
236                 if (!wma->lpAudioIndex)
237                     wma->lpAudioIndex = HeapAlloc(GetProcessHeap(), 0,
238                                                   alb->numAudioAllocated * sizeof(struct MMIOPos));
239                 else
240                     wma->lpAudioIndex = HeapReAlloc(GetProcessHeap(), 0, wma->lpAudioIndex,
241                                                     alb->numAudioAllocated * sizeof(struct MMIOPos));
242                 if (!wma->lpAudioIndex) return FALSE;
243             }
244             wma->lpAudioIndex[alb->numAudioBlocks].dwOffset = mmck->dwDataOffset;
245             wma->lpAudioIndex[alb->numAudioBlocks].dwSize = mmck->cksize;
246             if (alb->inAudioSize < mmck->cksize)
247                 alb->inAudioSize = mmck->cksize;
248             alb->numAudioBlocks++;
249         } else {
250             WARN("Wave chunk without wave format... discarding\n");
251         }
252         break;
253     default:
254         WARN("Unknown frame type %4.4s\n", (LPSTR)&mmck->ckid);
255         break;
256     }
257     return TRUE;
258 }
259
260 BOOL MCIAVI_GetInfo(WINE_MCIAVI* wma)
261 {
262     MMCKINFO            ckMainRIFF;
263     MMCKINFO            mmckHead;
264     MMCKINFO            mmckList;
265     MMCKINFO            mmckInfo;
266     struct AviListBuild alb;
267     DWORD stream_n;
268
269     if (mmioDescend(wma->hFile, &ckMainRIFF, NULL, 0) != 0) {
270         WARN("Can't find 'RIFF' chunk\n");
271         return FALSE;
272     }
273
274     if ((ckMainRIFF.ckid != FOURCC_RIFF) || (ckMainRIFF.fccType != formtypeAVI)) {
275         WARN("Can't find 'AVI ' chunk\n");
276         return FALSE;
277     }
278
279     mmckHead.fccType = listtypeAVIHEADER;
280     if (mmioDescend(wma->hFile, &mmckHead, &ckMainRIFF, MMIO_FINDLIST) != 0) {
281         WARN("Can't find 'hdrl' list\n");
282         return FALSE;
283     }
284
285     mmckInfo.ckid = ckidAVIMAINHDR;
286     if (mmioDescend(wma->hFile, &mmckInfo, &mmckHead, MMIO_FINDCHUNK) != 0) {
287         WARN("Can't find 'avih' chunk\n");
288         return FALSE;
289     }
290
291     mmioRead(wma->hFile, (LPSTR)&wma->mah, sizeof(wma->mah));
292
293     TRACE("mah.dwMicroSecPerFrame=%ld\n",       wma->mah.dwMicroSecPerFrame);
294     TRACE("mah.dwMaxBytesPerSec=%ld\n",         wma->mah.dwMaxBytesPerSec);
295     TRACE("mah.dwPaddingGranularity=%ld\n",     wma->mah.dwPaddingGranularity);
296     TRACE("mah.dwFlags=%ld\n",                  wma->mah.dwFlags);
297     TRACE("mah.dwTotalFrames=%ld\n",            wma->mah.dwTotalFrames);
298     TRACE("mah.dwInitialFrames=%ld\n",          wma->mah.dwInitialFrames);
299     TRACE("mah.dwStreams=%ld\n",                wma->mah.dwStreams);
300     TRACE("mah.dwSuggestedBufferSize=%ld\n",    wma->mah.dwSuggestedBufferSize);
301     TRACE("mah.dwWidth=%ld\n",                  wma->mah.dwWidth);
302     TRACE("mah.dwHeight=%ld\n",                 wma->mah.dwHeight);
303
304     mmioAscend(wma->hFile, &mmckInfo, 0);
305
306     TRACE("Start of streams\n");
307     wma->video_stream_n = 0;
308     wma->audio_stream_n = 0;
309
310     for (stream_n = 0; stream_n < wma->mah.dwStreams; stream_n++)
311     {
312         MMCKINFO mmckStream;
313
314         mmckList.fccType = listtypeSTREAMHEADER;
315         if (mmioDescend(wma->hFile, &mmckList, &mmckHead, MMIO_FINDLIST) != 0)
316             break;
317
318         mmckStream.ckid = ckidSTREAMHEADER;
319         if (mmioDescend(wma->hFile, &mmckStream, &mmckList, MMIO_FINDCHUNK) != 0)
320         {
321             WARN("Can't find 'strh' chunk\n");
322             continue;
323         }
324
325         TRACE("Stream #%ld fccType %4.4s\n", stream_n, (LPSTR)&mmckStream.fccType);
326
327         if (mmckStream.fccType == streamtypeVIDEO)
328         {
329             TRACE("found video stream\n");
330             if (wma->inbih)
331                 WARN("ignoring another video stream\n");
332             else
333             {
334                 if (!MCIAVI_GetInfoVideo(wma, &mmckList, &mmckStream))
335                     return FALSE;
336                 wma->video_stream_n = stream_n;
337             }
338         }
339         else if (mmckStream.fccType == streamtypeAUDIO)
340         {
341             TRACE("found audio stream\n");
342             if (wma->lpWaveFormat)
343                 WARN("ignoring another audio stream\n");
344             else
345             {
346                 if (!MCIAVI_GetInfoAudio(wma, &mmckList, &mmckStream))
347                     return FALSE;
348                 wma->audio_stream_n = stream_n;
349             }
350         }
351         else
352             TRACE("Unsupported stream type %4.4s\n", (LPSTR)&mmckStream.fccType);
353
354         mmioAscend(wma->hFile, &mmckList, 0);
355     }
356
357     TRACE("End of streams\n");
358
359     mmioAscend(wma->hFile, &mmckHead, 0);
360
361     /* no need to read optional JUNK chunk */
362
363     mmckList.fccType = listtypeAVIMOVIE;
364     if (mmioDescend(wma->hFile, &mmckList, &ckMainRIFF, MMIO_FINDLIST) != 0) {
365         WARN("Can't find 'movi' list\n");
366         return FALSE;
367     }
368
369     wma->dwPlayableVideoFrames = wma->mah.dwTotalFrames;
370     wma->lpVideoIndex = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
371                                   wma->dwPlayableVideoFrames * sizeof(struct MMIOPos));
372     if (!wma->lpVideoIndex) {
373         WARN("Can't alloc video index array\n");
374         return FALSE;
375     }
376     wma->dwPlayableAudioBlocks = 0;
377     wma->lpAudioIndex = NULL;
378
379     alb.numAudioBlocks = alb.numVideoFrames = 0;
380     alb.inVideoSize = alb.inAudioSize = 0;
381     alb.numAudioAllocated = 0;
382
383     while (mmioDescend(wma->hFile, &mmckInfo, &mmckList, 0) == 0) {
384         if (mmckInfo.fccType == listtypeAVIRECORD) {
385             MMCKINFO    tmp;
386
387             while (mmioDescend(wma->hFile, &tmp, &mmckInfo, 0) == 0) {
388                 MCIAVI_AddFrame(wma, &tmp, &alb);
389                 mmioAscend(wma->hFile, &tmp, 0);
390             }
391         } else {
392             MCIAVI_AddFrame(wma, &mmckInfo, &alb);
393         }
394
395         mmioAscend(wma->hFile, &mmckInfo, 0);
396     }
397     if (alb.numVideoFrames != wma->dwPlayableVideoFrames) {
398         WARN("Found %ld video frames (/%ld), reducing playable frames\n",
399              alb.numVideoFrames, wma->dwPlayableVideoFrames);
400         wma->dwPlayableVideoFrames = alb.numVideoFrames;
401     }
402     wma->dwPlayableAudioBlocks = alb.numAudioBlocks;
403
404     if (alb.inVideoSize > wma->ash_video.dwSuggestedBufferSize) {
405         WARN("inVideoSize=%ld suggestedSize=%ld\n", alb.inVideoSize, wma->ash_video.dwSuggestedBufferSize);
406         wma->ash_video.dwSuggestedBufferSize = alb.inVideoSize;
407     }
408     if (alb.inAudioSize > wma->ash_audio.dwSuggestedBufferSize) {
409         WARN("inAudioSize=%ld suggestedSize=%ld\n", alb.inAudioSize, wma->ash_audio.dwSuggestedBufferSize);
410         wma->ash_audio.dwSuggestedBufferSize = alb.inAudioSize;
411     }
412
413     wma->indata = HeapAlloc(GetProcessHeap(), 0, wma->ash_video.dwSuggestedBufferSize);
414     if (!wma->indata) {
415         WARN("Can't alloc input buffer\n");
416         return FALSE;
417     }
418
419     return TRUE;
420 }
421
422 BOOL    MCIAVI_OpenVideo(WINE_MCIAVI* wma)
423 {
424     HDC hDC;
425     DWORD       outSize;
426     FOURCC      fcc = wma->ash_video.fccHandler;
427
428     TRACE("fcc %4.4s\n", (LPSTR)&fcc);
429
430     wma->dwCachedFrame = -1;
431
432     /* get the right handle */
433     if (fcc == mmioFOURCC('C','R','A','M')) fcc = mmioFOURCC('M','S','V','C');
434
435     /* try to get a decompressor for that type */
436     wma->hic = ICLocate(ICTYPE_VIDEO, fcc, wma->inbih, NULL, ICMODE_DECOMPRESS);
437     if (!wma->hic) {
438         /* check for builtin DIB compressions */
439         fcc = wma->inbih->biCompression;
440         if ((fcc == mmioFOURCC('D','I','B',' ')) ||
441             (fcc == mmioFOURCC('R','L','E',' ')) ||
442             (fcc == BI_RGB) || (fcc == BI_RLE8) ||
443             (fcc == BI_RLE4) || (fcc == BI_BITFIELDS))
444             goto paint_frame;
445
446         WARN("Can't locate codec for the file\n");
447         return FALSE;
448     }
449
450     outSize = sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD);
451
452     wma->outbih = HeapAlloc(GetProcessHeap(), 0, outSize);
453     if (!wma->outbih) {
454         WARN("Can't alloc output BIH\n");
455         return FALSE;
456     }
457     if (!ICGetDisplayFormat(wma->hic, wma->inbih, wma->outbih, 0, 0, 0)) {
458         WARN("Can't open decompressor\n");
459         return FALSE;
460     }
461
462     TRACE("bih.biSize=%ld\n",           wma->outbih->biSize);
463     TRACE("bih.biWidth=%ld\n",          wma->outbih->biWidth);
464     TRACE("bih.biHeight=%ld\n",         wma->outbih->biHeight);
465     TRACE("bih.biPlanes=%d\n",          wma->outbih->biPlanes);
466     TRACE("bih.biBitCount=%d\n",        wma->outbih->biBitCount);
467     TRACE("bih.biCompression=%lx\n",    wma->outbih->biCompression);
468     TRACE("bih.biSizeImage=%ld\n",      wma->outbih->biSizeImage);
469     TRACE("bih.biXPelsPerMeter=%ld\n",  wma->outbih->biXPelsPerMeter);
470     TRACE("bih.biYPelsPerMeter=%ld\n",  wma->outbih->biYPelsPerMeter);
471     TRACE("bih.biClrUsed=%ld\n",        wma->outbih->biClrUsed);
472     TRACE("bih.biClrImportant=%ld\n",   wma->outbih->biClrImportant);
473
474     wma->outdata = HeapAlloc(GetProcessHeap(), 0, wma->outbih->biSizeImage);
475     if (!wma->outdata) {
476         WARN("Can't alloc output buffer\n");
477         return FALSE;
478     }
479
480     if (ICSendMessage(wma->hic, ICM_DECOMPRESS_BEGIN,
481                       (DWORD)wma->inbih, (DWORD)wma->outbih) != ICERR_OK) {
482         WARN("Can't begin decompression\n");
483         return FALSE;
484     }
485
486 paint_frame:
487     hDC = wma->hWndPaint ? GetDC(wma->hWndPaint) : 0;
488     if (hDC)
489     {
490         MCIAVI_PaintFrame(wma, hDC);
491         ReleaseDC(wma->hWndPaint, hDC);
492     }
493     return TRUE;
494 }
495
496 static void CALLBACK MCIAVI_waveCallback(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance,
497                                         DWORD_PTR dwParam1, DWORD_PTR dwParam2)
498 {
499     WINE_MCIAVI *wma = (WINE_MCIAVI *)MCIAVI_mciGetOpenDev(dwInstance);
500
501     if (!wma) return;
502
503     EnterCriticalSection(&wma->cs);
504
505     switch (uMsg) {
506     case WOM_OPEN:
507     case WOM_CLOSE:
508         break;
509     case WOM_DONE:
510         InterlockedIncrement(&wma->dwEventCount);
511         TRACE("Returning waveHdr=%lx\n", dwParam1);
512         SetEvent(wma->hEvent);
513         break;
514     default:
515         ERR("Unknown uMsg=%d\n", uMsg);
516     }
517
518     LeaveCriticalSection(&wma->cs);
519 }
520
521 DWORD MCIAVI_OpenAudio(WINE_MCIAVI* wma, unsigned* nHdr, LPWAVEHDR* pWaveHdr)
522 {
523     DWORD       dwRet;
524     LPWAVEHDR   waveHdr;
525     unsigned    i;
526
527     dwRet = waveOutOpen((HWAVEOUT *)&wma->hWave, WAVE_MAPPER, wma->lpWaveFormat,
528                        (DWORD_PTR)MCIAVI_waveCallback, wma->wDevID, CALLBACK_FUNCTION);
529     if (dwRet != 0) {
530         TRACE("Can't open low level audio device %ld\n", dwRet);
531         dwRet = MCIERR_DEVICE_OPEN;
532         wma->hWave = 0;
533         goto cleanUp;
534     }
535
536     /* FIXME: should set up a heuristic to compute the number of wave headers
537      * to be used...
538      */
539     *nHdr = 7;
540     waveHdr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
541                         *nHdr * (sizeof(WAVEHDR) + wma->ash_audio.dwSuggestedBufferSize));
542     if (!waveHdr) {
543         TRACE("Can't alloc wave headers\n");
544         dwRet = MCIERR_DEVICE_OPEN;
545         goto cleanUp;
546     }
547
548     for (i = 0; i < *nHdr; i++) {
549         /* other fields are zero:ed on allocation */
550         waveHdr[i].lpData = (char*)waveHdr +
551             *nHdr * sizeof(WAVEHDR) + i * wma->ash_audio.dwSuggestedBufferSize;
552         waveHdr[i].dwBufferLength = wma->ash_audio.dwSuggestedBufferSize;
553         if (waveOutPrepareHeader(wma->hWave, &waveHdr[i], sizeof(WAVEHDR))) {
554             dwRet = MCIERR_INTERNAL;
555             goto cleanUp;
556         }
557     }
558
559     if (wma->dwCurrVideoFrame != 0 && wma->lpWaveFormat) {
560         FIXME("Should recompute dwCurrAudioBlock, except unsynchronized sound & video\n");
561     }
562     wma->dwCurrAudioBlock = 0;
563
564     wma->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
565     wma->dwEventCount = *nHdr - 1;
566     *pWaveHdr = waveHdr;
567  cleanUp:
568     return dwRet;
569 }
570
571 void MCIAVI_PlayAudioBlocks(WINE_MCIAVI* wma, unsigned nHdr, LPWAVEHDR waveHdr)
572 {
573     if (!wma->lpAudioIndex) 
574         return;
575     TRACE("%ld (ec=%lu)\n", wma->lpAudioIndex[wma->dwCurrAudioBlock].dwOffset, wma->dwEventCount);
576
577     /* push as many blocks as possible => audio gets priority */
578     while (wma->dwStatus != MCI_MODE_STOP && wma->dwStatus != MCI_MODE_NOT_READY &&
579            wma->dwCurrAudioBlock < wma->dwPlayableAudioBlocks) {
580         unsigned        whidx = wma->dwCurrAudioBlock % nHdr;
581
582         ResetEvent(wma->hEvent);
583         if (InterlockedDecrement(&wma->dwEventCount) < 0 ||
584             !wma->lpAudioIndex[wma->dwCurrAudioBlock].dwOffset)
585         {
586             InterlockedIncrement(&wma->dwEventCount);
587             break;
588         }
589
590         mmioSeek(wma->hFile, wma->lpAudioIndex[wma->dwCurrAudioBlock].dwOffset, SEEK_SET);
591         mmioRead(wma->hFile, waveHdr[whidx].lpData, wma->lpAudioIndex[wma->dwCurrAudioBlock].dwSize);
592
593         waveHdr[whidx].dwFlags &= ~WHDR_DONE;
594         waveHdr[whidx].dwBufferLength = wma->lpAudioIndex[wma->dwCurrAudioBlock].dwSize;
595         waveOutWrite(wma->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
596         wma->dwCurrAudioBlock++;
597     }
598 }
599
600 LRESULT MCIAVI_PaintFrame(WINE_MCIAVI* wma, HDC hDC)
601 {
602     void*               pBitmapData = NULL;
603     LPBITMAPINFO        pBitmapInfo = NULL;
604     HDC                 hdcMem;
605     HBITMAP             hbmOld;
606     int                 nWidth;
607     int                 nHeight;
608
609     if (!hDC || !wma->inbih)
610         return TRUE;
611
612     TRACE("Painting frame %lu (cached %lu)\n", wma->dwCurrVideoFrame, wma->dwCachedFrame);
613
614     if (wma->dwCurrVideoFrame != wma->dwCachedFrame)
615     {
616         if (!wma->lpVideoIndex[wma->dwCurrVideoFrame].dwOffset)
617             return FALSE;
618
619         if (wma->lpVideoIndex[wma->dwCurrVideoFrame].dwSize)
620         {
621             mmioSeek(wma->hFile, wma->lpVideoIndex[wma->dwCurrVideoFrame].dwOffset, SEEK_SET);
622             mmioRead(wma->hFile, wma->indata, wma->lpVideoIndex[wma->dwCurrVideoFrame].dwSize);
623
624             /* FIXME ? */
625             wma->inbih->biSizeImage = wma->lpVideoIndex[wma->dwCurrVideoFrame].dwSize;
626
627             if (wma->hic && ICDecompress(wma->hic, 0, wma->inbih, wma->indata,
628                                          wma->outbih, wma->outdata) != ICERR_OK)
629             {
630                 WARN("Decompression error\n");
631                 return FALSE;
632             }
633         }
634
635         wma->dwCachedFrame = wma->dwCurrVideoFrame;
636     }
637
638     if (wma->hic) {
639         pBitmapData = wma->outdata;
640         pBitmapInfo = (LPBITMAPINFO)wma->outbih;
641
642         nWidth = wma->outbih->biWidth;
643         nHeight = wma->outbih->biHeight;
644     } else {
645         pBitmapData = wma->indata;
646         pBitmapInfo = (LPBITMAPINFO)wma->inbih;
647
648         nWidth = wma->inbih->biWidth;
649         nHeight = wma->inbih->biHeight;
650     }
651
652     if (!wma->hbmFrame)
653         wma->hbmFrame = CreateCompatibleBitmap(hDC, nWidth, nHeight);
654
655     SetDIBits(hDC, wma->hbmFrame, 0, nHeight, pBitmapData, pBitmapInfo, DIB_RGB_COLORS);
656
657     hdcMem = CreateCompatibleDC(hDC);
658     hbmOld = SelectObject(hdcMem, wma->hbmFrame);
659
660     StretchBlt(hDC,
661                wma->dest.left, wma->dest.top, wma->dest.right, wma->dest.bottom,
662                hdcMem,
663                wma->source.left, wma->source.top, wma->source.right, wma->source.bottom,
664                SRCCOPY);
665
666     SelectObject(hdcMem, hbmOld);
667     DeleteDC(hdcMem);
668     return TRUE;
669 }