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 if (alb.numVideoFrames != wma->dwPlayableVideoFrames) { 402 WARN("AVI header says %d frames, we found %d video frames, reducing playable frames\n", 403 wma->dwPlayableVideoFrames, alb.numVideoFrames); 404 wma->dwPlayableVideoFrames = alb.numVideoFrames; 405 } 406 wma->dwPlayableAudioBlocks = alb.numAudioBlocks; 407 408 if (alb.inVideoSize > wma->ash_video.dwSuggestedBufferSize) { 409 WARN("inVideoSize=%d suggestedSize=%d\n", alb.inVideoSize, wma->ash_video.dwSuggestedBufferSize); 410 wma->ash_video.dwSuggestedBufferSize = alb.inVideoSize; 411 } 412 if (alb.inAudioSize > wma->ash_audio.dwSuggestedBufferSize) { 413 WARN("inAudioSize=%d suggestedSize=%d\n", alb.inAudioSize, wma->ash_audio.dwSuggestedBufferSize); 414 wma->ash_audio.dwSuggestedBufferSize = alb.inAudioSize; 415 } 416 417 wma->indata = HeapAlloc(GetProcessHeap(), 0, wma->ash_video.dwSuggestedBufferSize); 418 if (!wma->indata) { 419 WARN("Can't alloc input buffer\n"); 420 return FALSE; 421 } 422 423 return TRUE; 424 } 425 426 BOOL MCIAVI_OpenVideo(WINE_MCIAVI* wma) 427 { 428 HDC hDC; 429 DWORD outSize; 430 FOURCC fcc = wma->ash_video.fccHandler; 431 432 TRACE("fcc %4.4s\n", (LPSTR)&fcc); 433 434 wma->dwCachedFrame = -1; 435 436 /* get the right handle */ 437 if (fcc == mmioFOURCC('C','R','A','M')) fcc = mmioFOURCC('M','S','V','C'); 438 439 /* try to get a decompressor for that type */ 440 wma->hic = ICLocate(ICTYPE_VIDEO, fcc, wma->inbih, NULL, ICMODE_DECOMPRESS); 441 if (!wma->hic) { 442 /* check for builtin DIB compressions */ 443 fcc = wma->inbih->biCompression; 444 if ((fcc == mmioFOURCC('D','I','B',' ')) || 445 (fcc == mmioFOURCC('R','L','E',' ')) || 446 (fcc == BI_RGB) || (fcc == BI_RLE8) || 447 (fcc == BI_RLE4) || (fcc == BI_BITFIELDS)) 448 goto paint_frame; 449 450 WARN("Can't locate codec for the file\n"); 451 return FALSE; 452 } 453 454 outSize = sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD); 455 456 wma->outbih = HeapAlloc(GetProcessHeap(), 0, outSize); 457 if (!wma->outbih) { 458 WARN("Can't alloc output BIH\n"); 459 return FALSE; 460 } 461 if (!ICGetDisplayFormat(wma->hic, wma->inbih, wma->outbih, 0, 0, 0)) { 462 WARN("Can't open decompressor\n"); 463 return FALSE; 464 } 465 466 TRACE("bih.biSize=%d\n", wma->outbih->biSize); 467 TRACE("bih.biWidth=%d\n", wma->outbih->biWidth); 468 TRACE("bih.biHeight=%d\n", wma->outbih->biHeight); 469 TRACE("bih.biPlanes=%d\n", wma->outbih->biPlanes); 470 TRACE("bih.biBitCount=%d\n", wma->outbih->biBitCount); 471 TRACE("bih.biCompression=%x\n", wma->outbih->biCompression); 472 TRACE("bih.biSizeImage=%d\n", wma->outbih->biSizeImage); 473 TRACE("bih.biXPelsPerMeter=%d\n", wma->outbih->biXPelsPerMeter); 474 TRACE("bih.biYPelsPerMeter=%d\n", wma->outbih->biYPelsPerMeter); 475 TRACE("bih.biClrUsed=%d\n", wma->outbih->biClrUsed); 476 TRACE("bih.biClrImportant=%d\n", wma->outbih->biClrImportant); 477 478 wma->outdata = HeapAlloc(GetProcessHeap(), 0, wma->outbih->biSizeImage); 479 if (!wma->outdata) { 480 WARN("Can't alloc output buffer\n"); 481 return FALSE; 482 } 483 484 if (ICSendMessage(wma->hic, ICM_DECOMPRESS_BEGIN, 485 (DWORD_PTR)wma->inbih, (DWORD_PTR)wma->outbih) != ICERR_OK) { 486 WARN("Can't begin decompression\n"); 487 return FALSE; 488 } 489 490 paint_frame: 491 hDC = wma->hWndPaint ? GetDC(wma->hWndPaint) : 0; 492 if (hDC) 493 { 494 MCIAVI_PaintFrame(wma, hDC); 495 ReleaseDC(wma->hWndPaint, hDC); 496 } 497 return TRUE; 498 } 499 500 static void CALLBACK MCIAVI_waveCallback(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance, 501 DWORD_PTR dwParam1, DWORD_PTR dwParam2) 502 { 503 WINE_MCIAVI *wma = MCIAVI_mciGetOpenDev(dwInstance); 504 505 if (!wma) return; 506 507 EnterCriticalSection(&wma->cs); 508 509 switch (uMsg) { 510 case WOM_OPEN: 511 case WOM_CLOSE: 512 break; 513 case WOM_DONE: 514 InterlockedIncrement(&wma->dwEventCount); 515 TRACE("Returning waveHdr=%lx\n", dwParam1); 516 SetEvent(wma->hEvent); 517 break; 518 default: 519 ERR("Unknown uMsg=%d\n", uMsg); 520 } 521 522 LeaveCriticalSection(&wma->cs); 523 } 524 525 DWORD MCIAVI_OpenAudio(WINE_MCIAVI* wma, unsigned* nHdr, LPWAVEHDR* pWaveHdr) 526 { 527 DWORD dwRet; 528 LPWAVEHDR waveHdr; 529 unsigned i; 530 531 dwRet = waveOutOpen((HWAVEOUT *)&wma->hWave, WAVE_MAPPER, wma->lpWaveFormat, 532 (DWORD_PTR)MCIAVI_waveCallback, wma->wDevID, CALLBACK_FUNCTION); 533 if (dwRet != 0) { 534 TRACE("Can't open low level audio device %d\n", dwRet); 535 dwRet = MCIERR_DEVICE_OPEN; 536 wma->hWave = 0; 537 goto cleanUp; 538 } 539 540 /* FIXME: should set up a heuristic to compute the number of wave headers 541 * to be used... 542 */ 543 *nHdr = 7; 544 waveHdr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 545 *nHdr * (sizeof(WAVEHDR) + wma->ash_audio.dwSuggestedBufferSize)); 546 if (!waveHdr) { 547 TRACE("Can't alloc wave headers\n"); 548 dwRet = MCIERR_DEVICE_OPEN; 549 goto cleanUp; 550 } 551 552 for (i = 0; i < *nHdr; i++) { 553 /* other fields are zero:ed on allocation */ 554 waveHdr[i].lpData = (char*)waveHdr + 555 *nHdr * sizeof(WAVEHDR) + i * wma->ash_audio.dwSuggestedBufferSize; 556 waveHdr[i].dwBufferLength = wma->ash_audio.dwSuggestedBufferSize; 557 if (waveOutPrepareHeader(wma->hWave, &waveHdr[i], sizeof(WAVEHDR))) { 558 dwRet = MCIERR_INTERNAL; 559 goto cleanUp; 560 } 561 } 562 563 if (wma->dwCurrVideoFrame != 0 && wma->lpWaveFormat) { 564 FIXME("Should recompute dwCurrAudioBlock, except unsynchronized sound & video\n"); 565 } 566 wma->dwCurrAudioBlock = 0; 567 568 wma->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL); 569 wma->dwEventCount = *nHdr - 1; 570 *pWaveHdr = waveHdr; 571 cleanUp: 572 return dwRet; 573 } 574 575 void MCIAVI_PlayAudioBlocks(WINE_MCIAVI* wma, unsigned nHdr, LPWAVEHDR waveHdr) 576 { 577 if (!wma->lpAudioIndex) 578 return; 579 TRACE("%d (ec=%u)\n", wma->lpAudioIndex[wma->dwCurrAudioBlock].dwOffset, wma->dwEventCount); 580 581 /* push as many blocks as possible => audio gets priority */ 582 while (wma->dwStatus != MCI_MODE_STOP && wma->dwStatus != MCI_MODE_NOT_READY && 583 wma->dwCurrAudioBlock < wma->dwPlayableAudioBlocks) { 584 unsigned whidx = wma->dwCurrAudioBlock % nHdr; 585 586 ResetEvent(wma->hEvent); 587 if (InterlockedDecrement(&wma->dwEventCount) < 0 || 588 !wma->lpAudioIndex[wma->dwCurrAudioBlock].dwOffset) 589 { 590 InterlockedIncrement(&wma->dwEventCount); 591 break; 592 } 593 594 mmioSeek(wma->hFile, wma->lpAudioIndex[wma->dwCurrAudioBlock].dwOffset, SEEK_SET); 595 mmioRead(wma->hFile, waveHdr[whidx].lpData, wma->lpAudioIndex[wma->dwCurrAudioBlock].dwSize); 596 597 waveHdr[whidx].dwFlags &= ~WHDR_DONE; 598 waveHdr[whidx].dwBufferLength = wma->lpAudioIndex[wma->dwCurrAudioBlock].dwSize; 599 waveOutWrite(wma->hWave, &waveHdr[whidx], sizeof(WAVEHDR)); 600 wma->dwCurrAudioBlock++; 601 } 602 } 603 604 double MCIAVI_PaintFrame(WINE_MCIAVI* wma, HDC hDC) 605 { 606 void* pBitmapData; 607 LPBITMAPINFO pBitmapInfo; 608 609 if (!hDC || !wma->inbih) 610 return 0; 611 612 TRACE("Painting frame %u (cached %u)\n", wma->dwCurrVideoFrame, wma->dwCachedFrame); 613 614 if (wma->dwCurrVideoFrame != wma->dwCachedFrame) 615 { 616 if (!wma->lpVideoIndex[wma->dwCurrVideoFrame].dwOffset) 617 return 0; 618 619 if (wma->lpVideoIndex[wma->dwCurrVideoFrame].dwSize) 620 { 621 mmioSeek(wma->hFile, wma->lpVideoIndex[wma->dwCurrVideoFrame].dwOffset, SEEK_SET); 622 mmioRead(wma->hFile, wma->indata, wma->lpVideoIndex[wma->dwCurrVideoFrame].dwSize); 623 624 wma->inbih->biSizeImage = wma->lpVideoIndex[wma->dwCurrVideoFrame].dwSize; 625 626 if (wma->hic && ICDecompress(wma->hic, 0, wma->inbih, wma->indata, 627 wma->outbih, wma->outdata) != ICERR_OK) 628 { 629 WARN("Decompression error\n"); 630 return 0; 631 } 632 } 633 634 wma->dwCachedFrame = wma->dwCurrVideoFrame; 635 } 636 637 if (wma->hic) { 638 pBitmapData = wma->outdata; 639 pBitmapInfo = (LPBITMAPINFO)wma->outbih; 640 } else { 641 pBitmapData = wma->indata; 642 pBitmapInfo = (LPBITMAPINFO)wma->inbih; 643 } 644 645 StretchDIBits(hDC, 646 wma->dest.left, wma->dest.top, 647 wma->dest.right - wma->dest.left, wma->dest.bottom - wma->dest.top, 648 wma->source.left, wma->source.top, 649 wma->source.right - wma->source.left, wma->source.bottom - wma->source.top, 650 pBitmapData, pBitmapInfo, DIB_RGB_COLORS, SRCCOPY); 651 652 return (wma->ash_video.dwScale / (double)wma->ash_video.dwRate) * 1000000; 653 } 654