Handle avi files with non standard video stream names.
[wine/gsoc_dplay.git] / dlls / winmm / mciavi / mmoutput.c
blob75f25b30a6b33538d4cebd0c86f972fef949396b
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
3 /*
4 * Digital video MCI Wine Driver
6 * Copyright 1999, 2000 Eric POUECH
7 * Copyright 2003 Dmitry Timoshkov
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.
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.
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
24 #include "private_mciavi.h"
25 #include "wine/debug.h"
27 WINE_DEFAULT_DEBUG_CHANNEL(mciavi);
29 static BOOL MCIAVI_GetInfoAudio(WINE_MCIAVI* wma, const MMCKINFO* mmckList, MMCKINFO *mmckStream)
31 MMCKINFO mmckInfo;
33 mmioRead(wma->hFile, (LPSTR)&wma->ash_audio, sizeof(wma->ash_audio));
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 TRACE("ash.fccHandler='%c%c%c%c'\n", LOBYTE(LOWORD(wma->ash_audio.fccHandler)),
40 HIBYTE(LOWORD(wma->ash_audio.fccHandler)),
41 LOBYTE(HIWORD(wma->ash_audio.fccHandler)),
42 HIBYTE(HIWORD(wma->ash_audio.fccHandler)));
43 TRACE("ash.dwFlags=%ld\n", wma->ash_audio.dwFlags);
44 TRACE("ash.wPriority=%d\n", wma->ash_audio.wPriority);
45 TRACE("ash.wLanguage=%d\n", wma->ash_audio.wLanguage);
46 TRACE("ash.dwInitialFrames=%ld\n", wma->ash_audio.dwInitialFrames);
47 TRACE("ash.dwScale=%ld\n", wma->ash_audio.dwScale);
48 TRACE("ash.dwRate=%ld\n", wma->ash_audio.dwRate);
49 TRACE("ash.dwStart=%ld\n", wma->ash_audio.dwStart);
50 TRACE("ash.dwLength=%ld\n", wma->ash_audio.dwLength);
51 TRACE("ash.dwSuggestedBufferSize=%ld\n", wma->ash_audio.dwSuggestedBufferSize);
52 TRACE("ash.dwQuality=%ld\n", wma->ash_audio.dwQuality);
53 TRACE("ash.dwSampleSize=%ld\n", wma->ash_audio.dwSampleSize);
54 TRACE("ash.rcFrame=(%d,%d,%d,%d)\n", wma->ash_audio.rcFrame.top, wma->ash_audio.rcFrame.left,
55 wma->ash_audio.rcFrame.bottom, wma->ash_audio.rcFrame.right);
57 /* rewind to the start of the stream */
58 mmioAscend(wma->hFile, mmckStream, 0);
60 mmckInfo.ckid = ckidSTREAMFORMAT;
61 if (mmioDescend(wma->hFile, &mmckInfo, mmckList, MMIO_FINDCHUNK) != 0) {
62 WARN("Can't find 'strf' chunk\n");
63 return FALSE;
65 if (mmckInfo.cksize < sizeof(WAVEFORMAT)) {
66 WARN("Size of strf chunk (%ld) < audio format struct\n", mmckInfo.cksize);
67 return FALSE;
69 wma->lpWaveFormat = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
70 if (!wma->lpWaveFormat) {
71 WARN("Can't alloc WaveFormat\n");
72 return FALSE;
75 mmioRead(wma->hFile, (LPSTR)wma->lpWaveFormat, mmckInfo.cksize);
77 TRACE("waveFormat.wFormatTag=%d\n", wma->lpWaveFormat->wFormatTag);
78 TRACE("waveFormat.nChannels=%d\n", wma->lpWaveFormat->nChannels);
79 TRACE("waveFormat.nSamplesPerSec=%ld\n", wma->lpWaveFormat->nSamplesPerSec);
80 TRACE("waveFormat.nAvgBytesPerSec=%ld\n", wma->lpWaveFormat->nAvgBytesPerSec);
81 TRACE("waveFormat.nBlockAlign=%d\n", wma->lpWaveFormat->nBlockAlign);
82 TRACE("waveFormat.wBitsPerSample=%d\n", wma->lpWaveFormat->wBitsPerSample);
83 if (mmckInfo.cksize >= sizeof(WAVEFORMATEX))
84 TRACE("waveFormat.cbSize=%d\n", wma->lpWaveFormat->cbSize);
86 return TRUE;
89 static BOOL MCIAVI_GetInfoVideo(WINE_MCIAVI* wma, const MMCKINFO* mmckList, MMCKINFO* mmckStream)
91 MMCKINFO mmckInfo;
93 mmioRead(wma->hFile, (LPSTR)&wma->ash_video, sizeof(wma->ash_video));
95 TRACE("ash.fccType='%c%c%c%c'\n", LOBYTE(LOWORD(wma->ash_video.fccType)),
96 HIBYTE(LOWORD(wma->ash_video.fccType)),
97 LOBYTE(HIWORD(wma->ash_video.fccType)),
98 HIBYTE(HIWORD(wma->ash_video.fccType)));
99 TRACE("ash.fccHandler='%c%c%c%c'\n", LOBYTE(LOWORD(wma->ash_video.fccHandler)),
100 HIBYTE(LOWORD(wma->ash_video.fccHandler)),
101 LOBYTE(HIWORD(wma->ash_video.fccHandler)),
102 HIBYTE(HIWORD(wma->ash_video.fccHandler)));
103 TRACE("ash.dwFlags=%ld\n", wma->ash_video.dwFlags);
104 TRACE("ash.wPriority=%d\n", wma->ash_video.wPriority);
105 TRACE("ash.wLanguage=%d\n", wma->ash_video.wLanguage);
106 TRACE("ash.dwInitialFrames=%ld\n", wma->ash_video.dwInitialFrames);
107 TRACE("ash.dwScale=%ld\n", wma->ash_video.dwScale);
108 TRACE("ash.dwRate=%ld\n", wma->ash_video.dwRate);
109 TRACE("ash.dwStart=%ld\n", wma->ash_video.dwStart);
110 TRACE("ash.dwLength=%ld\n", wma->ash_video.dwLength);
111 TRACE("ash.dwSuggestedBufferSize=%ld\n", wma->ash_video.dwSuggestedBufferSize);
112 TRACE("ash.dwQuality=%ld\n", wma->ash_video.dwQuality);
113 TRACE("ash.dwSampleSize=%ld\n", wma->ash_video.dwSampleSize);
114 TRACE("ash.rcFrame=(%d,%d,%d,%d)\n", wma->ash_video.rcFrame.top, wma->ash_video.rcFrame.left,
115 wma->ash_video.rcFrame.bottom, wma->ash_video.rcFrame.right);
117 /* rewind to the start of the stream */
118 mmioAscend(wma->hFile, mmckStream, 0);
120 mmckInfo.ckid = ckidSTREAMFORMAT;
121 if (mmioDescend(wma->hFile, &mmckInfo, mmckList, MMIO_FINDCHUNK) != 0) {
122 WARN("Can't find 'strf' chunk\n");
123 return FALSE;
126 wma->inbih = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
127 if (!wma->inbih) {
128 WARN("Can't alloc input BIH\n");
129 return FALSE;
132 mmioRead(wma->hFile, (LPSTR)wma->inbih, mmckInfo.cksize);
134 TRACE("bih.biSize=%ld\n", wma->inbih->biSize);
135 TRACE("bih.biWidth=%ld\n", wma->inbih->biWidth);
136 TRACE("bih.biHeight=%ld\n", wma->inbih->biHeight);
137 TRACE("bih.biPlanes=%d\n", wma->inbih->biPlanes);
138 TRACE("bih.biBitCount=%d\n", wma->inbih->biBitCount);
139 TRACE("bih.biCompression=%lx\n", wma->inbih->biCompression);
140 TRACE("bih.biSizeImage=%ld\n", wma->inbih->biSizeImage);
141 TRACE("bih.biXPelsPerMeter=%ld\n", wma->inbih->biXPelsPerMeter);
142 TRACE("bih.biYPelsPerMeter=%ld\n", wma->inbih->biYPelsPerMeter);
143 TRACE("bih.biClrUsed=%ld\n", wma->inbih->biClrUsed);
144 TRACE("bih.biClrImportant=%ld\n", wma->inbih->biClrImportant);
146 wma->source.left = 0;
147 wma->source.top = 0;
148 wma->source.right = wma->inbih->biWidth;
149 wma->source.bottom = wma->inbih->biHeight;
151 wma->dest = wma->source;
153 return TRUE;
156 struct AviListBuild {
157 DWORD numVideoFrames;
158 DWORD numAudioAllocated;
159 DWORD numAudioBlocks;
160 DWORD inVideoSize;
161 DWORD inAudioSize;
164 static BOOL MCIAVI_AddFrame(WINE_MCIAVI* wma, LPMMCKINFO mmck,
165 struct AviListBuild* alb)
167 const BYTE *p;
168 DWORD stream_n;
169 DWORD twocc;
171 if (mmck->ckid == ckidAVIPADDING) return TRUE;
173 p = (const BYTE *)&mmck->ckid;
175 if (!isxdigit(p[0]) || !isxdigit(p[1]))
177 WARN("wrongly encoded stream #\n");
178 return FALSE;
181 stream_n = (p[0] <= '9') ? (p[0] - '0') : (tolower(p[0]) - 'a' + 10);
182 stream_n <<= 4;
183 stream_n |= (p[1] <= '9') ? (p[1] - '0') : (tolower(p[1]) - 'a' + 10);
185 TRACE("ckid %4.4s (stream #%ld)\n", (LPSTR)&mmck->ckid, stream_n);
187 /* Some (rare?) AVI files have video streams name XXYY where XX = stream number and YY = TWOCC
188 * of the last 2 characters of the biCompression member of the BITMAPINFOHEADER structure.
189 * Ex: fccHandler = IV32 & biCompression = IV32 => stream name = XX32
190 * fccHandler = MSVC & biCompression = CRAM => stream name = XXAM
191 * Another possibility is that these TWOCC are simply ignored.
192 * Default to cktypeDIBcompressed when this case happens.
194 twocc = TWOCCFromFOURCC(mmck->ckid);
195 if (twocc == TWOCCFromFOURCC(wma->inbih->biCompression))
196 twocc = cktypeDIBcompressed;
198 switch (twocc) {
199 case cktypeDIBbits:
200 case cktypeDIBcompressed:
201 case cktypePALchange:
202 if (stream_n != wma->video_stream_n)
204 TRACE("data belongs to another video stream #%ld\n", stream_n);
205 return FALSE;
208 TRACE("Adding video frame[%ld]: %ld bytes\n",
209 alb->numVideoFrames, mmck->cksize);
211 if (alb->numVideoFrames < wma->dwPlayableVideoFrames) {
212 wma->lpVideoIndex[alb->numVideoFrames].dwOffset = mmck->dwDataOffset;
213 wma->lpVideoIndex[alb->numVideoFrames].dwSize = mmck->cksize;
214 if (alb->inVideoSize < mmck->cksize)
215 alb->inVideoSize = mmck->cksize;
216 alb->numVideoFrames++;
217 } else {
218 WARN("Too many video frames\n");
220 break;
221 case cktypeWAVEbytes:
222 if (stream_n != wma->audio_stream_n)
224 TRACE("data belongs to another audio stream #%ld\n", stream_n);
225 return FALSE;
228 TRACE("Adding audio frame[%ld]: %ld bytes\n",
229 alb->numAudioBlocks, mmck->cksize);
230 if (wma->lpWaveFormat) {
231 if (alb->numAudioBlocks >= alb->numAudioAllocated) {
232 alb->numAudioAllocated += 32;
233 if (!wma->lpAudioIndex)
234 wma->lpAudioIndex = HeapAlloc(GetProcessHeap(), 0,
235 alb->numAudioAllocated * sizeof(struct MMIOPos));
236 else
237 wma->lpAudioIndex = HeapReAlloc(GetProcessHeap(), 0, wma->lpAudioIndex,
238 alb->numAudioAllocated * sizeof(struct MMIOPos));
239 if (!wma->lpAudioIndex) return FALSE;
241 wma->lpAudioIndex[alb->numAudioBlocks].dwOffset = mmck->dwDataOffset;
242 wma->lpAudioIndex[alb->numAudioBlocks].dwSize = mmck->cksize;
243 if (alb->inAudioSize < mmck->cksize)
244 alb->inAudioSize = mmck->cksize;
245 alb->numAudioBlocks++;
246 } else {
247 WARN("Wave chunk without wave format... discarding\n");
249 break;
250 default:
251 WARN("Unknown frame type %4.4s\n", (LPSTR)&mmck->ckid);
252 break;
254 return TRUE;
257 BOOL MCIAVI_GetInfo(WINE_MCIAVI* wma)
259 MMCKINFO ckMainRIFF;
260 MMCKINFO mmckHead;
261 MMCKINFO mmckList;
262 MMCKINFO mmckInfo;
263 struct AviListBuild alb;
264 DWORD stream_n;
266 if (mmioDescend(wma->hFile, &ckMainRIFF, NULL, 0) != 0) {
267 WARN("Can't find 'RIFF' chunk\n");
268 return FALSE;
271 if ((ckMainRIFF.ckid != FOURCC_RIFF) || (ckMainRIFF.fccType != formtypeAVI)) {
272 WARN("Can't find 'AVI ' chunk\n");
273 return FALSE;
276 mmckHead.fccType = listtypeAVIHEADER;
277 if (mmioDescend(wma->hFile, &mmckHead, &ckMainRIFF, MMIO_FINDLIST) != 0) {
278 WARN("Can't find 'hdrl' list\n");
279 return FALSE;
282 mmckInfo.ckid = ckidAVIMAINHDR;
283 if (mmioDescend(wma->hFile, &mmckInfo, &mmckHead, MMIO_FINDCHUNK) != 0) {
284 WARN("Can't find 'avih' chunk\n");
285 return FALSE;
288 mmioRead(wma->hFile, (LPSTR)&wma->mah, sizeof(wma->mah));
290 TRACE("mah.dwMicroSecPerFrame=%ld\n", wma->mah.dwMicroSecPerFrame);
291 TRACE("mah.dwMaxBytesPerSec=%ld\n", wma->mah.dwMaxBytesPerSec);
292 TRACE("mah.dwPaddingGranularity=%ld\n", wma->mah.dwPaddingGranularity);
293 TRACE("mah.dwFlags=%ld\n", wma->mah.dwFlags);
294 TRACE("mah.dwTotalFrames=%ld\n", wma->mah.dwTotalFrames);
295 TRACE("mah.dwInitialFrames=%ld\n", wma->mah.dwInitialFrames);
296 TRACE("mah.dwStreams=%ld\n", wma->mah.dwStreams);
297 TRACE("mah.dwSuggestedBufferSize=%ld\n", wma->mah.dwSuggestedBufferSize);
298 TRACE("mah.dwWidth=%ld\n", wma->mah.dwWidth);
299 TRACE("mah.dwHeight=%ld\n", wma->mah.dwHeight);
301 mmioAscend(wma->hFile, &mmckInfo, 0);
303 TRACE("Start of streams\n");
304 wma->video_stream_n = 0;
305 wma->audio_stream_n = 0;
307 for (stream_n = 0; stream_n < wma->mah.dwStreams; stream_n++)
309 MMCKINFO mmckStream;
311 mmckList.fccType = listtypeSTREAMHEADER;
312 if (mmioDescend(wma->hFile, &mmckList, &mmckHead, MMIO_FINDLIST) != 0)
313 break;
315 mmckStream.ckid = ckidSTREAMHEADER;
316 if (mmioDescend(wma->hFile, &mmckStream, &mmckList, MMIO_FINDCHUNK) != 0)
318 WARN("Can't find 'strh' chunk\n");
319 continue;
322 TRACE("Stream #%ld fccType %4.4s\n", stream_n, (LPSTR)&mmckStream.fccType);
324 if (mmckStream.fccType == streamtypeVIDEO)
326 TRACE("found video stream\n");
327 if (wma->inbih)
328 WARN("ignoring another video stream\n");
329 else
331 if (!MCIAVI_GetInfoVideo(wma, &mmckList, &mmckStream))
332 return FALSE;
333 wma->video_stream_n = stream_n;
336 else if (mmckStream.fccType == streamtypeAUDIO)
338 TRACE("found audio stream\n");
339 if (wma->lpWaveFormat)
340 WARN("ignoring another audio stream\n");
341 else
343 if (!MCIAVI_GetInfoAudio(wma, &mmckList, &mmckStream))
344 return FALSE;
345 wma->audio_stream_n = stream_n;
348 else
349 TRACE("Unsupported stream type %4.4s\n", (LPSTR)&mmckStream.fccType);
351 mmioAscend(wma->hFile, &mmckList, 0);
354 TRACE("End of streams\n");
356 mmioAscend(wma->hFile, &mmckHead, 0);
358 /* no need to read optional JUNK chunk */
360 mmckList.fccType = listtypeAVIMOVIE;
361 if (mmioDescend(wma->hFile, &mmckList, &ckMainRIFF, MMIO_FINDLIST) != 0) {
362 WARN("Can't find 'movi' list\n");
363 return FALSE;
366 wma->dwPlayableVideoFrames = wma->mah.dwTotalFrames;
367 wma->lpVideoIndex = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
368 wma->dwPlayableVideoFrames * sizeof(struct MMIOPos));
369 if (!wma->lpVideoIndex) {
370 WARN("Can't alloc video index array\n");
371 return FALSE;
373 wma->dwPlayableAudioBlocks = 0;
374 wma->lpAudioIndex = NULL;
376 alb.numAudioBlocks = alb.numVideoFrames = 0;
377 alb.inVideoSize = alb.inAudioSize = 0;
378 alb.numAudioAllocated = 0;
380 while (mmioDescend(wma->hFile, &mmckInfo, &mmckList, 0) == 0) {
381 if (mmckInfo.fccType == listtypeAVIRECORD) {
382 MMCKINFO tmp;
384 while (mmioDescend(wma->hFile, &tmp, &mmckInfo, 0) == 0) {
385 MCIAVI_AddFrame(wma, &tmp, &alb);
386 mmioAscend(wma->hFile, &tmp, 0);
388 } else {
389 MCIAVI_AddFrame(wma, &mmckInfo, &alb);
392 mmioAscend(wma->hFile, &mmckInfo, 0);
394 if (alb.numVideoFrames != wma->dwPlayableVideoFrames) {
395 WARN("Found %ld video frames (/%ld), reducing playable frames\n",
396 alb.numVideoFrames, wma->dwPlayableVideoFrames);
397 wma->dwPlayableVideoFrames = alb.numVideoFrames;
399 wma->dwPlayableAudioBlocks = alb.numAudioBlocks;
401 if (alb.inVideoSize > wma->ash_video.dwSuggestedBufferSize) {
402 WARN("inVideoSize=%ld suggestedSize=%ld\n", alb.inVideoSize, wma->ash_video.dwSuggestedBufferSize);
403 wma->ash_video.dwSuggestedBufferSize = alb.inVideoSize;
405 if (alb.inAudioSize > wma->ash_audio.dwSuggestedBufferSize) {
406 WARN("inAudioSize=%ld suggestedSize=%ld\n", alb.inAudioSize, wma->ash_audio.dwSuggestedBufferSize);
407 wma->ash_audio.dwSuggestedBufferSize = alb.inAudioSize;
410 wma->indata = HeapAlloc(GetProcessHeap(), 0, wma->ash_video.dwSuggestedBufferSize);
411 if (!wma->indata) {
412 WARN("Can't alloc input buffer\n");
413 return FALSE;
416 return TRUE;
419 BOOL MCIAVI_OpenVideo(WINE_MCIAVI* wma)
421 HDC hDC;
422 DWORD outSize;
423 FOURCC fcc = wma->ash_video.fccHandler;
425 TRACE("fcc %4.4s\n", (LPSTR)&fcc);
427 wma->dwCachedFrame = -1;
429 /* check for builtin DIB compressions */
430 if ((fcc == mmioFOURCC('D','I','B',' ')) ||
431 (fcc == mmioFOURCC('R','L','E',' ')) ||
432 (fcc == BI_RGB) || (fcc == BI_RLE8) ||
433 (fcc == BI_RLE4) || (fcc == BI_BITFIELDS))
435 wma->hic = 0;
436 goto paint_frame;
439 /* get the right handle */
440 if (fcc == 0) fcc = wma->inbih->biCompression;
441 if (fcc == mmioFOURCC('C','R','A','M')) fcc = mmioFOURCC('M','S','V','C');
443 /* try to get a decompressor for that type */
444 wma->hic = ICLocate(ICTYPE_VIDEO, fcc, wma->inbih, NULL, ICMODE_DECOMPRESS);
445 if (!wma->hic) {
446 WARN("Can't locate codec for the file\n");
447 return FALSE;
450 outSize = sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD);
452 wma->outbih = HeapAlloc(GetProcessHeap(), 0, outSize);
453 if (!wma->outbih) {
454 WARN("Can't alloc output BIH\n");
455 return FALSE;
457 if (!ICGetDisplayFormat(wma->hic, wma->inbih, wma->outbih, 0, 0, 0)) {
458 WARN("Can't open decompressor\n");
459 return FALSE;
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);
474 wma->outdata = HeapAlloc(GetProcessHeap(), 0, wma->outbih->biSizeImage);
475 if (!wma->outdata) {
476 WARN("Can't alloc output buffer\n");
477 return FALSE;
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;
486 paint_frame:
487 hDC = wma->hWndPaint ? GetDC(wma->hWndPaint) : 0;
488 if (hDC)
490 MCIAVI_PaintFrame(wma, hDC);
491 ReleaseDC(wma->hWndPaint, hDC);
493 return TRUE;
496 static void CALLBACK MCIAVI_waveCallback(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance,
497 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
499 WINE_MCIAVI *wma = (WINE_MCIAVI *)MCIAVI_mciGetOpenDev(dwInstance);
501 if (!wma) return;
503 EnterCriticalSection(&wma->cs);
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);
518 LeaveCriticalSection(&wma->cs);
521 DWORD MCIAVI_OpenAudio(WINE_MCIAVI* wma, unsigned* nHdr, LPWAVEHDR* pWaveHdr)
523 DWORD dwRet;
524 LPWAVEHDR waveHdr;
525 unsigned i;
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;
536 /* FIXME: should set up a heuristic to compute the number of wave headers
537 * to be used...
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;
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;
559 if (wma->dwCurrVideoFrame != 0 && wma->lpWaveFormat) {
560 FIXME("Should recompute dwCurrAudioBlock, except unsynchronized sound & video\n");
562 wma->dwCurrAudioBlock = 0;
564 wma->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
565 wma->dwEventCount = *nHdr - 1;
566 *pWaveHdr = waveHdr;
567 cleanUp:
568 return dwRet;
571 void MCIAVI_PlayAudioBlocks(WINE_MCIAVI* wma, unsigned nHdr, LPWAVEHDR waveHdr)
573 if (!wma->lpAudioIndex)
574 return;
575 TRACE("%ld (ec=%lu)\n", wma->lpAudioIndex[wma->dwCurrAudioBlock].dwOffset, wma->dwEventCount);
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;
582 ResetEvent(wma->hEvent);
583 if (InterlockedDecrement(&wma->dwEventCount) < 0 ||
584 !wma->lpAudioIndex[wma->dwCurrAudioBlock].dwOffset)
586 InterlockedIncrement(&wma->dwEventCount);
587 break;
590 mmioSeek(wma->hFile, wma->lpAudioIndex[wma->dwCurrAudioBlock].dwOffset, SEEK_SET);
591 mmioRead(wma->hFile, waveHdr[whidx].lpData, wma->lpAudioIndex[wma->dwCurrAudioBlock].dwSize);
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++;
600 LRESULT MCIAVI_PaintFrame(WINE_MCIAVI* wma, HDC hDC)
602 void* pBitmapData = NULL;
603 LPBITMAPINFO pBitmapInfo = NULL;
604 HDC hdcMem;
605 HBITMAP hbmOld;
606 int nWidth;
607 int nHeight;
609 if (!hDC || !wma->inbih)
610 return TRUE;
612 TRACE("Painting frame %lu (cached %lu)\n", wma->dwCurrVideoFrame, wma->dwCachedFrame);
614 if (wma->dwCurrVideoFrame != wma->dwCachedFrame)
616 if (!wma->lpVideoIndex[wma->dwCurrVideoFrame].dwOffset)
617 return FALSE;
619 if (wma->lpVideoIndex[wma->dwCurrVideoFrame].dwSize)
621 mmioSeek(wma->hFile, wma->lpVideoIndex[wma->dwCurrVideoFrame].dwOffset, SEEK_SET);
622 mmioRead(wma->hFile, wma->indata, wma->lpVideoIndex[wma->dwCurrVideoFrame].dwSize);
624 /* FIXME ? */
625 wma->inbih->biSizeImage = wma->lpVideoIndex[wma->dwCurrVideoFrame].dwSize;
627 if (wma->hic && ICDecompress(wma->hic, 0, wma->inbih, wma->indata,
628 wma->outbih, wma->outdata) != ICERR_OK)
630 WARN("Decompression error\n");
631 return FALSE;
635 wma->dwCachedFrame = wma->dwCurrVideoFrame;
638 if (wma->hic) {
639 pBitmapData = wma->outdata;
640 pBitmapInfo = (LPBITMAPINFO)wma->outbih;
642 nWidth = wma->outbih->biWidth;
643 nHeight = wma->outbih->biHeight;
644 } else {
645 pBitmapData = wma->indata;
646 pBitmapInfo = (LPBITMAPINFO)wma->inbih;
648 nWidth = wma->inbih->biWidth;
649 nHeight = wma->inbih->biHeight;
652 if (!wma->hbmFrame)
653 wma->hbmFrame = CreateCompatibleBitmap(hDC, nWidth, nHeight);
655 SetDIBits(hDC, wma->hbmFrame, 0, nHeight, pBitmapData, pBitmapInfo, DIB_RGB_COLORS);
657 hdcMem = CreateCompatibleDC(hDC);
658 hbmOld = SelectObject(hdcMem, wma->hbmFrame);
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);
666 SelectObject(hdcMem, hbmOld);
667 DeleteDC(hdcMem);
668 return TRUE;