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