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