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