1 /* 2 * Digital video MCI Wine Driver 3 * 4 * Copyright 1999, 2000 Eric POUECH 5 * Copyright 2003 Dmitry Timoshkov 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this library; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 20 */ 21 22 #include "private_mciavi.h" 23 #include "wine/debug.h" 24 25 WINE_DEFAULT_DEBUG_CHANNEL(mciavi); 26 27 static BOOL MCIAVI_GetInfoAudio(WINE_MCIAVI* wma, const MMCKINFO* mmckList, MMCKINFO *mmckStream) 28 { 29 MMCKINFO mmckInfo; 30 31 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 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 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 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 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 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 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 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 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