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