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